Esempio n. 1
0
pr_netaddr_t *pr_netaddr_get_addr(pool *p, const char *name,
    array_header **addrs) {

  struct sockaddr_in v4;
  pr_netaddr_t *na = NULL;
  int res;

  if (p == NULL || name == NULL) {
    errno = EINVAL;
    return NULL;
  }

  pr_trace_msg(trace_channel, 10, "resolving name '%s' to IP address",
    name);

  /* Attempt to translate the given name into a pr_netaddr_t using
   * pr_inet_pton() first.
   *
   * First, if IPv6 support is enabled, we try to translate the name using
   * pr_inet_pton(AF_INET6) on the hopes that the given string is a valid
   * representation of an IPv6 address.  If that fails, or if IPv6 support
   * is not enabled, we try with pr_inet_pton(AF_INET).  If that fails, we
   * assume that the given name is a DNS name, and we call pr_getaddrinfo().
   */

  na = (pr_netaddr_t *) pcalloc(p, sizeof(pr_netaddr_t));

#ifdef PR_USE_IPV6
  if (use_ipv6) {
    struct sockaddr_in6 v6;
    memset(&v6, 0, sizeof(v6));
    v6.sin6_family = AF_INET6;

# ifdef SIN6_LEN
    v6.sin6_len = sizeof(struct sockaddr_in6);
# endif /* SIN6_LEN */

    res = pr_inet_pton(AF_INET6, name, &v6.sin6_addr);
    if (res > 0) {
      pr_netaddr_set_family(na, AF_INET6);
      pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v6);
      if (addrs)
        *addrs = NULL;

      pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv6 address %s", name,
        pr_netaddr_get_ipstr(na));
      return na;
    }
  }
#endif /* PR_USE_IPV6 */

  memset(&v4, 0, sizeof(v4));
  v4.sin_family = AF_INET;

# ifdef SIN_LEN
  v4.sin_len = sizeof(struct sockaddr_in);
# endif /* SIN_LEN */

  res = pr_inet_pton(AF_INET, name, &v4.sin_addr);
  if (res > 0) {
    pr_netaddr_set_family(na, AF_INET);
    pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v4);
    if (addrs)
      *addrs = NULL;

    pr_trace_msg(trace_channel, 7, "'%s' resolved to IPv4 address %s", name,
      pr_netaddr_get_ipstr(na));
    return na;

  } else if (res == 0) {

    /* If pr_inet_pton() returns 0, it means that name does not represent a
     * valid network address in the specified address family.  Usually,
     * this means that name is actually a DNS name, not an IP address
     * string.  So we treat it as a DNS name, and use getaddrinfo(3) to
     * resolve that name to its IP address(es).
     */

    struct addrinfo hints, *info = NULL;
    int gai_res = 0;

    memset(&hints, 0, sizeof(hints));

    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    pr_trace_msg(trace_channel, 7,
      "attempting to resolve '%s' to IPv4 address via DNS", name);
    gai_res = pr_getaddrinfo(name, NULL, &hints, &info);
    if (gai_res != 0) {
      if (gai_res != EAI_SYSTEM) {
        pr_trace_msg(trace_channel, 1, "IPv4 getaddrinfo '%s' error: %s",
          name, pr_gai_strerror(gai_res));

      } else {
        pr_trace_msg(trace_channel, 1,
          "IPv4 getaddrinfo '%s' system error: [%d] %s", name,
          errno, strerror(errno));
      }

      return NULL;
    }

    if (info) {
      /* Copy the first returned addr into na, as the return value. */
      pr_netaddr_set_family(na, info->ai_family);
      pr_netaddr_set_sockaddr(na, info->ai_addr);

      pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name,
        info->ai_family == AF_INET ? "IPv4" : "IPv6",
        pr_netaddr_get_ipstr(na));

      pr_freeaddrinfo(info);
    }

