1
0
mirror of https://github.com/rofl0r/proxychains-ng synced 2026-05-18 11:49:52 +08:00

udp wip 2

This commit is contained in:
hugoc
2023-09-08 14:42:52 +02:00
Unverified
parent 2c5ad9f81a
commit 8348e42608
3 changed files with 648 additions and 16 deletions
+521 -6
View File
@@ -424,16 +424,152 @@ static int tunnel_to(int sock, ip_type ip, unsigned short port, proxy_type pt, c
}
/* Given a socket connected to a SOCKS5 proxy server, performs a UDP_ASSOCIATE handshake and returns BND_ADDR and BND_PORT if successfull*/
static int udp_associate(int sock, ip_type dst_addr, unsigned short dst_port, ip_type *bnd_addr, unsigned short *bnd_port, char *user, char *pass){
/* Given a socket connected to a SOCKS5 proxy server, performs a UDP_ASSOCIATE handshake and returns BND_ADDR and BND_PORT if successfull.
Pass NULL dst_addr and dst_port to fill those fields with 0 if expected local addr and port for udp sending are unknown (see RFC1928) */
static int udp_associate(int sock, ip_type* dst_addr, unsigned short dst_port, socks5_addr* bnd_addr, unsigned short* bnd_port, char* user, char* pass){
//TODO hugoc
PFUNC();
size_t ulen = strlen(user);
size_t passlen = strlen(pass);
if(ulen > 0xFF || passlen > 0xFF) {
proxychains_write_log(LOG_PREFIX "error: maximum size of 255 for user/pass!\n");
goto err;
}
int len;
unsigned char buff[BUFF_SIZE];
char ip_buf[INET6_ADDRSTRLEN];
int n_methods = ulen ? 2 : 1;
buff[0] = 5; // version
buff[1] = n_methods ; // number of methods
buff[2] = 0; // no auth method
if(ulen) buff[3] = 2; /// auth method -> username / password
if(2+n_methods != write_n_bytes(sock, (char *) buff, 2+n_methods))
goto err;
if(2 != read_n_bytes(sock, (char *) buff, 2))
goto err;
if(buff[0] != 5 || (buff[1] != 0 && buff[1] != 2)) {
if(buff[0] == 5 && buff[1] == 0xFF)
return BLOCKED;
else
goto err;
}
if(buff[1] == 2) {
// authentication
char in[2];
char out[515];
char *cur = out;
size_t c;
*cur++ = 1; // version
c = ulen & 0xFF;
*cur++ = c;
memcpy(cur, user, c);
cur += c;
c = passlen & 0xFF;
*cur++ = c;
memcpy(cur, pass, c);
cur += c;
if((cur - out) != write_n_bytes(sock, out, cur - out))
goto err;
if(2 != read_n_bytes(sock, in, 2))
goto err;
/* according to RFC 1929 the version field for the user/pass auth sub-
negotiation should be 1, which is kinda counter-intuitive, so there
are some socks5 proxies that return 5 instead. other programs like
curl work fine when the version is 5, so let's do the same and accept
either of them. */
if(!(in[0] == 5 || in[0] == 1))
goto err;
if(in[1] != 0)
return BLOCKED;
}
int buff_iter = 0;
buff[buff_iter++] = 5; // version
buff[buff_iter++] = 3; // udp_associate
buff[buff_iter++] = 0; // reserved
if(dst_addr) {
int v6 = dst_addr->is_v6;
buff[buff_iter++] = v6 ? 4 : 1; // ip v4/v6
memcpy(buff + buff_iter, dst_addr->addr.v6, v6?16:4); // dest host
buff_iter += v6?16:4;
memcpy(buff + buff_iter, &dst_port, 2); // dest port
buff_iter += 2;
} else {
buff[buff_iter++] = 1; //we put atyp = 1, should we put 0 ?
buff[buff_iter++] = 0; // v4 byte1
buff[buff_iter++] = 0; // v4 byte2
buff[buff_iter++] = 0; // v4 byte3
buff[buff_iter++] = 0; // v4 byte4
buff[buff_iter++] = 0; // port byte1
buff[buff_iter++] = 0; // port byte2
}
if(buff_iter != write_n_bytes(sock, (char *) buff, buff_iter))
goto err;
if(4 != read_n_bytes(sock, (char *) buff, 4))
goto err;
if(buff[0] != 5 || buff[1] != 0)
goto err;
bnd_addr->atyp = buff[3];
switch (buff[3]) {
case 1:
len = 4;
break;
case 4:
len = 16;
break;
case 3:
PDEBUG("BND_ADDR in UDP_ASSOCIATE response should not be a domain name!\n");
goto err;
break;
default:
goto err;
}
if(len != read_n_bytes(sock, (char *) buff, len))
goto err;
memcpy(bnd_addr->addr.v6, buff+4,(len==16)?16:4);
if(2 != read_n_bytes(sock, (char *) buff, 2))
goto err;
memcpy(bnd_port, buff, 2);
return SUCCESS;
err:
return SOCKET_ERROR;
}
/* Fills buf with the SOCKS5 udp request header for the target dst_addr:dst_port*/
static int udp_header(socks5_addr dst_addr, unsigned short dst_port , char frag, char * buf, size_t buflen) {
static int write_udp_header(socks5_addr dst_addr, unsigned short dst_port , char frag, char * buf, size_t buflen) {
if (buflen <= 262) { //TODO: make something cleaner
int size = 0;
int v6 = dst_addr.atyp == ATYP_V6;
if(dst_addr.atyp == ATYP_DOM){
size = dst_addr.addr.dom.len;
} else {
size = v6?16:4;
}
if (buflen <= size) {
return -1;
}
@@ -443,7 +579,7 @@ static int udp_header(socks5_addr dst_addr, unsigned short dst_port , char frag,
buf[buf_iter++] = frag; // frag
buf[buf_iter++] = dst_addr.atyp; // atyp
int v6 = dst_addr.atyp == ATYP_V6;
switch (dst_addr.atyp){
case ATYP_V6:
case ATYP_V4:
@@ -464,7 +600,147 @@ static int udp_header(socks5_addr dst_addr, unsigned short dst_port , char frag,
return buf_iter;
}
static int encapsulate_udp_packet(udp_relay_chain rcd, ip_type target_addr, unsigned short target_port, char frag) {
int send_udp_packet(int sockfd, udp_relay_chain chain, ip_type target_ip, unsigned short target_port, char frag, char * data, unsigned int data_len) {
if (chain.head == NULL ){
PDEBUG("provided chain is empty\n");
return -1;
}
char *dns_name = NULL;
char hostnamebuf[MSG_LEN_MAX];
size_t dns_len = 0;
socks5_addr target_addr;
// we use ip addresses with 224.* to lookup their dns name in our table, to allow remote DNS resolution
// the range 224-255.* is reserved, and it won't go outside (unless the app does some other stuff with
// the results returned from gethostbyname et al.)
// the hardcoded number 224 can now be changed using the config option remote_dns_subnet to i.e. 127
if(!target_ip.is_v6 && proxychains_resolver >= DNSLF_RDNS_START && target_ip.addr.v4.octet[0] == remote_dns_subnet) {
target_addr.atyp = ATYP_DOM;
dns_len = rdns_get_host_for_ip(target_ip.addr.v4, target_addr.addr.dom.name);
if(!dns_len) goto err;
else dns_name = target_addr.addr.dom.name;
target_addr.addr.dom.len = dns_len && 0xFF;
} else {
if(target_ip.is_v6){
target_addr.atyp = ATYP_V6;
memcpy(target_addr.addr.v6, target_ip.addr.v6, 16);
} else {
target_addr.atyp = ATYP_V4;
memcpy(target_addr.addr.v4.octet, target_ip.addr.v4.octet, 4);
}
}
PDEBUG("host dns %s\n", dns_name ? dns_name : "<NULL>");
// Allocate a new buffer for the packet data and all the headers
unsigned int headers_size = 0;
udp_relay_node * tmp = chain.head;
int len = 0;
while(tmp->next != NULL){
switch ((tmp->next)->bnd_addr.atyp)
{
case ATYP_V4:
len = 4;
break;
case ATYP_V6:
len = 6;
break;
case ATYP_DOM:
len = (tmp->next)->bnd_addr.addr.dom.len;
break;
default:
break;
}
headers_size += len;
tmp = tmp->next;
}
switch (target_addr.atyp)
{
case ATYP_V4:
len = 4;
break;
case ATYP_V6:
len = 6;
break;
case ATYP_DOM:
len = target_addr.addr.dom.len;
break;
default:
break;
}
headers_size += len;
char * buff = NULL;
if (NULL == (buff = (char*)malloc(headers_size+data_len))){
PDEBUG("error malloc buffer\n");
return -1;
}
// Append each header to the buffer
unsigned int written = 0;
unsigned int offset = 0;
tmp = chain.head;
while (tmp->next != NULL)
{
written = write_udp_header((tmp->next)->bnd_addr, (tmp->next)->bnd_port, 0, buff+offset, headers_size + data_len - offset);
if (written == -1){
PDEBUG("error write_udp_header\n");
goto err;
}
offset += written;
tmp = tmp->next;
}
written = write_udp_header(target_addr, target_port, 0, buff + offset, headers_size + data_len - offset);
if (written == -1){
PDEBUG("error write_udp_header\n");
goto err;
}
offset += written;
// Append data to the buffer
memcpy(buff + offset, data, data_len);
// Send the packet
// FIXME: should write_n_bytes be used here instead ?
if(chain.head->bnd_addr.atyp == ATYP_DOM){
PDEBUG("BND_ADDR of type DOMAINE (0x03) not supported yet\n");
goto err;
}
int v6 = chain.head->bnd_addr.atyp == ATYP_V6;
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = chain.head->bnd_port,
.sin_addr.s_addr = (in_addr_t) chain.head->bnd_addr.addr.v4.as_int,
};
struct sockaddr_in6 addr6 = {
.sin6_family = AF_INET6,
.sin6_port = chain.head->bnd_port,
};
if(v6) memcpy(&addr6.sin6_addr.s6_addr, chain.head->bnd_addr.addr.v6, 16);
sendto(sockfd, buff, offset+data_len, 0, (struct sockaddr *) (v6?(void*)&addr6:(void*)&addr), v6?sizeof(addr6):sizeof(addr) );
err:
free(buff);
return -1;
}
@@ -474,8 +750,10 @@ static int encapsulate_udp_packet(udp_relay_chain rcd, ip_type target_addr, unsi
#define ST "Strict chain"
#define RT "Random chain"
#define RRT "Round Robin chain"
#define UDPC "UDP_ASSOCIATE tcp socket chain"
static int start_chain(int *fd, proxy_data * pd, char *begin_mark) {
PFUNC();
int v6 = pd->ip.is_v6;
*fd = socket(v6?PF_INET6:PF_INET, SOCK_STREAM, 0);
@@ -514,6 +792,9 @@ static int start_chain(int *fd, proxy_data * pd, char *begin_mark) {
}
static proxy_data *select_proxy(select_type how, proxy_data * pd, unsigned int proxy_count, unsigned int *offset) {
PFUNC();
PDEBUG("offset: %d\n", *offset);
PDEBUG("state: %d\n", pd[0].ps);
unsigned int i = 0, k = 0;
if(*offset >= proxy_count)
return NULL;
@@ -780,6 +1061,240 @@ int connect_proxy_chain(int sock, ip_type target_ip,
return -1;
}
// int connect_to_lastnode(int *sock, udp_relay_chain chain){
// udp_relay_node * current_node = chain.head;
// //First connect to the chain head
// if(SUCCESS != start_chain(sock, &(current_node->pd), UDPC)){
// PDEBUG("start_chain failed\n");
// return -1;
// }
// // Connect to the rest of the chain
// while(current_node->next != NULL){
// if(SUCCESS != chain_step(sock, &(current_node->pd), &(current_node->next->pd))){
// PDEBUG("chain step failed\n");
// return -1;
// }
// current_node = current_node->next;
// }
// return SUCCESS;
// }
int add_node_to_chain(proxy_data * pd, udp_relay_chain * chain){
PFUNC();
// Allocate memory for the new node structure
udp_relay_node * new_node = NULL;
if(NULL == (new_node = (udp_relay_node *)malloc(sizeof(udp_relay_node)))){
PDEBUG("error malloc new node\n");
return -1;
}
new_node->next = NULL;
udp_relay_node * tmp = chain->head;
if(tmp == NULL){ // Means new_node is the first node to be created
chain->head = new_node;
new_node->prev = NULL;
} else {
// Moving to the end of the current chain
while(tmp->next != NULL){
tmp = tmp->next;
}
// Adding the new node at the end
tmp->next = new_node;
new_node->prev = tmp;
}
// Initializing the new node
new_node->pd.ip = pd->ip;
new_node->pd.port = pd->port;
new_node->pd.pt = pd->pt;
new_node->pd.ps = pd->ps;
strcpy(new_node->pd.user, pd->user);
strcpy(new_node->pd.pass, pd->pass);
// Connecting the new node tcp_socketfd to the associated proxy through the current chain
//
tmp = chain->head;
// First connect to the chain head
if(SUCCESS != start_chain(&(new_node->tcp_sockfd), &(tmp->pd), UDPC)){
PDEBUG("start_chain failed\n");
new_node->tcp_sockfd = -1;
goto err;
}
// Connect to the rest of the chain
while(tmp->next != NULL){
if(SUCCESS != chain_step(new_node->tcp_sockfd, &(tmp->pd), &(tmp->next->pd))){
PDEBUG("chain step failed\n");
new_node->tcp_sockfd = -1;
goto err;
}
tmp = tmp->next;
}
// Performing UDP_ASSOCIATE handshake in order to fill the new node BND_ADDR and BND_PORT
if(SUCCESS != udp_associate(new_node->tcp_sockfd, NULL, NULL, &(new_node->bnd_addr), &(new_node->bnd_port), new_node->pd.user, new_node->pd.pass)){
PDEBUG("udp_associate failed\n");
goto err;
}
PDEBUG("new node added and open to relay UDP packets\n");
return SUCCESS;
err:
// Ensure new node tcp socket is closed
if(new_node->tcp_sockfd != -1){
close(new_node->tcp_sockfd);
}
// Remove the new node from the chain
if(new_node->prev == NULL){ // means new_node is the only node in chain
chain->head = NULL;
} else{
(new_node->prev)->next = NULL;
}
// Free memory
free(new_node);
return -1;
}
int free_relay_chain_nodes(udp_relay_chain chain){
if(chain.head == NULL){
return SUCCESS;
}
udp_relay_node * current = chain.head;
udp_relay_node * next = NULL;
while(current != NULL){
next = current->next;
close(current->tcp_sockfd);
free(current);
current = next;
}
return SUCCESS;
}
udp_relay_chain * open_relay_chain(proxy_data *pd, unsigned int proxy_count, chain_type ct, unsigned int max_chains){
PFUNC();
// Allocate memory for the new relay chain
udp_relay_chain * new_chain = NULL;
if(NULL == (new_chain = (udp_relay_chain *)malloc(sizeof(udp_relay_chain)))){
PDEBUG("error malloc new chain\n");
return NULL;
}
new_chain->head = NULL;
new_chain->sockfd = -1;
unsigned int alive_count = 0;
unsigned int offset = 0;
proxy_data *p1;
switch (ct)
{
case DYNAMIC_TYPE:
PDEBUG("DYNAMIC_TYPE not yet supported for UDP\n");
goto error;
break;
case ROUND_ROBIN_TYPE:
PDEBUG("ROUND_ROBIN_TYPE not yet supported for UDP\n");
goto error;
break;
case STRICT_TYPE:
alive_count = calc_alive(pd, proxy_count);
offset = 0;
PDEBUG("opening STRICT_TYPE relay chain, alive_count=%d, offset=%d\n", alive_count, offset);
while((p1 = select_proxy(FIFOLY, pd, proxy_count, &offset))) {
if(SUCCESS != add_node_to_chain(p1, new_chain)) {
PDEBUG("add_node_to_chain failed\n");
p1->pt = BLOCKED_STATE;
goto error;
}
p1->pt = BUSY_STATE;
}
return new_chain;
break;
case RANDOM_TYPE:
PDEBUG("RANDOM_TYPE not yet supported for UDP\n");
goto error;
break;
default:
break;
}
error:
PDEBUG("error\n");
release_all(pd, proxy_count);
free_relay_chain_nodes(*new_chain);
free(new_chain);
errno = ETIMEDOUT;
return NULL;
}
void add_relay_chain(udp_relay_chain_list* chains_list, udp_relay_chain* new_chain){
new_chain->next = NULL;
if(chains_list->tail == NULL){ // The current list is empty, set head and tail to the new chain
chains_list->head = new_chain;
chains_list->tail = new_chain;
new_chain->prev = NULL;
} else {
// Add the new chain at the end
chains_list->tail->next = new_chain;
new_chain->prev = chains_list->tail;
chains_list->tail = new_chain;
}
}
void del_relay_chain(udp_relay_chain_list* chains_list, udp_relay_chain* chain){
if(chain == chains_list->head){
if(chain->next == NULL){
free(chain);
chains_list->head = NULL;
chains_list->tail = NULL;
}else{
chains_list->head = chain->next;
free(chain);
}
} else if (chain = chains_list->tail){
chains_list->tail = chain->prev;
free(chain);
} else {
chain->next->prev = chain->prev;
chain->prev->next = chain->next;
free(chain);
}
}
udp_relay_chain* get_relay_chain(udp_relay_chain_list chains_list, int sockfd){
udp_relay_chain* tmp = chains_list.head;
while(tmp != NULL){
if(tmp->sockfd == sockfd){
break;
}
tmp = tmp->next;
}
return tmp;
}
static pthread_mutex_t servbyname_lock;
void core_initialize(void) {
MUTEX_INIT(&servbyname_lock);