diff options
author | Matthew Newhook <matthew@zeroc.com> | 2006-06-05 15:11:48 +0000 |
---|---|---|
committer | Matthew Newhook <matthew@zeroc.com> | 2006-06-05 15:11:48 +0000 |
commit | c7830493b1a04964c872095e7b924d86c08b2b52 (patch) | |
tree | 106dd9f15a637962b24dd870e023f7b3dd236f61 /java/ssl/jdk1.4/IceSSL | |
parent | Add addressFilter to build (diff) | |
download | ice-c7830493b1a04964c872095e7b924d86c08b2b52.tar.bz2 ice-c7830493b1a04964c872095e7b924d86c08b2b52.tar.xz ice-c7830493b1a04964c872095e7b924d86c08b2b52.zip |
added support for TrustOnly.
Diffstat (limited to 'java/ssl/jdk1.4/IceSSL')
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/AcceptorI.java | 8 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/ConnectionInfo.java | 12 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/ConnectorI.java | 2 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/EndpointI.java | 4 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/Instance.java | 34 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/RFC2253.java | 416 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/TrustManager.java | 252 | ||||
-rw-r--r-- | java/ssl/jdk1.4/IceSSL/Util.java | 4 |
8 files changed, 714 insertions, 18 deletions
diff --git a/java/ssl/jdk1.4/IceSSL/AcceptorI.java b/java/ssl/jdk1.4/IceSSL/AcceptorI.java index 48f9ae4b57c..d781bcd6a29 100644 --- a/java/ssl/jdk1.4/IceSSL/AcceptorI.java +++ b/java/ssl/jdk1.4/IceSSL/AcceptorI.java @@ -131,8 +131,8 @@ class AcceptorI implements IceInternal.Acceptor } } - connInfo = Util.populateConnectionInfo(fd); - _instance.verifyPeer(connInfo, fd, "", true); + connInfo = Util.populateConnectionInfo(fd, _adapterName, true); + _instance.verifyPeer(connInfo, fd, _adapterName, true); } catch(java.net.SocketTimeoutException ex) { @@ -271,9 +271,10 @@ class AcceptorI implements IceInternal.Acceptor return _addr.getPort(); } - AcceptorI(Instance instance, String host, int port) + AcceptorI(Instance instance, String adapterName, String host, int port) { _instance = instance; + _adapterName = adapterName; _logger = instance.communicator().getLogger(); _backlog = 0; @@ -447,6 +448,7 @@ class AcceptorI implements IceInternal.Acceptor } private Instance _instance; + private String _adapterName; private Ice.Logger _logger; private javax.net.ssl.SSLServerSocket _fd; private int _backlog; diff --git a/java/ssl/jdk1.4/IceSSL/ConnectionInfo.java b/java/ssl/jdk1.4/IceSSL/ConnectionInfo.java index 8e58d7edbaf..c087c838896 100644 --- a/java/ssl/jdk1.4/IceSSL/ConnectionInfo.java +++ b/java/ssl/jdk1.4/IceSSL/ConnectionInfo.java @@ -37,4 +37,16 @@ public class ConnectionInfo // The remote TCP/IP host & port. // public java.net.InetSocketAddress remoteAddr; + + // + // If the connection is incoming this bool is true, false + // otherwise. + // + boolean incoming; + + // + // The name of the object adapter that hosts this endpoint, if + // any. + // + String adapterName; } diff --git a/java/ssl/jdk1.4/IceSSL/ConnectorI.java b/java/ssl/jdk1.4/IceSSL/ConnectorI.java index b42cb6ce519..9e0b8dcefc1 100644 --- a/java/ssl/jdk1.4/IceSSL/ConnectorI.java +++ b/java/ssl/jdk1.4/IceSSL/ConnectorI.java @@ -131,7 +131,7 @@ final class ConnectorI implements IceInternal.Connector } } - connInfo = Util.populateConnectionInfo(fd); + connInfo = Util.populateConnectionInfo(fd, "", false); _instance.verifyPeer(connInfo, fd, _host, false); } catch(java.net.ConnectException ex) diff --git a/java/ssl/jdk1.4/IceSSL/EndpointI.java b/java/ssl/jdk1.4/IceSSL/EndpointI.java index debeb4482ff..9b09790bc81 100644 --- a/java/ssl/jdk1.4/IceSSL/EndpointI.java +++ b/java/ssl/jdk1.4/IceSSL/EndpointI.java @@ -353,9 +353,9 @@ final class EndpointI extends IceInternal.EndpointI // assigned. // public IceInternal.Acceptor - acceptor(IceInternal.EndpointIHolder endpoint) + acceptor(IceInternal.EndpointIHolder endpoint, String adapterName) { - AcceptorI p = new AcceptorI(_instance, _host, _port); + AcceptorI p = new AcceptorI(_instance, adapterName, _host, _port); endpoint.value = new EndpointI(_instance, _host, p.effectivePort(), _timeout, _connectionId, _compress, _publish); return p; diff --git a/java/ssl/jdk1.4/IceSSL/Instance.java b/java/ssl/jdk1.4/IceSSL/Instance.java index 0aaf8c22b31..b1794c64765 100644 --- a/java/ssl/jdk1.4/IceSSL/Instance.java +++ b/java/ssl/jdk1.4/IceSSL/Instance.java @@ -18,6 +18,7 @@ class Instance _securityTraceLevel = communicator.getProperties().getPropertyAsIntWithDefault("IceSSL.Trace.Security", 0); _securityTraceCategory = "Security"; _initialized = false; + _trustManager = new TrustManager(communicator); // // Register the endpoint factory. We have to do this now, rather than @@ -581,22 +582,32 @@ class Instance } } - if(_verifier != null) + if(!_trustManager.verify(info)) { - if(!_verifier.verify(info)) + String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by trust manager\n" + + IceInternal.Network.fdToString(fd); + if(_securityTraceLevel >= 1) { - String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier\n" + - IceInternal.Network.fdToString(fd); + _logger.trace(_securityTraceCategory, msg); + } + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; + } - if(_securityTraceLevel > 0) - { - _logger.trace(_securityTraceCategory, msg); - } + if(_verifier != null && !_verifier.verify(info)) + { + String msg = (incoming ? "incoming" : "outgoing") + " connection rejected by certificate verifier\n" + + IceInternal.Network.fdToString(fd); - Ice.SecurityException ex = new Ice.SecurityException(); - ex.reason = msg; - throw ex; + if(_securityTraceLevel > 0) + { + _logger.trace(_securityTraceCategory, msg); } + + Ice.SecurityException ex = new Ice.SecurityException(); + ex.reason = msg; + throw ex; } } @@ -728,4 +739,5 @@ class Instance private String[] _protocols; private boolean _checkCertName; private CertificateVerifier _verifier; + private TrustManager _trustManager; } diff --git a/java/ssl/jdk1.4/IceSSL/RFC2253.java b/java/ssl/jdk1.4/IceSSL/RFC2253.java new file mode 100644 index 00000000000..0b66a93aaa3 --- /dev/null +++ b/java/ssl/jdk1.4/IceSSL/RFC2253.java @@ -0,0 +1,416 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceSSL; + +// +// See RFC 2253 and RFC 1779. +// +class RFC2253 +{ + static class ParseException extends Ice.LocalException + { + public ParseException() + { + } + + public ParseException(String reason) + { + this.reason = reason; + } + + public String + ice_name() + { + return "RFC2253::ParseException"; + } + + public String reason; + } + + static class RDNPair + { + String key; + String value; + }; + + static private class ParseState + { + String data; + int pos; + }; + + public static java.util.List + parse(String data) + throws ParseException + { + java.util.List results = new java.util.LinkedList(); + java.util.List current = new java.util.LinkedList(); + ParseState state = new ParseState(); + state.data = data; + state.pos = 0; + while(state.pos < state.data.length()) + { + current.add(parseNameComponent(state)); + eatWhite(state); + if(state.pos < state.data.length() && state.data.charAt(state.pos) == ',') + { + ++state.pos; + } + else if(state.pos < state.data.length() && state.data.charAt(state.pos) == ';') + { + ++state.pos; + results.add(current); + current = new java.util.LinkedList(); + } + else if(state.pos < state.data.length()) + { + throw new ParseException("expected ',' or ';' at `" + state.data.substring(state.pos) + "'"); + } + } + if(!current.isEmpty()) + { + results.add(current); + } + + return results; + } + + public static java.util.List + parseStrict(String data) + throws ParseException + { + java.util.List results = new java.util.LinkedList(); + ParseState state = new ParseState(); + state.data = data; + state.pos = 0; + while(state.pos < state.data.length()) + { + results.add(parseNameComponent(state)); + eatWhite(state); + if(state.pos < state.data.length() && + (state.data.charAt(state.pos) == ',' || state.data.charAt(state.pos) == ';')) + { + ++state.pos; + } + else if(state.pos < state.data.length()) + { + throw new ParseException("expected ',' or ';' at `" + state.data.substring(state.pos) + "'"); + } + } + return results; + } + + private static RDNPair + parseNameComponent(ParseState state) + throws ParseException + { + RDNPair result = parseAttributeTypeAndValue(state); + while(state.pos < state.data.length()) + { + eatWhite(state); + if(state.pos < state.data.length() && state.data.charAt(state.pos) == '+') + { + ++state.pos; + } + else + { + break; + } + RDNPair p = parseAttributeTypeAndValue(state); + result.value += "+"; + result.value += p.key; + result.value += '='; + result.value += p.value; + } + return result; + } + + private static RDNPair + parseAttributeTypeAndValue(ParseState state) + throws ParseException + { + RDNPair p = new RDNPair(); + p.key = parseAttributeType(state); + eatWhite(state); + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute type/value pair (unexpected end of state.data)"); + } + if(state.data.charAt(state.pos) != '=') + { + throw new ParseException("invalid attribute type/value pair (missing =)"); + } + ++state.pos; + p.value = parseAttributeValue(state); + return p; + } + + private static String + parseAttributeType(ParseState state) + throws ParseException + { + eatWhite(state); + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute type (expected end of state.data)"); + } + + String result = new String(); + + // + // RFC 1779. + // <key> ::= 1*( <keychar> ) | "OID." <oid> | "oid." <oid> + // <oid> ::= <digitString> | <digitstring> "." <oid> + // RFC 2253: + // attributeType = (ALPHA 1*keychar) | oid + // keychar = ALPHA | DIGIT | "-" + // oid = 1*DIGIT *("." 1*DIGIT) + // + // In section 4 of RFC 2253 the document says: + // Implementations MUST allow an oid in the attribute type to be + // prefixed by one of the character Strings "oid." or "OID.". + // + // Here we must also check for "oid." and "OID." before parsing + // according to the ALPHA KEYCHAR* rule. + // + // First the OID case. + // + if(Character.isDigit(state.data.charAt(state.pos)) || + (state.data.length() - state.pos >= 4 && (state.data.substring(state.pos, state.pos + 4) == "oid." || + state.data.substring(state.pos, state.pos + 4) == "OID."))) + { + if(!Character.isDigit(state.data.charAt(state.pos))) + { + result += state.data.substring(state.pos, state.pos + 4); + state.pos += 4; + } + + while(true) + { + // 1*DIGIT + while(state.pos < state.data.length() && Character.isDigit(state.data.charAt(state.pos))) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + // "." 1*DIGIT + if(state.pos < state.data.length() && state.data.charAt(state.pos) == '.') + { + result += state.data.charAt(state.pos); + ++state.pos; + // 1*DIGIT must follow "." + if(state.pos < state.data.length() && !Character.isDigit(state.data.charAt(state.pos))) + { + throw new ParseException("invalid attribute type (expected end of state.data)"); + } + } + else + { + break; + } + } + } + else if(Character.isUpperCase(state.data.charAt(state.pos)) || Character.isLowerCase(state.data.charAt(state.pos))) + { + // + // The grammar is wrong in this case. It should be ALPHA + // KEYCHAR* otherwise it will not accept "O" as a valid + // attribute type. + // + result += state.data.charAt(state.pos); + ++state.pos; + // 1* KEYCHAR + while(state.pos < state.data.length() && + (Character.isDigit(state.data.charAt(state.pos)) || + Character.isUpperCase(state.data.charAt(state.pos)) || + Character.isLowerCase(state.data.charAt(state.pos)) || + state.data.charAt(state.pos) == '-')) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + } + else + { + throw new ParseException("invalid attribute type"); + } + return result; + } + + private static String + parseAttributeValue(ParseState state) + throws ParseException + { + eatWhite(state); + String result = new String(); + if(state.pos >= state.data.length()) + { + return result; + } + + // + // RFC 2253 + // # hexString + // + if(state.data.charAt(state.pos) == '#') + { + result += state.data.charAt(state.pos); + ++state.pos; + while(true) + { + String h = parseHexPair(state, true); + if(h.length() == 0) + { + break; + } + result += h; + } + } + // + // RFC 2253 + // QUOTATION *( quotechar | pair ) QUOTATION ; only from v2 + // quotechar = <any character except "\" or QUOTATION > + // + else if(state.data.charAt(state.pos) == '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + while(true) + { + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid attribute value (unexpected end of state.data)"); + } + // final terminating " + if(state.data.charAt(state.pos) == '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + break; + } + // any character except '\' + else if(state.data.charAt(state.pos) != '\\') + { + result += state.data.charAt(state.pos); + ++state.pos; + } + // pair '\' + else + { + result += parsePair(state); + } + } + } + // + // RFC 2253 + // * (Stringchar | pair) + // Stringchar = <any character except one of special, "\" or QUOTATION > + // + else + { + while(state.pos < state.data.length()) + { + if(state.data.charAt(state.pos) == '\\') + { + result += parsePair(state); + } + else if(special.indexOf(state.data.charAt(state.pos)) == -1 && state.data.charAt(state.pos) != '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + } + else + { + break; + } + } + } + return result; + } + + // + // RFC2253: + // pair = "\" ( special | "\" | QUOTATION | hexpair ) + // + private static String + parsePair(ParseState state) + throws ParseException + { + String result = new String(); + + assert(state.data.charAt(state.pos) == '\\'); + result += state.data.charAt(state.pos); + ++state.pos; + + if(state.pos >= state.data.length()) + { + throw new ParseException("invalid escape format (unexpected end of state.data)"); + } + + if(special.indexOf(state.data.charAt(state.pos)) != -1 || state.data.charAt(state.pos) != '\\' || + state.data.charAt(state.pos) != '"') + { + result += state.data.charAt(state.pos); + ++state.pos; + return result; + } + return parseHexPair(state, false); + } + + // + // RFC 2253 + // hexpair = hexchar hexchar + // + private static String + parseHexPair(ParseState state, boolean allowEmpty) + throws ParseException + { + String result = new String(); + if(state.pos < state.data.length() && hexvalid.indexOf(state.data.charAt(state.pos)) != -1) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + if(state.pos < state.data.length() && hexvalid.indexOf(state.data.charAt(state.pos)) != -1) + { + result += state.data.charAt(state.pos); + ++state.pos; + } + if(result.length() != 2) + { + if(allowEmpty && result.length() == 0) + { + return result; + } + throw new ParseException("invalid hex format"); + } + return result; + } + + // + // RFC 2253: + // + // Implementations MUST allow for space (' ' ASCII 32) characters to be + // present between name-component and ',', between attributeTypeAndValue + // and '+', between attributeType and '=', and between '=' and + // attributeValue. These space characters are ignored when parsing. + // + private static void + eatWhite(ParseState state) + { + while(state.pos < state.data.length() && state.data.charAt(state.pos) == ' ') + { + ++state.pos; + } + } + + private final static String special = ",=+<>#;"; + private final static String hexvalid = "0123456789abcdefABCDEF"; +} diff --git a/java/ssl/jdk1.4/IceSSL/TrustManager.java b/java/ssl/jdk1.4/IceSSL/TrustManager.java new file mode 100644 index 00000000000..5f601354112 --- /dev/null +++ b/java/ssl/jdk1.4/IceSSL/TrustManager.java @@ -0,0 +1,252 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package IceSSL; + +class TrustManager +{ + TrustManager(Ice.Communicator communicator) + { + assert communicator != null; + _communicator = communicator; + Ice.Properties properties = communicator.getProperties(); + _traceLevel = properties.getPropertyAsInt("IceSSL.Trace.Security"); + String key = null; + try + { + key = "IceSSL.TrustOnly"; + _all = parse(properties.getProperty(key)); + key = "IceSSL.TrustOnly.Client"; + _client = parse(properties.getProperty(key)); + key = "IceSSL.TrustOnly.Server"; + _allServer = parse(properties.getProperty(key)); + java.util.Map dict = properties.getPropertiesForPrefix("IceSSL.TrustOnly.Server."); + java.util.Iterator p = dict.entrySet().iterator(); + while(p.hasNext()) + { + java.util.Map.Entry entry = (java.util.Map.Entry)p.next(); + key = (String)entry.getKey(); + String name = key.substring("IceSSL.TrustOnly.Server.".length()); + _server.put(name, parse((String)entry.getValue())); + } + } + catch(RFC2253.ParseException e) + { + Ice.PluginInitializationException ex = new Ice.PluginInitializationException(); + ex.reason = "IceSSL: invalid property " + key + ":\n" + e.reason; + throw ex; + } + } + + boolean + verify(ConnectionInfo info) + { + java.util.List trustset = new java.util.LinkedList(); + if(!_all.isEmpty()) + { + trustset.add(_all); + } + + if(info.incoming) + { + if(!_allServer.isEmpty()) + { + trustset.add(_allServer); + } + if(info.adapterName.length() > 0) + { + java.util.List p = (java.util.List)_server.get(info.adapterName); + if(p != null) + { + trustset.add(p); + } + } + } + else + { + if(!_client.isEmpty()) + { + trustset.add(_client); + } + } + + // + // If there is nothing to match against, then we accept the cert. + // + if(trustset.isEmpty()) + { + return true; + } + + // + // If there is no certificate then we match false. + // + if(info.certs.length != 0) + { + javax.security.auth.x500.X500Principal subjectDN = (javax.security.auth.x500.X500Principal) + ((java.security.cert.X509Certificate)info.certs[0]).getSubjectX500Principal(); + String subjectName = subjectDN.getName(javax.security.auth.x500.X500Principal.RFC2253); + assert subjectName != null; + try + { + // + // Decompose the subject DN into the RDNs. + // + if(_traceLevel > 0) + { + _communicator.getLogger().trace("Security", "trust manager evaluating peer DN:\n" + subjectName); + } + java.util.List dn = RFC2253.parseStrict(subjectName); + + // + // Try matching against everything in the trust set. + // + java.util.Iterator p = trustset.iterator(); + while(p.hasNext()) + { + if(match((java.util.List)p.next(), dn)) + { + return true; + } + } + } + catch(RFC2253.ParseException e) + { + _communicator.getLogger().warning( + "IceSSL: unable to parse certificate DN `" + subjectName + "'\nreason: " + e.reason); + } + } + + return false; + } + + private boolean + match(java.util.List matchSet, java.util.List subject) + { + java.util.Iterator r = matchSet.iterator(); + while(r.hasNext()) + { + if(matchRDNs((java.util.List)r.next(), subject)) + { + return true; + } + } + return false; + } + + private boolean + matchRDNs(java.util.List match, java.util.List subject) + { + java.util.Iterator p = match.iterator(); + while(p.hasNext()) + { + RFC2253.RDNPair matchRDN = (RFC2253.RDNPair)p.next(); + boolean found = false; + java.util.Iterator q = subject.iterator(); + while(q.hasNext()) + { + RFC2253.RDNPair subjectRDN = (RFC2253.RDNPair)q.next(); + if(matchRDN.key.equals(subjectRDN.key)) + { + found = true; + if(!matchRDN.value.equals(subjectRDN.value)) + { + return false; + } + } + } + if(!found) + { + return false; + } + } + return true; + } + + java.util.List + parse(String value) + throws RFC2253.ParseException + { + // + // Java X500Principal.getName says: + // + // If "RFC2253" is specified as the format, this method emits + // the attribute type keywords defined in RFC 2253 (CN, L, ST, + // O, OU, C, STREET, DC, UID). Any other attribute type is + // emitted as an OID. Under a strict reading, RFC 2253 only + // specifies a UTF-8 string representation. The String + // returned by this method is the Unicode string achieved by + // decoding this UTF-8 representation. + // + // This means that things like emailAddress and such will be turned into + // something like: + // + // 1.2.840.113549.1.9.1=#160e696e666f407a65726f632e636f6d + // + // The left hand side is the OID (see + // http://www.columbia.edu/~ariel/ssleay/asn1-oids.html) for a + // list. The right hand side is a BER encoding of the value. + // + // This means that the user input, unless it uses the + // unfriendly OID format, will not directly match the + // principal. + // + // Two possible solutions: + // + // Have the RFC2253 parser convert anything that is not CN, L, + // ST, O, OU, C, STREET, DC, UID into OID format, and have it + // convert the values into a BER encoding. + // + // Send the user data through X500Principal to string form and + // then through the RFC2253 encoder. This uses the + // X500Principal to do the encoding for us. + // + // The latter is much simpler, however, it means we need to + // send the data through the parser twice because we split the + // DNs on ';' which cannot be blindly split because of quotes, + // \ and such. + // + java.util.List result = new java.util.LinkedList(); + java.util.List l = RFC2253.parse(value); + java.util.Iterator p = l.iterator(); + while(p.hasNext()) + { + java.util.List dn = (java.util.List)p.next(); + String v = new String(); + boolean first = true; + java.util.Iterator q = dn.iterator(); + while(q.hasNext()) + { + if(!first) + { + v += ","; + } + first = false; + RFC2253.RDNPair pair = (RFC2253.RDNPair)q.next(); + v += pair.key; + v += "="; + v += pair.value; + } + javax.security.auth.x500.X500Principal princ = new javax.security.auth.x500.X500Principal(v); + String subjectName = princ.getName(javax.security.auth.x500.X500Principal.RFC2253); + java.util.List l2 = RFC2253.parse(subjectName); + assert l2.size() == 1; + result.add((java.util.List)l2.get(0)); + } + return result; + } + + private Ice.Communicator _communicator; + private int _traceLevel; + + private java.util.List _all; + private java.util.List _client; + private java.util.List _allServer; + private java.util.Map _server = new java.util.HashMap(); +} diff --git a/java/ssl/jdk1.4/IceSSL/Util.java b/java/ssl/jdk1.4/IceSSL/Util.java index 38031ef4f24..6f0ed5e3f44 100644 --- a/java/ssl/jdk1.4/IceSSL/Util.java +++ b/java/ssl/jdk1.4/IceSSL/Util.java @@ -95,7 +95,7 @@ public final class Util } static ConnectionInfo - populateConnectionInfo(javax.net.ssl.SSLSocket fd) + populateConnectionInfo(javax.net.ssl.SSLSocket fd, String adapterName, boolean incoming) { ConnectionInfo info = new ConnectionInfo(); javax.net.ssl.SSLSession session = fd.getSession(); @@ -110,6 +110,8 @@ public final class Util info.cipher = session.getCipherSuite(); info.localAddr = (java.net.InetSocketAddress)fd.getLocalSocketAddress(); info.remoteAddr = (java.net.InetSocketAddress)fd.getRemoteSocketAddress(); + info.adapterName = adapterName; + info.incoming = incoming; return info; } } |