busybox/libbb/xconnect.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Utility routines.
   4 *
   5 * Connect to host at port using address resolution from getaddrinfo
   6 *
   7 * Licensed under GPLv2, see file LICENSE in this source tree.
   8 */
   9#include <sys/types.h>
  10#include <sys/socket.h> /* netinet/in.h needs it */
  11#include <netinet/in.h>
  12#include <net/if.h>
  13#include <sys/un.h>
  14#if ENABLE_IFPLUGD || ENABLE_FEATURE_MDEV_DAEMON || ENABLE_UEVENT
  15# include <linux/netlink.h>
  16#endif
  17#include "libbb.h"
  18
  19int FAST_FUNC setsockopt_int(int fd, int level, int optname, int optval)
  20{
  21        return setsockopt(fd, level, optname, &optval, sizeof(int));
  22}
  23int FAST_FUNC setsockopt_1(int fd, int level, int optname)
  24{
  25        return setsockopt_int(fd, level, optname, 1);
  26}
  27int FAST_FUNC setsockopt_SOL_SOCKET_int(int fd, int optname, int optval)
  28{
  29        return setsockopt_int(fd, SOL_SOCKET, optname, optval);
  30}
  31int FAST_FUNC setsockopt_SOL_SOCKET_1(int fd, int optname)
  32{
  33        return setsockopt_SOL_SOCKET_int(fd, optname, 1);
  34}
  35
  36void FAST_FUNC setsockopt_reuseaddr(int fd)
  37{
  38        setsockopt_SOL_SOCKET_1(fd, SO_REUSEADDR);
  39}
  40int FAST_FUNC setsockopt_broadcast(int fd)
  41{
  42        return setsockopt_SOL_SOCKET_1(fd, SO_BROADCAST);
  43}
  44int FAST_FUNC setsockopt_keepalive(int fd)
  45{
  46        return setsockopt_SOL_SOCKET_1(fd, SO_KEEPALIVE);
  47}
  48
  49#ifdef SO_BINDTODEVICE
  50int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface)
  51{
  52        int r;
  53        struct ifreq ifr;
  54        strncpy_IFNAMSIZ(ifr.ifr_name, iface);
  55        /* NB: passing (iface, strlen(iface) + 1) does not work!
  56         * (maybe it works on _some_ kernels, but not on 2.6.26)
  57         * Actually, ifr_name is at offset 0, and in practice
  58         * just giving char[IFNAMSIZ] instead of struct ifreq works too.
  59         * But just in case it's not true on some obscure arch... */
  60        r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
  61        if (r)
  62                bb_perror_msg("can't bind to interface %s", iface);
  63        return r;
  64}
  65#else
  66int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM,
  67                const char *iface UNUSED_PARAM)
  68{
  69        bb_simple_error_msg("SO_BINDTODEVICE is not supported on this system");
  70        return -1;
  71}
  72#endif
  73
  74static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen))
  75{
  76        len_and_sockaddr lsa;
  77        len_and_sockaddr *lsa_ptr;
  78
  79        lsa.len = LSA_SIZEOF_SA;
  80        if (get_name(fd, &lsa.u.sa, &lsa.len) != 0)
  81                return NULL;
  82
  83        lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len);
  84        if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */
  85                lsa_ptr->len = lsa.len;
  86                get_name(fd, &lsa_ptr->u.sa, &lsa_ptr->len);
  87        } else {
  88                memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len);
  89        }
  90        return lsa_ptr;
  91}
  92
  93len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd)
  94{
  95        return get_lsa(fd, getsockname);
  96}
  97
  98len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd)
  99{
 100        return get_lsa(fd, getpeername);
 101}
 102
 103void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
 104{
 105        if (connect(s, s_addr, addrlen) < 0) {
 106                if (ENABLE_FEATURE_CLEAN_UP)
 107                        close(s);
 108                if (s_addr->sa_family == AF_INET)
 109                        bb_perror_msg_and_die("%s (%s)",
 110                                "can't connect to remote host",
 111                                inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
 112                bb_simple_perror_msg_and_die("can't connect to remote host");
 113        }
 114}
 115
 116/* Return port number for a service.
 117 * If "port" is a number use it as the port.
 118 * If "port" is a name it is looked up in /etc/services,
 119 * if it isnt found return default_port
 120 */
 121unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
 122{
 123        unsigned port_nr = default_port;
 124        if (port) {
 125                int old_errno;
 126
 127                /* Since this is a lib function, we're not allowed to reset errno to 0.
 128                 * Doing so could break an app that is deferring checking of errno. */
 129                old_errno = errno;
 130                port_nr = bb_strtou(port, NULL, 10);
 131                if (errno || port_nr > 65535) {
 132                        struct servent *tserv = getservbyname(port, protocol);
 133                        port_nr = default_port;
 134                        if (tserv)
 135                                port_nr = ntohs(tserv->s_port);
 136//FIXME: else: port string was garbage, but we don't report that???
 137                }
 138                errno = old_errno;
 139        }
 140        return (uint16_t)port_nr;
 141}
 142
 143
 144/* "New" networking API */
 145
 146
 147int FAST_FUNC get_nport(const struct sockaddr *sa)
 148{
 149#if ENABLE_FEATURE_IPV6
 150        if (sa->sa_family == AF_INET6) {
 151                return ((struct sockaddr_in6*)sa)->sin6_port;
 152        }
 153#endif
 154        if (sa->sa_family == AF_INET) {
 155                return ((struct sockaddr_in*)sa)->sin_port;
 156        }
 157        /* What? UNIX socket? IPX?? :) */
 158        return -1;
 159}
 160
 161void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port)
 162{
 163#if ENABLE_FEATURE_IPV6
 164        if (sa->sa_family == AF_INET6) {
 165                struct sockaddr_in6 *sin6 = (void*) sa;
 166                sin6->sin6_port = port;
 167                return;
 168        }
 169#endif
 170        if (sa->sa_family == AF_INET) {
 171                struct sockaddr_in *sin = (void*) sa;
 172                sin->sin_port = port;
 173                return;
 174        }
 175        /* What? UNIX socket? IPX?? :) */
 176}
 177
 178/* We hijack this constant to mean something else */
 179/* It doesn't hurt because we will remove this bit anyway */
 180#define DIE_ON_ERROR AI_CANONNAME
 181
 182/* host: "1.2.3.4[:port]", "www.google.com[:port]"
 183 * port: if neither of above specifies port # */
 184static len_and_sockaddr* str2sockaddr(
 185                const char *host, int port,
 186IF_FEATURE_IPV6(sa_family_t af,)
 187                int ai_flags)
 188{
 189IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
 190        int rc;
 191        len_and_sockaddr *r;
 192        struct addrinfo *result = NULL;
 193        struct addrinfo *used_res;
 194        const char *org_host = host; /* only for error msg */
 195        const char *cp;
 196        struct addrinfo hint;
 197
 198        if (ENABLE_FEATURE_UNIX_LOCAL && is_prefixed_with(host, "local:")) {
 199                struct sockaddr_un *sun;
 200
 201                r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
 202                r->len = sizeof(struct sockaddr_un);
 203                r->u.sa.sa_family = AF_UNIX;
 204                sun = (struct sockaddr_un *)&r->u.sa;
 205                safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path));
 206                return r;
 207        }
 208
 209        r = NULL;
 210
 211        /* Ugly parsing of host:addr */
 212        if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
 213                /* Even uglier parsing of [xx]:nn */
 214                host++;
 215                cp = strchr(host, ']');
 216                if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
 217                        /* Malformed: must be [xx]:nn or [xx] */
 218                        bb_error_msg("bad address '%s'", org_host);
 219                        if (ai_flags & DIE_ON_ERROR)
 220                                xfunc_die();
 221                        return NULL;
 222                }
 223        } else {
 224                cp = strrchr(host, ':');
 225                if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
 226                        /* There is more than one ':' (e.g. "::1") */
 227                        cp = NULL; /* it's not a port spec */
 228                }
 229        }
 230        if (cp) { /* points to ":" or "]:" */
 231                int sz = cp - host + 1;
 232
 233                host = safe_strncpy(alloca(sz), host, sz);
 234                if (ENABLE_FEATURE_IPV6 && *cp != ':') {
 235                        cp++; /* skip ']' */
 236                        if (*cp == '\0') /* [xx] without port */
 237                                goto skip;
 238                }
 239                cp++; /* skip ':' */
 240                port = bb_strtou(cp, NULL, 10);
 241                if (errno || (unsigned)port > 0xffff) {
 242                        bb_error_msg("bad port spec '%s'", org_host);
 243                        if (ai_flags & DIE_ON_ERROR)
 244                                xfunc_die();
 245                        return NULL;
 246                }
 247 skip: ;
 248        }
 249
 250        /* Next two if blocks allow to skip getaddrinfo()
 251         * in case host name is a numeric IP(v6) address.
 252         * getaddrinfo() initializes DNS resolution machinery,
 253         * scans network config and such - tens of syscalls.
 254         */
 255        /* If we were not asked specifically for IPv6,
 256         * check whether this is a numeric IPv4 */
 257        IF_FEATURE_IPV6(if(af != AF_INET6)) {
 258                struct in_addr in4;
 259                if (inet_aton(host, &in4) != 0) {
 260                        r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in));
 261                        r->len = sizeof(struct sockaddr_in);
 262                        r->u.sa.sa_family = AF_INET;
 263                        r->u.sin.sin_addr = in4;
 264                        goto set_port;
 265                }
 266        }
 267#if ENABLE_FEATURE_IPV6
 268        /* If we were not asked specifically for IPv4,
 269         * check whether this is a numeric IPv6 */
 270        if (af != AF_INET) {
 271                struct in6_addr in6;
 272                if (inet_pton(AF_INET6, host, &in6) > 0) {
 273                        r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6));
 274                        r->len = sizeof(struct sockaddr_in6);
 275                        r->u.sa.sa_family = AF_INET6;
 276                        r->u.sin6.sin6_addr = in6;
 277                        goto set_port;
 278                }
 279        }
 280#endif
 281
 282        memset(&hint, 0 , sizeof(hint));
 283        hint.ai_family = af;
 284        /* Need SOCK_STREAM, or else we get each address thrice (or more)
 285         * for each possible socket type (tcp,udp,raw...): */
 286        hint.ai_socktype = SOCK_STREAM;
 287        hint.ai_flags = ai_flags & ~DIE_ON_ERROR;
 288        rc = getaddrinfo(host, NULL, &hint, &result);
 289        if (rc || !result) {
 290                bb_error_msg("bad address '%s'", org_host);
 291                if (ai_flags & DIE_ON_ERROR)
 292                        xfunc_die();
 293                goto ret;
 294        }
 295        used_res = result;
 296#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS
 297        while (1) {
 298                if (used_res->ai_family == AF_INET)
 299                        break;
 300                used_res = used_res->ai_next;
 301                if (!used_res) {
 302                        used_res = result;
 303                        break;
 304                }
 305        }
 306#endif
 307        r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen);
 308        r->len = used_res->ai_addrlen;
 309        memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen);
 310
 311 set_port:
 312        set_nport(&r->u.sa, htons(port));
 313 ret:
 314        if (result)
 315                freeaddrinfo(result);
 316        return r;
 317}
 318#if !ENABLE_FEATURE_IPV6
 319#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags)
 320#endif
 321
 322#if ENABLE_FEATURE_IPV6
 323len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af)
 324{
 325        return str2sockaddr(host, port, af, 0);
 326}
 327
 328len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af)
 329{
 330        return str2sockaddr(host, port, af, DIE_ON_ERROR);
 331}
 332#endif
 333
 334len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port)
 335{
 336        return str2sockaddr(host, port, AF_UNSPEC, 0);
 337}
 338
 339len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port)
 340{
 341        return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
 342}
 343
 344len_and_sockaddr* FAST_FUNC xdotted2sockaddr(const char *host, int port)
 345{
 346        return str2sockaddr(host, port, AF_UNSPEC, AI_NUMERICHOST | DIE_ON_ERROR);
 347}
 348
 349int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
 350{
 351        len_and_sockaddr *lsa;
 352        int fd;
 353        int len;
 354
 355        if (family == AF_UNSPEC) {
 356#if ENABLE_FEATURE_IPV6
 357                fd = socket(AF_INET6, sock_type, 0);
 358                if (fd >= 0) {
 359                        family = AF_INET6;
 360                        goto done;
 361                }
 362#endif
 363                family = AF_INET;
 364        }
 365
 366        fd = xsocket(family, sock_type, 0);
 367
 368        len = sizeof(struct sockaddr_in);
 369        if (family == AF_UNIX)
 370                len = sizeof(struct sockaddr_un);
 371#if ENABLE_FEATURE_IPV6
 372        if (family == AF_INET6) {
 373 done:
 374                len = sizeof(struct sockaddr_in6);
 375        }
 376#endif
 377        lsa = xzalloc(LSA_LEN_SIZE + len);
 378        lsa->len = len;
 379        lsa->u.sa.sa_family = family;
 380        *lsap = lsa;
 381        return fd;
 382}
 383
 384int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap)
 385{
 386        return xsocket_type(lsap, AF_UNSPEC, SOCK_STREAM);
 387}
 388
 389static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
 390{
 391        int fd;
 392        len_and_sockaddr *lsa;
 393
 394        if (bindaddr && bindaddr[0]) {
 395                lsa = xdotted2sockaddr(bindaddr, port);
 396                /* user specified bind addr dictates family */
 397                fd = xsocket(lsa->u.sa.sa_family, sock_type, 0);
 398        } else {
 399                fd = xsocket_type(&lsa, AF_UNSPEC, sock_type);
 400                set_nport(&lsa->u.sa, htons(port));
 401        }
 402        setsockopt_reuseaddr(fd);
 403        xbind(fd, &lsa->u.sa, lsa->len);
 404        free(lsa);
 405        return fd;
 406}
 407
 408int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port)
 409{
 410        return create_and_bind_or_die(bindaddr, port, SOCK_STREAM);
 411}
 412
 413int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port)
 414{
 415        return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM);
 416}
 417
 418
 419#if ENABLE_IFPLUGD || ENABLE_FEATURE_MDEV_DAEMON || ENABLE_UEVENT
 420int FAST_FUNC create_and_bind_to_netlink(int proto, int grp, unsigned rcvbuf)
 421{
 422        struct sockaddr_nl sa;
 423        int fd;
 424
 425        fd = xsocket(AF_NETLINK, SOCK_DGRAM, proto);
 426
 427        /* Set receive buffer size before binding the socket
 428         * We want to have enough space before we start receiving messages.
 429         */
 430        if (rcvbuf != 0) {
 431                setsockopt_SOL_SOCKET_int(fd, SO_RCVBUF, rcvbuf);
 432                /* SO_RCVBUFFORCE (root only) can go above net.core.rmem_max */
 433                setsockopt_SOL_SOCKET_int(fd, SO_RCVBUFFORCE, rcvbuf);
 434# if 0
 435                {
 436                        int z;
 437                        socklen_t zl = sizeof(z);
 438                        getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &z, &zl);
 439                        bb_error_msg("SO_RCVBUF:%d", z);
 440                }
 441# endif
 442        }
 443
 444        memset(&sa, 0, sizeof(sa));
 445        sa.nl_family = AF_NETLINK;
 446        sa.nl_pid = getpid();
 447        sa.nl_groups = grp;
 448        xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
 449        close_on_exec_on(fd);
 450
 451        return fd;
 452}
 453#endif
 454
 455int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port)
 456{
 457        int fd;
 458        len_and_sockaddr *lsa;
 459
 460        lsa = xhost2sockaddr(peer, port);
 461        fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
 462        setsockopt_reuseaddr(fd);
 463        xconnect(fd, &lsa->u.sa, lsa->len);
 464        free(lsa);
 465        return fd;
 466}
 467
 468int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa)
 469{
 470        int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
 471        xconnect(fd, &lsa->u.sa, lsa->len);
 472        return fd;
 473}
 474
 475/* We hijack this constant to mean something else */
 476/* It doesn't hurt because we will add this bit anyway */
 477#define IGNORE_PORT NI_NUMERICSERV
 478static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
 479{
 480        char host[128];
 481        char serv[16];
 482        int rc;
 483        socklen_t salen;
 484
 485        if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) {
 486                struct sockaddr_un *sun = (struct sockaddr_un *)sa;
 487                return xasprintf("local:%.*s",
 488                                (int) sizeof(sun->sun_path),
 489                                sun->sun_path);
 490        }
 491
 492        salen = LSA_SIZEOF_SA;
 493#if ENABLE_FEATURE_IPV6
 494        if (sa->sa_family == AF_INET)
 495                salen = sizeof(struct sockaddr_in);
 496        if (sa->sa_family == AF_INET6)
 497                salen = sizeof(struct sockaddr_in6);
 498#endif
 499        rc = getnameinfo(sa, salen,
 500                        host, sizeof(host),
 501        /* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */
 502                        serv, sizeof(serv),
 503                        /* do not resolve port# into service _name_ */
 504                        flags | NI_NUMERICSERV
 505        );
 506        if (rc)
 507                return NULL;
 508        if (flags & IGNORE_PORT)
 509                return xstrdup(host);
 510#if ENABLE_FEATURE_IPV6
 511        if (sa->sa_family == AF_INET6) {
 512                if (strchr(host, ':')) /* heh, it's not a resolved hostname */
 513                        return xasprintf("[%s]:%s", host, serv);
 514                /*return xasprintf("%s:%s", host, serv);*/
 515                /* - fall through instead */
 516        }
 517#endif
 518        /* For now we don't support anything else, so it has to be INET */
 519        /*if (sa->sa_family == AF_INET)*/
 520                return xasprintf("%s:%s", host, serv);
 521        /*return xstrdup(host);*/
 522}
 523
 524char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa)
 525{
 526        return sockaddr2str(sa, 0);
 527}
 528
 529char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
 530{
 531        return sockaddr2str(sa, IGNORE_PORT);
 532}
 533
 534char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
 535{
 536        return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
 537}
 538#ifndef NI_NUMERICSCOPE
 539# define NI_NUMERICSCOPE 0
 540#endif
 541char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
 542{
 543        return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE);
 544}
 545
 546char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
 547{
 548        return sockaddr2str(sa, NI_NUMERICHOST | NI_NUMERICSCOPE | IGNORE_PORT);
 549}
 550