2015-03-17 8 views
8

से आईपीवी 6-केवल होस्ट से कनेक्ट नहीं हो सकता है मेरे पास कुछ आईपीवी 6-केवल होस्ट हैं। मैं सफलतापूर्वक कर्लजावा

$ curl -I my.ip.v6.only.host 
HTTP/1.1 200 OK 

द्वारा यह को कर्ल अनुरोध निष्पादित कर सकते हैं लेकिन जब मैं जावा से इसे पाने के लिए कोशिश कर रहा है मैं एक त्रुटि है:

HttpGet httpget = new HttpGet("http://my.ip.v6.only.host"); 
CloseableHttpResponse response = httpclient.execute(httpget);  

स्टैक ट्रेस: ​​

INFO: I/O exception (java.net.NoRouteToHostException) caught when processing request to {}->http://my.ip.v6.only.host: No route to host 
Mar 17, 2015 7:42:23 PM org.apache.http.impl.execchain.RetryExec execute 
INFO: Retrying request to {}->http://my.ip.v6.only.host 
java.net.NoRouteToHostException: No route to host 
    at java.net.PlainSocketImpl.socketConnect(Native Method) 
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339) 
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200) 
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182) 
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) 
    at java.net.Socket.connect(Socket.java:579) 
    at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:72) 
    at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:123) 
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:318) 
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:363) 
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219) 
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195) 
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86) 
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108) 
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) 
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) 
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106) 
    at MainTest.main(MainTest.java:25) 

समस्या जावा v1.7.0_65 और v1.8.0_40, मैकोज़ 10.10.2 पर हुआ है। पिछले संस्करण मैकोज़ 10.9.5 पर यह अच्छी तरह से काम करता है।

क्या चल रहा है? यह कैसे संभव है कि होस्ट curl तक पहुंच योग्य है और जावा से पहुंच योग्य नहीं है।

इसके अलावा, मैंने -Djava.net.preferIPv6Addresses=true और -Djava.net.preferIPv4Stack=false के आसपास खेलने की कोशिश की है और यह मदद नहीं कर सका।

युपीडी OpenJDK में एक संबंधित बग, JDK-8015415

युपीडी 2 जब मैं वाईफ़ाई के बजाय वायर्ड कनेक्शन का उपयोग करने की कोशिश की, यह मेरे में मदद मिली पाया। अजीब।

+0

अपाचे एचटीपी क्लाइंट का कौन सा संस्करण आप उपयोग कर रहे हैं? – Martijn

+0

org.apache.httpcomponents: httpclient: 4.3.5 –

+0

अभी तक सामान्य जावा एनआईओ अनुरोधों का प्रयास किया? मैं इस मुद्दे को लिनक्स बॉक्स एटीएम पर फिर से उत्पन्न नहीं कर सकता। – Martijn

उत्तर

14

यह AirDrop + जावा सहयोग में समस्या हो सकती है।

लघु जवाब - कोशिश:

$ sudo ifconfig awdl0 down 

नीचे समस्या (सेर्गेई Shinderuk के लिए धन्यवाद) की जांच:

import java.net.Socket; 

public class Test { 
    public static void main(String[] args) throws Exception { 
     new Socket("2a02:6b8::3", 80); // ya.ru 
    } 
} 

और जब:

हम जावा में इस तरह के कोड पुन: पेश करने के लिए है हम वाईफाई का उपयोग करते हैं, अपवाद प्राप्त करें: java.net.NoRouteToHostException: No route to host

Whil ई टेलनेट के साथ ठीक है:

$ telnet 2a02:6b8::3 80 
Trying 2a02:6b8::3... 
Connected to www.yandex.ru. 
Escape character is '^]'. 
^C 

जब हम वाईफाई बंद करते हैं, और वायर्ड कनेक्शन का उपयोग करते हैं - ठीक है। लेकिन अगर हम वायर्ड कनेक्शन का इस्तेमाल करते हैं, लेकिन वाईफाई चालू है - यह जावा कोड काम नहीं करेगा। जो बहुत अजीब है।

हमें जावा और टेलनेट के बीच connect(2) के लिए तर्कों की तुलना करने की आवश्यकता है।

$ sudo dtrace -qn 'syscall::connect:entry { print(*(struct sockaddr_in6 *)copyin(arg1, arg2)) }' -c './telnet 2a02:6b8::3 80' 

