xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_proto.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: inet_proto.c,v 1.2 2017/02/14 01:16:49 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 /*	INET_PROTO_INFO *inet_proto_init(context, protocols)
21 /*
22 /*	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 
85 /* System library. */
86 
87 #include <sys_defs.h>
88 #include <netinet/in.h>
89 #include <arpa/nameser.h>
90 #ifdef RESOLVE_H_NEEDS_STDIO_H
91 #include <stdio.h>
92 #endif
93 #include <resolv.h>
94 #include <stdarg.h>
95 #include <unistd.h>
96 
97 /* Utility library. */
98 
99 #include <mymalloc.h>
100 #include <msg.h>
101 #include <myaddrinfo.h>
102 #include <name_mask.h>
103 #include <inet_proto.h>
104 
105  /*
106   * Application-specific.
107   */
108 
109  /*
110   * Run-time initialization, so we can work around LINUX where IPv6 falls
111   * flat on its face because it is not turned on in the kernel.
112   */
113 INET_PROTO_INFO *inet_proto_table = 0;
114 
115  /*
116   * Infrastructure: lookup table with the protocol names that we support.
117   */
118 #define INET_PROTO_MASK_IPV4	(1<<0)
119 #define INET_PROTO_MASK_IPV6	(1<<1)
120 
121 static const NAME_MASK proto_table[] = {
122 #ifdef HAS_IPV6
123     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6,
124     INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6,
125 #else
126     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4,
127 #endif
128     INET_PROTO_NAME_IPV4, INET_PROTO_MASK_IPV4,
129     0,
130 };
131 
132 /* make_uchar_vector - create and initialize uchar vector */
133 
134 static unsigned char *make_uchar_vector(int len,...)
135 {
136     const char *myname = "make_uchar_vector";
137     va_list ap;
138     int     count;
139     unsigned char *vp;
140 
141     va_start(ap, len);
142     if (len <= 0)
143 	msg_panic("%s: bad vector length: %d", myname, len);
144     vp = (unsigned char *) mymalloc(sizeof(*vp) * len);
145     for (count = 0; count < len; count++)
146 	vp[count] = va_arg(ap, unsigned);
147     va_end(ap);
148     return (vp);
149 }
150 
151 /* make_unsigned_vector - create and initialize integer vector */
152 
153 static unsigned *make_unsigned_vector(int len,...)
154 {
155     const char *myname = "make_unsigned_vector";
156     va_list ap;
157     int     count;
158     unsigned *vp;
159 
160     va_start(ap, len);
161     if (len <= 0)
162 	msg_panic("%s: bad vector length: %d", myname, len);
163     vp = (unsigned *) mymalloc(sizeof(*vp) * len);
164     for (count = 0; count < len; count++)
165 	vp[count] = va_arg(ap, unsigned);
166     va_end(ap);
167     return (vp);
168 }
169 
170 /* inet_proto_free - destroy data */
171 
172 static void inet_proto_free(INET_PROTO_INFO *pf)
173 {
174     myfree((void *) pf->ai_family_list);
175     myfree((void *) pf->dns_atype_list);
176     myfree((void *) pf->sa_family_list);
177     myfree((void *) pf);
178 }
179 
180 /* inet_proto_init - convert protocol names to library inputs */
181 
182 INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
183 {
184     const char *myname = "inet_proto";
185     INET_PROTO_INFO *pf;
186     int     inet_proto_mask;
187     int     sock;
188 
189     /*
190      * Avoid run-time errors when all network protocols are disabled. We
191      * can't look up interface information, and we can't convert explicit
192      * names or addresses.
193      */
194     inet_proto_mask = name_mask(context, proto_table, protocols);
195 #ifdef HAS_IPV6
196     if (inet_proto_mask & INET_PROTO_MASK_IPV6) {
197 	if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
198 	    close(sock);
199 	} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
200 	    msg_warn("%s: disabling IPv6 name/address support: %m", context);
201 	    inet_proto_mask &= ~INET_PROTO_MASK_IPV6;
202 	} else {
203 	    msg_fatal("socket: %m");
204 	}
205     }
206 #endif
207     if (inet_proto_mask & INET_PROTO_MASK_IPV4) {
208 	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) {
209 	    close(sock);
210 	} else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
211 	    msg_warn("%s: disabling IPv4 name/address support: %m", context);
212 	    inet_proto_mask &= ~INET_PROTO_MASK_IPV4;
213 	} else {
214 	    msg_fatal("socket: %m");
215 	}
216     }
217 
218     /*
219      * Store addess family etc. info as null-terminated vectors. If that
220      * breaks because we must be able to store nulls, we'll deal with the
221      * additional complexity.
222      *
223      * XXX Use compile-time initialized data templates instead of building the
224      * reply on the fly.
225      */
226     switch (inet_proto_mask) {
227 #ifdef HAS_IPV6
228     case INET_PROTO_MASK_IPV6:
229 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
230 	pf->ai_family = PF_INET6;
231 	pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0);
232 	pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0);
233 	pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0);
234 	break;
235     case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4):
236 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
237 	pf->ai_family = PF_UNSPEC;
238 	pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0);
239 	pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0);
240 	pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0);
241 	break;
242 #endif
243     case INET_PROTO_MASK_IPV4:
244 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
245 	pf->ai_family = PF_INET;
246 	pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0);
247 	pf->dns_atype_list = make_unsigned_vector(2, T_A, 0);
248 	pf->sa_family_list = make_uchar_vector(2, AF_INET, 0);
249 	break;
250     case 0:
251 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
252 	pf->ai_family = PF_UNSPEC;
253 	pf->ai_family_list = make_unsigned_vector(1, 0);
254 	pf->dns_atype_list = make_unsigned_vector(1, 0);
255 	pf->sa_family_list = make_uchar_vector(1, 0);
256 	break;
257     default:
258 	msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask);
259     }
260     if (inet_proto_table)
261 	inet_proto_free(inet_proto_table);
262     return (inet_proto_table = pf);
263 }
264 
265 #ifdef TEST
266 
267  /*
268   * Small driver for unit tests.
269   */
270 
271 static char *print_unsigned_vector(VSTRING *buf, unsigned *vector)
272 {
273     unsigned *p;
274 
275     VSTRING_RESET(buf);
276     for (p = vector; *p; p++) {
277 	vstring_sprintf_append(buf, "%u", *p);
278 	if (p[1])
279 	    VSTRING_ADDCH(buf, ' ');
280     }
281     VSTRING_TERMINATE(buf);
282     return (vstring_str(buf));
283 }
284 
285 static char *print_uchar_vector(VSTRING *buf, unsigned char *vector)
286 {
287     unsigned char *p;
288 
289     VSTRING_RESET(buf);
290     for (p = vector; *p; p++) {
291 	vstring_sprintf_append(buf, "%u", *p);
292 	if (p[1])
293 	    VSTRING_ADDCH(buf, ' ');
294     }
295     VSTRING_TERMINATE(buf);
296     return (vstring_str(buf));
297 }
298 
299 int     main(int argc, char **argv)
300 {
301     const char *myname = argv[0];
302     INET_PROTO_INFO *pf;
303     VSTRING *buf;
304 
305     if (argc < 2)
306 	msg_fatal("usage: %s protocol(s)...", myname);
307 
308     buf = vstring_alloc(10);
309     while (*++argv) {
310 	msg_info("=== %s ===", *argv);
311 	inet_proto_init(myname, *argv);
312 	pf = inet_proto_table;
313 	msg_info("ai_family = %u", pf->ai_family);
314 	msg_info("ai_family_list = %s",
315 		 print_unsigned_vector(buf, pf->ai_family_list));
316 	msg_info("dns_atype_list = %s",
317 		 print_unsigned_vector(buf, pf->dns_atype_list));
318 	msg_info("sa_family_list = %s",
319 		 print_uchar_vector(buf, pf->sa_family_list));
320     }
321     vstring_free(buf);
322     return (0);
323 }
324 
325 #endif
326