I've written a small dynamic library that interposes calls to getaddrinfo and connect. I insert this library using DYLD_INSERT_LIBRARIES on Firefox and Safari to hijack requests for www.apple.com and send them to www.microsoft.com. The code works on Firefox, but while Safari calls into my interposed functions, it ignores the redirection.
I'm wondering if anyone has insight into why Safari ignores the redirection and whether there's a way, perhaps via addrinfo flags, to get Safari to behave as Firefox does. My code listing follows.
// -*- mode: c++ -*-
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <dlfcn.h>
#include <dns_sd.h>
#include <cstdio>
#include <cstdlib>
#include <cstdarg>
#include <cstring>
#include <string>
typedef int (*connect_type) (int, struct sockaddr const*, socklen_t);
typedef int (*getaddrinfo_type)(
const char*, const char*, struct addrinfo const*, addrinfo**);
connect_type real_connect = 0;
getaddrinfo_type real_getaddrinfo = 0;
int
do_getaddrinfo_impl(const char* nodename, const char* servname,
struct addrinfo const* hints, struct addrinfo** ppai)
{
int result = real_getaddrinfo (nodename, servname, hints, ppai);
if(result != 0) {
fprintf(stderr, "! real_getaddrinfo error on %s: %s\n", nodename, gai_strerror(result));
}
return result;
}
int
do_getaddrinfo(const char* nodename, const char* servname,
struct addrinfo const* hints, struct addrinfo** ppai)
{
if (0 == nodename) {
return do_getaddrinfo_impl(nodename, servname, hints, ppai);
}
if (hints->ai_family == AF_INET6) {
printf ("# do_getaddrinfo : IPv6 resolution\n");
return do_getaddrinfo_impl(nodename, servname, hints, ppai);
}
if ( hints->ai_flags & AI_PASSIVE
|| hints->ai_flags & AI_CANONNAME
|| hints->ai_flags & AI_NUMERICHOST
|| hints->ai_flags & AI_NUMERICSERV) {
printf ("# do_getaddrinfo : unsupported resolution flags\n");
return do_getaddrinfo_impl (nodename, servname, hints, ppai);
}
if ((hints->ai_flags & AI_V4MAPPED) && !(hints->ai_flags & AI_ALL)) {
printf ("# do_getaddrinfo : unsupported IPv6-mapping\n");
return do_getaddrinfo_impl (nodename, servname, hints, ppai);
}
// redirect requests for www.apple.com to microsoft at 65.55.57.80 (0x41373950)
if(0 == strcmp(nodename, "www.apple.com")) {
addrinfo* pai = (addrinfo*)calloc (1, sizeof (addrinfo));
pai->ai_flags = hints->ai_flags;
pai->ai_family = AF_INET;
pai->ai_socktype = hints->ai_socktype;
pai->ai_protocol = IPPROTO_TCP;
pai->ai_addrlen = sizeof (sockaddr_in);
sockaddr* psa = reinterpret_cast< sockaddr* > (
calloc (1, sizeof (sockaddr)));
sockaddr_in& sain = reinterpret_cast< sockaddr_in& > (*psa);
#if !defined (POS_NO_SIN_LEN)
sain.sin_len = sizeof *psa;
#endif // POS_NO_SIN_LEN
sain.sin_family = AF_INET;
sain.sin_addr.s_addr = htonl(0x41373950);
pai->ai_addr = psa;
pai->ai_canonname = 0;
pai->ai_next = 0;
*ppai = pai;
return 0;
}
else {
return do_getaddrinfo_impl(nodename, servname, hints, ppai);
}
}
////////////////////////////////////////////////////////////////////////
extern "C" {
int
fake_getaddrinfo(const char* nodename, const char* servname,
struct addrinfo const* hints, struct addrinfo** ppai)
{
if (real_getaddrinfo == 0)
real_getaddrinfo = &getaddrinfo;
return do_getaddrinfo (nodename, servname, hints, ppai);
}
int
fake_connect (int s, sockaddr const* ptr, socklen_t len)
{
if (real_connect == 0)
real_connect = &connect;
return do_connect (s, ptr, len);
}
__attribute__((used)) static struct {
void const *a, *b;
} arr [] __attribute__ ((section ("__DATA, __interpose"))) = {
{ (void*)fake_connect, (void*)connect },
{ (void*)fake_getaddrinfo, (void*)getaddrinfo }
};
} // extern "C"
By way of follow-up, I discovered that Safari was setting the AI_NUMERICHOST flag in the hints parameter and so the test for that flag as an unsupported resolution flag resulted in my redirection code never being called. I removed the test for AI_NUMERICHOST and added the following bit to resolve the issue.
Hope that helps someone else.