latest changes
[oweals/gnunet.git] / src / transport / plugin_transport_http_server.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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_http_server.c
23  * @brief HTTP/S server transport plugin
24  * @author Matthias Wachs
25  */
26
27 #include "platform.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_connection_lib.h"
30 #include "gnunet_server_lib.h"
31 #include "gnunet_service_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_transport_plugin.h"
35
36 #include "gnunet_container_lib.h"
37 #include "gnunet_nat_lib.h"
38 #include "plugin_transport_http_common.h"
39 #include "microhttpd.h"
40
41 #if BUILD_HTTPS
42 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init
43 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done
44 #else
45 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init
46 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done
47 #endif
48
49 #define HTTP_NOT_VALIDATED_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
50
51 /**
52  * Encapsulation of all of the state of the plugin.
53  */
54 struct Plugin;
55
56
57 /**
58  * Session handle for connections.
59  */
60 struct Session
61 {
62   /**
63    * Stored in a linked list.
64    */
65   struct Session *next;
66
67   /**
68    * Stored in a linked list.
69    */
70   struct Session *prev;
71
72   /**
73    * To whom are we talking to (set to our identity
74    * if we are still waiting for the welcome message)
75    */
76   struct GNUNET_PeerIdentity target;
77
78   /**
79    * Pointer to the global plugin struct.
80    */
81   struct HTTP_Server_Plugin *plugin;
82
83   /**
84    * next pointer for double linked list
85    */
86   struct HTTP_Message *msg_head;
87
88   /**
89    * previous pointer for double linked list
90    */
91   struct HTTP_Message *msg_tail;
92
93   /**
94    * Message stream tokenizer for incoming data
95    */
96   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
97
98   /**
99    * Client send handle
100    */
101   struct ServerConnection *server_recv;
102
103   /**
104    * Client send handle
105    */
106   struct ServerConnection *server_send;
107
108   /**
109    * Address
110    */
111   void *addr;
112 };
113
114 /**
115  * Encapsulation of all of the state of the plugin.
116  */
117 struct HTTP_Server_Plugin
118 {
119   /**
120    * Our environment.
121    */
122   struct GNUNET_TRANSPORT_PluginEnvironment *env;
123
124   /**
125    * Linked list head of open sessions.
126    */
127
128   struct Session *head;
129
130   /**
131    * Linked list tail of open sessions.
132    */
133   struct Session *tail;
134
135   /**
136    * Plugin name
137    */
138   char *name;
139
140   /**
141    * Protocol
142    */
143   char *protocol;
144
145   /**
146    * External address
147    */
148   char *external_hostname;
149
150   /**
151    * Maximum number of sockets the plugin can use
152    * Each http inbound /outbound connections are two connections
153    */
154   unsigned int max_connections;
155
156   /**
157    * Current number of sockets the plugin can use
158    * Each http inbound /outbound connections are two connections
159    */
160   unsigned int cur_connections;
161
162   /**
163    * External hostname the plugin can be connected to, can be different to
164    * the host's FQDN, used e.g. for reverse proxying
165    */
166   char *ext_addr;
167
168   /**
169    * External address length
170    */
171   size_t ext_addr_len;
172
173   /**
174    * use IPv6
175    */
176   uint16_t use_ipv6;
177
178   /**
179    * use IPv4
180    */
181   uint16_t use_ipv4;
182
183   /**
184    * Port used
185    */
186   uint16_t port;
187
188   /**
189    * Task calling transport service about external address
190    */
191   GNUNET_SCHEDULER_TaskIdentifier notify_ext_task;
192
193   /**
194    * NAT handle & address management
195    */
196   struct GNUNET_NAT_Handle *nat;
197
198   /**
199    * Server semi connections
200    * A full session consists of 2 semi-connections: send and receive
201    * If not both directions are established the server keeps this sessions here
202    */
203   struct Session *server_semi_head;
204
205   struct Session *server_semi_tail;
206
207   /**
208    * List of own addresses
209    */
210
211   /**
212    * IPv4 addresses DLL head
213    */
214   struct HttpAddressWrapper *addr_head;
215
216   /**
217    * IPv4 addresses DLL tail
218    */
219   struct HttpAddressWrapper *addr_tail;
220
221   /**
222    * IPv4 server socket to bind to
223    */
224   struct sockaddr_in *server_addr_v4;
225
226   /**
227    * IPv6 server socket to bind to
228    */
229   struct sockaddr_in6 *server_addr_v6;
230
231   /**
232    * MHD IPv4 task
233    */
234   GNUNET_SCHEDULER_TaskIdentifier server_v4_task;
235
236   /**
237    * MHD IPv6 task
238    */
239   GNUNET_SCHEDULER_TaskIdentifier server_v6_task;
240
241   /**
242    * The IPv4 server is scheduled to run asap
243    */
244   int server_v4_immediately;
245
246   /**
247    * The IPv6 server is scheduled to run asap
248    */
249   int server_v6_immediately;
250
251   /**
252    * MHD IPv4 daemon
253    */
254   struct MHD_Daemon *server_v4;
255
256   /**
257    * MHD IPv4 daemon
258    */
259   struct MHD_Daemon *server_v6;
260
261 #if BUILD_HTTPS
262   /**
263    * Crypto related
264    *
265    * Example:
266    *
267    * Use RC4-128 instead of AES:
268    * NONE:+VERS-TLS1.0:+ARCFOUR-128:+SHA1:+RSA:+COMP-NULL
269    *
270    */
271   char *crypto_init;
272
273   /**
274    * TLS key
275    */
276   char *key;
277
278   /**
279    * TLS certificate
280    */
281   char *cert;
282 #endif
283
284 };
285
286 /**
287  * Wrapper to manage addresses
288  */
289 struct HttpAddressWrapper
290 {
291   /**
292    * Linked list next
293    */
294   struct HttpAddressWrapper *next;
295
296   /**
297    * Linked list previous
298    */
299   struct HttpAddressWrapper *prev;
300
301   void *addr;
302
303   size_t addrlen;
304 };
305
306 /**
307  *  Message to send using http
308  */
309 struct HTTP_Message
310 {
311   /**
312    * next pointer for double linked list
313    */
314   struct HTTP_Message *next;
315
316   /**
317    * previous pointer for double linked list
318    */
319   struct HTTP_Message *prev;
320
321   /**
322    * buffer containing data to send
323    */
324   char *buf;
325
326   /**
327    * amount of data already sent
328    */
329   size_t pos;
330
331   /**
332    * buffer length
333    */
334   size_t size;
335
336   /**
337    * Continuation function to call once the transmission buffer
338    * has again space available.  NULL if there is no
339    * continuation to call.
340    */
341   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
342
343   /**
344    * Closure for transmit_cont.
345    */
346   void *transmit_cont_cls;
347 };
348
349
350 static struct Plugin * p;
351
352
353 /**
354  * Function that can be used by the transport service to transmit
355  * a message using the plugin.   Note that in the case of a
356  * peer disconnecting, the continuation MUST be called
357  * prior to the disconnect notification itself.  This function
358  * will be called with this peer's HELLO message to initiate
359  * a fresh connection to another peer.
360  *
361  * @param cls closure
362  * @param session which session must be used
363  * @param msgbuf the message to transmit
364  * @param msgbuf_size number of bytes in 'msgbuf'
365  * @param priority how important is the message (most plugins will
366  *                 ignore message priority and just FIFO)
367  * @param to how long to wait at most for the transmission (does not
368  *                require plugins to discard the message after the timeout,
369  *                just advisory for the desired delay; most plugins will ignore
370  *                this as well)
371  * @param cont continuation to call once the message has
372  *        been transmitted (or if the transport is ready
373  *        for the next transmission call; or if the
374  *        peer disconnected...); can be NULL
375  * @param cont_cls closure for cont
376  * @return number of bytes used (on the physical network, with overheads);
377  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
378  *         and does NOT mean that the message was not transmitted (DV)
379  */
380 static ssize_t
381 http_server_plugin_send (void *cls,
382                   struct Session *session,
383                   const char *msgbuf, size_t msgbuf_size,
384                   unsigned int priority,
385                   struct GNUNET_TIME_Relative to,
386                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
387 {
388   struct HTTP_Server_Plugin *plugin = cls;
389   int bytes_sent = 0;
390
391   GNUNET_assert (plugin != NULL);
392   GNUNET_assert (session != NULL);
393
394   GNUNET_break (0);
395
396   /*  struct Plugin *plugin = cls; */
397   return bytes_sent;
398 }
399
400
401
402 /**
403  * Function that can be used to force the plugin to disconnect
404  * from the given peer and cancel all previous transmissions
405  * (and their continuationc).
406  *
407  * @param cls closure
408  * @param target peer from which to disconnect
409  */
410 static void
411 http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
412 {
413   // struct Plugin *plugin = cls;
414   GNUNET_break (0);
415 }
416
417
418 /**
419  * Another peer has suggested an address for this
420  * peer and transport plugin.  Check that this could be a valid
421  * address.  If so, consider adding it to the list
422  * of addresses.
423  *
424  * @param cls closure
425  * @param addr pointer to the address
426  * @param addrlen length of addr
427  * @return GNUNET_OK if this is a plausible address for this peer
428  *         and transport
429  */
430 static int
431 http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
432 {
433   struct HTTP_Server_Plugin *plugin = cls;
434   struct HttpAddressWrapper *w = plugin->addr_head;
435
436   if (GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, plugin->ext_addr, plugin->ext_addr_len)))
437     return GNUNET_OK;
438
439   while (NULL != w)
440   {
441     if (GNUNET_YES == (http_common_cmp_addresses(addr,
442                                                  addrlen,
443                                                  w->addr,
444                                                  w->addrlen)))
445       return GNUNET_OK;
446   }
447
448   return GNUNET_NO;
449 }
450
451 /**
452  * Creates a new outbound session the transport
453  * service will use to send data to the peer
454  *
455  * Since HTTP/S server cannot create sessions, always return NULL
456  *
457  * @param cls the plugin
458  * @param address the address
459  * @return always NULL
460  */
461 static struct Session *
462 http_server_plugin_get_session (void *cls,
463                                 const struct GNUNET_HELLO_Address *address)
464 {
465   return NULL;
466 }
467
468
469 /**
470  * Deleting the session
471  * Must not be used afterwards
472  */
473
474 void
475 server_delete_session (struct Session *s)
476 {
477   struct HTTP_Server_Plugin *plugin = s->plugin;
478   stop_session_timeout(s);
479
480   GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
481   struct HTTP_Message *msg = s->msg_head;
482   struct HTTP_Message *tmp = NULL;
483
484   while (msg != NULL)
485   {
486     tmp = msg->next;
487
488     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
489     if (msg->transmit_cont != NULL)
490     {
491       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
492     }
493     GNUNET_free (msg);
494     msg = tmp;
495   }
496
497   if (s->msg_tk != NULL)
498   {
499     GNUNET_SERVER_mst_destroy (s->msg_tk);
500     s->msg_tk = NULL;
501   }
502   GNUNET_free (s->addr);
503   GNUNET_free_non_null (s->server_recv);
504   GNUNET_free_non_null (s->server_send);
505   GNUNET_free (s);
506 }
507
508
509 static int
510 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
511                   const char *url, const char *method, const char *version,
512                   const char *upload_data, size_t * upload_data_size,
513                   void **httpSessionCache)
514 {
515   /* FIXME SPLIT */
516   return MHD_NO;
517 }
518
519 static void
520 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
521                       void **httpSessionCache)
522 {
523   /* FIXME SPLIT */
524   GNUNET_break (0);
525 }
526
527 /**
528  * Check if incoming connection is accepted.
529  * NOTE: Here every connection is accepted
530  * @param cls plugin as closure
531  * @param addr address of incoming connection
532  * @param addr_len address length of incoming connection
533  * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
534  *
535  */
536 static int
537 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
538 {
539   struct HTTP_Server_Plugin *plugin = cls;
540   GNUNET_break (0);
541   if (plugin->cur_connections <= plugin->max_connections)
542     return MHD_YES;
543   else
544   {
545     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
546                 "Server: Cannot accept new connections\n");
547     return MHD_NO;
548   }
549 }
550
551 static void
552 server_log (void *arg, const char *fmt, va_list ap)
553 {
554   char text[1024];
555
556   vsnprintf (text, sizeof (text), fmt, ap);
557   va_end (ap);
558   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text);
559 }
560
561 /**
562  * Function that queries MHD's select sets and
563  * starts the task waiting for them.
564  * @param plugin plugin
565  * @param daemon_handle the MHD daemon handle
566  * @return gnunet task identifier
567  */
568 static GNUNET_SCHEDULER_TaskIdentifier
569 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
570                  int now);
571
572 /**
573  * Reschedule the execution of both IPv4 and IPv6 server
574  * @param plugin the plugin
575  * @param server which server to schedule v4 or v6?
576  * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait
577  * until timeout
578  */
579 static void
580 server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now)
581 {
582   if ((server == plugin->server_v4) && (plugin->server_v4 != NULL))
583   {
584     if (GNUNET_YES == plugin->server_v4_immediately)
585       return; /* No rescheduling, server will run asap */
586
587     if (GNUNET_YES == now)
588       plugin->server_v4_immediately = GNUNET_YES;
589
590     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
591     {
592       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
593       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
594     }
595     plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now);
596   }
597
598   if ((server == plugin->server_v6) && (plugin->server_v6 != NULL))
599   {
600     if (GNUNET_YES == plugin->server_v6_immediately)
601       return; /* No rescheduling, server will run asap */
602
603     if (GNUNET_YES == now)
604       plugin->server_v6_immediately = GNUNET_YES;
605
606     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
607     {
608       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
609       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
610     }
611     plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now);
612   }
613 }
614
615 /**
616  * Call MHD IPv4 to process pending requests and then go back
617  * and schedule the next run.
618  * @param cls plugin as closure
619  * @param tc task context
620  */
621 static void
622 server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
623 {
624   struct HTTP_Server_Plugin *plugin = cls;
625
626   GNUNET_assert (cls != NULL);
627
628   plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
629   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
630     return;
631 #if 0
632   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
633                    "Running IPv4 server\n");
634 #endif
635   plugin->server_v4_immediately = GNUNET_NO;
636   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
637   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
638 }
639
640
641 /**
642  * Call MHD IPv6 to process pending requests and then go back
643  * and schedule the next run.
644  * @param cls plugin as closure
645  * @param tc task context
646  */
647 static void
648 server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
649 {
650   struct HTTP_Server_Plugin *plugin = cls;
651
652   GNUNET_assert (cls != NULL);
653   plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
654   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
655     return;
656 #if 0
657   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
658                    "Running IPv6 server\n");
659 #endif
660   plugin->server_v6_immediately = GNUNET_NO;
661   GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
662   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
663 }
664
665
666 /**
667  * Function that queries MHD's select sets and
668  * starts the task waiting for them.
669  * @param plugin plugin
670  * @param daemon_handle the MHD daemon handle
671  * @return gnunet task identifier
672  */
673 static GNUNET_SCHEDULER_TaskIdentifier
674 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
675                  int now)
676 {
677   GNUNET_SCHEDULER_TaskIdentifier ret;
678   fd_set rs;
679   fd_set ws;
680   fd_set es;
681   struct GNUNET_NETWORK_FDSet *wrs;
682   struct GNUNET_NETWORK_FDSet *wws;
683   struct GNUNET_NETWORK_FDSet *wes;
684   int max;
685   unsigned MHD_LONG_LONG timeout;
686   static unsigned long long last_timeout = 0;
687   int haveto;
688
689   struct GNUNET_TIME_Relative tv;
690
691   ret = GNUNET_SCHEDULER_NO_TASK;
692   FD_ZERO (&rs);
693   FD_ZERO (&ws);
694   FD_ZERO (&es);
695   wrs = GNUNET_NETWORK_fdset_create ();
696   wes = GNUNET_NETWORK_fdset_create ();
697   wws = GNUNET_NETWORK_fdset_create ();
698   max = -1;
699   GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
700   haveto = MHD_get_timeout (daemon_handle, &timeout);
701   if (haveto == MHD_YES)
702   {
703     if (timeout != last_timeout)
704     {
705 #if VERBOSE_SERVER
706       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
707                        "SELECT Timeout changed from %llu to %llu\n",
708                        last_timeout, timeout);
709 #endif
710       last_timeout = timeout;
711     }
712     tv.rel_value = (uint64_t) timeout;
713   }
714   else
715     tv = GNUNET_TIME_UNIT_SECONDS;
716   /* Force immediate run, since we have outbound data to send */
717   if (now == GNUNET_YES)
718     tv = GNUNET_TIME_UNIT_MILLISECONDS;
719   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
720   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
721   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
722
723   if (daemon_handle == plugin->server_v4)
724   {
725     if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
726     {
727       GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
728       plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
729     }
730 #if 0
731     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
732                      "Scheduling IPv4 server task in %llu ms\n", tv);
733 #endif
734     ret =
735         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
736                                      tv, wrs, wws,
737                                      &server_v4_run, plugin);
738   }
739   if (daemon_handle == plugin->server_v6)
740   {
741     if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
742     {
743       GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
744       plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
745     }
746 #if 0
747     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
748                      "Scheduling IPv6 server task in %llu ms\n", tv);
749 #endif
750     ret =
751         GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
752                                      tv, wrs, wws,
753                                      &server_v6_run, plugin);
754   }
755   GNUNET_NETWORK_fdset_destroy (wrs);
756   GNUNET_NETWORK_fdset_destroy (wws);
757   GNUNET_NETWORK_fdset_destroy (wes);
758   return ret;
759 }
760
761
762 #if BUILD_HTTPS
763 static char *
764 server_load_file (const char *file)
765 {
766   struct GNUNET_DISK_FileHandle *gn_file;
767   uint64_t fsize;
768   char *text = NULL;
769
770   if (GNUNET_OK != GNUNET_DISK_file_size (file,
771       &fsize, GNUNET_NO, GNUNET_YES))
772     return NULL;
773   text = GNUNET_malloc (fsize + 1);
774   gn_file =
775       GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
776                              GNUNET_DISK_PERM_USER_READ);
777   if (gn_file == NULL)
778   {
779     GNUNET_free (text);
780     return NULL;
781   }
782   if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize))
783   {
784     GNUNET_free (text);
785     GNUNET_DISK_file_close (gn_file);
786     return NULL;
787   }
788   text[fsize] = '\0';
789   GNUNET_DISK_file_close (gn_file);
790   return text;
791 }
792 #endif
793
794
795 #if BUILD_HTTPS
796
797 static int
798 server_load_certificate (struct HTTP_Server_Plugin *plugin)
799 {
800   int res = GNUNET_OK;
801
802   char *key_file;
803   char *cert_file;
804
805   /* Get crypto init string from config
806    * If not present just use default values */
807
808   if (GNUNET_OK ==
809                  GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
810                                                         plugin->name,
811                                                         "CRYPTO_INIT",
812                                                         &plugin->crypto_init))
813       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
814                        "Using crypto init string `%s'\n",
815                        plugin->crypto_init);
816   else
817     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
818                      "Using default crypto init string \n");
819
820   if (GNUNET_OK !=
821       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
822                                                "KEY_FILE", &key_file))
823   {
824     key_file = GNUNET_strdup ("https_key.key");
825   }
826
827   if (GNUNET_OK !=
828       GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
829                                                "CERT_FILE", &cert_file))
830   {
831     GNUNET_asprintf (&cert_file, "%s", "https_cert.crt");
832   }
833
834   /* read key & certificates from file */
835   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
836               "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n",
837               key_file, cert_file);
838
839   plugin->key = server_load_file (key_file);
840   plugin->cert = server_load_file (cert_file);
841
842   if ((plugin->key == NULL) || (plugin->cert == NULL))
843   {
844     struct GNUNET_OS_Process *cert_creation;
845
846     GNUNET_free_non_null (plugin->key);
847     plugin->key = NULL;
848     GNUNET_free_non_null (plugin->cert);
849     plugin->cert = NULL;
850
851     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
852                 "No usable TLS certificate found, creating certificate\n");
853     errno = 0;
854     cert_creation =
855         GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
856                                  "gnunet-transport-certificate-creation",
857                                  "gnunet-transport-certificate-creation",
858                                  key_file, cert_file, NULL);
859     if (cert_creation == NULL)
860     {
861       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
862                        _
863                        ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
864       GNUNET_free (key_file);
865       GNUNET_free (cert_file);
866
867       GNUNET_free_non_null (plugin->key);
868       plugin->key = NULL;
869       GNUNET_free_non_null (plugin->cert);
870       plugin->cert = NULL;
871       GNUNET_free_non_null (plugin->crypto_init);
872       plugin->crypto_init = NULL;
873
874       return GNUNET_SYSERR;
875     }
876     GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
877     GNUNET_OS_process_destroy (cert_creation);
878
879     plugin->key = server_load_file (key_file);
880     plugin->cert = server_load_file (cert_file);
881   }
882
883   if ((plugin->key == NULL) || (plugin->cert == NULL))
884   {
885     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
886                      _
887                      ("No usable TLS certificate found and creating one failed!\n"),
888                      "transport-https");
889     GNUNET_free (key_file);
890     GNUNET_free (cert_file);
891
892     GNUNET_free_non_null (plugin->key);
893     plugin->key = NULL;
894     GNUNET_free_non_null (plugin->cert);
895     plugin->cert = NULL;
896     GNUNET_free_non_null (plugin->crypto_init);
897     plugin->crypto_init = NULL;
898
899     return GNUNET_SYSERR;
900   }
901   GNUNET_free (key_file);
902   GNUNET_free (cert_file);
903   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
904   return res;
905 }
906 #endif
907
908 int
909 server_start (struct HTTP_Server_Plugin *plugin)
910 {
911   unsigned int timeout;
912   GNUNET_assert (NULL != plugin);
913
914 #if BUILD_HTTPS
915   if (GNUNET_SYSERR == server_load_certificate (plugin))
916   {
917     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
918                      "Could not load or create server certificate! Loading plugin failed!\n");
919     return GNUNET_SYSERR;
920   }
921 #endif
922
923
924 #if MHD_VERSION >= 0x00090E00
925   timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000;
926   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
927                    "MHD can set timeout per connection! Default time out %u sec.\n",
928                    timeout);
929 #else
930   timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
931   GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
932                    "MHD cannot set timeout per connection! Default time out %u sec.\n",
933                    timeout);
934 #endif
935   plugin->server_v4 = NULL;
936   if (plugin->use_ipv4 == GNUNET_YES)
937   {
938     plugin->server_v4 = MHD_start_daemon (
939 #if VERBOSE_SERVER
940                                            MHD_USE_DEBUG |
941 #endif
942 #if BUILD_HTTPS
943                                            MHD_USE_SSL |
944 #endif
945                                            MHD_NO_FLAG, plugin->port,
946                                            &server_accept_cb, plugin,
947                                            &server_access_cb, plugin,
948                                            MHD_OPTION_SOCK_ADDR,
949                                            (struct sockaddr_in *)
950                                            plugin->server_addr_v4,
951                                            MHD_OPTION_CONNECTION_LIMIT,
952                                            (unsigned int)
953                                            plugin->max_connections,
954 #if BUILD_HTTPS
955                                            MHD_OPTION_HTTPS_PRIORITIES,
956                                            plugin->crypto_init,
957                                            MHD_OPTION_HTTPS_MEM_KEY,
958                                            plugin->key,
959                                            MHD_OPTION_HTTPS_MEM_CERT,
960                                            plugin->cert,
961 #endif
962                                            MHD_OPTION_CONNECTION_TIMEOUT,
963                                            timeout,
964                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
965                                            (size_t) (2 *
966                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
967                                            MHD_OPTION_NOTIFY_COMPLETED,
968                                            &server_disconnect_cb, plugin,
969                                            MHD_OPTION_EXTERNAL_LOGGER,
970                                            server_log, NULL, MHD_OPTION_END);
971   }
972   plugin->server_v6 = NULL;
973   if (plugin->use_ipv6 == GNUNET_YES)
974   {
975     plugin->server_v6 = MHD_start_daemon (
976 #if VERBOSE_SERVER
977                                            MHD_USE_DEBUG |
978 #endif
979 #if BUILD_HTTPS
980                                            MHD_USE_SSL |
981 #endif
982                                            MHD_USE_IPv6, plugin->port,
983                                            &server_accept_cb, plugin,
984                                            &server_access_cb, plugin,
985                                            MHD_OPTION_SOCK_ADDR,
986                                            (struct sockaddr_in6 *)
987                                            plugin->server_addr_v6,
988                                            MHD_OPTION_CONNECTION_LIMIT,
989                                            (unsigned int)
990                                            plugin->max_connections,
991 #if BUILD_HTTPS
992                                            MHD_OPTION_HTTPS_PRIORITIES,
993                                            plugin->crypto_init,
994                                            MHD_OPTION_HTTPS_MEM_KEY,
995                                            plugin->key,
996                                            MHD_OPTION_HTTPS_MEM_CERT,
997                                            plugin->cert,
998 #endif
999                                            MHD_OPTION_CONNECTION_TIMEOUT,
1000                                            timeout,
1001                                            MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1002                                            (size_t) (2 *
1003                                                      GNUNET_SERVER_MAX_MESSAGE_SIZE),
1004                                            MHD_OPTION_NOTIFY_COMPLETED,
1005                                            &server_disconnect_cb, plugin,
1006                                            MHD_OPTION_EXTERNAL_LOGGER,
1007                                            server_log, NULL, MHD_OPTION_END);
1008
1009   }
1010
1011   if ((plugin->use_ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1012   {
1013     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1014                      "Failed to start %s IPv4 server component on port %u\n",
1015                      plugin->name, plugin->port);
1016     return GNUNET_SYSERR;
1017   }
1018   server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1019
1020   if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1021   {
1022     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1023                      "Failed to start %s IPv6 server component on port %u\n",
1024                      plugin->name, plugin->port);
1025     return GNUNET_SYSERR;
1026   }
1027   server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1028   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1029                    "%s server component started on port %u\n", plugin->name,
1030                    plugin->port);
1031   return GNUNET_OK;
1032 }
1033
1034
1035 void
1036 server_stop (struct HTTP_Server_Plugin *plugin)
1037 {
1038   struct Session *s = NULL;
1039   struct Session *t = NULL;
1040
1041   struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1042   plugin->server_v4 = NULL;
1043
1044   struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1045   plugin->server_v6 = NULL;
1046
1047   if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1048   {
1049     GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1050     plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1051   }
1052
1053   if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1054   {
1055     GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1056     plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1057   }
1058
1059   if (server_v6_tmp != NULL)
1060   {
1061     MHD_stop_daemon (server_v4_tmp);
1062   }
1063   if (server_v6_tmp != NULL)
1064   {
1065     MHD_stop_daemon (server_v6_tmp);
1066   }
1067
1068   /* cleaning up semi-sessions never propagated */
1069   s = plugin->server_semi_head;
1070   while (s != NULL)
1071   {
1072 #if VERBOSE_SERVER
1073     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1074                      "Deleting semi-sessions %p\n", s);
1075 #endif
1076     t = s->next;
1077     struct HTTP_Message *msg = s->msg_head;
1078     struct HTTP_Message *tmp = NULL;
1079
1080     while (msg != NULL)
1081     {
1082       tmp = msg->next;
1083
1084       GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
1085       if (msg->transmit_cont != NULL)
1086       {
1087         msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
1088       }
1089       GNUNET_free (msg);
1090       msg = tmp;
1091     }
1092
1093     server_delete_session (s);
1094     s = t;
1095   }
1096
1097   p = NULL;
1098
1099 #if BUILD_HTTPS
1100   GNUNET_free_non_null (plugin->crypto_init);
1101   GNUNET_free_non_null (plugin->cert);
1102   GNUNET_free_non_null (plugin->key);
1103 #endif
1104
1105   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1106                    "%s server component stopped\n", plugin->name);
1107 }
1108
1109 static void
1110 server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
1111                  socklen_t addrlen)
1112 {
1113   struct HTTP_Server_Plugin *plugin = cls;
1114   struct HttpAddressWrapper *w = NULL;
1115
1116   if ((AF_INET == addr->sa_family) && (GNUNET_NO == plugin->use_ipv4))
1117     return;
1118
1119   if ((AF_INET6 == addr->sa_family) && (GNUNET_NO == plugin->use_ipv6))
1120     return;
1121
1122   w = GNUNET_malloc (sizeof (struct HttpAddressWrapper));
1123   w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1124   if (NULL == w->addr)
1125   {
1126     GNUNET_free (w);
1127     return;
1128   }
1129   w->addrlen = http_common_address_get_size (w->addr);
1130
1131   GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w);
1132   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1133                    "Notifying transport to add address `%s'\n",
1134                    http_common_plugin_address_to_string(NULL, w->addr, w->addrlen));
1135
1136   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1137 }
1138
1139
1140 static void
1141 server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
1142                     socklen_t addrlen)
1143 {
1144   struct HTTP_Server_Plugin *plugin = cls;
1145   struct HttpAddressWrapper *w = plugin->addr_head;
1146   size_t saddr_len;
1147   void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1148   if (NULL == saddr)
1149     return;
1150   saddr_len =  http_common_address_get_size (saddr);
1151
1152   while (NULL != w)
1153   {
1154       if (GNUNET_YES == http_common_cmp_addresses(w->addr, w->addrlen, saddr, saddr_len))
1155         break;
1156       w = w->next;
1157   }
1158   GNUNET_free (saddr);
1159
1160   if (NULL == w)
1161     return;
1162
1163   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1164                    "Notifying transport to remove address `%s'\n",
1165                    http_common_plugin_address_to_string (NULL, w->addr, w->addrlen));
1166   GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
1167   plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1168   GNUNET_free (w->addr);
1169   GNUNET_free (w);
1170 }
1171
1172
1173
1174 /**
1175  * Our external IP address/port mapping has changed.
1176  *
1177  * @param cls closure, the 'struct LocalAddrList'
1178  * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
1179  *     the previous (now invalid) one
1180  * @param addr either the previous or the new public IP address
1181  * @param addrlen actual lenght of the address
1182  */
1183 static void
1184 server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr,
1185                        socklen_t addrlen)
1186 {
1187   GNUNET_assert (cls != NULL);
1188   struct HTTP_Server_Plugin *plugin = cls;
1189
1190   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1191                    "NPMC called %s to address `%s'\n",
1192                    (add_remove == GNUNET_NO) ? "remove" : "add",
1193                    GNUNET_a2s (addr, addrlen));
1194
1195   switch (add_remove)
1196   {
1197   case GNUNET_YES:
1198     server_add_address (cls, add_remove, addr, addrlen);
1199     break;
1200   case GNUNET_NO:
1201     server_remove_address (cls, add_remove, addr, addrlen);
1202     break;
1203   }
1204 }
1205
1206
1207 static int
1208 server_get_addresses (struct HTTP_Server_Plugin *plugin,
1209                       const char *serviceName,
1210                       const struct GNUNET_CONFIGURATION_Handle *cfg,
1211                       struct sockaddr ***addrs, socklen_t ** addr_lens)
1212 {
1213   int disablev6;
1214   unsigned long long port;
1215   struct addrinfo hints;
1216   struct addrinfo *res;
1217   struct addrinfo *pos;
1218   struct addrinfo *next;
1219   unsigned int i;
1220   int resi;
1221   int ret;
1222   struct sockaddr **saddrs;
1223   socklen_t *saddrlens;
1224   char *hostname;
1225
1226   *addrs = NULL;
1227   *addr_lens = NULL;
1228
1229   disablev6 = !plugin->use_ipv6;
1230
1231   port = 0;
1232   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT"))
1233   {
1234     GNUNET_break (GNUNET_OK ==
1235                   GNUNET_CONFIGURATION_get_value_number (cfg, serviceName,
1236                                                          "PORT", &port));
1237     if (port > 65535)
1238     {
1239       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1240                   _
1241                   ("Require valid port number for service in configuration!\n"));
1242       return GNUNET_SYSERR;
1243     }
1244   }
1245   if (0 == port)
1246   {
1247     GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
1248                      "Starting in listen only mode\n");
1249     return -1; /* listen only */
1250   }
1251
1252
1253   if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO"))
1254   {
1255     GNUNET_break (GNUNET_OK ==
1256                   GNUNET_CONFIGURATION_get_value_string (cfg, serviceName,
1257                                                          "BINDTO", &hostname));
1258   }
1259   else
1260     hostname = NULL;
1261
1262   if (hostname != NULL)
1263   {
1264     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1265                      "Resolving `%s' since that is where `%s' will bind to.\n",
1266                      hostname, serviceName);
1267     memset (&hints, 0, sizeof (struct addrinfo));
1268     if (disablev6)
1269       hints.ai_family = AF_INET;
1270     if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
1271         (res == NULL))
1272     {
1273       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"),
1274                   hostname, gai_strerror (ret));
1275       GNUNET_free (hostname);
1276       return GNUNET_SYSERR;
1277     }
1278     next = res;
1279     i = 0;
1280     while (NULL != (pos = next))
1281     {
1282       next = pos->ai_next;
1283       if ((disablev6) && (pos->ai_family == AF_INET6))
1284         continue;
1285       i++;
1286     }
1287     if (0 == i)
1288     {
1289       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1290                   _("Failed to find %saddress for `%s'.\n"),
1291                   disablev6 ? "IPv4 " : "", hostname);
1292       freeaddrinfo (res);
1293       GNUNET_free (hostname);
1294       return GNUNET_SYSERR;
1295     }
1296     resi = i;
1297     saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1298     saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1299     i = 0;
1300     next = res;
1301     while (NULL != (pos = next))
1302     {
1303       next = pos->ai_next;
1304       if ((disablev6) && (pos->ai_family == AF_INET6))
1305         continue;
1306       if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0))
1307         continue;               /* not TCP */
1308       if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0))
1309         continue;               /* huh? */
1310       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1311                        "Service will bind to `%s'\n", GNUNET_a2s (pos->ai_addr,
1312                                                                   pos->ai_addrlen));
1313       if (pos->ai_family == AF_INET)
1314       {
1315         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
1316         saddrlens[i] = pos->ai_addrlen;
1317         saddrs[i] = GNUNET_malloc (saddrlens[i]);
1318         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1319         ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1320       }
1321       else
1322       {
1323         GNUNET_assert (pos->ai_family == AF_INET6);
1324         GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
1325         saddrlens[i] = pos->ai_addrlen;
1326         saddrs[i] = GNUNET_malloc (saddrlens[i]);
1327         memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1328         ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1329       }
1330       i++;
1331     }
1332     GNUNET_free (hostname);
1333     freeaddrinfo (res);
1334     resi = i;
1335   }
1336   else
1337   {
1338     /* will bind against everything, just set port */
1339     if (disablev6)
1340     {
1341       /* V4-only */
1342       resi = 1;
1343       i = 0;
1344       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1345       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1346
1347       saddrlens[i] = sizeof (struct sockaddr_in);
1348       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1349 #if HAVE_SOCKADDR_IN_SIN_LEN
1350       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
1351 #endif
1352       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1353       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1354     }
1355     else
1356     {
1357       /* dual stack */
1358       resi = 2;
1359       saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1360       saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1361       i = 0;
1362       saddrlens[i] = sizeof (struct sockaddr_in6);
1363       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1364 #if HAVE_SOCKADDR_IN_SIN_LEN
1365       ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
1366 #endif
1367       ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
1368       ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1369       i++;
1370       saddrlens[i] = sizeof (struct sockaddr_in);
1371       saddrs[i] = GNUNET_malloc (saddrlens[i]);
1372 #if HAVE_SOCKADDR_IN_SIN_LEN
1373       ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
1374 #endif
1375       ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1376       ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1377     }
1378   }
1379   *addrs = saddrs;
1380   *addr_lens = saddrlens;
1381   return resi;
1382 }
1383
1384 static void
1385 server_start_report_addresses (struct HTTP_Server_Plugin *plugin)
1386 {
1387   int res = GNUNET_OK;
1388   struct sockaddr **addrs;
1389   socklen_t *addrlens;
1390
1391   res = server_get_addresses (plugin,
1392                               plugin->name, plugin->env->cfg,
1393                               &addrs, &addrlens);
1394   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1395                    _("Found %u addresses to report to NAT service\n"), res);
1396
1397   if (GNUNET_SYSERR == res)
1398   {
1399     plugin->nat = NULL;
1400     return;
1401   }
1402
1403   plugin->nat =
1404       GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port,
1405                            (unsigned int) res,
1406                            (const struct sockaddr **) addrs, addrlens,
1407                            &server_nat_port_map_callback, NULL, plugin);
1408   while (res > 0)
1409   {
1410     res--;
1411     GNUNET_assert (addrs[res] != NULL);
1412     GNUNET_free (addrs[res]);
1413   }
1414   GNUNET_free_non_null (addrs);
1415   GNUNET_free_non_null (addrlens);
1416 }
1417
1418
1419 static void
1420 server_stop_report_addresses (struct HTTP_Server_Plugin *plugin)
1421 {
1422   /* Stop NAT handle */
1423   if (NULL != plugin->nat)
1424     GNUNET_NAT_unregister (plugin->nat);
1425
1426   /* Clean up addresses */
1427   struct HttpAddressWrapper *w;
1428
1429   while (plugin->addr_head != NULL)
1430   {
1431     w = plugin->addr_head;
1432     GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
1433     GNUNET_free (w->addr);
1434     GNUNET_free (w);
1435   }
1436 }
1437
1438
1439 /**
1440  * Check if IPv6 supported on this system
1441  */
1442 static int
1443 server_check_ipv6_support (struct HTTP_Server_Plugin *plugin)
1444 {
1445   struct GNUNET_NETWORK_Handle *desc = NULL;
1446   int res = GNUNET_NO;
1447
1448   /* Probe IPv6 support */
1449   desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
1450   if (NULL == desc)
1451   {
1452     if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
1453         (errno == EACCES))
1454     {
1455       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
1456     }
1457     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1458                      _
1459                      ("Disabling IPv6 since it is not supported on this system!\n"));
1460     res = GNUNET_NO;
1461   }
1462   else
1463   {
1464     GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
1465     desc = NULL;
1466     res = GNUNET_YES;
1467   }
1468   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1469                    "Testing IPv6 on this system: %s\n",
1470                    (res == GNUNET_YES) ? "successful" : "failed");
1471   return res;
1472 }
1473
1474
1475 /**
1476  * Function called when the service shuts down.  Unloads our plugins
1477  * and cancels pending validations.
1478  *
1479  * @param cls closure, unused
1480  * @param tc task context (unused)
1481  */
1482 static void
1483 server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1484 {
1485   struct HTTP_Server_Plugin *plugin = cls;
1486
1487   plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
1488
1489   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1490     return;
1491
1492   GNUNET_asprintf(&plugin->ext_addr, "%s://%s", plugin->protocol, plugin->external_hostname);
1493   plugin->ext_addr_len = strlen (plugin->ext_addr) + 1;
1494   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1495                    "Notifying transport about external hostname address `%s'\n", plugin->ext_addr);
1496   plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr, plugin->ext_addr_len );
1497 }
1498
1499
1500 static int
1501 server_configure_plugin (struct HTTP_Server_Plugin *plugin)
1502 {
1503   unsigned long long port;
1504   unsigned long long max_connections;
1505   char *bind4_address = NULL;
1506   char *bind6_address = NULL;
1507
1508   /* Use IPv4? */
1509   if (GNUNET_CONFIGURATION_have_value
1510       (plugin->env->cfg, plugin->name, "USE_IPv4"))
1511   {
1512     plugin->use_ipv4 =
1513         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
1514                                               "USE_IPv4");
1515   }
1516   else
1517     plugin->use_ipv4 = GNUNET_YES;
1518   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1519                    _("IPv4 support is %s\n"),
1520                    (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled");
1521
1522   /* Use IPv6? */
1523   if (GNUNET_CONFIGURATION_have_value
1524       (plugin->env->cfg, plugin->name, "USE_IPv6"))
1525   {
1526     plugin->use_ipv6 =
1527         GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
1528                                               "USE_IPv6");
1529   }
1530   else
1531     plugin->use_ipv6 = GNUNET_YES;
1532   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1533                    _("IPv6 support is %s\n"),
1534                    (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled");
1535
1536   if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO))
1537   {
1538     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1539                      _
1540                      ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"),
1541                      plugin->name);
1542     return GNUNET_SYSERR;
1543   }
1544
1545   /* Reading port number from config file */
1546   if ((GNUNET_OK !=
1547        GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name,
1548                                               "PORT", &port)) || (port > 65535))
1549   {
1550     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1551                      _("Port is required! Fix in configuration\n"),
1552                      plugin->name);
1553     return GNUNET_SYSERR;
1554   }
1555   plugin->port = port;
1556
1557   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1558                    _("Using port %u\n"), plugin->port);
1559
1560   if ((plugin->use_ipv4 == GNUNET_YES) &&
1561       (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1562                           plugin->name, "BINDTO", &bind4_address)))
1563   {
1564     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1565                      "Binding %s plugin to specific IPv4 address: `%s'\n",
1566                      plugin->protocol, bind4_address);
1567     plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
1568     if (1 != inet_pton (AF_INET, bind4_address,
1569                         &plugin->server_addr_v4->sin_addr))
1570     {
1571         GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1572                          _
1573                          ("Specific IPv4 address `%s' in configuration file is invalid!\n"),
1574                          bind4_address);
1575       GNUNET_free (bind4_address);
1576       GNUNET_free (plugin->server_addr_v4);
1577       plugin->server_addr_v4 = NULL;
1578       return GNUNET_SYSERR;
1579     }
1580     else
1581     {
1582       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1583                          _("Binding to IPv4 address %s\n"), bind4_address);
1584       plugin->server_addr_v4->sin_family = AF_INET;
1585       plugin->server_addr_v4->sin_port = htons (plugin->port);
1586     }
1587     GNUNET_free (bind4_address);
1588   }
1589
1590   if ((plugin->use_ipv6 == GNUNET_YES) &&
1591       (GNUNET_YES ==
1592        GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
1593                                               "BINDTO6", &bind6_address)))
1594   {
1595     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1596                      "Binding %s plugin to specific IPv6 address: `%s'\n",
1597                      plugin->protocol, bind6_address);
1598     plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
1599     if (1 !=
1600         inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr))
1601     {
1602       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1603                        _
1604                        ("Specific IPv6 address `%s' in configuration file is invalid!\n"),
1605                        bind6_address);
1606       GNUNET_free (bind6_address);
1607       GNUNET_free (plugin->server_addr_v6);
1608       plugin->server_addr_v6 = NULL;
1609       return GNUNET_SYSERR;
1610     }
1611     else
1612     {
1613       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1614                          _("Binding to IPv6 address %s\n"), bind6_address);
1615       plugin->server_addr_v6->sin6_family = AF_INET6;
1616       plugin->server_addr_v6->sin6_port = htons (plugin->port);
1617     }
1618     GNUNET_free (bind6_address);
1619   }
1620
1621   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
1622                                               "EXTERNAL_HOSTNAME", &plugin->external_hostname))
1623   {
1624       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1625                        _("Using external hostname `%s'\n"), plugin->external_hostname);
1626       plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin);
1627   }
1628   else
1629     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1630                      "No external hostname configured\n");
1631
1632
1633   /* Optional parameters */
1634   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1635                       plugin->name,
1636                       "MAX_CONNECTIONS", &max_connections))
1637     max_connections = 128;
1638   plugin->max_connections = max_connections;
1639
1640   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1641                    _("Maximum number of connections is %u\n"),
1642                    plugin->max_connections);
1643   return GNUNET_OK;
1644 }
1645
1646
1647 /**
1648  * Exit point from the plugin.
1649  */
1650 void *
1651 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1652 {
1653   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1654   struct HTTP_Server_Plugin *plugin = api->cls;
1655
1656   if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task)
1657   {
1658       GNUNET_SCHEDULER_cancel (plugin->notify_ext_task);
1659       plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
1660   }
1661
1662   if (NULL != plugin->ext_addr)
1663   {
1664       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1665                        "Notifying transport to remove address `%s'\n",
1666                        http_common_plugin_address_to_string (NULL,
1667                            plugin->ext_addr,
1668                            plugin->ext_addr_len));
1669       plugin->env->notify_address (plugin->env->cls,
1670                                    GNUNET_NO,
1671                                    plugin->ext_addr,
1672                                    plugin->ext_addr_len);
1673   }
1674
1675   /* Stop to report addresses to transport service */
1676   server_stop_report_addresses (plugin);
1677
1678   server_stop (plugin);
1679
1680   /* Clean up */
1681   GNUNET_free_non_null (plugin->external_hostname);
1682   GNUNET_free_non_null (plugin->ext_addr);
1683   GNUNET_free_non_null (plugin->server_addr_v4);
1684   GNUNET_free_non_null (plugin->server_addr_v6);
1685
1686   GNUNET_free (plugin);
1687   GNUNET_free (api);
1688   return NULL;
1689 }
1690
1691
1692 /**
1693  * Entry point for the plugin.
1694  */
1695 void *
1696 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
1697 {
1698   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1699   struct GNUNET_TRANSPORT_PluginFunctions *api;
1700   struct HTTP_Server_Plugin *plugin;
1701
1702   plugin = GNUNET_malloc (sizeof (struct HTTP_Server_Plugin));
1703   plugin->env = env;
1704   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1705   api->cls = plugin;
1706   api->send = &http_server_plugin_send;
1707   api->disconnect = &http_server_plugin_disconnect;
1708   api->check_address = &http_server_plugin_address_suggested;
1709   api->get_session = &http_server_plugin_get_session;
1710
1711   api->address_to_string = &http_common_plugin_address_to_string;
1712   api->string_to_address = &http_common_plugin_string_to_address;
1713   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1714
1715 #if BUILD_HTTPS
1716   plugin->name = "transport-https_server";
1717   plugin->protocol = "https";
1718 #else
1719   plugin->name = "transport-http_server";
1720   plugin->protocol = "http";
1721 #endif
1722
1723   /* Configure plugin */
1724   if (GNUNET_SYSERR == server_configure_plugin (plugin))
1725   {
1726       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1727       return NULL;
1728   }
1729
1730   /* Check IPv6 support */
1731   if (GNUNET_YES == plugin->use_ipv6)
1732     plugin->use_ipv6 = server_check_ipv6_support (plugin);
1733
1734   /* Report addresses to transport service */
1735   server_start_report_addresses (plugin);
1736
1737   if (GNUNET_SYSERR == server_start (plugin))
1738   {
1739       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1740       return NULL;
1741   }
1742
1743   return api;
1744 }
1745
1746
1747
1748
1749 /* end of plugin_transport_http_server.c */