xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_proto.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
1 /*	$NetBSD: inet_proto.c,v 1.4 2022/10/08 16:12:50 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	inet_proto 3
6 /* SUMMARY
7 /*	convert protocol names to assorted constants
8 /* SYNOPSIS
9 /*	#include <inet_proto.h>
10 /*
11 /*	typedef struct {
12 /* .in +4
13 /*		unsigned ai_family; /* PF_UNSPEC, PF_INET, or PF_INET6 */
14 /*		unsigned *ai_family_list; /* PF_INET and/or PF_INET6 */
15 /*		unsigned *dns_atype_list;/* TAAAA and/or TA */
16 /*		unsigned char *sa_family_list;/* AF_INET6 and/or AF_INET */
17 /* .in -4
18 /*	} INET_PROTO_INFO;
19 /*
20 /*	const INET_PROTO_INFO *inet_proto_init(context, protocols)
21 /*
22 /*	const INET_PROTO_INFO *inet_proto_info()
23 /* DESCRIPTION
24 /*	inet_proto_init() converts a string with protocol names
25 /*	into null-terminated lists of appropriate constants used
26 /*	by Postfix library routines.  The idea is that one should
27 /*	be able to configure an MTA for IPv4 only, without having
28 /*	to recompile code (what a concept).
29 /*
30 /*	Unfortunately, some compilers won't link initialized data
31 /*	without a function call into the same source module, so
32 /*	we invoke inet_proto_info() in order to access the result
33 /*	from inet_proto_init() from within library routines.
34 /*	inet_proto_info() also conveniently initializes the data
35 /*	to built-in defaults.
36 /*
37 /*	Arguments:
38 /* .IP context
39 /*	Typically, a configuration parameter name.
40 /* .IP protocols
41 /*	Null-terminated string with protocol names separated by
42 /*	whitespace and/or commas:
43 /* .RS
44 /* .IP INET_PROTO_NAME_ALL
45 /*	Enable all available IP protocols.
46 /* .IP INET_PROTO_NAME_IPV4
47 /*	Enable IP version 4 support.
48 /* .IP INET_PROTO_NAME_IPV6
49 /*	Enable IP version 6 support.
50 /* .RS
51 /* .PP
52 /*	Results:
53 /* .IP ai_family
54 /*	Only one of PF_UNSPEC, PF_INET, or PF_INET6. This can be
55 /*	used as input for the getaddrinfo() and getnameinfo()
56 /*	routines.
57 /* .IP ai_family_list
58 /*	One or more of PF_INET or PF_INET6. This can be used as
59 /*	input for the inet_addr_local() routine.
60 /* .IP dns_atype_list
61 /*	One or more of T_AAAA or T_A. This can be used as input for
62 /*	the dns_lookup_v() and dns_lookup_l() routines.
63 /* .IP sa_family_list
64 /*	One or more of AF_INET6 or AF_INET. This can be used as an
65 /*	output filter for the results from the getaddrinfo() and
66 /*	getnameinfo() routines.
67 /* SEE ALSO
68 /*	msg(3) diagnostics interface
69 /* DIAGNOSTICS
70 /*	This module will warn and turn off support for any protocol
71 /*	that is requested but unavailable.
72 /*
73 /*	Fatal errors: memory allocation problem.
74 /* LICENSE
75 /* .ad
76 /* .fi
77 /*	The Secure Mailer license must be distributed with this software.
78 /* AUTHOR(S)
79 /*	Wietse Venema
80 /*	IBM T.J. Watson Research
81 /*	P.O. Box 704
82 /*	Yorktown Heights, NY 10598, USA
83 /*
84 /*	Wietse Venema
85 /*	Google, Inc.
86 /*	111 8th Avenue
87 /*	New York, NY 10011, USA
88 /*--*/
89 
90 /* System library. */
91 
92 #include <sys_defs.h>
93 #include <netinet/in.h>
94 #include <arpa/nameser.h>
95 #ifdef RESOLVE_H_NEEDS_STDIO_H
96 #include <stdio.h>
97 #endif
98 #include <resolv.h>
99 #include <stdarg.h>
100 #include <unistd.h>
101 
102 /* Utility library. */
103 
104 #include <mymalloc.h>
105 #include <msg.h>
106 #include <myaddrinfo.h>
107 #include <name_mask.h>
108 #include <inet_proto.h>
109 
110  /*
111   * Application-specific.
112   */
113 
114  /*
115   * Run-time initialization, so we can work around LINUX where IPv6 falls
116   * flat on its face because it is not turned on in the kernel.
117   */
118 INET_PROTO_INFO *inet_proto_table = 0;
119 
120  /*
121   * Infrastructure: lookup table with the protocol names that we support.
122   */
123 #define INET_PROTO_MASK_IPV4	(1<<0)
124 #define INET_PROTO_MASK_IPV6	(1<<1)
125 
126 static const NAME_MASK proto_table[] = {
127 #ifdef HAS_IPV6
128     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6,
129     INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6,
130 #else
131     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4,
132 #endif
133     INET_PROTO_NAME_IPV4, INET_PROTO_MASK_IPV4,
134     0,
135 };
136 
137 /* make_uchar_vector - create and initialize uchar vector */
138 
make_uchar_vector(int len,...)139 static unsigned char *make_uchar_vector(int len,...)
140 {
141     const char *myname = "make_uchar_vector";
142     va_list ap;
143     int     count;
144     unsigned char *vp;
145 
146     va_start(ap, len);
147     if (len <= 0)
148 	msg_panic("%s: bad vector length: %d", myname, len);
149     vp = (unsigned char *) mymalloc(sizeof(*vp) * len);
150     for (count = 0; count < len; count++)
151 	vp[count] = va_arg(ap, unsigned);
152     va_end(ap);
153     return (vp);
154 }
155 
156 /* make_unsigned_vector - create and initialize integer vector */
157 
make_unsigned_vector(int len,...)158 static unsigned *make_unsigned_vector(int len,...)
159 {
160     const char *myname = "make_unsigned_vector";
161     va_list ap;
162     int     count;
163     unsigned *vp;
164 
165     va_start(ap, len);
166     if (len <= 0)
167 	msg_panic("%s: bad vector length: %d", myname, len);
168     vp = (unsigned *) mymalloc(sizeof(*vp) * len);
169     for (count = 0; count < len; count++)
170 	vp[count] = va_arg(ap, unsigned);
171     va_end(ap);
172     return (vp);
173 }
174 
175 /* inet_proto_free - destroy data */
176 
inet_proto_free(INET_PROTO_INFO * pf)177 static void inet_proto_free(INET_PROTO_INFO *pf)
178 {
179     myfree((void *) pf->ai_family_list);
180     myfree((void *) pf->dns_atype_list);
181     myfree((void *) pf->sa_family_list);
182     myfree((void *) pf);
183 }
184 
185 /* inet_proto_init - convert protocol names to library inputs */
186 
inet_proto_init(const char * context,const char * protocols)187 const INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
188 {
189     const char *myname = "inet_proto";
190     INET_PROTO_INFO *pf;
191     int     inet_proto_mask;
192     int     sock;
193 
194     /*
195      * Avoid run-time errors when all network protocols are disabled. We
196      * can't look up interface information, and we can't convert explicit
197      * names or addresses.
198      */
199     inet_proto_mask = name_mask(context, proto_table, protocols);
200 #ifdef HAS_IPV6
201     if (inet_proto_mask & INET_PROTO_MASK_IPV6) {
202 	if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
203 	    close(sock);
204 	} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
205 	    msg_warn("%s: disabling IPv6 name/address support: %m", context);
206 	    inet_proto_mask &= ~INET_PROTO_MASK_IPV6;
207 	} else {
208 	    msg_fatal("socket: %m");
209 	}
210     }
211 #endif
212     if (inet_proto_mask & INET_PROTO_MASK_IPV4) {
213 	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) {
214 	    close(sock);
215 	} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
216 	    msg_warn("%s: disabling IPv4 name/address support: %m", context);
217 	    inet_proto_mask &= ~INET_PROTO_MASK_IPV4;
218 	} else {
219 	    msg_fatal("socket: %m");
220 	}
221     }
222 
223     /*
224      * Store address family etc. info as null-terminated vectors. If that
225      * breaks because we must be able to store nulls, we'll deal with the
226      * additional complexity.
227      *
228      * XXX Use compile-time initialized data templates instead of building the
229      * reply on the fly.
230      */
231     switch (inet_proto_mask) {
232 #ifdef HAS_IPV6
233     case INET_PROTO_MASK_IPV6:
234 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
235 	pf->ai_family = PF_INET6;
236 	pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0);
237 	pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0);
238 	pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0);
239 	break;
240     case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4):
241 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
242 	pf->ai_family = PF_UNSPEC;
243 	pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0);
244 	pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0);
245 	pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0);
246 	break;
247 #endif
248     case INET_PROTO_MASK_IPV4:
249 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
250 	pf->ai_family = PF_INET;
251 	pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0);
252 	pf->dns_atype_list = make_unsigned_vector(2, T_A, 0);
253 	pf->sa_family_list = make_uchar_vector(2, AF_INET, 0);
254 	break;
255     case 0:
256 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
257 	pf->ai_family = PF_UNSPEC;
258 	pf->ai_family_list = make_unsigned_vector(1, 0);
259 	pf->dns_atype_list = make_unsigned_vector(1, 0);
260 	pf->sa_family_list = make_uchar_vector(1, 0);
261 	break;
262     default:
263 	msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask);
264     }
265     if (inet_proto_table)
266 	inet_proto_free(inet_proto_table);
267     return (inet_proto_table = pf);
268 }
269 
270 #ifdef TEST
271 
272  /*
273   * Small driver for unit tests.
274   */
275 
print_unsigned_vector(VSTRING * buf,unsigned * vector)276 static char *print_unsigned_vector(VSTRING *buf, unsigned *vector)
277 {
278     unsigned *p;
279 
280     VSTRING_RESET(buf);
281     for (p = vector; *p; p++) {
282 	vstring_sprintf_append(buf, "%u", *p);
283 	if (p[1])
284 	    VSTRING_ADDCH(buf, ' ');
285     }
286     VSTRING_TERMINATE(buf);
287     return (vstring_str(buf));
288 }
289 
print_uchar_vector(VSTRING * buf,unsigned char * vector)290 static char *print_uchar_vector(VSTRING *buf, unsigned char *vector)
291 {
292     unsigned char *p;
293 
294     VSTRING_RESET(buf);
295     for (p = vector; *p; p++) {
296 	vstring_sprintf_append(buf, "%u", *p);
297 	if (p[1])
298 	    VSTRING_ADDCH(buf, ' ');
299     }
300     VSTRING_TERMINATE(buf);
301     return (vstring_str(buf));
302 }
303 
main(int argc,char ** argv)304 int     main(int argc, char **argv)
305 {
306     const char *myname = argv[0];
307     INET_PROTO_INFO *pf;
308     VSTRING *buf;
309 
310     if (argc < 2)
311 	msg_fatal("usage: %s protocol(s)...", myname);
312 
313     buf = vstring_alloc(10);
314     while (*++argv) {
315 	msg_info("=== %s ===", *argv);
316 	inet_proto_init(myname, *argv);
317 	pf = inet_proto_table;
318 	msg_info("ai_family = %u", pf->ai_family);
319 	msg_info("ai_family_list = %s",
320 		 print_unsigned_vector(buf, pf->ai_family_list));
321 	msg_info("dns_atype_list = %s",
322 		 print_unsigned_vector(buf, pf->dns_atype_list));
323 	msg_info("sa_family_list = %s",
324 		 print_uchar_vector(buf, pf->sa_family_list));
325     }
326     vstring_free(buf);
327     return (0);
328 }
329 
330 #endif
331