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

sock.c

/******************************************************************************

      sock.c -- create and handle listening network sockets
      Copyright (C) 2004  Wessel Dankers <wsl@uvt.nl>

      This program is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      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 Public License
      along with this program; if not, write to the Free Software
      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

      $Id: sock.c 23144 2007-12-21 15:36:48Z wsl $
      $URL: https://infix.uvt.nl/its-id/trunk/sources/fair-0.5.1/src/sock.c $

******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "fair.h"
#include "sock.h"
#include "error.h"
#include "conf.h"
#include "fd.h"

#include "sock.h"

static int ptrcmp(byte *a, byte *b) {
      return a - b;
}

static avl_tree_t listeners = {NULL, NULL, NULL, (avl_compare_t)ptrcmp, NULL};

unsigned int listener_count(void) {
      return avl_count(&listeners);
}

void listener_delete(listener_t *lst) {
      unless(lst) return;
      avl_free_nodes(&lst->fds);
      avl_unlink_node(&listeners, &lst->node);
      free(lst);
}

static listener_t *listener_new(accept_hook_t func, void *data) {
      listener_t *lst;
      lst = xalloc(sizeof *lst);
      avl_init_node(&lst->node, lst);
      avl_init_tree(&lst->fds, (avl_compare_t)ptrcmp, (avl_freeitem_t)fd_close);
      lst->func = func;
      lst->data = data;
      avl_insert_node(&listeners, &lst->node);
      return lst;
}

static void sock_accept_tcp(fd_t *fd, fd_event_t evt) {
      int stream;
      struct sockaddr_storage sa;
      socklen_t sa_len = sizeof sa;
      listener_t *lst;

      if(evt != FD_EVENT_READ)
            return;

      assert(fd);
      lst = fd->rdata;

      memset(&sa, 0, sizeof sa);
      stream = accept(fd->fd, (struct sockaddr *)&sa, &sa_len);
      if(stream == -1) {
            if(errno == EINTR)
                  return;
            syslog(LOG_ERR, "accept(%d): %m", fd->fd);
      } else {
            lst->func(lst->data, stream, (struct sockaddr *)&sa, sa_len);
      }
}

const char *listener_new_tcp(const char *port, accept_hook_t func, void *data) {
      int fd, err, backlog;
      const int on = 1;
      struct addrinfo hints = {0}, *ais = NULL, *ai;
      const char *res = NULL;
      listener_t *lst;
      fd_t *nfd;

      unless(port) return "Bad port parameter";

      hints.ai_family = PF_UNSPEC;
      hints.ai_socktype = SOCK_STREAM;
      if(!conf_LocalTCP)
            hints.ai_flags = AI_PASSIVE;

      err = getaddrinfo(NULL, port, &hints, &ais);
      if(err)
            return gai_strerror(err) ?: "unknown error";
      if(!ais)
            return "No addresses for host";

      backlog = SOMAXCONN;
      if(backlog < 5)
            backlog = 5;

      lst = listener_new(func, data);
      assert(lst);

      for(ai = ais; ai; ai = ai->ai_next) {
            fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
            if(fd == -1) {
                  res = gai_strerror(errno);
            } else {
                  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
                  if(bind(fd, ai->ai_addr, ai->ai_addrlen) || listen(fd, backlog)) {
                        res = strerror(errno);
                  } else {
                        nfd = fd_new(fd);
                        assert(nfd);
                        fd_read(nfd, sock_accept_tcp, lst);
                        avl_insert(&lst->fds, nfd);
                        continue;
                  }
                  if(close(fd) == -1)
                        syslog(LOG_ERR, "close(): %m");
            }
      }

      if(!avl_count(&lst->fds))
            return listener_delete(lst), res;

      return NULL;
}

const char *listener_new_udp(const char *port, fd_hook_t func, void *data) {
      int fd, err;
      const int on = 1;
      struct addrinfo hints = {0}, *ais = NULL, *ai;
      const char *res = NULL;
      unsigned int gelukt = 0;
      fd_t *nfd;

      unless(port) return "Bad port parameter";

      hints.ai_family = PF_UNSPEC;
      hints.ai_socktype = SOCK_DGRAM;
      hints.ai_flags = AI_PASSIVE;

      err = getaddrinfo(NULL, port, &hints, &ais);
      if(err)
            return gai_strerror(err) ?: "kansloos";

      for(ai = ais; ai; ai = ai->ai_next) {
            fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
            if(fd == -1) {
                  res = gai_strerror(errno);
            } else {
                  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
                  if(bind(fd, ai->ai_addr, ai->ai_addrlen)) {
                        res = strerror(errno);
                  } else {
                        nfd = fd_new(fd);
                        assert(nfd);
                        fd_read(nfd, func, data);
                        gelukt++;
                        continue;
                  }
                  if(close(fd) == -1)
                        syslog(LOG_CRIT, "close(): %m");
            }
      }

      return gelukt ? NULL : res;
}

Generated by  Doxygen 1.6.0   Back to index