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