]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/ctl.c
fixedpoint: fix buffer overflow in fixed point computations
[thirdparty/lldpd.git] / src / ctl.c
index 60937206f98686b280bc0a9336a72391387110a4..a347908eaf5e429a1e5fcd71a8ecc5842ef06543 100644 (file)
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -1,7 +1,8 @@
+/* -*- mode: c; c-file-style: "openbsd" -*- */
 /*
  * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
  *
- * Permission to use, copy, modify, and distribute this software for any
+ * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include "lldpd.h"
-
+#include <stdlib.h>
 #include <unistd.h>
+#include <fcntl.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <string.h>
+
+#include "ctl.h"
+#include "marshal.h"
+#include "log.h"
+#include "compat/compat.h"
 
+/**
+ * Create a new listening Unix socket for control protocol.
+ *
+ * @param name The name of the Unix socket.
+ * @return The socket when successful, -1 otherwise.
+ */
 int
-ctl_create(char *name)
+ctl_create(const char *name)
 {
        int s;
        struct sockaddr_un su;
        int rc;
 
+       log_debug("control", "create control socket %s", name);
+
        if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
                return -1;
+       if (fcntl(s, F_SETFD, FD_CLOEXEC) == -1) {
+               close(s);
+               return -1;
+       }
        su.sun_family = AF_UNIX;
-       strlcpy(su.sun_path, name, UNIX_PATH_MAX);
+       strlcpy(su.sun_path, name, sizeof(su.sun_path));
        if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
                rc = errno; close(s); errno = rc;
                return -1;
        }
+
+       log_debug("control", "listen to control socket %s", name);
        if (listen(s, 5) == -1) {
                rc = errno; close(s); errno = rc;
+               log_debug("control", "cannot listen to control socket %s", name);
                return -1;
        }
        return s;
 }
 
+/**
+ * Connect to the control Unix socket.
+ *
+ * @param name The name of the Unix socket.
+ * @return The socket when successful, -1 otherwise.
+ */
 int
-ctl_connect(char *name)
+ctl_connect(const char *name)
 {
        int s;
        struct sockaddr_un su;
        int rc;
 
+       log_debug("control", "connect to control socket %s", name);
+
        if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
                return -1;
        su.sun_family = AF_UNIX;
-       strlcpy(su.sun_path, name, UNIX_PATH_MAX);
+       strlcpy(su.sun_path, name, sizeof(su.sun_path));
        if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
                rc = errno;
-               LLOG_WARN("unable to connect to socket " LLDPD_CTL_SOCKET);
+               log_warn("control", "unable to connect to socket %s", name);
+               close(s);
                errno = rc; return -1;
        }
        return s;
 }
 
+/**
+ * Remove the control Unix socket.
+ *
+ * @param name The name of the Unix socket.
+ */
 void
-ctl_msg_init(struct hmsg *t, enum hmsg_type type)
-{
-       memset(t, 0, MAX_HMSGSIZE);
-       t->hdr.type = type;
-       t->hdr.len = 0;
-       t->hdr.pid = getpid();
-}
-
-int
-ctl_msg_send(int fd, struct hmsg *t)
-{
-       return write(fd, t, t->hdr.len + sizeof(struct hmsg_hdr));
-}
-
-int
-ctl_msg_recv(int fd, struct hmsg *t)
-{
-       int n;
-       if ((n = read(fd, t, MAX_HMSGSIZE)) == -1) {
-               return -1;
-       }
-       if (n < sizeof(struct hmsg_hdr)) {
-               LLOG_WARNX("message received too short");
-               errno = 0;
-               return -1;
-       }
-       if (n != sizeof(struct hmsg_hdr) + t->hdr.len) {
-               LLOG_WARNX("message from %d seems to be truncated (or too large)",
-                       t->hdr.pid);
-               errno = 0;
-               return -1;
-       }
-       return 1;
-}
-
-void
-ctl_cleanup(char *name)
+ctl_cleanup(const char *name)
 {
+       log_debug("control", "cleanup control socket");
        if (unlink(name) == -1)
-               LLOG_WARN("unable to unlink %s", name);
+               log_warn("control", "unable to unlink %s", name);
 }
 
