1 | #include <gio/gio.h> |
2 | #include <gio/gunixsocketaddress.h> |
3 | #include <glib.h> |
4 | #include <stdlib.h> |
5 | #include <string.h> |
6 | |
7 | GMainLoop *loop; |
8 | |
9 | int port = 7777; |
10 | gboolean verbose = FALSE; |
11 | gboolean dont_reuse_address = FALSE; |
12 | gboolean non_blocking = FALSE; |
13 | gboolean use_udp = FALSE; |
14 | int cancel_timeout = 0; |
15 | int read_timeout = 0; |
16 | int delay = 0; |
17 | gboolean unix_socket = FALSE; |
18 | const char *tls_cert_file = NULL; |
19 | |
20 | static GOptionEntry cmd_entries[] = { |
21 | {"port" , 'p', 0, G_OPTION_ARG_INT, &port, |
22 | "Local port to bind to" , NULL}, |
23 | {"cancel" , 'c', 0, G_OPTION_ARG_INT, &cancel_timeout, |
24 | "Cancel any op after the specified amount of seconds" , NULL}, |
25 | {"udp" , 'u', 0, G_OPTION_ARG_NONE, &use_udp, |
26 | "Use udp instead of tcp" , NULL}, |
27 | {"verbose" , 'v', 0, G_OPTION_ARG_NONE, &verbose, |
28 | "Be verbose" , NULL}, |
29 | {"no-reuse" , 0, 0, G_OPTION_ARG_NONE, &dont_reuse_address, |
30 | "Don't SOADDRREUSE" , NULL}, |
31 | {"non-blocking" , 'n', 0, G_OPTION_ARG_NONE, &non_blocking, |
32 | "Enable non-blocking i/o" , NULL}, |
33 | #ifdef G_OS_UNIX |
34 | {"unix" , 'U', 0, G_OPTION_ARG_NONE, &unix_socket, |
35 | "Use a unix socket instead of IP" , NULL}, |
36 | #endif |
37 | {"delay" , 'd', 0, G_OPTION_ARG_INT, &delay, |
38 | "Delay responses by the specified number of seconds" , NULL}, |
39 | {"timeout" , 't', 0, G_OPTION_ARG_INT, &read_timeout, |
40 | "Time out reads after the specified number of seconds" , NULL}, |
41 | {"tls" , 'T', 0, G_OPTION_ARG_STRING, &tls_cert_file, |
42 | "Use TLS (SSL) with indicated server certificate" , "CERTFILE" }, |
43 | {NULL} |
44 | }; |
45 | |
46 | #include "socket-common.c" |
47 | |
48 | int |
49 | main (int argc, |
50 | char *argv[]) |
51 | { |
52 | GSocket *socket, *new_socket, *recv_socket; |
53 | GSocketAddress *src_address; |
54 | GSocketAddress *address; |
55 | GSocketType socket_type; |
56 | GSocketFamily socket_family; |
57 | GError *error = NULL; |
58 | GOptionContext *context; |
59 | GCancellable *cancellable; |
60 | char *display_addr; |
61 | GTlsCertificate *tlscert = NULL; |
62 | GIOStream *connection; |
63 | GInputStream *istream; |
64 | GOutputStream *ostream; |
65 | |
66 | context = g_option_context_new (parameter_string: " - Test GSocket server stuff" ); |
67 | g_option_context_add_main_entries (context, entries: cmd_entries, NULL); |
68 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
69 | { |
70 | g_printerr (format: "%s: %s\n" , argv[0], error->message); |
71 | return 1; |
72 | } |
73 | |
74 | if (unix_socket && argc != 2) |
75 | { |
76 | g_printerr (format: "%s: %s\n" , argv[0], "Need to specify unix socket name" ); |
77 | return 1; |
78 | } |
79 | |
80 | if (cancel_timeout) |
81 | { |
82 | GThread *thread; |
83 | cancellable = g_cancellable_new (); |
84 | thread = g_thread_new (name: "cancel" , func: cancel_thread, data: cancellable); |
85 | g_thread_unref (thread); |
86 | } |
87 | else |
88 | { |
89 | cancellable = NULL; |
90 | } |
91 | |
92 | if (tls_cert_file) |
93 | { |
94 | if (use_udp) |
95 | { |
96 | g_printerr (format: "DTLS (TLS over UDP) is not supported" ); |
97 | return 1; |
98 | } |
99 | |
100 | tlscert = g_tls_certificate_new_from_file (file: tls_cert_file, error: &error); |
101 | if (!tlscert) |
102 | { |
103 | g_printerr (format: "Could not read server certificate '%s': %s\n" , |
104 | tls_cert_file, error->message); |
105 | return 1; |
106 | } |
107 | } |
108 | |
109 | loop = g_main_loop_new (NULL, FALSE); |
110 | |
111 | if (use_udp) |
112 | socket_type = G_SOCKET_TYPE_DATAGRAM; |
113 | else |
114 | socket_type = G_SOCKET_TYPE_STREAM; |
115 | |
116 | if (unix_socket) |
117 | socket_family = G_SOCKET_FAMILY_UNIX; |
118 | else |
119 | socket_family = G_SOCKET_FAMILY_IPV4; |
120 | |
121 | socket = g_socket_new (family: socket_family, type: socket_type, protocol: 0, error: &error); |
122 | |
123 | if (socket == NULL) |
124 | { |
125 | g_printerr (format: "%s: %s\n" , argv[0], error->message); |
126 | return 1; |
127 | } |
128 | |
129 | if (non_blocking) |
130 | g_socket_set_blocking (socket, FALSE); |
131 | |
132 | if (unix_socket) |
133 | { |
134 | src_address = socket_address_from_string (name: argv[1]); |
135 | if (src_address == NULL) |
136 | { |
137 | g_printerr (format: "%s: Could not parse '%s' as unix socket name\n" , argv[0], argv[1]); |
138 | return 1; |
139 | } |
140 | } |
141 | else |
142 | { |
143 | src_address = g_inet_socket_address_new (address: g_inet_address_new_any (family: G_SOCKET_FAMILY_IPV4), port); |
144 | } |
145 | |
146 | if (!g_socket_bind (socket, address: src_address, allow_reuse: !dont_reuse_address, error: &error)) |
147 | { |
148 | g_printerr (format: "Can't bind socket: %s\n" , error->message); |
149 | return 1; |
150 | } |
151 | g_object_unref (object: src_address); |
152 | |
153 | if (!use_udp) |
154 | { |
155 | if (!g_socket_listen (socket, error: &error)) |
156 | { |
157 | g_printerr (format: "Can't listen on socket: %s\n" , error->message); |
158 | return 1; |
159 | } |
160 | |
161 | address = g_socket_get_local_address (socket, error: &error); |
162 | if (!address) |
163 | { |
164 | g_printerr (format: "Error getting local address: %s\n" , |
165 | error->message); |
166 | return 1; |
167 | } |
168 | display_addr = socket_address_to_string (address); |
169 | g_print (format: "listening on %s...\n" , display_addr); |
170 | g_free (mem: display_addr); |
171 | |
172 | ensure_socket_condition (socket, condition: G_IO_IN, cancellable); |
173 | new_socket = g_socket_accept (socket, cancellable, error: &error); |
174 | if (!new_socket) |
175 | { |
176 | g_printerr (format: "Error accepting socket: %s\n" , |
177 | error->message); |
178 | return 1; |
179 | } |
180 | |
181 | if (non_blocking) |
182 | g_socket_set_blocking (socket: new_socket, FALSE); |
183 | if (read_timeout) |
184 | g_socket_set_timeout (socket: new_socket, timeout: read_timeout); |
185 | |
186 | address = g_socket_get_remote_address (socket: new_socket, error: &error); |
187 | if (!address) |
188 | { |
189 | g_printerr (format: "Error getting remote address: %s\n" , |
190 | error->message); |
191 | return 1; |
192 | } |
193 | |
194 | display_addr = socket_address_to_string (address); |
195 | g_print (format: "got a new connection from %s\n" , display_addr); |
196 | g_free(mem: display_addr); |
197 | g_object_unref (object: address); |
198 | |
199 | recv_socket = new_socket; |
200 | |
201 | connection = G_IO_STREAM (g_socket_connection_factory_create_connection (recv_socket)); |
202 | g_object_unref (object: new_socket); |
203 | } |
204 | else |
205 | { |
206 | recv_socket = socket; |
207 | connection = NULL; |
208 | } |
209 | |
210 | if (tlscert) |
211 | { |
212 | GIOStream *tls_conn; |
213 | |
214 | tls_conn = g_tls_server_connection_new (base_io_stream: connection, certificate: tlscert, error: &error); |
215 | if (!tls_conn) |
216 | { |
217 | g_printerr (format: "Could not create TLS connection: %s\n" , |
218 | error->message); |
219 | return 1; |
220 | } |
221 | |
222 | if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn), |
223 | cancellable, error: &error)) |
224 | { |
225 | g_printerr (format: "Error during TLS handshake: %s\n" , |
226 | error->message); |
227 | return 1; |
228 | } |
229 | |
230 | g_object_unref (object: connection); |
231 | connection = tls_conn; |
232 | } |
233 | |
234 | if (connection) |
235 | { |
236 | istream = g_io_stream_get_input_stream (stream: connection); |
237 | ostream = g_io_stream_get_output_stream (stream: connection); |
238 | } |
239 | else |
240 | { |
241 | g_assert (use_udp); |
242 | istream = NULL; |
243 | ostream = NULL; |
244 | } |
245 | |
246 | while (TRUE) |
247 | { |
248 | gchar buffer[4096]; |
249 | gssize size; |
250 | gsize to_send; |
251 | |
252 | if (use_udp) |
253 | { |
254 | ensure_socket_condition (socket: recv_socket, condition: G_IO_IN, cancellable); |
255 | size = g_socket_receive_from (socket: recv_socket, address: &address, |
256 | buffer, size: sizeof buffer, |
257 | cancellable, error: &error); |
258 | } |
259 | else |
260 | { |
261 | ensure_connection_condition (stream: connection, condition: G_IO_IN, cancellable); |
262 | size = g_input_stream_read (stream: istream, |
263 | buffer, count: sizeof buffer, |
264 | cancellable, error: &error); |
265 | } |
266 | |
267 | if (size < 0) |
268 | { |
269 | g_printerr (format: "Error receiving from socket: %s\n" , |
270 | error->message); |
271 | return 1; |
272 | } |
273 | |
274 | if (size == 0) |
275 | break; |
276 | |
277 | g_print (format: "received %" G_GSSIZE_FORMAT " bytes of data" , size); |
278 | if (use_udp) |
279 | g_print (format: " from %s" , socket_address_to_string (address)); |
280 | g_print (format: "\n" ); |
281 | |
282 | if (verbose) |
283 | g_print (format: "-------------------------\n" |
284 | "%.*s\n" |
285 | "-------------------------\n" , |
286 | (int)size, buffer); |
287 | |
288 | to_send = size; |
289 | |
290 | if (delay) |
291 | { |
292 | if (verbose) |
293 | g_print (format: "delaying %d seconds before response\n" , delay); |
294 | g_usleep (microseconds: 1000 * 1000 * delay); |
295 | } |
296 | |
297 | while (to_send > 0) |
298 | { |
299 | if (use_udp) |
300 | { |
301 | ensure_socket_condition (socket: recv_socket, condition: G_IO_OUT, cancellable); |
302 | size = g_socket_send_to (socket: recv_socket, address, |
303 | buffer, size: to_send, cancellable, error: &error); |
304 | } |
305 | else |
306 | { |
307 | ensure_connection_condition (stream: connection, condition: G_IO_OUT, cancellable); |
308 | size = g_output_stream_write (stream: ostream, |
309 | buffer, count: to_send, |
310 | cancellable, error: &error); |
311 | } |
312 | |
313 | if (size < 0) |
314 | { |
315 | if (g_error_matches (error, |
316 | G_IO_ERROR, |
317 | code: G_IO_ERROR_WOULD_BLOCK)) |
318 | { |
319 | g_print (format: "socket send would block, handling\n" ); |
320 | g_error_free (error); |
321 | error = NULL; |
322 | continue; |
323 | } |
324 | else |
325 | { |
326 | g_printerr (format: "Error sending to socket: %s\n" , |
327 | error->message); |
328 | return 1; |
329 | } |
330 | } |
331 | |
332 | g_print (format: "sent %" G_GSSIZE_FORMAT " bytes of data\n" , size); |
333 | |
334 | if (size == 0) |
335 | { |
336 | g_printerr (format: "Unexpected short write\n" ); |
337 | return 1; |
338 | } |
339 | |
340 | to_send -= size; |
341 | } |
342 | } |
343 | |
344 | g_print (format: "connection closed\n" ); |
345 | |
346 | if (connection) |
347 | { |
348 | if (!g_io_stream_close (stream: connection, NULL, error: &error)) |
349 | { |
350 | g_printerr (format: "Error closing connection stream: %s\n" , |
351 | error->message); |
352 | return 1; |
353 | } |
354 | g_object_unref (object: connection); |
355 | } |
356 | |
357 | if (!g_socket_close (socket, error: &error)) |
358 | { |
359 | g_printerr (format: "Error closing socket: %s\n" , |
360 | error->message); |
361 | return 1; |
362 | } |
363 | g_object_unref (object: socket); |
364 | |
365 | return 0; |
366 | } |
367 | |