X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fxconnect.c;h=3a0dc2653fc61707af18120899192be680ec537e;hb=959cb6742832a3b403a5d0116088a09f33afe927;hp=950aee82648b904a861535ed79d34ee7083564fa;hpb=4e6d5117b839cef41cd3919966f95bf25d24d8d9;p=oweals%2Fbusybox.git diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 950aee826..3a0dc2653 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c @@ -4,38 +4,119 @@ * * Connect to host at port using address resolution from getaddrinfo * + * Licensed under GPLv2, see file LICENSE in this source tree. */ +#include +#include /* netinet/in.h needs it */ #include +#include +#include #include "libbb.h" -void setsockopt_reuseaddr(int fd) +int FAST_FUNC setsockopt_int(int fd, int level, int optname, int optval) { - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)); + return setsockopt(fd, level, optname, &optval, sizeof(int)); } -int setsockopt_broadcast(int fd) +int FAST_FUNC setsockopt_1(int fd, int level, int optname) { - return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1)); + return setsockopt_int(fd, level, optname, 1); +} +int FAST_FUNC setsockopt_SOL_SOCKET_int(int fd, int optname, int optval) +{ + return setsockopt_int(fd, SOL_SOCKET, optname, optval); +} +int FAST_FUNC setsockopt_SOL_SOCKET_1(int fd, int optname) +{ + return setsockopt_SOL_SOCKET_int(fd, optname, 1); } -void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) +void FAST_FUNC setsockopt_reuseaddr(int fd) +{ + setsockopt_SOL_SOCKET_1(fd, SO_REUSEADDR); +} +int FAST_FUNC setsockopt_broadcast(int fd) +{ + return setsockopt_SOL_SOCKET_1(fd, SO_BROADCAST); +} +int FAST_FUNC setsockopt_keepalive(int fd) +{ + return setsockopt_SOL_SOCKET_1(fd, SO_KEEPALIVE); +} + +#ifdef SO_BINDTODEVICE +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; +} +#else +int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM, + const char *iface UNUSED_PARAM) +{ + bb_error_msg("SO_BINDTODEVICE is not supported on this system"); + return -1; +} +#endif + +static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen)) +{ + len_and_sockaddr lsa; + len_and_sockaddr *lsa_ptr; + + lsa.len = LSA_SIZEOF_SA; + if (get_name(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; + get_name(fd, &lsa_ptr->u.sa, &lsa_ptr->len); + } else { + memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len); + } + return lsa_ptr; +} + +len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd) +{ + return get_lsa(fd, getsockname); +} + +len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd) +{ + return get_lsa(fd, getpeername); +} + +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) { @@ -57,32 +138,10 @@ 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) -{ - 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); -} - - -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; -} -*/ - /* "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 +155,18 @@ int get_nport(const struct sockaddr *sa) return -1; } -void set_nport(len_and_sockaddr *lsa, unsigned port) +void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port) { #if ENABLE_FEATURE_IPV6 - if (lsa->u.sa.sa_family == AF_INET6) { - lsa->u.sin6.sin6_port = port; + if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *sin6 = (void*) sa; + sin6->sin6_port = port; return; } #endif - if (lsa->u.sa.sa_family == AF_INET) { - lsa->u.sin.sin_port = port; + if (sa->sa_family == AF_INET) { + struct sockaddr_in *sin = (void*) sa; + sin->sin_port = port; return; } /* What? UNIX socket? IPX?? :) */ @@ -119,23 +180,38 @@ 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 && is_prefixed_with(host, "local:")) { + 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 */ + 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(); @@ -150,9 +226,13 @@ USE_FEATURE_IPV6(sa_family_t af,) } 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 = bb_strtou(cp, NULL, 10); if (errno || (unsigned)port > 0xffff) { @@ -161,15 +241,44 @@ USE_FEATURE_IPV6(sa_family_t af,) 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) + /* Need SOCK_STREAM, or else we get each address thrice (or more) * for each possible socket type (tcp,udp,raw...): */ hint.ai_socktype = SOCK_STREAM; hint.ai_flags = ai_flags & ~DIE_ON_ERROR; @@ -192,12 +301,15 @@ USE_FEATURE_IPV6(sa_family_t af,) } } #endif - r = xmalloc(offsetof(len_and_sockaddr, u.sa) + used_res->ai_addrlen); + 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_nport(r, htons(port)); + + set_port: + set_nport(&r->u.sa, htons(port)); ret: - freeaddrinfo(result); + if (result) + freeaddrinfo(result); return r; } #if !ENABLE_FEATURE_IPV6 @@ -205,68 +317,70 @@ 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); } -#undef xsocket_type -int xsocket_type(len_and_sockaddr **lsap, USE_FEATURE_IPV6(int family,) int sock_type) +int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type) { - SKIP_FEATURE_IPV6(enum { family = AF_INET };) len_and_sockaddr *lsa; int fd; int len; -#if ENABLE_FEATURE_IPV6 if (family == AF_UNSPEC) { +#if ENABLE_FEATURE_IPV6 fd = socket(AF_INET6, sock_type, 0); if (fd >= 0) { family = AF_INET6; goto done; } +#endif family = AF_INET; } -#endif + fd = xsocket(family, sock_type, 0); + len = sizeof(struct sockaddr_in); + if (family == AF_UNIX) + len = sizeof(struct sockaddr_un); #if ENABLE_FEATURE_IPV6 if (family == AF_INET6) { done: len = sizeof(struct sockaddr_in6); } #endif - lsa = xzalloc(offsetof(len_and_sockaddr, u.sa) + len); + lsa = xzalloc(LSA_LEN_SIZE + len); lsa->len = len; 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, USE_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM); + return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM); } static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) @@ -279,8 +393,8 @@ static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) /* user specified bind addr dictates family */ fd = xsocket(lsa->u.sa.sa_family, sock_type, 0); } else { - fd = xsocket_type(&lsa, USE_FEATURE_IPV6(AF_UNSPEC,) sock_type); - set_nport(lsa, htons(port)); + fd = xsocket_type(&lsa, AF_UNSPEC, sock_type); + set_nport(&lsa->u.sa, htons(port)); } setsockopt_reuseaddr(fd); xbind(fd, &lsa->u.sa, lsa->len); @@ -288,18 +402,18 @@ static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type) 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; @@ -312,7 +426,7 @@ int create_and_connect_stream_or_die(const char *peer, int port) return fd; } -int xconnect_stream(const len_and_sockaddr *lsa) +int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa) { int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0); xconnect(fd, &lsa->u.sa, lsa->len); @@ -322,13 +436,20 @@ int xconnect_stream(const len_and_sockaddr *lsa) /* 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, int flags) +static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags) { char host[128]; char serv[16]; 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) @@ -361,26 +482,29 @@ static char* sockaddr2str(const struct sockaddr *sa, int flags) /*return xstrdup(host);*/ } -char* xmalloc_sockaddr2host(const struct sockaddr *sa) +char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa) { return sockaddr2str(sa, 0); } -char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa) +char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa) { return sockaddr2str(sa, IGNORE_PORT); } -char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) +char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) { return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT); } -char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) +#ifndef NI_NUMERICSCOPE +# define NI_NUMERICSCOPE 0 +#endif +char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa) { - return sockaddr2str(sa, NI_NUMERICHOST); + return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE); } -char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) +char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) { - return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT); + return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE | IGNORE_PORT); }