How to bind to IPv6 and IPv4 individually in Java

1.8k views Asked by At

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.

2

There are 2 answers

0
arnt On

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.

0
plugwash On

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.