Proxy : How to parse https request and response coming from socket connection - java

i'm creating a simple proxy in java.
What i'm doing is use ServerSocket to start it on a specific port and route the requests i've receive to a list of external proxies.
So, teorically i don't need parse the request and response, simply takes bytes recived and route them to the final destination.
My objective is to choose the destination proxy with some application logic.
In order to do this it wold be very usefull to know :
Ip of the client that connect to my proxy
Http response code that i receive from the final proxy
For a "normal" http connection this is possibile. When i read the data i can parse it and get the information i need.
With a https connection, all data are encrypted so i think this is not possibile. Anyone can confirm this and eventually suggest a way to bypass this "problem"?
Here some code as an example :
//start my proxy
ServerSocket ss = new ServerSocket(portNum);
Socket client= ss.accept();
//connection arrived : get pointer to streams
final InputStream from_client = client.getInputStream();
final OutputStream to_client = client.getOutputStream();
//Try to open socket to destination proxy
Socket server = null;
try {
server = new Socket([destination proxy host], [destination proxy port]);
} catch (IOException e) {
PrintWriter out = new PrintWriter(new OutputStreamWriter(to_client));
out.println("Proxy server cannot connect to " + opt.getProxyHost() + ":" + opt.getProxyPort() + ":\n" + e));
out.flush();
client.close();
}
some stuff ...
//get streams from destination
final InputStream from_server = server.getInputStream();
final OutputStream to_server = server.getOutputStream();
//in a separate Thread :
try {
//transfer bytes from client to server ( java >= 9 )
//**NOTE : the https connection is encrypted, is not possibile to parse the request received, just route it**
from_client.transferTo(to_server);
} catch (IOException e) {
//Manage exception
}
The response have the same problem :
int bytes_read;
try {
//**NOTE : the https connection is encrypted, is not possibile to parse the response received, just route it back**
from_server.transferTo(to_client);
} catch (IOException e) {
//Manage exception
}
Thanks
Davide

Related

How to send a TCP request with payload in Java

We are working for the Camera integration project, we have connected this camera through wifi ( camera act as a server), for interrations camera company provides TCP protocol, I have confused to how to send a request for this. We are trying this in JAVA. Please help us to understand this communication protocols.
Please give a hint or idea to commuction with this device.
We have tried below code and its not worked as expected.
try {
// Create a socket to connect to the server
Socket socket = new Socket("192.168.150.1", 31000);
// Send the payload
OutputStream out = socket.getOutputStream();
out.write(25004);
out.flush();
// Receive the payload
InputStream in = socket.getInputStream();
byte[] buffer = new byte[1024];
int bytesRead = in.read(buffer);
String response = new String(buffer, 0, bytesRead);
System.out.println("Response from server: " + response);
// Close the socket
socket.close();
} catch (IOException e) {
e.printStackTrace();
}

Make Java IO Socket listen just like a ServerSocket

