Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IPv6 Can't assign requested address #429

Closed
Smeegol opened this issue May 13, 2016 · 39 comments
Closed

IPv6 Can't assign requested address #429

Smeegol opened this issue May 13, 2016 · 39 comments

Comments

@Smeegol
Copy link

Smeegol commented May 13, 2016

As Apple says, tried to connect to IPv4 address in IPv6-only network, here are codes:

GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
socket.IPv4PreferredOverIPv6 = NO;
[socket connectToHost:@"xxx.xxx.xxx.xxx" onPort:xxx withTimeout:10 error:nil];

Always failed with error:
Error Domain=NSPOSIXErrorDomain Code=49 "Can't assign requested address" UserInfo={NSLocalizedDescription=Can't assign requested address, NSLocalizedFailureReason=Error in connect() function}

I thought CocoaAsyncSocket will Synthesize IPv6 address from IPv4 address automatically, but I was wrong. Can somebody help me?

@Smeegol
Copy link
Author

Smeegol commented May 16, 2016

Update: Using AsyncSocket instead of GCDAsyncSocket will be OK, I don't know why.

@etring
Copy link

etring commented May 16, 2016

I also get this problem. when I use the ip4 address like @"192.168.7.31", the error code 49, "Can't assign requested address" is got. when I change the ip address to the host name, it's ok. Through the wifi set like this link "Test for IPv6 DNS64/NAT64 Compatibility Regularly " https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/UnderstandingandPreparingfortheIPv6Transition/UnderstandingandPreparingfortheIPv6Transition.html

@chenzhuolin1002
Copy link

I get this too.

@chenzhuolin1002
Copy link

I fixed it with this
// Found IPv6 address. // Wrap the native address structure, and add to results. struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)res->ai_addr; sockaddr->sin6_port =htons(port);``
in + (NSMutableArray )lookupHost:(NSString *)host port:(uint16_t)port error:(NSError *)errPtr method

@Smeegol
Copy link
Author

Smeegol commented May 19, 2016

@chenzhuolin1002 You mean after applying your code, like this?

// Found IPv6 address.
// Wrap the native address structure, and add to results.

struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)res->ai_addr;
sockaddr->sin6_port = htons(port);

NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
[addresses addObject:address6];

but the code above don't work for me, can you provide more details or show more code?

@etring
Copy link

etring commented May 19, 2016

@Smeegol I test the code provided by @chenzhuolin1002 .
It's OK on the system version 9.3.1.
But can not work on the system 7.1.2

@chenzhuolin1002
Copy link

@etring iOS 8.1 cannot work either. I don't know How to fix it.

@WuChuming
Copy link

@etring It even can't work on the system version 9.3.1while I am using those code as Smeegol used them. Could you tell me more detail about how you test the code? It seems server has to support the IPV6 protocol .Although GCDAsyncSocket has split those two kinds of protocols and connected host-server with different data, the connection is still failed.

@newacct
Copy link

newacct commented May 21, 2016

I believe it may be related to the bug mentioned here: https://forums.developer.apple.com/thread/47312

GCDAsyncSocket uses getaddrinfo() on the hostname given, which on iOS 9.2+ should synthesize IPv6 addresses even when given an IPv4 literal. GCDAsyncSocket then uses the socket address returned (IPv4 or IPv6) to connect.

However, there currently seems to be a bug with getaddrinfo(), when given a numeric port, that it returns a socket address with ports being 0 (instead the of the port passed in). Trying to send a message to port 0 fails. The workaround is to manually set the port back on all the resulting sockaddr structs after the call to getaddrinfo(). It should be pretty easy to add this workaround into CocoaAsyncSocket.

