How to bind to IPv6 and IPv4 individually in Java - java

I'm writing some software that has to bind to IPv6 and IPv4 (UDP4, UDP6) individually. I have an existing code base that works elsewhere and can't be modified for this task.
Java, in all of its glory, automatically creates a socket that can handle both IPv6 and IPv4, so when my existing unmodifiable code tries creating the second set of sockets, it errors out because the port is already in use.
Relevant: http://docs.oracle.com/javase/7/docs/technotes/guides/net/ipv6_guide/
I can provide code that I have, but don't think it will help in answering this question. Thank you so much!
My bind function (not final production code):
private void bind(String uuid, String exclusiveStr, String portStr, CallbackContext c){
final JSSocket socket = socketMap.get(uuid);
boolean exclusive = Boolean.parseBoolean(exclusiveStr);
int port=0;
try{
port=Integer.parseInt(portStr);
}catch(Exception e){
//port wasn't there, use default value
}
Log.d("bind", "Attempting to bind uuid: " + uuid + " to port: " + port);
socket.bind(exclusive, port);
Log.d("bind", "new port: " + socket.getPort());
final InetAddress inetAddress = socket.getAddress();
//HashMap<String, String> setAddress= new HashMap<String,String>();
//setAddress.put("address", socket.getAddress().getHostAddress());
//setAddress.put("port", "" + socket.getPort());
JSONObject json = new JSONObject();
PluginResult result;
try {
json.put("address", socket.getAddress().getHostAddress());
json.put("port", "" + socket.getPort());
Log.d("BINDING********", json.toString());
c.success(json.toString());
}catch(JSONException e){
//todo better
e.printStackTrace();
}
}
The other code uses Node.js's datagram module. I have to write to that interface. It has separate (my understanding) sockets for IPv4 and IPv6.

At the operating system level this is controlled by the IPV6_V6ONLY flag. Unfortunately java doesn't seem to provide any access to that.
One possible workaround may be to bind to individual IP addresses. Afaict binding to an individual IP address will always restrict your socket to one IP version.

You have two options: Either try{}catch() around the second invocation of bind(), and allow it to fail harmlessly. Or you can bind() once, then try to send yourself a packet both on IPv4 and IPv6, and if either fails, bind() the other.
The reason your code worked elsewhere is that some kernels provide a socket that works on both, and others do not.

Related

Cannot Get DNS Requests via Java Code in Windows 10 and DLINK DIR-615 router

