switchbrew / libnx

Library for Switch Homebrew

Home Page:https://switchbrew.github.io/libnx/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Problem with getaddrinfo() and gethostbyname()

rsn8887 opened this issue · comments

I need getaddrinfo for networking in OpenTTD. https://github.com/rsn8887/OpenTTD/blob/836a341b55e46f839d51a4f86635b7ab52925bc9/src/network/core/address.cpp#L248
And in general it would be nice to have it working on the Switch.

However, I seem to have stumbled upon a bug, or I am missing something:

  • int e=getaddrinfo(...); is supposed to return a valid error number e, but gai_strerror(e) gives me "unknown error" clearly something is wrong.
  • I checked that I am submitting a valid address, "content.openttd.org" and valid port 3978. I tried family AF_UNSPEC (default) and AF_INET, to make sure it is not ipv6 that is the problem.
  • The error number I get is "22"
  • The max possible number should be "15", according to https://linux.die.net/man/3/getaddrinfo and the libnx header nx/external/bsd/include/netdb.h
  • It looks to me as if the getaddrinfo implementation is taking that error from sfdnsresGetAddrInfo, which ultimately traces back to ipcparse.
    static inline Result ipcParse(IpcParsedCommand* r) {
  • The rough call sequence initiated internally by getaddrinfo is this: getaddrinfo -> sfdnsresGetAddrInfo -> _sfdnsresDnsRequestCommand -> _sfdnsresDispatchDnsRequest -> _sfdnsresDispatchCommand -> ipcParse and somehow an error 22 pops out in the end.

Which brings me to the questions:

  • Is there anything special I have to do (compiler flags etc.) to make getaddrinfo() work on Switch?
  • Is there any software that used this succesfully?
  • Looking for a working example, I saw that switch-libcurl is configured with "--disable-ipv6 --disable-unix-sockets". Is that maybe related?
  • At least two recent commits seem to have fixed some problems in ipc-related structs by adding the ALIGNED attribute. See 966d554 and 9f45bb4
    In the sequence of function calls initiated via getaddrinfo, a similar structure is used. Could this be related? See here:

static Result _sfdnsresDispatchDnsRequest(IpcCommand *c, SfdnsresRequestResults *ret, const void *raw, size_t raw_size, bool has_serialized_data_out) {

Try gai_strerror. EDIT: nvm

Sorry the line mentioned in the last question with the suspicious strict was wrong it should be

static Result _sfdnsresDispatchDnsRequest(IpcCommand *c, SfdnsresRequestResults *ret, const void *raw, size_t raw_size, bool has_serialized_data_out) {

Not sure if getaddrinfo ever worked (?).

"It looks to me as if ... The rough call sequence ...." It's just a service command + wrappers...

"switch-libcurl ... Is that maybe related?" no

"ALIGNED" *PACKED That's also unrelated.

Somewhat small example showing the problem that both getaddrinfo and gethostbyname fail.

Compiles with standard switchbrew/switch-examples Makefile.

Am I missing come kind of libnx init_tcp call or something to enable the network? Or maybe I am including the wrong headers?

This little app should simply use DNS to turn hardcoded hostname into IP numbers and print the numbers. Hardcoded hostname and port can be changed to anything. Tried with "www.google.com" "http", "172.217.2.36" "80" etc everything throws error 22. I also tested gethostbyname it also always returns a zero.

Adapted from the best resource on sockets I could find: http://beej.us/guide/bgnet/html/multi/syscalls.html#socket

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include <switch.h>

//See also libnx hid.h.

int main(int argc, char **argv)
{
    consoleInit(NULL);

    char hostname[] = "content.openttd.org";
    char port[] = "3978";

    struct addrinfo hints, *res, *p;
    int status;
    char ipstr[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
    hints.ai_socktype = SOCK_STREAM;

    printf("Calling getaddrinfo hostname: \"%s\" port \"%s\":\n", hostname, port);

    if ((status = getaddrinfo(hostname, port, &hints, &res)) != 0) {
        printf("ERR getaddrinfo: %s, error nr: %d\n", gai_strerror(status), status);
        printf("Calling gethostbyname since getaddrinfo failed\n");
    	struct hostent *he = gethostbyname(hostname);
    	if (he == NULL) {
    		printf("ERR gethostbyname returned NULL\n");
        }
    }

    printf("IP addresses for %s:\n", hostname);

    for(p = res;p != NULL; p = p->ai_next) {
        void *addr;
        char *ipver;

        // get the pointer to the address itself,
        // different fields in IPv4 and IPv6:
        if (p->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
        } else { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }

        // convert the IP to a string and print it:
        inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
        printf("  %s: %s\n", ipver, ipstr);
    }

    freeaddrinfo(res); // free the linked list

    // Main loop
    while(appletMainLoop())
    {
        //Scan all the inputs. This should be done once for each frame
        hidScanInput();
        //hidKeysDown returns information about which buttons have been just pressed (and they weren't in the previous frame)
        u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
        if (kDown & KEY_PLUS) break; // break in order to return to hbmenu
        consoleUpdate(NULL);
    }

    consoleExit(NULL);
    return 0;
}

Here's the output from above program:

socketInitialize/socketExit missing.

Perfect that was it! Now it works. :) Thank you so much!

I will update the example.