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

listener.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: listener.c,v 1.54.2.2 2004/12/07 03:05:13 pneumatus Exp $
 */

#include "memory.h"
#include "setup.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "ssl.h"
#include "h.h"
#include "fd.h"
#include "dlink.h"
#include "user_ban.h"
#include <sys/types.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <utmp.h>
#include <time.h>

#ifdef SOMAXCONN
#undef HYBRID_SOMAXCONN
#define HYBRID_SOMAXCONN SOMAXCONN
#endif

dlink_list listener_list = DLINK_LIST_INIT;

char *get_listener_name(Listener *l)
{
      static char buf[HOSTLEN + HOSTIPLEN + 8];

      ASSERT(l != NULL);

      ircsprintf(buf, "%s[%s/%d]", me.name, (*l->ipaddr != '\0') ? l->ipaddr : "*",
            l->port);
      return buf;
}

char *get_listener_flags(Listener *l)
{
      static char buf[128];
      int idx = 0;

      ASSERT(l != NULL);

      if (l->flags & LISTEN_CLIENTONLY) {
            ADD_STRING("client_only", buf, idx);
      }
      if (l->flags & LISTEN_SERVERONLY) {
            ADD_STRING("server_only", buf, idx);
      }
#ifdef USE_OPENSSL
      if (l->flags & LISTEN_SECURE) {
            ADD_STRING("secure", buf, idx);
      }
#endif

      ASSERT(l->conf != NULL);
      if (l->conf->item.temp) {
            ADD_STRING("temporary", buf, idx);
      }

      return (idx) ? buf : NULL;
}

static void report_lerror(char *text, Listener *l)
{
      char err_buf[512], *host = get_listener_name(l);
      int err_no = get_sockerr(l->fd);

      ircsprintf(err_buf, text, host, strerror(err_no));

      Debug((DEBUG_ERROR, err_buf));
      sendto_realops_lev(DEBUG_LEV, err_buf);
      ircdlog(LOG_ERROR, err_buf);

      if (Internal.run_in_terminal) {
            fprintf(stderr, err_buf);
            fputc('\n', stderr);
      }
}

static Listener *free_listener(Listener *l)
{
      if (l->fd >= 0) {
            fd_close(l->fd);
      }
      MyFree(l);
      return NULL;
}

static void accept_connection(int fd, void *data, int engine_status_unused)
{
      Listener *l = (Listener *)data;
      struct sockaddr_in addr;
      int newfd;
      char ipaddr[HOSTIPLEN + 1];
      userBan *uban = NULL;

      ASSERT(l != NULL);

      l->lasttime = timeofday;
      if ((newfd = engine_accept(l->fd, &addr)) == -1) {
            if (errno == EMFILE) {
                  report_lerror("Couldn't accept connection on %s: %s", l);
            }
            engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
            return;
      }
      
      if (l->item.temp) {
            ircstp->is_ref++;

            send(newfd, "ERROR :This listener is closed\r\n", 33, 0);

            fd_close(newfd);
            engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
            return;
      }

      if (newfd >= (HARD_FDLIMIT - 10)) {
            ircstp->is_ref++;

            sendto_realops_lev(REJ_LEV, "Rejecting connection on listener %s (all "
                  "connections in use)", get_listener_name(l));
            send(newfd, "ERROR :All connections in use\r\n", 32, 0);

            fd_close(newfd);
            engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
            return;
      }

      inetntop(&addr.sin_addr, ipaddr, HOSTIPLEN + 1);

#ifdef USE_THROTTLE
      if (!throttle_check(ipaddr, newfd, timeofday)) {
            ircstp->is_ref++;
            fd_close(newfd);
            engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
            return;
      }
#endif

      if ((uban = ip_find_ban(&addr.sin_addr, ipaddr)) != NULL) {
            char buf[BUFSIZE];
            int len;

            ircstp->is_ref++;

            ircdlog(LOG_KILL, "zap active for *@%s", ipaddr);
            sendto_realops_lev(REJ_LEV, "zap active for *@%s", ipaddr);

            len = ircsnprintf(buf, BUFSIZE - 1, "ERROR :You has been zapped: %s\r\n", BanReason(uban));
            send(newfd, buf, len, 0);

            fd_close(newfd);
            engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
            return;
      }

      ircstp->is_ac++;
      add_connection(l, newfd);
      
      engine_set_call(l->fd, FDEV_READ, accept_connection, l, 0);
}

