Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / s_auth.c
index b84d100db1d2c7f70320d8a3a014a6a46493168e..6ad278a87e35082cbe675434dbd573d4109b726c 100644 (file)
  *     any messages from it.
  *     --Bleep  Thomas Helvey <tomh@inxpress.net>
  */
+#include "config.h"
+
 #include "s_auth.h"
 #include "client.h"
 #include "IPcheck.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
 #include "ircd_chattr.h"
+#include "ircd_events.h"
+#include "ircd_features.h"
 #include "ircd_log.h"
 #include "ircd_osdep.h"
+#include "ircd_snprintf.h"
 #include "ircd_string.h"
 #include "list.h"
 #include "numeric.h"
@@ -44,7 +49,6 @@
 #include "s_debug.h"
 #include "s_misc.h"
 #include "send.h"
-#include "sprintf_irc.h"
 #include "struct.h"
 #include "sys.h"               /* TRUE bleah */
 
@@ -92,13 +96,125 @@ typedef enum {
 } ReportType;
 
 #define sendheader(c, r) \
-   send((c)->fd, HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
+   send(cli_fd(c), HeaderMessages[(r)].message, HeaderMessages[(r)].length, 0)
 
 struct AuthRequest* AuthPollList = 0; /* GLOBAL - auth queries pending io */
 static struct AuthRequest* AuthIncompleteList = 0;
 
 enum { AUTH_TIMEOUT = 60 };
 
+static void release_auth_client(struct Client* client);
+static void unlink_auth_request(struct AuthRequest* request,
+                                struct AuthRequest** list);
+void free_auth_request(struct AuthRequest* auth);
+
+/*
+ * auth_timeout - timeout a given auth request
+ */
+static void auth_timeout_callback(struct Event* ev)
+{
+  struct AuthRequest* auth;
+
+  assert(0 != ev_timer(ev));
+  assert(0 != t_data(ev_timer(ev)));
+
+  auth = t_data(ev_timer(ev));
+
+  if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
+    auth->flags &= ~AM_TIMEOUT;
+
+    if (!(auth->flags & AM_FREE_MASK)) {
+      Debug((DEBUG_LIST, "Freeing auth from timeout callback; %p [%p]", auth,
+            ev_timer(ev)));
+      MyFree(auth); /* done with it, finally */
+    }
+  } else {
+    assert(ev_type(ev) == ET_EXPIRE);
+
+    destroy_auth_request(auth, 1);
+  }
+}
+
+/*
+ * auth_sock_callback - called when an event occurs on the socket
+ */
+static void auth_sock_callback(struct Event* ev)
+{
+  struct AuthRequest* auth;
+
+  assert(0 != ev_socket(ev));
+  assert(0 != s_data(ev_socket(ev)));
+
+  auth = s_data(ev_socket(ev));
+
+  switch (ev_type(ev)) {
+  case ET_DESTROY: /* being destroyed */
+    auth->flags &= ~AM_SOCKET;
+
+    if (!(auth->flags & AM_FREE_MASK)) {
+      Debug((DEBUG_LIST, "Freeing auth from sock callback; %p [%p]", auth,
+            ev_socket(ev)));
+      MyFree(auth); /* done with it finally */
+    }
+    break;
+
+  case ET_CONNECT: /* socket connection completed */
+    Debug((DEBUG_LIST, "Connection completed for auth %p [%p]; sending query",
+          auth, ev_socket(ev)));
+    socket_state(&auth->socket, SS_CONNECTED);
+    send_auth_query(auth);
+    break;
+
+  case ET_READ: /* socket is readable */
+  case ET_EOF: /* end of file on socket */
+  case ET_ERROR: /* error on socket */
+    Debug((DEBUG_LIST, "Auth socket %p [%p] readable", auth, ev_socket(ev)));
+    read_auth_reply(auth);
+    break;
+
+  default:
+#ifndef NDEBUG
+    abort(); /* unrecognized event */
+#endif
+    break;
+  }
+}
+
+/*
+ * destroy_auth_request - stop an auth request completely
+ */
+void destroy_auth_request(struct AuthRequest* auth, int send_reports)
+{
+  struct AuthRequest** authList;
+
+  if (IsDoingAuth(auth)) {
+    authList = &AuthPollList;
+    if (-1 < auth->fd) {
+      close(auth->fd);
+      auth->fd = -1;
+      socket_del(&auth->socket);
+    }
+
+    if (send_reports && IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_ID);
+  } else
+    authList = &AuthIncompleteList;
+
+  if (IsDNSPending(auth)) {
+    delete_resolver_queries(auth);
+    if (send_reports && IsUserPort(auth->client))
+      sendheader(auth->client, REPORT_FAIL_DNS);
+  }
+
+  if (send_reports)
+    log_write(LS_RESOLVER, L_INFO, 0, "DNS/AUTH timeout %s",
+             get_client_name(auth->client, HIDE_IP));
+
+  release_auth_client(auth->client);
+  unlink_auth_request(auth, authList);
+  free_auth_request(auth);
+}
+
 /*
  * make_auth_request - allocate a new auth request
  */
