XMPPTCPConnection public method getSocket() not available

In all the Jitsi xmpp client implementation, it makes access to public method getSocket() from TCPConnection.java in asmack library to provide user with some of its features implemented e.g. “connection info” to view SSL certificate

When I started developing the android xmpp aTalk client, a fork from jitsi-android; I ported the whole aTalk application to use the Smack library. However I found that the method getSocket() is not being implemented in XMPPTCPConnection.java (smack-tcp-4.1.8.jar).

Currently I have to manually delete the XMPPTCPConnection.class from its original library jar downloaded from maven repository,

and added the method getSocket() in local extracted source XMPPTCPConnection.java in my application for inclusion.

I would really appreciate if the Smack team can seriously consider adding the public method getScoket() in XMPPTCPConnection.java in all new official smack library releases.

/**

  • To retrieve the XMPP connection socket used by the protocolProvider
  • @return SSL socket

*/
public Socket getSocket()
{
return socket;
}

Note: For any user who are interested, aTalk-release.apk is freely available and can be downloaded from the site below:

http://atalk.sytes.net/releases/atalk-android/aTalk-release.apk

I would really appreciate if the Smack team can seriously consider adding the public method getScoket() in XMPPTCPConnection.java
No, following good OOP paradigms, I won’t expose an internal object via a public API. The socket is mutable internal state of the XMPPConnection. However I’m not opposing adding methods that present a read-only view to certain aspects of the Socket and/or SLLContext (like the remote host’s IP address).

Currently I have to manually delete the XMPPTCPConnection.class from its original library jar downloaded from maven repository,

and added the method getSocket() in local extracted source XMPPTCPConnection.java in my application for inclusion.

You don’t have to. You could use reflection to obtain the socket. Or configure a custom SocketFactory to grab the Socket before it’s given to XMPP(TCP)Connection.

Thanks for the response.

You don’t have to. You could use reflection to obtain the socket. Or configure a custom SocketFactory to grab the Socket before it’s given to XMPP(TCP)Connection.

Moving along the line as your recommendation, While reviewing the sources:

In the method private void connectUsingConfiguration() in XMPPTCPConnection.java, I see that the socket is being created using

socket = socketFactory.createSocket(); using DefaultSocketFactory class implementation within android system (not traceable). I am no sure how to extend this class to grasp the socket. Any advice is appreciated.

As for the reflection approach, the atalk/jitsi application’s public class ProtocolProviderServiceJabberImpl is using the following method to get the SSLSocket. No idea how to approach from here to use reflection. Any example I can refer to?

/**

  • Return the SSL socket (if TLS used).
  • @return The SSL socket or null if not used
    */
    public SSLSocket getSSLSocket()
    {
    SSLSocket result = null;
    if (connection != null) {
    Socket socket = connection.getSocket();
    if (socket instanceof SSLSocket) {
    result = (SSLSocket) socket;
    }
    }
    return result;
    }

I believe any of the above implementations is not simple. I am still hoping the team can reconsider my request; as it only get the connected socket established by the class (without modifying it), which I feel it does not really degrading the OOP concept.

Moving along the line as your recommendation, While reviewing the sources:

In the method private void connectUsingConfiguration() in XMPPTCPConnection.java, I see that the socket is being created using

socket = socketFactory.createSocket(); using DefaultSocketFactory class implementation within android system (not traceable). I am no sure how to extend this class to grasp the socket. Any advice is appreciated

.Just follow the trace where socketFactory comes from and you will find ConnectionConfiguration.Builder.setSocketFactory(SocketFactory());

I believe any of the above implementations is not simple. I am still hoping the team can reconsider my request; as it only get the connected socket established by the class (without modifying it), which I feel it does not really degrading the OOP concept.

It’s a major violation of encapsulation. The user could bring the whole connection in an undefined state, for example by performing a read() or write() on the Socket. Again, patches that only expose a read-only view could be fine. But even then, I would first get an idea why my users want access to the socket and then provide a easy to use API for their use cases. For what, besides the remote host IP do you need access to the Socket?

.Just follow the trace where socketFactory comes from and you will find ConnectionConfiguration.Builder .setSocketFactory(SocketFactory());

Not sure if I understand your remarks i.e. to getSocket() via the SocketFactory with extended class: follows are my inputs

Below is the extraction of the ProtocolProviderServiceJabbperImpl.java for atalk. Actually in the ConnectionConfiguration.Builder, I did not use .setSocketFactory. Even if I have and get a reference to the SocketFactory(). I see the abstract class SocketFactory does not keep a local copy of socket but return immediately to the caller in all its creatSocket() methods. Not sure how to extend this class under this case as I have not control on how XMPPTCPConnection calling createSocket().

=================

