Logo Search packages:      
Sourcecode: dcc version File versions  Download package

get_port.c

/* Distributed Checksum Clearinghouse
 *
 * convert a service name to a port number
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.55 $Revision$
 */

#include "dcc_clnt.h"
#ifndef DCC_WIN32
#include <arpa/inet.h>              /* for AIX */
#endif


DCC_SOCKU dcc_hostaddrs[MAX_DCC_HOSTADDRS];
DCC_SOCKU *dcc_hostaddrs_end;


static u_char
copy_hp_to_hostaddrs(const struct hostent *hp,
                 u_char use_ipv6)
{
      DCC_SOCKU *sup;
      struct in6_addr addr6;
      struct in_addr addr;
      const void *v;
      int i;

      i = 0;
      sup = dcc_hostaddrs;
      while (i < DIM(dcc_hostaddrs)) {
            v = hp->h_addr_list[i];
            if (!v)
                  break;

            if (hp->h_addrtype == AF_INET) {
                  if (use_ipv6 == 1) {
                        dcc_ipv4toipv6(&addr6, *(struct in_addr *)v);
                        dcc_mk_su(sup, AF_INET6, &addr6, 0);

                  } else {
                        dcc_mk_su(sup, AF_INET, v, 0);
                  }

            } else if (hp->h_addrtype == AF_INET6) {
                  if (use_ipv6 == 1) {
                        dcc_mk_su(sup, AF_INET6, v, 0);

                  } else {
                        if (!dcc_ipv6toipv4(&addr,
                                        (struct in6_addr *)v))
                              continue;
                        dcc_mk_su(sup, AF_INET, &addr, 0);
                  }

            } else {
                  continue;
            }

            ++i;
            ++sup;
      }
      dcc_hostaddrs_end = sup;

      return i > 0;
}



#ifdef USE_GETADDRINFO
static u_char
copy_ai_to_hostaddrs(const struct addrinfo *ai,
                 u_char use_ipv6)
{
      DCC_SOCKU *sup;
      struct in6_addr addr6;
      struct in_addr addr;
      int i;

      i = 0;
      sup = dcc_hostaddrs;
      while (i < DIM(dcc_hostaddrs) && ai) {
            if (ai->ai_family == AF_INET) {
                  if (use_ipv6 == 1) {
                        dcc_ipv4toipv6(&addr6,
                                     ((struct sockaddr_in *
                                     )ai->ai_addr)->sin_addr);
                        dcc_mk_su(sup, AF_INET6, &addr6, 0);

                  } else {
                        dcc_mk_su(sup, AF_INET,
                                &((struct sockaddr_in *
                                   )ai->ai_addr)->sin_addr, 0);
                  }

            } else if (ai->ai_family == AF_INET6) {
                  if (use_ipv6 == 1) {
                        dcc_mk_su(sup, AF_INET6,
                                &((struct sockaddr_in6 *
                                  )ai->ai_addr)->sin6_addr, 0);

                  } else {
                        if (!dcc_ipv6toipv4(&addr,
                                        &((struct sockaddr_in6 *
                                           )ai->ai_addr)
                                        ->sin6_addr))
                              continue;
                        dcc_mk_su(sup, AF_INET, &addr, 0);
                  }

            } else {
                  continue;
            }

            ai = ai->ai_next;
            ++i;
            ++sup;
      }
      dcc_hostaddrs_end = sup;

      return i > 0;
}
#endif



/* get port number
 *    Note that this function uses dcc_host_lock() and dcc_host_unlock() */
u_int                         /* DCC_GET_PORT_INVALID or port # */
dcc_get_port(DCC_EMSG emsg,
           const char *portname,
           u_int def_port,          /* DCC_GET_PORT_INVALID or default */
           const char *fnm, int lineno)
{
      char *p;
      unsigned long l;
      struct servent *sp;
      u_int16_t port;


      if (portname[0] == '\0'
          || !strcmp(portname, "-")) {
            if (def_port != DCC_GET_PORT_INVALID)
                  return def_port;
            dcc_pemsg(EX_USAGE, emsg, "missing port%s",
                    fnm_lineno(fnm, lineno));
            return DCC_GET_PORT_INVALID;
      }

      /* first try a numeric port number, since that is common and
       * the getservby* functions are so slow. */
      l = strtoul(portname, &p,0);
      if (*p == '\0' && l > 0 && l <= 65535)
            return htons((u_int16_t)l);

      dcc_host_lock();
      sp = getservbyname(portname, 0);
      if (sp) {
            port = sp->s_port;
            dcc_host_unlock();
            return port;
      }
      dcc_host_unlock();

      dcc_pemsg(EX_USAGE, emsg, "invalid port \"%s\"%s",
              portname, fnm_lineno(fnm, lineno));
      return DCC_GET_PORT_INVALID;
}



