Logo Search packages:      
Sourcecode: tcpreen version File versions  Download package

socketinfo.c

/*
 * socketinfo.c - protocol independant socket handling
 * (on top of getaddrinfo()).
 * $Id: socketinfo.c,v 1.5 2003/01/26 09:09:08 rdenisc Exp $
 */

/***********************************************************************
 *  Copyright (C) 2002-2003 Rémi Denis-Courmont.                       *
 *  This program is free software; you can redistribute and/or modify  *
 *  it under the terms of the GNU General Public License as published  *
 *  by the Free Software Foundation; version 2 of the license.         *
 *                                                                     *
 *  This program is distributed in the hope that it will be useful,    *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
 *  See the GNU General Public License for more details.               *
 *                                                                     *
 *  You should have received a copy of the GNU General Pulic License   *
 *  along with this program; if not, you can get it from:              *
 *  http://www.gnu.org/copyleft/gpl.html                               *
 ***********************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h> /* fprintf() */
#include <stdlib.h> /* realloc(), free() */
#include <string.h> /* memset() */
#include <limits.h> /* INT_MAX */
#ifdef HAVE_UNISTD_H
# include <sys/time.h>
# include <sys/types.h>
# include <unistd.h> /* fd_set, close() */
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif

#include "socketinfo.h"
#include "getaddrinfo.h"

/*
 * Creates a socket, and binds it if required.
 */
static int
socket_from_addrinfo (const struct addrinfo *info)
{
      int fd;

      if ((fd = socket (info->ai_family, info->ai_socktype,
                        info->ai_protocol)) != -1)
      {
            const int val = 1;
            setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
                        (const void *)&val, sizeof(val));
            
            /* if this socket is to be listen()ed to, binds it */
            if ((info->ai_flags & AI_PASSIVE)
             && bind (fd, info->ai_addr, info->ai_addrlen))
            {
                  close (fd);
                  fd = -1;
            }
      }
      return fd;
}

/*
 * Creates a socket connected to destname/destserv.
 * Return 0 on success, an EAI_* error code on getaddrinfo() failure,
 * -1 on other error (see errno).
 * hints, destname and destserv may be NULL.
 * AI_PASSIVE should not be set in hints->ai_flags (in such case, an
 * error will be returned).
 */
static int
socket_connect (int *fd, const struct addrinfo *hints,
               const char *destname, const char *destserv)
{
      int newfd = -1, retval;
      struct addrinfo *info, *curinfo;

      /* resolves destination */
      retval = getaddrinfo (destname, destserv, hints, &info);
      if (retval)
            return retval;

      /* tries to connect to destination */
      for (curinfo = info; (curinfo != NULL) && (newfd == -1);
            curinfo = curinfo->ai_next)
      {
            newfd = socket_from_addrinfo (curinfo);
            if (newfd != -1)
                  if (connect (newfd, curinfo->ai_addr,
                              curinfo->ai_addrlen))
                  {
                        close (newfd);
                        newfd = -1;
                  }
      }
      
      freeaddrinfo (info);

      if (newfd == -1)
            return EAI_SYSTEM; /* failure */
      
      *fd = newfd; /* success */
      return 0;
}

/*
 * Creates a table of listening sockets according to <hints>.
 * For correct operation, AI_PASSIVE has to be set in hints->ai_flags,
 * it will not work properly (it won't crash, but it will cause various
 * errors).
 * Returns 0 on success, and puts a pointer to a (-1)-terminated array
 * of socket descriptors. This array is to be freed by fd_freearray().
 * You have to close all descriptors yourself.
 * On I/O error, returns -1.
 * On getaddrinfo error, returns a EAI_* error code suitable for use
 * with gai_strerror() or socket_perror().
 */
static int *fdarray_addfd (int fd, fd_array *fdvptr, int *fdcptr);
static void fdarray_free (fd_array array);

static int
socket_listen (fd_array *arrayptr, const struct addrinfo *hints,
            const char *locname, const char *service)
{
      struct addrinfo *info, *curinfo;
      int check, fdcount;
      fd_array fdv;

      if ((locname == NULL) && (service == NULL))
            service = "0"; /* trick for dynamic port allocation when
                  neither host nor service names are set */

      if ((fdv = (int *)malloc (sizeof (int))) == NULL)
            return EAI_SYSTEM; /* not enough memory */
      fdcount = 1;
      fdv[0] = -1;

      check = getaddrinfo (locname, service, hints, &info);
      if (check)
            return check;

      for (curinfo = info; curinfo != NULL; curinfo = curinfo->ai_next)
      {
            int newfd;
            
            newfd = socket_from_addrinfo (curinfo);
            if (newfd != -1)
            {
                  if (listen (newfd, INT_MAX) == 0)
                        fdarray_addfd (newfd, &fdv, &fdcount);
                  else
                        close (newfd);
            }
      }

      freeaddrinfo (info);

      if (fdcount == 1)
      {
            fdarray_free (fdv);
            return EAI_SYSTEM;
      }
      *arrayptr = fdv;
      return 0;
}

static void
fdarray_free (fd_array array)
{
      if (array != NULL)
            free (array);
}