struct sockaddr_in6 { 
    __uint8_t sin6_len = 0x1c 
    sa_family_t sin6_family = 0x1e 
    in_port_t sin6_port = 0x5000 
    __uint32_t sin6_flowinfo = 0 
    struct in6_addr sin6_addr = { 
     union __u6_addr = { 
      __uint8_t [16] __u6_addr8 = [ 0x2a, 0x2, 0x6, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3 ] 
      __uint16_t [8] __u6_addr16 = [ 0x22a, 0xb806, 0, 0, 0, 0, 0, 0x300 ] 
      __uint32_t [4] __u6_addr32 = [ 0xb806022a, 0, 0, 0x3000000 ] 
     } 
    } 
    __uint32_t sin6_scope_id = 0 
} 

आप देख सकते हैं कि हम struct रूप connect(2) का दूसरा तर्क मुद्रित है। इसके अलावा आप सभी अपेक्षित जानकारी देख सकते हैं: AF_INET6, पोर्ट 80, और आईपीवी 6-पता।

Make a note: we've launched ./telnet , not telnet - dtrace can't work with system binaries signed by Apple. So we should copy it.

ही जावा के लिए: - sin6_scope_id = 0x8

$ sudo dtrace -qn 'syscall::connect:entry { print(*(struct sockaddr_in6 *)copyin(arg1, arg2)) }' -c '/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/bin/java Test' 
[...] 
struct sockaddr_in6 { 
    __uint8_t sin6_len = 0 
    sa_family_t sin6_family = 0x1e 
    in_port_t sin6_port = 0x5000 
    __uint32_t sin6_flowinfo = 0 
    struct in6_addr sin6_addr = { 
     union __u6_addr = { 
      __uint8_t [16] __u6_addr8 = [ 0x2a, 0x2, 0x6, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x3 ] 
      __uint16_t [8] __u6_addr16 = [ 0x22a, 0xb806, 0, 0, 0, 0, 0, 0x300 ] 
      __uint32_t [4] __u6_addr32 = [ 0xb806022a, 0, 0, 0x3000000 ] 
     } 
    } 
    __uint32_t sin6_scope_id = 0x8 
} 

हम देख सकते हैं, मुख्य अंतर यह है टेलनेट भेजता है sin6_len == 0 लेकिन जावा है। वास्तव में sin6_scope_id में मुख्य समस्या। टेलनेट और कर्ल भेजता scope_id == 0, लेकिन जावा - 0x8। और जब हम वायर्ड कनेक्शन का उपयोग करते हैं, जावा scope_id == 0xb भेजता है।

स्पष्ट है कि, हम टेलनेट साथ scope_id के साथ समस्या यह पुन: पेश करने की कोशिश करो। का उपयोग वाईफ़ाई कार्य करें:

$ telnet 2a02:6b8::3%0 80 
Trying 2a02:6b8::3... 
Connected to www.yandex.ru. 

$ telnet 2a02:6b8::3%8 80 
Trying 2a02:6b8::3... 
telnet: connect to address 2a02:6b8::3: No route to host 
telnet: Unable to connect to remote host 

$ telnet 2a02:6b8::3%b 80 
Trying 2a02:6b8::3... 
Connected to www.yandex.ru. 

तो टेलनेट 0xb साथ जुड़ सकते हैं, लेकिन 0x8 साथ नहीं कर सकते।

ऐसा लगता है जावा के लिए इस कोड के लिए सही जगह है: http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/8fe85977d5a6/src/solaris/native/java/net/net_util_md.c#l105

हमने देखा है कि scope_id निजी क्षेत्र java.net.NetworkInterface.defaultIndex है, जो कुछ डिफ़ॉल्ट इंटरफ़ेस के सूचकांक में शामिल है के मूल्य के साथ भर दिया। तार पर

$ java Netif 
name:awdl0 (awdl0) 8 
name:en0 (en0) 4 
name:lo0 (lo0) 1 
defaultIndex = 8 

पर वायर्ड

$ java Netif 
name:en4 (en4) 11 
name:lo0 (lo0) 1 
defaultIndex = 11 

+ वाईफ़ाई

$ java Netif 
name:awdl0 (awdl0) 8 
name:en4 (en4) 11 
name:en0 (en0) 4 
name:lo0 (lo0) 1 
defaultIndex = 8 
:

हम कोड के साथ सभी अनुक्रमित मुद्रित कर सकते हैं:

