/* called within retry/fork loop, before connect() */ int _xioopen_socks4_connect0(struct single *xfd, const char *hostname, /* socks target host */ int socks4a, struct socks4 *sockhead, ssize_t *headlen, /* get available space, return used length*/ int level) { int result; if (!socks4a) { union sockaddr_union sau; socklen_t saulen = sizeof(sau); if ((result = xiogetaddrinfo(hostname, NULL, PF_INET, SOCK_STREAM, IPPROTO_TCP, &sau, &saulen, xfd->para.socket.ip.res_opts[1], xfd->para.socket.ip.res_opts[0])) != STAT_OK) { return result; /*! STAT_RETRY? */ } memcpy(&sockhead->dest, &sau.ip4.sin_addr, 4); } #if WITH_SOCKS4A else { /*! noresolve */ sockhead->dest = htonl(0x00000001); /* three bytes zero */ } #endif /* WITH_SOCKS4A */ #if WITH_SOCKS4A if (socks4a) { /* SOCKS4A requires us to append the host name to resolve after the user name's trailing 0 byte. */ char* insert_position = (char*) sockhead + *headlen; insert_position[0] = '\0'; strncat(insert_position, hostname, BUFF_LEN-*headlen-1); ((char *)sockhead)[BUFF_LEN-1] = 0; *headlen += strlen(hostname) + 1; if (*headlen > BUFF_LEN) { *headlen = BUFF_LEN; } } #endif /* WITH_SOCKS4A */ return STAT_OK; }
/* applies and consumes the following options: OPT_PROTOCOL_FAMILY, OPT_BIND */ int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, unsigned long res_opts0, unsigned long res_opts1, union sockaddr_union *us, socklen_t *uslen, int socktype) { char *bindname = NULL; int result; retropt_socket_pf(opts, pf); retropt_string(opts, OPT_BIND, &bindname); if ((result = xiogetaddrinfo(bindname, portname, *pf, socktype, ipproto, (union sockaddr_union *)us, uslen, res_opts0, res_opts1)) != STAT_OK) { /*! STAT_RETRY? */ return result; } *opts0 = copyopts(opts, GROUP_ALL); return STAT_OK; }
/* returns STAT_OK on success or some other value on failure applies and consumes the following options: PH_EARLY OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT */ int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, unsigned long res_opts0, unsigned long res_opts1, union sockaddr_union *them, socklen_t *themlen, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype) { uint16_t port; char infobuff[256]; int result; retropt_socket_pf(opts, pf); if ((result = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, (union sockaddr_union *)them, themlen, res_opts0, res_opts1 )) != STAT_OK) { return STAT_NORETRY; /*! STAT_RETRYLATER? */ } if (*pf == PF_UNSPEC) { *pf = them->soa.sa_family; } applyopts(-1, opts, PH_EARLY); /* 3 means: IP address AND port accepted */ if (retropt_bind(opts, *pf, socktype, protocol, (struct sockaddr *)us, uslen, 3, res_opts0, res_opts1) != STAT_NOACTION) { *needbind = true; } else { switch (*pf) { #if WITH_IP4 case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break; #endif /* WITH_IP6 */ } } if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) { switch (*pf) { #if WITH_IP4 case PF_INET: us->ip4.sin_port = htons(port); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: us->ip6.sin6_port = htons(port); break; #endif /* WITH_IP6 */ default: Error("unsupported protocol family"); } *needbind = true; } retropt_bool(opts, OPT_LOWPORT, lowport); *opts0 = copyopts(opts, GROUP_ALL); Notice1("opening connection to %s", sockaddr_info((struct sockaddr *)them, *themlen, infobuff, sizeof(infobuff))); return STAT_OK; }
static int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int pf, int socktype, int ipproto) { union sockaddr_union us; socklen_t uslen = sizeof(us); char *rangename; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } retropt_socket_pf(opts, &pf); 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 } if ((result = xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, xfd->stream.para.socket.ip.res_opts[0], xfd->stream.para.socket.ip.res_opts[1])) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = us.soa.sa_family; } #if 1 { union sockaddr_union la; socklen_t lalen = sizeof(la); if (retropt_bind(opts, pf, socktype, ipproto, &xfd->stream.para.socket.la.soa, &lalen, 1, xfd->stream.para.socket.ip.res_opts[0], xfd->stream.para.socket.ip.res_opts[1]) != STAT_NOACTION) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break; #endif } } else { xfd->stream.para.socket.la.soa.sa_family = pf; } } #endif #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &xfd->stream.para.socket.range) < 0) { return STAT_NORETRY; } xfd->stream.para.socket.dorange = true; } #endif #if WITH_LIBWRAP xio_retropt_tcpwrap(&xfd->stream, opts); #endif /* WITH_LIBWRAP */ if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) { xfd->stream.para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport); xfd->stream.dtype = XIODATA_RECV; if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen, opts, pf, socktype, ipproto, E_ERROR)) != STAT_OK) { return result; } _xio_openlate(&xfd->stream, opts); return result; }
static int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, unsigned groups, int pf, int socktype, int ipproto) { union sockaddr_union us; socklen_t uslen = sizeof(us); bool needbind = false; int result; if (argc != 2) { Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1); return STAT_NORETRY; } xfd->stream.howtoend = END_NONE; retropt_socket_pf(opts, &pf); 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 } if ((result = xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, xfd->stream.para.socket.ip.res_opts[0], xfd->stream.para.socket.ip.res_opts[1])) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = us.soa.sa_family; } { union sockaddr_union la; socklen_t lalen = sizeof(la); if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1, xfd->stream.para.socket.ip.res_opts[0], xfd->stream.para.socket.ip.res_opts[1]) != STAT_NOACTION) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break; #endif } needbind = true; } } if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) { xfd->stream.para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport); xfd->stream.dtype = XIODATA_RECVFROM_ONE; if ((result = _xioopen_dgram_recvfrom(&xfd->stream, xioflags, &us.soa, uslen, opts, pf, socktype, ipproto, E_ERROR)) != STAT_OK) { return result; } _xio_openlate(&xfd->stream, opts); return STAT_OK; }
/* applies and consumes the following option: PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE OFUNC_OFFSET OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC */ static int _xioopen_udp_sendto(const char *hostname, const char *servname, struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int pf, int socktype, int ipproto) { xiosingle_t *xfd = &xxfd->stream; union sockaddr_union us; socklen_t uslen; int feats = 3; /* option bind supports address and port */ bool needbind = false; int result; xfd->howtoend = END_SHUTDOWN; /* ...res_opts[] */ if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); xfd->salen = sizeof(xfd->peersa); if ((result = xiogetaddrinfo(hostname, servname, pf, socktype, ipproto, &xfd->peersa, &xfd->salen, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1])) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = xfd->peersa.soa.sa_family; } uslen = socket_init(pf, &us); if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1]) != STAT_NOACTION) { needbind = true; } if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_port = htons(xfd->para.socket.ip.sourceport); break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_port = htons(xfd->para.socket.ip.sourceport); break; #endif } needbind = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport); if (xfd->para.socket.ip.lowport) { switch (pf) { #if WITH_IP4 case PF_INET: /*!!! this is buggy */ us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break; #endif #if WITH_IP6 case PF_INET6: /*!!! this is buggy */ us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break; #endif } needbind = true; } xfd->dtype = XIODATA_RECVFROM; return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, pf, socktype, ipproto); }
/* applies and consumes the following options: PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE OFUNC_OFFSET OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC */ static int _xioopen_rawip_sendto(const char *hostname, const char *protname, struct opt *opts, int xioflags, xiofile_t *xxfd, unsigned groups, int *pf) { char *garbage; xiosingle_t *xfd = &xxfd->stream; int rw = (xioflags&XIO_ACCMODE); union sockaddr_union us; socklen_t uslen; int feats = 1; /* option bind supports only address, not port */ int socktype = SOCK_RAW; int ipproto; bool needbind = false; int result; if ((ipproto = strtoul(protname, &garbage, 0)) >= 256) { Error3("xioopen_rawip_sendto(\"%s:%s\",,): protocol number exceeds 255 (%u)", hostname, protname, ipproto); return STAT_NORETRY; } else if (*garbage) { Warn2("xioopen_rawip_sendto(\"%s:%s\",,): trailing garbage in protocol specification", hostname, protname); /*return STAT_NORETRY;*/ } retropt_int(opts, OPT_PROTOCOL_FAMILY, pf); /* ...res_opts[] */ if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; applyopts(-1, opts, PH_INIT); xfd->salen = sizeof(xfd->peersa); if ((result = xiogetaddrinfo(hostname, NULL, *pf, socktype, ipproto, &xfd->peersa, &xfd->salen, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1])) != STAT_OK) { return result; } if (*pf == PF_UNSPEC) { *pf = xfd->peersa.soa.sa_family; } uslen = socket_init(*pf, &us); xfd->dtype = XIODATA_RECVFROM_SKIPIP; if (retropt_bind(opts, *pf, socktype, ipproto, &us.soa, &uslen, feats, xfd->para.socket.ip.res_opts[0], xfd->para.socket.ip.res_opts[1]) != STAT_NOACTION) { needbind = true; } if ((result = _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, xfd, groups, *pf, socktype, ipproto)) != STAT_OK) { return result; } if (XIOWITHWR(rw)) xfd->wfd = xfd->rfd; if (!XIOWITHRD(rw)) xfd->rfd = -1; return STAT_OK; }