X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fxconnect.c;h=8a1e1c1b28bda42c098849a3e893d3ec0c9ab859;hb=9f5a577a3241597cb5e7ba9f6df33c2e3c440e44;hp=8466325c79081f94549847c49435effa525abc41;hpb=29fe7265b8c1917ebc03283f22a3eb61e9195979;p=oweals%2Fbusybox.git diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 8466325c7..8a1e1c1b2 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -4,38 +4,77 @@ * * Connect to host at port using address resolution from getaddrinfo * + * Licensed under GPLv2, see file LICENSE in this tarball for details. */ +#include /* netinet/in.h needs it */ #include +#include +#include #include "libbb.h" -int setsockopt_reuseaddr(int fd) +void FAST_FUNC setsockopt_reuseaddr(int fd) { - return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); } -int setsockopt_broadcast(int fd) +int FAST_FUNC setsockopt_broadcast(int fd) { return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); } +int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface) +{ + int r; + struct ifreq ifr; + strncpy_IFNAMSIZ(ifr.ifr_name, iface); + /* NB: passing (iface, strlen(iface) + 1) does not work! + * (maybe it works on _some_ kernels, but not on 2.6.26) + * Actually, ifr_name is at offset 0, and in practice + * just giving char[IFNAMSIZ] instead of struct ifreq works too. + * But just in case it's not true on some obscure arch... */ + r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + if (r) + bb_perror_msg("can't bind to interface %s", iface); + return r; +} + +len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd) +{ + len_and_sockaddr lsa; + len_and_sockaddr *lsa_ptr; + + lsa.len = LSA_SIZEOF_SA; + if (getsockname(fd, &lsa.u.sa, &lsa.len) != 0) + return NULL; + + lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len); + if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */ + lsa_ptr->len = lsa.len; + getsockname(fd, &lsa_ptr->u.sa, &lsa_ptr->len); + } else { + memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); + } + return lsa_ptr; +} -void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +void FAST_FUNC 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", + "can't connect to remote host", inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); - bb_perror_msg_and_die("cannot connect to remote host"); + bb_perror_msg_and_die("can't connect to remote host"); } } /* Return 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 bb_lookup_port(const char *port, const char *protocol, unsigned default_port) + * If "port" is a name it is looked up in /etc/services, + * if it isnt found return default_port + */ +unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port) { unsigned port_nr = default_port; if (port) { @@ -60,7 +99,7 @@ unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default /* "Old" networking API - only IPv4 */ /* -void bb_lookup_host(struct sockaddr_in *s_in, const char *host) +void FAST_FUNC bb_lookup_host(struct sockaddr_in *s_in, const char *host) { struct hostent *he; @@ -71,7 +110,7 @@ void bb_lookup_host(struct sockaddr_in *s_in, const char *host) } -int xconnect_tcp_v4(struct sockaddr_in *s_addr) +int FAST_FUNC 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)); @@ -82,7 +121,7 @@ int xconnect_tcp_v4(struct sockaddr_in *s_addr) /* "New" networking API */ -int get_nport(const struct sockaddr *sa) +int FAST_FUNC get_nport(const struct sockaddr *sa) { #if ENABLE_FEATURE_IPV6 if (sa->sa_family == AF_INET6) { @@ -96,16 +135,16 @@ int get_nport(const struct sockaddr *sa) return -1; } -void set_nport(len_and_sockaddr *lsa, unsigned port) +void FAST_FUNC set_nport(len_and_sockaddr *lsa, unsigned port) { #if ENABLE_FEATURE_IPV6 - if (lsa->sa.sa_family == AF_INET6) { - lsa->sin6.sin6_port = port; + if (lsa->u.sa.sa_family == AF_INET6) { + lsa->u.sin6.sin6_port = port; return; } #endif - if (lsa->sa.sa_family == AF_INET) { - lsa->sin.sin_port = port; + if (lsa->u.sa.sa_family == AF_INET) { + lsa->u.sin.sin_port = port; return; } /* What? UNIX socket? IPX?? :) */ @@ -119,23 +158,43 @@ void set_nport(len_and_sockaddr *lsa, unsigned port) * port: if neither of above specifies port # */ static len_and_sockaddr* str2sockaddr( const char *host, int port, -USE_FEATURE_IPV6(sa_family_t af,) +IF_FEATURE_IPV6(sa_family_t af,) int ai_flags) { +IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;) int rc; - len_and_sockaddr *r = NULL; + len_and_sockaddr *r; struct addrinfo *result = NULL; + struct addrinfo *used_res; const char *org_host = host; /* only for error msg */ const char *cp; struct addrinfo hint; + if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) { + struct sockaddr_un *sun; + + r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un)); + r->len = sizeof(struct sockaddr_un); + r->u.sa.sa_family = AF_UNIX; + sun = (struct sockaddr_un *)&r->u.sa; + safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path)); + return r; + } + + r = NULL; + /* Ugly parsing of host:addr */ if (ENABLE_FEATURE_IPV6 && host[0] == '[') { + /* Even uglier parsing of [xx]:nn */ host++; cp = strchr(host, ']'); - if (!cp || cp[1] != ':') /* Malformed: must have [xx]:nn */ - bb_error_msg_and_die("bad address '%s'", org_host); - //return r; /* return NULL */ + if (!cp || (cp[1] != ':' && cp[1] != '\0')) { + /* Malformed: must be [xx]:nn or [xx] */ + bb_error_msg("bad address '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } } else { cp = strrchr(host, ':'); if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) { @@ -143,21 +202,60 @@ USE_FEATURE_IPV6(sa_family_t af,) cp = NULL; /* it's not a port spec */ } } - if (cp) { + if (cp) { /* points to ":" or "]:" */ int sz = cp - host + 1; + host = safe_strncpy(alloca(sz), host, sz); - if (ENABLE_FEATURE_IPV6 && *cp != ':') + if (ENABLE_FEATURE_IPV6 && *cp != ':') { cp++; /* skip ']' */ + if (*cp == '\0') /* [xx] without port */ + goto skip; + } cp++; /* skip ':' */ - port = xatou16(cp); + port = bb_strtou(cp, NULL, 10); + if (errno || (unsigned)port > 0xffff) { + bb_error_msg("bad port spec '%s'", org_host); + if (ai_flags & DIE_ON_ERROR) + xfunc_die(); + return NULL; + } + skip: ; } + /* Next two if blocks allow to skip getaddrinfo() + * in case host name is a numeric IP(v6) address. + * getaddrinfo() initializes DNS resolution machinery, + * scans network config and such - tens of syscalls. + */ + /* If we were not asked specifically for IPv6, + * check whether this is a numeric IPv4 */ + IF_FEATURE_IPV6(if(af != AF_INET6)) { + struct in_addr in4; + if (inet_aton(host, &in4) != 0) { + r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in)); + r->len = sizeof(struct sockaddr_in); + r->u.sa.sa_family = AF_INET; + r->u.sin.sin_addr = in4; + goto set_port; + } + } +#if ENABLE_FEATURE_IPV6 + /* If we were not asked specifically for IPv4, + * check whether this is a numeric IPv6 */ + if (af != AF_INET) { + struct in6_addr in6; + if (inet_pton(AF_INET6, host, &in6) > 0) { + r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6)); + r->len = sizeof(struct sockaddr_in6); + r->u.sa.sa_family = AF_INET6; + r->u.sin6.sin6_addr = in6; + goto set_port; + } + } +#endif + memset(&hint, 0 , sizeof(hint)); -#if !ENABLE_FEATURE_IPV6 - hint.ai_family = AF_INET; /* do not try to find IPv6 */ -#else hint.ai_family = af; -#endif /* Needed. Or else we will get each address thrice (or more) * for each possible socket type (tcp,udp,raw...): */ hint.ai_socktype = SOCK_STREAM; @@ -166,12 +264,26 @@ USE_FEATURE_IPV6(sa_family_t af,) if (rc || !result) { bb_error_msg("bad address '%s'", org_host); if (ai_flags & DIE_ON_ERROR) - sleep_and_die(); + xfunc_die(); goto ret; } - r = xmalloc(offsetof(len_and_sockaddr, sa) + result->ai_addrlen); - r->len = result->ai_addrlen; - memcpy(&r->sa, result->ai_addr, result->ai_addrlen); + used_res = result; +#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS + while (1) { + if (used_res->ai_family == AF_INET) + break; + used_res = used_res->ai_next; + if (!used_res) { + used_res = result; + break; + } + } +#endif + r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen); + r->len = used_res->ai_addrlen; + memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen); + + set_port: set_nport(r, htons(port)); ret: freeaddrinfo(result); @@ -182,59 +294,68 @@ USE_FEATURE_IPV6(sa_family_t af,) #endif #if ENABLE_FEATURE_IPV6 -len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af) +len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af) { return str2sockaddr(host, port, af, 0); } -len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) +len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) { return str2sockaddr(host, port, af, DIE_ON_ERROR); } #endif -len_and_sockaddr* host2sockaddr(const char *host, int port) +len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, 0); } -len_and_sockaddr* xhost2sockaddr(const char *host, int port) +len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR); } -len_and_sockaddr* xdotted2sockaddr(const char *host, int port) +len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port) { return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR); } -int xsocket_type(len_and_sockaddr **lsap, int sock_type) +#undef xsocket_type +int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, IF_FEATURE_IPV6(int family,) int sock_type) { + IF_NOT_FEATURE_IPV6(enum { family = AF_INET };) len_and_sockaddr *lsa; int fd; - int len = sizeof(struct sockaddr_in); - int family = AF_INET; + int len; #if ENABLE_FEATURE_IPV6 - fd = socket(AF_INET6, sock_type, 0); - if (fd >= 0) { - len = sizeof(struct sockaddr_in6); - family = AF_INET6; - } else + if (family == AF_UNSPEC) { + fd = socket(AF_INET6, sock_type, 0); + if (fd >= 0) { + family = AF_INET6; + goto done; + } + family = AF_INET; + } #endif - { - fd = xsocket(AF_INET, sock_type, 0); + fd = xsocket(family, sock_type, 0); + len = sizeof(struct sockaddr_in); +#if ENABLE_FEATURE_IPV6 + if (family == AF_INET6) { + done: + len = sizeof(struct sockaddr_in6); } - lsa = xzalloc(offsetof(len_and_sockaddr, sa) + len); +#endif + lsa = xzalloc(LSA_LEN_SIZE + len); lsa->len = len; - lsa->sa.sa_family = family; + lsa->u.sa.sa_family = family; *lsap = lsa; return fd; } -int xsocket_stream(len_and_sockaddr **lsap) +int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap) { - return xsocket_type(lsap, SOCK_STREAM); + return xsocket_type(lsap, IF_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM); } static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) @@ -245,56 +366,73 @@ static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) if (bindaddr && bindaddr[0]) { lsa = xdotted2sockaddr(bindaddr, port); /* user specified bind addr dictates family */ - fd = xsocket(lsa->sa.sa_family, sock_type, 0); + fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); } else { - fd = xsocket_type(&lsa, sock_type); + fd = xsocket_type(&lsa, IF_FEATURE_IPV6(AF_UNSPEC,) sock_type); set_nport(lsa, htons(port)); } setsockopt_reuseaddr(fd); - xbind(fd, &lsa->sa, lsa->len); + xbind(fd, &lsa->u.sa, lsa->len); free(lsa); return fd; } -int create_and_bind_stream_or_die(const char *bindaddr, int port) +int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port) { return create_and_bind_or_die(bindaddr, port, SOCK_STREAM); } -int create_and_bind_dgram_or_die(const char *bindaddr, int port) +int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port) { return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM); } -int create_and_connect_stream_or_die(const char *peer, int port) +int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port) { int fd; len_and_sockaddr *lsa; lsa = xhost2sockaddr(peer, port); - fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0); + fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); setsockopt_reuseaddr(fd); - xconnect(fd, &lsa->sa, lsa->len); + xconnect(fd, &lsa->u.sa, lsa->len); free(lsa); return fd; } -int xconnect_stream(const len_and_sockaddr *lsa) +int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa) { - int fd = xsocket(lsa->sa.sa_family, SOCK_STREAM, 0); - xconnect(fd, &lsa->sa, lsa->len); + int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); + xconnect(fd, &lsa->u.sa, lsa->len); return fd; } /* We hijack this constant to mean something else */ /* It doesn't hurt because we will add this bit anyway */ #define IGNORE_PORT NI_NUMERICSERV -static char* sockaddr2str(const struct sockaddr *sa, socklen_t salen, int flags) +static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags) { char host[128]; char serv[16]; - int rc = getnameinfo(sa, salen, + int rc; + socklen_t salen; + + if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) { + struct sockaddr_un *sun = (struct sockaddr_un *)sa; + return xasprintf("local:%.*s", + (int) sizeof(sun->sun_path), + sun->sun_path); + } + + salen = LSA_SIZEOF_SA; +#if ENABLE_FEATURE_IPV6 + if (sa->sa_family == AF_INET) + salen = sizeof(struct sockaddr_in); + if (sa->sa_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); +#endif + rc = getnameinfo(sa, salen, host, sizeof(host), /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */ serv, sizeof(serv), @@ -319,26 +457,26 @@ static char* sockaddr2str(const struct sockaddr *sa, socklen_t salen, int flags) /*return xstrdup(host);*/ } -char* xmalloc_sockaddr2host(const struct sockaddr *sa, socklen_t salen) +char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa) { - return sockaddr2str(sa, salen, 0); + return sockaddr2str(sa, 0); } -char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa, socklen_t salen) +char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa) { - return sockaddr2str(sa, salen, IGNORE_PORT); + return sockaddr2str(sa, IGNORE_PORT); } -char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa, socklen_t salen) +char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) { - return sockaddr2str(sa, salen, NI_NAMEREQD | IGNORE_PORT); + return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); } -char* xmalloc_sockaddr2dotted(const struct sockaddr *sa, socklen_t salen) +char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) { - return sockaddr2str(sa, salen, NI_NUMERICHOST); + return sockaddr2str(sa, NI_NUMERICHOST); } -char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa, socklen_t salen) +char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) { - return sockaddr2str(sa, salen, NI_NUMERICHOST | IGNORE_PORT); + return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); }