From 7a1e0e0da32a6fb1028e361edd64a456fdc43ab8 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Tue, 15 May 2012 21:12:04 +0000 Subject: -new proxy --- src/gns/gnocksy/gnocksy.c | 436 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 src/gns/gnocksy/gnocksy.c (limited to 'src') diff --git a/src/gns/gnocksy/gnocksy.c b/src/gns/gnocksy/gnocksy.c new file mode 100644 index 000000000..a7b25e30c --- /dev/null +++ b/src/gns/gnocksy/gnocksy.c @@ -0,0 +1,436 @@ +/* + * The GNS Socks5 Proxy + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXEVENTS 64 + +/* The socks phases */ +enum +{ + SOCKS5_INIT, + SOCKS5_REQUEST, + SOCKS5_DATA_TRANSFER +}; + +/* Client hello */ +struct socks5_client_hello +{ + uint8_t version; + uint8_t num_auth_methods; + char* auth_methods; +}; + +/* Client socks request */ +struct socks5_client_request +{ + uint8_t version; + uint8_t command; + uint8_t resvd; + uint8_t addr_type; + /* + * followed by either an ip4/ipv6 address + * or a domain name with a length field in front + */ +}; + +/* Server hello */ +struct socks5_server_hello +{ + uint8_t version; + uint8_t auth_method; +}; + +/* Struct used to store connection + * information + */ +struct socks5_bridge +{ + int fd; + struct socks5_bridge* remote_end; + int status; +}; + +/* Server response to client requests */ +struct socks5_server_response +{ + uint8_t version; + uint8_t reply; + uint8_t reserved; + uint8_t addr_type; + uint8_t addr_port; +}; + + +/* + * Create an ipv4/6 tcp socket for a given port + * + * @param port the port to bind to + * @return the file descriptor of the socket or -1 + */ +static int +create_socket (char *port) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int s, sfd; + + memset (&hints, 0, sizeof (struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + s = getaddrinfo (NULL, port, &hints, &result); + if (s != 0) + { + fprintf (stderr, "getaddrinfo: %s\n", gai_strerror (s)); + return -1; + } + + for (rp = result; rp != NULL; rp = rp->ai_next) + { + sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sfd == -1) + continue; + + s = bind (sfd, rp->ai_addr, rp->ai_addrlen); + if (s == 0) + { + break; + } + close(sfd); + } + + if (rp == NULL) + { + fprintf (stderr, "Could not bind\n"); + return -1; + } + + freeaddrinfo (result); + + return sfd; +} + + +/* + * Make socket with fd non blocking + * + * @param fd the file descriptor of the socket + * @return -1 on error + */ +static int +setnonblocking (int fd) +{ + int flags, s; + + flags = fcntl (fd, F_GETFL, 0); + if (flags == -1) + { + perror ("fcntl"); + return -1; + } + + flags |= O_NONBLOCK; + s = fcntl (fd, F_SETFL, flags); + if (s == -1) + { + perror ("fcntl"); + return -1; + } + + return 0; +} + +int main ( int argc, char *argv[] ) +{ + int sfd, s; + int efd; + struct epoll_event event; + struct epoll_event *events; + int ev_states[MAXEVENTS]; + int j; + struct socks5_bridge* br; + + for (j = 0; j < MAXEVENTS; j++) + ev_states[j] = SOCKS5_INIT; + + if (argc != 2) + { + fprintf (stderr, "Usage: %s [port]\n", argv[0]); + exit (EXIT_FAILURE); + } + + sfd = create_socket(argv[1]); + if (s == -1) + abort (); + + s = setnonblocking (sfd); + if (s == -1) + abort (); + + s = listen (sfd, SOMAXCONN); + if (s == -1) + { + perror ("listen"); + abort (); + } + + efd = epoll_create1 (0); + if (efd == -1) + { + perror ("epoll create"); + abort (); + } + + br = malloc(sizeof (struct socks5_bridge)); + event.data.ptr = br; + br->fd = sfd; + br->remote_end = NULL; + + event.events = EPOLLIN | EPOLLET; + s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event); + if (s == -1) + { + perror ("epoll ctl"); + abort (); + } + + events = calloc (MAXEVENTS, sizeof event); + + while (1) + { + int n, i; + + n = epoll_wait (efd, events, MAXEVENTS, -1); + for (i = 0; i < n; i++) + { + br = (struct socks5_bridge*)(events[i].data.ptr); + + if ((events[i].events & EPOLLERR) || + (events[i].events & EPOLLHUP) || + (!(events[i].events & EPOLLIN))) + { + fprintf (stderr, "epoll error %d\n", events[i].events); + fprintf (stderr, "closing fd %d\n", br->fd); + close (br->fd); + continue; + } + else if (sfd == br->fd) + { + /* New connection(s) */ + while (1) + { + struct sockaddr in_addr; + socklen_t in_len; + int infd; + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + + in_len = sizeof (in_addr); + infd = accept (sfd, &in_addr, &in_len); + if (infd == -1) + { + if ((errno == EAGAIN) || + (errno == EWOULDBLOCK)) + { + break; + } + else + { + perror ("accept"); + break; + } + } + + s = getnameinfo (&in_addr, in_len, + hbuf, sizeof (hbuf), + sbuf, sizeof (sbuf), + NI_NUMERICHOST | NI_NUMERICSERV); + if (s == -1) + abort (); + + s = setnonblocking (infd); + if (s == -1) + abort (); + + event.events = EPOLLIN | EPOLLET; + br = malloc (sizeof (struct socks5_bridge)); + br->fd = infd; + br->remote_end = NULL; + br->status = SOCKS5_INIT; + event.data.ptr = br; + + s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event); + if (s == -1) + { + perror ("epoll ctl"); + abort (); + } + } + continue; + } + else + { + /* Incoming data */ + int done = 0; + + while (1) + { + ssize_t count; + char buf[512]; + struct socks5_server_hello hello; + struct socks5_server_response* resp; + struct socks5_client_request* req; + struct socks5_bridge* new_br; + char domain[256]; + uint8_t msg[16]; + uint8_t dom_len; + uint32_t srv_ip; + uint16_t srv_port; + struct sockaddr_in srv_addr; + int conn_fd; + struct hostent *phost; + struct in_addr *sin_addr; + + count = read (br->fd, buf, sizeof (buf)); + + if (count == -1) + { + if (errno != EAGAIN) + { + perror ("read"); + done = 1; + } + break; + } + else if (count == 0) + { + done = 1; + break; + } + + if (br->status == SOCKS5_DATA_TRANSFER) + { + if (br->remote_end) + s = write (br->remote_end->fd, buf, count); + } + + if (br->status == SOCKS5_INIT) + { + hello.version = 0x05; + hello.auth_method = 0; + write (br->fd, &hello, sizeof (hello)); + br->status = SOCKS5_REQUEST; + } + if (br->status == SOCKS5_REQUEST) + { + req = (struct socks5_client_request*)buf; + + memset(msg, 0, sizeof(msg)); + resp = (struct socks5_server_response*)msg; + + if (req->addr_type == 3) + { + dom_len = *((uint8_t*)(&(req->addr_type) + 1)); + memset(domain, 0, sizeof(domain)); + strncpy(domain, (char*)(&(req->addr_type) + 2), dom_len); + + phost = (struct hostent*)gethostbyname (domain); + if (phost == NULL) + { + printf ("Resolve %s error!\n" , domain ); + resp->version = 0x05; + resp->reply = 0x01; + write (br->fd, resp, sizeof (struct socks5_server_response)); + break; + } + + sin_addr = (struct in_addr*)(phost->h_addr); + srv_ip = sin_addr->s_addr; + srv_port = *((uint16_t*)(&(req->addr_type) + 2 + dom_len)); + conn_fd = socket(AF_INET, SOCK_STREAM, 0); + memset(&srv_addr, 0, sizeof(srv_addr)); + srv_addr.sin_family = AF_INET; + srv_addr.sin_addr.s_addr = srv_ip; + srv_addr.sin_port = srv_port; + //printf("target server: %s:%u\n", inet_ntoa(srv_addr.sin_addr), + //ntohs(srv_port)); + + if (connect (conn_fd, (struct sockaddr*)&srv_addr, + sizeof (struct sockaddr)) < 0) + { + printf("socket request error...\n"); + resp->version = 0x05; + resp->reply = 0x01; + close(conn_fd); + write (br->fd, resp, 10); + } + else + { + setnonblocking(conn_fd); + resp->version = 0x05; + resp->reply = 0x00; + resp->reserved = 0x00; + resp->addr_type = 0x01; + + new_br = malloc (sizeof (struct socks5_bridge)); + br->remote_end = new_br; + br->status = SOCKS5_DATA_TRANSFER; + new_br->fd = conn_fd; + new_br->remote_end = br; + new_br->status = SOCKS5_DATA_TRANSFER; + + event.data.ptr = new_br; + event.events = EPOLLIN | EPOLLET; + epoll_ctl (efd, EPOLL_CTL_ADD, conn_fd, &event); + write (br->fd, resp, 10); + } + + } + else + { + printf("not implemented address type %02X\n", (int)req->addr_type); + } + } + + + if (s == -1) + { + perror ("write"); + abort (); + } + } + + if (done) + { + close (br->fd); + + if (br->remote_end) + { + close (br->remote_end->fd); + free(br->remote_end); + } + free(br); + } + } + } + } + + free (events); + + close (sfd); + + return EXIT_SUCCESS; +} -- cgit v1.2.3