/* This file is part of GNUnet. (C) 2012 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "platform.h" #include #include "gns_proxy_proto.h" #include "gns.h" #define GNUNET_GNS_PROXY_PORT 7777 //TODO maybe make this an api call /** * Checks if name is in tld * * @param name the name to check * @param tld the TLD to check for * @return GNUNET_YES or GNUNET_NO */ int is_tld(const char* name, const char* tld) { int offset = 0; if (strlen(name) <= strlen(tld)) { return GNUNET_NO; } offset = strlen(name)-strlen(tld); if (strcmp (name+offset, tld) != 0) { GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "%s is not in .%s TLD\n", name, tld); return GNUNET_NO; } return GNUNET_YES; } struct Socks5Request { struct Socks5Request *prev; struct Socks5Request *next; struct GNUNET_NETWORK_Handle *sock; struct GNUNET_NETWORK_Handle *remote_sock; int state; GNUNET_SCHEDULER_TaskIdentifier rtask; GNUNET_SCHEDULER_TaskIdentifier wtask; char rbuf[512]; char wbuf[512]; unsigned int rbuf_len; unsigned int wbuf_len; }; struct Socks5Connections { struct Socks5Request *head; struct Socks5Request *tail; }; unsigned long port = GNUNET_GNS_PROXY_PORT; static struct GNUNET_NETWORK_Handle *lsock; GNUNET_SCHEDULER_TaskIdentifier ltask; static struct Socks5Connections s5conns; /** * Write data to socket * * @param cls the closure * @param tc scheduler context */ static void do_write (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct Socks5Request *s5r = cls; unsigned int len = s5r->wbuf_len; s5r->wtask = GNUNET_SCHEDULER_NO_TASK; if ((NULL != tc->read_ready) && (GNUNET_NETWORK_fdset_isset (tc->write_ready, s5r->sock)) && (len = GNUNET_NETWORK_socket_send (s5r->sock, &s5r->wbuf, len))) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully sent %d bytes to socket\n", len); } else { GNUNET_NETWORK_socket_close (s5r->sock); GNUNET_free(s5r); return; } } /** * Read data from incoming connection * * @param cls the closure * @param tc the scheduler context */ static void do_read (void* cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct Socks5Request *s5r = cls; unsigned int len; struct socks5_client_hello *c_hello; struct socks5_server_hello *s_hello; struct socks5_client_request *c_req; struct socks5_server_response *s_resp; char domain[256]; uint8_t dom_len; uint16_t req_port; struct hostent *phost; uint32_t remote_ip; struct sockaddr_in remote_addr; struct in_addr *r_sin_addr; s5r->rtask = GNUNET_SCHEDULER_NO_TASK; if ((NULL != tc->write_ready) && (GNUNET_NETWORK_fdset_isset (tc->read_ready, s5r->sock)) && (len = GNUNET_NETWORK_socket_recv (s5r->sock, &s5r->rbuf, sizeof (s5r->rbuf)))) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully read %d bytes from socket\n", len); } else { //ERROR! GNUNET_NETWORK_socket_close (s5r->sock); GNUNET_free(s5r); return; } if (s5r->state == SOCKS5_INIT) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SOCKS5 init\n"); c_hello = (struct socks5_client_hello*)&s5r->rbuf; GNUNET_assert (c_hello->version == SOCKS_VERSION_5); s_hello = (struct socks5_server_hello*)&s5r->wbuf; s5r->wbuf_len = sizeof( struct socks5_server_hello ); s_hello->version = c_hello->version; s_hello->auth_method = SOCKS_AUTH_NONE; /* Write response to client */ s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_write, s5r); s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_read, s5r); s5r->state = SOCKS5_REQUEST; return; } if (s5r->state == SOCKS5_REQUEST) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing SOCKS5 request\n"); c_req = (struct socks5_client_request*)&s5r->rbuf; s_resp = (struct socks5_server_response*)&s5r->wbuf; s5r->wbuf_len = sizeof (struct socks5_server_response); GNUNET_assert (c_req->addr_type == 3); dom_len = *((uint8_t*)(&(c_req->addr_type) + 1)); memset(domain, 0, sizeof(domain)); strncpy(domain, (char*)(&(c_req->addr_type) + 2), dom_len); req_port = *((uint16_t*)(&(c_req->addr_type) + 2 + dom_len)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requested connection is %s:%d\n", domain, ntohs(req_port)); if (is_tld (domain, GNUNET_GNS_TLD) || is_tld (domain, GNUNET_GNS_TLD_ZKEY)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Requested connection is gnunet tld\n", domain); } else { phost = (struct hostent*)gethostbyname (domain); if (phost == NULL) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resolve %s error!\n", domain ); s_resp->version = 0x05; s_resp->reply = 0x01; s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_write, s5r); //ERROR! //TODO! close socket after the write! schedule task //GNUNET_NETWORK_socket_close (s5r->sock); //GNUNET_free(s5r); return; } s5r->remote_sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); r_sin_addr = (struct in_addr*)(phost->h_addr); remote_ip = r_sin_addr->s_addr; memset(&remote_addr, 0, sizeof(remote_addr)); remote_addr.sin_family = AF_INET; remote_addr.sin_addr.s_addr = remote_ip; remote_addr.sin_port = req_port; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "target server: %s:%u\n", inet_ntoa(remote_addr.sin_addr), ntohs(req_port)); GNUNET_assert ( s5r->remote_sock != NULL ); if (GNUNET_OK != GNUNET_NETWORK_socket_connect ( s5r->remote_sock, (struct sockaddr*)&remote_addr, sizeof (struct sockaddr))) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "socket request error...\n"); s_resp->version = 0x05; s_resp->reply = 0x01; s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_write, s5r); //TODO see above return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "new remote connection\n"); s_resp->version = 0x05; s_resp->reply = 0x00; s_resp->reserved = 0x00; s_resp->addr_type = 0x01; s5r->state = SOCKS5_DATA_TRANSFER; s5r->wtask = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_write, s5r); } } //GNUNET_CONTAINER_DLL_remove (s5conns.head, s5conns.tail, s5r); } /** * Accept new incoming connections * * @param cls the closure * @param tc the scheduler context */ static void do_accept (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_NETWORK_Handle *s; struct Socks5Request *s5r; ltask = GNUNET_SCHEDULER_NO_TASK; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, lsock, &do_accept, NULL); s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL); if (NULL == s) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_INFO, "accept"); return; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got an inbound connection, waiting for data\n"); s5r = GNUNET_malloc (sizeof (struct Socks5Request)); s5r->sock = s; s5r->state = SOCKS5_INIT; s5r->rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, s5r->sock, &do_read, s5r); GNUNET_CONTAINER_DLL_insert (s5conns.head, s5conns.tail, s5r); } /** * Main function that will be run * * @param cls closure * @param args remaining command-line arguments * @param cfgfile name of the configuration file used (for saving, can be NULL!) * @param cfg configuration */ static void run (void *cls, char *const *args, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { struct sockaddr_in sa; memset (&sa, 0, sizeof (sa)); sa.sin_family = AF_INET; sa.sin_port = htons (port); #if HAVE_SOCKADDR_IN_SIN_LEN sa.sin_len = sizeof (sa); #endif lsock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); if ((NULL == lsock) || (GNUNET_OK != GNUNET_NETWORK_socket_bind (lsock, (const struct sockaddr *) &sa, sizeof (sa)))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to create listen socket bound to `%s'", GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa))); if (NULL != lsock) GNUNET_NETWORK_socket_close (lsock); return; } if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock, 5)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to listen on socket bound to `%s'", GNUNET_a2s ((const struct sockaddr *) &sa, sizeof (sa))); return; } ltask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, lsock, &do_accept, NULL); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Proxy listens on port %u\n", port); } /** * The main function for gnunet-gns-proxy. * * @param argc number of arguments from the command line * @param argv command line arguments * @return 0 ok, 1 on error */ int main (int argc, char *const *argv) { static const struct GNUNET_GETOPT_CommandLineOption options[] = { {'p', "port", NULL, gettext_noop ("listen on specified port"), 1, &GNUNET_GETOPT_set_string, &port}, GNUNET_GETOPT_OPTION_END }; int ret; GNUNET_log_setup ("gnunet-gns-proxy", "WARNING", NULL); ret = (GNUNET_OK == GNUNET_PROGRAM_run (argc, argv, "gnunet-gns-proxy", _("GNUnet GNS proxy"), options, &run, NULL)) ? 0 : 1; return ret; }