From 96eb0ce49262b5a8a7a99993733d52dbcf135f01 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 29 Oct 2015 17:57:59 -0700 Subject: [PATCH] Added state dump, fixed numerous RPC related bugs --- make-linux.mk | 2 +- netcon/Intercept.c | 7 +- netcon/Intercept.h | 3 +- netcon/NetconEthernetTap.cpp | 282 +++++++++++++++++++++++------------ netcon/NetconEthernetTap.hpp | 8 +- netcon/NetconService.hpp | 3 +- netcon/NetconUtilities.cpp | 40 +++++ netcon/NetconUtilities.hpp | 5 + netcon/libintercept.so.1.0 | Bin 56400 -> 56400 bytes osdep/Phy.hpp | 8 + 10 files changed, 256 insertions(+), 102 deletions(-) diff --git a/make-linux.mk b/make-linux.mk index 2f60597c8..af8d39674 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -85,7 +85,7 @@ else CFLAGS+=-Wall -fPIE -fvisibility=hidden -pthread $(INCLUDES) -DNDEBUG $(DEFS) CXXFLAGS?=-O3 -fstack-protector CXXFLAGS+=-Wall -Wreorder -fPIE -fvisibility=hidden -fno-rtti -pthread $(INCLUDES) -DNDEBUG $(DEFS) - LDFLAGS=-ldl -pie -Wl,-z,relro,-z,now + LDFLAGS=-ldl -lcurses -pie -Wl,-z,relro,-z,now STRIP=strip --strip-all endif diff --git a/netcon/Intercept.c b/netcon/Intercept.c index 3a14e5387..27e57c76f 100755 --- a/netcon/Intercept.c +++ b/netcon/Intercept.c @@ -202,7 +202,6 @@ int is_mapped_to_service(int sockfd) return get_retval(); } - /*------------------------------------------------------------------------------ ---------- Unix-domain socket lazy initializer (for fd-transfers)-------------- ------------------------------------------------------------------------------*/ @@ -439,10 +438,10 @@ int socket(SOCKET_SIG) memset(cmd, '\0', BUF_SZ); cmd[0] = RPC_SOCKET; - memcpy(&cmd[1], &rpc_st, sizeof(struct socket_st)); + dwr("pid = %d\n", thispid); + memcpy(&cmd[1], &thispid, sizeof(pid_t)); + memcpy(&cmd[1]+sizeof(pid_t), &rpc_st, sizeof(struct socket_st)); pthread_mutex_lock(&lock); - - dwr("sending RPC...\n"); send_command(fdret_sock, cmd); /* get new fd */ diff --git a/netcon/Intercept.h b/netcon/Intercept.h index 71012be38..86fb0049f 100755 --- a/netcon/Intercept.h +++ b/netcon/Intercept.h @@ -31,7 +31,7 @@ #include -#define BUF_SZ 32 +#define BUF_SZ 128 #define ERR_OK 0 /* Userland RPC codes */ @@ -54,6 +54,7 @@ #define RPC_MAP_REQ 21 // A call to determine whether an fd is mapped to the service #define RPC_RETVAL 22 // not RPC per se, but something we should codify #define RPC_KILL_INTERCEPT 23 // Tells the service we need to shut down all connections +#define RPC_I_AM 24 // Tells the service the PID for the current client /* Connection statuses */ #define UNSTARTED 0 diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index a50eb30e1..dd49a1862 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -49,9 +49,11 @@ #include "Intercept.h" #include "NetconUtilities.hpp" +#include + #define APPLICATION_POLL_FREQ 20 #define ZT_LWIP_TCP_TIMER_INTERVAL 10 -#define STATUS_TMR_INTERVAL 2000 // How often we check connection statuses +#define STATUS_TMR_INTERVAL 100 // How often we check connection statuses namespace ZeroTier { @@ -85,6 +87,7 @@ NetconEthernetTap::NetconEthernetTap( lwipstack->lwip_init(); _unixListenSocket = _phy.unixListen(sockPath,(void *)this); + dwr(" NetconEthernetTap(): RPC listening on: %d\n", _phy.getDescriptor(_unixListenSocket)); if (!_unixListenSocket) throw std::runtime_error(std::string("unable to bind to ")+sockPath); _thread = Thread::start(this); @@ -184,7 +187,7 @@ void NetconEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType // First pbuf gets ethernet header at start q = p; if (q->len < sizeof(ethhdr)) { - fprintf(stderr,"_put(): Dropped packet: first pbuf smaller than ethernet header\n"); + dwr("_put(): Dropped packet: first pbuf smaller than ethernet header\n"); return; } memcpy(q->payload,ðhdr,sizeof(ethhdr)); @@ -197,14 +200,14 @@ void NetconEthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType dataptr += q->len; } } else { - fprintf(stderr, "_put(): Dropped packet: no pbufs available\n"); + dwr("_put(): Dropped packet: no pbufs available\n"); return; } { Mutex::Lock _l2(lwipstack->_lock); if(interface.input(p, &interface) != ERR_OK) { - fprintf(stderr, "_put(): Error while RXing packet (netif->input)\n"); + dwr("_put(): Error while RXing packet (netif->input)\n"); } } } @@ -243,15 +246,6 @@ void NetconEthernetTap::scanMulticastGroups(std::vector &added,s _multicastGroups.swap(newGroups); } -TcpConnection *NetconEthernetTap::getConnectionByPCB(struct tcp_pcb *pcb) -{ - for(size_t i=0; ipcb == pcb) - return tcp_connections[i]; - } - return NULL; -} - TcpConnection *NetconEthernetTap::getConnectionByTheirFD(PhySocket *sock, int fd) { for(size_t i=0; ipid); + if(tcp_connections[j]->rpcSock==rpc_sockets[i]){ + fprintf(stderr, " |\n"); + fprintf(stderr, " | Connection(0x%x):\n", tcp_connections[j]); + fprintf(stderr, " | path\t\t\t= %s\n", buf); + fprintf(stderr, " | perceived_fd\t\t= %d\t(fd)\n", tcp_connections[j]->perceived_fd); + fprintf(stderr, " | their_fd\t\t= %d\t(fd)\n", tcp_connections[j]->their_fd); + fprintf(stderr, " | dataSock(0x%x)\t= %d\t(fd)\n", tcp_connections[j]->dataSock, _phy.getDescriptor(tcp_connections[j]->dataSock)); + fprintf(stderr, " | rpcSock(0x%x)\t= %d\t(fd)\n", tcp_connections[j]->rpcSock, _phy.getDescriptor(tcp_connections[j]->rpcSock)); + fprintf(stderr, " | pending\t\t= %d\n", tcp_connections[j]->pending); + fprintf(stderr, " | listening\t\t= %d\n", tcp_connections[j]->listening); + fprintf(stderr, " \\------pcb(0x%x)->state\t= %d\n", tcp_connections[j]->pcb, tcp_connections[j]->pcb->state); + } + } + } + fprintf(stderr, "\n\ndie(): END SERVICE STATE DUMP\n\n---\n\n"); +} + +/* + * Dumps service state and then exits + */ +void NetconEthernetTap::die(int exret) { + dump(); + exit(exret); +} + /* * Closes a TcpConnection and associated LWIP PCB strcuture. */ void NetconEthernetTap::closeConnection(TcpConnection *conn) { - fprintf(stderr, "closeConnection(%x, %d)\n", conn->pcb, _phy.getDescriptor(conn->dataSock)); + fprintf(stderr, " closeConnection(%x, %d)\n", conn->pcb, _phy.getDescriptor(conn->dataSock)); //lwipstack->_tcp_sent(conn->pcb, NULL); //lwipstack->_tcp_recv(conn->pcb, NULL); @@ -319,6 +384,7 @@ void NetconEthernetTap::closeAll() void NetconEthernetTap::threadMain() throw() { + //initscr(); //signal(SIGPIPE, SIG_IGN); uint64_t prev_tcp_time = 0; uint64_t prev_status_time = 0; @@ -345,7 +411,6 @@ void NetconEthernetTap::threadMain() // Main timer loop while (_run) { uint64_t now = OSUtils::now(); - uint64_t since_tcp = now - prev_tcp_time; uint64_t since_etharp = now - prev_etharp_time; uint64_t since_status = now - prev_status_time; @@ -353,9 +418,13 @@ void NetconEthernetTap::threadMain() uint64_t etharp_remaining = ARP_TMR_INTERVAL; uint64_t status_remaining = STATUS_TMR_INTERVAL; + // Connection prunning if (since_status >= STATUS_TMR_INTERVAL) { + //compact_dump(); prev_status_time = now; if(rpc_sockets.size() || tcp_connections.size()) { + + //dump(); /* Here we will periodically check the list of rpc_sockets for those that do not currently have any data connection associated with them. If they are unused, then we will try to read from them, if they fail, we can safely assume @@ -368,14 +437,14 @@ void NetconEthernetTap::threadMain() } else { // Here we should handle the case there there is incoming data (?) - fprintf(stderr, "Listening socketpair closed. Removing RPC connection (%d)\n", + dwr(" tap_thread(): Listening socketpair closed. Removing RPC connection (%d)\n", _phy.getDescriptor(tcp_connections[i]->dataSock)); closeConnection(tcp_connections[i]); } } } } - fprintf(stderr, "tcp_conns = %d, rpc_socks = %d\n", tcp_connections.size(), rpc_sockets.size()); + dwr(" tap_thread(): tcp_conns = %d, rpc_socks = %d\n", tcp_connections.size(), rpc_sockets.size()); for(size_t i=0, associated = 0; irpcSock == rpc_sockets[i]) @@ -383,20 +452,26 @@ void NetconEthernetTap::threadMain() } if(!associated){ // No TCP connections are associated, this is a candidate for removal + int fd = _phy.getDescriptor(rpc_sockets[i]); + fcntl(fd, F_SETFL, O_NONBLOCK); 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])); + int n; + if((n = read(fd,&tmpbuf,BUF_SZ)) < 0) { + dwr(" tap_thread(): RPC close(%d)\n", _phy.getDescriptor(rpc_sockets[i])); closeClient(rpc_sockets[i]); } - else { + // < 0 is failure + // 0 nothing to read, RPC still active + // > 0 RPC data read, handle it + else if (n > 0) { // Handle RPC call, this is rare - fprintf(stderr, "run(): RPC read during connection check\n"); - exit(0); // FIXME: This should be addressed - Raise APPLICATION_POLL_FREQ to make it less likely - phyOnUnixData(rpc_sockets[i],NULL,&tmpbuf,BUF_SZ); + dwr(" tap_thread(): RPC read during connection check (%d bytes)\n", n); + phyOnUnixData(rpc_sockets[i],_phy.getuptr(rpc_sockets[i]),&tmpbuf,BUF_SZ); } } } } + // Main TCP/ETHARP timer section if (since_tcp >= ZT_LWIP_TCP_TIMER_INTERVAL) { prev_tcp_time = now; lwipstack->tcp_tmr(); @@ -444,7 +519,7 @@ void NetconEthernetTap::phyOnFileDescriptorActivity(PhySocket *sock,void **uptr, } } else { - fprintf(stderr, "phyOnFileDescriptorActivity(): PhySocket not readable\n"); + dwr("phyOnFileDescriptorActivity(): PhySocket not readable\n"); } } @@ -452,6 +527,11 @@ void NetconEthernetTap::phyOnFileDescriptorActivity(PhySocket *sock,void **uptr, * Add a new PhySocket for the client connection */ void NetconEthernetTap::phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) { + if(find(rpc_sockets.begin(), rpc_sockets.end(), sockN) != rpc_sockets.end()){ + dwr("SockN (0x%x) already exists!\n", sockN); + return; + } + dwr("phyOnUnixAccept(): push_back( 0x%x )\n", sockN); rpc_sockets.push_back(sockN); } @@ -464,42 +544,63 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,uns switch(buf[0]) { case RPC_SOCKET: - fprintf(stderr, "RPC_SOCKET\n"); + dwr("RPC_SOCKET\n"); struct socket_st socket_rpc; - memcpy(&socket_rpc, &buf[1], sizeof(struct socket_st)); - handle_socket(sock, uptr, &socket_rpc); + pid_t pid; + memcpy(&pid, &buf[1], sizeof(pid_t)); // PID for client RPC tracking (only for debug) + memcpy(&socket_rpc, &buf[64], sizeof(struct socket_st)); + if(handle_socket(sock, uptr, &socket_rpc) >= 0) { + fprintf(stderr, "pidmap[...] = %d\n", pid); + pidmap[sock] = pid; + } break; case RPC_LISTEN: - fprintf(stderr, "RPC_LISTEN\n"); + dwr("RPC_LISTEN\n"); struct listen_st listen_rpc; memcpy(&listen_rpc, &buf[1], sizeof(struct listen_st)); handle_listen(sock, uptr, &listen_rpc); break; case RPC_BIND: - fprintf(stderr, "RPC_BIND\n"); + dwr("RPC_BIND\n"); struct bind_st bind_rpc; memcpy(&bind_rpc, &buf[1], sizeof(struct bind_st)); handle_bind(sock, uptr, &bind_rpc); break; case RPC_CONNECT: - fprintf(stderr, "RPC_CONNECT\n"); + dwr("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_MAP: - fprintf(stderr, "RPC_MAP\n"); + dwr("RPC_MAP\n"); handle_retval(sock, uptr, buf); break; case RPC_MAP_REQ: - fprintf(stderr, "RPC_MAP_REQ\n"); + dwr("RPC_MAP_REQ\n"); handle_map_request(sock, uptr, buf); break; + case RPC_I_AM: + dwr("RPC_I_AM\n"); + handle_i_am(sock, uptr, buf); + break; default: break; } } + +void NetconEthernetTap::handle_i_am(PhySocket *sock, void **uptr, unsigned char* buf) +{ + TcpConnection *conn = (TcpConnection*)*uptr; + if(!conn) + return; + int pid; + memcpy(&pid, &buf[1], sizeof(pid)); + dwr(" pid = %d\n", pid); + conn->pid = pid; +} + /* * Send a 'retval' and 'errno' to the client for an RPC over connection->rpcSock */ @@ -510,7 +611,7 @@ int NetconEthernetTap::send_return_value(TcpConnection *conn, int retval, int _e if(n > 0) conn->pending = false; else { - fprintf(stderr, "Unable to send return value to the intercept. Closing connection\n"); + dwr("Unable to send return value to the intercept. Closing connection\n"); closeConnection(conn); } return n; @@ -579,7 +680,7 @@ int NetconEthernetTap::send_return_value(int fd, int retval, int _errno = 0) */ err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err) { - fprintf(stderr, "nc_accept()\n"); + dwr("nc_accept()\n"); Larg *l = (Larg*)arg; TcpConnection *conn = l->conn; NetconEthernetTap *tap = l->tap; @@ -590,7 +691,7 @@ err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err) if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { if(errno < 0) { l->tap->send_return_value(conn, -1, errno); - fprintf(stderr, "nc_accept(): unable to create socketpair\n"); + dwr("nc_accept(): unable to create socketpair\n"); return ERR_MEM; } } @@ -600,12 +701,12 @@ 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]); + dwr("socketpair = {%d, %d}\n", fds[0], fds[1]); int n, send_fd = tap->_phy.getDescriptor(conn->rpcSock); - fprintf(stderr, "write(%d,...)\n", listening_fd); + dwr("write(%d,...)\n", listening_fd); //int n = write(listening_fd, "z", 1); // accept() in library waits for this byte if((n = send(listening_fd, "z", 1, MSG_NOSIGNAL)) < 0) { - fprintf(stderr, " nc_accept(): Error: [send(listening_fd,...) = MSG_NOSIGNAL].\n"); + dwr(" nc_accept(): Error: [send(listening_fd,...) = MSG_NOSIGNAL].\n"); return -1; } else if(n > 0) { @@ -614,11 +715,11 @@ err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err) new_tcp_conn->pending = true; } else { - fprintf(stderr, "nc_accept(%d): unable to send fd to client\n", listening_fd); + dwr("nc_accept(%d): unable to send fd to client\n", listening_fd); } } else { - fprintf(stderr, "nc_accept(%d): error writing signal byte (send_fd = %d, perceived_fd = %d)\n", listening_fd, send_fd, fds[1]); + dwr("nc_accept(%d): error writing signal byte (send_fd = %d, perceived_fd = %d)\n", listening_fd, send_fd, fds[1]); return -1; } tap->lwipstack->_tcp_arg(newpcb, new Larg(tap, new_tcp_conn)); @@ -630,7 +731,7 @@ err_t NetconEthernetTap::nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err) return ERR_OK; } else { - fprintf(stderr, "nc_accept(%d): can't locate Connection object for PCB.\n", listening_fd); + dwr("nc_accept(%d): can't locate Connection object for PCB.\n", listening_fd); } return -1; } @@ -655,16 +756,16 @@ err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf struct pbuf* q = p; if(!l->conn) { - fprintf(stderr, "nc_recved(): no connection object\n"); + dwr("nc_recved(): no connection object\n"); return ERR_OK; // ? } if(p == NULL) { if(l->conn) { - fprintf(stderr, "nc_recved(): closing connection\n"); + dwr("nc_recved(): closing connection\n"); l->tap->closeConnection(l->conn); } else { - fprintf(stderr, "nc_recved(): can't locate connection via (arg)\n"); + dwr("nc_recved(): can't locate connection via (arg)\n"); } return err; } @@ -674,12 +775,12 @@ err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf break; // ? if((n = l->tap->_phy.streamSend(l->conn->dataSock,p->payload, p->len)) > 0) { if(n < p->len) { - fprintf(stderr, "nc_recved(): unable to write entire pbuf to buffer\n"); + dwr("nc_recved(): unable to write entire pbuf to buffer\n"); } l->tap->lwipstack->_tcp_recved(tpcb, n); // TODO: would it be more efficient to call this once at the end? } else { - fprintf(stderr, "nc_recved(): No data written to intercept buffer\n"); + dwr("nc_recved(): No data written to intercept buffer\n"); } p = p->next; } @@ -700,52 +801,50 @@ err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf void NetconEthernetTap::nc_err(void *arg, err_t err) { Larg *l = (Larg*)arg; - //fprintf(stderr, "larg = %x, nc_err() = %d\n", l, err); - if(!l->conn) - fprintf(stderr, "nc_err(): Connection is NULL!\n"); + dwr("nc_err(): Connection is NULL!\n"); if(l->conn) { switch(err) { case ERR_MEM: - fprintf(stderr, "nc_err(): ERR_MEM->ENOMEM\n"); + dwr("nc_err(): ERR_MEM->ENOMEM\n"); l->tap->send_return_value(l->conn, -1, ENOMEM); break; case ERR_BUF: - fprintf(stderr, "nc_err(): ERR_BUF->ENOBUFS\n"); + dwr("nc_err(): ERR_BUF->ENOBUFS\n"); l->tap->send_return_value(l->conn, -1, ENOBUFS); break; case ERR_TIMEOUT: - fprintf(stderr, "nc_err(): ERR_TIMEOUT->ETIMEDOUT\n"); + dwr("nc_err(): ERR_TIMEOUT->ETIMEDOUT\n"); l->tap->send_return_value(l->conn, -1, ETIMEDOUT); break; case ERR_RTE: - fprintf(stderr, "nc_err(): ERR_RTE->ENETUNREACH\n"); + dwr("nc_err(): ERR_RTE->ENETUNREACH\n"); l->tap->send_return_value(l->conn, -1, ENETUNREACH); break; case ERR_INPROGRESS: - fprintf(stderr, "nc_err(): ERR_INPROGRESS->EINPROGRESS\n"); + dwr("nc_err(): ERR_INPROGRESS->EINPROGRESS\n"); l->tap->send_return_value(l->conn, -1, EINPROGRESS); break; case ERR_VAL: - fprintf(stderr, "nc_err(): ERR_VAL->EINVAL\n"); + dwr("nc_err(): ERR_VAL->EINVAL\n"); l->tap->send_return_value(l->conn, -1, EINVAL); break; case ERR_WOULDBLOCK: - fprintf(stderr, "nc_err(): ERR_WOULDBLOCK->EWOULDBLOCK\n"); + dwr("nc_err(): ERR_WOULDBLOCK->EWOULDBLOCK\n"); l->tap->send_return_value(l->conn, -1, EWOULDBLOCK); break; case ERR_USE: - fprintf(stderr, "nc_err(): ERR_USE->EADDRINUSE\n"); + dwr("nc_err(): ERR_USE->EADDRINUSE\n"); l->tap->send_return_value(l->conn, -1, EADDRINUSE); break; case ERR_ISCONN: - fprintf(stderr, "nc_err(): ERR_ISCONN->EISCONN\n"); + dwr("nc_err(): ERR_ISCONN->EISCONN\n"); l->tap->send_return_value(l->conn, -1, EISCONN); break; case ERR_ABRT: - fprintf(stderr, "nc_err(): ERR_ABRT->ECONNREFUSED\n"); + dwr("nc_err(): ERR_ABRT->ECONNREFUSED\n"); l->tap->send_return_value(l->conn, -1, ECONNREFUSED); break; @@ -769,11 +868,11 @@ void NetconEthernetTap::nc_err(void *arg, err_t err) default: break; } - fprintf(stderr, "nc_err(): closing connection\n"); + dwr("nc_err(): closing connection\n"); l->tap->closeConnection(l->conn); } else { - fprintf(stderr, "nc_err(): can't locate connection object for PCB\n"); + dwr("nc_err(): can't locate connection object for PCB\n"); } } @@ -788,11 +887,6 @@ void NetconEthernetTap::nc_err(void *arg, err_t err) err_t NetconEthernetTap::nc_poll(void* arg, struct tcp_pcb *tpcb) { //Larg *l = (Larg*)arg; - - - - - /* Larg *l = (Larg*)arg; TcpConnection *conn = l->conn; @@ -821,7 +915,7 @@ err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len) //fprintf(stderr, " nc_sent()\n"); Larg *l = (Larg*)arg; if(len) { - fprintf(stderr, " nc_sent(): ACKING len = %d, setting read-notify = true, (sndbuf = %d)\n", len, l->conn->pcb->snd_buf); + //fprintf(stderr, " nc_sent(): ACKING len = %d, setting read-notify = true, (sndbuf = %d)\n", len, l->conn->pcb->snd_buf); l->tap->_phy.setNotifyReadable(l->conn->dataSock, true); l->tap->_phy.whack(); } @@ -840,7 +934,7 @@ err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len) */ err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err) { - fprintf(stderr, "nc_connected()\n"); + dwr("nc_connected()\n"); Larg *l = (Larg*)arg; l->tap->send_return_value(l->conn, ERR_OK); return ERR_OK; @@ -864,13 +958,13 @@ void NetconEthernetTap::handle_map_request(PhySocket *sock, void **uptr, unsigne for(size_t i=0; irpcSock == conn->rpcSock && tcp_connections[i]->perceived_fd == req_fd){ send_return_value(conn, 1, ERR_OK); // True - fprintf(stderr, " handle_map_request(their=%d): MAPPED (to %d)\n", req_fd, + dwr(" handle_map_request(their=%d): MAPPED (to %d)\n", req_fd, _phy.getDescriptor(tcp_connections[i]->dataSock)); return; } } send_return_value(conn, 0, ERR_OK); // False - fprintf(stderr, " handle_map_request(their=%d): NOT MAPPED\n", req_fd); + dwr(" handle_map_request(their=%d): NOT MAPPED\n", req_fd); } /** @@ -891,7 +985,7 @@ void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned cha memcpy(&(conn->perceived_fd), &buf[1], sizeof(int)); conn->pending = false; - fprintf(stderr, " handle_retval(): CONN:%x - Mapping [our=%d -> their=%d]\n",conn, + dwr(" 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 --- @@ -908,12 +1002,12 @@ void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned cha 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]); + dwr(" 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); + dwr(" handle_retval(): CONN:%x - This socket is mapped to two different pipes (?). Exiting.\n", conn); + die(0); // FIXME: Print service mapping state and exit } } } @@ -973,8 +1067,8 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st d[1] = (ip >> 8) & 0xFF; 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, " handle_bind(): err = %d\n", err); + dwr(" handle_bind(): error binding to %d.%d.%d.%d : %d\n", d[0],d[1],d[2],d[3], conn_port); + dwr(" handle_bind(): err = %d\n", err); if(err == ERR_USE) send_return_value(conn, -1, EADDRINUSE); @@ -987,12 +1081,12 @@ 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 (%x) not in CLOSED state. Ignoring BIND request.\n", conn->pcb); + dwr(" handle_bind(): PCB (%x) not in CLOSED state. Ignoring BIND request.\n", conn->pcb); send_return_value(conn, -1, EINVAL); } } else { - fprintf(stderr, " handle_bind(): can't locate connection for PCB\n"); + dwr(" handle_bind(): can't locate connection for PCB\n"); send_return_value(conn, -1, EBADF); } } @@ -1018,17 +1112,17 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st */ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct listen_st *listen_rpc) { - fprintf(stderr, " handle_listen(their=%d):\n", listen_rpc->sockfd); + dwr(" handle_listen(their=%d):\n", listen_rpc->sockfd); TcpConnection *conn = getConnectionByTheirFD(sock, listen_rpc->sockfd); if(!conn){ - fprintf(stderr, " handle_listen(): unable to locate connection object\n"); + dwr(" handle_listen(): unable to locate connection object\n"); // ? send_return_value(conn, -1, EBADF); return; } - fprintf(stderr, " handle_listen(our=%d -> their=%d)\n", _phy.getDescriptor(conn->dataSock), conn->perceived_fd); + dwr(" handle_listen(our=%d -> their=%d)\n", _phy.getDescriptor(conn->dataSock), conn->perceived_fd); if(conn->pcb->state == LISTEN) { - fprintf(stderr, " handle_listen(): PCB is already in listening state.\n"); + dwr(" handle_listen(): PCB is already in listening state.\n"); return; } struct tcp_pcb* listening_pcb; @@ -1092,22 +1186,22 @@ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct liste [?] EPROTONOSUPPORT - The protocol type or the specified protocol is not supported within this domain. */ -void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) +int NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc) { int rpc_fd = _phy.getDescriptor(sock); struct tcp_pcb *newpcb = lwipstack->tcp_new(); - fprintf(stderr, " handle_socket(): pcb=%x\n", newpcb); + dwr(" handle_socket(): pcb=%x\n", newpcb); if(newpcb != NULL) { ZT_PHY_SOCKFD_TYPE fds[2]; if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { if(errno < 0) { send_return_value(rpc_fd, -1, errno); - return; + return -1; } } - fprintf(stderr, " handle_socket(): socketpair = {%d, %d}\n", fds[0], fds[1]); + dwr(" handle_socket(): 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; @@ -1119,11 +1213,13 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke close(fds[1]); // close other end of socketpair // Once the client tells us what its fd is on the other end, we can then complete the mapping new_conn->pending = true; + return 0; } else { sock_fd_write(rpc_fd, -1); // Send a bad fd, to signal error - fprintf(stderr, " handle_socket(): Memory not available for new PCB\n"); + dwr(" handle_socket(): Memory not available for new PCB\n"); send_return_value(rpc_fd, -1, ENOMEM); + return -1; } } @@ -1224,14 +1320,14 @@ void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct conn // that's it! // - Most instances of a retval for a connect() should happen // in the nc_connect() and nc_err() callbacks! - fprintf(stderr, " handle_connect(): unable to connect\n"); + dwr(" handle_connect(): unable to connect\n"); send_return_value(conn, -1, EAGAIN); } // Everything seems to be ok, but we don't have enough info to retval conn->pending=true; } else { - fprintf(stderr, " handle_connect(): could not locate PCB based on their fd\n"); + dwr(" handle_connect(): could not locate PCB based on their fd\n"); send_return_value(conn, -1, EBADF); } } @@ -1243,12 +1339,12 @@ void NetconEthernetTap::handle_write(TcpConnection *conn) int r; if(!conn) { - fprintf(stderr, " handle_write(): could not locate connection for this fd\n"); + dwr(" handle_write(): could not locate connection for this fd\n"); return; } if(conn->idx < max) { if(!conn->pcb) { - fprintf(stderr, " handle_write(): conn->pcb == NULL. Failed to write.\n"); + dwr(" handle_write(): conn->pcb == NULL. Failed to write.\n"); return; } int sndbuf = conn->pcb->snd_buf; // How much we are currently allowed to write to the connection @@ -1275,7 +1371,7 @@ void NetconEthernetTap::handle_write(TcpConnection *conn) int err = lwipstack->_tcp_write(conn->pcb, &conn->buf, r, TCP_WRITE_FLAG_COPY); lwipstack->_tcp_output(conn->pcb); if(err != ERR_OK) { - fprintf(stderr, " handle_write(): error while writing to PCB, (err = %d)\n", err); + dwr(" handle_write(): error while writing to PCB, (err = %d)\n", err); return; } else { @@ -1288,7 +1384,7 @@ void NetconEthernetTap::handle_write(TcpConnection *conn) } } else { - fprintf(stderr, " handle_write(): LWIP stack full\n"); + dwr(" handle_write(): LWIP stack full\n"); return; } } diff --git a/netcon/NetconEthernetTap.hpp b/netcon/NetconEthernetTap.hpp index 159e40427..930807a50 100644 --- a/netcon/NetconEthernetTap.hpp +++ b/netcon/NetconEthernetTap.hpp @@ -107,8 +107,9 @@ private: void handle_bind(PhySocket *sock, void **uptr, struct bind_st *bind_rpc); void handle_listen(PhySocket *sock, void **uptr, struct listen_st *listen_rpc); void handle_map_request(PhySocket *sock, void **uptr, unsigned char* buf); + void handle_i_am(PhySocket *sock, void **uptr, unsigned char* buf); void handle_retval(PhySocket *sock, void **uptr, unsigned char* buf); - void handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc); + int handle_socket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc); void handle_connect(PhySocket *sock, void **uptr, struct connect_st* connect_rpc); void handle_write(TcpConnection *conn); @@ -141,16 +142,19 @@ private: // Client helpers TcpConnection *getConnectionByTheirFD(PhySocket *sock, int fd); - TcpConnection *getConnectionByPCB(struct tcp_pcb *pcb); void closeConnection(TcpConnection *conn); void closeAll(); void closeClient(PhySocket *sock); + void compact_dump(); + void dump(); + void die(int exret); Phy _phy; PhySocket *_unixListenSocket; std::vector tcp_connections; std::vector rpc_sockets; + std::map pidmap; netif interface; diff --git a/netcon/NetconService.hpp b/netcon/NetconService.hpp index 8f1928239..fc6bd46c3 100644 --- a/netcon/NetconService.hpp +++ b/netcon/NetconService.hpp @@ -56,7 +56,8 @@ namespace ZeroTier { int their_fd; bool pending; bool listening; - + int pid; + PhySocket *rpcSock; PhySocket *dataSock; struct tcp_pcb *pcb; diff --git a/netcon/NetconUtilities.cpp b/netcon/NetconUtilities.cpp index 642d77bab..45332512f 100644 --- a/netcon/NetconUtilities.cpp +++ b/netcon/NetconUtilities.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "lwip/ip.h" @@ -41,6 +42,45 @@ namespace ZeroTier { + void dwr(char *fmt, ... ) + { +//#ifdef ZT_DEBUG + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fflush(stderr); + va_end(ap); +//#endif + } + + void clearscreen() + { + fprintf(stderr, "\033[2J"); + } + + //void reset_cursor() + void gotoxy(int x,int y) { + fprintf(stderr, "%c[%d;%df",0x1B,y,x); + } + + // Gets the process/path name associated with a pid + void get_path_from_pid(char* dest, int pid) + { + char ppath[80]; + sprintf(ppath, "/proc/%d/exe", pid); + if (readlink (ppath, dest, 80) != -1){ + } + } + + // Gets the process/path name associated with a fd + void get_path_from_fd(char* dest, int pid, int fd) + { + char ppfd[80]; + sprintf(ppfd, "/proc/%d/fd/%d", pid, fd); + if (readlink (ppfd, dest, 80) != -1){ + } + } + // Functions used to pass file descriptors between processes ssize_t sock_fd_write(int sock, int fd) { diff --git a/netcon/NetconUtilities.hpp b/netcon/NetconUtilities.hpp index ac6042e60..938247053 100644 --- a/netcon/NetconUtilities.hpp +++ b/netcon/NetconUtilities.hpp @@ -31,6 +31,11 @@ namespace ZeroTier { + void dwr(char *str, ... ); + void clearscreen(); + void gotoxy(int x,int y); + void get_path_from_pid(char* dest, int pid); + void get_path_from_fd(char* dest, int pid, int fd); ip_addr_t ip_addr_sin(register struct sockaddr_in *sin); ssize_t sock_fd_write(int sock, int fd); ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index acac7b3b89349bdc41a051feacd845000fda221a..46a1f0248b7a4f13fe292e9bda2830d7d4acde10 100755 GIT binary patch delta 21639 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

