Solved How to gethostbyname(3) ?

Dear community,

Please be advised that my C knowledge is limited to reading code here and there if I feel I have to. I have never written anything in C before.

I played a little with ipfw tables and I got curious why ipfw DNS-resolved something I added to an addr-type table.

The following compiles using cc but gives me a segmentation fault when it reaches the second printf (I think). I'm sure I'm doing a basic mistake related to pointers... I cannot figure out how to access the contents of the struct.

Here is my code:
C:
#include <unistd.h> // gethostname()
#include <sys/param.h> // MAXHOSTNAMELEN
#include <stdio.h> // printf()
#include <netdb.h> // gethostbyname()

int main(void) {
    char hostbuf[MAXHOSTNAMELEN]; // for gethostname(), not gethostBYname()

    struct hostent *ptr; // gethostbyname(3) describes what hostent contains

    /*
    ipfw set 0 table ADDR add anything
    ipfw resolved "anything" because of my "search" in resolv.conf(5) which
    led me to gethostname(3) and gethostbyname(3)
    the following seemed relevant which is why I'm trying to figure out
    what is happening when I "add anything" to an ipfw table of type addr
    $ grep -n gethostbyname /usr/src/sbin/ipfw/tables.c
    135:        if ((he = gethostbyname(host)) == NULL)
    */
    const char ipfw_table_arg[] = "anything";

    gethostname(hostbuf, sizeof(hostbuf));
    printf("gethostname() says: %s\n", hostbuf);

    gethostbyname(ipfw_table_arg);
    printf("gethostbyname() says: %s\n", ptr->h_name);
    /*
    char *h_name
    char **h_aliases
    int h_addrtype
    int h_length
    char **h_addr_list;
    */
}

Could someone assist me with my little programming exercise?
 
You need to assign to the pointer.

C:
#include <unistd.h> // gethostname()
#include <sys/param.h> // MAXHOSTNAMELEN
#include <stdio.h> // printf()
#include <netdb.h> // gethostbyname()
 
int main(void) {
    char hostbuf[MAXHOSTNAMELEN]; // for gethostname(), not gethostBYname()
    const char ipfw_table_arg[] = "www.freebsd.org";

    gethostname(hostbuf, sizeof(hostbuf));
    printf("gethostname() says: %s\n", hostbuf);

    struct hostent *ptr = gethostbyname(ipfw_table_arg);
    if (ptr)
    {
        printf("gethostbyname() says: %s\n", ptr->h_name);
    }
}

I haven't done much error handling. You should add some error messages in case of failure.
 
What’s also good is to build with debug symbols and follow your code through a debugger like gdb.

I’ve not done anything like that myself in a long time so can’t give useful instructions but it’s a good way to learn what’s actually happening as you single step through your code.
 
Thank you, problem solved and new rabbit hole opened ✅

Notes for my future self that might also be useful for anyone who finds this thread:

Code:
# compiling program normally
cc -o a.out test_gethostname.c # lldb will show assembly language and give hints on function calls

# compiling program with options
cc -o a_debug.out -O0 -g test_gethostname.c # lldb will show the source code and underline stuff

# inspecting both side by side looks like a worthwhile activity
lldb -- a.out
lldb -- a_debug.out

# executing the original program creates a.out.core due to the segmentation fault
user@myhostname $ ./a.out
gethostname() says: myhostname
Segmentation fault

# lldb - inspect .core file
lldb -c a.out.core -- a.out # see FreeBSD Developers Handbook section 2.6.2.3.

# lldb - basic commands to use; see FreeBSD Developers Handbook section 2
(lldb) breakpoint set -n main
(lldb) process launch
(lldb) thread step-over # until function causing segmentation fault is reached
(lldb) thread step-in # internal view of function execution

Note:
  • ** pointer to pointer.
  • printf() wants suitable format specifiers.
  • If there is no search keyword present in /etc/resolv.conf (with a domain that is proberly configured) you also get a segmentation fault.

C:
#include <unistd.h> // gethostname()
#include <sys/param.h> // MAXHOSTNAMELEN
#include <stdio.h> // printf()
#include <netdb.h> // gethostbyname()

int main(void) {
    char hostbuf[MAXHOSTNAMELEN];
    gethostname(hostbuf, sizeof(hostbuf));
    printf("gethostname() says: %s\n", hostbuf);

    const char ipfw_table_arg[] = "anything";
    struct hostent *ptr;
    ptr = gethostbyname(ipfw_table_arg);
    printf("gethostbyname() *h_name says: %s\n", ptr->h_name);
    printf("gethostbyname() **h_aliases says: %s\n", *ptr->h_aliases);
    printf("gethostbyname() h_addrtype says: %d\n", ptr->h_addrtype);
    printf("gethostbyname() h_length says: %d\n", ptr->h_length);
    printf("gethostbyname() **h_addr_list says: %s\n", *ptr->h_addr_list);
    /*
    gethostbyname(3) manual page:
    struct hostent {
        char *h_name;
        char **h_aliases;
        int h_addrtype;
        int h_length;
        char **h_addr_list;
    };
    */
}
 
Note:
  • If there is no search keyword present in /etc/resolv.conf (with a domain that is proberly configured) you also get a segmentation fault.
No, that is not how things works, you should not get a segmentation fault because /etc/resolv.conf does not have a specific keyword, you have to do the work of first verifying that gethostname return 0 and not -1 and to check if the ptr is not null.
 