/**

  • Connects xmpp connection and login. Returning the state whether is it final - Abort due to
  • certificate cancel or keep trying cause only current address has failed or stop trying cause
  • we succeeded.
  • @param address
  • the address to connect to
  • @param serviceName
  • the service name to use
  • @param userName
  • the username to use
  • @param resource
  • and the resource.
  • @param loginStrategy
  • the login strategy to use
  • @return return the state how to continue the connect process.
  • @throws XMPPException
  • if we cannot connect for some reason
    */
    private ConnectState connectAndLogin(InetSocketAddress address, String serviceName,
    String userName, String resource, JabberLoginStrategy loginStrategy)
    throws XMPPException
    {
    XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder();
    config.setServiceName(serviceName);
    config.setResource(resource);
    config.setHost(address.getAddress().getHostAddress());
    config.setPort(address.getPort());
    // config.setProxyInfo(proxy);
    config.setCompressionEnabled(false);
    // config.setReconnectionAllowed(false); // Not supported in Smack 4.1.8 ???

// if we have OperationSetPersistentPresence, skip sending initial presence during login;
// it is taken care by OperationSet
if (getOperationSet(OperationSetPersistentPresence.class) != null)
config.setSendPresence(false);

// user have the possibility to disable TLS but in this case, it will not be able to
// connect to a server which requires TLS
boolean tlsRequired = loginStrategy.isTlsRequired();
config.setSecurityMode(tlsRequired
? ConnectionConfiguration.SecurityMode.required
: ConnectionConfiguration.SecurityMode.ifpossible);
TLSUtils.setTLSOnly(config);

// Need to disable certain ReflectionDebuggerFactory.DEFAULT_DEBUGGERS loading for
// Android (that are only for windows)
SmackConfiguration.addDisabledSmackClass(
“org.jivesoftware.smackx.debugger.EnhancedDebugger”);
SmackConfiguration.addDisabledSmackClass(“org.jivesoftware.smack.debugger.LiteDebugger”);
config.setDebuggerEnabled(true);

// cmeng - disable interpreted xml printing - see no additional info included
// AbstractDebugger.printInterpreted = true;

if (connection != null) {
logger.error(“Connection is not null and is Connected:” + connection.isConnected(),
new Exception("Trace possible duplicate connections: "

  • getAccountID().getAccountAddress()));
    disconnectAndCleanConnection();
    }

this.address = address;
CertificateService cvs = getCertificateVerificationService();
if (cvs != null) {
SSLContext sslContext;
try {
sslTrustManager = getTrustManager(cvs, serviceName);
sslContext = loginStrategy.createSslContext(cvs, sslTrustManager);
config.setCustomSSLContext(sslContext);

}
catch (GeneralSecurityException e) {
logger.error(“Error creating custom trust manager”, e);
throw new XMPPException.XMPPErrorException(
new XMPPError(XMPPError.Condition.service_unavailable));
}
}
else if (tlsRequired) {
throw new XMPPException.XMPPErrorException(
new XMPPError(XMPPError.Condition.service_unavailable));
}

// commented: atalk.org ejabberd XMPP Server supports SCRAMSHA1Mechanism
// SASLAuthentication.unregisterSASLMechanism(SCRAMSHA1Mechanism.class.getName());

XMPPTCPConnection.setUseStreamManagementDefault(true);
xmppConfig = config.build();
connection = new XMPPTCPConnection(xmppConfig);

// Allow longer timeout during login
// connection.setPacketReplyTimeout(
// ProtocolProviderServiceJabberImpl.SMACK_PACKET_REPLY_TIMEOUT);
try {
connection.connect();
}
catch (SmackException e) {
Throwable exception = e.getCause();
if ((exception instanceof SSLHandshakeException)
|| (exception instanceof CertificateException)) {
// javax.net.ssl.SSLHandshakeException:certification path not found :
// exception never throw, as it was masked off by JabberConnectionListener…
}
else {
String exMsg = e.getMessage();
logger.error("Encounter problem during login: " + exMsg);

throw new XMPPException.XMPPErrorException(
new XMPPError(Condition.internal_server_error, null, exMsg,
XMPPError.Type.CANCEL, null, null)
);
}
}
catch (IOException e) {
e.printStackTrace();
}
// Reset back to default
// connection.setPacketReplyTimeout(
// ProtocolProviderServiceJabberImpl.SMACK_PACKET_REPLY_DEFAULT_TIMEOUT);

setTrafficClass();
if (abortConnecting) {
abortConnecting = false;
disconnectAndCleanConnection();
return ConnectState.ABORT_CONNECTING;
}
registerServiceDiscoveryManager();

if (connectionListener == null) {
connectionListener = new JabberConnectionListener();
}
if (!connection.isSecureConnection() && tlsRequired) {
throw new XMPPException.XMPPErrorException(
new XMPPError(XMPPError.Condition.service_unavailable));
}

if (!connection.isConnected()) {
// connection is not connected, lets set state to our connection as failed seems
// there is some lag/problem with network and this way we will inform for it and
// later reconnect if needed as IllegalStateException that is thrown within
// addConnectionListener is not handled properly
disconnectAndCleanConnection();

logger.error(“Connection not established, server not found!”);
eventDuringLogin = null;

fireRegistrationStateChanged(getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_SERVER_NOT_FOUND, null);
return ConnectState.ABORT_CONNECTING;
}
connection.addConnectionListener(connectionListener);

if (abortConnecting) {
abortConnecting = false;
disconnectAndCleanConnection();
return ConnectState.ABORT_CONNECTING;
}
fireRegistrationStateChanged(getRegistrationState(), RegistrationState.REGISTERING,
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);

if (!loginStrategy.login(connection, userName, resource)) {
disconnectAndCleanConnection();
eventDuringLogin = null;
fireRegistrationStateChanged(getRegistrationState(),
RegistrationState.CONNECTION_FAILED,
RegistrationStateChangeEvent.REASON_AUTHENTICATION_FAILED,
loginStrategy.getClass().getName() + " requests abort");
return ConnectState.ABORT_CONNECTING;
}

if (connection.isAuthenticated()) {
eventDuringLogin = null;
fireRegistrationStateChanged(getRegistrationState(), RegistrationState.REGISTERED,
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);
OperationSetPersistentPresenceJabberImpl opSet =
(OperationSetPersistentPresenceJabberImpl) getOperationSet(
OperationSetPersistentPresence.class);
try {
opSet.publishPresenceStatus(getJabberStatusEnum().getStatus(“Available”), “”);
}
catch (Exception e) {
logger.error(“Failed to publish presence status”);
}
return ConnectState.STOP_TRYING;
}
else {
disconnectAndCleanConnection();
eventDuringLogin = null;
fireRegistrationStateChanged(getRegistrationState(), RegistrationState.UNREGISTERED,
RegistrationStateChangeEvent.REASON_NOT_SPECIFIED, null);

return ConnectState.CONTINUE_TRYING;
}
}

