xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/normalize_mailhost_addr.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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