/*
- * $Id$
- *
- * 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"
#include "MemBuf.h"
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;
- IdentClient *clients;
+ MemBuf queryMsg; ///< the lookup message sent to IDENT server
+ 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;
-static PF Close;
+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);
+}
+
+Ident::IdentStateData::~IdentStateData() {
+ assert(!clients);
+
+ if (Comm::IsConnOpen(conn)) {
+ comm_remove_close_handler(conn->fd, Ident::Close, this);
+ conn->close();
+ }
- if (result && *result == '\0')
- result = NULL;
+ hash_remove_link(ident_hash, (hash_link *) this);
+ xfree(hash.key);
+}
- while ((client = state->clients)) {
+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);
}
void
-Ident::Close(int fdnotused, void *data)
+Ident::Close(const CommCloseCbParams ¶ms)
{
- IdentStateData *state = (IdentStateData *)data;
- identCallback(state, NULL);
- state->conn->close();
- hash_remove_link(ident_hash, (hash_link *) state);
- xfree(state->hash.key);
- cbdataFree(state);
+ IdentStateData *state = (IdentStateData *)params.data;
+ 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
*/
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);
- MemBuf mb;
- mb.init();
- mb.Printf("%d, %d\r\n",
- conn->remote.GetPort(),
- conn->local.GetPort());
- AsyncCall::Pointer nil;
- Comm::Write(conn, &mb, nil);
+ AsyncCall::Pointer writeCall = commCbCall(5,4, "Ident::WriteFeedback",
+ CommIoCbPtrFun(Ident::WriteFeedback, state));
+ Comm::Write(conn, &state->queryMsg, writeCall);
AsyncCall::Pointer readCall = commCbCall(5,4, "Ident::ReadReply",
- CommIoCbPtrFun(Ident::ReadReply, state));
+ CommIoCbPtrFun(Ident::ReadReply, state));
comm_read(conn, state->buf, IDENT_BUFSIZE, readCall);
AsyncCall::Pointer timeoutCall = commCbCall(5,4, "Ident::Timeout",
- CommTimeoutCbPtrFun(Ident::Timeout, state));
+ CommTimeoutCbPtrFun(Ident::Timeout, state));
commSetConnTimeout(conn, Ident::TheConfig.timeout, timeoutCall);
}
void
-Ident::ReadReply(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, conn << ": Wrote IDENT request " << len << " bytes.");
+
+ // TODO handle write errors better. retry or abort?
+ 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::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;
}
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
*C = c;
}
-CBDATA_TYPE(IdentStateData);
-
-/**** PUBLIC FUNCTIONS ****/
-
/*
* start a TCP connection to the peer host on port 113
*/
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);
+ identConn->local.port(0);
+ identConn->remote.port(IDENT_PORT);
+
+ // build our query from the original connection details
+ state->queryMsg.init();
+ 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 */
+