Author: Ghostwolf <foxxe@wtfs.net>
[ircu2.10.12-pk.git] / ircd / s_auth.c
index 01c207af95ef495ef56ef6337afce96c1337e4c7..17dce053b1627e5e382ae52aeb4f53f755bf6c24 100644 (file)
 #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"
@@ -47,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 */
 
@@ -102,6 +103,119 @@ 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
  */
@@ -111,9 +225,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(timer_init(&auth->timeout), auth_timeout_callback, (void*) auth,
+           TT_RELATIVE, AUTH_TIMEOUT);
   return auth;
 }
 
@@ -122,9 +239,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);
 }
 
 /*
@@ -162,12 +283,14 @@ static void link_auth_request(struct AuthRequest* request,
 static void release_auth_client(struct Client* client)
 {
   assert(0 != 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]",
          cli_username(client), cli_sockhost(client), cli_sock_ip(client)));
 }
@@ -182,6 +305,7 @@ static void auth_kill_client(struct AuthRequest* auth)
     delete_resolver_queries(auth);
   IPcheck_disconnect(auth->client);
   Count_unknowndisconnects(UserStats);
+  cli_auth(auth->client) = 0;
   free_client(auth->client);
   free_auth_request(auth);
 }
@@ -263,6 +387,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);
@@ -301,29 +426,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;
   }
@@ -341,10 +457,6 @@ static int start_auth_query(struct AuthRequest* auth)
   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;
   }
@@ -353,7 +465,10 @@ static int start_auth_query(struct AuthRequest* auth)
   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...
@@ -364,9 +479,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;
 }
 
@@ -447,69 +566,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
  */
@@ -524,6 +580,8 @@ void start_auth(struct Client* client)
   auth = make_auth_request(client);
   assert(0 != auth);
 
+  Debug((DEBUG_INFO, "Beginning auth request on client %p", client));
+
   if (!feature_bool(FEAT_NODNS)) {
     if (LOOPBACK == inet_netof(cli_ip(client)))
       strcpy(cli_sockhost(client), cli_name(&me));
@@ -545,69 +603,28 @@ void start_auth(struct Client* client)
                     HOSTLEN);
        if (IsUserPort(auth->client))
          sendheader(client, REPORT_FIN_DNSC);
+       Debug((DEBUG_LIST, "DNS entry for %p was cached", auth->client));
       } else
        SetDNSPending(auth);
     }
   }
 
-  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);
-      }
-      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, &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);
-      log_write(LS_RESOLVER, L_INFO, 0, "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
@@ -630,9 +647,9 @@ void send_auth_query(struct AuthRequest* auth)
     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);
@@ -660,14 +677,19 @@ 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)) {
@@ -683,9 +705,6 @@ void read_auth_reply(struct AuthRequest* auth)
   }
   else {
     ++ServerStats->is_abad;
-#if 0
-    strcpy(cli_username(auth->client), "unknown");
-#endif
   }
   unlink_auth_request(auth, &AuthPollList);
 
@@ -696,5 +715,3 @@ void read_auth_reply(struct AuthRequest* auth)
     free_auth_request(auth);
   }
 }
-
-