@@ -108,9 +224,12 @@ static struct AuthRequest* make_auth_request(struct Client* client)
                (struct AuthRequest*) MyMalloc(sizeof(struct AuthRequest));
   assert(0 != auth);
   memset(auth, 0, sizeof(struct AuthRequest));
+  auth->flags   = AM_TIMEOUT;
   auth->fd      = -1;
   auth->client  = client;
-  auth->timeout = CurrentTime + AUTH_TIMEOUT;
+  cli_auth(client) = auth;
+  timer_add(&auth->timeout, auth_timeout_callback, (void*) auth, TT_RELATIVE,
+           AUTH_TIMEOUT);
   return auth;
 }
 
@@ -119,9 +238,13 @@ static struct AuthRequest* make_auth_request(struct Client* client)
  */
 void free_auth_request(struct AuthRequest* auth)
 {
-  if (-1 < auth->fd)
+  if (-1 < auth->fd) {
     close(auth->fd);
-  MyFree(auth);
+    Debug((DEBUG_LIST, "Deleting auth socket for %p", auth->client));
+    socket_del(&auth->socket);
+  }
+  Debug((DEBUG_LIST, "Deleting auth timeout timer for %p", auth->client));
+  timer_del(&auth->timeout);
 }
 
 /*
@@ -159,14 +282,16 @@ static void link_auth_request(struct AuthRequest* request,
 static void release_auth_client(struct Client* client)
 {
   assert(0 != client);
-  client->lasttime = client->since = CurrentTime;
-  if (client->fd > HighestFd)
-    HighestFd = client->fd;
-  LocalClientArray[client->fd] = client;
+  cli_auth(client) = 0;
+  cli_lasttime(client) = cli_since(client) = CurrentTime;
+  if (cli_fd(client) > HighestFd)
+    HighestFd = cli_fd(client);
+  LocalClientArray[cli_fd(client)] = client;
 
   add_client_to_list(client);
+  socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE);
   Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]",
-         client->username, client->sockhost, client->sock_ip));
+         cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
 }
  
 static void auth_kill_client(struct AuthRequest* auth)
@@ -211,25 +336,25 @@ static void auth_dns_callback(void* vptr, struct DNSReply* reply)
      * the ip#(s) for the socket is listed for the host.
      */
     for (i = 0; hp->h_addr_list[i]; ++i) {
-      if (0 == memcmp(hp->h_addr_list[i], &auth->client->ip,
+      if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)),
                       sizeof(struct in_addr)))
          break;
     }
     if (!hp->h_addr_list[i]) {
       if (IsUserPort(auth->client))
         sendheader(auth->client, REPORT_IP_MISMATCH);
-      sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
-                     auth->client->sock_ip, hp->h_name, 
-                     ircd_ntoa(hp->h_addr_list[0]));
-#if defined(KILL_IPMISMATCH)
-      auth_kill_client(auth);
-      return;
-#endif
+      sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]",
+                          cli_sock_ip(auth->client), hp->h_name, 
+                          ircd_ntoa(hp->h_addr_list[0]));
+      if (feature_bool(FEAT_KILL_IPMISMATCH)) {
+       auth_kill_client(auth);
+       return;
+      }
     }
     else {
       ++reply->ref_count;
-      auth->client->dns_reply = reply;
-      ircd_strncpy(auth->client->sockhost, hp->h_name, HOSTLEN);
+      cli_dns_reply(auth->client) = reply;
+      ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN);
       if (IsUserPort(auth->client))
         sendheader(auth->client, REPORT_FIN_DNS);
     }