I'm doing a financial messaging integration as follows:
A server has an interface listening for requests from a client socket on a specific IP and port
Server sends a response for every request, back to the client socket
Also, server sends requests to the same client socket
The following is working perfectly:
The client socket (Socket object of Java IO) successfully sends requests to the server interface
The client socket successfully receives response for every request
try {
Socket clientSocket = new Socket("example.com", 8888);
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
output.write(data);
output.flush();
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// Read responses or new request from input
} catch (IOException e) {
e.printStackTrace();
}
The client socket is supposed to receive any request from the server - the same way it's receiving responses from the same sever. However, when a server initiates a request to the client socket, the request is never received. However, we can trace the traffic from the tcpdump on the client environment.
How can I make the client socket listen to requests from the server, and not just responses?
Update
It might help to clarify something on this integration:
a. The 'server' in this case is a third party system, with it's integration rules
b. My client socket posts message to the server (above)
c. My implementation listens to responses and requests from the third party system server, either by creating my own server socket (ruled out), or using the very client socket I'm sending with (the solution I was seeking)
This is a very common mistake, you are writing a message without writing "\n" (end line identifier) at the end so no messages will be read. To fix this use PrintWriter with println.
That will send a line to the other socket.
Here is an example of a thread-per-client model of a server
//create a new server socket with the port as a parameter, this will bind it to the specified port: 6000
ServerSocket server = new ServerSocket(6000);
System.out.println("Binded");
//create a while loop accepting sockets
while(true)
{
//accept a socket
Socket client = server.accept();
System.out.println("Client has connected");
//create a new thread for this socket
new Thread(() ->
{
try
{
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("Waiting for requests...");
//create a while loop reading requests (lines)
String request;
while((request = reader.readLine()) != null)
{
System.out.println("Received message: " + request);
//here find the correct response and return it, I just sent a message, replace it with the correct response
writer.println("Hello there! How are you today?");
//flush, flushing will write the data to the client
writer.flush();
}
} catch(IOException e)
{
//print an exception if it occurred, if an exception occurrs its most likely just a disconnection exception
e.printStackTrace();
}
}).start();
}
and here is an example of a client
//connect to the server at "localhost" on port 6000
Socket client = new Socket("localhost", 6000);
System.out.println("Connected");
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
//write a request
writer.println("Hi there!");
//flush, flushing will write the data to the server
writer.flush();
System.out.println("Written");
System.out.println("Waiting for responses...");
//create a while loop reading responses (lines)
//you may want to do this while loop in another thread
String response;
while((response = reader.readLine()) != null)
{
System.out.println("Received response: " + response);
}
Also if this is involved with financial information I recommend using TLS (SSL).
You don't have to worry Java already has it implemented and has made it easy to use, here is an example of a server
//create a new SSL server socket with the port as a parameter, this will bind it to the specified port: 6000
//you create it by getting the default SSLServerSocketFactory which will create a new SSLServerSocket
//you need to cast it since it actually returns ServerSocket but SSLServerSocket extends ServerSocket and this returns SSLServerSocket so it is safe
SSLServerSocket server = (SSLServerSocket) SSLServerSocketFactory.getDefault().createServerSocket(6000);
System.out.println("Binded");
//set the enabled ciphersuites, until you buy a certificate set only to ciphersuites with "anon" more info on ciphersuites on https://en.wikipedia.org/wiki/Cipher_suite
server.setEnabledCipherSuites(new String[]{"TLS_ECDH_anon_WITH_AES_256_CBC_SHA"});
//create a while loop accepting sockets
while(true)
{
//accept a socket a SSLSocket
SSLSocket client = (SSLSocket) server.accept();
System.out.println("Client has connected");
//create a new thread for this socket
new Thread(() ->
{
try
{
//begin a handshake more info about handshakes in https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm
client.startHandshake();
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("Waiting for requests...");
//create a while loop reading requests (lines)
String request;
while((request = reader.readLine()) != null)
{
System.out.println("Received message: " + request);
//here find the correct response and return it, I just sent a message, replace it with the correct response
writer.println("Hello there! How are you today?");
//flush, flushing will write the data to the client
writer.flush();
}
} catch(IOException e)
{
//print an exception if it occurred, if an exception occurrs its most likely just a disconnection exception
e.printStackTrace();
}
}).start();
}
And here is an example of a client
//connect to the server at "localhost" on port 6000
//you create a SSLSocket by getting the default SSLSocketFactory which will create a new SSLSocket
//you need to cast it since it actually returns Socket but SSLSocket extends Socket and this returns SSLSocket so it is safe
SSLSocket client = (SSLSocket) SSLSocketFactory.getDefault().createSocket("localhost", 6000);
System.out.println("Connected");
//set the enabled ciphersuites to everything supported so the server can decide the ciphersuite, you can modify this to specified ciphersuites
client.setEnabledCipherSuites(client.getSupportedCipherSuites());
//begin a handshake more info about handshakes in https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.1.0/com.ibm.mq.doc/sy10660_.htm
client.startHandshake();
/*
create a print writer so you can write a line, not only a message like BufferedWriter does,
if for some reason you still want to use BufferedWriter use
writer.write(message + "\n");
*/
PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
//Create a new reader
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
//write a request
writer.println("Hi there!");
//flush, flushing will write the data to the server
writer.flush();
System.out.println("Written");
System.out.println("Waiting for responses...");
//create a while loop reading responses (lines)
//you may want to do this while loop in another thread
String response;
while((response = reader.readLine()) != null)
{
System.out.println("Received response: " + response);
}