int
fdarray_close (fd_array array)
{
      int *fdp, fd, retval = 0;

      for (fdp = array; (fd = *fdp) != -1; fdp++)
            if (close (fd))
                  retval = -1;
      fdarray_free (array);
      return retval;
}


/*
 * Converts an array of file descriptors into a select()-able fd_set.
 * Returns the biggest fd plus one (useful for later select()),
 * or 0 if the array is empty.
 */
int
fdarraytofdset (const int *array, fd_set *set)
{
      int fd, max = -1;
      
      FD_ZERO (set);
      while ((fd = (*array)) != -1)
      {
            FD_SET (fd, set);
            if (fd > max)
                  max = fd;
            array++;
      }
      return max + 1;
}

/*
 * Adds a fd in an array of fd.
 * Returns NULL on error (*fdvptr is unchanged in this case).
 *
 * DIRTY INTERNAL FUNCTION. DO NOT USE FROM THE OUTSIDE.
 */
static int
*fdarray_addfd (int fd, fd_array *fdvptr, int *fdcptr)
{
      int c;
      fd_array v;
      v = *fdvptr;
      c = *fdcptr;
      
      v = (fd_array)realloc (v, (++c) * sizeof (int));
      if (v == NULL)
            return NULL;

      v[c-1] = -1;
      v[c-2] = fd;

      *fdvptr = v;
      *fdcptr = c;
      return v;
}

/*
 * Finds the first file descriptor in an array that is found in a set,
 * removes it from the set and returns it.
 * Returns -1 if none were found.
 *
 * This function could be optimized if depending on how fd_set is
 * defined.
 *
 * This is mainly for use after select() to determine which fd were
 * returned.
 */
int
fdarray_dequeuefromset (const int *array, fd_set *set)
{
      int fd;

      for (fd = array[0]; fd != -1; fd = *(array++))
      {
            if (FD_ISSET (fd, set))
            {
                  FD_CLR (fd, set);
                  return fd;
            }
      }
      return -1;
}

/*
 * Accepts a connection from a table of socket assumed to all be
 * listening.
 * Returns the newly created socket, or -1 on error.
 */
int
fdarray_accept (const fd_array array)
{
      fd_set set;
      int maxfd, fd = -1;

      maxfd = fdarraytofdset (array, &set);

      if (select (maxfd, &set, NULL, NULL, NULL) > 0)
      {
            const int val = 1;
            
            fd = accept( fdarray_dequeuefromset (array, &set), NULL, 0);
            if (fd != -1)
                  setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
                              (const void *)&val, sizeof (val));
      }
            
      return fd;
}

/*
 * Displays a socket error message.
 */
void
socket_perror(int num, const char *nodename, const char *service)
{
      if (num == EAI_SYSTEM)
      {
            fprintf (stderr, "[%s]:", (nodename != NULL) ? nodename
                                              : _("any"));
            perror ((service != NULL) ? service : _("any"));
      }
      else
            fprintf (stderr, "[%s]:%s: %s\n",
                  (nodename != NULL) ? nodename : _("any"),
                  (service != NULL) ? service : _("any"),
                  gai_strerror (num));
}

int
sockhostinfo_listen(fd_array *array, const struct sockhostinfo *hostinfo)
{
      struct addrinfo hints;
      const char *nodename, *servicename;

      memset(&hints, 0, sizeof(hints));
      hints.ai_flags = AI_PASSIVE;
      hints.ai_family = hostinfo->family;
      hints.ai_socktype = hostinfo->socktype;
      hints.ai_protocol = hostinfo->protocol;

      nodename = hostinfo->hostname;
      if (!nodename[0])
            nodename = NULL;
      servicename = hostinfo->service;
      if (!servicename[0])
            servicename = NULL;

      return socket_listen(array, &hints, nodename, servicename);
}

int
sockhostinfo_connect(int *fd, const struct sockhostinfo *hostinfo)
{
      struct addrinfo hints;
      const char *nodename, *servicename;

      memset(&hints, 0, sizeof(hints));
      hints.ai_family = hostinfo->family;
      hints.ai_socktype = hostinfo->socktype;
      hints.ai_protocol = hostinfo->protocol;

      nodename = hostinfo->hostname;
      if (!nodename[0])
            nodename = NULL; /* (nodename = "") => (nodename = NULL) */
      servicename = hostinfo->service;
      if (!servicename[0])
            servicename = NULL;

      return socket_connect(fd, &hints, nodename, servicename);
}

void
sockhostinfo_perror(int errnum, const struct sockhostinfo *info)
{
      const char *nodename, *servicename;

      nodename = info->hostname;
      if (!nodename[0])
            nodename = NULL;
      servicename = info->service;
      if (!servicename[0])
            servicename = NULL;
      socket_perror(errnum, nodename, servicename);
}

#ifdef _WINSOCKAPI_
/*
 * Bogus Winsock API functions clear previously set error number when they
 * succeed so that is a pain to trace an error value.
 */
int winstub_close (int fd)
{
      int errval, retval;

      errval = WSAGetLastError ();
      retval = closesocket ((SOCKET)fd);
      if (errval)
            WSASetLastError (errval);
      return retval;
}
#endif /* ifdef _WINSOCKAPI_ */


Generated by  Doxygen 1.6.0   Back to index