-more
[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 "gns_proxy_proto.h"
24 #include "gns.h"
25
26 #define GNUNET_GNS_PROXY_PORT 7777
27
28 //TODO maybe make this an api call
29 /**
30  * Checks if name is in tld
31  *
32  * @param name the name to check
33  * @param tld the TLD to check for
34  * @return GNUNET_YES or GNUNET_NO
35  */
36 int
37 is_tld(const char* name, const char* tld)
38 {
39   int offset = 0;
40
41   if (strlen(name) <= strlen(tld))
42   {
43     return GNUNET_NO;
44   }
45
46   offset = strlen(name)-strlen(tld);
47   if (strcmp (name+offset, tld) != 0)
48   {
49     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
50                "%s is not in .%s TLD\n", name, tld);
51     return GNUNET_NO;
52   }
53
54   return GNUNET_YES;
55 }
56
57 struct Socks5Request
58 {
59   struct Socks5Request *prev;
60   struct Socks5Request *next;
61
62   struct GNUNET_NETWORK_Handle *sock;
63   struct GNUNET_NETWORK_Handle *remote_sock;
64
65   int state;
66
67   GNUNET_SCHEDULER_TaskIdentifier rtask;
68   GNUNET_SCHEDULER_TaskIdentifier fwdrtask;
69   GNUNET_SCHEDULER_TaskIdentifier wtask;
70   GNUNET_SCHEDULER_TaskIdentifier fwdwtask;
71
72   char rbuf[2048];
73   char wbuf[2048];
74   unsigned int rbuf_len;
75   unsigned int wbuf_len;
76 };
77
78 struct Socks5Connections
79 {
80   struct Socks5Request *head;
81   struct Socks5Request *tail;
82 };
83
84
85 unsigned long port = GNUNET_GNS_PROXY_PORT;
86 static struct GNUNET_NETWORK_Handle *lsock;
87 GNUNET_SCHEDULER_TaskIdentifier ltask;
88 static struct Socks5Connections s5conns;
89
90 /**
91  * Read data from socket
92  *
93  * @param cls the closure
94  * @param tc scheduler context
95  */
96 static void
97 do_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
98
99 /**
100  * Read from remote end
101  *
102  * @param cls closure
103  * @param tc scheduler context
104  */
105 static void
106 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
107
108 /**
109  * Write data to remote socket
110  *
111  * @param cls the closure
112  * @param tc scheduler context
113  */
114 static void
115 do_write_remote (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
116 {
117   struct Socks5Request *s5r = cls;
118   unsigned int len;
119
120   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
121
122   if ((NULL != tc->read_ready) &&
123       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->remote_sock)) &&
124       (len = GNUNET_NETWORK_socket_send (s5r->remote_sock, &s5r->rbuf,
125                                          s5r->rbuf_len)))
126   {
127     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
128                 "Successfully sent %d bytes to remote socket\n",
129                 len);
130   }
131   else
132   {
133     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write remote");
134     //Really!?!?!?
135     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
136       GNUNET_SCHEDULER_cancel (s5r->rtask);
137     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
138       GNUNET_SCHEDULER_cancel (s5r->wtask);
139     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
140       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
141     GNUNET_NETWORK_socket_close (s5r->remote_sock);
142     GNUNET_NETWORK_socket_close (s5r->sock);
143     GNUNET_free(s5r);
144     return;
145   }
146
147   s5r->rtask =
148     GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
149                                    s5r->sock,
150                                    &do_read, s5r);
151 }
152
153
154 /**
155  * Write data to socket
156  *
157  * @param cls the closure
158  * @param tc scheduler context
159  */
160 static void
161 do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
162 {
163   struct Socks5Request *s5r = cls;
164   unsigned int len = s5r->wbuf_len;
165
166   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
167
168   if ((NULL != tc->read_ready) &&
169       (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) &&
170       (len = GNUNET_NETWORK_socket_send (s5r->sock, &s5r->wbuf,
171                                          len)))
172   {
173     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
174                 "Successfully sent %d bytes to socket\n",
175                 len);
176   }
177   else
178   {
179     
180     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "write");
181     //Really!?!?!?
182     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
183       GNUNET_SCHEDULER_cancel (s5r->rtask);
184     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
185       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
186     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
187       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
188     GNUNET_NETWORK_socket_close (s5r->remote_sock);
189     GNUNET_NETWORK_socket_close (s5r->sock);
190     GNUNET_free(s5r);
191     return;
192   }
193
194   if (s5r->state == SOCKS5_DATA_TRANSFER)
195     s5r->fwdrtask =
196       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
197                                      s5r->remote_sock,
198                                      &do_read_remote, s5r);
199 }
200
201 /**
202  * Read from remote end
203  *
204  * @param cls closure
205  * @param tc scheduler context
206  */
207 static void
208 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
209 {
210   struct Socks5Request *s5r = cls;
211   
212   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
213
214
215   GNUNET_assert (NULL != tc->write_ready);
216   GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock));
217   
218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
219               "Read reason %d\n",
220               tc->reason);
221
222   if ((NULL != tc->write_ready) &&
223       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
224       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, &s5r->wbuf,
225                                          sizeof (s5r->wbuf))))
226   {
227     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228                 "Successfully read %d bytes from remote socket\n",
229                 s5r->wbuf_len);
230   }
231   else
232   {
233     if (s5r->wbuf_len == 0)
234       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
235                   "0 bytes received from remote... graceful shutdown!\n");
236     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
237       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
238     s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
239     
240     GNUNET_NETWORK_socket_close (s5r->remote_sock);
241     s5r->remote_sock = NULL;
242
243     return;
244   }
245   
246   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
247                                                s5r->sock,
248                                                &do_write, s5r);
249   
250 }
251
252
253
254 /**
255  * Read data from incoming connection
256  *
257  * @param cls the closure
258  * @param tc the scheduler context
259  */
260 static void
261 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
262 {
263   struct Socks5Request *s5r = cls;
264   struct socks5_client_hello *c_hello;
265   struct socks5_server_hello *s_hello;
266   struct socks5_client_request *c_req;
267   struct socks5_server_response *s_resp;
268
269   char domain[256];
270   uint8_t dom_len;
271   uint16_t req_port;
272   struct hostent *phost;
273   uint32_t remote_ip;
274   struct sockaddr_in remote_addr;
275   struct in_addr *r_sin_addr;
276
277   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
278
279   if ((NULL != tc->write_ready) &&
280       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
281       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, &s5r->rbuf,
282                                          sizeof (s5r->rbuf))))
283   {
284     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
285                 "Successfully read %d bytes from socket\n",
286                 s5r->rbuf_len);
287   }
288   else
289   {
290     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
291     //Really!?!?!?
292     if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
293       GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
294     if (s5r->wtask != GNUNET_SCHEDULER_NO_TASK)
295       GNUNET_SCHEDULER_cancel (s5r->wtask);
296     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
297       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
298     GNUNET_NETWORK_socket_close (s5r->remote_sock);
299     GNUNET_NETWORK_socket_close (s5r->sock);
300     GNUNET_free(s5r);
301     return;
302   }
303
304   if (s5r->state == SOCKS5_INIT)
305   {
306     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
307                 "SOCKS5 init\n");
308     c_hello = (struct socks5_client_hello*)&s5r->rbuf;
309
310     GNUNET_assert (c_hello->version == SOCKS_VERSION_5);
311
312     s_hello = (struct socks5_server_hello*)&s5r->wbuf;
313     s5r->wbuf_len = sizeof( struct socks5_server_hello );
314
315     s_hello->version = c_hello->version;
316     s_hello->auth_method = SOCKS_AUTH_NONE;
317
318     /* Write response to client */
319     s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
320                                                 s5r->sock,
321                                                 &do_write, s5r);
322
323     s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
324                                                 s5r->sock,
325                                                 &do_read, s5r);
326
327     s5r->state = SOCKS5_REQUEST;
328     return;
329   }
330
331   if (s5r->state == SOCKS5_REQUEST)
332   {
333     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
334                 "Processing SOCKS5 request\n");
335     c_req = (struct socks5_client_request*)&s5r->rbuf;
336     s_resp = (struct socks5_server_response*)&s5r->wbuf;
337     s5r->wbuf_len = sizeof (struct socks5_server_response);
338
339     GNUNET_assert (c_req->addr_type == 3);
340
341     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
342     memset(domain, 0, sizeof(domain));
343     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
344     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
345
346     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
347                 "Requested connection is %s:%d\n",
348                 domain,
349                 ntohs(req_port));
350
351     if (is_tld (domain, GNUNET_GNS_TLD) ||
352         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
353     {
354       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
355                   "Requested connection is gnunet tld\n",
356                   domain);
357     }
358     else
359     {
360       phost = (struct hostent*)gethostbyname (domain);
361       if (phost == NULL)
362       {
363         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
364                     "Resolve %s error!\n", domain );
365         s_resp->version = 0x05;
366         s_resp->reply = 0x01;
367         s5r->wtask = 
368           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
369                                           s5r->sock,
370                                           &do_write, s5r);
371         //ERROR!
372         //TODO! close socket after the write! schedule task
373         //GNUNET_NETWORK_socket_close (s5r->sock);
374         //GNUNET_free(s5r);
375         return;
376       }
377
378       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
379                                                        SOCK_STREAM,
380                                                        0);
381       r_sin_addr = (struct in_addr*)(phost->h_addr);
382       remote_ip = r_sin_addr->s_addr;
383       memset(&remote_addr, 0, sizeof(remote_addr));
384       remote_addr.sin_family = AF_INET;
385 #if HAVE_SOCKADDR_IN_SIN_LEN
386       remote_addr.sin_len = sizeof (remote_addr);
387 #endif
388       remote_addr.sin_addr.s_addr = remote_ip;
389       remote_addr.sin_port = req_port;
390       
391       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
393                   ntohs(req_port));
394
395       if ((GNUNET_OK !=
396           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
397                                           (const struct sockaddr*)&remote_addr,
398                                           sizeof (remote_addr)))
399           && (errno != EINPROGRESS))
400       {
401         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
402         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
403                     "socket request error...\n");
404         s_resp->version = 0x05;
405         s_resp->reply = 0x01;
406         s5r->wtask =
407           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
408                                           s5r->sock,
409                                           &do_write, s5r);
410         //TODO see above
411         return;
412       }
413
414       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
415                   "new remote connection\n");
416
417       s_resp->version = 0x05;
418       s_resp->reply = 0x00;
419       s_resp->reserved = 0x00;
420       s_resp->addr_type = 0x01;
421
422       s5r->state = SOCKS5_DATA_TRANSFER;
423
424       s5r->wtask =
425         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
426                                         s5r->sock,
427                                         &do_write, s5r);
428       s5r->rtask =
429         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
430                                        s5r->sock,
431                                        &do_read, s5r);
432
433     }
434     return;
435   }
436
437   if (s5r->state == SOCKS5_DATA_TRANSFER)
438   {
439     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
440     {
441       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442                   "Closing connection to client\n");
443       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
444         GNUNET_SCHEDULER_cancel (s5r->rtask);
445       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
446         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
447       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
448         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
449       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
450         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
451       
452       if (s5r->remote_sock != NULL)
453         GNUNET_NETWORK_socket_close (s5r->remote_sock);
454       GNUNET_NETWORK_socket_close (s5r->sock);
455       GNUNET_free(s5r);
456       return;
457     }
458
459     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
460                 "forwarding %d bytes from client\n", s5r->rbuf_len);
461
462     s5r->fwdwtask =
463       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
464                                       s5r->remote_sock,
465                                       &do_write_remote, s5r);
466
467     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
468     {
469       s5r->fwdrtask =
470         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
471                                        s5r->remote_sock,
472                                        &do_read_remote, s5r);
473     }
474
475
476   }
477
478   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
479
480 }
481
482 /**
483  * Accept new incoming connections
484  *
485  * @param cls the closure
486  * @param tc the scheduler context
487  */
488 static void
489 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
490 {
491   struct GNUNET_NETWORK_Handle *s;
492   struct Socks5Request *s5r;
493
494   ltask = GNUNET_SCHEDULER_NO_TASK;
495   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
496     return;
497
498   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
499                                          lsock,
500                                          &do_accept, NULL);
501
502   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
503
504   if (NULL == s)
505   {
506     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
507     return;
508   }
509
510   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511               "Got an inbound connection, waiting for data\n");
512
513   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
514   s5r->sock = s;
515   s5r->state = SOCKS5_INIT;
516   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
517   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
518   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
519   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
520                                               s5r->sock,
521                                               &do_read, s5r);
522   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
523 }
524
525 /**
526  * Main function that will be run
527  *
528  * @param cls closure
529  * @param args remaining command-line arguments
530  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
531  * @param cfg configuration
532  */
533 static void
534 run (void *cls, char *const *args, const char *cfgfile,
535      const struct GNUNET_CONFIGURATION_Handle *cfg)
536 {
537   struct sockaddr_in sa;
538
539   memset (&sa, 0, sizeof (sa));
540   sa.sin_family = AF_INET;
541   sa.sin_port = htons (port);
542 #if HAVE_SOCKADDR_IN_SIN_LEN
543   sa.sin_len = sizeof (sa);
544 #endif
545
546   lsock = GNUNET_NETWORK_socket_create (AF_INET,
547                                         SOCK_STREAM,
548                                         0);
549
550   if ((NULL == lsock) ||
551       (GNUNET_OK !=
552        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
553                                    sizeof (sa))))
554   {
555     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
556                 "Failed to create listen socket bound to `%s'",
557                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
558     if (NULL != lsock)
559       GNUNET_NETWORK_socket_close (lsock);
560     return;
561   }
562
563   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
564   {
565     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
566                 "Failed to listen on socket bound to `%s'",
567                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
568     return;
569   }
570
571   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
572                                          lsock, &do_accept, NULL);
573
574   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575               "Proxy listens on port %u\n",
576               port);
577
578 }
579
580 /**
581  * The main function for gnunet-gns-proxy.
582  *
583  * @param argc number of arguments from the command line
584  * @param argv command line arguments
585  * @return 0 ok, 1 on error
586  */
587 int
588 main (int argc, char *const *argv)
589 {
590   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
591     {'p', "port", NULL,
592      gettext_noop ("listen on specified port"), 1,
593      &GNUNET_GETOPT_set_string, &port},
594     GNUNET_GETOPT_OPTION_END
595   };
596
597   int ret;
598
599   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
600   ret =
601       (GNUNET_OK ==
602        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
603                            _("GNUnet GNS proxy"),
604                            options,
605                            &run, NULL)) ? 0 : 1;
606   return ret;
607 }