X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fxconnect.c;h=bc0691531dbba55056e912ab768b3ecd0c20c8b9;hb=666da5e2c6edec979966d16771818b32dcfafe04;hp=9e771495d671f9147f4b5de2496e986dae885477;hpb=d378c3149c6c24e7788f04a6d20ba360f3ea407e;p=oweals%2Fbusybox.git diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 9e771495d..bc0691531 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -2,79 +2,153 @@ /* * Utility routines. * - * Connect to host at port using address resolusion from getaddrinfo + * Connect to host at port using address resolution from getaddrinfo * */ -#include "inet_common.h" -#include -#include -#include -#include -#include -#include -#include #include "libbb.h" -int xconnect(const char *host, const char *port) +/* Return network byte ordered port number for a service. + * If "port" is a number use it as the port. + * If "port" is a name it is looked up in /etc/services, if it isnt found return + * default_port + */ +unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port) { -#if CONFIG_FEATURE_IPV6 - struct addrinfo hints; - struct addrinfo *res; - struct addrinfo *addr_info; - int error; - int s; - - memset(&hints, 0, sizeof(hints)); - /* set-up hints structure */ - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(host, port, &hints, &res); - if (error||!res) - perror_msg_and_die(gai_strerror(error)); - addr_info=res; - while (res) { - s=socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s<0) - { - error=s; - res=res->ai_next; - continue; + unsigned short port_nr = htons(default_port); + if (port) { + char *endptr; + int old_errno; + long port_long; + + /* Since this is a lib function, we're not allowed to reset errno to 0. + * Doing so could break an app that is deferring checking of errno. */ + old_errno = errno; + errno = 0; + port_long = strtol(port, &endptr, 10); + if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) { + struct servent *tserv = getservbyname(port, protocol); + if (tserv) { + port_nr = tserv->s_port; + } + } else { + port_nr = htons(port_long); } - /* try to connect() to res->ai_addr */ - error = connect(s, res->ai_addr, res->ai_addrlen); - if (error >= 0) - break; - close(s); - res=res->ai_next; + errno = old_errno; } - freeaddrinfo(addr_info); - if (error < 0) - { - perror_msg_and_die("Unable to connect to remote host (%s)", host); + return port_nr; +} + +void bb_lookup_host(struct sockaddr_in *s_in, const char *host) +{ + struct hostent *he; + + memset(s_in, 0, sizeof(struct sockaddr_in)); + s_in->sin_family = AF_INET; + he = xgethostbyname(host); + memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length); +} + +void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +{ + if (connect(s, s_addr, addrlen) < 0) { + if (ENABLE_FEATURE_CLEAN_UP) close(s); + if (s_addr->sa_family == AF_INET) + bb_perror_msg_and_die("%s (%s)", + "cannot connect to remote host", + inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); + bb_perror_msg_and_die("cannot connect to remote host"); } +} + +int xconnect_tcp_v4(struct sockaddr_in *s_addr) +{ + int s = xsocket(AF_INET, SOCK_STREAM, 0); + xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); return s; -#else - struct sockaddr_in s_addr; - int s = socket(AF_INET, SOCK_STREAM, 0); - struct servent *tserv; - int port_nr=atoi(port); - struct hostent * he; +} + +static const int one = 1; +int setsockopt_reuseaddr(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); +} +int setsockopt_broadcast(int fd) +{ + return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)); +} - if (port_nr==0 && (tserv = getservbyname(port, "tcp")) != NULL) - port_nr = tserv->s_port; +int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) +{ + union { + struct in_addr a4; +#if ENABLE_FEATURE_IPV6 + struct in6_addr a6; +#endif + } a; - memset(&s_addr, 0, sizeof(struct sockaddr_in)); - s_addr.sin_family = AF_INET; - s_addr.sin_port = htons(port_nr); + /* TODO maybe: port spec? like n.n.n.n:nn */ - he = xgethostbyname(host); - memcpy(&s_addr.sin_addr, he->h_addr, sizeof s_addr.sin_addr); +#if ENABLE_FEATURE_IPV6 + if (socklen >= sizeof(struct sockaddr_in6) + && inet_pton(AF_INET6, dotted, &a.a6) > 0 + ) { + ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; + /* ((struct sockaddr_in6*)sp)->sin6_port = */ + return 0; /* success */ + } +#endif + if (socklen >= sizeof(struct sockaddr_in) + && inet_pton(AF_INET, dotted, &a.a4) > 0 + ) { + ((struct sockaddr_in*)sp)->sin_family = AF_INET; + ((struct sockaddr_in*)sp)->sin_addr = a.a4; + /* ((struct sockaddr_in*)sp)->sin_port = */ + return 0; /* success */ + } + return 1; +} - if (connect(s, (struct sockaddr *)&s_addr, sizeof s_addr) < 0) +int xsocket_stream_ip4or6(sa_family_t *fp) +{ + int fd; +#if ENABLE_FEATURE_IPV6 + fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fp) *fp = AF_INET6; + if (fd < 0) +#endif { - perror_msg_and_die("Unable to connect to remote host (%s)", host); + fd = xsocket(AF_INET, SOCK_STREAM, 0); + if (fp) *fp = AF_INET; } - return s; + return fd; +} + +int create_and_bind_socket_ip4or6(const char *hostaddr, int port) +{ + int fd; + sockaddr_inet sa; + + memset(&sa, 0, sizeof(sa)); + if (hostaddr) { + if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) + bb_error_msg_and_die("bad address '%s'", hostaddr); + /* user specified bind addr dictates family */ + fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); + } else + fd = xsocket_stream_ip4or6(&sa.sa.sa_family); + setsockopt_reuseaddr(fd); + + /* if (port >= 0) { */ +#if ENABLE_FEATURE_IPV6 + if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) + sa.sin6.sin6_port = htons(port); #endif + if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) + sa.sin.sin_port = htons(port); + /* } */ + + xbind(fd, &sa.sa, sizeof(sa)); + return fd; }