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