]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/ident/Ident.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / ident / Ident.cc
index 87634f20567c5791ed6624b3ed70e2cc15dcfbe2..2f4bd328c05266aeb7c13c7866c6b6aca312c7d3 100644 (file)
@@ -1,42 +1,23 @@
 /*
- * DEBUG: section 30    Ident (RFC 931)
- * AUTHOR: Duane Wessels
- *
- * SQUID Web Proxy Cache          http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- *  Squid is the result of efforts by numerous individuals from
- *  the Internet community; see the CONTRIBUTORS file for full
- *  details.   Many organizations have provided support for Squid's
- *  development; see the SPONSORS file for full details.  Squid is
- *  Copyrighted (C) 2001 by the Regents of the University of
- *  California; see the COPYRIGHT file for full details.  Squid
- *  incorporates software developed and/or copyrighted by other
- *  sources; see the CREDITS file for full details.
- *
- *  This program 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.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  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, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
  */
+
+/* DEBUG: section 30    Ident (RFC 931) */
+
 #include "squid.h"
 
 #if USE_IDENT
+#include "base/JobWait.h"
 #include "comm.h"
 #include "comm/Connection.h"
 #include "comm/ConnOpener.h"
-#include "CommCalls.h"
+#include "comm/Read.h"
 #include "comm/Write.h"
+#include "CommCalls.h"
 #include "globals.h"
 #include "ident/Config.h"
 #include "ident/Ident.h"
@@ -56,13 +37,35 @@ typedef struct _IdentClient {
     struct _IdentClient *next;
 } IdentClient;
 
-typedef struct _IdentStateData {
-    hash_link hash;            /* must be first */
+class IdentStateData
+{
+public:
+    hash_link hash;     /* must be first */
+private:
+    CBDATA_CLASS(IdentStateData);
+
+public:
+    /* AsyncJob API emulated */
+    void deleteThis(const char *aReason);
+    void swanSong();
+
+    /// notify all waiting IdentClient callbacks
+    void notify(const char *result);
+
     Comm::ConnectionPointer conn;
     MemBuf queryMsg;  ///< the lookup message sent to IDENT server
-    IdentClient *clients;
+    IdentClient *clients = nullptr;
     char buf[IDENT_BUFSIZE];
-} IdentStateData;
+
+    /// waits for a connection to the IDENT server to be established/opened
+    JobWait<Comm::ConnOpener> connWait;
+
+private:
+    // use deleteThis() to destroy
+    ~IdentStateData();
+};
+
+CBDATA_CLASS_INIT(IdentStateData);
 
 // TODO: make these all a series of Async job calls. They are self-contained callbacks now.
 static IOCB ReadReply;
@@ -70,27 +73,46 @@ static IOCB WriteFeedback;
 static CLCB Close;
 static CTCB Timeout;
 static CNCB ConnectDone;
-static hash_table *ident_hash = NULL;
+static hash_table *ident_hash = nullptr;
 static void ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data);
-static void identCallback(IdentStateData * state, char *result);
 
 } // namespace Ident
 
 Ident::IdentConfig Ident::TheConfig;
 
-/**** PRIVATE FUNCTIONS ****/
+void
+Ident::IdentStateData::deleteThis(const char *reason)
+{
+    debugs(30, 3, reason);
+    swanSong();
+    delete this;
+}
 
 void
-Ident::identCallback(IdentStateData * state, char *result)
+Ident::IdentStateData::swanSong()
 {
-    IdentClient *client;
+    if (clients != nullptr)
+        notify(nullptr);
+}
 
