diff options
Diffstat (limited to 'src/util/server.c')
-rw-r--r-- | src/util/server.c | 1091 |
1 files changed, 1091 insertions, 0 deletions
diff --git a/src/util/server.c b/src/util/server.c new file mode 100644 index 000000000..91bc8cc7a --- /dev/null +++ b/src/util/server.c | |||
@@ -0,0 +1,1091 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2009 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/server.c | ||
23 | * @brief library for building GNUnet network servers | ||
24 | * @author Christian Grothoff | ||
25 | * | ||
26 | * TODO: | ||
27 | * - fix inefficient memmove in message processing | ||
28 | */ | ||
29 | |||
30 | #include "platform.h" | ||
31 | #include "gnunet_common.h" | ||
32 | #include "gnunet_network_lib.h" | ||
33 | #include "gnunet_scheduler_lib.h" | ||
34 | #include "gnunet_server_lib.h" | ||
35 | #include "gnunet_time_lib.h" | ||
36 | |||
37 | /** | ||
38 | * List of arrays of message handlers. | ||
39 | */ | ||
40 | struct HandlerList | ||
41 | { | ||
42 | /** | ||
43 | * This is a linked list. | ||
44 | */ | ||
45 | struct HandlerList *next; | ||
46 | |||
47 | /** | ||
48 | * NULL-terminated array of handlers. | ||
49 | */ | ||
50 | const struct GNUNET_SERVER_MessageHandler *handlers; | ||
51 | }; | ||
52 | |||
53 | |||
54 | /** | ||
55 | * List of arrays of message handlers. | ||
56 | */ | ||
57 | struct NotifyList | ||
58 | { | ||
59 | /** | ||
60 | * This is a linked list. | ||
61 | */ | ||
62 | struct NotifyList *next; | ||
63 | |||
64 | /** | ||
65 | * Function to call. | ||
66 | */ | ||
67 | GNUNET_SERVER_DisconnectCallback callback; | ||
68 | |||
69 | /** | ||
70 | * Closure for callback. | ||
71 | */ | ||
72 | void *callback_cls; | ||
73 | }; | ||
74 | |||
75 | |||
76 | /** | ||
77 | * @brief handle for a server | ||
78 | */ | ||
79 | struct GNUNET_SERVER_Handle | ||
80 | { | ||
81 | /** | ||
82 | * My scheduler. | ||
83 | */ | ||
84 | struct GNUNET_SCHEDULER_Handle *sched; | ||
85 | |||
86 | /** | ||
87 | * List of handlers for incoming messages. | ||
88 | */ | ||
89 | struct HandlerList *handlers; | ||
90 | |||
91 | /** | ||
92 | * List of our current clients. | ||
93 | */ | ||
94 | struct GNUNET_SERVER_Client *clients; | ||
95 | |||
96 | /** | ||
97 | * Linked list of functions to call on disconnects by clients. | ||
98 | */ | ||
99 | struct NotifyList *disconnect_notify_list; | ||
100 | |||
101 | /** | ||
102 | * Function to call for access control. | ||
103 | */ | ||
104 | GNUNET_NETWORK_AccessCheck access; | ||
105 | |||
106 | /** | ||
107 | * Closure for access. | ||
108 | */ | ||
109 | void *access_cls; | ||
110 | |||
111 | /** | ||
112 | * After how long should an idle connection time | ||
113 | * out (on write). | ||
114 | */ | ||
115 | struct GNUNET_TIME_Relative idle_timeout; | ||
116 | |||
117 | /** | ||
118 | * maximum write buffer size for accepted sockets | ||
119 | */ | ||
120 | size_t maxbuf; | ||
121 | |||
122 | /** | ||
123 | * Pipe used to signal shutdown of the server. | ||
124 | */ | ||
125 | int shutpipe[2]; | ||
126 | |||
127 | /** | ||
128 | * Socket used to listen for new connections. Set to | ||
129 | * "-1" by GNUNET_SERVER_destroy to initiate shutdown. | ||
130 | */ | ||
131 | int listen_socket; | ||
132 | |||
133 | /** | ||
134 | * Set to GNUNET_YES if we are shutting down. | ||
135 | */ | ||
136 | int do_shutdown; | ||
137 | |||
138 | /** | ||
139 | * Do we ignore messages of types that we do not | ||
140 | * understand or do we require that a handler | ||
141 | * is found (and if not kill the connection)? | ||
142 | */ | ||
143 | int require_found; | ||
144 | |||
145 | }; | ||
146 | |||
147 | |||
148 | /** | ||
149 | * @brief handle for a client of the server | ||
150 | */ | ||
151 | struct GNUNET_SERVER_Client | ||
152 | { | ||
153 | |||
154 | /** | ||
155 | * Size of the buffer for incoming data. Should be | ||
156 | * first so we get nice alignment. | ||
157 | */ | ||
158 | char incoming_buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE]; | ||
159 | |||
160 | /** | ||
161 | * This is a linked list. | ||
162 | */ | ||
163 | struct GNUNET_SERVER_Client *next; | ||
164 | |||
165 | /** | ||
166 | * Server that this client belongs to. | ||
167 | */ | ||
168 | struct GNUNET_SERVER_Handle *server; | ||
169 | |||
170 | /** | ||
171 | * Client closure for callbacks. | ||
172 | */ | ||
173 | void *client_closure; | ||
174 | |||
175 | /** | ||
176 | * Callback to receive from client. | ||
177 | */ | ||
178 | GNUNET_SERVER_ReceiveCallback receive; | ||
179 | |||
180 | /** | ||
181 | * Callback to cancel receive from client. | ||
182 | */ | ||
183 | GNUNET_SERVER_ReceiveCancelCallback receive_cancel; | ||
184 | |||
185 | /** | ||
186 | * Callback to ask about transmit-ready notification. | ||
187 | */ | ||
188 | GNUNET_SERVER_TransmitReadyCallback notify_transmit_ready; | ||
189 | |||
190 | /** | ||
191 | * Callback to ask about transmit-ready notification. | ||
192 | */ | ||
193 | GNUNET_SERVER_TransmitReadyCancelCallback notify_transmit_ready_cancel; | ||
194 | |||
195 | /** | ||
196 | * Callback to check if client is still valid. | ||
197 | */ | ||
198 | GNUNET_SERVER_CheckCallback check; | ||
199 | |||
200 | /** | ||
201 | * Callback to destroy client. | ||
202 | */ | ||
203 | GNUNET_SERVER_DestroyCallback destroy; | ||
204 | |||
205 | /** | ||
206 | * Side-buffer for incoming data used when processing | ||
207 | * is suspended. | ||
208 | */ | ||
209 | char *side_buf; | ||
210 | |||
211 | /** | ||
212 | * Number of bytes in the side buffer. | ||
213 | */ | ||
214 | size_t side_buf_size; | ||
215 | |||
216 | /** | ||
217 | * Last activity on this socket (used to time it out | ||
218 | * if reference_count == 0). | ||
219 | */ | ||
220 | struct GNUNET_TIME_Absolute last_activity; | ||
221 | |||
222 | /** | ||
223 | * Current task identifier for the receive call | ||
224 | * (or GNUNET_SCHEDULER_NO_PREREQUISITE_TASK for none). | ||
225 | */ | ||
226 | GNUNET_SCHEDULER_TaskIdentifier my_receive; | ||
227 | |||
228 | /** | ||
229 | * How many bytes in the "incoming_buffer" are currently | ||
230 | * valid? (starting at offset 0). | ||
231 | */ | ||
232 | size_t receive_pos; | ||
233 | |||
234 | /** | ||
235 | * Number of external entities with a reference to | ||
236 | * this client object. | ||
237 | */ | ||
238 | unsigned int reference_count; | ||
239 | |||
240 | /** | ||
241 | * Was processing if incoming messages suspended while | ||
242 | * we were still processing data already received? | ||
243 | * This is a counter saying how often processing was | ||
244 | * suspended (once per handler invoked). | ||
245 | */ | ||
246 | unsigned int suspended; | ||
247 | |||
248 | /** | ||
249 | * Are we currently in the "process_client_buffer" function (and | ||
250 | * will hence restart the receive job on exit if suspended == 0 once | ||
251 | * we are done?). If this is set, then "receive_done" will | ||
252 | * essentially only decrement suspended; if this is not set, then | ||
253 | * "receive_done" may need to restart the receive process (either | ||
254 | * from the side-buffer or via select/recv). | ||
255 | */ | ||
256 | int in_process_client_buffer; | ||
257 | |||
258 | /** | ||
259 | * We're about to close down this client due to some serious | ||
260 | * error. | ||
261 | */ | ||
262 | int shutdown_now; | ||
263 | |||
264 | }; | ||
265 | |||
266 | |||
267 | /** | ||
268 | * Server has been asked to shutdown, free resources. | ||
269 | */ | ||
270 | static void | ||
271 | destroy_server (struct GNUNET_SERVER_Handle *server) | ||
272 | { | ||
273 | struct GNUNET_SERVER_Client *pos; | ||
274 | struct HandlerList *hpos; | ||
275 | struct NotifyList *npos; | ||
276 | |||
277 | GNUNET_assert (server->listen_socket == -1); | ||
278 | GNUNET_break (0 == CLOSE (server->shutpipe[0])); | ||
279 | GNUNET_break (0 == CLOSE (server->shutpipe[1])); | ||
280 | while (server->clients != NULL) | ||
281 | { | ||
282 | pos = server->clients; | ||
283 | server->clients = pos->next; | ||
284 | pos->server = NULL; | ||
285 | } | ||
286 | while (NULL != (hpos = server->handlers)) | ||
287 | { | ||
288 | server->handlers = hpos->next; | ||
289 | GNUNET_free (hpos); | ||
290 | } | ||
291 | while (NULL != (npos = server->disconnect_notify_list)) | ||
292 | { | ||
293 | server->disconnect_notify_list = npos->next; | ||
294 | GNUNET_free (npos); | ||
295 | } | ||
296 | GNUNET_free (server); | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Scheduler says our listen socket is ready. | ||
302 | * Process it! | ||
303 | */ | ||
304 | static void | ||
305 | process_listen_socket (void *cls, | ||
306 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
307 | { | ||
308 | struct GNUNET_SERVER_Handle *server = cls; | ||
309 | struct GNUNET_NETWORK_SocketHandle *sock; | ||
310 | struct GNUNET_SERVER_Client *client; | ||
311 | fd_set r; | ||
312 | |||
313 | if ((server->do_shutdown) || | ||
314 | ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)) | ||
315 | { | ||
316 | /* shutdown was initiated */ | ||
317 | GNUNET_assert (server->listen_socket != -1); | ||
318 | GNUNET_break (0 == CLOSE (server->listen_socket)); | ||
319 | server->listen_socket = -1; | ||
320 | if (server->do_shutdown) | ||
321 | destroy_server (server); | ||
322 | return; | ||
323 | } | ||
324 | GNUNET_assert (FD_ISSET (server->listen_socket, tc->read_ready)); | ||
325 | GNUNET_assert (!FD_ISSET (server->shutpipe[0], tc->read_ready)); | ||
326 | sock = GNUNET_NETWORK_socket_create_from_accept (tc->sched, | ||
327 | server->access, | ||
328 | server->access_cls, | ||
329 | server->listen_socket, | ||
330 | server->maxbuf); | ||
331 | if (sock != NULL) | ||
332 | { | ||
333 | client = GNUNET_SERVER_connect_socket (server, sock); | ||
334 | /* decrement reference count, we don't keep "client" alive */ | ||
335 | GNUNET_SERVER_client_drop (client); | ||
336 | } | ||
337 | /* listen for more! */ | ||
338 | FD_ZERO (&r); | ||
339 | FD_SET (server->listen_socket, &r); | ||
340 | FD_SET (server->shutpipe[0], &r); | ||
341 | GNUNET_SCHEDULER_add_select (server->sched, | ||
342 | GNUNET_YES, | ||
343 | GNUNET_SCHEDULER_PRIORITY_HIGH, | ||
344 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
345 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
346 | GNUNET_MAX (server->listen_socket, | ||
347 | server->shutpipe[0]) + 1, &r, NULL, | ||
348 | &process_listen_socket, server); | ||
349 | } | ||
350 | |||
351 | |||
352 | /** | ||
353 | * Create and initialize a listen socket for the server. | ||
354 | * | ||
355 | * @return -1 on error, otherwise the listen socket | ||
356 | */ | ||
357 | static int | ||
358 | open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen) | ||
359 | { | ||
360 | const static int on = 1; | ||
361 | int fd; | ||
362 | uint16_t port; | ||
363 | |||
364 | switch (serverAddr->sa_family) | ||
365 | { | ||
366 | case AF_INET: | ||
367 | port = ntohs (((const struct sockaddr_in *) serverAddr)->sin_port); | ||
368 | break; | ||
369 | case AF_INET6: | ||
370 | port = ntohs (((const struct sockaddr_in6 *) serverAddr)->sin6_port); | ||
371 | break; | ||
372 | default: | ||
373 | GNUNET_break (0); | ||
374 | return -1; | ||
375 | } | ||
376 | fd = SOCKET (serverAddr->sa_family, SOCK_STREAM, 0); | ||
377 | if (fd < 0) | ||
378 | { | ||
379 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
380 | return -1; | ||
381 | } | ||
382 | if (0 != fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC)) | ||
383 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
384 | "fcntl"); | ||
385 | if (SETSOCKOPT (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) | ||
386 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
387 | "setsockopt"); | ||
388 | /* bind the socket */ | ||
389 | if (BIND (fd, serverAddr, socklen) < 0) | ||
390 | { | ||
391 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); | ||
392 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
393 | _ | ||
394 | ("`%s' failed for port %d. Is the service already running?\n"), | ||
395 | "bind", port); | ||
396 | GNUNET_break (0 == CLOSE (fd)); | ||
397 | return -1; | ||
398 | } | ||
399 | if (0 != LISTEN (fd, 5)) | ||
400 | { | ||
401 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); | ||
402 | GNUNET_break (0 == CLOSE (fd)); | ||
403 | return -1; | ||
404 | } | ||
405 | return fd; | ||
406 | } | ||
407 | |||
408 | |||
409 | /** | ||
410 | * Create a new server. | ||
411 | * | ||
412 | * @param sched scheduler to use | ||
413 | * @param access function for access control | ||
414 | * @param access_cls closure for access | ||
415 | * @param serverAddr address to listen on (including port), use NULL | ||
416 | * for internal server (no listening) | ||
417 | * @param socklen length of serverAddr | ||
418 | * @param maxbuf maximum write buffer size for accepted sockets | ||
419 | * @param idle_timeout after how long should we timeout idle connections? | ||
420 | * @param require_found if YES, connections sending messages of unknown type | ||
421 | * will be closed | ||
422 | * @return handle for the new server, NULL on error | ||
423 | * (typically, "port" already in use) | ||
424 | */ | ||
425 | struct GNUNET_SERVER_Handle * | ||
426 | GNUNET_SERVER_create (struct GNUNET_SCHEDULER_Handle *sched, | ||
427 | GNUNET_NETWORK_AccessCheck access, | ||
428 | void *access_cls, | ||
429 | const struct sockaddr *serverAddr, | ||
430 | socklen_t socklen, | ||
431 | size_t maxbuf, | ||
432 | struct GNUNET_TIME_Relative | ||
433 | idle_timeout, int require_found) | ||
434 | { | ||
435 | struct GNUNET_SERVER_Handle *ret; | ||
436 | int lsock; | ||
437 | fd_set r; | ||
438 | |||
439 | lsock = -2; | ||
440 | if (serverAddr != NULL) | ||
441 | { | ||
442 | lsock = open_listen_socket (serverAddr, socklen); | ||
443 | if (lsock == -1) | ||
444 | return NULL; | ||
445 | } | ||
446 | ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle)); | ||
447 | if (0 != PIPE (ret->shutpipe)) | ||
448 | { | ||
449 | GNUNET_break (0 == CLOSE (lsock)); | ||
450 | GNUNET_free (ret); | ||
451 | return NULL; | ||
452 | } | ||
453 | ret->sched = sched; | ||
454 | ret->maxbuf = maxbuf; | ||
455 | ret->idle_timeout = idle_timeout; | ||
456 | ret->listen_socket = lsock; | ||
457 | ret->access = access; | ||
458 | ret->access_cls = access_cls; | ||
459 | ret->require_found = require_found; | ||
460 | if (lsock >= 0) | ||
461 | { | ||
462 | FD_ZERO (&r); | ||
463 | FD_SET (ret->listen_socket, &r); | ||
464 | FD_SET (ret->shutpipe[0], &r); | ||
465 | GNUNET_SCHEDULER_add_select (sched, | ||
466 | GNUNET_YES, | ||
467 | GNUNET_SCHEDULER_PRIORITY_HIGH, | ||
468 | GNUNET_SCHEDULER_NO_PREREQUISITE_TASK, | ||
469 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
470 | GNUNET_MAX (ret->listen_socket, | ||
471 | ret->shutpipe[0]) + 1, &r, | ||
472 | NULL, &process_listen_socket, ret); | ||
473 | } | ||
474 | return ret; | ||
475 | } | ||
476 | |||
477 | |||
478 | /** | ||
479 | * Free resources held by this server. | ||
480 | */ | ||
481 | void | ||
482 | GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s) | ||
483 | { | ||
484 | static char c; | ||
485 | |||
486 | GNUNET_assert (s->do_shutdown == GNUNET_NO); | ||
487 | s->do_shutdown = GNUNET_YES; | ||
488 | if (s->listen_socket == -1) | ||
489 | destroy_server (s); | ||
490 | else | ||
491 | GNUNET_break (1 == WRITE (s->shutpipe[1], &c, 1)); | ||
492 | } | ||
493 | |||
494 | |||
495 | /** | ||
496 | * Add additional handlers to an existing server. | ||
497 | * | ||
498 | * @param server the server to add handlers to | ||
499 | * @param handlers array of message handlers for | ||
500 | * incoming messages; the last entry must | ||
501 | * have "NULL" for the "callback"; multiple | ||
502 | * entries for the same type are allowed, | ||
503 | * they will be called in order of occurence. | ||
504 | * These handlers can be removed later; | ||
505 | * the handlers array must exist until removed | ||
506 | * (or server is destroyed). | ||
507 | */ | ||
508 | void | ||
509 | GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, | ||
510 | const struct GNUNET_SERVER_MessageHandler | ||
511 | *handlers) | ||
512 | { | ||
513 | struct HandlerList *p; | ||
514 | |||
515 | p = GNUNET_malloc (sizeof (struct HandlerList)); | ||
516 | p->handlers = handlers; | ||
517 | p->next = server->handlers; | ||
518 | server->handlers = p; | ||
519 | } | ||
520 | |||
521 | |||
522 | /** | ||
523 | * Inject a message into the server, pretend it came | ||
524 | * from the specified client. Delivery of the message | ||
525 | * will happen instantly (if a handler is installed; | ||
526 | * otherwise the call does nothing). | ||
527 | * | ||
528 | * @param server the server receiving the message | ||
529 | * @param sender the "pretended" sender of the message | ||
530 | * can be NULL! | ||
531 | * @param message message to transmit | ||
532 | * @return GNUNET_OK if the message was OK and the | ||
533 | * connection can stay open | ||
534 | * GNUNET_SYSERR if the connection to the | ||
535 | * client should be shut down | ||
536 | */ | ||
537 | int | ||
538 | GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, | ||
539 | struct GNUNET_SERVER_Client *sender, | ||
540 | const struct GNUNET_MessageHeader *message) | ||
541 | { | ||
542 | struct HandlerList *pos; | ||
543 | const struct GNUNET_SERVER_MessageHandler *mh; | ||
544 | unsigned int i; | ||
545 | uint16_t type; | ||
546 | uint16_t size; | ||
547 | int found; | ||
548 | |||
549 | type = ntohs (message->type); | ||
550 | size = ntohs (message->size); | ||
551 | pos = server->handlers; | ||
552 | found = GNUNET_NO; | ||
553 | while (pos != NULL) | ||
554 | { | ||
555 | i = 0; | ||
556 | while (pos->handlers[i].callback != NULL) | ||
557 | { | ||
558 | mh = &pos->handlers[i]; | ||
559 | if (mh->type == type) | ||
560 | { | ||
561 | if ((mh->expected_size != 0) && (mh->expected_size != size)) | ||
562 | { | ||
563 | GNUNET_break_op (0); | ||
564 | return GNUNET_SYSERR; | ||
565 | } | ||
566 | if (sender != NULL) | ||
567 | sender->suspended++; | ||
568 | mh->callback (mh->callback_cls, server, sender, message); | ||
569 | found = GNUNET_YES; | ||
570 | } | ||
571 | i++; | ||
572 | } | ||
573 | pos = pos->next; | ||
574 | } | ||
575 | if (found == GNUNET_NO) | ||
576 | { | ||
577 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, | ||
578 | _("Received message of unknown type %d\n"), type); | ||
579 | if (server->require_found == GNUNET_YES) | ||
580 | return GNUNET_SYSERR; | ||
581 | } | ||
582 | return GNUNET_OK; | ||
583 | } | ||
584 | |||
585 | |||
586 | /** | ||
587 | * We're finished with this client and especially its input | ||
588 | * processing. If the RC is zero, free all resources otherwise wait | ||
589 | * until RC hits zero to do so. | ||
590 | */ | ||
591 | static void | ||
592 | shutdown_incoming_processing (struct GNUNET_SERVER_Client *client) | ||
593 | { | ||
594 | struct GNUNET_SERVER_Client *prev; | ||
595 | struct GNUNET_SERVER_Client *pos; | ||
596 | struct GNUNET_SERVER_Handle *server; | ||
597 | struct NotifyList *n; | ||
598 | unsigned int rc; | ||
599 | |||
600 | GNUNET_assert (client->my_receive == GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
601 | rc = client->reference_count; | ||
602 | if (client->server != NULL) | ||
603 | { | ||
604 | server = client->server; | ||
605 | client->server = NULL; | ||
606 | prev = NULL; | ||
607 | pos = server->clients; | ||
608 | while ((pos != NULL) && (pos != client)) | ||
609 | { | ||
610 | prev = pos; | ||
611 | pos = pos->next; | ||
612 | } | ||
613 | GNUNET_assert (pos != NULL); | ||
614 | if (prev == NULL) | ||
615 | server->clients = pos->next; | ||
616 | else | ||
617 | prev->next = pos->next; | ||
618 | n = server->disconnect_notify_list; | ||
619 | while (n != NULL) | ||
620 | { | ||
621 | n->callback (n->callback_cls, client); | ||
622 | n = n->next; | ||
623 | } | ||
624 | } | ||
625 | /* wait for RC to hit zero, then free */ | ||
626 | if (rc > 0) | ||
627 | return; | ||
628 | client->destroy (client->client_closure); | ||
629 | GNUNET_free (client); | ||
630 | } | ||
631 | |||
632 | |||
633 | static void | ||
634 | process_client_buffer (struct GNUNET_SERVER_Client *client) | ||
635 | { | ||
636 | struct GNUNET_SERVER_Handle *server; | ||
637 | const struct GNUNET_MessageHeader *hdr; | ||
638 | size_t msize; | ||
639 | |||
640 | client->in_process_client_buffer = GNUNET_YES; | ||
641 | server = client->server; | ||
642 | while ((client->receive_pos >= sizeof (struct GNUNET_MessageHeader)) && | ||
643 | (0 == client->suspended) && (GNUNET_YES != client->shutdown_now)) | ||
644 | { | ||
645 | hdr = (const struct GNUNET_MessageHeader *) &client->incoming_buffer; | ||
646 | msize = ntohs (hdr->size); | ||
647 | if (msize > client->receive_pos) | ||
648 | break; | ||
649 | if ((msize < sizeof (struct GNUNET_MessageHeader)) || | ||
650 | (GNUNET_OK != GNUNET_SERVER_inject (server, client, hdr))) | ||
651 | { | ||
652 | client->in_process_client_buffer = GNUNET_NO; | ||
653 | shutdown_incoming_processing (client); | ||
654 | return; | ||
655 | } | ||
656 | /* FIXME: this is highly inefficient; we should | ||
657 | try to avoid this if the new base address is | ||
658 | already nicely aligned. See old handler code... */ | ||
659 | memmove (client->incoming_buffer, | ||
660 | &client->incoming_buffer[msize], client->receive_pos - msize); | ||
661 | client->receive_pos -= msize; | ||
662 | } | ||
663 | client->in_process_client_buffer = GNUNET_NO; | ||
664 | if (GNUNET_YES == client->shutdown_now) | ||
665 | shutdown_incoming_processing (client); | ||
666 | } | ||
667 | |||
668 | |||
669 | /** | ||
670 | * We are receiving an incoming message. Process it. | ||
671 | */ | ||
672 | static void | ||
673 | process_incoming (void *cls, | ||
674 | const void *buf, | ||
675 | size_t available, | ||
676 | const struct sockaddr *addr, socklen_t addrlen, int errCode) | ||
677 | { | ||
678 | struct GNUNET_SERVER_Client *client = cls; | ||
679 | struct GNUNET_SERVER_Handle *server = client->server; | ||
680 | const char *cbuf = buf; | ||
681 | size_t maxcpy; | ||
682 | |||
683 | client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
684 | if ((buf == NULL) || | ||
685 | (available == 0) || | ||
686 | (errCode != 0) || | ||
687 | (server == NULL) || | ||
688 | (client->shutdown_now == GNUNET_YES) || | ||
689 | (GNUNET_YES != client->check (client->client_closure))) | ||
690 | { | ||
691 | /* other side closed connection, error connecting, etc. */ | ||
692 | shutdown_incoming_processing (client); | ||
693 | return; | ||
694 | } | ||
695 | GNUNET_SERVER_client_keep (client); | ||
696 | client->last_activity = GNUNET_TIME_absolute_get (); | ||
697 | /* process data (if available) */ | ||
698 | while (available > 0) | ||
699 | { | ||
700 | maxcpy = available; | ||
701 | if (maxcpy > sizeof (client->incoming_buffer) - client->receive_pos) | ||
702 | maxcpy = sizeof (client->incoming_buffer) - client->receive_pos; | ||
703 | memcpy (&client->incoming_buffer[client->receive_pos], cbuf, maxcpy); | ||
704 | client->receive_pos += maxcpy; | ||
705 | cbuf += maxcpy; | ||
706 | available -= maxcpy; | ||
707 | if (0 < client->suspended) | ||
708 | { | ||
709 | if (available > 0) | ||
710 | { | ||
711 | client->side_buf_size = available; | ||
712 | client->side_buf = GNUNET_malloc (available); | ||
713 | memcpy (client->side_buf, cbuf, available); | ||
714 | available = 0; | ||
715 | } | ||
716 | break; /* do not run next client iteration! */ | ||
717 | } | ||
718 | process_client_buffer (client); | ||
719 | } | ||
720 | GNUNET_assert (available == 0); | ||
721 | if ((client->suspended == 0) && | ||
722 | (GNUNET_YES != client->shutdown_now) && (client->server != NULL)) | ||
723 | { | ||
724 | /* Finally, keep receiving! */ | ||
725 | client->my_receive = client->receive (client->client_closure, | ||
726 | GNUNET_SERVER_MAX_MESSAGE_SIZE, | ||
727 | server->idle_timeout, | ||
728 | &process_incoming, client); | ||
729 | } | ||
730 | if (GNUNET_YES == client->shutdown_now) | ||
731 | shutdown_incoming_processing (client); | ||
732 | GNUNET_SERVER_client_drop (client); | ||
733 | } | ||
734 | |||
735 | |||
736 | static void | ||
737 | restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
738 | { | ||
739 | struct GNUNET_SERVER_Client *client = cls; | ||
740 | |||
741 | process_client_buffer (client); | ||
742 | if (0 == client->suspended) | ||
743 | client->my_receive = client->receive (client->client_closure, | ||
744 | GNUNET_SERVER_MAX_MESSAGE_SIZE, | ||
745 | client->server->idle_timeout, | ||
746 | &process_incoming, client); | ||
747 | } | ||
748 | |||
749 | |||
750 | /** | ||
751 | * Add a client to the set of our clients and | ||
752 | * start receiving. | ||
753 | */ | ||
754 | static void | ||
755 | add_client (struct GNUNET_SERVER_Handle *server, | ||
756 | struct GNUNET_SERVER_Client *client) | ||
757 | { | ||
758 | client->server = server; | ||
759 | client->last_activity = GNUNET_TIME_absolute_get (); | ||
760 | client->next = server->clients; | ||
761 | server->clients = client; | ||
762 | client->my_receive = client->receive (client->client_closure, | ||
763 | GNUNET_SERVER_MAX_MESSAGE_SIZE, | ||
764 | server->idle_timeout, | ||
765 | &process_incoming, client); | ||
766 | } | ||
767 | |||
768 | static GNUNET_SCHEDULER_TaskIdentifier | ||
769 | sock_receive (void *cls, | ||
770 | size_t max, | ||
771 | struct GNUNET_TIME_Relative timeout, | ||
772 | GNUNET_NETWORK_Receiver receiver, void *receiver_cls) | ||
773 | { | ||
774 | return GNUNET_NETWORK_receive (cls, max, timeout, receiver, receiver_cls); | ||
775 | } | ||
776 | |||
777 | static void | ||
778 | sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti) | ||
779 | { | ||
780 | GNUNET_NETWORK_receive_cancel (cls, ti); | ||
781 | } | ||
782 | |||
783 | |||
784 | static void * | ||
785 | sock_notify_transmit_ready (void *cls, | ||
786 | size_t size, | ||
787 | struct GNUNET_TIME_Relative timeout, | ||
788 | GNUNET_NETWORK_TransmitReadyNotify notify, | ||
789 | void *notify_cls) | ||
790 | { | ||
791 | return GNUNET_NETWORK_notify_transmit_ready (cls, size, timeout, notify, | ||
792 | notify_cls); | ||
793 | } | ||
794 | |||
795 | |||
796 | static void | ||
797 | sock_notify_transmit_ready_cancel (void *cls, void *h) | ||
798 | { | ||
799 | GNUNET_NETWORK_notify_transmit_ready_cancel (h); | ||
800 | } | ||
801 | |||
802 | |||
803 | /** | ||
804 | * Check if socket is still valid (no fatal errors have happened so far). | ||
805 | * | ||
806 | * @param cls the socket | ||
807 | * @return GNUNET_YES if valid, GNUNET_NO otherwise | ||
808 | */ | ||
809 | static int | ||
810 | sock_check (void *cls) | ||
811 | { | ||
812 | return GNUNET_NETWORK_socket_check (cls); | ||
813 | } | ||
814 | |||
815 | |||
816 | /** | ||
817 | * Destroy this socket (free resources). | ||
818 | * | ||
819 | * @param cls the socket | ||
820 | */ | ||
821 | static void | ||
822 | sock_destroy (void *cls) | ||
823 | { | ||
824 | GNUNET_NETWORK_socket_destroy (cls); | ||
825 | } | ||
826 | |||
827 | |||
828 | /** | ||
829 | * Add a TCP socket-based connection to the set of handles managed by | ||
830 | * this server. Use this function for outgoing (P2P) connections that | ||
831 | * we initiated (and where this server should process incoming | ||
832 | * messages). | ||
833 | * | ||
834 | * @param server the server to use | ||
835 | * @param connection the connection to manage (client must | ||
836 | * stop using this connection from now on) | ||
837 | * @return the client handle (client should call | ||
838 | * "client_drop" on the return value eventually) | ||
839 | */ | ||
840 | struct GNUNET_SERVER_Client * | ||
841 | GNUNET_SERVER_connect_socket (struct | ||
842 | GNUNET_SERVER_Handle | ||
843 | *server, | ||
844 | struct GNUNET_NETWORK_SocketHandle *connection) | ||
845 | { | ||
846 | struct GNUNET_SERVER_Client *client; | ||
847 | |||
848 | client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); | ||
849 | client->client_closure = connection; | ||
850 | client->receive = &sock_receive; | ||
851 | client->receive_cancel = &sock_receive_cancel; | ||
852 | client->notify_transmit_ready = &sock_notify_transmit_ready; | ||
853 | client->notify_transmit_ready_cancel = &sock_notify_transmit_ready_cancel; | ||
854 | client->check = &sock_check; | ||
855 | client->destroy = &sock_destroy; | ||
856 | client->reference_count = 1; | ||
857 | add_client (server, client); | ||
858 | return client; | ||
859 | } | ||
860 | |||
861 | |||
862 | /** | ||
863 | * Add an arbitrary connection to the set of handles managed by this | ||
864 | * server. This can be used if a sending and receiving does not | ||
865 | * really go over the network (internal transmission) or for servers | ||
866 | * using UDP. | ||
867 | * | ||
868 | * @param server the server to use | ||
869 | * @param chandle opaque handle for the connection | ||
870 | * @param creceive receive function for the connection | ||
871 | * @param ccancel cancel receive function for the connection | ||
872 | * @param cnotify transmit notification function for the connection | ||
873 | * @param cnotify_cancel transmit notification cancellation function for the connection | ||
874 | * @param ccheck function to test if the connection is still up | ||
875 | * @param cdestroy function to close and free the connection | ||
876 | * @return the client handle (client should call | ||
877 | * "client_drop" on the return value eventually) | ||
878 | */ | ||
879 | struct GNUNET_SERVER_Client * | ||
880 | GNUNET_SERVER_connect_callback (struct | ||
881 | GNUNET_SERVER_Handle | ||
882 | *server, | ||
883 | void *chandle, | ||
884 | GNUNET_SERVER_ReceiveCallback | ||
885 | creceive, | ||
886 | GNUNET_SERVER_ReceiveCancelCallback | ||
887 | ccancel, | ||
888 | GNUNET_SERVER_TransmitReadyCallback | ||
889 | cnotify, | ||
890 | GNUNET_SERVER_TransmitReadyCancelCallback | ||
891 | cnotify_cancel, | ||
892 | GNUNET_SERVER_CheckCallback | ||
893 | ccheck, | ||
894 | GNUNET_SERVER_DestroyCallback cdestroy) | ||
895 | { | ||
896 | struct GNUNET_SERVER_Client *client; | ||
897 | |||
898 | client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client)); | ||
899 | client->client_closure = chandle; | ||
900 | client->receive = creceive; | ||
901 | client->receive_cancel = ccancel; | ||
902 | client->notify_transmit_ready = cnotify; | ||
903 | client->notify_transmit_ready_cancel = cnotify_cancel; | ||
904 | client->check = ccheck; | ||
905 | client->destroy = cdestroy; | ||
906 | client->reference_count = 1; | ||
907 | add_client (server, client); | ||
908 | return client; | ||
909 | } | ||
910 | |||
911 | |||
912 | /** | ||
913 | * Notify the server that the given client handle should | ||
914 | * be kept (keeps the connection up if possible, increments | ||
915 | * the internal reference counter). | ||
916 | * | ||
917 | * @param client the client to keep | ||
918 | */ | ||
919 | void | ||
920 | GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) | ||
921 | { | ||
922 | client->reference_count++; | ||
923 | } | ||
924 | |||
925 | |||
926 | /** | ||
927 | * Notify the server that the given client handle is no | ||
928 | * longer required. Decrements the reference counter. If | ||
929 | * that counter reaches zero an inactive connection maybe | ||
930 | * closed. | ||
931 | * | ||
932 | * @param client the client to drop | ||
933 | */ | ||
934 | void | ||
935 | GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) | ||
936 | { | ||
937 | GNUNET_assert (client->reference_count > 0); | ||
938 | client->reference_count--; | ||
939 | if ((client->server == NULL) && (client->reference_count == 0)) | ||
940 | shutdown_incoming_processing (client); | ||
941 | } | ||
942 | |||
943 | |||
944 | /** | ||
945 | * Obtain the network address of the other party. | ||
946 | * | ||
947 | * @param client the client to get the address for | ||
948 | * @param addr where to store the address | ||
949 | * @param addrlen where to store the length of the address | ||
950 | * @return GNUNET_OK on success | ||
951 | */ | ||
952 | int | ||
953 | GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, | ||
954 | void **addr, size_t * addrlen) | ||
955 | { | ||
956 | if (client->receive != &sock_receive) | ||
957 | return GNUNET_SYSERR; /* not a network client */ | ||
958 | return GNUNET_NETWORK_socket_get_address (client->client_closure, | ||
959 | addr, addrlen); | ||
960 | } | ||
961 | |||
962 | |||
963 | /** | ||
964 | * Ask the server to notify us whenever a client disconnects. | ||
965 | * This function is called whenever the actual network connection | ||
966 | * is closed; the reference count may be zero or larger than zero | ||
967 | * at this point. | ||
968 | * | ||
969 | * @param server the server manageing the clients | ||
970 | * @param callback function to call on disconnect | ||
971 | * @param callback_cls closure for callback | ||
972 | */ | ||
973 | void | ||
974 | GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, | ||
975 | GNUNET_SERVER_DisconnectCallback callback, | ||
976 | void *callback_cls) | ||
977 | { | ||
978 | struct NotifyList *n; | ||
979 | |||
980 | n = GNUNET_malloc (sizeof (struct NotifyList)); | ||
981 | n->callback = callback; | ||
982 | n->callback_cls = callback_cls; | ||
983 | n->next = server->disconnect_notify_list; | ||
984 | server->disconnect_notify_list = n; | ||
985 | } | ||
986 | |||
987 | |||
988 | /** | ||
989 | * Ask the server to disconnect from the given client. | ||
990 | * This is the same as returning GNUNET_SYSERR from a message | ||
991 | * handler, except that it allows dropping of a client even | ||
992 | * when not handling a message from that client. | ||
993 | * | ||
994 | * @param client the client to disconnect from | ||
995 | */ | ||
996 | void | ||
997 | GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) | ||
998 | { | ||
999 | if (client->server == NULL) | ||
1000 | return; /* already disconnected */ | ||
1001 | GNUNET_assert (client->my_receive != GNUNET_SCHEDULER_NO_PREREQUISITE_TASK); | ||
1002 | client->receive_cancel (client->client_closure, client->my_receive); | ||
1003 | client->my_receive = GNUNET_SCHEDULER_NO_PREREQUISITE_TASK; | ||
1004 | shutdown_incoming_processing (client); | ||
1005 | } | ||
1006 | |||
1007 | |||
1008 | /** | ||
1009 | * Notify us when the server has enough space to transmit | ||
1010 | * a message of the given size to the given client. | ||
1011 | * | ||
1012 | * @param server the server to use | ||
1013 | * @param client client to transmit message to | ||
1014 | * @param size requested amount of buffer space | ||
1015 | * @param timeout after how long should we give up (and call | ||
1016 | * notify with buf NULL and size 0)? | ||
1017 | * @param callback function to call when space is available | ||
1018 | * @param callback_cls closure for callback | ||
1019 | * @return non-NULL if the notify callback was queued; can be used | ||
1020 | * to cancel the request using | ||
1021 | * GNUNET_NETWORK_notify_transmit_ready_cancel. | ||
1022 | * NULL if we are already going to notify someone else (busy) | ||
1023 | */ | ||
1024 | struct GNUNET_NETWORK_TransmitHandle * | ||
1025 | GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, | ||
1026 | size_t size, | ||
1027 | struct GNUNET_TIME_Relative timeout, | ||
1028 | GNUNET_NETWORK_TransmitReadyNotify | ||
1029 | callback, void *callback_cls) | ||
1030 | { | ||
1031 | return client->notify_transmit_ready (client->client_closure, | ||
1032 | size, | ||
1033 | timeout, callback, callback_cls); | ||
1034 | } | ||
1035 | |||
1036 | |||
1037 | /** | ||
1038 | * Resume receiving from this client, we are done processing the | ||
1039 | * current request. This function must be called from within each | ||
1040 | * GNUNET_SERVER_MessageCallback (or its respective continuations). | ||
1041 | * | ||
1042 | * @param client client we were processing a message of | ||
1043 | * @param success GNUNET_OK to keep the connection open and | ||
1044 | * continue to receive | ||
1045 | * GNUNET_SYSERR to close the connection (signal | ||
1046 | * serious error) | ||
1047 | */ | ||
1048 | void | ||
1049 | GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success) | ||
1050 | { | ||
1051 | char *sb; | ||
1052 | |||
1053 | if (client == NULL) | ||
1054 | return; | ||
1055 | GNUNET_assert (client->suspended > 0); | ||
1056 | client->suspended--; | ||
1057 | if (success != GNUNET_OK) | ||
1058 | client->shutdown_now = GNUNET_YES; | ||
1059 | if (client->suspended > 0) | ||
1060 | return; | ||
1061 | if (client->in_process_client_buffer == GNUNET_YES) | ||
1062 | return; | ||
1063 | if (client->side_buf_size > 0) | ||
1064 | { | ||
1065 | /* resume processing from side-buf */ | ||
1066 | sb = client->side_buf; | ||
1067 | client->side_buf = NULL; | ||
1068 | /* this will also resume the receive job */ | ||
1069 | if (GNUNET_YES != client->shutdown_now) | ||
1070 | process_incoming (client, sb, client->side_buf_size, NULL, 0, 0); | ||
1071 | else | ||
1072 | shutdown_incoming_processing (client); | ||
1073 | /* finally, free the side-buf */ | ||
1074 | GNUNET_free (sb); | ||
1075 | return; | ||
1076 | } | ||
1077 | /* resume receive job */ | ||
1078 | if (GNUNET_YES != client->shutdown_now) | ||
1079 | { | ||
1080 | GNUNET_SCHEDULER_add_continuation (client->server->sched, | ||
1081 | GNUNET_NO, | ||
1082 | &restart_processing, | ||
1083 | client, | ||
1084 | GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||
1085 | return; | ||
1086 | } | ||
1087 | shutdown_incoming_processing (client); | ||
1088 | } | ||
1089 | |||
1090 | |||
1091 | /* end of server.c */ | ||