1

I'm running a program which does (in its source code):

gethostbyname("whatever");

and I want it to use the local machine's address instead. I can't change the source. If I were root, it would be easy-peasy - I would just alias this name in /etc/hosts. But - is there something I can do as a non_host user for the same effect on my gethostbyname() call?

/etc/nsswitch.conf has:

hosts:      files nis dns myhostname

in case it matters.

muru
  • 69,900
  • 13
  • 192
  • 292
einpoklum
  • 8,772
  • 19
  • 65
  • 129

2 Answers2

2

Run it in a container

Using podman (or possibly rootless Docker) you could run your code inside a container in which you have set up an alias. E.g., I have the following code that calls gethostbyname:

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

int main(int argc, char **argv) {
  for (int i = 1; i < argc; i++) {
    struct hostent *entry = gethostbyname(argv[i]);

    for (int j = 0; entry->h_addr_list[j] != NULL; j++) {
      printf("%s: %s\n", entry->h_name,
             inet_ntoa(*(struct in_addr *)entry->h_addr_list[j]));
    }
  }
  return 0;
}

If I compile this into the command lookuphost and then run ./lookuphost google.com, I get:

$ ./lookuphost google.com
google.com: 142.251.32.110

If I want google.com to resolve to 1.2.3.4, I can do something like this:

$ podman run -it --rm \
  --add-host google.com:1.2.3.4 \
  -v $PWD:$PWD -w $PWD fedora:37 \
  ./lookuphost google.com
google.com: 1.2.3.4

Use function interposition

You can use function interposition to wrap the gethostbyname call with your own code. For example, if I put the following in gethostbyname.c:

#define _GNU_SOURCE

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

struct hostent *gethostbyname(const char *name) {
  static struct hostent *(*real_gethostbyname)(const char *) = NULL;

  if (!real_gethostbyname) {
    real_gethostbyname = dlsym(RTLD_NEXT, "gethostbyname");
  }

  struct hostent *entry = real_gethostbyname(name);

  if (strcmp(name, "google.com") == 0) {
    struct in_addr inp;

    inet_aton("1.2.3.4", &inp);
    entry->h_addr_list[0] = (char *)&inp;
  }

  return entry;
}

And then build that into a shared library:

gcc -c -fPIC gethostbyname.c
gcc -shared -ldl -fPIC gethostbyname.o -o gethostbyname.so

I can LD_PRELOAD that and run lookuphost and get the expected answer:

$ LD_PRELOAD=./gethostbyname.so ./lookuphost google.com
google.com: 1.2.3.4
larsks
  • 32,449
  • 5
  • 54
  • 70
1
  • Use the nss_wrapper preload library. It is similar to the suggestion by @larsks, but works at a slightly lower level (and provides emulation of passwd as well as hosts). Samba uses it for its testsuite.

    export LD_PRELOAD=libnss_wrapper.so
    export NSS_WRAPPER_HOSTS=/path/to/your/hosts
    
  • Glibc used to have a specific feature for this – you could create a "host aliases" file via $HOSTALIASES, containing a list of alias real.hostname.tld pairs (i.e. an additional level of indirection, not pointing to IP addresses).

    $ export HOSTALIASES=~/.hosts
    $ cat > $HOSTALIASES <<!
    whatever   real.example.net
    !
    

    However, I believe there were plans to remove this feature, and it no longer seems to work on my systems, at least not with getent hosts. If you're running a distro with an older Glibc, this may still work.

u1686_grawity
  • 4,580
  • 20
  • 27