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
normalize_mailhost_addr(const char * string,char ** mailhost_addr,char ** bare_addr,int * addr_family)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 */
main(int argc,char ** argv)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