-/* Packing/unpacking */
-
-/* This structure is used to track memory allocation when unpacking */
-struct gc {
-       TAILQ_ENTRY(gc) next;
-       void *pointer;
-};
-TAILQ_HEAD(gc_l, gc);
-
-typedef struct { char c; int16_t x; } st_int16;
-typedef struct { char c; int32_t x; } st_int32;
-typedef struct { char c; time_t x; } st_timet;
-typedef struct { char c; void *x; } st_void_p;
-
-#define INT16_ALIGN (sizeof(st_int16) - sizeof(int16_t))
-#define INT32_ALIGN (sizeof(st_int32) - sizeof(int32_t))
-#define TIMET_ALIGN (sizeof(st_timet) - sizeof(time_t))
-#define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *))
-
-struct formatdef {
-       char format;
-       int  size;
-       int  alignment;
-       int (*pack)(struct hmsg*, void **, void *,
-           const struct formatdef *);
-       int (*unpack)(struct hmsg*, void **, void *,
-           const struct formatdef *, struct gc_l *);
-};
-
-/* void** is a pointer to a pointer to the end of struct hmsg*. It should be
- * updated. void* is a pointer to the entity to pack */
-
-static int
-ctl_alloc_pointer(struct gc_l *pointers, void *pointer)
+/**
+ * Serialize and "send" a structure through the control protocol.
+ *
+ * This function does not really send the message but outputs it to a buffer.
+ *
+ * @param output_buffer A pointer to a buffer to which the message will be
+ *                      appended. Can be @c NULL. In this case, the buffer will
+ *                      be allocated.
+ * @param[in,out] output_len The length of the provided buffer. Will be updated
+ *                           with the new length
+ * @param type  The type of message we want to send.
+ * @param t     The structure to be serialized and sent.
+ * @param mi    The appropriate marshal structure for serialization.
+ * @return -1 in case of failure, 0 in case of success.
+ *
+ * Make sure this function logic matches the server-side one: @c levent_ctl_recv().
+ */
+int
+ctl_msg_send_unserialized(uint8_t **output_buffer, size_t *output_len,
+    enum hmsg_type type,
+    void *t, struct marshal_info *mi)
 {
-       struct gc *gpointer;
-       if (pointers != NULL) {
-               if ((gpointer = (struct gc *)calloc(1,
-                           sizeof(struct gc))) == NULL) {
-                       LLOG_WARN("unable to allocate memory for garbage collector");
+       ssize_t len = 0, newlen;
+       void *buffer = NULL;
+
+       log_debug("control", "send a message through control socket");
+       if (t) {
+               len = marshal_serialize_(mi, t, &buffer, 0, NULL, 0);
+               if (len <= 0) {
+                       log_warnx("control", "unable to serialize data");
                        return -1;
                }
-               gpointer->pointer = pointer;
-               TAILQ_INSERT_TAIL(pointers, gpointer, next);
-       }
-       return 0;
-}
-
-static void
-ctl_free_pointers(struct gc_l *pointers, int listonly)
-{
-       struct gc *pointer, *pointer_next;
-       for (pointer = TAILQ_FIRST(pointers);
-            pointer != NULL;
-            pointer = pointer_next) {
-               pointer_next = TAILQ_NEXT(pointer, next);
-               TAILQ_REMOVE(pointers, pointer, next);
-               if (!listonly)
-                       free(pointer->pointer);
-               free(pointer);
-       }
-}
-
-static int
-pack_copy(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct)
-{
-       if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
-               LLOG_WARNX("message became too large");
-               return -1;
        }
-       memcpy(*p, s, ct->size);
-       *p += ct->size;
-       h->hdr.len += ct->size;
-       return ct->size;
-}
 
-static int
-unpack_copy(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct, struct gc_l *pointers)
-{
-       memcpy(s, *p, ct->size);
-       *p += ct->size;
-       return ct->size;
-}
-
-static int
-pack_string(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct)
-{
-       int len, ss;
-       if ((*(char**)s) == NULL)
-               len = -1;
-       else
-               len = strlen(*(char**)s);
-       if (h->hdr.len + len + sizeof(int) > MAX_HMSGSIZE -
-           sizeof(struct hmsg_hdr)) {
-               LLOG_WARNX("message became too large");
-               return -1;
-       }
-       memcpy(*p, &len, sizeof(int));
-       *p += sizeof(int);
-       ss = sizeof(int);
-       if (len != -1) {
-               memcpy(*p, *(char **)s, len);
-               *p += len;
-               ss += len;
-       }
-       h->hdr.len += ss;
-       return ss;
-}
+       newlen = len + sizeof(struct hmsg_header);
 
-static int
-unpack_string(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct, struct gc_l *pointers)
-{
-       char *string;
-       int len;
-       memcpy(&len, *p, sizeof(int));
-       *p += sizeof(int);
-       if (len == -1) {
-               string = NULL;
-       } else {
-               if ((string = (char *)calloc(1, len + 1)) == NULL) {
-                       LLOG_WARNX("unable to allocate new string");
+       if (*output_buffer == NULL) {
+               *output_len = 0;
+               if ((*output_buffer = malloc(newlen)) == NULL) {
+                       log_warn("control", "no memory available");
+                       free(buffer);
                        return -1;
                }
-               if (ctl_alloc_pointer(pointers, string) == -1) {
-                       free(string);
+       } else {
+               void *new = realloc(*output_buffer, *output_len + newlen);
+               if (new == NULL) {
+                       log_warn("control", "no memory available");
+                       free(buffer);
                        return -1;
                }
-               memcpy(string, *p, len);
-               *p += len;
+               *output_buffer = new;
        }
-       memcpy(s, &string, sizeof(char *));
-       return sizeof(char*);
+
+       struct hmsg_header hdr;
+       memset(&hdr, 0, sizeof(struct hmsg_header));
+       hdr.type = type;
+       hdr.len = len;
+       memcpy(*output_buffer + *output_len, &hdr, sizeof(struct hmsg_header));
+       if (t)
+               memcpy(*output_buffer + *output_len + sizeof(struct hmsg_header), buffer, len);
+       *output_len += newlen;
+       free(buffer);
+       return 0;
 }
 
-static int
-pack_chars(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct)
+/**
+ * "Receive" and unserialize a structure through the control protocol.
+ *
+ * Like @c ctl_msg_send_unserialized(), this function uses buffer to receive the
+ * incoming message.
+ *
+ * @param[in,out] input_buffer The buffer with the incoming message. Will be
+ *                             updated once the message has been unserialized to
+ *                             point to the remaining of the message or will be
+ *                             freed if all the buffer has been consumed. Can be
+ *                             @c NULL.
+ * @param[in,out] input_len    The length of the provided buffer. Will be updated
+ *                             to the length of remaining data once the message
+ *                             has been unserialized.
+ * @param expected_type        The expected message type.
+ * @param[out] t               Will contain a pointer to the unserialized structure.
+ *                             Can be @c NULL if we don't want to store the
+ *                             answer.
+ * @param mi                   The appropriate marshal structure for unserialization.
+ *
+ * @return -1 in case of error, 0 in case of success and the number of bytes we
+ *         request to complete unserialization.
+ *
+ * When requesting a notification, the input buffer is left untouched if we
+ * don't get one and we fail silently.
+ */
+size_t
+ctl_msg_recv_unserialized(uint8_t **input_buffer, size_t *input_len,
+    enum hmsg_type expected_type,
+    void **t, struct marshal_info *mi)
 {
-       char *string;
-       int string_len;
-       string = *(char **)s;
-       s += sizeof(char *);
-       memcpy(&string_len, s, sizeof(int));
+       struct hmsg_header hdr;
+       int rc = -1;
 
-       if (h->hdr.len + string_len + sizeof(int) > MAX_HMSGSIZE -
-           sizeof(struct hmsg_hdr)) {
-               LLOG_WARNX("message became too large");
-               return -1;
+       if (*input_buffer == NULL ||
+           *input_len < sizeof(struct hmsg_header)) {
+               /* Not enough data. */
+               return sizeof(struct hmsg_header) - *input_len;
        }
-       memcpy(*p, &string_len, sizeof(int));
-       *p += sizeof(int);
-       memcpy(*p, string, string_len);
-       *p += string_len;
-       h->hdr.len += sizeof(int) + string_len;
-       return sizeof(int) + string_len;
-}
 
-static int
-unpack_chars(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct, struct gc_l *pointers)
-{
-       char *string;
-       struct __attribute__ ((__packed__)) {
-               char *string;
-               int len;
-       } reals;
-       int len;
-       memcpy(&len, *p, sizeof(int));
-       *p += sizeof(int);
-       if ((string = (char *)malloc(len)) == NULL) {
-               LLOG_WARN("unable to allocate new string");
+       log_debug("control", "receive a message through control socket");
+       memcpy(&hdr, *input_buffer, sizeof(struct hmsg_header));
+       if (hdr.len > HMSG_MAX_SIZE) {
+               log_warnx("control", "message received is too large");
+               /* We discard the whole buffer */
+               free(*input_buffer);
+               *input_buffer = NULL;
+               *input_len = 0;
                return -1;
        }
-       if (ctl_alloc_pointer(pointers, string) == -1) {
-               free(string);
-               return -1;
+       if (*input_len < sizeof(struct hmsg_header) + hdr.len) {
+               /* Not enough data. */
+               return sizeof(struct hmsg_header) + hdr.len - *input_len;
        }
-       memcpy(string, *p, len);
-       *p += len;
-       reals.string = string;
-       reals.len = len;
-       memcpy(s, &reals, sizeof(reals));
-       return sizeof(char*);
-}
-
-static int
-pack_zero(struct hmsg *h, void **p, void *s,
-    const struct formatdef *ct)
-{
-       if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
-               LLOG_WARNX("message became too large");
-               return -1;
+       if (hdr.type != expected_type) {
+               if (expected_type == NOTIFICATION) return -1;
+               log_warnx("control", "incorrect received message type (expected: %d, received: %d)",
+                   expected_type, hdr.type);
+               goto end;
        }
-       memset(*p, 0, ct->size);
-       *p += ct->size;
-       h->hdr.len += ct->size;
-       return ct->size;
-}
-
-static struct formatdef conv_table[] = {
-       {'b',   1,                              0,
-        pack_copy,     unpack_copy},
-       {'w',   2,                              INT16_ALIGN,
-        pack_copy,     unpack_copy},
-       {'l',   4,                              INT32_ALIGN,
-        pack_copy,     unpack_copy},
-       {'t',   sizeof(time_t),                 TIMET_ALIGN,
-        pack_copy,     unpack_copy},
-       /* Null terminated string */
-       {'s',   sizeof(void*),                  VOID_P_ALIGN,
-        pack_string,   unpack_string},
-       /* Pointer (is packed with 0) */
-       {'P',   sizeof(void*),                  VOID_P_ALIGN,
-        pack_zero,     unpack_copy},
-       /* A list (same as pointer), should be at the beginning */
-       {'L',   sizeof(void*)*2,                VOID_P_ALIGN,
-        pack_zero,     unpack_copy},
-       /* Non null terminated string, followed by an int for the size */
-       {'C',   sizeof(void*) + sizeof(int),    VOID_P_ALIGN,
-        pack_chars,    unpack_chars},
-       {0, 0, 0, NULL, NULL}
-};
 
-/* Lists can be packed only if the "next" member is the first one of the
- * structure! No check is done for this. */
-struct fakelist_m {
-       TAILQ_ENTRY(fakelist_m)  next;
-       void *data;
-};
-TAILQ_HEAD(fakelist_l, fakelist_m);
-
-static int ctl_msg_get_alignment(char *format)
-{
-       char *f;
-       int maxalign = 0, align;
-       int paren = 0;
-       struct formatdef *ce;
-
-       /* We just want to get the maximum required alignment for the
-        * structure. Instead of going recursive, we just count parentheses to
-        * get the end of the structure. */
-       for (f = format; *f != 0; f++) {
-               if (*f == ')') {
-                       paren--;
-                       if (!paren)
-                               return maxalign;
-                       continue;
-               } else if (*f == '(') {
-                       paren++;
-                       continue;
-               } else {
-                       for (ce = conv_table;
-                            (ce->format != 0) && (ce->format != *f);
-                            ce++);
-                       align = ce->alignment;
-               }
-               if (align != 0)
-                       maxalign = (maxalign>align)?maxalign:align;
+       if (t && !hdr.len) {
+               log_warnx("control", "no payload available in answer");
+               goto end;
        }
-       if (paren)
-               LLOG_WARNX("unbalanced parenthesis in format '%s'",
-                   format);
-       return maxalign;
-}
-
-/* Define a stack of align values */
-struct stack_align {
-       SLIST_ENTRY(stack_align) next;
-       int                      align;
-};
-
-static int
-ctl_msg_packunpack_structure(char *format, void *structure, unsigned int size,
-    struct hmsg *h, void **p, struct gc_l *pointers, int pack)
-{
-       char *f;
-       struct formatdef *ce = NULL;
-       unsigned int csize = 0;
-       uintptr_t offset;
-       struct stack_align *align, *align_next;
-       int talign;
-       SLIST_HEAD(, stack_align) aligns;
-       
-       SLIST_INIT(&aligns);
-       for (f = format; *f != 0; f++) {
-               /* If we have a substructure, when entering into the structure,
-                * we get the alignment and push it to the stack. When exiting
-                * the structure, we pop the alignment from the stack and we do
-                * the padding. This means that the whole structure should be
-                * enclosed into parentheses, otherwise the padding won't
-                * occur. */
-               ce = NULL;
-               if (*f == '(') {
-                       /* We need to align, compute the needed alignment */
-                       if ((align = calloc(1,
-                                   sizeof(struct stack_align))) == NULL) {
-                               LLOG_WARN("unable to allocate memory "
-                                   "for alignment stack");
-                               goto packunpack_error;
-                       }
-                       talign = align->align = ctl_msg_get_alignment(f);
-                       SLIST_INSERT_HEAD(&aligns, align, next);
-               } else if (*f == ')') {
-                       /* We need to pad, retrieve the needed alignment */
-                       align = SLIST_FIRST(&aligns);
-                       talign = align->align;
-                       align_next = SLIST_NEXT(align, next);
-                       SLIST_REMOVE_HEAD(&aligns, next);
-                       free(align);
-               } else {
-                       for (ce = conv_table;
-                            (ce->format != 0) && (ce->format != *f);
-                            ce++);
-                       if (ce->format != *f) {
-                               LLOG_WARNX("unknown format char %c", *f);
-                               goto packunpack_error;
-                       }
-                       talign = ce->alignment;
+       if (t) {
+               /* We have data to unserialize. */
+               if (marshal_unserialize_(mi, *input_buffer + sizeof(struct hmsg_header),
+                       hdr.len, t, NULL, 0, 0) <= 0) {
+                       log_warnx("control", "unable to deserialize received data");
+                       goto end;
                }
-
-               /* Align the structure member */
-               if (talign != 0) {
-                       offset = (uintptr_t)structure % talign;
-                       if (offset != 0) {
-                               structure += talign - offset;
-                               csize += talign - offset;
-                       }
-               }
-
-               if (!ce) continue;
-
-               /* Check that the size is still ok */
-               csize += ce->size;
-               if (csize > size) {
-                       LLOG_WARNX("size of structure is too small for given "
-                           "format (%d vs %d)", size, csize);
-                       goto packunpack_error;
-               }
-               
-               /* Pack or unpack */
-               if (pack) {
-                       if (ce->pack(h, p, structure, ce) == -1) {
-                               LLOG_WARNX("error while packing %c in %s", *f,
-                                   format);
-                               goto packunpack_error;
-                       }
-               } else {
-                       if (ce->unpack(h, p, structure, ce, pointers) == -1) {
-                               LLOG_WARNX("error while unpacking %c", *f);
-                               goto packunpack_error;
-                       }
-               }
-               structure += ce->size;
-       }
-
-       if (size < csize) {
-               LLOG_WARNX("size of structure does not match its "
-                   "declaration (%d vs %d)", size, csize);
-               goto packunpack_error;
        }
-       if (!SLIST_EMPTY(&aligns)) {
-               LLOG_WARNX("format is badly balanced ('%s')", format);
-               goto packunpack_error;
-       }
-       return 0;
-
-packunpack_error:
-       for (align = SLIST_FIRST(&aligns);
-            align != NULL;
-            align = align_next) {
-               align_next = SLIST_NEXT(align, next);
-               SLIST_REMOVE_HEAD(&aligns, next);
-               free(align);
-       }
-       return -1;
-       
-}
-
-int
-ctl_msg_pack_structure(char *format, void *structure, unsigned int size,
-    struct hmsg *h, void **p)
-{
-       return ctl_msg_packunpack_structure(format, structure, size, h, p, NULL, 1);
-}
 
-int
-ctl_msg_unpack_structure(char *format, void *structure, unsigned int size,
-    struct hmsg *h, void **p)
-{
-       struct gc_l pointers;
-       int rc;
-       TAILQ_INIT(&pointers);
-       if ((rc = ctl_msg_packunpack_structure(format, structure, size,
-                   h, p, &pointers, 0)) == -1) {
-               LLOG_WARNX("unable to unpack structure, freeing");
-               ctl_free_pointers(&pointers, 0);
-               return -1;
-       }
-       ctl_free_pointers(&pointers, 1);
+       rc = 0;
+end:
+       /* Discard input buffer */
+       *input_len -= sizeof(struct hmsg_header) + hdr.len;
+       if (*input_len == 0) {
+               free(*input_buffer);
+               *input_buffer = NULL;
+       } else
+               memmove(*input_buffer,
+                   *input_buffer + sizeof(struct hmsg_header) + hdr.len,
+                   *input_len);
        return rc;
 }
-
-int
-ctl_msg_pack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
-{
-       struct fakelist_m *member;
-       struct fakelist_l *flist = (struct fakelist_l *)list;
-       TAILQ_FOREACH(member, flist, next) {
-               if (ctl_msg_pack_structure(format, member, size, h, p) == -1) {
-                       LLOG_WARNX("error while packing list, aborting");
-                       return -1;
-               }
-       }
-       return 0;
-}
-
-int
-ctl_msg_unpack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
-{
-       struct fakelist_m *member, *member_next;
-       struct gc_l pointers;
-       struct fakelist_l *flist = (struct fakelist_l *)list;
-       TAILQ_INIT(flist);
-       TAILQ_INIT(&pointers);
-       while (*p - (void *)h - sizeof(struct hmsg_hdr) < h->hdr.len) {
-               if ((member = calloc(1, size)) == NULL) {
-                       LLOG_WARN("unable to allocate memory for structure");
-                       return -1;
-               }
-               if (ctl_msg_packunpack_structure(format, member, size,
-                       h, p, &pointers, 0) == -1) {
-                       LLOG_WARNX("unable to unpack list, aborting");
-                       free(member);
-                       /* Free each list member */
-                       for (member = TAILQ_FIRST(flist);
-                            member != NULL;
-                            member = member_next) {
-                               member_next = TAILQ_NEXT(member, next);
-                               TAILQ_REMOVE(flist, member, next);
-                               free(member);
-                       }
-                       ctl_free_pointers(&pointers, 0);
-                       return -1;
-               }
-               TAILQ_INSERT_TAIL(flist, member, next);
-       }
-       ctl_free_pointers(&pointers, 1);
-       return 0;
-}