1 /* $NetBSD: normalize_mailhost_addr.c,v 1.3 2022/10/08 16:12:45 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* normalize_mailhost_addr 3 6 /* SUMMARY 7 /* normalize mailhost address string representation 8 /* SYNOPSIS 9 /* #include <normalize_mailhost_addr.h> 10 /* 11 /* int normalize_mailhost_addr( 12 /* const char *string, 13 /* char **mailhost_addr, 14 /* char **bare_addr, 15 /* int *addr_family) 16 /* DESCRIPTION 17 /* normalize_mailhost_addr() takes the RFC 2821 string 18 /* representation of an IPv4 or IPv6 network address, and 19 /* normalizes the "IPv6:" prefix and numeric form. An IPv6 or 20 /* IPv4 form is rejected if supposed for that protocol is 21 /* disabled or non-existent. If both IPv6 and IPv4 support are 22 /* enabled, a V4-in-V6 address is replaced with the IPv4 form. 23 /* 24 /* Arguments: 25 /* .IP string 26 /* Null-terminated string with the RFC 2821 string representation 27 /* of an IPv4 or IPv6 network address. 28 /* .IP mailhost_addr 29 /* Null pointer, or pointer to null-terminated string with the 30 /* normalized RFC 2821 string representation of an IPv4 or 31 /* IPv6 network address. Storage must be freed with myfree(). 32 /* .IP bare_addr 33 /* Null pointer, or pointer to null-terminated string with the 34 /* numeric address without prefix, such as "IPv6:". Storage 35 /* must be freed with myfree(). 36 /* .IP addr_family 37 /* Null pointer, or pointer to integer for storing the address 38 /* family. 39 /* DIAGNOSTICS 40 /* normalize_mailhost_addr() returns -1 if the input is malformed, 41 /* zero otherwise. 42 /* LICENSE 43 /* .ad 44 /* .fi 45 /* The Secure Mailer license must be distributed with this software. 46 /* AUTHOR(S) 47 /* Wietse Venema 48 /* Google, Inc. 49 /* 111 8th Avenue 50 /* New York, NY 10011, USA 51 /*--*/ 52 53 /* 54 * System library. 55 */ 56 #include <sys_defs.h> 57 #include <string.h> 58 59 #ifdef STRCASECMP_IN_STRINGS_H 60 #include <strings.h> 61 #endif 62 63 /* 64 * Utility library. 65 */ 66 #include <inet_proto.h> 67 #include <msg.h> 68 #include <myaddrinfo.h> 69 #include <mymalloc.h> 70 #include <stringops.h> 71 72 /* 73 * Global library. 74 */ 75 #include <normalize_mailhost_addr.h> 76 #include <valid_mailhost_addr.h> 77 78 /* normalize_mailhost_addr - parse and normalize mailhost IP address */ 79 80 int normalize_mailhost_addr(const char *string, char **mailhost_addr, 81 char **bare_addr, int *addr_family) 82 { 83 const char myname[] = "normalize_mailhost_addr"; 84 const INET_PROTO_INFO *proto_info = inet_proto_info(); 85 struct addrinfo *res = 0; 86 MAI_HOSTADDR_STR hostaddr; 87 const char *valid_addr; /* IPv6:fc00::1 */ 88 const char *normal_addr; /* 192.168.0.1 */ 89 int normal_family; 90 91 #define UPDATE_BARE_ADDR(s, v) do { \ 92 if (s) myfree(s); \ 93 (s) = mystrdup(v); \ 94 } while(0) 95 #define UPDATE_MAILHOST_ADDR(s, prefix, addr) do { \ 96 if (s) myfree(s); \ 97 (s) = concatenate((prefix), (addr), (char *) 0); \ 98 } while (0) 99 100 /* 101 * Parse and normalize the input. 102 */ 103 if ((valid_addr = valid_mailhost_addr(string, DONT_GRIPE)) == 0 104 || hostaddr_to_sockaddr(valid_addr, (char *) 0, 0, &res) != 0 105 || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen, 106 &hostaddr, (MAI_SERVPORT_STR *) 0, 0) != 0) { 107 normal_addr = 0; 108 #ifdef HAS_IPV6 109 } else if (res->ai_family == AF_INET6 110 && strncasecmp("::ffff:", hostaddr.buf, 7) == 0 111 && strchr((char *) proto_info->sa_family_list, AF_INET)) { 112 normal_addr = hostaddr.buf + 7; 113 normal_family = AF_INET; 114 #endif 115 } else if (strchr((char *) proto_info->sa_family_list, res->ai_family)) { 116 normal_addr = hostaddr.buf; 117 normal_family = res->ai_family; 118 } else { 119 normal_addr = 0; 120 } 121 if (res) 122 freeaddrinfo(res); 123 if (normal_addr == 0) 124 return (-1); 125 126 /* 127 * Write the applicable outputs. 128 */ 129 if (bare_addr) { 130 UPDATE_BARE_ADDR(*bare_addr, normal_addr); 131 if (msg_verbose) 132 msg_info("%s: bare_addr=%s", myname, *bare_addr); 133 } 134 if (mailhost_addr) { 135 #ifdef HAS_IPV6 136 if (normal_family == AF_INET6) 137 UPDATE_MAILHOST_ADDR(*mailhost_addr, IPV6_COL, normal_addr); 138 else 139 #endif 140 UPDATE_BARE_ADDR(*mailhost_addr, normal_addr); 141 if (msg_verbose) 142 msg_info("%s: mailhost_addr=%s", myname, *mailhost_addr); 143 } 144 if (addr_family) { 145 *addr_family = normal_family; 146 if (msg_verbose) 147 msg_info("%s: addr_family=%s", myname, 148 *addr_family == AF_INET6 ? "AF_INET6" 149 : *addr_family == AF_INET ? "AF_INET" 150 : "unknown"); 151 } 152 return (0); 153 } 154 155 /* 156 * Test program. 157 */ 158 #ifdef TEST 159 #include <stdlib.h> 160 #include <mymalloc.h> 161 #include <msg.h> 162 163 /* 164 * Main test program. 165 */ 166 int main(int argc, char **argv) 167 { 168 /* Test cases with inputs and expected outputs. */ 169 typedef struct TEST_CASE { 170 const char *inet_protocols; 171 const char *mailhost_addr; 172 int exp_return; 173 const char *exp_mailhost_addr; 174 char *exp_bare_addr; 175 int exp_addr_family; 176 } TEST_CASE; 177 static TEST_CASE test_cases[] = { 178 /* IPv4 in IPv6. */ 179 {"ipv4, ipv6", "ipv6:::ffff:1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, 180 {"ipv6", "ipv6:::ffff:1.2.3.4", 0, "IPv6:::ffff:1.2.3.4", "::ffff:1.2.3.4", AF_INET6}, 181 /* Pass IPv4 or IPV6. */ 182 {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", AF_INET6}, 183 {"ipv4, ipv6", "1.2.3.4", 0, "1.2.3.4", "1.2.3.4", AF_INET}, 184 /* Normalize IPv4 or IPV6. */ 185 {"ipv4, ipv6", "ipv6:fc00::0", 0, "IPv6:fc00::", "fc00::", AF_INET6}, 186 {"ipv4, ipv6", "01.02.03.04", 0, "1.2.3.4", "1.2.3.4", AF_INET}, 187 /* Suppress specific outputs. */ 188 {"ipv4, ipv6", "ipv6:fc00::1", 0, 0, "fc00::1", AF_INET6}, 189 {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", 0, AF_INET6}, 190 {"ipv4, ipv6", "ipv6:fc00::1", 0, "IPv6:fc00::1", "fc00::1", -1}, 191 /* Address type mismatch. */ 192 {"ipv4, ipv6", "::ffff:1.2.3.4", -1}, 193 {"ipv4", "ipv6:fc00::1", -1}, 194 {"ipv6", "1.2.3.4", -1}, 195 0, 196 }; 197 TEST_CASE *test_case; 198 199 /* Actual results. */ 200 int act_return; 201 char *act_mailhost_addr = mystrdup("initial_mailhost_addr"); 202 char *act_bare_addr = mystrdup("initial_bare_addr"); 203 int act_addr_family = 0xdeadbeef; 204 205 /* Findings. */ 206 int tests_failed = 0; 207 int test_failed; 208 209 for (tests_failed = 0, test_case = test_cases; test_case->inet_protocols; 210 tests_failed += test_failed, test_case++) { 211 test_failed = 0; 212 inet_proto_init(argv[0], test_case->inet_protocols); 213 act_return = 214 normalize_mailhost_addr(test_case->mailhost_addr, 215 test_case->exp_mailhost_addr ? 216 &act_mailhost_addr : (char **) 0, 217 test_case->exp_bare_addr ? 218 &act_bare_addr : (char **) 0, 219 test_case->exp_addr_family >= 0 ? 220 &act_addr_family : (int *) 0); 221 if (act_return != test_case->exp_return) { 222 msg_warn("test case %d return expected=%d actual=%d", 223 (int) (test_case - test_cases), 224 test_case->exp_return, act_return); 225 test_failed = 1; 226 continue; 227 } 228 if (test_case->exp_return != 0) 229 continue; 230 if (test_case->exp_mailhost_addr 231 && strcmp(test_case->exp_mailhost_addr, act_mailhost_addr)) { 232 msg_warn("test case %d mailhost_addr expected=%s actual=%s", 233 (int) (test_case - test_cases), 234 test_case->exp_mailhost_addr, act_mailhost_addr); 235 test_failed = 1; 236 } 237 if (test_case->exp_bare_addr 238 && strcmp(test_case->exp_bare_addr, act_bare_addr)) { 239 msg_warn("test case %d bare_addr expected=%s actual=%s", 240 (int) (test_case - test_cases), 241 test_case->exp_bare_addr, act_bare_addr); 242 test_failed = 1; 243 } 244 if (test_case->exp_addr_family >= 0 245 && test_case->exp_addr_family != act_addr_family) { 246 msg_warn("test case %d addr_family expected=0x%x actual=0x%x", 247 (int) (test_case - test_cases), 248 test_case->exp_addr_family, act_addr_family); 249 test_failed = 1; 250 } 251 } 252 if (act_mailhost_addr) 253 myfree(act_mailhost_addr); 254 if (act_bare_addr) 255 myfree(act_bare_addr); 256 if (tests_failed) 257 msg_info("tests failed: %d", tests_failed); 258 exit(tests_failed != 0); 259 } 260 261 #endif 262