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