simplify
[oweals/gnunet.git] / src / transport / gnunet-service-transport_clients.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 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 3, 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 transport/gnunet-service-transport_clients.c
23  * @brief plugin management API
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
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"
33
34 /**
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.
39  *
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
44  * fail.
45  */
46 #define MAX_PENDING (128 * 1024)
47
48
49 /**
50  * Linked list of messages to be transmitted to the client.  Each
51  * entry is followed by the actual message.
52  */
53 struct ClientMessageQueueEntry
54 {
55   /**
56    * This is a doubly-linked list.
57    */
58   struct ClientMessageQueueEntry *next;
59
60   /**
61    * This is a doubly-linked list.
62    */
63   struct ClientMessageQueueEntry *prev;
64 };
65
66
67 /**
68  * Client connected to the transport service.
69  */
70 struct TransportClient
71 {
72
73   /**
74    * This is a doubly-linked list.
75    */
76   struct TransportClient *next;
77
78   /**
79    * This is a doubly-linked list.
80    */
81   struct TransportClient *prev;
82
83   /**
84    * Handle to the client.
85    */
86   struct GNUNET_SERVER_Client *client;
87
88   /**
89    * Linked list of messages yet to be transmitted to
90    * the client.
91    */
92   struct ClientMessageQueueEntry *message_queue_head;
93
94   /**
95    * Tail of linked list of messages yet to be transmitted to the
96    * client.
97    */
98   struct ClientMessageQueueEntry *message_queue_tail;
99
100   /**
101    * Current transmit request handle.
102    */
103   struct GNUNET_CONNECTION_TransmitHandle *th;
104
105   /**
106    * Length of the list of messages pending for this client.
107    */
108   unsigned int message_count;
109
110 };
111
112
113 /**
114  * Head of linked list of all clients to this service.
115  */
116 static struct TransportClient *clients_head;
117
118 /**
119  * Tail of linked list of all clients to this service.
120  */
121 static struct TransportClient *clients_tail;
122
123
124 /**
125  * Find the internal handle associated with the given client handle
126  *
127  * @param client server's client handle to look up
128  * @return internal client handle
129  */
130 static struct TransportClient *
131 lookup_client (struct GNUNET_SERVER_Client *client)
132 {
133   struct TransportClient *tc;
134
135   tc = clients_head; 
136   while (tc != NULL)
137     {
138       if (tc->client == client)
139         return tc;
140       tc = tc->next;
141     }
142   return NULL;
143 }
144
145
146 /**
147  * Create the internal handle for the given server client handle
148  *
149  * @param client server's client handle to create our internal handle for
150  * @return fresh internal client handle
151  */
152 static struct TransportClient *
153 setup_client (struct GNUNET_SERVER_Client *client)
154 {
155   struct TransportClient *tc;
156   
157   tc = GNUNET_malloc (sizeof (struct TransportClient));
158   tc->client = client;
159   GNUNET_CONTAINER_DLL_insert (clients_head,
160                                clients_tail,
161                                tc);
162   return tc;
163 }
164
165
166 /**
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.
170  *
171  * @param cls closure
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
175  */
176 static size_t
177 transmit_to_client_callback (void *cls, 
178                              size_t size, 
179                              void *buf)
180 {
181   struct TransportClient *tc = cls;
182   struct ClientMessageQueueEntry *q;
183   const struct GNUNET_MessageHeader *msg;
184   char *cbuf;
185   uint16_t msize;
186   size_t tsize;
187
188   tc->th = NULL;
189   if (buf == NULL)
190     {
191 #if DEBUG_TRANSPORT 
192       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
193                   "Transmission to client failed, closing connection.\n");
194 #endif
195       return 0;
196     }
197   cbuf = buf;
198   tsize = 0;
199   while (NULL != (q = tc->message_queue_head))
200     {
201       msg = (const struct GNUNET_MessageHeader *) &q[1];
202       msize = ntohs (msg->size);
203       if (msize + tsize > size)
204         break;
205 #if DEBUG_TRANSPORT
206       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
207                   "Transmitting message of type %u to client.\n",
208                   ntohs (msg->type));
209 #endif
210       GNUNET_CONTAINER_DLL_remove (tc->message_queue_head,
211                                    tc->message_queue_tail,
212                                    q);
213       tc->message_count--;
214       memcpy (&cbuf[tsize], 
215               msg, 
216               msize);
217       GNUNET_free (q);
218       tsize += msize;
219     }
220   if (NULL != q)
221     {
222       GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
223       tc->th = GNUNET_SERVER_notify_transmit_ready (tc->client,
224                                                     msize,
225                                                     GNUNET_TIME_UNIT_FOREVER_REL,
226                                                     &transmit_to_client_callback,
227                                                     tc);
228       GNUNET_assert (tc->th != NULL);
229     }
230   return tsize;
231 }
232
233
234 /**
235  * Queue the given message for transmission to the given client
236  *
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
240  */
241 static void
242 unicast (struct TransportClient *tc,
243          const struct GNUNET_MessageHeader *msg,
244          int may_drop)
245 {
246   struct ClientMessageQueueEntry *q;
247   uint16_t msize;
248
249   if ( (tc->message_count >= MAX_PENDING) && 
250        (GNUNET_YES == may_drop) )
251     {
252       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
253                   _("Dropping message of type %u and size %u, have %u/%u messages pending\n"),
254                   ntohs (msg->type),
255                   ntohs (msg->size),
256                   tc->message_count,
257                   MAX_PENDING);
258       GNUNET_STATISTICS_update (GST_stats,
259                                 gettext_noop ("# messages dropped due to slow client"),
260                                 1,
261                                 GNUNET_NO);
262       return;
263     }
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,
270                                     q);
271   tc->message_count++;
272   if (tc->th != NULL)
273     return;
274   tc->th = GNUNET_SERVER_notify_transmit_ready (tc->client,
275                                                 msize,
276                                                 GNUNET_TIME_UNIT_FOREVER_REL,
277                                                 &transmit_to_client_callback,
278                                                 tc);
279   GNUNET_assert (tc->th != NULL);    
280 }
281
282
283 /**
284  * Called whenever a client is disconnected.  Frees our
285  * resources associated with that client.
286  *
287  * @param cls closure
288  * @param client identification of the client
289  */
290 static void
291 client_disconnect_notification (void *cls,
292                                 struct GNUNET_SERVER_Client *client)
293 {
294   struct TransportClient *tc;
295   struct ClientMessageQueueEntry *mqe;
296
297   if (client == NULL)
298     return;
299   tc = lookup_client (client);
300   if (tc == NULL)
301     return;
302 #if DEBUG_TRANSPORT
303   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
304               "Client disconnected, cleaning up.\n");
305 #endif
306   while (NULL != (mqe = tc->message_queue_head))
307     {
308       GNUNET_CONTAINER_DLL_remove (tc->message_queue_head,
309                                    tc->message_queue_tail,
310                                    mqe);
311       tc->message_count--;
312       GNUNET_free (mqe);
313     }
314   GNUNET_CONTAINER_DLL_remove (clients_head,
315                                clients_tail,
316                                tc);
317   if (tc->th != NULL)
318     {
319       GNUNET_CONNECTION_notify_transmit_ready_cancel (tc->th);
320       tc->th = NULL;
321     }
322   GNUNET_break (0 == tc->message_count);
323   GNUNET_free (tc);
324 }
325
326
327 /**
328  * Start handling requests from clients.
329  *
330  * @param server server used to accept clients from.
331  */
332 void 
333 GST_clients_start (struct GNUNET_SERVER_Handle *server)
334 {
335   GNUNET_SERVER_disconnect_notify (server,
336                                    &client_disconnect_notification, NULL);
337 }
338
339
340 /**
341  * Stop processing clients.
342  */
343 void
344 GST_clients_stop ()
345 {
346   /* nothing to do */
347 }
348
349
350 /**
351  * Function called for each of our connected neighbours.  Notify the
352  * client about the existing neighbour.
353  *
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)
358  */
359 static void
360 notify_client_about_neighbour (void *cls,
361                                const struct GNUNET_PeerIdentity *peer,
362                                const struct GNUNET_TRANSPORT_ATS_Information *ats,
363                                uint32_t ats_count)
364 {
365   struct TransportClient *tc = cls;
366   struct ConnectInfoMessage *cim;
367   size_t size;
368
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);
375   cim->id = *peer;
376   memcpy (&cim->ats,
377           ats, 
378           ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
379   unicast (tc, &cim->header, GNUNET_NO);
380   GNUNET_free (cim);
381 }
382
383
384 /**
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
387  * messages.
388  *
389  * @param cls unused
390  * @param client the client
391  * @param message the start message that was sent
392  */
393 void
394 GST_clients_handle_start (void *cls,
395                           struct GNUNET_SERVER_Client *client,
396                           const struct GNUNET_MessageHeader *message)
397 {
398   const struct StartMessage *start;
399   struct TransportClient *tc;
400
401   tc = lookup_client (client);
402   if (tc != NULL)
403     {
404       /* got 'start' twice from the same client, not allowed */
405       GNUNET_break (0);
406       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
407       return;
408     }
409   start = (const struct StartMessage*) message;
410   if ( (GNUNET_NO != ntohl (start->do_check)) &&
411        (0 != memcmp (&start->self,
412                      &GST_my_identity,
413                      sizeof (struct GNUNET_PeerIdentity))) )
414     {
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);
420       return;
421     }  
422   tc = setup_client (client);
423   unicast (tc, GST_hello_get(), GNUNET_NO);
424   GST_neighbours_iterate (&notify_client_about_neighbour, tc);
425   GNUNET_SERVER_receive_done (client, GNUNET_OK);
426 }
427
428
429 /**
430  * Client asked for transmission to a peer.  Process the request.
431  *
432  * @param cls unused
433  * @param client the client
434  * @param message the send message that was sent
435  */
436 void
437 GST_clients_handle_send (void *cls,
438                          struct GNUNET_SERVER_Client *client,
439                          const struct GNUNET_MessageHeader *message)
440 {
441 }
442
443
444 /**
445  * Client asked for a quota change for a particular peer.  Process the request.
446  *
447  * @param cls unused
448  * @param client the client
449  * @param message the quota changing message
450  */
451 void
452 GST_clients_handle_set_quota (void *cls,
453                               struct GNUNET_SERVER_Client *client,
454                               const struct GNUNET_MessageHeader *message)
455 {
456   const struct QuotaSetMessage *qsm;
457
458   qsm = (const struct QuotaSetMessage *) message;
459   GNUNET_STATISTICS_update (GST_stats,
460                             gettext_noop ("# SET QUOTA messages received"),
461                             1,
462                             GNUNET_NO);
463 #if DEBUG_TRANSPORT 
464   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
465               "Received `%s' request (new quota %u) from client for peer `%4s'\n",
466               "SET_QUOTA",
467               (unsigned int) ntohl (qsm->quota.value__),
468               GNUNET_i2s (&qsm->peer));
469 #endif
470   GST_neighbours_set_incoming_quota (&qsm->peer,
471                                      qsm->quota);
472   GNUNET_SERVER_receive_done (client, GNUNET_OK);
473 }
474
475
476 /**
477  * Take the given address and append it to the set of results sent back to
478  * the client.
479  *
480  * @param cls the transmission context used ('struct GNUNET_SERVER_TransmitContext*')
481  * @param address the resolved name, NULL to indicate the last response
482  */
483 static void
484 transmit_address_to_client (void *cls, 
485                             const char *address)
486 {
487   struct GNUNET_SERVER_TransmitContext *tc = cls;
488
489   if (NULL == address)
490     {
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);
495       return;
496     }
497   GNUNET_SERVER_transmit_context_append_data (tc, 
498                                               address, strlen (address) + 1,
499                                               GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_REPLY);
500 }
501
502
503 /**
504  * Client asked to resolve an address.  Process the request.
505  *
506  * @param cls unused
507  * @param client the client
508  * @param message the resolution request
509  */
510 void
511 GST_clients_handle_address_lookup (void *cls,
512                                    struct GNUNET_SERVER_Client *client,
513                                    const struct GNUNET_MessageHeader *message)
514 {
515   const struct AddressLookupMessage *alum;
516   struct GNUNET_TRANSPORT_PluginFunctions *papi;
517   const char *plugin_name;
518   const char *address;
519   uint32_t address_len;
520   uint16_t size;
521   struct GNUNET_SERVER_TransmitContext *tc;
522   struct GNUNET_TIME_Relative rtimeout;
523   int32_t numeric;
524
525   size = ntohs (message->size);
526   if (size < sizeof (struct AddressLookupMessage))
527     {
528       GNUNET_break (0);
529       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
530       return;
531     }
532   alum = (const struct AddressLookupMessage *) message;
533   address_len = ntohl (alum->addrlen);
534   if (size <= sizeof (struct AddressLookupMessage) + address_len)
535     {
536       GNUNET_break (0);
537       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
538       return;
539     }
540   address = (const char *) &alum[1];
541   plugin_name = (const char *) &address[address_len];
542   if (plugin_name
543       [size - sizeof (struct AddressLookupMessage) - address_len - 1] != '\0')
544     {
545       GNUNET_break (0);
546       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
547       return;
548     }
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);
553   if (NULL == papi)
554     {
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);
558       return;
559     }
560   GNUNET_SERVER_disable_receive_done_warning (client);
561   papi->address_pretty_printer (papi->cls,
562                                 plugin_name,
563                                 address, address_len,
564                                 numeric,
565                                 rtimeout,
566                                 &transmit_address_to_client, tc);
567 }
568
569
570 /**
571  * Client asked to obtain information about a peer's addresses.
572  * Process the request.
573  *
574  * @param cls unused
575  * @param client the client
576  * @param message the peer address information request
577  */
578 void
579 GST_clients_handle_peer_address_lookup (void *cls,
580                                         struct GNUNET_SERVER_Client *client,
581                                         const struct GNUNET_MessageHeader *message)
582 {
583 }
584
585
586 /**
587  * Client asked to obtain information about all addresses.
588  * Process the request.
589  *
590  * @param cls unused
591  * @param client the client
592  * @param message the peer address information request
593  */
594 void
595 GST_clients_handle_address_iterate (void *cls,
596                                     struct GNUNET_SERVER_Client *client,
597                                     const struct GNUNET_MessageHeader *message)
598 {
599 }
600
601
602 /**
603  * Broadcast the given message to all of our clients.
604  *
605  * @param msg message to broadcast
606  * @param may_drop GNUNET_YES if the message can be dropped
607  */
608 void
609 GST_clients_broadcast (const struct GNUNET_MessageHeader *msg,
610                        int may_drop)
611 {
612   struct TransportClient *tc;
613
614   for (tc = clients_head; tc != NULL; tc = tc->next)
615     unicast (tc, msg, may_drop);
616 }
617
618
619 /**
620  * Send the given message to a particular client
621  *
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
625  */
626 void
627 GST_clients_unicast (struct GNUNET_SERVER_Client *client,
628                      const struct GNUNET_MessageHeader *msg,
629                      int may_drop)
630 {
631   struct TransportClient *tc;
632
633   tc = lookup_client (client);
634   if (NULL == tc)
635     tc = setup_client (client);
636   unicast (tc, msg, may_drop);
637 }
638
639
640 /* end of file gnunet-service-transport_clients.c */