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

net.c

#define _GNU_SOURCE

#define BSD_COMP

#include "config.h"

#include <stdio.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include "net.h"
#include "servlink.h"
#include "log.h"
#include "util.h"
#include "irc.h"
#include "timer.h"
#include "types.h"
#include "dns.h"

static int p_csock, p_ssock, p_bsock;
#ifdef HAVE_IPV6
static struct sockaddr_in6 p_cinaddr, p_sinaddr;
#else
static struct sockaddr_in p_cinaddr, p_sinaddr;
#endif

extern iac_server_configuration_t iac_cfg;
extern iac_server_status_t iac_status;

extern i32 iac_msg_cache[IAC_MAX_SERVER_CONNECTIONS][IAC_MSGCACHE_SIZE];

extern fd_set iac_rfds;
extern int iac_serv_closed;
extern int iac_client_closed;
extern rl_list_t *iac_client_conns;
extern rl_list_t *iac_server_conns;

#ifndef HAVE_IPV6
extern struct dns_resolver resolvers[];
#endif

void
iac_sbsock(void)
{
#ifndef HAVE_IPV6
      int i;
#endif  
      rl_list_item_t *li;

      p_bsock = p_csock;

    if (p_ssock > p_bsock)
        p_bsock = p_ssock;

      li = iac_client_conns->rl_list_start;
      while (li) {
            iac_client_conn_t *cc = (iac_client_conn_t *) li->rli_data;
            if (cc->sock > p_bsock)
            p_bsock = cc->sock;
            li = li->rli_next;
      }

    li = iac_server_conns->rl_list_start;
    while (li) {
        iac_server_conn_t *sc = (iac_server_conn_t *) li->rli_data;
        if (sc->sock > p_bsock)
            p_bsock = sc->sock;
        li = li->rli_next;
    }
#ifndef HAVE_IPV6
      for(i = 0; i < IAC_MAX_DNS_RESOLVERS; i++)
            if(resolvers[i].s1 - 1 > p_bsock)
                  p_bsock = resolvers[i].s1 - 1; 
#endif
}

int
p_listen_on_port(int port, struct sockaddr *addr, socklen_t addrlen)
{
      int sock, on;

#ifdef HAVE_IPV6

      memset(addr, 0, sizeof(struct sockaddr_in6));

      if ((sock = socket(PF_INET6, SOCK_STREAM, 0)) < 0)
            iac_log(IAC_ERR, "Unable to create socket: %s\n", strerror(errno));

      ((struct sockaddr_in6 * ) addr)->sin6_family = AF_INET6;
      ((struct sockaddr_in6 * ) addr)->sin6_port = htons(port);

    if ( iac_cfg.interface[0] )
    {
        /*
         * FIXME: ipv4 address will not be parsed correctly
         */

        iac_log ( IAC_VERBOSE, "[   ] - Binding to interface %s\n", iac_cfg.interface );
        if ( inet_pton ( AF_INET6, 
                    iac_cfg.interface,
                    &((struct sockaddr_in6 * ) addr)->sin6_addr ) < 0 )
        {
            iac_log ( IAC_ERR, "Unable to convert address: %s :%s\n",
                    iac_cfg.interface, strerror(errno));
        }
    }
    else
        ((struct sockaddr_in6 * ) addr)->sin6_addr = in6addr_any;

#else
    
      memset(addr, 0, sizeof(struct sockaddr_in));

      if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
            iac_log(IAC_ERR, "Unable to create socket: %s\n", strerror(errno));

      ((struct sockaddr_in * ) addr)->sin_family = AF_INET;
      ((struct sockaddr_in * ) addr)->sin_port = htons(port);

    if ( iac_cfg.interface[0] )
    {
        iac_log ( IAC_VERBOSE, "[   ] - Binding to interface %s\n", iac_cfg.interface );
        if ( inet_pton ( AF_INET, 
                    iac_cfg.interface,
                    &((struct sockaddr_in * ) addr)->sin_addr ) < 0 )
        {
            iac_log ( IAC_ERR, "Unable to convert address: %s :%s\n",
                    iac_cfg.interface, strerror(errno));
        }
    }
    else
        ((struct sockaddr_in * ) addr)->sin_addr.s_addr = INADDR_ANY;

#endif

      on = 1;
      setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

      if (bind(sock, addr, addrlen) < 0)
            iac_log(IAC_ERR, "Unable to bind to socket: %s\n", strerror(errno));

      if (listen(sock, SOMAXCONN) < 0)
            iac_log(IAC_ERR, "Unable to listen on port: %s\n", strerror(errno));

      FD_SET(sock, &iac_rfds);

      return sock;
}