JEYaGHY922J&doXE=T19$Ca_F}= zN3KpU0dW!2`RX3XTPek4b^hXM1t`ZX&!N{=y1oVd;r$$X6A ziMMWPf4!G&~TEQ^cex64h4RA35LJJMwO+wSKgx zK67%(^t{X8&uESJUS+7n}f5OF#rO-#m^;W@|_|MK0t>;0SAB<~+eH zK7pBsqhxnmv1jB{YwyUn)SX5J?SOqya*-bInk%{Q7ky!DxLPRuXXXBM8ooaerjG<@c`InZks-_nDjf`^A?Cq&sZdcyhENzYO#&3zwlEJcz*U66m}+pLU>FnT zK9K1l^9?3vEs?T=*#QxQ@gfnLVN96Kj0_<#W}3w0#GQyxgoP6$Lm-T)4L2tS#zb~R z;CvVl7EX)|F)$`9oERAbVN5@SaWH1E1ho7Ic_45;%mXkdGBO0hm}!$^mde7T9Ogns zh9DSI7jAkij0tld$nYR6j*Z21Y!J+`0SMz@OccikVR38_!m$d=WZ}^Zb0H%`1dOQ< zH$4uw(PY_Jc?#X#8q_8N6fGIdJ*=vO~Cv4gutXvi}n+FQPt8fcLVa#hFpUGfR z5rU*5ai@$7rWt{76<|kWDhNU-P*^65#e@iif{vB4^{@&2PY7qin5Up~q>K!~FlG*P zz7doLLg&MH6QQDv48btwRHzU{A&gfGo$zC12!=8x8P-C@ASz)zE}6*-H%r1@_Y&^9 z5E%0^+;w3vCd@q`qe9@Wn*?jGiSY`CQ{Feb9=0^zQkf=~!!!rTLLUEq8ej|(~<$H)NMrT|J=lj~P-%D^VFcfx}s zKpGmNDw7|smXd+ZKBmnAth8B9r18H47-csrq@j0|BgCM-Na4hcdI9mhqIVXz zT#$E>c!3+FxZrYNCn8C9ub0w?%_2{NTNesrib5wF85zQ0Ov%X)Hps$fo|nS)gus~p z;X=VM<{zjrYz&_nL+qvIvqPuxK!X>MBoz!}212D^N@4sIs5m1-aEd$wgtG=hF~HQq z_>u~sV%h-aFPOs^83JKU8K{F88Dd~eNrlO=OJxk2pk}r~X-0+s7;_n1C>X|EBM%CC zMc7R9R;bt67>XGp)eQ|<{XeoA@gB8&ngECPa7cw8=xWFw4$3X_@KqYM;jCly|xEL4{<{XfnfpEv=!tIQKF=5VuD1`B* zArwNHk|-e;h;W<|xDJ8Mu)+d|k%4yMgyIrNFoDKu7(6BiZjmy8C5a@s7Xo0+EVxiG zj9H)v$_t6G$y-UN3J!+g$t;sa0w%M-M4X{A{2-Z-$sD1RIbtTW1WaauNxug3h}!GK9jI=FsUV zMuuP*GaV`fiq6paFkU-U6qFI5%-K*Hq7cU03>9T$2!=8x8MZ^kASz)zDHTw01$SK} z+;t%^=6ASI7>o&X56Gwxxa(#>Ens8_gE3+5fhdIWwjdNjnW(M{nUCZ;P^%IacA%j~ zNTv*gG1K5?#lV=zt_y^_ZWclzj0tlO$aR79VZ3b!g-|A{>jIHom%l<%1{P=5aE$>l zX53`Pol>qxpdkngK1K$3NX9|+fSiCoBqt~Al&!x5H3>F(%L-1U5-{!us5Bzk!$hQ@ z_FyD?m;}rd$jKfi26H6uMv!#a6i9JS! zP#E(eTqqdE{0bFfVwmU%W9vYtyg(rpIv>V!hKe#W1jCr&P$7sy2v3)RAs;Ht$Pf%; z7DI&~Dq%b(HE@oA&F^YJ-NMKa0%K}I^)WJp!I&`jfQ$-(yDkKw5XOYKhk*g25XJ+| zfrVN%gfQE$+ln#K>L8>6>$ujdf zn3&Wyb04|LIQiDmZ;U4JF_JE1~=F#ln$E{PsxJ@ zv?n*6c9Vql#$Zc!L1$Kh&f9!2`Qd4IPFUi7G}-WsJ>!STg=h2`KTKYD<~n1;(s)->sock; } + + /** + * @param s Socket object + * @return Pointer to user object + */ + static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast(s)->uptr); } + + /** * Cause poll() to stop waiting immediately *