diff --git a/netcon/common.c b/netcon/Common.c similarity index 99% rename from netcon/common.c rename to netcon/Common.c index 2e683625a..beb530c62 100755 --- a/netcon/common.c +++ b/netcon/Common.c @@ -37,7 +37,7 @@ #include #include -#include "common.h" +#include "Common.h" void dwr(const char *fmt, ...); diff --git a/netcon/common.h b/netcon/Common.h similarity index 100% rename from netcon/common.h rename to netcon/Common.h diff --git a/netcon/Intercept.c b/netcon/Intercept.c new file mode 100755 index 000000000..97af0c94a --- /dev/null +++ b/netcon/Intercept.c @@ -0,0 +1,936 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + + +#ifdef USE_GNU_SOURCE +#define _GNU_SOURCE +#endif + +/* Name used in err msgs */ +char *progname = ""; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* For NPs */ +#include +#include +#include + +/* for mmap */ +#include + +#ifdef USE_SOCKS_DNS + #include +#endif + +#include "Intercept.h" +#include "Common.h" + +#ifdef CHECKS + //#include + #include + #include /* for NPROTO */ + + #define SOCK_MAX (SOCK_PACKET + 1) + #define SOCK_TYPE_MASK 0xf +#endif + +/* Global Declarations */ +#ifdef USE_SOCKS_DNS +static int (*realresinit)(void); +#endif +static int (*realconnect)(CONNECT_SIG); +static int (*realselect)(SELECT_SIG); +static int (*realpoll)(POLL_SIG); +static int (*realbind)(BIND_SIG); +static int (*realaccept)(ACCEPT_SIG); +static int (*reallisten)(LISTEN_SIG); +static int (*realsocket)(SOCKET_SIG); +static int (*realsetsockopt)(SETSOCKOPT_SIG); +static int (*realgetsockopt)(GETSOCKOPT_SIG); +static int (*realaccept4)(ACCEPT4_SIG); + +/* Exported Function Prototypes */ +void my_init(void); +int connect(CONNECT_SIG); +int select(SELECT_SIG); +int poll(POLL_SIG); +int close(CLOSE_SIG); +int bind(BIND_SIG); +int accept(ACCEPT_SIG); +int listen(LISTEN_SIG); +int socket(SOCKET_SIG); +int setsockopt(SETSOCKOPT_SIG); +int getsockopt(GETSOCKOPT_SIG); +int accept4(ACCEPT4_SIG); + +#ifdef USE_SOCKS_DNS +int res_init(void); +#endif + +int connect_to_service(void); +int init_service_connection(); +void dwr(const char *fmt, ...); +void load_symbols(void); +void set_up_intercept(); +int checkpid(); + + +#define BUF_SZ 1024 +#define SERVICE_CONNECT_ATTEMPTS 30 + +#define ERR_OK 0 + +ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); + +/* threading */ +pthread_mutex_t lock; +pthread_mutex_t loglock; + + +/*------------------------------------------------------------------------------ +------------------- Intercept<--->Service Comm mechanisms----------------------- +------------------------------------------------------------------------------*/ + +// TODO: Find minimum BUF_SZ for RPC +// TODO: Refactor RPC send logic + +static int is_initialized = 0; +static int fdret_sock; // used for fd-transfers +static int newfd; // used for "this_end" socket +static int thispid; +static char* af_sock_name = "/tmp/.ztnc_e5cd7a9e1c5311ab"; + +/* + * Check for forking + */ +int checkpid() { + if(thispid != getpid()) { + printf("clone/fork detected. re-initializing this instance.\n"); + set_up_intercept(); + fdret_sock = init_service_connection(); + thispid = getpid(); + } + return 0; +} + +/* + * Sends an RPC command to the service + */ +void send_command(int rpc_fd, char *cmd) +{ + int n_write = write(rpc_fd, cmd, BUF_SZ); + if(n_write < 0){ + dwr("Error writing command to service (CMD = %d)\n", cmd[0]); + errno = 0; + //return -1; + } + +} +/* + * Reads a return value from the service and sets errno (if applicable) + */ +int get_retval() +{ + if(fdret_sock >= 0) { + int retval; + int sz = sizeof(char) + sizeof(retval) + sizeof(errno); + char retbuf[BUF_SZ]; + memset(&retbuf, '\0', sz); + int n_read = read(fdret_sock, &retbuf, sz); + if(n_read > 0) { + memcpy(&retval, &retbuf[1], sizeof(retval)); + memcpy(&errno, &retbuf[1+sizeof(retval)], sizeof(errno)); + return retval; + } + } + dwr("unable to read connect: return value\n"); + return -1; +} + + +/*------------------------------------------------------------------------------ +---------- Unix-domain socket lazy initializer (for fd-transfers)-------------- +------------------------------------------------------------------------------*/ + +/* Sets up the connection pipes and sockets to the service */ +int init_service_connection() +{ + if(!is_initialized) + { + struct sockaddr_un addr; + int tfd = -1; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, af_sock_name, sizeof(addr.sun_path)-1); + + int attempts = 0; + int conn_err = -1; + + if ( (tfd = realsocket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket error"); + exit(-1); + } + + while(conn_err < 0 && attempts < SERVICE_CONNECT_ATTEMPTS) + { + dwr("trying connection (%d): %s\n", tfd, af_sock_name); + conn_err = realconnect(tfd, (struct sockaddr*)&addr, sizeof(addr)); + + if(conn_err < 0) { + dwr("re-attempting connection in %ds\n", 1+attempts); + sleep(1); + } + else { + dwr("AF_UNIX connection established: %d\n", tfd); + is_initialized = 1; + return tfd; + } + attempts++; + } + } + return -1; +} + +/*------------------------------------------------------------------------------ +------------------------ ctors and dtors (and friends)------------------------- +------------------------------------------------------------------------------*/ + +void my_dest(void) __attribute__ ((destructor)); +void my_dest(void) { + + dwr("closing connections to service...\n"); + close(fdret_sock); + pthread_mutex_destroy(&lock); +} + + +void load_symbols(void) +{ +#ifdef USE_OLD_DLSYM + void *lib; +#endif + /* possibly add check to beginning of each method to avoid needing to cll the constructor */ + if(thispid == getpid()) { + dwr("detected duplicate call to global ctor (pid=%d).\n", thispid); + } + dwr(" -- pid = %d\n", getpid()); + dwr(" -- uid = %d\n", getuid()); + thispid = getpid(); + +#ifndef USE_OLD_DLSYM + realconnect = dlsym(RTLD_NEXT, "connect"); + realbind = dlsym(RTLD_NEXT, "bind"); + realaccept = dlsym(RTLD_NEXT, "accept"); + reallisten = dlsym(RTLD_NEXT, "listen"); + realsocket = dlsym(RTLD_NEXT, "socket"); + realbind = dlsym(RTLD_NEXT, "bind"); + realpoll = dlsym(RTLD_NEXT, "poll"); + realselect = dlsym(RTLD_NEXT, "select"); + realsetsockopt = dlsym(RTLD_NEXT, "setsockopt"); + realgetsockopt = dlsym(RTLD_NEXT, "getsockopt"); + realaccept4 = dlsym(RTLD_NEXT, "accept4"); + + #ifdef USE_SOCKS_DNS + realresinit = dlsym(RTLD_NEXT, "res_init"); + #endif + +#else + lib = dlopen(LIBCONNECT, RTLD_LAZY); + realconnect = dlsym(lib, "connect"); + realbind = dlsym(lib, "bind"); + realaccept = dlsym(lib, "accept"); + reallisten = dlsym(lib, "listen"); + realsocket = dlsym(lib, "socket"); + realpoll = dlsym(lib, "poll"); + realselect = dlsym(lib, "select"); + realsetsockopt = dlsym(lib, "setsockopt"); + realgetsockopt = dlsym(lib, "getsockopt"); + realaccept4 = dlsym(lib), "accept4"); + + #ifdef USE_SOCKS_DNS + realresinit = dlsym(lib, "res_init"); + #endif + dlclose(lib); + + lib = dlopen(LIBC, RTLD_LAZY); + dlclose(lib); +#endif +} + +/* Private Function Prototypes */ +void _init(void) __attribute__ ((constructor)); +void _init(void) { + set_up_intercept(); +} + +/* get symbols and initialize mutexes */ +void set_up_intercept() +{ + load_symbols(); + if(pthread_mutex_init(&lock, NULL) != 0) { + printf("error while initializing service call mutex\n"); + } + if(pthread_mutex_init(&loglock, NULL) != 0) { + printf("error while initializing log mutex mutex\n"); + } +} + + +/*------------------------------------------------------------------------------ +------------------------- ioctl(), fcntl(), setsockopt()------------------------ +------------------------------------------------------------------------------*/ + +char *cmd_to_str(int cmd) +{ + switch(cmd) + { + case F_DUPFD: + return "F_DUPFD"; + case F_GETFD: + return "F_GETFD"; + case F_SETFD: + return "F_SETFD"; + case F_GETFL: + return "F_GETFL"; + case F_SETFL: + return "F_SETFL"; + case F_GETLK: + return "F_GETLK"; + case F_SETLK: + return "F_SETLK"; + case F_SETLKW: + return "F_SETLKW"; + default: + return "?"; + } + return "?"; +} + +void arg_to_str(int arg) +{ + if(arg & O_RDONLY) dwr("O_RDONLY "); + if(arg & O_WRONLY) dwr("O_WRONLY "); + if(arg & O_RDWR) dwr("O_RDWR "); + if(arg & O_CREAT) dwr("O_CREAT "); + if(arg & O_EXCL) dwr("O_EXCL "); + if(arg & O_NOCTTY) dwr("O_NOCTTY "); + if(arg & O_TRUNC) dwr("O_TRUNC "); + if(arg & O_APPEND) dwr("O_APPEND "); + if(arg & O_ASYNC) dwr("O_ASYNC "); + if(arg & O_DIRECT) dwr("O_DIRECT "); + if(arg & O_NOATIME) dwr("O_NOATIME "); + if(arg & O_NONBLOCK) dwr("O_NONBLOCK "); + if(arg & O_DSYNC) dwr("O_DSYNC "); + if(arg & O_SYNC) dwr("O_SYNC "); +} + +char* level_to_str(int level) +{ + switch(level) + { + case SOL_SOCKET: + return "SOL_SOCKET"; + case IPPROTO_TCP: + return "IPPROTO_TCP"; + default: + return "?"; + } + return "?"; +} + +char* option_name_to_str(int opt) +{ + if(opt == SO_DEBUG) return "SO_DEBUG"; + if(opt == SO_BROADCAST) return "SO_BROADCAST"; + if(opt == SO_BINDTODEVICE) return "SO_BINDTODEVICE"; + if(opt == SO_REUSEADDR) return "SO_REUSEADDR"; + if(opt == SO_KEEPALIVE) return "SO_KEEPALIVE"; + if(opt == SO_LINGER) return "SO_LINGER"; + if(opt == SO_OOBINLINE) return "SO_OOBINLINE"; + if(opt == SO_SNDBUF) return "SO_SNDBUF"; + if(opt == SO_RCVBUF) return "SO_RCVBUF"; + if(opt == SO_DONTROUTE) return "SO_DONTROUTEO_ASYNC"; + if(opt == SO_RCVLOWAT) return "SO_RCVLOWAT"; + if(opt == SO_RCVTIMEO) return "SO_RCVTIMEO"; + if(opt == SO_SNDLOWAT) return "SO_SNDLOWAT"; + if(opt == SO_SNDTIMEO)return "SO_SNDTIMEO"; + return "?"; +} + +/*------------------------------------------------------------------------------ +--------------------------------- setsockopt() --------------------------------- +------------------------------------------------------------------------------*/ +/* int socket, int level, int option_name, const void *option_value, socklen_t option_len */ +int setsockopt(SETSOCKOPT_SIG) +{ +#ifdef DUMMY + dwr("setsockopt(%d)\n", socket); + return realsetsockopt(socket, level, option_name, option_value, option_len); + +#else + /* make sure we don't touch any standard outputs */ + if(socket == STDIN_FILENO || socket == STDOUT_FILENO || socket == STDERR_FILENO) + return(realsetsockopt(socket, level, option_name, option_value, option_len)); + int err = realsetsockopt(socket, level, option_name, option_value, option_len); + if(err < 0){ + //perror("setsockopt():\n"); + } + return 0; +#endif +} + + +/*------------------------------------------------------------------------------ +--------------------------------- getsockopt() --------------------------------- +------------------------------------------------------------------------------*/ +/* int sockfd, int level, int optname, void *optval, socklen_t *optlen */ + +int getsockopt(GETSOCKOPT_SIG) +{ +#ifdef DUMMY + dwr("getsockopt(%d)\n", sockfd); + return realgetsockopt(sockfd, level, optname, optval, optlen); + +#else + // make sure we don't touch any standard outputs + int err = realgetsockopt(sockfd, level, optname, optval, optlen); + + // FIXME: this condition will need a little more intelligence later on + // -- we will need to know if this fd is a local we are spoofing, or a true local + if(optname == SO_TYPE) + { + int* val = (int*)optval; + *val = 2; + optval = (void*)val; + } + if(err < 0){ + //perror("setsockopt():\n"); + } + return 0; +#endif +} + + +/*------------------------------------------------------------------------------ +---------------------------------- shutdown() ---------------------------------- +------------------------------------------------------------------------------*/ + +void shutdown_arg_to_str(int arg) +{ + if(arg & O_RDONLY) dwr("O_RDONLY "); + if(arg & O_WRONLY) dwr("O_WRONLY "); + if(arg & O_RDWR) dwr("O_RDWR "); + if(arg & O_CREAT) dwr("O_CREAT "); + if(arg & O_EXCL) dwr("O_EXCL "); + if(arg & O_NOCTTY) dwr("O_NOCTTY "); + if(arg & O_TRUNC) dwr("O_TRUNC "); + if(arg & O_APPEND) dwr("O_APPEND "); + if(arg & O_ASYNC) dwr("O_ASYNC "); + if(arg & O_DIRECT) dwr("O_DIRECT "); + if(arg & O_NOATIME) dwr("O_NOATIME "); + if(arg & O_NONBLOCK) dwr("O_NONBLOCK "); + if(arg & O_DSYNC) dwr("O_DSYNC "); + if(arg & O_SYNC) dwr("O_SYNC "); +} + +/*------------------------------------------------------------------------------ +----------------------------------- socket() ----------------------------------- +------------------------------------------------------------------------------*/ + +void sock_type_to_str(int arg) +{ + if(arg == SOCK_STREAM) printf("SOCK_STREAM "); + if(arg == SOCK_DGRAM) printf("SOCK_DGRAM "); + if(arg == SOCK_SEQPACKET) printf("SOCK_SEQPACKET "); + if(arg == SOCK_RAW) printf("SOCK_RAW "); + if(arg == SOCK_RDM) printf("SOCK_RDM "); + if(arg == SOCK_PACKET) printf("SOCK_PACKET "); + if(arg & SOCK_NONBLOCK) printf("| SOCK_NONBLOCK "); + if(arg & SOCK_CLOEXEC) printf("| SOCK_CLOEXEC "); +} + +void sock_domain_to_str(int domain) +{ + if(domain == AF_UNIX) printf("AF_UNIX "); + if(domain == AF_LOCAL) printf("AF_LOCAL "); + if(domain == AF_INET) printf("AF_INET "); + if(domain == AF_INET6) printf("AF_INET6 "); + if(domain == AF_IPX) printf("AF_IPX "); + if(domain == AF_NETLINK) printf("AF_NETLINK "); + if(domain == AF_X25) printf("AF_X25 "); + if(domain == AF_AX25) printf("AF_AX25 "); + if(domain == AF_ATMPVC) printf("AF_ATMPVC "); + if(domain == AF_APPLETALK) printf("AF_APPLETALK "); + if(domain == AF_PACKET) printf("AF_PACKET "); +} + +/* int socket_family, int socket_type, int protocol + socket() intercept function */ + +int socket(SOCKET_SIG) +{ + int err; +#ifdef CHECKS + /* Check that type makes sense */ + int flags = socket_type & ~SOCK_TYPE_MASK; + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) + return -EINVAL; + socket_type &= SOCK_TYPE_MASK; + /* Check protocol is in range */ + if (socket_family < 0 || socket_family >= NPROTO) + return -EAFNOSUPPORT; + if (socket_type < 0 || socket_type >= SOCK_MAX) + return -EINVAL; + /* Check that we haven't hit the soft-limit file descriptors allowed */ + /* FIXME: Find number of open fds + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + if(sockfd >= rl.rlim_cur){ + errno = EMFILE; + return -1; + } + */ + /* FIXME: detect ENFILE condition */ +#endif + +#ifdef DUMMY + dwr("socket(fam=%d, type=%d, prot=%d)\n", socket_family, socket_type, protocol); + return realsocket(socket_family, socket_type, protocol); + +#else + char cmd[BUF_SZ]; + fdret_sock = !is_initialized ? init_service_connection() : fdret_sock; + + if(socket_family == AF_LOCAL + || socket_family == AF_NETLINK + || socket_family == AF_UNIX) { + return realsocket(socket_family, socket_type, protocol); + } + + /* Assemble and route command */ + struct socket_st rpc_st; + rpc_st.socket_family = socket_family; + rpc_st.socket_type = socket_type; + rpc_st.protocol = protocol; + rpc_st.__tid = syscall(SYS_gettid); + + memset(cmd, '\0', BUF_SZ); + cmd[0] = RPC_SOCKET; + memcpy(&cmd[1], &rpc_st, sizeof(struct socket_st)); + pthread_mutex_lock(&lock); + write(fdret_sock,cmd, BUF_SZ); + + /* get new fd */ + char gmybuf[16]; + ssize_t size = sock_fd_read(fdret_sock, gmybuf, sizeof(gmybuf), &newfd); + if(size > 0) + { + /* send our local-fd number back to service so + it can complete its mapping table entry */ + memset(cmd, '\0', BUF_SZ); + cmd[0] = RPC_FD_MAP_COMPLETION; + memcpy(&cmd[1], &newfd, sizeof(newfd)); + if(newfd > -1) { + send_command(fdret_sock, cmd); + pthread_mutex_unlock(&lock); + errno = ERR_OK; // OK + return newfd; + } + else { // Try to read retval+errno since we RXed a bad fd + dwr("Error, service sent bad fd.\n"); + err = get_retval(); + pthread_mutex_unlock(&lock); + return err; + } + } + else { + dwr("Error while receiving new FD.\n"); + err = get_retval(); + pthread_mutex_unlock(&lock); + return err; + } +#endif +} + +/*------------------------------------------------------------------------------ +---------------------------------- connect() ----------------------------------- +------------------------------------------------------------------------------*/ + +/* int __fd, const struct sockaddr * __addr, socklen_t __len + connect() intercept function */ +int connect(CONNECT_SIG) +{ + struct sockaddr_in *connaddr; + connaddr = (struct sockaddr_in *) __addr; + +#ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(__fd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(__fd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } + /* Check family */ + if (connaddr->sin_family < 0 || connaddr->sin_family >= NPROTO){ + errno = EAFNOSUPPORT; + return -1; + } + /* FIXME: Check that address is in user space, return EFAULT ? */ +#endif + +#ifdef DUMMY + dwr("connect(%d)\n", __fd); + return realconnect(__fd, __addr, __len); + +#else + /* make sure we don't touch any standard outputs */ + if(__fd == STDIN_FILENO || __fd == STDOUT_FILENO || __fd == STDERR_FILENO){ + if (realconnect == NULL) { + dwr("Unresolved symbol: connect(). Library is exiting.\n"); + exit(-1); + } + return(realconnect(__fd, __addr, __len)); + } + + if(__addr != NULL && (connaddr->sin_family == AF_LOCAL + || connaddr->sin_family == PF_NETLINK + || connaddr->sin_family == AF_NETLINK + || connaddr->sin_family == AF_UNIX)) { + int err = realconnect(__fd, __addr, __len); + return err; + } + + /* assemble and route command */ + int err; + char cmd[BUF_SZ]; + memset(cmd, '\0', BUF_SZ); + struct connect_st rpc_st; + rpc_st.__tid = syscall(SYS_gettid); + rpc_st.__fd = __fd; + memcpy(&rpc_st.__addr, __addr, sizeof(struct sockaddr)); + memcpy(&rpc_st.__len, &__len, sizeof(socklen_t)); + cmd[0] = RPC_CONNECT; + memcpy(&cmd[1], &rpc_st, sizeof(struct connect_st)); + pthread_mutex_lock(&lock); + send_command(fdret_sock, cmd); + err = get_retval(); + pthread_mutex_unlock(&lock); + return err; +#endif +} + +/*------------------------------------------------------------------------------ +---------------------------------- select() ------------------------------------ +------------------------------------------------------------------------------*/ + +/* int n, fd_set *readfds, fd_set *writefds, +fd_set *exceptfds, struct timeval *timeout */ +int select(SELECT_SIG) +{ +#ifdef DUMMY + dwr("select(n=%d, , , , )\n", n); + return realselect(n, readfds, writefds, exceptfds, timeout); + +#else + return realselect(n, readfds, writefds, exceptfds, timeout); +#endif +} + +/*------------------------------------------------------------------------------ +----------------------------------- poll() ------------------------------------- +------------------------------------------------------------------------------*/ + +/* struct pollfd *__fds, nfds_t __nfds, int __timeout */ +int poll(POLL_SIG) +{ +#ifdef DUMMY + dwr("poll(, nfds=%d, timeout=%d)\n", __fds, __timeout); + return realpoll(__fds, __nfds, __timeout); + +#else + return realpoll(__fds, __nfds, __timeout); +#endif +} + +/*------------------------------------------------------------------------------ +------------------------------------ bind() ------------------------------------ +------------------------------------------------------------------------------*/ + +/* int sockfd, const struct sockaddr *addr, socklen_t addrlen + bind() intercept function */ +int bind(BIND_SIG) +{ +#ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(sockfd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type = -1; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } +#endif + + int err; +#ifdef DUMMY + dwr("bind(%d)\n", sockfd); + return realbind(sockfd, addr, addrlen); +#else + /* make sure we don't touch any standard outputs */ + if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO) + return(realbind(sockfd, addr, addrlen)); + + /* If local, just use normal syscall */ + struct sockaddr_in *connaddr; + connaddr = (struct sockaddr_in *) addr; + + if (addr != NULL && (connaddr->sin_family == AF_LOCAL + || connaddr->sin_family == PF_NETLINK + || connaddr->sin_family == AF_NETLINK + || connaddr->sin_family == AF_UNIX)) + { + if(realbind == NULL) { + dwr("Unresolved symbol: bind(). Library is exiting.\n"); + exit(-1); + } + return(realbind(sockfd, addr, addrlen)); + } + /* Assemble and route command */ + char cmd[BUF_SZ]; + struct bind_st rpc_st; + rpc_st.sockfd = sockfd; + rpc_st.__tid = syscall(SYS_gettid); + memcpy(&rpc_st.addr, addr, sizeof(struct sockaddr)); + memcpy(&rpc_st.addrlen, &addrlen, sizeof(socklen_t)); + cmd[0]=RPC_BIND; + memcpy(&cmd[1], &rpc_st, sizeof(struct bind_st)); + pthread_mutex_lock(&lock); + send_command(fdret_sock, cmd); + err = get_retval(); + pthread_mutex_unlock(&lock); + errno = ERR_OK; + return err; +#endif +} + + +/*------------------------------------------------------------------------------ +----------------------------------- accept4() ---------------------------------- +------------------------------------------------------------------------------*/ + + +/* int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags */ +int accept4(ACCEPT4_SIG) +{ +#ifdef CHECKS + if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) + return -EINVAL; +#endif +#ifdef DUMMY + dwr("accept4(%d)\n", sockfd); + return accept(sockfd, addr, addrlen); +#else + return accept(sockfd, addr, addrlen); +#endif +} + + +/*------------------------------------------------------------------------------ +----------------------------------- accept() ----------------------------------- +------------------------------------------------------------------------------*/ + +/* int sockfd struct sockaddr *addr, socklen_t *addrlen + accept() intercept function */ +int accept(ACCEPT_SIG) +{ +#ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(sockfd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } + /* Check that this socket supports accept() */ + if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { + errno = EOPNOTSUPP; + return -1; + } + /* Check that we haven't hit the soft-limit file descriptors allowed */ + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + if(sockfd >= rl.rlim_cur){ + errno = EMFILE; + return -1; + } +#endif + +#ifdef DUMMY + return realaccept(sockfd, addr, addrlen); +#else + /* make sure we don't touch any standard outputs */ + if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO) + return(realaccept(sockfd, addr, addrlen)); + + addr->sa_family = AF_INET; + /* TODO: also get address info */ + + char cmd[BUF_SZ]; + if(realaccept == NULL) { + dwr( "Unresolved symbol: accept()\n"); + return -1; + } + + char gmybuf[16], c[1]; + int new_conn_socket, n = read(sockfd, c, sizeof(c)); + if(n > 0) + { + ssize_t size = sock_fd_read(fdret_sock, gmybuf, sizeof(gmybuf), &new_conn_socket); + if(size > 0) { + /* Send our local-fd number back to service so it can complete its mapping table */ + memset(cmd, '\0', BUF_SZ); + cmd[0] = RPC_FD_MAP_COMPLETION; + memcpy(&cmd[1], &new_conn_socket, sizeof(new_conn_socket)); + pthread_mutex_lock(&lock); + int n_write = write(fdret_sock, cmd, BUF_SZ); + if(n_write < 0) { + dwr("Error sending perceived FD to service.\n"); + errno = ECONNABORTED; + return -1; + } + pthread_mutex_unlock(&lock); + errno = ERR_OK; + return new_conn_socket; // OK + } + else { + dwr("Error receiving new FD from service.\n"); + errno = ECONNABORTED; + return -1; + } + } + dwr("Error reading signal byte from service.\n"); + //errno = EWOULDBLOCK; + errno = ECONNABORTED; + return -1; +#endif +} + + +/*------------------------------------------------------------------------------ +------------------------------------- listen()---------------------------------- +------------------------------------------------------------------------------*/ + +/* int sockfd, int backlog + listen() intercept function */ +int listen(LISTEN_SIG) +{ + #ifdef CHECKS + /* Check that this is a valid fd */ + if(fcntl(sockfd, F_GETFD) < 0) { + return -1; + errno = EBADF; + } + /* Check that it is a socket */ + int sock_type; + socklen_t sock_type_len = sizeof(sock_type); + if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + return -1; + } + /* Check that this socket supports accept() */ + if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { + errno = EOPNOTSUPP; + return -1; + } + #endif + + int err; + +#ifdef DUMMY + dwr("listen(%d)\n", sockfd); + return reallisten(sockfd, backlog); +#else + /* make sure we don't touch any standard outputs */ + if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO) + return(reallisten(sockfd, backlog)); + + char cmd[BUF_SZ]; + dwr("listen(%d)\n", sockfd); + /* Assemble and route command */ + memset(cmd, '\0', BUF_SZ); + struct listen_st rpc_st; + rpc_st.sockfd = sockfd; + rpc_st.backlog = backlog; + rpc_st.__tid = syscall(SYS_gettid); + cmd[0] = RPC_LISTEN; + memcpy(&cmd[1], &rpc_st, sizeof(struct listen_st)); + pthread_mutex_lock(&lock); + send_command(fdret_sock, cmd); + err = get_retval(); + pthread_mutex_unlock(&lock); + errno = ERR_OK; + return err; +#endif +} diff --git a/netcon/sendfd.c b/netcon/Sendfd.c similarity index 100% rename from netcon/sendfd.c rename to netcon/Sendfd.c diff --git a/netcon/sendfd.h b/netcon/Sendfd.h similarity index 100% rename from netcon/sendfd.h rename to netcon/Sendfd.h diff --git a/netcon/intercept.h b/netcon/intercept.h deleted file mode 100755 index f190e2161..000000000 --- a/netcon/intercept.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - - -#ifndef _HARNESS_H -#define _HARNESS_H 1 - -#include - - -/* Userland RPC codes */ -#define RPC_UNDEFINED 0 -#define RPC_CONNECT 1 -#define RPC_CONNECT_SOCKARG 2 -#define RPC_SELECT 3 -#define RPC_POLL 4 -#define RPC_CLOSE 5 -#define RPC_READ 6 -#define RPC_WRITE 7 -#define RPC_BIND 8 -#define RPC_ACCEPT 9 -#define RPC_LISTEN 10 -#define RPC_SOCKET 11 -#define RPC_SHUTDOWN 12 - -/* Administration RPC codes */ -#define RPC_FD_MAP_COMPLETION 20 // Give the service the value we "see" for the new buffer fd -#define RPC_RETVAL 21 // not RPC per se, but something we should codify -#define RPC_KILL_INTERCEPT 22 // Tells the service we need to shut down all connections - -/* Connection statuses */ -#define UNSTARTED 0 -#define CONNECTING 1 -#define CONNECTED 2 -#define SENDING 3 -#define RECEIVING 4 -#define SENTV4REQ 5 -#define GOTV4REQ 6 -#define SENTV5METHOD 7 -#define GOTV5METHOD 8 -#define SENTV5AUTH 9 -#define GOTV5AUTH 10 -#define SENTV5CONNECT 11 -#define GOTV5CONNECT 12 -#define DONE 13 -#define FAILED 14 - -/* Flags to indicate what events a - socket was select()ed for */ -#define READ (POLLIN|POLLRDNORM) -#define WRITE (POLLOUT|POLLWRNORM|POLLWRBAND) -#define EXCEPT (POLLRDBAND|POLLPRI) -#define READWRITE (READ|WRITE) -#define READWRITEEXCEPT (READ|WRITE|EXCEPT) - - -/* for AF_UNIX sockets */ -#define MAX_PATH_NAME_SIZE 64 - -// bind -#define BIND_SIG int sockfd, const struct sockaddr *addr, socklen_t addrlen -struct bind_st -{ - int sockfd; - struct sockaddr addr; - socklen_t addrlen; - int __tid; -}; - - -// connect -#define CONNECT_SIG int __fd, const struct sockaddr * __addr, socklen_t __len -struct connect_st -{ - int __fd; - struct sockaddr __addr; - socklen_t __len; - int __tid; -}; - - -// close -#define CLOSE_SIG int fd -struct close_st -{ - int fd; -}; - - -// read -#define DEFAULT_READ_BUFFER_SIZE 1024 * 63 -// read buffer sizes (on test machine) min: 4096 default: 87380 max:6147872 -#define READ_SIG int __fd, void *__buf, size_t __nbytes -struct read_st -{ - int fd; - size_t count; - unsigned char buf[DEFAULT_READ_BUFFER_SIZE]; -}; - - - -#define DEFAULT_WRITE_BUFFER_SIZE 1024 * 63 -// write buffer sizes (on test machine) min: 4096 default: 16384 max:4194304 -#define WRITE_SIG int __fd, const void *__buf, size_t __n -struct write_st -{ - int fd; - size_t count; - char buf[DEFAULT_WRITE_BUFFER_SIZE]; -}; - - -#define LISTEN_SIG int sockfd, int backlog -struct listen_st -{ - int sockfd; - int backlog; - int __tid; -}; - - - -#define SOCKET_SIG int socket_family, int socket_type, int protocol -struct socket_st -{ - int socket_family; - int socket_type; - int protocol; - int __tid; -}; - - -#define ACCEPT4_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags - - -#define ACCEPT_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen -struct accept_st -{ - int sockfd; - struct sockaddr addr; - socklen_t addrlen; - int __tid; -}; - - -#define SHUTDOWN_SIG int socket, int how -struct shutdown_st -{ - int socket; - int how; -}; - -#define CONNECT_SOCKARG struct sockaddr * -#define SELECT_SIG int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout -#define POLL_SIG struct pollfd *__fds, nfds_t __nfds, int __timeout -#define IOCTL_SIG int __fd, unsigned long int __request, ... -#define FCNTL_SIG int __fd, int __cmd, ... -#define CLONE_SIG int (*fn) (void *arg), void *child_stack, int flags, void *arg -#define DAEMON_SIG int nochdir, int noclose - - -#define SETSOCKOPT_SIG int socket, int level, int option_name, const void *option_value, socklen_t option_len -#define GETSOCKOPT_SIG int sockfd, int level, int optname, void *optval, socklen_t *optlen - - -/* LWIP error beautification */ -const char *lwiperror(int n) -{ - switch(n) - { - case 0: - return "ERR_OK"; - case -1: - return "ERR_MEM (out of memory)"; - case -2: - return "ERR_BUF (buffer error)"; - case -3: - return "ERR_TIMEOUT (timeout)"; - case -4: - return "ERR_RTE (routing problem)"; - case -5: - return "ERR_INPROGRESS (operation in progress)"; - case -6: - return "ERR_VAL (illegal value)"; - case -7: - return "ERR_WOULDBLOCK (operation would block)"; - case -8: - return "ERR_USE (address in use)"; - case -9: - return "ERR_ISCONN (already connected)"; - case -10: - return "Fatal: ERR_ABRT (connection aborted)"; - case -11: - return "Fatal: ERR_RST (connection reset)"; - case -12: - return "Fatal: ERR_CLSD (connection closed)"; - case -13: - return "Fatal: ERR_CONN (not connected)"; - case -14: - return "Fatal: ERR_ARG (illegal argument)"; - case -15: - return "Fatal: ERR_IF (low level netif error)"; - default: - return "UNKNOWN_RET_VAL"; - } -} - -#endif diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index dc8ca9dc2..192e9b977 100755 Binary files a/netcon/libintercept.so.1.0 and b/netcon/libintercept.so.1.0 differ diff --git a/netcon/make-intercept.mk b/netcon/make-intercept.mk index e70b0e6cc..77d26de7a 100644 --- a/netcon/make-intercept.mk +++ b/netcon/make-intercept.mk @@ -32,17 +32,17 @@ LIB_NAME = intercept SHLIB_EXT=dylib SHLIB_MAJOR = 1 SHLIB_MINOR = 8 -COMMON = common -OBJS= intercept.o +COMMON = Common +OBJS= Intercept.o SHLIB = ${LIB_NAME}.${SHLIB_EXT}.${SHLIB_MAJOR}.${SHLIB_MINOR} SHLDFLAGS = -g -O2 -Wall -I. -nostdlib -shared LIBS = -ldl -lc -lrt -lpthread lib: - ${SHCC} $(intercept_CFLAGS) -I. intercept.c -o intercept.o - ${SHCC} $(intercept_CFLAGS) -I. common.c -o common.o - ${SHCC} $(intercept_CFLAGS) -I. sendfd.c -o sendfd.o - ${SHCC} $(SHLDFLAGS) intercept.o common.o sendfd.o -o libintercept.so.1.0 $(LIBS) + ${SHCC} $(intercept_CFLAGS) -I. Intercept.c -o Intercept.o + ${SHCC} $(intercept_CFLAGS) -I. Common.c -o Common.o + ${SHCC} $(intercept_CFLAGS) -I. Sendfd.c -o Sendfd.o + ${SHCC} $(SHLDFLAGS) Intercept.o Common.o Sendfd.o -o libintercept.so.1.0 $(LIBS) install: cp libintercept.so.1.0 /lib/libintercept.so.1.0