I have a Netty TCP server, and I want to reject/refuse incoming connection attempts selectively (based on their remote address). I guess I have to use ServerBootstrap.setParentHandler(ChannelHandler), but what do I do in the ChannelHandler? What event am I handling? How do I refuse the connection?
As Norman said, there is no way to refuse the connection, but you can close it immediately by adding a Netty's IpFilterHandler to server pipeline as the first handler. It will also stop propagating the upstream channel state events for filtered connection too.
#ChannelHandler.Sharable
public class MyFilterHandler extends IpFilteringHandlerImpl {
private final Set<InetSocketAddress> deniedRemoteAddress;
public MyFilterHandler(Set<InetSocketAddress> deniedRemoteAddress) {
this.deniedRemoteAddress = deniedRemoteAddress;
}
#Override
protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) throws Exception {
return !deniedRemoteAddress.contains(inetSocketAddress);
}
}
if you have list of patterns of IP address to block, you can use IpFilterRuleHandler,
//Example: allow only localhost:
new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*"))
If you have several network interfaces and you want to accept connections from one interface only you just need to set the local address in ServerBootstrap. This may be enough if your server is running in a machine that's connected to several networks and you want to serve only one of them. In this case any connection attempts from the other networks would be refused by the OS.
Once you have a connection in the application layer it's too late to refuse it. The best you can do is close it immediately.
This is enough if for example you want the server available only on localhost and invisible to the outside world: the loopback network 127.0.0.0/8 is served by a separate interface.
After having looked at org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink in the Netty sources, I am fairly certain that Netty accepts all incoming connections, and there is no way to refuse them (but, of course, they can be closed after being accepted).
Related
I was going through VpnService.java from the ToyVpn project.
I see these following lines in the run method initially
// Create a DatagramChannel as the VPN tunnel.
tunnel = DatagramChannel.open();
// Protect the tunnel before connecting to avoid loopback.
if (!protect(tunnel.socket())) {
throw new IllegalStateException("Cannot protect the tunnel");
}
I understand that the first line creates a Datagram/UDP channel to connect to a remote server. But I do not understand what exactly the "protect" method is doing.
Could someone explain to me in as much technical detail. Thank you.
It is a function from the android.net.VpnService library that is imported in the ToyVpnService.java class as you can see in the source code
You can find what it does here
public boolean protect (int socket)
Added in API level 14
Protect a socket from VPN connections. After
protecting, data sent through this socket will go directly to the
underlying network, so its traffic will not be forwarded through the
VPN. This method is useful if some connections need to be kept outside
of VPN. For example, a VPN tunnel should protect itself if its
destination is covered by VPN routes. Otherwise its outgoing packets
will be sent back to the VPN interface and cause an infinite loop.
This method will fail if the application is not prepared or is
revoked.
The socket is NOT closed by this method.
Returns true on success.
I am implementing the TFTP (trivial FTP) protocol in Java. I have a client and a server, and so far the client can request a file and the server sends him that data.
Here is where I hit my problem - for obvious testing reasons, I run both the client and the server on my machine. However, when sending a file, there have to be two sockets listening on the same port:
the client needs to listen for received data packages
the server needs to listen for the client's acknowledgements
...and also two respective sockets for sending data and acknowledgements, also sharing a port.
This normally does happen on the same port, but on different machines. Is there a way to get around this, and make both client and server work peacefully on the same host, without any ugly hacks? And by ugly hacks I mean something like:
predefined offset for the ACK communication port (such as +15 over the data port; this is what I'm currently using now. It sort of works but it feels wrong and is error-prone)
repeatedly closing and opening sockets (send data, close socket used to send data so that the client can use that port to send his ACK, etc.); this also works at the moment, but also via a hack. Here is, for example, how I "reopen" a socket used for sending things:
public void open() {
try {
socket = new DatagramSocket(localPortCache);
} catch (SocketException e) {
e.printStackTrace();
}
}
This is evil. My sockets, originally, receive dynamically-allocated ephemeral port numbers. I then memorize that value and use it to "restore" my socket to the old port. However, it can't be guaranteed that that port is still available. It usually is, but there's no guarantee. Am I being overly-paranoid in this case?
generating a new ACK-communication port in the handshake and send it to the client via the control port (69) in an additional step
Update:
I have managed to fix my problem. My issue was that I wasn't trying to reuse my sockets. For instance, I sent something from a socket on port X, but then tried to allocate a new socket on that port to listen for the ACK, instead of just reusing the old socket.
The client doesn't need to use a fixed port number. Just bind it to zero. The server should send the response back to the originating client whatever its port number.
If I make a connection using new Socket("unit.domain.com", 100) and the unit.domain.com DNS record has multiple IP addresses in the A record.. In the event of a failed connection, Does Java automatically connect to one of the other addresses in the list like the browser does? or does that have to be implemented manually?
No!
Creating a socket via new Socket(String, int) results in a resolving like that
addr = InetAddress.getByName(hostname);
which is a shortcut for
return InetAddress.getAllByName(host)[0];
The address resolution is performed in the Socket c-tor.
If you have to reconnect (failover) use the result returned by InetAddress.getAllByName(host), randomize (or use round-robin) and connect to the necessary addresses.
Edit: also if you are going to need to connect with some likely failure, you'd be better off using connect method of the Socket class with a timeout. Also make sure you close even failed sockets (and esp. channels) since they may leak a FD on *Nix.
For those who do not want to read a long question here is a short version:
A server has an opened socket for a client. The server gets a request to open a socket from
the same client-IP and client-port. I want to fore the server not to refuse such a request but to close the old socket and open a new one. How can I do ti?
And here is a long (original) question:
I have the following situation. There is an established connection between a server and client. Then an external software (Bonjour) says to my client the it does not see the server in the local network. Well, client does nothing about that because of the following reasons:
If Bonjour does not see the server it does not necessarily means that client cannot see the server.
Even if the client trusts the Bonjour and close the socket it does not improve the situation ("to have no open socket" is worser that "to have a potentially bad socket").
So, client do nothing if server becomes invisible to Bonjour. But than the server re-appears in the Bonjour and Bonjour notify the client about that. In this situation the following situations are possible:
The server reappears on a new IP address. So, the client needs to open a new socket to be able to communicate with the server.
The server reappears on the old IP address. In this case we have two subcases:
2.1. The server was restarted (switched off and then switched on). So, it does not remember the old socket (which is still used by the client). So, client needs to close the old socket and open a new one (on the same server-IP address and the same server-port).
2.2. We had a temporal network problem and the server was running the whole time. So, the old socket is still available for the use. In this case the client does not really need to close the old socket and reopen a new one.
But to simplify my life I decide to close and reopen the socket on the client side in any case (in spite on the fact that it is not really needed in the last described situation).
But I can have problems with that solution. If I close the socket on the client side and than try to reopen a socket from the same client-IP and client-port, server will not accept the call for a new socket. The server will think that such a socket already exists.
Can I write the server in such a way, that it does not refuse such calls. For example, if it (the server) sees that a client send a request for a socket from the same client-IP and client-port, it (server) close the available socket, associated with this client-IP and client-port and than it reopens a new socket.
You can't "reopen" a socket on your server. If the socket already exists and the client is trying to reconnect then you should get an BindException (see your previous question). The scenario that may be possible:
Client Shuts down socket
Server OS "notices" socket is dead on client side and shuts its side down
Client reconnects on the same port, but with a "new" socket
In this case you may consider it be the "same" socket, but it really isn't. That said a strategy you may wish to adopt is to have some sort of map (hash of client IP/port) to whatever mechanism you are using to service the socket or some kind of persistent state data, so that it can simulate a continuation of a previous socket (in the same vein as http sessioning). Something along the lines of:
HashMap<Client, State> sessions = ...;
public void server(){
...
while(true){
Socket socket = server.accept();
Client client = new Client(socket);
State s = sessions.get(client);
if(s == null){
s = new State();
sessions.put(client, s);
}
client.setState(s);
service(client);
}
...
}
and you can adjust the map lookup to define what a "session" means within your application (same client IP, same client IP & client port, some sessionid sent over the wire, etc).
If you are just trying to make it possible for the client to reconnect and force the server to "notice" the client is disconnected, the only real way in Java is to try and read/write data, and if it has been shutdown then it should throw an exception. Therefore as was mentioned in your other question you could add some kind of ack/nak feature to your protocol and add some type of check if you believe the client is disconnected (for example if you haven't read any data in the last N milliseconds, send a message the client must echo within M milliseconds, otherwise it is assumed to be disconnected). You can also try isConnected, isInputShutdown, isOutputShutdown, but I have found those to be unreliable in my own code to indicate the socket state, unless you have closed the socket (i.e. the one you are testing on the server).
The situation you describe is impossible. You can't get a new connect request from the same remote IP:port as an existing connection. The client will not permit it to occur.
Based on the comments:
You cannot write the server in a way that it will close a socket it still thinks is connected and automatically accept the new connection, as application code does not have that kind of control over the TCP stack, nor is there a way to reopen a connection.
The chance of the port numbers being the same between your client restarts is very small.
But still, if that happens, the server will note that that you're trying to set up an already connected socket, and refuse your new connection. There's not much else your client can do in this case besides close your socket, create a new one and try to connect again - and another random port will be selected.
additional note, your server should take some form of action to detect and close dead sockets, if all your server does is read incoming data, the "dead" sockets will never be
closed as they will never be detected as dead.(enabling tcp keepalive is one cheap measure to take against dead sockets staying up for months, though it will take a couple of hours to detect them as such by default.)
I have the following problem. My client program monitor for availability of server in the local network (using Bonjour, but it does not rally mater). As soon as a server is "noticed" by the client application, the client tries to create a socket: Socket(serverIP,serverPort);.
At some point the client can loose the server (Bonjour says that server is not visible in the network anymore). So, the client decide to close the socket, because it is not valid anymore.
At some moment the server appears again. So, the client tries to create a new socket associated with this server. But! The server can refuse to create this socket since it (server) has already a socket associated with the client IP and client port. It happens because the socket was closed by the client, not by the server. Can it happen? And if it is the case, how this problem can be solved?
Well, I understand that it is unlikely that the client will try to connect to the server from the same port (client port), since client selects its ports randomly. But it still can happen (just by chance). Right?
Yes, close the socket, as soon as you detect a failure.
The socket will be "stuck" in "close_wait" if not closed properly.
Even if the socket is closed, it's state will be in time_wait for a short period.
However, if You design the application to use a different local port for each new connection, there is no need to wait for the old socket to be closed.
(As you are then creating a completly different socket, since a socket is identified by the remote-ip, remote port, local ip and local port.)
A quick/ dirty illustration of why this can't happen (note the client forcibly uses the same local port in its connection):
public class Server{
public static void main(String[] args) throws Exception {
new Thread(){
java.net.ServerSocket server = new java.net.ServerSocket(12345);
java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>();
public void run() {
try{
while(true){
java.net.Socket client = server.accept();
System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort());
l.add(client);
}
}catch(Exception e){e.printStackTrace();}
}
}.start();
}
and a client (replace server address with something valid):
import java.net.InetAddress;
import java.net.Socket;
public class SocketTest {
public static void main(String[] args) throws Exception {
InetAddress server = InetAddress.getByName("192.168.0.256");
InetAddress localhost = InetAddress.getLocalHost();
Socket s = new Socket(server, 12345, localhost, 54321);
System.out.println("Client created socket");
s.close();
s = null;
System.gc();
System.gc();
Thread.sleep(1000);
s = new Socket(server, 12345, localhost, 54321);
System.out.println("Client created second socket");
s.close();
System.exit(55);
}
}
If you start the server and then try to run the client the first connection will succeed, but the second will fail with a "java.net.BindException: Address already in use: connect"
Short answer: yes, you should close socket on both ends.
Although the answer is simple, in reality it may be very hard to detect that the peer has stopped responding if you don't build some ACK/NACK scheme into your client-server protocol.
Even with your protocol ACKs your processing thread may be hanging waiting for ACKs that will never come from the client or vice versa.
If you use Blocking I/O, I would start by setting read timeouts on a socket. Unfortunately, if the peer becomes unresponsive, there is no corresponding time-out for the writes.
One blunt instrument that I found has value in our environment is to create blocking Sockets through java.nio methods and then interrupt the processing thread at configurable intervals.
Interrupting processing thread will close socket, but if you pick the timeout large enough you will know that there is a trouble. We chose this approach because application was written initially with blocking I/O and a cost to transition it to non-blocking was very high.
With the non-blocking I/O, though, you may check at a finer-grained interval the status of your connection and react more intelligently to the slow/unresponsive connections.
Though non-blocking I/O requires a higher upfront investment, I think it will pay better dividends in terms of reliability and better throughput later on.
the client operating system will not allocate the same port to a new socket so soon. there are several mechanism that prevents it. one of which is the TIME_WAIT state that reserves the port for some time after the connection is closed.
I wouldn't worry about it.
if you really need to detect disconnection you will have to implement ping/pong protocol, initiated by both the client and the server.
It sounds like your client is detecting loss of connectivity to the server (using Bonjour), but you don't have the corresponding capability in the other direction.
You're certainly going to want some kind of timeout for inactive connections on the server side as well, otherwise dead connections will hang around forever. Beyond the problem of potential IP address/port # collisions you mention, there's also the fact that the dead connections are consuming OS and application resources (such as open file descriptors)
Conversely, you might also want to consider not being too aggressive in closing a connection from the client side when Bonjour says the service is no longer visible. If you're in a wireless scenario, a transient loss of connectivity isn't that uncommon, and it's possible for a TCP connection to remain open and valid after connectivity is restored (assuming the client still has the same IP address). The optimum strategy depends on what kind of connection you're talking about. If it's a relatively stateless connection where the cost of discarding the connection and retrying is low (like HTTP), then it makes sense to toss the connection at the first sign of trouble. But if it's a long-lived connection with significant user state (like an SSH login session), it makes sense to try harder to keep the connection alive.
If you close server socket only in case of blocking socket then client socket will be closed but not vice versa.
otherwise it would be better socket in both end. Because socket is a heavy weigt for you system. It will use a local port and a remote port of your system forever.
Thanks
Sunil Kumar Sahoo