kopia lustrzana https://github.com/ryukoposting/Signal-Android
159 wiersze
5.5 KiB
Java
159 wiersze
5.5 KiB
Java
package org.signal.devicetransfer;
|
|
|
|
import android.content.Context;
|
|
import android.os.Handler;
|
|
|
|
import androidx.annotation.AnyThread;
|
|
import androidx.annotation.NonNull;
|
|
|
|
import org.signal.core.util.StreamUtil;
|
|
import org.signal.core.util.logging.Log;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.ServerSocket;
|
|
import java.net.Socket;
|
|
import java.util.Arrays;
|
|
|
|
/**
|
|
* Performs the networking setup/tear down for the server. This includes
|
|
* connecting to the client, generating TLS keys, performing the TLS/SAS verification,
|
|
* running an arbitrarily provided {@link ServerTask}, and then cleaning up.
|
|
*/
|
|
final class NetworkServerThread extends Thread {
|
|
|
|
private static final String TAG = Log.tag(NetworkServerThread.class);
|
|
|
|
public static final int NETWORK_SERVER_STARTED = 1001;
|
|
public static final int NETWORK_SERVER_STOPPED = 1002;
|
|
public static final int NETWORK_CLIENT_CONNECTED = 1003;
|
|
public static final int NETWORK_CLIENT_DISCONNECTED = 1004;
|
|
public static final int NETWORK_CLIENT_SSL_ESTABLISHED = 1005;
|
|
|
|
private volatile ServerSocket serverSocket;
|
|
private volatile boolean isRunning;
|
|
private volatile Boolean isVerified;
|
|
|
|
private final Context context;
|
|
private final ServerTask serverTask;
|
|
private final SelfSignedIdentity.SelfSignedKeys keys;
|
|
private final Handler handler;
|
|
private final Object verificationLock;
|
|
|
|
public NetworkServerThread(@NonNull Context context,
|
|
@NonNull ServerTask serverTask,
|
|
@NonNull SelfSignedIdentity.SelfSignedKeys keys,
|
|
@NonNull Handler handler)
|
|
{
|
|
this.context = context;
|
|
this.serverTask = serverTask;
|
|
this.keys = keys;
|
|
this.handler = handler;
|
|
this.verificationLock = new Object();
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
Log.i(TAG, "Server thread running");
|
|
isRunning = true;
|
|
|
|
Log.i(TAG, "Starting up server socket...");
|
|
try {
|
|
serverSocket = SelfSignedIdentity.getServerSocketFactory(keys).createServerSocket(0);
|
|
handler.sendMessage(handler.obtainMessage(NETWORK_SERVER_STARTED, serverSocket.getLocalPort(), 0));
|
|
while (shouldKeepRunning() && !serverSocket.isClosed()) {
|
|
Log.i(TAG, "Waiting for client socket accept...");
|
|
try (Socket clientSocket = serverSocket.accept()) {
|
|
InputStream inputStream = clientSocket.getInputStream();
|
|
OutputStream outputStream = clientSocket.getOutputStream();
|
|
int authenticationCode = DeviceTransferAuthentication.generateServerAuthenticationCode(keys.getX509Encoded(), inputStream, outputStream);
|
|
|
|
handler.sendMessage(handler.obtainMessage(NETWORK_CLIENT_SSL_ESTABLISHED, authenticationCode));
|
|
|
|
Log.i(TAG, "Waiting for user to verify sas");
|
|
awaitAuthenticationCodeVerification();
|
|
Log.d(TAG, "Waiting for client to tell us they also verified");
|
|
outputStream.write(0x43);
|
|
outputStream.flush();
|
|
try {
|
|
int result = inputStream.read();
|
|
if (result == -1) {
|
|
Log.w(TAG, "Something happened waiting for client to verify");
|
|
throw new DeviceTransferAuthentication.DeviceTransferAuthenticationException("client disconnected while we waited");
|
|
}
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Something happened waiting for client to verify", e);
|
|
throw new DeviceTransferAuthentication.DeviceTransferAuthenticationException(e);
|
|
}
|
|
|
|
handler.sendEmptyMessage(NETWORK_CLIENT_CONNECTED);
|
|
serverTask.run(context, inputStream);
|
|
|
|
outputStream.write(0x53);
|
|
outputStream.flush();
|
|
} catch (IOException e) {
|
|
if (isRunning) {
|
|
Log.i(TAG, "Error connecting with client or server socket closed.", e);
|
|
} else {
|
|
Log.i(TAG, "Server shutting down...");
|
|
}
|
|
} finally {
|
|
handler.sendEmptyMessage(NETWORK_CLIENT_DISCONNECTED);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
Log.w(TAG, e);
|
|
} finally {
|
|
StreamUtil.close(serverSocket);
|
|
}
|
|
|
|
Log.i(TAG, "Server exiting");
|
|
isRunning = false;
|
|
handler.sendEmptyMessage(NETWORK_SERVER_STOPPED);
|
|
}
|
|
|
|
private void awaitAuthenticationCodeVerification() throws DeviceTransferAuthentication.DeviceTransferAuthenticationException {
|
|
synchronized (verificationLock) {
|
|
try {
|
|
while (isVerified == null) {
|
|
verificationLock.wait();
|
|
}
|
|
if (!isVerified) {
|
|
throw new DeviceTransferAuthentication.DeviceTransferAuthenticationException("User verification failed");
|
|
}
|
|
} catch (InterruptedException e) {
|
|
throw new DeviceTransferAuthentication.DeviceTransferAuthenticationException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean shouldKeepRunning() {
|
|
return !isInterrupted() && isRunning;
|
|
}
|
|
|
|
@AnyThread
|
|
public int getLocalPort() {
|
|
ServerSocket localServerSocket = serverSocket;
|
|
if (localServerSocket != null) {
|
|
return localServerSocket.getLocalPort();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
@AnyThread
|
|
public void setVerified(boolean isVerified) {
|
|
this.isVerified = isVerified;
|
|
synchronized (verificationLock) {
|
|
verificationLock.notify();
|
|
}
|
|
}
|
|
|
|
@AnyThread
|
|
public void shutdown() {
|
|
isRunning = false;
|
|
StreamUtil.close(serverSocket);
|
|
interrupt();
|
|
}
|
|
}
|