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; }
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; }
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; }