================
* FIX: Fix a crash when using pam_winbind. Fixes bug #516.
* NEW: New global/volume option "ignored attributes"
+* FIX: "afp listen" option failed to take IPv6 addresses. Bug #515.
Changes in 3.0.4
================
any incoming request. The network address may be specified either
in dotted-decimal format for IPv4 or in hexadecimal format for
IPv6.</para>
+ <para>IPv6 address + port combination must use URL the format
+ using square brackets [IPv6]:port</para>
</listitem>
</varlistentry>
extern unsigned int getip_port(const struct sockaddr *sa);
extern void apply_ip_mask(struct sockaddr *ai, int maskbits);
extern int compare_ip(const struct sockaddr *sa1, const struct sockaddr *sa2);
+extern int tokenize_ip_port(const char *ipurl, char **address, char **port);
/* Structures and functions dealing with dynamic pollfd arrays */
enum fdtype {IPC_FD, LISTEN_FD};
#define AI_NUMERICSERV 0
#endif
-/* this needs to accept passed in addresses */
+/*!
+ * Initialize DSI over TCP
+ *
+ * @param dsi (rw) DSI handle
+ * @param hostname (r) pointer to hostname string
+ * @param inaddress (r) Optional IPv4 or IPv6 address with an optional port, may be NULL
+ * @param inport (r) pointer to port string
+ *
+ * Creates listening AFP/DSI socket. If the parameter inaddress is NULL, then we listen
+ * on the wildcard address, ie on all interfaces. That should mean listening on the IPv6
+ * address "::" on IPv4/IPv6 dual stack kernels, accepting both v4 and v6 requests.
+ *
+ * If the parameter inaddress is not NULL, then we only listen on the given address.
+ * The parameter may contain a port number using the URL format for address and port:
+ *
+ * IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port
+ *
+ * Parameter inport must be a valid pointer to a port string and is used if the inaddress
+ * parameter doesn't contain a port.
+ *
+ * @returns 0 on success, -1 on failure
+ */
int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
{
EC_INIT;
int flag, err;
- char *a = NULL, *b;
- const char *address;
- const char *port;
+ char *address = NULL, *port = NULL;
struct addrinfo hints, *servinfo, *p;
- /* 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;
- }
+ /* inaddress may be NULL */
+ AFP_ASSERT(dsi && hostname && inport);
+
+ if (inaddress)
+ /* Check whether address is of the from IP:PORT and split */
+ EC_ZERO( tokenize_ip_port(inaddress, &address, &port) );
+
+ if (port == NULL)
+ /* inport is supposed to always contain a valid port string */
+ EC_NULL( port = strdup(inport) );
/* Prepare hint for getaddrinfo */
memset(&hints, 0, sizeof hints);
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));
+ if ((ret = getaddrinfo(address, port, &hints, &servinfo)) != 0) {
+ LOG(log_error, logtype_dsi, "dsi_tcp_init(%s): getaddrinfo: %s\n", address ? address : "*", gai_strerror(ret));
EC_FAIL;
}
guess_interface(dsi, hostname, port ? port : "548");
EC_CLEANUP:
- if (a)
- free(a);
+ if (address)
+ free(address);
+ if (port)
+ free(port);
EC_EXIT;
}
#include <atalk/logger.h>
#include <atalk/util.h>
+#include <atalk/errchk.h>
static char ipv4mapprefix[] = {0,0,0,0,0,0,0,0,0,0,0xff,0xff};
return ret;
}
+/*!
+ * Tokenize IP(4/6) addresses with an optional port into address and port
+ *
+ * @param ipurl (r) IP URL string
+ * @param address (w) IP address
+ * @param port (w) IP port
+ *
+ * @returns 0 on success, -1 on failure
+ *
+ * Tokenize IPv4, IPv4:port, IPv6, [IPv6] or [IPv6:port] URL into address and
+ * port and return two allocated strings with the address and the port.
+ *
+ * If the function returns 0, then address point to a newly allocated
+ * valid address string, port may either be NULL or point to a newly
+ * allocated port number.
+ *
+ * If the function returns -1, then the contents of address and port are
+ * undefined.
+ */
+int tokenize_ip_port(const char *ipurl, char **address, char **port)
+{
+ EC_INIT;
+ char *p = NULL;
+ char *s;
+
+ AFP_ASSERT(ipurl && address && port);
+ EC_NULL( p = strdup(ipurl));
+
+ /* Either ipv4, ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+ if (!strchr(p, ':')) {
+ /* IPv4 address without port */
+ *address = p;
+ p = NULL; /* prevent free() */
+ *port = NULL;
+ EC_EXIT_STATUS(0);
+ }
+
+ /* Either ipv4:port, ipv6, [ipv6] or [ipv6]:port */
+
+ if (strchr(p, '.')) {
+ /* ipv4:port */
+ *address = p;
+ p = strchr(p, ':');
+ *p = '\0';
+ EC_NULL( *port = strdup(p + 1));
+ p = NULL; /* prevent free() */
+ EC_EXIT_STATUS(0);
+ }
+
+ /* Either ipv6, [ipv6] or [ipv6]:port */
+
+ if (p[0] != '[') {
+ /* ipv6 */
+ *address = p;
+ p = NULL; /* prevent free() */
+ *port = NULL;
+ EC_EXIT_STATUS(0);
+ }
+
+ /* [ipv6] or [ipv6]:port */
+
+ EC_NULL( *address = strdup(p + 1) );
+
+ if ((s = strchr(*address, ']')) == NULL) {
+ LOG(log_error, logtype_dsi, "tokenize_ip_port: malformed ipv6 address %s\n", ipurl);
+ EC_FAIL;
+ }
+ *s = '\0';
+ /* address now points to the ipv6 address without [] */
+
+ if (s[1] == ':') {
+ /* [ipv6]:port */
+ EC_NULL( *port = strdup(s + 2) );
+ } else {
+ /* [ipv6] */
+ *port = NULL;
+ }
+
+EC_CLEANUP:
+ if (p)
+ free(p);
+ EC_EXIT;
+}
+
/*!
* Add a fd to a dynamic pollfd array that is allocated and grown as needed
*
Specifies the IP address that the server should advertise
\fBand\fR
listens to\&. The default is advertise the first IP address of the system, but to listen for any incoming request\&. The network address may be specified either in dotted\-decimal format for IPv4 or in hexadecimal format for IPv6\&.
+.sp
+IPv6 address + port combination must use URL the format using square brackets [IPv6]:port
.RE
.PP
afp port = \fIport number\fR \fB(G)\fR