I am trying to make Reverse Proxy for android, that kind of proxy that uses client`s internet. And I have encountered problem doing such. Client speaks with server via SSL and using json messages (IdentificationMessage, ProxyInit, ReverseProxyConnect, ReverseProxyConnectResponse, ReverseProxyData, ReverseProxyDisconnect). Server uses Socks5 proxy and only sends data to client for processing, also server is written on C#. Response from server that are in ReverseProxyClient is present and sent to server that connected via SSL, however no new messages are sent to client for processing. Meanwhile a C# client that makes same thing does it successfully. I have a suspicion that mobile phone can not properly proccess PC requests and so responses are not proper for PC. In android manifest internet is enabled.
ReverseProxyHandler:
public class ReverseProxyHandler {
static List<ReverseProxyClient> _clients = new LinkedList<ReverseProxyClient>();
static public TcpClient stream;
static public ReverseProxyClient GetClientById(int id)
{
return _clients.stream().filter(x->x.ConnectionId == id).findAny().get();
}
static public void MessageSrvExecuter(IMessage message)
{
try {
stream.sendMessage(message);
} catch (Exception e) { }
}
static public void MessageClnExecuter(IMessage message)
{
if(message instanceof ReverseProxyConnect) {
ReverseProxyConnect i = (ReverseProxyConnect)message;
ExecuteCln(i);
} else if(message instanceof ReverseProxyData) {
ReverseProxyData i = (ReverseProxyData)message;
ExecuteCln(i);
} else if(message instanceof ReverseProxyDisconnect) {
ReverseProxyDisconnect i = (ReverseProxyDisconnect)message;
ExecuteCln(i);
}
}
static private void ExecuteCln(ReverseProxyConnect message)
{
Log.e(TcpClient.TAG,String.valueOf(_clients.size()));
ReverseProxyClient rpc =new ReverseProxyClient(message);
new Thread(rpc).start();
_clients.add(rpc);
}
static private void ExecuteCln(ReverseProxyData message)
{
ReverseProxyClient proxyClient = GetClientById(message.ConnectionId);
if(proxyClient!=null)
proxyClient.SendToTargetServer(message.Data);
}
static private void ExecuteCln(ReverseProxyDisconnect message)
{
ReverseProxyClient proxyClient = GetClientById(message.ConnectionId);
if(proxyClient!=null)
proxyClient.Disconnect();
}
}
ReverseProxyClient:
public class ReverseProxyClient implements Runnable {
private InputStream m_ServerInput = null;
private OutputStream m_ServerOutput = null;
public static final int BUFFER_SIZE = 1024 * 8;
public static final int DEFAULT_PROXY_TIMEOUT = 10000;
boolean isActive = true;
public int ConnectionId;
public Socket Handle;
public String Target;
public int Port;
byte[] m_Buffer = new byte[BUFFER_SIZE];
private boolean _disconnectIsSend;
private Object m_lock;
public ReverseProxyClient(ReverseProxyConnect command)
{
this.ConnectionId = command.ConnectionId;
this.Target = command.Target;
this.Port = command.Port;
m_lock = this;
}
public void connectToServer() throws IOException {
try
{
Log.e(TcpClient.TAG, "connecting to server");
Handle = new Socket(Target, Port);
Handle.setSoTimeout(DEFAULT_PROXY_TIMEOUT);
prepareServer();
ReverseProxyConnectResponse i = new ReverseProxyConnectResponse();
i.ConnectionId = ConnectionId;
i.IsConnected = true;
i.LocalAddress = Handle.getLocalAddress().getAddress();
i.LocalPort = Handle.getLocalPort();
i.HostName = Target;
ReverseProxyHandler.MessageSrvExecuter(i);
}
catch (Exception e)
{
Log.e(TcpClient.TAG, e.getMessage());
ReverseProxyConnectResponse i = new ReverseProxyConnectResponse();
i.ConnectionId = ConnectionId;
i.IsConnected = true;
i.LocalAddress = Handle.getLocalAddress().getAddress();
i.LocalPort = Handle.getLocalPort();
i.HostName = Target;
ReverseProxyHandler.MessageSrvExecuter(i);
}
}
protected void prepareServer() throws IOException {
synchronized (m_lock) {
m_ServerInput = Handle.getInputStream();
m_ServerOutput = Handle.getOutputStream();
Log.e(TcpClient.TAG, "set streams");
}
}
public void Disconnect() {
isActive = false;
Log.e(TcpClient.TAG, "closing");
if (!_disconnectIsSend)
{
_disconnectIsSend = true;
close();
//send to the Server we've been disconnected
ReverseProxyDisconnect i = new ReverseProxyDisconnect();
i.ConnectionId = ConnectionId;
ReverseProxyHandler.MessageSrvExecuter(i);
}
try
{
Handle.close();
}
catch(Exception e) {
Log.e(TcpClient.TAG, e.getMessage());
}
}
public void SendToTargetServer(byte[] data)
{
int len = data.length;
if (m_ServerOutput != null && len > 0 && len <= m_Buffer.length) {
try {
m_ServerOutput.write(data, 0, len);
m_ServerOutput.flush();
} catch (IOException e) {
Log.e(TcpClient.TAG, e.getMessage());
}
}
int dlen;
dlen = checkServerData();
Log.e(TcpClient.TAG, "sending "+dlen);
if (dlen > 0) {
byte[] payload = new byte[dlen];
System.arraycopy(m_Buffer, 0, payload, 0, dlen);
ReverseProxyData i = new ReverseProxyData();
i.ConnectionId = ConnectionId;
i.Data = payload;
ReverseProxyHandler.MessageSrvExecuter(i);
}
}
public void setLock(Object lock) {
this.m_lock = lock;
}
public int checkServerData() {
synchronized (m_lock) {
if (m_ServerInput == null)
{
return -1;
}
int dlen;
try {
dlen = m_ServerInput.read(m_Buffer, 0, BUFFER_SIZE);
} catch (InterruptedIOException e) {
Log.e(TcpClient.TAG, e.getMessage());
return -1;
} catch (IOException e) {
Log.e(TcpClient.TAG, e.getMessage());
Disconnect(); // Close the server on this exception
return -1;
}
if (dlen < 0) {
Disconnect();
}
return dlen;
}
}
@Override
public void run() {
setLock(this);
try {
connectToServer();
} catch (IOException e) {
Log.e(TcpClient.TAG, e.getMessage());
}
relay();
close();
}
public void close() {
try {
if (m_ServerOutput != null) {
m_ServerOutput.flush();
m_ServerOutput.close();
}
} catch (IOException e) {
// ignore
}
}
public void relay() {
while (isActive) {
int dlen;
Log.d(TcpClient.TAG,"Trying to...");
dlen = checkServerData();
Log.e(TcpClient.TAG, "got "+dlen);
if (dlen < 0) isActive = false;
if (dlen > 0) {
byte[] payload = new byte[dlen];
System.arraycopy(m_Buffer, 0, payload, 0, dlen);
ReverseProxyData i = new ReverseProxyData();
i.ConnectionId = ConnectionId;
i.Data = payload;
ReverseProxyHandler.MessageSrvExecuter(i);
}
Thread.yield();
}
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
TcpClient mTcpClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView v = (TextView)findViewById(R.id.text);
try {
Toast.makeText(this, "before", Toast.LENGTH_LONG).show();
mTcpClient = new TcpClient(message -> {
if(message.charAt(0)=='1') {
ReverseProxyConnect i = new ReverseProxyConnect();
i.FromJSON(message.substring(1));
ReverseProxyHandler.MessageClnExecuter(i);
Log.e(TcpClient.TAG,"connect");
} else if(message.charAt(0)=='3') {
ReverseProxyData i = new ReverseProxyData();
i.FromJSON(message.substring(1));
ReverseProxyHandler.MessageClnExecuter(i);
Log.e(TcpClient.TAG,"data");
} else if(message.charAt(0)=='4') {
ReverseProxyDisconnect i = new ReverseProxyDisconnect();
i.FromJSON(message.substring(1));
ReverseProxyHandler.MessageClnExecuter(i);
Log.e(TcpClient.TAG,"disconnect");
}
});
ReverseProxyHandler.stream = mTcpClient;
Toast.makeText(this, "created", Toast.LENGTH_LONG).show();
new Thread(()->{
try {
Log.d(TcpClient.TAG,"before ran");
mTcpClient.run();
} catch (Exception e) {
Log.e(TcpClient.TAG,e.getMessage());
}
}).start();
Toast.makeText(this, "started", Toast.LENGTH_LONG).show();
}
catch (Exception e)
{
v.setText(e.getMessage());
}
}
}
TcpClient:
public class TcpClient {
public static final String TAG = "Kranstein";
public static final String SERVER_IP = "93.95.224.224"; //server IP address
public static final int SERVER_PORT = 8001; //server port
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
private Queue<IMessage> msgs;
private boolean is_sending = false;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TcpClient(OnMessageReceived listener) {
msgs = new LinkedBlockingQueue<>();
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* @param message text entered by client
*/
public void sendMessage(final String message) {
Runnable runnable = new Runnable() {
@Override
public void run() {
if (mBufferOut != null) {
Log.d(TAG, "Sending: " + message);
mBufferOut.print(message);
mBufferOut.flush();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
public void sendMessage(final IMessage message) {
msgs.add(message);
Runnable runnable = new Runnable() {
@Override
public void run() {
if(!is_sending)
{
if (mBufferOut != null) {
is_sending = true;
while(!msgs.isEmpty())
{
IMessage message = msgs.remove();
Log.d(TAG, "Sending: " + message.GetID()+message.ToJSON());
mBufferOut.print(message.GetID()+message.ToJSON());
mBufferOut.flush();
Log.d(TAG, "Sent");
}
is_sending = false;
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
/**
* Close the connection and release the members
*/
public void stopClient() {
mRun = false;
sendMessage("end");
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() throws IOException {
mRun = true;
try {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL"); // Add in try catch block if you get error.
sc.init(null, trustAllCerts, new java.security.SecureRandom());
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
SSLSocketFactory ssf = sc.getSocketFactory();
SSLSocket socket = null;
socket = (SSLSocket) ssf.createSocket(serverAddr, SERVER_PORT);
socket.setNeedClientAuth(false);
socket.addHandshakeCompletedListener((HandshakeCompletedEvent handshakeCompletedEvent)-> {
try {
mBufferOut = new PrintWriter(handshakeCompletedEvent.getSocket().getOutputStream());
mBufferIn = new BufferedReader(new InputStreamReader(handshakeCompletedEvent.getSocket().getInputStream(), StandardCharsets.UTF_8));
int charsRead = 0; char[] buffer = new char[8096]; //choose your buffer size if you need other than 1024
Log.e(TAG,"identifaction");
IdentificationMessage mes = new IdentificationMessage();
mes.id = 0;
mes.mac = "";
mes.type = 0;
sendMessage(mes);
Log.e(TAG,"sent identification");
ProxyInit i = new ProxyInit();
i.Password = "asd";
i.Username = "asd";
i.Port = 2005;
sendMessage(i);
Log.e(TAG,"sent init");
while (mRun) {
charsRead = mBufferIn.read(buffer);
if(charsRead!=-1)
{
mServerMessage = new String(buffer).substring(0, charsRead);
if (mServerMessage != null && mMessageListener != null) {
mMessageListener.messageReceived(mServerMessage);
}
}
mServerMessage = null;
}
handshakeCompletedEvent.getSocket().close();
} catch (IOException e) {
Log.e(TAG,e.getMessage());
}
});
socket.startHandshake();
} catch (IOException | NoSuchAlgorithmException | KeyManagementException e) {
Log.e(TAG,""+e.getMessage());
}
}
public interface OnMessageReceived {
public void messageReceived(String message);
}
}