/*
* Copyright 2017 rootkiwi
*
* AN2Linux-client is licensed under GNU General Public License 3.
*
* See LICENSE for more details.
*/
package kiwi.root.an2linuxclient.network;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.cert.Certificate;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
import kiwi.root.an2linuxclient.crypto.Sha256Helper;
import kiwi.root.an2linuxclient.crypto.TlsHelper;
import static kiwi.root.an2linuxclient.network.PairingConnectionCallbackMessage.CallbackType.*;
import kiwi.root.an2linuxclient.utils.ConnectionHelper;
public class TcpPairingConnection extends PairingConnection {
private String serverAddress;
private int serverPort;
TcpPairingConnection(String serverAddress, int serverPort, Context c) {
super(c);
this.serverAddress = serverAddress;
this.serverPort = serverPort;
}
@Override
void acceptPairing() {
try {
mOut.write(ACCEPT_PAIRING);
mPairResponseSent = true;
} catch (IOException ioe) {}
}
@Override
void denyPairing() {
try {
mOut.write(DENY_PAIRING);
mPairResponseSent = true;
} catch (IOException ioe) {}
}
@Override
public void run() {
try {
Socket s = new Socket();
try {
s.connect(new InetSocketAddress(serverAddress, serverPort), 5000);
} catch (UnknownHostException | IllegalArgumentException e) {
notifyObservers(new PairingConnectionCallbackMessage(UNKNOWN_HOST));
try {
s.close();
} catch (IOException e2) {}
return;
} catch (SocketTimeoutException e) {
notifyObservers(new PairingConnectionCallbackMessage(TIMED_OUT));
try {
s.close();
} catch (IOException e2) {}
return;
} catch (SocketException e) {
notifyObservers(new PairingConnectionCallbackMessage(FAILED_TO_CONNECT));
try {
s.close();
} catch (IOException e2) {}
return;
}
mOut = s.getOutputStream();
mOut.write(INITIATE_PAIRING);
SSLSocket tlsSocket = (SSLSocket) TlsHelper.getPairingTlsContext().getSocketFactory()
.createSocket(s, serverAddress, serverPort, true);
tlsSocket.setUseClientMode(true);
tlsSocket.setEnabledProtocols(TlsHelper.TLS_VERSIONS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH){
tlsSocket.setEnabledCipherSuites(TlsHelper.TLS_CIPHERS);
} else {
tlsSocket.setEnabledCipherSuites(TlsHelper.TLS_CIPHERS_COMPAT);
}
final byte[] clientCertBytes = TlsHelper.getCertificateBytes(c);
tlsSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
try {
Certificate serverCert = event.getPeerCertificates()[0];
byte[] sha256 = Sha256Helper.sha256(clientCertBytes, serverCert.getEncoded());
notifyObservers(new PairingConnectionCallbackMessage(
TLS_HANDSHAKE_COMPLETED,
Sha256Helper.getFourLineHexString(sha256),
serverCert));
} catch (Exception e) {
Log.e("TcpPairingConnection", "run:handshakeCompleted");
Log.e("StackTrace", Log.getStackTraceString(e));
}
}
});
try {
tlsSocket.startHandshake();
} catch (IOException e) {
notifyObservers(new PairingConnectionCallbackMessage(FAILED_TO_CONNECT));
try {
mOut.close();
tlsSocket.close();
} catch (IOException e2) {}
return;
}
mOut = tlsSocket.getOutputStream();
mIn = tlsSocket.getInputStream();
mOut.write(ConnectionHelper.intToByteArray(clientCertBytes.length));
mOut.write(clientCertBytes);
tlsSocket.setSoTimeout(1000);
while (!mCancel) {
try {
int serverPairResponse = mIn.read();
if (serverPairResponse == ACCEPT_PAIRING) {
notifyObservers(new PairingConnectionCallbackMessage(SERVER_ACCEPTED_PAIR));
while (!mCancel && !mPairResponseSent) {
try {
if (mIn.read() == -1) {
// socket closed
notifyObservers(new PairingConnectionCallbackMessage(SOCKET_CLOSED));
mCancel = true;
}
} catch (SocketTimeoutException e) {}
}
mCancel = true;
} else if (serverPairResponse == DENY_PAIRING) {
notifyObservers(new PairingConnectionCallbackMessage(SERVER_DENIED_PAIR));
mCancel = true;
} else {
// socket closed or recieved something strange
notifyObservers(new PairingConnectionCallbackMessage(SOCKET_CLOSED));
mCancel = true;
}
} catch (SocketTimeoutException ste) {}
}
mIn.close();
mOut.close();
tlsSocket.close();
} catch (Exception e) {
Log.e("TcpPairingConnection", "run");
Log.e("StackTrace", Log.getStackTraceString(e));
}
}
}