2 This file is part of GNUnet.
3 (C) 2010,2011 Christian Grothoff (and other contributing authors)
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 3, or (at your
8 option) any later version.
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.
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.
22 * @file transport/gnunet-service-transport_clients.c
23 * @brief plugin management API
24 * @author Christian Grothoff
27 #include "gnunet-service-transport_clients.h"
28 #include "gnunet-service-transport_hello.h"
29 #include "gnunet-service-transport_neighbours.h"
30 #include "gnunet-service-transport_plugins.h"
31 #include "gnunet-service-transport.h"
32 #include "transport.h"
35 * How many messages can we have pending for a given client process
36 * before we start to drop incoming messages? We typically should
37 * have only one client and so this would be the primary buffer for
38 * messages, so the number should be chosen rather generously.
40 * The expectation here is that most of the time the queue is large
41 * enough so that a drop is virtually never required. Note that
42 * this value must be about as large as 'TOTAL_MSGS' in the
43 * 'test_transport_api_reliability.c', otherwise that testcase may
46 #define MAX_PENDING (128 * 1024)
50 * Linked list of messages to be transmitted to the client. Each
51 * entry is followed by the actual message.
53 struct ClientMessageQueueEntry
56 * This is a doubly-linked list.
58 struct ClientMessageQueueEntry *next;
61 * This is a doubly-linked list.
63 struct ClientMessageQueueEntry *prev;
68 * Client connected to the transport service.
70 struct TransportClient
74 * This is a doubly-linked list.
76 struct TransportClient *next;
79 * This is a doubly-linked list.
81 struct TransportClient *prev;
84 * Handle to the client.
86 struct GNUNET_SERVER_Client *client;
89 * Linked list of messages yet to be transmitted to
92 struct ClientMessageQueueEntry *message_queue_head;
95 * Tail of linked list of messages yet to be transmitted to the
98 struct ClientMessageQueueEntry *message_queue_tail;
101 * Current transmit request handle.
103 struct GNUNET_CONNECTION_TransmitHandle *th;
106 * Length of the list of messages pending for this client.
108 unsigned int message_count;
114 * Head of linked list of all clients to this service.
116 static struct TransportClient *clients_head;
119 * Tail of linked list of all clients to this service.
121 static struct TransportClient *clients_tail;
125 * Find the internal handle associated with the given client handle
127 * @param client server's client handle to look up
128 * @return internal client handle
130 static struct TransportClient *
131 lookup_client (struct GNUNET_SERVER_Client *client)
133 struct TransportClient *tc;
138 if (tc->client == client)
147 * Create the internal handle for the given server client handle
149 * @param client server's client handle to create our internal handle for
150 * @return fresh internal client handle
152 static struct TransportClient *
153 setup_client (struct GNUNET_SERVER_Client *client)
155 struct TransportClient *tc;
157 tc = GNUNET_malloc (sizeof (struct TransportClient));
159 GNUNET_CONTAINER_DLL_insert (clients_head,
167 * Function called to notify a client about the socket being ready to
168 * queue more data. "buf" will be NULL and "size" zero if the socket
169 * was closed for writing in the meantime.
172 * @param size number of bytes available in buf
173 * @param buf where the callee should write the message
174 * @return number of bytes written to buf
177 transmit_to_client_callback (void *cls,
181 struct TransportClient *tc = cls;
182 struct ClientMessageQueueEntry *q;
183 const struct GNUNET_MessageHeader *msg;
192 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
193 "Transmission to client failed, closing connection.\n");
199 while (NULL != (q = tc->message_queue_head))
201 msg = (const struct GNUNET_MessageHeader *) &q[1];
202 msize = ntohs (msg->size);
203 if (msize + tsize > size)
206 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
207 "Transmitting message of type %u to client.\n",
210 GNUNET_CONTAINER_DLL_remove (tc->message_queue_head,
211 tc->message_queue_tail,
214 memcpy (&cbuf[tsize],
222 GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
223 tc->th = GNUNET_SERVER_notify_transmit_ready (tc->client,
225 GNUNET_TIME_UNIT_FOREVER_REL,
226 &transmit_to_client_callback,
228 GNUNET_assert (tc->th != NULL);
235 * Queue the given message for transmission to the given client
237 * @param client target of the message
238 * @param msg message to transmit
239 * @param may_drop GNUNET_YES if the message can be dropped
242 unicast (struct TransportClient *tc,
243 const struct GNUNET_MessageHeader *msg,
246 struct ClientMessageQueueEntry *q;
249 if ( (tc->message_count >= MAX_PENDING) &&
250 (GNUNET_YES == may_drop) )
252 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
253 _("Dropping message of type %u and size %u, have %u/%u messages pending\n"),
258 GNUNET_STATISTICS_update (GST_stats,
259 gettext_noop ("# messages dropped due to slow client"),
264 msize = ntohs (msg->size);
265 GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
266 q = GNUNET_malloc (sizeof (struct ClientMessageQueueEntry) + msize);
267 memcpy (&q[1], msg, msize);
268 GNUNET_CONTAINER_DLL_insert_tail (tc->message_queue_head,
269 tc->message_queue_tail,
274 tc->th = GNUNET_SERVER_notify_transmit_ready (tc->client,
276 GNUNET_TIME_UNIT_FOREVER_REL,
277 &transmit_to_client_callback,
279 GNUNET_assert (tc->th != NULL);
284 * Called whenever a client is disconnected. Frees our
285 * resources associated with that client.
288 * @param client identification of the client
291 client_disconnect_notification (void *cls,
292 struct GNUNET_SERVER_Client *client)
294 struct TransportClient *tc;
295 struct ClientMessageQueueEntry *mqe;
299 tc = lookup_client (client);
303 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
304 "Client disconnected, cleaning up.\n");
306 while (NULL != (mqe = tc->message_queue_head))
308 GNUNET_CONTAINER_DLL_remove (tc->message_queue_head,
309 tc->message_queue_tail,
314 GNUNET_CONTAINER_DLL_remove (clients_head,
319 GNUNET_CONNECTION_notify_transmit_ready_cancel (tc->th);
322 GNUNET_break (0 == tc->message_count);
328 * Start handling requests from clients.
330 * @param server server used to accept clients from.
333 GST_clients_start (struct GNUNET_SERVER_Handle *server)
335 GNUNET_SERVER_disconnect_notify (server,
336 &client_disconnect_notification, NULL);
341 * Stop processing clients.
351 * Function called for each of our connected neighbours. Notify the
352 * client about the existing neighbour.
354 * @param cls the 'struct TransportClient' to notify
355 * @param peer identity of the neighbour
356 * @param ats performance data
357 * @param ats_count number of entries in ats (excluding 0-termination)
360 notify_client_about_neighbour (void *cls,
361 const struct GNUNET_PeerIdentity *peer,
362 const struct GNUNET_TRANSPORT_ATS_Information *ats,
365 struct TransportClient *tc = cls;
366 struct ConnectInfoMessage *cim;
369 size = sizeof (struct ConnectInfoMessage) + ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information);
370 GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
371 cim = GNUNET_malloc (size);
372 cim->header.size = htons (size);
373 cim->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT);
374 cim->ats_count = htonl(ats_count);
378 ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
379 unicast (tc, &cim->header, GNUNET_NO);
385 * Initialize a normal client. We got a start message from this
386 * client, add him to the list of clients for broadcasting of inbound
390 * @param client the client
391 * @param message the start message that was sent
394 GST_clients_handle_start (void *cls,
395 struct GNUNET_SERVER_Client *client,
396 const struct GNUNET_MessageHeader *message)
398 const struct StartMessage *start;
399 struct TransportClient *tc;
401 tc = lookup_client (client);
404 /* got 'start' twice from the same client, not allowed */
406 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
409 start = (const struct StartMessage*) message;
410 if ( (GNUNET_NO != ntohl (start->do_check)) &&
411 (0 != memcmp (&start->self,
413 sizeof (struct GNUNET_PeerIdentity))) )
415 /* client thinks this is a different peer, reject */
416 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
417 _("Rejecting control connection from peer `%s', which is not me!\n"),
418 GNUNET_i2s (&start->self));
419 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
422 tc = setup_client (client);
423 unicast (tc, GST_hello_get(), GNUNET_NO);
424 GST_neighbours_iterate (¬ify_client_about_neighbour, tc);
425 GNUNET_SERVER_receive_done (client, GNUNET_OK);
430 * Client asked for transmission to a peer. Process the request.
433 * @param client the client
434 * @param message the send message that was sent
437 GST_clients_handle_send (void *cls,
438 struct GNUNET_SERVER_Client *client,
439 const struct GNUNET_MessageHeader *message)
445 * Client asked for a quota change for a particular peer. Process the request.
448 * @param client the client
449 * @param message the quota changing message
452 GST_clients_handle_set_quota (void *cls,
453 struct GNUNET_SERVER_Client *client,
454 const struct GNUNET_MessageHeader *message)
456 const struct QuotaSetMessage *qsm;
458 qsm = (const struct QuotaSetMessage *) message;
459 GNUNET_STATISTICS_update (GST_stats,
460 gettext_noop ("# SET QUOTA messages received"),
464 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465 "Received `%s' request (new quota %u) from client for peer `%4s'\n",
467 (unsigned int) ntohl (qsm->quota.value__),
468 GNUNET_i2s (&qsm->peer));
470 GST_neighbours_set_incoming_quota (&qsm->peer,
472 GNUNET_SERVER_receive_done (client, GNUNET_OK);
477 * Take the given address and append it to the set of results sent back to
480 * @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
481 * @param address the resolved name, NULL to indicate the last response
484 transmit_address_to_client (void *cls,
487 struct GNUNET_SERVER_TransmitContext *tc = cls;
491 GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
492 GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
493 GNUNET_SERVER_transmit_context_run (tc,
494 GNUNET_TIME_UNIT_FOREVER_REL);
497 GNUNET_SERVER_transmit_context_append_data (tc,
498 address, strlen (address) + 1,
499 GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
504 * Client asked to resolve an address. Process the request.
507 * @param client the client
508 * @param message the resolution request
511 GST_clients_handle_address_lookup (void *cls,
512 struct GNUNET_SERVER_Client *client,
513 const struct GNUNET_MessageHeader *message)
515 const struct AddressLookupMessage *alum;
516 struct GNUNET_TRANSPORT_PluginFunctions *papi;
517 const char *plugin_name;
519 uint32_t address_len;
521 struct GNUNET_SERVER_TransmitContext *tc;
522 struct GNUNET_TIME_Relative rtimeout;
525 size = ntohs (message->size);
526 if (size < sizeof (struct AddressLookupMessage))
529 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
532 alum = (const struct AddressLookupMessage *) message;
533 address_len = ntohl (alum->addrlen);
534 if (size <= sizeof (struct AddressLookupMessage) + address_len)
537 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
540 address = (const char *) &alum[1];
541 plugin_name = (const char *) &address[address_len];
543 [size - sizeof (struct AddressLookupMessage) - address_len - 1] != '\0')
546 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
549 rtimeout = GNUNET_TIME_relative_ntoh (alum->timeout);
550 numeric = ntohl (alum->numeric_only);
551 tc = GNUNET_SERVER_transmit_context_create (client);
552 papi = GST_plugins_find (plugin_name);
555 GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
556 GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
557 GNUNET_SERVER_transmit_context_run (tc, rtimeout);
560 GNUNET_SERVER_disable_receive_done_warning (client);
561 papi->address_pretty_printer (papi->cls,
563 address, address_len,
566 &transmit_address_to_client, tc);
571 * Client asked to obtain information about a peer's addresses.
572 * Process the request.
575 * @param client the client
576 * @param message the peer address information request
579 GST_clients_handle_peer_address_lookup (void *cls,
580 struct GNUNET_SERVER_Client *client,
581 const struct GNUNET_MessageHeader *message)
587 * Client asked to obtain information about all addresses.
588 * Process the request.
591 * @param client the client
592 * @param message the peer address information request
595 GST_clients_handle_address_iterate (void *cls,
596 struct GNUNET_SERVER_Client *client,
597 const struct GNUNET_MessageHeader *message)
603 * Broadcast the given message to all of our clients.
605 * @param msg message to broadcast
606 * @param may_drop GNUNET_YES if the message can be dropped
609 GST_clients_broadcast (const struct GNUNET_MessageHeader *msg,
612 struct TransportClient *tc;
614 for (tc = clients_head; tc != NULL; tc = tc->next)
615 unicast (tc, msg, may_drop);
620 * Send the given message to a particular client
622 * @param client target of the message
623 * @param msg message to transmit
624 * @param may_drop GNUNET_YES if the message can be dropped
627 GST_clients_unicast (struct GNUNET_SERVER_Client *client,
628 const struct GNUNET_MessageHeader *msg,
631 struct TransportClient *tc;
633 tc = lookup_client (client);
635 tc = setup_client (client);
636 unicast (tc, msg, may_drop);
640 /* end of file gnunet-service-transport_clients.c */