1 /* $NetBSD: inet_proto.c,v 1.5 2026/05/09 18:49:22 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( 21 /* const char *context, 22 /* const char *protocols) 23 /* 24 /* const INET_PROTO_INFO *inet_proto_info() 25 /* DESCRIPTION 26 /* inet_proto_init() converts a string with protocol names 27 /* into null-terminated lists of appropriate constants used 28 /* by Postfix library routines. The idea is that one should 29 /* be able to configure an MTA for IPv4 only, without having 30 /* to recompile code (what a concept). 31 /* 32 /* Unfortunately, some compilers won't link initialized data 33 /* without a function call into the same source module, so 34 /* we invoke inet_proto_info() in order to access the result 35 /* from inet_proto_init() from within library routines. 36 /* inet_proto_info() also conveniently initializes the data 37 /* to built-in defaults. 38 /* 39 /* Arguments: 40 /* .IP context 41 /* Typically, a configuration parameter name. 42 /* .IP protocols 43 /* Null-terminated string with protocol names separated by 44 /* whitespace and/or commas: 45 /* .RS 46 /* .IP INET_PROTO_NAME_ALL 47 /* Enable all available IP protocols. 48 /* .IP INET_PROTO_NAME_IPV4 49 /* Enable IP version 4 support. 50 /* .IP INET_PROTO_NAME_IPV6 51 /* Enable IP version 6 support. 52 /* .RS 53 /* .PP 54 /* Results: 55 /* .IP ai_family 56 /* Only one of PF_UNSPEC, PF_INET, or PF_INET6. This can be 57 /* used as input for the getaddrinfo() and getnameinfo() 58 /* routines. 59 /* .IP ai_family_list 60 /* One or more of PF_INET or PF_INET6. This can be used as 61 /* input for the inet_addr_local() routine. 62 /* .IP dns_atype_list 63 /* One or more of T_AAAA or T_A. This can be used as input for 64 /* the dns_lookup_v() and dns_lookup_l() routines. 65 /* .IP sa_family_list 66 /* One or more of AF_INET6 or AF_INET. This can be used as an 67 /* output filter for the results from the getaddrinfo() and 68 /* getnameinfo() routines. 69 /* SEE ALSO 70 /* msg(3) diagnostics interface 71 /* DIAGNOSTICS 72 /* This module will warn and turn off support for any protocol 73 /* that is requested but unavailable. 74 /* 75 /* Fatal errors: memory allocation problem. 76 /* LICENSE 77 /* .ad 78 /* .fi 79 /* The Secure Mailer license must be distributed with this software. 80 /* AUTHOR(S) 81 /* Wietse Venema 82 /* IBM T.J. Watson Research 83 /* P.O. Box 704 84 /* Yorktown Heights, NY 10598, USA 85 /* 86 /* Wietse Venema 87 /* Google, Inc. 88 /* 111 8th Avenue 89 /* New York, NY 10011, USA 90 /*--*/ 91 92 /* System library. */ 93 94 #include <sys_defs.h> 95 #include <netinet/in.h> 96 #include <arpa/nameser.h> 97 #ifdef RESOLVE_H_NEEDS_STDIO_H 98 #include <stdio.h> 99 #endif 100 #include <resolv.h> 101 #include <stdarg.h> 102 #include <unistd.h> 103 104 /* Utility library. */ 105 106 #include <mymalloc.h> 107 #include <msg.h> 108 #include <myaddrinfo.h> 109 #include <name_mask.h> 110 #include <inet_proto.h> 111 112 /* 113 * Application-specific. 114 */ 115 116 /* 117 * Run-time initialization, so we can work around LINUX where IPv6 falls 118 * flat on its face because it is not turned on in the kernel. 119 */ 120 INET_PROTO_INFO *inet_proto_table = 0; 121 122 /* 123 * Infrastructure: lookup table with the protocol names that we support. 124 */ 125 #define INET_PROTO_MASK_IPV4 (1<<0) 126 #define INET_PROTO_MASK_IPV6 (1<<1) 127 128 static const NAME_MASK proto_table[] = { 129 #ifdef HAS_IPV6 130 INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4 | INET_PROTO_MASK_IPV6, 131 INET_PROTO_NAME_IPV6, INET_PROTO_MASK_IPV6, 132 #else 133 INET_PROTO_NAME_ALL, INET_PROTO_MASK_IPV4, 134 #endif 135 INET_PROTO_NAME_IPV4, INET_PROTO_MASK_IPV4, 136 0, 137 }; 138 139 /* make_uchar_vector - create and initialize uchar vector */ 140 141 static unsigned char *make_uchar_vector(int len,...) 142 { 143 const char *myname = "make_uchar_vector"; 144 va_list ap; 145 int count; 146 unsigned char *vp; 147 148 va_start(ap, len); 149 if (len <= 0) 150 msg_panic("%s: bad vector length: %d", myname, len); 151 vp = (unsigned char *) mymalloc(sizeof(*vp) * len); 152 for (count = 0; count < len; count++) 153 vp[count] = va_arg(ap, unsigned); 154 va_end(ap); 155 return (vp); 156 } 157 158 /* make_unsigned_vector - create and initialize integer vector */ 159 160 static unsigned *make_unsigned_vector(int len,...) 161 { 162 const char *myname = "make_unsigned_vector"; 163 va_list ap; 164 int count; 165 unsigned *vp; 166 167 va_start(ap, len); 168 if (len <= 0) 169 msg_panic("%s: bad vector length: %d", myname, len); 170 vp = (unsigned *) mymalloc(sizeof(*vp) * len); 171 for (count = 0; count < len; count++) 172 vp[count] = va_arg(ap, unsigned); 173 va_end(ap); 174 return (vp); 175 } 176 177 /* inet_proto_free - destroy data */ 178 179 static void inet_proto_free(INET_PROTO_INFO *pf) 180 { 181 myfree((void *) pf->ai_family_list); 182 myfree((void *) pf->dns_atype_list); 183 myfree((void *) pf->sa_family_list); 184 myfree((void *) pf); 185 } 186 187 /* inet_proto_init - convert protocol names to library inputs */ 188 189 const INET_PROTO_INFO *inet_proto_init(const char *context, const char *protocols) 190 { 191 const char *myname = "inet_proto"; 192 INET_PROTO_INFO *pf; 193 int inet_proto_mask; 194 int sock; 195 196 /* 197 * Avoid run-time errors when all network protocols are disabled. We 198 * can't look up interface information, and we can't convert explicit 199 * names or addresses. 200 */ 201 inet_proto_mask = name_mask(context, proto_table, protocols); 202 #ifdef HAS_IPV6 203 if (inet_proto_mask & INET_PROTO_MASK_IPV6) { 204 if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) >= 0) { 205 close(sock); 206 } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { 207 msg_warn("%s: disabling IPv6 name/address support: %m", context); 208 inet_proto_mask &= ~INET_PROTO_MASK_IPV6; 209 } else { 210 msg_fatal("socket: %m"); 211 } 212 } 213 #endif 214 if (inet_proto_mask & INET_PROTO_MASK_IPV4) { 215 if ((sock = socket(PF_INET, SOCK_STREAM, 0)) >= 0) { 216 close(sock); 217 } else if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) { 218 msg_warn("%s: disabling IPv4 name/address support: %m", context); 219 inet_proto_mask &= ~INET_PROTO_MASK_IPV4; 220 } else { 221 msg_fatal("socket: %m"); 222 } 223 } 224 225 /* 226 * Store address family etc. info as null-terminated vectors. If that 227 * breaks because we must be able to store nulls, we'll deal with the 228 * additional complexity. 229 * 230 * XXX Use compile-time initialized data templates instead of building the 231 * reply on the fly. 232 */ 233 switch (inet_proto_mask) { 234 #ifdef HAS_IPV6 235 case INET_PROTO_MASK_IPV6: 236 pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf)); 237 pf->ai_family = PF_INET6; 238 pf->ai_family_list = make_unsigned_vector(2, PF_INET6, 0); 239 pf->dns_atype_list = make_unsigned_vector(2, T_AAAA, 0); 240 pf->sa_family_list = make_uchar_vector(2, AF_INET6, 0); 241 break; 242 case (INET_PROTO_MASK_IPV6 | INET_PROTO_MASK_IPV4): 243 pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf)); 244 pf->ai_family = PF_UNSPEC; 245 pf->ai_family_list = make_unsigned_vector(3, PF_INET, PF_INET6, 0); 246 pf->dns_atype_list = make_unsigned_vector(3, T_A, T_AAAA, 0); 247 pf->sa_family_list = make_uchar_vector(3, AF_INET, AF_INET6, 0); 248 break; 249 #endif 250 case INET_PROTO_MASK_IPV4: 251 pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf)); 252 pf->ai_family = PF_INET; 253 pf->ai_family_list = make_unsigned_vector(2, PF_INET, 0); 254 pf->dns_atype_list = make_unsigned_vector(2, T_A, 0); 255 pf->sa_family_list = make_uchar_vector(2, AF_INET, 0); 256 break; 257 case 0: 258 pf = (INET_PROTO_INFO *) mymalloc(sizeof(*pf)); 259 pf->ai_family = PF_UNSPEC; 260 pf->ai_family_list = make_unsigned_vector(1, 0); 261 pf->dns_atype_list = make_unsigned_vector(1, 0); 262 pf->sa_family_list = make_uchar_vector(1, 0); 263 break; 264 default: 265 msg_panic("%s: bad inet_proto_mask 0x%x", myname, inet_proto_mask); 266 } 267 if (inet_proto_table) 268 inet_proto_free(inet_proto_table); 269 return (inet_proto_table = pf); 270 } 271 272 #ifdef TEST 273 274 /* 275 * Small driver for unit tests. 276 */ 277 278 static char *print_unsigned_vector(VSTRING *buf, unsigned *vector) 279 { 280 unsigned *p; 281 282 VSTRING_RESET(buf); 283 for (p = vector; *p; p++) { 284 vstring_sprintf_append(buf, "%u", *p); 285 if (p[1]) 286 VSTRING_ADDCH(buf, ' '); 287 } 288 VSTRING_TERMINATE(buf); 289 return (vstring_str(buf)); 290 } 291 292 static char *print_uchar_vector(VSTRING *buf, unsigned char *vector) 293 { 294 unsigned char *p; 295 296 VSTRING_RESET(buf); 297 for (p = vector; *p; p++) { 298 vstring_sprintf_append(buf, "%u", *p); 299 if (p[1]) 300 VSTRING_ADDCH(buf, ' '); 301 } 302 VSTRING_TERMINATE(buf); 303 return (vstring_str(buf)); 304 } 305 306 int main(int argc, char **argv) 307 { 308 const char *myname = argv[0]; 309 INET_PROTO_INFO *pf; 310 VSTRING *buf; 311 312 if (argc < 2) 313 msg_fatal("usage: %s protocol(s)...", myname); 314 315 buf = vstring_alloc(10); 316 while (*++argv) { 317 msg_info("=== %s ===", *argv); 318 inet_proto_init(myname, *argv); 319 pf = inet_proto_table; 320 msg_info("ai_family = %u", pf->ai_family); 321 msg_info("ai_family_list = %s", 322 print_unsigned_vector(buf, pf->ai_family_list)); 323 msg_info("dns_atype_list = %s", 324 print_unsigned_vector(buf, pf->dns_atype_list)); 325 msg_info("sa_family_list = %s", 326 print_uchar_vector(buf, pf->sa_family_list)); 327 } 328 vstring_free(buf); 329 return (0); 330 } 331 332 #endif 333