glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / transport / plugin_transport_unix.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2010-2014 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15
16 /**
17  * @file transport/plugin_transport_unix.c
18  * @brief Transport plugin using unix domain sockets (!)
19  *        Clearly, can only be used locally on Unix/Linux hosts...
20  *        ONLY INTENDED FOR TESTING!!!
21  * @author Christian Grothoff
22  * @author Nathan Evans
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_hello_lib.h"
27 #include "gnunet_protocols.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_transport_service.h"
30 #include "gnunet_transport_plugin.h"
31 #include "transport.h"
32
33
34 /**
35  * Return code we give on 'send' if we failed to send right now
36  * but it makes sense to retry later. (Note: we might want to
37  * move this to the plugin API!?).
38  */
39 #define RETRY 0
40
41 /**
42  * Name of the plugin.
43  */
44 #define PLUGIN_NAME "unix"
45
46 /**
47  * Options for UNIX Domain addresses.
48  */
49 enum UNIX_ADDRESS_OPTIONS
50 {
51   /**
52    * No special options.
53    */
54   UNIX_OPTIONS_NONE = 0,
55
56   /**
57    * Linux abstract domain sockets should be used.
58    */
59   UNIX_OPTIONS_USE_ABSTRACT_SOCKETS = 1
60 };
61
62
63 /**
64  * How long until we give up on transmitting the welcome message?
65  */
66 #define HOSTNAME_RESOLVE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
67
68 #define LOG(kind,...) GNUNET_log_from (kind, "transport-unix",__VA_ARGS__)
69
70
71 GNUNET_NETWORK_STRUCT_BEGIN
72
73 /**
74  * Binary format for an UNIX Domain Socket address in GNUnet.
75  */
76 struct UnixAddress
77 {
78   /**
79    * Options to use for the address, in NBO
80    */
81   uint32_t options GNUNET_PACKED;
82
83   /**
84    * Length of the address (path length), in NBO
85    */
86   uint32_t addrlen GNUNET_PACKED;
87
88   /* followed by actual path */
89 };
90
91
92 /**
93  * UNIX Message-Packet header.
94  */
95 struct UNIXMessage
96 {
97   /**
98    * Message header.
99    */
100   struct GNUNET_MessageHeader header;
101
102   /**
103    * What is the identity of the sender (GNUNET_hash of public key)
104    */
105   struct GNUNET_PeerIdentity sender;
106
107 };
108
109 GNUNET_NETWORK_STRUCT_END
110
111
112 /**
113  * Information we track for a message awaiting transmission.
114  */
115 struct UNIXMessageWrapper
116 {
117   /**
118    * We keep messages in a doubly linked list.
119    */
120   struct UNIXMessageWrapper *next;
121
122   /**
123    * We keep messages in a doubly linked list.
124    */
125   struct UNIXMessageWrapper *prev;
126
127   /**
128    * The actual payload (allocated separately right now).
129    */
130   struct UNIXMessage *msg;
131
132   /**
133    * Session this message belongs to.
134    */
135   struct GNUNET_ATS_Session *session;
136
137   /**
138    * Function to call upon transmission.
139    */
140   GNUNET_TRANSPORT_TransmitContinuation cont;
141
142   /**
143    * Closure for @e cont.
144    */
145   void *cont_cls;
146
147   /**
148    * Timeout for this message.
149    */
150   struct GNUNET_TIME_Absolute timeout;
151
152   /**
153    * Number of bytes in @e msg.
154    */
155   size_t msgsize;
156
157   /**
158    * Number of bytes of payload encapsulated in @e msg.
159    */
160   size_t payload;
161
162   /**
163    * Priority of the message (ignored, just dragged along in UNIX).
164    */
165   unsigned int priority;
166 };
167
168
169 /**
170  * Handle for a session.
171  */
172 struct GNUNET_ATS_Session
173 {
174
175   /**
176    * Sessions with pending messages (!) are kept in a DLL.
177    */
178   struct GNUNET_ATS_Session *next;
179
180   /**
181    * Sessions with pending messages (!) are kept in a DLL.
182    */
183   struct GNUNET_ATS_Session *prev;
184
185   /**
186    * To whom are we talking to (set to our identity
187    * if we are still waiting for the welcome message).
188    *
189    * FIXME: information duplicated with 'peer' in address!
190    */
191   struct GNUNET_PeerIdentity target;
192
193   /**
194    * Pointer to the global plugin struct.
195    */
196   struct Plugin *plugin;
197
198   /**
199    * Address of the other peer.
200    */
201   struct GNUNET_HELLO_Address *address;
202
203   /**
204    * Number of bytes we currently have in our write queue.
205    */
206   unsigned long long bytes_in_queue;
207
208   /**
209    * Timeout for this session.
210    */
211   struct GNUNET_TIME_Absolute timeout;
212
213   /**
214    * Session timeout task.
215    */
216   struct GNUNET_SCHEDULER_Task * timeout_task;
217
218   /**
219    * Number of messages we currently have in our write queue.
220    */
221   unsigned int msgs_in_queue;
222
223 };
224
225
226 /**
227  * Encapsulation of all of the state of the plugin.
228  */
229 struct Plugin;
230
231
232 /**
233  * Information we keep for each of our listen sockets.
234  */
235 struct UNIX_Sock_Info
236 {
237   /**
238    * The network handle
239    */
240   struct GNUNET_NETWORK_Handle *desc;
241 };
242
243
244 /**
245  * Encapsulation of all of the state of the plugin.
246  */
247 struct Plugin
248 {
249
250   /**
251    * ID of task used to update our addresses when one expires.
252    */
253   struct GNUNET_SCHEDULER_Task * address_update_task;
254
255   /**
256    * ID of read task
257    */
258   struct GNUNET_SCHEDULER_Task * read_task;
259
260   /**
261    * ID of write task
262    */
263   struct GNUNET_SCHEDULER_Task * write_task;
264
265   /**
266    * Number of bytes we currently have in our write queues.
267    */
268   unsigned long long bytes_in_queue;
269
270   /**
271    * Our environment.
272    */
273   struct GNUNET_TRANSPORT_PluginEnvironment *env;
274
275   /**
276    * Sessions (map from peer identity to `struct GNUNET_ATS_Session`)
277    */
278   struct GNUNET_CONTAINER_MultiPeerMap *session_map;
279
280   /**
281    * Head of queue of messages to transmit.
282    */
283   struct UNIXMessageWrapper *msg_head;
284
285   /**
286    * Tail of queue of messages to transmit.
287    */
288   struct UNIXMessageWrapper *msg_tail;
289
290   /**
291    * Path of our unix domain socket (/tmp/unix-plugin)
292    */
293   char *unix_socket_path;
294
295   /**
296    * Function to call about session status changes.
297    */
298   GNUNET_TRANSPORT_SessionInfoCallback sic;
299
300   /**
301    * Closure for @e sic.
302    */
303   void *sic_cls;
304
305   /**
306    * socket that we transmit all data with
307    */
308   struct UNIX_Sock_Info unix_sock;
309
310   /**
311    * Address options in HBO
312    */
313   uint32_t myoptions;
314
315   /**
316    * Are we using an abstract UNIX domain socket?
317    */
318   int is_abstract;
319
320 };
321
322
323 /**
324  * If a session monitor is attached, notify it about the new
325  * session state.
326  *
327  * @param plugin our plugin
328  * @param session session that changed state
329  * @param state new state of the session
330  */
331 static void
332 notify_session_monitor (struct Plugin *plugin,
333                         struct GNUNET_ATS_Session *session,
334                         enum GNUNET_TRANSPORT_SessionState state)
335 {
336   struct GNUNET_TRANSPORT_SessionInfo info;
337
338   if (NULL == plugin->sic)
339     return;
340   memset (&info, 0, sizeof (info));
341   info.state = state;
342   info.is_inbound = GNUNET_SYSERR; /* hard to say */
343   info.num_msg_pending = session->msgs_in_queue;
344   info.num_bytes_pending = session->bytes_in_queue;
345   /* info.receive_delay remains zero as this is not supported by UNIX
346      (cannot selectively not receive from 'some' peer while continuing
347      to receive from others) */
348   info.session_timeout = session->timeout;
349   info.address = session->address;
350   plugin->sic (plugin->sic_cls,
351                session,
352                &info);
353 }
354
355
356 /**
357  * Function called for a quick conversion of the binary address to
358  * a numeric address.  Note that the caller must not free the
359  * address and that the next call to this function is allowed
360  * to override the address again.
361  *
362  * @param cls closure
363  * @param addr binary address
364  * @param addrlen length of the @a addr
365  * @return string representing the same address
366  */
367 static const char *
368 unix_plugin_address_to_string (void *cls,
369                                const void *addr,
370                                size_t addrlen)
371 {
372   static char rbuf[1024];
373   struct UnixAddress *ua = (struct UnixAddress *) addr;
374   char *addrstr;
375   size_t addr_str_len;
376   unsigned int off;
377
378   if ((NULL == addr) || (sizeof (struct UnixAddress) > addrlen))
379   {
380     GNUNET_break(0);
381     return NULL;
382   }
383   addrstr = (char *) &ua[1];
384   addr_str_len = ntohl (ua->addrlen);
385
386   if (addr_str_len != addrlen - sizeof(struct UnixAddress))
387   {
388     GNUNET_break(0);
389     return NULL;
390   }
391   if ('\0' != addrstr[addr_str_len - 1])
392   {
393     GNUNET_break(0);
394     return NULL;
395   }
396   if (strlen (addrstr) + 1 != addr_str_len)
397   {
398     GNUNET_break(0);
399     return NULL;
400   }
401
402   off = 0;
403   if ('\0' == addrstr[0])
404     off++;
405   memset (rbuf, 0, sizeof (rbuf));
406   GNUNET_snprintf (rbuf,
407                    sizeof (rbuf) - 1,
408                    "%s.%u.%s%.*s",
409                    PLUGIN_NAME,
410                    ntohl (ua->options),
411                    (off == 1) ? "@" : "",
412                    (int) (addr_str_len - off),
413                    &addrstr[off]);
414   return rbuf;
415 }
416
417
418 /**
419  * Functions with this signature are called whenever we need
420  * to close a session due to a disconnect or failure to
421  * establish a connection.
422  *
423  * @param cls closure with the `struct Plugin *`
424  * @param session session to close down
425  * @return #GNUNET_OK on success
426  */
427 static int
428 unix_plugin_session_disconnect (void *cls,
429                                 struct GNUNET_ATS_Session *session)
430 {
431   struct Plugin *plugin = cls;
432   struct UNIXMessageWrapper *msgw;
433   struct UNIXMessageWrapper *next;
434
435   LOG (GNUNET_ERROR_TYPE_DEBUG,
436        "Disconnecting session for peer `%s' `%s'\n",
437        GNUNET_i2s (&session->target),
438        unix_plugin_address_to_string (NULL,
439                                       session->address->address,
440                                       session->address->address_length));
441   plugin->env->session_end (plugin->env->cls,
442                             session->address,
443                             session);
444   next = plugin->msg_head;
445   while (NULL != next)
446   {
447     msgw = next;
448     next = msgw->next;
449     if (msgw->session != session)
450       continue;
451     GNUNET_CONTAINER_DLL_remove (plugin->msg_head,
452                                  plugin->msg_tail,
453                                  msgw);
454     session->msgs_in_queue--;
455     GNUNET_assert (session->bytes_in_queue >= msgw->msgsize);
456     session->bytes_in_queue -= msgw->msgsize;
457     GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
458     plugin->bytes_in_queue -= msgw->msgsize;
459     if (NULL != msgw->cont)
460       msgw->cont (msgw->cont_cls,
461                   &msgw->session->target,
462                   GNUNET_SYSERR,
463                   msgw->payload, 0);
464     GNUNET_free (msgw->msg);
465     GNUNET_free (msgw);
466   }
467   GNUNET_assert (GNUNET_YES ==
468                  GNUNET_CONTAINER_multipeermap_remove (plugin->session_map,
469                                                        &session->target,
470                                                        session));
471   GNUNET_STATISTICS_set (plugin->env->stats,
472                          "# UNIX sessions active",
473                          GNUNET_CONTAINER_multipeermap_size (plugin->session_map),
474                          GNUNET_NO);
475   if (NULL != session->timeout_task)
476   {
477     GNUNET_SCHEDULER_cancel (session->timeout_task);
478     session->timeout_task = NULL;
479     session->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
480   }
481   notify_session_monitor (plugin,
482                           session,
483                           GNUNET_TRANSPORT_SS_DONE);
484   GNUNET_HELLO_address_free (session->address);
485   GNUNET_break (0 == session->bytes_in_queue);
486   GNUNET_break (0 == session->msgs_in_queue);
487   GNUNET_free (session);
488   return GNUNET_OK;
489 }
490
491
492 /**
493  * Session was idle for too long, so disconnect it
494  *
495  * @param cls the `struct GNUNET_ATS_Session *` to disconnect
496  */
497 static void
498 session_timeout (void *cls)
499 {
500   struct GNUNET_ATS_Session *session = cls;
501   struct GNUNET_TIME_Relative left;
502
503   session->timeout_task = NULL;
504   left = GNUNET_TIME_absolute_get_remaining (session->timeout);
505   if (0 != left.rel_value_us)
506   {
507     /* not actually our turn yet, but let's at least update
508        the monitor, it may think we're about to die ... */
509     notify_session_monitor (session->plugin,
510                             session,
511                             GNUNET_TRANSPORT_SS_UPDATE);
512     session->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
513                                                           &session_timeout,
514                                                           session);
515     return;
516   }
517   LOG (GNUNET_ERROR_TYPE_DEBUG,
518        "Session %p was idle for %s, disconnecting\n",
519        session,
520        GNUNET_STRINGS_relative_time_to_string (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
521                                                GNUNET_YES));
522   unix_plugin_session_disconnect (session->plugin, session);
523 }
524
525
526 /**
527  * Increment session timeout due to activity.  We do not immediately
528  * notify the monitor here as that might generate excessive
529  * signalling.
530  *
531  * @param session session for which the timeout should be rescheduled
532  */
533 static void
534 reschedule_session_timeout (struct GNUNET_ATS_Session *session)
535 {
536   GNUNET_assert (NULL != session->timeout_task);
537   session->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
538 }
539
540
541 /**
542  * Convert unix path to a `struct sockaddr_un *`
543  *
544  * @param unixpath path to convert
545  * @param[out] sock_len set to the length of the address
546  * @return converted unix path
547  */
548 static struct sockaddr_un *
549 unix_address_to_sockaddr (const char *unixpath,
550                           socklen_t *sock_len)
551 {
552   struct sockaddr_un *un;
553   size_t slen;
554
555   GNUNET_assert (0 < strlen (unixpath));        /* sanity check */
556   un = GNUNET_new (struct sockaddr_un);
557   un->sun_family = AF_UNIX;
558   slen = strlen (unixpath);
559   if (slen >= sizeof (un->sun_path))
560     slen = sizeof (un->sun_path) - 1;
561   GNUNET_memcpy (un->sun_path, unixpath, slen);
562   un->sun_path[slen] = '\0';
563   slen = sizeof (struct sockaddr_un);
564 #if HAVE_SOCKADDR_UN_SUN_LEN
565   un->sun_len = (u_char) slen;
566 #endif
567   (*sock_len) = slen;
568   return un;
569 }
570
571
572 /**
573  * Closure to #lookup_session_it().
574  */
575 struct LookupCtx
576 {
577   /**
578    * Location to store the session, if found.
579    */
580   struct GNUNET_ATS_Session *res;
581
582   /**
583    * Address we are looking for.
584    */
585   const struct GNUNET_HELLO_Address *address;
586 };
587
588
589 /**
590  * Function called to find a session by address.
591  *
592  * @param cls the `struct LookupCtx *`
593  * @param key peer we are looking for (unused)
594  * @param value a session
595  * @return #GNUNET_YES if not found (continue looking), #GNUNET_NO on success
596  */
597 static int
598 lookup_session_it (void *cls,
599                    const struct GNUNET_PeerIdentity * key,
600                    void *value)
601 {
602   struct LookupCtx *lctx = cls;
603   struct GNUNET_ATS_Session *session = value;
604
605   if (0 == GNUNET_HELLO_address_cmp (lctx->address,
606                                      session->address))
607   {
608     lctx->res = session;
609     return GNUNET_NO;
610   }
611   return GNUNET_YES;
612 }
613
614
615 /**
616  * Find an existing session by address.
617  *
618  * @param plugin the plugin
619  * @param address the address to find
620  * @return NULL if session was not found
621  */
622 static struct GNUNET_ATS_Session *
623 lookup_session (struct Plugin *plugin,
624                 const struct GNUNET_HELLO_Address *address)
625 {
626   struct LookupCtx lctx;
627
628   lctx.address = address;
629   lctx.res = NULL;
630   GNUNET_CONTAINER_multipeermap_get_multiple (plugin->session_map,
631                                               &address->peer,
632                                               &lookup_session_it, &lctx);
633   return lctx.res;
634 }
635
636
637 /**
638  * Function that is called to get the keepalive factor.
639  * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
640  * calculate the interval between keepalive packets.
641  *
642  * @param cls closure with the `struct Plugin`
643  * @return keepalive factor
644  */
645 static unsigned int
646 unix_plugin_query_keepalive_factor (void *cls)
647 {
648   return 3;
649 }
650
651
652 /**
653  * Actually send out the message, assume we've got the address and
654  * send_handle squared away!
655  *
656  * @param cls closure
657  * @param send_handle which handle to send message on
658  * @param target who should receive this message (ignored by UNIX)
659  * @param msgbuf one or more GNUNET_MessageHeader(s) strung together
660  * @param msgbuf_size the size of the @a msgbuf to send
661  * @param priority how important is the message (ignored by UNIX)
662  * @param timeout when should we time out (give up) if we can not transmit?
663  * @param addr the addr to send the message to, needs to be a sockaddr for us
664  * @param addrlen the len of @a addr
665  * @param payload bytes payload to send
666  * @param cont continuation to call once the message has
667  *        been transmitted (or if the transport is ready
668  *        for the next transmission call; or if the
669  *        peer disconnected...)
670  * @param cont_cls closure for @a cont
671  * @return on success the number of bytes written, RETRY for retry, -1 on errors
672  */
673 static ssize_t
674 unix_real_send (void *cls,
675                 struct GNUNET_NETWORK_Handle *send_handle,
676                 const struct GNUNET_PeerIdentity *target,
677                 const char *msgbuf,
678                 size_t msgbuf_size,
679                 unsigned int priority,
680                 struct GNUNET_TIME_Absolute timeout,
681                 const struct UnixAddress *addr,
682                 size_t addrlen,
683                 size_t payload,
684                 GNUNET_TRANSPORT_TransmitContinuation cont,
685                 void *cont_cls)
686 {
687   struct Plugin *plugin = cls;
688   ssize_t sent;
689   struct sockaddr_un *un;
690   socklen_t un_len;
691   const char *unixpath;
692
693   if (NULL == send_handle)
694   {
695     GNUNET_break (0); /* We do not have a send handle */
696     return GNUNET_SYSERR;
697   }
698   if ((NULL == addr) || (0 == addrlen))
699   {
700     GNUNET_break (0); /* Can never send if we don't have an address */
701     return GNUNET_SYSERR;
702   }
703
704   /* Prepare address */
705   unixpath = (const char *)  &addr[1];
706   if (NULL == (un = unix_address_to_sockaddr (unixpath,
707                                               &un_len)))
708   {
709     GNUNET_break (0);
710     return -1;
711   }
712
713   if ((GNUNET_YES == plugin->is_abstract) &&
714       (0 != (UNIX_OPTIONS_USE_ABSTRACT_SOCKETS & ntohl(addr->options) )) )
715   {
716     un->sun_path[0] = '\0';
717   }
718 resend:
719   /* Send the data */
720   sent = GNUNET_NETWORK_socket_sendto (send_handle,
721                                        msgbuf,
722                                        msgbuf_size,
723                                        (const struct sockaddr *) un,
724                                        un_len);
725   if (GNUNET_SYSERR == sent)
726   {
727     if ( (EAGAIN == errno) ||
728          (ENOBUFS == errno) )
729     {
730       GNUNET_free (un);
731       return RETRY; /* We have to retry later  */
732     }
733     if (EMSGSIZE == errno)
734     {
735       socklen_t size = 0;
736       socklen_t len = sizeof (size);
737
738       GNUNET_NETWORK_socket_getsockopt ((struct GNUNET_NETWORK_Handle *)
739                                         send_handle, SOL_SOCKET, SO_SNDBUF, &size,
740                                         &len);
741       if (size < msgbuf_size)
742       {
743         LOG (GNUNET_ERROR_TYPE_DEBUG,
744              "Trying to increase socket buffer size from %u to %u for message size %u\n",
745              (unsigned int) size,
746              (unsigned int) ((msgbuf_size / 1000) + 2) * 1000,
747              (unsigned int) msgbuf_size);
748         size = ((msgbuf_size / 1000) + 2) * 1000;
749         if (GNUNET_OK ==
750             GNUNET_NETWORK_socket_setsockopt ((struct GNUNET_NETWORK_Handle *) send_handle,
751                                               SOL_SOCKET, SO_SNDBUF,
752                                               &size, sizeof (size)))
753           goto resend; /* Increased buffer size, retry sending */
754         else
755         {
756           /* Could not increase buffer size: error, no retry */
757           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "setsockopt");
758           GNUNET_free (un);
759           return GNUNET_SYSERR;
760         }
761       }
762       else
763       {
764         /* Buffer is bigger than message:  error, no retry
765          * This should never happen!*/
766         GNUNET_break (0);
767         GNUNET_free (un);
768         return GNUNET_SYSERR;
769       }
770     }
771   }
772
773   LOG (GNUNET_ERROR_TYPE_DEBUG,
774        "UNIX transmitted %u-byte message to %s (%d: %s)\n",
775        (unsigned int) msgbuf_size,
776        GNUNET_a2s ((const struct sockaddr *)un, un_len),
777        (int) sent,
778        (sent < 0) ? STRERROR (errno) : "ok");
779   GNUNET_free (un);
780   return sent;
781 }
782
783
784 /**
785  * Function obtain the network type for a session
786  *
787  * @param cls closure ('struct Plugin*')
788  * @param session the session
789  * @return the network type in HBO or #GNUNET_SYSERR
790  */
791 static enum GNUNET_ATS_Network_Type
792 unix_plugin_get_network (void *cls,
793                          struct GNUNET_ATS_Session *session)
794 {
795   GNUNET_assert (NULL != session);
796   return GNUNET_ATS_NET_LOOPBACK;
797 }
798
799
800 /**
801  * Function obtain the network type for a session
802  *
803  * @param cls closure (`struct Plugin *`)
804  * @param address the address
805  * @return the network type
806  */
807 static enum GNUNET_ATS_Network_Type
808 unix_plugin_get_network_for_address (void *cls,
809                                      const struct GNUNET_HELLO_Address *address)
810
811 {
812   return GNUNET_ATS_NET_LOOPBACK;
813 }
814
815
816 /**
817  * Creates a new outbound session the transport service will use to send data to the
818  * peer
819  *
820  * @param cls the plugin
821  * @param address the address
822  * @return the session or NULL of max connections exceeded
823  */
824 static struct GNUNET_ATS_Session *
825 unix_plugin_get_session (void *cls,
826                          const struct GNUNET_HELLO_Address *address)
827 {
828   struct Plugin *plugin = cls;
829   struct GNUNET_ATS_Session *session;
830   struct UnixAddress *ua;
831   char * addrstr;
832   uint32_t addr_str_len;
833   uint32_t addr_option;
834
835   ua = (struct UnixAddress *) address->address;
836   if ((NULL == address->address) || (0 == address->address_length) ||
837                 (sizeof (struct UnixAddress) > address->address_length))
838   {
839     GNUNET_break (0);
840     return NULL;
841   }
842   addrstr = (char *) &ua[1];
843   addr_str_len = ntohl (ua->addrlen);
844   addr_option = ntohl (ua->options);
845
846   if ( (0 != (UNIX_OPTIONS_USE_ABSTRACT_SOCKETS & addr_option)) &&
847     (GNUNET_NO == plugin->is_abstract))
848   {
849     return NULL;
850   }
851
852   if (addr_str_len != address->address_length - sizeof (struct UnixAddress))
853   {
854     return NULL; /* This can be a legacy address */
855   }
856
857   if ('\0' != addrstr[addr_str_len - 1])
858   {
859     GNUNET_break (0);
860     return NULL;
861   }
862   if (strlen (addrstr) + 1 != addr_str_len)
863   {
864     GNUNET_break (0);
865     return NULL;
866   }
867
868   /* Check if a session for this address already exists */
869   if (NULL != (session = lookup_session (plugin,
870                                          address)))
871     {
872     LOG (GNUNET_ERROR_TYPE_DEBUG,
873          "Found existing session %p for address `%s'\n",
874          session,
875          unix_plugin_address_to_string (NULL,
876                                         address->address,
877                                         address->address_length));
878     return session;
879   }
880
881   /* create a new session */
882   session = GNUNET_new (struct GNUNET_ATS_Session);
883   session->target = address->peer;
884   session->address = GNUNET_HELLO_address_copy (address);
885   session->plugin = plugin;
886   session->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
887   session->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
888                                                         &session_timeout,
889                                                         session);
890   LOG (GNUNET_ERROR_TYPE_DEBUG,
891        "Creating a new session %p for address `%s'\n",
892        session,
893        unix_plugin_address_to_string (NULL,
894                                       address->address,
895                                       address->address_length));
896   (void) GNUNET_CONTAINER_multipeermap_put (plugin->session_map,
897                                             &address->peer, session,
898                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
899   GNUNET_STATISTICS_set (plugin->env->stats,
900                          "# UNIX sessions active",
901                          GNUNET_CONTAINER_multipeermap_size (plugin->session_map),
902                          GNUNET_NO);
903   notify_session_monitor (plugin,
904                           session,
905                           GNUNET_TRANSPORT_SS_INIT);
906   notify_session_monitor (plugin,
907                           session,
908                           GNUNET_TRANSPORT_SS_UP);
909   return session;
910 }
911
912
913 /**
914  * Function that will be called whenever the transport service wants
915  * to notify the plugin that a session is still active and in use and
916  * therefore the session timeout for this session has to be updated
917  *
918  * @param cls closure with the `struct Plugin *`
919  * @param peer which peer was the session for
920  * @param session which session is being updated
921  */
922 static void
923 unix_plugin_update_session_timeout (void *cls,
924                                     const struct GNUNET_PeerIdentity *peer,
925                                     struct GNUNET_ATS_Session *session)
926 {
927   struct Plugin *plugin = cls;
928
929   if (GNUNET_OK !=
930       GNUNET_CONTAINER_multipeermap_contains_value (plugin->session_map,
931                                                     &session->target,
932                                                     session))
933   {
934     GNUNET_break (0);
935     return;
936   }
937   reschedule_session_timeout (session);
938 }
939
940
941 /**
942  * Demultiplexer for UNIX messages
943  *
944  * @param plugin the main plugin for this transport
945  * @param sender from which peer the message was received
946  * @param currhdr pointer to the header of the message
947  * @param ua address to look for
948  * @param ua_len length of the address @a ua
949  */
950 static void
951 unix_demultiplexer (struct Plugin *plugin,
952                     struct GNUNET_PeerIdentity *sender,
953                     const struct GNUNET_MessageHeader *currhdr,
954                     const struct UnixAddress *ua,
955                     size_t ua_len)
956 {
957   struct GNUNET_ATS_Session *session;
958   struct GNUNET_HELLO_Address *address;
959
960   GNUNET_assert (ua_len >= sizeof (struct UnixAddress));
961   LOG (GNUNET_ERROR_TYPE_DEBUG,
962        "Received message from %s\n",
963        unix_plugin_address_to_string (NULL, ua, ua_len));
964   GNUNET_STATISTICS_update (plugin->env->stats,
965                             "# bytes received via UNIX",
966                             ntohs (currhdr->size),
967                             GNUNET_NO);
968
969   /* Look for existing session */
970   address = GNUNET_HELLO_address_allocate (sender,
971                                            PLUGIN_NAME,
972                                            ua, ua_len,
973                                            GNUNET_HELLO_ADDRESS_INFO_NONE); /* UNIX does not have "inbound" sessions */
974   session = lookup_session (plugin, address);
975   if (NULL == session)
976   {
977     session = unix_plugin_get_session (plugin, address);
978     /* Notify transport and ATS about new inbound session */
979     plugin->env->session_start (NULL,
980                                 session->address,
981                                 session,
982                                 GNUNET_ATS_NET_LOOPBACK);
983   }
984   else
985   {
986     reschedule_session_timeout (session);
987   }
988   GNUNET_HELLO_address_free (address);
989   plugin->env->receive (plugin->env->cls,
990                         session->address,
991                         session,
992                         currhdr);
993 }
994
995
996 /**
997  * Read from UNIX domain socket (it is ready).
998  *
999  * @param plugin the plugin
1000  */
1001 static void
1002 unix_plugin_do_read (struct Plugin *plugin)
1003 {
1004   char buf[65536] GNUNET_ALIGN;
1005   struct UnixAddress *ua;
1006   struct UNIXMessage *msg;
1007   struct GNUNET_PeerIdentity sender;
1008   struct sockaddr_un un;
1009   socklen_t addrlen;
1010   ssize_t ret;
1011   int offset;
1012   int tsize;
1013   int is_abstract;
1014   char *msgbuf;
1015   const struct GNUNET_MessageHeader *currhdr;
1016   uint16_t csize;
1017   size_t ua_len;
1018
1019   addrlen = sizeof (un);
1020   memset (&un, 0, sizeof (un));
1021   ret = GNUNET_NETWORK_socket_recvfrom (plugin->unix_sock.desc,
1022                                         buf, sizeof (buf),
1023                                         (struct sockaddr *) &un,
1024                                         &addrlen);
1025   if ((GNUNET_SYSERR == ret) && ((errno == EAGAIN) || (errno == ENOBUFS)))
1026     return;
1027   if (GNUNET_SYSERR == ret)
1028   {
1029     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
1030                          "recvfrom");
1031     return;
1032   }
1033   else
1034   {
1035     LOG (GNUNET_ERROR_TYPE_DEBUG,
1036          "Read %d bytes from socket %s\n",
1037          (int) ret,
1038          un.sun_path);
1039   }
1040
1041   GNUNET_assert (AF_UNIX == (un.sun_family));
1042   is_abstract = GNUNET_NO;
1043   if ('\0' == un.sun_path[0])
1044   {
1045     un.sun_path[0] = '@';
1046     is_abstract = GNUNET_YES;
1047   }
1048
1049   ua_len = sizeof (struct UnixAddress) + strlen (un.sun_path) + 1;
1050   ua = GNUNET_malloc (ua_len);
1051   ua->addrlen = htonl (strlen (&un.sun_path[0]) +1);
1052   GNUNET_memcpy (&ua[1], &un.sun_path[0], strlen (un.sun_path) + 1);
1053   if (is_abstract)
1054     ua->options = htonl(UNIX_OPTIONS_USE_ABSTRACT_SOCKETS);
1055   else
1056     ua->options = htonl(UNIX_OPTIONS_NONE);
1057
1058   msg = (struct UNIXMessage *) buf;
1059   csize = ntohs (msg->header.size);
1060   if ((csize < sizeof (struct UNIXMessage)) || (csize > ret))
1061   {
1062     GNUNET_break_op (0);
1063     GNUNET_free (ua);
1064     return;
1065   }
1066   msgbuf = (char *) &msg[1];
1067   GNUNET_memcpy (&sender,
1068           &msg->sender,
1069           sizeof (struct GNUNET_PeerIdentity));
1070   offset = 0;
1071   tsize = csize - sizeof (struct UNIXMessage);
1072   while (offset + sizeof (struct GNUNET_MessageHeader) <= tsize)
1073   {
1074     currhdr = (struct GNUNET_MessageHeader *) &msgbuf[offset];
1075     csize = ntohs (currhdr->size);
1076     if ((csize < sizeof (struct GNUNET_MessageHeader)) ||
1077         (csize > tsize - offset))
1078     {
1079       GNUNET_break_op (0);
1080       break;
1081     }
1082     unix_demultiplexer (plugin, &sender, currhdr, ua, ua_len);
1083     offset += csize;
1084   }
1085   GNUNET_free (ua);
1086 }
1087
1088
1089 /**
1090  * Write to UNIX domain socket (it is ready).
1091  *
1092  * @param plugin handle to the plugin
1093  */
1094 static void
1095 unix_plugin_do_write (struct Plugin *plugin)
1096 {
1097   ssize_t sent = 0;
1098   struct UNIXMessageWrapper *msgw;
1099   struct GNUNET_ATS_Session *session;
1100   int did_delete;
1101
1102   session = NULL;
1103   did_delete = GNUNET_NO;
1104   while (NULL != (msgw = plugin->msg_head))
1105   {
1106     if (GNUNET_TIME_absolute_get_remaining (msgw->timeout).rel_value_us > 0)
1107       break; /* Message is ready for sending */
1108     /* Message has a timeout */
1109     did_delete = GNUNET_YES;
1110     LOG (GNUNET_ERROR_TYPE_DEBUG,
1111          "Timeout for message with %u bytes \n",
1112          (unsigned int) msgw->msgsize);
1113     GNUNET_CONTAINER_DLL_remove (plugin->msg_head,
1114                                  plugin->msg_tail,
1115                                  msgw);
1116     session = msgw->session;
1117     session->msgs_in_queue--;
1118     GNUNET_assert (session->bytes_in_queue >= msgw->msgsize);
1119     session->bytes_in_queue -= msgw->msgsize;
1120     GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
1121     plugin->bytes_in_queue -= msgw->msgsize;
1122     GNUNET_STATISTICS_set (plugin->env->stats,
1123                            "# bytes currently in UNIX buffers",
1124                            plugin->bytes_in_queue,
1125                            GNUNET_NO);
1126     GNUNET_STATISTICS_update (plugin->env->stats,
1127                               "# UNIX bytes discarded",
1128                               msgw->msgsize,
1129                               GNUNET_NO);
1130     if (NULL != msgw->cont)
1131       msgw->cont (msgw->cont_cls,
1132                   &msgw->session->target,
1133                   GNUNET_SYSERR,
1134                   msgw->payload,
1135                   0);
1136     GNUNET_free (msgw->msg);
1137     GNUNET_free (msgw);
1138   }
1139   if (NULL == msgw)
1140   {
1141     if (GNUNET_YES == did_delete)
1142       notify_session_monitor (plugin,
1143                               session,
1144                               GNUNET_TRANSPORT_SS_UPDATE);
1145     return; /* Nothing to send at the moment */
1146   }
1147   session = msgw->session;
1148   sent = unix_real_send (plugin,
1149                          plugin->unix_sock.desc,
1150                          &session->target,
1151                          (const char *) msgw->msg,
1152                          msgw->msgsize,
1153                          msgw->priority,
1154                          msgw->timeout,
1155                          msgw->session->address->address,
1156                          msgw->session->address->address_length,
1157                          msgw->payload,
1158                          msgw->cont, msgw->cont_cls);
1159   if (RETRY == sent)
1160   {
1161     GNUNET_STATISTICS_update (plugin->env->stats,
1162                               "# UNIX retry attempts",
1163                               1, GNUNET_NO);
1164     notify_session_monitor (plugin,
1165                             session,
1166                             GNUNET_TRANSPORT_SS_UPDATE);
1167     return;
1168   }
1169   GNUNET_CONTAINER_DLL_remove (plugin->msg_head,
1170                                plugin->msg_tail,
1171                                msgw);
1172   session->msgs_in_queue--;
1173   GNUNET_assert (session->bytes_in_queue >= msgw->msgsize);
1174   session->bytes_in_queue -= msgw->msgsize;
1175   GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
1176   plugin->bytes_in_queue -= msgw->msgsize;
1177   GNUNET_STATISTICS_set (plugin->env->stats,
1178                          "# bytes currently in UNIX buffers",
1179                          plugin->bytes_in_queue, GNUNET_NO);
1180   notify_session_monitor (plugin,
1181                           session,
1182                           GNUNET_TRANSPORT_SS_UPDATE);
1183   if (GNUNET_SYSERR == sent)
1184   {
1185     /* failed and no retry */
1186     if (NULL != msgw->cont)
1187       msgw->cont (msgw->cont_cls,
1188                   &msgw->session->target,
1189                   GNUNET_SYSERR,
1190                   msgw->payload, 0);
1191     GNUNET_STATISTICS_update (plugin->env->stats,
1192                               "# UNIX bytes discarded",
1193                               msgw->msgsize,
1194                               GNUNET_NO);
1195     GNUNET_free (msgw->msg);
1196     GNUNET_free (msgw);
1197     return;
1198   }
1199   /* successfully sent bytes */
1200   GNUNET_break (sent > 0);
1201   GNUNET_STATISTICS_update (plugin->env->stats,
1202                             "# bytes transmitted via UNIX",
1203                             msgw->msgsize,
1204                             GNUNET_NO);
1205   if (NULL != msgw->cont)
1206     msgw->cont (msgw->cont_cls,
1207                 &msgw->session->target,
1208                 GNUNET_OK,
1209                 msgw->payload,
1210                 msgw->msgsize);
1211   GNUNET_free (msgw->msg);
1212   GNUNET_free (msgw);
1213 }
1214
1215
1216 /**
1217  * We have been notified that our socket has something to read.
1218  * Then reschedule this function to be called again once more is available.
1219  *
1220  * @param cls the plugin handle
1221  */
1222 static void
1223 unix_plugin_select_read (void *cls)
1224 {
1225   struct Plugin *plugin = cls;
1226   const struct GNUNET_SCHEDULER_TaskContext *tc;
1227
1228   plugin->read_task = NULL;
1229   tc = GNUNET_SCHEDULER_get_task_context ();
1230   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
1231     unix_plugin_do_read (plugin);
1232   plugin->read_task =
1233     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1234                                    plugin->unix_sock.desc,
1235                                    &unix_plugin_select_read, plugin);
1236 }
1237
1238
1239 /**
1240  * We have been notified that our socket is ready to write.
1241  * Then reschedule this function to be called again once more is available.
1242  *
1243  * @param cls the plugin handle
1244  */
1245 static void
1246 unix_plugin_select_write (void *cls)
1247 {
1248   struct Plugin *plugin = cls;
1249   const struct GNUNET_SCHEDULER_TaskContext *tc;
1250
1251   plugin->write_task = NULL;
1252   tc = GNUNET_SCHEDULER_get_task_context ();
1253   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY))
1254     unix_plugin_do_write (plugin);
1255   if (NULL == plugin->msg_head)
1256     return; /* write queue empty */
1257   plugin->write_task =
1258     GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1259                                     plugin->unix_sock.desc,
1260                                     &unix_plugin_select_write, plugin);
1261 }
1262
1263
1264 /**
1265  * Function that can be used by the transport service to transmit
1266  * a message using the plugin.   Note that in the case of a
1267  * peer disconnecting, the continuation MUST be called
1268  * prior to the disconnect notification itself.  This function
1269  * will be called with this peer's HELLO message to initiate
1270  * a fresh connection to another peer.
1271  *
1272  * @param cls closure
1273  * @param session which session must be used
1274  * @param msgbuf the message to transmit
1275  * @param msgbuf_size number of bytes in @a msgbuf
1276  * @param priority how important is the message (most plugins will
1277  *                 ignore message priority and just FIFO)
1278  * @param to how long to wait at most for the transmission (does not
1279  *                require plugins to discard the message after the timeout,
1280  *                just advisory for the desired delay; most plugins will ignore
1281  *                this as well)
1282  * @param cont continuation to call once the message has
1283  *        been transmitted (or if the transport is ready
1284  *        for the next transmission call; or if the
1285  *        peer disconnected...); can be NULL
1286  * @param cont_cls closure for @a cont
1287  * @return number of bytes used (on the physical network, with overheads);
1288  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1289  *         and does NOT mean that the message was not transmitted (DV)
1290  */
1291 static ssize_t
1292 unix_plugin_send (void *cls,
1293                   struct GNUNET_ATS_Session *session,
1294                   const char *msgbuf,
1295                   size_t msgbuf_size,
1296                   unsigned int priority,
1297                   struct GNUNET_TIME_Relative to,
1298                   GNUNET_TRANSPORT_TransmitContinuation cont,
1299                   void *cont_cls)
1300 {
1301   struct Plugin *plugin = cls;
1302   struct UNIXMessageWrapper *wrapper;
1303   struct UNIXMessage *message;
1304   int ssize;
1305
1306   if (GNUNET_OK !=
1307       GNUNET_CONTAINER_multipeermap_contains_value (plugin->session_map,
1308                                                     &session->target,
1309                                                     session))
1310   {
1311     LOG (GNUNET_ERROR_TYPE_ERROR,
1312          "Invalid session for peer `%s' `%s'\n",
1313          GNUNET_i2s (&session->target),
1314          unix_plugin_address_to_string (NULL,
1315                                         session->address->address,
1316                                         session->address->address_length));
1317     GNUNET_break (0);
1318     return GNUNET_SYSERR;
1319   }
1320   LOG (GNUNET_ERROR_TYPE_DEBUG,
1321        "Sending %u bytes with session for peer `%s' `%s'\n",
1322        msgbuf_size,
1323        GNUNET_i2s (&session->target),
1324        unix_plugin_address_to_string (NULL,
1325                                       session->address->address,
1326                                       session->address->address_length));
1327   ssize = sizeof (struct UNIXMessage) + msgbuf_size;
1328   message = GNUNET_malloc (sizeof (struct UNIXMessage) + msgbuf_size);
1329   message->header.size = htons (ssize);
1330   message->header.type = htons (0);
1331   GNUNET_memcpy (&message->sender, plugin->env->my_identity,
1332           sizeof (struct GNUNET_PeerIdentity));
1333   GNUNET_memcpy (&message[1], msgbuf, msgbuf_size);
1334   wrapper = GNUNET_new (struct UNIXMessageWrapper);
1335   wrapper->msg = message;
1336   wrapper->msgsize = ssize;
1337   wrapper->payload = msgbuf_size;
1338   wrapper->priority = priority;
1339   wrapper->timeout = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
1340                                                to);
1341   wrapper->cont = cont;
1342   wrapper->cont_cls = cont_cls;
1343   wrapper->session = session;
1344   GNUNET_CONTAINER_DLL_insert_tail (plugin->msg_head,
1345                                     plugin->msg_tail,
1346                                     wrapper);
1347   plugin->bytes_in_queue += ssize;
1348   session->bytes_in_queue += ssize;
1349   session->msgs_in_queue++;
1350   GNUNET_STATISTICS_set (plugin->env->stats,
1351                          "# bytes currently in UNIX buffers",
1352                          plugin->bytes_in_queue,
1353                          GNUNET_NO);
1354   notify_session_monitor (plugin,
1355                           session,
1356                           GNUNET_TRANSPORT_SS_UPDATE);
1357   if (NULL == plugin->write_task)
1358     plugin->write_task =
1359       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1360                                       plugin->unix_sock.desc,
1361                                       &unix_plugin_select_write, plugin);
1362   return ssize;
1363 }
1364
1365
1366 /**
1367  * Create a slew of UNIX sockets.  If possible, use IPv6 and IPv4.
1368  *
1369  * @param cls closure for server start, should be a `struct Plugin *`
1370  * @return number of sockets created or #GNUNET_SYSERR on error
1371  */
1372 static int
1373 unix_transport_server_start (void *cls)
1374 {
1375   struct Plugin *plugin = cls;
1376   struct sockaddr_un *un;
1377   socklen_t un_len;
1378
1379   un = unix_address_to_sockaddr (plugin->unix_socket_path,
1380                                  &un_len);
1381   if (GNUNET_YES == plugin->is_abstract)
1382   {
1383     plugin->unix_socket_path[0] = '@';
1384     un->sun_path[0] = '\0';
1385   }
1386   plugin->unix_sock.desc =
1387       GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_DGRAM, 0);
1388   if (NULL == plugin->unix_sock.desc)
1389   {
1390     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
1391     GNUNET_free (un);
1392     return GNUNET_SYSERR;
1393   }
1394   if ('\0' != un->sun_path[0])
1395   {
1396     if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (un->sun_path))
1397     {
1398       LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot create path to `%s'\n"),
1399           un->sun_path);
1400       GNUNET_NETWORK_socket_close (plugin->unix_sock.desc);
1401       plugin->unix_sock.desc = NULL;
1402       GNUNET_free (un);
1403       return GNUNET_SYSERR;
1404     }
1405   }
1406   if (GNUNET_OK !=
1407       GNUNET_NETWORK_socket_bind (plugin->unix_sock.desc,
1408                                   (const struct sockaddr *) un, un_len))
1409   {
1410     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
1411     LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot bind to `%s'\n"),
1412         un->sun_path);
1413     GNUNET_NETWORK_socket_close (plugin->unix_sock.desc);
1414     plugin->unix_sock.desc = NULL;
1415     GNUNET_free (un);
1416     return GNUNET_SYSERR;
1417   }
1418   LOG (GNUNET_ERROR_TYPE_DEBUG,
1419        "Bound to `%s'\n",
1420        plugin->unix_socket_path);
1421   plugin->read_task =
1422     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1423                                    plugin->unix_sock.desc,
1424                                    &unix_plugin_select_read, plugin);
1425   GNUNET_free (un);
1426   return 1;
1427 }
1428
1429
1430 /**
1431  * Function that will be called to check if a binary address for this
1432  * plugin is well-formed and corresponds to an address for THIS peer
1433  * (as per our configuration).  Naturally, if absolutely necessary,
1434  * plugins can be a bit conservative in their answer, but in general
1435  * plugins should make sure that the address does not redirect
1436  * traffic to a 3rd party that might try to man-in-the-middle our
1437  * traffic.
1438  *
1439  * @param cls closure, should be our handle to the Plugin
1440  * @param addr pointer to the address
1441  * @param addrlen length of @a addr
1442  * @return #GNUNET_OK if this is a plausible address for this peer
1443  *         and transport, #GNUNET_SYSERR if not
1444  *
1445  */
1446 static int
1447 unix_plugin_check_address (void *cls,
1448                            const void *addr,
1449                            size_t addrlen)
1450 {
1451   struct Plugin* plugin = cls;
1452   const struct UnixAddress *ua = addr;
1453   char *addrstr;
1454   size_t addr_str_len;
1455
1456   if ( (NULL == addr) ||
1457        (0 == addrlen) ||
1458        (sizeof (struct UnixAddress) > addrlen) )
1459   {
1460     GNUNET_break (0);
1461     return GNUNET_SYSERR;
1462   }
1463   addrstr = (char *) &ua[1];
1464   addr_str_len = ntohl (ua->addrlen);
1465   if ('\0' != addrstr[addr_str_len - 1])
1466   {
1467     GNUNET_break (0);
1468     return GNUNET_SYSERR;
1469   }
1470   if (strlen (addrstr) + 1 != addr_str_len)
1471   {
1472     GNUNET_break (0);
1473     return GNUNET_SYSERR;
1474   }
1475
1476   if (0 == strcmp (plugin->unix_socket_path, addrstr))
1477         return GNUNET_OK;
1478   return GNUNET_SYSERR;
1479 }
1480
1481
1482 /**
1483  * Convert the transports address to a nice, human-readable
1484  * format.
1485  *
1486  * @param cls closure
1487  * @param type name of the transport that generated the address
1488  * @param addr one of the addresses of the host, NULL for the last address
1489  *        the specific address format depends on the transport
1490  * @param addrlen length of the @a addr
1491  * @param numeric should (IP) addresses be displayed in numeric form?
1492  * @param timeout after how long should we give up?
1493  * @param asc function to call on each string
1494  * @param asc_cls closure for @a asc
1495  */
1496 static void
1497 unix_plugin_address_pretty_printer (void *cls, const char *type,
1498                                     const void *addr,
1499                                     size_t addrlen,
1500                                     int numeric,
1501                                     struct GNUNET_TIME_Relative timeout,
1502                                     GNUNET_TRANSPORT_AddressStringCallback asc,
1503                                     void *asc_cls)
1504 {
1505   const char *ret;
1506
1507   if ( (NULL != addr) && (addrlen > 0))
1508     ret = unix_plugin_address_to_string (NULL,
1509                                          addr,
1510                                          addrlen);
1511   else
1512     ret = NULL;
1513   asc (asc_cls,
1514        ret,
1515        (NULL == ret) ? GNUNET_SYSERR : GNUNET_OK);
1516   asc (asc_cls, NULL, GNUNET_OK);
1517 }
1518
1519
1520 /**
1521  * Function called to convert a string address to
1522  * a binary address.
1523  *
1524  * @param cls closure (`struct Plugin *`)
1525  * @param addr string address
1526  * @param addrlen length of the @a addr (strlen(addr) + '\0')
1527  * @param buf location to store the buffer
1528  *        If the function returns #GNUNET_SYSERR, its contents are undefined.
1529  * @param added length of created address
1530  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1531  */
1532 static int
1533 unix_plugin_string_to_address (void *cls,
1534                                const char *addr,
1535                                uint16_t addrlen,
1536                                void **buf, size_t *added)
1537 {
1538   struct UnixAddress *ua;
1539   char *address;
1540   char *plugin;
1541   char *optionstr;
1542   uint32_t options;
1543   size_t ua_size;
1544
1545   /* Format unix.options.address */
1546   address = NULL;
1547   plugin = NULL;
1548   optionstr = NULL;
1549
1550   if ((NULL == addr) || (addrlen == 0))
1551   {
1552     GNUNET_break (0);
1553     return GNUNET_SYSERR;
1554   }
1555   if ('\0' != addr[addrlen - 1])
1556   {
1557     GNUNET_break (0);
1558     return GNUNET_SYSERR;
1559   }
1560   if (strlen (addr) != addrlen - 1)
1561   {
1562     GNUNET_break (0);
1563     return GNUNET_SYSERR;
1564   }
1565   plugin = GNUNET_strdup (addr);
1566   optionstr = strchr (plugin, '.');
1567   if (NULL == optionstr)
1568   {
1569     GNUNET_break (0);
1570     GNUNET_free (plugin);
1571     return GNUNET_SYSERR;
1572   }
1573   optionstr[0] = '\0';
1574   optionstr++;
1575   options = atol (optionstr);
1576   address = strchr (optionstr, '.');
1577   if (NULL == address)
1578   {
1579     GNUNET_break (0);
1580     GNUNET_free (plugin);
1581     return GNUNET_SYSERR;
1582   }
1583   address[0] = '\0';
1584   address++;
1585   if (0 != strcmp(plugin, PLUGIN_NAME))
1586   {
1587     GNUNET_break (0);
1588     GNUNET_free (plugin);
1589     return GNUNET_SYSERR;
1590   }
1591
1592   ua_size = sizeof (struct UnixAddress) + strlen (address) + 1;
1593   ua = GNUNET_malloc (ua_size);
1594   ua->options = htonl (options);
1595   ua->addrlen = htonl (strlen (address) + 1);
1596   GNUNET_memcpy (&ua[1], address, strlen (address) + 1);
1597   GNUNET_free (plugin);
1598
1599   (*buf) = ua;
1600   (*added) = ua_size;
1601   return GNUNET_OK;
1602 }
1603
1604
1605 /**
1606  * Notify transport service about address
1607  *
1608  * @param cls the plugin
1609  */
1610 static void
1611 address_notification (void *cls)
1612 {
1613   struct Plugin *plugin = cls;
1614   struct GNUNET_HELLO_Address *address;
1615   size_t len;
1616   struct UnixAddress *ua;
1617   char *unix_path;
1618
1619   len = sizeof (struct UnixAddress) + strlen (plugin->unix_socket_path) + 1;
1620   ua = GNUNET_malloc (len);
1621   ua->options = htonl (plugin->myoptions);
1622   ua->addrlen = htonl(strlen (plugin->unix_socket_path) + 1);
1623   unix_path = (char *) &ua[1];
1624   GNUNET_memcpy (unix_path, plugin->unix_socket_path, strlen (plugin->unix_socket_path) + 1);
1625
1626   plugin->address_update_task = NULL;
1627   address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
1628                                            PLUGIN_NAME,
1629                                            ua,
1630                                            len,
1631                                            GNUNET_HELLO_ADDRESS_INFO_NONE);
1632   plugin->env->notify_address (plugin->env->cls,
1633                                GNUNET_YES,
1634                                address);
1635   GNUNET_free (ua);
1636   GNUNET_free (address);
1637 }
1638
1639
1640 /**
1641  * Function called on sessions to disconnect
1642  *
1643  * @param cls the plugin
1644  * @param key peer identity (unused)
1645  * @param value the `struct GNUNET_ATS_Session *` to disconnect
1646  * @return #GNUNET_YES (always, continue to iterate)
1647  */
1648 static int
1649 get_session_delete_it (void *cls,
1650                        const struct GNUNET_PeerIdentity *key,
1651                        void *value)
1652 {
1653   struct Plugin *plugin = cls;
1654   struct GNUNET_ATS_Session *session = value;
1655
1656   unix_plugin_session_disconnect (plugin, session);
1657   return GNUNET_YES;
1658 }
1659
1660
1661 /**
1662  * Disconnect from a remote node.  Clean up session if we have one for this peer
1663  *
1664  * @param cls closure for this call (should be handle to Plugin)
1665  * @param target the peeridentity of the peer to disconnect
1666  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the operation failed
1667  */
1668 static void
1669 unix_plugin_peer_disconnect (void *cls,
1670                              const struct GNUNET_PeerIdentity *target)
1671 {
1672   struct Plugin *plugin = cls;
1673
1674   GNUNET_CONTAINER_multipeermap_get_multiple (plugin->session_map,
1675                                               target,
1676                                               &get_session_delete_it, plugin);
1677 }
1678
1679
1680 /**
1681  * Return information about the given session to the
1682  * monitor callback.
1683  *
1684  * @param cls the `struct Plugin` with the monitor callback (`sic`)
1685  * @param peer peer we send information about
1686  * @param value our `struct GNUNET_ATS_Session` to send information about
1687  * @return #GNUNET_OK (continue to iterate)
1688  */
1689 static int
1690 send_session_info_iter (void *cls,
1691                         const struct GNUNET_PeerIdentity *peer,
1692                         void *value)
1693 {
1694   struct Plugin *plugin = cls;
1695   struct GNUNET_ATS_Session *session = value;
1696
1697   notify_session_monitor (plugin,
1698                           session,
1699                           GNUNET_TRANSPORT_SS_INIT);
1700   notify_session_monitor (plugin,
1701                           session,
1702                           GNUNET_TRANSPORT_SS_UP);
1703   return GNUNET_OK;
1704 }
1705
1706
1707 /**
1708  * Begin monitoring sessions of a plugin.  There can only
1709  * be one active monitor per plugin (i.e. if there are
1710  * multiple monitors, the transport service needs to
1711  * multiplex the generated events over all of them).
1712  *
1713  * @param cls closure of the plugin
1714  * @param sic callback to invoke, NULL to disable monitor;
1715  *            plugin will being by iterating over all active
1716  *            sessions immediately and then enter monitor mode
1717  * @param sic_cls closure for @a sic
1718  */
1719 static void
1720 unix_plugin_setup_monitor (void *cls,
1721                            GNUNET_TRANSPORT_SessionInfoCallback sic,
1722                            void *sic_cls)
1723 {
1724   struct Plugin *plugin = cls;
1725
1726   plugin->sic = sic;
1727   plugin->sic_cls = sic_cls;
1728   if (NULL != sic)
1729   {
1730     GNUNET_CONTAINER_multipeermap_iterate (plugin->session_map,
1731                                            &send_session_info_iter,
1732                                            plugin);
1733     /* signal end of first iteration */
1734     sic (sic_cls, NULL, NULL);
1735   }
1736 }
1737
1738
1739 /**
1740  * The exported method.  Initializes the plugin and returns a
1741  * struct with the callbacks.
1742  *
1743  * @param cls the plugin's execution environment
1744  * @return NULL on error, plugin functions otherwise
1745  */
1746 void *
1747 libgnunet_plugin_transport_unix_init (void *cls)
1748 {
1749   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1750   struct GNUNET_TRANSPORT_PluginFunctions *api;
1751   struct Plugin *plugin;
1752   int sockets_created;
1753
1754   if (NULL == env->receive)
1755   {
1756     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
1757        initialze the plugin or the API */
1758     api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
1759     api->cls = NULL;
1760     api->address_pretty_printer = &unix_plugin_address_pretty_printer;
1761     api->address_to_string = &unix_plugin_address_to_string;
1762     api->string_to_address = &unix_plugin_string_to_address;
1763     return api;
1764   }
1765
1766   plugin = GNUNET_new (struct Plugin);
1767   if (GNUNET_OK !=
1768       GNUNET_CONFIGURATION_get_value_filename (env->cfg,
1769                                                "transport-unix",
1770                                                "UNIXPATH",
1771                                                &plugin->unix_socket_path))
1772   {
1773     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1774                                "transport-unix",
1775                                "UNIXPATH");
1776     GNUNET_free (plugin);
1777     return NULL;
1778   }
1779
1780   plugin->env = env;
1781
1782   /* Initialize my flags */
1783 #ifdef LINUX
1784   plugin->is_abstract = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
1785                                                               "testing",
1786                                                               "USE_ABSTRACT_SOCKETS");
1787 #endif
1788   plugin->myoptions = UNIX_OPTIONS_NONE;
1789   if (GNUNET_YES == plugin->is_abstract)
1790     plugin->myoptions = UNIX_OPTIONS_USE_ABSTRACT_SOCKETS;
1791
1792   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
1793   api->cls = plugin;
1794   api->get_session = &unix_plugin_get_session;
1795   api->send = &unix_plugin_send;
1796   api->disconnect_peer = &unix_plugin_peer_disconnect;
1797   api->disconnect_session = &unix_plugin_session_disconnect;
1798   api->query_keepalive_factor = &unix_plugin_query_keepalive_factor;
1799   api->address_pretty_printer = &unix_plugin_address_pretty_printer;
1800   api->address_to_string = &unix_plugin_address_to_string;
1801   api->check_address = &unix_plugin_check_address;
1802   api->string_to_address = &unix_plugin_string_to_address;
1803   api->get_network = &unix_plugin_get_network;
1804   api->get_network_for_address = &unix_plugin_get_network_for_address;
1805   api->update_session_timeout = &unix_plugin_update_session_timeout;
1806   api->setup_monitor = &unix_plugin_setup_monitor;
1807   sockets_created = unix_transport_server_start (plugin);
1808   if ((0 == sockets_created) || (GNUNET_SYSERR == sockets_created))
1809   {
1810     LOG (GNUNET_ERROR_TYPE_WARNING,
1811          _("Failed to open UNIX listen socket\n"));
1812     GNUNET_free (api);
1813     GNUNET_free (plugin->unix_socket_path);
1814     GNUNET_free (plugin);
1815     return NULL;
1816   }
1817   plugin->session_map = GNUNET_CONTAINER_multipeermap_create (10, GNUNET_NO);
1818   plugin->address_update_task = GNUNET_SCHEDULER_add_now (&address_notification,
1819                                                           plugin);
1820   return api;
1821 }
1822
1823
1824 /**
1825  * Shutdown the plugin.
1826  *
1827  * @param cls the plugin API returned from the initialization function
1828  * @return NULL (always)
1829  */
1830 void *
1831 libgnunet_plugin_transport_unix_done (void *cls)
1832 {
1833   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1834   struct Plugin *plugin = api->cls;
1835   struct GNUNET_HELLO_Address *address;
1836   struct UNIXMessageWrapper * msgw;
1837   struct UnixAddress *ua;
1838   size_t len;
1839   struct GNUNET_ATS_Session *session;
1840
1841   if (NULL == plugin)
1842   {
1843     GNUNET_free (api);
1844     return NULL;
1845   }
1846   len = sizeof (struct UnixAddress) + strlen (plugin->unix_socket_path) + 1;
1847   ua = GNUNET_malloc (len);
1848   ua->options = htonl (plugin->myoptions);
1849   ua->addrlen = htonl(strlen (plugin->unix_socket_path) + 1);
1850   GNUNET_memcpy (&ua[1],
1851           plugin->unix_socket_path,
1852           strlen (plugin->unix_socket_path) + 1);
1853   address = GNUNET_HELLO_address_allocate (plugin->env->my_identity,
1854                                            PLUGIN_NAME,
1855                                            ua, len,
1856                                            GNUNET_HELLO_ADDRESS_INFO_NONE);
1857   plugin->env->notify_address (plugin->env->cls,
1858                                GNUNET_NO,
1859                                address);
1860
1861   GNUNET_free (address);
1862   GNUNET_free (ua);
1863
1864   while (NULL != (msgw = plugin->msg_head))
1865   {
1866     GNUNET_CONTAINER_DLL_remove (plugin->msg_head,
1867                                  plugin->msg_tail,
1868                                  msgw);
1869     session = msgw->session;
1870     session->msgs_in_queue--;
1871     GNUNET_assert (session->bytes_in_queue >= msgw->msgsize);
1872     session->bytes_in_queue -= msgw->msgsize;
1873     GNUNET_assert (plugin->bytes_in_queue >= msgw->msgsize);
1874     plugin->bytes_in_queue -= msgw->msgsize;
1875     if (NULL != msgw->cont)
1876       msgw->cont (msgw->cont_cls,
1877                   &msgw->session->target,
1878                   GNUNET_SYSERR,
1879                   msgw->payload, 0);
1880     GNUNET_free (msgw->msg);
1881     GNUNET_free (msgw);
1882   }
1883
1884   if (NULL != plugin->read_task)
1885   {
1886     GNUNET_SCHEDULER_cancel (plugin->read_task);
1887     plugin->read_task = NULL;
1888   }
1889   if (NULL != plugin->write_task)
1890   {
1891     GNUNET_SCHEDULER_cancel (plugin->write_task);
1892     plugin->write_task = NULL;
1893   }
1894   if (NULL != plugin->address_update_task)
1895   {
1896     GNUNET_SCHEDULER_cancel (plugin->address_update_task);
1897     plugin->address_update_task = NULL;
1898   }
1899   if (NULL != plugin->unix_sock.desc)
1900   {
1901     GNUNET_break (GNUNET_OK ==
1902                   GNUNET_NETWORK_socket_close (plugin->unix_sock.desc));
1903     plugin->unix_sock.desc = NULL;
1904   }
1905   GNUNET_CONTAINER_multipeermap_iterate (plugin->session_map,
1906                                          &get_session_delete_it,
1907                                          plugin);
1908   GNUNET_CONTAINER_multipeermap_destroy (plugin->session_map);
1909   GNUNET_break (0 == plugin->bytes_in_queue);
1910   GNUNET_free (plugin->unix_socket_path);
1911   GNUNET_free (plugin);
1912   GNUNET_free (api);
1913   return NULL;
1914 }
1915
1916 /* end of plugin_transport_unix.c */