blob: 4a98a12d562fee6b68aa8484c765544080b31d16 [file] [log] [blame]
Shuyi Chend7955ce2013-05-22 14:51:55 -07001// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.net.*;
6import 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 */
35public class ClientSubnetOption extends EDNSOption {
36
37private static final long serialVersionUID = -3868158449890266347L;
38
39private int family;
40private int sourceNetmask;
41private int scopeNetmask;
42private InetAddress address;
43
44ClientSubnetOption() {
45 super(EDNSOption.Code.CLIENT_SUBNET);
46}
47
48private static int
49checkMaskLength(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 */
69public
70ClientSubnetOption(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 */
92public
93ClientSubnetOption(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 */
101public int
102getFamily() {
103 return family;
104}
105
106/** Returns the source netmask. */
107public int
108getSourceNetmask() {
109 return sourceNetmask;
110}
111
112/** Returns the scope netmask. */
113public int
114getScopeNetmask() {
115 return scopeNetmask;
116}
117
118/** Returns the IP address of the client. */
119public InetAddress
120getAddress() {
121 return address;
122}
123
124void
125optionFromWire(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
156void
157optionToWire(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
164String
165optionToString() {
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}