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

connauth.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: connauth.c,v 1.56.2.2 2004/12/07 03:05:08 pneumatus Exp $
 */

#include "memory.h"
#include "struct.h"
#include "common.h"
#include "config.h"
#include "sys.h"
#include "res.h"
#include "numeric.h"
#include "patchlevel.h"
#include "setup.h"
#include "ssl.h"
#include "h.h"
#include "fd.h"
#include "dlink.h"
#include "hook.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <fcntl.h>

dlink_list connauth_list = DLINK_LIST_INIT;

enum {
      CONNAUTH_DO_DNS,
      CONNAUTH_FIN_DNS,
      CONNAUTH_FAIL_DNS,
      CONNAUTH_DO_ID,
      CONNAUTH_FIN_ID,
      CONNAUTH_FAIL_ID,
      CONNAUTH_HOST_TOOLONG
};

static const struct {
      const char *str;
      int len;
} headers[] = {
      { "NOTICE AUTH :*** Looking up your hostname...\r\n", 46 },
      { "NOTICE AUTH :*** Found your hostname\r\n", 38 },
      { "NOTICE AUTH :*** Couldn't lookup your hostname\r\n", 48 },
      { "NOTICE AUTH :*** Checking ident...\r\n", 36 },
      { "NOTICE AUTH :*** Got identd response\r\n", 38 },
      { "NOTICE AUTH :*** No identd response\r\n", 37 },
      { "NOTICE AUTH :*** Your hostname is too long, using ip address\r\n", 62 }
};

#ifdef USE_OPENSSL
#define SEND_HEADER(c, n)     if (GeneralConfig.show_headers && !((c)->localClient->listener->flags & LISTEN_SECURE)) \
                              ircsend((c), headers[(n)].str, headers[(n)].len)
#else
#define SEND_HEADER(c, n)     if (GeneralConfig.show_headers) ircsend((c), headers[(n)].str, headers[(n)].len)
#endif

static inline void release_client(aClient *cptr)
{
      cptr->localClient->auth = NULL;
      Debug((DEBUG_DEBUG, "ConnectAuth: releasing client fd %d", cptr->localClient->fd));

      add_client_to_list(cptr);
      engine_read_packet(cptr->localClient->fd, cptr, ENGINE_OK);
}

static void ident_error(ConnectAuth *auth)
{
      ircstp->is_abad++;

      ASSERT(auth != NULL);

      fd_close(auth->fd);
      auth->fd = -1;

      auth->ident_connect = 0;
      auth->ident_pending = 0;

      SEND_HEADER(auth->cptr, CONNAUTH_FAIL_ID);

      if (!auth->dns_pending) {
            dlink_del_nofree(&connauth_list, NULL, &auth->self);
            release_client(auth->cptr);
            MyFree(auth);
      }
}

static char *ident_parse(char *id)
{
      int remp = 0, locp = 0;
      char *colon1, *colon2, *colon3, *comma, *remstring = id;

      if ((colon1 = strchr(remstring, ':')) == NULL) {
            return NULL;
      }
      *colon1 = '\0';
      colon1++;

      if ((colon2 = strchr(colon1, ':')) == NULL) {
            return NULL;
      }
      *colon2 = '\0';
      colon2++;

      if ((comma = strchr(remstring, ',')) == NULL) {
            return NULL;
      }
      *comma = '\0';
      comma++;

      if (!(remp = atoi(remstring))) {
            return NULL;
      }
      if (!(locp = atoi(comma))) {
            return NULL;
      }
      if (!strstr(colon1, "USERID")) {
            return NULL;
      }
      if ((colon3 = strchr(colon2, ':')) == NULL) {
            return NULL;
      }
      *colon3++ = '\0';

      return colon3;
}

