From aa6ff039c46ffe51f5ceb49a500f07519e6b7f3b Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 13 Oct 2015 18:33:56 -0400 Subject: [PATCH] Improved bind() logic, added more error handling to accept() --- netcon/NetconEthernetTap.cpp | 18 ++++++++---- netcon/intercept.c | 53 +++++++++++++++++++++++------------ netcon/libintercept.so.1.0 | Bin 52984 -> 53400 bytes 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index f01dcadf..5ef52b72 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -490,6 +490,12 @@ int NetconEthernetTap::send_return_value(int fd, int retval, int _errno = 0) * @param error code * @return ERR_OK if everything is ok, -1 otherwise + i := should be implemented in intercept lib + I := is implemented in intercept lib + X := is implemented in service + ? := required treatment Unknown + - := Not needed + [ ] EAGAIN or EWOULDBLOCK - The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, @@ -499,12 +505,12 @@ int NetconEthernetTap::send_return_value(int fd, int retval, int _errno = 0) [i] EFAULT - The addr argument is not in a writable part of the user address space. [ ] EINTR - The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7). [ ] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative). - [ ] EINVAL - (accept4()) invalid value in flags. + [I] EINVAL - (accept4()) invalid value in flags. [I] EMFILE - The per-process limit of open file descriptors has been reached. [ ] ENFILE - The system limit on the total number of open files has been reached. [ ] ENOBUFS, ENOMEM - Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory. - [i] ENOTSOCK - The descriptor references a file, not a socket. + [I] ENOTSOCK - The descriptor references a file, not a socket. [I] EOPNOTSUPP - The referenced socket is not of type SOCK_STREAM. [ ] EPROTO - Protocol error. @@ -802,10 +808,12 @@ void NetconEthernetTap::handle_retval(PhySocket *sock, void **uptr, unsigned cha [ ] EACCES - The address is protected, and the user is not the superuser. [X] EADDRINUSE - The given address is already in use. - [X] EBADF - sockfd is not a valid descriptor. + [I] EBADF - sockfd is not a valid descriptor. [X] EINVAL - The socket is already bound to an address. - [i] ENOTSOCK - sockfd is a descriptor for a file, not a socket. - [-] The following errors are specific to UNIX domain (AF_UNIX) sockets: + [I] ENOTSOCK - sockfd is a descriptor for a file, not a socket. + + - The following errors are specific to UNIX domain (AF_UNIX) sockets: + [-] EACCES - Search permission is denied on a component of the path prefix. (See also path_resolution(7).) [-] EADDRNOTAVAIL - A nonexistent interface was requested or the requested address was not local. [-] EFAULT - addr points outside the user's accessible address space. diff --git a/netcon/intercept.c b/netcon/intercept.c index 46f01987..5060aa2c 100755 --- a/netcon/intercept.c +++ b/netcon/intercept.c @@ -688,6 +688,21 @@ int poll(POLL_SIG) 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); @@ -697,28 +712,23 @@ int bind(BIND_SIG) if(sockfd == STDIN_FILENO || sockfd == STDOUT_FILENO || sockfd == STDERR_FILENO) return(realbind(sockfd, addr, addrlen)); - int sock_type = -1; - socklen_t sock_type_len = sizeof(sock_type); + /* If local, just use normal syscall */ struct sockaddr_in *connaddr; connaddr = (struct sockaddr_in *) addr; - getsockopt(sockfd, SOL_SOCKET, SO_TYPE, - (void *) &sock_type, &sock_type_len); - if (addr != NULL && (connaddr->sin_family == AF_LOCAL || connaddr->sin_family == PF_NETLINK || connaddr->sin_family == AF_NETLINK - || connaddr->sin_family == AF_UNIX)) { - return(realbind(sockfd, addr, addrlen)); + || connaddr->sin_family == AF_UNIX)) + { + if(realbind == NULL) { + dwr("Unresolved symbol: bind(). Library is exiting.\n"); + exit(-1); + } + return(realbind(sockfd, addr, addrlen)); } - - char cmd[BUF_SZ]; - if(realbind == NULL) { - dwr("Unresolved symbol: bind()\n"); - return -1; - } - /* Assemble and route command */ + char cmd[BUF_SZ]; struct bind_st rpc_st; rpc_st.sockfd = sockfd; rpc_st.__tid = syscall(SYS_gettid); @@ -768,16 +778,22 @@ int accept(ACCEPT_SIG) #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { + dwr("EBADF\n"); return -1; errno = EBADF; } - int sock_type = -1; + /* Check that it is a socket */ + int sock_type; socklen_t sock_type_len = sizeof(sock_type); - getsockopt(sockfd, SOL_SOCKET, SO_TYPE, - (void *) &sock_type, &sock_type_len); + if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) { + errno = ENOTSOCK; + dwr("ENOTSOCK\n"); + return -1; + } /* Check that this socket supports accept() */ - if(!(sock_type & (SOCK_STREAM | SOCK_SEQPACKET))) { + if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) { errno = EOPNOTSUPP; + dwr("EOPNOTSUPP\n"); return -1; } /* Check that we haven't hit the soft-limit file descriptors allowed */ @@ -785,6 +801,7 @@ int accept(ACCEPT_SIG) getrlimit(RLIMIT_NOFILE, &rl); if(sockfd >= rl.rlim_cur){ errno = EMFILE; + dwr("EMFILE\n"); return -1; } #endif diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index 968eb4bf43f03c3158ac2eea55d6fee71fe4792d..1dc1f0da0ae853d94bd429d850d81e0f1f459607 100755 GIT binary patch delta 11522 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 delta 10686 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