void
p_new_client_conn(void)
{
      iac_client_conn_t *cc;
    int n = 0;
#ifndef HAVE_IPV6
    /* FIXME */
    rl_list_item_t *li;
#else
    pid_t pid;
#endif

    /*
     * allocate client handle
     */
      if (!(cc = (iac_client_conn_t *) malloc(sizeof(iac_client_conn_t))))
            iac_log(IAC_ERR, "Unable to allocate mempry for client connection\n");
      memset(cc, 0, sizeof(iac_client_conn_t));

    /*
     * mark as local connection
     */
      cc->flags |= IAC_LOCAL;

#ifdef IAC_COUNT_IDLETIME
    /*
     * remeber signon time for further use
     */
    cc->signon = time(0);
    cc->last_action = cc->signon;
#endif

    cc->server = iac_cfg.servername;
    cc->server_info = iac_cfg.info;

    iac_status.local_connections++;
    iac_status.total_connections++;

    /*
     * accept connection
     */
      errno = 0;
      if ((cc->sock = accept(p_csock, (struct sockaddr *) &cc->addr,
            &cc->addrlen)) < 0) {

            free(cc);
            iac_log(IAC_WARN, "accept() failed: %s\n", strerror(errno));
            return;
      }

    
    /*
     * DoS protection against mass connection attempts. The connection is
     * established but not initialized (i.e. NICK and USER arent sent yet.)
     * Thus we just limit connections in that state. Choose the value in
     * config.h wisely, a too low value will prevent users from logging in
     * if you run a high traffic ircd.
     *
     * FIXME: implement as config value
     */
    if ( iac_status.waiting >= IAC_MAX_WAITING ) {
        iac_net_client_send(cc, "ERROR: Too many connection attemps. " \
            "Disconnecting.\r\n");
        close(cc->sock);
        free(cc);
        return;
    }

#ifdef HAVE_IPV6
    cc->addrlen = sizeof(struct sockaddr_in6);
#else
    cc->addrlen = sizeof(struct sockaddr_in);
#endif


    /*
     * get address of peer (the client)
     */
      errno = 0;
      if (getpeername(cc->sock, (struct sockaddr *) &cc->addr,
            &cc->addrlen) < 0) {
            free(cc);
            iac_log(IAC_WARN, "getpeername() failed: %s\n", strerror(errno));
            return;
      }


    /*
     * save ip address and cut it
     */
    if ( !(cc->host = (char *) calloc( IAC_HOSTLEN+1, 1 )) )
        iac_log(IAC_ERR, "Unable to allocate memory for server handle\n");
        

#ifdef HAVE_IPV6
    if (!inet_ntop ( AF_INET6, (char *) &cc->addr.sin6_addr, cc->host, IAC_HOSTLEN ))
    {
        iac_log(IAC_ERR, "inet_ntop failed: %s\n", strerror(errno));
    }


    cc->host[IAC_HOSTLEN] = '\0';

#ifndef NO_CLIENT_HACKS
    if ( IN6_IS_ADDR_V4MAPPED ( &cc->addr.sin6_addr ) ) {

        if ( strlen(cc->host) > 7 ) {
            cc->ip = strdup ( cc->host + 7 );
            free(cc->host);
            cc->host = strdup(cc->ip);
        } else {
            asprintf(&cc->ip, "0:0:0:0:0%s", cc->host+1);
            free(cc->host);
            cc->host = strdup(cc->ip);
        }
    } else
#endif
    if ( *cc->host == ':' ) {
        asprintf(&cc->ip, "0:0:0:0:0%s", cc->host+1);
        free(cc->host);
        cc->host = strdup(cc->ip);
    } else {
        cc->ip = strdup(cc->host);
    }

    cc->port = ntohs(cc->addr.sin6_port);

#else
    if (!inet_ntop ( AF_INET, (char *) &cc->addr.sin_addr, cc->host, IAC_HOSTLEN ))
    {
        iac_log(IAC_ERR, "inet_ntop failed: %s\n", strerror(errno));
    }

    cc->host[IAC_HOSTLEN] = '\0';

    cc->ip = strdup(cc->host);

    cc->port = ntohs(cc->addr.sin_port);
#endif


    /*
     * DoS protection against mass connection attemps from the same host,
     * also called clones. Even a high value will prevent from most DoS
     * attack because most attackers are poor and use the same source ip
     * address for all their connections.
     *
     * the piece of code  below is dedicated to tyr, a good friend,
     * who doesnt understand why this code is not crap. ;)
     */
#ifdef HAVE_IPV6
    /* FIXME */
#else
    for (li = iac_client_conns->rl_list_start, n = 0; li; li = li->rli_next)
        if (!strcmp(cc->host, inet_ntoa(((iac_client_conn_t *) li->rli_data)->addr.sin_addr)))
            n++;
#endif

    if ( n >= IAC_MAX_CLONES ) {
        iac_net_client_send(cc, "ERROR: Too many clones. Disconnecting.\r\n");
        close(cc->sock);
        free(cc->host);
        free(cc);
        return;
    }
    


    iac_status.waiting++;


    /*
     * add client to the list of clients
     */
      rl_list_add(iac_client_conns, (void *) cc, FALSE);

      FD_SET(cc->sock, &iac_rfds);
      iac_sbsock();

#ifdef HAVE_IPV6
  
    /*
     * create pipe to allow communication to the async lookup child
     */
    pipe(cc->pipe);

    /*
     * fork child process to lookup ip address
     */
    pid = fork();

    if (pid == 0) {
        /*
         * inside of child process, do the lookup
         */
        struct hostent * hostinfo = gethostbyaddr((char *) &cc->addr.sin6_addr,
            sizeof(struct in6_addr), AF_INET6);
#if 0      
        struct hostent * hostinfo = gethostbyaddr((char *) &cc->addr.sin_addr,
            sizeof(struct in_addr), AF_INET);
#endif

        /*
         * write hostname to pipe if successfull
         */
        if (hostinfo) {
            write(cc->pipe[1], hostinfo->h_name, strlen(hostinfo->h_name)+1);
            _exit(0);
        } else {
            _exit(errno);
        }
    } else {
        /*
         * parent process, save child pid
         */
        cc->child_pid = pid;

        iac_log(IAC_DEBUG, "[   ] C fork() (%d)\n", pid);
    }
#endif

    /*
     * add lastlog entry
     */
    if ( iac_cfg.flags & IAC_LASTLOG ) {
        iac_lastlog_t *ll = (iac_lastlog_t *) malloc(sizeof(iac_lastlog_t));
        memset(ll, 0, sizeof(iac_lastlog_t));

        ll->signon = cc->signon;
        ll->ip = strdup(cc->ip);
        ll->port = cc->port;

        iac_cfg.lastlog_entries++;

        /*
         * link previous entry to this one
         */
        if (iac_cfg.lastlog)
            iac_cfg.lastlog->prev = ll;

        /*
         * link this entry to the previous one
         */
        ll->next = iac_cfg.lastlog;

        /*
         * point lastlog to new last entry
         */
        iac_cfg.lastlog = ll;

        if (!iac_cfg.lastlog_tail)
            iac_cfg.lastlog_tail = ll;

        /*
         * check if need to delete the oldest entry
         */
        if (iac_cfg.lastlog_entries >= IAC_MAX_LASTLOG) {
            if (iac_cfg.lastlog_tail) {
                if ( iac_cfg.lastlog_tail->prev ) {
                    iac_lastlog_t *lt;

                    iac_cfg.lastlog_tail->prev->next = NULL;
                    lt = iac_cfg.lastlog_tail;
                    iac_cfg.lastlog_tail = iac_cfg.lastlog_tail->prev;

                    RL_FREE(lt->ip);
                    RL_FREE(lt->hostname);
                    RL_FREE(lt->nick);
                    RL_FREE(lt->username);
                    RL_FREE(lt->ircname);
                    RL_FREE(lt);

                    iac_cfg.lastlog_entries--;
                }
            }
        }

    }

      iac_log(IAC_VERBOSE, "[***] C New client connection (%s:%d)\n",
        cc->host, cc->port);
}

