]> arthur.barton.de Git - netatalk.git/blobdiff - libatalk/dsi/dsi_tcp.c
First try for a volume defaults config
[netatalk.git] / libatalk / dsi / dsi_tcp.c
index 9fcf6146cce20408e961df0b20cc8d6470d119a6..37a00bf40c2b163904412d72bb2d43808f36729d 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: dsi_tcp.c,v 1.18 2009-11-05 14:38:08 franklahm Exp $
- *
  * Copyright (c) 1997, 1998 Adrian Sun (asun@zoology.washington.edu)
  * All rights reserved. See COPYRIGHT.
  *
@@ -17,9 +15,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif /* HAVE_UNISTD_H */
 #include <errno.h>
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/socket.h>
-
-#ifdef HAVE_STDINT_H
 #include <stdint.h>
-#endif /* HAVE_STDINT_H */
 
 #include <sys/ioctl.h>
 #ifdef TRU64
@@ -58,8 +51,7 @@ int deny_severity = log_warning;
 #include <atalk/dsi.h>
 #include <atalk/compat.h>
 #include <atalk/util.h>
-#include <netatalk/endian.h>
-#include "dsi_private.h"
+#include <atalk/errchk.h>
 
 #define min(a,b)  ((a) < (b) ? (a) : (b))
 
@@ -86,29 +78,10 @@ static void dsi_tcp_close(DSI *dsi)
     dsi->socket = -1;
 }
 
-static void dsi_tcp_timeout(DSI *dsi)
-{
-    struct timeval tv;
-    /* 2 seconds delay, most of the time it translates to 4 seconds:
-     * send/write returns first with whatever it has written and the
-     * second time it returns EAGAIN
-     */
-    tv.tv_sec = 2;
-    tv.tv_usec = 0;
-
-    /* Note: write isn't a restartable syscall if there's a timeout on the socket
-     * we have to test for EINTR
-     */
-    if (setsockopt(dsi->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
-        LOG(log_error, logtype_default, "dsi_tcp_open: unable to set timeout %s", strerror(errno));
-        exit(EXITERR_CLNT);
-    }
-}
-
 /* alarm handler for tcp_open */
 static void timeout_handler(int sig _U_)
 {
-    LOG(log_error, logtype_default, "dsi_tcp_open: connection timed out");
+    LOG(log_error, logtype_dsi, "dsi_tcp_open: connection timed out");
     exit(EXITERR_CLNT);
 }
 
@@ -125,10 +98,10 @@ static int dsi_tcp_open(DSI *dsi)
 #ifdef TCPWRAP
     {
         struct request_info req;
-        request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
+        request_init(&req, RQ_DAEMON, "afpd", RQ_FILE, dsi->socket, NULL);
         fromhost(&req);
         if (!hosts_access(&req)) {
-            LOG(deny_severity, logtype_default, "refused connect from %s", eval_client(&req));
+            LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req));
             close(dsi->socket);
             errno = ECONNREFUSED;
             dsi->socket = -1;
@@ -143,7 +116,7 @@ static int dsi_tcp_open(DSI *dsi)
     if (0 == (pid = fork()) ) { /* child */
         static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
         struct sigaction newact, oldact;
-        u_int8_t block[DSI_BLOCKSIZ];
+        uint8_t block[DSI_BLOCKSIZ];
         size_t stored;
 
         /* reset signals */
@@ -160,7 +133,7 @@ static int dsi_tcp_open(DSI *dsi)
 
         if ((sigaction(SIGALRM, &newact, &oldact) < 0) ||
             (setitimer(ITIMER_REAL, &timer, NULL) < 0)) {
-            LOG(log_error, logtype_default, "dsi_tcp_open: %s", strerror(errno));
+            LOG(log_error, logtype_dsi, "dsi_tcp_open: %s", strerror(errno));
             exit(EXITERR_SYS);
         }
 #endif
@@ -176,7 +149,7 @@ static int dsi_tcp_open(DSI *dsi)
             exit(EXITERR_CLNT);
         }
         if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
-            LOG(log_error, logtype_default, "dsi_tcp_open: invalid header");
+            LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header");
             exit(EXITERR_CLNT);
         }
 
@@ -187,7 +160,7 @@ static int dsi_tcp_open(DSI *dsi)
             if (len > 0)
                 stored += len;
             else {
-                LOG(log_error, logtype_default, "dsi_tcp_open: stream_read: %s", strerror(errno));
+                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
                 exit(EXITERR_CLNT);
             }
         }
@@ -211,7 +184,7 @@ static int dsi_tcp_open(DSI *dsi)
             if (len > 0)
                 stored += len;
             else {
-                LOG(log_error, logtype_default, "dsi_tcp_open: stream_read: %s", strerror(errno));
+                LOG(log_error, logtype_dsi, "dsi_tcp_open: stream_read: %s", strerror(errno));
                 exit(EXITERR_CLNT);
             }
         }
