Home | History | Annotate | Line # | Download | only in util
      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