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