void
p_new_server_conn(void)
{
    iac_server_conn_t *sc;

    /*
     * allocate some memory
     */
    if ( !(sc = (iac_server_conn_t *) malloc(sizeof(iac_server_conn_t))) )
        iac_log(IAC_ERR, "Unable to allocate memory for server handle\n");
    memset(sc, 0, sizeof(iac_server_conn_t));

    /*
     * accept connection
     */
    errno = 0;
    if ((sc->sock = accept(p_ssock, (struct sockaddr *) &(sc->addr),
            &(sc->addrlen) )) < 0) {
        free(sc);
        iac_log(IAC_WARN, "accept() failed: %s\n", strerror(errno));
        return;
    }


    /*
     * DoS protection: limit maximum server connections
     */
    if (iac_status.server_connections >= IAC_MAX_SERVER_CONNECTIONS) {
        close(sc->sock);
        free(sc);
        iac_log(IAC_WARN, "Server limit reached\n");
        return;
    }

    /*
     * get address of peer (the client)
     */
      errno = 0;
      if (getpeername(sc->sock, (struct sockaddr *) &sc->addr, &sc->addrlen) < 0) {
            free(sc);
            iac_log(IAC_WARN, "getpeername() failed: %s\n", strerror(errno));
            return;
      }

    /*
     * save ip address and cut it
     */
    sc->ip = strdup(inet_ntoa(sc->addr.sin_addr));
    if ( strlen(sc->ip) > IAC_HOSTLEN )
        sc->ip[IAC_HOSTLEN] = '\0';

    sc->port = ntohs(sc->addr.sin_port);

    sc->time_init = time(0);


      FD_SET(sc->sock, &iac_rfds);

    /*
     * add connection to server list
     */
    rl_list_add(iac_server_conns, (void *) sc, FALSE);

      iac_sbsock();


    iac_log(IAC_VERBOSE, "[***] S New server connection (%s:%d)\n",
        (sc->ip) ? sc->ip : "(null)", sc->port);
}

