/* Copy a connection structure, also creates a sub pool for the new * connection. */ conn_t *pr_inet_copy_conn(pool *p, conn_t *c) { conn_t *res = NULL; pool *sub_pool = NULL; if (p == NULL || c == NULL) { errno = EINVAL; return NULL; } sub_pool = make_sub_pool(p); pr_pool_tag(sub_pool, "inet_copy_conn pool"); res = (conn_t *) pcalloc(sub_pool, sizeof(conn_t)); memcpy(res, c, sizeof(conn_t)); res->pool = sub_pool; res->instrm = res->outstrm = NULL; if (c->local_addr) { res->local_addr = pr_netaddr_alloc(res->pool); if (pr_netaddr_set_family(res->local_addr, pr_netaddr_get_family(c->local_addr)) < 0) { destroy_pool(res->pool); return NULL; } pr_netaddr_set_sockaddr(res->local_addr, pr_netaddr_get_sockaddr(c->local_addr)); } if (c->remote_addr) { res->remote_addr = pr_netaddr_alloc(res->pool); if (pr_netaddr_set_family(res->remote_addr, pr_netaddr_get_family(c->remote_addr)) < 0) { destroy_pool(res->pool); return NULL; } pr_netaddr_set_sockaddr(res->remote_addr, pr_netaddr_get_sockaddr(c->remote_addr)); } if (c->remote_name) { res->remote_name = pstrdup(res->pool, c->remote_name); } register_cleanup(res->pool, (void *) res, conn_cleanup_cb, conn_cleanup_cb); return res; }
END_TEST START_TEST (netaddr_get_sockaddr_test) { pr_netaddr_t *addr; struct sockaddr *sockaddr; const char *name; #ifdef PR_USE_IPV6 int family; #endif /* PR_USE_IPV6 */ sockaddr = pr_netaddr_get_sockaddr(NULL); fail_unless(sockaddr == NULL, "Failed to handle null argument"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); name = "127.0.0.1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); sockaddr = pr_netaddr_get_sockaddr(addr); fail_unless(sockaddr != NULL, "Failed to get sock addr: %s", strerror(errno)); #ifdef PR_USE_IPV6 name = "::1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); sockaddr = pr_netaddr_get_sockaddr(addr); fail_unless(sockaddr != NULL, "Failed to get sock addr: %s", strerror(errno)); pr_netaddr_disable_ipv6(); sockaddr = pr_netaddr_get_sockaddr(addr); fail_unless(sockaddr == NULL, "Got sock addr for IPv6 addr", strerror(errno)); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); pr_netaddr_enable_ipv6(); family = addr->na_family; addr->na_family = 777; sockaddr = pr_netaddr_get_sockaddr(addr); fail_unless(sockaddr == NULL, "Got sock addr for IPv6 addr", strerror(errno)); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); addr->na_family = family; #endif /* PR_USE_IPV6 */ }
void pr_netaddr_set_sess_addrs(void) { pr_netaddr_set_family(&sess_local_addr, pr_netaddr_get_family(session.c->local_addr)); pr_netaddr_set_sockaddr(&sess_local_addr, pr_netaddr_get_sockaddr(session.c->local_addr)); have_sess_local_addr = TRUE; pr_netaddr_set_family(&sess_remote_addr, pr_netaddr_get_family(session.c->remote_addr)); pr_netaddr_set_sockaddr(&sess_remote_addr, pr_netaddr_get_sockaddr(session.c->remote_addr)); memset(sess_remote_name, '\0', sizeof(sess_remote_name)); sstrncpy(sess_remote_name, session.c->remote_name, sizeof(sess_remote_name)); have_sess_remote_addr = TRUE; }
pr_netaddr_t *pr_netaddr_dup(pool *p, pr_netaddr_t *na) { pr_netaddr_t *dup_na; if (!p || !na) { errno = EINVAL; return NULL; } dup_na = pr_netaddr_alloc(p); pr_netaddr_set_family(dup_na, pr_netaddr_get_family(na)); pr_netaddr_set_sockaddr(dup_na, pr_netaddr_get_sockaddr(na)); return dup_na; }
const char *pr_netaddr_get_ipstr(pr_netaddr_t *na) { #ifdef PR_USE_IPV6 char buf[INET6_ADDRSTRLEN]; #else char buf[INET_ADDRSTRLEN]; #endif /* PR_USE_IPV6 */ int res = 0; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an IP string, return the * cached string. */ if (na->na_have_ipstr) return na->na_ipstr; memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST); if (res != 0) { if (res != EAI_SYSTEM) { pr_log_pri(PR_LOG_INFO, "getnameinfo error: %s", pr_gai_strerror(res)); } else { pr_log_pri(PR_LOG_INFO, "getnameinfo system error: [%d] %s", errno, strerror(errno)); } return NULL; } /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_ipstr, '\0', sizeof(na->na_ipstr)); sstrncpy(na->na_ipstr, buf, sizeof(na->na_ipstr)); na->na_have_ipstr = TRUE; return na->na_ipstr; }
pr_netacl_t *pr_netacl_dup(pool *p, const pr_netacl_t *acl) { pr_netacl_t *acl2; if (p == NULL || acl == NULL) { errno = EINVAL; return NULL; } acl2 = pcalloc(p, sizeof(pr_netacl_t)); /* A simple memcpy(3) won't suffice; we need a deep copy. */ acl2->type = acl->type; if (acl->pattern != NULL) { acl2->pattern = pstrdup(p, acl->pattern); } acl2->negated = acl->negated; if (acl->addr != NULL) { pr_netaddr_t *addr; addr = pr_netaddr_alloc(p); pr_netaddr_set_family(addr, pr_netaddr_get_family(acl->addr)); pr_netaddr_set_sockaddr(addr, pr_netaddr_get_sockaddr(acl->addr)); acl2->addr = addr; } acl2->masklen = acl->masklen; if (acl->aclstr != NULL) { acl2->aclstr = pstrdup(p, acl->aclstr); } return acl2; }
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; }
/* This differs from pr_netaddr_get_ipstr() in that pr_netaddr_get_ipstr() * returns a string of the numeric form of the given network address, whereas * this function returns a string of the DNS name (if present). */ const char *pr_netaddr_get_dnsstr(pr_netaddr_t *na) { char *name = NULL; char buf[256]; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an DNS string, return the * cached string. */ if (na->na_have_dnsstr) return na->na_dnsstr; if (reverse_dns) { int res = 0; pr_trace_msg(trace_channel, 3, "verifying DNS name for IP address %s via reverse DNS lookup", pr_netaddr_get_ipstr(na)); memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); buf[sizeof(buf)-1] = '\0'; if (res == 0) { char **checkaddr; struct hostent *hent = NULL; unsigned char ok = FALSE; int family = pr_netaddr_get_family(na); void *inaddr = pr_netaddr_get_inaddr(na); #ifdef HAVE_GETHOSTBYNAME2 if (pr_netaddr_is_v4mappedv6(na) == TRUE) { family = AF_INET; inaddr = get_v4inaddr(na); } hent = gethostbyname2(buf, family); #else hent = gethostbyname(buf); #endif /* HAVE_GETHOSTBYNAME2 */ if (hent != NULL) { char **alias; pr_trace_msg(trace_channel, 10, "checking addresses associated with host '%s'", hent->h_name ? hent->h_name : "(null)"); for (alias = hent->h_aliases; *alias; ++alias) { pr_trace_msg(trace_channel, 10, "host '%s' has alias '%s'", hent->h_name ? hent->h_name : "(null)", *alias); } switch (hent->h_addrtype) { case AF_INET: if (family == AF_INET) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #ifdef PR_USE_IPV6 case AF_INET6: if (use_ipv6 && family == AF_INET6) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #endif /* PR_USE_IPV6 */ } if (ok) { name = buf; pr_trace_msg(trace_channel, 8, "using DNS name '%s' for IP address '%s'", name, pr_netaddr_get_ipstr(na)); } else { name = NULL; pr_trace_msg(trace_channel, 8, "unable to verify any DNS names for IP address '%s'", pr_netaddr_get_ipstr(na)); } } else pr_log_debug(DEBUG1, "notice: unable to resolve '%s': %s", buf, hstrerror(errno)); } } else pr_log_debug(DEBUG10, "UseReverseDNS off, returning IP address instead of DNS name"); if (name) { name = pr_inet_validate(name); } else { name = (char *) pr_netaddr_get_ipstr(na); } /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_dnsstr, '\0', sizeof(na->na_dnsstr)); sstrncpy(na->na_dnsstr, name, sizeof(na->na_dnsstr)); na->na_have_dnsstr = TRUE; return na->na_dnsstr; }
/* This differs from pr_netaddr_get_ipstr() in that pr_netaddr_get_ipstr() * returns a string of the numeric form of the given network address, whereas * this function returns a string of the DNS name (if present). */ const char *pr_netaddr_get_dnsstr(pr_netaddr_t *na) { char *name = NULL; char buf[256]; if (!na) { errno = EINVAL; return NULL; } /* If this pr_netaddr_t has already been resolved to an DNS string, return the * cached string. */ if (na->na_have_dnsstr) return na->na_dnsstr; if (reverse_dns) { int res = 0; memset(buf, '\0', sizeof(buf)); res = pr_getnameinfo(pr_netaddr_get_sockaddr(na), pr_netaddr_get_sockaddr_len(na), buf, sizeof(buf), NULL, 0, NI_NAMEREQD); if (res == 0) { char **checkaddr; struct hostent *hent = NULL; unsigned char ok = FALSE; int family = pr_netaddr_get_family(na); void *inaddr = pr_netaddr_get_inaddr(na); #ifdef HAVE_GETHOSTBYNAME2 if (pr_netaddr_is_v4mappedv6(na) == TRUE) { family = AF_INET; inaddr = get_v4inaddr(na); } hent = gethostbyname2(buf, family); #else hent = gethostbyname(buf); #endif /* HAVE_GETHOSTBYNAME2 */ if (hent != NULL) { switch (hent->h_addrtype) { case AF_INET: if (family == AF_INET) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #ifdef PR_USE_IPV6 case AF_INET6: if (family == AF_INET6) { for (checkaddr = hent->h_addr_list; *checkaddr; ++checkaddr) { if (memcmp(*checkaddr, inaddr, hent->h_length) == 0) { ok = TRUE; break; } } } break; #endif /* PR_USE_IPV6 */ } name = ok ? buf : NULL; } else pr_log_debug(DEBUG1, "notice: unable to resolve '%s': %s", buf, hstrerror(errno)); } } if (!name) name = (char *) pr_netaddr_get_ipstr(na); name = pr_inet_validate(name); /* Copy the string into the pr_netaddr_t cache as well, so we only * have to do this once for this pr_netaddr_t. */ memset(na->na_dnsstr, '\0', sizeof(na->na_dnsstr)); sstrncpy(na->na_dnsstr, name, sizeof(na->na_dnsstr)); na->na_have_dnsstr = TRUE; return na->na_dnsstr; }
static int data_active_open(char *reason, off_t size) { conn_t *c; int bind_port, rev; pr_netaddr_t *bind_addr; unsigned char *root_revoke = NULL; if (!reason && session.xfer.filename) reason = session.xfer.filename; if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) { bind_addr = session.c->local_addr; } else { /* In this scenario, the server has an IPv6 socket, but the remote client * is an IPv4 (or IPv4-mapped IPv6) peer. */ bind_addr = pr_netaddr_v6tov4(session.xfer.p, session.c->local_addr); } /* Default source port to which to bind for the active transfer, as * per RFC959. */ bind_port = session.c->local_port-1; /* A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and * 2 indicates 'NonCompliantActiveTransfer'. We change the source port for * a RootRevoke value of 2. */ root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE); if (root_revoke != NULL && *root_revoke == 2) { bind_port = INPORT_ANY; } session.d = pr_inet_create_conn(session.pool, -1, bind_addr, bind_port, TRUE); /* Default remote address to which to connect for an active transfer, * if the client has not specified a different address via PORT/EPRT, * as per RFC 959. */ if (pr_netaddr_get_family(&session.data_addr) == AF_UNSPEC) { pr_log_debug(DEBUG6, "Client has not sent previous PORT/EPRT command, " "defaulting to %s#%u for active transfer", pr_netaddr_get_ipstr(session.c->remote_addr), session.c->remote_port); pr_netaddr_set_family(&session.data_addr, pr_netaddr_get_family(session.c->remote_addr)); pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(session.c->remote_addr)); } /* Set the "stalled" timer, if any, to prevent the connection * open from taking too long */ if (timeout_stalled) { pr_timer_add(timeout_stalled, PR_TIMER_STALLED, NULL, stalled_timeout_cb, "TimeoutStalled"); } rev = pr_netaddr_set_reverse_dns(ServerUseReverseDNS); /* Protocol and socket options should be set before handshaking. */ if (session.xfer.direction == PR_NETIO_IO_RD) { pr_inet_set_socket_opts(session.d->pool, session.d, (main_server->tcp_rcvbuf_override ? main_server->tcp_rcvbuf_len : 0), 0, main_server->tcp_keepalive); } else { pr_inet_set_socket_opts(session.d->pool, session.d, 0, (main_server->tcp_sndbuf_override ? main_server->tcp_sndbuf_len : 0), main_server->tcp_keepalive); } /* Make sure that the necessary socket options are set on the socket prior * to the call to connect(2). */ pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len, 0, IPTOS_THROUGHPUT, 1); pr_inet_generate_socket_event("core.data-connect", main_server, session.d->local_addr, session.d->listen_fd); if (pr_inet_connect(session.d->pool, session.d, &session.data_addr, session.data_port) == -1) { pr_log_debug(DEBUG6, "Error connecting to %s#%u for active data transfer: %s", pr_netaddr_get_ipstr(&session.data_addr), session.data_port, strerror(session.d->xerrno)); pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; } c = pr_inet_openrw(session.pool, session.d, NULL, PR_NETIO_STRM_DATA, session.d->listen_fd, -1, -1, TRUE); pr_netaddr_set_reverse_dns(rev); if (c) { pr_log_debug(DEBUG4, "active data connection opened - local : %s:%d", pr_netaddr_get_ipstr(session.d->local_addr), session.d->local_port); pr_log_debug(DEBUG4, "active data connection opened - remote : %s:%d", pr_netaddr_get_ipstr(session.d->remote_addr), session.d->remote_port); if (session.xfer.xfer_type != STOR_UNIQUE) { if (size) { pr_response_send(R_150, _("Opening %s mode data connection for %s " "(%" PR_LU " bytes)"), MODE_STRING, reason, (pr_off_t) size); } else { pr_response_send(R_150, _("Opening %s mode data connection for %s"), MODE_STRING, reason); } } else { /* Format of 150 responses for STOU is explicitly dictated by * RFC 1123: * * 4.1.2.9 STOU Command: RFC-959 Section 4.1.3 * * The STOU command stores into a uniquely named file. When it * receives an STOU command, a Server-FTP MUST return the * actual file name in the "125 Transfer Starting" or the "150 * Opening Data Connection" message that precedes the transfer * (the 250 reply code mentioned in RFC-959 is incorrect). The * exact format of these messages is hereby defined to be as * follows: * * 125 FILE: pppp * 150 FILE: pppp * * where pppp represents the unique pathname of the file that * will be written. */ pr_response_send(R_150, "FILE: %s", reason); } pr_inet_close(session.pool, session.d); pr_inet_set_nonblock(session.pool, session.d); session.d = c; return 0; } pr_response_add_err(R_425, _("Unable to build data connection: %s"), strerror(session.d->xerrno)); errno = session.d->xerrno; destroy_pool(session.d->pool); session.d = NULL; return -1; }