Added JNA network utility, added getting DNS nameservers via JNA

connection_type_change
Robinson 2021-04-06 01:28:09 +02:00
parent 546ed90ec2
commit d33cee3817
13 changed files with 806 additions and 13 deletions

View File

@ -1,33 +1,42 @@
package dorkbox.netUtil
import com.sun.jna.Memory
import com.sun.jna.Pointer.*
import com.sun.jna.platform.win32.WinError
import dorkbox.executor.Executor
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException
import dorkbox.netUtil.jna.windows.IPHlpAPI
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_ADDRESSES_LH
import dorkbox.netUtil.jna.windows.structs.IP_ADAPTER_DNS_SERVER_ADDRESS_XP
import java.io.*
import java.net.*
import java.nio.file.Files
import java.nio.file.Paths
import java.util.*
import javax.naming.Context
import javax.naming.NamingException
import javax.naming.directory.DirContext
import javax.naming.directory.InitialDirContext
/**
*
*/
object Dns {
/**
* @throws IOException if the DNS resolve.conf file cannot be read
*/
fun setDNSServers(dnsServersString: String) {
if (Common.OS_LINUX) {
val dnsServers = dnsServersString.split(",");
val dnsFile = File("/etc/resolvconf/resolv.conf.d/head");
val dnsServers = dnsServersString.split(",")
val dnsFile = File("/etc/resolvconf/resolv.conf.d/head")
if (!dnsFile.canRead()) {
throw IOException("Unable to initialize dns server file. Something is SERIOUSLY wrong");
throw IOException("Unable to initialize dns server file. Something is SERIOUSLY wrong")
}
BufferedWriter(FileWriter(dnsFile)).use {
it.write("# File location: /etc/resolvconf/resolv.conf.d/head\n");
it.write("# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)\n");
it.write("# File location: /etc/resolvconf/resolv.conf.d/head\n")
it.write("# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)\n")
dnsServers.forEach { dns ->
it.write("nameserver $dns\n");
it.write("nameserver $dns\n")
}
it.flush();
@ -38,4 +47,331 @@ object Dns {
throw RuntimeException("NOT IMPL.")
}
}
/** Returns all located name servers, which may be empty. */
val defaultNameServers: List<InetSocketAddress> by lazy {
val nameServers = getUnsortedDefaultNameServers()
if (IPv6.isPreferred) {
// prefer IPv6: return IPv6 first, then IPv4 (each in the order added)
nameServers.filter { it.address is Inet6Address } + nameServers.filter { it.address is Inet4Address }
} else if (IPv4.isPreferred) {
// skip IPv6 addresses
nameServers.filter { it.address is Inet4Address }
}
// neither is specified, return in the order added
nameServers
}
// largely from:
// https://github.com/dnsjava/dnsjava/blob/fb4889ee7a73f391f43bf6dc78b019d87ae15f15/src/main/java/org/xbill/DNS/config/BaseResolverConfigProvider.java#L22
private fun getUnsortedDefaultNameServers() : List<InetSocketAddress> {
val defaultNameServers = mutableListOf<InetSocketAddress>()
// Using jndi-dns to obtain the default name servers.
//
// See:
// - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
// - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
// Using jndi-dns to obtain the default name servers.
//
// See:
// - http://docs.oracle.com/javase/8/docs/technotes/guides/jndi/jndi-dns.html
// - http://mail.openjdk.java.net/pipermail/net-dev/2017-March/010695.html
val env = Hashtable<String, String>()
env[Context.INITIAL_CONTEXT_FACTORY] = "com.sun.jndi.dns.DnsContextFactory"
env["java.naming.provider.url"] = "dns://"
try {
val ctx: DirContext = InitialDirContext(env)
val dnsUrls = ctx.environment["java.naming.provider.url"] as String?
val servers = dnsUrls!!.split(" ".toRegex()).toTypedArray()
for (server in servers) {
try {
defaultNameServers.add(Common.socketAddress(URI(server).host, 53))
} catch (e: URISyntaxException) {
Common.logger.debug("Skipping a malformed nameserver URI: {}", server, e)
}
}
} catch (ignore: NamingException) {
// Will also try JNA/etc if this fails.
}
if (defaultNameServers.isNotEmpty()) {
return defaultNameServers
}
if (Common.OS_WINDOWS) {
// have to use JNA to access the WINDOWS resolver info
// modified from: https://github.com/dnsjava/dnsjava/blob/master/src/main/java/org/xbill/DNS/config/WindowsResolverConfigProvider.java
// The recommended method of calling the GetAdaptersAddresses function is to pre-allocate a 15KB working buffer
var buffer: Memory? = Memory(15 * 1024)
val size: com.sun.jna.ptr.IntByReference = com.sun.jna.ptr.IntByReference(0)
val flags: Int = IPHlpAPI.GAA_FLAG_SKIP_UNICAST or
IPHlpAPI.GAA_FLAG_SKIP_ANYCAST or
IPHlpAPI.GAA_FLAG_SKIP_MULTICAST or
IPHlpAPI.GAA_FLAG_SKIP_FRIENDLY_NAME
var error: Int = IPHlpAPI.GetAdaptersAddresses(
IPHlpAPI.AF_UNSPEC,
flags,
NULL,
buffer,
size
)
if (error == WinError.ERROR_BUFFER_OVERFLOW) {
buffer = Memory(size.value.toLong())
error = IPHlpAPI.GetAdaptersAddresses(
IPHlpAPI.AF_UNSPEC,
flags,
NULL,
buffer,
size
)
}
if (error == WinError.ERROR_SUCCESS) {
var result: IP_ADAPTER_ADDRESSES_LH? = IP_ADAPTER_ADDRESSES_LH(buffer)
while (result != null) {
// only interfaces with IfOperStatusUp
if (result.OperStatus == 1) {
var dns: IP_ADAPTER_DNS_SERVER_ADDRESS_XP? = result.FirstDnsServerAddress
while (dns != null) {
var address: InetAddress
try {
address = dns.Address.toAddress()
if (address is Inet4Address || !address.isSiteLocalAddress) {
defaultNameServers.add(InetSocketAddress(address, 53))
} else {
Common.logger.debug("Skipped site-local IPv6 server address {} on adapter index {}", address, result.IfIndex)
}
} catch (e: UnknownHostException) {
Common.logger.warn("Invalid nameserver address on adapter index {}", result.IfIndex, e)
}
dns = dns.Next
}
}
result = result.Next
}
}
} else {
// try resolve.conf
// first try the default unix config path
var tryParse = tryParseResolvConfNameservers("/etc/resolv.conf")
if (!tryParse.first) {
// then fallback to netware
tryParse = tryParseResolvConfNameservers("sys:/etc/resolv.cfg")
}
defaultNameServers.addAll(tryParse.second)
}
return defaultNameServers
}
private fun tryParseResolvConfNameservers(path: String): Pair<Boolean, MutableList<InetSocketAddress>> {
val p = Paths.get(path)
if (Files.exists(p)) {
try {
Files.newInputStream(p).use { `in` ->
return Pair(true, parseResolvConfNameservers(`in`))
}
} catch (e: IOException) {
// ignore
}
}
return Pair(false, mutableListOf())
}
private fun parseResolvConfNameservers(`in`: InputStream): MutableList<InetSocketAddress> {
val defaultNameServers = mutableListOf<InetSocketAddress>()
InputStreamReader(`in`).use { isr ->
BufferedReader(isr).use { br ->
var line: String?
while (br.readLine().also { line = it } != null) {
val st = StringTokenizer(line)
if (!st.hasMoreTokens()) {
continue
}
when (st.nextToken()) {
"nameserver" -> defaultNameServers.add(InetSocketAddress(st.nextToken(), 53))
}
}
}
}
return defaultNameServers
}
/**
* Gets the threshold for the number of dots which must appear in a name before it is considered
* absolute. The default is 1.
*/
val numberDots: Int by lazy {
if (Common.OS_WINDOWS) {
1
} else {
// first try the default unix config path
var tryParse = tryParseResolvConfNDots("/etc/resolv.conf")
if (!tryParse.first) {
// then fallback to netware
tryParse = tryParseResolvConfNDots("sys:/etc/resolv.cfg")
}
tryParse.second
}
}
private fun tryParseResolvConfNDots(path: String): Pair<Boolean, Int> {
val p = Paths.get(path)
if (Files.exists(p)) {
try {
Files.newInputStream(p).use { `in` ->
return Pair(true, getNdots(`in`))
}
} catch (e: IOException) {
// ignore
}
}
return Pair(false, 1)
}
// @kotlin.jvm.Throws(IOException::class)
// private fun parseResolvConf(`in`: InputStream) {
// InputStreamReader(`in`).use { isr ->
// BufferedReader(isr).use { br ->
//
// var line: String?
// loop@ while (br.readLine().also { line = it } != null) {
// val st = StringTokenizer(line)
// if (!st.hasMoreTokens()) {
// continue
// }
// when (st.nextToken()) {
// "domain" -> {
// // man resolv.conf:
// // The domain and search keywords are mutually exclusive. If more than one instance of
// // these keywords is present, the last instance wins.
// searchlist.clear()
// if (!st.hasMoreTokens()) {
// continue@loop
// }
// addSearchPath(st.nextToken())
// }
// "search" -> {
// // man resolv.conf:
// // The domain and search keywords are mutually exclusive. If more than one instance of
// // these keywords is present, the last instance wins.
// searchlist.clear()
// while (st.hasMoreTokens()) {
// addSearchPath(st.nextToken())
// }
// }
// }
// }
// }
// }
//
// // man resolv.conf:
// // The search keyword of a system's resolv.conf file can be overridden on a per-process basis by
// // setting the environment variable LOCALDOMAIN to a space-separated list of search domains.
// val localdomain = System.getenv("LOCALDOMAIN")
// if (localdomain != null && !localdomain.isEmpty()) {
// searchlist.clear()
// parseSearchPathList(localdomain, " ")
// }
// }
// @kotlin.jvm.Throws(IOException::class)
private fun getNdots(`in`: InputStream): Int {
var ndots = 1
InputStreamReader(`in`).use { isr ->
BufferedReader(isr).use { br ->
var line: String?
loop@ while (br.readLine().also { line = it } != null) {
val st = StringTokenizer(line)
if (!st.hasMoreTokens()) {
continue
}
when (st.nextToken()) {
"domain" -> {
// man resolv.conf:
// The domain and search keywords are mutually exclusive. If more than one instance of
// these keywords is present, the last instance wins.
if (!st.hasMoreTokens()) {
continue@loop
}
}
"search" -> {
// man resolv.conf:
// The domain and search keywords are mutually exclusive. If more than one instance of
// these keywords is present, the last instance wins.
while (st.hasMoreTokens()) {
val token = st.nextToken()
if (token.startsWith("ndots:")) {
ndots = parseNdots(token.substring(6))
}
}
}
"options" -> while (st.hasMoreTokens()) {
val token = st.nextToken()
if (token.startsWith("ndots:")) {
ndots = parseNdots(token.substring(6))
}
}
}
}
}
}
// man resolv.conf:
// The options keyword of a system's resolv.conf file can be amended on a per-process basis by
// setting the environment variable RES_OPTIONS to a space-separated list of resolver options as
// explained above under options.
val resOptions = System.getenv("RES_OPTIONS")
if (resOptions != null && !resOptions.isEmpty()) {
val st = StringTokenizer(resOptions, " ")
while (st.hasMoreTokens()) {
val token = st.nextToken()
if (token.startsWith("ndots:")) {
ndots = parseNdots(token.substring(6))
}
}
}
return ndots
}
private fun parseNdots(token: String): Int {
if (token.isNotEmpty()) {
try {
var ndots = token.toInt()
if (ndots >= 0) {
if (ndots > 15) {
// man resolv.conf:
// The value for this option is silently capped to 15
ndots = 15
}
return ndots
}
} catch (e: NumberFormatException) {
// ignore
}
}
return 1
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2016 dorkbox, llc
*
* 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 dorkbox.netUtil.jna
import com.sun.jna.Library
import com.sun.jna.Native
import com.sun.jna.NativeLibrary
import com.sun.jna.win32.W32APIOptions
import dorkbox.netUtil.Common
import java.util.*
/**
* Helper method to get the library info from JNA when registering via direct map
*/
object JnaHelper {
@kotlin.jvm.Throws(IllegalArgumentException::class)
fun register(libraryName: String, clazz: Class<*>): NativeLibrary {
val options: MutableMap<String, Any?> = HashMap()
options[Library.OPTION_CLASSLOADER] = clazz.classLoader
if (Common.OS_WINDOWS) {
val entries: Set<Map.Entry<String, Any?>> = W32APIOptions.DEFAULT_OPTIONS.entries
for ((key, value) in entries) {
options[key] = value
}
}
val library = NativeLibrary.getInstance(libraryName, options)
?: throw IllegalArgumentException("$libraryName doesn't exist or cannot be loaded.")
Native.register(clazz, library)
return library
}
}

View File

@ -0,0 +1,37 @@
package dorkbox.netUtil.jna.windows
import com.sun.jna.Pointer
import com.sun.jna.ptr.IntByReference
import dorkbox.netUtil.jna.JnaHelper.register
/**
* https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/
*/
object IPHlpAPI {
init {
register("IPHlpAPI", IPHlpAPI::class.java)
}
const val AF_UNSPEC = 0
const val AF_INET = 2
const val AF_INET6 = 23
const val GAA_FLAG_SKIP_UNICAST = 0x0001
const val GAA_FLAG_SKIP_ANYCAST = 0x0002
const val GAA_FLAG_SKIP_MULTICAST = 0x0004
const val GAA_FLAG_SKIP_DNS_SERVER = 0x0008
const val GAA_FLAG_INCLUDE_PREFIX = 0x0010
const val GAA_FLAG_SKIP_FRIENDLY_NAME = 0x0020
const val GAA_FLAG_INCLUDE_WINS_INFO = 0x0040
const val GAA_FLAG_INCLUDE_GATEWAYS = 0x0080
const val GAA_FLAG_INCLUDE_ALL_INTERFACES = 0x0100
const val GAA_FLAG_INCLUDE_ALL_COMPARTMENTS = 0x0200
const val GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER = 0x0400
external fun GetAdaptersAddresses(
family: Int,
flags: Int,
reserved: Pointer?,
adapterAddresses: Pointer?,
sizePointer: IntByReference?
): Int
}

View File

@ -0,0 +1,119 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid;
public
class IP_ADAPTER_ADDRESSES_LH extends Structure {
public static
class ByReference extends IP_ADAPTER_ADDRESSES_LH implements Structure.ByReference {}
public int Length;
public int IfIndex;
public IP_ADAPTER_ADDRESSES_LH.ByReference Next;
public String AdapterName;
public IP_ADAPTER_UNICAST_ADDRESS_LH.ByReference FirstUnicastAddress;
public IP_ADAPTER_ANYCAST_ADDRESS_XP.ByReference FirstAnycastAddress;
public IP_ADAPTER_MULTICAST_ADDRESS_XP.ByReference FirstMulticastAddress;
public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference FirstDnsServerAddress;
public WString DnsSuffix;
public WString Description;
public WString FriendlyName;
public byte[] PhysicalAddress = new byte[8];
public int PhysicalAddressLength;
public int Flags;
public int Mtu;
public int IfType;
public int OperStatus;
public int Ipv6IfIndex;
public int[] ZoneIndices = new int[16];
public Pointer FirstPrefix;
public long TransmitLinkSpeed;
public long ReceiveLinkSpeed;
public Pointer FirstWinsServerAddress;
public Pointer FirstGatewayAddress;
public int Ipv4Metric;
public int Ipv6Metric;
public LUID Luid;
public SOCKET_ADDRESS Dhcpv4Server;
public int CompartmentId;
public Guid.GUID NetworkGuid;
public int ConnectionType;
public int TunnelType;
public SOCKET_ADDRESS Dhcpv6Server;
public byte[] Dhcpv6ClientDuid = new byte[130];
public int Dhcpv6ClientDuidLength;
public int Dhcpv6Iaid;
public IP_ADAPTER_DNS_SUFFIX.ByReference FirstDnsSuffix;
public
IP_ADAPTER_ADDRESSES_LH(Pointer p) {
super(p);
read();
}
public
IP_ADAPTER_ADDRESSES_LH() {}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Length",
"IfIndex",
"Next",
"AdapterName",
"FirstUnicastAddress",
"FirstAnycastAddress",
"FirstMulticastAddress",
"FirstDnsServerAddress",
"DnsSuffix",
"Description",
"FriendlyName",
"PhysicalAddress",
"PhysicalAddressLength",
"Flags",
"Mtu",
"IfType",
"OperStatus",
"Ipv6IfIndex",
"ZoneIndices",
"FirstPrefix",
"TransmitLinkSpeed",
"ReceiveLinkSpeed",
"FirstWinsServerAddress",
"FirstGatewayAddress",
"Ipv4Metric",
"Ipv6Metric",
"Luid",
"Dhcpv4Server",
"CompartmentId",
"NetworkGuid",
"ConnectionType",
"TunnelType",
"Dhcpv6Server",
"Dhcpv6ClientDuid",
"Dhcpv6ClientDuidLength",
"Dhcpv6Iaid",
"FirstDnsSuffix");
}
}

View File

@ -0,0 +1,25 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class IP_ADAPTER_ANYCAST_ADDRESS_XP extends Structure {
public static
class ByReference extends IP_ADAPTER_ANYCAST_ADDRESS_XP implements Structure.ByReference {}
public int Length;
public int Reserved;
public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next;
public SOCKET_ADDRESS Address;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Length", "Reserved", "Next", "Address");
}
}

View File

@ -0,0 +1,25 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class IP_ADAPTER_DNS_SERVER_ADDRESS_XP extends Structure {
public static
class ByReference extends IP_ADAPTER_DNS_SERVER_ADDRESS_XP implements Structure.ByReference {}
public int Length;
public int Reserved;
public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next;
public SOCKET_ADDRESS Address;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Length", "Reserved", "Next", "Address");
}
}

