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