From 721d58b464e5fa612b4f7cb4e91b9dca629e69a9 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 27 Oct 2015 17:12:32 -0700 Subject: [PATCH] Added support for Redis --- netcon/Common.c | 22 ++++++ netcon/Intercept.c | 149 +++++++++++++++++++++++++++++------ netcon/Intercept.h | 4 +- netcon/NetconEthernetTap.cpp | 93 +++++++++++++++------- netcon/NetconUtilities.cpp | 1 - netcon/README.md | 15 +++- netcon/libintercept.so.1.0 | Bin 47448 -> 53904 bytes netcon/make-intercept.mk | 2 +- 8 files changed, 226 insertions(+), 60 deletions(-) diff --git a/netcon/Common.c b/netcon/Common.c index beb530c62..59d6648f9 100755 --- a/netcon/Common.c +++ b/netcon/Common.c @@ -48,6 +48,28 @@ void dwr(const char *fmt, ...); extern pthread_mutex_t loglock; +void print_addr(struct sockaddr *addr) +{ + char *s = NULL; + switch(addr->sa_family) { + case AF_INET: { + struct sockaddr_in *addr_in = (struct sockaddr_in *)addr; + s = malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN); + break; + } + case AF_INET6: { + struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)addr; + s = malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN); + break; + } + default: + break; + } + fprintf(stderr, "IP address: %s\n", s); + free(s); +} #ifdef NETCON_SERVICE diff --git a/netcon/Intercept.c b/netcon/Intercept.c index 2a32be17b..bbb1d4c07 100755 --- a/netcon/Intercept.c +++ b/netcon/Intercept.c @@ -43,6 +43,7 @@ char *progname = ""; #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ char *progname = ""; #include #include #include +#include #include #include @@ -77,6 +79,8 @@ static int (*realsetsockopt)(SETSOCKOPT_SIG); static int (*realgetsockopt)(GETSOCKOPT_SIG); static int (*realaccept4)(ACCEPT4_SIG); static long (*realsyscall)(SYSCALL_SIG); +//static int (*realclone)(CLONE_SIG); +//static int (*realpoll)(POLL_SIG); /* Exported Function Prototypes */ void my_init(void); @@ -91,6 +95,8 @@ int setsockopt(SETSOCKOPT_SIG); int getsockopt(GETSOCKOPT_SIG); int accept4(ACCEPT4_SIG); long syscall(SYSCALL_SIG); +//int clone(CLONE_SIG); +//int poll(POLL_SIG); #ifdef USE_SOCKS_DNS int res_init(void); @@ -111,6 +117,18 @@ ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); pthread_mutex_t lock; pthread_mutex_t loglock; +void handle_error(char *name, char *info, int err) +{ +#ifdef ERRORS_ARE_FATAL + if(err < 0) { + dwr("handle_error(%s)=%d: FATAL: %s\n", name, err, info); + //exit(-1); + } +#endif +#ifdef VERBOSE + dwr("%s()=%d\n", name, err); +#endif +} /*------------------------------------------------------------------------------ ------------------- Intercept<--->Service Comm mechanisms----------------------- @@ -241,6 +259,7 @@ void load_symbols(void) realaccept4 = dlsym(RTLD_NEXT, "accept4"); //realclone = dlsym(RTLD_NEXT, "clone"); realsyscall = dlsym(RTLD_NEXT, "syscall"); + //realsyscall = dlsym(RTLD_NEXT, "poll"); #ifdef USE_SOCKS_DNS realresinit = dlsym(RTLD_NEXT, "res_init"); #endif @@ -258,6 +277,7 @@ void load_symbols(void) realaccept4 = dlsym(lib), "accept4"); //realclone = dlsym(lib, "clone"); realsyscall = dlsym(lib, "syscall"); + //realsyscall = dlsym(lib, "poll"); #ifdef USE_SOCKS_DNS realresinit = dlsym(lib, "res_init"); #endif @@ -292,6 +312,9 @@ void set_up_intercept() /* int socket, int level, int option_name, const void *option_value, socklen_t option_len */ int setsockopt(SETSOCKOPT_SIG) { + dwr("setsockopt(%d)\n", socket); + //return(realsetsockopt(socket, level, option_name, option_value, option_len)); + if(level == IPPROTO_TCP || (level == SOL_SOCKET && option_name == SO_KEEPALIVE)){ return 0; } @@ -313,9 +336,8 @@ int setsockopt(SETSOCKOPT_SIG) int getsockopt(GETSOCKOPT_SIG) { - // make sure we don't touch any standard outputs + dwr("setsockopt(%d)\n", sockfd); 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) @@ -347,16 +369,19 @@ int socket(SOCKET_SIG) int flags = socket_type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { errno = EINVAL; + handle_error("socket", "", -1); return -1; } socket_type &= SOCK_TYPE_MASK; /* Check protocol is in range */ if (socket_family < 0 || socket_family >= NPROTO){ errno = EAFNOSUPPORT; + handle_error("socket", "", -1); return -1; } if (socket_type < 0 || socket_type >= SOCK_MAX) { errno = EINVAL; + handle_error("socket", "", -1); return -1; } /* Check that we haven't hit the soft-limit file descriptors allowed */ @@ -375,13 +400,18 @@ int socket(SOCKET_SIG) fdret_sock = !is_initialized ? init_service_connection() : fdret_sock; if(fdret_sock < 0) { dwr("BAD service connection. exiting.\n"); + handle_error("socket", "", -1); exit(-1); } if(socket_family == AF_LOCAL || socket_family == AF_NETLINK || socket_family == AF_UNIX) { - return realsocket(socket_family, socket_type, protocol); + + int err = realsocket(socket_family, socket_type, protocol); + dwr("realsocket, err = %d\n", err); + handle_error("socket", "", err); + return err; } /* Assemble and send RPC */ @@ -395,6 +425,8 @@ int socket(SOCKET_SIG) cmd[0] = RPC_SOCKET; memcpy(&cmd[1], &rpc_st, sizeof(struct socket_st)); pthread_mutex_lock(&lock); + + dwr("sending RPC...\n"); send_command(fdret_sock, cmd); /* get new fd */ @@ -405,13 +437,14 @@ int socket(SOCKET_SIG) /* 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; + cmd[0] = RPC_MAP; memcpy(&cmd[1], &newfd, sizeof(newfd)); //if(newfd > -1) { send_command(fdret_sock, cmd); pthread_mutex_unlock(&lock); errno = ERR_OK; // OK + handle_error("socket", "", newfd); return newfd; //} /* @@ -427,6 +460,7 @@ int socket(SOCKET_SIG) dwr("Error while receiving new FD.\n"); err = get_retval(); pthread_mutex_unlock(&lock); + handle_error("socket", "", -1); return err; } } @@ -439,26 +473,30 @@ int socket(SOCKET_SIG) connect() intercept function */ int connect(CONNECT_SIG) { - dwr("connect()*:\n"); + dwr("connect(%d):\n", __fd); + print_addr(__addr); 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; + handle_error("connect", "EBADF", -1); + return -1; } /* 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; + handle_error("connect", "ENOTSOCK", -1); return -1; } /* Check family */ if (connaddr->sin_family < 0 || connaddr->sin_family >= NPROTO){ errno = EAFNOSUPPORT; + handle_error("connect", "EAFNOSUPPORT", -1); return -1; } /* FIXME: Check that address is in user space, return EFAULT ? */ @@ -467,7 +505,7 @@ int connect(CONNECT_SIG) /* 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"); + handle_error("connect", "Unresolved symbol [connect]", -1); exit(-1); } return(realconnect(__fd, __addr, __len)); @@ -478,6 +516,8 @@ int connect(CONNECT_SIG) || connaddr->sin_family == AF_NETLINK || connaddr->sin_family == AF_UNIX)) { int err = realconnect(__fd, __addr, __len); + perror("connect():"); + //handle_error("connect", "Cannot connect to local socket", err); return err; } @@ -502,6 +542,7 @@ int connect(CONNECT_SIG) */ err = get_retval(); pthread_mutex_unlock(&lock); + //handle_error("connect", "", err); return err; } @@ -525,18 +566,21 @@ int select(SELECT_SIG) bind() intercept function */ int bind(BIND_SIG) { - dwr("bind()*:\n"); + dwr("bind(%d):\n", sockfd); + print_addr(addr); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { - return -1; errno = EBADF; + handle_error("bind", "EBADF", -1); + return -1; } /* Check that it is a socket */ int opt = -1; socklen_t opt_len; if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) { errno = ENOTSOCK; + handle_error("bind", "ENOTSOCK", -1); return -1; } #endif @@ -555,11 +599,11 @@ int bind(BIND_SIG) || 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)); + if(realbind == NULL) { + handle_error("bind", "Unresolved symbol [bind]", -1); + exit(-1); + } + return(realbind(sockfd, addr, addrlen)); } /* Assemble and send RPC */ char cmd[BUF_SZ]; @@ -575,6 +619,7 @@ int bind(BIND_SIG) err = get_retval(); pthread_mutex_unlock(&lock); errno = ERR_OK; + handle_error("bind", "", err); return err; } @@ -587,7 +632,7 @@ int bind(BIND_SIG) /* int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags */ int accept4(ACCEPT4_SIG) { - dwr("accept4()*:\n"); + dwr("accept4(%d):\n", sockfd); #ifdef CHECKS if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { errno = EINVAL; @@ -601,6 +646,7 @@ int accept4(ACCEPT4_SIG) if(flags & SOCK_NONBLOCK) fcntl(newfd, F_SETFL, O_NONBLOCK); } + handle_error("accept4", "", newfd); return newfd; } @@ -613,23 +659,30 @@ int accept4(ACCEPT4_SIG) accept() intercept function */ int accept(ACCEPT_SIG) { - dwr("accept()*:\n"); + dwr("accept(%d):\n", sockfd); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { return -1; errno = EBADF; + dwr("EBADF\n"); + handle_error("accept", "EBADF", -1); + return -1; } /* Check that it is a socket */ int opt; socklen_t opt_len; if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) { errno = ENOTSOCK; + dwr("ENOTSOCK\n"); + handle_error("accept", "ENOTSOCK", -1); return -1; } /* Check that this socket supports accept() */ if(!(opt && (SOCK_STREAM | SOCK_SEQPACKET))) { errno = EOPNOTSUPP; + dwr("EOPNOTSUPP\n"); + handle_error("accept", "EOPNOTSUPP", -1); return -1; } /* Check that we haven't hit the soft-limit file descriptors allowed */ @@ -637,18 +690,24 @@ int accept(ACCEPT_SIG) getrlimit(RLIMIT_NOFILE, &rl); if(sockfd >= rl.rlim_cur){ errno = EMFILE; + dwr("EMFILE\n"); + handle_error("accept", "EMFILE", -1); return -1; } /* Check address length */ if(addrlen < 0) { errno = EINVAL; + dwr("EINVAL\n"); + handle_error("accept", "EINVAL", -1); return -1; } #endif /* redirect calls for standard I/O descriptors to kernel */ - if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO) + if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO){ + dwr("realaccept():\n"); return(realaccept(sockfd, addr, addrlen)); + } if(addr) addr->sa_family = AF_INET; @@ -656,15 +715,15 @@ int accept(ACCEPT_SIG) char cmd[BUF_SZ]; if(realaccept == NULL) { - dwr( "Unresolved symbol: accept()\n"); + handle_error("accept", "Unresolved symbol [accept]", -1); return -1; } - //if(opt & O_NONBLOCK) //fcntl(sockfd, F_SETFL, O_NONBLOCK); char rbuf[16], c[1]; int new_conn_socket; + int n = read(sockfd, c, sizeof(c)); // Read signal byte if(n > 0) { @@ -672,28 +731,29 @@ int accept(ACCEPT_SIG) 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; + cmd[0] = RPC_MAP; 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; // FIXME: Closest match, service unreachable + handle_error("accept", "ECONNABORTED - Error sending perceived FD to service", -1); return -1; } pthread_mutex_unlock(&lock); errno = ERR_OK; - dwr("accepting for %d\n", new_conn_socket); + dwr("accept()=%d\n", new_conn_socket); + handle_error("accept", "", new_conn_socket); return new_conn_socket; // OK } else { - dwr("Error receiving new FD from service.\n"); errno = ECONNABORTED; // FIXME: Closest match, service unreachable + handle_error("accept", "ECONNABORTED - Error receiving new FD from service", -1); return -1; } } - dwr("Error reading signal byte from service.\n"); errno = EAGAIN; /* necessary? */ + handle_error("accept", "EAGAIN - Error reading signal byte from service", -1); return -EAGAIN; } @@ -706,23 +766,26 @@ int accept(ACCEPT_SIG) listen() intercept function */ int listen(LISTEN_SIG) { - dwr("listen()*:\n"); + dwr("listen(%d):\n", sockfd); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { - return -1; errno = EBADF; + handle_error("listen", "EBADF", -1); + return -1; } /* 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; + handle_error("listen", "ENOTSOCK", -1); return -1; } /* Check that this socket supports accept() */ if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { errno = EOPNOTSUPP; + handle_error("listen", "EOPNOTSUPP", -1); return -1; } #endif @@ -744,15 +807,49 @@ int listen(LISTEN_SIG) send_command(fdret_sock, cmd); //err = get_retval(); pthread_mutex_unlock(&lock); + handle_error("listen", "", ERR_OK); return ERR_OK; } + + + +/*------------------------------------------------------------------------------ +-------------------------------------- clone()---------------------------------- +------------------------------------------------------------------------------*/ + +// int (*fn)(void *), void *child_stack, int flags, void *arg, ... +/* +int clone(CLONE_SIG) +{ + dwr("clone()\n"); + return realclone(fn, child_stack, flags, arg); +} +*/ + + + +/*------------------------------------------------------------------------------ +-------------------------------------- poll()----------------------------------- +------------------------------------------------------------------------------*/ + +// struct pollfd *fds, nfds_t nfds, int timeout +/* +int poll(POLL_SIG) +{ + dwr("poll()\n"); + return realpoll(fds, nfds, timeout); + //return ERESTART_RESTARTBLOCK; +} +*/ + /*------------------------------------------------------------------------------ ------------------------------------ syscall()---------------------------------- ------------------------------------------------------------------------------*/ long syscall(SYSCALL_SIG) { + dwr("syscall():\n"); va_list ap; uintptr_t a,b,c,d,e,f; va_start(ap, number); diff --git a/netcon/Intercept.h b/netcon/Intercept.h index 9b9d5a976..6f1888a81 100755 --- a/netcon/Intercept.h +++ b/netcon/Intercept.h @@ -50,7 +50,7 @@ #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_MAP 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 @@ -173,5 +173,7 @@ struct shutdown_st #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 #define SYSCALL_SIG long number, ... +#define CLONE_SIG int (*fn)(void *), void *child_stack, int flags, void *arg, ... +#define POLL_SIG struct pollfd *fds, nfds_t nfds, int timeout #endif diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index cc72ef919..03274b089 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -188,7 +188,7 @@ void NetconEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType return; } memcpy(q->payload,ðhdr,sizeof(ethhdr)); - memcpy(q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr)); + memcpy((char*)q->payload + sizeof(ethhdr),dataptr,q->len - sizeof(ethhdr)); dataptr += q->len - sizeof(ethhdr); // Remaining pbufs (if any) get rest of data @@ -266,13 +266,13 @@ TcpConnection *NetconEthernetTap::getConnectionByTheirFD(PhySocket *sock, int fd */ void NetconEthernetTap::closeConnection(TcpConnection *conn) { - //lwipstack->_tcp_arg(conn->pcb, NULL); - //lwipstack->_tcp_sent(conn->pcb, NULL); - //lwipstack->_tcp_recv(conn->pcb, NULL); - //lwipstack->_tcp_err(conn->pcb, NULL); - //lwipstack->_tcp_poll(conn->pcb, NULL, 0); - //lwipstack->_tcp_close(conn->pcb); - //close(conn->their_fd); + fprintf(stderr, "closeConnection(%x, %d)\n", conn->pcb, _phy.getDescriptor(conn->dataSock)); + lwipstack->_tcp_arg(conn->pcb, NULL); + lwipstack->_tcp_sent(conn->pcb, NULL); + lwipstack->_tcp_recv(conn->pcb, NULL); + lwipstack->_tcp_err(conn->pcb, NULL); + lwipstack->_tcp_poll(conn->pcb, NULL, 0); + lwipstack->_tcp_close(conn->pcb); if(conn->dataSock) { close(_phy.getDescriptor(conn->dataSock)); _phy.close(conn->dataSock,false); @@ -318,6 +318,7 @@ void NetconEthernetTap::closeAll() void NetconEthernetTap::threadMain() throw() { + //signal(SIGPIPE, SIG_IGN); uint64_t prev_tcp_time = 0; uint64_t prev_status_time = 0; uint64_t prev_etharp_time = 0; @@ -351,7 +352,7 @@ void NetconEthernetTap::threadMain() uint64_t etharp_remaining = ARP_TMR_INTERVAL; uint64_t status_remaining = STATUS_TMR_INTERVAL; - if (since_status >= STATUS_TMR_INTERVAL) { + if (since_status >= STATUS_TMR_INTERVAL && true == false) { prev_status_time = now; if(rpc_sockets.size() || tcp_connections.size()) { /* Here we will periodically check the list of rpc_sockets for those that @@ -383,12 +384,13 @@ void NetconEthernetTap::threadMain() // No TCP connections are associated, this is a candidate for removal unsigned char tmpbuf[BUF_SZ]; if(read(_phy.getDescriptor(rpc_sockets[i]),&tmpbuf,BUF_SZ) < 0) { + fprintf(stderr, "run() ---> RPC close(%d)\n", _phy.getDescriptor(rpc_sockets[i])); closeClient(rpc_sockets[i]); } else { // Handle RPC call, this is rare fprintf(stderr, "run(): RPC read during connection check\n"); - exit(0); + exit(0); // FIXME: This should be addressed - Raise APPLICATION_POLL_FREQ to make it less likely phyOnUnixData(rpc_sockets[i],NULL,&tmpbuf,BUF_SZ); } } @@ -412,6 +414,14 @@ void NetconEthernetTap::threadMain() dlclose(lwipstack->_libref); } +// Unused -- no UDP or TCP from this thread/Phy<> +void NetconEthernetTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {} +void NetconEthernetTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} +void NetconEthernetTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {} +void NetconEthernetTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} +void NetconEthernetTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} +void NetconEthernetTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {} + void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr) { // FIXME: What do? @@ -437,14 +447,6 @@ void NetconEthernetTap::phyOnFileDescriptorActivity(PhySocket *sock,void **uptr, } } -// Unused -- no UDP or TCP from this thread/Phy<> -void NetconEthernetTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {} -void NetconEthernetTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} -void NetconEthernetTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {} -void NetconEthernetTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} -void NetconEthernetTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} -void NetconEthernetTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {} - /* * Add a new PhySocket for the client connection */ @@ -478,17 +480,14 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,uns memcpy(&bind_rpc, &buf[1], sizeof(struct bind_st)); handle_bind(sock, uptr, &bind_rpc); break; - case RPC_KILL_INTERCEPT: - fprintf(stderr, "RPC_KILL_INTERCEPT\n"); - break; case RPC_CONNECT: fprintf(stderr, "RPC_CONNECT\n"); struct connect_st connect_rpc; memcpy(&connect_rpc, &buf[1], sizeof(struct connect_st)); handle_connect(sock, uptr, &connect_rpc); break; - case RPC_FD_MAP_COMPLETION: - fprintf(stderr, "RPC_FD_MAP_COMPLETION\n"); + case RPC_MAP: + fprintf(stderr, "RPC_MAP\n"); handle_retval(sock, uptr, buf); break; default: @@ -596,8 +595,9 @@ err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err) new_tcp_conn->pcb = newpcb; new_tcp_conn->their_fd = fds[1]; tap->tcp_connections.push_back(new_tcp_conn); - + fprintf(stderr, "socketpair = {%d, %d}\n", fds[0], fds[1]); int send_fd = tap->_phy.getDescriptor(conn->rpcSock); + fprintf(stderr, "write(%d,...)\n", larg_fd); int n = write(larg_fd, "z", 1); // accept() in library waits for this byte if(n > 0) { if(sock_fd_write(send_fd, fds[1]) > 0) { @@ -854,11 +854,39 @@ err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned char* buf) { TcpConnection *conn = (TcpConnection*)*uptr; - if(conn->pending) { - memcpy(&(conn->perceived_fd), &buf[1], sizeof(int)); - //fprintf(stderr, "handle_retval(): Mapping [our=%d -> their=%d]\n", - //_phy.getDescriptor(conn->dataSock), conn->perceived_fd); - conn->pending = false; + if(!conn->pending) + return; + + // Copy data from buffer to TcpConnection object, update status + memcpy(&(conn->perceived_fd), &buf[1], sizeof(int)); + conn->pending = false; + + fprintf(stderr, "handle_retval(): CONN:%x - Mapping [our=%d -> their=%d]\n",conn, + _phy.getDescriptor(conn->dataSock), conn->perceived_fd); + + /* Check for pre-existing connection for this socket --- + This block is in response to interesting behaviour from redis-server. A + socket is created, setsockopt is called and the socket is set to IPV6 but fails (for now), + then it is closed and re-opened and consequently remapped. With two pipes mapped + to the same socket, makes it possible that we write to the wrong pipe and fail. So + this block merely searches for a possible duplicate mapping and erases it + */ + for(size_t i=0; irpcSock == conn->rpcSock) { + if(tcp_connections[i]->perceived_fd == conn->perceived_fd) { + int n; + if((n = send(_phy.getDescriptor(tcp_connections[i]->dataSock), "z", 1, MSG_NOSIGNAL)) < 0) { + fprintf(stderr, "handle_retval(): CONN:%x - Socket (%d) already mapped (originally CONN:%x)\n", conn, tcp_connections[i]->perceived_fd, tcp_connections[i]); + closeConnection(tcp_connections[i]); + } + else { + fprintf(stderr, "handle_retval(): CONN:%x - This socket is mapped to two different pipes (?). Exiting.\n", conn); + exit(0); + } + } + } } } @@ -916,6 +944,7 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st d[2] = (ip >> 16) & 0xFF; d[3] = (ip >> 24) & 0xFF; fprintf(stderr, "handle_bind(): error binding to %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], conn_port); + fprintf(stderr, "err = %d\n", err); if(err == ERR_USE) send_return_value(conn, -1, EADDRINUSE); if(err == ERR_MEM) @@ -927,7 +956,7 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st send_return_value(conn, ERR_OK, ERR_OK); // Success } else { - fprintf(stderr, "handle_bind(): PCB not in CLOSED state. Ignoring BIND request.\n"); + fprintf(stderr, "handle_bind(): PCB (%x) not in CLOSED state. Ignoring BIND request.\n", conn->pcb); send_return_value(conn, -1, EINVAL); } } @@ -1037,6 +1066,9 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke { int rpc_fd = _phy.getDescriptor(sock); struct tcp_pcb *newpcb = lwipstack->tcp_new(); + + fprintf(stderr, "handle_socket(): pcb=%x, (state == CLOSED) = %d\n", newpcb, (newpcb->state==CLOSED)); + if(newpcb != NULL) { ZT_PHY_SOCKFD_TYPE fds[2]; if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { @@ -1045,6 +1077,7 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke return; } } + fprintf(stderr, "socketpair = {%d, %d}\n", fds[0], fds[1]); TcpConnection *new_conn = new TcpConnection(); new_conn->dataSock = _phy.wrapSocket(fds[0], new_conn); *uptr = new_conn; diff --git a/netcon/NetconUtilities.cpp b/netcon/NetconUtilities.cpp index fdfef369f..642d77bab 100644 --- a/netcon/NetconUtilities.cpp +++ b/netcon/NetconUtilities.cpp @@ -42,7 +42,6 @@ namespace ZeroTier { // Functions used to pass file descriptors between processes - ssize_t sock_fd_write(int sock, int fd) { ssize_t size; diff --git a/netcon/README.md b/netcon/README.md index 9cc1b1bbd..b0c8ebbe8 100644 --- a/netcon/README.md +++ b/netcon/README.md @@ -15,6 +15,7 @@ vsftpd [BROKEN as of 20151021] Server sends 500 when 220 is expected mysql [BROKEN as of 20151021] postresql [BROKEN as of 20151021] MongoDB [BROKEN as of 20151021] +Redis-server [ WORKS as of 20151027] pure-ftpd [BROKEN as of 20151021] Socket operation on non-socket To Test: @@ -27,8 +28,16 @@ To Test: Multithreaded software (e.g. apache in thread mode) -20151021 Added Node.js support +20151027 Added Redis-server support +Notes: + - Added extra logic to detect socket re-issuing and consequent service-side double mapping. + Redis appears to try to set its initial listen socket to IPV6 only, this currently fails. As + a result, Redis will close the socket and re-open it. The server will now test for closures + during mapping and will eliminate any mappings to broken pipes. + + +20151021 Added Node.js support Notes: - syscall(long number, ...) is now intercepted and re-directs the __NR_accept4 call to our intercepted accept4() function - accept() now returns -EAGAIN in the case that we cannot read a signal byte from the descriptor linked to the service. This @@ -40,3 +49,7 @@ Notes: This might be unnecessary or might need a better workaround - Careful attention should be given to how arguments are passed in the intercepted syscall() function, this differs for 32/64-bit systems + + + + diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index a75d6ac0200e89dfcae6791d03199a080a33eef5..356514297b308d9052d02c2b50701ad6fdc75ddf 100755 GIT binary patch literal 53904 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óOf;yH*N-8OTVMPLMNNpbmHgI`|a{pCPyebxGw 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 literal 47448 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 Nj%?j=CEBO0{udyBx6=Rs diff --git a/netcon/make-intercept.mk b/netcon/make-intercept.mk index a16ff7e7f..fbae717f4 100644 --- a/netcon/make-intercept.mk +++ b/netcon/make-intercept.mk @@ -27,7 +27,7 @@ SHCC=gcc -intercept_CFLAGS = -c -fPIC -g -O2 -Wall -std=c99 -DCHECKS -D_GNU_SOURCE -DNETCON_INTERCEPT +intercept_CFLAGS = -c -fPIC -g -O2 -Wall -std=c99 -DERRORS_ARE_FATAL -DVERBOSE -DCHECKS -D_GNU_SOURCE -DNETCON_INTERCEPT LIB_NAME = intercept SHLIB_EXT=dylib SHLIB_MAJOR = 1