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