1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) xbill.org) 2 3 package org.xbill.DNS; 4 5 import java.net.*; 6 import java.util.regex.*; 7 8 /** 9 * The Client Subnet EDNS Option, defined in 10 * http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-00 11 * ("Client subnet in DNS requests"). 12 * 13 * The option is used to convey information about the IP address of the 14 * originating client, so that an authoritative server can make decisions 15 * based on this address, rather than the address of the intermediate 16 * caching name server. 17 * 18 * The option is transmitted as part of an OPTRecord in the additional section 19 * of a DNS message, as defined by RFC 2671 (EDNS0). 20 * 21 * An option code has not been assigned by IANA; the value 20730 (used here) is 22 * also used by several other implementations. 23 * 24 * The wire format of the option contains a 2-byte length field (1 for IPv4, 2 25 * for IPv6), a 1-byte source netmask, a 1-byte scope netmask, and an address 26 * truncated to the source netmask length (where the final octet is padded with 27 * bits set to 0) 28 * 29 * 30 * @see OPTRecord 31 * 32 * @author Brian Wellington 33 * @author Ming Zhou <mizhou (at) bnivideo.com>, Beaumaris Networks 34 */ 35 public class ClientSubnetOption extends EDNSOption { 36 37 private static final long serialVersionUID = -3868158449890266347L; 38 39 private int family; 40 private int sourceNetmask; 41 private int scopeNetmask; 42 private InetAddress address; 43 44 ClientSubnetOption() { 45 super(EDNSOption.Code.CLIENT_SUBNET); 46 } 47 48 private static int 49 checkMaskLength(String field, int family, int val) { 50 int max = Address.addressLength(family) * 8; 51 if (val < 0 || val > max) 52 throw new IllegalArgumentException("\"" + field + "\" " + val + 53 " must be in the range " + 54 "[0.." + max + "]"); 55 return val; 56 } 57 58 /** 59 * Construct a Client Subnet option. Note that the number of significant bits in 60 * the address must not be greater than the supplied source netmask. 61 * XXX something about Java's mapped addresses 62 * @param sourceNetmask The length of the netmask pertaining to the query. 63 * In replies, it mirrors the same value as in the requests. 64 * @param scopeNetmask The length of the netmask pertaining to the reply. 65 * In requests, it MUST be set to 0. In responses, this may or may not match 66 * the source netmask. 67 * @param address The address of the client. 68 */ 69 public 70 ClientSubnetOption(int sourceNetmask, int scopeNetmask, InetAddress address) { 71 super(EDNSOption.Code.CLIENT_SUBNET); 72 73 this.family = Address.familyOf(address); 74 this.sourceNetmask = checkMaskLength("source netmask", this.family, 75 sourceNetmask); 76 this.scopeNetmask = checkMaskLength("scope netmask", this.family, 77 scopeNetmask); 78 this.address = Address.truncate(address, sourceNetmask); 79 80 if (!address.equals(this.address)) 81 throw new IllegalArgumentException("source netmask is not " + 82 "valid for address"); 83 } 84 85 /** 86 * Construct a Client Subnet option with scope netmask set to 0. 87 * @param sourceNetmask The length of the netmask pertaining to the query. 88 * In replies, it mirrors the same value as in the requests. 89 * @param address The address of the client. 90 * @see ClientSubnetOption 91 */ 92 public 93 ClientSubnetOption(int sourceNetmask, InetAddress address) { 94 this(sourceNetmask, 0, address); 95 } 96 97 /** 98 * Returns the family of the network address. This will be either IPv4 (1) 99 * or IPv6 (2). 100 */ 101 public int 102 getFamily() { 103 return family; 104 } 105 106 /** Returns the source netmask. */ 107 public int 108 getSourceNetmask() { 109 return sourceNetmask; 110 } 111 112 /** Returns the scope netmask. */ 113 public int 114 getScopeNetmask() { 115 return scopeNetmask; 116 } 117 118 /** Returns the IP address of the client. */ 119 public InetAddress 120 getAddress() { 121 return address; 122 } 123 124 void 125 optionFromWire(DNSInput in) throws WireParseException { 126 family = in.readU16(); 127 if (family != Address.IPv4 && family != Address.IPv6) 128 throw new WireParseException("unknown address family"); 129 sourceNetmask = in.readU8(); 130 if (sourceNetmask > Address.addressLength(family) * 8) 131 throw new WireParseException("invalid source netmask"); 132 scopeNetmask = in.readU8(); 133 if (scopeNetmask > Address.addressLength(family) * 8) 134 throw new WireParseException("invalid scope netmask"); 135 136 // Read the truncated address 137 byte [] addr = in.readByteArray(); 138 if (addr.length != (sourceNetmask + 7) / 8) 139 throw new WireParseException("invalid address"); 140 141 // Convert it to a full length address. 142 byte [] fulladdr = new byte[Address.addressLength(family)]; 143 System.arraycopy(addr, 0, fulladdr, 0, addr.length); 144 145 try { 146 address = InetAddress.getByAddress(fulladdr); 147 } catch (UnknownHostException e) { 148 throw new WireParseException("invalid address", e); 149 } 150 151 InetAddress tmp = Address.truncate(address, sourceNetmask); 152 if (!tmp.equals(address)) 153 throw new WireParseException("invalid padding"); 154 } 155 156 void 157 optionToWire(DNSOutput out) { 158 out.writeU16(family); 159 out.writeU8(sourceNetmask); 160 out.writeU8(scopeNetmask); 161 out.writeByteArray(address.getAddress(), 0, (sourceNetmask + 7) / 8); 162 } 163 164 String 165 optionToString() { 166 StringBuffer sb = new StringBuffer(); 167 sb.append(address.getHostAddress()); 168 sb.append("/"); 169 sb.append(sourceNetmask); 170 sb.append(", scope netmask "); 171 sb.append(scopeNetmask); 172 return sb.toString(); 173 } 174 175 } 176