How to use source port to send and receive data by TCP/IP in Java

I have started a client in my system. It is running on port no 7913. I am sending a request data via TCP/IP from Java to server socket running on 7913.
log is Message sent to Socket [addr=/190.161.153.109,port=7913,localport=54717]
I have also received the response from server for that particular data. Now the server is also trying to send a request to my localport 54717, not to port where my application is listening [ie 7913].
How to handle the request? When I try to connect with telnet to my localport, connection is refused.
The code:
public static String ickTransport(String ickHeader,String ickdata, Socket connection) throws UnknownHostException, IOException
try
{
connection.setSoTimeout(Integer.parseInt(ickTimeOut));
log.debug("ick Message for "+connection.toString()+" is " + ickMessage);
BufferedOutputStream bos = new BufferedOutputStream(connection.getOutputStream());
DataOutputStream osw = new DataOutputStream(bos);
osw.writeShort(Integer.parseInt(ickHeader));
osw.writeBytes(ickMessage);
osw.flush();
DataInputStream stream = new DataInputStream(connection.getInputStream());
int numberRecords = stream.readShort();
if (numberRecords > 0) {
int nSizeRead = 0;
byte[] bRequest = new byte[numberRecords];
int nSizeBuffer;
for (; numberRecords > 0; numberRecords -= nSizeBuffer) {
byte[] bBuffer = new byte[numberRecords];
nSizeBuffer = stream.read(bBuffer);
System.arraycopy(bBuffer, 0, bRequest, nSizeRead, nSizeBuffer);
nSizeRead += nSizeBuffer;
}
ickResponse = new String(bRequest);
log.debug("Response from ick is " + ickResponse);
}
}
catch (SocketTimeoutException e)
{
log.error(e.getMessage());
}
return ickResponse;
To understand what is going on you should understand what is listen socket and how it differs from connection socket.
When your application listens it (this ServerSocket does):
Attaches to the port that you specify in bind request or in constructor
Ask JVM to receive new connection on that port
When connection is received listen socket changes it state and provide you new socket for new connection with accept method.
When your application establishes NEW connection it use connect method. Unless you use bind request on socket it:
Allocates new dynamic port (54717 in your example)
Sends connect request to the server
After connection established you can use it for sending/receiving requests to/from server
Because nobody listens this dynamic port telnet requests are refused on it.

Socket exception - connection reset - can't replicate

