Add typecast to avoid a warning from some buggy Gentoo version of gcc.
[srvx.git] / src / mod-sockcheck.c
index 1f252c6c6ae3d4e05690b0936378920d35786d88..5b09ff8d7e2b10200dfc93c30f2a5e8823870a86 100644 (file)
@@ -1,11 +1,12 @@
-/* sockcheck.c - insecure proxy checking
- * Copyright 2000-2004 srvx Development Team
+/* mod-sockcheck.c - insecure proxy checking
+ * Copyright 2000-2006 srvx Development Team
  *
- * This program is free software; you can redistribute it and/or modify
+ * This file is part of srvx.
+ *
+ * srvx 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.  Important limitations are
- * listed in the COPYING file that accompanies this software.
+ * (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
@@ -13,7 +14,8 @@
  * 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, email srvx-maintainers@srvx.net.
+ * along with srvx; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  */
 
 #include "conf.h"
@@ -49,11 +51,11 @@ enum sockcheck_decision {
 };
 
 typedef struct {
-    enum sockcheck_decision decision;
-    time_t last_touched;
+    irc_in_addr_t addr;
     const char *reason;
-    struct in_addr addr;
-    char hostname[16];
+    time_t last_touched;
+    enum sockcheck_decision decision;
+    char hostname[IRC_NTOP_MAX_SIZE]; /* acts as key for checked_ip_dict */
 } *sockcheck_cache_info;
 
 DECLARE_LIST(sci_list, sockcheck_cache_info);
@@ -73,7 +75,7 @@ static dict_t checked_ip_dict;
  * state and the input).  Mealy state machines require fewer states to
  * match the same input than Moore machines (where the output is only
  * a function of the current state).
