aboutsummaryrefslogtreecommitdiff
path: root/src/org/xbill/DNS/ClientSubnetOption.java
blob: 4a98a12d562fee6b68aa8484c765544080b31d16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// 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();
}

}