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