=====================

If you have install the atalk.apk on an android phone; select Settings | SSL Certificate:

The last three items in the property page if each Jid i.e. TLS protocol; TLS cipher suite; and view certificate. These information are retrieved via the getSSLSocket()

Continue …
If you have installed the atalk apk on an android phone; select “Settings | SSL Certificate”:

The last three items in the property page for each Jid i.e. TLS protocol; TLS cipher suite; and view certificate; these information are retrieved via the getSSLSocket() path as inidcated in OperationSetTLSJabberImpl.java i.e.

public class OperationSetTLSJabberImpl implements OperationSetTLS
{
private final ProtocolProviderServiceJabberImpl jabberService;

public OperationSetTLSJabberImpl(ProtocolProviderServiceJabberImpl jabberService)
{
this.jabberService = jabberService;
}

/**

  • @see OperationSetTLS#getCipherSuite()
    */
    @Override
    public String getCipherSuite()
    {
    final String result;
    final SSLSocket socket = jabberService.getSSLSocket();
    if (socket == null) {
    result = null;
    }
    else {
    result = socket.getSession().getCipherSuite();
    }
    return result;
    }

/**

  • @see OperationSetTLS#getProtocol()
    */
    @Override
    public String getProtocol()
    {
    final String result;
    jabberService.getConnection();

final SSLSocket socket = jabberService.getSSLSocket();
if (socket == null) {
result = null;
}
else {
result = socket.getSession().getProtocol();
}
return result;
}

/**

  • @see OperationSetTLS#getServerCertificates()
    */
    @Override
    public Certificate[] getServerCertificates()
    {
    Certificate[] result = null;
    final SSLSocket socket = jabberService.getSSLSocket();
    if (socket != null) {
    try {
    result = socket.getSession().getPeerCertificates();
    }
    catch (SSLPeerUnverifiedException ignored) // NOPMD
    {
    // result will be null
    }
    }
    return result;
    }
    }

=====================

Also the setTrafficClass(); in the above method also make use of getSocket(); personally not sure when need to setTrafficClass in this case.

/**

  • Sets the traffic class for the XMPP signalling socket.
    */
    private void setTrafficClass()
    {
    Socket s = connection.getSocket();
    if (s != null) {
    ConfigurationService configService = JabberActivator.getConfigurationService();
    String dscp = configService.getString(XMPP_DSCP_PROPERTY);

    if (dscp != null) {
    try {
    int dscpInt = Integer.parseInt(dscp) << 2;
    if (dscpInt > 0)
    s.setTrafficClass(dscpInt);
    }
    catch (Exception e) {
    logger.info(“Failed to set trafficClass”, e);
    }
    }
    }
    }