Home | History | Annotate | Download | only in DNS
      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