View File

@ -0,0 +1,22 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class IP_ADAPTER_DNS_SUFFIX extends Structure {
public static
class ByReference extends IP_ADAPTER_DNS_SUFFIX implements Structure.ByReference {}
public IP_ADAPTER_DNS_SUFFIX.ByReference Next;
public char[] _String = new char[256];
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Next", "_String");
}
}

View File

@ -0,0 +1,25 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class IP_ADAPTER_MULTICAST_ADDRESS_XP extends Structure {
public static
class ByReference extends IP_ADAPTER_MULTICAST_ADDRESS_XP implements Structure.ByReference {}
public int Length;
public int Reserved;
public IP_ADAPTER_DNS_SERVER_ADDRESS_XP.ByReference Next;
public SOCKET_ADDRESS Address;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Length", "Reserved", "Next", "Address");
}
}

View File

@ -0,0 +1,44 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class IP_ADAPTER_UNICAST_ADDRESS_LH extends Structure {
public static
class ByReference extends IP_ADAPTER_UNICAST_ADDRESS_LH implements Structure.ByReference {}
public int Length;
public int IfIndex;
public IP_ADAPTER_UNICAST_ADDRESS_LH.ByReference Next;
public SOCKET_ADDRESS Address;
public int PrefixOrigin;
public int SuffixOrigin;
public int DadState;
public int ValidLifetime;
public int PreferredLifetime;
public int LeaseLifetime;
public byte OnLinkPrefixLength;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("Length",
"IfIndex",
"Next",
"Address",
"PrefixOrigin",
"SuffixOrigin",
"DadState",
"ValidLifetime",
"PreferredLifetime",
"LeaseLifetime",
"OnLinkPrefixLength");
}
}

