Java: How to abstract between "regular" TCP Socket and SSLSocket - java

Edit: Removed startHandshake(); as it's irrelevant to the question and rarely needed (for example, not in my case)
I have a rather specific and rare client-server protocol (over TCP).
I've implemented it using SSLSocket.
Now, I foresee that I might need to use the same protocol over an un-encrypted connection.
My problem is that the class implementing the protocol has a field: public SSLSocket currentSocket;
(and then the methods in my client class do all sorts of .read(), .write(), flush()...)
I thought about changing the field type, like so: public Socket currentSocket;
However, then, the problem is that my connection procedure is incompatible:
public static void connect () {
currentSocket = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
...
java.net.Socket 's default constructor obviously doesn't accept keystore stuff
I don't want to re-implement my whole client just for this difference...
One thought I have is, when I need a plaintext Socket, to create an SSLSocket with no encryption.
I don't know if that's a professional way of doing it or if it will even work (the server will expect a plaintext client socket in the new use case)
My other idea is to define two fields, one for plaintext socket, one for SSL socket and then use logic to link the in/out streams to the correct field, as needed. However, that will result in a "hanging" field. If you use SSL, there will be a redundant field Socket plaintextSocket and vice-versa...
Is there a way to make my currentSocket field more abstract, so that I can define it in the same client, then instruct a slightly different client code path depending on a known variable (something like needSSLsocket=true) for the instantiation and connection?

SSLSocket extends Socket, so you can assign an SSLSocket object to a Socket variable. You are right to change your currentSocket field to a Socket. Simply use another variable to handle the SSLSocket when needed, eg:
public static void connect () {
if (needSSLsocket) {
SSLSocket ssl = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
ssl.startHandshake();
...
currentSocket = ssl;
/* or:
currentSocket = SslUtils.getSSLsocket(host, port, keystoreFile, keystorePass, pkPass);
((SSLSocket) currentSocket).startHandshake();
...
*/
} else {
currentSocket = new Socket(host, port);
}
...
}

Related

How to accept connections from local network only in Java

I have a ServerSocket object that is listening with accept method. How do I accept connections only coming from the same network and not outside?
If your system has multiple network interfaces and one of them is receiving local connections only while others should be ignored, you can set up a ServerSocket with the IP-address of the netork interface to bind it only to that particular interface:
ServerSocket ssocket = new ServerSocket(1234, 10, InetAddress.getByAddress("192.168.1.1"));
With IPv4 and IPv6 you obviously need two ServerSockets.
If local and remote connections are received from the same network interface, you have to check the remote peer yourself. This can be done in different ways:
You can set a SecurityManager that implements checkAccept and throws a SecurityException if the connection attempts comes from the wrong place. This is a global setting, so with this you can't set up a ServerSocket at some other part of the application that should accept connections from there.
You can check the IP and port of the Socket being returned from ServerSocket to be local and close the socket if it's not instead of continuing with the request
You can create a subclass of ServerSocket overriding the method checkAccept where call super.accept, do the corresponding check as described in option 2 and only return the retrieved connection if it fit to your criterias or otherwise close it and call the super-method again.
Create the ServerSocket via the constructor that accepts a local IP address, and supply 127.0.0.1 as that IP address.

Apache InternalHttpClient: Determining local port number

I am trying to debug http client request. I need to get the local port number that was used when the connection was made. I can use the HttpClientContext to get the connection, and retrieve the local port when the connection is successful. However, in cases where the IOExceptions are thrown, I get a ConnectionShutdownException when retrieving the local port number. Any clue on how I can obtain the local port number for all http requests in case of error.
This is for HTTPClient 4.0.1 (last version I have with me).
I did not find any simple one liner...
The part of http client that actually binds sockets to local host/port and connects to the remote host/port is, unsurprisingly, the SocketFactory. In HTTPClient, the socket factory is associated to a SchemeRegistry, that in turns belongs to the connection manager. Note that the HTTPClient's SocketFactory is NOT a javax.net.SocketFactory, but a wrapper around such an object. You define a scheme like so :
SchemeRegistry schRgstr = new SchemeRegistry();
Scheme httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
schRgstr.register(httpScheme);
Of course, org.apache.http.conn.scheme.SocketFactory is an Interface, so you can decorate it to do anything you want in particular, this will come in handy.
The part of httpclient that calls the socket factory is called the ClientConnectionOperator (which is an interface too). This object is actually also tied to the connection manager, and not the client per se. So if you want to customize the connection operator, you may override the connection manager too, for example, like so (anonymous class) :
ThreadSafeClientConnManager connMngr = new ThreadSafeClientConnManager(httpParams, schRgstr) {
protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
return new YourConnectionOperator(schreg);
};
};
The lifecycle model of the socket goes like so:
When the connection manager needs a now connection, it calls createConnection on the operator (which basically does nothing but create an internal object that eventually will hold the actual socket).
Further along the way it calls openConnection
openConnection goes to the SocketFactory and asks for a new Socket, then tries to connect it like so (here, sf is the "httpclient socket factory")
sock = sf.connectSocket(sock, target.getHostName(),
schm.resolvePort(target.getPort()),
local, 0, params);
If this call fails, an exception is thrown and more information will not be accessible. We'll get back to that.
If the connectSocket works, though, the prepareSocket method is called on the connection operator. And so, you can override the method and put the port information into the context, (or anything else you fancy) :
#Override
protected void prepareSocket(Socket sock, HttpContext context, HttpParams params) throws IOException {
super.prepareSocket(sock, context, params);
context.setAttribute("LOCAL PORT INTERCEPTOR", sock.getLocalPort());
}
The HttpContext instance that is used is passed when you invoke HTTPClient, so you can access it even if the call fails later, because of some other exception. When you place your call, make it so:
HttpContext ctx = new BasicHttpContext();
HttpGet get = new HttpGet(targetUrl.getUrl());
HttpResponse resp = client.execute(get, ctx);
In this code, if the client could go as far as step 5, you have your port info accessible, even if an exception occurs later on (connection drop out, timeout, invalid HTTP, ...).
Going further
There is still a dark zone in all this : if the call fails at step 4 (actual opening of the socket, like if you have a DNS error while resolving the destination host name)... I'm not sure this case is actually interesting to you, (I can not see why it would be). Seeing dealing with it starts to get "messy", you should really consider wether or not you need this.
For that we need to start overriding really deep, and that involves lots of work - some of it I would not consider very good design.
The complexity arises because authors of HTTPClient did not provide the necessary methods one could override to get to the information you need. Inside the socket factory, the interesting point is :
sock = createSocket();
InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);
sock.bind(isa);
// go on and connect to the server
// like socket.connect...
There is no method to override that splits the local and the server side of the socket openning, so that if any socket exception is thrown on the server side, your access to the socket instance is lost, and the local port info gone with it.
But all is not lost because we do have one entry point we can play with : the createSocket method! Default implementation is
public Socket createSocket() {
return new Socket();
}
But as Socket is not a final class, you can... play with it !
public Socket createSocket() {
return new Socket() {
#Override
public void bind(SocketAddress bindpoint) throws IOException {
super.bind(bindpoint);
// get the local port and give the info back to whomever you like
}
};
}
Problem is : this works with plain sockets (because we can create an anonymous subclass), but this does not work with HTTPS, because you can not simply instanciate a Socket on this case, you have to do :
return (SSLSocket) this.javaxNetSSLSocketFactory.createSocket();
And you can not create an anonymous subclass in that case. And as Socket is no interface either, you can not even proxy it to decorate it. So you could (uglyest code ever) create a Socket subclass that wraps and delegates to the SSLSocket, but that'd be desperate.
So, recap time.
If we only care about sockets that were at some point connected to the server, the solution is fairly simple.
A scheme registry we builb is used in a custom ConnectionManager that overrides the ConnectionOperator. The operator's overriden methods is prepareSocketthat allows us to simply update the HttpContext of any request we send with the port information. Which is an easy way to find the information when everything goes well.
If we want to care about local ports attributed to a socket that never, ever, got connected (which I would argue is of limited use), one need to go deeper.
Our own SchemeRegistry should be designed to have custom SocketFactories. These should probably be decorators or the default ones... the createSocket overriden method allows us to "intercept" the binding on the local port (and store this into maybe a ConcurrentMap<Socket, Integer> or a ThreadLocal), and by overriding 'connectSocket' we trap any exception that may happen, and rethrow it, but not before wrapping it in our own exception type that can hold local port information. That way, if the exceptions passes though when you call the client, by checking the cause chain, you will find your port data. If no exception occurs, we clean or map instance / thread local.

