- don't free user-supplied string (via -e)
[oweals/busybox.git] / libbb / xconnect.c
index e7d5106782eb2fa0b3264610f52a790d69c97c3b..d48c50339990ba2079ae83fbf1a558571859022e 100644 (file)
@@ -9,16 +9,16 @@
 #include <netinet/in.h>
 #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));
 }
 
-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)
@@ -35,7 +35,7 @@ void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
  * 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)
+unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
 {
        unsigned port_nr = default_port;
        if (port) {
@@ -60,7 +60,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 +71,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 +82,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 +96,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?? :) */
@@ -125,17 +125,22 @@ USE_FEATURE_IPV6(sa_family_t af,)
        int rc;
        len_and_sockaddr *r = NULL;
        struct addrinfo *result = NULL;
+       struct addrinfo *used_res;
        const char *org_host = host; /* only for error msg */
        const char *cp;
        struct addrinfo hint;
 
        /* 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] != ':') { /* Malformed: must have [xx]:nn */
+                       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,13 +148,19 @@ 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 != ':')
                        cp++; /* 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;
+               }
        }
 
        memset(&hint, 0 , sizeof(hint));
@@ -169,9 +180,21 @@ USE_FEATURE_IPV6(sa_family_t af,)
                        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(offsetof(len_and_sockaddr, u.sa) + 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));
  ret:
        freeaddrinfo(result);
@@ -182,59 +205,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, USE_FEATURE_IPV6(int family,) int sock_type)
 {
+       SKIP_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(offsetof(len_and_sockaddr, u.sa) + 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, USE_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM);
 }
 
 static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
@@ -245,56 +277,66 @@ 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, USE_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;
+
+       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 +361,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);
 }