/* Convert an IPv4 host name to an IPv4 or IPv6 address     by calling
 *    Rgethostbyname() or gethostbyname()
 * This must be protected with dcc_host_lock() and dcc_host_unlock(). */
static u_char                       /* 0=failed */
dcc_get_host_ipv4(const char *nm,   /* look for this name */
              u_char use_ipv6,      /* 0=IPv4, 1=require IPv6, 2=guess */
              int *ep,        /* put errno or herrno here */
              struct hostent *(WSAAPI fnc)(const char *))
{
      const struct hostent *hp;
      static struct in_addr ipv4;

#ifdef NO_IPV6
      if (use_ipv6 == 2)
            use_ipv6 = 0;
#endif
      if (!dcc_host_locked)
            dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");


      /* First see if it is a number to avoid the MicroStupid stall
       * when doing a gethostbyname() on a number */
      ipv4.s_addr = inet_addr(nm);
      if (ipv4.s_addr != INADDR_NONE) {
            if (use_ipv6) {
                  dcc_hostaddrs[0].sa.sa_family = AF_INET6;
                  dcc_ipv4toipv6(&dcc_hostaddrs[0].ipv6.sin6_addr, ipv4);
            } else {
                  dcc_hostaddrs[0].sa.sa_family = AF_INET;
                  dcc_hostaddrs[0].ipv4.sin_addr = ipv4;
            }
            dcc_hostaddrs_end = &dcc_hostaddrs[1];
            return 1;
      }

      hp = fnc(nm);
      if (!hp) {
            *ep = h_errno;
            return 0;
      }

      return copy_hp_to_hostaddrs(hp, use_ipv6);
}



/* This must be protected with dcc_host_lock()and dcc_host_unlock(). */
u_char                              /* 0=failed */
dcc_get_host_SOCKS(const char *nm,  /* look for this name */
               u_char use_ipv6,     /* 0=IPv4, 1=require IPv6, 2=guess */
               int *ep)       /* put errno or herrno here */
{
      /* since there is no Rgetaddrinfo() or equivalent,
       * use the normal resolver if we need an IPv6 address */
      if (use_ipv6 == 1)
            return dcc_get_host(nm, use_ipv6, ep);
      return dcc_get_host_ipv4(nm, use_ipv6, ep, Rgethostbyname);
}



/* This must be protected with dcc_host_lock()and dcc_host_unlock().
 *    It does not assme that gethostbyname() or whatever is thread safe.
 */
u_char                              /* 0=failed */
dcc_get_host(const char *nm,        /* look for this name */
           u_char use_ipv6,         /* 0=IPv4, 1=require IPv6, 2=guess */
           int *ep)                 /* put errno or herrno here */
{
#ifdef NO_IPV6
      return dcc_get_host_ipv4(nm, use_ipv6, ep, gethostbyname);
#endif
#ifdef USE_GETIPNODEBYNAME
      static struct hostent *hp;
      u_char result;

      if (!dcc_host_locked)
            dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");

      if (use_ipv6 == 2) {
            hp = getipnodebyname(nm, AF_INET6,
                             AI_V4MAPPED | AI_ADDRCONFIG, ep);
            if (hp) {
                  result = copy_hp_to_hostaddrs(hp, 1);
                  freehostent(hp);
                  if (result)
                        return 1;
            }
            use_ipv6 = 0;
      }
      if (use_ipv6)
            hp = getipnodebyname(nm, AF_INET6, AI_V4MAPPED|AI_ALL, ep);
      else
            hp = getipnodebyname(nm, AF_INET, 0, ep);
      if (!hp)
            return 0;
      result = copy_hp_to_hostaddrs(hp, use_ipv6);
      freehostent(hp);
      return result;
#endif
#ifdef USE_GETADDRINFO
      static struct addrinfo hints;
      struct addrinfo *ai;
      int error;
      u_char result;

      if (!dcc_host_locked)
            dcc_logbad(EX_SOFTWARE, "dcc_get_host() not locked");

      if (use_ipv6 == 2) {
            hints.ai_family = AF_INET6;
            if (!getaddrinfo(nm, 0, &hints, &ai)) {
                  result = copy_ai_to_hostaddrs(ai, 1);
                  freeaddrinfo(ai);
                  if (result)
                        return 1;
            }
            use_ipv6 = 0;
      }
      hints.ai_family = use_ipv6 ? 0 : AF_INET;
      error = getaddrinfo(nm, 0, &hints, &ai);
      if (error)
            return 0;
      *ep = error;
      result = copy_ai_to_hostaddrs(ai, use_ipv6);
      freeaddrinfo(ai);
      return result;
#endif
}



