/*
Another Virtual Network Environment
Version 0.5
Copyright (C) 2015 Rob van der Hoeven
This is an alpha release, intended for developers only.
-------------------------------------------------------
Support at : https://hoevenstein.nl
Compilation : gcc -Wall -o avne avne.c
Installation : chown root avne && chmod u+s avne
License.
--------
This program 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 of the License, or
(at your option) any later version.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <ctype.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <poll.h>
#include <netdb.h>
#include <resolv.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ip_icmp.h>
//
// NOTE: in Debian Wheezy the setns function does not have a prototype in <sched.h>
// in Debian Jessie the function below is not needed and must be removed
//
static inline int setns(int fd, int nstype)
{
#ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
#elif defined(__NR_set_ns)
return syscall(__NR_set_ns, fd, nstype);
#else
errno = ENOSYS;
return -1;
#endif
}
//
// constants
//
#define MTU 1500
#define TCP_BUFFER_SIZE 4096
#define DNS_BUFFER_SIZE 1024
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
#define UPSTREAM_CLOSED 0
#define UPSTREAM_CONNECTING 1
#define UPSTREAM_CONNECTED 2
#define SOCKS5_SEND_METHODS 10
#define SOCKS5_READ_METHODS 11
#define SOCKS5_SEND_CONNECT 12
#define SOCKS5_READ_CONNECT 13
#define SOCKS5_TOR_SEND_RESOLVE 20
#define SOCKS5_TOR_READ_RESOLVE 21
#define SOCKS5_TOR_DONE_RESOLVE 22
// a TCP or DNS upstream connection uses one file descriptor. max number of
// fd's is 1024 and also FD_SETSIZE is 1024. so *never* have the sum of
// TCP_CONNECTIONS_MAX and DNS_CONNECTIONS_MAX exceed 1000
#define TCP_CONNECTIONS_MAX 500
#define DNS_CONNECTIONS_MAX 500
#define DNS_REQUEST_NORMAL 0
#define DNS_REQUEST_PROBE 1
#define DNS_REQUEST_LOCAL 2
#define LOG_INFO 0
#define LOG_ERROR 1
//
// prototypes
//
typedef struct tcp_connection_s tcp_connection_t;
typedef struct dns_cache_entry_s dns_cache_entry_t;
typedef struct dns_connection_s dns_connection_t;
typedef struct upstream_connection_s upstream_connection_t;
typedef struct upstream_functions_s upstream_functions_t;
typedef struct buffer_s buffer_t;
u_int16_t tcp_connection_send_datagram(tcp_connection_t *tcp_connection, u_int8_t flags, u_int8_t *data, u_int32_t data_size);
void tcp_connection_write(tcp_connection_t *tcp_connection);
void tcp_connection_server_close(tcp_connection_t *tcp_connection);
void dns_connection_send_datagram(dns_connection_t *dns_connection);
void dns_connection_done(dns_connection_t *dns_connection);
char *dns_cache_entry_get_name(dns_cache_entry_t *dns_cache_entry);
char *dns_cache_get_name(u_int32_t address);
//
// types
//
struct buffer_s
{
u_int16_t size;
u_int16_t count;
u_int8_t *data;
};
struct upstream_functions_s
{
int (* const connect)(upstream_connection_t *upstream_connection);
int (* const disconnect)(upstream_connection_t *upstream_connection);
void (* const read)(upstream_connection_t *upstream_connection);
void (* const write)(upstream_connection_t *upstream_connection);
void (* const prepare_events)(upstream_connection_t *upstream_connection);
void (* const handle_events)(upstream_connection_t *upstream_connection);
};
struct upstream_connection_s
{
int id;
char *idstr;
int fd;
u_int16_t state;
u_int16_t resultcode;
u_int32_t server_address;
u_int16_t server_port;
char* server_name;
buffer_t *input; // input buffer == output buffer of tcp/dns connection
buffer_t *output; // output buffer == input buffer of tcp/dns connection
upstream_functions_t *functions;
};
struct tcp_connection_s
{
int id;
char *idstr;
tcp_connection_t *next;
upstream_connection_t upstream;
u_int32_t server_address; // stored in nbo
u_int16_t server_port; // stored in nbo
u_int32_t client_address; // stored in nbo
u_int16_t client_port; // stored in nbo
u_int32_t server_seq; // current server sequence
u_int32_t server_seq_ack; // last acked sequence from client
u_int16_t server_window;
u_int16_t server_mss;
u_int32_t client_seq; // next expected client sequence
u_int32_t client_seq_ack; // last ack send to client
u_int16_t client_window;
u_int16_t client_mss;
u_int8_t client_wscale; // not used
u_int16_t tcp_state;
buffer_t input; // contains data from client
buffer_t output; // data that must be written to client
};
struct dns_cache_entry_s
{
dns_cache_entry_t *next;
time_t expires;
char *name;
u_int32_t address;
u_int16_t request_size;
u_int8_t *request;
u_int16_t response_size;
u_int8_t *response;
u_int8_t request_type;
u_int16_t response_clients; // number of DNS connections waiting for the response
};
struct dns_connection_s
{
int id;
char *idstr;
dns_connection_t *next;
upstream_connection_t upstream;
u_int32_t server_address; // stored in nbo
u_int16_t server_port; // stored in nbo
u_int32_t client_address; // stored in nbo
u_int16_t client_port; // stored in nbo
u_int16_t transaction_id; // stored in nbo
buffer_t input; // from client
buffer_t output; // to client
// DNS requests and responses are cached for some time. for new connections
// the cache is searched for an entry with the same request. if this entry
// has a response then this response is send to the client immediately and
// no upstream connection is needed
dns_cache_entry_t *cache_entry;
};
//
// globals
//
int fd_tun=-1;
int fd_log=-1;
int fd_max=0;
int pid_child=0;
int child_running=1;
char log_filename[]="/var/log/avne/log.txt";
char http_filename[]="/var/log/avne/status.html";
char *socks_server_address=0;
u_int16_t socks_server_port=9050;
fd_set fdset_read;
fd_set fdset_write;
u_int8_t datagram[MTU];
struct iphdr *ip_header = (struct iphdr *) datagram;
upstream_functions_t *default_tcp_upstream_functions=0;
upstream_functions_t *default_dns_upstream_functions=0;
int tcp_connection_total_count=0;
int dns_connection_total_count=0;
//int dns_cache_total_count=0;
int dns_connection_fail_count=0;
int dns_connection_reject_count=0;
int dns_connection_cache_hit_count=0;
int dns_connection_use_tor_resolve=0;
int dns_cache_expired_count=0;
// linked lists of "active" items
int tcp_connection_count = 0;
tcp_connection_t *tcp_connection_root=0;
int dns_cache_count=0;
dns_cache_entry_t *dns_cache_entry_root=0;
int dns_connection_count=0;
dns_connection_t *dns_connection_root=0;
//
// Logging
//
char *get_log_time(void)
{
static char log_time[128];
struct timeval t;
struct tm *tm;
short p;
gettimeofday(&t, 0);
tm = localtime(&t.tv_sec);
if (tm)
{
if ((p=strftime(log_time, sizeof(log_time), "%F %T", tm)))
{
sprintf(log_time+p, ".%06ld",t.tv_usec);
return log_time;
}
}
return strcpy(log_time,"get_log_time failed");
}
void ts_log(int exitcode, int what, const char *where, const char *format, ...)
{
char buffer[512];
va_list arguments;
if (fd_log != -1)
{
if (where)
write(fd_log, buffer, sprintf(buffer,"[%s] %s [%s] ", get_log_time(), (what == LOG_ERROR) ? "Error" : "Info ", where));
else
write(fd_log, buffer, sprintf(buffer,"[%s] %s ", get_log_time(), (what == LOG_ERROR) ? "Error" : "Info "));
}
va_start(arguments, format);
vsnprintf(buffer, sizeof(buffer)-1, format, arguments);
va_end(arguments);
buffer[sizeof(buffer)-1]=0;
if (fd_log != -1)
dprintf(fd_log, buffer);
// always report errors to stderr
if (what == LOG_ERROR)
dprintf(STDERR_FILENO, "avne: %s", buffer);
if (exitcode)
exit(exitcode);
}
void ts_log_hex(void* address, int count)
{
char hex_buffer[16*3+1];
char ascii_buffer[16+1];
char *hex_pos=hex_buffer;
char *hex_pos_max=hex_buffer+15*3;
char *ascii_pos=ascii_buffer;
char *c = (char *) address;
while (count--)
{
sprintf(hex_pos,"%.2x ",(unsigned char) *c);
if (isprint(*c))
sprintf(ascii_pos,"%c",*c);
else
sprintf(ascii_pos,".");
c++; // a very nice language....
if (hex_pos < hex_pos_max)
{
hex_pos+=3;
ascii_pos++;
continue;
}
ts_log(0, LOG_INFO, 0, "%-50s%s\n", hex_buffer, ascii_buffer);
hex_pos=hex_buffer;
ascii_pos=ascii_buffer;
}
if (hex_pos != hex_buffer)
ts_log(0, LOG_INFO, 0, "%-50s%s\n", hex_buffer, ascii_buffer);
}
void ts_log_open(const char *filename, int append)
{
int flags=O_WRONLY | O_CREAT | O_CLOEXEC;
umask(0);
if (append)
flags |= O_APPEND;
else
flags |= O_TRUNC;
if ((fd_log=open(filename, flags, S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH)) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to open logfile [%s] - error [%s]\n", filename, strerror(errno));
}
void ip_string(u_int32_t address, char *buffer, int buffer_size)
{
struct in_addr ip_address;
ip_address.s_addr = address;
inet_ntop(AF_INET,&ip_address, buffer, buffer_size);
}
//
// buffer
//
u_int8_t *buffer_init(buffer_t *buffer, u_int16_t size)
{
buffer->size=size;
buffer->count=0;
return buffer->data=malloc(size);
}
void buffer_done(buffer_t *buffer)
{
free(buffer->data);
buffer->size=0;
buffer->count=0;
}
u_int16_t buffer_space(buffer_t *buffer)
{
return buffer->size-buffer->count;
}
u_int8_t *buffer_tail(buffer_t *buffer)
{
return buffer->data+buffer->count;
}
int buffer_add(buffer_t *buffer, u_int8_t *data, u_int16_t data_size)
{
if (data && data_size)
{
if (data_size > buffer_space(buffer))
{
ts_log(0, LOG_ERROR, __FUNCTION__, "data_size [%u] > buffer_free [%u]\n", data_size, buffer_space(buffer));
return 0;
}
memcpy(buffer->data+buffer->count, data, data_size);
buffer->count+=data_size;
}
return 1;
}
int buffer_remove(buffer_t *buffer, u_int16_t data_size)
{
if (data_size)
{
if (data_size > buffer->count)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "data_size [%u] > count [%u]\n", data_size, buffer->count);
return 0;
}
memmove(buffer->data, buffer->data+data_size, buffer->count-data_size);
buffer->count-=data_size;
}
return 1;
}
//
// checksums
//
u_int16_t ip_checksum(void *buffer, int count) // From RFC 1071
{
u_int32_t sum = 0;
u_int16_t *addr=(u_int16_t *) buffer;
while (count > 1)
{
sum+=*addr++;
count-=2;
}
if(count > 0)
sum+=*(u_int8_t *) addr;
while (sum >> 16)
sum=(sum & 0xffff)+(sum >> 16);
return (u_int16_t) ~sum;
}
u_int16_t udp_checksum(struct iphdr *ip_header, struct udphdr *udp_header)
{
typedef struct
{
u_int32_t saddr;
u_int32_t daddr;
u_int8_t reserved;
u_int8_t protocol;
u_int16_t udp_length;
} udp_pseudo_header_t;
int count;
u_int32_t sum;
u_int16_t *addr, udp_header_check;
udp_pseudo_header_t pseudo_header;
pseudo_header.saddr=ip_header->saddr;
pseudo_header.daddr=ip_header->daddr;
pseudo_header.reserved=0;
pseudo_header.protocol=ip_header->protocol;
pseudo_header.udp_length=htons(ntohs(ip_header->tot_len)-(ip_header->ihl << 2));
sum = 0;
addr=(u_int16_t *) &pseudo_header;
count = sizeof(pseudo_header);
while (count > 1)
{
sum+=*addr++;
count-=2;
}
addr=(u_int16_t *) udp_header;
count=ntohs(ip_header->tot_len)-(ip_header->ihl << 2);
udp_header_check=udp_header->check;
udp_header->check=0;
while (count > 1)
{
sum+=*addr++;
count-=2;
}
if(count > 0)
sum+=*(u_int8_t *) addr;
while (sum >> 16)
sum=(sum & 0xffff)+(sum >> 16);
udp_header->check=udp_header_check;
return (u_int16_t) ~sum;
}
u_int16_t tcp_checksum(struct iphdr *ip_header, struct tcphdr *tcp_header)
{
typedef struct
{
u_int32_t saddr;
u_int32_t daddr;
u_int8_t reserved;
u_int8_t protocol;
u_int16_t segment_size;
} tcp_pseudo_header_t;
int count;
u_int32_t sum;
u_int16_t *addr, tcp_header_check;
tcp_pseudo_header_t pseudo_header;
pseudo_header.saddr=ip_header->saddr;
pseudo_header.daddr=ip_header->daddr;
pseudo_header.reserved=0;
pseudo_header.protocol=ip_header->protocol;
pseudo_header.segment_size=htons(ntohs(ip_header->tot_len)-(ip_header->ihl << 2));
sum = 0;
addr=(u_int16_t *) &pseudo_header;
count = sizeof(pseudo_header);
while (count > 1)
{
sum+=*addr++;
count-=2;
}
addr=(u_int16_t *) tcp_header;
count=ntohs(ip_header->tot_len)-(ip_header->ihl << 2);
tcp_header_check=tcp_header->check;
tcp_header->check=0;
while (count > 1)
{
sum+=*addr++;
count-=2;
}
if(count > 0)
sum+=*(u_int8_t *) addr;
while (sum >> 16)
sum=(sum & 0xffff)+(sum >> 16);
tcp_header->check=tcp_header_check;
return (u_int16_t) ~sum;
}
//
// TCP upstream functions
//
int tcp_upstream_connect_start(upstream_connection_t *upstream_connection)
{
struct sockaddr_in upstream_address;
//ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", upstream_connection->idstr);
if ((upstream_connection->fd=socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to create socket - error [%s]\n", upstream_connection->idstr, strerror(errno));
return 0;
}
upstream_address.sin_family=AF_INET;
upstream_address.sin_addr.s_addr=upstream_connection->server_address;
upstream_address.sin_port=upstream_connection->server_port;
while (1)
{
if (connect(upstream_connection->fd, (struct sockaddr *) &upstream_address, sizeof(upstream_address)) != -1)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] fd [%d] upstream connected.\n", upstream_connection->idstr, upstream_connection->fd);
upstream_connection->state=UPSTREAM_CONNECTED;
return 1;
}
switch (errno)
{
case EINTR:
continue;
case EINPROGRESS:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] fd [%d] upstream connecting\n", upstream_connection->idstr, upstream_connection->fd);
upstream_connection->state=UPSTREAM_CONNECTING;
return 1;
default:
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] fd [%d] failed to start nonblocking connection\n", upstream_connection->idstr, upstream_connection->fd);
close(upstream_connection->fd);
upstream_connection->fd=-1;
return 0;
}
}
return 0;
}
int tcp_upstream_connect_end(upstream_connection_t *upstream_connection)
{
int socket_error=0;
socklen_t socket_error_size=sizeof(socket_error);
//ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", upstream_connection->idstr);
if (getsockopt(upstream_connection->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_size) == -1)
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to read socket_error - error [%s]\n", upstream_connection->idstr, strerror(errno));
else
if (socket_error == 0)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] upstream connected.\n", upstream_connection->idstr);
upstream_connection->state=UPSTREAM_CONNECTED;
return 1;
}
else
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] upstream failed to connect - error [%s]\n", upstream_connection->idstr, strerror(socket_error));
upstream_connection->state=UPSTREAM_CLOSED;
close(upstream_connection->fd);
upstream_connection->fd=-1;
return 0;
}
int tcp_upstream_disconnect(upstream_connection_t *upstream_connection)
{
ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", upstream_connection->idstr);
upstream_connection->state=UPSTREAM_CLOSED;
close(upstream_connection->fd);
upstream_connection->fd=-1;
return 1;
}
void tcp_upstream_read(upstream_connection_t *upstream_connection)
{
int data_count;
if (upstream_connection->state != UPSTREAM_CONNECTED)
return;
if (!buffer_space(upstream_connection->input))
return;
while (1)
{
data_count=read(upstream_connection->fd, buffer_tail(upstream_connection->input), buffer_space(upstream_connection->input));
if (data_count == -1)
{
if (errno == EINTR)
continue;
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to read fd_upstream - error [%s]\n", upstream_connection->idstr, strerror(errno));
return;
}
if (data_count == 0)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] upstream socket closed\n", upstream_connection->idstr);
close(upstream_connection->fd);
upstream_connection->fd=-1;
upstream_connection->state=UPSTREAM_CLOSED;
}
break;
}
upstream_connection->input->count+=data_count;
}
void tcp_upstream_write(upstream_connection_t *upstream_connection)
{
int data_count;
if (upstream_connection->state != UPSTREAM_CONNECTED)
return;
while (upstream_connection->output->count)
{
data_count=write(upstream_connection->fd, upstream_connection->output->data, upstream_connection->output->count);
if (data_count == -1)
{
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "unexpected error [%s]\n", strerror(errno));
return;
}
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to write to upstream socket - error [%s]\n", upstream_connection->idstr, strerror(errno));
return;
}
if (data_count == 0)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] failed to write to upstream socket\n", upstream_connection->idstr);
return;
}
buffer_remove(upstream_connection->output, data_count);
}
}
void tcp_upstream_prepare_events(upstream_connection_t *upstream_connection)
{
if (!upstream_connection || upstream_connection->fd == -1)
return;
switch (upstream_connection->state)
{
case UPSTREAM_CLOSED:
return;
case UPSTREAM_CONNECTING:
FD_SET(upstream_connection->fd, &fdset_write);
break;
case UPSTREAM_CONNECTED:
if (!buffer_space(upstream_connection->input) && !upstream_connection->output->count)
return;
if (buffer_space(upstream_connection->input))
FD_SET(upstream_connection->fd, &fdset_read);
if (upstream_connection->output->count)
FD_SET(upstream_connection->fd, &fdset_write);
break;
}
// fd_upstream included in read or write set
if (upstream_connection->fd > fd_max)
fd_max=upstream_connection->fd;
}
void tcp_upstream_handle_events(upstream_connection_t *upstream_connection)
{
if (!upstream_connection || upstream_connection->fd == -1)
return;
switch (upstream_connection->state)
{
case UPSTREAM_CLOSED:
return;
case UPSTREAM_CONNECTING:
if (FD_ISSET(upstream_connection->fd, &fdset_write))
tcp_upstream_connect_end(upstream_connection);
break;
case UPSTREAM_CONNECTED:
if (FD_ISSET(upstream_connection->fd, &fdset_read))
upstream_connection->functions->read(upstream_connection);
// the read above may have closed the connection, so check if fd still ok
if (upstream_connection->fd != -1 && FD_ISSET(upstream_connection->fd, &fdset_write))
upstream_connection->functions->write(upstream_connection);
break;
}
}
upstream_functions_t tcp_upstream_functions=
{
.connect = &tcp_upstream_connect_start,
.disconnect = &tcp_upstream_disconnect,
.read = &tcp_upstream_read,
.write = &tcp_upstream_write,
.prepare_events = &tcp_upstream_prepare_events,
.handle_events = &tcp_upstream_handle_events
};
//
// SOCKS5 upstream functions
//
int socks5_upstream_connect_start(upstream_connection_t *upstream_connection)
{
struct sockaddr_in upstream_address;
struct in_addr socks5_address;
ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", upstream_connection->idstr);
if ((upstream_connection->fd=socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to create socket - error [%s]\n", upstream_connection->idstr, strerror(errno));
return 0;
}
inet_aton(socks_server_address, &socks5_address);
upstream_address.sin_family=AF_INET;
upstream_address.sin_addr.s_addr=socks5_address.s_addr;
upstream_address.sin_port=htons(socks_server_port);
while (1)
{
if (connect(upstream_connection->fd, (struct sockaddr *) &upstream_address, sizeof(upstream_address)) != -1)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] upstream connected.\n", upstream_connection->idstr);
upstream_connection->state=SOCKS5_SEND_METHODS;
return 1;
}
switch (errno)
{
case EINTR:
continue;
case EINPROGRESS:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] upstream connecting\n", upstream_connection->idstr);
upstream_connection->state=UPSTREAM_CONNECTING;
return 1;
default:
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to start nonblocking connection\n", upstream_connection->idstr);
close(upstream_connection->fd);
upstream_connection->fd=-1;
return 0;
}
}
return 0;
}
void socks5_upstream_prepare_events(upstream_connection_t *upstream_connection)
{
if (!upstream_connection || upstream_connection->fd == -1)
return;
switch (upstream_connection->state)
{
case UPSTREAM_CLOSED:
case SOCKS5_TOR_DONE_RESOLVE:
return;
case UPSTREAM_CONNECTING:
case SOCKS5_SEND_METHODS:
case SOCKS5_SEND_CONNECT:
case SOCKS5_TOR_SEND_RESOLVE:
FD_SET(upstream_connection->fd, &fdset_write);
break;
case SOCKS5_READ_METHODS:
case SOCKS5_READ_CONNECT:
case SOCKS5_TOR_READ_RESOLVE:
FD_SET(upstream_connection->fd, &fdset_read);
break;
case UPSTREAM_CONNECTED:
if (!buffer_space(upstream_connection->input) && !upstream_connection->output->count)
return;
if (buffer_space(upstream_connection->input))
FD_SET(upstream_connection->fd, &fdset_read);
if (upstream_connection->output->count)
FD_SET(upstream_connection->fd, &fdset_write);
break;
}
// fd_upstream included in read or write set
if (upstream_connection->fd > fd_max)
fd_max=upstream_connection->fd;
}
void socks5_upstream_handle_events(upstream_connection_t *upstream_connection)
{
static u_int8_t buffer[300];
u_int8_t length;
u_int8_t client_methods[]={0x05,0x01,0x00};
int count;
if (!upstream_connection || upstream_connection->fd == -1)
return;
switch (upstream_connection->state)
{
case UPSTREAM_CLOSED:
case SOCKS5_TOR_DONE_RESOLVE:
return;
case UPSTREAM_CONNECTING:
if (FD_ISSET(upstream_connection->fd, &fdset_write))
tcp_upstream_connect_end(upstream_connection);
if (upstream_connection->state == UPSTREAM_CONNECTED)
upstream_connection->state=SOCKS5_SEND_METHODS;
break;
case SOCKS5_SEND_METHODS:
if (!FD_ISSET(upstream_connection->fd, &fdset_write))
break;
if ((count=write(upstream_connection->fd, client_methods, sizeof(client_methods))) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to send client methods - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
upstream_connection->state=SOCKS5_READ_METHODS;
break;
case SOCKS5_READ_METHODS:
if (!FD_ISSET(upstream_connection->fd, &fdset_read))
break;
if ((count=read(upstream_connection->fd, buffer, sizeof(buffer))) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to read server method - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
if (buffer[0] != 0x05 || buffer[1] != 0x00)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unexpected socks reply for methods [%x][%x]\n", upstream_connection->idstr, buffer[0], buffer[1]);
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
if (!upstream_connection->server_name)
upstream_connection->state=SOCKS5_SEND_CONNECT;
else
upstream_connection->state=SOCKS5_TOR_SEND_RESOLVE;
break;
case SOCKS5_TOR_SEND_RESOLVE:
if (!FD_ISSET(upstream_connection->fd, &fdset_write))
break;
length=strlen(upstream_connection->server_name);
buffer[0]=0x05;
buffer[1]=0xf0;
buffer[2]=0x00;
buffer[3]=0x03;
buffer[4]=length;
memcpy(buffer+5, upstream_connection->server_name, length);
buffer[6+length]=0x00;
buffer[7+length]=0x50;
if ((count=write(upstream_connection->fd, buffer, 7+length)) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to send resolve request - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
upstream_connection->state=SOCKS5_TOR_READ_RESOLVE;
break;
case SOCKS5_TOR_READ_RESOLVE:
if (!FD_ISSET(upstream_connection->fd, &fdset_read))
break;
if ((count=read(upstream_connection->fd, buffer, sizeof(buffer))) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to read resolve reply - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
if (buffer[0] == 0x05 && buffer[1] == 0x00 && buffer[2] == 0x00 && buffer[3] == 0x01)
memcpy(&upstream_connection->server_address, buffer+4, 4);
else
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unexpected resolve reply [%x] [%x]\n", upstream_connection->idstr, buffer[0], buffer[1]);
ts_log_hex(buffer, count);
}
upstream_connection->resultcode=buffer[1];
upstream_connection->state=SOCKS5_TOR_DONE_RESOLVE;
break;
case SOCKS5_SEND_CONNECT:
if (!FD_ISSET(upstream_connection->fd, &fdset_write))
break;
buffer[0]=0x05;
buffer[1]=0x01;
buffer[2]=0x00;
buffer[3]=0x01;
memcpy(buffer+4, &upstream_connection->server_address,4);
memcpy(buffer+8, &upstream_connection->server_port,2);
if ((count=write(upstream_connection->fd, buffer, 10)) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to send connect request - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
upstream_connection->state=SOCKS5_READ_CONNECT;
break;
case SOCKS5_READ_CONNECT:
if (!FD_ISSET(upstream_connection->fd, &fdset_read))
break;
if ((count=read(upstream_connection->fd, buffer, sizeof(buffer))) == -1)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to read connect reply - error [%s]\n", upstream_connection->idstr, strerror(errno));
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
if (buffer[0] != 0x05 || buffer[1] != 0x00)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unexpected server connect reply [%x][%x]\n", upstream_connection->idstr, buffer[0], buffer[1]);
upstream_connection->state=UPSTREAM_CLOSED;
break;
}
upstream_connection->state=UPSTREAM_CONNECTED;
break;
case UPSTREAM_CONNECTED:
if (FD_ISSET(upstream_connection->fd, &fdset_read))
upstream_connection->functions->read(upstream_connection);
if (upstream_connection->fd != -1 && FD_ISSET(upstream_connection->fd, &fdset_write))
upstream_connection->functions->write(upstream_connection);
break;
}
}
upstream_functions_t socks5_upstream_functions=
{
.connect = &socks5_upstream_connect_start,
.prepare_events = &socks5_upstream_prepare_events,
.handle_events = &socks5_upstream_handle_events,
.disconnect = &tcp_upstream_disconnect,
.read = &tcp_upstream_read,
.write = &tcp_upstream_write
};
//
// HTTP upstream functions
//
u_int8_t http_200_response_header[]=
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Server: AVNE Status 0.5\r\n"
"\r\n";
u_int8_t http_404_html_response[]=
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Server: AVNE Status 0.5\r\n"
"\r\n"
"<!DOCTYPE html>\r\n"
"<html lang=\"en\">\r\n"
" <head>\r\n"
" <title>AVNE status</title>\r\n"
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n"
" <meta name=\"description\" content=\"404 Not Found\">\r\n"
" </head>\r\n"
" <body>\r\n"
" <h1>404 Not Found</h1>\r\n"
" </body>\r\n"
"</html>\r\n";
char html_response_start[]=
"<!DOCTYPE html>\r\n"
"<html lang=\"en\">\r\n"
" <head>\r\n"
" <title>AVNE status</title>\r\n"
" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\r\n"
" <meta name=\"description\" content=\"current status\">\r\n"
" <style>\r\n"
" body {font-family: Arial, Helvetica, sans-serif;}\r\n"
" td, a, b {padding : 0.25em 1em;}\r\n"
" </style>\r\n"
" </head>\r\n"
" <body>\r\n"
" <h1>Another Virtual Network Environment....</h1>\r\n"
" <h3>%s<a href=""http://10.10.10.10"">refresh</a></h3>\r\n"
" <table>\r\n"
" <tr><td>TCP Connections</td><td>%d</td><td>active</td><td>%d</td></tr>\r\n"
" <tr><td>DNS Connections</td><td>%d</td><td>active</td><td>%d</td></tr>\r\n"
" <tr><td>DNS failes</td><td>%d</td><td>rejects</td><td>%d</td></tr>\r\n"
" <tr><td>DNS cache entries</td><td>%d</td><td>cache hits</td><td>%d</td></tr>\r\n"
" </table>\r\n"
"";
char html_response_end[]=
" </body>\r\n"
"</html>";
int http_upstream_connect(upstream_connection_t *upstream_connection)
{
// write a status html page
if ((upstream_connection->fd=open(http_filename, O_RDWR | O_CREAT | O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1)
{
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to open http_file [%s] - error [%s]\n", http_filename, strerror(errno));
return 0;
}
dprintf(upstream_connection->fd, html_response_start,
get_log_time(),
tcp_connection_total_count, tcp_connection_count,
dns_connection_total_count, dns_connection_count,
dns_connection_fail_count, dns_connection_reject_count,
dns_cache_count, dns_connection_cache_hit_count,
dns_connection_reject_count);
// write list of open tcp connections
char client_ip_string[20],server_ip_string[20];
tcp_connection_t *tcp_connection=tcp_connection_root;
dprintf(upstream_connection->fd, "<h3>List of open TCP Connections</h3>\r\n");
while (tcp_connection)
{
ip_string(tcp_connection->client_address, client_ip_string, sizeof(client_ip_string));
ip_string(tcp_connection->server_address, server_ip_string, sizeof(server_ip_string));
dprintf(upstream_connection->fd,"<p>[%s] [%s:%u] -> [%s:%u] <b>(%s)</b></p>\r\n",
tcp_connection->idstr,
client_ip_string, ntohs(tcp_connection->client_port),
server_ip_string, ntohs(tcp_connection->server_port),
dns_cache_get_name(tcp_connection->server_address));
tcp_connection=tcp_connection->next;
}
// write list of open dns connections
dns_connection_t *dns_connection=dns_connection_root;
dprintf(upstream_connection->fd, "<h3>List of open DNS Connections</h3>\r\n");
while (dns_connection)
{
dprintf(upstream_connection->fd,"<p>[%s] [%s]</p>\r\n", dns_connection->idstr,
dns_cache_entry_get_name(dns_connection->cache_entry));
dns_connection=dns_connection->next;
}
dprintf(upstream_connection->fd, html_response_end);
lseek(upstream_connection->fd, 0, SEEK_SET);
upstream_connection->state=UPSTREAM_CONNECTING;
return 1;
}
void http_upstream_prepare_events(upstream_connection_t *upstream_connection)
{
if (!upstream_connection || upstream_connection->fd == -1)
return;
switch (upstream_connection->state)
{
case UPSTREAM_CONNECTING:
if (upstream_connection->output->count)
{
if (strcasestr((char*) upstream_connection->output->data, "GET / "))
{
buffer_add(upstream_connection->input, http_200_response_header, sizeof(http_200_response_header)-1);
lseek(upstream_connection->fd, 0, SEEK_SET);
}
else
buffer_add(upstream_connection->input, http_404_html_response, sizeof(http_404_html_response)-1);
upstream_connection->output->count=0;
upstream_connection->state=UPSTREAM_CONNECTED;
}
break;
case UPSTREAM_CONNECTED:
if (!buffer_space(upstream_connection->input))
return;
FD_SET(upstream_connection->fd, &fdset_read);
if (upstream_connection->fd > fd_max)
fd_max=upstream_connection->fd;
break;
}
}
upstream_functions_t http_upstream_functions=
{
.connect = &http_upstream_connect,
.prepare_events = &http_upstream_prepare_events,
.disconnect = &tcp_upstream_disconnect,
.read = &tcp_upstream_read,
.write = &tcp_upstream_write,
.handle_events = &tcp_upstream_handle_events
};
//
// The next code does absolutely nothing...
//
int nop_upstream_int(upstream_connection_t *upstream_connection)
{
return 1;
}
void nop_upstream_void(upstream_connection_t *upstream_connection)
{
}
upstream_functions_t nop_upstream_functions=
{
.connect = &nop_upstream_int,
.disconnect = &nop_upstream_int,
.read = &nop_upstream_void,
.write = &nop_upstream_void,
.prepare_events = &nop_upstream_void,
.handle_events = &nop_upstream_void
};
//
// TCP
//
void log_tcp_datagram(struct iphdr *ip_header, struct tcphdr *tcp_header, int what, const char *where, const char *message)
{
char client_ip_string[20],server_ip_string[20];
ip_string(ip_header->saddr, client_ip_string, sizeof(client_ip_string));
ip_string(ip_header->daddr, server_ip_string, sizeof(server_ip_string));
ts_log(0, what, where, "[%s:%u] -> [%s:%u] %s\n",
client_ip_string, ntohs(tcp_header->source),
server_ip_string, ntohs(tcp_header->dest),
message);
}
void log_tcp_connection(tcp_connection_t *tcp_connection, int what, const char *where, const char *message)
{
char client_ip_string[20],server_ip_string[20];
ip_string(tcp_connection->client_address, client_ip_string, sizeof(client_ip_string));
ip_string(tcp_connection->server_address, server_ip_string, sizeof(server_ip_string));
ts_log(0, what, where, "[%s] [%s:%u] -> [%s:%u] %s\n", tcp_connection->idstr,
client_ip_string, ntohs(tcp_connection->client_port),
server_ip_string, ntohs(tcp_connection->server_port),
message);
}
int seq_between(u_int32_t seq, u_int32_t seq_start, u_int32_t seq_end)
{
if (seq_end >= seq_start)
return (seq >= seq_start && seq <= seq_end) ? 1 : 0;
// seq_end has wrapped, this means that seq *might* have wrapped also...
return (seq_between(seq, seq_start, 0xffffffff) || seq_between(seq, 0, seq_end)) ? 1 : 0;
}
void tcp_connection_upstream_init(tcp_connection_t *tcp_connection, upstream_functions_t *functions)
{
tcp_connection->upstream.fd=-1;
tcp_connection->upstream.state=UPSTREAM_CLOSED;
tcp_connection->upstream.id=tcp_connection->id;
tcp_connection->upstream.idstr=tcp_connection->idstr;
tcp_connection->upstream.server_address=tcp_connection->server_address;
tcp_connection->upstream.server_port=tcp_connection->server_port;
tcp_connection->upstream.input=&tcp_connection->output;
tcp_connection->upstream.output=&tcp_connection->input;
tcp_connection->upstream.functions=functions;
}
void tcp_connection_init(tcp_connection_t *tcp_connection, struct iphdr *ip_header, struct tcphdr * tcp_header)
{
char idstr[16];
if (!(tcp_connection && ip_header && tcp_header))
return;
memset(tcp_connection, 0, sizeof(tcp_connection_t));
tcp_connection->id=tcp_connection_total_count++;
sprintf(idstr,"tcp:%d",tcp_connection->id);
tcp_connection->idstr=strdup(idstr);
tcp_connection->server_address=ip_header->daddr;
tcp_connection->server_port=tcp_header->dest;
tcp_connection->client_address=ip_header->saddr;
tcp_connection->client_port=tcp_header->source;
tcp_connection->server_seq=1;
tcp_connection->server_mss=MTU-sizeof(struct iphdr)-sizeof(struct tcphdr);
tcp_connection->server_window=TCP_BUFFER_SIZE;
tcp_connection->tcp_state=TCP_LISTEN;
tcp_connection->client_seq=ntohl(tcp_header->seq);
tcp_connection->client_window=ntohs(tcp_header->window);
buffer_init(&tcp_connection->input, TCP_BUFFER_SIZE);
buffer_init(&tcp_connection->output, TCP_BUFFER_SIZE);
tcp_connection->upstream.functions=&nop_upstream_functions;
// insert into linked list
tcp_connection->next=tcp_connection_root;
tcp_connection_root=tcp_connection;
tcp_connection_count++;
char client_ip_string[20],server_ip_string[20];
ip_string(tcp_connection->client_address, client_ip_string, sizeof(client_ip_string));
ip_string(tcp_connection->server_address, server_ip_string, sizeof(server_ip_string));
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] [%s:%u] -> [%s:%u] (%s) - active [%d]\n",
tcp_connection->idstr,
client_ip_string, ntohs(tcp_connection->client_port),
server_ip_string, ntohs(tcp_connection->server_port),
dns_cache_get_name(tcp_connection->server_address),
tcp_connection_count);
}
void tcp_connection_done(tcp_connection_t *tcp_connection)
{
tcp_connection_t *tcp_connection_previous;
tcp_connection->upstream.functions->disconnect(&tcp_connection->upstream);
// remove from linked list
tcp_connection_count--;
if (tcp_connection_root == tcp_connection)
tcp_connection_root=tcp_connection->next;
else
{
tcp_connection_previous=tcp_connection_root;
while (tcp_connection_previous && tcp_connection_previous->next != tcp_connection)
tcp_connection_previous=tcp_connection_previous->next;
if (tcp_connection_previous)
tcp_connection_previous->next=tcp_connection->next;
else
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to find previous linked-list entry\n", tcp_connection->idstr);
}
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] active [%d]\n", tcp_connection->idstr, tcp_connection_count);
buffer_done(&tcp_connection->input);
buffer_done(&tcp_connection->output);
free(tcp_connection->idstr);
free(tcp_connection);
}
void tcp_connection_prepare_events(tcp_connection_t *tcp_connection)
{
if (!tcp_connection->upstream.functions->prepare_events)
return;
tcp_connection->upstream.functions->prepare_events(&tcp_connection->upstream);
if (tcp_connection->output.count)
tcp_connection_write(tcp_connection);
}
void tcp_connection_handle_events(tcp_connection_t *tcp_connection)
{
u_int16_t previous_input_count, previous_upstream_state, bytes_to_ack;
if (!tcp_connection->upstream.functions->handle_events)
return;
previous_input_count=tcp_connection->input.count;
previous_upstream_state=tcp_connection->upstream.state;
tcp_connection->upstream.functions->handle_events(&tcp_connection->upstream);
if ((bytes_to_ack = previous_input_count - tcp_connection->input.count))
{
tcp_connection->client_seq_ack+=bytes_to_ack;
tcp_connection_send_datagram(tcp_connection, TCP_PSH+TCP_ACK, 0, 0);
}
if (tcp_connection->output.count)
tcp_connection_write(tcp_connection);
if (tcp_connection->upstream.state == UPSTREAM_CLOSED &&
previous_upstream_state != UPSTREAM_CLOSED)
tcp_connection_server_close(tcp_connection);
}
u_int16_t tcp_connection_send_datagram(tcp_connection_t *tcp_connection, u_int8_t flags, u_int8_t *data, u_int32_t data_size)
{
// send a server->client datagram
//
// NOTES:
//
// 1) uses the same buffer as the client->server datagram
// 2) never sends a datagram larger than MTU
// 3) never sends more data than MIN(client_mss , server_mss, client_window_left)
// 4) returns the number of data bytes send
typedef struct
{
u_int8_t kind;
u_int8_t length;
u_int16_t mss;
} tcp_options_t;
struct iphdr *ip_header;
struct tcphdr *tcp_header;
tcp_options_t *tcp_options;
u_int8_t *tcp_data;
u_int16_t data_count=0;
u_int16_t datagram_size;
int count;
//ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", tcp_connection->idstr);
ip_header=(struct iphdr *) datagram;
ip_header->ihl=5;
ip_header->version=4;
ip_header->id=htons((u_int16_t) tcp_connection->server_seq);
ip_header->frag_off=0x40; // do not fragment flag
ip_header->ttl=10;
ip_header->protocol=IPPROTO_TCP;
ip_header->check=0;
ip_header->saddr=tcp_connection->server_address;
ip_header->daddr=tcp_connection->client_address;
tcp_header=(struct tcphdr *) (datagram+sizeof(struct iphdr));
tcp_header->source=tcp_connection->server_port;
tcp_header->dest=tcp_connection->client_port;
tcp_header->seq=htonl(tcp_connection->server_seq);
tcp_header->ack_seq=htonl(tcp_connection->client_seq_ack);
tcp_header->fin=(flags & TCP_FIN) ? 1 : 0;
tcp_header->syn=(flags & TCP_SYN) ? 1 : 0;
tcp_header->rst=(flags & TCP_RST) ? 1 : 0;
tcp_header->psh=(flags & TCP_PSH) ? 1 : 0;
tcp_header->ack=(flags & TCP_ACK) ? 1 : 0;
tcp_header->urg=(flags & TCP_URG) ? 1 : 0;
tcp_header->res1=0;
tcp_header->res2=0;
tcp_header->window=htons(TCP_BUFFER_SIZE);
if (tcp_header->syn) // add mss option, no data
{
tcp_options = (tcp_options_t *) (datagram+sizeof(struct iphdr)+sizeof(struct tcphdr));
tcp_options->kind=TCPOPT_MAXSEG;
tcp_options->length=TCPOLEN_MAXSEG;
tcp_options->mss=htons(MTU-sizeof(struct iphdr)-sizeof(struct tcphdr));
tcp_header->doff=6;
datagram_size=sizeof(struct iphdr)+sizeof(struct tcphdr)+sizeof(tcp_options_t);
ip_header->tot_len=htons(datagram_size);
}
else // add data, no options
{
u_int32_t client_window_left;
if (tcp_connection->server_seq >= tcp_connection->server_seq_ack)
client_window_left=tcp_connection->client_window-(tcp_connection->server_seq-tcp_connection->server_seq_ack);
else
{
if (tcp_connection->server_seq_ack-tcp_connection->server_seq > 0x8fffffff)
client_window_left=tcp_connection->client_window-(0xffffffff-tcp_connection->server_seq_ack+tcp_connection->server_seq);
else
{
client_window_left=0;
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unable to determine client_window_left - server_seq [%u] server_seq_ack [%u]\n",
tcp_connection->idstr, tcp_connection->server_seq, tcp_connection->server_seq_ack);
}
}
if (!client_window_left)
{
//ts_log(0, LOG_INFO, __FUNCTION__, "[%s] no client window left\n", tcp_connection->idstr);
}
data_count=data_size;
if (data_count > tcp_connection->client_mss)
data_count=tcp_connection->client_mss;
if (data_count > tcp_connection->server_mss)
data_count=tcp_connection->server_mss;
if (data_count > client_window_left)
data_count=client_window_left;
if ((data_count == 0) && data_size)
return 0;
tcp_data = datagram+sizeof(struct iphdr)+sizeof(struct tcphdr);
if (data && data_count)
memcpy(tcp_data, data, data_count);
tcp_header->doff=5;
datagram_size=sizeof(struct iphdr)+sizeof(struct tcphdr)+data_count;
ip_header->tot_len=htons(datagram_size);
}
ip_header->check=ip_checksum(ip_header, sizeof(struct iphdr));
tcp_header->check=tcp_checksum(ip_header, tcp_header);
while (1)
{
count=write(fd_tun, datagram, datagram_size);
if (count == -1)
{
if (errno == EINTR)
continue;
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to write [%u] bytes to fd_tun - error [%s]\n", tcp_connection->idstr, datagram_size, strerror(errno));
}
else
if (count != datagram_size)
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] datagram size [%u] != written size [%d]\n", tcp_connection->idstr, datagram_size, count);
break;
}
return data_count;
}
tcp_connection_t* get_tcp_connection(struct iphdr *ip_header, struct tcphdr *tcp_header)
{
tcp_connection_t *tcp_connection;
// new connection ?
if (tcp_header->syn)
{
tcp_connection_t *tcp_connection=malloc(sizeof(tcp_connection_t));
tcp_connection_init(tcp_connection, ip_header, tcp_header);
// address 10.10.10.10:80 is for the internal status server...
if (tcp_connection->server_address == 0x0a0a0a0a && tcp_connection->server_port == htons(80))
tcp_connection_upstream_init(tcp_connection, &http_upstream_functions);
else
tcp_connection_upstream_init(tcp_connection, default_tcp_upstream_functions);
return tcp_connection;
}
// lookup existing connection
tcp_connection=tcp_connection_root;
while (tcp_connection)
{
if (tcp_connection->server_address == ip_header->daddr &&
tcp_connection->server_port == tcp_header->dest &&
tcp_connection->client_address == ip_header->saddr &&
tcp_connection->client_port == tcp_header->source)
return tcp_connection;
tcp_connection=tcp_connection->next;
}
log_tcp_datagram(ip_header, tcp_header, LOG_ERROR, __FUNCTION__,"failed to find tcp connection");
return 0;
}
void tcp_connection_read(tcp_connection_t *tcp_connection, u_int8_t *data, u_int16_t data_size)
{
if (!data_size)
return;
buffer_add(&tcp_connection->input, data, data_size);
tcp_connection->client_seq+=data_size;
}
void tcp_connection_write(tcp_connection_t *tcp_connection)
{
//ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", tcp_connection->idstr);
u_int16_t tcp_data_size;
while (tcp_connection->output.count)
{
tcp_data_size=tcp_connection_send_datagram(tcp_connection, TCP_ACK, tcp_connection->output.data, tcp_connection->output.count);
if (!tcp_data_size)
{
//ts_log(0, LOG_INFO, __FUNCTION__, "[%s] failed to write to client\n", tcp_connection->idstr);
break;
}
buffer_remove(&tcp_connection->output, tcp_data_size);
tcp_connection->server_seq+=tcp_data_size;
}
}
void tcp_connection_server_close(tcp_connection_t *tcp_connection)
{
ts_log(0,LOG_INFO, __FUNCTION__,"[%s]\n", tcp_connection->idstr);
if (tcp_connection->tcp_state != TCP_ESTABLISHED)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] wrong tcp_state [%u]\n", tcp_connection->idstr, tcp_connection->tcp_state);
return;
}
tcp_connection_send_datagram(tcp_connection, TCP_FIN+TCP_ACK, 0,0);
tcp_connection->server_seq++;
tcp_connection->tcp_state=TCP_FIN_WAIT1;
}
void tcp_connection_process_header_options(tcp_connection_t *tcp_connection, struct tcphdr *tcp_header)
{
u_int8_t *tcp_option, *tcp_data, tcp_option_length;
if (tcp_header->doff == 5)
return;
tcp_option=(u_int8_t *) tcp_header + sizeof(struct tcphdr);
tcp_data=(u_int8_t*) tcp_header + (tcp_header->doff << 2);
while (tcp_option < tcp_data)
{
tcp_option_length=*(tcp_option+1);
switch (*tcp_option)
{
case TCPOPT_EOL:
case TCPOPT_NOP:
tcp_option_length=1;
break;
case TCPOPT_MAXSEG:
tcp_connection->client_mss=ntohs(*((u_int16_t *) (tcp_option+2)));
break;
case TCPOPT_WINDOW:
tcp_connection->client_wscale=*(tcp_option+2);
break;
default:
break;
}
tcp_option+=tcp_option_length;
}
}
void handle_tcp(u_int16_t datagram_size)
{
struct tcphdr *tcp_header;
tcp_connection_t *tcp_connection;
u_int8_t *tcp_data;
u_int32_t ip_header_size;
u_int32_t tcp_header_size, tcp_header_seq, tcp_header_seq_ack, tcp_data_size;
u_int16_t tcp_check;
ip_header_size=ip_header->ihl << 2;
tcp_header=(struct tcphdr*) (datagram+ip_header_size);
if ((tcp_check=tcp_checksum(ip_header, tcp_header)) != tcp_header->check)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "checksum error - header [%x], calculated [%x]\n", ntohs(tcp_header->check), ntohs(tcp_check));
return;
}
if (!(tcp_connection=get_tcp_connection(ip_header, tcp_header)))
{
tcp_connection_t *tcp_connection_reply=malloc(sizeof(tcp_connection_t));
tcp_connection_init(tcp_connection_reply, ip_header, tcp_header);
tcp_connection_send_datagram(tcp_connection_reply, TCP_RST+TCP_ACK, 0, 0);
tcp_connection_done(tcp_connection_reply);
return;
}
if (tcp_header->rst)
{
log_tcp_connection(tcp_connection, LOG_INFO, __FUNCTION__, "got RST");
// TODO - response datagram?
// NO, but... sometimes (very seldom) I see packets from client after RST...
tcp_connection_done(tcp_connection);
return;
}
tcp_header_size=tcp_header->doff << 2;
tcp_header_seq = ntohl(tcp_header->seq);
tcp_header_seq_ack = ntohl(tcp_header->ack_seq);
tcp_data=(u_int8_t *) tcp_header+tcp_header_size;
tcp_data_size=datagram_size-ip_header_size-tcp_header_size;
tcp_connection->client_window=ntohs(tcp_header->window);
tcp_connection_process_header_options(tcp_connection, tcp_header);
switch (tcp_connection->tcp_state)
{
case TCP_LISTEN:
tcp_connection->client_seq++;
tcp_connection->client_seq_ack=tcp_connection->client_seq;
tcp_connection_send_datagram(tcp_connection, TCP_SYN+TCP_ACK, 0, 0);
tcp_connection->server_seq++;
tcp_connection->tcp_state=TCP_SYN_RECV;
break;
case TCP_SYN_RECV:
if (tcp_header->ack == 1 && tcp_header_seq == tcp_connection->client_seq)
{
tcp_connection->tcp_state=TCP_ESTABLISHED;
tcp_connection->upstream.functions->connect(&tcp_connection->upstream);
break;
}
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] TCP_SYN_RECV client did not correctly finisch TCP handshake\n", tcp_connection->idstr);
tcp_connection_send_datagram(tcp_connection, TCP_RST, 0, 0);
tcp_connection_done(tcp_connection);
tcp_connection=0;
break;
case TCP_ESTABLISHED:
// check if the segment has the expected seq nr
if (tcp_header_seq == tcp_connection->client_seq)
{
if (tcp_header->fin)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_ESTABLISHED got FIN\n", tcp_connection->idstr);
tcp_connection->client_seq++;
tcp_connection->client_seq_ack=tcp_connection->client_seq;
tcp_connection_send_datagram(tcp_connection, TCP_ACK+TCP_FIN, 0, 0);
tcp_connection->server_seq++;
tcp_connection->tcp_state=TCP_LAST_ACK;
break;
}
if (seq_between(tcp_header_seq_ack, tcp_connection->server_seq_ack, tcp_connection->server_seq))
tcp_connection->server_seq_ack=tcp_header_seq_ack;
else
ts_log(0,LOG_ERROR, __FUNCTION__,"[%s] TCP_ESTABLISHED unexpected ACK value\n", tcp_connection->idstr);
}
else
{
// keepalive packet?
if ((tcp_header_seq == tcp_connection->client_seq-1)
&& seq_between(tcp_header_seq_ack, tcp_connection->server_seq_ack, tcp_connection->server_seq))
{
tcp_connection_send_datagram(tcp_connection, TCP_ACK, 0, 0);
break;
}
else
{
// probably retransmission, ignore the segment
break;
}
}
tcp_connection_read(tcp_connection, tcp_data, tcp_data_size);
// an ACK or window update from the client may have opened the
// send-window to the client so always try to write after a read...
tcp_connection_write(tcp_connection);
break;
case TCP_CLOSE_WAIT:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_CLOSE_WAIT\n", tcp_connection->idstr);
tcp_connection->client_seq++;
tcp_connection->client_seq_ack=tcp_connection->client_seq;
tcp_connection_send_datagram(tcp_connection, TCP_ACK+TCP_FIN, 0, 0);
tcp_connection->server_seq++;
tcp_connection->tcp_state=TCP_LAST_ACK;
break;
case TCP_LAST_ACK:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_LAST_ACK\n", tcp_connection->idstr);
if (tcp_header->ack && tcp_header_seq_ack == tcp_connection->server_seq)
{
tcp_connection_done(tcp_connection);
tcp_connection=0;
}
break;
case TCP_FIN_WAIT1:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_FIN_WAIT1\n", tcp_connection->idstr);
// only ACK on server FIN ?
if (tcp_header->ack && !tcp_header->fin && tcp_header_seq_ack == tcp_connection->server_seq)
{
tcp_connection->tcp_state = TCP_FIN_WAIT2;
break;
}
// only FIN from client ?
if (tcp_header->fin && !tcp_header->ack)
{
tcp_connection->client_seq=tcp_header_seq+1;
tcp_connection->client_seq_ack=tcp_header_seq+1;
tcp_connection_send_datagram(tcp_connection, TCP_ACK, 0, 0);
tcp_connection->tcp_state = TCP_CLOSING;
break;
}
// received FIN+ACK ?
if (tcp_header->fin && tcp_header->ack && tcp_header_seq_ack == tcp_connection->server_seq)
{
tcp_connection->client_seq=tcp_header_seq+1;
tcp_connection->client_seq_ack=tcp_header_seq+1;
tcp_connection_send_datagram(tcp_connection, TCP_ACK, 0, 0);
tcp_connection->tcp_state = TCP_CLOSE;
tcp_connection_done(tcp_connection);
break;
}
//ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unexpected TCP_FIN_WAIT1 response", tcp_connection->idstr);
break;
case TCP_FIN_WAIT2:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_FIN_WAIT2\n", tcp_connection->idstr);
// cient FIN ?
if (tcp_header->fin)
{
tcp_connection->client_seq=tcp_header_seq+1;
tcp_connection->client_seq_ack=tcp_header_seq+1;
tcp_connection_send_datagram(tcp_connection, TCP_ACK, 0, 0);
tcp_connection_done(tcp_connection);
break;
}
//ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] unexpected TCP_FIN_WAIT2 response", tcp_connection->idstr);
break;
case TCP_CLOSING:
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] TCP_CLOSING\n", tcp_connection->idstr);
if (tcp_header->ack && tcp_header_seq_ack == tcp_connection->server_seq)
tcp_connection_done(tcp_connection);
break;
// TCP_TIME_WAIT is only needed for connections that can loose
// the server ACK on the last FIN, this can not happen in our case so
// we go to the closed state immediately
case TCP_TIME_WAIT:
case TCP_CLOSE:
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] client sended on a CLOSED connection\n", tcp_connection->idstr);
break;
}
}
//
// DNS cache
//
typedef struct dns_header_s dns_header_t;
typedef struct dns_answer_s dns_answer_t;
struct dns_header_s
{
u_int16_t transaction_id;
#if __BYTE_ORDER == __LITTLE_ENDIAN
u_int16_t rd:1;
u_int16_t tc:1;
u_int16_t aa:1;
u_int16_t opcode:4;
u_int16_t qr:1;
u_int16_t rcode:4;
u_int16_t cd:1;
u_int16_t ad:1;
u_int16_t z:1;
u_int16_t ra:1;
#elif __BYTE_ORDER == __BIG_ENDIAN
u_int16_t qr:1;
u_int16_t opcode:4;
u_int16_t aa:1;
u_int16_t tc:1;
u_int16_t rd:1;
u_int16_t ra:1;
u_int16_t z:1;
u_int16_t ad:1;
u_int16_t cd:1;
u_int16_t rcode:4;
#else
# error "Adjust your <bits/endian.h> defines"
#endif
union
{
u_int16_t qdcount;
u_int16_t zocount;
};
union
{
u_int16_t ancount;
u_int16_t prcount;
};
union
{
u_int16_t nscount;
u_int16_t upcount;
};
union
{
u_int16_t adcount;
u_int16_t arcount;
};
} __attribute__((packed));
struct dns_answer_s
{
u_int8_t name_compression;
u_int8_t name_offset;
u_int16_t type;
u_int16_t class;
u_int32_t ttl;
u_int16_t rdl;
u_int32_t address;
} __attribute__((packed));
void log_dns_message(dns_header_t *dns_header, u_int16_t dns_header_size)
{
ts_log(0, LOG_INFO, __FUNCTION__, "\n");
ts_log(0, LOG_INFO, 0, "transaction_id [%x]\n", ntohs(dns_header->transaction_id));
ts_log(0, LOG_INFO, 0, "qr [%u] opcode [%u] aa [%u] tc [%u] rd [%u]\n",
dns_header->qr, dns_header->opcode, dns_header->aa, dns_header->tc, dns_header->rd);
ts_log(0, LOG_INFO, 0, "ra [%u] z [%u] ad [%u] cd [%u] rcode [%u]\n",
dns_header->ra, dns_header->z, dns_header->ad, dns_header->cd, dns_header->rcode);
ts_log(0, LOG_INFO, 0, "qd/zo count [%u]\n" , ntohs(dns_header->qdcount));
ts_log(0, LOG_INFO, 0, "an/pr count [%u]\n" , ntohs(dns_header->ancount));
ts_log(0, LOG_INFO, 0, "ns/up count [%u]\n" , ntohs(dns_header->nscount));
ts_log(0, LOG_INFO, 0, "ad/ar count [%u]\n" , ntohs(dns_header->adcount));
ts_log_hex(((u_int8_t *) dns_header) + sizeof(dns_header_t), dns_header_size-sizeof(dns_header_t));
}
void dns_cache_entry_init(dns_cache_entry_t *dns_cache_entry)
{
memset(dns_cache_entry, 0, sizeof(dns_cache_entry_t));
dns_cache_entry->expires=time(0)+3;
// insert into linked list
dns_cache_count++;
dns_cache_entry->next=dns_cache_entry_root;
dns_cache_entry_root=dns_cache_entry;
}
void dns_cache_entry_done(dns_cache_entry_t *dns_cache_entry)
{
dns_cache_entry_t *dns_cache_entry_previous;
dns_cache_count--;
if (dns_cache_entry_root == dns_cache_entry)
dns_cache_entry_root=dns_cache_entry->next;
else
{
dns_cache_entry_previous=dns_cache_entry_root;
while (dns_cache_entry_previous && dns_cache_entry_previous->next != dns_cache_entry)
dns_cache_entry_previous=dns_cache_entry_previous->next;
if (dns_cache_entry_previous)
dns_cache_entry_previous->next=dns_cache_entry->next;
else
ts_log(0, LOG_ERROR, __FUNCTION__, "failed to find previous linked-list entry\n");
}
free(dns_cache_entry->name);
free(dns_cache_entry->request);
free(dns_cache_entry->response);
free(dns_cache_entry);
}
char *dns_cache_entry_get_name(dns_cache_entry_t *dns_cache_entry)
{
static char question_name[256];
u_int8_t length, *length_pos, *question;
if (dns_cache_entry->name)
return dns_cache_entry->name;
if (dns_cache_entry && dns_cache_entry->request)
{
question=dns_cache_entry->request+sizeof(dns_header_t);
strncpy(question_name, (char *) question, sizeof(question_name));
length_pos= (u_int8_t*) question_name;
while ((length=*length_pos))
{
*length_pos='.';
length_pos+=length+1;
}
dns_cache_entry->name=strdup(question_name+1);
}
else
strcpy(question_name, "?unknown question name");
return question_name+1;
}
// nbo
#define DNS_TYPE_A 0x0100
#define DNS_CLASS_IN 0x0100
u_int32_t dns_cache_entry_get_address(dns_cache_entry_t *dns_cache_entry)
{
char *query_question;
dns_header_t *header;
dns_answer_t *answer;
u_int16_t *query_type, *query_class, i;
if (!dns_cache_entry->response)
return 0;
if (dns_cache_entry->address)
return dns_cache_entry->address;
header=(dns_header_t *) dns_cache_entry->response;
if (ntohs(header->qdcount) != 1 || ntohs(header->ancount) == 0 || header->rcode != 0)
{
// ts_log(0, LOG_INFO, __FUNCTION__, "no address - rcode [%u] query count [%u] answer count [%u]\n",
// header->rcode, ntohs(header->qdcount), ntohs(header->ancount));
return 0;
}
query_question=(char *) header + sizeof(dns_header_t);
query_type= (u_int16_t *) (query_question+strlen(query_question)+1); // only safe if 1 query
query_class=query_type+1;
if (*query_type != DNS_TYPE_A || *query_class != DNS_CLASS_IN)
{
ts_log(0, LOG_INFO, __FUNCTION__, "no address for query of type [%x] class [%x]\n", ntohs(*query_type), ntohs(*query_class));
return 0;
}
answer=(dns_answer_t *)(query_class+1);
for (i=0; i < header->ancount; i++)
{
if (answer->name_compression != 0xc0)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "expected name compression\n");
return 0;
}
if (answer->type == DNS_TYPE_A && answer->class == DNS_CLASS_IN)
{
dns_cache_entry->address=answer->address;
return (answer->address);
}
answer = (dns_answer_t*) ((u_int8_t *) answer + sizeof(dns_answer_t) - 4 + ntohs(answer->rdl));
}
return 0;
}
void dns_cache_entry_set_request(dns_cache_entry_t *dns_cache_entry, struct udphdr *udp_header)
{
u_int8_t *udp_data=(u_int8_t *) udp_header+sizeof(struct udphdr);
u_int16_t udp_data_size=ntohs(udp_header->len)-sizeof(struct udphdr);
dns_cache_entry->request_size=udp_data_size;
dns_cache_entry->request=malloc(udp_data_size);
memcpy(dns_cache_entry->request, udp_data, udp_data_size);
// find out if its a local or fake request
char* name=dns_cache_entry_get_name(dns_cache_entry);
int local_name_length=strlen(_res.defdname);
if (strchr(name, '.') == NULL)
{
dns_cache_entry->request_type=DNS_REQUEST_PROBE;
return;
}
dns_cache_entry->request_type=DNS_REQUEST_NORMAL;
if (local_name_length)
{
char* local_name_pos=strcasestr(name, _res.defdname);
if (local_name_pos && *(local_name_pos+local_name_length) == 0)
dns_cache_entry->request_type=DNS_REQUEST_LOCAL;
}
}
void dns_cache_entry_set_response(dns_cache_entry_t *dns_cache_entry, u_int8_t *udp_data, u_int16_t udp_data_size)
{
char buffer[32];
dns_cache_entry->expires=time(0)+3600;
dns_cache_entry->response_size=udp_data_size;
dns_cache_entry->response=malloc(udp_data_size);
memcpy(dns_cache_entry->response, udp_data, udp_data_size);
ip_string(dns_cache_entry_get_address(dns_cache_entry), buffer, sizeof(buffer));
ts_log(0, LOG_INFO, __FUNCTION__, "response address [%s]\n", buffer);
}
void dns_cache_entry_build_response(dns_cache_entry_t *dns_cache_entry, u_int8_t rcode, u_int32_t server_address)
{
dns_header_t *dns_header;
dns_answer_t answer;
switch (rcode)
{
case 3: // Name Error
case 5: // Refused
dns_cache_entry_set_response(dns_cache_entry, dns_cache_entry->request, dns_cache_entry->request_size);
dns_header= (dns_header_t *) dns_cache_entry->response;
dns_header->qr=1;
dns_header->rcode=rcode;
break;
case 0: // Success!
answer.name_compression=0xc0;
answer.name_offset=sizeof(dns_header_t);
answer.type=DNS_TYPE_A;
answer.class=DNS_CLASS_IN;
answer.ttl=htonl(3600);
answer.rdl=htons(4);
answer.address=server_address;
dns_cache_entry->expires=time(0)+3600;
dns_cache_entry->response_size=dns_cache_entry->request_size+sizeof(answer);
dns_cache_entry->response=malloc(dns_cache_entry->response_size);
memcpy(dns_cache_entry->response, dns_cache_entry->request, dns_cache_entry->request_size);
memcpy(dns_cache_entry->response+dns_cache_entry->request_size, &answer, sizeof(answer));
dns_header=(dns_header_t *) dns_cache_entry->response;
dns_header->qr=1;
dns_header->rcode=rcode;
dns_header->ancount=htons(1);
break;
default:
ts_log(0, LOG_ERROR, __FUNCTION__, "rcode [%u] is not supported\n", rcode);
break;
}
}
int dns_cache_entry_request_equal(dns_cache_entry_t *dns_cache_entry, struct udphdr *udp_header)
{
u_int8_t *udp_data=(u_int8_t *) udp_header+sizeof(struct udphdr);
u_int16_t udp_data_size=ntohs(udp_header->len)-sizeof(struct udphdr);
// compare udp_data with request except for the first two bytes which contain the transaction_id
if (dns_cache_entry->request_size != udp_data_size)
return 0;
return (memcmp(dns_cache_entry->request+2, udp_data+2, udp_data_size-2) == 0) ? 1 : 0;
}
int dns_cache_entry_handle_expired(dns_cache_entry_t *dns_cache_entry)
{
dns_connection_t *dns_connection, *dns_connection_next;
if (time(0) < dns_cache_entry->expires)
return 0;
// remove DNS connections that have this cache_entry
if (dns_cache_entry->response == 0)
{
dns_connection=dns_connection_root;
while (dns_connection)
{
dns_connection_next=dns_connection->next;
if (dns_connection->cache_entry == dns_cache_entry)
dns_connection_done(dns_connection);
dns_connection=dns_connection_next;
}
}
dns_cache_entry_done(dns_cache_entry);
return 1;
}
dns_cache_entry_t *dns_cache_get_entry(struct udphdr *udp_header)
{
ts_log(0, LOG_INFO, __FUNCTION__, "start\n");
dns_cache_entry_t *dns_cache_entry, *dns_cache_entry_next;
dns_cache_entry=dns_cache_entry_root;
while (dns_cache_entry)
{
dns_cache_entry_next=dns_cache_entry->next;
if (dns_cache_entry_request_equal(dns_cache_entry, udp_header))
{
if (dns_cache_entry_handle_expired(dns_cache_entry))
break;
else
return dns_cache_entry;
}
dns_cache_entry=dns_cache_entry_next;
}
// insert a new entry
ts_log(0, LOG_INFO, __FUNCTION__, "insert new entry start\n");
dns_cache_entry=malloc(sizeof(dns_cache_entry_t));
dns_cache_entry_init(dns_cache_entry);
dns_cache_entry_set_request(dns_cache_entry, udp_header);
return dns_cache_entry;
}
char *dns_cache_get_name(u_int32_t address)
{
static char unknown[]="unknown";
dns_cache_entry_t *dns_cache_entry=dns_cache_entry_root;
while (dns_cache_entry)
{
if (address == dns_cache_entry_get_address(dns_cache_entry))
return dns_cache_entry_get_name(dns_cache_entry);
dns_cache_entry=dns_cache_entry->next;
}
return unknown;
}
//
// DNS connection
//
void dns_connection_upstream_init(dns_connection_t *dns_connection, upstream_functions_t *functions)
{
//ts_log(0, LOG_INFO, __FUNCTION__, "start\n");
dns_connection->upstream.fd=-1;
dns_connection->upstream.state=UPSTREAM_CLOSED;
dns_connection->upstream.id=dns_connection->id;
dns_connection->upstream.idstr=dns_connection->idstr;
if (functions == &socks5_upstream_functions && dns_connection_use_tor_resolve)
{
dns_connection->upstream.server_address=0;
dns_connection->upstream.server_port=0;
dns_connection->upstream.server_name=strdup(dns_cache_entry_get_name(dns_connection->cache_entry));
}
else
{
dns_connection->upstream.server_address=0x08080808; // Google DNS snooper
dns_connection->upstream.server_port=dns_connection->server_port;
dns_connection->upstream.server_name=0;
}
dns_connection->upstream.input=&dns_connection->output;
dns_connection->upstream.output=&dns_connection->input;
dns_connection->upstream.functions=functions;
}
void dns_connection_init(dns_connection_t *dns_connection, struct iphdr *ip_header, struct udphdr *udp_header)
{
char idstr[16];
u_int8_t *udp_data;
u_int16_t udp_data_size, udp_data_size_nbo;
dns_cache_entry_t *cache_entry;
udp_data=(u_int8_t *) udp_header+sizeof(struct udphdr);
udp_data_size=ntohs(udp_header->len)-sizeof(struct udphdr);
log_dns_message((dns_header_t *) udp_data, udp_data_size);
memset(dns_connection, 0, sizeof(dns_connection_t));
dns_connection->id=dns_connection_total_count++;
sprintf(idstr,"dns:%d",dns_connection->id);
dns_connection->idstr=strdup(idstr);
dns_connection->transaction_id=*((u_int16_t *) udp_data);
dns_connection->server_address=ip_header->daddr;
dns_connection->server_port=udp_header->dest;
dns_connection->client_address=ip_header->saddr;
dns_connection->client_port=udp_header->source;
// insert connection into the linked list
dns_connection->next=dns_connection_root;
dns_connection_root=dns_connection;
dns_connection_count++;
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] active [%d]\n",dns_connection->idstr, dns_connection_count);
cache_entry=dns_cache_get_entry(udp_header);
dns_connection->cache_entry=cache_entry;
dns_connection_upstream_init(dns_connection, default_dns_upstream_functions);
if (cache_entry->response)
{
dns_connection_cache_hit_count++;
dns_connection_send_datagram(dns_connection);
return;
}
// not resolved yet, test for unwanted requests
if (cache_entry->request_type == DNS_REQUEST_LOCAL)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] dropping local DNS request [%s]\n", dns_connection->idstr, dns_cache_entry_get_name(cache_entry));
dns_connection_reject_count++;
dns_cache_entry_build_response(cache_entry, 3, 0);
dns_connection_send_datagram(dns_connection);
dns_cache_entry_done(cache_entry);
return;
}
if (cache_entry->request_type == DNS_REQUEST_PROBE)
{
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] dropping probe DNS request [%s]\n", dns_connection->idstr, dns_cache_entry_get_name(cache_entry));
dns_connection_reject_count++;
dns_cache_entry_build_response(cache_entry, 5, 0);
dns_connection_send_datagram(dns_connection);
dns_cache_entry_done(cache_entry);
return;
}
// are we the first to see this request?
if (++cache_entry->response_clients == 1)
{
// then we need an upstream connection....
buffer_init(&dns_connection->input, DNS_BUFFER_SIZE);
buffer_init(&dns_connection->output, DNS_BUFFER_SIZE);
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] name [%s]\n",dns_connection->idstr, dns_cache_entry_get_name(dns_connection->cache_entry));
// convert the UDP request into a TCP request and put it in the input buffer
udp_data_size_nbo=htons(udp_data_size);
buffer_add(&dns_connection->input, (u_int8_t *) &udp_data_size_nbo, sizeof(udp_data_size_nbo));
buffer_add(&dns_connection->input, udp_data, udp_data_size);
dns_connection->upstream.functions->connect(&dns_connection->upstream);
}
}
void dns_connection_done(dns_connection_t *dns_connection)
{
dns_connection_t *dns_connection_previous;
dns_connection->upstream.functions->disconnect(&dns_connection->upstream);
// remove connection from linked list
dns_connection_count--;
dns_connection->cache_entry->response_clients--;
if (dns_connection_root == dns_connection)
dns_connection_root=dns_connection->next;
else
{
dns_connection_previous=dns_connection_root;
while (dns_connection_previous && dns_connection_previous->next != dns_connection)
dns_connection_previous=dns_connection_previous->next;
if (dns_connection_previous)
dns_connection_previous->next=dns_connection->next;
else
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to find previous linked-list entry\n", dns_connection->idstr);
}
ts_log(0, LOG_INFO, __FUNCTION__, "[%s] active [%d] total/cache_hits/failed/rejected [%d/%d/%d/%d] \n",dns_connection->idstr,
dns_connection_count, dns_connection_total_count, dns_connection_cache_hit_count,
dns_connection_fail_count, dns_connection_reject_count);
free(dns_connection->upstream.server_name);
buffer_done(&dns_connection->input);
buffer_done(&dns_connection->output);
free(dns_connection->idstr);
free(dns_connection);
}
void dns_connection_send_datagram(dns_connection_t *dns_connection)
{
struct iphdr *ip_header;
struct udphdr *udp_header;
u_int8_t *udp_data;
u_int16_t datagram_size;
int data_count;
ts_log(0, LOG_INFO, __FUNCTION__, "start\n");
ip_header=(struct iphdr *) datagram;
ip_header->ihl=5;
ip_header->version=4;
ip_header->id=htons((u_int16_t) dns_connection_count);
ip_header->frag_off=0x40; // do not fragment flag
ip_header->ttl=10;
ip_header->protocol=IPPROTO_UDP;
ip_header->check=0;
ip_header->saddr=dns_connection->server_address;
ip_header->daddr=dns_connection->client_address;
udp_header=(struct udphdr *)(datagram +sizeof(struct iphdr));
udp_header->source=dns_connection->server_port;
udp_header->dest=dns_connection->client_port;
udp_header->len=htons(sizeof(struct udphdr) + dns_connection->cache_entry->response_size);
udp_header->check=0;
udp_data=(u_int8_t *) udp_header+sizeof(struct udphdr);
*((u_int16_t *) udp_data)=dns_connection->transaction_id;
memcpy(udp_data+2, dns_connection->cache_entry->response+2, dns_connection->cache_entry->response_size-2);
log_dns_message((dns_header_t *) udp_data, dns_connection->cache_entry->response_size);
datagram_size=sizeof(struct iphdr)+sizeof(struct udphdr)+dns_connection->cache_entry->response_size;
ip_header->tot_len=htons(datagram_size);
ip_header->check=ip_checksum(ip_header, sizeof(struct iphdr));
udp_header->check=udp_checksum(ip_header, udp_header);
while (1)
{
data_count=write(fd_tun, datagram, datagram_size);
if (data_count == -1)
{
if (errno == EINTR)
continue;
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to write datagram - error [%s]\n", dns_connection->idstr, strerror(errno));
}
break;
}
if (data_count != datagram_size)
ts_log(0, LOG_ERROR, __FUNCTION__, "[%s] failed to write datagram - size [%u] written [%d]\n", dns_connection->idstr, datagram_size, data_count);
dns_connection_done(dns_connection);
}
void dns_connection_prepare_events(dns_connection_t *dns_connection)
{
dns_connection->upstream.functions->prepare_events(&dns_connection->upstream);
}
void dns_connection_handle_events(dns_connection_t *dns_connection)
{
dns_connection_t *dns_connection_next;
dns_cache_entry_t *dns_cache_entry;
u_int16_t response_size;
dns_connection->upstream.functions->handle_events(&dns_connection->upstream);
if (dns_connection->upstream.functions == &socks5_upstream_functions && dns_connection_use_tor_resolve)
{
// Tor - DNS response expected...
if (dns_connection->upstream.state != SOCKS5_TOR_DONE_RESOLVE)
return;
if (dns_connection->upstream.resultcode == 0x04) // SOCKS5_HOST_UNREACHABLE
dns_cache_entry_build_response(dns_connection->cache_entry, 3, dns_connection->upstream.server_address); // report name error
else
dns_cache_entry_build_response(dns_connection->cache_entry, 0, dns_connection->upstream.server_address);
}
else
{
// TCP-response, is it complete?
if (dns_connection->output.count <= 2)
return;
response_size=ntohs(*((u_int16_t *) dns_connection->output.data));
if ((response_size+2) != dns_connection->output.count)
return;
dns_cache_entry_set_response(dns_connection->cache_entry, dns_connection->output.data+2, response_size);
}
// send datagram to all connections with our cache_entry
dns_cache_entry=dns_connection->cache_entry;
dns_connection=dns_connection_root;
while (dns_cache_entry->response_clients && dns_connection)
{
dns_connection_next=dns_connection->next;
if (dns_connection->cache_entry == dns_cache_entry)
dns_connection_send_datagram(dns_connection);
dns_connection=dns_connection_next;
}
}
//
// UDP
//
void handle_udp(u_int16_t datagram_size)
{
struct udphdr *udp_header;
u_int16_t ip_header_size, udp_check;
dns_connection_t *dns_connection;
ip_header_size=ip_header->ihl << 2;
udp_header=(struct udphdr*) (datagram+ip_header_size);
//ts_log(0,LOG_INFO, __FUNCTION__,"start\n");
if ((udp_check=udp_checksum(ip_header, udp_header)) != udp_header->check)
{
ts_log(0, LOG_ERROR, __FUNCTION__, "wrong udp checksum got [0x%x] calculated [0x%x]\n", ntohs(udp_header->check), ntohs(udp_check));
return;
}
switch (ntohs(udp_header->dest))
{
case 53:
dns_connection=malloc(sizeof(dns_connection_t));
dns_connection_init(dns_connection, ip_header, udp_header);
break;
default:
break;
}
}
//
// ICMP
//
void handle_icmp(u_int16_t datagram_size)
{
struct icmphdr *icmp_header;
u_int32_t source_address;
u_int16_t ip_header_size, icmp_message_size;
ip_header_size=ip_header->ihl << 2;
icmp_header=(struct icmphdr*) (datagram+ip_header_size);
icmp_message_size=datagram_size-ip_header_size;
if (ip_checksum(icmp_header, icmp_message_size))
{
ts_log(0, LOG_INFO, __FUNCTION__, "wrong checksum\n");
return;
}
ts_log(0, LOG_INFO, __FUNCTION__, "got ICMP message of type [%u] code [%u] size [%d]\n",icmp_header->type, icmp_header->code, icmp_message_size);
switch (icmp_header->type)
{
case ICMP_ECHO:
// just a simple ping responder for testing....
icmp_header->type=ICMP_ECHOREPLY;
icmp_header->checksum=0;
icmp_header->checksum=ip_checksum(icmp_header, icmp_message_size);
source_address=ip_header->saddr;
ip_header->saddr=ip_header->daddr;
ip_header->daddr=source_address;
ip_header->check=0;
ip_header->check=ip_checksum(ip_header, ip_header_size);
if (write(fd_tun, datagram, datagram_size) == -1)
ts_log(0, LOG_INFO, __FUNCTION__, "failed to write ICMP_ECHO reply - error [%s]\n", strerror(errno));
break;
default:
break;
}
}
//
// IP
//
void handle_datagram(u_int16_t datagram_size)
{
if (ip_checksum(ip_header, ip_header->ihl << 2))
{
ts_log(0, LOG_ERROR, __FUNCTION__, "wrong ip_checksum\n");
return;
}
if (ntohs(ip_header->tot_len) != datagram_size)
ts_log(0, LOG_INFO, __FUNCTION__, "packet size mismatch: got [%u] - tot_len [%u]\n", datagram_size, ntohs(ip_header->tot_len));
switch (ip_header->protocol)
{
case IPPROTO_ICMP:
handle_icmp(datagram_size);
break;
case IPPROTO_UDP:
handle_udp(datagram_size);
break;
case IPPROTO_TCP:
handle_tcp(datagram_size);
break;
}
}
void tun_handle_events()
{
int datagram_size;
if (!FD_ISSET(fd_tun, &fdset_read))
return;
while (1)
{
datagram_size=read(fd_tun, datagram, sizeof(datagram));
if (datagram_size == -1)
{
if (errno == EWOULDBLOCK)
break;
if (errno == EINTR)
continue;
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to read datagram - error [%s]\n", strerror(errno));
}
handle_datagram(datagram_size);
break;
}
}
void io_loop()
{
dns_connection_t *dns_connection, *dns_connection_next;
tcp_connection_t *tcp_connection, *tcp_connection_next;
while (child_running)
{
FD_ZERO(&fdset_read);
FD_ZERO(&fdset_write);
FD_SET(fd_tun, &fdset_read);
fd_max=fd_tun;
// prepare TCP events
tcp_connection=tcp_connection_root;
while (tcp_connection)
{
tcp_connection_prepare_events(tcp_connection);
tcp_connection=tcp_connection->next;
}
// prepare DNS events
dns_connection=dns_connection_root;
while (dns_connection)
{
dns_connection_prepare_events(dns_connection);
dns_connection=dns_connection->next;
}
// gather events
if (select(1+fd_max, &fdset_read, &fdset_write, 0, 0) == -1)
{
if (errno == EINTR)
continue;
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "select failed - error [%s]\n", strerror(errno));
}
// handle TCP events
tcp_connection=tcp_connection_root;
while (tcp_connection)
{
tcp_connection_next=tcp_connection->next;
tcp_connection_handle_events(tcp_connection);
tcp_connection=tcp_connection_next;
}
// handle DNS events
dns_connection=dns_connection_root;
while (dns_connection)
{
dns_connection_next=dns_connection->next;
dns_connection_handle_events(dns_connection);
dns_connection=dns_connection_next;
}
// handle client events
tun_handle_events();
}
}
void configure_network(char *tun_interface_name)
{
int fd;
struct ifreq interface_request;
struct rtentry route_entry;
struct sockaddr_in *socket_address, *gateway_address, *destination_address, *genmask;
// open tun interface
if ((fd_tun=open("/dev/net/tun", O_RDWR)) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to open tun device with name [%s] - error [%s]\n", tun_interface_name, strerror(errno));
memset(&interface_request, 0, sizeof(interface_request));
strcpy(interface_request.ifr_name, tun_interface_name);
interface_request.ifr_flags=IFF_TUN | IFF_NO_PI;
if(ioctl(fd_tun, TUNSETIFF, (void *) &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set tun device interface - error [%s]\n", strerror(errno));
if (fcntl(fd_tun, F_SETFL, O_NONBLOCK) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to make tun device nonblocking - error [%s]\n", strerror(errno));
// configure tun interface.
memset(&interface_request, 0, sizeof(interface_request));
strcpy(interface_request.ifr_name, tun_interface_name);
fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
// set address
socket_address= (struct sockaddr_in *) &interface_request.ifr_addr;
socket_address->sin_family=AF_INET;
inet_aton("10.0.0.1", &socket_address->sin_addr);
if (ioctl(fd, SIOCSIFADDR, (void *) &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set tun interface address - error [%s]\n", strerror(errno));
// set netmask
socket_address->sin_addr.s_addr=htonl(0xffffff00);
if (ioctl(fd, SIOCSIFNETMASK, (void *) &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set tun interface netmask - error [%s]\n", strerror(errno));
// set state
interface_request.ifr_flags=IFF_POINTOPOINT | IFF_RUNNING | IFF_NOARP | IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set tun interface UP flags - error [%s]\n", strerror(errno));
// set gateway
memset(&route_entry, 0, sizeof(route_entry));
route_entry.rt_flags = RTF_UP | RTF_GATEWAY;
route_entry.rt_metric = 0;
route_entry.rt_dev=tun_interface_name;
gateway_address= (struct sockaddr_in *) &route_entry.rt_gateway;
gateway_address->sin_family = AF_INET;
inet_aton("10.0.0.1", &gateway_address->sin_addr);
destination_address= (struct sockaddr_in *) &route_entry.rt_dst;
destination_address->sin_family = AF_INET;
destination_address->sin_addr.s_addr = INADDR_ANY;
genmask= (struct sockaddr_in *) &route_entry.rt_genmask;
genmask->sin_family = AF_INET;
genmask->sin_addr.s_addr = INADDR_ANY;
if (ioctl(fd, SIOCADDRT, &route_entry) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set tun interface default route - error [%s]\n", strerror(errno));
// MTU?
// configure localhost
memset(&interface_request, 0, sizeof(interface_request));
strcpy(interface_request.ifr_name, "lo");
socket_address->sin_family=AF_INET;
inet_aton("127.0.0.1", &socket_address->sin_addr);
if (ioctl(fd, SIOCSIFADDR, (void *) &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set localhost address - error [%s]\n", strerror(errno));
socket_address->sin_addr.s_addr=htonl(0xff000000);
if (ioctl(fd, SIOCSIFNETMASK, (void *) &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set localhost netmask - error [%s]\n", strerror(errno));
interface_request.ifr_flags=IFF_RUNNING | IFF_UP;
if (ioctl(fd, SIOCSIFFLAGS, &interface_request) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to change localhost state to UP - error [%s]\n", strerror(errno));
close(fd);
}
static void sigchld_handler(int sig)
{
int errno_process=errno;
if (waitpid(pid_child, NULL, WNOHANG) == pid_child)
child_running=0;
errno=errno_process;
}
void inject_program(int argc, char *argv[])
{
char filename[PATH_MAX];
char *hostname="AVNE";
int fd_netns_attach;
// run a program inside an existing network namespace
if (atoi(argv[1]) == 0)
{
dprintf(STDERR_FILENO, "avne: invalid pid [%s] - error [%s]\n", argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
sprintf(filename,"/proc/%s/ns/net", argv[1]);
if ((fd_netns_attach = open(filename, O_RDONLY)) == -1)
{
dprintf(STDERR_FILENO, "avne: failed to open netns of [%s] - error [%s]\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
if (setns(fd_netns_attach, CLONE_NEWNET) == -1)
{
dprintf(STDERR_FILENO, "avne: failed to switch to netns of [%s] - error [%s]\n", filename, strerror(errno));
exit(EXIT_FAILURE);
}
close(fd_netns_attach);
// unshare the uts namespace so we can change the host name
if (unshare(CLONE_NEWUTS) == -1)
{
dprintf(STDERR_FILENO, "avne: failed to unshare uts namespace - error [%s]\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (sethostname(hostname, strlen(hostname)) == -1)
{
dprintf(STDERR_FILENO, "avne: failed to change hostname to [%s] - error [%s]\n", hostname, strerror(errno));
exit(EXIT_FAILURE);
}
argv+=2;
if (setuid(getuid()) == -1)
{
dprintf(STDERR_FILENO, "avne: failed drop privileges - error [%s]\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (execvp(argv[0], argv) == -1)
{
dprintf(STDERR_FILENO, "avne: failed to exec program [%s] - error [%s]\n", argv[0], strerror(errno));
exit(EXIT_FAILURE);
}
}
void start_program(int argc, char *argv[])
{
char buffer[256];
char *filename=buffer;
char *hostname="AVNE";
int fd_netns_old;
struct sigaction sa_sigchld;
sprintf(filename,"/proc/%u/ns/net", getpid());
if ((fd_netns_old = open(filename, O_RDONLY)) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to open netns_old - error [%s]\n", strerror(errno));
// unshare the uts namespace so we can change the host name
if (unshare(CLONE_NEWUTS) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to unshare uts namespace - error [%s]\n", strerror(errno));
ts_log(0, LOG_INFO, __FUNCTION__, "setting hostname to [%s]\n", hostname);
if (sethostname(hostname, strlen(hostname)) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to change hostname to [%s] - error [%s]\n", hostname, strerror(errno));
// create a new network namespace
if (unshare(CLONE_NEWNET) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to unshare network namespace - error [%s]\n", strerror(errno));
configure_network("avni0");
// fork a child that execs the client program
sigemptyset(&sa_sigchld.sa_mask);
sa_sigchld.sa_handler = &sigchld_handler;
sa_sigchld.sa_flags = SA_RESTART | SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa_sigchld, 0) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to set SIGCHLD handler - error [%s]\n", strerror(errno));
pid_child=fork();
if (pid_child == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to fork - error [%s]\n", strerror(errno));
if (pid_child == 0)
{
close(fd_netns_old);
close(fd_tun);
if (setuid(getuid()) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed drop child privileges - error [%s]\n", argv[0], strerror(errno));
if (execvp(argv[0], argv) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to exec program [%s] - error [%s]\n", argv[0], strerror(errno));
}
printf("avne: child pid is [%d]\n",pid_child);
// change network namespace back to original namespace so we can make upstream connections
if (setns(fd_netns_old, CLONE_NEWNET) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to switch back to netns_old - error [%s]\n", strerror(errno));
if (setuid(getuid()) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed drop privileges - error [%s]\n", argv[0], strerror(errno));
}
void read_settings()
{
char conf_filename[PATH_MAX];
char *buffer=conf_filename;
char *buffer_pos, *line_pos, *value_pos;
FILE *stream;
char *line = 0;
size_t line_buffer_size = 0;
ssize_t line_length;
long port;
u_int32_t address;
// default settings
socks_server_address="127.0.0.1";
socks_server_port=9050;
dns_connection_use_tor_resolve=1;
default_tcp_upstream_functions=&socks5_upstream_functions;
default_dns_upstream_functions=&socks5_upstream_functions;
if (readlink( "/proc/self/exe", conf_filename, PATH_MAX ) == -1)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "failed to get program path - error [%s]\n", strerror(errno));
if (strlen(conf_filename)+6 > PATH_MAX)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "conf filename would be too long\n");
strcat(conf_filename, ".conf");
if ((stream=fopen(conf_filename, "r")) == 0)
{
ts_log(0, LOG_INFO, __FUNCTION__, "failed to read configuration file [%s] : using defaults\n", conf_filename);
return;
}
while ((line_length=getline(&line, &line_buffer_size, stream)) != -1)
{
buffer_pos=buffer;
line_pos=line;
while (line_pos <= line+line_length)
{
if (!isspace(*line_pos))
*buffer_pos++=*line_pos;
line_pos++;
}
if ((buffer[0] == '#') || !(value_pos=strchr(buffer,'=')))
continue;
*value_pos++=0;
if (strcasecmp(buffer, "socks_address") == 0)
{
if (inet_pton(AF_INET, value_pos, &address) == 0)
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "illegal value for socks_address [%s]\n", value_pos);
socks_server_address=strdup(value_pos);
continue;
}
if (strcasecmp(buffer, "socks_port") == 0)
{
port=strtol(value_pos,0, 10);
if (port > 1024 && port < 0xffff)
socks_server_port=port;
else
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "illegal value for socks_port [%s]\n", value_pos);
continue;
}
if (strcasecmp(buffer, "socks_tcp_connection") == 0)
{
if (strcmp(value_pos,"0") == 0)
default_tcp_upstream_functions=&tcp_upstream_functions;
{
if (strcmp(value_pos,"1") == 0)
default_tcp_upstream_functions=&socks5_upstream_functions;
else
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "illegal value for socks_tcp_connection [%s]\n", value_pos);
}
continue;
}
if (strcasecmp(buffer, "socks_dns_connection") == 0)
{
if (strcmp(value_pos,"0") == 0)
default_dns_upstream_functions=&tcp_upstream_functions;
else
{
if (strcmp(value_pos,"1") == 0)
default_dns_upstream_functions=&socks5_upstream_functions;
else
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "illegal value for socks_dns_connection [%s]\n", value_pos);
}
continue;
}
if (strcasecmp(buffer, "socks_dns_use_tor_resolve") == 0)
{
if (strcmp(value_pos,"0") == 0)
dns_connection_use_tor_resolve=0;
else
{
if (strcmp(value_pos,"1") == 0)
dns_connection_use_tor_resolve=1;
else
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "illegal value for socks_dns_use_tor_resolve [%s]\n", value_pos);
}
continue;
}
// unknown configuration option
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "unknown configuration option: name [%s] value [%s]\n", buffer, value_pos);
}
// sanity check
if (dns_connection_use_tor_resolve && (default_dns_upstream_functions != &socks5_upstream_functions))
ts_log(EXIT_FAILURE, LOG_ERROR, __FUNCTION__, "you specified socks_dns_use_tor_resolve=1 on a non-socks connection\n");
ts_log(0, LOG_INFO, __FUNCTION__, "\n");
ts_log(0, LOG_INFO, 0, "socks_address [%s]\n", socks_server_address);
ts_log(0, LOG_INFO, 0, "socks_port [%u]\n", socks_server_port);
ts_log(0, LOG_INFO, 0, "socks_tcp_connection [%d]\n", (default_tcp_upstream_functions == &socks5_upstream_functions) ? 1 : 0);
ts_log(0, LOG_INFO, 0, "socks_dns_connection [%d]\n", (default_dns_upstream_functions == &socks5_upstream_functions) ? 1 : 0);
ts_log(0, LOG_INFO, 0, "socks_dns_use_tor_resolve [%d]\n", dns_connection_use_tor_resolve);
free(line);
fclose(stream);
}
const char *program_version="0.5";
const char *program_options=""
"\n"
"Another Virtual Network Environment - version %s\n"
"\n"
"Usage 1:\n"
"\n"
" avne [program path] [program options]\n"
"\n"
"Examples:\n"
"\n"
" avne chromium --disable-cache\n"
" avne wget https://blog.torproject.org/\n"
"\n"
"Usage 2:\n"
"\n"
" avne --use-namespace [pid] [program path] [program options]\n"
"\n"
"Example:\n"
"\n"
" avne --use-namespace 1234 wireshark\n"
"\n"
"The --use-namespace option does not configure a new network environment, but\n"
"allows you to use an existing network environment. Specify a pid of a running\n"
"avne CHILD program (e.g. a firefox instance that was previously started by\n"
"avne)\n"
"\n";
int main(int argc, char *argv[])
{
res_init();
if (argc < 2 )
{
printf(program_options, program_version);
return EXIT_SUCCESS;
}
if (*argv[1] == '-')
{
if (strcasecmp(argv[1],"-h") == 0 || strcasecmp(argv[1],"--help") == 0)
{
printf(program_options, program_version);
return EXIT_SUCCESS;
}
if (strcasecmp(argv[1],"--use-namespace") != 0 || argc < 4)
{
printf("\n\nERROR: INVALID OPTION OR TOO FEW PARAMETERS\n\n");
printf(program_options, program_version);
return EXIT_FAILURE;
}
}
if (strcasecmp(argv[1],"--use-namespace") == 0)
{
inject_program(argc-1, argv+1);
return 0;
}
ts_log_open(log_filename, 0);
read_settings();
printf("AVNE started at %s\n", get_log_time());
start_program(argc-1, argv+1);
io_loop();
printf("AVNE closed\n");
close(fd_tun);
return EXIT_SUCCESS;
}