diff --git a/configure.ac b/configure.ac index 44c5b88a320..77e07904042 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 d29658a56bd..e37da79ca5c 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 3a9c3530652..00000000000 --- 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 8f5a50639c1..00000000000 --- 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 66902ab78b8..00000000000 --- 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 e76c0ed66f7..00000000000 --- 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 b824e75ec46..00000000000 --- 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 08ed2e7edd8..00000000000 --- 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 bdb7c8f7fd6..00000000000 --- 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 -} -