Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: mynetworks.c,v 1.3 2026/05/09 18:49:16 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	mynetworks 3
      6 /* SUMMARY
      7 /*	generate patterns for my own interface addresses
      8 /* SYNOPSIS
      9 /*	#include <mynetworks.h>
     10 /*
     11 /*	const char *mynetworks()
     12 /* AUXILIARY FUNCTIONS
     13 /*	const char *mynetworks_host()
     14 /* DESCRIPTION
     15 /*	This routine uses the address list built by own_inet_addr()
     16 /*	to produce a list of patterns that match the corresponding
     17 /*	networks.
     18 /*
     19 /*	The interface list is specified with the "inet_interfaces"
     20 /*	configuration parameter.
     21 /*
     22 /*	The address to netblock conversion style is specified with
     23 /*	the "mynetworks_style" parameter: one of "class" (match
     24 /*	whole class A, B, C or D networks), "subnet" (match local
     25 /*	subnets), or "host" (match local interfaces only).
     26 /*
     27 /*	mynetworks_host() uses the "host" style.
     28 /* LICENSE
     29 /* .ad
     30 /* .fi
     31 /*	The Secure Mailer license must be distributed with this software.
     32 /* AUTHOR(S)
     33 /*	Wietse Venema
     34 /*	IBM T.J. Watson Research
     35 /*	P.O. Box 704
     36 /*	Yorktown Heights, NY 10598, USA
     37 /*
     38 /*	Dean C. Strik
     39 /*	Department ICT Services
     40 /*	Eindhoven University of Technology
     41 /*	P.O. Box 513
     42 /*	5600 MB  Eindhoven, Netherlands
     43 /*	E-mail: <dean (at) ipnet6.org>
     44 /*--*/
     45 
     46 /* System library. */
     47 
     48 #include <sys_defs.h>
     49 #include <sys/param.h>
     50 #include <netinet/in.h>
     51 #include <arpa/inet.h>
     52 
     53 #ifndef IN_CLASSD_NET
     54 #define IN_CLASSD_NET		0xf0000000
     55 #define IN_CLASSD_NSHIFT 	28
     56 #endif
     57 
     58 /* Utility library. */
     59 
     60 #include <msg.h>
     61 #include <vstring.h>
     62 #include <inet_addr_list.h>
     63 #include <name_mask.h>
     64 #include <myaddrinfo.h>
     65 #include <mask_addr.h>
     66 #include <argv.h>
     67 #include <inet_proto.h>
     68 #include <mymalloc.h>
     69 
     70 /* Global library. */
     71 
     72 #include <own_inet_addr.h>
     73 #include <mail_params.h>
     74 #include <mynetworks.h>
     75 #include <sock_addr.h>
     76 #include <been_here.h>
     77 
     78 /* Application-specific. */
     79 
     80 #define MASK_STYLE_CLASS	(1 << 0)
     81 #define MASK_STYLE_SUBNET	(1 << 1)
     82 #define MASK_STYLE_HOST		(1 << 2)
     83 
     84 static const NAME_MASK mask_styles[] = {
     85     MYNETWORKS_STYLE_CLASS, MASK_STYLE_CLASS,
     86     MYNETWORKS_STYLE_SUBNET, MASK_STYLE_SUBNET,
     87     MYNETWORKS_STYLE_HOST, MASK_STYLE_HOST,
     88     0,
     89 };
     90 
     91 /* mynetworks_core - return patterns for specific mynetworks style */
     92 
     93 static const char *mynetworks_core(const char *style)
     94 {
     95     const char *myname = "mynetworks_core";
     96     VSTRING *result;
     97     INET_ADDR_LIST *my_addr_list;
     98     INET_ADDR_LIST *my_mask_list;
     99     unsigned shift;
    100     unsigned junk;
    101     int     i;
    102     unsigned mask_style;
    103     struct sockaddr_storage *sa;
    104     struct sockaddr_storage *ma;
    105     int     net_mask_count = 0;
    106     ARGV   *argv;
    107     BH_TABLE *dup_filter;
    108     char  **cpp;
    109 
    110     /*
    111      * Avoid run-time errors when all network protocols are disabled. We
    112      * can't look up interface information, and we can't convert explicit
    113      * names or addresses.
    114      */
    115     if (inet_proto_info()->ai_family_list[0] == 0) {
    116 	if (msg_verbose)
    117 	    msg_info("skipping %s setting - "
    118 		     "all network protocols are disabled",
    119 		     VAR_MYNETWORKS);
    120 	return (mystrdup(""));
    121     }
    122     mask_style = name_mask("mynetworks mask style", mask_styles, style);
    123 
    124     /*
    125      * XXX Workaround: name_mask() needs a flags argument so that we can
    126      * require exactly one value, or we need to provide an API that is
    127      * dedicated for single-valued flags.
    128      *
    129      * XXX Why not use name_code() instead?
    130      */
    131     for (i = 0, junk = mask_style; junk != 0; junk >>= 1U)
    132 	i += (junk & 1);
    133     if (i != 1)
    134 	msg_fatal("bad %s value: %s; specify exactly one value",
    135 		  VAR_MYNETWORKS_STYLE, var_mynetworks_style);
    136 
    137     result = vstring_alloc(20);
    138     my_addr_list = own_inet_addr_list();
    139     my_mask_list = own_inet_mask_list();
    140 
    141     for (sa = my_addr_list->addrs, ma = my_mask_list->addrs;
    142 	 sa < my_addr_list->addrs + my_addr_list->used;
    143 	 sa++, ma++) {
    144 	unsigned long addr;
    145 	unsigned long mask;
    146 	struct in_addr net;
    147 
    148 	if (SOCK_ADDR_FAMILY(sa) == AF_INET) {
    149 	    addr = ntohl(SOCK_ADDR_IN_ADDR(sa).s_addr);
    150 	    mask = ntohl(SOCK_ADDR_IN_ADDR(ma).s_addr);
    151 
    152 	    switch (mask_style) {
    153 
    154 		/*
    155 		 * Natural mask. This is dangerous if you're customer of an
    156 		 * ISP who gave you a small portion of their network.
    157 		 */
    158 	    case MASK_STYLE_CLASS:
    159 		if (IN_CLASSA(addr)) {
    160 		    mask = IN_CLASSA_NET;
    161 		    shift = IN_CLASSA_NSHIFT;
    162 		} else if (IN_CLASSB(addr)) {
    163 		    mask = IN_CLASSB_NET;
    164 		    shift = IN_CLASSB_NSHIFT;
    165 		} else if (IN_CLASSC(addr)) {
    166 		    mask = IN_CLASSC_NET;
    167 		    shift = IN_CLASSC_NSHIFT;
    168 		} else if (IN_CLASSD(addr)) {
    169 		    mask = IN_CLASSD_NET;
    170 		    shift = IN_CLASSD_NSHIFT;
    171 		} else {
    172 		    msg_fatal("%s: unknown address class: %s",
    173 			      myname, inet_ntoa(SOCK_ADDR_IN_ADDR(sa)));
    174 		}
    175 		break;
    176 
    177 		/*
    178 		 * Subnet mask. This is less unsafe, but still bad if you're
    179 		 * connected to a large subnet.
    180 		 */
    181 	    case MASK_STYLE_SUBNET:
    182 		for (junk = mask, shift = MAI_V4ADDR_BITS; junk != 0;
    183 		     shift--, junk <<= 1)
    184 		     /* void */ ;
    185 		break;
    186 
    187 		/*
    188 		 * Host only. Do not relay authorize other hosts.
    189 		 */
    190 	    case MASK_STYLE_HOST:
    191 		mask = ~0UL;
    192 		shift = 0;
    193 		break;
    194 
    195 	    default:
    196 		msg_panic("unknown mynetworks mask style: %s",
    197 			  var_mynetworks_style);
    198 	    }
    199 	    net.s_addr = htonl(addr & mask);
    200 	    vstring_sprintf_append(result, "%s/%d ",
    201 				   inet_ntoa(net), MAI_V4ADDR_BITS - shift);
    202 	    net_mask_count++;
    203 	    continue;
    204 	}
    205 #ifdef HAS_IPV6
    206 	else if (SOCK_ADDR_FAMILY(sa) == AF_INET6) {
    207 	    MAI_HOSTADDR_STR hostaddr;
    208 	    unsigned char *ac;
    209 	    unsigned char *end;
    210 	    unsigned char ch;
    211 	    struct sockaddr_in6 net6;
    212 
    213 	    switch (mask_style) {
    214 
    215 		/*
    216 		 * There are no classes for IPv6. We default to subnets
    217 		 * instead.
    218 		 */
    219 	    case MASK_STYLE_CLASS:
    220 
    221 		/* FALLTHROUGH */
    222 
    223 		/*
    224 		 * Subnet mask.
    225 		 */
    226 	    case MASK_STYLE_SUBNET:
    227 		ac = (unsigned char *) &SOCK_ADDR_IN6_ADDR(ma);
    228 		end = ac + sizeof(SOCK_ADDR_IN6_ADDR(ma));
    229 		shift = MAI_V6ADDR_BITS;
    230 		while (ac < end) {
    231 		    if ((ch = *ac++) == (unsigned char) ~0U) {
    232 			shift -= CHAR_BIT;
    233 			continue;
    234 		    } else {
    235 			while (ch != 0)
    236 			    shift--, ch <<= 1;
    237 			break;
    238 		    }
    239 		}
    240 		break;
    241 
    242 		/*
    243 		 * Host only. Do not relay authorize other hosts.
    244 		 */
    245 	    case MASK_STYLE_HOST:
    246 		shift = 0;
    247 		break;
    248 
    249 	    default:
    250 		msg_panic("unknown mynetworks mask style: %s",
    251 			  var_mynetworks_style);
    252 	    }
    253 	    /* FIX 200501: IPv6 patch did not clear host bits. */
    254 	    net6 = *SOCK_ADDR_IN6_PTR(sa);
    255 	    mask_addr((unsigned char *) &net6.sin6_addr,
    256 		      sizeof(net6.sin6_addr),
    257 		      MAI_V6ADDR_BITS - shift);
    258 	    SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(&net6), SOCK_ADDR_LEN(&net6),
    259 				 &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
    260 	    vstring_sprintf_append(result, "[%s]/%d ",
    261 				   hostaddr.buf, MAI_V6ADDR_BITS - shift);
    262 	    net_mask_count++;
    263 	    continue;
    264 	}
    265 #endif
    266 	else {
    267 	    msg_warn("%s: skipping unknown address family %d",
    268 		     myname, SOCK_ADDR_FAMILY(sa));
    269 	    continue;
    270 	}
    271     }
    272 
    273     /*
    274      * FIX 200501 IPv6 patch produced repeated results. Some systems report
    275      * the same interface multiple times, notably multi-homed systems with
    276      * IPv6 link-local or site-local addresses. A straight-forward sort+uniq
    277      * produces ugly results, though. Instead we preserve the original order
    278      * and use a duplicate filter to suppress repeated information.
    279      */
    280     if (net_mask_count > 1) {
    281 	argv = argv_split(vstring_str(result), " ");
    282 	VSTRING_RESET(result);
    283 	dup_filter = been_here_init(net_mask_count, BH_FLAG_NONE);
    284 	for (cpp = argv->argv; cpp < argv->argv + argv->argc; cpp++)
    285 	    if (!been_here_fixed(dup_filter, *cpp))
    286 		vstring_sprintf_append(result, "%s ", *cpp);
    287 	argv_free(argv);
    288 	been_here_free(dup_filter);
    289     }
    290     if (msg_verbose)
    291 	msg_info("%s: %s", myname, vstring_str(result));
    292     return (vstring_export(result));
    293 }
    294 
    295 /* mynetworks - return patterns that match my own networks */
    296 
    297 const char *mynetworks(void)
    298 {
    299     static const char *result;
    300 
    301     if (result == 0)
    302 	result = mynetworks_core(var_mynetworks_style);
    303     return (result);
    304 }
    305 
    306 /* mynetworks_host - return patterns for "host" mynetworks style */
    307 
    308 const char *mynetworks_host(void)
    309 {
    310     static const char *result;
    311 
    312     if (result == 0)
    313 	result = mynetworks_core(MYNETWORKS_STYLE_HOST);
    314     return (result);
    315 }
    316 
    317 #ifdef TEST
    318 #include <inet_proto.h>
    319 
    320 int     main(int argc, char **argv)
    321 {
    322     const INET_PROTO_INFO *proto_info;
    323 
    324     if (argc != 4)
    325 	msg_fatal("usage: %s protocols mask_style interface_list (e.g. \"all subnet all\")",
    326 		  argv[0]);
    327     msg_verbose = 10;
    328     proto_info = inet_proto_init(argv[0], argv[1]);
    329     var_mynetworks_style = argv[2];
    330     var_inet_interfaces = argv[3];
    331     mynetworks();
    332     return (0);
    333 }
    334 
    335 #endif
    336