@@ -223,9 +196,7 @@ static int dsi_tcp_open(DSI *dsi)
         sigaction(SIGALRM, &oldact, NULL);
 #endif
 
-        dsi_tcp_timeout(dsi);
-
-        LOG(log_info, logtype_default, "AFP/TCP session from %s:%u",
+        LOG(log_info, logtype_dsi, "AFP/TCP session from %s:%u",
             getip_string((struct sockaddr *)&dsi->client),
             getip_port((struct sockaddr *)&dsi->client));
     }
@@ -234,85 +205,161 @@ static int dsi_tcp_open(DSI *dsi)
     return pid;
 }
 
+/* get it from the interface list */
+#ifndef IFF_SLAVE
+#define IFF_SLAVE 0
+#endif
+
+static void guess_interface(DSI *dsi, const char *hostname, const char *port)
+{
+    int fd;
+    char **start, **list;
+    struct ifreq ifr;
+    struct sockaddr_in *sa = (struct sockaddr_in *)&dsi->server;
+
+    start = list = getifacelist();
+    if (!start)
+        return;
+        
+    fd = socket(PF_INET, SOCK_STREAM, 0);
+
+    while (list && *list) {
+        strlcpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
+        list++;
+
+
+        if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
+            continue;
+
+        if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
+            continue;
+
+        if (!(ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) )
+            continue;
+
+        if (ioctl(fd, SIOCGIFADDR, &ifr) < 0)
+            continue;
+
+        memset(&dsi->server, 0, sizeof(struct sockaddr_storage));
+        sa->sin_family = AF_INET;
+        sa->sin_port = htons(atoi(port));
+        sa->sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+
+        LOG(log_info, logtype_dsi, "dsi_tcp: '%s:%s' on interface '%s' will be used instead.",
+            getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
+        goto iflist_done;
+    }
+    LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
+        "Check to make sure %s is in /etc/hosts and the correct domain is in "
+        "/etc/resolv.conf: %s", hostname, strerror(errno));
+
+iflist_done:
+    close(fd);
+    freeifacelist(start);
+}
+
+
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+
 /* this needs to accept passed in addresses */
-int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
-                 const char *port, const int proxy)
+int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
 {
-    int                ret;
+    EC_INIT;
     int                flag;
+    char               *a = NULL, *b;
+    const char         *address;
+    const char         *port;
     struct addrinfo    hints, *servinfo, *p;
 
-    dsi->protocol = DSI_TCPIP;
+    /* Check whether address is of the from IP:PORT and split */
+    address = inaddress;
+    port = inport;
+    if (address && strchr(address, ':')) {
+        EC_NULL_LOG( address = a = strdup(address) );
+        b = strchr(a, ':');
+        *b = 0;
+        port = b + 1;
+    }
 
     /* Prepare hint for getaddrinfo */
     memset(&hints, 0, sizeof hints);
+#if !defined(FREEBSD)
     hints.ai_family = AF_UNSPEC;
+#endif
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_NUMERICSERV;
-    if ( ! address)
+
+    if ( ! address) {
         hints.ai_flags |= AI_PASSIVE;
-    else
+#if defined(FREEBSD)
+        hints.ai_family = AF_INET6;
+#endif
+    } else {
         hints.ai_flags |= AI_NUMERICHOST;
-
-    if ((ret = getaddrinfo(address ? address : NULL, port ? port : "548", &hints, &servinfo)) != 0) {
-        LOG(log_error, logtype_default, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
-        return 0;
+#if defined(FREEBSD)
+        hints.ai_family = AF_UNSPEC;
+#endif
+    }
+    if ((ret = getaddrinfo(address ? address : NULL, port, &hints, &servinfo)) != 0) {
+        LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
+        EC_FAIL;
     }
 
-    /* create a socket */
-    if (proxy)
-        dsi->serversock = -1;
-    else {
-        /* loop through all the results and bind to the first we can */
-        for (p = servinfo; p != NULL; p = p->ai_next) {
-            if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
-                LOG(log_info, logtype_default, "dsi_tcp_init: socket: %s", strerror(errno));
-                continue;
-            }
+    /* loop through all the results and bind to the first we can */
+    for (p = servinfo; p != NULL; p = p->ai_next) {
+        if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+            LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno));
+            continue;
+        }
 
-            /*
-             * Set some socket options:
-             * SO_REUSEADDR deals w/ quick close/opens
-             * TCP_NODELAY diables Nagle
-             */
+        /*
+         * Set some socket options:
+         * SO_REUSEADDR deals w/ quick close/opens
+         * TCP_NODELAY diables Nagle
+         */
 #ifdef SO_REUSEADDR
-            flag = 1;
-            setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+        flag = 1;
+        setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+#endif
+#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY)
+        int on = 0;
+        setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
 #endif
 
 #ifdef USE_TCP_NODELAY
 #ifndef SOL_TCP
 #define SOL_TCP IPPROTO_TCP
 #endif
-            flag = 1;
-            setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
+        flag = 1;
+        setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
 #endif /* USE_TCP_NODELAY */
             
-            if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
-                close(dsi->serversock);
-                LOG(log_info, logtype_default, "dsi_tcp_init: bind: %s\n", strerror(errno));
-                continue;
-            }
-
-            if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
-                close(dsi->serversock);
-                LOG(log_info, logtype_default, "dsi_tcp_init: listen: %s\n", strerror(errno));
-                continue;
-            }
-            
-            break;
+        if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
+            close(dsi->serversock);
+            LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno));
+            continue;
         }
 
