- fix
[oweals/gnunet.git] / src / transport / plugin_transport_unix.c
1 /*
2      This file is part of GNUnet
3      (C) 2010 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/plugin_transport_unix.c
23  * @brief Transport plugin using unix domain sockets (!)
24  *        Clearly, can only be used locally on Unix/Linux hosts...
25  *        ONLY INTENDED FOR TESTING!!!
26  * @author Christian Grothoff
27  * @author Nathan Evans
28  */
29
30 #include "platform.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_connection_lib.h"
33 #include "gnunet_container_lib.h"
34 #include "gnunet_os_lib.h"
35 #include "gnunet_peerinfo_service.h"
36 #include "gnunet_protocols.h"
37 #include "gnunet_resolver_service.h"
38 #include "gnunet_server_lib.h"
39 #include "gnunet_signatures.h"
40 #include "gnunet_statistics_service.h"
41 #include "gnunet_transport_service.h"
42 #include "gnunet_transport_plugin.h"
43 #include "transport.h"
44
45 #define DEBUG_UNIX GNUNET_EXTRALOGGING
46 #define DETAILS GNUNET_NO
47
48 #define MAX_PROBES 20
49
50 /*
51  * Transport cost to peer, always 1 for UNIX (direct connection)
52  */
53 #define UNIX_DIRECT_DISTANCE 1
54
55 #define DEFAULT_NAT_PORT 0
56
57 /**
58  * How long until we give up on transmitting the welcome message?
59  */
60 #define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
61
62 /**
63  * Starting port for listening and sending, eventually a config value
64  */
65 #define UNIX_NAT_DEFAULT_PORT 22086
66
67 GNUNET_NETWORK_STRUCT_BEGIN
68
69 /**
70  * UNIX Message-Packet header.
71  */
72 struct UNIXMessage
73 {
74   /**
75    * Message header.
76    */
77   struct GNUNET_MessageHeader header;
78
79   /**
80    * What is the identity of the sender (GNUNET_hash of public key)
81    */
82   struct GNUNET_PeerIdentity sender;
83
84 };
85
86 struct Session
87 {
88   void *addr;
89   size_t addrlen;
90   struct GNUNET_PeerIdentity target;
91 };
92
93 struct UNIXMessageWrapper
94 {
95   struct UNIXMessageWrapper *next;
96   struct UNIXMessageWrapper *prev;
97
98   struct UNIXMessage * msg;
99   size_t msgsize;
100
101   struct GNUNET_TIME_Relative timeout;
102   unsigned int priority;
103
104   struct Session *session;
105   GNUNET_TRANSPORT_TransmitContinuation cont;
106   void *cont_cls;
107 };
108
109 /* Forward definition */
110 struct Plugin;
111
112
113 /**
114  * UNIX NAT "Session"
115  */
116 struct PeerSession
117 {
118
119   /**
120    * Stored in a linked list.
121    */
122   struct PeerSession *next;
123
124   /**
125    * Pointer to the global plugin struct.
126    */
127   struct Plugin *plugin;
128
129   /**
130    * To whom are we talking to (set to our identity
131    * if we are still waiting for the welcome message)
132    */
133   struct GNUNET_PeerIdentity target;
134
135   /**
136    * Address of the other peer (either based on our 'connect'
137    * call or on our 'accept' call).
138    */
139   void *connect_addr;
140
141   /**
142    * Length of connect_addr.
143    */
144   size_t connect_alen;
145
146   /**
147    * Are we still expecting the welcome message? (GNUNET_YES/GNUNET_NO)
148    */
149   int expecting_welcome;
150
151   /**
152    * From which socket do we need to send to this peer?
153    */
154   struct GNUNET_NETWORK_Handle *sock;
155
156   /*
157    * Queue of messages for this peer, in the case that
158    * we have to await a connection...
159    */
160   struct MessageQueue *messages;
161
162 };
163
164 /**
165  * Information we keep for each of our listen sockets.
166  */
167 struct UNIX_Sock_Info
168 {
169   /**
170    * The network handle
171    */
172   struct GNUNET_NETWORK_Handle *desc;
173
174   /**
175    * The port we bound to
176    */
177   uint16_t port;
178 };
179
180
181 /**
182  * Encapsulation of all of the state of the plugin.
183  */
184 struct Plugin
185 {
186   /**
187    * Our environment.
188    */
189   struct GNUNET_TRANSPORT_PluginEnvironment *env;
190
191   /*
192    * Session of peers with whom we are currently connected
193    */
194   struct PeerSession *sessions;
195
196   /*
197    * Sessions
198    */
199   struct GNUNET_CONTAINER_MultiHashMap *session_map;
200
201   /**
202    * ID of task used to update our addresses when one expires.
203    */
204   GNUNET_SCHEDULER_TaskIdentifier address_update_task;
205
206   /**
207    * ID of select task
208    */
209   GNUNET_SCHEDULER_TaskIdentifier select_task;
210
211   /**
212    * Integer to append to unix domain socket.
213    */
214   uint16_t port;
215
216   /**
217    * FD Read set
218    */
219   struct GNUNET_NETWORK_FDSet *rs;
220
221   /**
222    * FD Write set
223    */
224   struct GNUNET_NETWORK_FDSet *ws;
225
226   int with_ws;
227
228   /**
229    * socket that we transmit all data with
230    */
231   struct UNIX_Sock_Info unix_sock;
232
233   /**
234    * Path of our unix domain socket (/tmp/unix-plugin-PORT)
235    */
236   char *unix_socket_path;
237
238   struct UNIXMessageWrapper *msg_head;
239   struct UNIXMessageWrapper *msg_tail;
240
241   /**
242    * ATS network
243    */
244   struct GNUNET_ATS_Information ats_network;
245
246   unsigned int bytes_in_queue;
247 };
248
249
250 static int
251 get_session_delete_it (void *cls, const GNUNET_HashCode * key, void *value)
252 {
253   struct Session *s = value;
254   struct Plugin *plugin = cls;
255   GNUNET_assert (plugin != NULL);
256
257 #if DEBUG_UNIX
258   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting session for peer `%s' `%s' \n", GNUNET_i2s (&s->target), s->addr);
259 #endif
260
261   plugin->env->session_end (plugin->env->cls, &s->target, s);
262
263   GNUNET_assert (GNUNET_YES ==
264                  GNUNET_CONTAINER_multihashmap_remove(plugin->session_map, &s->target.hashPubKey, s));
265
266   GNUNET_STATISTICS_set(plugin->env->stats,
267                         "# UNIX sessions active",
268                         GNUNET_CONTAINER_multihashmap_size(plugin->session_map),
269                         GNUNET_NO);
270
271   GNUNET_free (s);
272
273   return GNUNET_YES;
274 }
275
276 /**
277  * Disconnect from a remote node.  Clean up session if we have one for this peer
278  *
279  * @param cls closure for this call (should be handle to Plugin)
280  * @param target the peeridentity of the peer to disconnect
281  * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
282  */
283 void
284 unix_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
285 {
286   struct Plugin *plugin = cls;
287   GNUNET_assert (plugin != NULL);
288
289   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->session_map, &target->hashPubKey, &get_session_delete_it, plugin);
290   return;
291 }
292
293 /**
294  * Shutdown the server process (stop receiving inbound traffic). Maybe
295  * restarted later!
296  *
297  * @param cls Handle to the plugin for this transport
298  *
299  * @return returns the number of sockets successfully closed,
300  *         should equal the number of sockets successfully opened
301  */
302 static int
303 unix_transport_server_stop (void *cls)
304 {
305   struct Plugin *plugin = cls;
306
307   struct UNIXMessageWrapper * msgw = plugin->msg_head;
308
309   while (NULL != (msgw = plugin->msg_head))
310   {
311     GNUNET_CONTAINER_DLL_remove (plugin->msg_head, plugin->msg_tail, msgw);
312     if (msgw->cont != NULL)
313       msgw->cont (msgw->cont_cls,  &msgw->session->target, GNUNET_SYSERR);
314     GNUNET_free (msgw->msg);
315     GNUNET_free (msgw);
316   }
317
318   if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
319   {
320     GNUNET_SCHEDULER_cancel (plugin->select_task);
321     plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
322   }
323
324   if (NULL != plugin->unix_sock.desc)
325   {
326     GNUNET_break (GNUNET_OK ==
327                   GNUNET_NETWORK_socket_close (plugin->unix_sock.desc));
328     plugin->unix_sock.desc = NULL;
329     plugin->with_ws = GNUNET_NO;
330   }
331   return GNUNET_OK;
332 }
333
334
335 struct PeerSession *
336 find_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *peer)
337 {
338   struct PeerSession *pos;
339
340   pos = plugin->sessions;
341   while (pos != NULL)
342   {
343     if (memcmp (&pos->target, peer, sizeof (struct GNUNET_PeerIdentity)) == 0)
344       return pos;
345     pos = pos->next;
346   }
347
348   return pos;
349 }
350
351
352 /**
353  * Actually send out the message, assume we've got the address and
354  * send_handle squared away!
355  *
356  * @param cls closure
357  * @param send_handle which handle to send message on
358  * @param target who should receive this message (ignored by UNIX)
359  * @param msgbuf one or more GNUNET_MessageHeader(s) strung together
360  * @param msgbuf_size the size of the msgbuf to send
361  * @param priority how important is the message (ignored by UNIX)
362  * @param timeout when should we time out (give up) if we can not transmit?
363  * @param addr the addr to send the message to, needs to be a sockaddr for us
364  * @param addrlen the len of addr
365  * @param cont continuation to call once the message has
366  *        been transmitted (or if the transport is ready
367  *        for the next transmission call; or if the
368  *        peer disconnected...)
369  * @param cont_cls closure for cont
370  *
371  * @return the number of bytes written, -1 on errors
372  */
373 static ssize_t
374 unix_real_send (void *cls,
375                 struct GNUNET_NETWORK_Handle *send_handle,
376                 const struct GNUNET_PeerIdentity *target, const char *msgbuf,
377                 size_t msgbuf_size, unsigned int priority,
378                 struct GNUNET_TIME_Relative timeout, const void *addr,
379                 size_t addrlen, GNUNET_TRANSPORT_TransmitContinuation cont,
380                 void *cont_cls)
381 {
382
383   ssize_t sent;
384   const void *sb;
385   size_t sbs;
386   struct sockaddr_un un;
387   size_t slen;
388   int retry;
389
390   if (send_handle == NULL)
391   {
392 #if DEBUG_UNIX
393     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394                 "unix_real_send with send_handle NULL!\n");
395 #endif
396     /* failed to open send socket for AF */
397     if (cont != NULL)
398       cont (cont_cls, target, GNUNET_SYSERR);
399     return 0;
400   }
401   if ((addr == NULL) || (addrlen == 0))
402   {
403 #if DEBUG_UNIX
404     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
405                 "unix_real_send called without address, returning!\n");
406 #endif
407     if (cont != NULL)
408       cont (cont_cls, target, GNUNET_SYSERR);
409     return 0;                   /* Can never send if we don't have an address!! */
410   }
411
412   memset (&un, 0, sizeof (un));
413   un.sun_family = AF_UNIX;
414   slen = strlen (addr) + 1;
415   if (slen >= sizeof (un.sun_path))
416     slen = sizeof (un.sun_path) - 1;
417   sent = 0;
418   GNUNET_assert (slen < sizeof (un.sun_path));
419   memcpy (un.sun_path, addr, slen);
420   un.sun_path[slen] = '\0';
421   slen = sizeof (struct sockaddr_un);
422 #if LINUX
423   un.sun_path[0] = '\0';
424 #endif
425 #if HAVE_SOCKADDR_IN_SIN_LEN
426   un.sun_len = (u_char) slen;
427 #endif
428   sb = (struct sockaddr *) &un;
429   sbs = slen;
430   retry = GNUNET_NO;
431   sent = GNUNET_NETWORK_socket_sendto (send_handle, msgbuf, msgbuf_size, sb, sbs);
432
433   if ((GNUNET_SYSERR == sent) && ((errno == EAGAIN) || (errno == ENOBUFS)))
434     retry = GNUNET_YES;
435
436   if ((GNUNET_SYSERR == sent) && (errno == EMSGSIZE))
437   {
438     socklen_t size = 0;
439     socklen_t len = sizeof (size);
440
441     GNUNET_NETWORK_socket_getsockopt ((struct GNUNET_NETWORK_Handle *)
442                                       send_handle, SOL_SOCKET, SO_SNDBUF, &size,
443                                       &len);
444
445     if (size < msgbuf_size)
446     {
447       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448                   "Trying to increase socket buffer size from %i to %i for message size %i\n",
449                   size, ((msgbuf_size / 1000) + 2) * 1000, msgbuf_size);
450       size = ((msgbuf_size / 1000) + 2) * 1000;
451       if (GNUNET_NETWORK_socket_setsockopt
452           ((struct GNUNET_NETWORK_Handle *) send_handle, SOL_SOCKET, SO_SNDBUF,
453            &size, sizeof (size)) == GNUNET_OK)
454       {
455         sent = GNUNET_NETWORK_socket_sendto (send_handle, msgbuf, msgbuf_size, sb, sbs);
456       }
457       else
458       {
459         GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsockopt");
460       }
461     }
462   }
463
464 #if DEBUG_UNIX
465   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
466               "UNIX transmit %u-byte message to %s (%d: %s)\n",
467               (unsigned int) msgbuf_size, GNUNET_a2s (sb, sbs), (int) sent,
468               (sent < 0) ? STRERROR (errno) : "ok");
469 #endif
470   /* Calling continuation */
471   if (cont != NULL)
472   {
473     if ((sent == GNUNET_SYSERR) && (retry == GNUNET_NO))
474       cont (cont_cls, target, GNUNET_SYSERR);
475     if (sent > 0)
476       cont (cont_cls, target, GNUNET_OK);
477   }
478
479   /* return number of bytes successfully sent */
480   if (sent > 0)
481     return sent;
482   /* failed and retry: return 0 */
483   if ((GNUNET_SYSERR == sent) && (retry == GNUNET_YES))
484     return 0;
485   /* failed and no retry: return -1 */
486   if ((GNUNET_SYSERR == sent) && (retry == GNUNET_NO))
487     return -1;
488
489   return sent;
490 }
491
492 struct gsi_ctx
493 {
494   char *address;
495   size_t addrlen;
496   struct Session *res;
497 };
498
499 static int
500 get_session_it (void *cls, const GNUNET_HashCode * key, void *value)
501 {
502   struct gsi_ctx *gsi = cls;
503   struct Session *s = value;
504
505 #if DEBUG_UNIX
506   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Comparing session %s %s\n", gsi->address, s->addr);
507 #endif
508   if ((gsi->addrlen == s->addrlen) &&
509       (0 == memcmp (gsi->address, s->addr, s->addrlen)))
510   {
511     gsi->res = s;
512     return GNUNET_NO;
513   }
514   return GNUNET_YES;
515 }
516
517 /**
518  * Creates a new outbound session the transport service will use to send data to the
519  * peer
520  *
521  * @param cls the plugin
522  * @param address the address
523  * @return the session or NULL of max connections exceeded
524  */
525 static struct Session *
526 unix_plugin_get_session (void *cls,
527                   const struct GNUNET_HELLO_Address *address)
528 {
529   struct Session * s = NULL;
530   struct Plugin *plugin = cls;
531   struct gsi_ctx gsi;
532
533   /* Checks */
534   GNUNET_assert (plugin != NULL);
535   GNUNET_assert (address != NULL);
536
537   /* Check if already existing */
538   gsi.address = (char *) address->address;
539   gsi.addrlen = address->address_length;
540   gsi.res = NULL;
541   GNUNET_CONTAINER_multihashmap_get_multiple (plugin->session_map, &address->peer.hashPubKey, &get_session_it, &gsi);
542   if (gsi.res != NULL)
543   {
544 #if DEBUG_UNIX
545     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found existing session\n");
546 #endif
547     return gsi.res;
548   }
549
550   /* Create a new session */
551
552   s = GNUNET_malloc (sizeof (struct Session) + address->address_length);
553   s->addr = &s[1];
554   s->addrlen = address->address_length;
555   memcpy(s->addr, address->address, s->addrlen);
556   memcpy(&s->target, &address->peer, sizeof (struct GNUNET_PeerIdentity));
557
558   GNUNET_CONTAINER_multihashmap_put (plugin->session_map,
559       &address->peer.hashPubKey, s,
560       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
561
562   GNUNET_STATISTICS_set(plugin->env->stats,
563                         "# UNIX sessions active",
564                         GNUNET_CONTAINER_multihashmap_size(plugin->session_map),
565                         GNUNET_NO);
566
567 #if DEBUG_UNIX
568     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating new session\n");
569 #endif
570
571   return s;
572 }
573
574 /*
575  * @param cls the plugin handle
576  * @param tc the scheduling context (for rescheduling this function again)
577  *
578  * We have been notified that our writeset has something to read.  We don't
579  * know which socket needs to be read, so we have to check each one
580  * Then reschedule this function to be called again once more is available.
581  *
582  */
583 static void
584 unix_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
585
586 /**
587  * Function that can be used by the transport service to transmit
588  * a message using the plugin.   Note that in the case of a
589  * peer disconnecting, the continuation MUST be called
590  * prior to the disconnect notification itself.  This function
591  * will be called with this peer's HELLO message to initiate
592  * a fresh connection to another peer.
593  *
594  * @param cls closure
595  * @param session which session must be used
596  * @param msgbuf the message to transmit
597  * @param msgbuf_size number of bytes in 'msgbuf'
598  * @param priority how important is the message (most plugins will
599  *                 ignore message priority and just FIFO)
600  * @param to how long to wait at most for the transmission (does not
601  *                require plugins to discard the message after the timeout,
602  *                just advisory for the desired delay; most plugins will ignore
603  *                this as well)
604  * @param cont continuation to call once the message has
605  *        been transmitted (or if the transport is ready
606  *        for the next transmission call; or if the
607  *        peer disconnected...); can be NULL
608  * @param cont_cls closure for cont
609  * @return number of bytes used (on the physical network, with overheads);
610  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
611  *         and does NOT mean that the message was not transmitted (DV)
612  */
613 static ssize_t
614 unix_plugin_send (void *cls,
615                   struct Session *session,
616                   const char *msgbuf, size_t msgbuf_size,
617                   unsigned int priority,
618                   struct GNUNET_TIME_Relative to,
619                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
620 {
621   struct Plugin *plugin = cls;
622   struct UNIXMessageWrapper *wrapper;
623   struct UNIXMessage *message;
624   int ssize;
625
626   GNUNET_assert (plugin != NULL);
627   GNUNET_assert (session != NULL);
628
629   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_contains_value(plugin->session_map,
630       &session->target.hashPubKey, session))
631   {
632     GNUNET_break (0);
633     return GNUNET_SYSERR;
634   }
635
636   ssize = sizeof (struct UNIXMessage) + msgbuf_size;
637   message = GNUNET_malloc (sizeof (struct UNIXMessage) + msgbuf_size);
638   message->header.size = htons (ssize);
639   message->header.type = htons (0);
640   memcpy (&message->sender, plugin->env->my_identity,
641           sizeof (struct GNUNET_PeerIdentity));
642   memcpy (&message[1], msgbuf, msgbuf_size);
643
644
645   wrapper = GNUNET_malloc (sizeof (struct UNIXMessageWrapper));
646   wrapper->msg = message;
647   wrapper->msgsize = ssize;
648   wrapper->priority = priority;
649   wrapper->timeout = to;
650   wrapper->cont = cont;
651   wrapper->cont_cls = cont_cls;
652   wrapper->session = session;
653
654   GNUNET_CONTAINER_DLL_insert(plugin->msg_head, plugin->msg_tail, wrapper);
655
656   plugin->bytes_in_queue += ssize;
657   GNUNET_STATISTICS_set (plugin->env->stats,"# UNIX bytes in send queue",
658       plugin->bytes_in_queue, GNUNET_NO);
659
660 #if DEBUG_UNIX
661   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sent %d bytes to `%s'\n", ssize,
662               (char *) session->addr);
663 #endif
664
665   if (plugin->with_ws == GNUNET_NO)
666   {
667     if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
668       GNUNET_SCHEDULER_cancel(plugin->select_task);
669
670     plugin->select_task =
671         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
672                                      GNUNET_TIME_UNIT_FOREVER_REL,
673                                      plugin->rs,
674                                      plugin->ws,
675                                      &unix_plugin_select, plugin);
676     plugin->with_ws = GNUNET_YES;
677   }
678   return ssize;
679 }
680
681
682 /**
683  * Demultiplexer for UNIX messages
684  *
685  * @param plugin the main plugin for this transport
686  * @param sender from which peer the message was received
687  * @param currhdr pointer to the header of the message
688  * @param un the address from which the message was received
689  * @param fromlen the length of the address
690  */
691 static void
692 unix_demultiplexer (struct Plugin *plugin, struct GNUNET_PeerIdentity *sender,
693                     const struct GNUNET_MessageHeader *currhdr,
694                     const struct sockaddr_un *un, size_t fromlen)
695 {
696   struct GNUNET_ATS_Information ats[2];
697
698   ats[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
699   ats[0].value = htonl (UNIX_DIRECT_DISTANCE);
700   ats[1] = plugin->ats_network;
701   GNUNET_break (ntohl(plugin->ats_network.value) != GNUNET_ATS_NET_UNSPECIFIED);
702
703   GNUNET_assert (fromlen >= sizeof (struct sockaddr_un));
704
705 #if DEBUG_UNIX
706   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message from %s\n",
707               un->sun_path);
708 #endif
709   plugin->env->receive (plugin->env->cls, sender, currhdr,
710                         (const struct GNUNET_ATS_Information *) &ats, 2,
711                         NULL, un->sun_path, strlen (un->sun_path) + 1);
712 }
713
714
715 static void
716 unix_plugin_select_read (struct Plugin * plugin)
717 {
718   char buf[65536] GNUNET_ALIGN;
719   struct UNIXMessage *msg;
720   struct GNUNET_PeerIdentity sender;
721   struct sockaddr_un un;
722   socklen_t addrlen;
723   ssize_t ret;
724   int offset;
725   int tsize;
726   char *msgbuf;
727   const struct GNUNET_MessageHeader *currhdr;
728   uint16_t csize;
729
730   addrlen = sizeof (un);
731   memset (&un, 0, sizeof (un));
732
733   ret =
734       GNUNET_NETWORK_socket_recvfrom (plugin->unix_sock.desc, buf, sizeof (buf),
735                                       (struct sockaddr *) &un, &addrlen);
736
737   if ((GNUNET_SYSERR == ret) && ((errno == EAGAIN) || (errno == ENOBUFS)))
738     return;
739
740   if (ret == GNUNET_SYSERR)
741   {
742     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "recvfrom");
743     return;
744   }
745   else
746   {
747 #if LINUX
748     un.sun_path[0] = '/';
749 #endif
750 #if DEBUG_UNIX
751     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %d bytes from socket %s\n", ret,
752                 &un.sun_path[0]);
753 #endif
754   }
755
756   GNUNET_assert (AF_UNIX == (un.sun_family));
757
758   msg = (struct UNIXMessage *) buf;
759   csize = ntohs (msg->header.size);
760   if ((csize < sizeof (struct UNIXMessage)) || (csize > ret))
761   {
762     GNUNET_break_op (0);
763     return;
764   }
765   msgbuf = (char *) &msg[1];
766   memcpy (&sender, &msg->sender, sizeof (struct GNUNET_PeerIdentity));
767   offset = 0;
768   tsize = csize - sizeof (struct UNIXMessage);
769   while (offset + sizeof (struct GNUNET_MessageHeader) <= tsize)
770   {
771     currhdr = (struct GNUNET_MessageHeader *) &msgbuf[offset];
772     csize = ntohs (currhdr->size);
773     if ((csize < sizeof (struct GNUNET_MessageHeader)) ||
774         (csize > tsize - offset))
775     {
776       GNUNET_break_op (0);
777       break;
778     }
779     unix_demultiplexer (plugin, &sender, currhdr, &un, sizeof (un));
780     offset += csize;
781   }
782 }
783
784 static void
785 unix_plugin_select_write (struct Plugin * plugin)
786 {
787   int sent = 0;
788   struct UNIXMessageWrapper * msgw = plugin->msg_head;
789
790   sent = unix_real_send (plugin,
791                          plugin->unix_sock.desc,
792                          &msgw->session->target,
793                          (const char *) msgw->msg,
794                          msgw->msgsize,
795                          msgw->priority,
796                          msgw->timeout,
797                          msgw->session->addr,
798                          msgw->session->addrlen,
799                          msgw->cont, msgw->cont_cls);
800
801   /* successfully sent bytes */
802   if (sent > 0)
803   {
804     GNUNET_CONTAINER_DLL_remove(plugin->msg_head, plugin->msg_tail, msgw);
805
806     GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
807     plugin->bytes_in_queue -= msgw->msgsize;
808     GNUNET_STATISTICS_set (plugin->env->stats,"# UNIX bytes in send queue",
809         plugin->bytes_in_queue, GNUNET_NO);
810
811     GNUNET_free (msgw->msg);
812     GNUNET_free (msgw);
813     return;
814   }
815
816   /* failed and no retry */
817   if (sent == -1)
818   {
819     GNUNET_CONTAINER_DLL_remove(plugin->msg_head, plugin->msg_tail, msgw);
820
821     GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
822     plugin->bytes_in_queue -= msgw->msgsize;
823     GNUNET_STATISTICS_set (plugin->env->stats,"# UNIX bytes in send queue",
824         plugin->bytes_in_queue, GNUNET_NO);
825
826     GNUNET_free (msgw->msg);
827     GNUNET_free (msgw);
828     return;
829   }
830
831   /* failed and retry */
832   if (sent == 0)
833     return;
834 }
835
836 /*
837  * @param cls the plugin handle
838  * @param tc the scheduling context (for rescheduling this function again)
839  *
840  * We have been notified that our writeset has something to read.  We don't
841  * know which socket needs to be read, so we have to check each one
842  * Then reschedule this function to be called again once more is available.
843  *
844  */
845 static void
846 unix_plugin_select (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
847 {
848   struct Plugin *plugin = cls;
849
850   plugin->select_task = GNUNET_SCHEDULER_NO_TASK;
851   if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
852     return;
853
854   plugin->with_ws = GNUNET_NO;
855   if ((tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0)
856   {
857     GNUNET_assert (GNUNET_NETWORK_fdset_isset
858                    (tc->write_ready, plugin->unix_sock.desc));
859     if (plugin->msg_head != NULL)
860       unix_plugin_select_write (plugin);
861   }
862
863   if ((tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) != 0)
864   {
865     GNUNET_assert (GNUNET_NETWORK_fdset_isset
866                    (tc->read_ready, plugin->unix_sock.desc));
867     unix_plugin_select_read (plugin);
868   }
869
870   if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK)
871     GNUNET_SCHEDULER_cancel (plugin->select_task);
872   plugin->select_task =
873       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
874                                    GNUNET_TIME_UNIT_FOREVER_REL,
875                                    plugin->rs,
876                                    (plugin->msg_head != NULL) ? plugin->ws : NULL,
877                                    &unix_plugin_select, plugin);
878   if (plugin->msg_head != NULL)
879     plugin->with_ws = GNUNET_YES;
880 }
881
882 /**
883  * Create a slew of UNIX sockets.  If possible, use IPv6 and IPv4.
884  *
885  * @param cls closure for server start, should be a struct Plugin *
886  * @return number of sockets created or GNUNET_SYSERR on error
887 */
888 static int
889 unix_transport_server_start (void *cls)
890 {
891   struct Plugin *plugin = cls;
892   struct sockaddr *serverAddr;
893   socklen_t addrlen;
894   struct sockaddr_un un;
895   size_t slen;
896
897   memset (&un, 0, sizeof (un));
898   un.sun_family = AF_UNIX;
899   slen = strlen (plugin->unix_socket_path) + 1;
900   if (slen >= sizeof (un.sun_path))
901     slen = sizeof (un.sun_path) - 1;
902
903   memcpy (un.sun_path, plugin->unix_socket_path, slen);
904   un.sun_path[slen] = '\0';
905   slen = sizeof (struct sockaddr_un);
906 #if HAVE_SOCKADDR_IN_SIN_LEN
907   un.sun_len = (u_char) slen;
908 #endif
909
910   serverAddr = (struct sockaddr *) &un;
911   addrlen = slen;
912 #if LINUX
913   un.sun_path[0] = '\0';
914 #endif
915   plugin->ats_network = plugin->env->get_address_type (plugin->env->cls, serverAddr, addrlen);
916   plugin->unix_sock.desc =
917       GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_DGRAM, 0);
918   if (NULL == plugin->unix_sock.desc)
919   {
920     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
921     return GNUNET_SYSERR;
922   }
923   if (GNUNET_NETWORK_socket_bind (plugin->unix_sock.desc, serverAddr, addrlen)
924       != GNUNET_OK)
925   {
926     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
927     GNUNET_NETWORK_socket_close (plugin->unix_sock.desc);
928     plugin->unix_sock.desc = NULL;
929     return GNUNET_SYSERR;
930   }
931 #if DEBUG_UNIX
932   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "unix", "Bound to `%s'\n",
933                    &un.sun_path[0]);
934 #endif
935   plugin->rs = GNUNET_NETWORK_fdset_create ();
936   plugin->ws = GNUNET_NETWORK_fdset_create ();
937   GNUNET_NETWORK_fdset_zero (plugin->rs);
938   GNUNET_NETWORK_fdset_zero (plugin->ws);
939   GNUNET_NETWORK_fdset_set (plugin->rs, plugin->unix_sock.desc);
940   GNUNET_NETWORK_fdset_set (plugin->ws, plugin->unix_sock.desc);
941
942   plugin->select_task =
943       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
944                                    GNUNET_TIME_UNIT_FOREVER_REL,
945                                    plugin->rs,
946                                    NULL,
947                                    &unix_plugin_select, plugin);
948   plugin->with_ws = GNUNET_NO;
949
950   return 1;
951 }
952
953
954 /**
955  * Function that will be called to check if a binary address for this
956  * plugin is well-formed and corresponds to an address for THIS peer
957  * (as per our configuration).  Naturally, if absolutely necessary,
958  * plugins can be a bit conservative in their answer, but in general
959  * plugins should make sure that the address does not redirect
960  * traffic to a 3rd party that might try to man-in-the-middle our
961  * traffic.
962  *
963  * @param cls closure, should be our handle to the Plugin
964  * @param addr pointer to the address
965  * @param addrlen length of addr
966  * @return GNUNET_OK if this is a plausible address for this peer
967  *         and transport, GNUNET_SYSERR if not
968  *
969  */
970 static int
971 unix_check_address (void *cls, const void *addr, size_t addrlen)
972 {
973
974 #if DEBUG_UNIX
975   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976               "Informing transport service about my address `%s'\n",
977               (char *) addr);
978 #endif
979   return GNUNET_OK;
980 }
981
982
983 /**
984  * Convert the transports address to a nice, human-readable
985  * format.
986  *
987  * @param cls closure
988  * @param type name of the transport that generated the address
989  * @param addr one of the addresses of the host, NULL for the last address
990  *        the specific address format depends on the transport
991  * @param addrlen length of the address
992  * @param numeric should (IP) addresses be displayed in numeric form?
993  * @param timeout after how long should we give up?
994  * @param asc function to call on each string
995  * @param asc_cls closure for asc
996  */
997 static void
998 unix_plugin_address_pretty_printer (void *cls, const char *type,
999                                     const void *addr, size_t addrlen,
1000                                     int numeric,
1001                                     struct GNUNET_TIME_Relative timeout,
1002                                     GNUNET_TRANSPORT_AddressStringCallback asc,
1003                                     void *asc_cls)
1004 {
1005   if ((addr != NULL) && (addrlen > 0))
1006     asc (asc_cls, (const char *) addr);
1007   else
1008   {
1009     GNUNET_break (0);
1010     asc (asc_cls, "Invalid UNIX address");
1011   }
1012
1013 }
1014
1015 /**
1016  * Function called to convert a string address to
1017  * a binary address.
1018  *
1019  * @param cls closure ('struct Plugin*')
1020  * @param addr string address
1021  * @param addrlen length of the address
1022  * @param buf location to store the buffer
1023  *        If the function returns GNUNET_SYSERR, its contents are undefined.
1024  * @param added length of created address
1025  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1026  */
1027 int
1028 unix_string_to_address (void *cls, const char *addr, uint16_t addrlen,
1029     void **buf, size_t *added)
1030 {
1031   if ((NULL == addr) || (addrlen == 0))
1032   {
1033     GNUNET_break (0);
1034     return GNUNET_SYSERR;
1035   }
1036
1037   char * tmp = GNUNET_malloc (addrlen + 1);
1038   memcpy (tmp, addr, addrlen);
1039   tmp[addrlen] = '\0';
1040
1041   //GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "`%s'\n", tmp);
1042
1043   (*buf) = tmp;
1044   (*added) = strlen (tmp) + 1;
1045   return GNUNET_OK;
1046 }
1047
1048
1049
1050
1051 /**
1052  * Function called for a quick conversion of the binary address to
1053  * a numeric address.  Note that the caller must not free the
1054  * address and that the next call to this function is allowed
1055  * to override the address again.
1056  *
1057  * @param cls closure
1058  * @param addr binary address
1059  * @param addrlen length of the address
1060  * @return string representing the same address
1061  */
1062 static const char *
1063 unix_address_to_string (void *cls, const void *addr, size_t addrlen)
1064 {
1065   if ((addr != NULL) && (addrlen > 0))
1066     return (const char *) addr;
1067   else
1068     return NULL;
1069 }
1070
1071 /**
1072  * Notify transport service about address
1073  *
1074  * @param cls the plugin
1075  * @param tc unused
1076  */
1077 static void
1078 address_notification (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1079 {
1080   struct Plugin *plugin = cls;
1081
1082   plugin->env->notify_address (plugin->env->cls, GNUNET_YES,
1083                                plugin->unix_socket_path,
1084                                strlen (plugin->unix_socket_path) + 1);
1085 }
1086
1087 /**
1088  * The exported method. Makes the core api available via a global and
1089  * returns the unix transport API.
1090  */
1091 void *
1092 libgnunet_plugin_transport_unix_init (void *cls)
1093 {
1094   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1095   unsigned long long port;
1096   struct GNUNET_TRANSPORT_PluginFunctions *api;
1097   struct Plugin *plugin;
1098   int sockets_created;
1099
1100   if (NULL == env->receive)
1101   {
1102     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
1103        initialze the plugin or the API */
1104     api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1105     api->cls = NULL;
1106     api->address_pretty_printer = &unix_plugin_address_pretty_printer;
1107     api->address_to_string = &unix_address_to_string;
1108     api->string_to_address = &unix_string_to_address;
1109     return api;
1110   }
1111   GNUNET_assert( NULL != env->stats);
1112
1113   if (GNUNET_OK !=
1114       GNUNET_CONFIGURATION_get_value_number (env->cfg, "transport-unix", "PORT",
1115                                              &port))
1116     port = UNIX_NAT_DEFAULT_PORT;
1117   plugin = GNUNET_malloc (sizeof (struct Plugin));
1118   plugin->port = port;
1119   plugin->env = env;
1120   GNUNET_asprintf (&plugin->unix_socket_path, "/tmp/unix-plugin-sock.%d",
1121                    plugin->port);
1122
1123   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1124   api->cls = plugin;
1125
1126   api->get_session = &unix_plugin_get_session;
1127   api->send = &unix_plugin_send;
1128   api->disconnect = &unix_disconnect;
1129   api->address_pretty_printer = &unix_plugin_address_pretty_printer;
1130   api->address_to_string = &unix_address_to_string;
1131   api->check_address = &unix_check_address;
1132   api->string_to_address = &unix_string_to_address;
1133   sockets_created = unix_transport_server_start (plugin);
1134   if (sockets_created == 0)
1135     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to open UNIX sockets\n"));
1136
1137   plugin->session_map = GNUNET_CONTAINER_multihashmap_create(10);
1138
1139   GNUNET_SCHEDULER_add_now (address_notification, plugin);
1140   return api;
1141 }
1142
1143 void *
1144 libgnunet_plugin_transport_unix_done (void *cls)
1145 {
1146   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1147   struct Plugin *plugin = api->cls;
1148
1149   if (NULL == plugin)
1150   {
1151     GNUNET_free (api);
1152     return NULL;
1153   }
1154   unix_transport_server_stop (plugin);
1155
1156   GNUNET_CONTAINER_multihashmap_iterate (plugin->session_map, &get_session_delete_it, plugin);
1157   GNUNET_CONTAINER_multihashmap_destroy (plugin->session_map);
1158
1159   GNUNET_NETWORK_fdset_destroy (plugin->rs);
1160   GNUNET_NETWORK_fdset_destroy (plugin->ws);
1161   GNUNET_free (plugin->unix_socket_path);
1162   GNUNET_free (plugin);
1163   GNUNET_free (api);
1164   return NULL;
1165 }
1166
1167 /* end of plugin_transport_unix.c */