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