static Listener *add_listener(char *ipaddr, int port, unsigned short flags)
{
      Listener *l = NULL;
      struct sockaddr_in addr;
      int addrlen = sizeof(struct sockaddr_in), opt = 1;

      l = (Listener *)MyMalloc(sizeof(Listener));
      l->port = port;
      l->flags = flags;
      l->fd = -1;

      if (*ipaddr != '*') {
            strncpyzt(l->ipaddr, ipaddr, HOSTIPLEN + 1);
            if (!inetpton(l->ipaddr, &l->ip.s_addr)) {
                  report_lerror("Error converting ip to netmask for %s", l);
                  return free_listener(l);
            }
      }
      else {
            *l->ipaddr = '*';
            *(l->ipaddr + 1) = '\0';
      }
      if ((l->fd = engine_open(SOCK_STREAM, 0)) == -1) {
            report_lerror("Error opening listener socket for %s: %s", l);
            return free_listener(l);
      }
      if (l->fd >= (HARD_FDLIMIT - 10)) {
            sendto_realops("No more listeners allowed (%s)", get_listener_name(l));
            return free_listener(l);
      }
      if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt))) {
            report_lerror("Error setting SO_REUSEADDR socket option for %s: %s", l);
            return free_listener(l);
      }

      memset(&addr, '\0', addrlen);
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = (l->ip.s_addr) ? l->ip.s_addr : INADDR_ANY;
      addr.sin_port = htons(l->port);

      if (bind(l->fd, (struct sockaddr *)&addr, addrlen)) {
            report_lerror("Error binding listener socket for %s: %s", l);
            return free_listener(l);
      }
      if (listen(l->fd, HYBRID_SOMAXCONN)) {
            report_lerror("Error during listen() for %s: %s", l);
            return free_listener(l);
      }
      if (!set_non_blocking(l->fd)) {
            report_lerror("set_non_blocking() failed for %s: %s", l);
      }

      dlink_add(&listener_list, l);
      accept_connection(l->fd, l, ENGINE_OK);

      return l;
}

void close_one_listener(Listener *l, dlink_node *node)
{
      ASSERT(l != NULL);

      Debug((DEBUG_DEBUG, "Closing listener %x", l));

      dlink_del(&listener_list, l, node);
      detach_listen(l);
      free_listener(l);
}

static Listener *find_listener(char *ipaddr, int port)
{
      dlink_node *node;
      Listener *l;

      DLINK_FOREACH_DATA(listener_list.head, node, l, Listener) {
            if (l->port != port) {
                  continue;
            }
            if (!mycmp(l->ipaddr, ipaddr)) {
                  return l;
            }
      }
      return NULL;
}

void open_listeners()
{
      dlink_node *node;
      ConfigItem_listen *conf;
      Listener *l;

      DLINK_FOREACH_DATA(conf_listen_list.head, node, conf, ConfigItem_listen) {
            if ((l = find_listener(conf->ipaddr, conf->port)) == NULL) {
                  l = add_listener(conf->ipaddr, conf->port, conf->flags);
            }
            else if (conf->item.temp) {
                  if (!l->clients) {
                        close_one_listener(l, NULL);
                  }
                  continue;
            }
            if (l == NULL) {
                  Debug((DEBUG_DEBUG, "add_listener() returned NULL, skipping listener %s/%d...",
                        conf->ipaddr, conf->port));
                  continue;
            }
            attach_listen(l, conf);
            l->item.temp = 0;
      }
}

void close_listeners()
{
      dlink_node *node, *next = NULL;
      Listener *l;

      DLINK_FOREACH_SAFE_DATA(listener_list.head, node, next, l, Listener) {
            if (!l->conf->item.temp) {
                  continue;
            }
            if (l->clients) {
                  l->item.temp = 1;
                  continue;
            }
            close_one_listener(l, node);
      }
}

void attach_listener(aClient *cptr, Listener *l)
{
      ASSERT(l != NULL);
      ASSERT(cptr != NULL);
      ASSERT(cptr->localClient != NULL);

      cptr->localClient->listener = l;
      l->item.refcount++;
      l->clients++;

      Debug((DEBUG_DEBUG, "Attached listener %s/%d(%x) to client %x", l->ipaddr, l->port, l, cptr));
}

void detach_listener(aClient *cptr)
{
      Listener *l;

      ASSERT(cptr != NULL);
      ASSERT(cptr->localClient != NULL);

      if (cptr->localClient->listener == NULL) {
            return;
      }

      l = cptr->localClient->listener;
      cptr->localClient->listener = NULL;
      l->item.refcount--;
      l->clients--;

      Debug((DEBUG_DEBUG, "Detached listener %s/%d(%x) from client %x", l->ipaddr, l->port, l, cptr));

      if (!l->clients && l->item.temp) {
            close_one_listener(l, NULL);
      }
}

Generated by  Doxygen 1.6.0   Back to index