We only need to spend CPU computing a new error string IFF there was an error
[deb_libnfs.git] / lib / socket.c
index 6f626529be9a2c3d6c7b982a648e9edf806d2e2d..055109a73c9f7b4be1f69d73f0552feb5c2d51ff 100644 (file)
    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
-#ifdef WIN32
-#include "win32_compat.h"
-#else
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#endif/*WIN32*/
-
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #include "aros_compat.h"
 #endif
 
+#ifdef WIN32
+#include "win32_compat.h"
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
 #ifdef HAVE_POLL_H
 #include <poll.h>
 #endif
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <assert.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
 #ifdef HAVE_SYS_FILIO_H
 #include <sys/filio.h>
 #endif
+
 #ifdef HAVE_SYS_SOCKIO_H
 #include <sys/sockio.h>
 #endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
 #include <sys/types.h>
 #include "libnfs-zdr.h"
 #include "libnfs.h"
@@ -69,13 +89,33 @@ static void set_nonblocking(int fd)
        int v = 0;
 #if defined(WIN32)
        long nonblocking=1;
-       v = ioctlsocket(fd, FIONBIO,&nonblocking);
+       v = ioctl(fd, FIONBIO, &nonblocking);
 #else
        v = fcntl(fd, F_GETFL, 0);
         fcntl(fd, F_SETFL, v | O_NONBLOCK);
 #endif //FIXME
 }
 
