If a Java application creates a ServerSocket that accepts TCP connections, is there a way to restrict which processes are allowed to connect to it?
For example, this is my current code:
ServerSocket serverSocket = new ServerSocket(1234);
Socket socket = serverSocket.accept();
and I want to make sure that other devices on my network and even other processes running on the same machine are not able to connect to it (it would be a security risk if they did). I was able to solve the former by binding serverSocket only to the loopback address (checking if socket.getRemoteAddress() points to the local host would work too) but I couldn't find a way to restrict it to my current process.
This is even more of a problem when doing it on Android. In my application, I want to create a WebView (owned by my process) and point it to serverSocket but I don't want others apps to be able to connect to it.
Is there a way to solve this problem?
You can bind it to 127.0.0.1,[1] which prevents any process outside the localhost from even seeing it. But any process in the localhost can connect to it. That being what it's for. If you want to restrict that to certain processes you will have to implement an authentication step in your protocol.
Hard to see why. If you can't trust other processes in the localhost you have a rather large problemm in general, not just here.
[1] Or indeed 127.0.0.x where 1 <= x <= 254. Using an obscure number like 200 might help by obscurity but it still isn't really secure.
I don't think that you can prevent other processes from connecting to the ServerSocket but you accept a connection you can definitely determine if it belongs to you or to some other process. The first step is figure out if the connection originated from localhost:
InetSocketAddress remoteAddress = (InetSocketAddress) socket.getRemoteSocketAddress();
String hostname = remoteAddress.getHostName();
if (!hostname.equals("localhost")) { socket.close(); }
Alternatively you can bind the socket to a loopback address like 127.0.0.1 or 0.0.0.0 (like EJP mentioned) and skip this step. Once you know that the connection came from localhost all you have to do is find the remote port and figure out if your process owns it.
int remotePort = remoteAddress.getPort();
if (ownPort(remotePort) == 1) { socket.close(); }
As far as I know, Java doesn't have an API that you can use to list your process ports but you can definitely do that via JNI. On the Java side you would need something like:
private native int doOwnPort(int port);
And on the native side:
JNIEXPORT jint JNICALL Java_something_doOwnPort(JNIEnv *env, jobject object, jint port) {
long totalFDs = getdtablesize();
struct sockaddr_in sa;
struct stat sb;
// Iterate through all file descriptors
for (int i = 0; i < totalFDs; ++i) {
// Check if "i" is a valid FD
memset(&sb, 0, sizeof(sb));
if (fstat(i, &sb) < 0)
continue;
// Check if "i" is a socket
if (!S_ISSOCK(sb.st_mode))
continue;
// Get local address of socket with FD "i"
memset(&sa, 0, sizeof(sa));
socklen_t sa_len = sizeof(sa);
getsockname(i, (struct sockaddr*) &sa, &sa_len);
// Check if the port matches
if (sa.sin_port == port)
return 1; // We own the port
}
return -1; // We don't own the port
}
PS: This code is for Linux but should work on Android/Windows/OSX too.
Maybe there is a more direct/efficient way to check if the port is owned by the current process without having to iterate through the FD table but that's a separate problem. HTH!
You can get this type of security
using firewalls
implementing some kind of authentication yourself on your ServerSocket. Ask for username and password?
Sockets were not designed for restricting to distinct processes.
From Android Security Tips:
We have seen some applications use localhost network ports for handling sensitive IPC. We discourage this approach since these interfaces are accessible by other applications on the device. Instead, you should use an Android IPC mechanism where authentication is possible such as with a Service. (Even worse than using loopback is to bind to INADDR_ANY since then your application may receive requests from anywhere.)
Related
This question already has answers here:
Java socket API: How to tell if a connection has been closed?
(9 answers)
Closed 5 years ago.
When I'm using e.g. PuTTY and my connection gets lost (or when I do a manual ipconfig /release on Windows), it responds directly and notifies my connection was lost.
I want to create a Java program which monitors my Internet connection (to some reliable server), to log the date/times when my internet fails.
I tried use the Socket.isConnected() method but that will just forever return "true". How can I do this in Java?
Well, the best way to tell if your connection is interrupted is to try to read/write from the socket. If the operation fails, then you have lost your connection sometime.
So, all you need to do is to try reading at some interval, and if the read fails try reconnecting.
The important events for you will be when a read fails - you lost connection, and when a new socket is connected - you regained connection.
That way you can keep track of up time and down time.
Even though TCP/IP is "connection oriented" protocol, normally no data is sent over an idle connection. You can have a socket open for a year without a single bit sent over it by the IP stack. In order to notice that a connection is lost, you have to send some data on the application level.(*) You can try this out by unplugging the phone cable from your ADSL modem. All connections in your PC should stay up, unless the applications have some kind of application level keepalive mechanism.
So the only way to notice lost connection is to open TCP connection to some server and read some data from it. Maybe the most simple way could be to connect to some FTP server and fetch a small file - or directory listing - once in a while. I have never seen a generic server which was really meant to be used for this case, and owners of the FTP server may not like clients doing this.
(*) There is also a mechanism called TCP keepalive but in many OS's you have to activate it for all applications, and it is not really practical to use if you want to notice loss of connection quickly
If the client disconnects properly, a read() will return -1, readLine() returns null, readXXX() for any other X throws EOFException. The only reliable way to detect a lost TCP connection is to write to it. Eventually this will throw an IOException 'connection reset', but it takes at least two writes due to buffering.
Why not use the isReachable() method of the java.net.InetAddress class?
How this works is JVM implementation specific but:
A typical implementation will use ICMP ECHO REQUESTs if the privilege can be obtained, otherwise it will try to establish a TCP connection on port 7 (Echo) of the destination host.
If you want to keep a connection open continually so you can see when that fails you could connect to server running the ECHO protocol yourself rather than having isReachable() do it for you and read and write data and wait for it to fail.
You might want to try looking at the socket timeout interval. With a short timeout (I believe the default is 'infinite timeout') then you might be able to trap an exception or something when the host becomes unreachable.
Okay so I finally got it working with
try
{
Socket s = new Socket("stackoverflow.com",80);
DataOutputStream os = new DataOutputStream(s.getOutputStream());
DataInputStream is = new DataInputStream(s.getInputStream());
while (true)
{
os.writeBytes("GET /index.html HTTP/1.0\n\n");
is.available();
Thread.sleep(1000);
}
}
catch (IOException e)
{
System.out.println("connection probably lost");
e.printStackTrace();
}
Not as clean as I hoped but it's not working if I leave out the os.writeBytes().
You could ping a machine every number of seconds, and this would be pretty accurate. Be careful that you don't DOS it.
Another alternative would be run a small server on a remote machine and keep a connection to it.
Its probably simpler to connect to yahoo/google or somewhere like this.
URL yahoo = new URL("http://www.yahoo.com/");
URLConnection yc = yahoo.openConnection();
int dataLen = yc.getContentLength() ;
Neil
The isConnected()method inside Socket.java class is a little misleading. It does not tell you if the socket is currently connected to a remote host (like if it is unclosed). Instead, it tells you whether the socket has ever been connected to a remote host. If the socket was able to connect to the remote host at all, this method returns true, even after that socket has been closed. To tell if a socket is currently open, you need to check that isConnected() returns true and isClosed() returns false.
For example:
boolean connected = socket.isConnected() && !socket.isClosed();
I don't find any information on this topic on the internet and asked here. For example I have server with IP 1.1.1.1 and 2.2.2.2 and two domain names pointing to it one.example.com and example2.net, and socke listening on port 1234 for incoming connections.
For example:
C/C++:
listenfd=socket(AF_INET, SOCK_STREAM, 0);
bind(...);
listen(...);
while(...) accept(...);
or Java:
ServerSocket socket = new ServerSocket(1234);
while(...) {
Socket connectionSocket = welcomeSocket.accept();
...
}
When client accepted on my socket I need to know what domain name/IP is used by the client to connect. It may be one.example.com or example2.net and/or IP 1.1.1.1 or 2.2.2.2 (if connected using IP only).
Apache somehow determined ip/domain of incoming reques, and I need to do such thing in pure socket code. C++ (main) or Java (or any other) accepted, I need to know mechaniics of this.
The IP is stored inside the IP packet header and you can read it from there. In order to get the host, you'll probably have to ask a DNS server by sending a request (or use a function which does it for you). You can find examples for both of the problems, even on this site
I want to find an open local port in some range.
How can I do that in the most efficient way, without connecting to the port.
If you want to find a local open port to bind a server to, then you can create a ServerSocket and if it does not throw an Exception, then it's open.
I did the following in one of my projects:
private int getAvailablePort() throws IOException {
int port = 0;
do {
port = RANDOM.get().nextInt(20000) + 10000;
} while (!isPortAvailable(port));
return port;
}
private boolean isPortAvailable(final int port) throws IOException {
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
ss.setReuseAddress(true);
return true;
} catch (final IOException e) {
} finally {
if (ss != null) {
ss.close();
}
}
return false;
}
RANDOM is a ThreadLocal here, but of course you can do an incrementing part there.
There's a little problem you may face in a multitasking windows/unix environment: if some isPortAvailable(final int port) from any of the answers returns a true for you, that doesn't mean, that at the moment when you will actually bind it it still will be available. The better solution would be create a method
ServerSocket tryBind(int portRangeBegin, int portRangeEnd) throws UnableToBindException;
So that you will just launch it and receive open socket for you, on some available port in the given range.
If you mean port in a remote server, then you might need a library that support raw-socket to send a sync packet and wait for sync-ack packet, just like nmap does.
One way to do is use some native network command and parse the output.
You can try netstat command as its available on Windows and *nix platforms.
Typical command would be netstat -n
Its output is of following format.
you need to parse the 'Foreign Address column for localhost or 127.0.0.1' and get a list of busy ports. Then see if they are in the range you specified.
If this is not about port-sniffing, but about service discovery, consider using a rendez-vous server (like an RMI server) or using the UDP protocol. Back in the day we used JXTA for this, but I hope there is a better alternative for this now.
Essentially the same idea as Karaszi, but instead of constructing that many sockets, use the InetSocketAddress and try to bind a ServerSocket to every address in range, until you hit an open one.
If you don't want to bind to that port (although if you don't, the socket may as well be bound the next moment after you check), use a plain Socket Object and try to connect to the ports - if it works, the port is taken, if it doesn't (and you don't have a firewall forbiding the connection), then it's most likely free.
Just pass zero as the port number to new ServerSocket(), then it will find one for you. But you can forget about the range, it will choose from the system-defined range.
I have some code like this:
InetAddress bind = InetAddress.getByName("192.168.0.1")
MulticastSocket socket = new MulticastSocket(new InetSocketAddress(bind,0));
socket.setInterface(bind);
On windows 7 and windows XP with JDK6u17,I got a SocketException: Socket operation on non socket.
But if I change the line 2 to :
MulticastSocket socket = new MulticastSocket(0);
It's ok, and works find too with jdk6u14.
Why? thanks.
EDIT:
Why port 0 should be the matter?
MulticastSocket socket = new MulticastSocket(0);
Everything goes well with this code.But not
MulticastSocket socket = new MulticastSocket(new InetSocketAddress(bind,port));
Whatever the port is.
As you are binding to a specific interface, calling setInterface() to the same interface is redundant. Remove it. It's only needed when you bind to INADDR_ANY, or in Java an InetAddress of null (or unspecified as a parameter).
To address errors in some of the other answers, and their implications:
Port zero is legal. It means a system-assigned port.
You only need a MulticastSocket for receiving multicasts. For sending, you can just use a DatagramSocket.
If the multicast interface needs to be specified, which it doesn't in this case, it can be done either via MulticastSocket.setInterface() or when calling joinGroup() or leaveGroup(). The latter option gives you granularity at the group level, but both techniques work. That's why they're both provided.
If you don't bind to a specific interface you should definitely call setInterface(). If you are on a multi-homed host you must to call joinGroup()/leaveGroup() once per interface, if you want to receive via all of them.
And a question: is 192.168.0.1 an IP address of an NIC on the local machine? It needs to be.
According to the documentation, you are supposed to instantiate it with a port number (thus 0 would be valid).
I am not so sure.
What's the constructor MulticastSocket(SocketAddress bindaddr) for.
And why it works fine with jdk6u14,but not jdk6u17?
And why it ok on windows 2003 server with jdk6u17?
On RHEL5.2 jdk1.4+
http://www.sockets.com/err_lst1.htm
Berkeley description: An operation was attempted on something that is not a socket. The specified socket parameter refers to a file, not a socket.
WinSock description: Same as Berkeley. The socket input parameter is not a valid socket handle (either it never was valid, it's a file handle (not a socket handle), or if it was a socket handle, it has been closed).
Detailed description:
select(): fails with WSAENOTSOCK if any socket in an fd_set is an invalid socket handle.
Developer suggestions: Did you close a socket inadvertently in one part of an application without keeping another part notified? Use socket state in an application and/or handle this error gracefully as a non-fatal error.
when the MulticastSocket created,socket.isClosed()==true
I haven't used these classes before, but the Exception occurs on line 3 when you call the setInterface method.
I would guess it's something to the effect that you're using the same reference twice or something.
I found a snippet of code that looked like this, maybe this is how you should be doing it:
MulticastSocket ms = new MulticastSocket(new InetSocketAddress(0));
ms.setInterface(InetAddress.getByName("192.168.0.1"));
You should first create the Multicast socket with a well known port - something higher than 1024 and less than 65535 - as already stated 0 means the operating system will choose a port for you (but then its going to be kinda random - which I guess you don't want).
For multicast - you generally need to set the interface to use on joinGroup() not on creation - e.g:
MulticastSocket socket = new MulticastSocket(2121);
InetSocketAddress socketAddress = new InetSocketAddress("localhost", 2121);
if (networkInterfaceName != null){
NetworkInterface ni = NetworkInterface.getByName(networkInterfaceName);
socket.joinGroup(this.socketAddress, ni);
}else {
socket.joinGroup(socketAddress.getAddress());
}
According to the MulticastSocket documentation you should use
Class D IP addresses in the range
224.0.0.0 to 239.255.255.255, inclusive
to bind a MulticastSocket. Apparently, the "192.168.0.1" is out of the multicast range.
This question already has answers here:
Java socket API: How to tell if a connection has been closed?
(9 answers)
Closed 5 years ago.
When I'm using e.g. PuTTY and my connection gets lost (or when I do a manual ipconfig /release on Windows), it responds directly and notifies my connection was lost.
I want to create a Java program which monitors my Internet connection (to some reliable server), to log the date/times when my internet fails.
I tried use the Socket.isConnected() method but that will just forever return "true". How can I do this in Java?
Well, the best way to tell if your connection is interrupted is to try to read/write from the socket. If the operation fails, then you have lost your connection sometime.
So, all you need to do is to try reading at some interval, and if the read fails try reconnecting.
The important events for you will be when a read fails - you lost connection, and when a new socket is connected - you regained connection.
That way you can keep track of up time and down time.
Even though TCP/IP is "connection oriented" protocol, normally no data is sent over an idle connection. You can have a socket open for a year without a single bit sent over it by the IP stack. In order to notice that a connection is lost, you have to send some data on the application level.(*) You can try this out by unplugging the phone cable from your ADSL modem. All connections in your PC should stay up, unless the applications have some kind of application level keepalive mechanism.
So the only way to notice lost connection is to open TCP connection to some server and read some data from it. Maybe the most simple way could be to connect to some FTP server and fetch a small file - or directory listing - once in a while. I have never seen a generic server which was really meant to be used for this case, and owners of the FTP server may not like clients doing this.
(*) There is also a mechanism called TCP keepalive but in many OS's you have to activate it for all applications, and it is not really practical to use if you want to notice loss of connection quickly
If the client disconnects properly, a read() will return -1, readLine() returns null, readXXX() for any other X throws EOFException. The only reliable way to detect a lost TCP connection is to write to it. Eventually this will throw an IOException 'connection reset', but it takes at least two writes due to buffering.
Why not use the isReachable() method of the java.net.InetAddress class?
How this works is JVM implementation specific but:
A typical implementation will use ICMP ECHO REQUESTs if the privilege can be obtained, otherwise it will try to establish a TCP connection on port 7 (Echo) of the destination host.
If you want to keep a connection open continually so you can see when that fails you could connect to server running the ECHO protocol yourself rather than having isReachable() do it for you and read and write data and wait for it to fail.
You might want to try looking at the socket timeout interval. With a short timeout (I believe the default is 'infinite timeout') then you might be able to trap an exception or something when the host becomes unreachable.
Okay so I finally got it working with
try
{
Socket s = new Socket("stackoverflow.com",80);
DataOutputStream os = new DataOutputStream(s.getOutputStream());
DataInputStream is = new DataInputStream(s.getInputStream());
while (true)
{
os.writeBytes("GET /index.html HTTP/1.0\n\n");
is.available();
Thread.sleep(1000);
}
}
catch (IOException e)
{
System.out.println("connection probably lost");
e.printStackTrace();
}
Not as clean as I hoped but it's not working if I leave out the os.writeBytes().
You could ping a machine every number of seconds, and this would be pretty accurate. Be careful that you don't DOS it.
Another alternative would be run a small server on a remote machine and keep a connection to it.
Its probably simpler to connect to yahoo/google or somewhere like this.
URL yahoo = new URL("http://www.yahoo.com/");
URLConnection yc = yahoo.openConnection();
int dataLen = yc.getContentLength() ;
Neil
The isConnected()method inside Socket.java class is a little misleading. It does not tell you if the socket is currently connected to a remote host (like if it is unclosed). Instead, it tells you whether the socket has ever been connected to a remote host. If the socket was able to connect to the remote host at all, this method returns true, even after that socket has been closed. To tell if a socket is currently open, you need to check that isConnected() returns true and isClosed() returns false.
For example:
boolean connected = socket.isConnected() && !socket.isClosed();