@@ -260,6 +385,7 @@ static void auth_error(struct AuthRequest* auth, int kill)
   assert(0 != auth);
   close(auth->fd);
   auth->fd = -1;
+  socket_del(&auth->socket);
 
   if (IsUserPort(auth->client))
     sendheader(auth->client, REPORT_FAIL_ID);
@@ -298,29 +424,20 @@ static int start_auth_query(struct AuthRequest* auth)
   struct sockaddr_in remote_addr;
   struct sockaddr_in local_addr;
   int                fd;
+  IOResult           result;
 
   assert(0 != auth);
   assert(0 != auth->client);
 
   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
-#if 0
-    report_error(SOCKET_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
-#endif
     ++ServerStats->is_abad;
     return 0;
   }
   if ((MAXCONNECTIONS - 10) < fd) {
-#if 0
-    report_error(CONNLIMIT_ERROR_MSG, 
-                 get_client_name(auth->client, HIDE_IP), errno);
-#endif
     close(fd);
     return 0;
   }
   if (!os_set_nonblocking(fd)) {
-#if 0
-    report_error(NONB_ERROR_MSG, get_client_name(auth->client, HIDE_IP), errno);
-#endif
     close(fd);
     return 0;
   }
@@ -334,23 +451,22 @@ static int start_auth_query(struct AuthRequest* auth)
    * and machines with multiple IP addresses are common now
    */
   memset(&local_addr, 0, sizeof(struct sockaddr_in));
-  os_get_sockname(auth->client->fd, &local_addr);
+  os_get_sockname(cli_fd(auth->client), &local_addr);
   local_addr.sin_port = htons(0);
 
   if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) {
-#if 0
-    report_error(BIND_ERROR_MSG,
-                 get_client_name(auth->client, HIDE_IP), errno);
-#endif
     close(fd);
     return 0;
   }
 
-  remote_addr.sin_addr.s_addr = auth->client->ip.s_addr;
+  remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr;
   remote_addr.sin_port = htons(113);
   remote_addr.sin_family = AF_INET;
 
-  if (!os_connect_nonb(fd, &remote_addr)) {
+  if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE ||
+      !socket_add(&auth->socket, auth_sock_callback, (void*) auth,
+                 result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING,
+                 SOCK_EVENT_READABLE, fd)) {
     ServerStats->is_abad++;
     /*
      * No error report from this...
@@ -361,9 +477,13 @@ static int start_auth_query(struct AuthRequest* auth)
     return 0;
   }
 
+  auth->flags |= AM_SOCKET;
   auth->fd = fd;
 
   SetAuthConnect(auth);
+  if (result == IO_SUCCESS)
+    send_auth_query(auth); /* this does a SetAuthPending(auth) for us */
+
   return 1;
 }
 
@@ -444,69 +564,6 @@ static char* check_ident_reply(char* reply)
   return token;
 }
 
