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