getaddrinfo .. nodename nor servname provided

I working on ssh protocol based app.


I wrote code to get hostname:


struct addrinfo hints;
        int rv;
        struct in6_addr serveraddr;
        memset(&hints, 0, sizeof(hints));
        struct addrinfo *res, *res0;
        hints.ai_family = PF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_NUMERICSERV;
        int result = inet_pton(PF_INET, [server.hostname UTF8String], &serveraddr);
        if (result == 1)
        {
            hints.ai_family = PF_INET;
            hints.ai_flags |= AI_NUMERICHOST;
        }
        else
        {
            result = inet_pton(PF_INET6, [server.hostname UTF8String], &serveraddr);
            if (result == 1)
            {
               
                hints.ai_family = PF_INET6;
                hints.ai_flags |= AI_NUMERICHOST;
            }
        }
        rv = getaddrinfo([server.hostname UTF8String], [[[server getPort] stringValue] UTF8String], &hints, &res0) ;
        if (rv) {
            dispatch_async(dispatch_get_main_queue(), ^{
                server.error = true ;
                server.details = [NSString stringWithUTF8String:gai_strerror(rv)] ;
                [[NSNotificationCenter defaultCenter] postNotificationName:@"serverInfo"
                                                                    object:self
                                                                  userInfo:[NSDictionary dictionaryWithObject:server
                                                                                                       forKey:@"server"]];
            }) ;
            [Helpers debugWithString:[NSString stringWithFormat:@"getaddrinfo: %s\n", gai_strerror(rv)]] ;
            return 1;
        }

Many times I get "nodename nor servname provided" error message (randomly) .. If I refreshing connections I'm not getting this error.


How to write reliable code for dns resolution ? And fix this problem ?

Accepted Reply

With regards your

getaddrinfo
problem, I’m not exactly sure what’s going wrong here but my first inclination is that you define this problem away by not doing your own DNS resolution. Let me explain…

I working on ssh protocol based app.

I presume this is an SSH client. If so, I’m concerned about your use of

getaddrinfo
. When I see
getaddrinfo
in a network client that’s usually because folks are doing the traditional BSD Sockets two-step connect, that is:
  1. Use

    getaddrinfo
    to get a list of IP addresses for a DNS name.
  2. Connect to those IP addresses.

This is problematic on iOS because:

  • To work reasonably well in a wide variety of circumstances you have to write a lot of extra code (at a minimum you need to implement Happy Eyeballs, and even that code won’t be as good as the Happy Eyeballs 2 code that’s built in to the system).

  • Even once you do that there are still things that you won’t be able to make work. Read this post to learn more.

A better approach is to use one of the system’s connect-by-name APIs. How you do that depends on how your networking code works:

  • If you’re writing your own networking code, use an Apple API for TCP and you’ll get connect-by-name for free. The best API for this is the Network framework, but if you want to support older versions of iOS then older APIs (like

    NSStream
    ) also work fine.
  • If you’re using a third-party library for your networking code and that library assumes BSD Sockets, my recommendation is that you cut the ‘head’ of that library, using an Apple API to get connect-by-name and then extracting the socket and passing that to your library. See this post for more details.

I realise that this is extra work, but you’re already spending time investigating your

getaddrinfo
problems and IMO it’s a better to use that time to solve the problem properly.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

With regards your

getaddrinfo
problem, I’m not exactly sure what’s going wrong here but my first inclination is that you define this problem away by not doing your own DNS resolution. Let me explain…

I working on ssh protocol based app.

I presume this is an SSH client. If so, I’m concerned about your use of

getaddrinfo
. When I see
getaddrinfo
in a network client that’s usually because folks are doing the traditional BSD Sockets two-step connect, that is:
  1. Use

    getaddrinfo
    to get a list of IP addresses for a DNS name.
  2. Connect to those IP addresses.

This is problematic on iOS because:

  • To work reasonably well in a wide variety of circumstances you have to write a lot of extra code (at a minimum you need to implement Happy Eyeballs, and even that code won’t be as good as the Happy Eyeballs 2 code that’s built in to the system).

  • Even once you do that there are still things that you won’t be able to make work. Read this post to learn more.

A better approach is to use one of the system’s connect-by-name APIs. How you do that depends on how your networking code works:

  • If you’re writing your own networking code, use an Apple API for TCP and you’ll get connect-by-name for free. The best API for this is the Network framework, but if you want to support older versions of iOS then older APIs (like

    NSStream
    ) also work fine.
  • If you’re using a third-party library for your networking code and that library assumes BSD Sockets, my recommendation is that you cut the ‘head’ of that library, using an Apple API to get connect-by-name and then extracting the socket and passing that to your library. See this post for more details.

I realise that this is extra work, but you’re already spending time investigating your

getaddrinfo
problems and IMO it’s a better to use that time to solve the problem properly.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Not best code, but might be useful for somebody:


Boolean ipv4 = false, ipv6 = false ;
        struct sockaddr_in serveraddr4;
        struct sockaddr_in6 serveraddr6;
        struct sockaddr *addressGeneric;
       
        CFHostRef hostRef = CFHostCreateWithName(kCFAllocatorDefault, (__bridge CFStringRef _Nonnull)(server.hostname));
       
        if (hostRef != NULL) {
            CFStreamError  resolutionError ;
            CFHostScheduleWithRunLoop(hostRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode) ;
           
            Boolean result = CFHostStartInfoResolution(hostRef, kCFHostAddresses, &resolutionError) ;
            if (result == true) {
                Boolean hasBeenResolved ;
                CFArrayRef sockaddrs = CFHostGetAddressing(hostRef, &hasBeenResolved) ;
                if (hasBeenResolved == true) {
                    if (sockaddrs != NULL) {
                        CFHostScheduleWithRunLoop(hostRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode) ;
                        CFHostCancelInfoResolution(hostRef, kCFHostAddresses) ;
                        int i = 0;
                        for (i=0; i < CFArrayGetCount(sockaddrs); i++) {
                            NSData *data = CFArrayGetValueAtIndex(sockaddrs, i) ;
                            addressGeneric = (struct sockaddr*) data.bytes ;
                            switch (addressGeneric->sa_family) {
                                    case AF_INET: {
                                        ipv4 = true ;
                                        memcpy((void*)&serveraddr4, (void*)addressGeneric, sizeof(struct sockaddr_in)) ;
                                    } break ;
                                    case AF_INET6: {
                                        ipv6 = true ;
                                        memcpy((void*)&serveraddr6, (void*)addressGeneric, sizeof(struct sockaddr_in6)) ;
                                    } break ;
                            }
                        }
                    }
                } else {
                   
                }
            }
            CFRelease(hostRef) ;
        }

might be useful for somebody

Switching to a different DNS resolution API might help your specific problem with

getaddrinfo
but it doesn’t address the fundamental problems associated with doing a two-step connection process (resolve-then-connect). To avoid those problems you must use a connect-by-name API.

If you stick with this resolve-then-connect approach then you will inevitably get bug reports from users with problems that only show up in their specific oddball network environment.

Beyond that, the code you posted is very strange. It looks ilke you’re trying to use

CFHost
synchronously (less than ideal but let’s go with that for the moment) but then you’re also messing around with run loops. There are three standard ways of using
CFHost
:
  • Asynchronously (A)

  • Synchronously (B)

  • Synthetic synchronous, is asynchronous but scheduled on a custom run loop mode that you run while waiting for the resolution to complete (C)

It looks like you’re trying to do B but with bits of C added in for reasons I don’t understand.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"