void
iac_net_close_client_conn(void *data)
{
    iac_channel_link_t *cl, *next;
      iac_client_conn_t *cc = (iac_client_conn_t *) data;


    if (!cc->quitmsg) {
        cc->quitmsg = strdup("EOT");
    }
    
    /*
     * send quit messags
     */
    iac_irc_real_quit(cc);

    /*
     * close and remove socket
     */
    if (cc->flags & IAC_LOCAL)
    {
        FD_CLR(cc->sock, &iac_rfds);
        close(cc->sock);

        if ( cc->flags & IAC_SUICIDE || !(cc->flags & IAC_KILLED) ) {
            iac_log(IAC_VERBOSE, "[   ] C Enabling client closed flag\n");
            iac_client_closed = 1;
        }

        /*
         * in case the user disconnects before sends a proper NICK and
         * USER command the connection is still in waiting state. we
         * have to decrease the counter because the connection is no
         * longer active
         */
        if ( !(cc->flags & IAC_OK) ) {
            if (iac_status.waiting > 0)
                iac_status.waiting--;
        }
    }

      iac_log(IAC_DEBUG, "[***] C Lost client connection [%s] (%s:%d)\n",
            (cc->hostmask) ? cc->hostmask : "(null)", 
            (cc->host) ? cc->host : "(null)", 
            cc->port);

    if ( cc->flags & IAC_LOCAL ) {
        /*
         * remove user from user list
         */
        rl_list_rem(iac_client_conns, cc);

        iac_sbsock();
        
        iac_status.local_connections--;
    }

    iac_status.total_connections--;

    /*
     * free stuff
     */
    cl = cc->channels;
    while (cl) {
        next = cl->next;
        RL_FREE(cl);
        cl = next;
    }

      /*
       * when there's a lookup in progress for this client
       * then kill and free the resolver, because when it
       * would terminate it would reference to the free'd
       * client.
       */
      if(cc->resolver)
            dns_free(cc->resolver);

      RL_FREE(cc->nick)
      RL_FREE(cc->user)
      RL_FREE(cc->host)
    RL_FREE(cc->ip);
      RL_FREE(cc->hostmask)
      RL_FREE(cc->ircname)
    RL_FREE(cc->away)
    RL_FREE(cc->quitmsg);
    RL_FREE(cc->msg);

    free(cc);
}

