dd75c2efaa8250073989978b20b39cb2b942037a
[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;
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                                          s5r->wbuf_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 == GNUNET_SCHEDULER_NO_TASK))
196     s5r->fwdrtask =
197       GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
198                                      s5r->remote_sock,
199                                      &do_read_remote, s5r);
200 }
201
202 /**
203  * Read from remote end
204  *
205  * @param cls closure
206  * @param tc scheduler context
207  */
208 static void
209 do_read_remote (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
210 {
211   struct Socks5Request *s5r = cls;
212   
213   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
214
215
216   if ((NULL != tc->write_ready) &&
217       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->remote_sock)) &&
218       (s5r->wbuf_len = GNUNET_NETWORK_socket_recv (s5r->remote_sock, s5r->wbuf,
219                                          sizeof (s5r->wbuf))))
220   {
221     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
222                 "Successfully read %d bytes from remote socket\n",
223                 s5r->wbuf_len);
224   }
225   else
226   {
227     if (s5r->wbuf_len == 0)
228       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
229                   "0 bytes received from remote... graceful shutdown!\n");
230     if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
231       GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
232     if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
233       GNUNET_SCHEDULER_cancel (s5r->rtask);
234     
235     GNUNET_NETWORK_socket_close (s5r->remote_sock);
236     s5r->remote_sock = NULL;
237     GNUNET_NETWORK_socket_close (s5r->sock);
238     GNUNET_free(s5r);
239
240     return;
241   }
242   
243   s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
244                                                s5r->sock,
245                                                &do_write, s5r);
246   
247 }
248
249
250
251 /**
252  * Read data from incoming connection
253  *
254  * @param cls the closure
255  * @param tc the scheduler context
256  */
257 static void
258 do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
259 {
260   struct Socks5Request *s5r = cls;
261   struct socks5_client_hello *c_hello;
262   struct socks5_server_hello *s_hello;
263   struct socks5_client_request *c_req;
264   struct socks5_server_response *s_resp;
265
266   char domain[256];
267   uint8_t dom_len;
268   uint16_t req_port;
269   struct hostent *phost;
270   uint32_t remote_ip;
271   struct sockaddr_in remote_addr;
272   struct in_addr *r_sin_addr;
273
274   s5r->rtask = GNUNET_SCHEDULER_NO_TASK;
275
276   if ((NULL != tc->write_ready) &&
277       (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) &&
278       (s5r->rbuf_len = GNUNET_NETWORK_socket_recv (s5r->sock, s5r->rbuf,
279                                          sizeof (s5r->rbuf))))
280   {
281     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
282                 "Successfully read %d bytes from socket\n",
283                 s5r->rbuf_len);
284   }
285   else
286   {
287     if (s5r->rbuf_len != 0)
288       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "read");
289     else
290       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "client disco!\n");
291
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     //Only 10byte for ipv4 response!
338     s5r->wbuf_len = 10;//sizeof (struct socks5_server_response);
339
340     GNUNET_assert (c_req->addr_type == 3);
341
342     dom_len = *((uint8_t*)(&(c_req->addr_type) + 1));
343     memset(domain, 0, sizeof(domain));
344     strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len);
345     req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len));
346
347     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
348                 "Requested connection is %s:%d\n",
349                 domain,
350                 ntohs(req_port));
351
352     if (is_tld (domain, GNUNET_GNS_TLD) ||
353         is_tld (domain, GNUNET_GNS_TLD_ZKEY))
354     {
355       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356                   "Requested connection is gnunet tld\n",
357                   domain);
358     }
359     else
360     {
361       phost = (struct hostent*)gethostbyname (domain);
362       if (phost == NULL)
363       {
364         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
365                     "Resolve %s error!\n", domain );
366         s_resp->version = 0x05;
367         s_resp->reply = 0x01;
368         s5r->wtask = 
369           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
370                                           s5r->sock,
371                                           &do_write, s5r);
372         //ERROR!
373         //TODO! close socket after the write! schedule task
374         //GNUNET_NETWORK_socket_close (s5r->sock);
375         //GNUNET_free(s5r);
376         return;
377       }
378
379       s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET,
380                                                        SOCK_STREAM,
381                                                        0);
382       r_sin_addr = (struct in_addr*)(phost->h_addr);
383       remote_ip = r_sin_addr->s_addr;
384       memset(&remote_addr, 0, sizeof(remote_addr));
385       remote_addr.sin_family = AF_INET;
386 #if HAVE_SOCKADDR_IN_SIN_LEN
387       remote_addr.sin_len = sizeof (remote_addr);
388 #endif
389       remote_addr.sin_addr.s_addr = remote_ip;
390       remote_addr.sin_port = req_port;
391       
392       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
393                   "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr),
394                   ntohs(req_port));
395
396       if ((GNUNET_OK !=
397           GNUNET_NETWORK_socket_connect ( s5r->remote_sock,
398                                           (const struct sockaddr*)&remote_addr,
399                                           sizeof (remote_addr)))
400           && (errno != EINPROGRESS))
401       {
402         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "connect");
403         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404                     "socket request error...\n");
405         s_resp->version = 0x05;
406         s_resp->reply = 0x01;
407         s5r->wtask =
408           GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
409                                           s5r->sock,
410                                           &do_write, s5r);
411         //TODO see above
412         return;
413       }
414
415       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416                   "new remote connection\n");
417
418       s_resp->version = 0x05;
419       s_resp->reply = 0x00;
420       s_resp->reserved = 0x00;
421       s_resp->addr_type = 0x01;
422
423       s5r->state = SOCKS5_DATA_TRANSFER;
424
425       s5r->wtask =
426         GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
427                                         s5r->sock,
428                                         &do_write, s5r);
429       s5r->rtask =
430         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
431                                        s5r->sock,
432                                        &do_read, s5r);
433
434     }
435     return;
436   }
437
438   if (s5r->state == SOCKS5_DATA_TRANSFER)
439   {
440     if ((s5r->remote_sock == NULL) || (s5r->rbuf_len == 0))
441     {
442       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443                   "Closing connection to client\n");
444       if (s5r->rtask != GNUNET_SCHEDULER_NO_TASK)
445         GNUNET_SCHEDULER_cancel (s5r->rtask);
446       if (s5r->fwdwtask != GNUNET_SCHEDULER_NO_TASK)
447         GNUNET_SCHEDULER_cancel (s5r->fwdwtask);
448       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
449         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
450       if (s5r->fwdrtask != GNUNET_SCHEDULER_NO_TASK)
451         GNUNET_SCHEDULER_cancel (s5r->fwdrtask);
452       
453       if (s5r->remote_sock != NULL)
454         GNUNET_NETWORK_socket_close (s5r->remote_sock);
455       GNUNET_NETWORK_socket_close (s5r->sock);
456       GNUNET_free(s5r);
457       return;
458     }
459
460     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461                 "forwarding %d bytes from client\n", s5r->rbuf_len);
462
463     s5r->fwdwtask =
464       GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL,
465                                       s5r->remote_sock,
466                                       &do_write_remote, s5r);
467
468     if (s5r->fwdrtask == GNUNET_SCHEDULER_NO_TASK)
469     {
470       s5r->fwdrtask =
471         GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
472                                        s5r->remote_sock,
473                                        &do_read_remote, s5r);
474     }
475
476
477   }
478
479   //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r);
480
481 }
482
483 /**
484  * Accept new incoming connections
485  *
486  * @param cls the closure
487  * @param tc the scheduler context
488  */
489 static void
490 do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
491 {
492   struct GNUNET_NETWORK_Handle *s;
493   struct Socks5Request *s5r;
494
495   ltask = GNUNET_SCHEDULER_NO_TASK;
496   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
497     return;
498
499   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
500                                          lsock,
501                                          &do_accept, NULL);
502
503   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
504
505   if (NULL == s)
506   {
507     GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept");
508     return;
509   }
510
511   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512               "Got an inbound connection, waiting for data\n");
513
514   s5r = GNUNET_malloc (sizeof (struct Socks5Request));
515   s5r->sock = s;
516   s5r->state = SOCKS5_INIT;
517   s5r->wtask = GNUNET_SCHEDULER_NO_TASK;
518   s5r->fwdwtask = GNUNET_SCHEDULER_NO_TASK;
519   s5r->fwdrtask = GNUNET_SCHEDULER_NO_TASK;
520   s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
521                                               s5r->sock,
522                                               &do_read, s5r);
523   //GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r);
524 }
525
526 /**
527  * Main function that will be run
528  *
529  * @param cls closure
530  * @param args remaining command-line arguments
531  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
532  * @param cfg configuration
533  */
534 static void
535 run (void *cls, char *const *args, const char *cfgfile,
536      const struct GNUNET_CONFIGURATION_Handle *cfg)
537 {
538   struct sockaddr_in sa;
539
540   memset (&sa, 0, sizeof (sa));
541   sa.sin_family = AF_INET;
542   sa.sin_port = htons (port);
543 #if HAVE_SOCKADDR_IN_SIN_LEN
544   sa.sin_len = sizeof (sa);
545 #endif
546
547   lsock = GNUNET_NETWORK_socket_create (AF_INET,
548                                         SOCK_STREAM,
549                                         0);
550
551   if ((NULL == lsock) ||
552       (GNUNET_OK !=
553        GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa,
554                                    sizeof (sa))))
555   {
556     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
557                 "Failed to create listen socket bound to `%s'",
558                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
559     if (NULL != lsock)
560       GNUNET_NETWORK_socket_close (lsock);
561     return;
562   }
563
564   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5))
565   {
566     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
567                 "Failed to listen on socket bound to `%s'",
568                 GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa)));
569     return;
570   }
571
572   ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
573                                          lsock, &do_accept, NULL);
574
575   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576               "Proxy listens on port %u\n",
577               port);
578
579 }
580
581 /**
582  * The main function for gnunet-gns-proxy.
583  *
584  * @param argc number of arguments from the command line
585  * @param argv command line arguments
586  * @return 0 ok, 1 on error
587  */
588 int
589 main (int argc, char *const *argv)
590 {
591   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
592     {'p', "port", NULL,
593      gettext_noop ("listen on specified port"), 1,
594      &GNUNET_GETOPT_set_string, &port},
595     GNUNET_GETOPT_OPTION_END
596   };
597
598   int ret;
599
600   GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL);
601   ret =
602       (GNUNET_OK ==
603        GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy",
604                            _("GNUnet GNS proxy"),
605                            options,
606                            &run, NULL)) ? 0 : 1;
607   return ret;
608 }