Author: Kev <klmitch@mit.edu>
[ircu2.10.12-pk.git] / ircd / s_auth.c
index 01c207af95ef495ef56ef6337afce96c1337e4c7..5ac17026e57bf808463d022eaf7bafa8773e4aca 100644 (file)
@@ -35,6 +35,7 @@
 #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"
@@ -102,6 +103,118 @@ 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 +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;
 }
 
@@ -122,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);
 }
 
 /*
@@ -162,12 +282,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)));
 }
@@ -263,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);
@@ -301,6 +424,7 @@ 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);
@@ -353,7 +477,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 +491,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;
 }
 
@@ -524,6 +655,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 +678,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
@@ -660,14 +752,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)) {