#include "config.h"
#endif /* HAVE_CONFIG_H */
-#if !defined(__FreeBSD__)
-# ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE 600
-# endif
-# ifndef __EXTENSIONS__
-# define __EXTENSIONS__
-# endif
-# ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-# endif
-#endif
+#include <atalk/standards.h>
+
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <arpa/inet.h>
+#include <sys/uio.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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};
* @param lenght (r) how many bytes to read
* @param setnonblocking (r) when non-zero this func will enable and disable non blocking
* io mode for the socket
- * @param timeout (r) number of seconds to try reading
+ * @param timeout (r) number of seconds to try reading, 0 means no timeout
*
* @returns number of bytes actually read or -1 on timeout or error
*/
}
/* Calculate end time */
- (void)gettimeofday(&now, NULL);
- end = now;
- end.tv_sec += timeout;
+ if (timeout) {
+ (void)gettimeofday(&now, NULL);
+ end = now;
+ end.tv_sec += timeout;
+ }
while (stored < length) {
- len = read(socket, (char *) data + stored, length - stored);
+ len = recv(socket, (char *) data + stored, length - stored, 0);
if (len == -1) {
switch (errno) {
case EINTR:
continue;
case EAGAIN:
FD_SET(socket, &rfds);
- tv.tv_usec = 0;
- tv.tv_sec = timeout;
-
- while ((ret = select(socket + 1, &rfds, NULL, NULL, &tv)) < 1) {
+ if (timeout) {
+ tv.tv_usec = 0;
+ tv.tv_sec = timeout;
+ }
+
+ while ((ret = select(socket + 1, &rfds, NULL, NULL, timeout ? &tv : NULL)) < 1) {
switch (ret) {
case 0:
- LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+ LOG(log_debug, logtype_dsi, "select timeout %d s", timeout);
errno = EAGAIN;
goto exit;
default: /* -1 */
switch (errno) {
case EINTR:
- (void)gettimeofday(&now, NULL);
- if (now.tv_sec >= end.tv_sec && now.tv_usec >= end.tv_usec) {
- LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
- goto exit;
- }
- if (now.tv_usec > end.tv_usec) {
- tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
- tv.tv_sec = end.tv_sec - now.tv_sec - 1;
- } else {
- tv.tv_usec = end.tv_usec - now.tv_usec;
- tv.tv_sec = end.tv_sec - now.tv_sec;
+ if (timeout) {
+ (void)gettimeofday(&now, NULL);
+ if (now.tv_sec > end.tv_sec
+ ||
+ (now.tv_sec == end.tv_sec && now.tv_usec >= end.tv_usec)) {
+ LOG(log_debug, logtype_afpd, "select timeout %d s", timeout);
+ goto exit;
+ }
+ if (now.tv_usec > end.tv_usec) {
+ tv.tv_usec = 1000000 + end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec - 1;
+ } else {
+ tv.tv_usec = end.tv_usec - now.tv_usec;
+ tv.tv_sec = end.tv_sec - now.tv_sec;
+ }
}
FD_SET(socket, &rfds);
continue;
return ret;
}
-#define POLL_FD_SET_STARTSIZE 512
-#define POLL_FD_SET_INCREASE 128
/*!
- * Add a fd to a dynamic pollfd array that is allocated and grown as needed
+ * 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
*
- * This uses an additional array of struct polldata which stores type information
- * (enum fdtype) and a pointer to anciliary user data.
+ * 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.
*
- * 1. Allocate the arrays with an intial size of [POLL_FD_SET_STARTSIZE] if
- * *fdsetp is NULL.
- * 2. Grow array as needed
- * 3. Fill in both array elements and increase count of used elements
- *
- * @param fdsetp (rw) pointer to callers pointer to the pollfd array
- * @param polldatap (rw) pointer to callers pointer to the polldata array
- * @param fdset_usedp (rw) pointer to an int with the number of used elements
- * @param fdset_sizep (rw) pointer to an int which stores the array sizes
- * @param fd (r) file descriptor to add to the arrays
- * @param fdtype (r) type of fd, currently IPC_FD or LISTEN_FD
- * @param data (rw) pointer to data the caller want to associate with an fd
+ * 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.
*/
-void fdset_add_fd(struct pollfd **fdsetp,
- struct polldata **polldatap,
- int *fdset_usedp,
- int *fdset_sizep,
- int fd,
- enum fdtype fdtype,
- void *data)
+int tokenize_ip_port(const char *ipurl, char **address, char **port)
{
- struct pollfd *fdset = *fdsetp;
- struct polldata *polldata = *polldatap;
- int fdset_size = *fdset_sizep;
-
- LOG(log_debug, logtype_default, "fdset_add_fd: adding fd %i in slot %i", fd, *fdset_usedp);
-
- if (fdset == NULL) { /* 1 */
- /* Initialize with space for 512 fds */
- fdset = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct pollfd));
- if (! fdset)
- exit(EXITERR_SYS);
-
- polldata = calloc(POLL_FD_SET_STARTSIZE, sizeof(struct polldata));
- if (! polldata)
- exit(EXITERR_SYS);
-
- fdset_size = 512;
- *fdset_sizep = fdset_size;
- *fdsetp = fdset;
- *polldatap = polldata;
+ 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);
}
- if (*fdset_usedp >= fdset_size) { /* 2 */
- fdset = realloc(fdset, sizeof(struct pollfd) * (fdset_size + POLL_FD_SET_INCREASE));
- if (fdset == NULL)
- exit(EXITERR_SYS);
+ /* Either ipv4:port, ipv6, [ipv6] or [ipv6]:port */
- polldata = realloc(polldata, sizeof(struct polldata) * (fdset_size + POLL_FD_SET_INCREASE));
- if (polldata == NULL)
- exit(EXITERR_SYS);
+ 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 */
- fdset_size += POLL_FD_SET_INCREASE;
- *fdset_sizep = fdset_size;
- *fdsetp = fdset;
- *polldatap = polldata;
+ if (p[0] != '[') {
+ /* ipv6 */
+ *address = p;
+ p = NULL; /* prevent free() */
+ *port = NULL;
+ EC_EXIT_STATUS(0);
}
- /* 3 */
- fdset[*fdset_usedp].fd = fd;
- fdset[*fdset_usedp].events = POLLIN;
- polldata[*fdset_usedp].fdtype = fdtype;
- polldata[*fdset_usedp].data = data;
- (*fdset_usedp)++;
+ /* [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;
}
-/*!
- * Remove a fd from our pollfd array
- *
- * 1. Search fd
- * 2a
- * 2b If we remove the last array elemnt, just decrease count
- * 3. If found move all following elements down by one
- * 4. Decrease count of used elements in array
+/**
+ * Allocate and initialize atalk socket event struct
+ **/
+struct asev *asev_init(int max)
+{
+ struct asev *asev = calloc(1, sizeof(struct asev));
+
+ if (asev == NULL) {
+ return NULL;
+ }
+
+ /* Initialize with space for all possibly active fds */
+ asev->fdset = calloc(max, sizeof(struct pollfd));
+ asev->data = calloc(max, sizeof(struct asev_data));
+
+ if (asev->fdset == NULL || asev->data == NULL) {
+ free(asev->fdset);
+ free(asev->data);
+ free(asev);
+ return NULL;
+ }
+
+ asev->max = max;
+ asev->used = 0;
+
+ return asev;
+}
+
+/**
+ * Add a fd to a dynamic pollfd array and associated data array
*
- * This currently doesn't shrink the allocated storage of the array.
+ * This uses an additional array of struct polldata which stores type
+ * information (enum fdtype) and a pointer to anciliary user data.
+ **/
+bool asev_add_fd(struct asev *asev,
+ int fd,
+ enum asev_fdtype fdtype,
+ void *private)
+{
+ if (asev == NULL) {
+ return false;
+ }
+
+ if (!(asev->used < asev->max)) {
+ return false;
+ }
+
+ asev->fdset[asev->used].fd = fd;
+ asev->fdset[asev->used].events = POLLIN;
+ asev->data[asev->used].fdtype = fdtype;
+ asev->data[asev->used].private = private;
+ asev->used++;
+
+ return true;
+}
+
+/**
+ * Remove fd from asev
*
- * @param fdsetp (rw) pointer to callers pointer to the pollfd array
- * @param polldatap (rw) pointer to callers pointer to the polldata array
- * @param fdset_usedp (rw) pointer to an int with the number of used elements
- * @param fdset_sizep (rw) pointer to an int which stores the array sizes
- * @param fd (r) file descriptor to remove from the arrays
- */
-void fdset_del_fd(struct pollfd **fdsetp,
- struct polldata **polldatap,
- int *fdset_usedp,
- int *fdset_sizep _U_,
- int fd)
+ * @returns true if the fd was deleted, otherwise false
+ **/
+bool asev_del_fd(struct asev *asev, int fd)
{
- struct pollfd *fdset = *fdsetp;
- struct polldata *polldata = *polldatap;
-
- if (*fdset_usedp < 1)
- return;
-
- for (int i = 0; i < *fdset_usedp; i++) {
- if (fdset[i].fd == fd) { /* 1 */
- if (i == 0 && *fdset_usedp == 1) { /* 2a */
- fdset[i].fd = -1;
- memset(&polldata[i], 0, sizeof(struct polldata));
- } else if (i < (*fdset_usedp - 1)) { /* 2b */
- memmove(&fdset[i], &fdset[i+1], (*fdset_usedp - 1) * sizeof(struct pollfd)); /* 3 */
- memmove(&polldata[i], &polldata[i+1], (*fdset_usedp - 1) * sizeof(struct polldata)); /* 3 */
+ int i;
+ int numafter;
+
+ if (asev == NULL) {
+ return false;
+ }
+
+ if (asev->used == 0) {
+ LOG(log_error, logtype_cnid, "asev_del_fd: empty");
+ return false;
+ }
+
+ for (i = 0; i < asev->used; i++) {
+ /*
+ * Scan the array for a matching fd
+ */
+ if (asev->fdset[i].fd == fd) {
+ /*
+ * found fd
+ */
+ if ((i + 1) == asev->used) {
+ /*
+ * it's the last (or only) array element, simply null it
+ */
+ asev->fdset[i].fd = -1;
+ asev->data[i].fdtype = 0;
+ asev->data[i].private = NULL;
+ } else {
+ /*
+ * Move down by one all subsequent elements
+ */
+ numafter = asev->used - (i + 1);
+ memmove(&asev->fdset[i], &asev->fdset[i+1],
+ numafter * sizeof(struct pollfd));
+ memmove(&asev->data[i], &asev->data[i+1],
+ numafter * sizeof(struct asev_data));
}
- (*fdset_usedp)--;
- break;
+ asev->used--;
+ return true;
}
}
+
+ return false;
}
/* Length of the space taken up by a padded control message of length len */