View File

@ -0,0 +1,18 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Structure;
public
class LUID extends Structure {
public int LowPart;
public int HighPart;
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("LowPart", "HighPart");
}
}

View File

@ -0,0 +1,27 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public
class SOCKADDR_IN extends Structure {
public short sin_family;
public short sin_port;
public byte[] sin_addr = new byte[4];
public byte[] sin_zero = new byte[8];
public
SOCKADDR_IN(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("sin_family", "sin_port", "sin_addr", "sin_zero");
}
}

View File

@ -0,0 +1,29 @@
package dorkbox.netUtil.jna.windows.structs;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public
class SOCKADDR_IN6 extends Structure {
public short sin6_family;
public short sin6_port;
public int sin6_flowinfo;
public byte[] sin6_addr = new byte[16];
public int sin6_scope_id;
public
SOCKADDR_IN6(Pointer p) {
super(p);
read();
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("sin6_family", "sin6_port", "sin6_flowinfo", "sin6_addr", "sin6_scope_id");
}
}

View File

@ -0,0 +1,39 @@
package dorkbox.netUtil.jna.windows.structs;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public
class SOCKET_ADDRESS extends Structure {
public static final int AF_INET = 2;
public static final int AF_INET6 = 23;
public Pointer lpSockaddr;
public int iSockaddrLength;
public
InetAddress toAddress() throws UnknownHostException {
switch (((int) lpSockaddr.getShort(0))) {
case AF_INET:
SOCKADDR_IN in4 = new SOCKADDR_IN(lpSockaddr);
return InetAddress.getByAddress(in4.sin_addr);
case AF_INET6:
SOCKADDR_IN6 in6 = new SOCKADDR_IN6(lpSockaddr);
return Inet6Address.getByAddress("", in6.sin6_addr, in6.sin6_scope_id);
}
return null;
}
@Override
protected
List<String> getFieldOrder() {
return Arrays.asList("lpSockaddr", "iSockaddrLength");
}
}