| // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) |
| |
| package org.xbill.DNS; |
| |
| import java.net.*; |
| import java.util.regex.*; |
| |
| /** |
| * The Client Subnet EDNS Option, defined in |
| * http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-00 |
| * ("Client subnet in DNS requests"). |
| * |
| * The option is used to convey information about the IP address of the |
| * originating client, so that an authoritative server can make decisions |
| * based on this address, rather than the address of the intermediate |
| * caching name server. |
| * |
| * The option is transmitted as part of an OPTRecord in the additional section |
| * of a DNS message, as defined by RFC 2671 (EDNS0). |
| * |
| * An option code has not been assigned by IANA; the value 20730 (used here) is |
| * also used by several other implementations. |
| * |
| * The wire format of the option contains a 2-byte length field (1 for IPv4, 2 |
| * for IPv6), a 1-byte source netmask, a 1-byte scope netmask, and an address |
| * truncated to the source netmask length (where the final octet is padded with |
| * bits set to 0) |
| * |
| * |
| * @see OPTRecord |
| * |
| * @author Brian Wellington |
| * @author Ming Zhou <mizhou@bnivideo.com>, Beaumaris Networks |
| */ |
| public class ClientSubnetOption extends EDNSOption { |
| |
| private static final long serialVersionUID = -3868158449890266347L; |
| |
| private int family; |
| private int sourceNetmask; |
| private int scopeNetmask; |
| private InetAddress address; |
| |
| ClientSubnetOption() { |
| super(EDNSOption.Code.CLIENT_SUBNET); |
| } |
| |
| private static int |
| checkMaskLength(String field, int family, int val) { |
| int max = Address.addressLength(family) * 8; |
| if (val < 0 || val > max) |
| throw new IllegalArgumentException("\"" + field + "\" " + val + |
| " must be in the range " + |
| "[0.." + max + "]"); |
| return val; |
| } |
| |
| /** |
| * Construct a Client Subnet option. Note that the number of significant bits in |
| * the address must not be greater than the supplied source netmask. |
| * XXX something about Java's mapped addresses |
| * @param sourceNetmask The length of the netmask pertaining to the query. |
| * In replies, it mirrors the same value as in the requests. |
| * @param scopeNetmask The length of the netmask pertaining to the reply. |
| * In requests, it MUST be set to 0. In responses, this may or may not match |
| * the source netmask. |
| * @param address The address of the client. |
| */ |
| public |
| ClientSubnetOption(int sourceNetmask, int scopeNetmask, InetAddress address) { |
| super(EDNSOption.Code.CLIENT_SUBNET); |
| |
| this.family = Address.familyOf(address); |
| this.sourceNetmask = checkMaskLength("source netmask", this.family, |
| sourceNetmask); |
| this.scopeNetmask = checkMaskLength("scope netmask", this.family, |
| scopeNetmask); |
| this.address = Address.truncate(address, sourceNetmask); |
| |
| if (!address.equals(this.address)) |
| throw new IllegalArgumentException("source netmask is not " + |
| "valid for address"); |
| } |
| |
| /** |
| * Construct a Client Subnet option with scope netmask set to 0. |
| * @param sourceNetmask The length of the netmask pertaining to the query. |
| * In replies, it mirrors the same value as in the requests. |
| * @param address The address of the client. |
| * @see ClientSubnetOption |
| */ |
| public |
| ClientSubnetOption(int sourceNetmask, InetAddress address) { |
| this(sourceNetmask, 0, address); |
| } |
| |
| /** |
| * Returns the family of the network address. This will be either IPv4 (1) |
| * or IPv6 (2). |
| */ |
| public int |
| getFamily() { |
| return family; |
| } |
| |
| /** Returns the source netmask. */ |
| public int |
| getSourceNetmask() { |
| return sourceNetmask; |
| } |
| |
| /** Returns the scope netmask. */ |
| public int |
| getScopeNetmask() { |
| return scopeNetmask; |
| } |
| |
| /** Returns the IP address of the client. */ |
| public InetAddress |
| getAddress() { |
| return address; |
| } |
| |
| void |
| optionFromWire(DNSInput in) throws WireParseException { |
| family = in.readU16(); |
| if (family != Address.IPv4 && family != Address.IPv6) |
| throw new WireParseException("unknown address family"); |
| sourceNetmask = in.readU8(); |
| if (sourceNetmask > Address.addressLength(family) * 8) |
| throw new WireParseException("invalid source netmask"); |
| scopeNetmask = in.readU8(); |
| if (scopeNetmask > Address.addressLength(family) * 8) |
| throw new WireParseException("invalid scope netmask"); |
| |
| // Read the truncated address |
| byte [] addr = in.readByteArray(); |
| if (addr.length != (sourceNetmask + 7) / 8) |
| throw new WireParseException("invalid address"); |
| |
| // Convert it to a full length address. |
| byte [] fulladdr = new byte[Address.addressLength(family)]; |
| System.arraycopy(addr, 0, fulladdr, 0, addr.length); |
| |
| try { |
| address = InetAddress.getByAddress(fulladdr); |
| } catch (UnknownHostException e) { |
| throw new WireParseException("invalid address", e); |
| } |
| |
| InetAddress tmp = Address.truncate(address, sourceNetmask); |
| if (!tmp.equals(address)) |
| throw new WireParseException("invalid padding"); |
| } |
| |
| void |
| optionToWire(DNSOutput out) { |
| out.writeU16(family); |
| out.writeU8(sourceNetmask); |
| out.writeU8(scopeNetmask); |
| out.writeByteArray(address.getAddress(), 0, (sourceNetmask + 7) / 8); |
| } |
| |
| String |
| optionToString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append(address.getHostAddress()); |
| sb.append("/"); |
| sb.append(sourceNetmask); |
| sb.append(", scope netmask "); |
| sb.append(scopeNetmask); |
| return sb.toString(); |
| } |
| |
| } |