-#if 0
-/*
- * GetValidIdent - parse ident query reply from identd server
- * 
- * Inputs        - pointer to ident buf
- * Output        - NULL if no valid ident found, otherwise pointer to name
- * Side effects        -
- */
-static char* GetValidIdent(char *buf)
-{
-  int   remp = 0;
-  int   locp = 0;
-  char* colon1Ptr;
-  char* colon2Ptr;
-  char* colon3Ptr;
-  char* commaPtr;
-  char* remotePortString;
-
-  /* All this to get rid of a sscanf() fun. */
-  remotePortString = buf;
-  
-  colon1Ptr = strchr(remotePortString,':');
-  if(!colon1Ptr)
-    return 0;
-
-  *colon1Ptr = '\0';
-  colon1Ptr++;
-  colon2Ptr = strchr(colon1Ptr,':');
-  if(!colon2Ptr)
-    return 0;
-
-  *colon2Ptr = '\0';
-  colon2Ptr++;
-  commaPtr = strchr(remotePortString, ',');
-
-  if(!commaPtr)
-    return 0;
-
-  *commaPtr = '\0';
-  commaPtr++;
-
-  remp = atoi(remotePortString);
-  if(!remp)
-    return 0;
-              
-  locp = atoi(commaPtr);
-  if(!locp)
-    return 0;
-
-  /* look for USERID bordered by first pair of colons */
-  if(!strstr(colon1Ptr, "USERID"))
-    return 0;
-
-  colon3Ptr = strchr(colon2Ptr,':');
-  if(!colon3Ptr)
-    return 0;
-  
-  *colon3Ptr = '\0';
-  colon3Ptr++;
-  return(colon3Ptr);
-}
-#endif
-
 /*
  * start_auth - starts auth (identd) and dns queries for a client
  */
@@ -521,89 +578,51 @@ void start_auth(struct Client* client)
   auth = make_auth_request(client);
   assert(0 != auth);
 
-#if !defined(NODNS)
-  if (LOOPBACK == inet_netof(client->ip)) {
-    strcpy(client->sockhost, me.name);
-  }
-  else {
-    struct DNSQuery query;
-
-    query.vptr     = auth;
-    query.callback = auth_dns_callback;
+  Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
 
-    if (IsUserPort(auth->client))
-      sendheader(client, REPORT_DO_DNS);
+  if (!feature_bool(FEAT_NODNS)) {
+    if (LOOPBACK == inet_netof(cli_ip(client)))
+      strcpy(cli_sockhost(client), cli_name(&me));
+    else {
+      struct DNSQuery query;
 
-    client->dns_reply = gethost_byaddr((const char*) &client->ip, &query);
+      query.vptr     = auth;
+      query.callback = auth_dns_callback;
 
-    if (client->dns_reply) {
-      ++client->dns_reply->ref_count;
-      ircd_strncpy(client->sockhost, client->dns_reply->hp->h_name, HOSTLEN);
       if (IsUserPort(auth->client))
-        sendheader(client, REPORT_FIN_DNSC);
+       sendheader(client, REPORT_DO_DNS);
+
+      cli_dns_reply(client) = gethost_byaddr((const char*) &(cli_ip(client)),
+                                            &query);
+
+      if (cli_dns_reply(client)) {
+       ++(cli_dns_reply(client))->ref_count;
+       ircd_strncpy(cli_sockhost(client), cli_dns_reply(client)->hp->h_name,
+                    HOSTLEN);
+       if (IsUserPort(auth->client))
+         sendheader(client, REPORT_FIN_DNSC);
+       Debug((DEBUG_LIST, "DNS entry for %p was cached", auth->client));
+      } else
+       SetDNSPending(auth);
     }
-    else
-      SetDNSPending(auth);
   }
-#endif
 
-  if (start_auth_query(auth))
+  if (start_auth_query(auth)) {
+    Debug((DEBUG_LIST, "identd query for %p initiated successfully",
+          auth->client));
     link_auth_request(auth, &AuthPollList);
-  else if (IsDNSPending(auth))
+  } else if (IsDNSPending(auth)) {
+    Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
+          "waiting on DNS", auth->client));
     link_auth_request(auth, &AuthIncompleteList);
-  else {
+  } else {
+    Debug((DEBUG_LIST, "identd query for %p not initiated successfully; "
+          "no DNS pending; releasing immediately", auth->client));
     free_auth_request(auth);
     release_auth_client(client);
   }
 }
 