In Java, must non-serializable Sockets prevent use of a client-server Observer pattern?

In my Java Sockets program, I have implemented a client-server Observer pattern. That is, the server subject publishes its server events to client observers that have subscribed to the server. That's the theory.
In practice, I am unable to send the client observer through the socket to subscribe to the server. The error that comes up is: "java.io.NotSerializableException: java.net.Socket." I think this means Java is complaining that the client observer contains a Socket which, as we all know, is not Serializable.
However, the Socket is the means of communication between the client and the server!
How can I implement a client-server Observer pattern when the client appears to contain a non-Serializable roadblock?
Here is a code overview to give you an understanding of what is happening:
Server
public class Server implements ServerSocketPublisher {
// traditional Observer publisher methods implemented here, such as register,
// deregister, notifySubscribers
// ServerSocket implemented here. Waiting on accept()
}
Client
public class Client implements ClientSocketSubscriber, Serializable {
// traditional Observer subscriber methods implemented here, i.e. updateClient
Socket connectingSocket = null; //I SUSPECT THIS VARIABLE IS THE PROBLEM
try {
connectingSocket = new Socket();
// set SocketAddress and timeout
connectingSocket.connect(sockAddr, timeout)
if (connectingSocket.isConnected()) {
ObjectOutputStream oos = new ObjectOutputStream
(connectingSocket.getOutputStream());
oos.writeObject(this); // THIS LINE THROWS THE ERROR in STACKTRACES
oos.flush();
oos.close();
}
} catch (/*various exceptions*/) {
}
// close connectingSocket
}
You have couple of ways to get this fixed:
Mark your socket as transient
transient Socket connectingSocket = null;
Instead of implementing Serializable implement Externalizable and then in your implementation of read and write object ignore the Socket.
Along with this you should also read
About transient:
Post on SO
About Externalizable :
Javabeat
you cannot write the Client to the output stream socket since it contains a Socket. If you serialize the Client, you serialize all non-transient vars in it, and thats when you get the exception.
However, the server already has the socket on its side, so you don't need to send it and the client across. If all clients are observers once the connection has occurred you can pretty much at that point start waiting for data from the socket on the client side. The server will need to keep a list of sockets its ready to broadcast to, and when it gets an event to send, loop over all sockets and send the register, deregister, notifySubscriber messages
Alternatively if you wish to treat the client as an object on the server side and call methods on it (which it looks like you might be trying to do), maybe you need to look into RMI - where the server holds stubs of the client and invoking the stub sends messages to the client.

