-fix 2447
[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 GNUNET_GNS_PROXY_PORT 7777
38 #define MHD_MAX_CONNECTIONS 300
39
40 /* MHD/cURL defines */
41 #define BUF_WAIT_FOR_CURL 0
42 #define BUF_WAIT_FOR_MHD 1
43 #define HTML_HDR_CONTENT "Content-Type: text/html"
44
45 /* regexp */
46 //#define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
47 #define RE_A_HREF  "<a href=\"https?://(([A-Za-z0-9]+[.])+)([+]|zkey)"
48 #define RE_N_MATCHES 4
49
50 /* The usual suspects */
51 #define HTTP_PORT 80
52 #define HTTPS_PORT 443
53
54
55 /**
56  * A structure for CA cert/key
57  */
58 struct ProxyCA
59 {
60   /* The certificate */
61   gnutls_x509_crt_t cert;
62
63   /* The private key */
64   gnutls_x509_privkey_t key;
65 };
66
67
68 /**
69  * Structure for GNS certificates
70  */
71 struct ProxyGNSCertificate
72 {
73   /* The certificate as PEM */
74   char cert[10 * 1024];
75
76   /* The private key as PEM */
77   char key[10 * 1024];
78 };
79
80
81 /**
82  * A structure for socks requests
83  */
84 struct Socks5Request
85 {
86   /* The client socket */
87   struct GNUNET_NETWORK_Handle *sock;
88
89   /* The server socket */
90   struct GNUNET_NETWORK_Handle *remote_sock;
91   
92   /* The socks state */
93   int state;
94   
95   /* Client socket read task */
96   GNUNET_SCHEDULER_TaskIdentifier rtask;
97
98   /* Server socket read task */
99   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
100
101   /* Client socket write task */
102   GNUNET_SCHEDULER_TaskIdentifier wtask;
103
104   /* Server socket write task */
105   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
106
107   /* Read buffer */
108   char rbuf[2048];
109
110   /* Write buffer */
111   char wbuf[2048];
112
113   /* Length of data in read buffer */
114   unsigned int rbuf_len;
115
116   /* Length of data in write buffer */
117   unsigned int wbuf_len;
118
119   /* This handle is scheduled for cleanup? */
120   int cleanup;
121
122   /* Shall we close the client socket on cleanup? */
123   int cleanup_sock;
124 };
125
126 /**
127  * DLL for Network Handles
128  */
129 struct NetworkHandleList
130 {
131   /*DLL*/
132   struct NetworkHandleList *next;
133
134   /*DLL*/
135   struct NetworkHandleList *prev;
136
137   /* The handle */
138   struct GNUNET_NETWORK_Handle *h;
139 };
140
141 /**
142  * A structure for all running Httpds
143  */
144 struct MhdHttpList
145 {
146   /* DLL for httpds */
147   struct MhdHttpList *prev;
148
149   /* DLL for httpds */
150   struct MhdHttpList *next;
151
152   /* is this an ssl daemon? */
153   int is_ssl;
154
155   /* the domain name to server (only important for SSL) */
156   char domain[256];
157
158   /* The daemon handle */
159   struct MHD_Daemon *daemon;
160
161   /* Optional proxy certificate used */
162   struct ProxyGNSCertificate *proxy_cert;
163
164   /* The task ID */
165   GNUNET_SCHEDULER_TaskIdentifier httpd_task;
166
167   /* Handles associated with this daemon */
168   struct NetworkHandleList *socket_handles_head;
169   
170   /* Handles associated with this daemon */
171   struct NetworkHandleList *socket_handles_tail;
172 };
173
174 /**
175  * A structure for MHD<->cURL streams
176  */
177 struct ProxyCurlTask
178 {
179   /* DLL for tasks */
180   struct ProxyCurlTask *prev;
181
182   /* DLL for tasks */
183   struct ProxyCurlTask *next;
184
185   /* Handle to cURL */
186   CURL *curl;
187
188   /* Optional header replacements for curl (LEHO) */
189   struct curl_slist *headers;
190
191   /* Optional resolver replacements for curl (LEHO) */
192   struct curl_slist *resolver;
193
194   /* The URL to fetch */
195   char url[2048];
196
197   /* The cURL write buffer / MHD read buffer */
198   char buffer[CURL_MAX_WRITE_SIZE];
199
200   /* The pointer to the data in the buffer */
201   char *buffer_ptr;
202
203   /* The buffer status (BUF_WAIT_FOR_CURL or BUF_WAIT_FOR_MHD) */
204   int buf_status;
205
206   /* Number of bytes in buffer */
207   unsigned int bytes_in_buffer;
208
209   /* Indicates wheather the download is in progress */
210   int download_in_progress;
211
212   /* Indicates wheather the download was successful */
213   int download_successful;
214
215   /* Indicates wheather the download failed */
216   int download_error;
217
218   /* Indicates wheather we need to parse HTML */
219   int parse_content;
220
221   /* Indicates wheather we are postprocessing the HTML right now */
222   int is_postprocessing;
223
224   /* Indicates wheather postprocessing has finished */
225   int pp_finished;
226
227   /* Task ID of the postprocessing task */
228   GNUNET_SCHEDULER_TaskIdentifier pp_task;
229
230   /* The postprocessing buffer TODO length? */
231   char pp_buf[256];
232
233   /* The authority of the corresponding host (site of origin) */
234   char authority[256];
235
236   /* The hostname (Host header field) */
237   char host[256];
238
239   /* The LEgacy HOstname (can be empty) */
240   char leho[256];
241
242   /* The associated daemon list entry */
243   struct MhdHttpList *mhd;
244
245   /* The associated response */
246   struct MHD_Response *response;
247   
248 };
249
250 /* The port the proxy is running on (default 7777) */
251 static unsigned long port = GNUNET_GNS_PROXY_PORT;
252
253 /* The CA file (pem) to use for the proxy CA */
254 static char* cafile_opt;
255
256 /* The listen socket of the proxy */
257 static struct GNUNET_NETWORK_Handle *lsock;
258
259 /* The listen task ID */
260 GNUNET_SCHEDULER_TaskIdentifier ltask;
261
262 /* The cURL download task */
263 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
264
265 /* The non SSL httpd daemon handle */
266 static struct MHD_Daemon *httpd;
267
268 /* Number of current mhd connections */
269 static unsigned int total_mhd_connections;
270
271 /* The cURL multi handle */
272 static CURLM *curl_multi;
273
274 /* Handle to the GNS service */
275 static struct GNUNET_GNS_Handle *gns_handle;
276
277 /* DLL for ProxyCurlTasks */
278 static struct ProxyCurlTask *ctasks_head;
279
280 /* DLL for ProxyCurlTasks */
281 static struct ProxyCurlTask *ctasks_tail;
282
283 /* DLL for http daemons */
284 static struct MhdHttpList *mhd_httpd_head;
285
286 /* DLL for http daemons */
287 static struct MhdHttpList *mhd_httpd_tail;
288
289 /* Handle to the regex for dotplus (.+) replacement in HTML */
290 static regex_t re_dotplus;
291
292 /* The users local GNS zone hash */
293 static struct GNUNET_CRYPTO_ShortHashCode local_gns_zone;
294
295 /* The users local private zone */
296 static struct GNUNET_CRYPTO_ShortHashCode local_private_zone;
297
298 /* The users local shorten zone */
299 static struct GNUNET_CRYPTO_ShortHashCode local_shorten_zone;
300
301 /* The CA for SSL certificate generation */
302 static struct ProxyCA proxy_ca;
303
304 /* UNIX domain socket for mhd */
305 struct GNUNET_NETWORK_Handle *mhd_unix_socket;
306
307 /* Shorten zone private key */
308 struct GNUNET_CRYPTO_RsaPrivateKey *shorten_zonekey;
309
310 /**
311  * Checks if name is in tld
312  *
313  * @param name the name to check
314  * @param tld the TLD to check for
315  * @return GNUNET_YES or GNUNET_NO
316  */
317 int
318 is_tld(const char* name, const char* tld)
319 {
320   int offset = 0;
321
322   if (strlen(name) <= strlen(tld))
323   {
324     return GNUNET_NO;
325   }
326
327   offset = strlen(name)-strlen(tld);
328   if (strcmp (name+offset, tld) != 0)
329   {
330     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
331                "%s is not in .%s TLD\n", name, tld);
332     return GNUNET_NO;
333   }
334
335   return GNUNET_YES;
336 }
337
338
339 /**
340  * Read HTTP request header field 'Host'
341  *
342  * @param cls buffer to write to
343  * @param kind value kind
344  * @param key field key
345  * @param value field value
346  * @return MHD_NO when Host found
347  */
348 static int
349 con_val_iter (void *cls,
350               enum MHD_ValueKind kind,
351               const char *key,
352               const char *value)
353 {
354   char* buf = (char*)cls;
355
356   if (0 == strcmp ("Host", key))
357   {
358     strcpy (buf, value);
359     return MHD_NO;
360   }
361   return MHD_YES;
362 }
363
364
365 /**
366  * Check HTTP response header for mime
367  *
368  * @param buffer curl buffer
369  * @param size curl blocksize
370  * @param nmemb curl blocknumber
371  * @param cls handle
372  * @return size of read bytes
373  */
374 static size_t
375 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
376 {
377   size_t bytes = size * nmemb;
378   struct ProxyCurlTask *ctask = cls;
379   int len = strlen (HTML_HDR_CONTENT);
380   char hdr[len+1];
381   
382   if ( (len+1) > bytes)
383     return bytes;
384
385   memcpy (hdr, buffer, len);
386   hdr[len] = '\0';
387
388   if (0 == strcmp (hdr, HTML_HDR_CONTENT))
389   {
390     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
391                 "Got HTML HTTP response header\n");
392     ctask->parse_content = GNUNET_YES;
393   }
394
395   return bytes;
396 }
397
398 /**
399  * schedule mhd
400  *
401  * @param hd a http daemon list entry
402  */
403 static void
404 run_httpd (struct MhdHttpList *hd);
405
406
407 /**
408  * schedule all mhds
409  *
410  */
411 static void
412 run_httpds (void);
413
414
415 /**
416  * Task that simply runs MHD main loop
417  *
418  * @param cls NULL
419  * @param tc task context
420  */
421 static void
422 run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
423 {
424
425   struct MhdHttpList *hd = cls;
426
427   //for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
428     MHD_run (hd->daemon);
429 }
430
431
432 /**
433  * Process cURL download bits
434  *
435  * @param ptr buffer with data
436  * @param size size of a record
437  * @param nmemb number of records downloaded
438  * @param ctx context
439  * @return number of processed bytes
440  */
441 static size_t
442 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
443 {
444   const char *cbuf = ptr;
445   size_t total;
446   struct ProxyCurlTask *ctask = ctx;
447
448   //MHD_run (httpd);
449
450   total = size*nmemb;
451
452   if (total == 0)
453   {
454     return total;
455   }
456
457   if (total > sizeof (ctask->buffer))
458   {
459     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
460                 "CURL gave us too much data to handle (%d)!\n",
461                 total);
462     return 0;
463   }
464   
465   if (ctask->buf_status == BUF_WAIT_FOR_MHD)
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468                 "CURL: Waiting for MHD (%s)\n", ctask->url);
469     return CURL_WRITEFUNC_PAUSE;
470   }
471
472
473   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
474               "CURL: Copying to MHD (%s, %d)\n", ctask->url, total);
475   memcpy (ctask->buffer, cbuf, total);
476   ctask->bytes_in_buffer = total;
477   ctask->buffer_ptr = ctask->buffer;
478
479   ctask->buf_status = BUF_WAIT_FOR_MHD;
480
481   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
483   //run_mhd (NULL, NULL);
484   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
485   return total;
486 }
487
488 /**
489  * Ask cURL for the select sets and schedule download
490  */
491 static void
492 curl_download_prepare ();
493
494 /**
495  * Callback to free content
496  *
497  * @param cls content to free
498  * @param tc task context
499  */
500 static void
501 mhd_content_free (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
502 {
503   struct ProxyCurlTask *ctask = cls;
504
505   if (NULL != ctask->headers)
506     curl_slist_free_all (ctask->headers);
507
508   if (NULL != ctask->headers)
509     curl_slist_free_all (ctask->resolver);
510
511   if (NULL != ctask->response)
512     MHD_destroy_response (ctask->response);
513
514   GNUNET_free (ctask);
515
516 }
517
518
519 /**
520  * Shorten result callback
521  *
522  * @param cls the proxycurltask
523  * @param short_name the shortened name (NULL on error)
524  */
525 static void
526 process_shorten (void* cls, const char* short_name)
527 {
528   struct ProxyCurlTask *ctask = cls;
529
530   char tmp[strlen(ctask->pp_buf)]; //TODO length
531
532   if (NULL == short_name)
533   {
534     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535                 "MHD PP: Unable to shorten %s\n",
536                 ctask->pp_buf);
537     return;
538   }
539
540   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
541               "MHD PP: Shorten %s -> %s\n",
542               ctask->pp_buf,
543               short_name);
544
545   sprintf (tmp, "<a href=\"http://%s", short_name);
546   strcpy (ctask->pp_buf, tmp);
547
548   ctask->pp_finished = GNUNET_YES;
549
550   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
551 }
552
553
554 /**
555  * Callback for MHD response
556  *
557  * @param cls closure
558  * @param pos in buffer
559  * @param buf buffer
560  * @param max space in buffer
561  * @return number of bytes written
562  */
563 static ssize_t
564 mhd_content_cb (void *cls,
565                 uint64_t pos,
566                 char* buf,
567                 size_t max)
568 {
569   struct ProxyCurlTask *ctask = cls;
570   ssize_t copied = 0;
571   size_t bytes_to_copy;
572   int nomatch;
573   char *hostptr;
574   regmatch_t m[RE_N_MATCHES];
575
576   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
577               "MHD: content cb %s\n", ctask->url);
578
579   if (ctask->download_successful &&
580       (ctask->buf_status == BUF_WAIT_FOR_CURL))
581   {
582     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
583                 "MHD: sending response for %s\n", ctask->url);
584     ctask->download_in_progress = GNUNET_NO;
585     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
586     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
587     total_mhd_connections--;
588     return MHD_CONTENT_READER_END_OF_STREAM;
589   }
590   
591   if (ctask->download_error &&
592       (ctask->buf_status == BUF_WAIT_FOR_CURL))
593   {
594     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595                 "MHD: sending error response\n");
596     ctask->download_in_progress = GNUNET_NO;
597     GNUNET_SCHEDULER_add_now (&mhd_content_free, ctask);
598     GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
599     total_mhd_connections--;
600     return MHD_CONTENT_READER_END_WITH_ERROR;
601   }
602
603   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
604     return 0;
605
606   bytes_to_copy = ctask->bytes_in_buffer;
607   
608   if (ctask->parse_content == GNUNET_YES)
609   {
610
611     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
612                  "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr);
613
614     nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0);
615
616     if (nomatch)
617     {
618       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
619                   "MHD RE: No match\n");
620     }
621     else
622     {
623       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
624                   "MHD RE: Match\n");
625
626       GNUNET_assert (m[1].rm_so != -1);
627
628       hostptr = ctask->buffer_ptr+m[1].rm_so;
629
630       if (m[0].rm_so > 0)
631       {
632         bytes_to_copy = m[0].rm_so;
633         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634                     "Copying %d bytes.\n", m[0].rm_so);
635
636
637       }
638       else
639       {
640         if (ctask->is_postprocessing == GNUNET_YES)
641         {
642           
643           /*Done?*/
644           if ( ctask->pp_finished == GNUNET_NO )
645           {
646             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
647                         "MHD PP: Waiting for PP of %s\n", ctask->pp_buf);
648             return 0;
649           }
650           
651           ctask->is_postprocessing = GNUNET_NO;
652
653           ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
654           ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
655           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656                       "Skipping next %d bytes in buffer\n", m[0].rm_eo);
657
658           GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
659
660           if ( strlen (ctask->pp_buf) <= max )
661           {
662             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
663                         "Copying postprocessed %s.\n", ctask->pp_buf);
664             memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) );
665             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
666                         "Done %s.\n", buf);
667             ctask->is_postprocessing = GNUNET_NO;
668             return strlen (ctask->pp_buf);
669           }
670           
671           return 0;
672         }
673
674         memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
675         
676         /* If .+ extend with authority */
677         if (*(ctask->buffer_ptr+m[1].rm_eo) == '+')
678         {
679           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680                       "Links is .+\n");
681            memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
682            strcpy ( ctask->pp_buf+strlen(ctask->pp_buf),
683                     ctask->authority);
684         }
685         /* If .zkey simply copy the name */
686         else
687         {
688           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
689                       "Link is zkey\n");
690           memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so + strlen (GNUNET_GNS_TLD_ZKEY)));
691         }
692
693         ctask->is_postprocessing = GNUNET_YES;
694         ctask->pp_finished = GNUNET_NO;
695         
696         GNUNET_GNS_shorten_zone (gns_handle,
697                                  ctask->pp_buf,
698                                  &local_private_zone,
699                                  &local_shorten_zone,
700                                  &local_gns_zone,
701                                  &process_shorten,
702                                  ctask);
703
704         return 0;
705       }
706     }
707   }
708
709   if ( bytes_to_copy > max )
710   {
711     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
712                  "MHD: buffer in response too small! (%s)\n",
713                  ctask->url);
714     memcpy ( buf, ctask->buffer_ptr, max);
715     ctask->bytes_in_buffer -= max;
716     ctask->buffer_ptr += max;
717     copied = max;
718   }
719   else
720   {
721     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
722                  "MHD: copying %d bytes to mhd response at offset %d\n",
723                  bytes_to_copy, pos);
724
725     memcpy ( buf, ctask->buffer_ptr, bytes_to_copy );
726     copied = bytes_to_copy;
727     if (bytes_to_copy < ctask->bytes_in_buffer)
728     {
729       ctask->bytes_in_buffer -= bytes_to_copy;
730       ctask->buffer_ptr += bytes_to_copy;
731     }
732     else
733     {
734       ctask->bytes_in_buffer = 0;
735       ctask->buf_status = BUF_WAIT_FOR_CURL;
736       ctask->buffer_ptr = ctask->buffer;
737       if (NULL != ctask->curl)
738         curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
739       GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
740     }
741   }
742
743   GNUNET_SCHEDULER_add_now (&run_mhd, ctask->mhd);
744
745   return copied;
746 }
747
748
749
750 /**
751  * Task that is run when we are ready to receive more data
752  * from curl
753  *
754  * @param cls closure
755  * @param tc task context
756  */
757 static void
758 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
759
760 /**
761  * Ask cURL for the select sets and schedule download
762  */
763 static void
764 curl_download_prepare ()
765 {
766   CURLMcode mret;
767   fd_set rs;
768   fd_set ws;
769   fd_set es;
770   int max;
771   struct GNUNET_NETWORK_FDSet *grs;
772   struct GNUNET_NETWORK_FDSet *gws;
773   long to;
774   struct GNUNET_TIME_Relative rtime;
775
776   max = -1;
777   FD_ZERO (&rs);
778   FD_ZERO (&ws);
779   FD_ZERO (&es);
780   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
781
782   if (mret != CURLM_OK)
783   {
784     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
785                 "%s failed at %s:%d: `%s'\n",
786                 "curl_multi_fdset", __FILE__, __LINE__,
787                 curl_multi_strerror (mret));
788     //TODO cleanup here?
789     return;
790   }
791
792   mret = curl_multi_timeout (curl_multi, &to);
793   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
794
795   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796               "cURL multi fds: max=%d timeout=%llu\n", max, to);
797
798   grs = GNUNET_NETWORK_fdset_create ();
799   gws = GNUNET_NETWORK_fdset_create ();
800   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
801   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
802   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
803               "Scheduling task cURL\n");
804
805   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
806     GNUNET_SCHEDULER_cancel (curl_download_task);
807   
808   curl_download_task =
809     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
810                                  rtime,
811                                  grs, gws,
812                                  &curl_task_download, curl_multi);
813   GNUNET_NETWORK_fdset_destroy (gws);
814   GNUNET_NETWORK_fdset_destroy (grs);
815
816 }
817
818
819 /**
820  * Task that is run when we are ready to receive more data
821  * from curl
822  *
823  * @param cls closure
824  * @param tc task context
825  */
826 static void
827 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
828 {
829   int running;
830   int msgnum;
831   struct CURLMsg *msg;
832   CURLMcode mret;
833   struct ProxyCurlTask *ctask;
834   int num_ctasks;
835
836   struct ProxyCurlTask *clean_head = NULL;
837   struct ProxyCurlTask *clean_tail = NULL;
838
839   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
840
841   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
842   {
843     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844                 "Shutdown requested while trying to download\n");
845     //TODO cleanup
846     return;
847   }
848   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
849               "Ready to dl\n");
850
851   do
852   {
853     running = 0;
854     num_ctasks = 0;
855     
856     mret = curl_multi_perform (curl_multi, &running);
857
858     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
859                 "Running curl tasks: %d\n", running);
860
861     ctask = ctasks_head;
862     for (; ctask != NULL; ctask = ctask->next)
863     {
864       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
865                   "CTask: %s\n", ctask->url);
866       num_ctasks++;
867     }
868
869     if (num_ctasks != running)
870     {
871       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872                   "%d ctasks, %d curl running\n", num_ctasks, running);
873     }
874     
875     do
876     {
877       ctask = ctasks_head;
878       msg = curl_multi_info_read (curl_multi, &msgnum);
879       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
880                   "Messages left: %d\n", msgnum);
881       
882       if (msg == NULL)
883         break;
884       switch (msg->msg)
885       {
886        case CURLMSG_DONE:
887          if ((msg->data.result != CURLE_OK) &&
888              (msg->data.result != CURLE_GOT_NOTHING))
889          {
890            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
891                        "Download curl failed");
892             
893            for (; ctask != NULL; ctask = ctask->next)
894            {
895              if (NULL == ctask->curl)
896                continue;
897
898              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
899                continue;
900              
901              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
902                          "Download curl failed for task %s: %s.\n",
903                          ctask->url,
904                          curl_easy_strerror (msg->data.result));
905              ctask->download_successful = GNUNET_NO;
906              ctask->download_error = GNUNET_YES;
907              //curl_multi_remove_handle (curl_multi, ctask->curl);
908              //curl_easy_cleanup (ctask->curl);
909              //ctask->curl = NULL;
910              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
911                                           ctask);
912              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
913              break;
914            }
915            GNUNET_assert (ctask != NULL);
916          }
917          else
918          {
919            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920                        "cURL download completed.\n");
921
922            for (; ctask != NULL; ctask = ctask->next)
923            {
924              if (NULL == ctask->curl)
925                continue;
926
927              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
928                continue;
929              
930              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931                          "cURL task %s found.\n", ctask->url);
932              ctask->download_successful = GNUNET_YES;
933              //curl_multi_remove_handle (curl_multi, ctask->curl);
934              //curl_easy_cleanup (ctask->curl);
935              //ctask->curl = NULL;
936              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
937                                           ctask);
938              GNUNET_CONTAINER_DLL_insert (clean_head, clean_tail, ctask);
939
940              break;
941            }
942            GNUNET_assert (ctask != NULL);
943          }
944          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
945                      "curl end %s\n", curl_easy_strerror(msg->data.result));
946          break;
947        default:
948          GNUNET_assert (0);
949          break;
950       }
951     } while (msgnum > 0);
952
953     for (ctask=clean_head; ctask != NULL; ctask = ctask->next)
954     {
955       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
956                   "Removing cURL task %s.\n", ctask->url);
957       curl_multi_remove_handle (curl_multi, ctask->curl);
958       curl_easy_cleanup (ctask->curl);
959       ctask->curl = NULL;
960     }
961     
962     num_ctasks=0;
963     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
964     {
965       num_ctasks++;
966     }
967     
968     if (num_ctasks != running)
969     {
970       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
971                   "%d ctasks, %d curl running\n", num_ctasks, running);
972     }
973
974     GNUNET_assert ( num_ctasks == running );
975
976     run_httpds ();
977
978   } while (mret == CURLM_CALL_MULTI_PERFORM);
979   
980   
981   if (mret != CURLM_OK)
982   {
983     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
984                 "curl_multi_perform", __FILE__, __LINE__,
985                 curl_multi_strerror (mret));
986   }
987   curl_download_prepare();
988 }
989
990 /**
991  * Process LEHO lookup
992  *
993  * @param cls the ctask
994  * @param rd_count number of records returned
995  * @param rd record data
996  */
997 static void
998 process_leho_lookup (void *cls,
999                      uint32_t rd_count,
1000                      const struct GNUNET_NAMESTORE_RecordData *rd)
1001 {
1002   struct ProxyCurlTask *ctask = cls;
1003   char hosthdr[262]; //256 + "Host: "
1004   int i;
1005   CURLcode ret;
1006   CURLMcode mret;
1007   struct hostent *phost;
1008   char *ssl_ip;
1009   char resolvename[512];
1010   char curlurl[512];
1011
1012   ctask->headers = NULL;
1013
1014   strcpy (ctask->leho, "");
1015
1016   if (rd_count == 0)
1017     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018                 "No LEHO present!\n");
1019
1020   for (i=0; i<rd_count; i++)
1021   {
1022     if (rd[i].record_type != GNUNET_GNS_RECORD_LEHO)
1023       continue;
1024
1025     memcpy (ctask->leho, rd[i].data, rd[i].data_size);
1026
1027     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1028                 "Found LEHO %s for %s\n", ctask->leho, ctask->url);
1029   }
1030
1031   if (0 != strcmp (ctask->leho, ""))
1032   {
1033     sprintf (hosthdr, "%s%s", "Host: ", ctask->leho);
1034     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1035                 "New HTTP header value: %s\n", hosthdr);
1036     ctask->headers = curl_slist_append (ctask->headers, hosthdr);
1037     GNUNET_assert (NULL != ctask->headers);
1038     ret = curl_easy_setopt (ctask->curl, CURLOPT_HTTPHEADER, ctask->headers);
1039     if (CURLE_OK != ret)
1040     {
1041       GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s failed at %s:%d: `%s'\n",
1042                            "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret));
1043     }
1044
1045   }
1046
1047   if (ctask->mhd->is_ssl)
1048   {
1049     phost = (struct hostent*)gethostbyname (ctask->host);
1050
1051     if (phost!=NULL)
1052     {
1053       ssl_ip = inet_ntoa(*((struct in_addr*)(phost->h_addr)));
1054       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1055                   "SSL target server: %s\n", ssl_ip);
1056       sprintf (resolvename, "%s:%d:%s", ctask->leho, HTTPS_PORT, ssl_ip);
1057       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1058                   "Curl resolve: %s\n", resolvename);
1059       ctask->resolver = curl_slist_append ( ctask->resolver, resolvename);
1060       curl_easy_setopt (ctask->curl, CURLOPT_RESOLVE, ctask->resolver);
1061       sprintf (curlurl, "https://%s%s", ctask->leho, ctask->url);
1062       curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1063     }
1064     else
1065     {
1066       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1067                   "gethostbyname failed for %s!\n", ctask->host);
1068       ctask->download_successful = GNUNET_NO;
1069       ctask->download_error = GNUNET_YES;
1070       return;
1071     }
1072   }
1073
1074   if (CURLM_OK != (mret=curl_multi_add_handle (curl_multi, ctask->curl)))
1075   {
1076     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1077                 "%s failed at %s:%d: `%s'\n",
1078                 "curl_multi_add_handle", __FILE__, __LINE__,
1079                 curl_multi_strerror (mret));
1080     ctask->download_successful = GNUNET_NO;
1081     ctask->download_error = GNUNET_YES;
1082     return;
1083   }
1084   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
1085
1086   curl_download_prepare ();
1087
1088 }
1089
1090 /**
1091  * Initialize download and trigger curl
1092  *
1093  * @param cls the proxycurltask
1094  * @param auth_name the name of the authority (site of origin) of ctask->host
1095  *
1096  */
1097 static void
1098 process_get_authority (void *cls,
1099                        const char* auth_name)
1100 {
1101   struct ProxyCurlTask *ctask = cls;
1102
1103   if (NULL == auth_name)
1104   {
1105     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1106                 "Get authority failed!\n");
1107     strcpy (ctask->authority, "");
1108   }
1109   else
1110   {
1111     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1112                 "Get authority yielded %s\n", auth_name);
1113     strcpy (ctask->authority, auth_name);
1114   }
1115
1116   GNUNET_GNS_lookup_zone (gns_handle,
1117                           ctask->host,
1118                           &local_gns_zone,
1119                           GNUNET_GNS_RECORD_LEHO,
1120                           GNUNET_YES, //Only cached for performance
1121                           shorten_zonekey,
1122                           &process_leho_lookup,
1123                           ctask);
1124 }
1125
1126 /**
1127  * Main MHD callback for handling requests.
1128  *
1129  * @param cls unused
1130  * @param con MHD connection handle
1131  * @param url the url in the request
1132  * @param meth the HTTP method used ("GET", "PUT", etc.)
1133  * @param ver the HTTP version string (i.e. "HTTP/1.1")
1134  * @param upload_data the data being uploaded (excluding HEADERS,
1135  *        for a POST that fits into memory and that is encoded
1136  *        with a supported encoding, the POST data will NOT be
1137  *        given in upload_data and is instead available as
1138  *        part of MHD_get_connection_values; very large POST
1139  *        data *will* be made available incrementally in
1140  *        upload_data)
1141  * @param upload_data_size set initially to the size of the
1142  *        upload_data provided; the method must update this
1143  *        value to the number of bytes NOT processed;
1144  * @param con_cls pointer to location where we store the 'struct Request'
1145  * @return MHD_YES if the connection was handled successfully,
1146  *         MHD_NO if the socket must be closed due to a serious
1147  *         error while handling the request
1148  */
1149 static int
1150 create_response (void *cls,
1151                  struct MHD_Connection *con,
1152                  const char *url,
1153                  const char *meth,
1154                  const char *ver,
1155                  const char *upload_data,
1156                  size_t *upload_data_size,
1157                  void **con_cls)
1158 {
1159   static int dummy;
1160   struct MhdHttpList* hd = cls;
1161   const char* page = "<html><head><title>gnoxy</title>"\
1162                       "</head><body>cURL fail</body></html>";
1163   
1164   char host[265];
1165   char curlurl[512];
1166   int ret = MHD_YES;
1167
1168   struct ProxyCurlTask *ctask;
1169   
1170   if (0 != strcmp (meth, "GET"))
1171     return MHD_NO;
1172   if (&dummy != *con_cls)
1173   {
1174     *con_cls = &dummy;
1175     return MHD_YES;
1176   }
1177
1178   if (0 != *upload_data_size)
1179     return MHD_NO;
1180
1181   *con_cls = NULL;
1182
1183   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184               "url %s\n", url);
1185
1186   MHD_get_connection_values (con,
1187                              MHD_HEADER_KIND,
1188                              &con_val_iter, host);
1189
1190   
1191   /* Do cURL */
1192   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
1193   ctask->mhd = hd;
1194
1195   if (curl_multi == NULL)
1196     curl_multi = curl_multi_init ();
1197
1198   ctask->curl = curl_easy_init();
1199   
1200   if ((ctask->curl == NULL) || (curl_multi == NULL))
1201   {
1202     ctask->response = MHD_create_response_from_buffer (strlen (page),
1203                                               (void*)page,
1204                                               MHD_RESPMEM_PERSISTENT);
1205     ret = MHD_queue_response (con,
1206                               MHD_HTTP_OK,
1207                               ctask->response);
1208     MHD_destroy_response (ctask->response);
1209     GNUNET_free (ctask);
1210     return ret;
1211   }
1212   
1213   ctask->prev = NULL;
1214   ctask->next = NULL;
1215   ctask->headers = NULL;
1216   ctask->resolver = NULL;
1217   ctask->buffer_ptr = NULL;
1218   ctask->download_in_progress = GNUNET_YES;
1219   ctask->download_successful = GNUNET_NO;
1220   ctask->buf_status = BUF_WAIT_FOR_CURL;
1221   ctask->bytes_in_buffer = 0;
1222   ctask->parse_content = GNUNET_NO;
1223
1224   curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
1225   curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
1226   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
1227   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
1228   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
1229   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
1230   /* no need to abort if the above failed */
1231   if (GNUNET_NO == ctask->mhd->is_ssl)
1232   {
1233     sprintf (curlurl, "http://%s%s", host, url);
1234     curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1235     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1236                 "Adding new curl task for %s\n", curlurl);
1237   }
1238   strcpy (ctask->host, host);
1239   strcpy (ctask->url, url);
1240   
1241   //curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
1242   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
1243   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
1244   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
1245
1246   GNUNET_GNS_get_authority (gns_handle,
1247                             ctask->host,
1248                             &process_get_authority,
1249                             ctask);
1250   //download_prepare (ctask);
1251   //curl_download_prepare ();
1252
1253   ctask->response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1254                                                 20,
1255                                                 &mhd_content_cb,
1256                                                 ctask,
1257                                                 NULL);
1258   
1259   ret = MHD_queue_response (con, MHD_HTTP_OK, ctask->response);
1260   
1261   //MHD_destroy_response (response);
1262
1263   return ret;
1264 }
1265
1266 /**
1267  * Task run whenever HTTP server operations are pending.
1268  *
1269  * @param cls unused
1270  * @param tc sched context
1271  */
1272 static void
1273 do_httpd (void *cls,
1274           const struct GNUNET_SCHEDULER_TaskContext *tc);
1275
1276
1277 /**
1278  * run all httpd
1279  */
1280 static void
1281 run_httpds ()
1282 {
1283   struct MhdHttpList *hd;
1284
1285   for (hd=mhd_httpd_head; hd != NULL; hd = hd->next)
1286     run_httpd (hd);
1287
1288 }
1289
1290 /**
1291  * schedule mhd
1292  *
1293  * @param hd the daemon to run
1294  */
1295 static void
1296 run_httpd (struct MhdHttpList *hd)
1297 {
1298   fd_set rs;
1299   fd_set ws;
1300   fd_set es;
1301   struct GNUNET_NETWORK_FDSet *wrs;
1302   struct GNUNET_NETWORK_FDSet *wws;
1303   struct GNUNET_NETWORK_FDSet *wes;
1304   int max;
1305   int haveto;
1306   unsigned MHD_LONG_LONG timeout;
1307   struct GNUNET_TIME_Relative tv;
1308
1309   FD_ZERO (&rs);
1310   FD_ZERO (&ws);
1311   FD_ZERO (&es);
1312   wrs = GNUNET_NETWORK_fdset_create ();
1313   wes = GNUNET_NETWORK_fdset_create ();
1314   wws = GNUNET_NETWORK_fdset_create ();
1315   max = -1;
1316   GNUNET_assert (MHD_YES == MHD_get_fdset (hd->daemon, &rs, &ws, &es, &max));
1317   
1318   
1319   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1320               "MHD fds: max=%d\n", max);
1321   
1322   haveto = MHD_get_timeout (hd->daemon, &timeout);
1323
1324   if (haveto == MHD_YES)
1325     tv.rel_value = (uint64_t) timeout;
1326   else
1327     tv = GNUNET_TIME_UNIT_FOREVER_REL;
1328   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1329   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1330   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1331   
1332   if (hd->httpd_task != GNUNET_SCHEDULER_NO_TASK)
1333     GNUNET_SCHEDULER_cancel (hd->httpd_task);
1334   hd->httpd_task =
1335     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
1336                                  tv, wrs, wws,
1337                                  &do_httpd, hd);
1338   GNUNET_NETWORK_fdset_destroy (wrs);
1339   GNUNET_NETWORK_fdset_destroy (wws);
1340   GNUNET_NETWORK_fdset_destroy (wes);
1341 }
1342
1343
1344 /**
1345  * Task run whenever HTTP server operations are pending.
1346  *
1347  * @param cls unused
1348  * @param tc sched context
1349  */
1350 static void
1351 do_httpd (void *cls,
1352           const struct GNUNET_SCHEDULER_TaskContext *tc)
1353 {
1354   struct MhdHttpList *hd = cls;
1355   
1356   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1357   
1358   MHD_run (hd->daemon);
1359   run_httpd (hd);
1360 }
1361
1362
1363 /**
1364  * Read data from socket
1365  *
1366  * @param cls the closure
1367  * @param tc scheduler context
1368  */
1369 static void
1370 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1371
1372 /**
1373  * Read from remote end
1374  *
1375  * @param cls closure
1376  * @param tc scheduler context
1377  */
1378 static void
1379 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
1380
1381 /**
1382  * Write data to remote socket
1383  *
1384  * @param cls the closure
1385  * @param tc scheduler context
1386  */
1387 static void
1388 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1389 {
1390   struct Socks5Request *s5r = cls;
1391   unsigned int len;
1392
1393   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1394
1395   if ((NULL != tc->read_ready) &&
1396       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
1397       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
1398                                          s5r->rbuf_len)>0)))
1399   {
1400     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1401                 "Successfully sent %d bytes to remote socket\n",
1402                 len);
1403   }
1404   else
1405   {
1406     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
1407     //Really!?!?!?
1408     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1409       GNUNET_SCHEDULER_cancel (s5r->rtask);
1410     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1411       GNUNET_SCHEDULER_cancel (s5r->wtask);
1412     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1413       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1414     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1415     GNUNET_NETWORK_socket_close (s5r->sock);
1416     GNUNET_free(s5r);
1417     return;
1418   }
1419
1420   s5r->rtask =
1421     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1422                                    s5r->sock,
1423                                    &do_read, s5r);
1424 }
1425
1426
1427 /**
1428  * Clean up s5r handles
1429  *
1430  * @param s5r the handle to destroy
1431  */
1432 static void
1433 cleanup_s5r (struct Socks5Request *s5r)
1434 {
1435   if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1436     GNUNET_SCHEDULER_cancel (s5r->rtask);
1437   if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1438     GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1439   if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1440     GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1441   
1442   if (NULL != s5r->remote_sock)
1443     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1444   if ((NULL != s5r->sock) && (s5r->cleanup_sock == GNUNET_YES))
1445     GNUNET_NETWORK_socket_close (s5r->sock);
1446   
1447   GNUNET_free(s5r);
1448 }
1449
1450 /**
1451  * Write data to socket
1452  *
1453  * @param cls the closure
1454  * @param tc scheduler context
1455  */
1456 static void
1457 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1458 {
1459   struct Socks5Request *s5r = cls;
1460   unsigned int len;
1461
1462   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1463
1464   if ((NULL != tc->read_ready) &&
1465       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
1466       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
1467                                          s5r->wbuf_len)>0)))
1468   {
1469     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1470                 "Successfully sent %d bytes to socket\n",
1471                 len);
1472   }
1473   else
1474   {
1475     
1476     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1477     s5r->cleanup = GNUNET_YES;
1478     s5r->cleanup_sock = GNUNET_YES;
1479     cleanup_s5r (s5r);
1480     
1481     return;
1482   }
1483
1484   if (GNUNET_YES == s5r->cleanup)
1485   {
1486     cleanup_s5r (s5r);
1487     return;
1488   }
1489
1490   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
1491       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
1492     s5r->fwdrtask =
1493       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1494                                      s5r->remote_sock,
1495                                      &do_read_remote, s5r);
1496 }
1497
1498 /**
1499  * Read from remote end
1500  *
1501  * @param cls closure
1502  * @param tc scheduler context
1503  */
1504 static void
1505 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1506 {
1507   struct Socks5Request *s5r = cls;
1508   
1509   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1510
1511
1512   if ((NULL != tc->write_ready) &&
1513       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
1514       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
1515                                          sizeof (s5r->wbuf))))
1516   {
1517     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1518                 "Successfully read %d bytes from remote socket\n",
1519                 s5r->wbuf_len);
1520   }
1521   else
1522   {
1523     if (s5r->wbuf_len == 0)
1524       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1525                   "0 bytes received from remote... graceful shutdown!\n");
1526     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1527       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1528     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1529       GNUNET_SCHEDULER_cancel (s5r->rtask);
1530     
1531     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1532     s5r->remote_sock = NULL;
1533     GNUNET_NETWORK_socket_close (s5r->sock);
1534     GNUNET_free(s5r);
1535
1536     return;
1537   }
1538   
1539   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1540                                                s5r->sock,
1541                                                &do_write, s5r);
1542   
1543 }
1544
1545
1546 /**
1547  * Adds a socket to MHD
1548  *
1549  * @param h the handle to the socket to add
1550  * @param daemon the daemon to add the fd to
1551  * @return whatever MHD_add_connection returns
1552  */
1553 static int
1554 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h, struct MHD_Daemon *daemon)
1555 {
1556   int fd;
1557   struct sockaddr *addr;
1558   socklen_t len;
1559
1560   fd = dup (GNUNET_NETWORK_get_fd (h));
1561   addr = GNUNET_NETWORK_get_addr (h);
1562   len = GNUNET_NETWORK_get_addrlen (h);
1563
1564   return MHD_add_connection (daemon, fd, addr, len);
1565 }
1566
1567 /**
1568  * Calculate size of file
1569  *
1570  * @param filename name of file
1571  * @return filesize or 0 on error
1572  */
1573 static long
1574 get_file_size (const char* filename)
1575 {
1576   FILE *fp;
1577
1578   fp = fopen (filename, "rb");
1579   if (fp)
1580   {
1581     long size;
1582
1583     if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
1584       size = 0;
1585
1586     fclose (fp);
1587
1588     return size;
1589   }
1590   
1591   return 0;
1592 }
1593
1594 /**
1595  * Read file in filename
1596  *
1597  * @param filename file to read
1598  * @param size pointer where filesize is stored
1599  * @return data
1600  */
1601 static char*
1602 load_file (const char* filename, unsigned int* size)
1603 {
1604   FILE *fp;
1605   char *buffer;
1606
1607   *size = get_file_size (filename);
1608   if (*size == 0)
1609     return NULL;
1610
1611   fp = fopen (filename, "rb");
1612   if (!fp)
1613     return NULL;
1614
1615   buffer = GNUNET_malloc (*size);
1616   if (!buffer)
1617   {
1618     fclose (fp);
1619     return NULL;
1620   }
1621
1622   if (*size != fread (buffer, 1, *size, fp))
1623   {
1624     GNUNET_free (buffer);
1625     buffer = NULL;
1626   }
1627
1628   fclose (fp);
1629   return buffer;
1630 }
1631
1632 /**
1633  * Load PEM key from file
1634  *
1635  * @param key where to store the data
1636  * @param keyfile path to the PEM file
1637  */
1638 static void
1639 load_key_from_file (gnutls_x509_privkey_t key, char* keyfile)
1640 {
1641   gnutls_datum_t key_data;
1642   key_data.data = NULL;
1643   int ret;
1644
1645   key_data.data = (unsigned char*)load_file (keyfile, &key_data.size);
1646
1647   ret = gnutls_x509_privkey_import (key, &key_data,
1648                                     GNUTLS_X509_FMT_PEM);
1649   
1650   if (GNUTLS_E_SUCCESS != ret)
1651   {
1652     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1653                 "Unable to import private key %s(ret=%d)\n", key_data.data, ret);
1654     GNUNET_break (0);
1655   }
1656
1657   GNUNET_free (key_data.data);
1658 }
1659
1660 /**
1661  * Load cert from file
1662  *
1663  * @param crt struct to store data in
1664  * @param certfile path to pem file
1665  */
1666 static void
1667 load_cert_from_file (gnutls_x509_crt_t crt, char* certfile)
1668 {
1669   gnutls_datum_t cert_data;
1670   cert_data.data = NULL;
1671   int ret;
1672
1673   cert_data.data = (unsigned char*)load_file (certfile, &cert_data.size);
1674
1675   ret = gnutls_x509_crt_import (crt, &cert_data,
1676                                  GNUTLS_X509_FMT_PEM);
1677   if (GNUTLS_E_SUCCESS != ret)
1678   {
1679     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1680                 "Unable to import certificate %s(ret=%d)\n", certfile, ret);
1681     GNUNET_break (0);
1682   }
1683
1684   GNUNET_free (cert_data.data);
1685
1686 }
1687
1688
1689 /**
1690  * Generate new certificate for specific name
1691  *
1692  * @param name the subject name to generate a cert for
1693  * @return a struct holding the PEM data
1694  */
1695 static struct ProxyGNSCertificate *
1696 generate_gns_certificate (const char *name)
1697 {
1698
1699   int ret;
1700   unsigned int serial;
1701   size_t key_buf_size;
1702   size_t cert_buf_size;
1703   gnutls_x509_crt_t request;
1704   time_t etime;
1705   struct tm *tm_data;
1706
1707   ret = gnutls_x509_crt_init (&request);
1708
1709   if (GNUTLS_E_SUCCESS != ret)
1710   {
1711     GNUNET_break (0);
1712   }
1713
1714   ret = gnutls_x509_crt_set_key (request, proxy_ca.key);
1715
1716   if (GNUTLS_E_SUCCESS != ret)
1717   {
1718     GNUNET_break (0);
1719   }
1720
1721   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Generating cert\n");
1722
1723   struct ProxyGNSCertificate *pgc =
1724     GNUNET_malloc (sizeof (struct ProxyGNSCertificate));
1725
1726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding DNs\n");
1727   
1728   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COUNTRY_NAME,
1729                                  0, "DE", 2);
1730
1731   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_ORGANIZATION_NAME,
1732                                  0, "GNUnet", 6);
1733
1734   gnutls_x509_crt_set_dn_by_oid (request, GNUTLS_OID_X520_COMMON_NAME,
1735                                  0, name, strlen (name));
1736
1737   ret = gnutls_x509_crt_set_version (request, 3);
1738
1739   ret = gnutls_rnd (GNUTLS_RND_NONCE, &serial, sizeof (serial));
1740
1741   etime = time (NULL);
1742   tm_data = localtime (&etime);
1743   
1744
1745   ret = gnutls_x509_crt_set_serial (request,
1746                                     &serial,
1747                                     sizeof (serial));
1748
1749   ret = gnutls_x509_crt_set_activation_time (request,
1750                                              etime);
1751   tm_data->tm_year++;
1752   etime = mktime (tm_data);
1753
1754   if (-1 == etime)
1755   {
1756     GNUNET_break (0);
1757   }
1758
1759   ret = gnutls_x509_crt_set_expiration_time (request,
1760                                              etime);
1761   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Signing...\n");
1762
1763   ret = gnutls_x509_crt_sign (request, proxy_ca.cert, proxy_ca.key);
1764
1765   key_buf_size = sizeof (pgc->key);
1766   cert_buf_size = sizeof (pgc->cert);
1767
1768   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exporting certificate...\n");
1769   
1770   gnutls_x509_crt_export (request, GNUTLS_X509_FMT_PEM,
1771                           pgc->cert, &cert_buf_size);
1772
1773   gnutls_x509_privkey_export (proxy_ca.key, GNUTLS_X509_FMT_PEM,
1774                           pgc->key, &key_buf_size);
1775
1776
1777   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
1778   gnutls_x509_crt_deinit (request);
1779
1780   return pgc;
1781
1782 }
1783
1784
1785 /*
1786  * Accept policy for mhdaemons
1787  *
1788  * @param cls NULL
1789  * @param addr the sockaddr
1790  * @param addrlen the sockaddr length
1791  * @return MHD_NO if sockaddr is wrong or #conns too high
1792  */
1793 static int
1794 accept_cb (void* cls, const struct sockaddr *addr, socklen_t addrlen)
1795 {
1796   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1797               "In MHD accept policy cb\n");
1798
1799   if (addr != NULL)
1800   {
1801     if (addr->sa_family == AF_UNIX)
1802       return MHD_NO;
1803   }
1804
1805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806               "Connection accepted\n");
1807
1808   return MHD_YES;
1809 }
1810
1811
1812 /**
1813  * Adds a socket to an SSL MHD instance
1814  * It is important the the domain name is
1815  * correct. In most cases we need to start a new daemon
1816  *
1817  * @param h the handle to add to a daemon
1818  * @param domain the domain the ssl daemon has to serve
1819  * @return MHD_YES on success
1820  */
1821 static int
1822 add_handle_to_ssl_mhd (struct GNUNET_NETWORK_Handle *h, char* domain)
1823 {
1824   struct MhdHttpList *hd = NULL;
1825   struct ProxyGNSCertificate *pgc;
1826   struct NetworkHandleList *nh;
1827
1828   for (hd = mhd_httpd_head; hd != NULL; hd = hd->next)
1829   {
1830     if (0 == strcmp (hd->domain, domain))
1831       break;
1832   }
1833
1834   if (NULL == hd)
1835   {
1836     
1837     pgc = generate_gns_certificate (domain);
1838     
1839     hd = GNUNET_malloc (sizeof (struct MhdHttpList));
1840     hd->is_ssl = GNUNET_YES;
1841     strcpy (hd->domain, domain);
1842     hd->proxy_cert = pgc;
1843
1844     /* Start new MHD */
1845     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1846                 "No previous SSL instance found... starting new one for %s\n",
1847                 domain);
1848
1849     hd->daemon = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_SSL, 4444,
1850             &accept_cb, NULL,
1851             &create_response, hd,
1852             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
1853             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
1854             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1855             MHD_OPTION_NOTIFY_COMPLETED,
1856             NULL, NULL,
1857             MHD_OPTION_HTTPS_MEM_KEY, pgc->key,
1858             MHD_OPTION_HTTPS_MEM_CERT, pgc->cert,
1859             MHD_OPTION_END);
1860
1861     GNUNET_assert (hd->daemon != NULL);
1862     hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
1863     
1864     GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
1865   }
1866
1867   nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
1868   nh->h = h;
1869
1870   GNUNET_CONTAINER_DLL_insert (hd->socket_handles_head,
1871                                hd->socket_handles_tail,
1872                                nh);
1873   
1874   return add_handle_to_mhd (h, hd->daemon);
1875 }
1876
1877
1878
1879 /**
1880  * Read data from incoming connection
1881  *
1882  * @param cls the closure
1883  * @param tc the scheduler context
1884  */
1885 static void
1886 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1887 {
1888   struct Socks5Request *s5r = cls;
1889   struct socks5_client_hello *c_hello;
1890   struct socks5_server_hello *s_hello;
1891   struct socks5_client_request *c_req;
1892   struct socks5_server_response *s_resp;
1893
1894   int ret;
1895   char domain[256];
1896   uint8_t dom_len;
1897   uint16_t req_port;
1898   struct hostent *phost;
1899   uint32_t remote_ip;
1900   struct sockaddr_in remote_addr;
1901   struct in_addr *r_sin_addr;
1902
1903   struct NetworkHandleList *nh;
1904
1905   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
1906
1907   if ((NULL != tc->write_ready) &&
1908       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
1909       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
1910                                          sizeof (s5r->rbuf))))
1911   {
1912     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913                 "Successfully read %d bytes from socket\n",
1914                 s5r->rbuf_len);
1915   }
1916   else
1917   {
1918     if (s5r->rbuf_len != 0)
1919       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1920     else
1921       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
1922
1923     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1924       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1925     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1926       GNUNET_SCHEDULER_cancel (s5r->wtask);
1927     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1928       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1929     GNUNET_NETWORK_socket_close (s5r->remote_sock);
1930     GNUNET_NETWORK_socket_close (s5r->sock);
1931     GNUNET_free(s5r);
1932     return;
1933   }
1934
1935   if (s5r->state == SOCKS5_INIT)
1936   {
1937     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1938                 "SOCKS5 init\n");
1939     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
1940
1941     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
1942
1943     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
1944     s5r->wbuf_len = sizeof( struct socks5_server_hello );
1945
1946     s_hello->version = c_hello->version;
1947     s_hello->auth_method = SOCKS_AUTH_NONE;
1948
1949     /* Write response to client */
1950     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1951                                                 s5r->sock,
1952                                                 &do_write, s5r);
1953
1954     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1955                                                 s5r->sock,
1956                                                 &do_read, s5r);
1957
1958     s5r->state = SOCKS5_REQUEST;
1959     return;
1960   }
1961
1962   if (s5r->state == SOCKS5_REQUEST)
1963   {
1964     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1965                 "Processing SOCKS5 request\n");
1966     c_req = (struct socks5_client_request*)&s5r->rbuf;
1967     s_resp = (struct socks5_server_response*)&s5r->wbuf;
1968     //Only 10byte for ipv4 response!
1969     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
1970
1971     GNUNET_assert (c_req->addr_type == 3);
1972
1973     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
1974     memset(domain, 0, sizeof(domain));
1975     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
1976     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
1977
1978     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1979                 "Requested connection is %s:%d\n",
1980                 domain,
1981                 ntohs(req_port));
1982
1983     if (is_tld (domain, GNUNET_GNS_TLD) ||
1984         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
1985     {
1986       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1987                   "Requested connection is gnunet tld\n",
1988                   domain);
1989       
1990       ret = MHD_NO;
1991       if (ntohs(req_port) == HTTPS_PORT)
1992       {
1993         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1994                     "Requested connection is HTTPS\n");
1995         ret = add_handle_to_ssl_mhd ( s5r->sock, domain );
1996       }
1997       else if (NULL != httpd)
1998       {
1999         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2000                     "Requested connection is HTTP\n");
2001         nh = GNUNET_malloc (sizeof (struct NetworkHandleList));
2002         nh->h = s5r->sock;
2003
2004         GNUNET_CONTAINER_DLL_insert (mhd_httpd_head->socket_handles_head,
2005                                mhd_httpd_head->socket_handles_tail,
2006                                nh);
2007
2008         ret = add_handle_to_mhd ( s5r->sock, httpd );
2009       }
2010
2011       if (ret != MHD_YES)
2012       {
2013         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2014                     _("Failed to start HTTP server\n"));
2015         s_resp->version = 0x05;
2016         s_resp->reply = 0x01;
2017         s5r->cleanup = GNUNET_YES;
2018         s5r->cleanup_sock = GNUNET_YES;
2019         s5r->wtask = 
2020           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2021                                         s5r->sock,
2022                                         &do_write, s5r);
2023         return;
2024       }
2025       
2026       /* Signal success */
2027       s_resp->version = 0x05;
2028       s_resp->reply = 0x00;
2029       s_resp->reserved = 0x00;
2030       s_resp->addr_type = 0x01;
2031       
2032       s5r->cleanup = GNUNET_YES;
2033       s5r->cleanup_sock = GNUNET_NO;
2034       s5r->wtask =
2035         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2036                                         s5r->sock,
2037                                         &do_write, s5r);
2038       run_httpds ();
2039       return;
2040     }
2041     else
2042     {
2043       phost = (struct hostent*)gethostbyname (domain);
2044       if (phost == NULL)
2045       {
2046         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2047                     "Resolve %s error!\n", domain );
2048         s_resp->version = 0x05;
2049         s_resp->reply = 0x01;
2050         s5r->cleanup = GNUNET_YES;
2051         s5r->cleanup_sock = GNUNET_YES;
2052         s5r->wtask = 
2053           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2054                                           s5r->sock,
2055                                           &do_write, s5r);
2056         return;
2057       }
2058
2059       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
2060                                                        SOCK_STREAM,
2061                                                        0);
2062       r_sin_addr = (struct in_addr*)(phost->h_addr);
2063       remote_ip = r_sin_addr->s_addr;
2064       memset(&remote_addr, 0, sizeof(remote_addr));
2065       remote_addr.sin_family = AF_INET;
2066 #if HAVE_SOCKADDR_IN_SIN_LEN
2067       remote_addr.sin_len = sizeof (remote_addr);
2068 #endif
2069       remote_addr.sin_addr.s_addr = remote_ip;
2070       remote_addr.sin_port = req_port;
2071       
2072       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2073                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
2074                   ntohs(req_port));
2075
2076       if ((GNUNET_OK !=
2077           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
2078                                           (const struct sockaddr*)&remote_addr,
2079                                           sizeof (remote_addr)))
2080           && (errno != EINPROGRESS))
2081       {
2082         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
2083         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2084                     "socket request error...\n");
2085         s_resp->version = 0x05;
2086         s_resp->reply = 0x01;
2087         s5r->wtask =
2088           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2089                                           s5r->sock,
2090                                           &do_write, s5r);
2091         //TODO see above
2092         return;
2093       }
2094
2095       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2096                   "new remote connection\n");
2097
2098       s_resp->version = 0x05;
2099       s_resp->reply = 0x00;
2100       s_resp->reserved = 0x00;
2101       s_resp->addr_type = 0x01;
2102
2103       s5r->state = SOCKS5_DATA_TRANSFER;
2104
2105       s5r->wtask =
2106         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2107                                         s5r->sock,
2108                                         &do_write, s5r);
2109       s5r->rtask =
2110         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2111                                        s5r->sock,
2112                                        &do_read, s5r);
2113
2114     }
2115     return;
2116   }
2117
2118   if (s5r->state == SOCKS5_DATA_TRANSFER)
2119   {
2120     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
2121     {
2122       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2123                   "Closing connection to client\n");
2124       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
2125         GNUNET_SCHEDULER_cancel (s5r->rtask);
2126       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
2127         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
2128       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2129         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2130       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
2131         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
2132       
2133       if (s5r->remote_sock != NULL)
2134         GNUNET_NETWORK_socket_close (s5r->remote_sock);
2135       GNUNET_NETWORK_socket_close (s5r->sock);
2136       GNUNET_free(s5r);
2137       return;
2138     }
2139
2140     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2141                 "forwarding %d bytes from client\n", s5r->rbuf_len);
2142
2143     s5r->fwdwtask =
2144       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
2145                                       s5r->remote_sock,
2146                                       &do_write_remote, s5r);
2147
2148     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
2149     {
2150       s5r->fwdrtask =
2151         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2152                                        s5r->remote_sock,
2153                                        &do_read_remote, s5r);
2154     }
2155
2156
2157   }
2158
2159   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
2160
2161 }
2162
2163
2164 /**
2165  * Accept new incoming connections
2166  *
2167  * @param cls the closure
2168  * @param tc the scheduler context
2169  */
2170 static void
2171 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2172 {
2173   struct GNUNET_NETWORK_Handle *s;
2174   struct Socks5Request *s5r;
2175
2176   ltask = GNUNET_SCHEDULER_NO_TASK;
2177   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2178     return;
2179
2180   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2181                                          lsock,
2182                                          &do_accept, NULL);
2183
2184   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
2185
2186   if (NULL == s)
2187   {
2188     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
2189     return;
2190   }
2191
2192   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2193               "Got an inbound connection, waiting for data\n");
2194
2195   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
2196   s5r->sock = s;
2197   s5r->state = SOCKS5_INIT;
2198   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
2199   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
2200   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
2201   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2202                                               s5r->sock,
2203                                               &do_read, s5r);
2204   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
2205 }
2206
2207
2208 /**
2209  * Task run on shutdown
2210  *
2211  * @param cls closure
2212  * @param tc task context
2213  */
2214 static void
2215 do_shutdown (void *cls,
2216              const struct GNUNET_SCHEDULER_TaskContext *tc)
2217 {
2218
2219   struct MhdHttpList *hd;
2220   struct MhdHttpList *tmp_hd;
2221   struct NetworkHandleList *nh;
2222   struct NetworkHandleList *tmp_nh;
2223   struct ProxyCurlTask *ctask;
2224   struct ProxyCurlTask *ctask_tmp;
2225   
2226   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2227               "Shutting down...\n");
2228
2229   gnutls_global_deinit ();
2230
2231   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
2232   {
2233     GNUNET_SCHEDULER_cancel (curl_download_task);
2234     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
2235   }
2236
2237   for (hd = mhd_httpd_head; hd != NULL; hd = tmp_hd)
2238   {
2239     tmp_hd = hd->next;
2240
2241     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2242                 "Stopping daemon\n");
2243
2244     if (GNUNET_SCHEDULER_NO_TASK != hd->httpd_task)
2245     {
2246       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2247                   "Stopping select task %d\n",
2248                   hd->httpd_task);
2249       GNUNET_SCHEDULER_cancel (hd->httpd_task);
2250       hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2251     }
2252
2253     if (NULL != hd->daemon)
2254     {
2255       MHD_stop_daemon (hd->daemon);
2256       hd->daemon = NULL;
2257     }
2258
2259     for (nh = hd->socket_handles_head; nh != NULL; nh = tmp_nh)
2260     {
2261       tmp_nh = nh->next;
2262
2263       GNUNET_NETWORK_socket_close (nh->h);
2264
2265       GNUNET_free (nh);
2266     }
2267
2268     if (NULL != hd->proxy_cert)
2269     {
2270       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2271                   "Free certificate\n");
2272       GNUNET_free (hd->proxy_cert);
2273     }
2274
2275     GNUNET_free (hd);
2276   }
2277
2278   for (ctask=ctasks_head; ctask != NULL; ctask=ctask_tmp)
2279   {
2280     ctask_tmp = ctask->next;
2281
2282     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2283                 "Cleaning up cURL task\n");
2284
2285     if (ctask->curl != NULL)
2286       curl_easy_cleanup (ctask->curl);
2287     ctask->curl = NULL;
2288     if (NULL != ctask->headers)
2289       curl_slist_free_all (ctask->headers);
2290     if (NULL != ctask->resolver)
2291       curl_slist_free_all (ctask->resolver);
2292
2293     if (NULL != ctask->response)
2294       MHD_destroy_response (ctask->response);
2295
2296
2297     GNUNET_free (ctask);
2298   }
2299
2300   GNUNET_GNS_disconnect (gns_handle);
2301 }
2302
2303
2304 /**
2305  * Compiles a regex for us
2306  *
2307  * @param re ptr to re struct
2308  * @param rt the expression to compile
2309  * @return 0 on success
2310  */
2311 static int
2312 compile_regex (regex_t *re, const char* rt)
2313 {
2314   int status;
2315   char err[1024];
2316
2317   status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
2318   if (status)
2319   {
2320     regerror (status, re, err, 1024);
2321     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2322                 "Regex error compiling '%s': %s\n", rt, err);
2323     return 1;
2324   }
2325   return 0;
2326 }
2327
2328
2329 /**
2330  * Loads the users local zone key
2331  *
2332  * @return GNUNET_YES on success
2333  */
2334 static int
2335 load_local_zone_key (const struct GNUNET_CONFIGURATION_Handle *cfg)
2336 {
2337   char *keyfile;
2338   struct GNUNET_CRYPTO_RsaPrivateKey *key = NULL;
2339   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
2340   struct GNUNET_CRYPTO_ShortHashCode *zone = NULL;
2341   struct GNUNET_CRYPTO_ShortHashAsciiEncoded zonename;
2342
2343   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2344                                                             "ZONEKEY", &keyfile))
2345   {
2346     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2347                 "Unable to load zone key config value!\n");
2348     return GNUNET_NO;
2349   }
2350
2351   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2352   {
2353     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2354                 "Unable to load zone key %s!\n", keyfile);
2355     GNUNET_free(keyfile);
2356     return GNUNET_NO;
2357   }
2358
2359   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2360   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2361   GNUNET_CRYPTO_short_hash(&pkey,
2362                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2363                            &local_gns_zone);
2364   zone = &local_gns_zone;
2365   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2367               "Using zone: %s!\n", &zonename);
2368   GNUNET_CRYPTO_rsa_key_free(key);
2369   GNUNET_free(keyfile);
2370   
2371   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2372                                                    "PRIVATE_ZONEKEY", &keyfile))
2373   {
2374     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2375                 "Unable to load private zone key config value!\n");
2376     return GNUNET_NO;
2377   }
2378
2379   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2380   {
2381     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2382                 "Unable to load private zone key %s!\n", keyfile);
2383     GNUNET_free(keyfile);
2384     return GNUNET_NO;
2385   }
2386
2387   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2388   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2389   GNUNET_CRYPTO_short_hash(&pkey,
2390                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2391                            &local_private_zone);
2392   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2393   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2394               "Using private zone: %s!\n", &zonename);
2395   GNUNET_CRYPTO_rsa_key_free(key);
2396   GNUNET_free(keyfile);
2397
2398   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns",
2399                                                    "SHORTEN_ZONEKEY", &keyfile))
2400   {
2401     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2402                 "Unable to load shorten zone key config value!\n");
2403     return GNUNET_NO;
2404   }
2405
2406   if (GNUNET_NO == GNUNET_DISK_file_test (keyfile))
2407   {
2408     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2409                 "Unable to load shorten zone key %s!\n", keyfile);
2410     GNUNET_free(keyfile);
2411     return GNUNET_NO;
2412   }
2413
2414   key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
2415   GNUNET_CRYPTO_rsa_key_get_public (key, &pkey);
2416   GNUNET_CRYPTO_short_hash(&pkey,
2417                            sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
2418                            &local_shorten_zone);
2419   GNUNET_CRYPTO_short_hash_to_enc (zone, &zonename);
2420   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2421               "Using shorten zone: %s!\n", &zonename);
2422   GNUNET_CRYPTO_rsa_key_free(key);
2423   GNUNET_free(keyfile);
2424
2425   return GNUNET_YES;
2426 }
2427
2428 /**
2429  * Main function that will be run
2430  *
2431  * @param cls closure
2432  * @param args remaining command-line arguments
2433  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2434  * @param cfg configuration
2435  */
2436 static void
2437 run (void *cls, char *const *args, const char *cfgfile,
2438      const struct GNUNET_CONFIGURATION_Handle *cfg)
2439 {
2440   struct sockaddr_in sa;
2441   struct MhdHttpList *hd;
2442   struct sockaddr_un mhd_unix_sock_addr;
2443   size_t len;
2444   char* proxy_sockfile;
2445   char* cafile_cfg = NULL;
2446   char* cafile;
2447   char* shorten_keyfile;
2448
2449   curl_multi = NULL;
2450
2451   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2452               "Loading CA\n");
2453   
2454   cafile = cafile_opt;
2455
2456   if (NULL == cafile)
2457   {
2458     if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2459                                                           "PROXY_CACERT",
2460                                                           &cafile_cfg))
2461     {
2462       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2463                   "Unable to load proxy CA config value!\n");
2464       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2465                   "No proxy CA provided!\n");
2466       return;
2467     }
2468     cafile = cafile_cfg;
2469   }
2470   
2471   gnutls_global_init ();
2472
2473   gnutls_x509_crt_init (&proxy_ca.cert);
2474   gnutls_x509_privkey_init (&proxy_ca.key);
2475   
2476   load_cert_from_file (proxy_ca.cert, cafile);
2477   load_key_from_file (proxy_ca.key, cafile);
2478
2479   GNUNET_free_non_null (cafile_cfg);
2480   
2481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2482               "Loading Template\n");
2483   
2484   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2485                                                         "PROXY_CACERT",
2486                                                         &shorten_keyfile))
2487   {
2488     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2489                 "Unable to load shorten zonekey config value!\n");
2490     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2491                 "No shorten key provided!\n");
2492     return;
2493   }
2494   else
2495   {
2496     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2497                 "Loading shorten zonekey %s!\n",
2498                 shorten_keyfile);
2499     shorten_zonekey = GNUNET_CRYPTO_rsa_key_create_from_file (shorten_keyfile);
2500     GNUNET_free (shorten_keyfile);
2501   }
2502   
2503   compile_regex (&re_dotplus, (char*) RE_A_HREF);
2504
2505   gns_handle = GNUNET_GNS_connect (cfg);
2506
2507   if (GNUNET_NO == load_local_zone_key (cfg))
2508   {
2509     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2510                 "Unable to load zone!\n");
2511     return;
2512   }
2513
2514   if (NULL == gns_handle)
2515   {
2516     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2517                 "Unable to connect to GNS!\n");
2518     return;
2519   }
2520
2521   memset (&sa, 0, sizeof (sa));
2522   sa.sin_family = AF_INET;
2523   sa.sin_port = htons (port);
2524 #if HAVE_SOCKADDR_IN_SIN_LEN
2525   sa.sin_len = sizeof (sa);
2526 #endif
2527
2528   lsock = GNUNET_NETWORK_socket_create (AF_INET,
2529                                         SOCK_STREAM,
2530                                         0);
2531
2532   if ((NULL == lsock) ||
2533       (GNUNET_OK !=
2534        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
2535                                    sizeof (sa))))
2536   {
2537     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2538                 "Failed to create listen socket bound to `%s'",
2539                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2540     if (NULL != lsock)
2541       GNUNET_NETWORK_socket_close (lsock);
2542     return;
2543   }
2544
2545   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
2546   {
2547     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2548                 "Failed to listen on socket bound to `%s'",
2549                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
2550     return;
2551   }
2552
2553   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
2554                                          lsock, &do_accept, NULL);
2555
2556   ctasks_head = NULL;
2557   ctasks_tail = NULL;
2558
2559   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
2560   {
2561     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2562                 "cURL global init failed!\n");
2563     return;
2564   }
2565
2566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2567               "Proxy listens on port %u\n",
2568               port);
2569
2570   mhd_httpd_head = NULL;
2571   mhd_httpd_tail = NULL;
2572   total_mhd_connections = 0;
2573
2574   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "gns-proxy",
2575                                                             "PROXY_UNIXPATH",
2576                                                             &proxy_sockfile))
2577   {
2578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2579                 "Specify PROXY_UNIXPATH in gns-proxy config section!\n");
2580     return;
2581   }
2582   
2583   mhd_unix_socket = GNUNET_NETWORK_socket_create (AF_UNIX,
2584                                                 SOCK_STREAM,
2585                                                 0);
2586
2587   if (NULL == mhd_unix_socket)
2588   {
2589     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2590                 "Unable to create unix domain socket!\n");
2591     return;
2592   }
2593
2594   mhd_unix_sock_addr.sun_family = AF_UNIX;
2595   strcpy (mhd_unix_sock_addr.sun_path, proxy_sockfile);
2596   unlink (proxy_sockfile);
2597   len = strlen (proxy_sockfile) + sizeof(AF_UNIX);
2598
2599   GNUNET_free (proxy_sockfile);
2600
2601   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (mhd_unix_socket,
2602                                (struct sockaddr*)&mhd_unix_sock_addr,
2603                                len))
2604   {
2605     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2606                 "Unable to bind unix domain socket!\n");
2607     return;
2608   }
2609
2610   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (mhd_unix_socket,
2611                                                  1))
2612   {
2613     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2614                 "Unable to listen on unix domain socket!\n");
2615     return;
2616   }
2617
2618   hd = GNUNET_malloc (sizeof (struct MhdHttpList));
2619   hd->is_ssl = GNUNET_NO;
2620   strcpy (hd->domain, "");
2621   httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444, //Dummy port
2622             &accept_cb, NULL,
2623             &create_response, hd,
2624             MHD_OPTION_LISTEN_SOCKET, GNUNET_NETWORK_get_fd (mhd_unix_socket),
2625             MHD_OPTION_CONNECTION_LIMIT, MHD_MAX_CONNECTIONS,
2626             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
2627             MHD_OPTION_NOTIFY_COMPLETED,
2628             NULL, NULL,
2629             MHD_OPTION_END);
2630
2631   GNUNET_assert (httpd != NULL);
2632   hd->daemon = httpd;
2633   hd->httpd_task = GNUNET_SCHEDULER_NO_TASK;
2634
2635   GNUNET_CONTAINER_DLL_insert (mhd_httpd_head, mhd_httpd_tail, hd);
2636
2637   run_httpds ();
2638
2639   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
2640                                 &do_shutdown, NULL);
2641
2642 }
2643
2644
2645 /**
2646  * The main function for gnunet-gns-proxy.
2647  *
2648  * @param argc number of arguments from the command line
2649  * @param argv command line arguments
2650  * @return 0 ok, 1 on error
2651  */
2652 int
2653 main (int argc, char *const *argv)
2654 {
2655   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
2656     {'p', "port", NULL,
2657      gettext_noop ("listen on specified port"), 1,
2658      &GNUNET_GETOPT_set_string, &port},
2659     {'a', "authority", NULL,
2660       gettext_noop ("pem file to use as CA"), 1,
2661       &GNUNET_GETOPT_set_string, &cafile_opt},
2662     GNUNET_GETOPT_OPTION_END
2663   };
2664
2665   int ret;
2666
2667   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
2668     return 2;
2669
2670
2671   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
2672   ret =
2673       (GNUNET_OK ==
2674        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
2675                            _("GNUnet GNS proxy"),
2676                            options,
2677                            &run, NULL)) ? 0 : 1;
2678   GNUNET_free_non_null ((char*)argv);
2679
2680   return ret;
2681 }