Shuyi Chen | d7955ce | 2013-05-22 14:51:55 -0700 | [diff] [blame] | 1 | // Copyright (c) 1999-2004 Brian Wellington (bwelling@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@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 | } |