So I am working on a software that will monitor(and may alter by acting as a Forrowder) all the DNS requests made by my router.
What I did?
So for first I wrote a Java code that can listens to a specific port and prints all the requests to the console[For now I just want to test with the requests].
The code is:
import java.net.*;
import java.io.*;
public class PortLogger{
public static void main(String[] args) {
LoggerServer loggerServer = new LoggerServer(53);
loggerServer.start();
}
}
class LoggerServer extends Thread{
private int port;
public LoggerServer(int port){
this.port = port;
}
#Override
public void run(){
try{
int id = 1;
ServerSocket server = new ServerSocket(port);
System.out.println("Server Listening at port " + port);
Socket client;
while(true){
client = server.accept();
ClientHandler clientHandler = new ClientHandler(client, id++);
clientHandler.start();
}
}catch(Exception ex){
System.out.println("Exception at Server : 1 :: EX = " + ex);
}
}
}
class ClientHandler extends Thread{
private Socket client;
private int id;
public ClientHandler(Socket client, int id){
this.client = client;
this.id = id;
}
#Override
public void run(){
try {
String data = "";
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
while(true){
data = reader.readLine();
if(data.length() > 0){
System.out.println("Client : " + id + " :: " + data);
}
}
}catch(Exception ex){
System.out.println("Exception at Client : " + id + " :: EX = " + ex);
}
}
}
The sole propose of this code for now is to Show me all the requests made to the server.
I know that I also have to change the DNS Server in my router for this.
So, for that I first tried by going to internet setup and put the local IP of my computer as DNS server.
But it was showing :
DNS IP and LAN IP must be on different networks!
But I found another way to do it.
It is as follows:
I went to the setup wizard of the router and the set the DNS Server to the same IP.
Surprisingly this worked!
[I have no idea whether this is a bug in the D-Link Firmware or not.
I have also added an exception to allow all request both inbound and outbound to port 53.
What is the problem?
So now the problem is that even after successfully changing the DNS to my servers. There seemed to be no requests at all to the console. I tried a lot but nothing.
I checked that the program was working fine by voluntarily sending request to it using telnet?
Now am I doing anything wrong or there is some bug with the router(its is a old one).
NOTE: The black lines on the images are just to hide my public IP address nothing special.
EDIT: I tried a few more times then found that websites were not opening when I changed the DNS in my router but still nothing in the console!
While it is difficult to give you a complete answer why your application doesn't work I can suggest some ways to investigate:
Port 53 is a privileged port. This means on Linux binding to that port requires root privileges and the application will throw an exception due to 'permission denied' if executed as a 'normal' user. As you are using Windows I don't know what it does if you try to bind as a 'normal' user, or you might be executing as an Admin user (or whatever the equivalent of 'root' is in Windows) and you don't know it. It might even just silently fail i.e. appear to bind when in fact it hasn't and no data is passed through you your application. As an aside, defaulting to 'root' as the default execution user in Linux is not the norm because it's insecure and most Linux distributions if not all do not allow this by default i.e. you can have this but you have to tell the distribution this is what you intend during installation. I'll let you come to your own conclusions what stance Windows takes for making users 'admin'...
In a scenario such as this if it were me I would immediately go to some networking tools to see what is happening. On Linux this is tcpdump or Wireshark. You can also get Wireshark for Windows as it's a GUI application. This will let you monitor and filter network traffic and so will be independent of your application. You can filter by source or destination address and/or port number.
I would leave the DNS setting alone in the router and change the DNS settings in one machine first, call it the test client, and set its DNS address to the machine where your application is running. Using tcpdump or Wireshark you can then make requests on your test_client e.g. browser requests and see the resulting network traffic.
You never mentioned if after changing your router's DNS settings all browser requests from clients fail. This is what I would expect to see if your router can no longer get a name resolution. However there maybe some DNS caching going on in your clients so you may appear to get successful DNS requests on your test_client. Again look at network traffic or use a Linux client which will provide you with much better networking tools.

Creating a DHCP client list java

I'm trying to write a program that will show a DHCP client list in Java. I want to get the IP addresses, MAC addresses and the host names of all the devices connected to my wifi network.
I have a Belkin router. Its homepage has a 'DHCP client list' option which when clicked shows me this table :
That's exactly what I'm looking for. But I want to show all this data in the form of a list in a Java Swing program. I also want to be able to update this list by pressing a refresh button. Is there any way to achieve this?
It should look something like this :
I've written a basic java program that shows all the IP addresses that are online. Here's the code :
public static void main(String[] args) throws IOException {
InetAddress localhost = InetAddress.getLocalHost();
// this code assumes IPv4 is used
byte[] ip = localhost.getAddress();
for (int i = 1; i <= 254; i++)
{
ip[3] = (byte)i;
InetAddress address = InetAddress.getByAddress(ip);
if (address.isReachable(1000))
{
// machine is turned on and can be pinged
System.out.println(address + "is online");
}
else if (!address.getHostAddress().equals(address.getHostName()))
{
// machine is known in a DNS lookup
System.out.println(address + "is in a DNS lookup");
}
else
{
// the host address and host name are equal, meaning the host name could not be resolved
System.out.println(address + " is not online");
}
}
}
But this doesn't serve the purpose and it's really slow. I want to write a Swing program that'll show me the DHCP client list as seen in the image above.
Any help is appreciated.
I would think about three possible alternatives:
1-The one you implemented that is slow but it can work. You need to find a JAVA API to get the MAC addresses of received messages (I don't know if it exists or not). You can also send ARP messages asking "who has this IP address) and obtain the MAC address from the response. Use some Java interface for pcap library: jNetPcap vs Jpcap , http://jnetpcap.com/
2-Create an app that accesses your router web interface using HTTP and sending the appropriate messages with data as if you were using the UI. In this way you can programatically follow the steps a human would go and get the list that you browser shows, parse it and obtain the data.
3-If the router/access point provides a web API, which I doubt, you can use it.

What is the correct way to construct InetSocketAddress with any host an IP address?

I want to create an InetSocketAddress but I want to do it right no matter if I get a host:port or a ip:port. I see it has two constructors, one for host (String) and another one for IP (InetAddress). Do I have to determine myself if I got an IP or HOST in order to choose between these two constructors? Am I missing something here?
You can infer from the Javadoc, and see in the source code, that new InetSocketAddress(String hostname, int port) calls InetAddress.getByName(hostname), which sorts all that out for you as documented.
So the problem you're posting about doesn't really exist. Just pass whatever string you get, whether host name or IP address.
I'm not entirely sure what it is your asking, but, I did this quick test on my PC without any issue
try {
String ipAddress = ""; // Add your own
String hostName = ""; // Add your own
int port = ...; // You'll need some sort of service to connect to
InetSocketAddress byAddress1 = new InetSocketAddress(ipAddress, port);
InetSocketAddress byAddress2 = new InetSocketAddress(InetAddress.getByName(ipAddress), port);
InetSocketAddress byName1 = new InetSocketAddress(hostName, port);
InetSocketAddress byName2 = new InetSocketAddress(InetAddress.getByName(hostName), port);
} catch (UnknownHostException unknownHostException) {
unknownHostException.printStackTrace();
}
The bigger question is, what are expected to get as input? IP address, host name or some other form??
You will have to determine whether the String passed to the constructor is an IP or a Host name. I'd do it with a Regex for the IP address. If that fails, it's probably a host name.
Both IP addresses and Host names are String, so you will how only one constructor.
Also it is worth to mention, if you don't know your dns name or ip, you can simple use constructor with port only.
new InetSocketAddress(8080)
it internally invokes InetAddress.anyLocalAddress()

How can I modify this code to allow my client socket program to send a string to the server?

Hi ive written some code to connect to a server through the use of a socket. Id like to write some simple code that allows me to send a string to the server, im assuming this will involve input and output streams but I am new to this. Ive put the code I am working with below, any insights into the best way to accomplish this would be great.
import java.net.*;
import java.io.*;
public class SocketMarket
{
public static void main(String [] args)
{
String serverName = "XX.X.X.XXX";
int port = XXXX;
try
{
System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Connected to " + client.getRemoteSocketAddress());
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
Thanks in advance
client.getOutputStream().write("Hello World".getBytes());
client.getOutputStream().flush();
The above is how you would send just a String, but you will probably want to build up some infrastructure around sending arbitrary text.
The general idea is that your server and client will communicate with each other using InputStream's and OutputStream's, which can be accessed from a Socket via getInputStream() and getOutputStream(), once the connection between them is made.
For your server to receive connections, you should be using a ServerSocket to accept() incoming connections.
Insight is here, the "really big index" for java.
http://docs.oracle.com/javase/tutorial/networking/sockets/readingWriting.html
Yes, it involves OutputStreams. If you want to output Strings you could write raw bytes via the OutputStream you get from the connection but then you completely loose control over encoding. You need to learn about reader/writer/streams first, then networking via sockets is simple. You can find the relevant part of the Java tutorials here: http://docs.oracle.com/javase/tutorial/essential/io/ (you can ignore the NIO part completely for the beginning). After that you can learn about socket networking: http://docs.oracle.com/javase/tutorial/networking/sockets/index.html .

How can i find an open ports in range of ports?

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.

Categories