8 * Note: Only supports addr type 3 (domain) for now.
9 * Chrome uses it automatically
10 * For FF: about:config -> network.proxy.socks_remote_dns true
14 * - LEHO replacement and glue
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <sys/epoll.h>
28 #include <arpa/inet.h>
29 #include <microhttpd.h>
30 #include <curl/curl.h>
41 #define HTML_HDR_CONTENT "Content-Type: text/html\r\n"
43 #define RE_DOTPLUS "<a href=\"http://(([A-Za-z]+[.])+)([+])"
45 #define RE_N_MATCHES 4
48 #define HTTPS_PORT 443
50 static struct MHD_Daemon *mhd_daemon;
51 static regex_t re_dotplus;
54 curl_write_data (void *buffer, size_t size, size_t nmemb, void* cls)
56 const char* page = buffer;
57 uint64_t bytes = size * nmemb;
58 struct socks5_bridge* br = cls;
62 regmatch_t m[RE_N_MATCHES];
68 uint64_t bytes_copied = 0;
70 char new_buf[CURL_MAX_WRITE_SIZE+1];
73 pthread_mutex_lock ( &br->m_buf );
74 if (br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_MHD)
76 pthread_mutex_unlock ( &br->m_buf );
77 return CURL_WRITEFUNC_PAUSE;
81 if ( br->res_is_html )
83 printf ("result is html text\n");
84 memset (new_buf, 0, sizeof(new_buf));
85 memcpy (new_buf, page, bytes);
90 nomatch = regexec ( &re_dotplus, p, RE_N_MATCHES, m, 0);
95 printf ("No more matches\n");
99 printf ("Error p<buf!\n");
102 memcpy ( br->MHD_CURL_BUF+bytes_copied, p, bytes-(p-new_buf));
103 bytes_copied += bytes-(p-new_buf);
108 printf ("Got match\n");
110 if (m[1].rm_so != -1)
112 hostptr = p+m[1].rm_so;
114 printf ("Copying %d bytes.\n", (hostptr-p));
115 memcpy (br->MHD_CURL_BUF+bytes_copied, p, (hostptr-p));
116 bytes_copied += (hostptr-p);
117 memset (new_host, 0, sizeof(new_host));
118 memset (to_exp, 0, sizeof (to_exp));
119 memcpy (to_exp, hostptr, (m[1].rm_eo-m[1].rm_so));
121 gns_glue_expand_and_shorten ( to_exp,
126 printf ("Glue fin\n");
127 printf ("Copying new name %s \n", new_host);
129 memcpy ( br->MHD_CURL_BUF+bytes_copied, new_host,
131 bytes_copied += strlen (new_host);
135 printf ("Done. Next in %d bytes\n", m[3].rm_so);
137 //TODO check buf lenghts!
140 br->MHD_CURL_BUF_SIZE = bytes_copied;
144 memcpy (br->MHD_CURL_BUF, buffer, bytes);
145 br->MHD_CURL_BUF_SIZE = bytes;
148 br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_MHD;
150 pthread_mutex_unlock ( &br->m_buf );
153 //MHD_destroy_response (response);
155 printf( "buffer:\n%s\n", (char*)br->MHD_CURL_BUF );
160 curl_check_hdr (void *buffer, size_t size, size_t nmemb, void* cls)
162 size_t bytes = size * nmemb;
163 struct socks5_bridge* br = cls;
166 memcpy(hdr, buffer, bytes);
170 printf ("got hdr: %s", hdr);
172 if (0 == strcmp(hdr, HTML_HDR_CONTENT))
180 * Create an ipv4/6 tcp socket for a given port
182 * @param port the port to bind to
183 * @return the file descriptor of the socket or -1
186 create_socket (char *port)
188 struct addrinfo hints;
189 struct addrinfo *result, *rp;
192 memset (&hints, 0, sizeof (struct addrinfo));
193 hints.ai_family = AF_UNSPEC;
194 hints.ai_socktype = SOCK_STREAM;
195 hints.ai_flags = AI_PASSIVE;
197 s = getaddrinfo (NULL, port, &hints, &result);
200 fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s));
204 for (rp = result; rp != NULL; rp = rp->ai_next)
206 sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);
210 s = bind (sfd, rp->ai_addr, rp->ai_addrlen);
220 fprintf (stderr, "Could not bind\n");
224 freeaddrinfo (result);
231 * Make socket with fd non blocking
233 * @param fd the file descriptor of the socket
234 * @return -1 on error
237 setnonblocking (int fd)
241 flags = fcntl (fd, F_GETFL, 0);
249 s = fcntl (fd, F_SETFL, flags);
260 * Checks if name is in tld
262 * @param name the name to check
263 * @param tld the TLD to check for
264 * @return -1 if name not in tld
267 is_tld (const char* name, const char* tld)
271 if (strlen (name) <= strlen (tld))
274 offset = strlen (name) - strlen (tld);
275 if (strcmp (name+offset, tld) != 0)
283 * Connect to host specified in phost
285 * @param phost the hostentry containing the IP
286 * @return fd to the connection or -1 on error
289 connect_to_domain (struct hostent* phost, uint16_t srv_port)
292 struct sockaddr_in srv_addr;
293 struct in_addr *sin_addr;
297 sin_addr = (struct in_addr*)(phost->h_addr);
298 srv_ip = sin_addr->s_addr;
299 conn_fd = socket(AF_INET, SOCK_STREAM, 0);
300 memset(&srv_addr, 0, sizeof(srv_addr));
301 srv_addr.sin_family = AF_INET;
302 srv_addr.sin_addr.s_addr = srv_ip;
303 srv_addr.sin_port = srv_port;
305 printf("target server: %s:%u\n", inet_ntoa(srv_addr.sin_addr),
308 if (connect (conn_fd, (struct sockaddr*)&srv_addr,
309 sizeof (struct sockaddr)) < 0)
311 printf("socket request error...\n");
316 setnonblocking(conn_fd);
322 access_cb (void* cls,
323 const struct sockaddr *addr,
326 printf ("access cb called\n");
331 fetch_url (struct socks5_bridge* br)
336 br->curl = curl_easy_init();
338 /* TODO optionally do LEHO stuff here */
342 curl_easy_setopt (br->curl, CURLOPT_URL, br->full_url);
343 curl_easy_setopt (br->curl, CURLOPT_HEADERFUNCTION, &curl_check_hdr);
344 curl_easy_setopt (br->curl, CURLOPT_HEADERDATA, br);
345 curl_easy_setopt (br->curl, CURLOPT_WRITEFUNCTION, &curl_write_data);
346 curl_easy_setopt (br->curl, CURLOPT_WRITEDATA, br);
347 ret = curl_easy_perform (br->curl);
349 pthread_mutex_lock ( &br->m_done );
351 pthread_mutex_unlock ( &br->m_done );
353 curl_easy_cleanup (br->curl);
357 printf("all good on the curl end\n");
360 printf("error on the curl end %s\n", curl_easy_strerror(ret));
365 mhd_content_cb (void* cls,
370 struct socks5_bridge* br = cls;
372 pthread_mutex_lock ( &br->m_done );
373 /* if done and buf empty */
374 if ( (br->is_done == 1) &&
375 (br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_CURL) )
377 printf("done. sending response...\n");
379 pthread_mutex_unlock ( &br->m_done );
380 return MHD_CONTENT_READER_END_OF_STREAM;
382 pthread_mutex_unlock ( &br->m_done );
384 pthread_mutex_lock ( &br->m_buf );
385 if ( br->MHD_CURL_BUF_STATUS == BUF_WAIT_FOR_CURL )
387 pthread_mutex_unlock ( &br->m_buf );
393 if ( br->MHD_CURL_BUF_SIZE > max )
395 printf("buffer in mhd response too small!\n");
396 pthread_mutex_unlock ( &br->m_buf );
397 return MHD_CONTENT_READER_END_WITH_ERROR;
400 if (0 != br->MHD_CURL_BUF_SIZE)
402 printf("copying %d bytes to mhd response at offset %d\n",
403 br->MHD_CURL_BUF_SIZE, pos);
404 memcpy ( buf, br->MHD_CURL_BUF, br->MHD_CURL_BUF_SIZE );
406 br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_CURL;
407 curl_easy_pause (br->curl, CURLPAUSE_CONT);
408 pthread_mutex_unlock ( &br->m_buf );
410 return br->MHD_CURL_BUF_SIZE;
414 accept_cb (void *cls,
415 struct MHD_Connection *con,
419 const char *upload_data,
420 size_t *upload_data_size,
424 const char* page = "<html><head><title>gnoxy</title>"\
425 "</head><body>gnoxy demo</body></html>";
426 struct MHD_Response *response;
427 struct socks5_bridge *br = cls;
431 if (0 != strcmp (meth, "GET"))
433 if (&dummy != *con_cls)
439 if (0 != *upload_data_size)
444 if (-1 == asprintf (&br->full_url, "%s%s", br->host, url))
446 printf ("error building url!\n");
449 printf ("url %s\n", br->full_url);
451 pthread_mutex_lock ( &br->m_done );
453 pthread_mutex_unlock ( &br->m_done );
455 br->MHD_CURL_BUF_STATUS = BUF_WAIT_FOR_CURL;
458 response = MHD_create_response_from_callback (-1, -1,
461 NULL); //TODO destroy resp here
463 ret = MHD_queue_response (con, MHD_HTTP_OK, response);
464 pthread_create ( &br->thread, NULL, &fetch_url, br );
472 compile_regex (regex_t *re, const char* rt)
477 status = regcomp (re, rt, REG_EXTENDED|REG_NEWLINE);
480 regerror (status, re, err, 1024);
481 printf ("Regex error compiling '%s': %s\n", rt, err);
488 int main ( int argc, char *argv[] )
492 struct epoll_event event;
493 struct epoll_event *events;
494 int ev_states[MAXEVENTS];
496 struct socks5_bridge* br;
498 struct sockaddr in_addr;
501 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
503 /* port offset for ssl daemons */
509 struct socks5_server_hello hello;
510 struct socks5_server_response resp;
511 struct socks5_client_request *req;
512 struct socks5_bridge* new_br;
517 struct hostent *phost;
520 curl_global_init(CURL_GLOBAL_ALL);
522 //compile_regex ( &re_htmlhdr, RE_HTML );
523 compile_regex( &re_dotplus, (char*)RE_DOTPLUS );
525 for (j = 0; j < MAXEVENTS; j++)
526 ev_states[j] = SOCKS5_INIT;
530 fprintf (stderr, "Usage: %s [port]\n", argv[0]);
534 sfd = create_socket(argv[1]);
538 s = setnonblocking (sfd);
542 s = listen (sfd, SOMAXCONN);
549 efd = epoll_create1 (0);
552 perror ("epoll create");
556 br = malloc(sizeof (struct socks5_bridge));
559 br->remote_end = NULL;
561 event.events = EPOLLIN | EPOLLET;
562 s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);
565 perror ("epoll ctl");
569 events = calloc (MAXEVENTS, sizeof event);
573 n = epoll_wait (efd, events, MAXEVENTS, -1);
574 for (i = 0; i < n; i++)
576 br = (struct socks5_bridge*)(events[i].data.ptr);
578 if ((events[i].events & EPOLLERR) ||
579 (events[i].events & EPOLLHUP) ||
580 (!(events[i].events & EPOLLIN)))
582 fprintf (stderr, "epoll error %d\n", events[i].events);
583 fprintf (stderr, "closing fd %d\n", br->fd);
587 else if (sfd == br->fd)
589 /* New connection(s) */
593 in_len = sizeof (in_addr);
594 infd = accept (sfd, &in_addr, &in_len);
597 if ((errno == EAGAIN) ||
598 (errno == EWOULDBLOCK))
609 s = getnameinfo (&in_addr, in_len,
612 NI_NUMERICHOST | NI_NUMERICSERV);
616 s = setnonblocking (infd);
620 event.events = EPOLLIN | EPOLLET;
621 br = malloc (sizeof (struct socks5_bridge));
624 br->addr_len = in_len;
625 br->remote_end = NULL;
626 br->status = SOCKS5_INIT;
629 s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);
632 perror ("epoll ctl");
646 memset (buf, 0, sizeof (buf));
648 count = read (br->fd, buf, sizeof (buf));
665 if (br->status == SOCKS5_DATA_TRANSFER)
669 printf ("Trying to fwd %d bytes from %d to %d!\n" ,
670 count, br->fd, br->remote_end->fd );
673 s = write (br->remote_end->fd, buf, count);
675 printf ("%d bytes written\n", s);
678 if (br->status == SOCKS5_INIT)
681 printf ("SOCKS5 init for %d\n", br->fd);
682 hello.version = 0x05;
683 hello.auth_method = 0;
684 write (br->fd, &hello, sizeof (hello));
685 br->status = SOCKS5_REQUEST;
687 if (br->status == SOCKS5_REQUEST)
689 req = (struct socks5_client_request*)buf;
691 memset(&resp, 0, sizeof(resp));
693 if (req->addr_type == 3)
695 dom_len = *((uint8_t*)(&(req->addr_type) + 1));
696 memset(domain, 0, sizeof(domain));
697 strncpy(domain, (char*)(&(req->addr_type) + 2), dom_len);
698 req_port = *((uint16_t*)(&(req->addr_type) + 2 + dom_len));
701 printf ("Requested connection is %s:%d\n",
705 phost = (struct hostent*)gethostbyname (domain);
709 printf ("Resolve %s error!\n" , domain );
712 write (br->fd, &resp, sizeof (struct socks5_server_response));
717 printf ("trying to add %d to MHD\n", br->fd);
719 if ( -1 != is_tld (domain, ".gnunet") )
721 strcpy (br->host, domain);
722 if (HTTP_PORT == ntohs(req_port))
725 if (NULL == mhd_daemon)
728 MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION,
735 if (MHD_YES != MHD_add_connection (mhd_daemon,
741 printf ("Error adding %d to mhd\n", br->fd);
745 if (HTTPS_PORT == ntohs(req_port))
748 * custom daemon for SSL requests
749 * TODO make more efficient with
750 * per name SSL daemons?
754 MHD_start_daemon( MHD_USE_THREAD_PER_CONNECTION |
759 MHD_OPTION_HTTPS_MEM_KEY, NULL,
760 MHD_OPTION_HTTPS_MEM_CERT, NULL,
765 if (MHD_YES != MHD_add_connection (br->ssl_daemon,
771 printf ("Error adding %d to mhd\n", br->fd);
776 event.events = EPOLLIN | EPOLLET;
777 epoll_ctl (efd, EPOLL_CTL_DEL, br->fd, &event);
780 resp.reserved = 0x00;
781 resp.addr_type = 0x01;
782 write (br->fd, &resp, 10);
787 conn_fd = connect_to_domain (phost, req_port);
792 printf("cannot create remote connection from %d to %s:%d\n",
793 br->fd, domain, ntohs(req_port));
796 write (br->fd, &resp, 10);
801 printf("new remote connection %d to %d\n", br->fd, conn_fd);
804 resp.reserved = 0x00;
805 resp.addr_type = 0x01;
807 new_br = malloc (sizeof (struct socks5_bridge));
808 if (br->remote_end != NULL)
809 printf ("WARNING remote end was not NULL!\n");
810 br->remote_end = new_br;
811 br->status = SOCKS5_DATA_TRANSFER;
812 new_br->fd = conn_fd;
813 new_br->remote_end = br;
814 new_br->status = SOCKS5_DATA_TRANSFER;
816 event.data.ptr = new_br;
817 event.events = EPOLLIN | EPOLLET;
818 epoll_ctl (efd, EPOLL_CTL_ADD, conn_fd, &event);
819 write (br->fd, &resp, 10);
827 printf("not implemented address type %02X\n", (int)req->addr_type);
845 //close (br->remote_end->fd);
846 //free(br->remote_end);
855 MHD_stop_daemon (mhd_daemon);
856 regfree ( &re_dotplus );