static void ident_read_reply(int fd, void *data, int engine_status_unused)
{
      ConnectAuth *auth = (ConnectAuth *)data;
      char buf[129], *s = NULL, *t = NULL;
      int len, usercnt;

      ASSERT(auth != NULL);

      if (((len = recv(auth->fd, buf, 128, 0)) == -1) && engine_ignore_errno(errno)) {
            engine_set_call(fd, FDEV_READ, ident_read_reply, auth, 0);
            return;
      }

      if (len > 0) {
            buf[len] = '\0';
            if ((s = ident_parse(buf)) != NULL) {
                  t = auth->cptr->username;
                  for (usercnt = USERLEN; (*s != '\0') && (*s != '@') && (usercnt > 0); s++) {
                        if (!IsSpace(*s) && (*s != ':')) {
                              *t++ = *s;
                              usercnt--;
                        }
                  }
                  *t = '\0';
            }
      }

      fd_close(auth->fd);
      auth->fd = -1;
      auth->ident_pending = 0;

      if (s == NULL) {
            ircstp->is_abad++;
            strcpy(auth->cptr->username, "unknown");
            SEND_HEADER(auth->cptr, CONNAUTH_FAIL_ID);
      }
      else {
            ircstp->is_asuc++;
            SetGotID(auth->cptr);
            SEND_HEADER(auth->cptr, CONNAUTH_FIN_ID);
      }

      if (!auth->dns_pending) {
            dlink_del_nofree(&connauth_list, NULL, &auth->self);
            release_client(auth->cptr);
            MyFree(auth);
      }
}

static void ident_connect_callback(int fd, void *data, int engine_status)
{
      ConnectAuth *auth = (ConnectAuth *)data;
      struct sockaddr_in us, them;
      char authbuf[32];
      int ulen, tlen, alen;

      ASSERT(auth != NULL);

      if (engine_status != ENGINE_OK) {
            ident_error(auth);
            return;
      }

      tlen = ulen = sizeof(us);

      if (getsockname(auth->cptr->localClient->fd, (struct sockaddr *)&us, &ulen)
        || getpeername(auth->cptr->localClient->fd, (struct sockaddr *)&them, &tlen)) {
            ircdlog(LOG_ERROR, "ConnectAuth get{sock,peer}name error for %s", get_client_name(auth->cptr, SHOW_IP));
            ident_error(auth);
            return;
      }

      alen = ircsprintf(authbuf, "%u , %u\r\n", (unsigned int)ntohs(them.sin_port), (unsigned int)ntohs(us.sin_port));

      if (send(auth->fd, authbuf, alen, 0) == -1) {
            ident_error(auth);
            return;
      }

      auth->ident_connect = 0;
      auth->ident_pending = 1;

      ident_read_reply(auth->fd, auth, ENGINE_OK);
}

static int ident_init(ConnectAuth *auth)
{
      struct sockaddr_in addr;
      int sinlen = sizeof(struct sockaddr_in), fd = -1;

      ASSERT(auth != NULL);

      if ((fd = engine_open(SOCK_STREAM, 0)) == -1) {
            char *tmp = get_client_name(auth->cptr, SHOW_IP);
            Debug((DEBUG_ERROR, "Unable to create ConnectAuth id socket for %s", tmp));
            ircdlog(LOG_ERROR, "Unable to create ConnectAuth id socket for %s", tmp);
            ircstp->is_abad++;
            return 0;
      }

      if (fd > (HARD_FDLIMIT - 10)) {
            sendto_realops_lev(DEBUG_LEV, "Cannot allocate fd for ConnectAuth on %s",
                  get_client_name(auth->cptr, SHOW_IP));
            fd_close(fd);
            return 0;
      }

      SEND_HEADER(auth->cptr, CONNAUTH_DO_ID);

      memset(&addr, '\0', sinlen);
      getsockname(auth->cptr->localClient->fd, (struct sockaddr *)&addr, &sinlen);

      addr.sin_port = 0;
      auth->fd = fd;
      auth->ident_connect = 1;

      engine_connect_tcp(fd, auth->cptr->localClient->sockhost, 113, (struct sockaddr *)&addr,
            sinlen, ident_connect_callback, auth, GeneralConfig.connauth_timeout);

      return 1;
}

