Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / util / inet_proto.c
blobdab0ae50a6ca1531812c40a7c8e786d8eb0104cf
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* inet_proto 3
6 /* SUMMARY
7 /* convert protocol names to assorted constants
8 /* SYNOPSIS
9 /* #include <inet_proto.h>
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;
20 /* INET_PROTO_INFO *inet_proto_init(context, protocols)
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).
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.
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.
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 /*--*/
86 /* System library. */
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>
98 /* Utility library. */
100 #include <mymalloc.h>
101 #include <msg.h>
102 #include <myaddrinfo.h>
103 #include <name_mask.h>
104 #include <inet_proto.h>
107 * Application-specific.
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.
114 INET_PROTO_INFO *inet_proto_table = 0;
117 * Infrastructure: lookup table with the protocol names that we support.
119 #define INET_PROTO_MASK_IPV4 (1<<0)
120 #define INET_PROTO_MASK_IPV6 (1<<1)
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,
133 /* make_uchar_vector - create and initialize uchar vector */
135 static unsigned char *make_uchar_vector(int len,...)
137 const char *myname = "make_uchar_vector";
138 va_list ap;
139 int count;
140 unsigned char *vp;
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);
152 /* make_unsigned_vector - create and initialize integer vector */
154 static unsigned *make_unsigned_vector(int len,...)
156 const char *myname = "make_unsigned_vector";
157 va_list ap;
158 int count;
159 unsigned *vp;
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);
171 /* inet_proto_free - destroy data */
173 static void inet_proto_free(INET_PROTO_INFO *pf)
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);
181 /* inet_proto_init - convert protocol names to library inputs */
183 INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
185 const char *myname = "inet_proto";
186 INET_PROTO_INFO *pf;
187 int inet_proto_mask;
188 int sock;
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.
195 * XXX Use compile-time initialized data templates instead of building the
196 * reply on the fly.
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");
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");
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);
242 if (inet_proto_table)
243 inet_proto_free(inet_proto_table);
244 return (inet_proto_table = pf);
247 #ifdef TEST
250 * Small driver for unit tests.
252 int main(int argc, char **argv)
254 const char *myname = argv[0];
255 INET_PROTO_INFO *pf;
257 if (argc < 2)
258 msg_fatal("usage: %s protocol(s)...", myname);
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]);
273 return (0);
276 #endif