(AsyncSocket doesn't have this problem, because it uses the higher-level CFStream APIs, so it already works works with IPv4 literals on iOS 9.2+ (using IPv6 transparently if necessary). But GCDAsyncSocket uses lower-level socket APIs directly, so needs to use getaddrinfo() to synthesize it.)

@newacct
Copy link

newacct commented May 22, 2016

Try changing the following lines in +[GCDAsyncSocket lookupHost:port:error:]:

            for (res = res0; res; res = res->ai_next)
            {
                if (res->ai_family == AF_INET)
                {
                    // Found IPv4 address.
                    // Wrap the native address structure, and add to results.

                    NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
                    [addresses addObject:address4];
                }
                else if (res->ai_family == AF_INET6)
                {
                    // Found IPv6 address.
                    // Wrap the native address structure, and add to results.

                    NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
                    [addresses addObject:address6];
                }
            }

to

            for (res = res0; res; res = res->ai_next)
            {
                if (res->ai_family == AF_INET)
                {
                    // Found IPv4 address.
                    // Wrap the native address structure, and add to results.

                    if (((struct sockaddr_in *)res->ai_addr)->sin_port == 0)
                        ((struct sockaddr_in *)res->ai_addr)->sin_port = htons(port);
                    NSData *address4 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
                    [addresses addObject:address4];
                }
                else if (res->ai_family == AF_INET6)
                {
                    // Found IPv6 address.
                    // Wrap the native address structure, and add to results.

                    if (((struct sockaddr_in6 *)res->ai_addr)->sin6_port == 0)
                        ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(port);
                    NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
                    [addresses addObject:address6];
                }
            }

and see if that fixes the problem.
(Analogous changes need to be made in GCDAsyncUdpSocket if you are using UDP.)

@WuChuming
Copy link

@newacct Thanks, Bro! Apparently, you solve one of my problem what I was confused with no long before. Now, with your advice, I can get the correct address with port information for the socket connection. However, the client application still can't connect to our server. 0ur situation is like this: Our server only supports IPV4 protocol and I test IPV6 connection with Apple's test method (http://www.jianshu.com/p/8837739251ad), Every time when my application apply to set socket connection, the method of "socketDidDisconnect" will be recalled. We check our server, it didn't get any connection information from the application. That is quit confuse us as we are not master-hands for it. Do you have any suggestions about this situation for us? We are still searching solution for this. Does the server must support IPV6?

@WuChuming
Copy link

@newacct I found some discussions related to my question here yesterday:http://stackoverflow.com/questions/16480729/connecting-ipv4-client-to-ipv6-server-connection-refused, It cleared my confusion. In their opinions, A listening IPv4 socket can accept incoming connections from only IPv4 clients. But, what is the usage of GCDasyncSocket automatically create IPv4 socket while its destination address is a IPV6 address? I once tried to connect our IPV4 server with a IPV6 client application. the responded error is "Network is unreachable".

@hxbtec
Copy link

hxbtec commented May 24, 2016

@newacct I tried your solution in GCDAsyncUdpSocket with a NAT64 network,this error didn't occur anymore,and the delegate method udpSocket: didSendDataWithTag: had been called.But another delegate method udpSocket: didReceiveData: fromAddress: withFilterContext: never be called,I don't
know how can I solve this problem.I hope you can give me some advice.Thanks.

@afanti100
Copy link

@WuChuming m Hi, I've met this problem too. And I tried to resolve it according to the method above. But not valid. The problem I met like you. Do you resolve it now?

@WuChuming
Copy link

@afanti100 @newacct I haven't solve it yet, and I got a bad news. Our android team can connect to our server which only has IPV4 address through the Wifi emitted by MAC with NAT64 without doing anything. Meanwhile, our application always fails to connect to server and responds "Network is unreachable".

@newacct
Copy link

newacct commented May 27, 2016

If you turn off IPv4PreferredOverIPv6 (which is true by default), then it will work:

asyncSocket.IPv4PreferredOverIPv6 = NO;

Unfortunately, it seems that the getaddrinfo() API gives back both IPv4 and IPv6 addresses, even on IPv6-only networks (I'm not sure why), and if IPv4 is preferred over IPv6, then it will use the non-working IPv4 address.

@WuChuming
Copy link

@newacct Thanks for your answering! I had set this property, but it still can't connect to the host. I did serval tests. I would like to draw some pictures to explicate different situations in different kinds of connections. In some kinds of connections, The connection can be OK. But I don't know how to show those pictures here. The conclusion is: If host is set in IPV6, Clients in IPV4 and IPV6 can connect to it with literal address, but if host is in IPV4, Clients in IPV6 can't connect to this host and responded "Can't assign requested address".
Besides, I read an article related to IPV6-only on Apple.developer. It makes a detailed explanation on it. And there are five common barriers for connection:

  1. IP address literals embedded in protocols. Many communications protocols, such as Session Initiation Protocol (SIP), File Transfer Protocol (FTP), WebSockets, and Peer-to-Peer Protocol (P2PP), include IP address literals in protocol messages. For example, the FTP parameter commands DATA PORT and PASSIVE exchange information that includes IP address literals. Similarly, IP address literals may appear in the values of SIP header fields, such as To, From, Contact, Record-Route, and Via. See Use High-Level Networking Frameworks and Don’t Use IP Address Literals.
  2. IP address literals embedded in configuration files. Configuration files often include IP address literals. See Don’t Use IP Address Literals.
  3. Network preflighting. Many apps attempt to proactively check for an Internet connection or an active Wi-Fi connection by passing IP address literals to network reachability APIs. See Connect Without Preflight.
  4. Using low-level networking APIs. Some apps work directly with sockets and other raw network APIs such as gethostbyname, gethostbyname2, and inet_aton. These APIs are prone to misuse or they only support IPv4—for example, resolving hostnames for the AF_INET address family, rather than the AF_UNSPEC address family. See Use High-Level Networking Frameworks.
  5. Using small address family storage containers. Some apps and networking libraries use address storage containers—such as uint32_t, in_addr, and sockaddr_in—that are 32 bits or smaller. See Use Appropriately Sized Storage Containers.
    What attract me is NO.2, I think does it must have pass them hostnames or fully qualified domain names (FQDNs)? However, I can't test it now, for I don't know where is the hostname.
    Thank you again!

@newacct
Copy link

newacct commented May 28, 2016

Connecting to an IPv4-only server with IPv4 literals from an client on IPv6-only networks works fine. That's the point of DNS64/NAT64.

The error 49 "Can't assign requested address" is caused by using port 0 as returned by getaddrinfo() due to the bug in getaddrinfo(). Didn't you make the changes I mentioned above about putting the ports back in? Then the other problem you had, the "Network is unreachable", that's caused by trying to connect to the IPv4 address anyway, due to the IPv4PreferredOverIPv6 setting in GCDAsyncSocket. You fix this by turning that off as I showed. Did you do both of these things?

@WuChuming
Copy link

@newacct What a good news! I connected it with hostname. I find hostname in computer reference -> sharing. GCDSocket supports IPV6. I change the address form literals to hostname, then It works. I am so exciting!
Of course, I did both of those things you mentioned.
Thank you a lot!

@newacct
Copy link

newacct commented May 28, 2016

But the IPv4 address literal should work. Are you on iOS 9.2+?

@WuChuming
Copy link

I was on iOS 8.1.

@newacct
Copy link

newacct commented May 28, 2016

Well then it's not going to work. Synthesizing IPv6 address from IPv4 addresses with getaddrinfo only works on iOS 9.2+. Your app only needs to work on IPv6-only networks on those iOS versions.

@WuChuming
Copy link

@newacct I tested it with an iPhone which is on iOS9.3.1 and a simulator on MAC with version10.11.1 system. The connection is failed. Do I need to upgrade the MAC system to test it? As The article mentions that * The ability to synthesize IPv6 addresses was added to getaddrinfo in iOS 9.2 and OS X 10.11.2*

@WuChuming
Copy link

@newacct Anyway, I am upgrading the MAC's system.

@Smeegol
Copy link
Author

Smeegol commented May 30, 2016

Well, thanks @chenzhuolin1002, @newacct and other guys, I finally solve my question by code below:

// Found IPv6 address.
// Wrap the native address structure, and add to results.

struct sockaddr_in6 *sockaddr = (struct sockaddr_in6 *)res->ai_addr;
in_port_t *portPtr = &sockaddr->sin6_port;
if ((portPtr != NULL) && (*portPtr == 0)) {
        *portPtr = htons(port);
}

NSData *address6 = [NSData dataWithBytes:res->ai_addr length:res->ai_addrlen];
[addresses addObject:address6];

PS: Don't forget to set socket's IPv4PreferredOverIPv6 to NO.

@Smeegol Smeegol closed this as completed May 30, 2016
@newacct
Copy link

newacct commented May 30, 2016

You should leave it open until it is fixed in CocoaAsyncSocket, as other people may have the same problem.

@afanti100
Copy link

@newacct @Smeegol @WuChuming ,yeah, I test your code in my project, but not valid. It looks like the port has value, not zero. But can't connects our server. Have you resolved it yet?

@newacct
Copy link

newacct commented May 31, 2016

@afanti100 Did you also set IPv4PreferredOverIPv6 to NO and are you on iOS 9.2+?

@WuChuming
Copy link

In my understanding, If your Phone is on iOS9.2+,it can connect to server with either address literal or hostname from DNS. But if your phone's system is below iOS9.2, it can only connect to server with hostname. Those two things newacct mentioned are key points, check them and if connection is still failed, it is better you get its error message for analyzing.

@afanti100
Copy link

Hmmm, I know. My Phone is iOS9.3 But IPv4PreferredOverIPv6 not set to NO. I set it and is has gone. But I don't know IPv4PreferredOverIPv6. Does it trigger other problems?

@afanti100
Copy link

Thanks for friends.

@Smeegol Smeegol reopened this May 31, 2016
@Smeegol
Copy link
Author

Smeegol commented May 31, 2016

By the way, Apple said that IPv6-only network support can only be tested on iOS 9.2+, so forget iOS 7 or 8.

@ymsmile
Copy link

ymsmile commented Jun 2, 2016

Thanks for all of friends, especially @WuChuming and @Smeegol , I have solved my problem.

@ymsmile
Copy link

ymsmile commented Jun 2, 2016

@WuChuming Envy you your English level ^_^

@keab42
Copy link

keab42 commented Jun 6, 2016

Thankyou for the detailed analysis in this thread, it has saved me many hours of headaches.

Is anybody going to make a PR for the fix?

@chrisballinger
Copy link
Collaborator

Does this commit plus IPv4PreferredOverIPv6 = NO; fix everyone's IPv6 approval issues without breaking anything on previous iOS versions? If so I will merge and push a new version.

@tangxiangbo
Copy link

hello,I met a problem different from yours,I use gcdsocket version is 7.5.0,and it test OK with Nat64 network(my iphone wifi DNS prefix is 2001:2,in this case,my app is normal), but rejected by Apple which is declare that our app can't work in IPV6-only network, I only disabled the IPv4PreferredOverIPv6 properity, I notice that Apple always use the latest OS for check, do I need change the code like yours? Thanks very much.

@ashishgupta301
Copy link

IPV6 problem resolved but code not work for IPV4.
I'm betting that the DNS lookup is returning IPv4 and IPv6 and the system tries to connect to IPv6 first and fails.
solution?

balord added a commit to Figure53/F53OSC that referenced this issue Jan 3, 2018
Fixes compiler warnings and data type errors, plus these specific fixes:
* File descriptor leak - robbiehanson/CocoaAsyncSocket#522
* Fixes UDP connection issues with IPv6: robbiehanson/CocoaAsyncSocket#429 (comment)
* Prevents truncating UDP packets: robbiehanson/CocoaAsyncSocket#222
* Allow UDP packet size above iOS default of 9216 bytes: robbiehanson/CocoaAsyncSocket#536

NOTE: We are holding back on updating to GCDAsyncSocket 7.6.2 because of reports that 7.6.1 may have introduced a crash:
* see robbiehanson/CocoaAsyncSocket#579
@github-actions
Copy link

github-actions bot commented Mar 7, 2020

This issue has been marked as stale, it will be closed automatically if there is no further activity.

AdvafeRaf03 added a commit to AdvafeRaf03/CocoaAsyncSocket that referenced this issue Aug 11, 2024
AdvafeRaf03 added a commit to AdvafeRaf03/CocoaAsyncSocket that referenced this issue Aug 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests