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