2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 #include <gnunet_util_lib.h>
23 #include <gnunet_gns_service.h>
24 #include <microhttpd.h>
25 #include <curl/curl.h>
27 #include "gns_proxy_proto.h"
30 #define GNUNET_GNS_PROXY_PORT 7777
32 //TODO maybe make this an api call
34 * Checks if name is in tld
36 * @param name the name to check
37 * @param tld the TLD to check for
38 * @return GNUNET_YES or GNUNET_NO
41 is_tld(const char* name, const char* tld)
45 if (strlen(name) <= strlen(tld))
50 offset = strlen(name)-strlen(tld);
51 if (strcmp (name+offset, tld) != 0)
53 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
54 "%s is not in .%s TLD\n", name, tld);
63 struct GNUNET_NETWORK_Handle *sock;
64 struct GNUNET_NETWORK_Handle *remote_sock;
68 GNUNET_SCHEDULER_TaskIdentifier rtask;
69 GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
70 GNUNET_SCHEDULER_TaskIdentifier wtask;
71 GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
75 unsigned int rbuf_len;
76 unsigned int wbuf_len;
80 #define BUF_WAIT_FOR_CURL 0
81 #define BUF_WAIT_FOR_MHD 1
82 #define HTML_HDR_CONTENT "Content-Type: text/html\r\n"
83 #define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
84 #define RE_N_MATCHES 4
87 #define HTTPS_PORT 443
92 struct ProxyCurlTask *prev;
93 struct ProxyCurlTask *next;
97 char buffer[CURL_MAX_WRITE_SIZE];
100 unsigned int bytes_downloaded;
101 unsigned int bytes_in_buffer;
102 int download_in_progress;
103 int download_successful;
105 struct MHD_Connection *connection;
107 int is_postprocessing;
110 GNUNET_SCHEDULER_TaskIdentifier pp_task;
118 unsigned long port = GNUNET_GNS_PROXY_PORT;
119 static struct GNUNET_NETWORK_Handle *lsock;
120 GNUNET_SCHEDULER_TaskIdentifier ltask;
121 GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
122 static struct MHD_Daemon *httpd;
123 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
124 static CURLM *curl_multi;
126 static struct GNUNET_GNS_Handle *gns_handle;
128 static struct ProxyCurlTask *ctasks_head;
129 static struct ProxyCurlTask *ctasks_tail;
131 static regex_t re_dotplus;
134 * Read HTTP request header field 'Host'
136 * @param cls buffer to write to
137 * @param kind value kind
138 * @param key field key
139 * @param value field value
140 * @return MHD_NO when Host found
143 con_val_iter (void *cls,
144 enum MHD_ValueKind kind,
148 char* buf = (char*)cls;
150 if (0 == strcmp ("Host", key))
160 * Check HTTP response header for mime
162 * @param buffer curl buffer
163 * @param size curl blocksize
164 * @param nmemb curl blocknumber
166 * @return size of read bytes
169 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void *cls)
171 size_t bytes = size * nmemb;
172 struct ProxyCurlTask *ctask = cls;
175 memcpy (hdr, buffer, bytes);
178 if (0 == strcmp (hdr, HTML_HDR_CONTENT))
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
181 "Got HTML HTTP response header\n");
182 ctask->parse_content = GNUNET_YES;
196 * Process cURL download bits
198 * @param ptr buffer with data
199 * @param size size of a record
200 * @param nmemb number of records downloaded
202 * @return number of processed bytes
205 callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
207 const char *cbuf = ptr;
209 struct ProxyCurlTask *ctask = ctx;
214 ctask->bytes_downloaded += total;
221 if (total > sizeof (ctask->buffer))
223 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
224 "CURL gave us too much data to handle (%d)!\n",
229 if (ctask->buf_status == BUF_WAIT_FOR_MHD)
231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232 "CURL: Waiting for MHD (%s)\n", ctask->url);
233 return CURL_WRITEFUNC_PAUSE;
237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
238 "CURL: Copying to MHD (%s, %d)\n", ctask->url, total);
239 memcpy (ctask->buffer, cbuf, total);
240 ctask->bytes_in_buffer = total;
241 ctask->buffer_ptr = ctask->buffer;
243 ctask->buf_status = BUF_WAIT_FOR_MHD;
245 //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
246 // "cURL chunk:\n%s\n", (char*)ctask->buffer);
254 * Callback to free content
256 * @param cls content to free
259 mhd_content_free (void *cls)
261 struct ProxyCurlTask *ctask = cls;
263 if (ctask->curl != NULL)
264 curl_easy_cleanup (ctask->curl);
271 run_mhd (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
277 process_shorten (void* cls, const char* short_name)
279 struct ProxyCurlTask *ctask = cls;
281 char tmp[strlen(ctask->pp_buf)]; //TODO length
283 if (NULL == short_name)
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
286 "MHD PP: Unable to shorten %s\n",
291 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292 "MHD PP: Shorten %s -> %s\n",
296 sprintf (tmp, "<a href=http://%s", short_name);
297 strcpy (ctask->pp_buf, tmp);
299 ctask->pp_finished = GNUNET_YES;
301 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
305 postprocess_name (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
307 struct ProxyCurlTask *ctask = cls;
308 char tmp[strlen(ctask->pp_buf)];
312 sprintf ( tmp, "%s%s", ctask->pp_buf, ctask->authority);
314 GNUNET_GNS_shorten (gns_handle,
322 * Callback for MHD response
325 * @param pos in buffer
327 * @param max space in buffer
330 mhd_content_cb (void *cls,
335 struct ProxyCurlTask *ctask = cls;
337 size_t bytes_to_copy;
340 regmatch_t m[RE_N_MATCHES];
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "MHD: content cb\n");
345 if (ctask->download_successful &&
346 (ctask->buf_status == BUF_WAIT_FOR_CURL))
348 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
349 "MHD: sending response for %s\n", ctask->url);
350 ctask->download_in_progress = GNUNET_NO;
351 curl_multi_remove_handle (curl_multi, ctask->curl);
352 curl_easy_cleanup (ctask->curl);
353 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
354 return MHD_CONTENT_READER_END_OF_STREAM;
357 if (ctask->download_error &&
358 (ctask->buf_status == BUF_WAIT_FOR_CURL))
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
361 "MHD: sending error response\n");
362 ctask->download_in_progress = GNUNET_NO;
363 curl_multi_remove_handle (curl_multi, ctask->curl);
364 curl_easy_cleanup (ctask->curl);
365 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
366 return MHD_CONTENT_READER_END_WITH_ERROR;
369 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
372 bytes_to_copy = ctask->bytes_in_buffer;
374 if (ctask->parse_content == GNUNET_YES)
377 GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
378 "MHD: We need to parse the HTML %s\n", ctask->buffer_ptr);
380 nomatch = regexec ( &re_dotplus, ctask->buffer_ptr, RE_N_MATCHES, m, 0);
384 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
387 GNUNET_assert (m[1].rm_so != -1);
389 hostptr = ctask->buffer_ptr+m[1].rm_so;
393 bytes_to_copy = m[0].rm_so;
394 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
395 "Copying %d bytes.\n", m[0].rm_so);
401 if (ctask->is_postprocessing == GNUNET_YES)
405 if ( ctask->pp_finished == GNUNET_NO )
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408 "MHD PP: Waiting for PP of %s\n", ctask->pp_buf);
412 ctask->is_postprocessing = GNUNET_NO;
414 ctask->bytes_in_buffer -= m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
415 ctask->buffer_ptr += m[0].rm_eo;//(m[1].rm_eo-m[1].rm_so);
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
417 "Skipping next %d bytes in buffer\n", m[0].rm_eo);
419 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
421 if ( strlen (ctask->pp_buf) <= max )
423 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424 "Copying postprocessed %s.\n", ctask->pp_buf);
425 memcpy ( buf, ctask->pp_buf, strlen (ctask->pp_buf) );
426 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
428 ctask->is_postprocessing = GNUNET_NO;
429 return strlen (ctask->pp_buf);
435 memset (ctask->pp_buf, 0, sizeof(ctask->pp_buf));
436 memcpy (ctask->pp_buf, hostptr, (m[1].rm_eo-m[1].rm_so));
438 ctask->is_postprocessing = GNUNET_YES;
439 ctask->pp_finished = GNUNET_NO;
441 //postprocess_name(ctask, NULL);
442 ctask->pp_task = GNUNET_SCHEDULER_add_now (&postprocess_name, ctask);
449 if ( bytes_to_copy > max )
451 GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
452 "MHD: buffer in response too small! (%s)\n",
454 memcpy ( buf, ctask->buffer_ptr, max);
455 ctask->bytes_in_buffer -= max;
456 ctask->buffer_ptr += max;
461 GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
462 "MHD: copying %d bytes to mhd response at offset %d\n",
465 memcpy ( buf, ctask->buffer_ptr, bytes_to_copy );
466 copied = bytes_to_copy;
467 if (bytes_to_copy < ctask->bytes_in_buffer)
469 ctask->bytes_in_buffer -= bytes_to_copy;
470 ctask->buffer_ptr += bytes_to_copy;
474 ctask->bytes_in_buffer = 0;
475 ctask->buf_status = BUF_WAIT_FOR_CURL;
476 ctask->buffer_ptr = ctask->buffer;
477 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
478 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
482 GNUNET_SCHEDULER_add_now (&run_mhd, NULL);
490 * Task that is run when we are ready to receive more data
494 * @param tc task context
497 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
500 * Ask cURL for the select sets and schedule download
503 curl_download_prepare ()
510 struct GNUNET_NETWORK_FDSet *grs;
511 struct GNUNET_NETWORK_FDSet *gws;
513 struct GNUNET_TIME_Relative rtime;
519 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
521 if (mret != CURLM_OK)
523 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
524 "%s failed at %s:%d: `%s'\n",
525 "curl_multi_fdset", __FILE__, __LINE__,
526 curl_multi_strerror (mret));
531 mret = curl_multi_timeout (curl_multi, &to);
532 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
534 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
535 "cURL multi fds: max=%d timeout=%llu\n", max, to);
537 grs = GNUNET_NETWORK_fdset_create ();
538 gws = GNUNET_NETWORK_fdset_create ();
539 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
540 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
541 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
542 "Scheduling task cURL\n");
544 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
545 GNUNET_SCHEDULER_cancel (curl_download_task);
548 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
551 &curl_task_download, curl_multi);
552 GNUNET_NETWORK_fdset_destroy (gws);
553 GNUNET_NETWORK_fdset_destroy (grs);
559 * Task that is run when we are ready to receive more data
563 * @param tc task context
566 curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
572 struct ProxyCurlTask *ctask;
575 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
577 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
580 "Shutdown requested while trying to download\n");
584 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592 mret = curl_multi_perform (curl_multi, &running);
594 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
595 "Running curl tasks: %d\n", running);
598 for (; ctask != NULL; ctask = ctask->next)
600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 "CTask: %s\n", ctask->url);
605 if (num_ctasks != running)
607 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
608 "%d ctasks, %d curl running\n", num_ctasks, running);
614 msg = curl_multi_info_read (curl_multi, &msgnum);
615 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
616 "Messages left: %d\n", msgnum);
623 if ((msg->data.result != CURLE_OK) &&
624 (msg->data.result != CURLE_GOT_NOTHING))
626 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
627 "Download curl failed");
629 for (; ctask != NULL; ctask = ctask->next)
631 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
634 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
635 "Download curl failed for task %s: %s.\n",
637 curl_easy_strerror (msg->data.result));
638 ctask->download_successful = GNUNET_NO;
639 ctask->download_error = GNUNET_YES;
640 //curl_multi_remove_handle (curl_multi, ctask->curl);
641 //curl_easy_cleanup (ctask->curl);
642 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
646 GNUNET_assert (ctask != NULL);
650 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
651 "cURL download completed.\n");
653 for (; ctask != NULL; ctask = ctask->next)
655 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) != 0)
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "cURL task %s found.\n", ctask->url);
660 ctask->download_successful = GNUNET_YES;
661 //curl_multi_remove_handle (curl_multi, ctask->curl);
662 //curl_easy_cleanup (ctask->curl);
663 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
667 GNUNET_assert (ctask != NULL);
669 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
670 "curl end %s\n", curl_easy_strerror(msg->data.result));
676 } while (msgnum > 0);
679 for (ctask=ctasks_head; ctask != NULL; ctask = ctask->next)
684 if (num_ctasks != running)
686 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
687 "%d ctasks, %d curl running\n", num_ctasks, running);
690 GNUNET_assert ( num_ctasks == running );
694 } while (mret == CURLM_CALL_MULTI_PERFORM);
697 if (mret != CURLM_OK)
699 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s failed at %s:%d: `%s'\n",
700 "curl_multi_perform", __FILE__, __LINE__,
701 curl_multi_strerror (mret));
703 curl_download_prepare();
707 * Initialize download and trigger curl
711 process_get_authority (void* cls,
712 const char* auth_name)
714 struct ProxyCurlTask *ctask = cls;
716 if (NULL == auth_name)
718 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
719 "Get authority failed!\n");
720 strcpy (ctask->authority, "");
723 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724 "Get authority yielded %s\n", auth_name);
725 strcpy (ctask->authority, auth_name);
727 curl_download_prepare ();
731 * Main MHD callback for handling requests.
734 * @param con MHD connection handle
735 * @param meth the HTTP method used ("GET", "PUT", etc.)
736 * @param ver the HTTP version string (i.e. "HTTP/1.1")
737 * @param upload_data the data being uploaded (excluding HEADERS,
738 * for a POST that fits into memory and that is encoded
739 * with a supported encoding, the POST data will NOT be
740 * given in upload_data and is instead available as
741 * part of MHD_get_connection_values; very large POST
742 * data *will* be made available incrementally in
744 * @param upload_data_size set initially to the size of the
745 * upload_data provided; the method must update this
746 * value to the number of bytes NOT processed;
747 * @param con_cls pointer to location where we store the 'struct Request'
748 * @return MHD_YES if the connection was handled successfully,
749 * MHD_NO if the socket must be closed due to a serious
750 * error while handling the request
753 create_response (void *cls,
754 struct MHD_Connection *con,
758 const char *upload_data,
759 size_t *upload_data_size,
763 const char* page = "<html><head><title>gnoxy</title>"\
764 "</head><body>cURL fail</body></html>";
765 struct MHD_Response *response;
771 struct ProxyCurlTask *ctask;
773 if (0 != strcmp (meth, "GET"))
775 if (&dummy != *con_cls)
781 if (0 != *upload_data_size)
786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
789 MHD_get_connection_values (con,
791 &con_val_iter, host);
795 ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
796 ctask->curl = curl_easy_init();
798 if (curl_multi == NULL)
799 curl_multi = curl_multi_init ();
801 if ((ctask->curl == NULL) || (curl_multi == NULL))
803 response = MHD_create_response_from_buffer (strlen (page),
805 MHD_RESPMEM_PERSISTENT);
806 ret = MHD_queue_response (con,
809 MHD_destroy_response (response);
814 ctask->download_in_progress = GNUNET_YES;
815 ctask->download_successful = GNUNET_NO;
816 ctask->bytes_downloaded = 0;
817 ctask->connection = con;
818 ctask->buf_status = BUF_WAIT_FOR_CURL;
819 ctask->bytes_in_buffer = 0;
820 ctask->parse_content = GNUNET_NO;
822 curl_easy_setopt (ctask->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
823 curl_easy_setopt (ctask->curl, CURLOPT_HEADERDATA, ctask);
824 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
825 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
826 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
827 curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
828 /* no need to abort if the above failed */
829 sprintf (curlurl, "http://%s%s", host, url);
830 strcpy (ctask->host, host);
831 strcpy (ctask->url, curlurl);
832 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
833 "Adding new curl task for %s\n", curlurl);
835 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
836 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
837 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 600L);
838 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 600L);
840 mret = curl_multi_add_handle (curl_multi, ctask->curl);
842 if (mret != CURLM_OK)
844 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
845 "%s failed at %s:%d: `%s'\n",
846 "curl_multi_add_handle", __FILE__, __LINE__,
847 curl_multi_strerror (mret));
848 response = MHD_create_response_from_buffer (strlen (page),
850 MHD_RESPMEM_PERSISTENT);
851 ret = MHD_queue_response (con,
854 MHD_destroy_response (response);
856 curl_easy_cleanup (ctask->curl);
861 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
863 GNUNET_GNS_get_authority (gns_handle,
865 &process_get_authority,
867 //download_prepare (ctask);
868 //curl_download_prepare ();
870 response = MHD_create_response_from_callback (-1, -1,
875 ret = MHD_queue_response (con, MHD_HTTP_OK, response);
877 //MHD_destroy_response (response);
883 * Task run whenever HTTP server operations are pending.
886 * @param tc sched context
890 const struct GNUNET_SCHEDULER_TaskContext *tc);
902 struct GNUNET_NETWORK_FDSet *wrs;
903 struct GNUNET_NETWORK_FDSet *wws;
904 struct GNUNET_NETWORK_FDSet *wes;
907 unsigned MHD_LONG_LONG timeout;
908 struct GNUNET_TIME_Relative tv;
913 wrs = GNUNET_NETWORK_fdset_create ();
914 wes = GNUNET_NETWORK_fdset_create ();
915 wws = GNUNET_NETWORK_fdset_create ();
917 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
920 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
921 "MHD fds: max=%d\n", max);
923 haveto = MHD_get_timeout (httpd, &timeout);
925 if (haveto == MHD_YES)
926 tv.rel_value = (uint64_t) timeout;
928 tv = GNUNET_TIME_UNIT_FOREVER_REL;
929 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
930 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
931 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
933 if (httpd_task != GNUNET_SCHEDULER_NO_TASK)
934 GNUNET_SCHEDULER_cancel (httpd_task);
936 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
939 GNUNET_NETWORK_fdset_destroy (wrs);
940 GNUNET_NETWORK_fdset_destroy (wws);
941 GNUNET_NETWORK_fdset_destroy (wes);
945 * Task run whenever HTTP server operations are pending.
948 * @param tc sched context
952 const struct GNUNET_SCHEDULER_TaskContext *tc)
954 httpd_task = GNUNET_SCHEDULER_NO_TASK;
955 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962 * Read data from socket
964 * @param cls the closure
965 * @param tc scheduler context
968 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
971 * Read from remote end
974 * @param tc scheduler context
977 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
980 * Write data to remote socket
982 * @param cls the closure
983 * @param tc scheduler context
986 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
988 struct Socks5Request *s5r = cls;
991 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
993 if ((NULL != tc->read_ready) &&
994 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
995 ((len = GNUNET_NETWORK_socket_send (s5r->remote_sock, s5r->rbuf,
998 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
999 "Successfully sent %d bytes to remote socket\n",
1004 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
1006 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1007 GNUNET_SCHEDULER_cancel (s5r->rtask);
1008 if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1009 GNUNET_SCHEDULER_cancel (s5r->wtask);
1010 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1011 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1012 GNUNET_NETWORK_socket_close (s5r->remote_sock);
1013 GNUNET_NETWORK_socket_close (s5r->sock);
1019 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1026 * Write data to socket
1028 * @param cls the closure
1029 * @param tc scheduler context
1032 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1034 struct Socks5Request *s5r = cls;
1037 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1039 if ((NULL != tc->read_ready) &&
1040 (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
1041 ((len = GNUNET_NETWORK_socket_send (s5r->sock, s5r->wbuf,
1044 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045 "Successfully sent %d bytes to socket\n",
1051 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
1053 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1054 GNUNET_SCHEDULER_cancel (s5r->rtask);
1055 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1056 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1057 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1058 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1059 GNUNET_NETWORK_socket_close (s5r->remote_sock);
1060 GNUNET_NETWORK_socket_close (s5r->sock);
1065 if ((s5r->state == SOCKS5_DATA_TRANSFER) &&
1066 (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK))
1068 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1070 &do_read_remote, s5r);
1074 * Read from remote end
1076 * @param cls closure
1077 * @param tc scheduler context
1080 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1082 struct Socks5Request *s5r = cls;
1084 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1087 if ((NULL != tc->write_ready) &&
1088 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
1089 (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
1090 sizeof (s5r->wbuf))))
1092 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1093 "Successfully read %d bytes from remote socket\n",
1098 if (s5r->wbuf_len == 0)
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100 "0 bytes received from remote... graceful shutdown!\n");
1101 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1102 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1103 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1104 GNUNET_SCHEDULER_cancel (s5r->rtask);
1106 GNUNET_NETWORK_socket_close (s5r->remote_sock);
1107 s5r->remote_sock = NULL;
1108 GNUNET_NETWORK_socket_close (s5r->sock);
1114 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1122 add_handle_to_mhd (struct GNUNET_NETWORK_Handle *h)
1125 struct sockaddr *addr;
1128 fd = GNUNET_NETWORK_get_fd (h);
1129 addr = GNUNET_NETWORK_get_addr (h);
1130 len = GNUNET_NETWORK_get_addrlen (h);
1132 return MHD_add_connection (httpd, fd, addr, len);
1136 * Read data from incoming connection
1138 * @param cls the closure
1139 * @param tc the scheduler context
1142 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1144 struct Socks5Request *s5r = cls;
1145 struct socks5_client_hello *c_hello;
1146 struct socks5_server_hello *s_hello;
1147 struct socks5_client_request *c_req;
1148 struct socks5_server_response *s_resp;
1153 struct hostent *phost;
1155 struct sockaddr_in remote_addr;
1156 struct in_addr *r_sin_addr;
1158 s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
1160 if ((NULL != tc->write_ready) &&
1161 (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
1162 (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
1163 sizeof (s5r->rbuf))))
1165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1166 "Successfully read %d bytes from socket\n",
1171 if (s5r->rbuf_len != 0)
1172 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
1174 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
1176 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1177 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1178 if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
1179 GNUNET_SCHEDULER_cancel (s5r->wtask);
1180 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1181 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1182 GNUNET_NETWORK_socket_close (s5r->remote_sock);
1183 GNUNET_NETWORK_socket_close (s5r->sock);
1188 if (s5r->state == SOCKS5_INIT)
1190 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1192 c_hello = (struct socks5_client_hello*)&s5r->rbuf;
1194 GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
1196 s_hello = (struct socks5_server_hello*)&s5r->wbuf;
1197 s5r->wbuf_len = sizeof( struct socks5_server_hello );
1199 s_hello->version = c_hello->version;
1200 s_hello->auth_method = SOCKS_AUTH_NONE;
1202 /* Write response to client */
1203 s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1207 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1211 s5r->state = SOCKS5_REQUEST;
1215 if (s5r->state == SOCKS5_REQUEST)
1217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1218 "Processing SOCKS5 request\n");
1219 c_req = (struct socks5_client_request*)&s5r->rbuf;
1220 s_resp = (struct socks5_server_response*)&s5r->wbuf;
1221 //Only 10byte for ipv4 response!
1222 s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
1224 GNUNET_assert (c_req->addr_type == 3);
1226 dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
1227 memset(domain, 0, sizeof(domain));
1228 strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
1229 req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
1231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1232 "Requested connection is %s:%d\n",
1236 if (is_tld (domain, GNUNET_GNS_TLD) ||
1237 is_tld (domain, GNUNET_GNS_TLD_ZKEY))
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1240 "Requested connection is gnunet tld\n",
1245 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1246 _("Failed to start HTTP server\n"));
1247 s_resp->version = 0x05;
1248 s_resp->reply = 0x01;
1250 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1254 //TODO! close socket after the write! schedule task
1255 //GNUNET_NETWORK_socket_close (s5r->sock);
1260 if (MHD_YES == add_handle_to_mhd ( s5r->sock ))
1261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1262 "Sucessfully added client to MHD!\n");
1263 s_resp->version = 0x05;
1264 s_resp->reply = 0x00;
1265 s_resp->reserved = 0x00;
1266 s_resp->addr_type = 0x01;
1269 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1273 //GNUNET_free ( s5r );
1274 //FIXME complete socks resp!
1279 phost = (struct hostent*)gethostbyname (domain);
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283 "Resolve %s error!\n", domain );
1284 s_resp->version = 0x05;
1285 s_resp->reply = 0x01;
1287 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1291 //TODO! close socket after the write! schedule task
1292 //GNUNET_NETWORK_socket_close (s5r->sock);
1297 s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
1300 r_sin_addr = (struct in_addr*)(phost->h_addr);
1301 remote_ip = r_sin_addr->s_addr;
1302 memset(&remote_addr, 0, sizeof(remote_addr));
1303 remote_addr.sin_family = AF_INET;
1304 #if HAVE_SOCKADDR_IN_SIN_LEN
1305 remote_addr.sin_len = sizeof (remote_addr);
1307 remote_addr.sin_addr.s_addr = remote_ip;
1308 remote_addr.sin_port = req_port;
1310 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1311 "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
1315 GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
1316 (const struct sockaddr*)&remote_addr,
1317 sizeof (remote_addr)))
1318 && (errno != EINPROGRESS))
1320 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1322 "socket request error...\n");
1323 s_resp->version = 0x05;
1324 s_resp->reply = 0x01;
1326 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1334 "new remote connection\n");
1336 s_resp->version = 0x05;
1337 s_resp->reply = 0x00;
1338 s_resp->reserved = 0x00;
1339 s_resp->addr_type = 0x01;
1341 s5r->state = SOCKS5_DATA_TRANSFER;
1344 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1348 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1356 if (s5r->state == SOCKS5_DATA_TRANSFER)
1358 if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
1360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1361 "Closing connection to client\n");
1362 if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
1363 GNUNET_SCHEDULER_cancel (s5r->rtask);
1364 if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
1365 GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
1366 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1367 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1368 if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
1369 GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
1371 if (s5r->remote_sock != NULL)
1372 GNUNET_NETWORK_socket_close (s5r->remote_sock);
1373 GNUNET_NETWORK_socket_close (s5r->sock);
1378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1379 "forwarding %d bytes from client\n", s5r->rbuf_len);
1382 GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
1384 &do_write_remote, s5r);
1386 if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
1389 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1391 &do_read_remote, s5r);
1397 //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
1402 * Accept new incoming connections
1404 * @param cls the closure
1405 * @param tc the scheduler context
1408 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1410 struct GNUNET_NETWORK_Handle *s;
1411 struct Socks5Request *s5r;
1413 ltask = GNUNET_SCHEDULER_NO_TASK;
1414 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1417 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1421 s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
1425 GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
1429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1430 "Got an inbound connection, waiting for data\n");
1432 s5r = GNUNET_malloc (sizeof (struct Socks5Request));
1434 s5r->state = SOCKS5_INIT;
1435 s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
1436 s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
1437 s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
1438 s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1441 //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
1445 * Task run on shutdown
1447 * @param cls closure
1448 * @param tc task context
1451 do_shutdown (void *cls,
1452 const struct GNUNET_SCHEDULER_TaskContext *tc)
1454 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
1456 GNUNET_SCHEDULER_cancel (httpd_task);
1457 httpd_task = GNUNET_SCHEDULER_NO_TASK;
1460 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
1462 GNUNET_SCHEDULER_cancel (curl_download_task);
1463 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1468 MHD_stop_daemon (httpd);
1474 * Compiles a regex for us
1476 * @param re ptr to re struct
1477 * @param rt the expression to compile
1478 * @return 0 on success
1481 compile_regex (regex_t *re, const char* rt)
1486 status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
1489 regerror (status, re, err, 1024);
1490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1491 "Regex error compiling '%s': %s\n", rt, err);
1498 * Main function that will be run
1500 * @param cls closure
1501 * @param args remaining command-line arguments
1502 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1503 * @param cfg configuration
1506 run (void *cls, char *const *args, const char *cfgfile,
1507 const struct GNUNET_CONFIGURATION_Handle *cfg)
1509 struct sockaddr_in sa;
1511 compile_regex (&re_dotplus, (char*) RE_DOTPLUS);
1513 gns_handle = GNUNET_GNS_connect (cfg);
1515 if (NULL == gns_handle)
1517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1518 "Unable to connect to GNS!\n");
1522 memset (&sa, 0, sizeof (sa));
1523 sa.sin_family = AF_INET;
1524 sa.sin_port = htons (port);
1525 #if HAVE_SOCKADDR_IN_SIN_LEN
1526 sa.sin_len = sizeof (sa);
1529 lsock = GNUNET_NETWORK_socket_create (AF_INET,
1533 if ((NULL == lsock) ||
1535 GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
1538 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1539 "Failed to create listen socket bound to `%s'",
1540 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1542 GNUNET_NETWORK_socket_close (lsock);
1546 if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
1548 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1549 "Failed to listen on socket bound to `%s'",
1550 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
1554 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1555 lsock, &do_accept, NULL);
1560 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1562 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1563 "cURL global init failed!\n");
1567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1568 "Proxy listens on port %u\n",
1571 httpd = MHD_start_daemon (MHD_USE_DEBUG, 4444,
1573 &create_response, NULL,
1574 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1575 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1576 MHD_OPTION_NOTIFY_COMPLETED,
1581 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1582 &do_shutdown, NULL);
1587 * The main function for gnunet-gns-proxy.
1589 * @param argc number of arguments from the command line
1590 * @param argv command line arguments
1591 * @return 0 ok, 1 on error
1594 main (int argc, char *const *argv)
1596 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1598 gettext_noop ("listen on specified port"), 1,
1599 &GNUNET_GETOPT_set_string, &port},
1600 GNUNET_GETOPT_OPTION_END
1605 GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
1608 GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
1609 _("GNUnet GNS proxy"),
1611 &run, NULL)) ? 0 : 1;