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