(no commit message)
[oweals/gnunet.git] / src / transport / plugin_transport_http.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 2, 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_template.c
23  * @brief template for a new transport service
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include "gnunet_constants.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_connection_lib.h"
31 #include "gnunet_server_lib.h"
32 #include "gnunet_service_lib.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_transport_service.h"
35 #include "gnunet_resolver_service.h"
36 #include "plugin_transport.h"
37 #include "gnunet_os_lib.h"
38 #include "microhttpd.h"
39 #include <curl/curl.h>
40
41 #define DEBUG_HTTP GNUNET_NO
42
43 /**
44  * Text of the response sent back after the last bytes of a PUT
45  * request have been received (just to formally obey the HTTP
46  * protocol).
47  */
48 #define HTTP_PUT_RESPONSE "Thank you!"
49
50 /**
51  * After how long do we expire an address that we
52  * learned from another peer if it is not reconfirmed
53  * by anyone?
54  */
55 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
56
57 /**
58  * Page returned if request invalid
59  */
60 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
61
62 /**
63  * Timeout for a http connect
64  */
65 #define HTTP_CONNECT_TIMEOUT 30
66
67 /**
68  * Timeout for a http connect
69  */
70 #define HTTP_MESSAGE_INITIAL_BUFFERSIZE GNUNET_SERVER_MAX_MESSAGE_SIZE
71
72
73 /**
74  * Encapsulation of all of the state of the plugin.
75  */
76 struct Plugin;
77
78 /**
79  * Network format for IPv4 addresses.
80  */
81 struct IPv4HttpAddress
82 {
83   /**
84    * IPv4 address, in network byte order.
85    */
86   uint32_t ipv4_addr;
87
88   /**
89    * Port number, in network byte order.
90    */
91   uint16_t u_port;
92
93 };
94
95
96 /**
97  * Network format for IPv6 addresses.
98  */
99 struct IPv6HttpAddress
100 {
101   /**
102    * IPv6 address.
103    */
104   struct in6_addr ipv6_addr;
105
106   /**
107    * Port number, in network byte order.
108    */
109   uint16_t u6_port;
110
111 };
112
113
114
115 /**
116  *  Message to send using http
117  */
118 struct HTTP_Message
119 {
120   /**
121    * Next field for linked list
122    */
123   struct HTTP_Message * next;
124
125   /**
126    * buffer containing data to send
127    */
128   char *buf;
129
130   /**
131    * amount of data already sent
132    */
133   size_t pos;
134
135   /**
136    * amount of data to sent
137    */
138   size_t len;
139 };
140
141
142 /**
143  * Session handle for connections.
144  */
145 struct Session
146 {
147
148   /**
149    * Stored in a linked list.
150    */
151   struct Session *next;
152
153   /**
154    * Pointer to the global plugin struct.
155    */
156   struct Plugin *plugin;
157
158   /**
159    * Continuation function to call once the transmission buffer
160    * has again space available.  NULL if there is no
161    * continuation to call.
162    */
163   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
164
165   /**
166    * Closure for transmit_cont.
167    */
168   void *transmit_cont_cls;
169
170   /**
171    * To whom are we talking to (set to our identity
172    * if we are still waiting for the welcome message)
173    */
174   struct GNUNET_PeerIdentity sender;
175
176   /**
177    * Sender's url
178    */
179   char * url;
180
181   /**
182    * Sender's ip address to distinguish between incoming connections
183    */
184   char * ip;
185
186   /**
187    * Sender's ip address to distinguish between incoming connections
188    */
189   struct sockaddr_in * addr;
190
191   /**
192    * Did we initiate the connection (GNUNET_YES) or the other peer (GNUNET_NO)?
193    */
194   unsigned int is_client;
195
196   /**
197    * Is the connection active (GNUNET_YES) or terminated (GNUNET_NO)?
198    */
199   unsigned int is_active;
200
201   /**
202    * At what time did we reset last_received last?
203    */
204   struct GNUNET_TIME_Absolute last_quota_update;
205
206   /**
207    * How many bytes have we received since the "last_quota_update"
208    * timestamp?
209    */
210   uint64_t last_received;
211
212   /**
213    * Number of bytes per ms that this peer is allowed
214    * to send to us.
215    */
216   uint32_t quota;
217
218   /**
219    * Is there a HTTP/PUT in progress?
220    */
221   unsigned int is_put_in_progress;
222
223   /**
224    * Is there a HTTP/PUT in progress?
225    */
226   unsigned int is_bad_request;
227
228   /**
229    * Encoded hash
230    */
231   struct GNUNET_CRYPTO_HashAsciiEncoded hash;
232
233   struct HTTP_Message * pending_outbound_msg;;
234
235   struct HTTP_Message * pending_inbound_msg;
236
237   CURL *curl_handle;
238 };
239
240 /**
241  * Encapsulation of all of the state of the plugin.
242  */
243 struct Plugin
244 {
245   /**
246    * Our environment.
247    */
248   struct GNUNET_TRANSPORT_PluginEnvironment *env;
249
250   /**
251    * Handle to the network service.
252    */
253   struct GNUNET_SERVICE_Context *service;
254
255   unsigned int port_inbound;
256
257   /**
258    * List of open sessions.
259    */
260   struct Session *sessions;
261
262   /**
263    * Number of active sessions
264    */
265
266   unsigned int session_count;
267 };
268
269 /**
270  * Daemon for listening for new IPv4 connections.
271  */
272 static struct MHD_Daemon *http_daemon_v4;
273
274 /**
275  * Daemon for listening for new IPv6connections.
276  */
277 static struct MHD_Daemon *http_daemon_v6;
278
279 /**
280  * Our primary task for http daemon handling IPv4 connections
281  */
282 static GNUNET_SCHEDULER_TaskIdentifier http_task_v4;
283
284 /**
285  * Our primary task for http daemon handling IPv6 connections
286  */
287 static GNUNET_SCHEDULER_TaskIdentifier http_task_v6;
288
289
290 /**
291  * The task sending data
292  */
293 static GNUNET_SCHEDULER_TaskIdentifier http_task_send;
294
295
296 /**
297  * Information about this plugin
298  */
299 static struct Plugin *plugin;
300
301 /**
302  * cURL Multihandle
303  */
304 static CURLM *multi_handle;
305
306 /**
307  * Our hostname
308  */
309 static char * hostname;
310
311 /**
312  * Our ASCII encoded, hashed peer identity
313  * This string is used to distinguish between connections and is added to the urls
314  */
315 static struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident;
316
317 struct GNUNET_TIME_Relative timeout;
318
319 /**
320  * Finds a http session in our linked list using peer identity as a key
321  * @param peer peeridentity
322  * @return http session corresponding to peer identity
323  */
324 static struct Session * find_session_by_pi( const struct GNUNET_PeerIdentity *peer )
325 {
326   struct Session * cur;
327   GNUNET_HashCode hc_peer;
328   GNUNET_HashCode hc_current;
329
330   cur = plugin->sessions;
331   hc_peer = peer->hashPubKey;
332   while (cur != NULL)
333   {
334     hc_current = cur->sender.hashPubKey;
335     if ( 0 == GNUNET_CRYPTO_hash_cmp( &hc_peer, &hc_current))
336       return cur;
337     cur = plugin->sessions->next;
338   }
339   return NULL;
340 }
341
342 /**
343  * Finds a http session in our linked list using libcurl handle as a key
344  * Needed when sending data with libcurl to differentiate between sessions
345  * @param handle peeridentity
346  * @return http session corresponding to peer identity
347  */
348 static struct Session * find_session_by_curlhandle( CURL* handle )
349 {
350   struct Session * cur;
351
352   cur = plugin->sessions;
353   while (cur != NULL)
354   {
355     if ( handle == cur->curl_handle )
356       return cur;
357     cur = plugin->sessions->next;
358   }
359   return NULL;
360 }
361
362 /**
363  * Create a new session
364  *
365  * @param address address the peer is using
366  * @param peer identity
367  * @return created session object
368  */
369 static struct Session * create_session (struct sockaddr_in *address, const struct GNUNET_PeerIdentity *peer)
370 {
371   struct sockaddr_in  *addrin;
372   struct sockaddr_in6 *addrin6;
373
374   struct Session * ses = GNUNET_malloc ( sizeof( struct Session) );
375   ses->addr = GNUNET_malloc ( sizeof (struct sockaddr_in) );
376
377   ses->next = NULL;
378   ses->plugin = plugin;
379
380   memcpy(ses->addr, address, sizeof (struct sockaddr_in));
381   if ( AF_INET == address->sin_family)
382   {
383     ses->ip = GNUNET_malloc (INET_ADDRSTRLEN);
384     addrin = address;
385     inet_ntop(addrin->sin_family,&(addrin->sin_addr),ses->ip,INET_ADDRSTRLEN);
386   }
387   if ( AF_INET6 == address->sin_family)
388   {
389     ses->ip = GNUNET_malloc (INET6_ADDRSTRLEN);
390     addrin6 = (struct sockaddr_in6 *) address;
391     inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr) ,ses->ip,INET6_ADDRSTRLEN);
392   }
393   memcpy(&ses->sender, peer, sizeof (struct GNUNET_PeerIdentity));
394   GNUNET_CRYPTO_hash_to_enc(&ses->sender.hashPubKey,&(ses->hash));
395   ses->is_active = GNUNET_NO;
396
397   return ses;
398 }
399
400 /**
401  * Callback called by MHD when a connection is terminated
402  */
403 static void requestCompletedCallback (void *cls, struct MHD_Connection * connection, void **httpSessionCache)
404 {
405   struct Session * cs;
406
407   cs = *httpSessionCache;
408   if (cs != NULL)
409   {
410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection from peer `%s' was terminated\n",GNUNET_i2s(&cs->sender));
411     /* session set to inactive */
412     cs->is_active = GNUNET_NO;
413     cs->is_put_in_progress = GNUNET_NO;
414   }
415   return;
416 }
417
418 /**
419  * Check if we are allowed to connect to the given IP.
420  */
421 static int
422 acceptPolicyCallback (void *cls,
423                       const struct sockaddr *addr, socklen_t addr_len)
424 {
425   /* Every connection is accepted, nothing more to do here */
426   return MHD_YES;
427 }
428
429
430 int serror;
431
432 /**
433  * Process GET or PUT request received via MHD.  For
434  * GET, queue response that will send back our pending
435  * messages.  For PUT, process incoming data and send
436  * to GNUnet core.  In either case, check if a session
437  * already exists and create a new one if not.
438  */
439 static int
440 accessHandlerCallback (void *cls,
441                        struct MHD_Connection *session,
442                        const char *url,
443                        const char *method,
444                        const char *version,
445                        const char *upload_data,
446                        size_t * upload_data_size, void **httpSessionCache)
447 {
448   struct MHD_Response *response;
449   struct Session * cs;
450   struct Session * cs_temp;
451   const union MHD_ConnectionInfo * conn_info;
452   struct sockaddr_in  *addrin;
453   struct sockaddr_in6 *addrin6;
454   char * address = NULL;
455   struct GNUNET_PeerIdentity pi_in;
456   int res = GNUNET_NO;
457   size_t bytes_recv;
458   struct GNUNET_MessageHeader *gn_msg;
459   int send_error_to_client;
460
461   gn_msg = NULL;
462   send_error_to_client = GNUNET_NO;
463
464   if ( NULL == *httpSessionCache)
465   {
466     /* check url for peer identity */
467     res = GNUNET_CRYPTO_hash_from_string ( &url[1], &(pi_in.hashPubKey));
468     if ( GNUNET_SYSERR == res )
469     {
470       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident\n");
471       response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
472       res = MHD_queue_response (session, MHD_HTTP_NOT_FOUND, response);
473       MHD_destroy_response (response);
474       return res;
475     }
476
477     conn_info = MHD_get_connection_info(session, MHD_CONNECTION_INFO_CLIENT_ADDRESS );
478     /* Incoming IPv4 connection */
479     if ( AF_INET == conn_info->client_addr->sin_family)
480     {
481       address = GNUNET_malloc (INET_ADDRSTRLEN);
482       addrin = conn_info->client_addr;
483       inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN);
484     }
485     /* Incoming IPv6 connection */
486     if ( AF_INET6 == conn_info->client_addr->sin_family)
487     {
488       address = GNUNET_malloc (INET6_ADDRSTRLEN);
489       addrin6 = (struct sockaddr_in6 *) conn_info->client_addr;
490       inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN);
491     }
492     /* find existing session for address */
493     cs = NULL;
494     if (plugin->session_count > 0)
495     {
496       cs = plugin->sessions;
497       while ( NULL != cs)
498       {
499
500         /* Comparison based on ip address */
501         // res = (0 == memcmp(&(conn_info->client_addr->sin_addr),&(cs->addr->sin_addr), sizeof (struct in_addr))) ? GNUNET_YES : GNUNET_NO;
502
503         /* Comparison based on ip address, port number and address family */
504         // res = (0 == memcmp((conn_info->client_addr),(cs->addr), sizeof (struct sockaddr_in))) ? GNUNET_YES : GNUNET_NO;
505
506         /* Comparison based on PeerIdentity */
507         res = (0 == memcmp(&pi_in,&(cs->sender), sizeof (struct GNUNET_PeerIdentity))) ? GNUNET_YES : GNUNET_NO;
508
509         if ( GNUNET_YES  == res)
510         {
511           /* existing session for this address found */
512           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session `%s' found\n",address);
513           break;
514         }
515         cs = cs->next;
516       }
517     }
518     /* no existing session, create a new one*/
519     if (cs == NULL )
520     {
521       /* create new session object */
522       cs = create_session(conn_info->client_addr, &pi_in);
523
524       /* Insert session into linked list */
525       if ( plugin->sessions == NULL)
526       {
527         plugin->sessions = cs;
528         plugin->session_count = 1;
529       }
530       cs_temp = plugin->sessions;
531       while ( cs_temp->next != NULL )
532       {
533         cs_temp = cs_temp->next;
534       }
535       if (cs_temp != cs )
536       {
537         cs_temp->next = cs;
538         plugin->session_count++;
539       }
540       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", address, plugin->session_count);
541     }
542     /* Set closure */
543     if (*httpSessionCache == NULL)
544     {
545       *httpSessionCache = cs;
546     }
547     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Daemon has new an incoming `%s' request from peer `%s' (`[%s]:%u')\n",method, GNUNET_i2s(&cs->sender),cs->ip,cs->addr->sin_port);
548   }
549   else
550   {
551     cs = *httpSessionCache;
552   }
553   /* Is it a PUT or a GET request */
554   if ( 0 == strcmp (MHD_HTTP_METHOD_PUT, method) )
555   {
556     /* New  */
557     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_NO))
558     {
559       if (cs->pending_inbound_msg != NULL)
560       {
561         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
562                     _("Incoming message from peer `%s, while existing message with %u bytes was not forwarded to transport'\n"),
563                     GNUNET_i2s(&cs->sender), cs->pending_inbound_msg->pos);
564
565         /* reusing memory for new message */
566         cs->pending_inbound_msg->next = NULL;
567         cs->pending_inbound_msg->pos = 0;
568       }
569       else
570       {
571         cs->pending_inbound_msg = GNUNET_malloc( sizeof (struct HTTP_Message));
572
573         /* FIXME: Don't alloc GNUNET_SERVER_MAX_MESSAGE_SIZE initially. Instead use less and realloc if more mem is needed */
574         cs->pending_inbound_msg->buf = GNUNET_malloc(GNUNET_SERVER_MAX_MESSAGE_SIZE);
575         cs->pending_inbound_msg->len = GNUNET_SERVER_MAX_MESSAGE_SIZE;
576         cs->pending_inbound_msg->pos = 0;
577       }
578
579
580       /* not yet ready */
581       cs->is_put_in_progress = GNUNET_YES;
582       cs->is_active = GNUNET_YES;
583       return MHD_YES;
584     }
585
586     if ((*upload_data_size > 0) && (*upload_data_size + cs->pending_inbound_msg->pos < cs->pending_inbound_msg->len) && (*upload_data_size + cs->pending_inbound_msg->pos <= GNUNET_SERVER_MAX_MESSAGE_SIZE))
587     {
588
589       bytes_recv = *upload_data_size ;
590
591       /* copy uploaded data to buffer */
592       memcpy(&cs->pending_inbound_msg->buf[cs->pending_inbound_msg->pos],upload_data,bytes_recv);
593       cs->pending_inbound_msg->pos += bytes_recv;
594       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Added %u bytes to message of %u bytes \n",bytes_recv, cs->pending_inbound_msg->pos);
595
596       *upload_data_size = 0;
597       return MHD_YES;
598
599       /* checking size */
600       #if 0
601       if (bytes_recv < sizeof (struct GNUNET_MessageHeader))
602       {
603         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Message too small, is %u bytes, has to be at least %u '\n",bytes_recv, sizeof(struct GNUNET_MessageHeader));
604         send_error_to_client = GNUNET_YES;
605       }
606
607       if ( bytes_recv > GNUNET_SERVER_MAX_MESSAGE_SIZE)
608       {
609         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Message too big, is %u bytes, maximum %u '\n",bytes_recv, GNUNET_SERVER_MAX_MESSAGE_SIZE);
610         send_error_to_client = GNUNET_YES;
611       }
612
613       struct GNUNET_MessageHeader * gn_msg = GNUNET_malloc (bytes_recv);
614       memcpy (gn_msg,upload_data,bytes_recv);
615
616       if ( ntohs(gn_msg->size) != bytes_recv )
617       {
618         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Malformed GNUnet: message has incorrect size, is %u bytes in header vs %u recieved\n",ntohs(gn_msg->size) , bytes_recv);
619         send_error_to_client = GNUNET_YES;
620       }
621
622       if (send_error_to_client == GNUNET_YES)
623       {
624         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Malformed GNUnet, should send error\n");
625         cs->is_bad_request = GNUNET_YES;
626         return MHD_YES;
627       }
628
629       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Recieved GNUnet message type %u size %u and payload %u \n",ntohs (gn_msg->type), ntohs (gn_msg->size), ntohs (gn_msg->size)-sizeof(struct GNUNET_MessageHeader));
630
631       /* forwarding message to transport */
632       plugin->env->receive(plugin->env, &(cs->sender), gn_msg, 1, cs , cs->ip, strlen(cs->ip) );
633       GNUNET_free (gn_msg);
634       #endif
635     }
636
637     if ((cs->is_bad_request == GNUNET_YES) && (cs->is_put_in_progress == GNUNET_YES))
638     {
639
640       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Info: size: %u method: %s \n",*upload_data_size,method);
641       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
642       if (response == NULL)
643         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"response NULL\n");
644       res = MHD_queue_response (session, MHD_HTTP_BAD_REQUEST, response);
645       if (res == MHD_YES)
646       {
647         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 400 BAD REQUEST as PUT Response\n");
648         cs->is_bad_request = GNUNET_NO;
649         cs->is_put_in_progress =GNUNET_NO;
650       }
651       else
652         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 400 BAD REQUEST as PUT Response not sent\n");
653       MHD_destroy_response (response);
654       *upload_data_size = 0;
655       return MHD_YES;
656     }
657
658     if ((*upload_data_size == 0) && (cs->is_put_in_progress == GNUNET_YES) && (cs->is_bad_request == GNUNET_NO))
659     {
660       cs->is_put_in_progress = GNUNET_NO;
661       response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
662       res = MHD_queue_response (session, MHD_HTTP_OK, response);
663       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sent HTTP/1.1: 200 OK as PUT Response\n",HTTP_PUT_RESPONSE, strlen (HTTP_PUT_RESPONSE), res );
664       MHD_destroy_response (response);
665       return res;
666     }
667   }
668   if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) )
669   {
670     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Got GET Request\n");
671     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"URL: `%s'\n",url);
672     response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO);
673     res = MHD_queue_response (session, MHD_HTTP_OK, response);
674     MHD_destroy_response (response);
675     return res;
676   }
677   return MHD_NO;
678 }
679
680
681 /**
682  * Call MHD to process pending requests and then go back
683  * and schedule the next run.
684  */
685 static void http_daemon_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
686
687 /**
688  * Function that queries MHD's select sets and
689  * starts the task waiting for them.
690  */
691 static GNUNET_SCHEDULER_TaskIdentifier
692 http_daemon_prepare (struct MHD_Daemon *daemon_handle)
693 {
694   GNUNET_SCHEDULER_TaskIdentifier ret;
695   fd_set rs;
696   fd_set ws;
697   fd_set es;
698   struct GNUNET_NETWORK_FDSet *wrs;
699   struct GNUNET_NETWORK_FDSet *wws;
700   struct GNUNET_NETWORK_FDSet *wes;
701   int max;
702   unsigned long long timeout;
703   int haveto;
704   struct GNUNET_TIME_Relative tv;
705
706   FD_ZERO(&rs);
707   FD_ZERO(&ws);
708   FD_ZERO(&es);
709   wrs = GNUNET_NETWORK_fdset_create ();
710   wes = GNUNET_NETWORK_fdset_create ();
711   wws = GNUNET_NETWORK_fdset_create ();
712   max = -1;
713   GNUNET_assert (MHD_YES ==
714                  MHD_get_fdset (daemon_handle,
715                                 &rs,
716                                 &ws,
717                                 &es,
718                                 &max));
719   haveto = MHD_get_timeout (daemon_handle, &timeout);
720   if (haveto == MHD_YES)
721     tv.value = (uint64_t) timeout;
722   else
723     tv = GNUNET_TIME_UNIT_FOREVER_REL;
724   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max);
725   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max);
726   GNUNET_NETWORK_fdset_copy_native (wes, &es, max);
727   ret = GNUNET_SCHEDULER_add_select (plugin->env->sched,
728                                      GNUNET_SCHEDULER_PRIORITY_HIGH,
729                                      GNUNET_SCHEDULER_NO_TASK,
730                                      tv,
731                                      wrs,
732                                      wws,
733                                      &http_daemon_run,
734                                      daemon_handle);
735   GNUNET_NETWORK_fdset_destroy (wrs);
736   GNUNET_NETWORK_fdset_destroy (wws);
737   GNUNET_NETWORK_fdset_destroy (wes);
738   return ret;
739 }
740
741 /**
742  * Call MHD to process pending requests and then go back
743  * and schedule the next run.
744  */
745 static void http_daemon_run (void *cls,
746                              const struct GNUNET_SCHEDULER_TaskContext *tc)
747 {
748   struct MHD_Daemon *daemon_handle = cls;
749
750   if (daemon_handle == http_daemon_v4)
751     http_task_v4 = GNUNET_SCHEDULER_NO_TASK;
752
753   if (daemon_handle == http_daemon_v6)
754     http_task_v6 = GNUNET_SCHEDULER_NO_TASK;
755
756   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
757     return;
758
759   GNUNET_assert (MHD_YES == MHD_run (daemon_handle));
760   if (daemon_handle == http_daemon_v4)
761     http_task_v4 = http_daemon_prepare (daemon_handle);
762   if (daemon_handle == http_daemon_v6)
763     http_task_v6 = http_daemon_prepare (daemon_handle);
764   return;
765 }
766
767 /**
768  * Removes a message from the linked list of messages
769  * @param ses session to remove message from
770  * @param msg message to remove
771  * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success
772  */
773
774 static int remove_http_message(struct Session * ses, struct HTTP_Message * msg)
775 {
776   struct HTTP_Message * cur;
777   struct HTTP_Message * next;
778
779   cur = ses->pending_outbound_msg;
780   next = NULL;
781
782   if (cur == NULL)
783     return GNUNET_SYSERR;
784
785   if (cur == msg)
786   {
787     ses->pending_outbound_msg = cur->next;
788     GNUNET_free (cur->buf);
789     GNUNET_free (cur);
790     cur = NULL;
791     return GNUNET_OK;
792   }
793
794   while (cur->next!=msg)
795   {
796     if (cur->next != NULL)
797       cur = cur->next;
798     else
799       return GNUNET_SYSERR;
800   }
801
802   cur->next = cur->next->next;
803   GNUNET_free (cur->next->buf);
804   GNUNET_free (cur->next);
805   cur->next = NULL;
806   return GNUNET_OK;
807 }
808
809
810 /**
811  * Callback method used with libcurl
812  * Method is called when libcurl needs to read data during sending
813  * @param stream pointer where to write data
814  * @param size size of an individual element
815  * @param nmemb count of elements that can be written to the buffer
816  * @param ptr source pointer, passed to the libcurl handle
817  * @return bytes written to stream
818  */
819 static size_t send_read_callback(void *stream, size_t size, size_t nmemb, void *ptr)
820 {
821   struct Session * ses = ptr;
822   struct HTTP_Message * msg = ses->pending_outbound_msg;
823   unsigned int bytes_sent;
824
825   bytes_sent = 0;
826   if (msg->len > (size * nmemb))
827     return CURL_READFUNC_ABORT;
828
829   if (( msg->pos < msg->len) && (msg->len < (size * nmemb)))
830   {
831     memcpy(stream, msg->buf, msg->len);
832     msg->pos = msg->len;
833     bytes_sent = msg->len;
834   }
835
836   return bytes_sent;
837 }
838
839 /**
840 * Callback method used with libcurl
841 * Method is called when libcurl needs to write data during sending
842 * @param stream pointer where to write data
843 * @param size size of an individual element
844 * @param nmemb count of elements that can be written to the buffer
845 * @param ptr destination pointer, passed to the libcurl handle
846 * @return bytes read from stream
847 */
848 static size_t send_write_callback( void *stream, size_t size, size_t nmemb, void *ptr)
849 {
850   char * data = malloc(size*nmemb +1);
851
852   memcpy( data, stream, size*nmemb);
853   data[size*nmemb] = '\0';
854   /* Just a dummy print for the response recieved for the PUT message */
855   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Recieved %u bytes: `%s' \n", size * nmemb, data);
856   free (data);
857   return (size * nmemb);
858
859 }
860
861 /**
862  * Function setting up file descriptors and scheduling task to run
863  * @param session session to send data to
864  * @return bytes sent to peer
865  */
866 static size_t send_prepare(struct Session* session );
867
868 /**
869  * Function setting up curl handle and selecting message to send
870  * @param ses session to send data to
871  * @return bytes sent to peer
872  */
873 static ssize_t send_select_init (struct Session* ses  )
874 {
875   char * url;
876   int bytes_sent = 0;
877   CURLMcode mret;
878   struct HTTP_Message * msg;
879
880   /* FIFO selection, send oldest msg first, perhaps priority here?  */
881   msg = ses->pending_outbound_msg;
882
883   url = GNUNET_malloc( 7 + strlen(ses->ip) + 7 + strlen ((char *) &(ses->hash)) + 1);
884   /* FIXME: use correct port number */
885   GNUNET_asprintf(&url,"http://%s:%u/%s",ses->ip,12389, (char *) &(ses->hash));
886
887   /* curl_easy_setopt(ses->curl_handle, CURLOPT_VERBOSE, 1L); */
888   curl_easy_setopt(ses->curl_handle, CURLOPT_URL, url);
889   curl_easy_setopt(ses->curl_handle, CURLOPT_PUT, 1L);
890   curl_easy_setopt(ses->curl_handle, CURLOPT_READFUNCTION, send_read_callback);
891   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
892   curl_easy_setopt(ses->curl_handle, CURLOPT_WRITEFUNCTION, send_write_callback);
893   curl_easy_setopt(ses->curl_handle, CURLOPT_READDATA, ses);
894   curl_easy_setopt(ses->curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) msg->len);
895   curl_easy_setopt(ses->curl_handle, CURLOPT_TIMEOUT, (long) (timeout.value / 1000 ));
896   curl_easy_setopt(ses->curl_handle, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT);
897
898   mret = curl_multi_add_handle(multi_handle, ses->curl_handle);
899   if (mret != CURLM_OK)
900   {
901     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
902                 _("%s failed at %s:%d: `%s'\n"),
903                 "curl_multi_add_handle", __FILE__, __LINE__,
904                 curl_multi_strerror (mret));
905     return -1;
906   }
907   bytes_sent = send_prepare (ses );
908   GNUNET_free ( url );
909   return bytes_sent;
910 }
911
912 static void send_execute (void *cls,
913              const struct GNUNET_SCHEDULER_TaskContext *tc)
914 {
915   int running;
916   struct CURLMsg *msg;
917   CURLMcode mret;
918   struct Session * cs = NULL;
919
920   http_task_send = GNUNET_SCHEDULER_NO_TASK;
921   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
922     return;
923
924   do
925     {
926       running = 0;
927       mret = curl_multi_perform (multi_handle, &running);
928       if (running == 0)
929         {
930           do
931             {
932
933               msg = curl_multi_info_read (multi_handle, &running);
934               GNUNET_break (msg != NULL);
935               if (msg == NULL)
936                 break;
937               /* get session for affected curl handle */
938               cs = find_session_by_curlhandle (msg->easy_handle);
939               GNUNET_assert ( cs != NULL );
940               switch (msg->msg)
941                 {
942
943                 case CURLMSG_DONE:
944                   if ( (msg->data.result != CURLE_OK) &&
945                        (msg->data.result != CURLE_GOT_NOTHING) )
946                     {
947
948                     GNUNET_log(GNUNET_ERROR_TYPE_INFO,
949                                _("%s failed for `%s' at %s:%d: `%s'\n"),
950                                "curl_multi_perform",
951                                cs->ip,
952                                __FILE__,
953                                __LINE__,
954                                curl_easy_strerror (msg->data.result));
955                     /* sending msg failed*/
956                     if ( NULL != cs->transmit_cont)
957                       cs->transmit_cont (NULL,&cs->sender,GNUNET_SYSERR);
958                     }
959                   else
960                     {
961                     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962                                 "Send to %s completed.\n", cs->ip);
963                     if (GNUNET_OK != remove_http_message(cs, cs->pending_outbound_msg))
964                       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message could not be removed from session `%s'", GNUNET_i2s(&cs->sender));
965
966                     curl_easy_cleanup(cs->curl_handle);
967                     cs->curl_handle=NULL;
968
969                     /* send pending messages */
970                     if (cs->pending_outbound_msg != NULL)
971                       send_select_init (cs);
972
973                     /* Calling transmit continuation  */
974                     if ( NULL != cs->transmit_cont)
975                       cs->transmit_cont (NULL,&cs->sender,GNUNET_OK);
976                     }
977                   return;
978                 default:
979                   break;
980                 }
981
982             }
983           while ( (running > 0) );
984         }
985     }
986   while (mret == CURLM_CALL_MULTI_PERFORM);
987   send_prepare(cls);
988 }
989
990
991 /**
992  * Function setting up file descriptors and scheduling task to run
993  * @param ses session to send data to
994  * @return bytes sent to peer
995  */
996 static size_t send_prepare(struct Session* session )
997 {
998   fd_set rs;
999   fd_set ws;
1000   fd_set es;
1001   int max;
1002   struct GNUNET_NETWORK_FDSet *grs;
1003   struct GNUNET_NETWORK_FDSet *gws;
1004   long to;
1005   CURLMcode mret;
1006
1007   max = -1;
1008   FD_ZERO (&rs);
1009   FD_ZERO (&ws);
1010   FD_ZERO (&es);
1011   mret = curl_multi_fdset (multi_handle, &rs, &ws, &es, &max);
1012   if (mret != CURLM_OK)
1013     {
1014       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1015                   _("%s failed at %s:%d: `%s'\n"),
1016                   "curl_multi_fdset", __FILE__, __LINE__,
1017                   curl_multi_strerror (mret));
1018       return -1;
1019     }
1020   mret = curl_multi_timeout (multi_handle, &to);
1021   if (mret != CURLM_OK)
1022     {
1023       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1024                   _("%s failed at %s:%d: `%s'\n"),
1025                   "curl_multi_timeout", __FILE__, __LINE__,
1026                   curl_multi_strerror (mret));
1027       return -1;
1028     }
1029
1030   grs = GNUNET_NETWORK_fdset_create ();
1031   gws = GNUNET_NETWORK_fdset_create ();
1032   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1033   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1034   http_task_send = GNUNET_SCHEDULER_add_select (plugin->env->sched,
1035                                    GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1036                                    GNUNET_SCHEDULER_NO_TASK,
1037                                    GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0),
1038                                    grs,
1039                                    gws,
1040                                    &send_execute,
1041                                    session);
1042   GNUNET_NETWORK_fdset_destroy (gws);
1043   GNUNET_NETWORK_fdset_destroy (grs);
1044
1045   /* FIXME: return bytes REALLY sent */
1046   return 0;
1047 }
1048
1049 /**
1050  * Function that can be used by the transport service to transmit
1051  * a message using the plugin.
1052  *
1053  * @param cls closure
1054  * @param target who should receive this message
1055  * @param priority how important is the message
1056  * @param msgbuf the message to transmit
1057  * @param msgbuf_size number of bytes in 'msgbuf'
1058  * @param to when should we time out
1059  * @param session which session must be used (or NULL for "any")
1060  * @param addr the address to use (can be NULL if the plugin
1061  *                is "on its own" (i.e. re-use existing TCP connection))
1062  * @param addrlen length of the address in bytes
1063  * @param force_address GNUNET_YES if the plugin MUST use the given address,
1064  *                otherwise the plugin may use other addresses or
1065  *                existing connections (if available)
1066  * @param cont continuation to call once the message has
1067  *        been transmitted (or if the transport is ready
1068  *        for the next transmission call; or if the
1069  *        peer disconnected...)
1070  * @param cont_cls closure for cont
1071  * @return number of bytes used (on the physical network, with overheads);
1072  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
1073  *         and does NOT mean that the message was not transmitted (DV)
1074  */
1075 static ssize_t
1076 http_plugin_send (void *cls,
1077                       const struct GNUNET_PeerIdentity *target,
1078                       const char *msgbuf,
1079                       size_t msgbuf_size,
1080                       unsigned int priority,
1081                       struct GNUNET_TIME_Relative to,
1082                       struct Session *session,
1083                       const void *addr,
1084                       size_t addrlen,
1085                       int force_address,
1086                       GNUNET_TRANSPORT_TransmitContinuation cont,
1087                       void *cont_cls)
1088 {
1089   struct Session* ses;
1090   struct Session* ses_temp;
1091   struct HTTP_Message * msg;
1092   struct HTTP_Message * tmp;
1093   int bytes_sent = 0;
1094
1095   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport told plugin to send to peer `%s'\n",GNUNET_i2s(target));
1096
1097   /* find session for peer */
1098   ses = find_session_by_pi (target);
1099   if (NULL != ses )
1100     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Existing session for peer `%s' found\n", GNUNET_i2s(target));
1101   if ( ses == NULL)
1102   {
1103     /* create new session object */
1104
1105     /*FIXME: what is const void * really? Assuming struct sockaddr_in * ! */
1106     ses = create_session((struct sockaddr_in *) addr, target);
1107     ses->is_active = GNUNET_YES;
1108     ses->transmit_cont = cont;
1109
1110     /* Insert session into linked list */
1111     if ( plugin->sessions == NULL)
1112     {
1113       plugin->sessions = ses;
1114       plugin->session_count = 1;
1115     }
1116     ses_temp = plugin->sessions;
1117     while ( ses_temp->next != NULL )
1118     {
1119       ses_temp = ses_temp->next;
1120     }
1121     if (ses_temp != ses )
1122     {
1123       ses_temp->next = ses;
1124       plugin->session_count++;
1125     }
1126     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"New Session `%s' inserted, count %u \n", GNUNET_i2s(target), plugin->session_count);
1127   }
1128
1129   ses->curl_handle = curl_easy_init();
1130   if( NULL == ses->curl_handle)
1131   {
1132     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Getting cURL handle failed\n");
1133     return -1;
1134   }
1135
1136   if ( NULL != cont)
1137     ses->transmit_cont = cont;
1138   timeout = to;
1139   /* setting up message */
1140   msg = GNUNET_malloc (sizeof (struct HTTP_Message));
1141   msg->next = NULL;
1142   msg->len = msgbuf_size;
1143   msg->pos = 0;
1144   msg->buf = GNUNET_malloc (msgbuf_size);
1145   memcpy (msg->buf,msgbuf, msgbuf_size);
1146
1147   /* insert created message in list of pending messages */
1148
1149   if (ses->pending_outbound_msg == NULL)
1150   {
1151     ses->pending_outbound_msg = msg;
1152   }
1153   tmp = ses->pending_outbound_msg;
1154   while ( NULL != tmp->next)
1155   {
1156     tmp = tmp->next;
1157   }
1158   if ( tmp != msg)
1159     tmp->next = msg;
1160
1161   bytes_sent = send_select_init (ses);
1162   return bytes_sent;
1163
1164 }
1165
1166
1167
1168 /**
1169  * Function that can be used to force the plugin to disconnect
1170  * from the given peer and cancel all previous transmissions
1171  * (and their continuationc).
1172  *
1173  * @param cls closure
1174  * @param target peer from which to disconnect
1175  */
1176 static void
1177 http_plugin_disconnect (void *cls,
1178                             const struct GNUNET_PeerIdentity *target)
1179 {
1180   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"HTTP Plugin: http_plugin_disconnect\n");
1181   // struct Plugin *plugin = cls;
1182   // FIXME
1183 }
1184
1185
1186 /**
1187  * Convert the transports address to a nice, human-readable
1188  * format.
1189  *
1190  * @param cls closure
1191  * @param type name of the transport that generated the address
1192  * @param addr one of the addresses of the host, NULL for the last address
1193  *        the specific address format depends on the transport
1194  * @param addrlen length of the address
1195  * @param numeric should (IP) addresses be displayed in numeric form?
1196  * @param timeout after how long should we give up?
1197  * @param asc function to call on each string
1198  * @param asc_cls closure for asc
1199  */
1200 static void
1201 http_plugin_address_pretty_printer (void *cls,
1202                                         const char *type,
1203                                         const void *addr,
1204                                         size_t addrlen,
1205                                         int numeric,
1206                                         struct GNUNET_TIME_Relative timeout,
1207                                         GNUNET_TRANSPORT_AddressStringCallback
1208                                         asc, void *asc_cls)
1209 {
1210   const struct IPv4HttpAddress *t4;
1211   const struct IPv6HttpAddress *t6;
1212   struct sockaddr_in a4;
1213   struct sockaddr_in6 a6;
1214   char * address;
1215   char * ret;
1216   unsigned int port;
1217
1218   if (addrlen == sizeof (struct IPv6HttpAddress))
1219     {
1220       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1221       t6 = addr;
1222       a6.sin6_addr = t6->ipv6_addr;
1223       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1224       port = ntohs(t6->u6_port);
1225     }
1226   else if (addrlen == sizeof (struct IPv4HttpAddress))
1227     {
1228       address = GNUNET_malloc (INET_ADDRSTRLEN);
1229       t4 = addr;
1230       a4.sin_addr.s_addr =  t4->ipv4_addr;
1231       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1232       port = ntohs(t4->u_port);
1233     }
1234   else
1235     {
1236       /* invalid address */
1237       GNUNET_break_op (0);
1238       asc (asc_cls, NULL);
1239       return;
1240     }
1241
1242   ret = GNUNET_malloc(strlen(address) +14);
1243   GNUNET_asprintf(&ret,"http://%s:%u/",address,port);
1244   GNUNET_free (address);
1245   asc (asc_cls, ret);
1246 }
1247
1248
1249
1250 /**
1251  * Another peer has suggested an address for this
1252  * peer and transport plugin.  Check that this could be a valid
1253  * address.  If so, consider adding it to the list
1254  * of addresses.
1255  *
1256  * @param cls closure
1257  * @param addr pointer to the address
1258  * @param addrlen length of addr
1259  * @return GNUNET_OK if this is a plausible address for this peer
1260  *         and transport
1261  */
1262 static int
1263 http_plugin_address_suggested (void *cls,
1264                                   void *addr, size_t addrlen)
1265 {
1266   struct IPv4HttpAddress *v4;
1267   struct IPv6HttpAddress *v6;
1268
1269   if ((addrlen != sizeof (struct IPv4HttpAddress)) &&
1270       (addrlen != sizeof (struct IPv6HttpAddress)))
1271     {
1272       GNUNET_break_op (0);
1273       return GNUNET_SYSERR;
1274     }
1275   if (addrlen == sizeof (struct IPv4HttpAddress))
1276     {
1277       v4 = (struct IPv4HttpAddress *) addr;
1278
1279       v4->u_port = ntohs (v4->u_port);
1280       if (v4->u_port != plugin->port_inbound)
1281       {
1282         GNUNET_break_op (0);
1283         return GNUNET_SYSERR;
1284       }
1285     }
1286   else
1287     {
1288       v6 = (struct IPv6HttpAddress *) addr;
1289       if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr))
1290         {
1291           GNUNET_break_op (0);
1292           return GNUNET_SYSERR;
1293         }
1294       v6->u6_port = ntohs (v6->u6_port);
1295       if (v6->u6_port != plugin->port_inbound)
1296       {
1297         GNUNET_break_op (0);
1298         return GNUNET_SYSERR;
1299       }
1300
1301     }
1302   return GNUNET_OK;
1303 }
1304
1305
1306 /**
1307  * Function called for a quick conversion of the binary address to
1308  * a numeric address.  Note that the caller must not free the
1309  * address and that the next call to this function is allowed
1310  * to override the address again.
1311  *
1312  * @param cls closure
1313  * @param addr binary address
1314  * @param addrlen length of the address
1315  * @return string representing the same address
1316  */
1317 static const char*
1318 http_plugin_address_to_string (void *cls,
1319                                    const void *addr,
1320                                    size_t addrlen)
1321 {
1322   const struct IPv4HttpAddress *t4;
1323   const struct IPv6HttpAddress *t6;
1324   struct sockaddr_in a4;
1325   struct sockaddr_in6 a6;
1326   char * address;
1327   char * ret;
1328   unsigned int port;
1329
1330   if (addrlen == sizeof (struct IPv6HttpAddress))
1331     {
1332       address = GNUNET_malloc (INET6_ADDRSTRLEN);
1333       t6 = addr;
1334       a6.sin6_addr = t6->ipv6_addr;
1335       inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN);
1336       port = ntohs(t6->u6_port);
1337     }
1338   else if (addrlen == sizeof (struct IPv4HttpAddress))
1339     {
1340       address = GNUNET_malloc (INET_ADDRSTRLEN);
1341       t4 = addr;
1342       a4.sin_addr.s_addr =  t4->ipv4_addr;
1343       inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN);
1344       port = ntohs(t4->u_port);
1345     }
1346   else
1347     {
1348       /* invalid address */
1349       return NULL;
1350     }
1351
1352   ret = GNUNET_malloc(strlen(address) +6);
1353   GNUNET_asprintf(&ret,"%s:%u",address,port);
1354   GNUNET_free (address);
1355   return ret;
1356 }
1357
1358 /**
1359  * Add the IP of our network interface to the list of
1360  * our external IP addresses.
1361  *
1362  * @param cls the 'struct Plugin*'
1363  * @param name name of the interface
1364  * @param isDefault do we think this may be our default interface
1365  * @param addr address of the interface
1366  * @param addrlen number of bytes in addr
1367  * @return GNUNET_OK to continue iterating
1368  */
1369 static int
1370 process_interfaces (void *cls,
1371                     const char *name,
1372                     int isDefault,
1373                     const struct sockaddr *addr, socklen_t addrlen)
1374 {
1375   struct IPv4HttpAddress t4;
1376   struct IPv6HttpAddress t6;
1377   int af;
1378   void *arg;
1379   uint16_t args;
1380
1381
1382
1383   af = addr->sa_family;
1384   if (af == AF_INET)
1385     {
1386       if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr))
1387       {
1388         /* skip loopback addresses */
1389         return GNUNET_OK;
1390       }
1391       t4.ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1392       t4.u_port = htons (plugin->port_inbound);
1393       arg = &t4;
1394       args = sizeof (t4);
1395     }
1396   else if (af == AF_INET6)
1397     {
1398       if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr))
1399         {
1400           /* skip link local addresses */
1401           return GNUNET_OK;
1402         }
1403       if (IN6_IS_ADDR_LOOPBACK (&((struct sockaddr_in6 *) addr)->sin6_addr))
1404         {
1405           /* skip loopback addresses */
1406           return GNUNET_OK;
1407         }
1408       memcpy (&t6.ipv6_addr,
1409               &((struct sockaddr_in6 *) addr)->sin6_addr,
1410               sizeof (struct in6_addr));
1411       t6.u6_port = htons (plugin->port_inbound);
1412       arg = &t6;
1413       args = sizeof (t6);
1414     }
1415   else
1416     {
1417       GNUNET_break (0);
1418       return GNUNET_OK;
1419     }
1420   plugin->env->notify_address(plugin->env->cls,"http",arg, args, GNUNET_TIME_UNIT_FOREVER_REL);
1421
1422   return GNUNET_OK;
1423 }
1424
1425 /**
1426  * Exit point from the plugin.
1427  */
1428 void *
1429 libgnunet_plugin_transport_http_done (void *cls)
1430 {
1431   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1432   struct Plugin *plugin = api->cls;
1433   struct Session * cs;
1434   struct Session * cs_next;
1435   CURLMcode mret;
1436
1437   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unloading http plugin...\n");
1438
1439   if ( http_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1440   {
1441     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_v4);
1442     http_task_v4 = GNUNET_SCHEDULER_NO_TASK;
1443   }
1444
1445   if ( http_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1446   {
1447     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_v6);
1448     http_task_v6 = GNUNET_SCHEDULER_NO_TASK;
1449   }
1450
1451   if ( http_task_send != GNUNET_SCHEDULER_NO_TASK)
1452   {
1453     GNUNET_SCHEDULER_cancel(plugin->env->sched, http_task_send);
1454     http_task_send = GNUNET_SCHEDULER_NO_TASK;
1455   }
1456
1457   if (http_daemon_v4 != NULL)
1458   {
1459     MHD_stop_daemon (http_daemon_v4);
1460     http_daemon_v4 = NULL;
1461   }
1462   if (http_daemon_v6 != NULL)
1463   {
1464     MHD_stop_daemon (http_daemon_v6);
1465     http_daemon_v6 = NULL;
1466   }
1467
1468   mret = curl_multi_cleanup(multi_handle);
1469   if ( CURLM_OK != mret)
1470     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed");
1471
1472   /* free all sessions */
1473   cs = plugin->sessions;
1474
1475   while ( NULL != cs)
1476     {
1477       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing session to `%s'\n",cs->ip);
1478
1479       cs_next = cs->next;
1480       /* freeing messages */
1481       struct HTTP_Message *cur;
1482       struct HTTP_Message *tmp;
1483       cur = cs->pending_outbound_msg;
1484
1485       while (cur != NULL)
1486       {
1487          tmp = cur->next;
1488          if (NULL != cur->buf)
1489            GNUNET_free (cur->buf);
1490          GNUNET_free (cur);
1491          cur = tmp;
1492       }
1493
1494       GNUNET_free (cs->ip);
1495       GNUNET_free (cs->addr);
1496       GNUNET_free (cs);
1497       plugin->session_count--;
1498       cs = cs_next;
1499
1500     }
1501
1502   /* GNUNET_SERVICE_stop (plugin->service); */
1503   GNUNET_free (hostname);
1504   GNUNET_free (plugin);
1505   GNUNET_free (api);
1506   return NULL;
1507 }
1508
1509
1510 /**
1511  * Entry point for the plugin.
1512  */
1513 void *
1514 libgnunet_plugin_transport_http_init (void *cls)
1515 {
1516   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1517   struct GNUNET_TRANSPORT_PluginFunctions *api;
1518   struct GNUNET_SERVICE_Context *service;
1519   unsigned int timeout;
1520   struct GNUNET_TIME_Relative gn_timeout;
1521   long long unsigned int port;
1522
1523   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting http plugin...\n");
1524
1525   service = NULL;
1526   /*
1527   service = GNUNET_SERVICE_start ("transport-http", env->sched, env->cfg);
1528   if (service == NULL)
1529     {
1530       GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "", _
1531                        ("Failed to start service for `%s' transport plugin.\n"),
1532                        "http");
1533       return NULL;
1534     }
1535     */
1536
1537   plugin = GNUNET_malloc (sizeof (struct Plugin));
1538   plugin->env = env;
1539   plugin->sessions = NULL;
1540   plugin->service = service;
1541   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1542   api->cls = plugin;
1543   api->send = &http_plugin_send;
1544   api->disconnect = &http_plugin_disconnect;
1545   api->address_pretty_printer = &http_plugin_address_pretty_printer;
1546   api->check_address = &http_plugin_address_suggested;
1547   api->address_to_string = &http_plugin_address_to_string;
1548
1549   hostname = GNUNET_RESOLVER_local_fqdn_get ();
1550
1551   /* Hashing our identity to use it in URLs */
1552   GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &my_ascii_hash_ident);
1553
1554   /* Reading port number from config file */
1555   if ((GNUNET_OK !=
1556        GNUNET_CONFIGURATION_get_value_number (env->cfg,
1557                                               "transport-http",
1558                                               "PORT",
1559                                               &port)) ||
1560       (port > 65535) )
1561     {
1562       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1563                        "http",
1564                        _
1565                        ("Require valid port number for transport plugin `%s' in configuration!\n"),
1566                        "transport-http");
1567       libgnunet_plugin_transport_http_done (api);
1568       return NULL;
1569     }
1570   plugin->port_inbound = port;
1571   gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT;
1572   timeout = ( gn_timeout.value / 1000);
1573   if ((http_daemon_v4 == NULL) && (http_daemon_v6 == NULL) && (port != 0))
1574     {
1575     http_daemon_v6 = MHD_start_daemon (MHD_USE_IPv6,
1576                                        port,
1577                                        &acceptPolicyCallback,
1578                                        NULL , &accessHandlerCallback, NULL,
1579                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1580                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1581                                        MHD_OPTION_CONNECTION_TIMEOUT, timeout,
1582                                        /* FIXME: set correct limit */
1583                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1584                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1585                                        MHD_OPTION_END);
1586     http_daemon_v4 = MHD_start_daemon (MHD_NO_FLAG,
1587                                        port,
1588                                        &acceptPolicyCallback,
1589                                        NULL , &accessHandlerCallback, NULL,
1590                                        MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 16,
1591                                        MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
1592                                        MHD_OPTION_CONNECTION_TIMEOUT, timeout,
1593                                        /* FIXME: set correct limit */
1594                                        MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024),
1595                                        MHD_OPTION_NOTIFY_COMPLETED, &requestCompletedCallback, NULL,
1596                                        MHD_OPTION_END);
1597     }
1598   if (http_daemon_v4 != NULL)
1599     http_task_v4 = http_daemon_prepare (http_daemon_v4);
1600   if (http_daemon_v6 != NULL)
1601     http_task_v6 = http_daemon_prepare (http_daemon_v6);
1602
1603   if (http_task_v4 != GNUNET_SCHEDULER_NO_TASK)
1604     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 on port %u\n",port);
1605   if (http_task_v6 != GNUNET_SCHEDULER_NO_TASK)
1606     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 on port %u\n",port);
1607
1608
1609   /* Initializing cURL */
1610   multi_handle = curl_multi_init();
1611   if ( NULL == multi_handle )
1612   {
1613     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
1614                      "http",
1615                      _("Could not initialize curl multi handle, failed to start http plugin!\n"),
1616                      "transport-http");
1617     libgnunet_plugin_transport_http_done (api);
1618     return NULL;
1619   }
1620
1621   GNUNET_OS_network_interfaces_list (&process_interfaces, plugin);
1622
1623   return api;
1624 }
1625
1626 /* end of plugin_transport_template.c */