static void connauth_dns_callback(void *vauth, adns_answer *answer)
{
      ConnectAuth *auth = (ConnectAuth *)vauth;

      ASSERT(auth != NULL);
      auth->dns_pending = 0;

      if ((answer != NULL) && (answer->status == adns_s_ok)) {
            if (strlen(*answer->rrs.str) <= HOSTLEN) {
                  strncpyzt(auth->cptr->host, *answer->rrs.str, HOSTLEN + 1);
                  SEND_HEADER(auth->cptr, CONNAUTH_FIN_DNS);
            }
            else {
                  strncpyzt(auth->cptr->host, auth->cptr->localClient->sockhost, HOSTLEN + 1);
                  SEND_HEADER(auth->cptr, CONNAUTH_HOST_TOOLONG);
            }
      }
      else {
            strncpyzt(auth->cptr->host, auth->cptr->localClient->sockhost, HOSTLEN + 1);
            SEND_HEADER(auth->cptr, CONNAUTH_FAIL_DNS);
      }

      MyFree(answer);
      auth->dns_query.ptr = NULL;
      auth->dns_query.callback = NULL;

      if (!auth->ident_connect && !auth->ident_pending) {
            dlink_del_nofree(&connauth_list, NULL, &auth->self);
            release_client(auth->cptr);
            MyFree(auth);
      }
}

void connauth_init_client(aClient *cptr)
{
      ConnectAuth *auth;
      HookData hdata = HOOKDATA_INIT;

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

      hdata.sptr = cptr;
      hook_run(h_pre_connauth, &hdata);

      if (!GeneralConfig.resolve_hostnames && !GeneralConfig.check_identd) {
            release_client(cptr);
            return;
      }

      auth = (ConnectAuth *)MyMalloc(sizeof(ConnectAuth));
      auth->fd = -1;
      auth->cptr = cptr;
      auth->timeout = timeofday + GeneralConfig.connauth_timeout;
      dlink_add_node(&connauth_list, &auth->self, auth);

      cptr->localClient->auth = auth;

      if (GeneralConfig.resolve_hostnames) {
            auth->dns_query.ptr = auth;
            auth->dns_query.callback = connauth_dns_callback;

            SEND_HEADER(cptr, CONNAUTH_DO_DNS);

            dns_getaddr(&cptr->ip, &auth->dns_query);
            auth->dns_pending = 1;
      }

      if (GeneralConfig.check_identd) {
            ident_init(auth);
      }
}

static void connauth_timeout_queries()
{
      dlink_node *node, *next = NULL;
      ConnectAuth *auth;

      DLINK_FOREACH_SAFE_DATA(connauth_list.head, node, next, auth, ConnectAuth) {
            if (auth->timeout > timeofday) {
                  continue;
            }

            if (auth->fd >= 0) {
                  fd_close(auth->fd);
                  auth->fd = -1;
            }

            if (auth->ident_connect || auth->ident_pending) {
                  auth->ident_connect = 0;
                  auth->ident_pending = 0;
                  SEND_HEADER(auth->cptr, CONNAUTH_FAIL_ID);
            }

            if (auth->dns_pending) {
                  auth->dns_pending = 0;
                  dns_delete_queries(&auth->dns_query);
                  SEND_HEADER(auth->cptr, CONNAUTH_FAIL_DNS);
            }

            Debug((DEBUG_NOTICE, "AUTH/DNS timeout for %s", get_client_name(auth->cptr, SHOW_IP)));

            dlink_del_nofree(&connauth_list, NULL, &auth->self);
            release_client(auth->cptr);
            MyFree(auth);
      }
}

void connauth_init()
{
      add_event("connauth_timeout_queries", connauth_timeout_queries, NULL, 1, 1);
}

void connauth_delete_queries(aClient *cptr)
{
      ConnectAuth *auth;

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

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

      if (auth->fd >= 0) {
            fd_close(auth->fd);
            auth->fd = -1;
      }

      if (auth->dns_pending) {
            dns_delete_queries(&auth->dns_query);
      }

      dlink_del_nofree(&connauth_list, NULL, &auth->self);
      MyFree(auth);
}

Generated by  Doxygen 1.6.0   Back to index