void
iac_net_close_server_conn(void *data)
{
    iac_link_line_t *ll;
    iac_server_conn_t *sc = (iac_server_conn_t *) data;

    /*
     * close socket and stuff
     */
    FD_CLR(sc->sock, &iac_rfds);
    close(sc->sock);

    iac_log(IAC_VERBOSE, "[   ] S Enabling server closed flag\n");
    iac_serv_closed = 1;

    iac_log(IAC_VERBOSE, "[***] S Lost server connection (%s:%d)\n",
        sc->ip, sc->port);
    iac_irc_notice_ops("Lost server connection (%s:%d)\r\n",
        sc->ip, sc->port);


    /*
     * remove connection from server list
     */
    rl_list_rem(iac_server_conns, sc);

    /*
     * XXX: send SQUIT to other servers
     */

    iac_sbsock();

    /*
     * clear message id cache for this server numeric
     */
    memset(&iac_msg_cache[sc->numeric][0], 0, IAC_MSGCACHE_SIZE);

    ll = iac_cfg.links;
    while ( ll ) {

        if ( ll->fd == sc->sock ) {

            /*
             * only retry on the master side
             */
            if (sc->flags & IAC_MASTER) {
                struct timeval tv;


                if (ll->flags & IAC_SLOWRETRY ) {
                    /*
                     * Longer retry. the fast retry didnt help
                     */
                    tv.tv_sec = IAC_SERVLINK_RETRY_TIME;
                    tv.tv_usec = 0;
                } else {

                    /*
                     * A fast retry, maybe the other host just restarted
                     *
                     * To avoid too many 1second retries we set SLOWRETRY, we'll
                     * reenable fast retry we disable slow retry in 10 seconds if
                     * the connection is established then
                     */
                    ll->flags |= IAC_SLOWRETRY;

                    tv.tv_sec = 10;
                    tv.tv_usec = 0;
                    ti_add_callback(&tv, iac_servlink_slowretry, ll->host);

                    tv.tv_sec = 1;
                    tv.tv_usec = 0;
                }

                ti_add_callback(&tv, iac_servlink_retry, ll->host);
            }

            ll->flags &= ~(IAC_CONNECTED);
            ll->flags &= ~(IAC_MASTER);
            ll->flags |= IAC_RETRY;
            break;
        }

        ll = ll->next;
    }
            

    RL_FREE(sc->msg);
    RL_FREE(sc->omsg);
    RL_FREE(sc->hostname);
    RL_FREE(sc->ip);
    RL_FREE(sc->info);

    free(sc);

    /*
     * sync with other servers
     */
    iac_servlink_start_sync();

}

void
p_handle_client_data(void *data)
{
      iac_client_conn_t *cc = (iac_client_conn_t *) data;
      iac_irc_parse(cc);
}

void
p_handle_server_data(void *data)
{
    iac_server_conn_t *sc = (iac_server_conn_t *) data;
    iac_servlink_parse(sc);
}


/*
 * reads data from the given socket. concats the new data to the given
 * char array pointer and calls the handle_func for each substring
 * delimited by \r\n. close_func is called if the other side has closed
 * the connection.
 */
void
p_read( int sock,
        char **msg,
            void (*close_func)(void *),
        void (*handle_func)(void *),
        void *data,
        char *host,
        int port,
        unsigned int *msg_counter,
        unsigned int *byte_counter,
        char type,
        int *closed)
{
      int i, f, n, r;
    char buf[128];

      memset(buf, 0, sizeof(buf) );

    /*
     * read from socket, call close function pointer if the
     * other side has closed the connection
     */
      if ( !(r = recv(sock, buf, sizeof(buf)-1, 0)) ) {
            close_func(data);
      } else {
            buf[sizeof(buf)-1] = '\0';

        *byte_counter += r;

        iac_log(IAC_RAW, "[<--] %c (%s:%d) %s$\n", type,
                host ? host : "unknown" , port, buf);

        /*
         * until all complete commands are parsed
         */
            f = 1;
            while (f) {
                  f = 0;

                  /*
             * look for end of command "\r\n"
             */
                  for (i=0; i < sizeof(buf); i++)  {
                        if ( buf[i] == '\r' || buf[i] == '\n') {
                              f = 1;
                              break;
                        }
                  }

                  if (f) {
                        /* 
                         * end of command found, mark end, parse it and
                         * look for next command
                         */
                        buf[i] = '\0';

                iac_astrcat(msg, buf);

                *msg_counter += 1;

                iac_log ( IAC_RAW, "[---] %s\n", *msg );

                        handle_func(data);

                /*
                 * stop parsing if the connection was closed
                 * during the parsing of the last message
                 */
                if ( *closed ) {
                    iac_log(IAC_VERBOSE, "[   ] * Escaping out of p_read\n");
                    return;
                }

                RL_FREE(*msg);

                        i++;
                        if (buf[i] == '\n') i++;

                        for (n=0; i < sizeof(buf); i++)
                              buf[n++] = buf[i];

                        memset(&buf[n+1], 0, (sizeof(buf) - n - 1) );
                  } else {
                iac_astrcat(msg, buf);

                /*
                 * DoS protection. Some dude could end up sending us endless
                 * data without any \r or \n and let us allocate memory again
                 * and again until we run out of it.
                 *
                 * Note: There is still a problem. The line per minute limit
                 * will probably be implemented later in the flow. so this
                 * protection makes the line per minute protection useless in
                 * some cases.
                 */
                if ( strlen(*msg) > IAC_PARSELEN ) {
                    iac_log(IAC_WARN, "Cutted of too long line\n");
                    RL_FREE(*msg);
                }
                  }
            }
      }
}