/* make socket address from an IP address, a family, and a port number */
DCC_SOCKU *
dcc_mk_su(DCC_SOCKU *su,            /* put it here */
        int family,                 /* AF_INET or AF_INET6 */
        const void *addrp,          /* this IP address; 0=INADDR_ANY */
        u_short port)
{
      memset(su, 0, sizeof(*su));   /* assume INADDR_ANY=0 */
      su->sa.sa_family = family;
      if (family == AF_INET) {
#ifdef DCC_HAVE_SA_LEN
            su->sa.sa_len = sizeof(struct sockaddr_in);
#endif
            su->ipv4.sin_port = port;
            if (addrp)
                  memcpy(&su->ipv4.sin_addr, addrp,
                         sizeof(su->ipv4.sin_addr));
      } else {
#ifdef DCC_HAVE_SA_LEN
            su->sa.sa_len = sizeof(struct sockaddr_in6);
#endif
            su->ipv6.sin6_port = port;
            if (addrp)
                  memcpy(&su->ipv6.sin6_addr, addrp,
                         sizeof(su->ipv6.sin6_addr));
      }

      return su;
}



/* strip leading and trailing white space */
static const char *
dcc_strip_white(const char *str, u_int *lenp)
{
      const char *end;
      char c;

      str += strspn(str, DCC_WHITESPACE);
      end = str+strlen(str);
      while (end > str) {
            c = *(end-1);
            if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
                  break;
            --end;
      }
      *lenp = end-str;
      return str;
}



/* get a socket address from a dotted quad or IPv6 string */
u_char
dcc_str2ip(DCC_SOCKU *su, const char *str)
{
#ifndef NO_IPV6
      u_int len;
      char buf[INET6_ADDRSTRLEN];
#endif

#ifdef HAVE_INET_ATON
      if (0 < inet_aton(str, &su->ipv4.sin_addr)) {
            su->sa.sa_family = AF_INET;
            return 1;
      }
#else
      u_int addr = inet_addr(str);
      if (su->ipv4.sin_addr.s_addr != INADDR_NONE) {
            su->ipv4.sin_addr.s_addr = addr;
            su->sa.sa_family = AF_INET;
            return 1;
      }
#endif

#ifndef NO_IPV6
      /* Try IPv6 only after failing to understand the address as IPv4.
       *
       * inet_pton() does not like blanks or terminal '\n'
       * It is also too smart by half and assumes that it is
       * given a pointer to struct sockaddr.  When it decodes
       * an IPv4 address, it sticks it 4 bytes before the
       * start of the IPv6 buffer it is given. */
      str = dcc_strip_white(str, &len);
      if (len == 0 || len >= sizeof(buf))
            return 0;
      memcpy(buf, str, len);
      buf[len] = '\0';
      if (0 < inet_pton(AF_INET6, buf, &su->ipv6.sin6_addr)) {
            su->sa.sa_family = AF_INET6;
            return 1;
      }
#endif
      return 0;
}



void
dcc_bits2mask(struct in6_addr *mask, int bits)
{
      int wordno, i;

      for (wordno = 0; wordno < 4; ++wordno) {
            i = bits - wordno*32;
            if (i >= 32) {
                  mask->s6_addr32[wordno] = 0xffffffff;
                  continue;
            }
            if (i <= 0) {
                  mask->s6_addr32[wordno] = 0;
            } else {
                  mask->s6_addr32[wordno] = 0xffffffff << (32-i);
            }
            mask->s6_addr32[wordno] = htonl(mask->s6_addr32[wordno]);
      }
}



