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