import java.lang.reflect.Field; 
import java.net.NetworkInterface; 
import java.util.Collections; 
import java.util.List; 

public class Test { 
    public static void main(String[] args) throws Exception { 
     List<NetworkInterface> netins = Collections.list(NetworkInterface.getNetworkInterfaces()); 
     for (NetworkInterface netin : netins) { 
      System.out.println(netin + " " + netin.getIndex()); 
     } 

     Field f = NetworkInterface.class.getDeclaredField("defaultIndex"); 
     f.setAccessible(true); 
     System.out.println("defaultIndex = " + f.get(NetworkInterface.class)); 
    } 
} 

वाईफ़ाई पर

जब wifi कनेक्ट, defaultIndex == 8, और डिफ़ॉल्ट इंटरफ़ेस awdl0 है।

तो हम बस

$ sudo ifconfig awdl0 down 

और जावा कोड काम करता है।

इसके अलावा

:

+0

https://youtrack.jetbrains.com/issue/WI-26878#comment=27-1069340 – lanwen

+0

से जुड़ा हुआ शानदार जवाब, धन्यवाद! दुर्भाग्यवश, ऐसे मामले हैं जहां 'awdl0' कम करना पर्याप्त नहीं है। मेरे मैकबुक पर, मेरा 'डिफ़ॉल्ट इंडेक्स' अंक 'en5' पर है, पूर्ण सूची है: en5, utun2, utun1, utun0, awdl0, en0, lo0; जहां en0 वह है जिसका उपयोग किया जाना चाहिए। काफी दर्दनाक दिलचस्प बात यह है कि जावा पहले से ही यह जानता है कि यह सही तरीके से कैसे करें, अगर आप 'सॉकेटChannel.open (inet6SockAddress) .socket()' का उपयोग करते हैं, तो आपको एक कामकाजी सॉकेट मिल जाएगी। बेशक, जावा कोड का _tons_ है जो मानक 'सॉकेट' एपीआई का उपयोग करता है, इसलिए यह मैक पर आईपीवी 6 के लिए एक बड़ी समस्या की तरह लगता है। अलेक्जेंडर ग्रिंको से – Lachlan

+0

ऑनलाइनर काम करता है! – lanwen

0

वायर्ड कनेक्शन भी मेरी मदद करता है।

साथ $ java -version java version "1.8.0_25" Java(TM) SE Runtime Environment (build 1.8.0_25-b17) Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

8

इस पैच के लेखक https://github.com/snaury है।

स्पष्टीकरण:

आपको libnet खोलने की आवश्यकता है।otool साथ dylib और पाते हैं _setDefaultScopeID प्रतीक:

otool -tv -p _setDefaultScopeID libnet.dylib 

यहाँ आप 0 और सशर्त कूद के साथ तुलना पा सकते हैं:

000000000000b882 cmpb $0x1e, 0x1(%r14) 
000000000000b887 jne 0xb8aa 
000000000000b889 cmpl $0x0, 0x18(%r14) 
000000000000b88e jne 0xb8aa 

आप किसी भी हेक्स संपादक के साथ बिना शर्त कूद करने के लिए सशर्त कूद प्रतिस्थापित करने की आवश्यकता:

000000000000b882 cmpb $0x1e, 0x1(%r14) 
000000000000b887 jne 0xb8aa 
000000000000b889 cmpl $0x0, 0x18(%r14) 
000000000000b88e jmp 0xb8aa 

JNE == 75 1a 
JMP == eb 1a 

या यह एक पंक्ति आदेश का उपयोग करें:

otool -tv -p _setDefaultScopeID libnet.dylib | awk '/cmpl.*\$0x0/ {print $1}' | python -c 'exec """\nwith open("libnet.dylib", "r+b") as fd:\n fd.seek(int(raw_input(), 16) + 5)\n fd.write(chr(235))\n"""' 
+1

एक लाइनर के लिए बहुत धन्यवाद! यह उल्लेख किया जाना चाहिए कि libnet.dylib '/ लाइब्रेरी/जावा/जावा वर्चुअल माचिन/jdk1.8.0_152.jdk/सामग्री/होम/जेआर/lib/libnet.dylib' – lanwen

+0

के समान जरे पथ में होना चाहिए बहुत धन्यवाद, 1.8.0_162 के साथ काम करता है भी! – bashnesnos