-/*
- * timeout_auth_queries - timeout resolver and identd requests
- * allow clients through if requests failed
- */
-void timeout_auth_queries(time_t now)
-{
-  struct AuthRequest* auth;
-  struct AuthRequest* auth_next = 0;
-
-  for (auth = AuthPollList; auth; auth = auth_next) {
-    auth_next = auth->next;
-    if (auth->timeout < CurrentTime) {
-      if (-1 < auth->fd) {
-        close(auth->fd);
-        auth->fd = -1;
-      }
-
-      if (IsUserPort(auth->client))
-        sendheader(auth->client, REPORT_FAIL_ID);
-      if (IsDNSPending(auth)) {
-        delete_resolver_queries(auth);
-        if (IsUserPort(auth->client))
-          sendheader(auth->client, REPORT_FAIL_DNS);
-      }
-      ircd_log(L_INFO, "DNS/AUTH timeout %s",
-               get_client_name(auth->client, HIDE_IP));
-
-      release_auth_client(auth->client);
-      unlink_auth_request(auth, &AuthPollList);
-      free_auth_request(auth);
-    }
-  }
-  for (auth = AuthIncompleteList; auth; auth = auth_next) {
-    auth_next = auth->next;
-    if (auth->timeout < CurrentTime) {
-      delete_resolver_queries(auth);
-      if (IsUserPort(auth->client))
-        sendheader(auth->client, REPORT_FAIL_DNS);
-      ircd_log(L_INFO, "DNS timeout %s", get_client_name(auth->client, HIDE_IP));
-
-      release_auth_client(auth->client);
-      unlink_auth_request(auth, &AuthIncompleteList);
-      free_auth_request(auth);
-    }
-  }
-}
-
 /*
  * send_auth_query - send the ident server a query giving "theirport , ourport"
  * The write is only attempted *once* so it is deemed to be a fail if the
@@ -621,14 +640,14 @@ void send_auth_query(struct AuthRequest* auth)
   assert(0 != auth);
   assert(0 != auth->client);
 
-  if (!os_get_sockname(auth->client->fd, &us) ||
-      !os_get_peername(auth->client->fd, &them)) {
+  if (!os_get_sockname(cli_fd(auth->client), &us) ||
+      !os_get_peername(cli_fd(auth->client), &them)) {
     auth_error(auth, 1);
     return;
   }
-  sprintf_irc(authbuf, "%u , %u\r\n",
-             (unsigned int) ntohs(them.sin_port),
-             (unsigned int) ntohs(us.sin_port));
+  ircd_snprintf(0, authbuf, sizeof(authbuf), "%u , %u\r\n",
+               (unsigned int) ntohs(them.sin_port),
+               (unsigned int) ntohs(us.sin_port));
 
   if (IO_SUCCESS == os_send_nonb(auth->fd, authbuf, strlen(authbuf), &count)) {
     ClearAuthConnect(auth);
@@ -656,18 +675,23 @@ void read_auth_reply(struct AuthRequest* auth)
 
   assert(0 != auth);
   assert(0 != auth->client);
+  assert(auth = cli_auth(auth->client));
 
   if (IO_SUCCESS == os_recv_nonb(auth->fd, buf, BUFSIZE, &len)) {
     buf[len] = '\0';
+    Debug((DEBUG_LIST, "Auth %p [%p] reply: %s", auth, &auth->socket, buf));
     username = check_ident_reply(buf);
+    Debug((DEBUG_LIST, "Username: %s", username));
   }
 
   close(auth->fd);
   auth->fd = -1;
+  Debug((DEBUG_LIST, "Deleting auth [%p] socket %p", auth, &auth->socket));
+  socket_del(&auth->socket);
   ClearAuth(auth);
   
   if (!EmptyString(username)) {
-    ircd_strncpy(auth->client->username, username, USERLEN);
+    ircd_strncpy(cli_username(auth->client), username, USERLEN);
     /*
      * Not needed, struct is zeroed by memset
      * auth->client->username[USERLEN] = '\0';
@@ -679,9 +703,6 @@ void read_auth_reply(struct AuthRequest* auth)
   }
   else {
     ++ServerStats->is_abad;
-#if 0
-    strcpy(auth->client->username, "unknown");
-#endif
   }
   unlink_auth_request(auth, &AuthPollList);
 
@@ -692,5 +713,3 @@ void read_auth_reply(struct AuthRequest* auth)
     free_auth_request(auth);
   }
 }
-
-