aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Schanzenbach <mschanzenbach@posteo.de>2012-06-02 18:07:33 +0000
committerMartin Schanzenbach <mschanzenbach@posteo.de>2012-06-02 18:07:33 +0000
commit42ae31239de393ac4ebd76f90c53f1f0d12b6a5a (patch)
tree52d0115763aa6957fc56af76df88de982e5328f1 /src
parentde7d8184873d8b25dd81cfcff2d1f3df77060ee8 (diff)
downloadgnunet-42ae31239de393ac4ebd76f90c53f1f0d12b6a5a.tar.gz
gnunet-42ae31239de393ac4ebd76f90c53f1f0d12b6a5a.zip
-mhd curl working, needs love
Diffstat (limited to 'src')
-rw-r--r--src/gns/Makefile.am2
-rw-r--r--src/gns/gnunet-gns-proxy.c460
2 files changed, 451 insertions, 11 deletions
diff --git a/src/gns/Makefile.am b/src/gns/Makefile.am
index 10b9ff3fa..277d5e323 100644
--- a/src/gns/Makefile.am
+++ b/src/gns/Makefile.am
@@ -207,7 +207,7 @@ gnunet_gns_DEPENDENCIES = \
207 207
208gnunet_gns_proxy_SOURCES = \ 208gnunet_gns_proxy_SOURCES = \
209 gnunet-gns-proxy.c gns_proxy_proto.h 209 gnunet-gns-proxy.c gns_proxy_proto.h
210gnunet_gns_proxy_LDADD = -lmicrohttpd \ 210gnunet_gns_proxy_LDADD = -lmicrohttpd -lcurl \
211 $(top_builddir)/src/gns/libgnunetgns.la \ 211 $(top_builddir)/src/gns/libgnunetgns.la \
212 $(top_builddir)/src/util/libgnunetutil.la \ 212 $(top_builddir)/src/util/libgnunetutil.la \
213 $(GN_LIBINTL) 213 $(GN_LIBINTL)
diff --git a/src/gns/gnunet-gns-proxy.c b/src/gns/gnunet-gns-proxy.c
index 876f53194..0f5246914 100644
--- a/src/gns/gnunet-gns-proxy.c
+++ b/src/gns/gnunet-gns-proxy.c
@@ -21,6 +21,7 @@
21#include "platform.h" 21#include "platform.h"
22#include <gnunet_util_lib.h> 22#include <gnunet_util_lib.h>
23#include <microhttpd.h> 23#include <microhttpd.h>
24#include <curl/curl.h>
24#include "gns_proxy_proto.h" 25#include "gns_proxy_proto.h"
25#include "gns.h" 26#include "gns.h"
26 27
@@ -73,11 +74,38 @@ struct Socks5Request
73 unsigned int wbuf_len; 74 unsigned int wbuf_len;
74}; 75};
75 76
77
78#define BUF_WAIT_FOR_CURL 0
79#define BUF_WAIT_FOR_MHD 1
80
81struct ProxyCurlTask
82{
83 //DLL
84 struct ProxyCurlTask *prev;
85 struct ProxyCurlTask *next;
86
87 CURL *curl;
88 char buffer[CURL_MAX_WRITE_SIZE];
89 int buf_status;
90 unsigned int bytes_downloaded;
91 unsigned int bytes_in_buffer;
92 int download_in_progress;
93 int download_successful;
94 int download_error;
95 struct MHD_Connection *connection;
96
97};
98
76unsigned long port = GNUNET_GNS_PROXY_PORT; 99unsigned long port = GNUNET_GNS_PROXY_PORT;
77static struct GNUNET_NETWORK_Handle *lsock; 100static struct GNUNET_NETWORK_Handle *lsock;
78GNUNET_SCHEDULER_TaskIdentifier ltask; 101GNUNET_SCHEDULER_TaskIdentifier ltask;
102GNUNET_SCHEDULER_TaskIdentifier curl_download_task;
79static struct MHD_Daemon *httpd; 103static struct MHD_Daemon *httpd;
80static GNUNET_SCHEDULER_TaskIdentifier httpd_task; 104static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
105CURLM *curl_multi;
106
107struct ProxyCurlTask *ctasks_head;
108struct ProxyCurlTask *ctasks_tail;
81 109
82static int 110static int
83con_val_iter (void *cls, 111con_val_iter (void *cls,
@@ -86,8 +114,6 @@ con_val_iter (void *cls,
86 const char *value) 114 const char *value)
87{ 115{
88 char* buf = (char*)cls; 116 char* buf = (char*)cls;
89 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
90 "%s:%s\n", key, value);
91 117
92 if (0 == strcmp ("Host", key)) 118 if (0 == strcmp ("Host", key))
93 { 119 {
@@ -98,6 +124,306 @@ con_val_iter (void *cls,
98} 124}
99 125
100/** 126/**
127 * Process cURL download bits
128 *
129 * @param ptr buffer with data
130 * @param size size of a record
131 * @param nmemb number of records downloaded
132 * @param ctx context
133 * @return number of processed bytes
134 */
135static size_t
136callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
137{
138 const char *cbuf = ptr;
139 size_t total;
140 struct ProxyCurlTask *ctask = ctx;
141
142 total = size*nmemb;
143 ctask->bytes_downloaded += total;
144
145 if (total == 0)
146 {
147 return total;
148 }
149
150 if (total > sizeof (ctask->buffer))
151 {
152 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
153 "cURL gave us too much data to handle (%d)!\n",
154 total);
155 return 0;
156 }
157
158 if (ctask->buf_status == BUF_WAIT_FOR_MHD)
159 {
160 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161 "Waiting for MHD\n");
162 return CURL_WRITEFUNC_PAUSE;
163 }
164
165 memcpy (ctask->buffer, cbuf, total);
166 ctask->bytes_in_buffer = total;
167
168 ctask->buf_status = BUF_WAIT_FOR_MHD;
169
170 //GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
171 // "cURL chunk:\n%s\n", (char*)ctask->buffer);
172 MHD_run (httpd);
173 return total;
174}
175
176/**
177 * Callback for MHD response
178 *
179 * @param cls closure
180 * @param pos in buffer
181 * @param buf buffer
182 * @param max space in buffer
183 */
184static ssize_t
185mhd_content_cb (void *cls,
186 uint64_t pos,
187 char* buf,
188 size_t max)
189{
190 struct ProxyCurlTask *ctask = cls;
191
192 if (ctask->download_successful &&
193 (ctask->buf_status == BUF_WAIT_FOR_CURL))
194 {
195 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
196 "MHD: sending response\n");
197 ctask->download_in_progress = GNUNET_NO;
198 return MHD_CONTENT_READER_END_OF_STREAM;
199 }
200
201 if (ctask->download_error &&
202 (ctask->buf_status == BUF_WAIT_FOR_CURL))
203 {
204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205 "MHD: error sending response\n");
206 ctask->download_in_progress = GNUNET_NO;
207 return MHD_CONTENT_READER_END_WITH_ERROR;
208 }
209
210 if ( ctask->buf_status == BUF_WAIT_FOR_CURL )
211 {
212 return 0;
213 }
214
215 if ( ctask->bytes_in_buffer > max )
216 {
217 GNUNET_log ( GNUNET_ERROR_TYPE_ERROR,
218 "MHD: buffer in response too small!\n");
219 return MHD_CONTENT_READER_END_WITH_ERROR;
220 }
221
222 if ( 0 != ctask->bytes_in_buffer )
223 {
224 GNUNET_log ( GNUNET_ERROR_TYPE_DEBUG,
225 "MHD: copying %d bytes to mhd response at offset %d\n",
226 ctask->bytes_in_buffer, pos);
227 memcpy ( buf, ctask->buffer, ctask->bytes_in_buffer );
228 }
229
230 ctask->buf_status = BUF_WAIT_FOR_CURL;
231 curl_easy_pause (ctask->curl, CURLPAUSE_CONT);
232
233 return ctask->bytes_in_buffer;
234}
235
236
237/**
238 * schedule mhd
239 */
240static void
241run_httpd (void);
242
243/**
244 * Task that is run when we are ready to receive more data
245 * from curl
246 *
247 * @param cls closure
248 * @param tc task context
249 */
250static void
251curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
252
253/**
254 * Ask cURL for the select sets and schedule download
255 */
256static void
257curl_download_prepare ()
258{
259 CURLMcode mret;
260 fd_set rs;
261 fd_set ws;
262 fd_set es;
263 int max;
264 struct GNUNET_NETWORK_FDSet *grs;
265 struct GNUNET_NETWORK_FDSet *gws;
266 long to;
267 struct GNUNET_TIME_Relative rtime;
268
269 max = -1;
270 FD_ZERO (&rs);
271 FD_ZERO (&ws);
272 FD_ZERO (&es);
273 mret = curl_multi_fdset (curl_multi, &rs, &ws, &es, &max);
274
275 if (mret != CURLM_OK)
276 {
277 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
278 "%s failed at %s:%d: `%s'\n",
279 "curl_multi_fdset", __FILE__, __LINE__,
280 curl_multi_strerror (mret));
281 //TODO cleanup here?
282 return;
283 }
284
285 mret = curl_multi_timeout (curl_multi, &to);
286 rtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
287
288 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289 "cURL multi fds: max=%d\n", max);
290
291 grs = GNUNET_NETWORK_fdset_create ();
292 gws = GNUNET_NETWORK_fdset_create ();
293 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
294 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296 "Scheduling task cURL\n");
297
298 if (curl_download_task != GNUNET_SCHEDULER_NO_TASK)
299 GNUNET_SCHEDULER_cancel (curl_download_task);
300
301 curl_download_task =
302 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
303 rtime,
304 grs, gws,
305 &curl_task_download, curl_multi);
306 GNUNET_NETWORK_fdset_destroy (gws);
307 GNUNET_NETWORK_fdset_destroy (grs);
308
309}
310
311
312/**
313 * Task that is run when we are ready to receive more data
314 * from curl
315 *
316 * @param cls closure
317 * @param tc task context
318 */
319static void
320curl_task_download (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
321{
322 int running;
323 int msgnum;
324 struct CURLMsg *msg;
325 CURLMcode mret;
326 struct ProxyCurlTask *ctask;
327
328 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
329
330 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
331 {
332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
333 "Shutdown requested while trying to download\n");
334 //TODO cleanup
335 return;
336 }
337 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338 "Ready to dl\n");
339
340 do
341 {
342 running = 0;
343
344 mret = curl_multi_perform (curl_multi, &running);
345
346 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347 "Running curl tasks: %d\n", running);
348 do
349 {
350 ctask = ctasks_head;
351 msg = curl_multi_info_read (curl_multi, &msgnum);
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Messages left: %d\n", msgnum);
354
355 if (msg == NULL)
356 break;
357 switch (msg->msg)
358 {
359 case CURLMSG_DONE:
360 if ((msg->data.result != CURLE_OK) &&
361 (msg->data.result != CURLE_GOT_NOTHING))
362 {
363 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
364 "Download curl failed %s\n",
365 curl_easy_strerror (msg->data.result));
366
367 for (; ctask != NULL; ctask = ctask->next)
368 {
369 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) == 0)
370 {
371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372 "cURL task found.\n");
373 ctask->download_successful = GNUNET_NO;
374 ctask->download_error = GNUNET_YES;
375 curl_multi_remove_handle (curl_multi, ctask->curl);
376 curl_easy_cleanup (ctask->curl);
377 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
378 ctask);
379 break;
380 }
381 }
382 }
383 else
384 {
385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386 "cURL download completed.\n");
387
388 for (; ctask != NULL; ctask = ctask->next)
389 {
390 if (memcmp (msg->easy_handle, ctask->curl, sizeof (CURL)) == 0)
391 {
392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393 "cURL task found.\n");
394 ctask->download_successful = GNUNET_YES;
395 curl_multi_remove_handle (curl_multi, ctask->curl);
396 curl_easy_cleanup (ctask->curl);
397 GNUNET_CONTAINER_DLL_remove (ctasks_head, ctasks_tail,
398 ctask);
399 break;
400 }
401 else
402 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403 "cURL task skipped.\n");
404 }
405 run_httpd ();
406 //TODO iterate list, find ctask
407 }
408 break;
409 default:
410 break;
411 }
412 } while (msgnum > 0);
413 } while (mret == CURLM_CALL_MULTI_PERFORM);
414
415 if (mret != CURLM_OK)
416 {
417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s failed at %s:%d: `%s'\n",
418 "curl_multi_perform", __FILE__, __LINE__,
419 curl_multi_strerror (mret));
420 //TODO cleanup
421 }
422 curl_download_prepare();
423}
424
425
426/**
101 * Main MHD callback for handling requests. 427 * Main MHD callback for handling requests.
102 * 428 *
103 * @param cls unused 429 * @param cls unused
@@ -131,10 +457,14 @@ create_response (void *cls,
131{ 457{
132 static int dummy; 458 static int dummy;
133 const char* page = "<html><head><title>gnoxy</title>"\ 459 const char* page = "<html><head><title>gnoxy</title>"\
134 "</head><body>gnoxy demo</body></html>"; 460 "</head><body>cURL fail</body></html>";
135 struct MHD_Response *response; 461 struct MHD_Response *response;
136 char host[265]; 462 char host[265];
137 int ret; 463 char curlurl[512];
464 int ret = MHD_YES;
465
466 CURLMcode mret;
467 struct ProxyCurlTask *ctask;
138 468
139 if (0 != strcmp (meth, "GET")) 469 if (0 != strcmp (meth, "GET"))
140 return MHD_NO; 470 return MHD_NO;
@@ -156,13 +486,73 @@ create_response (void *cls,
156 MHD_HEADER_KIND, 486 MHD_HEADER_KIND,
157 &con_val_iter, host); 487 &con_val_iter, host);
158 488
159 response = MHD_create_response_from_buffer (strlen (page), 489
490 /* Do cURL */
491 ctask = GNUNET_malloc (sizeof (struct ProxyCurlTask));
492 ctask->curl = curl_easy_init();
493
494 if (curl_multi == NULL)
495 curl_multi = curl_multi_init ();
496
497 if ((ctask->curl == NULL) || (curl_multi == NULL))
498 {
499 response = MHD_create_response_from_buffer (strlen (page),
160 (void*)page, 500 (void*)page,
161 MHD_RESPMEM_PERSISTENT); 501 MHD_RESPMEM_PERSISTENT);
162 ret = MHD_queue_response (con, 502 ret = MHD_queue_response (con,
163 MHD_HTTP_OK, 503 MHD_HTTP_OK,
164 response); 504 response);
165 MHD_destroy_response (response); 505 MHD_destroy_response (response);
506 GNUNET_free (ctask);
507 return ret;
508 }
509
510 ctask->download_in_progress = GNUNET_YES;
511 ctask->download_successful = GNUNET_NO;
512 ctask->bytes_downloaded = 0;
513 ctask->connection = con;
514 ctask->buf_status = BUF_WAIT_FOR_CURL;
515 ctask->bytes_in_buffer = 0;
516
517 curl_easy_setopt (ctask->curl, CURLOPT_WRITEFUNCTION, &callback_download);
518 curl_easy_setopt (ctask->curl, CURLOPT_WRITEDATA, ctask);
519 curl_easy_setopt (ctask->curl, CURLOPT_FOLLOWLOCATION, 1);
520 curl_easy_setopt (ctask->curl, CURLOPT_MAXREDIRS, 4);
521 /* no need to abort if the above failed */
522 sprintf (curlurl, "http://%s%s", host, url);
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
524 "Adding new curl task for %s\n", curlurl);
525
526 curl_easy_setopt (ctask->curl, CURLOPT_URL, curlurl);
527 curl_easy_setopt (ctask->curl, CURLOPT_FAILONERROR, 1);
528 curl_easy_setopt (ctask->curl, CURLOPT_CONNECTTIMEOUT, 60L);
529 curl_easy_setopt (ctask->curl, CURLOPT_TIMEOUT, 60L);
530
531 mret = curl_multi_add_handle (curl_multi, ctask->curl);
532
533 if (mret != CURLM_OK)
534 {
535 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536 "%s failed at %s:%d: `%s'\n",
537 "curl_multi_add_handle", __FILE__, __LINE__,
538 curl_multi_strerror (mret));
539 curl_easy_cleanup (ctask->curl);
540 GNUNET_free (ctask);
541 //TODO maybe error display here
542 return ret;
543 }
544
545 GNUNET_CONTAINER_DLL_insert (ctasks_head, ctasks_tail, ctask);
546
547 curl_download_prepare ();
548
549 response = MHD_create_response_from_callback (-1, -1,
550 &mhd_content_cb,
551 ctask,
552 NULL); //TODO Destroy resp here
553
554 ret = MHD_queue_response (con, MHD_HTTP_OK, response);
555
166 return ret; 556 return ret;
167} 557}
168 558
@@ -176,8 +566,9 @@ static void
176do_httpd (void *cls, 566do_httpd (void *cls,
177 const struct GNUNET_SCHEDULER_TaskContext *tc); 567 const struct GNUNET_SCHEDULER_TaskContext *tc);
178 568
569
179/** 570/**
180 * Schedule MHD 571 * schedule mhd
181 */ 572 */
182static void 573static void
183run_httpd () 574run_httpd ()
@@ -201,6 +592,11 @@ run_httpd ()
201 wws = GNUNET_NETWORK_fdset_create (); 592 wws = GNUNET_NETWORK_fdset_create ();
202 max = -1; 593 max = -1;
203 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max)); 594 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
595
596
597 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
598 "MHD fds: max=%d\n", max);
599
204 haveto = MHD_get_timeout (httpd, &timeout); 600 haveto = MHD_get_timeout (httpd, &timeout);
205 601
206 if (haveto == MHD_YES) 602 if (haveto == MHD_YES)
@@ -233,6 +629,8 @@ do_httpd (void *cls,
233 const struct GNUNET_SCHEDULER_TaskContext *tc) 629 const struct GNUNET_SCHEDULER_TaskContext *tc)
234{ 630{
235 httpd_task = GNUNET_SCHEDULER_NO_TASK; 631 httpd_task = GNUNET_SCHEDULER_NO_TASK;
632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
633 "MHD run \n");
236 MHD_run (httpd); 634 MHD_run (httpd);
237 run_httpd (); 635 run_httpd ();
238} 636}
@@ -721,6 +1119,35 @@ do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
721} 1119}
722 1120
723/** 1121/**
1122 * Task run on shutdown
1123 *
1124 * @param cls closure
1125 * @param tc task context
1126 */
1127static void
1128do_shutdown (void *cls,
1129 const struct GNUNET_SCHEDULER_TaskContext *tc)
1130{
1131 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
1132 {
1133 GNUNET_SCHEDULER_cancel (httpd_task);
1134 httpd_task = GNUNET_SCHEDULER_NO_TASK;
1135 }
1136
1137 if (GNUNET_SCHEDULER_NO_TASK != curl_download_task)
1138 {
1139 GNUNET_SCHEDULER_cancel (curl_download_task);
1140 curl_download_task = GNUNET_SCHEDULER_NO_TASK;
1141 }
1142
1143 if (NULL != httpd)
1144 {
1145 MHD_stop_daemon (httpd);
1146 httpd = NULL;
1147 }
1148}
1149
1150/**
724 * Main function that will be run 1151 * Main function that will be run
725 * 1152 *
726 * @param cls closure 1153 * @param cls closure
@@ -769,6 +1196,16 @@ run (void *cls, char *const *args, const char *cfgfile,
769 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 1196 ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
770 lsock, &do_accept, NULL); 1197 lsock, &do_accept, NULL);
771 1198
1199 ctasks_head = NULL;
1200 ctasks_tail = NULL;
1201
1202 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
1203 {
1204 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1205 "cURL global init failed!\n");
1206 return;
1207 }
1208
772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
773 "Proxy listens on port %u\n", 1210 "Proxy listens on port %u\n",
774 port); 1211 port);
@@ -783,6 +1220,9 @@ run (void *cls, char *const *args, const char *cfgfile,
783 MHD_OPTION_END); 1220 MHD_OPTION_END);
784 run_httpd (); 1221 run_httpd ();
785 1222
1223 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1224 &do_shutdown, NULL);
1225
786} 1226}
787 1227
788/** 1228/**