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 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 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 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 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 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 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 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