+#ifdef HAVE_NETINET_TCP_H
+int set_tcp_sockopt(int sockfd, int optname, int value)
+{
+       int level;
+
+       #if defined(__FreeBSD__) || defined(__sun) || (defined(__APPLE__) && defined(__MACH__))
+       struct protoent *buf;
+
+       if ((buf = getprotobyname("tcp")) != NULL)
+               level = buf->p_proto;
+       else
+               return -1;
+       #else
+               level = SOL_TCP;
+       #endif
+
+       return setsockopt(sockfd, level, optname, (char *)&value, sizeof(value));
+}
+#endif
+
 int rpc_get_fd(struct rpc_context *rpc)
 {
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
@@ -147,13 +187,7 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
 
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
-       assert(rpc->magic == RPC_CONTEXT_MAGIC);
-
-#if defined(WIN32)
-       if (ioctlsocket(rpc->fd, FIONREAD, &available) != 0) {
-#else
        if (ioctl(rpc->fd, FIONREAD, &available) != 0) {
-#endif
                rpc_set_error(rpc, "Ioctl FIONREAD returned error : %d. Closing socket.", errno);
                return -1;
        }
@@ -176,6 +210,7 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
                if (count < 0) {
                        rpc_set_error(rpc, "Failed recvfrom: %s", strerror(errno));
                        free(buf);
+                       return -1;
                }
                if (rpc_process_pdu(rpc, buf, count) != 0) {
                        rpc_set_error(rpc, "Invalid/garbage pdu received from server. Ignoring PDU");
@@ -217,7 +252,7 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
        pdu_size = rpc_get_pdu_size(rpc->inbuf);
        if (rpc->insize < pdu_size) {
                unsigned char *buf;
-               
+
                buf = malloc(pdu_size);
                if (buf == NULL) {
                        rpc_set_error(rpc, "Failed to allocate buffer of %d bytes for pdu, errno:%d. Closing socket.", pdu_size, errno);
@@ -246,14 +281,17 @@ static int rpc_read_from_socket(struct rpc_context *rpc)
        rpc->inpos += count;
 
        if (rpc->inpos == rpc->insize) {
-               if (rpc_process_pdu(rpc, rpc->inbuf, pdu_size) != 0) {
-                       rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket");
-                       return -1;
-               }
-               free(rpc->inbuf);
+               char *buf = rpc->inbuf;
+
                rpc->inbuf  = NULL;
                rpc->insize = 0;
                rpc->inpos  = 0;
+
+               if (rpc_process_pdu(rpc, buf, pdu_size) != 0) {
+                       rpc_set_error(rpc, "Invalid/garbage pdu received from server. Closing socket");
+                       return -1;
+               }
+               free(buf);
        }
 
        return 0;
@@ -355,6 +393,17 @@ void rpc_unset_autoreconnect(struct rpc_context *rpc)
        rpc->auto_reconnect = 0;
 }
 
+void rpc_set_tcp_syncnt(struct rpc_context *rpc, int v)
+{
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       rpc->tcp_syncnt = v;
+}
+
+#ifndef TCP_SYNCNT
+#define TCP_SYNCNT        7
+#endif
+
 static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_storage *s)
 {
        int socksize;
@@ -365,6 +414,11 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s
        case AF_INET:
                socksize = sizeof(struct sockaddr_in);
                rpc->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+#ifdef HAVE_NETINET_TCP_H
+               if (rpc->tcp_syncnt != RPC_PARAM_UNDEFINED) {
+                       set_tcp_sockopt(rpc->fd, TCP_SYNCNT, rpc->tcp_syncnt);
+               }
+#endif
                break;
        default:
                rpc_set_error(rpc, "Can not handle AF_FAMILY:%d", s->ss_family);
@@ -399,8 +453,12 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s
                static int portOfs = 0;
                const int firstPort = 512;      /* >= 512 according to Sun docs */
                const int portCount = IPPORT_RESERVED - firstPort;
-               int startOfs = portOfs, port, rc;
+               int startOfs, port, rc;
 
+               if (portOfs == 0) {
+                       portOfs = time(NULL) % 400;
+               }
+               startOfs = portOfs;
                do {
                        rc = -1;
                        port = htons(firstPort + portOfs);
@@ -428,10 +486,10 @@ static int rpc_connect_sockaddr_async(struct rpc_context *rpc, struct sockaddr_s
        if (connect(rpc->fd, (struct sockaddr *)s, socksize) != 0 && errno != EINPROGRESS) {
                rpc_set_error(rpc, "connect() to server failed. %s(%d)", strerror(errno), errno);
                return -1;
-       }               
+       }
 
        return 0;
-}          
+}
 
 int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc_cb cb, void *private_data)
 {
@@ -475,7 +533,7 @@ int rpc_connect_async(struct rpc_context *rpc, const char *server, int port, rpc
        }
 
        return 0;
-}          
+}
 
 int rpc_disconnect(struct rpc_context *rpc, char *error)
 {
@@ -484,11 +542,7 @@ int rpc_disconnect(struct rpc_context *rpc, char *error)
        rpc_unset_autoreconnect(rpc);
 
        if (rpc->fd != -1) {
-#if defined(WIN32)
-               closesocket(rpc->fd);
-#else
                close(rpc->fd);
-#endif
        }
        rpc->fd  = -1;
 
@@ -520,11 +574,7 @@ static int rpc_reconnect_requeue(struct rpc_context *rpc)
        assert(rpc->magic == RPC_CONTEXT_MAGIC);
 
        if (rpc->fd != -1) {
-#if defined(WIN32)
-               closesocket(rpc->fd);
-#else
                close(rpc->fd);
-#endif
        }
        rpc->fd  = -1;
 
@@ -568,7 +618,7 @@ int rpc_bind_udp(struct rpc_context *rpc, char *addr, int port)
        sprintf(service, "%d", port);
        if (getaddrinfo(addr, service, NULL, &ai) != 0) {
                rpc_set_error(rpc, "Invalid address:%s. "
-                       "Can not resolv into IPv4/v6 structure.");
+                             "Can not resolv into IPv4/v6 structure.", addr);
                return -1;
        }
 
@@ -576,13 +626,13 @@ int rpc_bind_udp(struct rpc_context *rpc, char *addr, int port)
        case AF_INET:
                rpc->fd = socket(ai->ai_family, SOCK_DGRAM, 0);
                if (rpc->fd == -1) {
-                       rpc_set_error(rpc, "Failed to create UDP socket: %s", strerror(errno)); 
+                       rpc_set_error(rpc, "Failed to create UDP socket: %s", strerror(errno));
                        freeaddrinfo(ai);
                        return -1;
                }
 
                if (bind(rpc->fd, (struct sockaddr *)ai->ai_addr, sizeof(struct sockaddr_in)) != 0) {
-                       rpc_set_error(rpc, "Failed to bind to UDP socket: %s",strerror(errno)); 
+                       rpc_set_error(rpc, "Failed to bind to UDP socket: %s",strerror(errno));
                        freeaddrinfo(ai);
                        return -1;
                }
@@ -613,7 +663,7 @@ int rpc_set_udp_destination(struct rpc_context *rpc, char *addr, int port, int i
        sprintf(service, "%d", port);
        if (getaddrinfo(addr, service, NULL, &ai) != 0) {
                rpc_set_error(rpc, "Invalid address:%s. "
-                       "Can not resolv into IPv4/v6 structure.");
+                             "Can not resolv into IPv4/v6 structure.", addr);
                return -1;
        }
 
@@ -658,3 +708,10 @@ int rpc_queue_length(struct rpc_context *rpc)
        }
        return i;
 }
+
+void rpc_set_fd(struct rpc_context *rpc, int fd)
+{
+       assert(rpc->magic == RPC_CONTEXT_MAGIC);
+
+       rpc->fd = fd;
+}