#ifdef PR_USE_IPV6
    if (use_ipv6 && addrs) {
      /* Do the call again, this time for IPv6 addresses.
       *
       * We make two separate getaddrinfo(3) calls, rather than one
       * with a hint of AF_UNSPEC, because of certain bugs where the use
       * of AF_UNSPEC does not function as advertised.  (I suspect this
       * bug was caused by proftpd's calling pattern, but as I could
       * not track it down, and as there are reports of AF_UNSPEC not
       * being as fast as AF_INET/AF_INET6, it just seemed easier to
       * do it this way.)
       */

      gai_res = 0;

      memset(&hints, 0, sizeof(hints));

      hints.ai_family = AF_INET6;
      hints.ai_socktype = SOCK_STREAM;

      pr_trace_msg(trace_channel, 7,
        "attempting to resolve '%s' to IPv6 address via DNS", name);
      gai_res = pr_getaddrinfo(name, NULL, &hints, &info);
      if (gai_res != 0) {
        if (gai_res != EAI_SYSTEM) {
          pr_trace_msg(trace_channel, 1, "IPv6 getaddrinfo '%s' error: %s",
            name, pr_gai_strerror(gai_res));

        } else {
          pr_trace_msg(trace_channel, 1, 
            "IPv6 getaddrinfo '%s' system error: [%d] %s", name,
            errno, strerror(errno));
        }

        return na;
      }

      if (info) {
        pr_netaddr_t **elt;

        *addrs = make_array(p, 0, sizeof(pr_netaddr_t *));
        elt = push_array(*addrs);

        *elt = pcalloc(p, sizeof(pr_netaddr_t));
        pr_netaddr_set_family(*elt, info->ai_family);
        pr_netaddr_set_sockaddr(*elt, info->ai_addr);

        pr_trace_msg(trace_channel, 7, "resolved '%s' to %s address %s", name,
          info->ai_family == AF_INET ? "IPv4" : "IPv6",
          pr_netaddr_get_ipstr(*elt));

        pr_freeaddrinfo(info);
      }
    }
#endif /* PR_USE_IPV6 */

    return na;
  }

  pr_trace_msg(trace_channel, 8, "failed to resolve '%s' to an IP address",
    name);
  return NULL;
}
Esempio n. 2
0
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg,
    const pr_netaddr_t *addr, int cmd_id, const char *net_proto) {
  pr_netaddr_t *res = NULL, na;
  int family = 0;
  unsigned short port = 0;
  char delim, *msg_str, *ptr;
  size_t msglen;

  if (p == NULL ||
      msg == NULL ||
      addr == NULL) {
    errno = EINVAL;
    return NULL;
  }

  if (cmd_id == PR_CMD_EPSV_ID) {
    /* First, find the opening '(' character. */
    ptr = strchr(msg, '(');
    if (ptr == NULL) {
      pr_trace_msg(trace_channel, 12,
        "missing starting '(' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    /* Make sure that the last character is a closing ')'. */
    msglen = strlen(ptr);
    if (ptr[msglen-1] != ')') {
      pr_trace_msg(trace_channel, 12,
        "missing ending ')' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    msg_str = pstrndup(p, ptr+1, msglen-2);

  } else {
    msg_str = pstrdup(p, msg);
  }

  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
   * where <d> is an arbitrary delimiter character.
   */
  delim = *msg_str++;

  /* If the network protocol string (e.g. sent by client in EPSV command) is
   * null, then determine the protocol family from the address family we were
   * given.
   */

  /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */
  if (net_proto != NULL) {
    if (strncasecmp(net_proto, "all", 4) == 0) {
      net_proto = NULL;
    }
  }

  if (net_proto == NULL) {
    if (*msg_str == delim) {
      switch (pr_netaddr_get_family(addr)) {
        case AF_INET:
          family = 1;
          break;

#ifdef PR_USE_IPV6
        case AF_INET6:
          if (pr_netaddr_use_ipv6()) {
            family = 2;
            break;
          }
#endif /* PR_USE_IPV6 */

        default:
          break;
      }

    } else {
      family = atoi(msg_str);
    }

  } else {
    family = atoi(net_proto);
  }

  switch (family) {
    case 1:
      pr_trace_msg(trace_channel, 19, "parsed IPv4 address from '%s'", msg);
      break;

#ifdef PR_USE_IPV6
    case 2:
      pr_trace_msg(trace_channel, 19, "parsed IPv6 address from '%s'", msg);
      if (pr_netaddr_use_ipv6()) {
        break;
      }
#endif /* PR_USE_IPV6 */

    default:
      pr_trace_msg(trace_channel, 12,
        "unsupported network protocol %d", family);
      errno = EPROTOTYPE;
      return NULL;
  }

  /* Now, skip past those numeric characters that atoi() used. */
  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str == delim) {
    msg_str++;

  } else {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  pr_netaddr_clear(&na);

  /* If the next character IS the delimiter, then the address portion is
   * omitted (which is permissible).
   */
  if (*msg_str == delim) {
    pr_netaddr_set_family(&na, pr_netaddr_get_family(addr));
    pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr));
    msg_str++;

  } else {
    ptr = strchr(msg_str, delim);
    if (ptr == NULL) {
      /* Badly formatted message. */
      errno = EINVAL;
      return NULL;
    }

    /* Twiddle the string so that just the address portion will be processed
     * by pr_inet_pton().
     */
    *ptr = '\0';

    /* Use pr_inet_pton() to translate the address string into the address
     * value.
     */
    switch (family) {
      case 1: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET;
        }

        if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv4 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }

      case 2: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET6);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET6;
        }

        if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv6 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }
    }

    /* Advance past the address portion of the argument. */
    msg_str = ++ptr;
  }

  port = atoi(msg_str);

  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str != delim) {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  /* XXX Use a pool other than session.pool here, in the future. */ 
  res = pr_netaddr_dup(session.pool, &na);
  pr_netaddr_set_port(res, htons(port));

  return res;
}
Esempio n. 3
0
pr_netaddr_t *pr_netaddr_get_addr(pool *p, const char *name,
    array_header **addrs) {

  struct sockaddr_in v4;
#ifdef PR_USE_IPV6
  struct sockaddr_in6 v6;
#endif /* PR_USE_IPV6 */
  pr_netaddr_t *na = NULL;
  int res;

  if (p == NULL || name == NULL) {
    errno = EINVAL;
    return NULL;
  }

  /* Attempt to translate the given name into a pr_netaddr_t using
   * pr_inet_pton() first.
   *
   * First, if IPv6 support is enabled, we try to translate the name using
   * pr_inet_pton(AF_INET6) on the hopes that the given string is a valid
   * representation of an IPv6 address.  If that fails, or if IPv6 support
   * is not enabled, we try with pr_inet_pton(AF_INET).  If that fails, we
   * assume that the given name is a DNS name, and we call pr_getaddrinfo().
   */

  na = (pr_netaddr_t *) pcalloc(p, sizeof(pr_netaddr_t));

#ifdef PR_USE_IPV6
  memset(&v6, 0, sizeof(v6));
  v6.sin6_family = AF_INET6;

# ifdef SIN6_LEN
  v6.sin6_len = sizeof(struct sockaddr_in6);
# endif /* SIN6_LEN */

  res = pr_inet_pton(AF_INET6, name, &v6.sin6_addr);
  if (res > 0) {
    pr_netaddr_set_family(na, AF_INET6);
    pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v6);
    if (addrs)
      *addrs = NULL;

    pr_log_debug(DEBUG10, "'%s' resolved to an IPv6 address", name);
    return na;
  }
