blob: 6dee0c496bbf0b951a3d9da822c90716a728a086 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.net.module.util.netlink;
import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_LEN;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.RdnssOption;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.StringJoiner;
/**
* The Recursive DNS Server Option. RFC 8106.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type | Length | Reserved |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Lifetime |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | |
* : Addresses of IPv6 Recursive DNS Servers :
* | |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
public class StructNdOptRdnss extends NdOption {
private static final String TAG = StructNdOptRdnss.class.getSimpleName();
public static final int TYPE = 25;
// Length in 8-byte units, only if one IPv6 address included.
public static final byte MIN_OPTION_LEN = 3;
public final RdnssOption header;
@NonNull
public final Inet6Address[] servers;
public StructNdOptRdnss(@NonNull final Inet6Address[] servers, long lifetime) {
super((byte) TYPE, servers.length * 2 + 1);
Objects.requireNonNull(servers, "Recursive DNS Servers address array must not be null");
if (servers.length == 0) {
throw new IllegalArgumentException("DNS server address array must not be empty");
}
this.header = new RdnssOption((byte) TYPE, (byte) (servers.length * 2 + 1),
(short) 0 /* reserved */, lifetime);
this.servers = servers.clone();
}
/**
* Parses an RDNSS option from a {@link ByteBuffer}.
*
* @param buf The buffer from which to parse the option. The buffer's byte order must be
* {@link java.nio.ByteOrder#BIG_ENDIAN}.
* @return the parsed option, or {@code null} if the option could not be parsed successfully.
*/
public static StructNdOptRdnss parse(@NonNull ByteBuffer buf) {
if (buf == null || buf.remaining() < MIN_OPTION_LEN * 8) return null;
try {
final RdnssOption header = Struct.parse(RdnssOption.class, buf);
if (header.type != TYPE) {
throw new IllegalArgumentException("Invalid type " + header.type);
}
if (header.length < MIN_OPTION_LEN || (header.length % 2 == 0)) {
throw new IllegalArgumentException("Invalid length " + header.length);
}
final int numOfDnses = (header.length - 1) / 2;
final Inet6Address[] servers = new Inet6Address[numOfDnses];
for (int i = 0; i < numOfDnses; i++) {
byte[] rawAddress = new byte[IPV6_ADDR_LEN];
buf.get(rawAddress);
servers[i] = (Inet6Address) InetAddress.getByAddress(rawAddress);
}
return new StructNdOptRdnss(servers, header.lifetime);
} catch (IllegalArgumentException | BufferUnderflowException | UnknownHostException e) {
// Not great, but better than throwing an exception that might crash the caller.
// Convention in this package is that null indicates that the option was truncated
// or malformed, so callers must already handle it.
Log.d(TAG, "Invalid RDNSS option: " + e);
return null;
}
}
protected void writeToByteBuffer(ByteBuffer buf) {
header.writeToByteBuffer(buf);
for (int i = 0; i < servers.length; i++) {
buf.put(servers[i].getAddress());
}
}
/** Outputs the wire format of the option to a new big-endian ByteBuffer. */
public ByteBuffer toByteBuffer() {
final ByteBuffer buf = ByteBuffer.allocate(Struct.getSize(RdnssOption.class)
+ servers.length * IPV6_ADDR_LEN);
writeToByteBuffer(buf);
buf.flip();
return buf;
}
@Override
@NonNull
public String toString() {
final StringJoiner sj = new StringJoiner(",", "[", "]");
for (int i = 0; i < servers.length; i++) {
sj.add(servers[i].getHostAddress());
}
return String.format("NdOptRdnss(%s,servers:%s)", header.toString(), sj.toString());
}
}