UPDATE I've found that this error is related to the GZIP streams. If I remove them and just use Object streams rather than GZIP wrapped with Object streams then my issues are resolved, no exception. I have no idea why this is, if anyone can help that would be great.
I also noted while using the GZIP that the error would be very random. E.g it seemed to depend a lot on the data within the object that I was sending across the socket. Alter the object data and it would sometimes solve the issue. For example I had a user object which contained String values such as, first name, surname, phone number. If they were all set I was getting the below issue, however if I cleared them all to be empty String values then I wouldn't get the below exception. It's very bizarre to be honest
I have a client/server socket which sends object via GZIP streams. This has worked without issue in the past. However now I am seeing connection reset when sending certain data from the server back to the client.
The client connects to the server and sends a request, to which the server will reply and send back some data. I believe the error is from the server as the connection reset exception is presented on the client side.
Here is a snippet of the exception
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:168)
at com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:293)
at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:331)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:863)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:820)
at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
It's a very strange issue because the same socket implementation is used for all client / server communication and in our test environment everything works fine. Also the same code is used successfully before the exception occurs to collect some data from the server when the client loads the application on startup.
The above information leads me to believe there could be an issue with the specific data that is being passed, although the client & server both present no exception other than the connection reset.
Looking at netstat ( netstat -anp ) while debugging I can see successful actions result in a socket in the state of "CLOSE_WAIT", however unsuccessful ones the socket just seems to disappear all together, it's not there in any state. I also note that there is a lot of RST when the connections are finished, even for sucessful cases where I don't get connection reset exception. My TCP knowledge isn't great, but I would have thought a clean connection would end with FIN rather than getting RST?
Here is an example of the code
Client:
ObjectOutputStream out = null;
ObjectInputStream ois = null;
SSLSocketFactory sf = AdminClientApplet.getSSLContext().getSocketFactory();
Socket socket = null;
DataResponse dataRes = null;
try
{
socket = sf.createSocket( server.getHost(), server.getPort() );
// Need to connect to server and send request type,
// ie what we are requesting to be sent to us
GZIPOutputStream gZipOut = new GZIPOutputStream(socket.getOutputStream());
out = new ObjectOutputStream(gZipOut);
// write our request
out.writeObject( dataReq );
out.flush();
gZipOut.finish();
InputStream in = socket.getInputStream();
GZIPInputStream gZipIn = new GZIPInputStream(in, 65536);
ois = new ObjectInputStream(gZipIn);
// read our response
dataRes = ( DataResponse )ois.readObject();
Log.CSSO.debug("Read object " + dataRes );
}
catch (IOException ie)
{
Log.GUIP.error("IOException communicating with Admin Server on "+server, ie);
}
catch ( Exception e)
{
Log.GUIP.error("Unexpected exception communicating with Admin Server on "+server, e);
}
catch (Throwable t)
{
Log.GUIP.error("Unexpected exception communicating with Admin Server on "+server, t);
}
finally
{
// now close the connection gracefully
CSSO.debug("closing socket");
try
{
if ( out != null )
out.close();
if ( ois != null )
ois.close();
if ( socket != null )
socket.close();
}
catch ( Exception e )
{
Log.CSSO.error( "Error closing socket connection during getDataFromServer()", e );
}
}
The server has already read the 'request' at this point, so this is just the part that returns the requested data to the client.
Server:
Socket socket = null;
try
{
socket = request.getSocket();
socket.setTcpNoDelay(true);
Log.CSSO.debug("Opening a response output stream to socket "+socket );
gZipOut = new GZIPOutputStream(socket.getOutputStream(), 65536 );
out = new ObjectOutputStream(gZipOut);
// the actual 'data' we are going to return
obj = getObject();
Log.CSSO.debug("About to write " + obj + " to socket" + socket.getRemoteSocketAddress() );
out.writeObject( obj );
if (Log.CSSO.isDebugEnabled())
{
Log.CSSO.debug("Wrote DataResponse to socket " + obj );
}
out.flush();
gZipOut.finish();
}
catch (IOException ie)
{
Log.CSSO.error("IOException caught sending data to client", ie);
}
catch (Exception e)
{
Log.CSSO.error("Unexpected exception caught sending data to client", e);
}
finally
{
Log.CSSO.debug( "Closing socket to " + socket.getRemoteSocketAddress() );
try
{
if ( out != null ) out.close();
if ( socket != null ) socket.close();
}
catch ( IOException e )
{
Log.CSSO.error("Unexpected exception caught closing socket", e );
}
}
Log.FLOW.debug("< run");
When on debug logging the server runs all the way through to completion, without any exceptions ( so "< run" is written in the logs ). However the client errors at
ois = new ObjectInputStream(gZipIn);
The other item worth noting is that the live system, where the exception occours is running linux ( Centos ), where as I'm not able to replicate the exception on windows and it doesn't happen on Linux Mint either. I don't expect this would be the cause but just thought I should mention it.
Any help much appreciated as I'm lost as to the cause of the issue here.

html5 audio GET connection reset by peer on mobile (works fine on PC)

