From 499d1628c71fc776665021726a13d11b328a71e7 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 14 Oct 2015 17:59:11 -0400 Subject: [PATCH] Improved error coverage --- netcon/NetconEthernetTap.cpp | 77 +++++++++++++++++++++++------------ netcon/libintercept.so.1.0 | Bin 0 -> 46264 bytes 2 files changed, 50 insertions(+), 27 deletions(-) create mode 100755 netcon/libintercept.so.1.0 diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index afb91fdbc..0ef3a6f04 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -442,7 +442,7 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,uns } /* - * Send a return value to the client for an RPC + * Send a 'retval' and 'errno' to the client for an RPC over connection->rpcSock */ int NetconEthernetTap::send_return_value(TcpConnection *conn, int retval, int _errno = 0) { @@ -642,8 +642,6 @@ void NetconEthernetTap::nc_err(void *arg, err_t err) if(l->conn) { switch(err) { - // FIXME: Check if connection is pending first? - case ERR_MEM: fprintf(stderr, "nc_err(): ERR_MEM->ENOMEM\n"); l->tap->send_return_value(l->conn, -1, ENOMEM); @@ -818,7 +816,7 @@ void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned cha [X] EINVAL - The socket is already bound to an address. [I] ENOTSOCK - sockfd is a descriptor for a file, not a socket. - [X] ENOMEM - Insufficient kernel memory was available. ??? + [X] ENOMEM - Insufficient kernel memory was available. - The following errors are specific to UNIX domain (AF_UNIX) sockets: @@ -857,9 +855,9 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st if(err == ERR_USE) send_return_value(conn, -1, EADDRINUSE); if(err == ERR_MEM) - send_return_value(conn, -1, ENOMEM); // FIXME: Likely won't happen - if(err == ERR_BUF) send_return_value(conn, -1, ENOMEM); + if(err == ERR_BUF) + send_return_value(conn, -1, ENOMEM); // FIXME: Closest match } else { send_return_value(conn, ERR_OK, ERR_OK); // Success @@ -870,10 +868,10 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st send_return_value(conn, -1, EINVAL); } } - //else { - // fprintf(stderr, "handle_bind(): can't locate connection for PCB\n"); - // send_return_value(conn, -1, EBADF); // FIXME: This makes no sense - //} + else { + fprintf(stderr, "handle_bind(): can't locate connection for PCB\n"); + send_return_value(conn, -1, EBADF); + } } /* @@ -890,7 +888,7 @@ void NetconEthernetTap::handle_bind(PhySocket *sock, void **uptr, struct bind_st - := Not needed [?] EADDRINUSE - Another socket is already listening on the same port. -[I] EBADF - The argument sockfd is not a valid descriptor. +[IX] EBADF - The argument sockfd is not a valid descriptor. [I] ENOTSOCK - The argument sockfd is not a socket. [I] EOPNOTSUPP - The socket is not of a type that supports the listen() operation. @@ -923,7 +921,13 @@ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct liste } else { fprintf(stderr, "handle_listen(): unable to allocate memory for new listening PCB\n"); - send_return_value(conn, -1, ENOMEM); // FIXME: This does not have an equivalent errno value + // FIXME: This does not have an equivalent errno value + // lwip will reclaim space with a tcp_listen call since a PCB in a LISTEN + // state takes up less space. If something goes wrong during the creation of a + // new listening socket we should return an error that implies we can't use this + // socket, even if the reason isn't describing what really happened internally. + // See: http://lwip.wikia.com/wiki/Raw/TCP + send_return_value(conn, -1, EBADF); } } else { @@ -963,12 +967,13 @@ void NetconEthernetTap::handle_listen(PhySocket *sock, void **uptr, struct liste */ void 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(); if(newpcb != NULL) { ZT_PHY_SOCKFD_TYPE fds[2]; if(socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) < 0) { if(errno < 0) { - send_return_value(_phy.getDescriptor(sock), -1, errno); + send_return_value(rpc_fd, -1, errno); return; } } @@ -984,7 +989,6 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke new_conn->pending = true; } else { - int rpc_fd = _phy.getDescriptor(sock); sock_fd_write(rpc_fd, -1); // Send a bad fd, to signal error fprintf(stderr, "handle_socket(): Memory not available for new PCB\n"); send_return_value(rpc_fd, -1, ENOMEM); @@ -1008,20 +1012,22 @@ void NetconEthernetTap::handle_socket(PhySocket *sock, void **uptr, struct socke [-] EACCES - For UNIX domain sockets, which are identified by pathname: Write permission is denied ... [?] EACCES, EPERM - The user tried to connect to a broadcast address without having the socket broadcast flag enabled ... - [i] EADDRINUSE - Local address is already in use. + [X] EADDRINUSE - Local address is already in use. [I] EAFNOSUPPORT - The passed address didn't have the correct address family in its sa_family field. - [?] EAGAIN - No more free local ports or insufficient entries in the routing cache. + [X] EAGAIN - No more free local ports or insufficient entries in the routing cache. [ ] EALREADY - The socket is nonblocking and a previous connection attempt has not yet been completed. - [I] EBADF - The file descriptor is not a valid index in the descriptor table. + [IX] EBADF - The file descriptor is not a valid index in the descriptor table. [ ] ECONNREFUSED - No-one listening on the remote address. [i] EFAULT - The socket structure address is outside the user's address space. [ ] EINPROGRESS - The socket is nonblocking and the connection cannot be completed immediately. [-] EINTR - The system call was interrupted by a signal that was caught. [X] EISCONN - The socket is already connected. - [?] ENETUNREACH - Network is unreachable. + [X] ENETUNREACH - Network is unreachable. [I] ENOTSOCK - The file descriptor is not associated with a socket. [X] ETIMEDOUT - Timeout while attempting connection. + [X] EINVAL - Invalid argument, SVr4, generally makes sense to set this + * */ void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct connect_st* connect_rpc) @@ -1033,7 +1039,7 @@ void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct conn ip_addr_t conn_addr = convert_ip((struct sockaddr_in *)&connect_rpc->__addr); if(conn != NULL) { - lwipstack->tcp_sent(conn->pcb, nc_sent); // FIXME: Move? + lwipstack->tcp_sent(conn->pcb, nc_sent); lwipstack->tcp_recv(conn->pcb, nc_recved); lwipstack->tcp_err(conn->pcb, nc_err); lwipstack->tcp_poll(conn->pcb, nc_poll, APPLICATION_POLL_FREQ); @@ -1042,25 +1048,41 @@ void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct conn int err = 0; if((err = lwipstack->tcp_connect(conn->pcb,&conn_addr,conn_port, nc_connected)) < 0) { + if(err == ERR_ISCONN) { + send_return_value(conn, -1, EISCONN); // Already in connected state + return; + } if(err == ERR_USE) { - send_return_value(conn, -1, EISCONN); // Already in use + send_return_value(conn, -1, EADDRINUSE); // Already in use return; } if(err == ERR_VAL) { - send_return_value(conn, -1, EAFNOSUPPORT); // FIXME: Invalid arguments? + send_return_value(conn, -1, EINVAL); // Invalid ipaddress parameter return; } if(err == ERR_RTE) { - send_return_value(conn, -1, ENETUNREACH); // FIXME: Host unreachable + send_return_value(conn, -1, ENETUNREACH); // No route to host return; } - if(err == ERR_BUF) - { - // FIXME + if(err == ERR_BUF) { + send_return_value(conn, -1, EAGAIN); // No more ports available + return; } if(err == ERR_MEM) { - // FIXME: return value originates from tcp_enqueue_flags() + /* Can occur for the following reasons: tcp_enqueue_flags() + + 1) tcp_enqueue_flags is always called with either SYN or FIN in flags. + We need one available snd_buf byte to do that. + This means we can't send FIN while snd_buf==0. A better fix would be to + not include SYN and FIN sequence numbers in the snd_buf count. + + 2) Cannot allocate new pbuf + 3) Cannot allocate new TCP segment + + */ + send_return_value(conn, -1, EAGAIN); // FIXME: Doesn't describe the problem well, but closest match + return; } // We should only return a value if failure happens immediately @@ -1071,13 +1093,14 @@ void NetconEthernetTap::handle_connect(PhySocket *sock, void **uptr, struct conn // - 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"); - send_return_value(conn, -1, err); // FIXME: Only catch unhandled errors + 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, "could not locate PCB based on their fd\n"); + send_return_value(conn, -1, EBADF); } } diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 new file mode 100755 index 0000000000000000000000000000000000000000..f8a46023602d89857773857897f2fe925539f665 GIT binary patch literal 46264 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 literal 0 HcmV?d00001