Postfix3.3.1
inet_proto.c
[詳解]
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 /* INET_PROTO_INFO *inet_proto_init(context, protocols)
19 /*
20 /* 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 
83 /* System library. */
84 
85 #include <sys_defs.h>
86 #include <netinet/in.h>
87 #include <arpa/nameser.h>
88 #ifdef RESOLVE_H_NEEDS_STDIO_H
89 #include <stdio.h>
90 #endif
91 #include <resolv.h>
92 #include <stdarg.h>
93 #include <unistd.h>
94 
95 /* Utility library. */
96 
97 #include <mymalloc.h>
98 #include <msg.h>
99 #include <myaddrinfo.h>
100 #include <name_mask.h>
101 #include <inet_proto.h>
102 
103  /*
104  * Application-specific.
105  */
106 
107  /*
108  * Run-time initialization, so we can work around LINUX where IPv6 falls
109  * flat on its face because it is not turned on in the kernel.
110  */
112 
113  /*
114  * Infrastructure: lookup table with the protocol names that we support.
115  */
116 #define INET_PROTO_MASK_IPV4 (1<<0)
117 #define INET_PROTO_MASK_IPV6 (1<<1)
118 
119 static const NAME_MASK proto_table[] = {
120 #ifdef HAS_IPV6
123 #else
125 #endif
127  0,
128 };
129 
130 /* make_uchar_vector - create and initialize uchar vector */
131 
132 static unsigned char *make_uchar_vector(int len,...)
133 {
134  const char *myname = "make_uchar_vector";
135  va_list ap;
136  int count;
137  unsigned char *vp;
138 
139  va_start(ap, len);
140  if (len <= 0)
141  msg_panic("%s: bad vector length: %d", myname, len);
142  vp = (unsigned char *) mymalloc(sizeof(*vp) * len);
143  for (count = 0; count < len; count++)
144  vp[count] = va_arg(ap, unsigned);
145  va_end(ap);
146  return (vp);
147 }
148 
149 /* make_unsigned_vector - create and initialize integer vector */
150 
151 static unsigned *make_unsigned_vector(int len,...)
152 {
153  const char *myname = "make_unsigned_vector";
154  va_list ap;
155  int count;
156  unsigned *vp;
157 
158  va_start(ap, len);
159  if (len <= 0)
160  msg_panic("%s: bad vector length: %d", myname, len);
161  vp = (unsigned *) mymalloc(sizeof(*vp) * len);
162  for (count = 0; count < len; count++)
163  vp[count] = va_arg(ap, unsigned);
164  va_end(ap);
165  return (vp);
166 }
167 
168 /* inet_proto_free - destroy data */
169 
170 static void inet_proto_free(INET_PROTO_INFO *pf)
171 {
172  myfree((void *) pf->ai_family_list);
173  myfree((void *) pf->dns_atype_list);
174  myfree((void *) pf->sa_family_list);
175  myfree((void *) pf);
176 }
177 
178 /* inet_proto_init - convert protocol names to library inputs */
179 
180 INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols)
181 {
182  const char *myname = "inet_proto";
183  INET_PROTO_INFO *pf;
184  int inet_proto_mask;
185  int sock;
186 
187  /*
188  * Avoid run-time errors when all network protocols are disabled. We
189  * can't look up interface information, and we can't convert explicit
190  * names or addresses.
191  */
192  inet_proto_mask = name_mask(context, proto_table, protocols);
193 #ifdef HAS_IPV6
194  if (inet_proto_mask & INET_PROTO_MASK_IPV6) {
195  if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) {
196  close(sock);
197  } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
198  msg_warn("%s: disabling IPv6 name/address support: %m", context);
199  inet_proto_mask &= ~INET_PROTO_MASK_IPV6;
200  } else {
201  msg_fatal("socket: %m");
202  }
203  }
204 #endif
205  if (inet_proto_mask & INET_PROTO_MASK_IPV4) {
206  if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) {
207  close(sock);
208  } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) {
209  msg_warn("%s: disabling IPv4 name/address support: %m", context);
210  inet_proto_mask &= ~INET_PROTO_MASK_IPV4;
211  } else {
212  msg_fatal("socket: %m");
213  }
214  }
215 
216  /*
217  * Store address family etc. info as null-terminated vectors. If that
218  * breaks because we must be able to store nulls, we'll deal with the
219  * additional complexity.
220  *
221  * XXX Use compile-time initialized data templates instead of building the
222  * reply on the fly.
223  */
224  switch (inet_proto_mask) {
225 #ifdef HAS_IPV6
226  case INET_PROTO_MASK_IPV6:
227  pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
228  pf->ai_family = PF_INET6;
229  pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0);
230  pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0);
231  pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0);
232  break;
233  case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4):
234  pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
235  pf->ai_family = PF_UNSPEC;
236  pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0);
237  pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0);
238  pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0);
239  break;
240 #endif
241  case INET_PROTO_MASK_IPV4:
242  pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
243  pf->ai_family = PF_INET;
244  pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0);
245  pf->dns_atype_list = make_unsigned_vector(2, T_A, 0);
246  pf->sa_family_list = make_uchar_vector(2, AF_INET, 0);
247  break;
248  case 0:
249  pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf));
250  pf->ai_family = PF_UNSPEC;
251  pf->ai_family_list = make_unsigned_vector(1, 0);
252  pf->dns_atype_list = make_unsigned_vector(1, 0);
253  pf->sa_family_list = make_uchar_vector(1, 0);
254  break;
255  default:
256  msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask);
257  }
258  if (inet_proto_table)
259  inet_proto_free(inet_proto_table);
260  return (inet_proto_table = pf);
261 }
262 
263 #ifdef TEST
264 
265  /*
266  * Small driver for unit tests.
267  */
268 
269 static char *print_unsigned_vector(VSTRING *buf, unsigned *vector)
270 {
271  unsigned *p;
272 
273  VSTRING_RESET(buf);
274  for (p = vector; *p; p++) {
275  vstring_sprintf_append(buf, "%u", *p);
276  if (p[1])
277  VSTRING_ADDCH(buf, ' ');
278  }
279  VSTRING_TERMINATE(buf);
280  return (vstring_str(buf));
281 }
282 
283 static char *print_uchar_vector(VSTRING *buf, unsigned char *vector)
284 {
285  unsigned char *p;
286 
287  VSTRING_RESET(buf);
288  for (p = vector; *p; p++) {
289  vstring_sprintf_append(buf, "%u", *p);
290  if (p[1])
291  VSTRING_ADDCH(buf, ' ');
292  }
293  VSTRING_TERMINATE(buf);
294  return (vstring_str(buf));
295 }
296 
297 int main(int argc, char **argv)
298 {
299  const char *myname = argv[0];
300  INET_PROTO_INFO *pf;
301  VSTRING *buf;
302 
303  if (argc < 2)
304  msg_fatal("usage: %s protocol(s)...", myname);
305 
306  buf = vstring_alloc(10);
307  while (*++argv) {
308  msg_info("=== %s ===", *argv);
309  inet_proto_init(myname, *argv);
310  pf = inet_proto_table;
311  msg_info("ai_family = %u", pf->ai_family);
312  msg_info("ai_family_list = %s",
313  print_unsigned_vector(buf, pf->ai_family_list));
314  msg_info("dns_atype_list = %s",
315  print_unsigned_vector(buf, pf->dns_atype_list));
316  msg_info("sa_family_list = %s",
317  print_uchar_vector(buf, pf->sa_family_list));
318  }
319  vstring_free(buf);
320  return (0);
321 }
322 
323 #endif
void myfree(void *ptr)
Definition: mymalloc.c:207
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
int main(int argc, char **argv)
Definition: anvil.c:1010
INET_PROTO_INFO * inet_proto_init(const char *context, const char *protocols)
Definition: inet_proto.c:180
#define INET_PROTO_NAME_ALL
Definition: mail_params.h:992
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
unsigned int * ai_family_list
Definition: inet_proto.h:19
#define INET_PROTO_MASK_IPV4
Definition: inet_proto.c:116
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
INET_PROTO_INFO * inet_proto_table
Definition: inet_proto.c:111
#define VSTRING_RESET(vp)
Definition: vstring.h:77
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
unsigned char * sa_family_list
Definition: inet_proto.h:21
#define name_mask(tag, table, str)
Definition: name_mask.h:49
#define INET_PROTO_NAME_IPV6
Definition: mail_params.h:991
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define INET_PROTO_MASK_IPV6
Definition: inet_proto.c:117
#define INET_PROTO_NAME_IPV4
Definition: mail_params.h:990
unsigned int ai_family
Definition: inet_proto.h:18
unsigned int * dns_atype_list
Definition: inet_proto.h:20
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199