6c25e08c99f00a050878c02bab31c141b0be52bc
[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   G_OPTION_ENTRY_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", 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   address = NULL;
265   connection = NULL;
266
267   context = g_option_context_new (" <hostname>[:port] - Test GSocket client stuff");
268   g_option_context_add_main_entries (context, cmd_entries, NULL);
269   if (!g_option_context_parse (context, &argc, &argv, &error))
270     {
271       g_printerr ("%s: %s\n", argv[0], error->message);
272       return 1;
273     }
274
275   if (argc != 2)
276     {
277       g_printerr ("%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 ("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 ("cancel", cancel_thread, 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 (argv[1], certificate, cancellable, &socket, &address,
304                            &connection, &istream, &ostream, &error))
305           break;
306
307       if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED))
308         {
309           g_clear_error (&error);
310           certificate = lookup_client_certificate (G_TLS_CLIENT_CONNECTION (connection), &error);
311           if (certificate != NULL)
312             continue;
313         }
314
315       g_printerr ("%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 (buffer, sizeof buffer, stdin) == NULL)
330         break;
331
332       to_send = strlen (buffer);
333       while (to_send > 0)
334         {
335           if (use_udp)
336             {
337               ensure_socket_condition (socket, G_IO_OUT, cancellable);
338               size = g_socket_send_to (socket, address,
339                                        buffer, to_send,
340                                        cancellable, &error);
341             }
342           else
343             {
344               ensure_connection_condition (connection, G_IO_OUT, cancellable);
345               size = g_output_stream_write (ostream,
346                                             buffer, to_send,
347                                             cancellable, &error);
348             }
349
350           if (size < 0)
351             {
352               if (g_error_matches (error,
353                                    G_IO_ERROR,
354                                    G_IO_ERROR_WOULD_BLOCK))
355                 {
356                   g_print ("socket send would block, handling\n");
357                   g_error_free (error);
358                   error = NULL;
359                   continue;
360                 }
361               else
362                 {
363                   g_printerr ("Error sending to socket: %s\n",
364                               error->message);
365                   return 1;
366                 }
367             }
368
369           g_print ("sent %" G_GSSIZE_FORMAT " bytes of data\n", size);
370
371           if (size == 0)
372             {
373               g_printerr ("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, G_IO_IN, cancellable);
383           size = g_socket_receive_from (socket, &src_address,
384                                         buffer, sizeof buffer,
385                                         cancellable, &error);
386         }
387       else
388         {
389           ensure_connection_condition (connection, G_IO_IN, cancellable);
390           size = g_input_stream_read (istream,
391                                       buffer, sizeof buffer,
392                                       cancellable, &error);
393         }
394
395       if (size < 0)
396         {
397           g_printerr ("Error receiving from socket: %s\n",
398                       error->message);
399           return 1;
400         }
401
402       if (size == 0)
403         break;
404
405       g_print ("received %" G_GSSIZE_FORMAT " bytes of data", size);
406       if (use_udp)
407         g_print (" from %s", socket_address_to_string (src_address));
408       g_print ("\n");
409
410       if (verbose)
411         g_print ("-------------------------\n"
412                  "%.*s"
413                  "-------------------------\n",
414                  (int)size, buffer);
415
416     }
417
418   g_print ("closing socket\n");
419
420   if (connection)
421     {
422       if (!g_io_stream_close (connection, cancellable, &error))
423         {
424           g_printerr ("Error closing connection: %s\n",
425                       error->message);
426           return 1;
427         }
428       g_object_unref (connection);
429     }
430   else
431     {
432       if (!g_socket_close (socket, &error))
433         {
434           g_printerr ("Error closing socket: %s\n",
435                       error->message);
436           return 1;
437         }
438     }
439
440   g_object_unref (socket);
441   g_object_unref (address);
442
443   return 0;
444 }