You should use getaddrinfo(3)() nowadays.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        char hostbuf[MAXHOSTNAMELEN];
        struct addrinfo hint, *si;
        struct addrinfo *res = NULL;
        struct sockaddr_in *sa;
        char *ip;
        int ret;

        gethostname(hostbuf, sizeof(hostbuf));
        hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
        hint.ai_family = AF_INET;
        hint.ai_socktype = 0;
        hint.ai_protocol = 0;
        hint.ai_addrlen = 0;
        hint.ai_canonname = NULL;
        hint.ai_addr = NULL;
        hint.ai_next = NULL;

        ret = getaddrinfo(hostbuf, NULL, &hint, &res);
        if (ret == 0) {
                printf("getaddrinfo says: %s\n", res->ai_canonname);
                for (si = res; si != NULL; si = si->ai_next) {
                        if (si->ai_addr != NULL) {
                                sa = (struct sockaddr_in *)(si->ai_addr);
                                ip = inet_ntoa(sa->sin_addr);
                                printf("ip address is: %s\n", ip);
                        }
                }
                freeaddrinfo(res);
        }
        else {
                printf("error: %s\n", gai_strerror(ret));
        }
        exit(0);
}
 
No, that is not how things works, you should not get a segmentation fault because /etc/resolv.conf does not have a specific keyword, you have to do the work of first verifying that gethostname return 0 and not -1 and to check if the ptr is not null.
So it is good practice to add error handling to my final code above (which makes sense) or is there another issue?

Here is what happens at the moment:
If I have no search keyword at all or search example.com (and I assume that example.com is not configured to handle DNS) in my /etc/resolv.conf I get with my final code above:
Code:
$ ./a.out
gethostname() says: myhostname
Segmentation fault (core dumped)

You should use getaddrinfo(3)() nowadays.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(int argc, char **argv)
{
        char hostbuf[MAXHOSTNAMELEN];
        struct addrinfo hint, *si;
        struct addrinfo *res = NULL;
        struct sockaddr_in *sa;
        char *ip;
        int ret;

        gethostname(hostbuf, sizeof(hostbuf));
        hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
        hint.ai_family = AF_INET;
        hint.ai_socktype = 0;
        hint.ai_protocol = 0;
        hint.ai_addrlen = 0;
        hint.ai_canonname = NULL;
        hint.ai_addr = NULL;
        hint.ai_next = NULL;

        ret = getaddrinfo(hostbuf, NULL, &hint, &res);
        if (ret == 0) {
                printf("getaddrinfo says: %s\n", res->ai_canonname);
                for (si = res; si != NULL; si = si->ai_next) {
                        if (si->ai_addr != NULL) {
                                sa = (struct sockaddr_in *)(si->ai_addr);
                                ip = inet_ntoa(sa->sin_addr);
                                printf("ip address is: %s\n", ip);
                        }
                }
                freeaddrinfo(res);
        }
        else {
                printf("error: %s\n", gai_strerror(ret));
        }
        exit(0);
}

So ipfw's codebase could also use getaddrinfo(3) and achieve the same as with gethostbyname(3)? Or is the presence of getaddrinfo(3) in ipfw's codebase the reason why ipfw does what it does (see bold below, this is what made me write the original code).

I originally had search fritz.box in my /etc/resolv.conf (context: consumer router vendor AVM used fritz.box for web GUI access to their routers, local DNS stuff, etc. and forgot to register the domain when .box became available as a TLD.).
So what happened is that when I tried to provoke some ipfw error messages using the command ipfw table ADDR add re0 (where ADDR is a table of type addr) ipfw apparently resolved re0.fritz.box and I was wondering who the heck 45.76.93.104 is and tried to figure out why ipfw did that:
Code:
# ipfw table ADDR create type addr
# ipfw table ADDR add re0 # expecting an error message because breaking things is fun
added: 45.76.93.104/32 0 # no error but an IP I have never heard of, wondering where it comes from

(the domain fritz.box does some sort of DNS handling it seems in contrast to example.com)

So I consulted resolver(5) and it mentions gethostname(3) and gethostbyname(3) and then I did:
Code:
$ grep -n -E "(gethostname|gethostbyname)" /usr/src/sbin/ipfw/*
/usr/src/sbin/ipfw/ipfw2.c:3097:        if ((he = gethostbyname(host)) == NULL)
/usr/src/sbin/ipfw/ipv6.c:303:        if ((he = gethostbyname2(host, AF_INET6)) == NULL)
/usr/src/sbin/ipfw/nat.c:207:    hp = gethostbyname (str);
/usr/src/sbin/ipfw/tables.c:135:        if ((he = gethostbyname(host)) == NULL)

I figured from the grep output that I'm on the right track decided that it cannot be that difficult to use gethostname(3) and getbyhostname(3) "manually" so I wrote my half-working program.

Anyway, I think my original question at the beginning of the thread and the question that made me write the program is answered, I just wanted to provide the full context.
 
From the manpage of gethostbyname() ,

The getaddrinfo(3) and getnameinfo(3) functions are preferred over the
gethostbyname(), gethostbyname2(), and gethostbyaddr() functions.

and getaddrinfo(3)(),

It is a replacement for
and provides more flexibility than the gethostbyname(3) and
getservbyname(3) functions.

So, I think replacing existing code that use gethostbyname() is not needed (It works for IPv4), but code that are written now should use getaddrinfo().
 
Back
Top