From 21058d44aabd9f97e2edfd245770584412c30f2d Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Sat, 11 May 2013 19:17:38 +0200 Subject: [PATCH] New option "afp interfaces" Allows specifying where Netatalk listens for AFP connections by interface name, from FR #79. --- NEWS | 3 + configure.ac | 2 + doc/manpages/man5/afp.conf.5.xml | 10 +++ etc/afpd/afp_config.c | 108 ++++++++++++++++++++++++++----- include/atalk/globals.h | 2 +- libatalk/util/netatalk_conf.c | 3 + man/man5/afp.conf.5.in | 5 ++ 7 files changed, 116 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index 566cfb5f..8eac3474 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,9 @@ Changes in 3.0.4 * FIX: Add missing include, fixes bug #512. * FIX: Change default FinderInfo for directories to be all 0, fixes bug 514. +* NEW: New option "afp interfaces" which allows specifying where + Netatalk listens for AFP connections by interface names. + From FR #79. Changes in 3.0.3 ================ diff --git a/configure.ac b/configure.ac index c5b18c17..e4d910c3 100644 --- a/configure.ac +++ b/configure.ac @@ -80,6 +80,8 @@ AC_CHECK_FUNCS(mmap utime getpagesize) dnl needed by tbd dnl search for necessary libraries AC_SEARCH_LIBS(gethostbyname, nsl) AC_SEARCH_LIBS(connect, socket) +AC_CHECK_FUNCS(getifaddrs) dnl comes after gethostbyname and connect so it picks up the libs + AX_PTHREAD(, [AC_MSG_ERROR([missing pthread_sigmask])]) AC_DEFINE(OPEN_NOFOLLOW_ERRNO, ELOOP, errno returned by open with O_NOFOLLOW) diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml index f5a5510f..a09eec5b 100644 --- a/doc/manpages/man5/afp.conf.5.xml +++ b/doc/manpages/man5/afp.conf.5.xml @@ -554,6 +554,16 @@ + + afp interfaces = name [name ...] + (G) + + Specifies the network interfaces that the server should + listens on. The default is advertise the first IP address of the + system, but to listen for any incoming request. + + + afp listen = ip address[:port] [ip address[:port] ...] (G) diff --git a/etc/afpd/afp_config.c b/etc/afpd/afp_config.c index 61bbf684..5a5aaa35 100644 --- a/etc/afpd/afp_config.c +++ b/etc/afpd/afp_config.c @@ -17,6 +17,9 @@ #include #include #include +#ifdef HAVE_GETIFADDRS +#include +#endif #include #include @@ -39,7 +42,6 @@ #include "volume.h" #include "afp_zeroconf.h" - /*! * Free and cleanup config and DSI * @@ -83,9 +85,13 @@ void configfree(AFPObj *obj, DSI *dsi) int configinit(AFPObj *obj) { EC_INIT; - DSI *dsi, **next = &obj->dsi; + DSI *dsi = NULL; + DSI **next = &obj->dsi; char *p = NULL, *q = NULL, *savep; const char *r; + struct ifaddrs *ifaddr, *ifa; + int family, s; + static char interfaddr[NI_MAXHOST]; auth_load(obj->options.uampath, obj->options.uamlist); set_signature(&obj->options); @@ -93,23 +99,99 @@ int configinit(AFPObj *obj) acl_ldap_freeconfig(); #endif /* HAVE_LDAP */ - LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, port: %s", + LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, listen: %s, interfaces: %s, port: %s", obj->options.hostname, - obj->options.listen ? obj->options.listen : "(default: hostname)", + obj->options.listen ? obj->options.listen : "-", + obj->options.interfaces ? obj->options.interfaces : "-", obj->options.port); - /* obj->options->listen is of the from "IP[:port][,IP:[PORT], ...]" */ - /* obj->options->port is the default port to listen (548) */ - + /* + * Setup addresses we listen on from hostname and/or "afp listen" option + */ if (obj->options.listen) { EC_NULL( q = p = strdup(obj->options.listen) ); EC_NULL( p = strtok_r(p, ", ", &savep) ); + while (p) { + if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL) + break; + + status_init(obj, dsi); + *next = dsi; + next = &dsi->next; + dsi->AFPobj = obj; + + LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d", + getip_string((struct sockaddr *)&dsi->server), + getip_port((struct sockaddr *)&dsi->server)); + + p = strtok_r(NULL, ", ", &savep); + } + if (q) { + free(q); + q = NULL; + } } - while (1) { - if ((dsi = dsi_init(obj, obj->options.hostname, p, obj->options.port)) == NULL) - break; + /* + * Setup addresses we listen on from "afp interfaces". + * We use getifaddrs() instead of if_nameindex() because the latter appears still + * to be unable to return ipv4 addresses + */ + if (obj->options.interfaces) { +#ifndef HAVE_GETIFADDRS + LOG(log_error, logtype_afpd, "option \"afp interfaces\" not supported"); +#else + if (getifaddrs(&ifaddr) == -1) { + LOG(log_error, logtype_afpd, "getinterfaddr: getifaddrs() failed: %s", strerror(errno)); + EC_FAIL; + } + EC_NULL( q = p = strdup(obj->options.interfaces) ); + EC_NULL( p = strtok_r(p, ", ", &savep) ); + while (p) { + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + if (STRCMP(ifa->ifa_name, !=, p)) + continue; + + family = ifa->ifa_addr->sa_family; + if (family == AF_INET || family == AF_INET6) { + if (getnameinfo(ifa->ifa_addr, + (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), + interfaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) { + LOG(log_error, logtype_afpd, "getinterfaddr: getnameinfo() failed %s", gai_strerror(errno)); + continue; + } + + if ((dsi = dsi_init(obj, obj->options.hostname, interfaddr, obj->options.port)) == NULL) + continue; + + status_init(obj, dsi); + *next = dsi; + next = &dsi->next; + dsi->AFPobj = obj; + + LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on interface %s with address %s:%d", + p, + getip_string((struct sockaddr *)&dsi->server), + getip_port((struct sockaddr *)&dsi->server)); + } /* if (family == AF_INET || family == AF_INET6) */ + } /* for (ifa != NULL) */ + p = strtok_r(NULL, ", ", &savep); + } + freeifaddrs(ifaddr); +#endif + } + + /* + * Check whether we got a valid DSI from options.listen or options.interfaces, + * if not add a DSI that accepts all connections and goes though the list of + * network interaces for determining an IP we can advertise in DSIStatus + */ + if (dsi == NULL) { + if ((dsi = dsi_init(obj, obj->options.hostname, NULL, obj->options.port)) == NULL) + EC_FAIL_LOG("no suitable network address found, use \"afp listen\" or \"afp interfaces\"", 0); status_init(obj, dsi); *next = dsi; next = &dsi->next; @@ -118,12 +200,6 @@ int configinit(AFPObj *obj) LOG(log_note, logtype_afpd, "Netatalk AFP/TCP listening on %s:%d", getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server)); - - if (p) - /* p is NULL if ! obj->options.listen */ - p = strtok_r(NULL, ", ", &savep); - if (!p) - break; } #ifdef HAVE_LDAP diff --git a/include/atalk/globals.h b/include/atalk/globals.h index a095b103..b2456013 100644 --- a/include/atalk/globals.h +++ b/include/atalk/globals.h @@ -94,7 +94,7 @@ struct afp_options { uint32_t server_quantum; int dsireadbuf; /* scale factor for sizefof(dsi->buffer) = server_quantum * dsireadbuf */ char *hostname; - char *listen, *port; + char *listen, *interfaces, *port; char *Cnid_srv, *Cnid_port; char *configfile; char *uampath, *fqdn; diff --git a/libatalk/util/netatalk_conf.c b/libatalk/util/netatalk_conf.c index fdb0bfc9..923e9c84 100644 --- a/libatalk/util/netatalk_conf.c +++ b/libatalk/util/netatalk_conf.c @@ -1740,6 +1740,7 @@ int afp_config_parse(AFPObj *AFPObj, char *processname) options->k5service = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 service", NULL); options->k5realm = iniparser_getstrdup(config, INISEC_GLOBAL, "k5 realm", NULL); options->listen = iniparser_getstrdup(config, INISEC_GLOBAL, "afp listen", NULL); + options->interfaces = iniparser_getstrdup(config, INISEC_GLOBAL, "afp interfaces", NULL); options->ntdomain = iniparser_getstrdup(config, INISEC_GLOBAL, "nt domain", NULL); options->addomain = iniparser_getstrdup(config, INISEC_GLOBAL, "ad domain", NULL); options->ntseparator = iniparser_getstrdup(config, INISEC_GLOBAL, "nt separator", NULL); @@ -1941,6 +1942,8 @@ void afp_config_free(AFPObj *obj) CONFIG_ARG_FREE(obj->options.k5realm); if (obj->options.listen) CONFIG_ARG_FREE(obj->options.listen); + if (obj->options.interfaces) + CONFIG_ARG_FREE(obj->options.interfaces); if (obj->options.ntdomain) CONFIG_ARG_FREE(obj->options.ntdomain); if (obj->options.addomain) diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in index a650d5ff..90ac57ac 100644 --- a/man/man5/afp.conf.5.in +++ b/man/man5/afp.conf.5.in @@ -414,6 +414,11 @@ Setting this option is not recommended since globally encrypting AFP connections .RE .RE .PP +afp interfaces = \fIname [name \&.\&.\&.]\fR \fB(G)\fR +.RS 4 +Specifies the network interfaces that the server should listens on\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. +.RE +.PP afp listen = \fIip address[:port] [ip address[:port] \&.\&.\&.]\fR \fB(G)\fR .RS 4 Specifies the IP address that the server should advertise -- 2.39.2