void
p_read_client_conn(iac_client_conn_t *cc)
{
    static unsigned int u1, u2;
    p_read( cc->sock,
           &cc->msg,
               &iac_net_close_client_conn,
           &p_handle_client_data,
           (void *) cc,
            cc->host,
            cc->port,
            &u1,
            &u2,
            'C',
            &iac_client_closed
          );
            
}

void
p_read_server_conn(iac_server_conn_t *sc)
{
    p_read( sc->sock,
           &sc->msg,
           &iac_net_close_server_conn,
           &p_handle_server_data,
           (void *) sc,
           sc->ip,
           sc->port,
           &(sc->msgs_recvd),
           &(sc->bytes_recvd),
           'S',
           &iac_serv_closed
          );
}

void
iac_net_start(int cport, int lport)
{
      FD_ZERO(&iac_rfds);

      iac_client_conns = rl_list_new();
    iac_server_conns = rl_list_new();

      p_csock = p_listen_on_port(cport, (struct sockaddr *) &p_cinaddr, sizeof(p_cinaddr) );

    iac_log(IAC_VERBOSE, "[   ] C Accepting connections (%d)\n", cport);

    p_ssock = p_listen_on_port(lport, (struct sockaddr *) &p_sinaddr, sizeof(p_sinaddr) );
    iac_log(IAC_VERBOSE, "[   ] S Accepting connections (%d)\n", lport);

      iac_sbsock();
}


void
iac_net_stop(void)
{
      close(p_csock);
    close(p_ssock);

    if (iac_server_conns)
        rl_list_del(iac_server_conns);

      if (iac_client_conns)
            rl_list_del(iac_client_conns);
}

#ifndef HAVE_IPV6
void
iac_lookup_start(fd_set *rfds, fd_set *wfds)
{
      int i, r = 0;
      rl_list_item_t *li = iac_client_conns->rl_list_start;
  
      /*
       * look for not resolved clients
       */         
      while(li) {
            iac_client_conn_t *cc = (iac_client_conn_t *) li->rli_data;
            li = li->rli_next;

            if(cc->lookup != IAC_DNS_NONE) continue;

            /*
             * try to find a free resolver
             */                 
            for(i = 0; i < IAC_MAX_DNS_RESOLVERS; i++)
            
                  if( dns_is_idle(&resolvers[i]) ) {

                        dns_init(&resolvers[i]);
                        dns_set_userarg(&resolvers[i], cc);

                        if(dns_addr_lookup(&resolvers[i], cc->addr.sin_addr)) {
                              iac_log(IAC_WARN, "Could not start reverse lookup\n");
                        } else {
                              iac_log(IAC_DEBUG, "[   ] Starting lookup for %s\n",
                                                (cc->host) ? cc->host : "(null)");
                              cc->lookup = IAC_DNS_BUSY;
                              cc->resolver = &resolvers[i];
                        }

                        break;                          
                  }
            /*
             * stop looping through clients when there is no free resolver
             */
            if(i == IAC_MAX_DNS_RESOLVERS) {
                  iac_log(IAC_DEBUG, "[   ] Ran out of idle resolvers.\n");
                  break;
            }
      }
  
      for(i = 0; i < IAC_MAX_DNS_RESOLVERS; i++)
            r += dns_pre_select(&resolvers[i], rfds, wfds);
      /*
       * only search for highest fd when fd_set has been changed
       */
      if(r) iac_sbsock();
}
#endif /* HAVE_IPV6 */

