aboutsummaryrefslogtreecommitdiff
path: root/src/util/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/server.c')
-rw-r--r--src/util/server.c1091
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 */
40struct 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 */
57struct 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 */
79struct 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 */
151struct 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 */
270static void
271destroy_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 */
304static void
305process_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 */
357static int
358open_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 */
425struct GNUNET_SERVER_Handle *
426GNUNET_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 */
481void
482GNUNET_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 */
508void
509GNUNET_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 */
537int
538GNUNET_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 */
591static void
592shutdown_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
633static void
634process_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 */
672static void
673process_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
736static void
737restart_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 */
754static void
755add_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
768static GNUNET_SCHEDULER_TaskIdentifier
769sock_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
777static void
778sock_receive_cancel (void *cls, GNUNET_SCHEDULER_TaskIdentifier ti)
779{
780 GNUNET_NETWORK_receive_cancel (cls, ti);
781}
782
783
784static void *
785sock_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
796static void
797sock_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 */
809static int
810sock_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 */
821static void
822sock_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 */
840struct GNUNET_SERVER_Client *
841GNUNET_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 */
879struct GNUNET_SERVER_Client *
880GNUNET_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 */
919void
920GNUNET_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 */
934void
935GNUNET_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 */
952int
953GNUNET_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 */
973void
974GNUNET_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 */
996void
997GNUNET_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 */
1024struct GNUNET_NETWORK_TransmitHandle *
1025GNUNET_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 */
1048void
1049GNUNET_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 */