gio/tests/socket-client: fix a crash (and some indentation)
[platform/upstream/glib.git] / gio / tests / socket-client.c
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 ("Certificate would have been rejected ( ");
49   if (errors & G_TLS_CERTIFICATE_UNKNOWN_CA)
50     g_print ("unknown-ca ");
51   if (errors & G_TLS_CERTIFICATE_BAD_IDENTITY)
52     g_print ("bad-identity ");
53   if (errors & G_TLS_CERTIFICATE_NOT_ACTIVATED)
54     g_print ("not-activated ");
55   if (errors & G_TLS_CERTIFICATE_EXPIRED)
56     g_print ("expired ");
57   if (errors & G_TLS_CERTIFICATE_REVOKED)
58     g_print ("revoked ");
59   if (errors & G_TLS_CERTIFICATE_INSECURE)
60     g_print ("insecure ");
61   g_print (") 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 (base);
81       certificates = g_tls_database_lookup_certificates_issued_by (database, l->data,
82                                                                    g_tls_connection_get_interaction (base),
83                                                                    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 (c->data);
93       g_list_free (certificates);
94     }
95
96   for (l = accepted; l != NULL; l = g_list_next (l))
97     g_byte_array_unref (l->data);
98   g_list_free (accepted);
99
100   if (certificate == NULL && error && !*error)
101     g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED,
102                          "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 (socket_family, socket_type, 0, error);
136   if (*socket == NULL)
137     return FALSE;
138
139   if (read_timeout)
140     g_socket_set_timeout (*socket, read_timeout);
141
142   if (unix_socket)
143     {
144       GSocketAddress *addr;
145
146       addr = socket_address_from_string (argument);
147       if (addr == NULL)
148         {
149           g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
150                        "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 (argument, 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 (error, G_IO_ERROR, G_IO_ERROR_FAILED,
170                                  "No more addresses to try");
171           return FALSE;
172         }
173
174       if (g_socket_connect (*socket, *address, cancellable, &err))
175         break;
176       g_message ("Connection to %s failed: %s, trying next\n", socket_address_to_string (*address), err->message);
177       g_clear_error (&err);
178
179       g_object_unref (*address);
180     }
181   g_object_unref (enumerator);
182
183   g_print ("Connected to %s\n",
184            socket_address_to_string (*address));
185
186   src_address = g_socket_get_local_address (*socket, error);
187   if (!src_address)
188     {
189       g_prefix_error (error, "Error getting local address: ");
190       return FALSE;
191     }
192
193   g_print ("local address: %s\n",
194            socket_address_to_string (src_address));
195   g_object_unref (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 (*connection, connectable, error);
211       if (!tls_conn)
212         {
213           g_prefix_error (error, "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 (interaction);
223
224       if (certificate)
225         g_tls_connection_set_certificate (G_TLS_CONNECTION (tls_conn), certificate);
226
227       g_object_unref (*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 (error, "Error during TLS handshake: ");
234           return FALSE;
235         }
236     }
237   g_object_unref (connectable);
238
239   if (*connection)
240     {
241       *istream = g_io_stream_get_input_stream (*connection);
242       *ostream = g_io_stream_get_output_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   g_thread_init (NULL);
265
266   g_type_init ();
267
268   address = NULL;
269   connection = NULL;
270
271   context = g_option_context_new (" <hostname>[:port] - Test GSocket client stuff");
272   g_option_context_add_main_entries (context, cmd_entries, NULL);
273   if (!g_option_context_parse (context, &argc, &argv, &error))
274     {
275       g_printerr ("%s: %s\n", argv[0], error->message);
276       return 1;
277     }
278
279   if (argc != 2)
280     {
281       g_printerr ("%s: %s\n", argv[0], "Need to specify hostname / unix socket name");
282       return 1;
283     }
284
285   if (use_udp && tls)
286     {
287       g_printerr ("DTLS (TLS over UDP) is not supported");
288       return 1;
289     }
290
291   if (cancel_timeout)
292     {
293       cancellable = g_cancellable_new ();
294       g_thread_create (cancel_thread, cancellable, FALSE, NULL);
295     }
296   else
297     {
298       cancellable = NULL;
299     }
300
301   loop = g_main_loop_new (NULL, FALSE);
302
303   for (i = 0; i < 2; i++)
304     {
305       if (make_connection (argv[1], certificate, cancellable, &socket, &address,
306                            &connection, &istream, &ostream, &error))
307           break;
308
309       if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED))
310         {
311           g_clear_error (&error);
312           certificate = lookup_client_certificate (G_TLS_CLIENT_CONNECTION (connection), &error);
313           if (certificate != NULL)
314             continue;
315         }
316
317       g_printerr ("%s: %s", argv[0], error->message);
318       return 1;
319     }
320
321   /* TODO: Test non-blocking connect/handshake */
322   if (non_blocking)
323     g_socket_set_blocking (socket, FALSE);
324
325   while (TRUE)
326     {
327       gchar buffer[4096];
328       gssize size;
329       gsize to_send;
330
331       if (fgets (buffer, sizeof buffer, stdin) == NULL)
332         break;
333
334       to_send = strlen (buffer);
335       while (to_send > 0)
336         {
337           if (use_udp)
338             {
339               ensure_socket_condition (socket, G_IO_OUT, cancellable);
340               size = g_socket_send_to (socket, address,
341                                        buffer, to_send,
342                                        cancellable, &error);
343             }
344           else
345             {
346               ensure_connection_condition (connection, G_IO_OUT, cancellable);
347               size = g_output_stream_write (ostream,
348                                             buffer, to_send,
349                                             cancellable, &error);
350             }
351
352           if (size < 0)
353             {
354               if (g_error_matches (error,
355                                    G_IO_ERROR,
356                                    G_IO_ERROR_WOULD_BLOCK))
357                 {
358                   g_print ("socket send would block, handling\n");
359                   g_error_free (error);
360                   error = NULL;
361                   continue;
362                 }
363               else
364                 {
365                   g_printerr ("Error sending to socket: %s\n",
366                               error->message);
367                   return 1;
368                 }
369             }
370
371           g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size);
372
373           if (size == 0)
374             {
375               g_printerr ("Unexpected short write\n");
376               return 1;
377             }
378
379           to_send -= size;
380         }
381
382       if (use_udp)
383         {
384           ensure_socket_condition (socket, G_IO_IN, cancellable);
385           size = g_socket_receive_from (socket, &src_address,
386                                         buffer, sizeof buffer,
387                                         cancellable, &error);
388         }
389       else
390         {
391           ensure_connection_condition (connection, G_IO_IN, cancellable);
392           size = g_input_stream_read (istream,
393                                       buffer, sizeof buffer,
394                                       cancellable, &error);
395         }
396
397       if (size < 0)
398         {
399           g_printerr ("Error receiving from socket: %s\n",
400                       error->message);
401           return 1;
402         }
403
404       if (size == 0)
405         break;
406
407       g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size);
408       if (use_udp)
409         g_print (" from %s", socket_address_to_string (src_address));
410       g_print ("\n");
411
412       if (verbose)
413         g_print ("-------------------------\n"
414                  "%.*s"
415                  "-------------------------\n",
416                  (int)size, buffer);
417
418     }
419
420   g_print ("closing socket\n");
421
422   if (connection)
423     {
424       if (!g_io_stream_close (connection, cancellable, &error))
425         {
426           g_printerr ("Error closing connection: %s\n",
427                       error->message);
428           return 1;
429         }
430       g_object_unref (connection);
431     }
432   else
433     {
434       if (!g_socket_close (socket, &error))
435         {
436           g_printerr ("Error closing master socket: %s\n",
437                       error->message);
438           return 1;
439         }
440     }
441
442   g_object_unref (socket);
443   g_object_unref (address);
444
445   return 0;
446 }