int _xioopen_proxy_prepare(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport) { struct hostent *host; retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr); retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve); retropt_string(opts, OPT_PROXY_AUTHORIZATION, &proxyvars->authstring); if (proxyvars->doresolve) { /* currently we only resolve to IPv4 addresses. This is in accordance to RFC 2396; however once it becomes clear how IPv6 addresses should be represented in CONNECT commands this code might be extended */ host = Gethostbyname(targetname); if (host == NULL) { int level = E_WARN; /* note: cast is req on AIX: */ Msg2(level, "gethostbyname(\"%s\"): %s", targetname, h_errno == NETDB_INTERNAL ? strerror(errno) : (char *)hstrerror(h_errno)/*0 h_messages[h_errno-1]*/); proxyvars->targetaddr = strdup(targetname); } else { #define LEN 16 /* www.xxx.yyy.zzz\0 */ if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) { return STAT_RETRYLATER; } snprintf(proxyvars->targetaddr, LEN, "%u.%u.%u.%u", (unsigned char)host->h_addr_list[0][0], (unsigned char)host->h_addr_list[0][1], (unsigned char)host->h_addr_list[0][2], (unsigned char)host->h_addr_list[0][3]); #undef LEN } } else { proxyvars->targetaddr = strdup(targetname); } proxyvars->targetport = htons(parseport(targetport, IPPROTO_TCP)); return STAT_OK; }
int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen) { struct servent *se; char *userid; /* generate socks header - points to final target */ sockhead->version = 4; sockhead->action = 1; sockhead->port = parseport(targetport, IPPROTO_TCP); /* network byte order */ if (retropt_string(opts, OPT_SOCKSPORT, socksport) < 0) { if ((se = getservbyname("socks", "tcp")) != NULL) { Debug1("\"socks/tcp\" resolves to %u", ntohs(se->s_port)); if ((*socksport = Malloc(6)) == NULL) { return -1; } sprintf(*socksport, "%u", ntohs(se->s_port)); } else { Debug1("cannot resolve service \"socks/tcp\", using %s", SOCKSPORT); if ((*socksport = strdup(SOCKSPORT)) == NULL) { errno = ENOMEM; return -1; } } } if (retropt_string(opts, OPT_SOCKSUSER, &userid) < 0) { if ((userid = getenv("LOGNAME")) == NULL) { if ((userid = getenv("USER")) == NULL) { userid = "anonymous"; } } } sockhead->userid[0] = '\0'; strncat(sockhead->userid, userid, *headlen-SIZEOF_STRUCT_SOCKS4-1); *headlen = SIZEOF_STRUCT_SOCKS4+strlen(userid)+1; return STAT_OK; }
/* we expect the form: port */ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, unsigned groups, int pf, int ipproto, int protname) { const char *portname = argv[1]; union sockaddr_union us; union sockaddr_union themunion; union sockaddr_union *them = &themunion; int socktype = SOCK_DGRAM; struct pollfd readfd; bool dofork = false; pid_t pid; char *rangename; char infobuff[256]; unsigned char buff1[1]; socklen_t uslen; socklen_t themlen; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); } if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 pf = PF_INET6; #else pf = PF_INET; #endif } retropt_socket_pf(opts, &pf); if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); uslen = socket_init(pf, &us); retropt_bind(opts, pf, socktype, IPPROTO_UDP, (struct sockaddr *)&us, &uslen, 1, fd->stream.para.socket.ip.res_opts[1], fd->stream.para.socket.ip.res_opts[0]); if (false) { ; #if WITH_IP4 } else if (pf == PF_INET) { us.ip4.sin_port = parseport(portname, ipproto); #endif #if WITH_IP6 } else if (pf == PF_INET6) { us.ip6.sin6_port = parseport(portname, ipproto); #endif } else { Error1("xioopen_ipdgram_listen(): unknown address family %d", pf); } retropt_bool(opts, OPT_FORK, &dofork); if (dofork) { if (!(xioflags & XIO_MAYFORK)) { Error("option fork not allowed here"); return STAT_NORETRY; } } #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &fd->stream.para.socket.range) < 0) { free(rangename); return STAT_NORETRY; } free(rangename); fd->stream.para.socket.dorange = true; } #endif #if WITH_LIBWRAP xio_retropt_tcpwrap(&fd->stream, opts); #endif /* WITH_LIBWRAP */ if (retropt_ushort(opts, OPT_SOURCEPORT, &fd->stream.para.socket.ip.sourceport) >= 0) { fd->stream.para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &fd->stream.para.socket.ip.lowport); if (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } while (true) { /* we loop with fork or prohibited packets */ /* now wait for some packet on this datagram socket, get its sender address, connect there, and return */ int one = 1; char infobuff[256]; union sockaddr_union _sockname; union sockaddr_union *la = &_sockname; /* local address */ if ((fd->stream.fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) { return STAT_RETRYLATER; } applyopts(fd->stream.fd, opts, PH_PASTSOCKET); if (Setsockopt(fd->stream.fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, &one, sizeof(one)) < 0) { Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s", fd->stream.fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, one, sizeof(one), strerror(errno)); } applyopts_cloexec(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_PREBIND); applyopts(fd->stream.fd, opts, PH_BIND); if (Bind(fd->stream.fd, &us.soa, uslen) < 0) { Error4("bind(%d, {%s}, "F_Zd"): %s", fd->stream.fd, sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); return STAT_RETRYLATER; } /* under some circumstances bind() fills sockaddr with interesting info. */ if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) { Error4("getsockname(%d, %p, {%d}): %s", fd->stream.fd, &us.soa, uslen, strerror(errno)); } applyopts(fd->stream.fd, opts, PH_PASTBIND); Notice1("listening on UDP %s", sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff))); readfd.fd = fd->stream.fd; readfd.events = POLLIN|POLLERR; while (xiopoll(&readfd, 1, NULL) < 0) { if (errno != EINTR) break; } themlen = socket_init(pf, them); do { result = Recvfrom(fd->stream.fd, buff1, 1, MSG_PEEK, &them->soa, &themlen); } while (result < 0 && errno == EINTR); if (result < 0) { Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_Zu"}): %s", fd->stream.fd, buff1, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } Notice1("accepting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (xiocheckpeer(&fd->stream, them, la) < 0) { /* drop packet */ char buff[512]; Recv(fd->stream.fd, buff, sizeof(buff), 0); /* drop packet */ Close(fd->stream.fd); continue; } Info1("permitting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { pid = xio_fork(false, E_ERROR); if (pid < 0) { return STAT_RETRYLATER; } if (pid == 0) { /* child */ break; } /* server: continue loop with socket()+recvfrom() */ /* when we dont close this we get awkward behaviour on Linux 2.4: recvfrom gives 0 bytes with invalid socket address */ if (Close(fd->stream.fd) < 0) { Info2("close(%d): %s", fd->stream.fd, strerror(errno)); } Sleep(1); /*! give child a chance to consume the old packet */ continue; } break; } applyopts(fd->stream.fd, opts, PH_CONNECT); if ((result = Connect(fd->stream.fd, &them->soa, themlen)) < 0) { Error4("connect(%d, {%s}, "F_Zd"): %s", fd->stream.fd, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } /* set the env vars describing the local and remote sockets */ if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", fd->stream.fd, &us.soa, uslen, strerror(errno)); } xiosetsockaddrenv("SOCK", &us, uslen, IPPROTO_UDP); xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP); fd->stream.howtoend = END_SHUTDOWN; applyopts_fchown(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_LATE); if ((result = _xio_openlate(&fd->stream, opts)) < 0) return result; return 0; }
/* the ultimate(?) socat resolver function node: the address to be resolved; supported forms: 1.2.3.4 (IPv4 address) [::2] (IPv6 address) hostname (hostname resolving to IPv4 or IPv6 address) hostname.domain (fq hostname resolving to IPv4 or IPv6 address) service: the port specification; may be numeric or symbolic family: PF_INET, PF_INET6, or PF_UNSPEC permitting both socktype: SOCK_STREAM, SOCK_DGRAM protocol: IPPROTO_UDP, IPPROTO_TCP sau: an uninitialized storage for the resulting socket address returns: STAT_OK, STAT_RETRYLATER */ int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, union sockaddr_union *sau, socklen_t *socklen, unsigned long res_opts0, unsigned long res_opts1) { int port = -1; /* port number in network byte order */ char *numnode = NULL; size_t nodelen; unsigned long save_res_opts = 0; #if HAVE_GETADDRINFO struct addrinfo hints = {0}; struct addrinfo *res = NULL; #else /* HAVE_GETIPNODEBYNAME || nothing */ struct hostent *host; #endif int error_num; #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { if (!(_res.options & RES_INIT)) { Res_init(); /*!!! returns -1 on error */ } save_res_opts = _res.options; _res.options &= ~res_opts0; _res.options |= res_opts1; Debug2("changed _res.options from 0x%lx to 0x%lx", save_res_opts, _res.options); } #endif /* HAVE_RESOLV_H */ memset(sau, 0, *socklen); sau->soa.sa_family = family; /* if service is numeric we don't want to have a lookup (might take long with NIS), so we handle this specially */ if (service && isdigit(service[0]&0xff)) { char *extra; port = htons(strtoul(service, &extra, 0)); if (*extra != '\0') { Warn2("xiogetaddrinfo(, \"%s\", ...): extra trailing data \"%s\"", service, extra); } service = NULL; } /* the resolver functions might handle numeric forms of node names by reverse lookup, that's not what we want. So we detect these and handle them specially */ if (node && isdigit(node[0]&0xff)) { #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) { family = PF_INET; #if HAVE_GETADDRINFO } else if (family == PF_INET6) { /* map "explicitely" into IPv6 address space; getipnodebyname() does this with AI_V4MAPPED, but not getaddrinfo() */ if ((numnode = Malloc(strlen(node)+7+1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } sprintf(numnode, "::ffff:%s", node); node = numnode; hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ } #if WITH_IP6 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') { if ((numnode = Malloc(nodelen-1)) == NULL) { #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_NORETRY; } strncpy(numnode, node+1, nodelen-2); numnode[nodelen-2] = '\0'; node = numnode; #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) family = PF_INET6; #endif /* WITH_IP6 */ } #if HAVE_GETADDRINFO if (node != NULL || service != NULL) { struct addrinfo *record; if (socktype != SOCK_STREAM && socktype != SOCK_DGRAM) { /* actual socket type value is not supported - fallback to a good one */ socktype = SOCK_DGRAM; } if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { /* actual protocol value is not supported - fallback to a good one */ if (socktype == SOCK_DGRAM) { protocol = IPPROTO_UDP; } else { protocol = IPPROTO_TCP; } } hints.ai_flags |= AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; if ((error_num = Getaddrinfo(node, service, &hints, &res)) != 0) { Error7("getaddrinfo(\"%s\", \"%s\", {%d,%d,%d,%d}, {}): %s", node, service, hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, (error_num == EAI_SYSTEM)? strerror(errno):gai_strerror(error_num)); if (res != NULL) freeaddrinfo(res); if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } service = NULL; /* do not resolve later again */ record = res; if (family == PF_UNSPEC && xioopts.preferred_ip == '0') { /* we just take the first result */ family = res[0].ai_addr->sa_family; } if (family == PF_UNSPEC) { int trypf; trypf = (xioopts.preferred_ip=='6'?PF_INET6:PF_INET); /* we must look for a matching entry */ while (record != NULL) { if (record->ai_family == trypf) { family = trypf; break; /* family and record set accordingly */ } record = record->ai_next; } if (record == NULL) { /* we did not find a "preferred" entry, take the first */ record = res; family = res[0].ai_addr->sa_family; } } switch (family) { #if WITH_IP4 case PF_INET: if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip4, record->ai_addr, *socklen); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: #if _AIX /* older AIX versions pass wrong length, so we correct it */ record->ai_addr->sa_len = sizeof(struct sockaddr_in6); #endif if (*socklen > record->ai_addrlen) { *socklen = record->ai_addrlen; } memcpy(&sau->ip6, record->ai_addr, *socklen); break; #endif /* WITH_IP6 */ default: Error1("address resolved to unknown protocol family %d", record->ai_addr->sa_family); break; } freeaddrinfo(res); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); break; #endif /* WITH_IP6 */ } } #elif HAVE_GETIPNODEBYNAME /* !HAVE_GETADDRINFO */ if (node != NULL) { /* first fallback is getipnodebyname() */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num); if (host == NULL) { const static char ai_host_not_found[] = "Host not found"; const static char ai_no_address[] = "No address"; const static char ai_no_recovery[] = "No recovery"; const static char ai_try_again[] = "Try again"; const char *error_msg = "Unknown error"; switch (error_num) { case HOST_NOT_FOUND: error_msg = ai_host_not_found; break; case NO_ADDRESS: error_msg = ai_no_address; case NO_RECOVERY: error_msg = ai_no_recovery; case TRY_AGAIN: error_msg = ai_try_again; } Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif } } freehostent(host); } #else /* !HAVE_GETIPNODEBYNAME */ if (node != NULL) { /* this is not a typical IP6 resolver function - but Linux "man gethostbyname" says that the only supported address type with this function is AF_INET _at present_, so maybe this fallback will be useful somewhere sometimesin a future even for IP6 */ if (family == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 family = xioopts.default_ip=='6'?PF_INET6:PF_INET; #elif WITH_IP6 family = PF_INET6; #else family = PF_INET; #endif } /*!!! try gethostbyname2 for IP6 */ if ((host = Gethostbyname(node)) == NULL) { Error2("gethostbyname(\"%s\"): %s", node, h_errno == NETDB_INTERNAL ? strerror(errno) : hstrerror(h_errno)); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif return STAT_RETRYLATER; } if (host->h_addrtype != family) { Error2("xioaddrinfo(): \"%s\" does not resolve to %s", node, family==PF_INET?"IP4":"IP6"); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif /* WITH_IP6 */ } } } #endif #if WITH_TCP || WITH_UDP if (service) { port = parseport(service, protocol); } if (port >= 0) { switch (family) { #if WITH_IP4 case PF_INET: sau->ip4.sin_port = port; break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: sau->ip6.sin6_port = port; break; #endif /* WITH_IP6 */ } } #endif /* WITH_TCP || WITH_UDP */ if (numnode) free(numnode); #if HAVE_RESOLV_H if (res_opts0 | res_opts1) { _res.options = (_res.options & (~res_opts0&~res_opts1) | save_res_opts& ( res_opts0| res_opts1)); } #endif /* HAVE_RESOLV_H */ return STAT_OK; }