-    if (result && *result == '\0')
-        result = NULL;
+Ident::IdentStateData::~IdentStateData() {
+    assert(!clients);
 
-    while ((client = state->clients)) {
+    if (Comm::IsConnOpen(conn)) {
+        comm_remove_close_handler(conn->fd, Ident::Close, this);
+        conn->close();
+    }
+
+    hash_remove_link(ident_hash, (hash_link *) this);
+    xfree(hash.key);
+}
+
+void
+Ident::IdentStateData::notify(const char *result)
+{
+    while (IdentClient *client = clients) {
         void *cbdata;
-        state->clients = client->next;
+        clients = client->next;
 
         if (cbdataReferenceValidDone(client->callback_data, &cbdata))
             client->callback(result, cbdata);
@@ -103,34 +125,43 @@ void
 Ident::Close(const CommCloseCbParams &params)
 {
     IdentStateData *state = (IdentStateData *)params.data;
-    identCallback(state, NULL);
-    state->conn->close();
-    hash_remove_link(ident_hash, (hash_link *) state);
-    xfree(state->hash.key);
-    cbdataFree(state);
+    if (state->conn) {
+        state->conn->noteClosure();
+        state->conn = nullptr;
+    }
+    state->deleteThis("connection closed");
 }
 
 void
 Ident::Timeout(const CommTimeoutCbParams &io)
 {
-    debugs(30, 3, HERE << io.conn);
-    io.conn->close();
+    debugs(30, 3, io.conn);
+    IdentStateData *state = (IdentStateData *)io.data;
+    state->deleteThis("timeout");
 }
 
 void
-Ident::ConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int xerrno, void *data)
+Ident::ConnectDone(const Comm::ConnectionPointer &conn, Comm::Flag status, int, void *data)
 {
     IdentStateData *state = (IdentStateData *)data;
-
-    if (status != COMM_OK) {
-        if (status == COMM_TIMEOUT) {
+    state->connWait.finish();
+
+    // Start owning the supplied connection (so that it is not orphaned if this
+    // function bails early). As a (tiny) optimization or perhaps just diff
+    // minimization, the close handler is added later, when we know we are not
+    // bailing. This delay is safe because comm_remove_close_handler() forgives
+    // missing handlers.
+    assert(conn); // but may be closed
+    assert(!state->conn);
+    state->conn = conn;
+
+    if (status != Comm::OK) {
+        if (status == Comm::TIMEOUT)
             debugs(30, 3, "IDENT connection timeout to " << state->conn->remote);
-        }
+        state->deleteThis(status == Comm::TIMEOUT ? "connect timeout" : "connect error");
         return;
     }
 
-    assert(conn != NULL && conn == state->conn);
-
     /*
      * see if any of our clients still care
      */
@@ -140,17 +171,17 @@ Ident::ConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int x
             break;
     }
 
-    if (c == NULL) {
-        /* no clients care */
-        conn->close();
+    if (c == nullptr) {
+        state->deleteThis("client(s) aborted");
         return;
     }
 
-    comm_add_close_handler(conn->fd, Ident::Close, state);
+    assert(state->conn->isOpen());
+    comm_add_close_handler(state->conn->fd, Ident::Close, state);
 
     AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
                                    CommIoCbPtrFun(Ident::WriteFeedback, state));
-    Comm::Write(conn, &queryMsg, writeCall);
+    Comm::Write(conn, &state->queryMsg, writeCall);
     AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
                                   CommIoCbPtrFun(Ident::ReadReply, state));
     comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
@@ -160,29 +191,30 @@ Ident::ConnectDone(const Comm::ConnectionPointer &conn, comm_err_t status, int x
 }
 
 void
-Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
+Ident::WriteFeedback(const Comm::ConnectionPointer &conn, char *, size_t len, Comm::Flag flag, int xerrno, void *data)
 {
-    debugs(30, 5, HERE << conn << ": Wrote IDENT request " << len << " bytes.");
+    debugs(30, 5, conn << ": Wrote IDENT request " << len << " bytes.");
 
     // TODO handle write errors better. retry or abort?
-    if (flag != COMM_OK) {
-        debugs(30, 2, HERE << conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
-        conn->close();
+    if (flag != Comm::OK) {
+        debugs(30, 2, conn << " err-flags=" << flag << " IDENT write error: " << xstrerr(xerrno));
+        IdentStateData *state = (IdentStateData *)data;
+        state->deleteThis("write error");
     }
 }
 
 void
-Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data)
+Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int, void *data)
 {
     IdentStateData *state = (IdentStateData *)data;
-    char *ident = NULL;
-    char *t = NULL;
+    char *ident = nullptr;
+    char *t = nullptr;
 
     assert(buf == state->buf);
     assert(conn->fd == state->conn->fd);
 
-    if (flag != COMM_OK || len <= 0) {
-        state->conn->close();
+    if (flag != Comm::OK || len <= 0) {
+        state->deleteThis("read error");
         return;
     }
 
@@ -199,16 +231,18 @@ Ident::ReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, com
     if ((t = strchr(buf, '\n')))
         *t = '\0';
 
-    debugs(30, 5, HERE << conn << ": Read '" << buf << "'");
+    debugs(30, 5, conn << ": Read '" << buf << "'");
 
     if (strstr(buf, "USERID")) {
         if ((ident = strrchr(buf, ':'))) {
             while (xisspace(*++ident));
-            Ident::identCallback(state, ident);
+            if (ident && *ident == '\0')
+                ident = nullptr;
+            state->notify(ident);
         }
     }
 
-    state->conn->close();
+    state->deleteThis("completed");
 }
 
 void
@@ -223,10 +257,6 @@ Ident::ClientAdd(IdentStateData * state, IDCB * callback, void *callback_data)
     *C = c;
 }
 
-CBDATA_TYPE(IdentStateData);
-
-/**** PUBLIC FUNCTIONS ****/
-
 /*
  * start a TCP connection to the peer host on port 113
  */
@@ -236,52 +266,44 @@ Ident::Start(const Comm::ConnectionPointer &conn, IDCB * callback, void *data)
     IdentStateData *state;
     char key1[IDENT_KEY_SZ];
     char key2[IDENT_KEY_SZ];
-    char key[IDENT_KEY_SZ];
+    char key[IDENT_KEY_SZ*2+2]; // key1 + ',' + key2 + terminator
 
-    conn->local.ToURL(key1, IDENT_KEY_SZ);
-    conn->remote.ToURL(key2, IDENT_KEY_SZ);
-    snprintf(key, IDENT_KEY_SZ, "%s,%s", key1, key2);
+    conn->local.toUrl(key1, IDENT_KEY_SZ);
+    conn->remote.toUrl(key2, IDENT_KEY_SZ);
+    int res = snprintf(key, sizeof(key), "%s,%s", key1, key2);
+    assert(res > 0);
+    assert(static_cast<unsigned int>(res) < sizeof(key));
 
     if (!ident_hash) {
-        Init();
+        ident_hash = hash_create((HASHCMP *) strcmp,
+                                 hashPrime(Squid_MaxFD / 8),
+                                 hash4);
     }
-    if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != NULL) {
+    if ((state = (IdentStateData *)hash_lookup(ident_hash, key)) != nullptr) {
         ClientAdd(state, callback, data);
         return;
     }
 
-    CBDATA_INIT_TYPE(IdentStateData);
-    state = cbdataAlloc(IdentStateData);
+    state = new IdentStateData;
     state->hash.key = xstrdup(key);
 
-    // copy the conn details. We dont want the original FD to be re-used by IDENT.
-    state->conn = conn->copyDetails();
+    // copy the conn details. We do not want the original FD to be re-used by IDENT.
+    const auto identConn = conn->cloneProfile();
     // NP: use random port for secure outbound to IDENT_PORT
-    state->conn->local.SetPort(0);
-    state->conn->remote.SetPort(IDENT_PORT);
+    identConn->local.port(0);
+    identConn->remote.port(IDENT_PORT);
 
     // build our query from the original connection details
     state->queryMsg.init();
-    state->queryMsg.printf("%d, %d\r\n", conn->remote.GetPort(), conn->local.GetPort());
+    state->queryMsg.appendf("%d, %d\r\n", conn->remote.port(), conn->local.port());
 
     ClientAdd(state, callback, data);
     hash_join(ident_hash, &state->hash);
 
     AsyncCall::Pointer call = commCbCall(30,3, "Ident::ConnectDone", CommConnectCbPtrFun(Ident::ConnectDone, state));
-    AsyncJob::Start(new Comm::ConnOpener(state->conn, call, Ident::TheConfig.timeout));
-}
-
-void
-Ident::Init(void)
-{
-    if (ident_hash) {
-        debugs(30, DBG_CRITICAL, "WARNING: Ident already initialized.");
-        return;
-    }
-
-    ident_hash = hash_create((HASHCMP *) strcmp,
-                             hashPrime(Squid_MaxFD / 8),
-                             hash4);
+    const auto connOpener = new Comm::ConnOpener(identConn, call, Ident::TheConfig.timeout);
+    state->connWait.start(connOpener, call);
 }
 
 #endif /* USE_IDENT */
+