What I'm trying to do is simply send an mp3 file over http/tcp with my own http headers. On the client side I have a webpage with the following line:
<audio src="http://192.168.0.21:14441" controls autoplay loop>
The Java server side has a ServerSocket and accepts basically any connection. It will then send a hardcoded http header followed by the binary data of the mp3-file.
My server class:
public class Server {
private int port = 14441;
private String localIPAddress;
private BufferedReader in;
private BufferedOutputStream out;
private ServerSocket serverSocket;
private Socket clientSocket;
public Server() {
}
public void start() {
new Thread(new Runnable() {
#Override
public void run() {
startAcceptingConnections();
}
}).start();
}
private void startAcceptingConnections() {
tryToOpenPort();//try to open external port with upnp
clientSocket = null;
try {
serverSocket = new ServerSocket(port);
} catch (IOException e) {
System.err.println("Could not listen on port:" + port);
System.exit(1);
}
System.out.println("Waiting for connection.....");
try {
clientSocket = serverSocket.accept();
System.out.println("Connection successful");
System.out.println("Waiting for input.....");
out = new BufferedOutputStream(clientSocket.getOutputStream());
in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
boolean isSendingMp3 = false;
while ((inputLine = in.readLine()) != null) {
//just output any line the client sends me
System.out.println("Received: " + inputLine);
//Whenever the client sends an empty line this means
//it's ready to receive
if (inputLine.equals("") && !isSendingMp3) {
isSendingMp3 = true;
//Making sure to keep listening to the InputStream
//so I send from a different thread
new Thread(new Runnable() {
#Override
public void run() {
sendMp3File();
}
}).start();
}
}//end of listening to client loop
out.close();
in.close();
clientSocket.close();
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMp3File() {
try {
//I've tried all sorts of headers
String response = "HTTP/1.x 200 OK\r\n"
+ "Content-Type: audio/mpeg\r\n"
+ "Content-Size: 2911084\r\n"
+ "Range: bytes 0-2911083/2911084\r\n"
+ "X-Content-Duration: 300.1\r\n"
+ "Connection: keep-alive\r\n"
+ "Content-Duration: 300.1\r\n"
+ "\r\n";
out.write(response.getBytes(Charset.forName("UTF-8")));
byte[] bytesRaw = new byte[1024 * 10];
InputStream is = new FileInputStream("C:/sample.mp3");
int byteCount = 0;
while ((byteCount = is.read(bytesRaw)) != -1) {
System.out.println("sending bytes:" + byteCount);
out.write(bytesRaw, 0, byteCount);
}
out.flush();
is.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
//Use Cling to open the port via upnp
private void tryToOpenPort() {
try {
localIPAddress = Inet4Address.getLocalHost().getHostAddress();
PortMapping desiredMapping
= new PortMapping(
port,
localIPAddress,
PortMapping.Protocol.TCP,
"Test server"
);
UpnpService upnpService = new UpnpServiceImpl(new PortMappingListener(desiredMapping));
upnpService.getControlPoint().search();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
This always works on a PC browser (Firefox, Chrome, IE) and gives no problems machine to machine (no firewall interference).
However, as soon as I run the webpage from a mobile device (both iOS and Android) the connection is suddenly closed after sending what seems to be a random amount of data. This is somewhere between 0 and 2 seconds after the connection has been established.
The Java application throws the exception:
java.net.SocketException: Connection reset by peer: socket write error
When I profile with Wireshark it shows me everything goes well and then suddenly the client starts sending a bunch of RST messages. I've tried multiple types of headers, even copied a number of headers from existing webservers, but nothing seems to work.
Even simple headers like
"HTTP/1.1 200 OK\r\n"
+ "Content-Type: audio/mpeg \r\n"
+ "\r\n";
Work when I open them from a computer browser, but reset the connection on mobile. Am I forgetting something important?
UPDATE
On mobile browsers it closes the connection after a bit of data has been send. It connects and disconnects again at what seems like random intervals, sometimes with a range in the header and sometimes not. Even when it has received the entire file it will continue to open connections.
I'm guessing some sort of high protocol 'protection' when sending big requests, maybe specifically for when on unstable mobile networks.
Is there some way to bypass this? Whatever it is, it seems a bit unduly.
What happens is that the html5 audio element asks for the first 2 bytes with a range header. When I then (according to rfc2616 validly) ignore this range and send the whole file, the audio player starts behaving as if it's an audio stream (or at-least becomes very confused). This still only happens on mobile browsers somehow.
The solution might be to start accepting range request so that the player doesn't get "confused". I'll post the results as soon as I get the time to try this.
I think the problem is in the code you have not shown. My guess is that you accept the connection and then simple send the response without reading the request. But if the connections gets closed with the request not read this will cause a connection reset. How this reset affects the client depends on the timing, i.e. it might be that the client processed the response before it got the reset or that it found the reset before it had time to process the response.
To fix it you need to read the HTTP request before you sent the response.

Categories

Resources