int pr_netaddr_is_loopback(const pr_netaddr_t *na) { if (!na) { errno = EINVAL; return -1; } switch (pr_netaddr_get_family(na)) { case AF_INET: return IN_IS_ADDR_LOOPBACK( (struct in_addr *) pr_netaddr_get_inaddr(na)); #ifdef PR_USE_IPV6 case AF_INET6: /* XXX *sigh* Different platforms implement the IN6_IS_ADDR macros * differently. For example, on Linux, those macros expect to operate * on s6_addr32, while on Solaris, the macros operate on struct in6_addr. * Certain Drafts define the macros to work on struct in6_addr *, as * Solaris does, so Linux may have it wrong. Tentative research on * Google shows some BSD netinet6/in6.h headers that define these * macros in terms of struct in6_addr *, so I'll go with that for now. * Joy. =P */ # ifndef LINUX return IN6_IS_ADDR_LOOPBACK( (struct in6_addr *) pr_netaddr_get_inaddr(na)); # else return IN6_IS_ADDR_LOOPBACK( ((struct in6_addr *) pr_netaddr_get_inaddr(na))->s6_addr32); # endif #endif /* PR_USE_IPV6 */ } return FALSE; }
int pr_netaddr_is_v4mappedv6(const pr_netaddr_t *na) { if (!na) { errno = EINVAL; return -1; } switch (pr_netaddr_get_family(na)) { case AF_INET: /* This function tests only IPv6 addresses, not IPv4 addresses. */ errno = EINVAL; return -1; #ifdef PR_USE_IPV6 case AF_INET6: { if (!use_ipv6) { errno = EINVAL; return -1; } # ifndef LINUX return IN6_IS_ADDR_V4MAPPED( (struct in6_addr *) pr_netaddr_get_inaddr(na)); # else return IN6_IS_ADDR_V4MAPPED( ((struct in6_addr *) pr_netaddr_get_inaddr(na))->s6_addr32); # endif } #endif /* PR_USE_IPV6 */ } errno = EPERM; return -1; }
END_TEST START_TEST (netaddr_get_inaddr_test) { pr_netaddr_t *addr; int family; void *inaddr; const char *name; inaddr = pr_netaddr_get_inaddr(NULL); fail_unless(inaddr == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); family = AF_INET; 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)); inaddr = pr_netaddr_get_inaddr(addr); fail_unless(inaddr != NULL, "Failed to get inaddr: %s", strerror(errno)); #ifdef PR_USE_IPV6 family = AF_INET6; name = "::1"; addr = pr_netaddr_get_addr(p, name, NULL); fail_unless(addr != NULL, "Failed to resolve '%s': %s", name, strerror(errno)); inaddr = pr_netaddr_get_inaddr(addr); fail_unless(inaddr != NULL, "Failed to get inaddr: %s", strerror(errno)); pr_netaddr_disable_ipv6(); inaddr = pr_netaddr_get_inaddr(addr); fail_unless(inaddr == NULL, "Got inaddr unexpectedly"); 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; inaddr = pr_netaddr_get_inaddr(addr); addr->na_family = family; fail_unless(inaddr == NULL, "Got inaddr unexpectedly"); fail_unless(errno == EPERM, "Expected EPERM (%d), got %s (%d)", EPERM, strerror(errno), errno); #endif /* PR_USE_IPV6 */ }
/* A slightly naughty function that should go away. It relies too much on * knowledge of the internal structures of struct in_addr, struct in6_addr. */ unsigned int pr_netaddr_get_addrno(const pr_netaddr_t *na) { if (!na) { errno = EINVAL; return -1; } switch (pr_netaddr_get_family(na)) { case AF_INET: return na->na_addr.v4.sin_addr.s_addr; #ifdef PR_USE_IPV6 case AF_INET6: { /* Linux defines s6_addr32 in its netinet/in.h header. * FreeBSD defines s6_addr32 in KAME's netinet6/in6.h header. * Solaris defines s6_addr32 in its netinet/in.h header, but only * for kernel builds. */ #if 0 int *addrs = ((struct sockaddr_in6 *) pr_netaddr_get_inaddr(na))->s6_addr32; return addrs[0]; #else return 0; #endif } #endif /* PR_USE_IPV6 */ } errno = EPERM; return -1; }
static void *get_v4inaddr(const pr_netaddr_t *na) { /* This function is specifically for IPv4 clients (when gethostbyname2(2) is * present) that have an IPv4-mapped IPv6 address, when performing reverse * DNS checks. This function is called iff the given netaddr object is * indeed an IPv4-mapped IPv6 address. IPv6 address have 128 bits in their * sin6_addr field. For IPv4-mapped IPv6 addresses, the relevant 32 bits * are the last of those 128 bits (or, alternatively, the last 4 bytes of * those 16 bytes); hence the read of 12 bytes after the start of the * sin6_addr pointer. */ return (((char *) pr_netaddr_get_inaddr(na)) + 12); }
/* The hashing function for the hash table of bindings. This algorithm * is stolen from Apache's http_vhost.c */ static unsigned int ipbind_hash_addr(pr_netaddr_t *addr) { size_t offset; unsigned int key; offset = pr_netaddr_get_inaddr_len(addr); /* The key is the last four bytes of the IP address. * For IPv4, this is the entire address, as always. * For IPv6, this is usually part of the MAC address. */ key = *(unsigned *) ((char *) pr_netaddr_get_inaddr(addr) + offset - 4); key ^= (key >> 16); return ((key >> 8) ^ key) % PR_BINDINGS_TABLE_SIZE; }
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; }
int log_wtmp(char *line, const char *name, const char *host, pr_netaddr_t *ip) { struct stat buf; struct utmp ut; int res = 0; static int fd = -1; #if ((defined(SVR4) || defined(__SVR4)) || \ (defined(__NetBSD__) && defined(HAVE_UTMPX_H))) && \ !(defined(LINUX) || defined(__hpux) || defined (_AIX)) /* This "auxilliary" utmp doesn't exist under linux. */ #ifdef __sparcv9 struct futmpx utx; time_t t; #else struct utmpx utx; #endif static int fdx = -1; #if !defined(WTMPX_FILE) && defined(_PATH_WTMPX) # define WTMPX_FILE _PATH_WTMPX #endif if (fdx < 0 && (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { pr_log_pri(PR_LOG_WARNING, "wtmpx %s: %s", WTMPX_FILE, strerror(errno)); return -1; } /* Unfortunately, utmp string fields are terminated by '\0' if they are * shorter than the size of the field, but if they are exactly the size of * the field they don't have to be terminated at all. Frankly, this sucks. * Insane if you ask me. Unless there's massive uproar, I prefer to err on * the side of caution and always null-terminate our strings. */ if (fstat(fdx, &buf) == 0) { memset(&utx, 0, sizeof(utx)); sstrncpy(utx.ut_user, name, sizeof(utx.ut_user)); sstrncpy(utx.ut_id, "ftp", sizeof(utx.ut_user)); sstrncpy(utx.ut_line, line, sizeof(utx.ut_line)); sstrncpy(utx.ut_host, host, sizeof(utx.ut_host)); utx.ut_pid = getpid(); #if defined(__NetBSD__) && defined(HAVE_UTMPX_H) memcpy(&utx.ut_ss, pr_netaddr_get_inaddr(ip), sizeof(utx.ut_ss)); gettimeofday(&utx.ut_tv, NULL); #else /* SVR4 */ utx.ut_syslen = strlen(utx.ut_host)+1; # ifdef __sparcv9 time(&t); utx.ut_tv.tv_sec = (time32_t)t; # else time(&utx.ut_tv.tv_sec); # endif #endif /* SVR4 */ if (*name) utx.ut_type = USER_PROCESS; else utx.ut_type = DEAD_PROCESS; #ifdef HAVE_UT_UT_EXIT utx.ut_exit.e_termination = 0; utx.ut_exit.e_exit = 0; #endif /* HAVE_UT_UT_EXIT */ if (write(fdx, (char *)&utx, sizeof(utx)) != sizeof(utx)) ftruncate(fdx, buf.st_size); } else { pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno)); res = -1; } #else /* Non-SVR4 systems */ if (fd < 0 && (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { pr_log_pri(PR_LOG_WARNING, "wtmp %s: %s", WTMP_FILE, strerror(errno)); return -1; } if (fstat(fd, &buf) == 0) { memset(&ut, 0, sizeof(ut)); #ifdef HAVE_UTMAXTYPE # ifdef LINUX if (ip) # ifndef PR_USE_IPV6 memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr)); # else memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6)); # endif /* !PR_USE_IPV6 */ # else sstrncpy(ut.ut_id, "ftp", sizeof(ut.ut_id)); # ifdef HAVE_UT_UT_EXIT ut.ut_exit.e_termination = 0; ut.ut_exit.e_exit = 0; # endif /* !HAVE_UT_UT_EXIT */ # endif /* !LINUX */ sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); if (name && *name) sstrncpy(ut.ut_user, name, sizeof(ut.ut_user)); ut.ut_pid = getpid(); if (name && *name) ut.ut_type = USER_PROCESS; else ut.ut_type = DEAD_PROCESS; #else /* !HAVE_UTMAXTYPE */ sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); if (name && *name) sstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); #endif /* HAVE_UTMAXTYPE */ #ifdef HAVE_UT_UT_HOST if (host && *host) sstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); #endif /* HAVE_UT_UT_HOST */ time(&ut.ut_time); if (write(fd, (char *)&ut, sizeof(ut)) != sizeof(ut)) ftruncate(fd, buf.st_size); } else { pr_log_debug(DEBUG0, "%s fstat(): %s",WTMP_FILE,strerror(errno)); res = -1; } #endif /* SVR4 */ return res; }
int pr_netaddr_ncmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2, unsigned int bitlen) { pool *tmp_pool = NULL; pr_netaddr_t *a, *b; unsigned int nbytes, nbits; const unsigned char *in1, *in2; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families, unless one * of the netaddrs has an AF_INET family, and the other has an * AF_INET6 family AND is an IPv4-mapped IPv6 address. */ if (pr_netaddr_is_v4mappedv6(na1) != TRUE && pr_netaddr_is_v4mappedv6(na2) != TRUE) { errno = EINVAL; return -1; } if (pr_netaddr_is_v4mappedv6(na1) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na1 is an IPv4-mapped IPv6 address, and * na2 is an IPv4 address. */ a = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(a, AF_INET); pr_netaddr_set_port(a, pr_netaddr_get_port(na1)); memcpy(&a->na_addr.v4.sin_addr, get_v4inaddr(na1), sizeof(struct in_addr)); b = (pr_netaddr_t *) na2; pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(b), pr_netaddr_get_ipstr(a)); } else if (pr_netaddr_is_v4mappedv6(na2) == TRUE) { tmp_pool = make_sub_pool(permanent_pool); /* This case means that na is an IPv4 address, and na2 is an * IPv4-mapped IPv6 address. */ a = (pr_netaddr_t *) na1; b = pr_netaddr_alloc(tmp_pool); pr_netaddr_set_family(b, AF_INET); pr_netaddr_set_port(b, pr_netaddr_get_port(na2)); memcpy(&b->na_addr.v4.sin_addr, get_v4inaddr(na2), sizeof(struct in_addr)); pr_trace_msg(trace_channel, 6, "comparing IPv4 address '%s' against " "IPv4-mapped IPv6 address '%s'", pr_netaddr_get_ipstr(a), pr_netaddr_get_ipstr(b)); } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } } else { a = (pr_netaddr_t *) na1; b = (pr_netaddr_t *) na2; } switch (pr_netaddr_get_family(a)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (bitlen > 32) { errno = EINVAL; return -1; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { if (use_ipv6) { /* Make sure that the given number of bits is not more than supported * for IPv6 addresses (128). */ if (bitlen > 128) { errno = EINVAL; return -1; } break; } } #endif /* PR_USE_IPV6 */ default: errno = EPERM; return -1; } /* Retrieve pointers to the contained in_addrs. */ in1 = (const unsigned char *) pr_netaddr_get_inaddr(a); in2 = (const unsigned char *) pr_netaddr_get_inaddr(b); /* Determine the number of bytes, and leftover bits, in the given * bit length. */ nbytes = bitlen / 8; nbits = bitlen % 8; /* Compare bytes, using memcmp(3), first. */ if (nbytes > 0) { int res = memcmp(in1, in2, nbytes); /* No need to continue comparing the addresses if they differ already. */ if (res != 0) { if (tmp_pool) destroy_pool(tmp_pool); return res; } } /* Next, compare the remaining bits in the addresses. */ if (nbits > 0) { unsigned char mask; /* Get the bytes in the addresses that have not yet been compared. */ unsigned char in1byte = in1[nbytes]; unsigned char in2byte = in2[nbytes]; /* Build up a mask covering the bits left to be checked. */ mask = (0xff << (8 - nbits)) & 0xff; if ((in1byte & mask) > (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return 1; } if ((in1byte & mask) < (in2byte & mask)) { if (tmp_pool) destroy_pool(tmp_pool); return -1; } } if (tmp_pool) destroy_pool(tmp_pool); /* If we've made it this far, the addresses match, for the given bit * length. */ return 0; }
/* 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; }
int pr_netaddr_ncmp(const pr_netaddr_t *na1, const pr_netaddr_t *na2, unsigned int bitlen) { unsigned int nbytes, nbits; const unsigned char *in1, *in2; if (na1 && !na2) return 1; if (!na1 && na2) return -1; if (!na1 && !na2) return 0; if (pr_netaddr_get_family(na1) != pr_netaddr_get_family(na2)) { /* Cannot compare addresses from different families. */ errno = EINVAL; return -1; } switch (pr_netaddr_get_family(na1)) { case AF_INET: { /* Make sure that the given number of bits is not more than supported * for IPv4 addresses (32). */ if (bitlen > 32) { errno = EINVAL; return -1; } break; } #ifdef PR_USE_IPV6 case AF_INET6: { /* Make sure that the given number of bits is not more than supported * for IPv6 addresses (128). */ if (bitlen > 128) { errno = EINVAL; return -1; } break; } #endif /* PR_USE_IPV6 */ default: errno = EPERM; return -1; } /* Retrieve pointers to the contained in_addrs. */ in1 = (const unsigned char *) pr_netaddr_get_inaddr(na1); in2 = (const unsigned char *) pr_netaddr_get_inaddr(na2); /* Determine the number of bytes, and leftover bits, in the given * bit length. */ nbytes = bitlen / 8; nbits = bitlen % 8; /* Compare bytes, using memcmp(3), first. */ if (nbytes > 0) { int res = memcmp(in1, in2, nbytes); /* No need to continue comparing the addresses if they differ already. */ if (res != 0) return res; } /* Next, compare the remaining bits in the addresses. */ if (nbits > 0) { unsigned char mask; /* Get the bytes in the addresses that have not yet been compared. */ unsigned char in1byte = in1[nbytes]; unsigned char in2byte = in2[nbytes]; /* Build up a mask covering the bits left to be checked. */ mask = (0xff << (8 - nbits)) & 0xff; if ((in1byte & mask) > (in2byte & mask)) return 1; if ((in1byte & mask) < (in2byte & mask)) return -1; } /* If we've made it this far, the addresses match, for the given bit * length. */ return 0; }
int log_wtmp(const char *line, const char *name, const char *host, pr_netaddr_t *ip) { struct stat buf; int res = 0; #if ((defined(SVR4) || defined(__SVR4)) || \ (defined(__NetBSD__) && defined(HAVE_UTMPX_H)) || \ (defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H))) && \ !(defined(LINUX) || defined(__hpux) || defined (_AIX)) /* This "auxilliary" utmp doesn't exist under linux. */ #if defined(__sparcv9) && !defined(__NetBSD__) && !defined(__FreeBSD__) struct futmpx utx; time_t t; #else struct utmpx utx; #endif static int fdx = -1; #if !defined(WTMPX_FILE) # if defined(_PATH_WTMPX) # define WTMPX_FILE _PATH_WTMPX # elif defined(_PATH_UTMPX) # define WTMPX_FILE _PATH_UTMPX # else /* This path works for FreeBSD; not sure what to do for other platforms which * don't define _PATH_WTMPX or _PATH_UTMPX. */ # define WTMPX_FILE "/var/log/utx.log" # endif #endif if (fdx < 0 && (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { int xerrno = errno; pr_log_pri(PR_LOG_WARNING, "failed to open wtmpx %s: %s", WTMPX_FILE, strerror(xerrno)); errno = xerrno; return -1; } (void) pr_fs_get_usable_fd2(&fdx); /* Unfortunately, utmp string fields are terminated by '\0' if they are * shorter than the size of the field, but if they are exactly the size of * the field they don't have to be terminated at all. Frankly, this sucks. * Insane if you ask me. Unless there's massive uproar, I prefer to err on * the side of caution and always null-terminate our strings. */ if (fstat(fdx, &buf) == 0) { memset(&utx, 0, sizeof(utx)); sstrncpy(utx.ut_user, name, sizeof(utx.ut_user)); sstrncpy(utx.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT), sizeof(utx.ut_user)); sstrncpy(utx.ut_line, line, sizeof(utx.ut_line)); sstrncpy(utx.ut_host, host, sizeof(utx.ut_host)); utx.ut_pid = session.pid ? session.pid : getpid(); #if defined(__NetBSD__) && defined(HAVE_UTMPX_H) memcpy(&utx.ut_ss, pr_netaddr_get_inaddr(ip), sizeof(utx.ut_ss)); gettimeofday(&utx.ut_tv, NULL); #elif defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H) gettimeofday(&utx.ut_tv, NULL); #else /* SVR4 */ utx.ut_syslen = strlen(utx.ut_host)+1; # if defined(__sparcv9) && !defined(__FreeBSD__) time(&t); utx.ut_tv.tv_sec = (time32_t)t; # else time(&utx.ut_tv.tv_sec); # endif #endif /* SVR4 */ if (*name) utx.ut_type = USER_PROCESS; else utx.ut_type = DEAD_PROCESS; #ifdef HAVE_UT_UT_EXIT utx.ut_exit.e_termination = 0; utx.ut_exit.e_exit = 0; #endif /* HAVE_UT_UT_EXIT */ if (write(fdx, (char *) &utx, sizeof(utx)) != sizeof(utx)) { (void) ftruncate(fdx, buf.st_size); } } else { pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno)); res = -1; } #else /* Non-SVR4 systems */ struct utmp ut; static int fd = -1; if (fd < 0 && (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { int xerrno = errno; pr_log_pri(PR_LOG_WARNING, "failed to open wtmp %s: %s", WTMP_FILE, strerror(xerrno)); errno = xerrno; return -1; } (void) pr_fs_get_usable_fd2(&fd); if (fstat(fd, &buf) == 0) { memset(&ut, 0, sizeof(ut)); #ifdef HAVE_UTMAXTYPE # ifdef LINUX if (ip) # ifndef PR_USE_IPV6 memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr)); # else memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6)); # endif /* !PR_USE_IPV6 */ # else sstrncpy(ut.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT), sizeof(ut.ut_id)); # ifdef HAVE_UT_UT_EXIT ut.ut_exit.e_termination = 0; ut.ut_exit.e_exit = 0; # endif /* !HAVE_UT_UT_EXIT */ # endif /* !LINUX */ sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); if (name && *name) sstrncpy(ut.ut_user, name, sizeof(ut.ut_user)); ut.ut_pid = session.pid ? session.pid : getpid(); if (name && *name) ut.ut_type = USER_PROCESS; else ut.ut_type = DEAD_PROCESS; #else /* !HAVE_UTMAXTYPE */ sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); if (name && *name) { sstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); } #endif /* HAVE_UTMAXTYPE */ #ifdef HAVE_UT_UT_HOST if (host && *host) { sstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); } #endif /* HAVE_UT_UT_HOST */ ut.ut_time = time(NULL); if (write(fd, (char *) &ut, sizeof(ut)) != sizeof(ut)) { if (ftruncate(fd, buf.st_size) < 0) { pr_log_debug(DEBUG0, "error truncating '%s': %s", WTMP_FILE, strerror(errno)); } } } else { pr_log_debug(DEBUG0, "%s fstat(): %s", WTMP_FILE, strerror(errno)); res = -1; } #endif /* SVR4 */ return res; }