1/* $NetBSD: mynetworks.c,v 1.2 2017/02/14 01:16:45 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@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 84static 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 93static 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 297const 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 308const 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 320char *var_inet_interfaces; 321char *var_mynetworks_style; 322 323int main(int argc, char **argv) 324{ 325 INET_PROTO_INFO *proto_info; 326 327 if (argc != 4) 328 msg_fatal("usage: %s protocols mask_style interface_list (e.g. \"all subnet all\")", 329 argv[0]); 330 msg_verbose = 10; 331 proto_info = inet_proto_init(argv[0], argv[1]); 332 var_mynetworks_style = argv[2]; 333 var_inet_interfaces = argv[3]; 334 mynetworks(); 335 return (0); 336} 337 338#endif 339