xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_proto.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: inet_proto.c,v 1.1.1.1 2009/06/23 10:09:00 tron 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 TA. 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 report if IPv6 is unavailable, and will
71 /*	disable IPv6 support in Postfix. When IPv6 is the only
72 /*	selected protocol, this is a fatal error.
73 /*
74 /*	Fatal errors: memory allocation problem.
75 /* LICENSE
76 /* .ad
77 /* .fi
78 /*	The Secure Mailer license must be distributed with this software.
79 /* AUTHOR(S)
80 /*	Wietse Venema
81 /*	IBM T.J. Watson Research
82 /*	P.O. Box 704
83 /*	Yorktown Heights, NY 10598, USA
84 /*--*/
85 
86 /* System library. */
87 
88 #include <sys_defs.h>
89 #include <netinet/in.h>
90 #include <arpa/nameser.h>
91 #ifdef RESOLVE_H_NEEDS_STDIO_H
92 #include <stdio.h>
93 #endif
94 #include <resolv.h>
95 #include <stdarg.h>
96 #include <unistd.h>
97 
98 /* Utility library. */
99 
100 #include <mymalloc.h>
101 #include <msg.h>
102 #include <myaddrinfo.h>
103 #include <name_mask.h>
104 #include <inet_proto.h>
105 
106  /*
107   * Application-specific.
108   */
109 
110  /*
111   * Run-time initialization, so we can work around LINUX where IPv6 falls
112   * flat on its face because it is not turned on in the kernel.
113   */
114 INET_PROTO_INFO *inet_proto_table = 0;
115 
116  /*
117   * Infrastructure: lookup table with the protocol names that we support.
118   */
119 #define INET_PROTO_MASK_IPV4	(1<<0)
120 #define INET_PROTO_MASK_IPV6	(1<<1)
121 
122 static const NAME_MASK proto_table[] = {
123 #ifdef HAS_IPV6
124     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6,
125     INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6,
126 #else
127     INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4,
128 #endif
129     INET_PROTO_NAME_IPV4, INET_PROTO_MASK_IPV4,
130     0,
131 };
132 
133 /* make_uchar_vector - create and initialize uchar vector */
134 
135 static unsigned char *make_uchar_vector(int len,...)
136 {
137     const char *myname = "make_uchar_vector";
138     va_list ap;
139     int     count;
140     unsigned char *vp;
141 
142     va_start(ap, len);
143     if (len <= 0)
144 	msg_panic("%s: bad vector length: %d", myname, len);
145     vp = (unsigned char *) mymalloc(sizeof(*vp) * len);
146     for (count = 0; count < len; count++)
147 	vp[count] = va_arg(ap, unsigned);
148     va_end(ap);
149     return (vp);
150 }
151 
152 /* make_unsigned_vector - create and initialize integer vector */
153 
154 static unsigned *make_unsigned_vector(int len,...)
155 {
156     const char *myname = "make_unsigned_vector";
157     va_list ap;
158     int     count;
159     unsigned *vp;
160 
161     va_start(ap, len);
162     if (len <= 0)
163 	msg_panic("%s: bad vector length: %d", myname, len);
164     vp = (unsigned *) mymalloc(sizeof(*vp) * len);
165     for (count = 0; count < len; count++)
166 	vp[count] = va_arg(ap, unsigned);
167     va_end(ap);
168     return (vp);
169 }
170 
171 /* inet_proto_free - destroy data */
172 
173 static void inet_proto_free(INET_PROTO_INFO *pf)
174 {
175     myfree((char *) pf->ai_family_list);
176     myfree((char *) pf->dns_atype_list);
177     myfree((char *) pf->sa_family_list);
178     myfree((char *) pf);
179 }
180 
181 /* inet_proto_init - convert protocol names to library inputs */
182 
183 INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
184 {
185     const char *myname = "inet_proto";
186     INET_PROTO_INFO *pf;
187     int     inet_proto_mask;
188     int     sock;
189 
190     /*
191      * Store addess family etc. info as null-terminated vectors. If that
192      * breaks because we must be able to store nulls, we'll deal with the
193      * additional complexity.
194      *
195      * XXX Use compile-time initialized data templates instead of building the
196      * reply on the fly.
197      */
198     inet_proto_mask = name_mask(context, proto_table, protocols);
199     switch (inet_proto_mask) {
200 #ifdef HAS_IPV6
201     case INET_PROTO_MASK_IPV6:
202 	if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
203 	    close(sock);
204 	    pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
205 	    pf->ai_family = PF_INET6;
206 	    pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0);
207 	    pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0);
208 	    pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0);
209 	    break;
210 	} else if (errno == EAFNOSUPPORT) {
211 	    msg_fatal("%s: IPv6 support is disabled: %m", context);
212 	} else {
213 	    msg_fatal("socket: %m");
214 	}
215     case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4):
216 	if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
217 	    close(sock);
218 	    pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
219 	    pf->ai_family = PF_UNSPEC;
220 	    pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0);
221 	    pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0);
222 	    pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0);
223 	    break;
224 	} else if (errno == EAFNOSUPPORT) {
225 	    msg_warn("%s: IPv6 support is disabled: %m", context);
226 	    msg_warn("%s: configuring for IPv4 support only", context);
227 	    /* FALLTHROUGH */
228 	} else {
229 	    msg_fatal("socket: %m");
230 	}
231 #endif
232     case INET_PROTO_MASK_IPV4:
233 	pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
234 	pf->ai_family = PF_INET;
235 	pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0);
236 	pf->dns_atype_list = make_unsigned_vector(2, T_A, 0);
237 	pf->sa_family_list = make_uchar_vector(2, AF_INET, 0);
238 	break;
239     default:
240 	msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask);
241     }
242     if (inet_proto_table)
243 	inet_proto_free(inet_proto_table);
244     return (inet_proto_table = pf);
245 }
246 
247 #ifdef TEST
248 
249  /*
250   * Small driver for unit tests.
251   */
252 int     main(int argc, char **argv)
253 {
254     const char *myname = argv[0];
255     INET_PROTO_INFO *pf;
256 
257     if (argc < 2)
258 	msg_fatal("usage: %s protocol(s)...", myname);
259 
260     while (*++argv) {
261 	msg_info("=== %s ===", *argv);
262 	if (**argv)
263 	    inet_proto_init(myname, *argv);
264 	pf = inet_proto_table;
265 	msg_info("ai_family = %u", pf->ai_family);
266 	msg_info("ai_family_list = %u %u...",
267 		 pf->ai_family_list[0], pf->ai_family_list[1]);
268 	msg_info("dns_atype_list = %u %u...",
269 		 pf->dns_atype_list[0], pf->dns_atype_list[1]);
270 	msg_info("sa_family_list = %u %u...",
271 		 pf->sa_family_list[0], pf->sa_family_list[1]);
272     }
273     return (0);
274 }
275 
276 #endif
277