/* get an IPv6 address and netmask */
int                           /* # of bits, 0=not address, -1 error */
dcc_str2cidr(DCC_EMSG emsg,
           struct in6_addr *addr6,
           struct in6_addr *mask6,
           const char *str,
           const char *fnm, int lineno)
{
      char addrstr[INET6_ADDRSTRLEN];
      DCC_SOCKU su;
      struct in6_addr mask6_loc;
      const char *mp;
      char *p;
      u_int str_len;
      int bits, wordno;

      str = dcc_strip_white(str, &str_len);
      mp = strchr(str, '/');

      if (!mp) {
            if (str_len >= ISZ(addrstr))
                  return 0;   /* not an IP address */
            memcpy(addrstr, str, str_len);
            addrstr[str_len] = '\0';
      } else if (mp == str
               || mp >= str+sizeof(addrstr)) {
            dcc_pemsg(EX_NOHOST, emsg,
                    "invalid IP address range \"%s\"%s",
                    str, fnm_lineno(fnm, lineno));
            return -1;
      } else {
            memcpy(addrstr, str, mp-str);
            addrstr[mp-str] = '\0';
      }
      if (!dcc_str2ip(&su, addrstr))
            return 0;         /* not an IP address */

      if (su.sa.sa_family == AF_INET6) {
            *addr6 = su.ipv6.sin6_addr;
            bits = mp ? strtoul(++mp, &p, 10) : 128;
      } else {
            dcc_ipv4toipv6(addr6, su.ipv4.sin_addr);
            bits = mp ? (strtoul(++mp, &p, 10) + 128-32) : 128;
      }
      if ((mp && *p != '\0' && p < str+str_len)
          || bits > 128 || bits <= 0) {
            dcc_pemsg(EX_NOHOST, emsg,
                    "invalid netmask length \"%s\"%s",
                    str, fnm_lineno(fnm, lineno));
            return -1;
      }

      if (!mask6)
            mask6 = &mask6_loc;
      dcc_bits2mask(mask6, bits);
      for (wordno = 0; wordno < 4; ++wordno) {
            if ((addr6->s6_addr32[wordno]
                 & ~mask6->s6_addr32[wordno]) != 0) {
                  dcc_pemsg(EX_NOHOST, emsg,
                          "%s does not start on %s-bit CIDR boundary%s",
                          str, mp, fnm_lineno(fnm, lineno));
                  return -1;
            }
      }

      return bits;
}



/* Create and bind a UDP socket.
 *    The client library uses this function to determine whether
 *    IPv6 works. */
u_char                              /* 0=fatal error, 1=done */
dcc_udp_bind(DCC_EMSG emsg,
           SOCKET *fdp,       /* INVALID_SOCKET or socket */
           const DCC_SOCKU *sup,
           int *retry_secs)         /* -1=try anonymous port */
{
      int tenths, i;
      DCC_SOCKU su;
#ifdef DCC_WIN32
      u_long on;
#endif


#ifdef NO_IPV6
      if (sup->sa.sa_family == AF_INET6) {
            *fdp = INVALID_SOCKET;
            return 1;
      }
#endif
      *fdp = socket(sup->sa.sa_family, SOCK_DGRAM, 0);
      if (*fdp == INVALID_SOCKET) {
            dcc_pemsg(EX_OSERR, emsg, "socket(UDP): %s", ERROR_STR());
#ifndef NO_IPV6
            /* let the caller try again if this system does not do IPv6 */
            if (sup->sa.sa_family == AF_INET6
                && (errno == EPFNOSUPPORT
                  || errno == EPROTONOSUPPORT))
                  return 1;
#endif
            return 0;
      }

#ifdef DCC_WIN32
      on = 1;
      if (SOCKET_ERROR == ioctlsocket(*fdp, FIONBIO, &on)) {
            dcc_pemsg(EX_OSERR, emsg, "ioctlsocket(UDP, FIONBIO): %s",
                    ERROR_STR());
            closesocket(*fdp);
            *fdp = INVALID_SOCKET;
            return 0;
      }
#else
      if (0 > fcntl(*fdp, F_SETFD, FD_CLOEXEC)) {
            dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP, FD_CLOEXEC): %s",
                    ERROR_STR());
            close(*fdp);
            *fdp = INVALID_SOCKET;
            return 0;
      }
      if (-1 == fcntl(*fdp, F_SETFL,
                  fcntl(*fdp, F_GETFL, 0) | O_NONBLOCK)) {
            dcc_pemsg(EX_OSERR, emsg, "fcntl(UDP O_NONBLOCK): %s",
                    ERROR_STR());
            close(*fdp);
            *fdp = INVALID_SOCKET;
            return 0;
      }
#endif

      tenths = 10;
      for (;;) {
            if (SOCKET_ERROR != bind(*fdp, &sup->sa, DCC_SU_LEN(sup)))
                  return 1;

            if (errno == EADDRINUSE
                && *retry_secs < 0
                && sup != &su) {
                  su = *sup;
                  *DCC_SU_PORT(&su) = 0;
                  sup = &su;
                  continue;
            }

            if (errno != EADDRINUSE
                || !retry_secs || *retry_secs <= 0) {
#ifndef NO_IPV6
                  if (sup->sa.sa_family == AF_INET6
                      && (errno == EPFNOSUPPORT
                        || errno == EPROTONOSUPPORT))
                        i = 1;
                  else
#endif
                        i = 0;
                  dcc_pemsg(EX_OSERR, emsg, "bind(UDP %s): %s",
                          dcc_su2str(sup), ERROR_STR());
                  closesocket(*fdp);
                  *fdp = INVALID_SOCKET;
                  return i;
            }

            usleep(100*1000);
            if (!--tenths) {
                  --*retry_secs;
                  tenths = 10;
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index