• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ClientSubnetOption()44 ClientSubnetOption() {
45 	super(EDNSOption.Code.CLIENT_SUBNET);
46 }
47 
48 private static int
checkMaskLength(String field, int family, int val)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
ClientSubnetOption(int sourceNetmask, int scopeNetmask, InetAddress address)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
ClientSubnetOption(int sourceNetmask, InetAddress address)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
getFamily()102 getFamily() {
103 	return family;
104 }
105 
106 /** Returns the source netmask. */
107 public int
getSourceNetmask()108 getSourceNetmask() {
109 	return sourceNetmask;
110 }
111 
112 /** Returns the scope netmask. */
113 public int
getScopeNetmask()114 getScopeNetmask() {
115 	return scopeNetmask;
116 }
117 
118 /** Returns the IP address of the client. */
119 public InetAddress
getAddress()120 getAddress() {
121 	return address;
122 }
123 
124 void
optionFromWire(DNSInput in)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
optionToWire(DNSOutput out)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
optionToString()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