/* * Michael John Wensley * * The idea of this is to get a program that tries to resolve one name, * to resolve another instead. This is just so that programs that do not support SRV yet * can be tricked into connecting properly. * * Changelog: 2016-01-21 added getaddrinfo nat64 capability * * 2>&1 ltrace -f -e gethost*+getaddr*+getname*+connect env LD_PRELOAD=libdivert.so LD_LIBRARY_PATH=$PWD getent ahosts my.example. * gcc -ggdb -Wall -Wextra -shared -o libdivert.so libdivert.c -ldl -fPIC -march=native && 2>&1 ltrace -f -e gethost*+getaddr*+getname*+connect env LD_PRELOAD=libdivert.so LD_LIBRARY_PATH=$PWD \ * env LD_PRELOAD=libdivert.so LD_LIBRARY_PATH=$PWD getent ahosts my.example. * dmesg | grep libdivert.so | cut -d" " -f8,14 | while read O P; do addr2line -e libdivert.so `printf %x $(( 0x$O - 0x${P:13:12}))`; done */ #include #include #include #include #include #include #include #include #define RTLD_NEXT ((void*)(-1l)) extern int h_errno; static const char* divert(const char* name) { char* next; next = getenv(name); if (next != NULL) { return next; fprintf(stderr, "Override a name with getaddrinfo %s and service \n", name); } return name; } // static void* libc = RTLD_NEXT; static int (*real_socket)(int domain, int type, int protocol) = 0; static int (*real_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = 0; static int (*real_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = 0; static int (*real_getaddrinfo)(const char*, const char*, const struct addrinfo *, struct addrinfo **) = 0; static int (*real_gethostbyname_r)(const char*, struct hostent*, char *, size_t, struct hostent**, int*) = 0; static int (*real_getnameinfo)(const struct sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, unsigned int) = 0; static struct hostent* (*real_gethostbyname)(const char*) = 0; /* try to locate the libc if possible */ static void __attribute__((constructor)) _init (void) { void* what = RTLD_NEXT; // libc = dlopen("libc.so.6", RTLD_LOCAL | RTLD_NODELETE | RTLD_NOW); //if (libc) { // what = libc; //} real_bind = dlsym(what, "bind"); real_socket = dlsym(what, "socket"); real_connect = dlsym(what, "connect"); real_getnameinfo = dlsym(what, "getnameinfo"); real_getaddrinfo = dlsym(what, "getaddrinfo"); real_gethostbyname = dlsym(what, "gethostbyname"); real_gethostbyname_r = dlsym(what, "gethostbyname_r"); } /* if we opened libc we try to close it */ static void __attribute__((destructor)) _fini (void) { // if (libc) { // dlclose(libc); // libc = 0; // } } /* sysdeps/posix/getaddrinfo.c hack - this depends on sockaddr directly following addrinfo * Noticed that it currently mallocs each addrinfo with a sockaddr_in or sockaddr_in6 suffixed * * Let's replace any returned IPv4 address with IPv6 0064:ff9b::IPv4, same with IPv6 ::ffff:/96 * warning: some programs seem to use gethost etc instead, use ltrace to see if this applies */ static inline void getaddrinfo_nat64(struct addrinfo** address) { while (*address != NULL) { /* only try if the name follows the address */ if ((*address)->ai_family == AF_INET) { if ((*address)->ai_addrlen == sizeof (struct sockaddr_in) && (void*)(*address + 1) == (*address)->ai_addr) { struct addrinfo* nat64addr = malloc ( sizeof (struct addrinfo) + sizeof (struct sockaddr_in6)); if (nat64addr) { *nat64addr = **address; struct sockaddr_in* oldaddress = (struct sockaddr_in*)((*address) + 1); struct sockaddr_in6* newaddress = (struct sockaddr_in6*)(nat64addr + 1); nat64addr->ai_flags = (*address)->ai_flags; nat64addr->ai_family = AF_INET6; nat64addr->ai_socktype = (*address)->ai_socktype; nat64addr->ai_protocol = (*address)->ai_protocol; nat64addr->ai_addrlen = sizeof (struct sockaddr_in6); nat64addr->ai_addr = (void*)newaddress; newaddress->sin6_family = AF_INET6; newaddress->sin6_port = oldaddress->sin_port; newaddress->sin6_flowinfo = 0; newaddress->sin6_scope_id = 0; uint32_t* bits = (uint32_t*)&(newaddress->sin6_addr); bits[0] = ntohl(0x0064ff9b); bits[1] = ntohl(0); bits[2] = ntohl(0); bits[3] = oldaddress->sin_addr.s_addr; free(*address ); *address = nat64addr; } } } /* if we get an ::ffff:0.0.0.0/96 address such as via AI_V4MAPPED convert to 64:ff9b::/96 */ if ((*address)->ai_family == AF_INET6) { if ((*address)->ai_addrlen == sizeof (struct sockaddr_in6) && (void*)(*address + 1) == (*address)->ai_addr) { struct sockaddr_in6* oldaddress = (struct sockaddr_in6*)((*address) + 1); uint32_t* bits = (uint32_t*)&(oldaddress->sin6_addr); if ( (bits[0] == ntohl(0x00000000)) && (bits[1] == ntohl(0x00000000)) && (bits[2] == ntohl(0x0000ffff)) ) { bits[0] = ntohl(0x0064ff9b); bits[2] = ntohl(0); } } } address = &((*address)->ai_next); } } /* lets make any sockets created inet6 ones */ int socket(int domain, int type, int protocol) { if (domain == AF_INET) { return (*real_socket)(AF_INET6, type, protocol); } return (*real_socket)(domain, type, protocol); } /* If they try 0.0.0.0, correct it to :: */ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { struct sockaddr_in6 newaddress; if (addr->sa_family == AF_INET && addrlen >= sizeof(struct sockaddr_in)) { struct sockaddr_in* oldaddress = (struct sockaddr_in*)addr; if (oldaddress->sin_addr.s_addr == htonl(INADDR_ANY)) { newaddress.sin6_family = AF_INET6; newaddress.sin6_port = oldaddress->sin_port; newaddress.sin6_flowinfo = 0; newaddress.sin6_addr = in6addr_any; newaddress.sin6_scope_id = 0; return (*real_bind)(sockfd, (const struct sockaddr *)&newaddress, sizeof(struct sockaddr_in6)); } } return (*real_bind)(sockfd, addr, addrlen); } /* and the connections as well, if passed an ::ffff:/96 then convert it */ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int result; struct sockaddr_in6 newaddress; if (addr->sa_family == AF_INET && addrlen >= sizeof(struct sockaddr_in)) { struct sockaddr_in* oldaddress = (struct sockaddr_in*)addr; newaddress.sin6_family = AF_INET6; newaddress.sin6_port = oldaddress->sin_port; newaddress.sin6_flowinfo = 0; newaddress.sin6_scope_id = 0; uint32_t* bits = (uint32_t*)&(newaddress.sin6_addr); bits[0] = ntohl(0x0064ff9b); bits[1] = ntohl(0); bits[2] = ntohl(0); bits[3] = oldaddress->sin_addr.s_addr; result = (*real_connect)(sockfd, (const struct sockaddr *)&newaddress, sizeof(struct sockaddr_in6)); return result; } if (addr->sa_family == AF_INET6 && addrlen >= sizeof(struct sockaddr_in6)) { struct sockaddr_in6* oldaddress = (struct sockaddr_in6*)addr; newaddress.sin6_family = AF_INET6; newaddress.sin6_port = oldaddress->sin6_port; newaddress.sin6_flowinfo = oldaddress->sin6_flowinfo; newaddress.sin6_scope_id = oldaddress->sin6_scope_id; newaddress.sin6_addr = oldaddress->sin6_addr; uint32_t* bits = (uint32_t*)&(newaddress.sin6_addr); if ( (bits[0] == ntohl(0)) && (bits[1] == ntohl(0)) && (bits[2] == ntohl(0x0000ffff)) ) { bits[0] = ntohl(0x0064ff9b); bits[2] = ntohl(0); result = (*real_connect)(sockfd, (const struct sockaddr *)&newaddress, sizeof(struct sockaddr_in6)); return result; } } return (*real_connect)(sockfd, addr, addrlen); } /* This hooks into any call to getaddrinfo by the target program * if it tries to resolve a trapped name, we have it resolve something else. */ int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo newhints; if (hints) { newhints = *hints; /* return ipv4 addresses even if we do not have ipv4 interfaces, we are going to rewrite them */ newhints.ai_flags &= ~AI_ADDRCONFIG; } int result = (*real_getaddrinfo)(divert(node), service, hints ? &newhints : NULL, res); // if (result == 0) { // getaddrinfo_nat64(res); // } return result; } int gethostbyname_r(const char *name, struct hostent *ret, char *buf, size_t buflen, struct hostent **result, int *h_errnop) { return (*real_gethostbyname_r)(divert(name), ret, buf, buflen, result, h_errnop); } struct hostent* gethostbyname(const char *name) { return (*real_gethostbyname)(divert(name)); } int getnameinfo(const struct sockaddr* sa, socklen_t salen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags) { return (*real_getnameinfo)(sa, salen, host, hostlen, serv, servlen, flags); }