static SOCKADDR_LIST *dynamic_remote_addr(CLI *c) { #ifdef SO_ORIGINAL_DST socklen_t addrlen=sizeof(SOCKADDR_UNION); #endif /* SO_ORIGINAL_DST */ /* check if the address was already set by a dynamic protocol * implemented protocols: CONNECT * protocols to be implemented: SOCKS4 */ if(c->connect_addr.num) return &c->connect_addr; #ifdef SO_ORIGINAL_DST if(c->opt->option.transparent_dst) { c->connect_addr.num=1; c->connect_addr.addr=str_alloc(sizeof(SOCKADDR_UNION)); if(getsockopt(c->local_rfd.fd, SOL_IP, SO_ORIGINAL_DST, c->connect_addr.addr, &addrlen)) { sockerror("setsockopt SO_ORIGINAL_DST"); longjmp(c->err, 1); } return &c->connect_addr; } #endif /* SO_ORIGINAL_DST */ if(c->opt->option.delayed_lookup) { if(!name2addrlist(&c->connect_addr, c->opt->connect_name, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } return &c->connect_addr; } return &c->opt->connect_addr; /* use pre-resolved (static) addresses */ }
static int connect_remote(CLI *c) { /* connect to remote host */ SOCKADDR_UNION bind_addr, addr; SOCKADDR_LIST resolved_list, *address_list; int error; int s; /* destination socket */ u16 i; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) return -1; /* no host resolved */ address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(i=0; i<address_list->num; i++) { memcpy(&addr, address_list->addr + address_list->cur, sizeof(SOCKADDR_UNION)); address_list->cur=(address_list->cur+1)%address_list->num; /* race condition is possible, but harmless in this case */ if((s=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { sockerror("remote socket"); return -1; } if(alloc_fd(s)) return -1; if(c->bind_addr.num) { /* explicit local bind or transparent proxy */ memcpy(&bind_addr, &c->bind_addr.addr[0], sizeof(SOCKADDR_UNION)); if(bind(s, &bind_addr.sa, addr_len(bind_addr))<0) { sockerror("bind transparent"); closesocket(s); return -1; } } /* try to connect for the 1st time */ s_ntop(c->connecting_address, &addr); s_log(LOG_DEBUG, "%s connecting %s", c->opt->servname, c->connecting_address); if(!connect(s, &addr.sa, addr_len(addr))) return s; /* no error -> success (should not be possible) */ error=get_last_socket_error(); if(error!=EINPROGRESS && error!=EWOULDBLOCK) { s_log(LOG_ERR, "remote connect (%s): %s (%d)", c->connecting_address, my_strerror(error), error); closesocket(s); continue; /* next IP */ } if(!connect_wait(c, s, c->opt->timeout_connect)) return s; /* success! */ closesocket(s); /* error -> next IP */ } return -1; }
int name2addr(SOCKADDR_UNION *addr, char *name, char *default_host) { SOCKADDR_LIST addr_list; int retval; addr_list.num=0; addr_list.addr=NULL; retval=name2addrlist(&addr_list, name, default_host); if(retval>0) memcpy(addr, &addr_list.addr[0], sizeof *addr); if(addr_list.addr) str_free(addr_list.addr); return retval; }
unsigned name2addr(SOCKADDR_UNION *addr, char *name, int passive) { SOCKADDR_LIST *addr_list; unsigned retval; addr_list=str_alloc(sizeof(SOCKADDR_LIST)); addrlist_clear(addr_list, passive); retval=name2addrlist(addr_list, name); if(retval) addrlist2addr(addr, addr_list); str_free(addr_list->addr); str_free(addr_list); return retval; }
static int connect_remote(CLI *c) { /* connect remote host */ SOCKADDR_UNION addr; SOCKADDR_LIST resolved_list, *address_list; int fd, ind_try, ind_cur; /* setup address_list */ if(c->opt->option.delayed_lookup) { resolved_list.num=0; if(!name2addrlist(&resolved_list, c->opt->remote_address, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } address_list=&resolved_list; } else /* use pre-resolved addresses */ address_list=&c->opt->remote_addr; /* try to connect each host from the list */ for(ind_try=0; ind_try<address_list->num; ind_try++) { if(c->opt->failover==FAILOVER_RR) { ind_cur=address_list->cur; /* the race condition here can be safely ignored */ address_list->cur=(ind_cur+1)%address_list->num; } else { /* FAILOVER_PRIO */ ind_cur=ind_try; /* ignore address_list->cur */ } memcpy(&addr, address_list->addr+ind_cur, sizeof addr); c->fd=s_socket(addr.sa.sa_family, SOCK_STREAM, 0, 1, "remote socket"); if(c->fd<0) longjmp(c->err, 1); if(c->bind_addr.num) /* explicit local bind or transparent proxy */ local_bind(c); if(connect_blocking(c, &addr, addr_len(addr))) { closesocket(c->fd); c->fd=-1; continue; /* next IP */ } print_bound_address(c); fd=c->fd; c->fd=-1; return fd; /* success! */ } longjmp(c->err, 1); return -1; /* some C compilers require a return value */ }
static SOCKADDR_LIST *dynamic_remote_addr(CLI * c) { if (c->connect_addr.num) return &c->connect_addr; if (c->opt->option.delayed_lookup) { if (!name2addrlist(&c->connect_addr, c->opt->connect_name, DEFAULT_LOOPBACK)) { s_log(LOG_ERR, "No host resolved"); longjmp(c->err, 1); } return &c->connect_addr; } return &c->opt->connect_addr; }
unsigned addrlist_resolve(SOCKADDR_LIST *addr_list) { unsigned num=0, rnd; NAME_LIST *host; addrlist_reset(addr_list); for(host=addr_list->names; host; host=host->next) num+=name2addrlist(addr_list, host->name); switch(num) { case 0: case 1: addr_list->rr_val=0; break; default: /* randomize the initial value of round-robin counter */ /* ignore the error value and the distribution bias */ RAND_bytes((unsigned char *)&rnd, sizeof rnd); addr_list->rr_val=rnd%num; } return num; }