/* * Copyright (c) 2001 Nicholas W. Sayer. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #if (defined(__unix__) || defined(unix)) && !defined(USG) #include #endif #define NUMSOCK (1024) /* Maximum number of supported sockets */ enum status { NEED_INIT, INACTIVE, ACTIVE }; static enum status active = NEED_INIT; struct list_node { struct list_node *next; in_addr_t phony_address; struct addrinfo *ai; }; static struct list_node *the_list; static in_addr_t current_address; static int sock_type[NUMSOCK]; static int (*R_socket)(); static int (*R_connect)(); static int (*R_close)(); static int (*R_getaddrinfo)(); static struct hostent *(*R_gethostbyname)(); static void initlib() { void *handle; int i; active = INACTIVE; for(i = 0; i < NUMSOCK; i++) sock_type[i] = -1; #ifdef BSD handle = dlopen("/usr/lib/libc.so", RTLD_LAZY); #elif defined(LINUX) handle = dlopen("/lib/libc.so", RTLD_LAZY); #else #error "Unknown operating system" #endif if (handle == NULL) return; #define SET_UP(x,y) if ((x = dlsym(handle, y)) == NULL) return SET_UP(R_socket, "socket"); SET_UP(R_connect, "connect"); SET_UP(R_close, "close"); SET_UP(R_getaddrinfo, "getaddrinfo"); SET_UP(R_gethostbyname, "gethostbyname"); the_list = NULL; current_address = 0; /* If we got here, everything worked. We are ready to go */ active = ACTIVE; } /* * Socket needs to do nothing more than make a note of the 2nd argument * if the socket is AF_INET */ int socket(int family, int type, int proto) { int sock; if (active == NEED_INIT) initlib(); sock = R_socket(family, type, proto); if (active == INACTIVE || sock < 0 || sock >= NUMSOCK || family != PF_INET) return sock; sock_type[sock] = type; return sock; } /* * If the socket had its 2nd argument noted before, then we need * to look up any previously cached information we got from * gethostbyname and make connections to the real addresses * instead. If we connect successfully, we dup2() over the old * (user-supplied) socket. */ int connect(int s, const struct sockaddr *name, socklen_t namelen) { struct list_node *trav; struct addrinfo *res; int out, newsock; char buf[256]; if (active == NEED_INIT) initlib(); if (active == INACTIVE) return R_connect(s, name, namelen); if (s < 0 || s >= NUMSOCK) /* ignore rather than core */ return R_connect(s, name, namelen); if (sock_type[s] < 0) /* if, for instance, it is AF_UNIX */ return R_connect(s, name, namelen); for(trav = the_list; trav != NULL; trav = trav->next) if (trav->phony_address == ((struct sockaddr_in *)name)->sin_addr.s_addr) { errno = ECONNREFUSED; /* if res == NULL */ for(res = trav->ai; res != NULL; res = res->ai_next) { #if 0 fprintf(stderr, "about to try a connect %s...\n", res->ai_family == PF_INET6? inet_ntop(AF_INET6, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, buf, sizeof(buf)): inet_ntoa(((struct sockaddr_in *)res->ai_addr)->sin_addr) ); #endif newsock = socket(res->ai_family, sock_type[s], 0); /* XXX should really find an agnostic way to do this */ switch (res->ai_family) { case AF_INET: ((struct sockaddr_in *)res->ai_addr)->sin_port = ((struct sockaddr_in *)name)->sin_port; break; case AF_INET6: ((struct sockaddr_in6 *)res->ai_addr)->sin6_port = ((struct sockaddr_in *)name)->sin_port; break; default: fprintf(stderr, "Found host not in AF_INET or AF_INET6. Not supported.\n"); } if (!R_connect(newsock, res->ai_addr, res->ai_addrlen)) { dup2(newsock, s); close(newsock); return 0; } close(newsock); } return -1; } errno = ECONNREFUSED; return -1; } int close(int s) { int out; if (active == NEED_INIT) initlib(); if (s >= 0 && s < NUMSOCK) sock_type[s] = -1; return R_close(s); } int getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { struct addrinfo myhints, *returned; struct list_node *new; int ret; if (active == NEED_INIT) initlib(); if (active == INACTIVE) return R_getaddrinfo(nodename, servname, hints, res); if (hints != NULL) bcopy(hints, &myhints, sizeof(struct addrinfo)); else bzero(&myhints, sizeof(struct addrinfo)); myhints.ai_family = PF_UNSPEC; active = INACTIVE; ret = getaddrinfo(nodename, servname, &myhints, &returned); active = ACTIVE; if (ret) return ret; /* we have a result. Now we must save it and send a dummy result */ if ((*res = malloc(sizeof(struct addrinfo))) == NULL) { return EAI_MEMORY; } if (((*res)->ai_addr = malloc(sizeof(struct sockaddr_in))) == NULL) { free(*res); *res = NULL; return EAI_MEMORY; } (*res)->ai_next = NULL; (*res)->ai_family = AF_INET; (*res)->ai_socktype = returned->ai_socktype; (*res)->ai_protocol = returned->ai_protocol; (*res)->ai_addrlen = sizeof(struct sockaddr_in); (*res)->ai_canonname = strdup("DUMMY"); #ifdef BSD ((struct sockaddr_in *)(*res)->ai_addr)->sin_len = (*res)->ai_addrlen; #endif ((struct sockaddr_in *)(*res)->ai_addr)->sin_family = (*res)->ai_family; ((struct sockaddr_in *)(*res)->ai_addr)->sin_port = ((struct sockaddr_in *)returned->ai_addr)->sin_port; ((struct sockaddr_in *)(*res)->ai_addr)->sin_addr.s_addr = htonl((current_address++ & 0xffffff) | 0x0a000000); if ((new = malloc(sizeof(struct list_node))) == NULL) { free((*res)->ai_addr); free(*res); *res = NULL; return EAI_MEMORY; } new->next = the_list; new->ai = returned; new->phony_address = ((struct sockaddr_in *)(*res)->ai_addr)->sin_addr.s_addr; the_list = new; return 0; } struct hostent * gethostbyname(const char *name) { static struct hostent ret; static in_addr_t phony_address; static in_addr_t *address_list[2]; static char *alias_list[1]; struct addrinfo *out, hints; if (active == NEED_INIT) initlib(); if (active == INACTIVE) return R_gethostbyname(name); bzero(&hints, sizeof(hints)); hints.ai_family = AF_INET; if (getaddrinfo(name, NULL, &hints, &out)) { h_errno = NO_DATA; return NULL; } phony_address = ((struct sockaddr_in *)out->ai_addr)->sin_addr.s_addr; address_list[0] = &phony_address; address_list[1] = NULL; freeaddrinfo(out); alias_list[0] = NULL; ret.h_name = "DUMMY"; ret.h_aliases = alias_list; ret.h_addrtype = AF_INET; ret.h_length = sizeof(in_addr_t); ret.h_addr_list = (char **)address_list; return &ret; }