-fix response buffer handling
[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 <microhttpd.h>
24 #include <curl/curl.h>
25 #include "gns_proxy_proto.h"
26 #include "gns.h"
27
28 #define GNUNET_GNS_PROXY_PORT 7777
29
30 //TODO maybe make this an api call
31 /**
32  * Checks if name is in tld
33  *
34  * @param name the name to check
35  * @param tld the TLD to check for
36  * @return GNUNET_YES or GNUNET_NO
37  */
38 int
39 is_tld(const char* name, const char* tld)
40 {
41   int offset = 0;
42
43   if (strlen(name) <= strlen(tld))
44   {
45     return GNUNET_NO;
46   }
47
48   offset = strlen(name)-strlen(tld);
49   if (strcmp (name+offset, tld) != 0)
50   {
51     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
52                "%s is not in .%s TLD\n", name, tld);
53     return GNUNET_NO;
54   }
55
56   return GNUNET_YES;
57 }
58
59 struct Socks5Request
60 {
61   struct GNUNET_NETWORK_Handle *sock;
62   struct GNUNET_NETWORK_Handle *remote_sock;
63
64   int state;
65
66   GNUNET_SCHEDULER_TaskIdentifier rtask;
67   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
68   GNUNET_SCHEDULER_TaskIdentifier wtask;
69   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
70
71   char rbuf[2048];
72   char wbuf[2048];
73   unsigned int rbuf_len;
74   unsigned int wbuf_len;
75 };
76
77
78 #define BUF_WAIT_FOR_CURL 0
79 #define BUF_WAIT_FOR_MHD 1
80
81 struct ProxyCurlTask
82 {
83   //DLL
84   struct ProxyCurlTask *prev;
85   struct ProxyCurlTask *next;
86
87   CURL *curl;
88   char url[2048];
89   char buffer[CURL_MAX_WRITE_SIZE];
90   char *buffer_ptr;
91   int buf_status;
92   unsigned int bytes_downloaded;
93   unsigned int bytes_in_buffer;
94   int download_in_progress;
95   int download_successful;
96   int download_error;
97   struct MHD_Connection *connection;
98   
99 };
100
101 unsigned long port = GNUNET_GNS_PROXY_PORT;
102 static struct GNUNET_NETWORK_Handle *lsock;
103 GNUNET_SCHEDULER_TaskIdentifier ltask;
104 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
105 static struct MHD_Daemon *httpd;
106 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
107 CURLM *curl_multi;
108
109 struct ProxyCurlTask *ctasks_head;
110 struct ProxyCurlTask *ctasks_tail;
111
112 static int
113 con_val_iter (void *cls,
114               enum MHD_ValueKind kind,
115               const char *key,
116               const char *value)
117 {
118   char* buf = (char*)cls;
119
120   if (0 == strcmp ("Host", key))
121   {
122     strcpy (buf, value);
123     return MHD_NO;
124   }
125   return MHD_YES;
126 }
127
128
129 /**
130  * schedule mhd
131  */
132 static void
133 run_httpd (void);
134
135
136 /**
137  * Process cURL download bits
138  *
139  * @param ptr buffer with data
140  * @param size size of a record
141  * @param nmemb number of records downloaded
142  * @param ctx context
143  * @return number of processed bytes
144  */
145 static size_t
146 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
147 {
148   const char *cbuf = ptr;
149   size_t total;
150   struct ProxyCurlTask *ctask = ctx;
151
152   MHD_run (httpd);
153
154   total = size*nmemb;
155   ctask->bytes_downloaded += total;
156
157   if (total == 0)
158   {
159     return total;
160   }
161
162   if (total > sizeof (ctask->buffer))
163   {
164     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
165                 "CURL gave us too much data to handle (%d)!\n",
166                 total);
167     return 0;
168   }
169   
170   if (ctask->buf_status == BUF_WAIT_FOR_MHD)
171   {
172     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173                 "CURL: Waiting for MHD (%s)\n", ctask->url);
174     return CURL_WRITEFUNC_PAUSE;
175   }
176
177
178   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
179               "CURL: Copying to MHD (%s, %d)\n", ctask->url, total);
180   memcpy (ctask->buffer, cbuf, total);
181   ctask->bytes_in_buffer = total;
182   ctask->buffer_ptr = ctask->buffer;
183
184   ctask->buf_status = BUF_WAIT_FOR_MHD;
185
186   //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
187   //            "cURL chunk:\n%s\n", (char*)ctask->buffer);
188   MHD_run (httpd);
189   return total;
190 }
191
192
193
194 /**
195  * Callback to free content
196  *
197  * @param cls content to free
198  */
199 static void
200 mhd_content_free (void *cls)
201 {
202   struct ProxyCurlTask *ctask = cls;
203
204   if (ctask->curl != NULL)
205     curl_easy_cleanup (ctask->curl);
206
207   GNUNET_free (ctask);
208
209 }
210
211 /**
212  * Callback for MHD response
213  *
214  * @param cls closure
215  * @param pos in buffer
216  * @param buf buffer
217  * @param max space in buffer
218  */
219 static ssize_t
220 mhd_content_cb (void *cls,
221                 uint64_t pos,
222                 char* buf,
223                 size_t max)
224 {
225   struct ProxyCurlTask *ctask = cls;
226   ssize_t copied = 0;
227
228   if (ctask->download_successful &&
229       (ctask->buf_status == BUF_WAIT_FOR_CURL))
230   {
231     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232                 "MHD: sending response for %s\n", ctask->url);
233     ctask->download_in_progress = GNUNET_NO;
234     curl_multi_remove_handle (curl_multi, ctask->curl);
235     curl_easy_cleanup (ctask->curl);
236     return MHD_CONTENT_READER_END_OF_STREAM;
237   }
238   
239   if (ctask->download_error &&
240       (ctask->buf_status == BUF_WAIT_FOR_CURL))
241   {
242     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243                 "MHD: sending error response\n");
244     ctask->download_in_progress = GNUNET_NO;
245     curl_multi_remove_handle (curl_multi, ctask->curl);
246     curl_easy_cleanup (ctask->curl);
247     return MHD_CONTENT_READER_END_WITH_ERROR;
248   }
249
250   if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
251   {
252     return 0;
253   }
254
255   if ( ctask->bytes_in_buffer > max )
256   {
257     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
258                  "MHD: buffer in response too small! (%s)\n",
259                  ctask->url);
260     memcpy ( buf, ctask->buffer_ptr, max);
261     ctask->bytes_in_buffer -= max;
262     ctask->buffer_ptr += max;
263     copied = max;
264   }
265   else
266   {
267     GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
268                  "MHD: copying %d bytes to mhd response at offset %d\n",
269                  ctask->bytes_in_buffer, pos);
270
271     memcpy ( buf, ctask->buffer_ptr, ctask->bytes_in_buffer );
272     copied = ctask->bytes_in_buffer;
273     ctask->buf_status = BUF_WAIT_FOR_CURL;
274     ctask->buffer_ptr = ctask->buffer;
275     curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
276   }
277
278   return copied;
279 }
280
281
282
283 /**
284  * Task that is run when we are ready to receive more data
285  * from curl
286  *
287  * @param cls closure
288  * @param tc task context
289  */
290 static void
291 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
292
293 /**
294  * Ask cURL for the select sets and schedule download
295  */
296 static void
297 curl_download_prepare ()
298 {
299   CURLMcode mret;
300   fd_set rs;
301   fd_set ws;
302   fd_set es;
303   int max;
304   struct GNUNET_NETWORK_FDSet *grs;
305   struct GNUNET_NETWORK_FDSet *gws;
306   long to;
307   struct GNUNET_TIME_Relative rtime;
308
309   max = -1;
310   FD_ZERO (&rs);
311   FD_ZERO (&ws);
312   FD_ZERO (&es);
313   mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
314
315   if (mret != CURLM_OK)
316   {
317     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318                 "%s failed at %s:%d: `%s'\n",
319                 "curl_multi_fdset", __FILE__, __LINE__,
320                 curl_multi_strerror (mret));
321     //TODO cleanup here?
322     return;
323   }
324
325   mret = curl_multi_timeout (curl_multi, &to);
326   rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
327
328   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
329               "cURL multi fds: max=%d timeout=%llu\n", max, to);
330
331   grs = GNUNET_NETWORK_fdset_create ();
332   gws = GNUNET_NETWORK_fdset_create ();
333   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
334   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "Scheduling task cURL\n");
337
338   if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
339     GNUNET_SCHEDULER_cancel (curl_download_task);
340   
341   curl_download_task =
342     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
343                                  rtime,
344                                  grs, gws,
345                                  &curl_task_download, curl_multi);
346   GNUNET_NETWORK_fdset_destroy (gws);
347   GNUNET_NETWORK_fdset_destroy (grs);
348
349 }
350
351
352 /**
353  * Task that is run when we are ready to receive more data
354  * from curl
355  *
356  * @param cls closure
357  * @param tc task context
358  */
359 static void
360 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
361 {
362   int running;
363   int msgnum;
364   struct CURLMsg *msg;
365   CURLMcode mret;
366   struct ProxyCurlTask *ctask;
367   int num_ctasks;
368
369   curl_download_task = GNUNET_SCHEDULER_NO_TASK;
370
371   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
372   {
373     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
374                 "Shutdown requested while trying to download\n");
375     //TODO cleanup
376     return;
377   }
378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
379               "Ready to dl\n");
380
381   do
382   {
383     running = 0;
384     num_ctasks = 0;
385     
386     mret = curl_multi_perform (curl_multi, &running);
387
388     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
389                 "Running curl tasks: %d\n", running);
390
391     ctask = ctasks_head;
392     for (; ctask != NULL; ctask = ctask->next)
393     {
394       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395                   "CTask: %s\n", ctask->url);
396       num_ctasks++;
397     }
398
399     if (num_ctasks != running)
400     {
401       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402                   "%d ctasks, %d curl running\n", num_ctasks, running);
403     }
404     
405     do
406     {
407       ctask = ctasks_head;
408       msg = curl_multi_info_read (curl_multi, &msgnum);
409       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
410                   "Messages left: %d\n", msgnum);
411       
412       if (msg == NULL)
413         break;
414       switch (msg->msg)
415       {
416        case CURLMSG_DONE:
417          if ((msg->data.result != CURLE_OK) &&
418              (msg->data.result != CURLE_GOT_NOTHING))
419          {
420            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
421                        "Download curl failed");
422             
423            for (; ctask != NULL; ctask = ctask->next)
424            {
425              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
426                continue;
427              
428              GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429                          "Download curl failed for task %s: %s.\n",
430                          ctask->url,
431                          curl_easy_strerror (msg->data.result));
432              ctask->download_successful = GNUNET_NO;
433              ctask->download_error = GNUNET_YES;
434              //curl_multi_remove_handle (curl_multi, ctask->curl);
435              //curl_easy_cleanup (ctask->curl);
436              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
437                                           ctask);
438              break;
439            }
440            GNUNET_assert (ctask != NULL);
441          }
442          else
443          {
444            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
445                        "cURL download completed.\n");
446
447            for (; ctask != NULL; ctask = ctask->next)
448            {
449              if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
450                continue;
451              
452              GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                          "cURL task %s found.\n", ctask->url);
454              ctask->download_successful = GNUNET_YES;
455              //curl_multi_remove_handle (curl_multi, ctask->curl);
456              //curl_easy_cleanup (ctask->curl);
457              GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
458                                           ctask);
459              break;
460            }
461            GNUNET_assert (ctask != NULL);
462          }
463          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464                      "curl end %s\n", curl_easy_strerror(msg->data.result));
465          break;
466        default:
467          GNUNET_assert (0);
468          break;
469       }
470     } while (msgnum > 0);
471     
472     num_ctasks=0;
473     for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
474     {
475       num_ctasks++;
476     }
477     
478     if (num_ctasks != running)
479     {
480       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481                   "%d ctasks, %d curl running\n", num_ctasks, running);
482     }
483
484     GNUNET_assert ( num_ctasks == running );
485
486     run_httpd ();
487
488   } while (mret == CURLM_CALL_MULTI_PERFORM);
489   
490   
491   if (mret != CURLM_OK)
492   {
493     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
494                 "curl_multi_perform", __FILE__, __LINE__,
495                 curl_multi_strerror (mret));
496   }
497   curl_download_prepare();
498 }
499
500
501 /**
502  * Main MHD callback for handling requests.
503  *
504  * @param cls unused
505  * @param con MHD connection handle
506  * @param meth the HTTP method used ("GET", "PUT", etc.)
507  * @param ver the HTTP version string (i.e. "HTTP/1.1")
508  * @param upload_data the data being uploaded (excluding HEADERS,
509  *        for a POST that fits into memory and that is encoded
510  *        with a supported encoding, the POST data will NOT be
511  *        given in upload_data and is instead available as
512  *        part of MHD_get_connection_values; very large POST
513  *        data *will* be made available incrementally in
514  *        upload_data)
515  * @param upload_data_size set initially to the size of the
516  *        upload_data provided; the method must update this
517  *        value to the number of bytes NOT processed;
518  * @param con_cls pointer to location where we store the 'struct Request'
519  * @return MHD_YES if the connection was handled successfully,
520  *         MHD_NO if the socket must be closed due to a serious
521  *         error while handling the request
522  */
523 static int
524 create_response (void *cls,
525                  struct MHD_Connection *con,
526                  const char *url,
527                  const char *meth,
528                  const char *ver,
529                  const char *upload_data,
530                  size_t *upload_data_size,
531                  void **con_cls)
532 {
533   static int dummy;
534   const char* page = "<html><head><title>gnoxy</title>"\
535                       "</head><body>cURL fail</body></html>";
536   struct MHD_Response *response;
537   char host[265];
538   char curlurl[512];
539   int ret = MHD_YES;
540
541   CURLMcode mret;
542   struct ProxyCurlTask *ctask;
543   
544   if (0 != strcmp (meth, "GET"))
545     return MHD_NO;
546   if (&dummy != *con_cls)
547   {
548     *con_cls = &dummy;
549     return MHD_YES;
550   }
551
552   if (0 != *upload_data_size)
553     return MHD_NO;
554
555   *con_cls = NULL;
556
557   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558               "url %s\n", url);
559
560   MHD_get_connection_values (con,
561                              MHD_HEADER_KIND,
562                              &con_val_iter, host);
563
564   
565   /* Do cURL */
566   ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
567   ctask->curl = curl_easy_init();
568
569   if (curl_multi == NULL)
570     curl_multi = curl_multi_init ();
571   
572   if ((ctask->curl == NULL) || (curl_multi == NULL))
573   {
574     response = MHD_create_response_from_buffer (strlen (page),
575                                               (void*)page,
576                                               MHD_RESPMEM_PERSISTENT);
577     ret = MHD_queue_response (con,
578                               MHD_HTTP_OK,
579                               response);
580     MHD_destroy_response (response);
581     GNUNET_free (ctask);
582     return ret;
583   }
584
585   ctask->download_in_progress = GNUNET_YES;
586   ctask->download_successful = GNUNET_NO;
587   ctask->bytes_downloaded = 0;
588   ctask->connection = con;
589   ctask->buf_status = BUF_WAIT_FOR_CURL;
590   ctask->bytes_in_buffer = 0;
591
592   curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
593   curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
594   curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
595   curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
596   /* no need to abort if the above failed */
597   sprintf (curlurl, "http://%s%s", host, url);
598   strcpy (ctask->url, curlurl);
599   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
600               "Adding new curl task for %s\n", curlurl);
601   
602   curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
603   curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
604   curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
605   curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
606
607   mret = curl_multi_add_handle (curl_multi, ctask->curl);
608
609   if (mret != CURLM_OK)
610   {
611     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
612                 "%s failed at %s:%d: `%s'\n",
613                 "curl_multi_add_handle", __FILE__, __LINE__,
614                 curl_multi_strerror (mret));
615     response = MHD_create_response_from_buffer (strlen (page),
616                                                 (void*)page,
617                                                 MHD_RESPMEM_PERSISTENT);
618     ret = MHD_queue_response (con,
619                               MHD_HTTP_OK,
620                               response);
621     MHD_destroy_response (response);
622
623     curl_easy_cleanup (ctask->curl);
624     GNUNET_free (ctask);
625     return ret;
626   }
627   
628   GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
629
630   curl_download_prepare ();
631
632   response = MHD_create_response_from_callback (-1, -1,
633                                                 &mhd_content_cb,
634                                                 ctask,
635                                                 &mhd_content_free);
636   
637   ret = MHD_queue_response (con, MHD_HTTP_OK, response);
638   
639   //MHD_destroy_response (response);
640
641   return ret;
642 }
643
644 /**
645  * Task run whenever HTTP server operations are pending.
646  *
647  * @param cls unused
648  * @param tc sched context
649  */
650 static void
651 do_httpd (void *cls,
652           const struct GNUNET_SCHEDULER_TaskContext *tc);
653
654
655 /**
656  * schedule mhd
657  */
658 static void
659 run_httpd ()
660 {
661   fd_set rs;
662   fd_set ws;
663   fd_set es;
664   struct GNUNET_NETWORK_FDSet *wrs;
665   struct GNUNET_NETWORK_FDSet *wws;
666   struct GNUNET_NETWORK_FDSet *wes;
667   int max;
668   int haveto;
669   unsigned MHD_LONG_LONG timeout;
670   struct GNUNET_TIME_Relative tv;
671
672   FD_ZERO (&rs);
673   FD_ZERO (&ws);
674   FD_ZERO (&es);
675   wrs = GNUNET_NETWORK_fdset_create ();
676   wes = GNUNET_NETWORK_fdset_create ();
677   wws = GNUNET_NETWORK_fdset_create ();
678   max = -1;
679   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
680   
681   
682   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683               "MHD fds: max=%d\n", max);
684   
685   haveto = MHD_get_timeout (httpd, &timeout);
686
687   if (haveto == MHD_YES)
688     tv.rel_value = (uint64_t) timeout;
689   else
690     tv = GNUNET_TIME_UNIT_FOREVER_REL;
691   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
692   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
693   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
694   
695   if (httpd_task != GNUNET_SCHEDULER_NO_TASK)
696     GNUNET_SCHEDULER_cancel (httpd_task);
697   httpd_task =
698     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
699                                  tv, wrs, wws,
700                                  &do_httpd, NULL);
701   GNUNET_NETWORK_fdset_destroy (wrs);
702   GNUNET_NETWORK_fdset_destroy (wws);
703   GNUNET_NETWORK_fdset_destroy (wes);
704 }
705
706 /**
707  * Task run whenever HTTP server operations are pending.
708  *
709  * @param cls unused
710  * @param tc sched context
711  */
712 static void
713 do_httpd (void *cls,
714           const struct GNUNET_SCHEDULER_TaskContext *tc)
715 {
716   httpd_task = GNUNET_SCHEDULER_NO_TASK;
717   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
718               "MHD run \n");
719   MHD_run (httpd);
720   run_httpd ();
721 }
722
723 /**
724  * Read data from socket
725  *
726  * @param cls the closure
727  * @param tc scheduler context
728  */
729 static void
730 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
731
732 /**
733  * Read from remote end
734  *
735  * @param cls closure
736  * @param tc scheduler context
737  */
738 static void
739 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
740
741 /**
742  * Write data to remote socket
743  *
744  * @param cls the closure
745  * @param tc scheduler context
746  */
747 static void
748 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
749 {
750   struct Socks5Request *s5r = cls;
751   unsigned int len;
752
753   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
754
755   if ((NULL != tc->read_ready) &&
756       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
757       ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
758                                          s5r->rbuf_len)>0)))
759   {
760     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
761                 "Successfully sent %d bytes to remote socket\n",
762                 len);
763   }
764   else
765   {
766     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
767     //Really!?!?!?
768     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
769       GNUNET_SCHEDULER_cancel (s5r->rtask);
770     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
771       GNUNET_SCHEDULER_cancel (s5r->wtask);
772     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
773       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
774     GNUNET_NETWORK_socket_close (s5r->remote_sock);
775     GNUNET_NETWORK_socket_close (s5r->sock);
776     GNUNET_free(s5r);
777     return;
778   }
779
780   s5r->rtask =
781     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
782                                    s5r->sock,
783                                    &do_read, s5r);
784 }
785
786
787 /**
788  * Write data to socket
789  *
790  * @param cls the closure
791  * @param tc scheduler context
792  */
793 static void
794 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
795 {
796   struct Socks5Request *s5r = cls;
797   unsigned int len;
798
799   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
800
801   if ((NULL != tc->read_ready) &&
802       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
803       ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
804                                          s5r->wbuf_len)>0)))
805   {
806     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807                 "Successfully sent %d bytes to socket\n",
808                 len);
809   }
810   else
811   {
812     
813     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
814     //Really!?!?!?
815     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
816       GNUNET_SCHEDULER_cancel (s5r->rtask);
817     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
818       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
819     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
820       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
821     GNUNET_NETWORK_socket_close (s5r->remote_sock);
822     GNUNET_NETWORK_socket_close (s5r->sock);
823     GNUNET_free(s5r);
824     return;
825   }
826
827   if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
828       (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
829     s5r->fwdrtask =
830       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
831                                      s5r->remote_sock,
832                                      &do_read_remote, s5r);
833 }
834
835 /**
836  * Read from remote end
837  *
838  * @param cls closure
839  * @param tc scheduler context
840  */
841 static void
842 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
843 {
844   struct Socks5Request *s5r = cls;
845   
846   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
847
848
849   if ((NULL != tc->write_ready) &&
850       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
851       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
852                                          sizeof (s5r->wbuf))))
853   {
854     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
855                 "Successfully read %d bytes from remote socket\n",
856                 s5r->wbuf_len);
857   }
858   else
859   {
860     if (s5r->wbuf_len == 0)
861       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
862                   "0 bytes received from remote... graceful shutdown!\n");
863     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
864       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
865     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
866       GNUNET_SCHEDULER_cancel (s5r->rtask);
867     
868     GNUNET_NETWORK_socket_close (s5r->remote_sock);
869     s5r->remote_sock = NULL;
870     GNUNET_NETWORK_socket_close (s5r->sock);
871     GNUNET_free(s5r);
872
873     return;
874   }
875   
876   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
877                                                s5r->sock,
878                                                &do_write, s5r);
879   
880 }
881
882
883 static int
884 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h)
885 {
886   int fd;
887   struct sockaddr *addr;
888   socklen_t len;
889
890   fd = GNUNET_NETWORK_get_fd (h);
891   addr = GNUNET_NETWORK_get_addr (h);
892   len = GNUNET_NETWORK_get_addrlen (h);
893
894   return MHD_add_connection (httpd, fd, addr, len);
895 }
896
897 /**
898  * Read data from incoming connection
899  *
900  * @param cls the closure
901  * @param tc the scheduler context
902  */
903 static void
904 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
905 {
906   struct Socks5Request *s5r = cls;
907   struct socks5_client_hello *c_hello;
908   struct socks5_server_hello *s_hello;
909   struct socks5_client_request *c_req;
910   struct socks5_server_response *s_resp;
911
912   char domain[256];
913   uint8_t dom_len;
914   uint16_t req_port;
915   struct hostent *phost;
916   uint32_t remote_ip;
917   struct sockaddr_in remote_addr;
918   struct in_addr *r_sin_addr;
919
920   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
921
922   if ((NULL != tc->write_ready) &&
923       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
924       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
925                                          sizeof (s5r->rbuf))))
926   {
927     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
928                 "Successfully read %d bytes from socket\n",
929                 s5r->rbuf_len);
930   }
931   else
932   {
933     if (s5r->rbuf_len != 0)
934       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
935     else
936       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
937
938     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
939       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
940     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
941       GNUNET_SCHEDULER_cancel (s5r->wtask);
942     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
943       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
944     GNUNET_NETWORK_socket_close (s5r->remote_sock);
945     GNUNET_NETWORK_socket_close (s5r->sock);
946     GNUNET_free(s5r);
947     return;
948   }
949
950   if (s5r->state == SOCKS5_INIT)
951   {
952     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
953                 "SOCKS5 init\n");
954     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
955
956     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
957
958     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
959     s5r->wbuf_len = sizeof( struct socks5_server_hello );
960
961     s_hello->version = c_hello->version;
962     s_hello->auth_method = SOCKS_AUTH_NONE;
963
964     /* Write response to client */
965     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
966                                                 s5r->sock,
967                                                 &do_write, s5r);
968
969     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
970                                                 s5r->sock,
971                                                 &do_read, s5r);
972
973     s5r->state = SOCKS5_REQUEST;
974     return;
975   }
976
977   if (s5r->state == SOCKS5_REQUEST)
978   {
979     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
980                 "Processing SOCKS5 request\n");
981     c_req = (struct socks5_client_request*)&s5r->rbuf;
982     s_resp = (struct socks5_server_response*)&s5r->wbuf;
983     //Only 10byte for ipv4 response!
984     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
985
986     GNUNET_assert (c_req->addr_type == 3);
987
988     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
989     memset(domain, 0, sizeof(domain));
990     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
991     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
992
993     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
994                 "Requested connection is %s:%d\n",
995                 domain,
996                 ntohs(req_port));
997
998     if (is_tld (domain, GNUNET_GNS_TLD) ||
999         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
1000     {
1001       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1002                   "Requested connection is gnunet tld\n",
1003                   domain);
1004
1005       if (NULL == httpd)
1006       {
1007         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1008                     _("Failed to start HTTP server\n"));
1009         s_resp->version = 0x05;
1010         s_resp->reply = 0x01;
1011         s5r->wtask = 
1012           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1013                                         s5r->sock,
1014                                         &do_write, s5r);
1015         //ERROR!
1016         //TODO! close socket after the write! schedule task
1017         //GNUNET_NETWORK_socket_close (s5r->sock);
1018         //GNUNET_free(s5r);
1019         return;
1020       }
1021
1022       if (MHD_YES == add_handle_to_mhd ( s5r->sock ))
1023         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1024                     "Sucessfully added client to MHD!\n");
1025       s_resp->version = 0x05;
1026       s_resp->reply = 0x00;
1027       s_resp->reserved = 0x00;
1028       s_resp->addr_type = 0x01;
1029
1030       s5r->wtask =
1031         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1032                                         s5r->sock,
1033                                         &do_write, s5r);
1034       run_httpd ();
1035       //GNUNET_free ( s5r );
1036       //FIXME complete socks resp!
1037       return;
1038     }
1039     else
1040     {
1041       phost = (struct hostent*)gethostbyname (domain);
1042       if (phost == NULL)
1043       {
1044         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045                     "Resolve %s error!\n", domain );
1046         s_resp->version = 0x05;
1047         s_resp->reply = 0x01;
1048         s5r->wtask = 
1049           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1050                                           s5r->sock,
1051                                           &do_write, s5r);
1052         //ERROR!
1053         //TODO! close socket after the write! schedule task
1054         //GNUNET_NETWORK_socket_close (s5r->sock);
1055         //GNUNET_free(s5r);
1056         return;
1057       }
1058
1059       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
1060                                                        SOCK_STREAM,
1061                                                        0);
1062       r_sin_addr = (struct in_addr*)(phost->h_addr);
1063       remote_ip = r_sin_addr->s_addr;
1064       memset(&remote_addr, 0, sizeof(remote_addr));
1065       remote_addr.sin_family = AF_INET;
1066 #if HAVE_SOCKADDR_IN_SIN_LEN
1067       remote_addr.sin_len = sizeof (remote_addr);
1068 #endif
1069       remote_addr.sin_addr.s_addr = remote_ip;
1070       remote_addr.sin_port = req_port;
1071       
1072       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1073                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
1074                   ntohs(req_port));
1075
1076       if ((GNUNET_OK !=
1077           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
1078                                           (const struct sockaddr*)&remote_addr,
1079                                           sizeof (remote_addr)))
1080           && (errno != EINPROGRESS))
1081       {
1082         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
1083         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1084                     "socket request error...\n");
1085         s_resp->version = 0x05;
1086         s_resp->reply = 0x01;
1087         s5r->wtask =
1088           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1089                                           s5r->sock,
1090                                           &do_write, s5r);
1091         //TODO see above
1092         return;
1093       }
1094
1095       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096                   "new remote connection\n");
1097
1098       s_resp->version = 0x05;
1099       s_resp->reply = 0x00;
1100       s_resp->reserved = 0x00;
1101       s_resp->addr_type = 0x01;
1102
1103       s5r->state = SOCKS5_DATA_TRANSFER;
1104
1105       s5r->wtask =
1106         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1107                                         s5r->sock,
1108                                         &do_write, s5r);
1109       s5r->rtask =
1110         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1111                                        s5r->sock,
1112                                        &do_read, s5r);
1113
1114     }
1115     return;
1116   }
1117
1118   if (s5r->state == SOCKS5_DATA_TRANSFER)
1119   {
1120     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
1121     {
1122       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1123                   "Closing connection to client\n");
1124       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1125         GNUNET_SCHEDULER_cancel (s5r->rtask);
1126       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1127         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1128       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1129         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1130       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1131         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1132       
1133       if (s5r->remote_sock != NULL)
1134         GNUNET_NETWORK_socket_close (s5r->remote_sock);
1135       GNUNET_NETWORK_socket_close (s5r->sock);
1136       GNUNET_free(s5r);
1137       return;
1138     }
1139
1140     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141                 "forwarding %d bytes from client\n", s5r->rbuf_len);
1142
1143     s5r->fwdwtask =
1144       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1145                                       s5r->remote_sock,
1146                                       &do_write_remote, s5r);
1147
1148     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
1149     {
1150       s5r->fwdrtask =
1151         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1152                                        s5r->remote_sock,
1153                                        &do_read_remote, s5r);
1154     }
1155
1156
1157   }
1158
1159   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
1160
1161 }
1162
1163 /**
1164  * Accept new incoming connections
1165  *
1166  * @param cls the closure
1167  * @param tc the scheduler context
1168  */
1169 static void
1170 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1171 {
1172   struct GNUNET_NETWORK_Handle *s;
1173   struct Socks5Request *s5r;
1174
1175   ltask = GNUNET_SCHEDULER_NO_TASK;
1176   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1177     return;
1178
1179   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1180                                          lsock,
1181                                          &do_accept, NULL);
1182
1183   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
1184
1185   if (NULL == s)
1186   {
1187     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
1188     return;
1189   }
1190
1191   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192               "Got an inbound connection, waiting for data\n");
1193
1194   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
1195   s5r->sock = s;
1196   s5r->state = SOCKS5_INIT;
1197   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1198   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1199   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1200   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1201                                               s5r->sock,
1202                                               &do_read, s5r);
1203   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
1204 }
1205
1206 /**
1207  * Task run on shutdown
1208  *
1209  * @param cls closure
1210  * @param tc task context
1211  */
1212 static void
1213 do_shutdown (void *cls,
1214              const struct GNUNET_SCHEDULER_TaskContext *tc)
1215 {
1216   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
1217   {
1218     GNUNET_SCHEDULER_cancel (httpd_task);
1219     httpd_task = GNUNET_SCHEDULER_NO_TASK;
1220   }
1221   
1222   if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
1223   {
1224     GNUNET_SCHEDULER_cancel (curl_download_task);
1225     curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1226   }
1227
1228   if (NULL != httpd)
1229   {
1230     MHD_stop_daemon (httpd);
1231     httpd = NULL;
1232   }
1233 }
1234
1235 /**
1236  * Main function that will be run
1237  *
1238  * @param cls closure
1239  * @param args remaining command-line arguments
1240  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1241  * @param cfg configuration
1242  */
1243 static void
1244 run (void *cls, char *const *args, const char *cfgfile,
1245      const struct GNUNET_CONFIGURATION_Handle *cfg)
1246 {
1247   struct sockaddr_in sa;
1248
1249   memset (&sa, 0, sizeof (sa));
1250   sa.sin_family = AF_INET;
1251   sa.sin_port = htons (port);
1252 #if HAVE_SOCKADDR_IN_SIN_LEN
1253   sa.sin_len = sizeof (sa);
1254 #endif
1255
1256   lsock = GNUNET_NETWORK_socket_create (AF_INET,
1257                                         SOCK_STREAM,
1258                                         0);
1259
1260   if ((NULL == lsock) ||
1261       (GNUNET_OK !=
1262        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
1263                                    sizeof (sa))))
1264   {
1265     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1266                 "Failed to create listen socket bound to `%s'",
1267                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1268     if (NULL != lsock)
1269       GNUNET_NETWORK_socket_close (lsock);
1270     return;
1271   }
1272
1273   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
1274   {
1275     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1276                 "Failed to listen on socket bound to `%s'",
1277                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1278     return;
1279   }
1280
1281   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1282                                          lsock, &do_accept, NULL);
1283
1284   ctasks_head = NULL;
1285   ctasks_tail = NULL;
1286
1287   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1288   {
1289     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1290                 "cURL global init failed!\n");
1291     return;
1292   }
1293
1294   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295               "Proxy listens on port %u\n",
1296               port);
1297   
1298   httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444,
1299                                NULL, NULL,
1300                                &create_response, NULL,
1301                                MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1302                                MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1303                                MHD_OPTION_NOTIFY_COMPLETED,
1304                                NULL, NULL,
1305                                MHD_OPTION_END);
1306   run_httpd ();
1307
1308   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1309                                 &do_shutdown, NULL);
1310
1311 }
1312
1313 /**
1314  * The main function for gnunet-gns-proxy.
1315  *
1316  * @param argc number of arguments from the command line
1317  * @param argv command line arguments
1318  * @return 0 ok, 1 on error
1319  */
1320 int
1321 main (int argc, char *const *argv)
1322 {
1323   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1324     {'p', "port", NULL,
1325      gettext_noop ("listen on specified port"), 1,
1326      &GNUNET_GETOPT_set_string, &port},
1327     GNUNET_GETOPT_OPTION_END
1328   };
1329
1330   int ret;
1331
1332   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
1333   ret =
1334       (GNUNET_OK ==
1335        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
1336                            _("GNUnet GNS proxy"),
1337                            options,
1338                            &run, NULL)) ? 0 : 1;
1339   return ret;
1340 }