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