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