1 | #include <gio/gio.h> |
2 | #include <gio/gunixsocketaddress.h> |
3 | #include <glib.h> |
4 | #include <stdlib.h> |
5 | #include <stdio.h> |
6 | #include <string.h> |
7 | |
8 | #include "gtlsconsoleinteraction.h" |
9 | |
10 | GMainLoop *loop; |
11 | |
12 | gboolean verbose = FALSE; |
13 | gboolean non_blocking = FALSE; |
14 | gboolean use_udp = FALSE; |
15 | int cancel_timeout = 0; |
16 | int read_timeout = 0; |
17 | gboolean unix_socket = FALSE; |
18 | gboolean tls = FALSE; |
19 | |
20 | static GOptionEntry cmd_entries[] = { |
21 | {"cancel" , 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, |
22 | "Cancel any op after the specified amount of seconds" , NULL}, |
23 | {"udp" , 'u', 0, G_OPTION_ARG_NONE, &use_udp, |
24 | "Use udp instead of tcp" , NULL}, |
25 | {"verbose" , 'v', 0, G_OPTION_ARG_NONE, &verbose, |
26 | "Be verbose" , NULL}, |
27 | {"non-blocking" , 'n', 0, G_OPTION_ARG_NONE, &non_blocking, |
28 | "Enable non-blocking i/o" , NULL}, |
29 | #ifdef G_OS_UNIX |
30 | {"unix" , 'U', 0, G_OPTION_ARG_NONE, &unix_socket, |
31 | "Use a unix socket instead of IP" , NULL}, |
32 | #endif |
33 | {"timeout" , 't', 0, G_OPTION_ARG_INT, &read_timeout, |
34 | "Time out reads after the specified number of seconds" , NULL}, |
35 | {"tls" , 'T', 0, G_OPTION_ARG_NONE, &tls, |
36 | "Use TLS (SSL)" , NULL}, |
37 | {NULL} |
38 | }; |
39 | |
40 | #include "socket-common.c" |
41 | |
42 | static gboolean |
43 | accept_certificate (GTlsClientConnection *conn, |
44 | GTlsCertificate *cert, |
45 | GTlsCertificateFlags errors, |
46 | gpointer user_data) |
47 | { |
48 | g_print (format: "Certificate would have been rejected ( " ); |
49 | if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA) |
50 | g_print (format: "unknown-ca " ); |
51 | if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY) |
52 | g_print (format: "bad-identity " ); |
53 | if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED) |
54 | g_print (format: "not-activated " ); |
55 | if (errors & G_TLS_CERTIFICATE_EXPIRED) |
56 | g_print (format: "expired " ); |
57 | if (errors & G_TLS_CERTIFICATE_REVOKED) |
58 | g_print (format: "revoked " ); |
59 | if (errors & G_TLS_CERTIFICATE_INSECURE) |
60 | g_print (format: "insecure " ); |
61 | g_print (format: ") but accepting anyway.\n" ); |
62 | |
63 | return TRUE; |
64 | } |
65 | |
66 | static GTlsCertificate * |
67 | lookup_client_certificate (GTlsClientConnection *conn, |
68 | GError **error) |
69 | { |
70 | GList *l, *accepted; |
71 | GList *c, *certificates; |
72 | GTlsDatabase *database; |
73 | GTlsCertificate *certificate = NULL; |
74 | GTlsConnection *base; |
75 | |
76 | accepted = g_tls_client_connection_get_accepted_cas (conn); |
77 | for (l = accepted; l != NULL; l = g_list_next (l)) |
78 | { |
79 | base = G_TLS_CONNECTION (conn); |
80 | database = g_tls_connection_get_database (conn: base); |
81 | certificates = g_tls_database_lookup_certificates_issued_by (self: database, issuer_raw_dn: l->data, |
82 | interaction: g_tls_connection_get_interaction (conn: base), |
83 | flags: G_TLS_DATABASE_LOOKUP_KEYPAIR, |
84 | NULL, error); |
85 | if (error && *error) |
86 | break; |
87 | |
88 | if (certificates) |
89 | certificate = g_object_ref (certificates->data); |
90 | |
91 | for (c = certificates; c != NULL; c = g_list_next (c)) |
92 | g_object_unref (object: c->data); |
93 | g_list_free (list: certificates); |
94 | } |
95 | |
96 | for (l = accepted; l != NULL; l = g_list_next (l)) |
97 | g_byte_array_unref (array: l->data); |
98 | g_list_free (list: accepted); |
99 | |
100 | if (certificate == NULL && error && !*error) |
101 | g_set_error_literal (err: error, G_TLS_ERROR, code: G_TLS_ERROR_CERTIFICATE_REQUIRED, |
102 | message: "Server requested a certificate, but could not find relevant certificate in database." ); |
103 | return certificate; |
104 | } |
105 | |
106 | static gboolean |
107 | make_connection (const char *argument, |
108 | GTlsCertificate *certificate, |
109 | GCancellable *cancellable, |
110 | GSocket **socket, |
111 | GSocketAddress **address, |
112 | GIOStream **connection, |
113 | GInputStream **istream, |
114 | GOutputStream **ostream, |
115 | GError **error) |
116 | { |
117 | GSocketType socket_type; |
118 | GSocketFamily socket_family; |
119 | GSocketAddressEnumerator *enumerator; |
120 | GSocketConnectable *connectable; |
121 | GSocketAddress *src_address; |
122 | GTlsInteraction *interaction; |
123 | GError *err = NULL; |
124 | |
125 | if (use_udp) |
126 | socket_type = G_SOCKET_TYPE_DATAGRAM; |
127 | else |
128 | socket_type = G_SOCKET_TYPE_STREAM; |
129 | |
130 | if (unix_socket) |
131 | socket_family = G_SOCKET_FAMILY_UNIX; |
132 | else |
133 | socket_family = G_SOCKET_FAMILY_IPV4; |
134 | |
135 | *socket = g_socket_new (family: socket_family, type: socket_type, protocol: 0, error); |
136 | if (*socket == NULL) |
137 | return FALSE; |
138 | |
139 | if (read_timeout) |
140 | g_socket_set_timeout (socket: *socket, timeout: read_timeout); |
141 | |
142 | if (unix_socket) |
143 | { |
144 | GSocketAddress *addr; |
145 | |
146 | addr = socket_address_from_string (name: argument); |
147 | if (addr == NULL) |
148 | { |
149 | g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED, |
150 | format: "Could not parse '%s' as unix socket name" , argument); |
151 | return FALSE; |
152 | } |
153 | connectable = G_SOCKET_CONNECTABLE (addr); |
154 | } |
155 | else |
156 | { |
157 | connectable = g_network_address_parse (host_and_port: argument, default_port: 7777, error); |
158 | if (connectable == NULL) |
159 | return FALSE; |
160 | } |
161 | |
162 | enumerator = g_socket_connectable_enumerate (connectable); |
163 | while (TRUE) |
164 | { |
165 | *address = g_socket_address_enumerator_next (enumerator, cancellable, error); |
166 | if (*address == NULL) |
167 | { |
168 | if (error != NULL && *error == NULL) |
169 | g_set_error_literal (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED, |
170 | message: "No more addresses to try" ); |
171 | return FALSE; |
172 | } |
173 | |
174 | if (g_socket_connect (socket: *socket, address: *address, cancellable, error: &err)) |
175 | break; |
176 | g_message ("Connection to %s failed: %s, trying next" , socket_address_to_string (*address), err->message); |
177 | g_clear_error (err: &err); |
178 | |
179 | g_object_unref (object: *address); |
180 | } |
181 | g_object_unref (object: enumerator); |
182 | |
183 | g_print (format: "Connected to %s\n" , |
184 | socket_address_to_string (address: *address)); |
185 | |
186 | src_address = g_socket_get_local_address (socket: *socket, error); |
187 | if (!src_address) |
188 | { |
189 | g_prefix_error (err: error, format: "Error getting local address: " ); |
190 | return FALSE; |
191 | } |
192 | |
193 | g_print (format: "local address: %s\n" , |
194 | socket_address_to_string (address: src_address)); |
195 | g_object_unref (object: src_address); |
196 | |
197 | if (use_udp) |
198 | { |
199 | *connection = NULL; |
200 | *istream = NULL; |
201 | *ostream = NULL; |
202 | } |
203 | else |
204 | *connection = G_IO_STREAM (g_socket_connection_factory_create_connection (*socket)); |
205 | |
206 | if (tls) |
207 | { |
208 | GIOStream *tls_conn; |
209 | |
210 | tls_conn = g_tls_client_connection_new (base_io_stream: *connection, server_identity: connectable, error); |
211 | if (!tls_conn) |
212 | { |
213 | g_prefix_error (err: error, format: "Could not create TLS connection: " ); |
214 | return FALSE; |
215 | } |
216 | |
217 | g_signal_connect (tls_conn, "accept-certificate" , |
218 | G_CALLBACK (accept_certificate), NULL); |
219 | |
220 | interaction = g_tls_console_interaction_new (); |
221 | g_tls_connection_set_interaction (G_TLS_CONNECTION (tls_conn), interaction); |
222 | g_object_unref (object: interaction); |
223 | |
224 | if (certificate) |
225 | g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_conn), certificate); |
226 | |
227 | g_object_unref (object: *connection); |
228 | *connection = G_IO_STREAM (tls_conn); |
229 | |
230 | if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), |
231 | cancellable, error)) |
232 | { |
233 | g_prefix_error (err: error, format: "Error during TLS handshake: " ); |
234 | return FALSE; |
235 | } |
236 | } |
237 | g_object_unref (object: connectable); |
238 | |
239 | if (*connection) |
240 | { |
241 | *istream = g_io_stream_get_input_stream (stream: *connection); |
242 | *ostream = g_io_stream_get_output_stream (stream: *connection); |
243 | } |
244 | |
245 | return TRUE; |
246 | } |
247 | |
248 | int |
249 | main (int argc, |
250 | char *argv[]) |
251 | { |
252 | GSocket *socket; |
253 | GSocketAddress *address; |
254 | GError *error = NULL; |
255 | GOptionContext *context; |
256 | GCancellable *cancellable; |
257 | GIOStream *connection; |
258 | GInputStream *istream; |
259 | GOutputStream *ostream; |
260 | GSocketAddress *src_address; |
261 | GTlsCertificate *certificate = NULL; |
262 | gint i; |
263 | |
264 | address = NULL; |
265 | connection = NULL; |
266 | |
267 | context = g_option_context_new (parameter_string: " <hostname>[:port] - Test GSocket client stuff" ); |
268 | g_option_context_add_main_entries (context, entries: cmd_entries, NULL); |
269 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
270 | { |
271 | g_printerr (format: "%s: %s\n" , argv[0], error->message); |
272 | return 1; |
273 | } |
274 | |
275 | if (argc != 2) |
276 | { |
277 | g_printerr (format: "%s: %s\n" , argv[0], "Need to specify hostname / unix socket name" ); |
278 | return 1; |
279 | } |
280 | |
281 | if (use_udp && tls) |
282 | { |
283 | g_printerr (format: "DTLS (TLS over UDP) is not supported" ); |
284 | return 1; |
285 | } |
286 | |
287 | if (cancel_timeout) |
288 | { |
289 | GThread *thread; |
290 | cancellable = g_cancellable_new (); |
291 | thread = g_thread_new (name: "cancel" , func: cancel_thread, data: cancellable); |
292 | g_thread_unref (thread); |
293 | } |
294 | else |
295 | { |
296 | cancellable = NULL; |
297 | } |
298 | |
299 | loop = g_main_loop_new (NULL, FALSE); |
300 | |
301 | for (i = 0; i < 2; i++) |
302 | { |
303 | if (make_connection (argument: argv[1], certificate, cancellable, socket: &socket, address: &address, |
304 | connection: &connection, istream: &istream, ostream: &ostream, error: &error)) |
305 | break; |
306 | |
307 | if (g_error_matches (error, G_TLS_ERROR, code: G_TLS_ERROR_CERTIFICATE_REQUIRED)) |
308 | { |
309 | g_clear_error (err: &error); |
310 | certificate = lookup_client_certificate (G_TLS_CLIENT_CONNECTION (connection), error: &error); |
311 | if (certificate != NULL) |
312 | continue; |
313 | } |
314 | |
315 | g_printerr (format: "%s: %s" , argv[0], error->message); |
316 | return 1; |
317 | } |
318 | |
319 | /* TODO: Test non-blocking connect/handshake */ |
320 | if (non_blocking) |
321 | g_socket_set_blocking (socket, FALSE); |
322 | |
323 | while (TRUE) |
324 | { |
325 | gchar buffer[4096]; |
326 | gssize size; |
327 | gsize to_send; |
328 | |
329 | if (fgets (s: buffer, n: sizeof buffer, stdin) == NULL) |
330 | break; |
331 | |
332 | to_send = strlen (s: buffer); |
333 | while (to_send > 0) |
334 | { |
335 | if (use_udp) |
336 | { |
337 | ensure_socket_condition (socket, condition: G_IO_OUT, cancellable); |
338 | size = g_socket_send_to (socket, address, |
339 | buffer, size: to_send, |
340 | cancellable, error: &error); |
341 | } |
342 | else |
343 | { |
344 | ensure_connection_condition (stream: connection, condition: G_IO_OUT, cancellable); |
345 | size = g_output_stream_write (stream: ostream, |
346 | buffer, count: to_send, |
347 | cancellable, error: &error); |
348 | } |
349 | |
350 | if (size < 0) |
351 | { |
352 | if (g_error_matches (error, |
353 | G_IO_ERROR, |
354 | code: G_IO_ERROR_WOULD_BLOCK)) |
355 | { |
356 | g_print (format: "socket send would block, handling\n" ); |
357 | g_error_free (error); |
358 | error = NULL; |
359 | continue; |
360 | } |
361 | else |
362 | { |
363 | g_printerr (format: "Error sending to socket: %s\n" , |
364 | error->message); |
365 | return 1; |
366 | } |
367 | } |
368 | |
369 | g_print (format: "sent %" G_GSSIZE_FORMAT " bytes of data\n" , size); |
370 | |
371 | if (size == 0) |
372 | { |
373 | g_printerr (format: "Unexpected short write\n" ); |
374 | return 1; |
375 | } |
376 | |
377 | to_send -= size; |
378 | } |
379 | |
380 | if (use_udp) |
381 | { |
382 | ensure_socket_condition (socket, condition: G_IO_IN, cancellable); |
383 | size = g_socket_receive_from (socket, address: &src_address, |
384 | buffer, size: sizeof buffer, |
385 | cancellable, error: &error); |
386 | } |
387 | else |
388 | { |
389 | ensure_connection_condition (stream: connection, condition: G_IO_IN, cancellable); |
390 | size = g_input_stream_read (stream: istream, |
391 | buffer, count: sizeof buffer, |
392 | cancellable, error: &error); |
393 | } |
394 | |
395 | if (size < 0) |
396 | { |
397 | g_printerr (format: "Error receiving from socket: %s\n" , |
398 | error->message); |
399 | return 1; |
400 | } |
401 | |
402 | if (size == 0) |
403 | break; |
404 | |
405 | g_print (format: "received %" G_GSSIZE_FORMAT " bytes of data" , size); |
406 | if (use_udp) |
407 | g_print (format: " from %s" , socket_address_to_string (address: src_address)); |
408 | g_print (format: "\n" ); |
409 | |
410 | if (verbose) |
411 | g_print (format: "-------------------------\n" |
412 | "%.*s" |
413 | "-------------------------\n" , |
414 | (int)size, buffer); |
415 | |
416 | } |
417 | |
418 | g_print (format: "closing socket\n" ); |
419 | |
420 | if (connection) |
421 | { |
422 | if (!g_io_stream_close (stream: connection, cancellable, error: &error)) |
423 | { |
424 | g_printerr (format: "Error closing connection: %s\n" , |
425 | error->message); |
426 | return 1; |
427 | } |
428 | g_object_unref (object: connection); |
429 | } |
430 | else |
431 | { |
432 | if (!g_socket_close (socket, error: &error)) |
433 | { |
434 | g_printerr (format: "Error closing socket: %s\n" , |
435 | error->message); |
436 | return 1; |
437 | } |
438 | } |
439 | |
440 | g_object_unref (object: socket); |
441 | g_object_unref (object: address); |
442 | |
443 | return 0; |
444 | } |
445 | |