/*++ /* NAME /* normalize_mailhost_addr 3 /* SUMMARY /* normalize mailhost address string representation /* SYNOPSIS /* #include <normalize_mailhost_addr.h> /* /* int normalize_mailhost_addr( /* const char *string, /* char **mailhost_addr, /* char **bare_addr, /* int *addr_family) /* DESCRIPTION /* normalize_mailhost_addr() takes the RFC 2821 string /* representation of an IPv4 or IPv6 network address, and /* normalizes the "IPv6:" prefix and numeric form. An IPv6 or /* IPv4 form is rejected if supposed for that protocol is /* disabled or non-existent. If both IPv6 and IPv4 support are /* enabled, a V4-in-V6 address is replaced with the IPv4 form. /* /* Arguments: /* .IP string /* Null-terminated string with the RFC 2821 string representation /* of an IPv4 or IPv6 network address. /* .IP mailhost_addr /* Null pointer, or pointer to null-terminated string with the /* normalized RFC 2821 string representation of an IPv4 or /* IPv6 network address. Storage must be freed with myfree(). /* .IP bare_addr /* Null pointer, or pointer to null-terminated string with the /* numeric address without prefix, such as "IPv6:". Storage /* must be freed with myfree(). /* .IP addr_family /* Null pointer, or pointer to integer for storing the address /* family. /* DIAGNOSTICS /* normalize_mailhost_addr() returns -1 if the input is malformed, /* zero otherwise. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* * System library. */ #include <sys_defs.h> #include <string.h> #ifdef STRCASECMP_IN_STRINGS_H #include <strings.h> #endif /* * Utility library. */ #include <inet_proto.h> #include <msg.h> #include <myaddrinfo.h> #include <mymalloc.h> #include <stringops.h> /* * Global library. */ #include <normalize_mailhost_addr.h> #include <valid_mailhost_addr.h> /* normalize_mailhost_addr - parse and normalize mailhost IP address */ int normalize_mailhost_addr(const char *string, char **mailhost_addr, char **bare_addr, int *addr_family) { const char myname[] = "normalize_mailhost_addr"; const INET_PROTO_INFO *proto_info = inet_proto_info(); struct addrinfo *res = 0; MAI_HOSTADDR_STR hostaddr; const char *valid_addr; /* IPv6:fc00::1 */ const char *normal_addr; /* 192.168.0.1 */ int normal_family; #define UPDATE_BARE_ADDR(s, v) do { \ if (s) myfree(s); \ (s) = mystrdup(v); \ } while(0) #define UPDATE_MAILHOST_ADDR(s, prefix, addr) do { \ if (s) myfree(s); \ (s) = concatenate((prefix), (addr), (char *) 0); \ } while (0) /* * Parse and normalize the input. */ if ((valid_addr = valid_mailhost_addr(string, DONT_GRIPE)) == 0 || hostaddr_to_sockaddr(valid_addr, (char *) 0, 0, &res) != 0 || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen, &hostaddr, (MAI_SERVPORT_STR *) 0, 0) != 0) { normal_addr = 0; #ifdef HAS_IPV6 } else if (res->ai_family == AF_INET6 && strncasecmp("::ffff:", hostaddr.buf, 7) == 0 && strchr((char *) proto_info->sa_family_list, AF_INET)) { normal_addr = hostaddr.buf + 7; normal_family = AF_INET; #endif } else if (strchr((char *) proto_info->sa_family_list, res->ai_family)) { normal_addr = hostaddr.buf; normal_family = res->ai_family; } else { normal_addr = 0; } if (res) freeaddrinfo(res); if (normal_addr == 0) return (-1); /* * Write the applicable outputs. */ if (bare_addr) { UPDATE_BARE_ADDR(*bare_addr, normal_addr); if (msg_verbose) msg_info("%s: bare_addr=%s", myname, *bare_addr); } if (mailhost_addr) { #ifdef HAS_IPV6 if (normal_family == AF_INET6) UPDATE_MAILHOST_ADDR(*mailhost_addr, IPV6_COL, normal_addr); else #endif UPDATE_BARE_ADDR(*mailhost_addr, normal_addr); if (msg_verbose) msg_info("%s: mailhost_addr=%s", myname, *mailhost_addr); } if (addr_family) { *addr_family = normal_family; if (msg_verbose) msg_info("%s: addr_family=%s", myname, *addr_family == AF_INET6 ? "AF_INET6" : *addr_family == AF_INET ? "AF_INET" : "unknown"); } return (0); } /* * Test program. */ #ifdef TEST #include <stdlib.h> #include <mymalloc.h> #include <msg.h> /* * Main test program. */ int main(int argc, char **argv) { /* Test cases with inputs and expected outputs. */ typedef struct TEST_CASE { const char *inet_protocols; const char *mailhost_addr; int exp_return; const char *exp_mailhost_addr; char *exp_bare_addr; int exp_addr_family; } TEST_CASE; static TEST_CASE test_cases[] = { /* IPv4 in IPv6. */ {"ipv4, ipv6", "ipv6:::ffff:1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, {"ipv6", "ipv6:::ffff:1.2.3.4", 0, "IPv6:::ffff:1.2.3.4", "::ffff:1.2.3.4", AF_INET6}, /* Pass IPv4 or IPV6. */ {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", AF_INET6}, {"ipv4, ipv6", "1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, /* Normalize IPv4 or IPV6. */ {"ipv4, ipv6", "ipv6:fc00::0", 0, "IPv6:fc00::", "fc00::", AF_INET6}, {"ipv4, ipv6", "01.02.03.04", 0, "1.2.3.4", "1.2.3.4", AF_INET}, /* Suppress specific outputs. */ {"ipv4, ipv6", "ipv6:fc00::1", 0, 0, "fc00::1", AF_INET6}, {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", 0, AF_INET6}, {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", -1}, /* Address type mismatch. */ {"ipv4, ipv6", "::ffff:1.2.3.4", -1}, {"ipv4", "ipv6:fc00::1", -1}, {"ipv6", "1.2.3.4", -1}, 0, }; TEST_CASE *test_case; /* Actual results. */ int act_return; char *act_mailhost_addr = mystrdup("initial_mailhost_addr"); char *act_bare_addr = mystrdup("initial_bare_addr"); int act_addr_family = 0xdeadbeef; /* Findings. */ int tests_failed = 0; int test_failed; for (tests_failed = 0, test_case = test_cases; test_case->inet_protocols; tests_failed += test_failed, test_case++) { test_failed = 0; inet_proto_init(argv[0], test_case->inet_protocols); act_return = normalize_mailhost_addr(test_case->mailhost_addr, test_case->exp_mailhost_addr ? &act_mailhost_addr : (char **) 0, test_case->exp_bare_addr ? &act_bare_addr : (char **) 0, test_case->exp_addr_family >= 0 ? &act_addr_family : (int *) 0); if (act_return != test_case->exp_return) { msg_warn("test case %d return expected=%d actual=%d", (int) (test_case - test_cases), test_case->exp_return, act_return); test_failed = 1; continue; } if (test_case->exp_return != 0) continue; if (test_case->exp_mailhost_addr && strcmp(test_case->exp_mailhost_addr, act_mailhost_addr)) { msg_warn("test case %d mailhost_addr expected=%s actual=%s", (int) (test_case - test_cases), test_case->exp_mailhost_addr, act_mailhost_addr); test_failed = 1; } if (test_case->exp_bare_addr && strcmp(test_case->exp_bare_addr, act_bare_addr)) { msg_warn("test case %d bare_addr expected=%s actual=%s", (int) (test_case - test_cases), test_case->exp_bare_addr, act_bare_addr); test_failed = 1; } if (test_case->exp_addr_family >= 0 && test_case->exp_addr_family != act_addr_family) { msg_warn("test case %d addr_family expected=0x%x actual=0x%x", (int) (test_case - test_cases), test_case->exp_addr_family, act_addr_family); test_failed = 1; } } if (act_mailhost_addr) myfree(act_mailhost_addr); if (act_bare_addr) myfree(act_bare_addr); if (tests_failed) msg_info("tests failed: %d", tests_failed); exit(tests_failed != 0); } #endif