-
Notifications
You must be signed in to change notification settings - Fork 262
Expand file tree
/
Copy pathClientSubnetOption.java
More file actions
158 lines (138 loc) · 5.39 KB
/
ClientSubnetOption.java
File metadata and controls
158 lines (138 loc) · 5.39 KB
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
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
package org.xbill.DNS;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* The Client Subnet EDNS Option.
*
* <p>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.
*
* <p>The option is transmitted as part of an OPTRecord in the additional section of a DNS message,
* as defined by RFC 6891 (EDNS0).
*
* <p>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
* @see <a href="https://datatracker.ietf.org/doc/html/rfc7871">RFC 7871</a>
* @author Brian Wellington
* @author Ming Zhou <mizhou@bnivideo.com>, Beaumaris Networks
*/
public class ClientSubnetOption extends EDNSOption {
private int family;
private int sourcePrefixLength;
private int scopePrefixLength;
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. There may also be issues related to Java's
* handling of mapped addresses
*
* @param sourcePrefixLength The length of the netmask pertaining to the query. In replies, it
* mirrors the same value as in the requests.
* @param scopePrefixLength 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 sourcePrefixLength, int scopePrefixLength, InetAddress address) {
super(EDNSOption.Code.CLIENT_SUBNET);
this.family = Address.familyOf(address);
this.sourcePrefixLength = checkMaskLength("source netmask", this.family, sourcePrefixLength);
this.scopePrefixLength = checkMaskLength("scope netmask", this.family, scopePrefixLength);
this.address = Address.truncate(address, sourcePrefixLength);
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 sourcePrefixLength 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 sourcePrefixLength, InetAddress address) {
this(sourcePrefixLength, 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 getSourcePrefixLength() {
return sourcePrefixLength;
}
/** Returns the scope netmask. */
public int getScopePrefixLength() {
return scopePrefixLength;
}
/** Returns the IP address of the client. */
public InetAddress getAddress() {
return address;
}
@Override
void optionFromWire(DNSInput in) throws WireParseException {
family = in.readU16();
if (family != Address.IPv4 && family != Address.IPv6) {
throw new WireParseException("unknown address family");
}
sourcePrefixLength = in.readU8();
if (sourcePrefixLength > Address.addressLength(family) * 8) {
throw new WireParseException("invalid source netmask");
}
scopePrefixLength = in.readU8();
if (scopePrefixLength > Address.addressLength(family) * 8) {
throw new WireParseException("invalid scope netmask");
}
// Read the truncated address
byte[] addr = in.readByteArray();
if (addr.length != (sourcePrefixLength + 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, sourcePrefixLength);
if (!tmp.equals(address)) {
throw new WireParseException("invalid padding");
}
}
@Override
void optionToWire(DNSOutput out) {
out.writeU16(family);
out.writeU8(sourcePrefixLength);
out.writeU8(scopePrefixLength);
out.writeByteArray(address.getAddress(), 0, (sourcePrefixLength + 7) / 8);
}
@Override
String optionToString() {
StringBuilder sb = new StringBuilder();
sb.append(address.getHostAddress());
sb.append("/");
sb.append(sourcePrefixLength);
sb.append(", scope netmask ");
sb.append(scopePrefixLength);
return sb.toString();
}
}