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