Socket connection on the client side

I have a questions that is perhaps indicative of my lack in experience and the fact that I am still a student.
I established a socket connection client side(server is already running) and after making the connection on the client side I immediately go to a different Form(that is also based on the client side) where I want to verify userName and password against database on the server side. Problem is, I feel that I do not want to make the connection again as I have already done this on the previous Form
clientSocket = new Socket(hostAdress, 7777);
How can I 'carry over' the fact that I have a connection already to the new form so that I just create and input and output stream without making the connection again on the new form.
Sorry, hope this question makes sense
Kind regards
Arian
Create a method like this:
public Socket getSocket() {
return clientSocket;
}
and call it from the other class (assuming that you have a reference to that object.
or static variable:
private static Socket clientSocket = new Socket(hostAdress, 7777);
and as Binyamin wrote, create a method , but in this case it would be static method

How do I ensure that RMI uses only a specific set of ports?

In our application, we are using RMI for client-server communication in very different ways:
Pushing data from the server to the client to be displayed.
Sending control information from the client to the server.
Callbacks from those control messages code paths that reach back from the server to the client (sidebar note - this is a side-effect of some legacy code and is not our long-term intent).
What we would like to do is ensure that all of our RMI-related code will use only a known specified inventory of ports. This includes the registry port (commonly expected to be 1099), the server port and any ports resulting from the callbacks.
Here is what we already know:
LocateRegistry.getRegistry(1099) or Locate.createRegistry(1099) will ensure that the registry is listening in on 1099.
Using the UnicastRemoteObject constructor / exportObject static method with a port argument will specify the server port.
These points are also covered in this Sun forum post.
What we don't know is: how do we ensure that the client connections back to the server resulting from the callbacks will only connect on a specified port rather than defaulting to an anonymous port?
EDIT: Added a longish answer summarizing my findings and how we solved the problem. Hopefully, this will help anyone else with similar issues.
SECOND EDIT: It turns out that in my application, there seems to be a race condition in my creation and modification of socket factories. I had wanted to allow the user to override my default settings in a Beanshell script. Sadly, it appears that my script is being run significantly after the first socket is created by the factory. As a result, I'm getting a mix of ports from the set of defaults and the user settings. More work will be required that's out of the scope of this question but I thought I would point it out as a point of interest for others who might have to tread these waters at some point....
You can do this with a custom RMI Socket Factory.
The socket factories create the sockets for RMI to use at both the client and server end so if you write your own you've got full control over the ports used. The client factories are created on the server, Serialized and then sent down to the client which is pretty neat.
Here's a guide at Sun telling you how to do it.
You don't need socket factories for this, or even multiple ports. If you're starting the Registry from your server JVM you can use port 1099 for everything, and indeed that is what will happen by default. If you're not starting the registry at all, as in a client callback object, you can provide port 1099 when exporting it.
The part of your question about 'the client connections back to the server resulting from callbacks' doesn't make sense. They are no different from the original client connections to the server, and they will use the same server port(s).
Summary of the long answer below: to solve the problem that I had (restricting server and callback ports at either end of the RMI connection), I needed to create two pairs of client and server socket factories.
Longer answer ensues:
Our solution to the callback problem had essentially three parts. The first was the object wrapping which needed the ability to specify that it was being used for a client to server connection vs. being used for a server to client callback. Using an extension of UnicastRemoteObject gave us the ability to specify the client and server socket factories that we wanted to use. However, the best place to lock down the socket factories is in the constructor of the remote object.
public class RemoteObjectWrapped extends UnicastRemoteObject {
// ....
private RemoteObjectWrapped(final boolean callback) throws RemoteException {
super((callback ? RemoteConnectionParameters.getCallbackPort() : RemoteConnectionParameters.getServerSidePort()),
(callback ? CALLBACK_CLIENT_SOCKET_FACTORY : CLIENT_SOCKET_FACTORY),
(callback ? CALLBACK_SERVER_SOCKET_FACTORY : SERVER_SOCKET_FACTORY));
}
// ....
}
So, the first argument specifies the part on which the object is expecting requests, whereas the second and third specify the socket factories that will be used at either end of the connection driving this remote object.
Since we wanted to restrict the ports used by the connection, we needed to extend the RMI socket factories and lock down the ports. Here are some sketches of our server and client factories:
public class SpecifiedServerSocketFactory implements RMIServerSocketFactory {
/** Always use this port when specified. */
private int serverPort;
/**
* #param ignoredPort This port is ignored.
* #return a {#link ServerSocket} if we managed to create one on the correct port.
* #throws java.io.IOException
*/
#Override
public ServerSocket createServerSocket(final int ignoredPort) throws IOException {
try {
final ServerSocket serverSocket = new ServerSocket(this.serverPort);
return serverSocket;
} catch (IOException ioe) {
throw new IOException("Failed to open server socket on port " + serverPort, ioe);
}
}
// ....
}
Note that the server socket factory above ensures that only the port that you previously specified will ever be used by this factory. The client socket factory has to be paired with the appropriate socket factory (or you'll never connect).
public class SpecifiedClientSocketFactory implements RMIClientSocketFactory, Serializable {
/** Serialization hint */
public static final long serialVersionUID = 1L;
/** This is the remote port to which we will always connect. */
private int remotePort;
/** Storing the host just for reference. */
private String remoteHost = "HOST NOT YET SET";
// ....
/**
* #param host The host to which we are trying to connect
* #param ignoredPort This port is ignored.
* #return A new Socket if we managed to create one to the host.
* #throws java.io.IOException
*/
#Override
public Socket createSocket(final String host, final int ignoredPort) throws IOException {
try {
final Socket socket = new Socket(host, remotePort);
this.remoteHost = host;
return socket;
} catch (IOException ioe) {
throw new IOException("Failed to open a socket back to host " + host + " on port " + remotePort, ioe);
}
}
// ....
}
So, the only thing remaining to force your two way connection to stay on the same set of ports is some logic to recognize that you are calling back to the client-side. In that situation, just make sure that your factory method for the remote object calls the RemoteObjectWrapper constructor up top with the callback parameter set to true.
I've been having various problems implementing an RMI Server/Client architecture, with Client Callbacks. My scenario is that both Server and Client are behind Firewall/NAT. In the end I got a fully working implementation. Here are the main things that I did:
Server Side , Local IP: 192.168.1.10. Public (Internet) IP 80.80.80.10
On the Firewall/Router/Local Server PC open port 6620.
On the Firewall/Router/Local Server PC open port 1099.
On the Router/NAT redirect incoming connections on port 6620 to 192.168.1.10:6620
On the Router/NAT redirect incoming connections on port 1099 to 192.168.1.10:1099
In the actual program:
System.getProperties().put("java.rmi.server.hostname", IP 80.80.80.10);
MyService rmiserver = new MyService();
MyService stub = (MyService) UnicastRemoteObject.exportObject(rmiserver, 6620);
LocateRegistry.createRegistry(1099);
Registry registry = LocateRegistry.getRegistry();
registry.rebind("FAManagerService", stub);
Client Side, Local IP: 10.0.1.123 Public (Internet) IP 70.70.70.20
On the Firewall/Router/Local Server PC open port 1999.
On the Router/NAT redirect incoming connections on port 1999 to 10.0.1.123:1999
In the actual program:
System.getProperties().put("java.rmi.server.hostname", 70.70.70.20);
UnicastRemoteObject.exportObject(this, 1999);
MyService server = (MyService) Naming.lookup("rmi://" + serverIP + "/MyService ");
Hope this helps.
Iraklis

Categories