-bugfixes, code cleanup
[oweals/gnunet.git] / src / gns / gnunet-gns-proxy.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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  * @author Martin Schanzenbach
22  * @file src/gns/gnunet-gns-proxy.c
23  * @brief HTTP(S) proxy that rewrites URIs and fakes certificats to make GADS work
24  *        with legacy browsers
25  */
26 #include "platform.h"
27 #include <microhttpd.h>
28 #include <curl/curl.h>
29 #include <gnutls/gnutls.h>
30 #include <gnutls/x509.h>
31 #include <gnutls/abstract.h>
32 #include <gnutls/crypto.h>
33 #include <regex.h>
34 #include "gnunet_util_lib.h"
35 #include "gnunet_gns_service.h"
36 #include "gns_proxy_proto.h"
37 #include "gns.h"
38
39 #define HAVE_MHD_NO_LISTEN_SOCKET (MHD_VERSION >= 0x00091401)
40
41 #define GNUNET_GNS_PROXY_PORT 7777
42 #define MHD_MAX_CONNECTIONS 300
43 #define MAX_HTTP_URI_LENGTH 2048
44 #define POSTBUFFERSIZE 4096
45
46 /* MHD/cURL defines */
47 enum BufferStatus
48   {
49     BUF_WAIT_FOR_CURL,
50     BUF_WAIT_FOR_MHD,
51     BUF_WAIT_FOR_PP 
52   };
53
54 #define HTML_HDR_CONTENT "Content-Type: text/html"
55
56 /* buffer padding for proper RE matching */
57 #define CURL_BUF_PADDING 1000
58
59 /* regexp */
60 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
61 #define RE_A_HREF  "href=\"https?://(([A-Za-z0-9]+[.])+)([+]|[a-z]+)"
62 #define RE_N_MATCHES 4
63
64 /* The usual suspects */
65 #define HTTP_PORT 80
66 #define HTTPS_PORT 443
67
68
69 /**
70  * A structure for CA cert/key
71  */
72 struct ProxyCA
73 {
74   /* The certificate */
75   gnutls_x509_crt_t cert;
76
77   /* The private key */
78   gnutls_x509_privkey_t key;
79 };
80
81 #define MAX_PEM_SIZE (10 * 1024)
82
83 /**
84  * Structure for GNS certificates
85  */
86 struct ProxyGNSCertificate
87 {
88   /* The certificate as PEM */
89   char cert[MAX_PEM_SIZE];
90
91   /* The private key as PEM */
92   char key[MAX_PEM_SIZE];
93 };
94
95
96 /**
97  * A structure for socks requests
98  */
99 struct Socks5Request
100 {
101   /* The client socket */
102   struct GNUNET_NETWORK_Handle *sock;
103
104   /* The server socket */
105   struct GNUNET_NETWORK_Handle *remote_sock;
106   
107   /* The socks state */
108   int state;
109   
110   /* Client socket read task */
111   GNUNET_SCHEDULER_TaskIdentifier rtask;
112
113   /* Server socket read task */
114   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
115
116   /* Client socket write task */
117   GNUNET_SCHEDULER_TaskIdentifier wtask;
118
119   /* Server socket write task */
120   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
121
122   /* Read buffer */
123   char rbuf[2048];
124
125   /* Write buffer */
126   char wbuf[2048];
127
128   /* Length of data in read buffer */
129   unsigned int rbuf_len;
130
131   /* Length of data in write buffer */
132   unsigned int wbuf_len;
133
134   /* This handle is scheduled for cleanup? */
135   int cleanup;
136
137   /* Shall we close the client socket on cleanup? */
138   int cleanup_sock;
139 };
140
141 /**
142  * DLL for Network Handles
143  */
144 struct NetworkHandleList
145 {
146   /*DLL*/
147   struct NetworkHandleList *next;
148
149   /*DLL*/
150   struct NetworkHandleList *prev;
151
152   /* The handle */
153   struct GNUNET_NETWORK_Handle *h;
154 };
155
156 /**
157  * A structure for all running Httpds
158  */
159 struct MhdHttpList
160 {
161   /* DLL for httpds */
162   struct MhdHttpList *prev;
163
164   /* DLL for httpds */
165   struct MhdHttpList *next;
166
167   /* is this an ssl daemon? */
168   int is_ssl;
169
170   /* the domain name to server (only important for SSL) */
171   char domain[256];
172
173   /* The daemon handle */
174   struct MHD_Daemon *daemon;
175
176   /* Optional proxy certificate used */
177   struct ProxyGNSCertificate *proxy_cert;
178
179   /* The task ID */
180   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
181
182   /* Handles associated with this daemon */
183   struct NetworkHandleList *socket_handles_head;
184   
185   /* Handles associated with this daemon */
186   struct NetworkHandleList *socket_handles_tail;
187 };
188
189 /**
190  * A structure for MHD<->cURL streams
191  */
192 struct ProxyCurlTask
193 {
194   /* DLL for tasks */
195   struct ProxyCurlTask *prev;
196
197   /* DLL for tasks */
198   struct ProxyCurlTask *next;
199
200   /* Handle to cURL */
201   CURL *curl;
202
203   /* Optional header replacements for curl (LEHO) */
204   struct curl_slist *headers;
205
206   /* Optional resolver replacements for curl (LEHO) */
207   struct curl_slist *resolver;
208
209   /* curl response code */
210   long curl_response_code;
211
212   /* The URL to fetch */
213   char url[MAX_HTTP_URI_LENGTH];
214
215   /* The cURL write buffer / MHD read buffer */
216   char buffer[CURL_MAX_WRITE_SIZE + CURL_BUF_PADDING];
217
218   /* Read pos of the data in the buffer */
219   char *buffer_read_ptr;
220
221   /* Write pos in the buffer */
222   char *buffer_write_ptr;
223
224   /* connection */
225   struct MHD_Connection *connection;
226
227   /*put*/
228   size_t put_read_offset;
229   size_t put_read_size;
230
231   /*post*/
232   struct MHD_PostProcessor *post_handler;
233
234   /* post data */
235   struct ProxyUploadData *upload_data_head;
236   struct ProxyUploadData *upload_data_tail;
237
238   /* the type of POST encoding */
239   char* post_type;
240
241   struct curl_httppost *httppost;
242
243   struct curl_httppost *httppost_last;
244
245   /* Number of bytes in buffer */
246   unsigned int bytes_in_buffer;
247
248   /* PP task */
249   GNUNET_SCHEDULER_TaskIdentifier pp_task;
250
251   /* PP match list */
252   struct ProxyREMatch *pp_match_head;
253
254   /* PP match list */
255   struct ProxyREMatch *pp_match_tail;
256
257   /* The associated daemon list entry */
258   struct MhdHttpList *mhd;
259
260   /* The associated response */
261   struct MHD_Response *response;
262
263   /* Cookies to set */
264   struct ProxySetCookieHeader *set_cookies_head;
265
266   /* Cookies to set */
267   struct ProxySetCookieHeader *set_cookies_tail;
268
269   /* The authority of the corresponding host (site of origin) */
270   char authority[256];
271
272   /* The hostname (Host header field) */
273   char host[256];
274
275   /* The LEgacy HOstname (can be empty) */
276   char leho[256];
277
278   /* The port */
279   uint16_t port;
280
281   /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
282   enum BufferStatus buf_status;
283
284   /* connection status */
285   int ready_to_queue;
286
287   /* is curl running? */
288   int curl_running;
289   
290   /* are we done */
291   int fin;
292
293   /* Already accepted */
294   int accepted;
295
296   /* Indicates wheather the download is in progress */
297   int download_in_progress;
298
299   /* Indicates wheather the download was successful */
300   int download_is_finished;
301
302   /* Indicates wheather the download failed */
303   int download_error;
304
305   /* Indicates wheather we need to parse HTML */
306   int parse_content;
307
308   /* Indicates wheather we are postprocessing the HTML right now */
309   int is_postprocessing;
310
311   /* Indicates wheather postprocessing has finished */
312   int pp_finished;
313
314   int post_done;
315
316   int is_httppost;
317   
318 };
319
320 /**
321  * Struct for RE matches in postprocessing of HTML
322  */
323 struct ProxyREMatch
324 {
325   /* DLL */
326   struct ProxyREMatch *next;
327
328   /* DLL */
329   struct ProxyREMatch *prev;
330
331   /* start of match in buffer */
332   char* start;
333
334   /* end of match in buffer */
335   char* end;
336
337   /* associated proxycurltask */
338   struct ProxyCurlTask *ctask;
339
340   /* hostname found */
341   char hostname[255];
342
343   /* PP result */
344   char result[255];
345
346   /* shorten task */
347   struct GNUNET_GNS_ShortenRequest *shorten_task;
348
349   /* are we done */
350   int done;
351
352   /* is SSL */
353   int is_ssl;
354
355 };
356
357 /**
358  * Struct for set-cookies
359  */
360 struct ProxySetCookieHeader
361 {
362   /* DLL */
363   struct ProxySetCookieHeader *next;
364
365   /* DLL */
366   struct ProxySetCookieHeader *prev;
367
368   /* the cookie */
369   char *cookie;
370 };
371
372 /**
373  * Post data structure
374  */
375 struct ProxyUploadData
376 {
377   /* DLL */
378   struct ProxyUploadData *next;
379
380   /* DLL */
381   struct ProxyUploadData *prev;
382
383   char *key;
384
385   char *filename;
386
387   char *content_type;
388
389   size_t content_length;
390   
391   /* value */
392   char *value;
393
394   /* to copy */
395   size_t bytes_left;
396
397   /* size */
398   size_t total_bytes;
399 };
400
401
402 /* The port the proxy is running on (default 7777) */
403 static unsigned long port = GNUNET_GNS_PROXY_PORT;
404
405 /* The CA file (pem) to use for the proxy CA */
406 static char* cafile_opt;
407
408 /* The listen socket of the proxy */
409 static struct GNUNET_NETWORK_Handle *lsock;
410
411 /* The listen task ID */
412 static GNUNET_SCHEDULER_TaskIdentifier ltask;
413
414 /* The cURL download task */
415 static GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
416
417 /* The non SSL httpd daemon handle */
418 static struct MHD_Daemon *httpd;
419
420 /* Number of current mhd connections */
421 static unsigned int total_mhd_connections;
422
423 /* The cURL multi handle */
424 static CURLM *curl_multi;
425
426 /* Handle to the GNS service */
427 static struct GNUNET_GNS_Handle *gns_handle;
428
429 /* DLL for ProxyCurlTasks */
430 static struct ProxyCurlTask *ctasks_head;
431
432 /* DLL for ProxyCurlTasks */
433 static struct ProxyCurlTask *ctasks_tail;
434
435 /* DLL for http daemons */
436 static struct MhdHttpList *mhd_httpd_head;
437
438 /* DLL for http daemons */
439 static struct MhdHttpList *mhd_httpd_tail;
440
441 /* Handle to the regex for dotplus (.+) replacement in HTML */
442 static regex_t re_dotplus;
443
444 /* The users local GNS zone hash */
445 static struct GNUNET_CRYPTO_ShortHashCode *local_gns_zone;
446
447 /* The users local private zone */
448 static struct GNUNET_CRYPTO_ShortHashCode *local_private_zone;
449
450 /* The users local shorten zone */
451 static struct GNUNET_CRYPTO_ShortHashCode *local_shorten_zone;
452
453 /* The CA for SSL certificate generation */
454 static struct ProxyCA proxy_ca;
455
456 /* UNIX domain socket for mhd */
457 #if !HAVE_MHD_NO_LISTEN_SOCKET
458 static struct GNUNET_NETWORK_Handle *mhd_unix_socket;
459 #endif
460
461 /* Shorten zone private key */
462 static struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
463
464
465 /**
466  * Checks if name is in tld
467  *
468  * @param name the name to check 
469  * @param tld the TLD to check for (must NOT begin with ".")
470  * @return GNUNET_YES or GNUNET_NO
471  */
472 static int
473 is_tld (const char* name, const char* tld)
474 {
475   size_t name_len = strlen (name);
476   size_t tld_len = strlen (tld);
477
478   GNUNET_break ('.' != tld[0]);
479   return ( (tld_len < name_len) &&
480            ( ('.' == name[name_len - tld_len - 1]) || (name_len == tld_len) ) &&
481            (0 == memcmp (tld,
482                          name + (name_len - tld_len),
483                          tld_len)) );
484 }
485
486
487 /**
488  * convert integer to string representation
489  *
490  * @param i integer
491  * @return the character
492  */
493 static char 
494 i_to_hexchar (unsigned char i)
495 {
496   static char hexmap[] = "0123456789abcdef";
497
498   GNUNET_assert (sizeof (hexmap) > (i & 15));
499   return hexmap[i & 15];
500 }
501
502
503 static int
504 con_post_data_iter (void *cls,
505                   enum MHD_ValueKind kind,
506                   const char *key,
507                   const char *filename,
508                   const char *content_type,
509                   const char *transfer_encoding,
510                   const char *data,
511                   uint64_t off,
512                   size_t size)
513 {
514   struct ProxyCurlTask* ctask = cls;
515   struct ProxyUploadData* pdata;
516   char* enc;
517   char* new_value;
518   
519   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
520               "Got POST data (file: %s, content type: %s): '%s=%.*s' at offset %llu size %llu\n",
521               filename, content_type,
522               key, (int) size, data, 
523               (unsigned long long) off, 
524               (unsigned long long) size);
525   GNUNET_assert (NULL != ctask->post_type);
526
527   if (0 == strcasecmp (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
528                        ctask->post_type))
529   {
530     ctask->is_httppost = GNUNET_YES;
531     /* new part */
532     if (0 == off)
533     {
534       pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
535       pdata->key = GNUNET_strdup (key);
536
537       if (NULL != filename)
538         pdata->filename = GNUNET_strdup (filename);
539       if (NULL != content_type)
540         pdata->content_type = GNUNET_strdup (content_type);
541       pdata->value = GNUNET_malloc (size);
542       pdata->total_bytes = size;
543       memcpy (pdata->value, data, size);
544       GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
545                                         ctask->upload_data_tail,
546                                         pdata);
547
548       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
549                   "Copied %llu bytes of POST Data\n", 
550                   (unsigned long long) size);
551       return MHD_YES;
552     }
553     
554     pdata = ctask->upload_data_tail;
555     new_value = GNUNET_malloc (size + pdata->total_bytes);
556     memcpy (new_value, pdata->value, pdata->total_bytes);
557     memcpy (new_value+off, data, size);
558     GNUNET_free (pdata->value);
559     pdata->value = new_value;
560     pdata->total_bytes += size;
561
562     return MHD_YES;
563   }
564
565   if (0 != strcasecmp (MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
566                        ctask->post_type))
567   {
568     return MHD_NO;
569   }
570
571   ctask->is_httppost = GNUNET_NO;
572   
573   if (NULL != ctask->curl)
574     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
575
576   if (0 == off)
577   {
578     enc = curl_easy_escape (ctask->curl, key, 0);
579     if (NULL == enc)
580       {
581         GNUNET_break (0);
582         return MHD_NO;
583       }
584     /* a key */
585     pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
586     pdata->value = GNUNET_malloc (strlen (enc) + 3);
587     if (NULL != ctask->upload_data_head)
588     {
589       pdata->value[0] = '&';
590       memcpy (pdata->value+1, enc, strlen (enc));
591     }
592     else
593       memcpy (pdata->value, enc, strlen (enc));
594     pdata->value[strlen (pdata->value)] = '=';
595     pdata->bytes_left = strlen (pdata->value);
596     pdata->total_bytes = pdata->bytes_left;
597     curl_free (enc);
598
599     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600                 "Escaped POST key: '%s'\n",
601                 pdata->value);
602
603     GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
604                                       ctask->upload_data_tail,
605                                       pdata);
606   }
607
608   /* a value */
609   enc = curl_easy_escape (ctask->curl, data, 0);
610   if (NULL == enc)
611     {
612       GNUNET_break (0);
613       return MHD_NO;
614     }
615   pdata = GNUNET_malloc (sizeof (struct ProxyUploadData));
616   pdata->value = GNUNET_malloc (strlen (enc) + 1);
617   memcpy (pdata->value, enc, strlen (enc));
618   pdata->bytes_left = strlen (pdata->value);
619   pdata->total_bytes = pdata->bytes_left;
620   curl_free (enc);
621
622   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
623               "Escaped POST value: '%s'\n",
624               pdata->value);
625
626   GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
627                                     ctask->upload_data_tail,
628                                     pdata);
629   return MHD_YES;
630 }
631
632
633 /**
634  * Read HTTP request header field 'Host'
635  *
636  * @param cls buffer to write to
637  * @param kind value kind
638  * @param key field key
639  * @param value field value
640  * @return MHD_NO when Host found
641  */
642 static int
643 con_val_iter (void *cls,
644               enum MHD_ValueKind kind,
645               const char *key,
646               const char *value)
647 {
648   struct ProxyCurlTask *ctask = cls;
649   char* buf = ctask->host;
650   char* port;
651   char* cstr;
652   const char* hdr_val;
653   unsigned int uport;
654
655   if (0 == strcmp ("Host", key))
656   {
657     port = strchr (value, ':');
658     if (NULL != port)
659     {
660       strncpy (buf, value, port-value);
661       port++;
662       if ((1 != sscanf (port, "%u", &uport)) ||
663            (uport > UINT16_MAX) ||
664            (0 == uport))
665         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
666                     "Unable to parse port!\n");
667       else
668         ctask->port = (uint16_t) uport;
669     }
670     else
671       strcpy (buf, value);
672     return MHD_YES;
673   }
674
675   if (0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING, key))
676     hdr_val = "";
677   else
678     hdr_val = value;
679
680   if (0 == strcasecmp (MHD_HTTP_HEADER_CONTENT_TYPE,
681                    key))
682   {
683     if (0 == strncasecmp (value,
684                      MHD_HTTP_POST_ENCODING_FORM_URLENCODED,
685                      strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED)))
686       ctask->post_type = MHD_HTTP_POST_ENCODING_FORM_URLENCODED;
687     else if (0 == strncasecmp (value,
688                           MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA,
689                           strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA)))
690       ctask->post_type = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA;
691     else
692       ctask->post_type = NULL;
693
694   }
695
696   cstr = GNUNET_malloc (strlen (key) + strlen (hdr_val) + 3);
697   GNUNET_snprintf (cstr, strlen (key) + strlen (hdr_val) + 3,
698                    "%s: %s", key, hdr_val);
699
700   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
701               "Client Header: %s\n", cstr);
702
703   ctask->headers = curl_slist_append (ctask->headers, cstr);
704   GNUNET_free (cstr);
705
706   return MHD_YES;
707 }
708
709
710 /**
711  * Callback for MHD response
712  *
713  * @param cls closure
714  * @param pos in buffer
715  * @param buf buffer
716  * @param max space in buffer
717  * @return number of bytes written
718  */
719 static ssize_t
720 mhd_content_cb (void *cls,
721                 uint64_t pos,
722                 char* buf,
723                 size_t max);
724
725
726 /**
727  * Check HTTP response header for mime
728  *
729  * @param buffer curl buffer
730  * @param size curl blocksize
731  * @param nmemb curl blocknumber
732  * @param cls handle
733  * @return size of read bytes
734  */
735 static size_t
736 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
737 {
738   size_t bytes = size * nmemb;
739   struct ProxyCurlTask *ctask = cls;
740   int html_mime_len = strlen (HTML_HDR_CONTENT);
741   int cookie_hdr_len = strlen (MHD_HTTP_HEADER_SET_COOKIE);
742   char hdr_mime[html_mime_len+1];
743   char hdr_generic[bytes+1];
744   char new_cookie_hdr[bytes+strlen (ctask->leho)+1];
745   char new_location[MAX_HTTP_URI_LENGTH+500];
746   char real_host[264];
747   char leho_host[264];
748   char* ndup;
749   char* tok;
750   char* cookie_domain;
751   char* hdr_type;
752   char* hdr_val;
753   int delta_cdomain;
754   size_t offset = 0;
755   char cors_hdr[strlen (ctask->leho) + strlen ("https://")];
756   
757   if (NULL == ctask->response)
758   {
759     /* FIXME: get total size from curl (if available) */
760     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761                 "Creating response for %s\n", ctask->url);
762     ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
763                                                          sizeof (ctask->buffer),
764                                                          &mhd_content_cb,
765                                                          ctask,
766                                                          NULL);
767
768     /* if we have a leho add a CORS header */
769     if (0 != strcmp ("", ctask->leho))
770     {
771       /* We could also allow ssl and http here */
772       if (ctask->mhd->is_ssl)
773         sprintf (cors_hdr, "https://%s", ctask->leho);
774       else
775         sprintf (cors_hdr, "http://%s", ctask->leho);
776
777       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778                   "MHD: Adding CORS header field %s\n",
779                   cors_hdr);
780
781       if (GNUNET_NO == MHD_add_response_header (ctask->response,
782                                               "Access-Control-Allow-Origin",
783                                               cors_hdr))
784       {
785         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
786                   "MHD: Error adding CORS header field %s\n",
787                   cors_hdr);
788       }
789     }
790     ctask->ready_to_queue = GNUNET_YES;
791   }
792   
793   if (html_mime_len <= bytes)
794   {
795     memcpy (hdr_mime, buffer, html_mime_len);
796     hdr_mime[html_mime_len] = '\0';
797
798     if (0 == strcmp (hdr_mime, HTML_HDR_CONTENT))
799     {
800       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                   "Got HTML HTTP response header\n");
802       ctask->parse_content = GNUNET_YES;
803     }
804   }
805
806   if (cookie_hdr_len > bytes)
807     return bytes;
808
809   memcpy (hdr_generic, buffer, bytes);
810   hdr_generic[bytes] = '\0';
811   /* remove crlf */
812   if ('\n' == hdr_generic[bytes-1])
813     hdr_generic[bytes-1] = '\0';
814
815   if (hdr_generic[bytes-2] == '\r')
816     hdr_generic[bytes-2] = '\0';
817   
818   if (0 == memcmp (hdr_generic,
819                    MHD_HTTP_HEADER_SET_COOKIE,
820                    cookie_hdr_len))
821   {
822     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
823                 "Looking for cookie in: `%s'\n", hdr_generic);    
824     ndup = GNUNET_strdup (hdr_generic+cookie_hdr_len+1);
825     memset (new_cookie_hdr, 0, sizeof (new_cookie_hdr));
826     for (tok = strtok (ndup, ";"); tok != NULL; tok = strtok (NULL, ";"))
827     {
828       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
829                   "Got Cookie token: %s\n", tok);
830       //memcpy (new_cookie_hdr+offset, tok, strlen (tok));
831       if (0 == memcmp (tok, " domain", strlen (" domain")))
832       {
833         cookie_domain = tok + strlen (" domain") + 1;
834
835         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
836                     "Got Set-Cookie Domain: %s\n", cookie_domain);
837
838         if (strlen (cookie_domain) < strlen (ctask->leho))
839         {
840           delta_cdomain = strlen (ctask->leho) - strlen (cookie_domain);
841           if (0 == strcmp (cookie_domain, ctask->leho + (delta_cdomain)))
842           {
843             GNUNET_snprintf (new_cookie_hdr+offset,
844                              sizeof (new_cookie_hdr),
845                              " domain=%s", ctask->authority);
846             offset += strlen (" domain=") + strlen (ctask->authority);
847             new_cookie_hdr[offset] = ';';
848             offset++;
849             continue;
850           }
851         }
852         else if (strlen (cookie_domain) == strlen (ctask->leho))
853         {
854           if (0 == strcmp (cookie_domain, ctask->leho))
855           {
856             GNUNET_snprintf (new_cookie_hdr+offset,
857                              sizeof (new_cookie_hdr),
858                              " domain=%s", ctask->host);
859             offset += strlen (" domain=") + strlen (ctask->host);
860             new_cookie_hdr[offset] = ';';
861             offset++;
862             continue;
863           }
864         }
865         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
866                     "Cookie domain invalid\n");
867
868         
869       }
870       memcpy (new_cookie_hdr+offset, tok, strlen (tok));
871       offset += strlen (tok);
872       new_cookie_hdr[offset] = ';';
873       offset++;
874     }
875     
876     GNUNET_free (ndup);
877
878     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
879                 "Got Set-Cookie HTTP header %s\n", new_cookie_hdr);
880
881     if (GNUNET_NO == MHD_add_response_header (ctask->response,
882                                               MHD_HTTP_HEADER_SET_COOKIE,
883                                               new_cookie_hdr))
884     {
885       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
886                   "MHD: Error adding set-cookie header field %s\n",
887                   hdr_generic+cookie_hdr_len+1);
888     }
889     return bytes;
890   }
891
892   ndup = GNUNET_strdup (hdr_generic);
893   hdr_type = strtok (ndup, ":");
894
895   if (NULL == hdr_type)
896   {
897     GNUNET_free (ndup);
898     return bytes;
899   }
900
901   hdr_val = strtok (NULL, "");
902
903   if (NULL == hdr_val)
904   {
905     GNUNET_free (ndup);
906     return bytes;
907   }
908
909   hdr_val++;
910
911   if (0 == strcasecmp (MHD_HTTP_HEADER_LOCATION, hdr_type))
912   {
913     if (ctask->mhd->is_ssl)
914     {
915       sprintf (leho_host, "https://%s", ctask->leho);
916       sprintf (real_host, "https://%s", ctask->host);
917     }
918     else
919     {
920       sprintf (leho_host, "http://%s", ctask->leho);
921       sprintf (real_host, "http://%s", ctask->host);
922     }
923
924     if (0 == memcmp (leho_host, hdr_val, strlen (leho_host)))
925     {
926       sprintf (new_location, "%s%s", real_host, hdr_val+strlen (leho_host));
927       hdr_val = new_location;
928     }
929   }
930
931   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
932               "Trying to set %s: %s\n",
933               hdr_type,
934               hdr_val);
935   if (GNUNET_NO == MHD_add_response_header (ctask->response,
936                                             hdr_type,
937                                             hdr_val))
938   {
939     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
940                 "MHD: Error adding %s header field %s\n",
941                 hdr_type,
942                 hdr_val);
943   }
944   GNUNET_free (ndup);
945   return bytes;
946 }
947
948
949 /**
950  * schedule mhd
951  *
952  * @param hd a http daemon list entry
953  */
954 static void
955 run_httpd (struct MhdHttpList *hd);
956
957
958 /**
959  * schedule all mhds
960  *
961  */
962 static void
963 run_httpds (void);
964
965
966 /**
967  * Task run whenever HTTP server operations are pending.
968  *
969  * @param cls unused
970  * @param tc sched context
971  */
972 static void
973 do_httpd (void *cls,
974           const struct GNUNET_SCHEDULER_TaskContext *tc);
975
976
977 static void
978 run_mhd_now (struct MhdHttpList *hd)
979 {
980   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
981   {
982     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
983                 "MHD: killing old task\n");
984     GNUNET_SCHEDULER_cancel (hd->httpd_task);
985   }
986   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
987               "MHD: Scheduling MHD now\n");
988   hd->httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, hd);
989 }
990
991
992 /**
993  * Ask cURL for the select sets and schedule download
994  */
995 static void
996 curl_download_prepare (void);
997
998
999 /**
1000  * Callback to free content
1001  *
1002  * @param cls content to free
1003  * @param tc task context
1004  */
1005 static void
1006 mhd_content_free (void *cls,
1007                   const struct GNUNET_SCHEDULER_TaskContext *tc)
1008 {
1009   struct ProxyCurlTask *ctask = cls;
1010   struct ProxyUploadData *pdata;
1011
1012   GNUNET_assert (NULL == ctask->pp_match_head);
1013   if (NULL != ctask->headers)
1014     curl_slist_free_all (ctask->headers);
1015
1016   if (NULL != ctask->headers)
1017     curl_slist_free_all (ctask->resolver);
1018
1019   if (NULL != ctask->response)
1020     MHD_destroy_response (ctask->response);
1021
1022   if (NULL != ctask->post_handler)
1023     MHD_destroy_post_processor (ctask->post_handler);
1024
1025   if (GNUNET_SCHEDULER_NO_TASK != ctask->pp_task)
1026     GNUNET_SCHEDULER_cancel (ctask->pp_task);
1027
1028   for (pdata = ctask->upload_data_head; NULL != pdata; pdata = ctask->upload_data_head)
1029   {
1030     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1031                                  ctask->upload_data_tail,
1032                                  pdata);
1033     GNUNET_free_non_null (pdata->filename);
1034     GNUNET_free_non_null (pdata->content_type);
1035     GNUNET_free_non_null (pdata->key);
1036     GNUNET_free_non_null (pdata->value);
1037     GNUNET_free (pdata);
1038   }
1039   GNUNET_free (ctask);
1040 }
1041
1042
1043 /**
1044  * Callback for MHD response
1045  *
1046  * @param cls closure
1047  * @param pos in buffer
1048  * @param buf buffer
1049  * @param max space in buffer
1050  * @return number of bytes written
1051  */
1052 static ssize_t
1053 mhd_content_cb (void *cls,
1054                 uint64_t pos,
1055                 char* buf,
1056                 size_t max)
1057 {
1058   struct ProxyCurlTask *ctask = cls;
1059   struct ProxyREMatch *re_match;
1060   ssize_t copied = 0;
1061   size_t bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1062
1063   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064               "MHD: content cb for %s. To copy: %u\n",
1065               ctask->url, (unsigned int) bytes_to_copy);
1066   if ((GNUNET_YES == ctask->download_is_finished) &&
1067       (GNUNET_NO == ctask->download_error) &&
1068       (0 == bytes_to_copy))
1069   {
1070     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1071                 "MHD: sending response for %s\n", ctask->url);
1072     ctask->download_in_progress = GNUNET_NO;
1073     run_mhd_now (ctask->mhd);
1074     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1075     total_mhd_connections--;
1076     return MHD_CONTENT_READER_END_OF_STREAM;
1077   }
1078   
1079   if ((GNUNET_YES == ctask->download_error) &&
1080       (GNUNET_YES == ctask->download_is_finished) &&
1081       (0 == bytes_to_copy))
1082   {
1083     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1084                 "MHD: sending error response\n");
1085     ctask->download_in_progress = GNUNET_NO;
1086     run_mhd_now (ctask->mhd);
1087     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
1088     total_mhd_connections--;
1089     return MHD_CONTENT_READER_END_WITH_ERROR;
1090   }
1091
1092   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
1093     return 0;
1094   
1095   copied = 0;
1096   for (re_match = ctask->pp_match_head; NULL != re_match; re_match = ctask->pp_match_head)
1097   {
1098     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1099                 "MHD: Processing PP %s\n",
1100                 re_match->hostname);
1101     bytes_to_copy = re_match->start - ctask->buffer_read_ptr;
1102     if (bytes_to_copy+copied > max)
1103     {
1104       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1105                   "MHD: buffer in response too small for %u. Using available space (%d). (%s)\n",
1106                   (unsigned int) bytes_to_copy,
1107                   max,
1108                   ctask->url);
1109       memcpy (buf+copied, ctask->buffer_read_ptr, max-copied);
1110       ctask->buffer_read_ptr += max-copied;
1111       copied = max;
1112       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113                   "MHD: copied %d bytes\n", (int) copied);
1114       return copied;
1115     }
1116
1117     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1118                 "MHD: copying %u bytes to mhd response at offset %d\n",
1119                 (unsigned int) bytes_to_copy, ctask->buffer_read_ptr);
1120     memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1121     copied += bytes_to_copy;
1122
1123     if (GNUNET_NO == re_match->done)
1124     {
1125       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1126                   "MHD: Waiting for PP of %s\n", re_match->hostname);
1127       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1128                   "MHD: copied %d bytes\n", (int) copied);
1129       ctask->buffer_read_ptr += bytes_to_copy;
1130       return copied;
1131     }
1132     
1133     if (strlen (re_match->result) > (max - copied))
1134     {
1135       //FIXME partially copy domain here
1136       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1137                   "MHD: buffer in response too small for %s! (%s)\n",
1138                   re_match->result,
1139                   ctask->url);
1140       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141                   "MHD: copied %d bytes\n", (int) copied);
1142       ctask->buffer_read_ptr += bytes_to_copy;
1143       return copied;
1144     }
1145     
1146     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147                 "MHD: Adding PP result %s to buffer\n",
1148                 re_match->result);
1149     memcpy (buf + copied, re_match->result, strlen (re_match->result));
1150     copied += strlen (re_match->result);
1151     ctask->buffer_read_ptr = re_match->end;
1152     GNUNET_CONTAINER_DLL_remove (ctask->pp_match_head,
1153                                  ctask->pp_match_tail,
1154                                  re_match);
1155     GNUNET_free (re_match);
1156   }
1157
1158   bytes_to_copy = ctask->buffer_write_ptr - ctask->buffer_read_ptr;
1159
1160   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1161               "MHD: copied: %d left: %u, space left in buf: %d\n",
1162               copied,
1163               (unsigned int) bytes_to_copy, (int) (max - copied));
1164   
1165   if (GNUNET_NO == ctask->download_is_finished)
1166   {
1167     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1168                 "MHD: Purging buffer\n");
1169     memmove (ctask->buffer, ctask->buffer_read_ptr, bytes_to_copy);
1170     ctask->buffer_read_ptr = ctask->buffer;
1171     ctask->buffer_write_ptr = ctask->buffer + bytes_to_copy;
1172     ctask->buffer[bytes_to_copy] = '\0';
1173   }
1174   
1175   if (bytes_to_copy + copied > max)
1176     bytes_to_copy = max - copied;
1177   memcpy (buf+copied, ctask->buffer_read_ptr, bytes_to_copy);
1178   ctask->buffer_read_ptr += bytes_to_copy;
1179   copied += bytes_to_copy;
1180   ctask->buf_status = BUF_WAIT_FOR_CURL;
1181   
1182   if (NULL != ctask->curl)
1183     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
1184
1185   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1186               "MHD: copied %d bytes\n", (int) copied);
1187   run_mhd_now (ctask->mhd);
1188   return copied;
1189 }
1190
1191
1192 /**
1193  * Shorten result callback
1194  *
1195  * @param cls the proxycurltask
1196  * @param short_name the shortened name (NULL on error)
1197  */
1198 static void
1199 process_shorten (void* cls, const char* short_name)
1200 {
1201   struct ProxyREMatch *re_match = cls;
1202   char result[sizeof (re_match->result)];
1203   
1204   if (NULL == short_name)
1205   {
1206     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1207                 "PP: Unable to shorten %s\n",
1208                 re_match->hostname);
1209     GNUNET_CONTAINER_DLL_remove (re_match->ctask->pp_match_head,
1210                                  re_match->ctask->pp_match_tail,
1211                                  re_match);
1212     GNUNET_free (re_match);
1213     return;
1214   }
1215
1216   if (0 == strcmp (short_name, re_match->ctask->leho))
1217     strcpy (result, re_match->ctask->host);
1218   else
1219     strcpy (result, short_name);
1220
1221   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1222               "PP: Shorten %s -> %s\n",
1223               re_match->hostname,
1224               result);
1225   
1226   if (re_match->is_ssl)
1227     sprintf (re_match->result, "href=\"https://%s", result);
1228   else
1229     sprintf (re_match->result, "href=\"http://%s", result);
1230
1231   re_match->done = GNUNET_YES;
1232   run_mhd_now (re_match->ctask->mhd);
1233 }
1234
1235
1236 /**
1237  * Postprocess data in buffer. From read ptr to write ptr
1238  *
1239  * @param cls the curlproxytask
1240  * @param tc task context
1241  */
1242 static void
1243 postprocess_buffer (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1244 {
1245   struct ProxyCurlTask *ctask = cls;
1246   struct ProxyREMatch *re_match;
1247   char* re_ptr = ctask->buffer_read_ptr;
1248   char re_hostname[255];
1249   regmatch_t m[RE_N_MATCHES];
1250
1251   ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
1252
1253   if (GNUNET_YES != ctask->parse_content)
1254   {
1255     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1256                 "PP: Not parsing content\n");
1257     ctask->buf_status = BUF_WAIT_FOR_MHD;
1258     run_mhd_now (ctask->mhd);
1259     return;
1260   }
1261
1262   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1263               "PP: We need to parse the HTML\n");
1264
1265   /* 0 means match found */
1266   while (0 == regexec (&re_dotplus, re_ptr, RE_N_MATCHES, m, 0))
1267   {
1268     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1269                 "PP: regex match\n");
1270
1271     GNUNET_assert (m[1].rm_so != -1);
1272
1273     memset (re_hostname, 0, sizeof (re_hostname));
1274     memcpy (re_hostname, re_ptr+m[1].rm_so, (m[3].rm_eo-m[1].rm_so));
1275     
1276
1277     re_match = GNUNET_malloc (sizeof (struct ProxyREMatch));
1278     re_match->start = re_ptr + m[0].rm_so;
1279     re_match->end = re_ptr + m[3].rm_eo;
1280     re_match->done = GNUNET_NO;
1281     re_match->ctask = ctask;
1282     
1283     if ('s' == *(re_ptr+m[1].rm_so-strlen("://")-1)) //FIXME strcmp
1284       re_match->is_ssl = GNUNET_YES;
1285     else
1286       re_match->is_ssl = GNUNET_NO;
1287       
1288     strcpy (re_match->hostname, re_hostname);
1289     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1290                 "PP: Got hostname %s\n", re_hostname);
1291     re_ptr += m[3].rm_eo;
1292
1293     if (GNUNET_YES == is_tld (re_match->hostname, GNUNET_GNS_TLD_PLUS))
1294     {
1295       re_match->hostname[strlen(re_match->hostname)-1] = '\0';
1296       strcpy (re_match->hostname+strlen(re_match->hostname),
1297               ctask->authority);
1298     }
1299
1300     re_match->shorten_task = GNUNET_GNS_shorten_zone (gns_handle,
1301                              re_match->hostname,
1302                              local_private_zone,
1303                              local_shorten_zone,
1304                              local_gns_zone,
1305                              &process_shorten,
1306                              re_match); //FIXME cancel appropriately
1307
1308     GNUNET_CONTAINER_DLL_insert_tail (ctask->pp_match_head,
1309                                       ctask->pp_match_tail,
1310                                       re_match);
1311   }
1312   
1313   ctask->buf_status = BUF_WAIT_FOR_MHD;
1314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1315               "PP: No more matches\n");
1316   run_mhd_now (ctask->mhd);
1317 }
1318
1319
1320 /**
1321  * Handle data from cURL
1322  *
1323  * @param ptr pointer to the data
1324  * @param size number of blocks of data
1325  * @param nmemb blocksize
1326  * @param ctx the curlproxytask
1327  * @return number of bytes handled
1328  */
1329 static size_t
1330 curl_download_cb (void *ptr, size_t size, size_t nmemb, void* ctx)
1331 {
1332   const char *cbuf = ptr;
1333   size_t total = size * nmemb;
1334   struct ProxyCurlTask *ctask = ctx;
1335   size_t buf_space = sizeof (ctask->buffer) -
1336     (ctask->buffer_write_ptr-ctask->buffer);
1337
1338   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1339               "CURL: Got %d. %d free in buffer\n",
1340               total, buf_space);
1341
1342   if (BUF_WAIT_FOR_CURL != ctask->buf_status)
1343     return CURL_WRITEFUNC_PAUSE;
1344
1345   if (total > (buf_space - CURL_BUF_PADDING))
1346   {
1347     if (ctask->buf_status == BUF_WAIT_FOR_CURL)
1348     {
1349       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1350                   "CURL: Buffer full starting postprocessing\n");
1351       ctask->buf_status = BUF_WAIT_FOR_PP;
1352       ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1353                                                  ctask);
1354       return CURL_WRITEFUNC_PAUSE;
1355     }
1356
1357     /* we should not get called in that case */
1358     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1359                 "CURL: called out of context and no space in buffer!\n");
1360     return CURL_WRITEFUNC_PAUSE;
1361   }
1362
1363   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1364               "CURL: Copying %d bytes to buffer (%s)\n", total, ctask->url);
1365   memcpy (ctask->buffer_write_ptr, cbuf, total);
1366   ctask->bytes_in_buffer += total;
1367   ctask->buffer_write_ptr += total;
1368   ctask->buffer_write_ptr[0] = '\0';
1369
1370   return total;
1371 }
1372
1373
1374 /**
1375  * cURL callback for put data
1376  */
1377 static size_t
1378 put_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1379 {
1380   struct ProxyCurlTask *ctask = cls;
1381   struct ProxyUploadData *pdata = ctask->upload_data_head;
1382   size_t len = size * nmemb;
1383   size_t to_copy;
1384   char* pos;
1385
1386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1387               "CURL: put read callback\n");
1388
1389   if (NULL == pdata)
1390     return CURL_READFUNC_PAUSE;
1391   
1392   //fin
1393   if (NULL == pdata->value)
1394   {
1395     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1396                 "CURL: Terminating PUT\n");
1397
1398     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1399                                  ctask->upload_data_tail,
1400                                  pdata);
1401     GNUNET_free (pdata);
1402     return 0;
1403   }
1404  
1405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1406               "CURL: read callback value %s\n", pdata->value); 
1407   
1408   to_copy = pdata->bytes_left;
1409   if (to_copy > len)
1410     to_copy = len;
1411   
1412   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1413   memcpy (buf, pos, to_copy);
1414   pdata->bytes_left -= to_copy;
1415   if (pdata->bytes_left <= 0)
1416   {
1417     GNUNET_free (pdata->value);
1418     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1419                                  ctask->upload_data_tail,
1420                                  pdata);
1421     GNUNET_free (pdata);
1422   }
1423   return to_copy;
1424 }
1425
1426
1427 /**
1428  * cURL callback for post data
1429  */
1430 static size_t
1431 post_read_callback (void *buf, size_t size, size_t nmemb, void *cls)
1432 {
1433   struct ProxyCurlTask *ctask = cls;
1434   struct ProxyUploadData *pdata = ctask->upload_data_head;
1435   size_t len = size * nmemb;
1436   size_t to_copy;
1437   char* pos;
1438
1439   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1440               "CURL: read callback\n");
1441
1442   if (NULL == pdata)
1443     return CURL_READFUNC_PAUSE;
1444   
1445   //fin
1446   if (NULL == pdata->value)
1447   {
1448     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1449                 "CURL: Terminating POST data\n");
1450
1451     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1452                                  ctask->upload_data_tail,
1453                                  pdata);
1454     GNUNET_free (pdata);
1455     return 0;
1456   }
1457  
1458   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459               "CURL: read callback value %s\n", pdata->value); 
1460   
1461   to_copy = pdata->bytes_left;
1462   if (to_copy > len)
1463     to_copy = len;
1464   
1465   pos = pdata->value + (pdata->total_bytes - pdata->bytes_left);
1466   memcpy (buf, pos, to_copy);
1467   pdata->bytes_left -= to_copy;
1468   if (pdata->bytes_left <= 0)
1469   {
1470     GNUNET_free (pdata->value);
1471     GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
1472                                  ctask->upload_data_tail,
1473                                  pdata);
1474     GNUNET_free (pdata);
1475   }
1476   return to_copy;
1477 }
1478
1479
1480 /**
1481  * Task that is run when we are ready to receive more data
1482  * from curl
1483  *
1484  * @param cls closure
1485  * @param tc task context
1486  */
1487 static void
1488 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1489
1490
1491 /**
1492  * Ask cURL for the select sets and schedule download
1493  */
1494 static void
1495 curl_download_prepare ()
1496 {
1497   CURLMcode mret;
1498   fd_set rs;
1499   fd_set ws;
1500   fd_set es;
1501   int max;
1502   struct GNUNET_NETWORK_FDSet *grs;
1503   struct GNUNET_NETWORK_FDSet *gws;
1504   long to;
1505   struct GNUNET_TIME_Relative rtime;
1506
1507   max = -1;
1508   FD_ZERO (&rs);
1509   FD_ZERO (&ws);
1510   FD_ZERO (&es);
1511   if (CURLM_OK != (mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max)))
1512   {
1513     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1514                 "%s failed at %s:%d: `%s'\n",
1515                 "curl_multi_fdset", __FILE__, __LINE__,
1516                 curl_multi_strerror (mret));
1517     //TODO cleanup here?
1518     return;
1519   }
1520   to = -1;
1521   GNUNET_break (CURLM_OK == curl_multi_timeout (curl_multi, &to));
1522   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1523               "cURL multi fds: max=%d timeout=%lld\n", max, (long long) to);
1524   if (-1 == to)
1525     rtime = GNUNET_TIME_UNIT_FOREVER_REL;
1526   else
1527     rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1528   grs = GNUNET_NETWORK_fdset_create ();
1529   gws = GNUNET_NETWORK_fdset_create ();
1530   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1531   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1532   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
1533     GNUNET_SCHEDULER_cancel (curl_download_task);  
1534   if (-1 != max)
1535   {
1536     curl_download_task =
1537       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1538                                    rtime,
1539                                    grs, gws,
1540                                    &curl_task_download, curl_multi);
1541   }
1542   else if (NULL != ctasks_head)
1543   {
1544     /* as specified in curl docs */
1545     curl_download_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
1546                                                        &curl_task_download,
1547                                                        curl_multi);
1548   }
1549   GNUNET_NETWORK_fdset_destroy (gws);
1550   GNUNET_NETWORK_fdset_destroy (grs);
1551 }
1552
1553
1554 /**
1555  * Task that is run when we are ready to receive more data
1556  * from curl
1557  *
1558  * @param cls closure
1559  * @param tc task context
1560  */
1561 static void
1562 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1563 {
1564   int running;
1565   int msgnum;
1566   struct CURLMsg *msg;
1567   CURLMcode mret;
1568   struct ProxyCurlTask *ctask;
1569   int num_ctasks;
1570   long resp_code;
1571   struct ProxyCurlTask *clean_head = NULL;
1572   struct ProxyCurlTask *clean_tail = NULL;
1573
1574   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1575
1576   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1577   {
1578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1579                 "Shutdown requested while trying to download\n");
1580     //TODO cleanup
1581     return;
1582   }
1583   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1584               "Ready to dl\n");
1585
1586   do
1587   {
1588     running = 0;
1589     num_ctasks = 0;
1590     
1591     mret = curl_multi_perform (curl_multi, &running);
1592
1593     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1594                 "Running curl tasks: %d\n", running);
1595
1596     for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1597     {
1598       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1599                   "CTask: %s\n", ctask->url);
1600       num_ctasks++;
1601     }
1602
1603     if (num_ctasks != running)
1604     {
1605       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1606                   "%d ctasks, %d curl running\n", num_ctasks, running);
1607     }
1608     
1609     do
1610     {
1611       
1612       msg = curl_multi_info_read (curl_multi, &msgnum);
1613       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1614                   "Messages left: %d\n", msgnum);
1615       
1616       if (msg == NULL)
1617         break;
1618       switch (msg->msg)
1619       {
1620        case CURLMSG_DONE:
1621          if ((msg->data.result != CURLE_OK) &&
1622              (msg->data.result != CURLE_GOT_NOTHING))
1623          {
1624            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1625                        "Download curl failed");
1626             
1627            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1628            {
1629              if (NULL == ctask->curl)
1630                continue;
1631
1632              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
1633                continue;
1634              
1635              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1636                          "CURL: Download failed for task %s: %s.\n",
1637                          ctask->url,
1638                          curl_easy_strerror (msg->data.result));
1639              ctask->download_is_finished = GNUNET_YES;
1640              ctask->download_error = GNUNET_YES;
1641              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1642                                                 CURLINFO_RESPONSE_CODE,
1643                                                 &resp_code))
1644                ctask->curl_response_code = resp_code;
1645              ctask->ready_to_queue = MHD_YES;
1646              ctask->buf_status = BUF_WAIT_FOR_MHD;
1647              run_mhd_now (ctask->mhd);
1648              
1649              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1650                                           ctask);
1651              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1652              break;
1653            }
1654            GNUNET_assert (ctask != NULL);
1655          }
1656          else
1657          {
1658            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1659                        "CURL: download completed.\n");
1660
1661            for (ctask = ctasks_head; NULL != ctask; ctask = ctask->next)
1662            {
1663              if (NULL == ctask->curl)
1664                continue;
1665
1666              if (0 != memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)))
1667                continue;
1668              
1669              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1670                          "CURL: completed task %s found.\n", ctask->url);
1671              if (CURLE_OK == curl_easy_getinfo (ctask->curl,
1672                                                 CURLINFO_RESPONSE_CODE,
1673                                                 &resp_code))
1674                ctask->curl_response_code = resp_code;
1675
1676
1677              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1678                          "CURL: Completed ctask!\n");
1679              if (GNUNET_SCHEDULER_NO_TASK == ctask->pp_task)
1680              {
1681               ctask->buf_status = BUF_WAIT_FOR_PP;
1682               ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_buffer,
1683                                                           ctask);
1684              }
1685
1686              ctask->ready_to_queue = MHD_YES;
1687              ctask->download_is_finished = GNUNET_YES;
1688
1689              /* We MUST not modify the multi handle else we loose messages */
1690              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
1691                                           ctask);
1692              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
1693
1694              break;
1695            }
1696            GNUNET_assert (ctask != NULL);
1697          }
1698          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1699                      "CURL: %s\n", curl_easy_strerror(msg->data.result));
1700          break;
1701        default:
1702          GNUNET_assert (0);
1703          break;
1704       }
1705     } while (msgnum > 0);
1706
1707     for (ctask=clean_head; NULL != ctask; ctask = ctask->next)
1708     {
1709       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1710                   "CURL: Removing task %s.\n", ctask->url);
1711       curl_multi_remove_handle (curl_multi, ctask->curl);
1712       curl_easy_cleanup (ctask->curl);
1713       ctask->curl = NULL;
1714     }
1715     
1716     num_ctasks=0;
1717     for (ctask=ctasks_head; NULL != ctask; ctask = ctask->next)    
1718       num_ctasks++; 
1719     
1720     if (num_ctasks != running)
1721     {
1722       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1723                   "CURL: %d tasks, %d running\n", num_ctasks, running);
1724     }
1725
1726     GNUNET_assert ( num_ctasks == running );
1727
1728   } while (mret == CURLM_CALL_MULTI_PERFORM);
1729   
1730   if (mret != CURLM_OK)
1731   {
1732     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "CURL: %s failed at %s:%d: `%s'\n",
1733                 "curl_multi_perform", __FILE__, __LINE__,
1734                 curl_multi_strerror (mret));
1735   }
1736   curl_download_prepare();
1737 }
1738
1739
1740 /**
1741  * Process LEHO lookup
1742  *
1743  * @param cls the ctask
1744  * @param rd_count number of records returned
1745  * @param rd record data
1746  */
1747 static void
1748 process_leho_lookup (void *cls,
1749                      uint32_t rd_count,
1750                      const struct GNUNET_NAMESTORE_RecordData *rd)
1751 {
1752   struct ProxyCurlTask *ctask = cls;
1753   char hosthdr[262]; //256 + "Host: "
1754   int i;
1755   CURLcode ret;
1756   CURLMcode mret;
1757   struct hostent *phost;
1758   char *ssl_ip;
1759   char resolvename[512];
1760   char curlurl[512];
1761
1762   strcpy (ctask->leho, "");
1763
1764   if (rd_count == 0)
1765     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1766                 "No LEHO present!\n");
1767
1768   for (i=0; i<rd_count; i++)
1769   {
1770     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1771       continue;
1772
1773     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1774
1775     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1776                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1777   }
1778
1779   if (0 != strcmp (ctask->leho, ""))
1780   {
1781     sprintf (hosthdr, "%s%s:%d", "Host: ", ctask->leho, ctask->port);
1782     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1783                 "New HTTP header value: %s\n", hosthdr);
1784     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1785     GNUNET_assert (NULL != ctask->headers);
1786     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1787     if (CURLE_OK != ret)
1788     {
1789       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1790                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1791     }
1792
1793   }
1794
1795   if (ctask->mhd->is_ssl)
1796   {
1797     phost = (struct hostent*)gethostbyname (ctask->host);
1798
1799     if (phost!=NULL)
1800     {
1801       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1802       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1803                   "SSL target server: %s\n", ssl_ip);
1804       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1805       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806                   "Curl resolve: %s\n", resolvename);
1807       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1808       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1809       sprintf (curlurl, "https://%s:%d%s", ctask->leho, ctask->port, ctask->url);
1810       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1811     }
1812     else
1813     {
1814       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1815                   "gethostbyname failed for %s!\n", ctask->host);
1816       ctask->download_is_finished = GNUNET_YES;
1817       ctask->download_error = GNUNET_YES;
1818       return;
1819     }
1820   }
1821
1822   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1823   {
1824     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1825                 "%s failed at %s:%d: `%s'\n",
1826                 "curl_multi_add_handle", __FILE__, __LINE__,
1827                 curl_multi_strerror (mret));
1828     ctask->download_is_finished = GNUNET_YES;
1829     ctask->download_error = GNUNET_YES;
1830     return;
1831   }
1832   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1833
1834   curl_download_prepare ();
1835
1836 }
1837
1838
1839 /**
1840  * Initialize download and trigger curl
1841  *
1842  * @param cls the proxycurltask
1843  * @param auth_name the name of the authority (site of origin) of ctask->host
1844  *
1845  */
1846 static void
1847 process_get_authority (void *cls,
1848                        const char* auth_name)
1849 {
1850   struct ProxyCurlTask *ctask = cls;
1851
1852   if (NULL == auth_name)
1853   {
1854     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1855                 "Get authority failed!\n");
1856     strcpy (ctask->authority, "");
1857   }
1858   else
1859   {
1860     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1861                 "Get authority yielded %s\n", auth_name);
1862     strcpy (ctask->authority, auth_name);
1863   }
1864
1865   GNUNET_GNS_lookup_zone (gns_handle,
1866                           ctask->host,
1867                           local_gns_zone,
1868                           GNUNET_GNS_RECORD_LEHO,
1869                           GNUNET_YES, //Only cached for performance
1870                           shorten_zonekey,
1871                           &process_leho_lookup,
1872                           ctask);
1873 }
1874
1875
1876 static void*
1877 mhd_log_callback (void* cls, const char* url)
1878 {
1879   struct ProxyCurlTask *ctask;
1880
1881   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1882   strcpy (ctask->url, url);
1883   return ctask;
1884 }
1885
1886
1887 /**
1888  * Main MHD callback for handling requests.
1889  *
1890  * @param cls unused
1891  * @param con MHD connection handle
1892  * @param url the url in the request
1893  * @param meth the HTTP method used ("GET", "PUT", etc.)
1894  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1895  * @param upload_data the data being uploaded (excluding HEADERS,
1896  *        for a POST that fits into memory and that is encoded
1897  *        with a supported encoding, the POST data will NOT be
1898  *        given in upload_data and is instead available as
1899  *        part of MHD_get_connection_values; very large POST
1900  *        data *will* be made available incrementally in
1901  *        upload_data)
1902  * @param upload_data_size set initially to the size of the
1903  *        upload_data provided; the method must update this
1904  *        value to the number of bytes NOT processed;
1905  * @param con_cls pointer to location where we store the 'struct Request'
1906  * @return MHD_YES if the connection was handled successfully,
1907  *         MHD_NO if the socket must be closed due to a serious
1908  *         error while handling the request
1909  */
1910 static int
1911 create_response (void *cls,
1912                  struct MHD_Connection *con,
1913                  const char *url,
1914                  const char *meth,
1915                  const char *ver,
1916                  const char *upload_data,
1917                  size_t *upload_data_size,
1918                  void **con_cls)
1919 {
1920   struct MhdHttpList* hd = cls;
1921   const char* page = "<html><head><title>gnunet-gns-proxy</title>"
1922                       "</head><body>cURL fail</body></html>";
1923   
1924   char curlurl[MAX_HTTP_URI_LENGTH]; // buffer overflow!
1925   int ret = MHD_YES;
1926   int i;
1927   struct ProxyCurlTask *ctask = *con_cls;
1928   struct ProxyUploadData *fin_post;
1929   struct curl_forms forms[5];
1930   struct ProxyUploadData *upload_data_iter;
1931   
1932   //FIXME handle
1933   if ((0 != strcasecmp (meth, MHD_HTTP_METHOD_GET)) &&
1934       (0 != strcasecmp (meth, MHD_HTTP_METHOD_PUT)) &&
1935       (0 != strcasecmp (meth, MHD_HTTP_METHOD_POST)) &&
1936       (0 != strcasecmp (meth, MHD_HTTP_METHOD_HEAD)))
1937   {
1938     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1939                 "MHD: %s NOT IMPLEMENTED!\n", meth);
1940     return MHD_NO;
1941   }
1942
1943
1944   if (GNUNET_NO == ctask->accepted)
1945   {
1946
1947     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1948                 "Got %s request for %s\n", meth, url);
1949     ctask->mhd = hd;
1950     ctask->curl = curl_easy_init();
1951     ctask->curl_running = GNUNET_NO;
1952     if (NULL == ctask->curl)
1953     {
1954       ctask->response = MHD_create_response_from_buffer (strlen (page),
1955                                                 (void*)page,
1956                                                 MHD_RESPMEM_PERSISTENT);
1957       ret = MHD_queue_response (con,
1958                                 MHD_HTTP_OK,
1959                                 ctask->response);
1960       MHD_destroy_response (ctask->response);
1961       GNUNET_free (ctask);
1962       return ret;
1963     }
1964     
1965     if (ctask->mhd->is_ssl)
1966       ctask->port = HTTPS_PORT;
1967     else
1968       ctask->port = HTTP_PORT;
1969
1970     MHD_get_connection_values (con,
1971                                MHD_HEADER_KIND,
1972                                &con_val_iter, ctask);
1973     
1974     curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1975     curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1976     curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &curl_download_cb);
1977     curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1978     curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 0);
1979     curl_easy_setopt (ctask->curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1980
1981     if (GNUNET_NO == ctask->mhd->is_ssl)
1982     {
1983       sprintf (curlurl, "http://%s:%d%s", ctask->host, ctask->port, ctask->url);
1984       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1985     }
1986     
1987
1988     curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1989     curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1990     curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1991     
1992     /* Add GNS header */
1993     ctask->headers = curl_slist_append (ctask->headers,
1994                                           "GNS: YES");
1995     ctask->accepted = GNUNET_YES;
1996     ctask->download_in_progress = GNUNET_YES;
1997     ctask->buf_status = BUF_WAIT_FOR_CURL;
1998     ctask->connection = con;
1999     ctask->curl_response_code = MHD_HTTP_OK;
2000     ctask->buffer_read_ptr = ctask->buffer;
2001     ctask->buffer_write_ptr = ctask->buffer;
2002     ctask->pp_task = GNUNET_SCHEDULER_NO_TASK;
2003     
2004
2005     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_PUT))
2006     {
2007       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2008                   "Setting up PUT\n");
2009       
2010       curl_easy_setopt (ctask->curl, CURLOPT_UPLOAD, 1);
2011       curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2012       curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION, &put_read_callback);
2013       ctask->headers = curl_slist_append (ctask->headers,
2014                                           "Transfer-Encoding: chunked");
2015     }
2016
2017     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2018     {
2019       //FIXME handle multipart
2020       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2021                   "Setting up POST processor\n");
2022       ctask->post_handler = MHD_create_post_processor (con,
2023                                  POSTBUFFERSIZE,
2024                                  &con_post_data_iter,
2025                                  ctask);
2026       ctask->headers = curl_slist_append (ctask->headers,
2027                                          "Transfer-Encoding: chunked");
2028       return MHD_YES;
2029     }
2030
2031     if (0 == strcasecmp (meth, MHD_HTTP_METHOD_HEAD))
2032     {
2033       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2034                   "Setting NOBODY\n");
2035       curl_easy_setopt (ctask->curl, CURLOPT_NOBODY, 1);
2036     }
2037
2038     
2039     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2040                   "MHD: Adding new curl task for %s\n", ctask->host);
2041
2042     GNUNET_GNS_get_authority (gns_handle,
2043                               ctask->host,
2044                               &process_get_authority,
2045                               ctask);
2046     ctask->ready_to_queue = GNUNET_NO;
2047     ctask->fin = GNUNET_NO;
2048     ctask->curl_running = GNUNET_YES;
2049     return MHD_YES;
2050   }
2051
2052   ctask = (struct ProxyCurlTask *) *con_cls;
2053   if (0 == strcasecmp (meth, MHD_HTTP_METHOD_POST))
2054   {
2055     if (0 != *upload_data_size)
2056     {
2057       
2058       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2059                   "Invoking POST processor\n");
2060       MHD_post_process (ctask->post_handler,
2061                         upload_data, *upload_data_size);
2062       *upload_data_size = 0;
2063       if ((GNUNET_NO == ctask->is_httppost) &&
2064           (GNUNET_NO == ctask->curl_running))
2065       {
2066         curl_easy_setopt (ctask->curl, CURLOPT_POST, 1);
2067         curl_easy_setopt (ctask->curl, CURLOPT_READFUNCTION,
2068                           &post_read_callback);
2069         curl_easy_setopt (ctask->curl, CURLOPT_READDATA, ctask);
2070         
2071         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2072                   "MHD: Adding new curl task for %s\n", ctask->host);
2073
2074         GNUNET_GNS_get_authority (gns_handle,
2075                                   ctask->host,
2076                                   &process_get_authority,
2077                                   ctask);
2078         ctask->ready_to_queue = GNUNET_NO;
2079         ctask->fin = GNUNET_NO;
2080         ctask->curl_running = GNUNET_YES;
2081       }
2082       return MHD_YES;
2083     }
2084     else if (GNUNET_NO == ctask->post_done)
2085     {
2086       if (GNUNET_YES == ctask->is_httppost)
2087       {
2088         for (upload_data_iter = ctask->upload_data_head;
2089              NULL != upload_data_iter;
2090              upload_data_iter = upload_data_iter->next)
2091         {
2092           i = 0;
2093           if (NULL != upload_data_iter->filename)
2094           {
2095             forms[i].option = CURLFORM_FILENAME;
2096             forms[i].value = upload_data_iter->filename;
2097             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2098                         "Adding filename %s\n",
2099                         forms[i].value);
2100             i++;
2101           }
2102           if (NULL != upload_data_iter->content_type)
2103           {
2104             forms[i].option = CURLFORM_CONTENTTYPE;
2105             forms[i].value = upload_data_iter->content_type;
2106             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2107                         "Adding content type %s\n",
2108                         forms[i].value);
2109             i++;
2110           }
2111           forms[i].option = CURLFORM_PTRCONTENTS;
2112           forms[i].value = upload_data_iter->value;
2113           forms[i+1].option = CURLFORM_END;
2114
2115           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2116                       "Adding formdata for %s (len=%lld)\n",
2117                       upload_data_iter->key,
2118                       upload_data_iter->total_bytes);
2119
2120           curl_formadd(&ctask->httppost, &ctask->httppost_last,
2121                        CURLFORM_COPYNAME, upload_data_iter->key,
2122                        CURLFORM_CONTENTSLENGTH, upload_data_iter->total_bytes,
2123                        CURLFORM_ARRAY, forms,
2124                        CURLFORM_END);
2125         }
2126         curl_easy_setopt (ctask->curl, CURLOPT_HTTPPOST,
2127                           ctask->httppost);
2128
2129         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2130                 "MHD: Adding new curl task for %s\n", ctask->host);
2131
2132         GNUNET_GNS_get_authority (gns_handle,
2133                                   ctask->host,
2134                                   &process_get_authority,
2135                                   ctask);
2136         ctask->ready_to_queue = GNUNET_YES;
2137         ctask->fin = GNUNET_NO;
2138         ctask->curl_running = GNUNET_YES;
2139         ctask->post_done = GNUNET_YES;
2140         return MHD_YES;
2141       }
2142
2143       fin_post = GNUNET_malloc (sizeof (struct ProxyUploadData));
2144       GNUNET_CONTAINER_DLL_insert_tail (ctask->upload_data_head,
2145                                         ctask->upload_data_tail,
2146                                         fin_post);
2147       ctask->post_done = GNUNET_YES;
2148       return MHD_YES;
2149     }
2150   }
2151   
2152   if (GNUNET_YES != ctask->ready_to_queue)
2153     return MHD_YES; /* wait longer */
2154   
2155   if (GNUNET_YES == ctask->fin)
2156     return MHD_YES;
2157
2158   ctask->fin = GNUNET_YES;
2159   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2160               "MHD: Queueing response for %s\n", ctask->url);
2161   ret = MHD_queue_response (con, ctask->curl_response_code, ctask->response);
2162   run_mhd_now (ctask->mhd);
2163   return ret;
2164 }
2165
2166
2167 /**
2168  * run all httpd
2169  */
2170 static void
2171 run_httpds ()
2172 {
2173   struct MhdHttpList *hd;
2174
2175   for (hd=mhd_httpd_head; NULL != hd; hd = hd->next)
2176     run_httpd (hd);
2177
2178 }
2179
2180
2181 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
2182
2183
2184 /**
2185  * schedule mhd
2186  *
2187  * @param hd the daemon to run
2188  */
2189 static void
2190 run_httpd (struct MhdHttpList *hd)
2191 {
2192   fd_set rs;
2193   fd_set ws;
2194   fd_set es;
2195   struct GNUNET_NETWORK_FDSet *wrs;
2196   struct GNUNET_NETWORK_FDSet *wws;
2197   struct GNUNET_NETWORK_FDSet *wes;
2198   int max;
2199   int haveto;
2200   UNSIGNED_MHD_LONG_LONG timeout;
2201   struct GNUNET_TIME_Relative tv;
2202
2203   FD_ZERO (&rs);
2204   FD_ZERO (&ws);
2205   FD_ZERO (&es);
2206   wrs = GNUNET_NETWORK_fdset_create ();
2207   wes = GNUNET_NETWORK_fdset_create ();
2208   wws = GNUNET_NETWORK_fdset_create ();
2209   max = -1;
2210   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
2211   
2212   
2213   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2214               "MHD fds: max=%d\n", max);
2215   
2216   haveto = MHD_get_timeout (hd->daemon, &timeout);
2217
2218   if (MHD_YES == haveto)
2219     tv.rel_value = (uint64_t) timeout;
2220   else
2221     tv = GNUNET_TIME_UNIT_FOREVER_REL;
2222   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
2223   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
2224   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
2225   
2226   if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2227     GNUNET_SCHEDULER_cancel (hd->httpd_task);
2228   hd->httpd_task =
2229     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
2230                                  tv, wrs, wws,
2231                                  &do_httpd, hd);
2232   GNUNET_NETWORK_fdset_destroy (wrs);
2233   GNUNET_NETWORK_fdset_destroy (wws);
2234   GNUNET_NETWORK_fdset_destroy (wes);
2235 }
2236
2237
2238 /**
2239  * Task run whenever HTTP server operations are pending.
2240  *
2241  * @param cls unused
2242  * @param tc sched context
2243  */
2244 static void
2245 do_httpd (void *cls,
2246           const struct GNUNET_SCHEDULER_TaskContext *tc)
2247 {
2248   struct MhdHttpList *hd = cls;
2249   
2250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2251               "MHD: Main loop\n");
2252   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK; 
2253   MHD_run (hd->daemon);
2254   run_httpd (hd);
2255 }
2256
2257
2258 /**
2259  * Read data from socket
2260  *
2261  * @param cls the closure
2262  * @param tc scheduler context
2263  */
2264 static void
2265 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2266
2267
2268 /**
2269  * Read from remote end
2270  *
2271  * @param cls closure
2272  * @param tc scheduler context
2273  */
2274 static void
2275 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
2276
2277
2278 /**
2279  * Write data to remote socket
2280  *
2281  * @param cls the closure
2282  * @param tc scheduler context
2283  */
2284 static void
2285 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2286 {
2287   struct Socks5Request *s5r = cls;
2288   unsigned int len;
2289
2290   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2291
2292   if ((NULL != tc->read_ready) &&
2293       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
2294       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
2295                                          s5r->rbuf_len)>0)))
2296   {
2297     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2298                 "Successfully sent %d bytes to remote socket\n",
2299                 len);
2300   }
2301   else
2302   {
2303     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
2304     if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2305       GNUNET_SCHEDULER_cancel (s5r->rtask);
2306     if (GNUNET_SCHEDULER_NO_TASK != s5r->wtask)
2307       GNUNET_SCHEDULER_cancel (s5r->wtask);
2308     if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2309       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2310     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2311     GNUNET_NETWORK_socket_close (s5r->sock);
2312     GNUNET_free(s5r);
2313     return;
2314   }
2315
2316   s5r->rtask =
2317     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2318                                    s5r->sock,
2319                                    &do_read, s5r);
2320 }
2321
2322
2323 /**
2324  * Clean up s5r handles
2325  *
2326  * @param s5r the handle to destroy
2327  */
2328 static void
2329 cleanup_s5r (struct Socks5Request *s5r)
2330 {
2331   if (GNUNET_SCHEDULER_NO_TASK != s5r->rtask)
2332     GNUNET_SCHEDULER_cancel (s5r->rtask);
2333   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdwtask)
2334     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2335   if (GNUNET_SCHEDULER_NO_TASK != s5r->fwdrtask)
2336     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);  
2337   if (NULL != s5r->remote_sock)
2338     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2339   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
2340     GNUNET_NETWORK_socket_close (s5r->sock);
2341   
2342   GNUNET_free(s5r);
2343 }
2344
2345
2346 /**
2347  * Write data to socket
2348  *
2349  * @param cls the closure
2350  * @param tc scheduler context
2351  */
2352 static void
2353 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2354 {
2355   struct Socks5Request *s5r = cls;
2356   unsigned int len;
2357
2358   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2359
2360   if ((NULL != tc->read_ready) &&
2361       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
2362       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
2363                                          s5r->wbuf_len)>0)))
2364   {
2365     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2366                 "Successfully sent %d bytes to socket\n",
2367                 len);
2368   }
2369   else
2370   {    
2371     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
2372     s5r->cleanup = GNUNET_YES;
2373     s5r->cleanup_sock = GNUNET_YES;
2374     cleanup_s5r (s5r); 
2375     return;
2376   }
2377
2378   if (GNUNET_YES == s5r->cleanup)
2379   {
2380     cleanup_s5r (s5r);
2381     return;
2382   }
2383
2384   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
2385       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
2386     s5r->fwdrtask =
2387       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2388                                      s5r->remote_sock,
2389                                      &do_read_remote, s5r);
2390 }
2391
2392
2393 /**
2394  * Read from remote end
2395  *
2396  * @param cls closure
2397  * @param tc scheduler context
2398  */
2399 static void
2400 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2401 {
2402   struct Socks5Request *s5r = cls;
2403   
2404   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2405   if ((NULL != tc->write_ready) &&
2406       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
2407       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
2408                                          sizeof (s5r->wbuf))))
2409   {
2410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2411                 "Successfully read %d bytes from remote socket\n",
2412                 s5r->wbuf_len);
2413   }
2414   else
2415   {
2416     if (0 == s5r->wbuf_len)
2417       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2418                   "0 bytes received from remote... graceful shutdown!\n");
2419     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2420       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2421     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2422       GNUNET_SCHEDULER_cancel (s5r->rtask);
2423     
2424     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2425     s5r->remote_sock = NULL;
2426     GNUNET_NETWORK_socket_close (s5r->sock);
2427     GNUNET_free(s5r);
2428
2429     return;
2430   }
2431   
2432   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2433                                                s5r->sock,
2434                                                &do_write, s5r);  
2435 }
2436
2437
2438 /**
2439  * Adds a socket to MHD
2440  *
2441  * @param h the handle to the socket to add
2442  * @param daemon the daemon to add the fd to
2443  * @return whatever MHD_add_connection returns
2444  */
2445 static int
2446 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
2447 {
2448   int fd;
2449   struct sockaddr *addr;
2450   socklen_t len;
2451
2452   fd = dup (GNUNET_NETWORK_get_fd (h));
2453   addr = GNUNET_NETWORK_get_addr (h);
2454   len = GNUNET_NETWORK_get_addrlen (h);
2455
2456   return MHD_add_connection (daemon, fd, addr, len);
2457 }
2458
2459
2460 /**
2461  * Read file in filename
2462  *
2463  * @param filename file to read
2464  * @param size pointer where filesize is stored
2465  * @return NULL on error
2466  */
2467 static void*
2468 load_file (const char* filename, 
2469            unsigned int* size)
2470 {
2471   void *buffer;
2472   uint64_t fsize;
2473
2474   if (GNUNET_OK !=
2475       GNUNET_DISK_file_size (filename, &fsize,
2476                              GNUNET_YES, GNUNET_YES))
2477     return NULL;
2478   if (fsize > MAX_PEM_SIZE)
2479     return NULL;
2480   *size = (unsigned int) fsize;
2481   buffer = GNUNET_malloc (*size);
2482   if (fsize != GNUNET_DISK_fn_read (filename, buffer, (size_t) fsize))
2483   {
2484     GNUNET_free (buffer);
2485     return NULL;
2486   }
2487   return buffer;
2488 }
2489
2490
2491 /**
2492  * Load PEM key from file
2493  *
2494  * @param key where to store the data
2495  * @param keyfile path to the PEM file
2496  * @return GNUNET_OK on success
2497  */
2498 static int
2499 load_key_from_file (gnutls_x509_privkey_t key, const char* keyfile)
2500 {
2501   gnutls_datum_t key_data;
2502   int ret;
2503
2504   key_data.data = load_file (keyfile, &key_data.size);
2505   ret = gnutls_x509_privkey_import (key, &key_data,
2506                                     GNUTLS_X509_FMT_PEM);
2507   if (GNUTLS_E_SUCCESS != ret)
2508   {
2509     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2510                 _("Unable to import private key from file `%s'\n"),
2511                 keyfile);
2512     GNUNET_break (0);
2513   }
2514   GNUNET_free (key_data.data);
2515   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2516 }
2517
2518
2519 /**
2520  * Load cert from file
2521  *
2522  * @param crt struct to store data in
2523  * @param certfile path to pem file
2524  * @return GNUNET_OK on success
2525  */
2526 static int
2527 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
2528 {
2529   gnutls_datum_t cert_data;
2530   int ret;
2531
2532   cert_data.data = load_file (certfile, &cert_data.size);
2533   ret = gnutls_x509_crt_import (crt, &cert_data,
2534                                 GNUTLS_X509_FMT_PEM);
2535   if (GNUTLS_E_SUCCESS != ret)
2536   {
2537     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2538                _("Unable to import certificate %s\n"), certfile);
2539     GNUNET_break (0);
2540   }
2541   GNUNET_free (cert_data.data);
2542   return (GNUTLS_E_SUCCESS != ret) ? GNUNET_SYSERR : GNUNET_OK;
2543 }
2544
2545
2546 /**
2547  * Generate new certificate for specific name
2548  *
2549  * @param name the subject name to generate a cert for
2550  * @return a struct holding the PEM data
2551  */
2552 static struct ProxyGNSCertificate *
2553 generate_gns_certificate (const char *name)
2554 {
2555   int ret;
2556   unsigned int serial;
2557   size_t key_buf_size;
2558   size_t cert_buf_size;
2559   gnutls_x509_crt_t request;
2560   time_t etime;
2561   struct tm *tm_data;
2562
2563   ret = gnutls_x509_crt_init (&request);
2564
2565   if (GNUTLS_E_SUCCESS != ret)
2566   {
2567     GNUNET_break (0);
2568   }
2569
2570   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_key (request, proxy_ca.key));
2571
2572   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
2573
2574   struct ProxyGNSCertificate *pgc =
2575     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
2576
2577   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
2578   
2579   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
2580                                  0, "DE", 2);
2581   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
2582                                  0, "GADS", 4);
2583   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
2584                                  0, name, strlen (name));
2585   GNUNET_break (GNUTLS_E_SUCCESS == gnutls_x509_crt_set_version (request, 3));
2586
2587   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
2588
2589   etime = time (NULL);
2590   tm_data = localtime (&etime);
2591   
2592
2593   ret = gnutls_x509_crt_set_serial (request,
2594                                     &serial,
2595                                     sizeof (serial));
2596
2597   ret = gnutls_x509_crt_set_activation_time (request,
2598                                              etime);
2599   tm_data->tm_year++;
2600   etime = mktime (tm_data);
2601
2602   if (-1 == etime)
2603   {
2604     GNUNET_break (0);
2605   }
2606
2607   ret = gnutls_x509_crt_set_expiration_time (request,
2608                                              etime);
2609   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
2610
2611   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
2612
2613   key_buf_size = sizeof (pgc->key);
2614   cert_buf_size = sizeof (pgc->cert);
2615
2616   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
2617   
2618   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
2619                           pgc->cert, &cert_buf_size);
2620
2621   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
2622                           pgc->key, &key_buf_size);
2623
2624
2625   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
2626   gnutls_x509_crt_deinit (request);
2627
2628   return pgc;
2629
2630 }
2631
2632
2633 /**
2634  * Accept policy for mhdaemons
2635  *
2636  * @param cls NULL
2637  * @param addr the sockaddr
2638  * @param addrlen the sockaddr length
2639  * @return MHD_NO if sockaddr is wrong or number of connections is too high
2640  */
2641 static int
2642 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
2643 {
2644   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2645               "In MHD accept policy cb\n");
2646
2647   if (addr != NULL)
2648   {
2649     if (addr->sa_family == AF_UNIX)
2650       return MHD_NO;
2651   }
2652
2653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2654               "Connection accepted\n");
2655
2656   return MHD_YES;
2657 }
2658
2659
2660 /**
2661  * Adds a socket to an SSL MHD instance
2662  * It is important the the domain name is
2663  * correct. In most cases we need to start a new daemon
2664  *
2665  * @param h the handle to add to a daemon
2666  * @param domain the domain the ssl daemon has to serve
2667  * @return MHD_YES on success
2668  */
2669 static int
2670 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, const char* domain)
2671 {
2672   struct MhdHttpList *hd;
2673   struct ProxyGNSCertificate *pgc;
2674   struct NetworkHandleList *nh;
2675
2676   for (hd = mhd_httpd_head; NULL != hd; hd = hd->next)
2677     if (0 == strcmp (hd->domain, domain))
2678       break;
2679
2680   if (NULL == hd)
2681   {    
2682     pgc = generate_gns_certificate (domain);
2683     
2684     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2685     hd->is_ssl = GNUNET_YES;
2686     strcpy (hd->domain, domain);
2687     hd->proxy_cert = pgc;
2688
2689     /* Start new MHD */
2690     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2691                 "No previous SSL instance found... starting new one for %s\n",
2692                 domain);
2693 #if HAVE_MHD_NO_LISTEN_SOCKET
2694     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL | MHD_USE_NO_LISTEN_SOCKET,
2695                                    0,
2696                                    &accept_cb, NULL,
2697                                    &create_response, hd,
2698                                    MHD_OPTION_CONNECTION_LIMIT,
2699                                    MHD_MAX_CONNECTIONS,
2700                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2701                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2702                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2703                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2704                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2705                                    NULL,
2706                                    MHD_OPTION_END);
2707 #else
2708     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL,
2709                                    4444 /* dummy port */,
2710                                    &accept_cb, NULL,
2711                                    &create_response, hd,
2712                                    MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2713                                    MHD_OPTION_CONNECTION_LIMIT,
2714                                    MHD_MAX_CONNECTIONS,
2715                                    MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2716                                    MHD_OPTION_NOTIFY_COMPLETED, NULL, NULL,
2717                                    MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
2718                                    MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
2719                                    MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback,
2720                                    NULL,
2721                                    MHD_OPTION_END);
2722 #endif
2723     GNUNET_assert (hd->daemon != NULL);
2724     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2725     
2726     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2727   }
2728
2729   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2730   nh->h = h;
2731
2732   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
2733                                hd->socket_handles_tail,
2734                                nh);
2735   
2736   return add_handle_to_mhd (h, hd->daemon);
2737 }
2738
2739
2740 /**
2741  * Read data from incoming connection
2742  *
2743  * @param cls the closure
2744  * @param tc the scheduler context
2745  */
2746 static void
2747 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2748 {
2749   struct Socks5Request *s5r = cls;
2750   struct socks5_client_hello *c_hello;
2751   struct socks5_server_hello *s_hello;
2752   struct socks5_client_request *c_req;
2753   struct socks5_server_response *s_resp;
2754   int ret;
2755   char domain[256];
2756   uint8_t dom_len;
2757   uint16_t req_port;
2758   struct hostent *phost;
2759   uint32_t remote_ip;
2760   struct sockaddr_in remote_addr;
2761   struct in_addr *r_sin_addr;
2762   struct NetworkHandleList *nh;
2763
2764   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
2765
2766   if ((NULL != tc->write_ready) &&
2767       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
2768       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
2769                                          sizeof (s5r->rbuf))))
2770   {
2771     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2772                 "Successfully read %d bytes from socket\n",
2773                 s5r->rbuf_len);
2774   }
2775   else
2776   {
2777     if (s5r->rbuf_len != 0)
2778       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
2779     else
2780       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
2781
2782     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2783       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2784     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
2785       GNUNET_SCHEDULER_cancel (s5r->wtask);
2786     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2787       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2788     GNUNET_NETWORK_socket_close (s5r->remote_sock);
2789     GNUNET_NETWORK_socket_close (s5r->sock);
2790     GNUNET_free(s5r);
2791     return;
2792   }
2793
2794   if (s5r->state == SOCKS5_INIT)
2795   {
2796     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2797                 "SOCKS5 init\n");
2798     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
2799
2800     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
2801
2802     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
2803     s5r->wbuf_len = sizeof( struct socks5_server_hello );
2804
2805     s_hello->version = c_hello->version;
2806     s_hello->auth_method = SOCKS_AUTH_NONE;
2807
2808     /* Write response to client */
2809     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2810                                                 s5r->sock,
2811                                                 &do_write, s5r);
2812
2813     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2814                                                 s5r->sock,
2815                                                 &do_read, s5r);
2816
2817     s5r->state = SOCKS5_REQUEST;
2818     return;
2819   }
2820
2821   if (s5r->state == SOCKS5_REQUEST)
2822   {
2823     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2824                 "Processing SOCKS5 request\n");
2825     c_req = (struct socks5_client_request*)&s5r->rbuf;
2826     s_resp = (struct socks5_server_response*)&s5r->wbuf;
2827     //Only 10byte for ipv4 response!
2828     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
2829
2830     GNUNET_assert (c_req->addr_type == 3);
2831
2832     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
2833     memset(domain, 0, sizeof(domain));
2834     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
2835     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
2836
2837     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2838                 "Requested connection is %s:%d\n",
2839                 domain,
2840                 ntohs(req_port));
2841
2842     if (is_tld (domain, GNUNET_GNS_TLD) ||
2843         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
2844     {
2845       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2846                   "Requested connection is gnunet tld\n",
2847                   domain);
2848       
2849       ret = MHD_NO;
2850       if (ntohs(req_port) == HTTPS_PORT)
2851       {
2852         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2853                     "Requested connection is HTTPS\n");
2854         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
2855       }
2856       else if (NULL != httpd)
2857       {
2858         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2859                     "Requested connection is HTTP\n");
2860         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2861         nh->h = s5r->sock;
2862
2863         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2864                                mhd_httpd_head->socket_handles_tail,
2865                                nh);
2866
2867         ret = add_handle_to_mhd ( s5r->sock, httpd );
2868       }
2869
2870       if (ret != MHD_YES)
2871       {
2872         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2873                     _("Failed to start HTTP server\n"));
2874         s_resp->version = 0x05;
2875         s_resp->reply = 0x01;
2876         s5r->cleanup = GNUNET_YES;
2877         s5r->cleanup_sock = GNUNET_YES;
2878         s5r->wtask = 
2879           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2880                                         s5r->sock,
2881                                         &do_write, s5r);
2882         return;
2883       }
2884       
2885       /* Signal success */
2886       s_resp->version = 0x05;
2887       s_resp->reply = 0x00;
2888       s_resp->reserved = 0x00;
2889       s_resp->addr_type = 0x01;
2890       
2891       s5r->cleanup = GNUNET_YES;
2892       s5r->cleanup_sock = GNUNET_NO;
2893       s5r->wtask =
2894         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2895                                         s5r->sock,
2896                                         &do_write, s5r);
2897       run_httpds ();
2898       return;
2899     }
2900     else
2901     {
2902       phost = (struct hostent*)gethostbyname (domain);
2903       if (phost == NULL)
2904       {
2905         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2906                     "Resolve %s error!\n", domain );
2907         s_resp->version = 0x05;
2908         s_resp->reply = 0x01;
2909         s5r->cleanup = GNUNET_YES;
2910         s5r->cleanup_sock = GNUNET_YES;
2911         s5r->wtask = 
2912           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2913                                           s5r->sock,
2914                                           &do_write, s5r);
2915         return;
2916       }
2917
2918       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2919                                                        SOCK_STREAM,
2920                                                        0);
2921       r_sin_addr = (struct in_addr*)(phost->h_addr);
2922       remote_ip = r_sin_addr->s_addr;
2923       memset(&remote_addr, 0, sizeof(remote_addr));
2924       remote_addr.sin_family = AF_INET;
2925 #if HAVE_SOCKADDR_IN_SIN_LEN
2926       remote_addr.sin_len = sizeof (remote_addr);
2927 #endif
2928       remote_addr.sin_addr.s_addr = remote_ip;
2929       remote_addr.sin_port = req_port;
2930       
2931       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2932                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2933                   ntohs(req_port));
2934
2935       if ((GNUNET_OK !=
2936           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2937                                           (const struct sockaddr*)&remote_addr,
2938                                           sizeof (remote_addr)))
2939           && (errno != EINPROGRESS))
2940       {
2941         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2942         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2943                     "socket request error...\n");
2944         s_resp->version = 0x05;
2945         s_resp->reply = 0x01;
2946         s5r->wtask =
2947           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2948                                           s5r->sock,
2949                                           &do_write, s5r);
2950         //TODO see above
2951         return;
2952       }
2953
2954       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2955                   "new remote connection\n");
2956
2957       s_resp->version = 0x05;
2958       s_resp->reply = 0x00;
2959       s_resp->reserved = 0x00;
2960       s_resp->addr_type = 0x01;
2961
2962       s5r->state = SOCKS5_DATA_TRANSFER;
2963
2964       s5r->wtask =
2965         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2966                                         s5r->sock,
2967                                         &do_write, s5r);
2968       s5r->rtask =
2969         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2970                                        s5r->sock,
2971                                        &do_read, s5r);
2972
2973     }
2974     return;
2975   }
2976
2977   if (s5r->state == SOCKS5_DATA_TRANSFER)
2978   {
2979     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2980     {
2981       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2982                   "Closing connection to client\n");
2983       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2984         GNUNET_SCHEDULER_cancel (s5r->rtask);
2985       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2986         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2987       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2988         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2989       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2990         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2991       
2992       if (s5r->remote_sock != NULL)
2993         GNUNET_NETWORK_socket_close (s5r->remote_sock);
2994       GNUNET_NETWORK_socket_close (s5r->sock);
2995       GNUNET_free(s5r);
2996       return;
2997     }
2998
2999     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3000                 "forwarding %d bytes from client\n", s5r->rbuf_len);
3001
3002     s5r->fwdwtask =
3003       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
3004                                       s5r->remote_sock,
3005                                       &do_write_remote, s5r);
3006
3007     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
3008     {
3009       s5r->fwdrtask =
3010         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3011                                        s5r->remote_sock,
3012                                        &do_read_remote, s5r);
3013     }
3014   }
3015 }
3016
3017
3018 /**
3019  * Accept new incoming connections
3020  *
3021  * @param cls the closure
3022  * @param tc the scheduler context
3023  */
3024 static void
3025 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3026 {
3027   struct GNUNET_NETWORK_Handle *s;
3028   struct Socks5Request *s5r;
3029
3030   ltask = GNUNET_SCHEDULER_NO_TASK;
3031   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
3032     return;
3033
3034   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3035                                          lsock,
3036                                          &do_accept, NULL);
3037
3038   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
3039
3040   if (NULL == s)
3041   {
3042     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
3043     return;
3044   }
3045
3046   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3047               "Got an inbound connection, waiting for data\n");
3048
3049   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
3050   s5r->sock = s;
3051   s5r->state = SOCKS5_INIT;
3052   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
3053   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
3054   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
3055   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3056                                               s5r->sock,
3057                                               &do_read, s5r);
3058 }
3059
3060
3061 /**
3062  * Task run on shutdown
3063  *
3064  * @param cls closure
3065  * @param tc task context
3066  */
3067 static void
3068 do_shutdown (void *cls,
3069              const struct GNUNET_SCHEDULER_TaskContext *tc)
3070 {
3071   struct MhdHttpList *hd;
3072   struct MhdHttpList *tmp_hd;
3073   struct NetworkHandleList *nh;
3074   struct NetworkHandleList *tmp_nh;
3075   struct ProxyCurlTask *ctask;
3076   struct ProxyCurlTask *ctask_tmp;
3077   struct ProxyUploadData *pdata;
3078   
3079   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3080               "Shutting down...\n");
3081   if (NULL != local_gns_zone)
3082     GNUNET_free (local_gns_zone); 
3083   if (NULL != local_private_zone)
3084     GNUNET_free (local_private_zone);
3085   if (NULL != local_shorten_zone)
3086     GNUNET_free (local_shorten_zone);
3087
3088   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
3089   {
3090     GNUNET_SCHEDULER_cancel (curl_download_task);
3091     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
3092   }
3093
3094   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
3095   {
3096     tmp_hd = hd->next;
3097
3098     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3099                 "Stopping daemon\n");
3100
3101     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
3102     {
3103       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3104                   "Stopping select task %d\n",
3105                   hd->httpd_task);
3106       GNUNET_SCHEDULER_cancel (hd->httpd_task);
3107       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3108     }
3109     if (NULL != hd->daemon)
3110     {
3111       MHD_stop_daemon (hd->daemon);
3112       hd->daemon = NULL;
3113     }
3114     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
3115     {
3116       tmp_nh = nh->next;
3117
3118       GNUNET_NETWORK_socket_close (nh->h);
3119
3120       GNUNET_free (nh);
3121     }
3122
3123     if (NULL != hd->proxy_cert)
3124     {
3125       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3126                   "Free certificate\n");
3127       GNUNET_free (hd->proxy_cert);
3128     }
3129
3130     GNUNET_free (hd);
3131   }
3132
3133   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
3134   {
3135     ctask_tmp = ctask->next;
3136
3137     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3138                 "Cleaning up cURL task\n");
3139
3140     if (ctask->curl != NULL)
3141       curl_easy_cleanup (ctask->curl);
3142     ctask->curl = NULL;
3143     if (NULL != ctask->headers)
3144       curl_slist_free_all (ctask->headers);
3145     if (NULL != ctask->resolver)
3146       curl_slist_free_all (ctask->resolver);
3147
3148     if (NULL != ctask->response)
3149       MHD_destroy_response (ctask->response);
3150
3151     pdata = ctask->upload_data_head;
3152
3153     //FIXME free pdata here
3154     for (; pdata != NULL; pdata = ctask->upload_data_head)
3155     {
3156       GNUNET_CONTAINER_DLL_remove (ctask->upload_data_head,
3157                                    ctask->upload_data_tail,
3158                                    pdata);
3159       GNUNET_free_non_null (pdata->filename);
3160       GNUNET_free_non_null (pdata->content_type);
3161       GNUNET_free_non_null (pdata->key);
3162       GNUNET_free_non_null (pdata->value);
3163       GNUNET_free (pdata);
3164     }
3165     GNUNET_free (ctask);
3166   }
3167   curl_multi_cleanup (curl_multi);
3168   GNUNET_GNS_disconnect (gns_handle);
3169   gnutls_global_deinit ();
3170 }
3171
3172
3173 /**
3174  * Compiles a regex for us
3175  *
3176  * @param re ptr to re struct
3177  * @param rt the expression to compile
3178  * @return 0 on success
3179  */
3180 static int
3181 compile_regex (regex_t *re, const char* rt)
3182 {
3183   int status;
3184   char err[1024];
3185
3186   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
3187   if (status)
3188   {
3189     regerror (status, re, err, 1024);
3190     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3191                 "Regex error compiling '%s': %s\n", rt, err);
3192     return 1;
3193   }
3194   return 0;
3195 }
3196
3197
3198 /**
3199  * Loads the users local zone key
3200  *
3201  * @return GNUNET_YES on success
3202  */
3203 static int
3204 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
3205 {
3206   char *keyfile;
3207   struct GNUNET_CRYPTO_RsaPrivateKey *key;
3208   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
3209   struct GNUNET_CRYPTO_ShortHashCode *zone;
3210   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
3211
3212   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3213                                                             "ZONEKEY", &keyfile))
3214   {
3215     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3216                 "Unable to load zone key config value!\n");
3217     return GNUNET_NO;
3218   }
3219
3220   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
3221   {
3222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3223                 "Unable to load zone key %s!\n", keyfile);
3224     GNUNET_free(keyfile);
3225     return GNUNET_NO;
3226   }
3227
3228   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3229   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3230   local_gns_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3231   GNUNET_CRYPTO_short_hash(&pkey,
3232                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3233                            local_gns_zone);
3234   zone = local_gns_zone;
3235   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3236   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3237               "Using zone: %s!\n", &zonename);
3238   GNUNET_CRYPTO_rsa_key_free(key);
3239   GNUNET_free(keyfile);
3240   keyfile = NULL;
3241
3242   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3243                                                    "PRIVATE_ZONEKEY", &keyfile))
3244   {
3245     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3246                 "Unable to load private zone key config value!\n");
3247   }
3248
3249   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3250   {
3251     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3252                 "Unable to load private zone key %s!\n", keyfile);
3253     GNUNET_free(keyfile);
3254   }
3255   else
3256   {
3257     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3258     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3259     local_private_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3260     GNUNET_CRYPTO_short_hash(&pkey,
3261                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3262                              local_private_zone);
3263     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3264     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3265                 "Using private zone: %s!\n", &zonename);
3266     GNUNET_CRYPTO_rsa_key_free(key);
3267     GNUNET_free(keyfile);
3268   }
3269   keyfile = NULL;
3270
3271   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
3272                                                    "SHORTEN_ZONEKEY", &keyfile))
3273   {
3274     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3275                 "Unable to load shorten zone key config value!\n");
3276   }
3277
3278   if ((NULL != keyfile) && (GNUNET_NO == GNUNET_DISK_file_test (keyfile)))
3279   {
3280     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3281                 "Unable to load shorten zone key %s!\n", keyfile);
3282     GNUNET_free(keyfile);
3283   }
3284   else
3285   {
3286     key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
3287     GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
3288     local_shorten_zone = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_ShortHashCode));
3289     GNUNET_CRYPTO_short_hash(&pkey,
3290                              sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
3291                              local_shorten_zone);
3292     GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
3293     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3294                 "Using shorten zone: %s!\n", &zonename);
3295     GNUNET_CRYPTO_rsa_key_free(key);
3296     GNUNET_free(keyfile);
3297   }
3298
3299   return GNUNET_YES;
3300 }
3301
3302
3303 /**
3304  * Main function that will be run
3305  *
3306  * @param cls closure
3307  * @param args remaining command-line arguments
3308  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3309  * @param cfg configuration
3310  */
3311 static void
3312 run (void *cls, char *const *args, const char *cfgfile,
3313      const struct GNUNET_CONFIGURATION_Handle *cfg)
3314 {
3315   struct sockaddr_in sa;
3316   struct MhdHttpList *hd;
3317   char* cafile_cfg = NULL;
3318   char* cafile;
3319 #if !HAVE_MHD_NO_LISTEN_SOCKET
3320   size_t len;
3321   char* proxy_sockfile;
3322   struct sockaddr_un mhd_unix_sock_addr;
3323 #endif
3324
3325   if (NULL == (curl_multi = curl_multi_init ()))
3326   {
3327     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3328                 "Failed to create cURL multo handle!\n");
3329     return;
3330   }
3331   
3332   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3333               "Loading CA\n");
3334   cafile = cafile_opt;
3335   if (NULL == cafile)
3336   {
3337     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3338                                                               "PROXY_CACERT",
3339                                                               &cafile_cfg))
3340     {
3341       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3342                   "Unable to load proxy CA config value!\n");
3343       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3344                   "No proxy CA provided!\n");
3345       return;
3346     }
3347     cafile = cafile_cfg;
3348   }
3349   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3350               "Using %s as CA\n", cafile);
3351   
3352   gnutls_global_init ();
3353   gnutls_x509_crt_init (&proxy_ca.cert);
3354   gnutls_x509_privkey_init (&proxy_ca.key);
3355   
3356   if ( (GNUNET_OK != load_cert_from_file (proxy_ca.cert, cafile)) ||
3357        (GNUNET_OK != load_key_from_file (proxy_ca.key, cafile)) )
3358   {
3359     // FIXME: release resources...
3360     return;
3361   }
3362
3363   GNUNET_free_non_null (cafile_cfg);
3364   
3365   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3366               "Loading Template\n");
3367   
3368   compile_regex (&re_dotplus, (char*) RE_A_HREF);
3369
3370   if (NULL == (gns_handle = GNUNET_GNS_connect (cfg)))
3371   {
3372     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3373                 "Unable to connect to GNS!\n");
3374     return;
3375   }
3376   if (GNUNET_NO == load_local_zone_key (cfg))
3377   {
3378     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3379                 "Unable to load zone!\n");
3380     return;
3381   }
3382
3383   memset (&sa, 0, sizeof (sa));
3384   sa.sin_family = AF_INET;
3385   sa.sin_port = htons (port);
3386 #if HAVE_SOCKADDR_IN_SIN_LEN
3387   sa.sin_len = sizeof (sa);
3388 #endif
3389
3390   lsock = GNUNET_NETWORK_socket_create (AF_INET,
3391                                         SOCK_STREAM,
3392                                         0);
3393
3394   if ((NULL == lsock) ||
3395       (GNUNET_OK !=
3396        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
3397                                    sizeof (sa))))
3398   {
3399     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3400                 "Failed to create listen socket bound to `%s'",
3401                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3402     if (NULL != lsock)
3403       GNUNET_NETWORK_socket_close (lsock);
3404     return;
3405   }
3406
3407   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
3408   {
3409     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3410                 "Failed to listen on socket bound to `%s'",
3411                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
3412     return;
3413   }
3414   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
3415                                          lsock, &do_accept, NULL);
3416
3417   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
3418   {
3419     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3420                 "cURL global init failed!\n");
3421     return;
3422   }
3423
3424   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3425               "Proxy listens on port %u\n",
3426               port);
3427 #if ! HAVE_MHD_NO_LISTEN_SOCKET
3428   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
3429                                                             "PROXY_UNIXPATH",
3430                                                             &proxy_sockfile))
3431   {
3432     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3433                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
3434     return;
3435   }
3436   if (NULL == (mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
3437                                                                SOCK_STREAM,
3438                                                                0)))
3439   {
3440     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3441                 "Unable to create unix domain socket!\n");
3442     return;
3443   }
3444
3445   mhd_unix_sock_addr.sun_family = AF_UNIX;
3446   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
3447
3448 #if LINUX
3449   mhd_unix_sock_addr.sun_path[0] = '\0';
3450 #endif
3451 #if HAVE_SOCKADDR_IN_SIN_LEN
3452   mhd_unix_sock_addr.sun_len = (u_char) sizeof (struct sockaddr_un);
3453 #endif
3454
3455   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
3456   GNUNET_free (proxy_sockfile);
3457
3458   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
3459                                (struct sockaddr*)&mhd_unix_sock_addr,
3460                                len))
3461   {
3462     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3463                 "Unable to bind unix domain socket!\n");
3464     return;
3465   }
3466
3467   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
3468                                                  1))
3469   {
3470     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3471                 "Unable to listen on unix domain socket!\n");
3472     return;
3473   }
3474 #endif
3475
3476   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
3477   hd->is_ssl = GNUNET_NO;
3478   strcpy (hd->domain, "");
3479
3480 #if HAVE_MHD_NO_LISTEN_SOCKET
3481   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
3482                             0,
3483                             &accept_cb, NULL,
3484                             &create_response, hd,
3485                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3486                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3487                             MHD_OPTION_NOTIFY_COMPLETED,
3488                             NULL, NULL,
3489                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3490                             MHD_OPTION_END);
3491 #else
3492   httpd = MHD_start_daemon (MHD_USE_DEBUG,
3493                             4444 /* Dummy port */,
3494                             &accept_cb, NULL,
3495                             &create_response, hd,
3496                             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
3497                             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
3498                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
3499                             MHD_OPTION_NOTIFY_COMPLETED,
3500                             NULL, NULL,
3501                             MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
3502                             MHD_OPTION_END);
3503 #endif
3504   GNUNET_break (httpd != NULL);
3505   hd->daemon = httpd;
3506   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
3507   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
3508   run_httpds ();
3509   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
3510                                 &do_shutdown, NULL);
3511 }
3512
3513
3514 /**
3515  * The main function for gnunet-gns-proxy.
3516  *
3517  * @param argc number of arguments from the command line
3518  * @param argv command line arguments
3519  * @return 0 ok, 1 on error
3520  */
3521 int
3522 main (int argc, char *const *argv)
3523 {
3524   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
3525     {'p', "port", NULL,
3526      gettext_noop ("listen on specified port (default: 7777)"), 1,
3527      &GNUNET_GETOPT_set_ulong, &port},
3528     {'a', "authority", NULL,
3529       gettext_noop ("pem file to use as CA"), 1,
3530       &GNUNET_GETOPT_set_string, &cafile_opt},
3531     GNUNET_GETOPT_OPTION_END
3532   };
3533   int ret;
3534
3535   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
3536     return 2;
3537   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
3538   ret =
3539       (GNUNET_OK ==
3540        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
3541                            _("GNUnet GNS proxy"),
3542                            options,
3543                            &run, NULL)) ? 0 : 1;
3544   GNUNET_free_non_null ((char *) argv);
3545   return ret;
3546 }
3547
3548 /* end of gnunet-gns-proxy.c */