- * 
+ *
  * A state is characterized by sending some data (possibly nothing),
  * waiting a certain amount of time to receive one of zero or more
  * responses, and a decision (accept, reject, continue to another
@@ -127,7 +129,7 @@ static struct {
     unsigned int max_read;
     unsigned int gline_duration;
     unsigned int max_cache_age;
-    struct sockaddr_in *local_addr;
+    struct sockaddr *local_addr;
     int local_addr_len;
 } sockcheck_conf;
 
@@ -150,7 +152,7 @@ static const struct message_entry msgtab[] = {
     { "PCMSG_DISABLED", "Proxy scanning is $bdisabled$b." },
     { "PCMSG_NOT_CACHED", "No proxycheck records exist for IP %s." },
     { "PCMSG_STATUS_CHECKING", "IP %s proxycheck state: last touched %s ago, still checking" },
-    { "PCMSG_STATUS_ACCEPTED", "IP %s proxycheck state: last touched %s ago, acepted" },
+    { "PCMSG_STATUS_ACCEPTED", "IP %s proxycheck state: last touched %s ago, accepted" },
     { "PCMSG_STATUS_REJECTED", "IP %s proxycheck state: last touched %s ago, rejected: %s" },
     { "PCMSG_STATUS_UNKNOWN", "IP %s proxycheck state: last touched %s ago, invalid status" },
     { "PCMSG_STATISTICS", "Since booting, I have checked %d clients for illicit proxies, and detected %d proxy hosts.\nI am currently checking %d clients (out of %d max) and have a backlog of %d more to start on.\nI currently have %d hosts cached.\nI know how to detect %d kinds of proxies." },
@@ -201,11 +203,10 @@ sockcheck_list_unref(struct sockcheck_list *list)
 static void
 sockcheck_issue_gline(sockcheck_cache_info sci)
 {
-    char *target = alloca(3+strlen(sci->hostname));
-    strcpy(target, "*@");
-    strcpy(target+2, sci->hostname);
-    log_module(PC_LOG, LOG_INFO, "Issuing gline for client at IP %s hostname %s: %s", inet_ntoa(sci->addr), sci->hostname, sci->reason);
-    gline_add("ProxyCheck", target, sockcheck_conf.gline_duration, sci->reason, now, 1);
+    char addr[IRC_NTOP_MAX_SIZE + 2] = {'*', '@', '\0'};
+    irc_ntop(addr + 2, sizeof(addr) - 2, &sci->addr);
+    log_module(PC_LOG, LOG_INFO, "Issuing gline for client at %s: %s", addr + 2, sci->reason);
+    gline_add("ProxyCheck", addr, sockcheck_conf.gline_duration, sci->reason, now, now, 1);
 }
 
 static struct sockcheck_client *
@@ -228,7 +229,9 @@ sockcheck_free_client(struct sockcheck_client *client)
     if (SOCKCHECK_DEBUG) {
         log_module(PC_LOG, LOG_INFO, "Goodbye %s (%p)!  I set you free!", client->addr->hostname, client);
     }
-    if (client->fd) ioset_close(client->fd->fd, 1);
+    verify(client);
+    ioset_close(client->fd, 1);
+    client->fd = NULL;
     sockcheck_list_unref(client->tests);
     free(client->read);
     free(client->resp_state);
@@ -246,6 +249,7 @@ sockcheck_timeout_client(void *data)
     if (SOCKCHECK_DEBUG) {
         log_module(PC_LOG, LOG_INFO, "Client %s timed out.", client->addr->hostname);
     }
+    verify(client);
     sockcheck_advance(client, client->state->responses.used-1);
 }
 
@@ -256,7 +260,8 @@ sockcheck_print_client(const struct sockcheck_client *client)
     log_module(PC_LOG, LOG_INFO, "client %p: { addr = %p { decision = %s; last_touched = "FMT_TIME_T"; reason = %s; hostname = \"%s\" }; "
         "test_index = %d; state = %p { port = %d; type = %s; template = \"%s\"; ... }; "
         "fd = %p(%d); read = %p; read_size = %d; read_used = %d; read_pos = %d; }",
-        client, client->addr, decs[client->addr->decision], client->addr->last_touched, client->addr->reason, client->addr->hostname,
+        client, client->addr, decs[client->addr->decision], client->addr->last_touched,
+        client->addr->reason, client->addr->hostname,
         client->test_index, client->state,
         (client->state ? client->state->port : 0),
         (client->state ? decs[client->state->type] : "N/A"),
@@ -282,13 +287,9 @@ expand_var(const struct sockcheck_client *client, char var, char **p_expansion,
     extern struct cManagerNode cManager;
     const char *expansion;
     unsigned int exp_length;
-#ifndef __SOLARIS__
-    u_int32_t exp4;
-    u_int16_t exp2;
-#else
     uint32_t exp4;
     uint16_t exp2;
-#endif
+
     /* expand variable */
     switch (var) {
     case 'c':
@@ -296,7 +297,7 @@ expand_var(const struct sockcheck_client *client, char var, char **p_expansion,
         exp_length = strlen(expansion);
         break;
     case 'i':
-       exp4 = client->addr->addr.s_addr;
+       exp4 = client->addr->addr.in6_32[3];
        exp_length = sizeof(exp4);
        expansion = (char*)&exp4;
        break;
@@ -430,7 +431,7 @@ sockcheck_decide(struct sockcheck_client *client, enum sockcheck_decision decisi
     case ACCEPT:
        /* do nothing */
         if (SOCKCHECK_DEBUG) {
-            log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at IP %s hostname %s.", inet_ntoa(client->addr->addr), client->addr->hostname);
+            log_module(PC_LOG, LOG_INFO, "Proxy check passed for client at %s.", client->addr->hostname);
         }
         break;
     case REJECT:
@@ -438,7 +439,7 @@ sockcheck_decide(struct sockcheck_client *client, enum sockcheck_decision decisi
        proxies_detected++;
        sockcheck_issue_gline(client->addr);
         if (SOCKCHECK_DEBUG) {
-            log_module(PC_LOG, LOG_INFO, "Proxy check rejects client at IP %s hostname %s (%s)", inet_ntoa(client->addr->addr), client->addr->hostname, client->addr->reason);
+            log_module(PC_LOG, LOG_INFO, "Proxy check rejects client at %s (%s)", client->addr->hostname, client->addr->reason);
         }
        /* Don't compare test_index != 0 directly, because somebody
         * else may have reordered the tests already. */
@@ -470,6 +471,7 @@ sockcheck_advance(struct sockcheck_client *client, unsigned int next_state)
 {
     struct sockcheck_state *ns;
 
+    verify(client);
     timeq_del(0, sockcheck_timeout_client, client, TIMEQ_IGNORE_WHEN);
     if (SOCKCHECK_DEBUG) {
         unsigned int n, m;
@@ -520,6 +522,7 @@ sockcheck_readable(struct io_fd *fd)
     unsigned int nn;
     int res;
 
+    verify(client);
     res = read(fd->fd, client->read + client->read_used, client->read_size - client->read_used);
     if (res < 0) {
         switch (res = errno) {
@@ -626,6 +629,7 @@ static void
 sockcheck_connected(struct io_fd *fd, int rc)
 {
     struct sockcheck_client *client = fd->data;
+    verify(client);
     client->fd = fd;
     switch (rc) {
     default:
@@ -640,7 +644,6 @@ sockcheck_connected(struct io_fd *fd, int rc)
         return;
     case 0: break;
     }
-    fd->wants_reads = 1;
     if (SOCKCHECK_DEBUG) {
         log_module(PC_LOG, LOG_INFO, "Connected: to %s port %d.", client->addr->hostname, client->state->port);
     }
@@ -652,15 +655,15 @@ sockcheck_begin_test(struct sockcheck_client *client)
 {
     struct io_fd *io_fd;
 
-    if (client->fd) {
-        ioset_close(client->fd->fd, 1);
-        client->fd = NULL;
-    }
+    verify(client);
+    ioset_close(client->fd, 1);
+    client->fd = NULL;
     do {
         client->state = client->tests->list[client->test_index];
         client->read_pos = 0;
         client->read_used = 0;
-        client->fd = io_fd = ioset_connect((struct sockaddr*)sockcheck_conf.local_addr, sizeof(struct sockaddr), client->addr->hostname, client->state->port, 0, client, sockcheck_connected);
+        io_fd = ioset_connect(sockcheck_conf.local_addr, sockcheck_conf.local_addr_len, client->addr->hostname, client->state->port, 0, client, sockcheck_connected);
+        client->fd = io_fd;
         if (!io_fd) {
             client->test_index++;
             continue;
@@ -692,20 +695,21 @@ sockcheck_start_client(unsigned int idx)
     sockcheck_num_clients++;
     if (!tests) return;
     client = client_list[idx] = sockcheck_alloc_client(sci);
-    log_module(PC_LOG, LOG_INFO, "Proxy-checking client at %s (%s) as client %d (%p) of %d.", inet_ntoa(sci->addr), sci->hostname, idx, client, sockcheck_num_clients);
+    log_module(PC_LOG, LOG_INFO, "Proxy-checking client at %s as client %d (%p) of %d.", sci->hostname, idx, client, sockcheck_num_clients);
     client->test_rep = 0;
     client->client_index = idx;
     sockcheck_begin_test(client);
 }
 
 void
-sockcheck_queue_address(struct in_addr addr)
+sockcheck_queue_address(irc_in_addr_t addr)
 {
     sockcheck_cache_info sci;
-    char *ipstr=inet_ntoa(addr);
+    const char *ipstr = irc_ntoa(&addr);
 
     sci = dict_find(checked_ip_dict, ipstr, NULL);
     if (sci) {
+        verify(sci);
         switch (sci->decision) {
         case CHECKING:
             /* We are already checking this host. */
@@ -1007,25 +1011,24 @@ static MODCMD_FUNC(cmd_defproxy)
 static MODCMD_FUNC(cmd_hostscan)
 {
     unsigned int n;
-    unsigned long addr;
-    struct in_addr ipaddr;
-    char hnamebuf[64];
+    irc_in_addr_t ipaddr;
+    char hnamebuf[IRC_NTOP_MAX_SIZE];
 
     for (n=1; n<argc; n++) {
        struct userNode *un = GetUserH(argv[n]);
 
         if (un) {
-            if ((un->ip.s_addr == 0) || (ntohl(un->ip.s_addr) == INADDR_LOOPBACK)) {
+            if (!irc_in_addr_is_valid(un->ip)
+                || irc_in_addr_is_loopback(un->ip)) {
                 reply("PCMSG_UNSCANNABLE_IP", un->nick);
             } else {
-                strcpy(hnamebuf, inet_ntoa(un->ip));
+                irc_ntop(hnamebuf, sizeof(hnamebuf), &un->ip);
                 sockcheck_queue_address(un->ip);
                 reply("PCMSG_ADDRESS_QUEUED", hnamebuf);
             }
         } else {
             char *scanhost = argv[n];
-            if (getipbyname(scanhost, &addr)) {
-                ipaddr.s_addr = htonl(addr);
+            if (irc_pton(&ipaddr, NULL, scanhost)) {
                 sockcheck_queue_address(ipaddr);
                 reply("PCMSG_ADDRESS_QUEUED", scanhost);
             } else {
@@ -1039,14 +1042,14 @@ static MODCMD_FUNC(cmd_hostscan)
 static MODCMD_FUNC(cmd_clearhost)
 {
     unsigned int n;
-    char hnamebuf[64];
+    char hnamebuf[IRC_NTOP_MAX_SIZE];
 
     for (n=1; n<argc; n++) {
         struct userNode *un = GetUserH(argv[n]);
         const char *scanhost;
 
         if (un) {
-            strcpy(hnamebuf, inet_ntoa(un->ip));
+            irc_ntop(hnamebuf, sizeof(hnamebuf), &un->ip);
             scanhost = hnamebuf;
         } else {
             scanhost = argv[n];
@@ -1078,7 +1081,7 @@ static MODCMD_FUNC(cmd_stats_proxycheck)
             reply("PCMSG_NOT_CACHED", hostname);
             return 0;
         }
-        intervalString(elapse_buf, now - sci->last_touched);
+        intervalString(elapse_buf, now - sci->last_touched, user->handle_info);
         switch (sci->decision) {
         case CHECKING: msg = "PCMSG_STATUS_CHECKING"; break;
         case ACCEPT: msg = "PCMSG_STATUS_ACCEPTED"; break;
@@ -1096,8 +1099,8 @@ static MODCMD_FUNC(cmd_stats_proxycheck)
 static int
 sockcheck_new_user(struct userNode *user) {
     /* If they have a bum IP, or are bursting in, don't proxy-check or G-line them. */
-    if (user->ip.s_addr
-        && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)
+    if (irc_in_addr_is_valid(user->ip)
+        && !irc_in_addr_is_loopback(user->ip)
         && !user->uplink->burst)
         sockcheck_queue_address(user->ip);
     return 0;
@@ -1120,6 +1123,7 @@ static void
 sockcheck_read_conf(void)
 {
     dict_t my_node;
+    struct addrinfo *ai;
     const char *str;
 
     /* set the defaults here in case the entire record is missing */
@@ -1144,25 +1148,16 @@ sockcheck_read_conf(void)
         str = database_get_data(my_node, "gline_duration", RECDB_QSTRING);
         if (str) sockcheck_conf.gline_duration = ParseInterval(str);
        str = database_get_data(my_node, "address", RECDB_QSTRING);
-       if (str) {
-           struct sockaddr_in *sin;
-           unsigned long addr;
-
-           sockcheck_conf.local_addr_len = sizeof(*sin);
-           if (getipbyname(str, &addr)) {
-               sin = malloc(sockcheck_conf.local_addr_len);
-               sin->sin_family = AF_INET;
-               sin->sin_port = 0;
-               sin->sin_addr.s_addr = addr;
-#ifdef HAVE_SIN_LEN
-               sin->sin_len = 0;
-#endif
-               memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
-               sockcheck_conf.local_addr = sin;
-           } else {
-               log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
-               sockcheck_conf.local_addr = NULL;
-           }
+        if (!getaddrinfo(str, NULL, NULL, &ai)) {
+           sockcheck_conf.local_addr_len = ai->ai_addrlen;
+            sockcheck_conf.local_addr = calloc(1, ai->ai_addrlen);
+            memcpy(sockcheck_conf.local_addr, ai->ai_addr, ai->ai_addrlen);
+            freeaddrinfo(ai);
+        } else {
+            sockcheck_conf.local_addr_len = 0;
+            sockcheck_conf.local_addr = NULL;
+            if (str)
+                log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str);
        }
     }
 }