/*
 * Copyright (c) 1990,1993 Regents of The University of Michigan.
 * Copyright (c) 1999-2000 Adrian Sun.
 * All Rights Reserved. See COPYRIGHT.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>

#ifdef __svr4__
#include <sys/sockio.h>
#endif

#include <sys/types.h>

#include <atalk/util.h>

/* allocation size for interface list. */
#define IFACE_NUM 5

/*! we leave all of the ioctl's to the application */
static int addname(char **list, int *i, const char *name)
{
    /* if we've run out of room, allocate some more. just return
     * the present list if we can't. */
    if ((list[*i] = strdup(name)) == NULL) {
        return -1;
    }

    (*i)++;
    list[*i] = NULL; /* zero out the next entry */
    return 0;
}


static int getifaces(const int sockfd, char ***list)
{
#ifdef HAVE_IFNAMEINDEX
    struct if_nameindex *ifstart, *ifs;
    int i = 0;
    char **new;
    ifs = ifstart = if_nameindex();
    new = (char **) malloc((sizeof(ifs) / sizeof(struct if_nameindex) + 1) * sizeof(
                               char *));

    while (ifs && ifs->if_name) {
        /* just bail if there's a problem */
        if (addname(new, &i, ifs->if_name) < 0) {
            break;
        }

        ifs++;
    }

    if_freenameindex(ifstart);
    *list = new;
    return i;
#else
    struct ifconf	ifc;
    struct ifreq	ifrs[64], *ifr, *nextifr;
    int			ifrsize, i = 0;
    char **new;

    if (!list) {
        return 0;
    }

    memset(&ifc, 0, sizeof(struct ifconf));
    ifc.ifc_len = sizeof(ifrs);
    memset(ifrs, 0, sizeof(ifrs));
    ifc.ifc_buf = (caddr_t)ifrs;

    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
        return 0;
    }

    new = (char **) malloc((ifc.ifc_len / sizeof(struct ifreq) + 1) * sizeof(
                               char *));

    for (ifr = ifc.ifc_req; ifc.ifc_len >= (int) sizeof(struct ifreq);
            ifc.ifc_len -= ifrsize, ifr = nextifr) {
#ifdef BSD4_4
        ifrsize = sizeof(ifr->ifr_name) +
                  (ifr->ifr_addr.sa_len > sizeof(struct sockaddr)
                   ? ifr->ifr_addr.sa_len : sizeof(struct sockaddr));
#else /* !BSD4_4 */
        ifrsize = sizeof(struct ifreq);
#endif /* BSD4_4 */
        nextifr = (struct ifreq *)((caddr_t)ifr + ifrsize);

        /* just bail if there's a problem */
        if (addname(new, &i, ifr->ifr_name) < 0) {
            break;
        }
    }

    *list = new;
    return i;
#endif
}


/*!
 * @brief Get interfaces from the kernel.
 * @note we keep an extra null entry to signify the end of the interface list.
 */
char **getifacelist(void)
{
    char **list = NULL; /* FIXME */
    int  i, fd;

    if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        return NULL;
    }

    if ((i = getifaces(fd, &list)) == 0) {
        free(list);
        close(fd);
        return NULL;
    }

    close(fd);
    return list;
}


/*! go through and free the interface list */
void freeifacelist(char **ifacelist)
{
    char *value, **list = ifacelist;

    if (!ifacelist) {
        return;
    }

    while ((value = *list++)) {
        free(value);
    }

    free(ifacelist);
}