#endif

  memset(&v4, 0, sizeof(v4));
  v4.sin_family = AF_INET;

# ifdef SIN_LEN
  v4.sin_len = sizeof(struct sockaddr_in);
# endif /* SIN_LEN */

  res = pr_inet_pton(AF_INET, name, &v4.sin_addr);
  if (res > 0) {
    pr_netaddr_set_family(na, AF_INET);
    pr_netaddr_set_sockaddr(na, (struct sockaddr *) &v4);
    if (addrs)
      *addrs = NULL;

    pr_log_debug(DEBUG10, "'%s' resolved to an IPv4 address", name);
    return na;

  } else if (res == 0) {

    /* If pr_inet_pton() returns 0, it means that name does not represent a
     * valid network address in the specified address family.  Usually,
     * this means that name is actually a DNS name, not an IP address
     * string.  So we treat it as a DNS name, and use getaddrinfo(3) to
     * resolve that name to its IP address(es).
     */

    struct addrinfo hints, *info = NULL;
    int gai_res = 0;

    memset(&hints, 0, sizeof(hints));

#ifdef PR_USE_IPV6
    /* This looks up both IPv4 (as IPv6-mapped) and IPv6 addresses. */
    hints.ai_family = AF_UNSPEC;
#else
    hints.ai_family = AF_INET;
#endif /* PR_USE_IPV6 */
    hints.ai_socktype = SOCK_STREAM;

    pr_log_debug(DEBUG10, "attempting to resolve '%s' via DNS", name);

    gai_res = pr_getaddrinfo(name, NULL, &hints, &info);
    if (gai_res != 0) {
      pr_log_pri(PR_LOG_INFO, "getaddrinfo '%s' error: %s", name,
        res != EAI_SYSTEM ? pr_gai_strerror(gai_res) : strerror(errno));
      return NULL;
    }

    if (info) {
      pr_log_debug(DEBUG10, "resolved '%s' to an %s address", name,
        info->ai_family == AF_INET ? "IPv4" : "IPv6");

      /* Copy the first returned addr into na, as the return value. */
      pr_netaddr_set_family(na, info->ai_family);
      pr_netaddr_set_sockaddr(na, info->ai_addr);

      /* If the caller provided a pointer for any additional addresses,
       * then we cycle through the rest of getaddrinfo(3)'s results and
       * build a list to return to the caller.
       */
      if (addrs) {
        struct addrinfo *ai;
        *addrs = make_array(p, 0, sizeof(pr_netaddr_t *));

        for (ai = info->ai_next; ai; ai = ai->ai_next) {
          pr_netaddr_t **elt = push_array(*addrs);

          *elt = pcalloc(p, sizeof(pr_netaddr_t));
          pr_netaddr_set_family(*elt, ai->ai_family);
          pr_netaddr_set_sockaddr(*elt, ai->ai_addr);
        }
      }

      pr_freeaddrinfo(info);
      return na;
    }
  }

  pr_log_debug(DEBUG10, "failed to resolve '%s' to an IP address", name);
  return NULL;
}