-        if (p == NULL)  {
-            LOG(log_error, logtype_default, "dsi_tcp_init: no suitable network config for TCP socket");
-            freeaddrinfo(servinfo);
-            return 0;
+        if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
+            close(dsi->serversock);
+            LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno));
+            continue;
         }
+            
+        break;
+    }
 
-        /* Copy struct sockaddr to struct sockaddr_storage */
-        memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
+    if (p == NULL)  {
+        LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket");
         freeaddrinfo(servinfo);
-    } /* if (proxy) */
+        EC_FAIL;
+    }
+
+    /* Copy struct sockaddr to struct sockaddr_storage */
+    memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
+    freeaddrinfo(servinfo);
 
     /* Point protocol specific functions to tcp versions */
     dsi->proto_open = dsi_tcp_open;
@@ -322,7 +369,7 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
 
     if (address) {
         /* address is a parameter, use it 'as is' */
-        return 1;
+        goto EC_CLEANUP;
     }
 
     /* Prepare hint for getaddrinfo */
@@ -330,9 +377,9 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
     hints.ai_family = AF_UNSPEC;
     hints.ai_socktype = SOCK_STREAM;
 
-    if ((ret = getaddrinfo(hostname, port ? port : "548", &hints, &servinfo)) != 0) {
-        LOG(log_error, logtype_default, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
-        return 0;
+    if ((ret = getaddrinfo(hostname, port, &hints, &servinfo)) != 0) {
+        LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(ret));
+        goto interfaces;
     }
 
     for (p = servinfo; p != NULL; p = p->ai_next) {
@@ -348,61 +395,21 @@ int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
         }
     }
 
-    if (p == NULL) { /* no advertisable address found */
-        LOG(log_info, logtype_default, "dsi_tcp: cannot resolve hostname '%s'", hostname);
+    if (p) {
+        /* Store found address in dsi->server */
+        memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
         freeaddrinfo(servinfo);
-        return 0;
+        goto EC_CLEANUP;
     }
-
-    /* Store found address in dsi->server */
-    memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
+    LOG(log_info, logtype_dsi, "dsi_tcp: hostname '%s' resolves to loopback address", hostname);
     freeaddrinfo(servinfo);
-    return 1;
-
-    /*
-     * Note: I'll leave this code just in case. With getaddrinfo I guess we
-     * should be able to get an IP != 127.0.0.1 or ::1 from the hostname. Hopefully.
-     */
-#if 0
-    /* get it from the interface list */
-    char **start, **list;
-    struct ifreq ifr;
-    start = list = getifacelist();
-    while (list && *list) {
-        strlcpy(ifr.ifr_name, *list, sizeof(ifr.ifr_name));
-        list++;
 
-#ifndef IFF_SLAVE
-#define IFF_SLAVE 0
-#endif
+interfaces:
+    guess_interface(dsi, hostname, port ? port : "548");
 
-        if (ioctl(dsi->serversock, SIOCGIFFLAGS, &ifr) < 0)
-            continue;
-
-        if (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT | IFF_SLAVE))
-            continue;
-
-        if (!(ifr.ifr_flags & (IFF_UP | IFF_RUNNING)) )
-            continue;
-
-        if (ioctl(dsi->serversock, SIOCGIFADDR, &ifr) < 0)
-            continue;
-
-        dsi->server.sin_addr.s_addr =
-            ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
-        LOG(log_info, logtype_default, "dsi_tcp: '%s' on interface '%s' will be used instead.",
-            inet_ntoa(dsi->server.sin_addr), ifr.ifr_name);
-        goto iflist_done;
-    }
-    LOG(log_info, logtype_default, "dsi_tcp (Chooser will not select afp/tcp) "
-        "Check to make sure %s is in /etc/hosts and the correct domain is in "
-        "/etc/resolv.conf: %s", hostname, strerror(errno));
-
-iflist_done:
-    if (start)
-        freeifacelist(start);
-    return 1;
-
-#endif
+EC_CLEANUP:
+    if (a)
+        free(a);
+    EC_EXIT;
 }