void
iac_net_handle(void)
{
      fd_set f;
      struct timeval t;
      rl_list_item_t *li, *prev, *next;
      int status;
      pid_t pid;
#ifndef HAVE_IPV6
      fd_set wfds;
      int i;
#endif

    /*
     * get optimal timeout value from timer code
     * 
     * FIXME: dns code should also have influence on this value
     *        for more accurate timeouts.
     */
      ti_get_timeout(&t);
      f = iac_rfds;

#ifndef HAVE_IPV6  
      FD_ZERO(&wfds);
      iac_lookup_start(&f, &wfds);
      errno = 0;
      switch (select(p_bsock + 1, &f, &wfds, NULL, &t))
#else
      errno = 0;
      switch (select(p_bsock + 1, &f, NULL, NULL, &t))
#endif    
      {
            case 0:
            /*
             * timeout, this means a timer needs to be called
             */
                  ti_call();
                  break;

            case -1:
                  if (errno != EINTR)
                        iac_log(IAC_ERR, "select() failed: %s\n", strerror(errno));
                  break;

            default:
                  /*
                   * check for new client connections
                   */
                  if (FD_ISSET(p_csock, &f))
                        p_new_client_conn();

            
            /*
             * check for new server connections
             */
            if (FD_ISSET(p_ssock, &f))
                p_new_server_conn();

                  /*
                   * check for data on client connections
                   */
            prev = NULL;
                  li = iac_client_conns->rl_list_start;
                  while (li) {
                        iac_client_conn_t *cc = (iac_client_conn_t *) li->rli_data;
                next = li->rli_next;

                        if (FD_ISSET(cc->sock, &f)) {
                    iac_client_closed = 0;
                              p_read_client_conn(cc);
                        }

                if (!iac_client_closed) {
                    if (li->rli_next)
                        li = next;
                    else
                        li = NULL;
                } else {
                    li = next;
                }

                iac_client_closed = 0;
                  }

            /*
             * check for data on server connections
             */
            li = iac_server_conns->rl_list_start;
            prev = NULL;
            while (li) {
                iac_server_conn_t *sc = (iac_server_conn_t *) li->rli_data;
                next = li->rli_next;
                if (FD_ISSET(sc->sock, &f)) {
                    iac_serv_closed = 0;
                    p_read_server_conn(sc);
                }

                if (!iac_serv_closed) {
                    if (li->rli_next)
                        li = next;
                    else
                        li = NULL;
                } else {
                    li = next;
                }

                iac_serv_closed = 0;
            }
            
                  /*
                   * check for events on resolvers
                   */
#ifndef HAVE_IPV6
                  for(i = 0; i < IAC_MAX_DNS_RESOLVERS; i++) {

                        switch( dns_post_select(&resolvers[i], &f, &wfds) ) {
                                  
                        /* continue */                                  
                        case 0: break;
                                  
                        /* an error occurred */
                        case -1: {
                                    iac_client_conn_t *cc = dns_get_userarg( &resolvers[i] );
                                  
                                    iac_log(IAC_WARN, "[   ] Resolver error: %s\n", strerror(errno));
                                  
                                    errno = 0;

                                    cc->lookup = IAC_DNS_FAIL;
                                    cc->resolver = NULL;
                                    dns_free(&resolvers[i]);

                                    break;
                              }
                                /* we got an answer */
                        default: {
                                    char host[IAC_HOSTLEN + 1];
                                    iac_client_conn_t *cc = dns_get_userarg(&resolvers[i]);

                                    if( dns_get_name(&resolvers[i], host, IAC_HOSTLEN) ) {
                                                  
                                          iac_log(IAC_DEBUG, "[   ] Resolved ip %s to hostname %s\n",
                                                                (cc->host) ? cc->host : "(null)",
                                                                (host) ? host : "(null)");
                                                  
                                          RL_FREE(cc->host);
                                          cc->host = strdup(host);
                                          IAC_GENHOSTMASK(cc);
                                                  
                                    } else {
                                                  iac_log(IAC_DEBUG, "[   ] Host %s not found.\n",
                                                          (cc->host) ? cc->host : "(null)");
                                    }
  
                                    cc->resolver = NULL;
                                    dns_free(&resolvers[i]);
                                  
                                    break;
                              }
                        }
                  }
#endif
                  break;
      }

    /*
     * let the childs die...
     */
      while ((pid = waitpid(0, &status, WNOHANG)) > 0) {

            iac_client_conn_t *cc;
        int cstate;
        int r;
            char buf[2048];

            iac_log(IAC_DEBUG, "[   ] - _exit (%d)\n", pid);

        /*
         * check all client connection for a running child process with
         * that pid
         */
            li = iac_client_conns->rl_list_start;
            while (li) {

                  cc = (iac_client_conn_t *) li->rli_data;

                  if (cc->child_pid == pid)
            {

                /*
                 * reset pid in the client handle to mark as finished
                 */
                        cc->child_pid = 0;

#ifdef HAVE_SCRIPTS

                if ( cc->script )
                {
                    ioctl(cc->pipe[0], FIONREAD, &cstate);
                    if (cstate > 0)
                    {
                        char *p, *s;

                        /*
                         * read from pipe
                         */
                        memset(buf, 0, sizeof(buf));
                        r = read(cc->pipe[0], buf, sizeof(buf)-1);
                        buf[r] = '\0';

                        for ( p = buf, s = buf; *p; p++ )
                        {
                            if ( *p == '\n' || *p == '\r' )
                            {
                                *p = '\0';

                                if ( *s )
                                {
                                    iac_net_client_send (
                                        cc,
                                        ":%s!%s@%s PRIVMSG %s :%s\r\n",
                                        cc->script->nick,
                                        cc->script->nick,
                                        iac_cfg.servername,
                                        cc->nick,
                                        s );
                                }

                                p++;
                                for (; *p == '\n' || *p == '\r'; p++);
                                s = p;

                                if ( *p == '\0' )
                                    break;
                            }
                        }

                        if ( *s )
                        {
                            iac_net_client_send (
                                cc,
                                ":%s!%s@%s PRIVMSG %s :%s\r\n",
                                cc->script->nick,
                                cc->script->nick,
                                iac_cfg.servername,
                                cc->nick,
                                s );
                        }


                    }

                    close(cc->pipe[0]);
                    close(cc->pipe[1]);
                }
                else
                {
#endif

                    /*
                     * 0 means success
                     */
                    if (status == 0) {

                        /*
                         * check if there is really data on the pipe
                         */
                        ioctl(cc->pipe[0], FIONREAD, &cstate);
                        if (cstate > 0) {

                            /*
                             * read from pipe
                             */
                            r = read(cc->pipe[0], buf, sizeof(buf)-1);
                            buf[r] = '\0';

                            iac_log(IAC_DEBUG, "[   ] C Resolved ip %s to hostname %s\n",
                                (cc->host) ? cc->host : "(null)",
                                (buf) ? buf : "(null)");

                            RL_FREE(cc->host);

                            cc->host = strdup(buf);

                            IAC_GENHOSTMASK(cc);

                            /*
                             * update lastlog entry (local users only)
                             */
                            if ( cc->flags & IAC_LOCAL &&
                                 iac_cfg.flags & IAC_LASTLOG ) {

                                iac_lastlog_t *ll = iac_cfg.lastlog;

                                while (ll) {

                                    /*
                                     * maybe not very safe but fast 8)
                                     */
                                    if (ll->signon == cc->signon &&
                                        ll->port == cc->port) {

                                        ll->hostname = strdup(buf);
                                    }

                                    ll = ll->next;
                                }
                            } /* end lastlog stuff */
                        }
                    } else {
                        iac_log(IAC_DEBUG, "Could not resolve address %s: %s\n",
                            cc->host, strerror(status));
                    }

                    /*
                     * close pipe fds
                     */
                    close(cc->pipe[0]);
                    close(cc->pipe[1]);
#ifdef HAVE_SCRIPTS
                }
#endif
            }
                  
                  li = li->rli_next;
            }
      }



}

void
iac_net_client_send(iac_client_conn_t *dst, const char *fmt, ...)
{
      if (dst->flags & IAC_LOCAL && dst->sock) {

        char *msg = NULL;
            va_list args;

            va_start(args, fmt);
        vasprintf(&msg, fmt, args);
            va_end(args);

        /*
         * I'm really sorry, but now it's too late, we have to
         * cut off the message
         */
        if ( strlen(msg) > IAC_MSGLEN ) {
            iac_log(IAC_RAW, "I'm sorry, I had to cut the messsage\n");
            msg[IAC_MSGLEN - 2] = '\r';
            msg[IAC_MSGLEN - 1] = '\n';
            msg[IAC_MSGLEN] = '\0';
        }

        iac_log(IAC_RAW, "[-->] C (%s:%d) %s", dst->host, dst->port, msg);
            send(dst->sock, msg, strlen(msg), 0);

        RL_FREE(msg);
      }
}

Generated by  Doxygen 1.6.0   Back to index