From 80cba6d3a8cb3fa952ed59bc1d3889076d3263f6 Mon Sep 17 00:00:00 2001 From: Francesco Chemolli <5175948+kinkie@users.noreply.github.com> Date: Sat, 21 Oct 2023 09:38:56 +0000 Subject: [PATCH] Remove squidclient (#1514) Since recent commit a4e35bd removed cache_object support, popular clients like wget and curl can do everything squidclient can, with one exception: They cannot expand `mgr:foo` macros into `http://localhost:3128/squid-internal-mgr/foo` URLs. That single feature is easily emulated and not worth keeping (fairly heavy) squidclient for, especially since recent Squid security improvements often require customizing squidclient commands with more and more options (to address problems tracked in Squid bug 5283). --- configure.ac | 1 - doc/release-notes/release-7.sgml.in | 5 + mksnapshot.sh | 2 +- po4a.conf | 2 - src/squid.8.in | 1 - tools/Makefile.am | 2 +- tools/cachemgr.cgi.8.in | 1 - tools/purge/purge.1 | 1 - tools/squidclient/Makefile.am | 68 --- tools/squidclient/Parameters.h | 38 -- tools/squidclient/Ping.cc | 217 --------- tools/squidclient/Ping.h | 63 --- tools/squidclient/Transport.cc | 531 ---------------------- tools/squidclient/Transport.h | 121 ----- tools/squidclient/gssapi_support.cc | 159 ------- tools/squidclient/gssapi_support.h | 53 --- tools/squidclient/squidclient.1 | 264 ----------- tools/squidclient/squidclient.cc | 682 ---------------------------- 18 files changed, 7 insertions(+), 2204 deletions(-) delete mode 100644 tools/squidclient/Makefile.am delete mode 100644 tools/squidclient/Parameters.h delete mode 100644 tools/squidclient/Ping.cc delete mode 100644 tools/squidclient/Ping.h delete mode 100644 tools/squidclient/Transport.cc delete mode 100644 tools/squidclient/Transport.h delete mode 100644 tools/squidclient/gssapi_support.cc delete mode 100644 tools/squidclient/gssapi_support.h delete mode 100644 tools/squidclient/squidclient.1 delete mode 100644 tools/squidclient/squidclient.cc diff --git a/configure.ac b/configure.ac index 44c5b88a32..77e0790404 100644 --- a/configure.ac +++ b/configure.ac @@ -3092,7 +3092,6 @@ AC_CONFIG_FILES([ tools/apparmor/Makefile tools/helper-mux/Makefile tools/purge/Makefile - tools/squidclient/Makefile tools/systemd/Makefile tools/sysvinit/Makefile ]) diff --git a/doc/release-notes/release-7.sgml.in b/doc/release-notes/release-7.sgml.in index d29658a56b..e37da79ca5 100644 --- a/doc/release-notes/release-7.sgml.in +++ b/doc/release-notes/release-7.sgml.in @@ -44,6 +44,11 @@ The Squid-@SQUID_RELEASE@ change history can be -#include -#include - -#if HAVE_GETOPT_H -#include -#endif - -namespace Ping -{ -Ping::TheConfig Config; - -/// measurements collected by the squidclient ping mode logics -class pingStats_ -{ -public: - pingStats_() {memset(this, 0, sizeof(pingStats_));} - - long counted; ///< number of transactions which have so far been measured - long pMin; ///< shortest transaction time seen - long pMax; ///< longest transaction time seen - long sum; ///< total time so far spent waiting on transactions - -} stats; - -} // namespace Ping - -/** - * Signal interrupt handler for squidclient ping. - * Displays final statistics and disables further pings. - */ -static void -catchSignal(int sig) -{ - Ping::DisplayStats(); - Ping::Config.enable = false; - std::cerr << "SIGNAL " << sig << " Interrupted." << std::endl; -} - -uint32_t -Ping::Init() -{ - if (Ping::Config.enable) { -#if HAVE_SIGACTION - struct sigaction sa, osa; - if (sigaction(SIGINT, nullptr, &osa) == 0 && osa.sa_handler == SIG_DFL) { - sa.sa_handler = catchSignal; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - (void) sigaction(SIGINT, &sa, nullptr); - } -#else - void (*osig) (int); - if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL) - (void) signal(SIGINT, osig); -#endif - return Ping::Config.count; - } - - return 1; -} - -static struct timeval tv1, tv2; - -void -Ping::TimerStart() -{ - if (!Ping::Config.enable) - return; - -#if GETTIMEOFDAY_NO_TZP - (void)gettimeofday(&tv1); -#else - (void)gettimeofday(&tv1, nullptr); -#endif -} - -void -Ping::TimerStop(size_t fsize) -{ - if (!Ping::Config.enable) - return; - - struct tm *tmp; - time_t t2s; - long elapsed_msec; - -#if GETTIMEOFDAY_NO_TZP - (void)gettimeofday(&tv2); -#else - (void)gettimeofday(&tv2, nullptr); -#endif - - elapsed_msec = tvSubMsec(tv1, tv2); - t2s = tv2.tv_sec; - tmp = localtime(&t2s); - char tbuf[4096]; - snprintf(tbuf, sizeof(tbuf)-1, "%d-%02d-%02d %02d:%02d:%02d [%ld]: %ld.%03ld secs, %f KB/s", - tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, - tmp->tm_hour, tmp->tm_min, tmp->tm_sec, stats.counted + 1, - elapsed_msec / 1000, elapsed_msec % 1000, - elapsed_msec ? (double) fsize / elapsed_msec : -1.0); - std::cerr << tbuf << std::endl; - - if (!stats.counted || elapsed_msec < stats.pMin) - stats.pMin = elapsed_msec; - - if (!stats.counted || elapsed_msec > stats.pMax) - stats.pMax = elapsed_msec; - - stats.sum += elapsed_msec; - - ++stats.counted; - - /* Delay until next "ping.interval" boundary */ - if (!LoopDone(stats.counted) && elapsed_msec < Ping::Config.interval) { - - struct timeval tvs; - long msec_left = Ping::Config.interval - elapsed_msec; - - tvs.tv_sec = msec_left / 1000; - tvs.tv_usec = (msec_left % 1000) * 1000; - select(0, nullptr, nullptr, nullptr, &tvs); - } -} - -void -Ping::DisplayStats() -{ - if (Ping::Config.enable && stats.counted) { - long mean = stats.sum / stats.counted; - std::cerr << std::endl - << stats.counted << " requests, round-trip (secs) min/avg/max = " - << (stats.pMin/1000) << "." << (stats.pMin%1000) - << "/" << (mean/1000) << "." << (mean%1000) - << "/" << (stats.pMax/1000) << "." << (stats.pMax%1000) - << std::endl; - } -} - -void -Ping::TheConfig::usage() -{ - std::cerr << "Ping Mode" << std::endl - << " --ping [options] Enable ping mode." << std::endl - << std::endl - << " options:" << std::endl - << " -g count Ping iteration count (default, loop until interrupted)." << std::endl - << " -I interval Ping interval in seconds (default 1 second)." << std::endl - << std::endl; -} - -bool -Ping::TheConfig::parseCommandOpts(int argc, char *argv[], int c, int &optIndex) -{ - // to get here --ping was seen - enable = true; - count = 0; // default is infinite loop - interval = 1 * 1000; // default is 1s intervals - - const char *shortOpStr = "g:I:?"; - - // options for controlling squidclient ping mode - static struct option pingOptions[] = { - {"count", no_argument, nullptr, 'g'}, - {"interval", no_argument, nullptr, 'I'}, - {nullptr, 0, nullptr, 0} - }; - - int saved_opterr = opterr; - opterr = 0; // suppress errors from getopt - while ((c = getopt_long(argc, argv, shortOpStr, pingOptions, &optIndex)) != -1) { - switch (c) { - case 'g': - if (optarg) - count = atoi(optarg); - else { - std::cerr << "ERROR: -g ping count missing parameter." << std::endl; - usage(); - } - break; - - case 'I': - if (!optarg) { - std::cerr << "ERROR: -I ping interval missing parameter." << std::endl; - usage(); - } else if ((interval = atoi(optarg) * 1000) <= 0) { - std::cerr << "ERROR: -I ping interval out of range (0-" << (INT_MAX/1000) << ")." << std::endl; - usage(); - } - break; - - default: - // rewind and let the caller handle unknown options - --optind; - opterr = saved_opterr; - return true; - } - } - - opterr = saved_opterr; - return false; -} - diff --git a/tools/squidclient/Ping.h b/tools/squidclient/Ping.h deleted file mode 100644 index 3a9c353065..0000000000 --- a/tools/squidclient/Ping.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ - -#ifndef _SQUID_TOOLS_CLIENT_PING_H -#define _SQUID_TOOLS_CLIENT_PING_H - -/** - * API for looping the squidclient request message - * repeatedly. - */ -namespace Ping -{ - -/// parameters controlling 'ping' mode message looping. -class TheConfig -{ -public: - TheConfig() : enable(false), count(0), interval(1*1000) {} - - /// display Ping Options command line help to stderr - void usage(); - - /** - * parse --ping command line options - * \return true if there are other options still to parse - */ - bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex); - - bool enable; - int count; - int interval; -}; - -extern TheConfig Config; - -/// initialize the squidclient ping mode -uint32_t Init(); - -/// whether ping loop is completed at the given iteration. -inline bool LoopDone(int i) -{ - return !Ping::Config.enable || (Ping::Config.count && i >= Ping::Config.count); -} - -/// start timing a new transaction -void TimerStart(); - -/// calculate and display the statistics for a complete transaction -/// \param fsize number of bytes transferred during this transaction (for KB/s measure) -void TimerStop(size_t fsize); - -/// display summary of ping data collected -void DisplayStats(); - -} // namespace Ping - -#endif /* _SQUID_TOOLS_CLIENT_PING_H */ - diff --git a/tools/squidclient/Transport.cc b/tools/squidclient/Transport.cc deleted file mode 100644 index 8f5a50639c..0000000000 --- a/tools/squidclient/Transport.cc +++ /dev/null @@ -1,531 +0,0 @@ -/* - * 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. - */ - -#include "squid.h" -#include "ip/Address.h" -#include "ip/tools.h" -#include "tools/squidclient/Ping.h" -#include "tools/squidclient/Transport.h" - -#if HAVE_GETOPT_H -#include -#endif -#if HAVE_GNUTLS_X509_H -#include -#endif -#include - -Transport::TheConfig Transport::Config; - -/// the current server connection FD -int conn = -1; - -void -Transport::TheConfig::usage() -{ - std::cerr << "Connection Settings" << std::endl - << " -h | --host host Send message to server on 'host'. Default is localhost." << std::endl - << " -l | --local host Specify a local IP address to bind to. Default is none." << std::endl - << " -p | --port port Port number on server to contact. Default is " << CACHE_HTTP_PORT << "." << std::endl - << " -T timeout Timeout in seconds for read/write operations" << std::endl -#if USE_GNUTLS - << " --https Use TLS/SSL on the HTTP connection" << std::endl - << std::endl - << " TLS options:" << std::endl - << " --anonymous-tls Use Anonymous TLS. Sets default parameters:" << std::endl - << " \"PERFORMANCE:+ANON-ECDH:+ANON-DH\"" << std::endl - << " --params=\"...\" Use the given parameters." << std::endl - << " --cert=FILE Path to a PEM file holding the client X.509 certificate chain." << std::endl - << " May be repeated if there are multiple certificates to use for the server." << std::endl - << " --trusted-ca=PATH Path to a PEM file holding trusted CA certificate(s)." << std::endl - << " May be repeated." << std::endl - << " Example path: \"/etc/ssl/certs/ca-certificates.crt\"" << std::endl -#endif - << std::endl; -} - -bool -Transport::TheConfig::parseCommandOpts(int argc, char *argv[], int c, int &optIndex) -{ - bool tls = false; - const char *shortOpStr = "h:l:p:T:?"; - - // options for controlling squidclient transport connection - static struct option longOptions[] = { - {"anonymous-tls",no_argument, nullptr, '\1'}, - {"https", no_argument, nullptr, '\3'}, - {"trusted-ca", required_argument, nullptr, 'A'}, - {"cert", required_argument, nullptr, 'C'}, - {"host", required_argument, nullptr, 'h'}, - {"local", required_argument, nullptr, 'l'}, - {"port", required_argument, nullptr, 'p'}, - {"params", required_argument, nullptr, 'P'}, - {nullptr, 0, nullptr, 0} - }; - - int saved_opterr = opterr; - opterr = 0; // suppress errors from getopt - do { - switch (c) { - case '\1': - tls = true; - tlsAnonymous = true; - params = "PERFORMANCE:+ANON-ECDH:+ANON-DH"; - break; - - case '\3': - tls = true; - break; - - case 'A': - tls = true; - caFiles.push_back(std::string(optarg)); - break; - - case 'C': - tls = true; - certFiles.push_back(std::string(optarg)); - break; - - case 'h': - hostname = optarg; - break; - - case 'l': - localHost = optarg; - break; - - case 'p': /* port number */ - sscanf(optarg, "%hd", &port); - if (port < 1) - port = CACHE_HTTP_PORT; /* default */ - break; - - case 'P': - tls = true; - params = optarg; - break; - - case 'T': - ioTimeout = atoi(optarg); - break; - - default: - if (tls) - Transport::InitTls(); - - // rewind and let the caller handle unknown options - --optind; - opterr = saved_opterr; - return true; - } - } while ((c = getopt_long(argc, argv, shortOpStr, longOptions, &optIndex)) != -1); - - if (tls) - Transport::InitTls(); - - opterr = saved_opterr; - return false; -} - -/// Set up the source socket address from which to send. -static int -client_comm_bind(int sock, const Ip::Address &addr) -{ - static struct addrinfo *AI = nullptr; - addr.getAddrInfo(AI); - int res = bind(sock, AI->ai_addr, AI->ai_addrlen); - Ip::Address::FreeAddr(AI); - return res; -} - -static void -resolveDestination(Ip::Address &iaddr) -{ - struct addrinfo *AI = nullptr; - - debugVerbose(2, "Transport detected: IPv4" << - ((Ip::EnableIpv6 & IPV6_SPECIAL_V4MAPPING) ? "-mapped " : "") << - (Ip::EnableIpv6 == IPV6_OFF ? "-only" : " and IPv6") << - ((Ip::EnableIpv6 & IPV6_SPECIAL_SPLITSTACK) ? " split-stack" : "")); - - if (Transport::Config.localHost) { - debugVerbose(2, "Resolving " << Transport::Config.localHost << " ..."); - - if ( !iaddr.GetHostByName(Transport::Config.localHost) ) { - std::cerr << "ERROR: Cannot resolve " << Transport::Config.localHost << ": Host unknown." << std::endl; - exit(1); - } - } else { - debugVerbose(2, "Resolving " << Transport::Config.hostname << " ..."); - /* Process the remote host name to locate the Protocol required - in case we are being asked to link to another version of squid */ - if ( !iaddr.GetHostByName(Transport::Config.hostname) ) { - std::cerr << "ERROR: Cannot resolve " << Transport::Config.hostname << ": Host unknown." << std::endl; - exit(1); - } - } - - iaddr.getAddrInfo(AI); - if ((conn = socket(AI->ai_family, AI->ai_socktype, 0)) < 0) { - std::cerr << "ERROR: could not open socket to " << iaddr << std::endl; - Ip::Address::FreeAddr(AI); - exit(1); - } - Ip::Address::FreeAddr(AI); - - if (Transport::Config.localHost) { - if (client_comm_bind(conn, iaddr) < 0) { - std::cerr << "ERROR: could not bind socket to " << iaddr << std::endl; - exit(1); - } - - iaddr.setEmpty(); - - debugVerbose(2, "Resolving... " << Transport::Config.hostname); - - if ( !iaddr.GetHostByName(Transport::Config.hostname) ) { - std::cerr << "ERROR: Cannot resolve " << Transport::Config.hostname << ": Host unknown." << std::endl; - exit(1); - } - } - - iaddr.port(Transport::Config.port); -} - -/// Set up the destination socket address for message to send to. -static int -client_comm_connect(int sock, const Ip::Address &addr) -{ - static struct addrinfo *AI = nullptr; - addr.getAddrInfo(AI); - int res = connect(sock, AI->ai_addr, AI->ai_addrlen); - Ip::Address::FreeAddr(AI); - Ping::TimerStart(); - return res; -} - -bool -Transport::Connect() -{ - Ip::Address iaddr; - resolveDestination(iaddr); - - debugVerbose(2, "Connecting... " << Config.hostname << " (" << iaddr << ")"); - - if (client_comm_connect(conn, iaddr) < 0) { - char hostnameBuf[MAX_IPSTRLEN]; - iaddr.toUrl(hostnameBuf, MAX_IPSTRLEN); - std::cerr << "ERROR: Cannot connect to " << hostnameBuf - << (!errno ?": Host unknown." : "") << std::endl; - exit(1); - } - debugVerbose(2, "Connected to: " << Config.hostname << " (" << iaddr << ")"); - - // do any TLS setup that might be needed - if (!Transport::MaybeStartTls(Config.hostname)) - return false; - - return true; -} - -ssize_t -Transport::Write(const void *buf, size_t len) -{ - if (conn < 0) - return -1; - - if (Config.tlsEnabled) { -#if USE_GNUTLS - gnutls_record_send(Config.session, buf, len); - return len; -#else - return 0; -#endif - } else { - -#if _SQUID_WINDOWS_ - return send(conn, buf, len, 0); -#else - alarm(Config.ioTimeout); - return write(conn, buf, len); -#endif - } -} - -ssize_t -Transport::Read(void *buf, size_t len) -{ - if (conn < 0) - return -1; - - if (Config.tlsEnabled) { -#if USE_GNUTLS - return gnutls_record_recv(Config.session, buf, len); -#else - return 0; -#endif - } else { - -#if _SQUID_WINDOWS_ - return recv(conn, buf, len, 0); -#else - alarm(Config.ioTimeout); - return read(conn, buf, len); -#endif - } -} - -void -Transport::CloseConnection() -{ - (void) close(conn); - conn = -1; -} - -#if USE_GNUTLS -/* This function will verify the peer's certificate, and check - * if the hostname matches, as well as the activation, expiration dates. - */ -static int -verifyByCA(gnutls_session_t session) -{ - /* read hostname */ - const char *hostname = static_cast(gnutls_session_get_ptr(session)); - - /* This verification function uses the trusted CAs in the credentials - * structure. So you must have installed one or more CA certificates. - */ - unsigned int status; - if (gnutls_certificate_verify_peers3(session, hostname, &status) < 0) { - std::cerr << "VERIFY peers failure"; - return GNUTLS_E_CERTIFICATE_ERROR; - } - - gnutls_certificate_type_t type = gnutls_certificate_type_get(session); - gnutls_datum_t out; - if (gnutls_certificate_verification_status_print(status, type, &out, 0) < 0) { - std::cerr << "VERIFY status failure"; - return GNUTLS_E_CERTIFICATE_ERROR; - } - - std::cerr << "VERIFY DATUM: " << out.data << std::endl; - gnutls_free(out.data); - - if (status != 0) /* Certificate is not trusted */ - return GNUTLS_E_CERTIFICATE_ERROR; - - /* notify gnutls to continue handshake normally */ - return GNUTLS_E_SUCCESS; -} - -static int -verifyTlsCertificate(gnutls_session_t session) -{ - // XXX: 1) try to verify using DANE -> Secure Authenticated Connection - - // 2) try to verify using CA - if (verifyByCA(session) == GNUTLS_E_SUCCESS) { - std::cerr << "SUCCESS: CA verified Encrypted Connection" << std::endl; - return GNUTLS_E_SUCCESS; - } - - // 3) fails both is insecure, but show the results anyway. - std::cerr << "WARNING: Insecure Connection" << std::endl; - return GNUTLS_E_SUCCESS; -} -#endif - -#if USE_GNUTLS -static void -gnutlsDebugHandler(int level, const char *msg) -{ - debugVerbose(level, "GnuTLS: " << msg); -} -#endif - -void -Transport::InitTls() -{ -#if USE_GNUTLS - debugVerbose(3, "Initializing TLS library..."); - // NP: gnutls init is re-entrant and lock-counted with deinit but not thread safe. - if (gnutls_global_init() != GNUTLS_E_SUCCESS) { - int xerrno = errno; - std::cerr << "FATAL ERROR: TLS Initialize failed: " << xstrerr(xerrno) << std::endl; - exit(1); - } - - Config.tlsEnabled = true; - -#if USE_GNUTLS - gnutls_global_set_log_function(&gnutlsDebugHandler); - gnutls_global_set_log_level(scParams.verbosityLevel); -#endif - - // Initialize for anonymous TLS - gnutls_anon_allocate_client_credentials(&Config.anonCredentials); - - // Initialize for X.509 certificate exchange - gnutls_certificate_allocate_credentials(&Config.certCredentials); - for (std::list::const_iterator i = Config.caFiles.begin(); i != Config.caFiles.end(); ++i) { - int x = gnutls_certificate_set_x509_trust_file(Config.certCredentials, (*i).c_str(), GNUTLS_X509_FMT_PEM); - if (x < 0) { - debugVerbose(3, "WARNING: Failed to load Certificate Authorities from " << *i); - } else { - debugVerbose(3, "Loaded " << x << " Certificate Authorities from " << *i); - } - } - gnutls_certificate_set_verify_function(Config.certCredentials, verifyTlsCertificate); - - for (std::list::const_iterator i = Config.certFiles.begin(); i != Config.certFiles.end(); ++i) { - if (gnutls_certificate_set_x509_key_file(Transport::Config.certCredentials, (*i).c_str(), (*i).c_str(), GNUTLS_X509_FMT_PEM) != GNUTLS_E_SUCCESS) { - debugVerbose(3, "WARNING: Failed to load Certificate from " << *i); - } else { - debugVerbose(3, "Loaded Certificate from " << *i); - } - } - -#else - std::cerr << "ERROR: TLS support not available." << std::endl; -#endif -} - -#if USE_GNUTLS - -// perform the actual handshake exchange with remote server -static bool -doTlsHandshake(const char *type) -{ - // setup the connection for TLS - gnutls_transport_set_int(Transport::Config.session, conn); - gnutls_handshake_set_timeout(Transport::Config.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - - debugVerbose(2, type << " TLS handshake ... "); - - int ret = 0; - do { - ret = gnutls_handshake(Transport::Config.session); - } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); - - if (ret < 0) { - std::cerr << "ERROR: " << type << " TLS Handshake failed (" << ret << ") " - << gnutls_alert_get_name(gnutls_alert_get(Transport::Config.session)) - << std::endl; - gnutls_perror(ret); - gnutls_deinit(Transport::Config.session); - return false; - } - - char *desc = gnutls_session_get_desc(Transport::Config.session); - debugVerbose(3, "TLS Session info: " << std::endl << desc << std::endl); - gnutls_free(desc); - return true; -} - -static bool -loadTlsParameters() -{ - const char *err = nullptr; - int x; - if ((x = gnutls_priority_set_direct(Transport::Config.session, Transport::Config.params, &err)) != GNUTLS_E_SUCCESS) { - if (x == GNUTLS_E_INVALID_REQUEST) - std::cerr << "ERROR: Syntax error at: " << err << std::endl; - gnutls_perror(x); - return false; - } - return true; -} - -// attempt an anonymous TLS handshake -// this encrypts the connection but does not secure it -// so many public servers do not support this handshake type. -static bool -tryTlsAnonymous() -{ - if (!loadTlsParameters()) - return false; - - // put the anonymous credentials to the current session - int x; - if ((x = gnutls_credentials_set(Transport::Config.session, GNUTLS_CRD_ANON, Transport::Config.anonCredentials)) != GNUTLS_E_SUCCESS) { - std::cerr << "ERROR: Anonymous TLS credentials setup failed (" << x << ") " << std::endl; - gnutls_perror(x); - return false; - } - - return doTlsHandshake("Anonymous"); -} - -// attempt a X.509 certificate exchange -// this both encrypts and authenticates the connection -static bool -tryTlsCertificate(const char *hostname) -{ - gnutls_session_set_ptr(Transport::Config.session, (void *) hostname); - gnutls_server_name_set(Transport::Config.session, GNUTLS_NAME_DNS, hostname, strlen(hostname)); - - if (!loadTlsParameters()) - return false; - - // put the X.509 credentials to the current session - gnutls_credentials_set(Transport::Config.session, GNUTLS_CRD_CERTIFICATE, Transport::Config.certCredentials); - - // setup the connection for TLS - gnutls_transport_set_int(Transport::Config.session, conn); - gnutls_handshake_set_timeout(Transport::Config.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); - - return doTlsHandshake("X.509"); -} -#endif - -bool -Transport::MaybeStartTls(const char *hostname) -{ -#if USE_GNUTLS - if (Config.tlsEnabled) { - - // Initialize TLS session - gnutls_init(&Transport::Config.session, GNUTLS_CLIENT); - - if (Transport::Config.tlsAnonymous && !tryTlsAnonymous()) { - gnutls_deinit(Config.session); - return false; - } - - if (!tryTlsCertificate(hostname)) { - gnutls_deinit(Config.session); - return false; - } - } -#else - (void)hostname; -#endif - return true; -} - -void -Transport::ShutdownTls() -{ -#if USE_GNUTLS - if (!Config.tlsEnabled) - return; - - debugVerbose(3, "Shutting down TLS library..."); - - // release any existing session and credentials - gnutls_deinit(Config.session); - gnutls_anon_free_client_credentials(Config.anonCredentials); - gnutls_certificate_free_credentials(Config.certCredentials); - - // NP: gnutls init is re-entrant and lock-counted with deinit but not thread safe. - gnutls_global_deinit(); - Config.tlsEnabled = false; -#endif -} - diff --git a/tools/squidclient/Transport.h b/tools/squidclient/Transport.h deleted file mode 100644 index 66902ab78b..0000000000 --- a/tools/squidclient/Transport.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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. - */ - -#ifndef SQUID_TOOLS_SQUIDCLIENT_TRANSPORT_H -#define SQUID_TOOLS_SQUIDCLIENT_TRANSPORT_H - -#include "tools/squidclient/Parameters.h" - -#if HAVE_GNUTLS_GNUTLS_H -#include -#endif -#include -#include - -namespace Transport -{ - -/// parameters controlling outgoing connection -class TheConfig -{ -public: - TheConfig() : - ioTimeout(120), - localHost(nullptr), - port(CACHE_HTTP_PORT), - tlsEnabled(false), - tlsAnonymous(false) { - params = "NORMAL"; - hostname = "localhost"; - } - -// TODO: implicit transport options depending on the protocol-specific options -// ie --https enables TLS connection settings - - /// display Transport Options command line help to stderr - void usage(); - - /** - * parse transport related command line options - * \return true if there are other options still to parse - */ - bool parseCommandOpts(int argc, char *argv[], int c, int &optIndex); - - /// I/O operation timeout - int ioTimeout; - - /// the local hostname to bind as for outgoing IP - const char *localHost; - - /// the destination server host name to contact - const char *hostname; - - /// port on the server to contact - uint16_t port; - - /// whether to enable TLS on the server connection - bool tlsEnabled; - - /// whether to do anonymous TLS (non-authenticated) - bool tlsAnonymous; - - /// The TLS parameters (list of ciphers, versions, flags) - /// Default is "NORMAL" unless tlsAnonymous is used, - /// in which case it becomes "PERFORMANCE:+ANON-ECDH:+ANON-DH". - /// see http://gnutls.org/manual/html_node/Priority-Strings.html - const char *params; - - // client certificate PEM file(s) - std::list certFiles; - - // client trusted x509 certificate authorities file - std::list caFiles; - -#if USE_GNUTLS - /// anonymous client credentials - gnutls_anon_client_credentials_t anonCredentials; - - // client x509 certificate credentials - gnutls_certificate_credentials_t certCredentials; - - /// TLS session state - gnutls_session_t session; -#endif -}; - -extern TheConfig Config; - -/// locate and connect to the configured server -bool Connect(); - -/// close the current connection -void CloseConnection(); - -/// Initialize TLS library environment when necessary. -void InitTls(); - -/// perform TLS handshake on the currently open connection if -/// TLS library has been initialized. -/// return false on errors, true otherwise even if TLS not performed. -bool MaybeStartTls(const char *hostname); - -/// De-initialize TLS library environment when necessary. -void ShutdownTls(); - -/// write len bytes to the currently open connection. -/// \return the number of bytes written, or -1 on errors -ssize_t Write(const void *buf, size_t len); - -/// read up to len bytes from the currently open connection. -/// \return the number of bytes read, or -1 on errors -ssize_t Read(void *buf, size_t len); - -} // namespace Transport - -#endif /* SQUID_TOOLS_SQUIDCLIENT_TRANSPORT_H */ - diff --git a/tools/squidclient/gssapi_support.cc b/tools/squidclient/gssapi_support.cc deleted file mode 100644 index e76c0ed66f..0000000000 --- a/tools/squidclient/gssapi_support.cc +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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. - */ - -#include "squid.h" - -#if HAVE_GSSAPI - -#include "base64.h" -#include "tools/squidclient/gssapi_support.h" - -#include - -#if !defined(gss_mech_spnego) -static gss_OID_desc _gss_mech_spnego = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; -gss_OID gss_mech_spnego = &_gss_mech_spnego; -#endif - -#define BUFFER_SIZE 8192 - -/** - * Check return valuse major_status, minor_status for error and print error description - * in case of an error. - * - * \retval true in case of gssapi error - * \retval false in case of no gssapi error - */ -bool -check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function) -{ - if (GSS_ERROR(major_status)) { - OM_uint32 maj_stat, min_stat; - OM_uint32 msg_ctx = 0; - gss_buffer_desc status_string; - char buf[BUFFER_SIZE]; - size_t len; - - len = 0; - msg_ctx = 0; - while (!msg_ctx) { - /* convert major status code (GSS-API error) to text */ - maj_stat = gss_display_status(&min_stat, major_status, - GSS_C_GSS_CODE, - GSS_C_NULL_OID, - &msg_ctx, &status_string); - if (maj_stat == GSS_S_COMPLETE) { - snprintf(buf + len, BUFFER_SIZE-len, "%s", (char *) status_string.value); - len += status_string.length; - gss_release_buffer(&min_stat, &status_string); - break; - } - gss_release_buffer(&min_stat, &status_string); - } - snprintf(buf + len, BUFFER_SIZE-len, "%s", ". "); - len += 2; - msg_ctx = 0; - while (!msg_ctx) { - /* convert minor status code (underlying routine error) to text */ - maj_stat = gss_display_status(&min_stat, minor_status, - GSS_C_MECH_CODE, - GSS_C_NULL_OID, - &msg_ctx, &status_string); - if (maj_stat == GSS_S_COMPLETE) { - snprintf(buf + len, BUFFER_SIZE-len,"%s", (char *) status_string.value); - len += status_string.length; - gss_release_buffer(&min_stat, &status_string); - break; - } - gss_release_buffer(&min_stat, &status_string); - } - std::cerr << "ERROR: " << function << " failed: " << buf << std::endl; - return true; - } - return false; -} - -/** - * Get gssapi token for service HTTP/ - * User has to initiate a kinit user@DOMAIN on commandline first for the - * function to be successful - * - * \return base64 encoded token if successful, - * string "ERROR" if unsuccessful - */ -char * -GSSAPI_token(const char *server) -{ - OM_uint32 major_status, minor_status; - gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; - gss_name_t server_name = GSS_C_NO_NAME; - gss_buffer_desc service = GSS_C_EMPTY_BUFFER; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - char *token = nullptr; - - setbuf(stdout, nullptr); - setbuf(stdin, nullptr); - - if (!server) { - std::cerr << "ERROR: GSSAPI: No server name" << std::endl; - token = new char[6]; - memcpy(token, "ERROR", 5); - token[5] = '\0'; - return token; - } - service.value = xmalloc(strlen("HTTP") + strlen(server) + 2); - snprintf((char *) service.value, strlen("HTTP") + strlen(server) + 2, "%s@%s", "HTTP", server); - service.length = strlen((char *) service.value); - - major_status = gss_import_name(&minor_status, &service, - gss_nt_service_name, &server_name); - - if (!check_gss_err(major_status, minor_status, "gss_import_name()")) { - - major_status = gss_init_sec_context(&minor_status, - GSS_C_NO_CREDENTIAL, - &gss_context, - server_name, - gss_mech_spnego, - 0, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - nullptr, - &output_token, - nullptr, - nullptr); - - if (!check_gss_err(major_status, minor_status, "gss_init_sec_context()") && output_token.length) { - token = new char[base64_encode_len(output_token.length)]; - struct base64_encode_ctx ctx; - base64_encode_init(&ctx); - size_t blen = base64_encode_update(&ctx, token, output_token.length, reinterpret_cast(output_token.value)); - blen += base64_encode_final(&ctx, token+blen); - token[blen] = '\0'; - } - } - - if (!output_token.length) { - token = new char[6]; - memcpy(token, "ERROR", 5); - token[5] = '\0'; - } - - gss_delete_sec_context(&minor_status, &gss_context, nullptr); - gss_release_buffer(&minor_status, &service); - gss_release_buffer(&minor_status, &input_token); - gss_release_buffer(&minor_status, &output_token); - gss_release_name(&minor_status, &server_name); - - return token; -} - -#endif /* HAVE_GSSAPI */ - diff --git a/tools/squidclient/gssapi_support.h b/tools/squidclient/gssapi_support.h deleted file mode 100644 index b824e75ec4..0000000000 --- a/tools/squidclient/gssapi_support.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef _SQUID_TOOLS_SQUIDCLIENT_GSSAPI_H -#define _SQUID_TOOLS_SQUIDCLIENT_GSSAPI_H - -#if HAVE_GSSAPI -#if USE_APPLE_KRB5 -#define GSSKRB_APPLE_DEPRECATED(x) -#endif - -#if USE_HEIMDAL_KRB5 -#if HAVE_GSSAPI_GSSAPI_H -#include -#elif HAVE_GSSAPI_H -#include -#endif /* HAVE_GSSAPI_GSSAPI_H/HAVE_GSSAPI_H */ -#elif USE_GNUGSS -#if HAVE_GSS_H -#include -#endif -#else -#if HAVE_GSSAPI_GSSAPI_H -#include -#elif HAVE_GSSAPI_H -#include -#endif /* HAVE_GSSAPI_GSSAPI_H/HAVE_GSSAPI_H */ -#if HAVE_GSSAPI_GSSAPI_KRB5_H -#include -#endif -#if HAVE_GSSAPI_GSSAPI_GENERIC_H -#include -#endif -#if HAVE_GSSAPI_GSSAPI_EXT_H -#include -#endif -#endif - -#ifndef gss_nt_service_name -#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE -#endif - -bool check_gss_err(OM_uint32 major_status, OM_uint32 minor_status, const char *function); -char *GSSAPI_token(const char *server); - -#endif /* HAVE_GSSAPI */ -#endif /* _SQUID_TOOLS_SQUIDCLIENT_GSSAPI_H */ - diff --git a/tools/squidclient/squidclient.1 b/tools/squidclient/squidclient.1 deleted file mode 100644 index 08ed2e7edd..0000000000 --- a/tools/squidclient/squidclient.1 +++ /dev/null @@ -1,264 +0,0 @@ -.if !'po4a'hide' .TH squidclient 1 -. -.SH NAME -squidclient \- A simple HTTP web client tool -. -.SH SYNOPSIS -.if !'po4a'hide' .B squidclient -.if !'po4a'hide' .B "[ \-aknNrsv ] " -.if !'po4a'hide' .B "[ \-\-ping [ping\-options] ] " -.if !'po4a'hide' .B "[ \-\-https] [tls\-options] [ \-A" -string -.if !'po4a'hide' .B "] [ \-h | \-\-host" -remote host -.if !'po4a'hide' .B "] [ \-H '" -string -.if !'po4a'hide' .B "' ] [ \-i" -IMS -.if !'po4a'hide' .B "] [ \-j '" -Host header -.if !'po4a'hide' .B "' ] [ \-l | \-\-local" -host -.if !'po4a'hide' .B "] [ \-m" -method -.if !'po4a'hide' .B "] [ \-p | \-\-port" -port -.if !'po4a'hide' .B "] [ \-P" -file -.if !'po4a'hide' .B "] [ \-t" -count -.if !'po4a'hide' .B "] [ \-T" -timeout -.if !'po4a'hide' .B "] [ \-u" -user -.if !'po4a'hide' .B "] [ \-U" -user -.if !'po4a'hide' .B "] [ \-V" -version -.if !'po4a'hide' .B "] [ \-w" -password -.if !'po4a'hide' .B "] [ \-W" -password -.if !'po4a'hide' .B "] " -url -. -.PP -.if !'po4a'hide' .B "Ping options: [ \-g" -count -.if !'po4a'hide' .B "] [ \-I" -interval -.if !'po4a'hide' .B "] " -. -.PP -.if !'po4a'hide' .B "TLS options: [ \-\-anonymous\-tls ] [ \-\-trusted\-ca" -CA certificates file -.if !'po4a'hide' .B "...] [ \-\-cert" -client X.509 certificate file -.if !'po4a'hide' .B "] [ \-\-params" -TLS session parameters -.if !'po4a'hide' .B "] " -. -.SH DESCRIPTION -.B squidclient -is a tool providing a command line interface for retrieving URLs. -Designed for testing any HTTP 0.9, 1.0, or 1.1 web server or proxy. -This tool can be combined with scripts to perform any basic HTTP operation. -Some additional features for access to the -.B squid -proxy object cache and management information are provided. -. -.SH OPTIONS -.if !'po4a'hide' .TP 12 -.if !'po4a'hide' .B "\-a" -Do NOT include Accept: header. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-A 'string'" -Send -.B string -as User-Agent: header. To omit the header completely set string to empty (''). -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-h | \-\-host host" -Retrieve URL from server host. Default is -.B localhost -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-H 'string'" -Extra headers to send. Use -.B '\en' -for new lines. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-i time" -If\-Modified\-Since time (in Epoch seconds). -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-j hosthdr" -Host header content -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-k" -Keep the connection active. Default is to do only one request then close. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-l | \-\-local host" -Specify a local IP address to bind to. Default is none. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-m method" -Request method, default is -.I GET. -Squid also supports a non-standard method called -.I PURGE. -You can use that to purge a specific URL from the cache. -You need to have -.I purge -access setup in -.B squid.conf -similar to -.I manager -access. Here is an example: -.if !'po4a'hide' .nf -.if !'po4a'hide' acl purge method PURGE -.if !'po4a'hide' http_access deny purge !localhost -.if !'po4a'hide' .fi -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-n" -Proxy Negotiate(Kerberos) authentication. -.if !'po4a'hide' .nf -Use kinit username@DOMAIN first to get initial TGS. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-N" -WWW Negotiate(Kerberos) authentication. -.if !'po4a'hide' .nf -Use kinit username@DOMAIN first to get initial TGS. -.if !'po4a'hide' .fi -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-p port" -Port number of cache. Default is 3128. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-P file" -Request body. Using the named file as data. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-r" -Force cache to reload URL. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-s" -Silent. Do not print data to stdout. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-t count" -Trace -.I count -HTTP relay or proxy hops -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-T timeout" -Timeout value (seconds) for read/write operations. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-u user" -Proxy authentication username -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-U user" -WWW authentication username -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-v" -Verbose. Print outgoing message to stderr. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-V version" -HTTP Version. Use '\-' for HTTP/0.9 omitted case -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-w password" -Proxy authentication password -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-W password" -WWW authentication password -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-https" -Use Transport Layer Security on the HTTP connection. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-anonymous\-tls" -Use TLS with unauthenticated (anonymous) certificate. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-cert file" -File containing client X.509 certificate in PEM format. -May be repeated to load several client certificates. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-trusted\-ca file" -File containing trusted Certificate Authority (CA) certificates in PEM format. -May be repeated to load any number of files. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-params values" -TLS library specific parameters for the communication session. -See the library documentation for details on valid parameters. -.if !'po4a'hide' .I "GnuTLS: http://gnutls.org/manual/html_node/Priority\-Strings.html" -If repeated only the last value will have effect. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-\-ping [options]" -Enable ping mode. Optional \-g and \-I parameters must follow immediately if used. -Repeated use resets to default ping settings. -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-g count" -Ping mode, perform -.I count -iterations (default is to loop until interrupted). -. -.if !'po4a'hide' .TP -.if !'po4a'hide' .B "\-I interval" -Ping interval in seconds (default 1 second). -. -.SH AUTHOR -This program and manual was written by -.if !'po4a'hide' .I Amos Jeffries -.PP -Based on original code derived from Harvest and further developed by -numerous individuals from the internet community. -. -.SH COPYRIGHT -.PP - * 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. -. -.SH QUESTIONS -Questions on the usage of this program can be sent to the -.I Squid Users mailing list -.if !'po4a'hide' -. -.SH REPORTING BUGS -See https://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report. -.PP -Report bugs or bug fixes using https://bugs.squid-cache.org/ -.PP -Report serious security bugs to -.I Squid Bugs -.PP -Report ideas for new improvements to the -.I Squid Developers mailing list -.if !'po4a'hide' -. -.SH SEE ALSO -.if !'po4a'hide' .BR squid "(8), " -.if !'po4a'hide' .BR cachemgr.cgi "(8)" diff --git a/tools/squidclient/squidclient.cc b/tools/squidclient/squidclient.cc deleted file mode 100644 index bdb7c8f7fd..0000000000 --- a/tools/squidclient/squidclient.cc +++ /dev/null @@ -1,682 +0,0 @@ -/* - * 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. - */ - -#include "squid.h" -#include "base64.h" -#include "ip/Address.h" -#include "ip/tools.h" -#include "time/gadgets.h" -#include "tools/squidclient/gssapi_support.h" -#include "tools/squidclient/Parameters.h" -#include "tools/squidclient/Ping.h" -#include "tools/squidclient/Transport.h" - -#if _SQUID_WINDOWS_ -/** \cond AUTODOCS-IGNORE */ -using namespace Squid; -/** \endcond */ -#endif - -#include -#include -#include -#include -#include -#include -#if _SQUID_WINDOWS_ -#include -#endif -#if HAVE_SYS_SOCKET_H -#include -#endif -#if HAVE_UNISTD_H -#include -#endif -#if HAVE_NETDB_H -#include -#endif -#if HAVE_SYS_STAT_H -#include -#endif -#if HAVE_FCNTL_H -#include -#endif -#if HAVE_NETINET_IN_H -#include -#endif -#if HAVE_GETOPT_H -#include -#endif - -#ifndef BUFSIZ -#define BUFSIZ 8192 -#endif - -/* Local functions */ -static void usage(const char *progname); - -void pipe_handler(int sig); -static void set_our_signal(void); - -Parameters scParams; - -static int put_fd; -static char *put_file = nullptr; - -static struct stat sb; -int total_bytes = 0; - -#if _SQUID_AIX_ -/* Bug 3854: AIX 6.1 tries to link in this fde.h global symbol - * despite squidclient not using any of the fd_* code. - */ -fde *fde::Table = nullptr; -#endif - -#if _SQUID_WINDOWS_ -void -Win32SockCleanup(void) -{ - WSACleanup(); - return; -} -#endif - -static void -usage(const char *progname) -{ - std::cerr << "Version: " << VERSION << std::endl - << "Usage: " << progname << " [Basic Options] [HTTP Options]" << std::endl - << std::endl; - std::cerr - << " -s | --quiet Silent. Do not print response message to stdout." << std::endl - << " -v | --verbose Verbose debugging. Repeat (-vv) to increase output level." << std::endl - << " Levels:" << std::endl - << " 1 - Print outgoing request message to stderr." << std::endl - << " 2 - Print action trace to stderr." << std::endl - << " --help Display this help text." << std::endl - << std::endl; - Transport::Config.usage(); - Ping::Config.usage(); - std::cerr - << "HTTP Options:" << std::endl - << " -a Do NOT include Accept: header." << std::endl - << " -A User-Agent: header. Use \"\" to omit." << std::endl - << " -H 'string' Extra headers to send. Supports '\\\\', '\\n', '\\r' and '\\t'." << std::endl - << " -i IMS If-Modified-Since time (in Epoch seconds)." << std::endl - << " -j hosthdr Host header content" << std::endl - << " -k Keep the connection active. Default is to do only one request then close." << std::endl - << " -m method Request method, default is GET." << std::endl -#if HAVE_GSSAPI - << " -n Proxy Negotiate(Kerberos) authentication" << std::endl - << " -N WWW Negotiate(Kerberos) authentication" << std::endl -#endif - << " -P file Send content from the named file as request payload" << std::endl - << " -r Force cache to reload URL" << std::endl - << " -t count Trace count cache-hops" << std::endl - << " -u user Proxy authentication username" << std::endl - << " -U user WWW authentication username" << std::endl - << " -V version HTTP Version. Use '-' for HTTP/0.9 omitted case" << std::endl - << " -w password Proxy authentication password" << std::endl - << " -W password WWW authentication password" << std::endl - ; - exit(EXIT_FAILURE); -} - -static void -shellUnescape(char *buf) -{ - if (!buf) - return; - - unsigned char *p, *d; - - d = p = reinterpret_cast(buf); - - while (auto ch = *p) { - - if (ch == '\\') { - ++p; - - switch (*p) { - case 'n': - ch = '\n'; - break; - case 'r': - ch = '\r'; - break; - case 't': - ch = '\t'; - break; - case '\\': - ch = '\\'; - break; - default: - ch = *p; - debugVerbose(1, "Warning: unsupported shell code '\\" << ch << "'"); - break; - } - - *d = ch; - - if (!ch) - continue; - - } else { - *d = *p; - } - - ++p; - ++d; - } - - *d = '\0'; -} - -/// [Proxy-]Authorization header producer -class Authorization -{ -public: - Authorization(const char *aHeader, const char *aDestination): - header(aHeader), destination(aDestination) {} - - /// finalizes and writes the right HTTP header to the given stream - void commit(std::ostream &os); - - std::string header; ///< HTTP header name to send - std::string destination; ///< used when describing password - const char *user = nullptr; ///< user name to encode and send - const char *password = nullptr; ///< user password to encode and send -}; - -void -Authorization::commit(std::ostream &os) -{ -#if HAVE_GETPASS - if (!password) - password = getpass((destination + " password: ").c_str()); -#endif - if (!password) { - std::cerr << "ERROR: " << destination << " password missing\n"; - exit(EXIT_FAILURE); - } - - struct base64_encode_ctx ctx; - base64_encode_init(&ctx); - const auto bcapacity = base64_encode_len(strlen(user) + 1 + strlen(password)); - const auto buf = new char[bcapacity]; - - size_t bsize = 0; - bsize += base64_encode_update(&ctx, buf, strlen(user), reinterpret_cast(user)); - bsize += base64_encode_update(&ctx, buf+bsize, 1, reinterpret_cast(":")); - bsize += base64_encode_update(&ctx, buf+bsize, strlen(password), reinterpret_cast(password)); - bsize += base64_encode_final(&ctx, buf+bsize); - assert(bsize <= bcapacity); // paranoid and late but better than nothing - - os << header << ": Basic "; - os.write(buf, bsize); - os << "\r\n"; - - delete[] buf; -} - -static Authorization ProxyAuthorization("Proxy-Authorization", "proxy"); -static Authorization OriginAuthorization("Authorization", "origin server"); - -int -main(int argc, char *argv[]) -{ - int len, bytesWritten; - bool to_stdout, reload; - int keep_alive = 0; - int opt_noaccept = 0; -#if HAVE_GSSAPI - int www_neg = 0, proxy_neg = 0; -#endif - char url[BUFSIZ]; - char buf[BUFSIZ]; - char *extra_hdrs = nullptr; - const char *method = "GET"; - extern char *optarg; - time_t ims = 0; - int max_forwards = -1; - - const char *host = nullptr; - const char *version = "1.0"; - const char *useragent = nullptr; - - /* set the defaults */ - to_stdout = true; - reload = false; - - Ip::ProbeTransport(); // determine IPv4 or IPv6 capabilities before parsing. - if (argc < 2 || argv[argc-1][0] == '-') { - usage(argv[0]); /* need URL */ - } else if (argc >= 2) { - strncpy(url, argv[argc - 1], sizeof(url)); - url[sizeof(url) - 1] = '\0'; - - int optIndex = 0; - const char *shortOpStr = "aA:h:j:V:l:P:i:km:nNp:rsvt:H:T:u:U:w:W:?"; - - // options for controlling squidclient - static struct option basicOptions[] = { - /* These are the generic options for squidclient itself */ - {"help", no_argument, nullptr, '?'}, - {"verbose", no_argument, nullptr, 'v'}, - {"quiet", no_argument, nullptr, 's'}, - {"host", required_argument, nullptr, 'h'}, - {"local", required_argument, nullptr, 'l'}, - {"port", required_argument, nullptr, 'p'}, - {"ping", no_argument, nullptr, '\1'}, - {"https", no_argument, nullptr, '\3'}, - {nullptr, 0, nullptr, 0} - }; - - int c; - while ((c = getopt_long(argc, argv, shortOpStr, basicOptions, &optIndex)) != -1) { - - // modules parse their own specific options - switch (c) { - case '\1': - to_stdout = 0; - Ping::Config.parseCommandOpts(argc, argv, c, optIndex); - continue; - - case 'h': /* remote host */ - case 'l': /* local host */ - case 'p': /* port number */ - // rewind and let the Transport::Config parser handle - optind -= 2; - Transport::Config.parseCommandOpts(argc, argv, c, optIndex); - continue; - - case '\3': // request over a TLS connection - Transport::Config.parseCommandOpts(argc, argv, c, optIndex); - continue; - - default: // fall through to next switch - break; - } - - switch (c) { - - case '\0': // dummy value for end-of-options - break; - - case 'a': - opt_noaccept = 1; - break; - - case 'A': - useragent = optarg; - break; - - case 'j': - host = optarg; - break; - - case 'V': - version = optarg; - break; - - case 's': /* silent */ - to_stdout = false; - break; - - case 'k': /* backward compat */ - keep_alive = 1; - break; - - case 'r': /* reload */ - reload = true; - break; - - case 'P': - put_file = xstrdup(optarg); - break; - - case 'i': /* IMS */ - ims = (time_t) atoi(optarg); - break; - - case 'm': - method = xstrdup(optarg); - break; - - case 't': - method = xstrdup("TRACE"); - max_forwards = atoi(optarg); - break; - - case 'H': - if (strlen(optarg)) { - if (extra_hdrs) { - std::cerr << "ERROR: multiple -H options not supported. Discarding previous value." << std::endl; - xfree(extra_hdrs); - } - extra_hdrs = xstrdup(optarg); - shellUnescape(extra_hdrs); - } - break; - - case 'T': - Transport::Config.ioTimeout = atoi(optarg); - break; - - case 'u': - ProxyAuthorization.user = optarg; - break; - - case 'w': - ProxyAuthorization.password = optarg; - break; - - case 'U': - OriginAuthorization.user = optarg; - break; - - case 'W': - OriginAuthorization.password = optarg; - break; - - case 'n': -#if HAVE_GSSAPI - proxy_neg = 1; -#else - std::cerr << "ERROR: Negotiate authentication not supported." << std::endl; - usage(argv[0]); -#endif - break; - - case 'N': -#if HAVE_GSSAPI - www_neg = 1; -#else - std::cerr << "ERROR: Negotiate authentication not supported." << std::endl; - usage(argv[0]); -#endif - break; - - case 'v': - /* undocumented: may increase verb-level by giving more -v's */ - ++scParams.verbosityLevel; - debugVerbose(2, "verbosity level set to " << scParams.verbosityLevel); - break; - - case '?': /* usage */ - - default: - usage(argv[0]); - break; - } - } - if (ProxyAuthorization.password && !ProxyAuthorization.user) { - std::cerr << "ERROR: Proxy authentication password (-w) is given, but username (-u) is missing\n"; - exit(EXIT_FAILURE); - } - if (OriginAuthorization.password && !OriginAuthorization.user) { - std::cerr << "ERROR: WWW authentication password (-W) is given, but username (-U) is missing\n"; - exit(EXIT_FAILURE); - } - } -#if _SQUID_WINDOWS_ - { - WSADATA wsaData; - WSAStartup(2, &wsaData); - atexit(Win32SockCleanup); - } -#endif - /* Build the HTTP request */ - const char *pathPassword = nullptr; - if (strncmp(url, "mgr:", 4) == 0) { - char *t = xstrdup(url + 4); - // XXX: Bail on snprintf() failures - snprintf(url, sizeof(url), "http://%s:%hu/squid-internal-mgr/%s", Transport::Config.hostname, Transport::Config.port, t); - if (const auto at = strrchr(url, '@')) { - if (!OriginAuthorization.user) { - std::cerr << "ERROR: Embedding a password in a cache manager command requires " << - "providing a username with -U: mgr:" << t << std::endl; - exit(EXIT_FAILURE); - } - *at = 0; // send password in Authorization header, not URL - pathPassword = at + 1; // the now-removed embedded @password overwrites OriginAuthorization.password further below - } - xfree(t); - } - if (put_file) { - put_fd = open(put_file, O_RDONLY); - set_our_signal(); - - if (put_fd < 0) { - int xerrno = errno; - std::cerr << "ERROR: can't open file (" << xstrerr(xerrno) << ")" << std::endl; - exit(EXIT_FAILURE); - } -#if _SQUID_WINDOWS_ - setmode(put_fd, O_BINARY); -#endif - - if (fstat(put_fd, &sb) < 0) { - int xerrno = errno; - std::cerr << "ERROR: can't identify length of file (" << xstrerr(xerrno) << ")" << std::endl; - } - } - - if (!host) { - char *newhost = strstr(url, "://"); - if (newhost) { - char *t; - newhost += 3; - newhost = xstrdup(newhost); - t = newhost + strcspn(newhost, "@/?"); - if (*t == '@') { - newhost = t + 1; - t = newhost + strcspn(newhost, "@/?"); - } - *t = '\0'; - host = newhost; - } - } - - std::stringstream msg; - - if (version[0] == '-' || !version[0]) { - /* HTTP/0.9, no headers, no version */ - msg << method << " " << url << "\r\n"; - } else { - const auto versionImpliesHttp = xisdigit(version[0]); // is HTTP/n.n - msg << method << " " - << url << " " - << (versionImpliesHttp ? "HTTP/" : "") << version - << "\r\n"; - - if (host) { - msg << "Host: " << host << "\r\n"; - } - - if (!useragent) { - msg << "User-Agent: squidclient/" << VERSION << "\r\n"; - } else if (useragent[0] != '\0') { - msg << "User-Agent: " << useragent << "\r\n"; - } // else custom: no value U-A header - - if (reload) { - msg << "Cache-Control: no-cache\r\n"; - } - if (put_fd > 0) { - msg << "Content-length: " << sb.st_size << "\r\n"; - } - if (opt_noaccept == 0) { - msg << "Accept: */*\r\n"; - } - if (ims) { - msg << "If-Modified-Since: " << Time::FormatRfc1123(ims) << "\r\n"; - } - if (max_forwards > -1) { - msg << "Max-Forwards: " << max_forwards << "\r\n"; - } - if (ProxyAuthorization.user) - ProxyAuthorization.commit(msg); - if (OriginAuthorization.user) { - const auto savedPassword = OriginAuthorization.password; - if (pathPassword) - OriginAuthorization.password = pathPassword; - OriginAuthorization.commit(msg); - OriginAuthorization.password = savedPassword; // restore the global password setting - } -#if HAVE_GSSAPI - if (www_neg) { - if (host) { - const char *token = GSSAPI_token(host); - msg << "Proxy-Authorization: Negotiate " << token << "\r\n"; - delete[] token; - } else - std::cerr << "ERROR: server host missing" << std::endl; - } - if (proxy_neg) { - if (Transport::Config.hostname) { - const char *token = GSSAPI_token(Transport::Config.hostname); - msg << "Proxy-Authorization: Negotiate " << token << "\r\n"; - delete[] token; - } else - std::cerr << "ERROR: proxy server host missing" << std::endl; - } -#endif - - /* HTTP/1.0 may need keep-alive explicitly */ - if (strcmp(version, "1.0") == 0 && keep_alive) - msg << "Connection: keep-alive\r\n"; - - /* HTTP/1.1 may need close explicitly */ - if (!keep_alive) - msg << "Connection: close\r\n"; - - if (extra_hdrs) { - msg << extra_hdrs; - safe_free(extra_hdrs); - } - msg << "\r\n"; // empty line ends MIME header block - } - - msg.flush(); - const auto messageHeader = msg.str(); - debugVerbose(1, "Request:" << std::endl << messageHeader << std::endl << "."); - - uint32_t loops = Ping::Init(); - - for (uint32_t i = 0; loops == 0 || i < loops; ++i) { - size_t fsize = 0; - - if (!Transport::Connect()) - continue; - - /* Send the HTTP request */ - debugVerbose(2, "Sending HTTP request ... "); - bytesWritten = Transport::Write(messageHeader.data(), messageHeader.length()); - - if (bytesWritten < 0) { - std::cerr << "ERROR: write" << std::endl; - exit(EXIT_FAILURE); - } else if (static_cast(bytesWritten) != messageHeader.length()) { - std::cerr << "ERROR: Failed to send the following request: " << std::endl - << messageHeader << std::endl; - exit(EXIT_FAILURE); - } - debugVerbose(2, "done."); - - if (put_file) { - debugVerbose(1, "Sending HTTP request payload ..."); - int x; - if ((x = lseek(put_fd, 0, SEEK_SET)) < 0) { - int xerrno = errno; - std::cerr << "ERROR: lseek: " << xstrerr(xerrno) << std::endl; - - } else while ((x = read(put_fd, buf, sizeof(buf))) > 0) { - - x = Transport::Write(buf, x); - - total_bytes += x; - - if (x <= 0) - break; - } - - if (x != 0) - std::cerr << "ERROR: Cannot send file." << std::endl; - else - debugVerbose(1, "done."); - } - /* Read the data */ - -#if _SQUID_WINDOWS_ - setmode(1, O_BINARY); -#endif - - while ((len = Transport::Read(buf, sizeof(buf))) > 0) { - fsize += len; - - if (to_stdout && fwrite(buf, len, 1, stdout) != 1) { - int xerrno = errno; - std::cerr << "ERROR: writing to stdout: " << xstrerr(xerrno) << std::endl; - } - } - -#if USE_GNUTLS - if (Transport::Config.tlsEnabled) { - if (len == 0) { - std::cerr << "- Peer has closed the TLS connection" << std::endl; - } else if (!gnutls_error_is_fatal(len)) { - std::cerr << "WARNING: " << gnutls_strerror(len) << std::endl; - } else { - std::cerr << "ERROR: " << gnutls_strerror(len) << std::endl; - } - } -#endif - -#if _SQUID_WINDOWS_ - setmode(1, O_TEXT); -#endif - - Transport::CloseConnection(); - - if (Ping::LoopDone(i)) - break; - - Ping::TimerStop(fsize); - } - - Ping::DisplayStats(); - Transport::ShutdownTls(); - return EXIT_SUCCESS; -} - -void -pipe_handler(int) -{ - std::cerr << "SIGPIPE received." << std::endl; -} - -static void -set_our_signal(void) -{ -#if HAVE_SIGACTION - struct sigaction sa; - sa.sa_handler = pipe_handler; - sa.sa_flags = SA_RESTART; - sigemptyset(&sa.sa_mask); - - if (sigaction(SIGPIPE, &sa, nullptr) < 0) { - std::cerr << "ERROR: Cannot set PIPE signal." << std::endl; - exit(EXIT_FAILURE); - } -#else - signal(SIGPIPE, pipe_handler); -#endif -} - -- 2.39.5