Repository: trafficserver Updated Branches: refs/heads/master 2f6d6e0d0 -> 044da6999
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/domain-tree.cc ---------------------------------------------------------------------- diff --git a/plugins/experimental/ssl_cert_loader/domain-tree.cc b/plugins/experimental/ssl_cert_loader/domain-tree.cc new file mode 100644 index 0000000..b659193 --- /dev/null +++ b/plugins/experimental/ssl_cert_loader/domain-tree.cc @@ -0,0 +1,168 @@ +/** @file + SSL dynamic certificate loader + Loads certificates into a hash table as they are requested +*/ + +# include <stdio.h> +# include <memory.h> +# include <inttypes.h> +# include <alloca.h> +# include "domain-tree.h" + +// return true if comparable. Return type of compare in relative parameter +// 0 if eq. < 0 if node key is broader. > 0 if parameter key is broader +bool +DomainNameTree::DomainNameNode::compare(std::string key, int &relative) { + size_t star_loc = key.find("*"); + bool is_wild = false; + if (star_loc != std::string::npos) { + star_loc++; + is_wild = true; + key = key.substr(star_loc); + } + return this->prunedCompare(key, relative, is_wild); +} + +bool +DomainNameTree::DomainNameNode::prunedCompare(std::string key, int &relative, bool is_wild) { + if (key == this->key) { + relative = 0; + return true; + } + else { + if (this->is_wild) { + size_t loc = key.find(this->key); + if (this->key == "") { // Match all + relative = -1; + return true; + } + else if (loc != std::string::npos) { + // node key is in search key + if ((key.length() - this->key.length()) == loc) { + // And node key is at the end of search key + relative = -1; + return true; + } + } + } + if (is_wild) { + if (key == "") { // Match all + relative = 1; + return true; + } + else { + size_t loc = this->key.find(key); + if (loc != std::string::npos) { + if ((this->key.length() - key.length()) == loc) { + relative = 1; + return true; + } + } + } + } + } + return false; +} + +DomainNameTree::DomainNameNode *DomainNameTree::find(std::string key, bool best_match) { + DomainNameNode *retval = NULL; + DomainNameNode *first = NULL; + size_t star_loc = key.find("*"); + bool is_wild = false; + if (star_loc != std::string::npos) { + key = key.substr(star_loc+1); + is_wild = true; + } + + bool set_iter = false; + std::deque<DomainNameNode *>::iterator sibPtr; + DomainNameNode *current_node = root; + while (current_node != NULL) { + bool partial_match = false; + int relative; + if (current_node->prunedCompare(key, relative, is_wild)) { + if (relative == 0) { + retval = current_node; + if (NULL == first || retval->order < first->order) { + first = retval; + } + break; + } + else if (relative < 0) { + retval = current_node; + partial_match = true; + if (NULL == first || retval->order < first->order) { + first = retval; + } + } + } + if (partial_match) { + // Check out the children, maybe there is something better there + sibPtr = current_node->children.begin(); + set_iter = true; + if (sibPtr == current_node->children.end()) break; // We are done + current_node = *(sibPtr++); + } + else { // No match here. Look at next sibling? + // Is there another sibling to look at? + if (set_iter && sibPtr != current_node->children.end()) { + current_node = *(sibPtr++); + } + else { // No more siblings to check, give it up. + break; + } + } + } + return best_match ? retval : first; +} + +DomainNameTree::DomainNameNode * +DomainNameTree::insert(std::string key, void *payload, int order) { + TSMutexLock(this->tree_mutex); + DomainNameNode *retval = NULL; + DomainNameNode *node = this->findBestMatch(key); + int relative; + if (node->compare(key, relative)) { + size_t star_loc = key.find("*"); + bool is_wild = false; + if (star_loc != std::string::npos) { + star_loc++; + key = key.substr(star_loc); + is_wild = true; + } + if (relative < 0) { + // Make a new node that is a child of node + DomainNameNode *new_node = new DomainNameNode(key, payload, order, is_wild); + new_node->parent = node; + node->children.push_back(new_node); + retval = new_node; + } + else if (relative > 0) { + // Insert new node as parent of node + DomainNameNode *new_node = new DomainNameNode(key, payload, order, is_wild); + new_node->parent = node->parent; + new_node->children.push_back(node); + // Replace the node with new_node in the child list of the parent; + std::deque<DomainNameNode *>::iterator iter = node->parent->children.begin(); + for (; iter != node->parent->children.end(); ++iter) { + if (*(iter) == node) { + *(iter) = new_node; + } + } + retval = new_node; + } + // Will not replace in the equal case + // Unless this is the root node + else { + if (node->key == "" && node->order == 0x7fffffff){ + node->key = key; + node->payload = payload; + node->order = order; + retval = node; + } + } + } + TSMutexUnlock(this->tree_mutex); + return retval; +} + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/domain-tree.h ---------------------------------------------------------------------- diff --git a/plugins/experimental/ssl_cert_loader/domain-tree.h b/plugins/experimental/ssl_cert_loader/domain-tree.h new file mode 100644 index 0000000..6f524d2 --- /dev/null +++ b/plugins/experimental/ssl_cert_loader/domain-tree.h @@ -0,0 +1,66 @@ +# include <string> +# include <deque> +# include <ts/ts.h> + +class DomainNameTree { +public: + + class DomainNameNode { + public: + DomainNameNode() : order(-1), payload(NULL), parent(NULL), is_wild(false) + { + } + DomainNameNode(std::string key, void *payload, int order, bool is_wild) : key(key), order(order), payload(payload), parent(NULL), is_wild(is_wild) + { + } + DomainNameNode *match(std::string value); + ~DomainNameNode() { + std::deque<DomainNameNode *>::iterator iter = children.begin(); + for (; iter != children.end(); iter++) { + delete *iter; + } + } + // return true if comparable. Return type of compare in relative parameter + // 0 if eq. < 0 if node key is broader. > 0 if parameter key is broader + bool compare(std::string key, int &relative); + // The wildcard is pruned out of the key + bool prunedCompare(std::string key, int &relative, bool is_wild); + std::string key; // The string trailing the * (if any) + int order; // Track insert order for conflict resolution + void *payload; + std::deque<DomainNameNode *> children; + DomainNameNode *parent; + bool is_wild; + private: + }; + + DomainNameTree() { + root = new DomainNameNode(); + root->key = ""; + root->order = 0x7FFFFFFF; + root->is_wild = true; + tree_mutex = TSMutexCreate(); + } + ~DomainNameTree() { + if (root != NULL) { delete root; } + } + DomainNameNode *findBestMatch(std::string key) { + TSMutexLock(this->tree_mutex); + DomainNameNode *retval = this->find(key, true); + TSMutexUnlock(this->tree_mutex); + return retval; + } + DomainNameNode *findFirstMatch(std::string key) { + TSMutexLock(this->tree_mutex); + DomainNameNode *retval = this->find(key, false); + TSMutexUnlock(this->tree_mutex); + return retval; + } + DomainNameNode *find(std::string key, bool best_match); + DomainNameNode *insert(std::string key, void *payload, int order); + +private: + DomainNameNode *root; + TSMutex tree_mutex; +}; + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/ssl-cert-loader.cc ---------------------------------------------------------------------- diff --git a/plugins/experimental/ssl_cert_loader/ssl-cert-loader.cc b/plugins/experimental/ssl_cert_loader/ssl-cert-loader.cc new file mode 100644 index 0000000..54a6603 --- /dev/null +++ b/plugins/experimental/ssl_cert_loader/ssl-cert-loader.cc @@ -0,0 +1,541 @@ +/** @file + SSL dynamic certificate loader + Loads certificates into a hash table as they are requested +*/ + +# include <stdio.h> +# include <memory.h> +# include <inttypes.h> +# include <ts/ts.h> +# include <tsconfig/TsValue.h> +# include <alloca.h> +# include <openssl/ssl.h> +# include <openssl/x509.h> +# include <openssl/x509v3.h> +# include <ts/ink_inet.h> +# include <ts/IpMap.h> +# include "domain-tree.h" + +using ts::config::Configuration; +using ts::config::Value; + +# define PN "ssl-cert-loader" +# define PCP "[" PN " Plugin] " + +namespace { + +class CertLookup { +public: + DomainNameTree tree; + IpMap ipmap; +} Lookup; + +class SslEntry { +public: + SslEntry() : ctx(NULL), op(TS_SSL_HOOK_OP_DEFAULT) + { + this->mutex = TSMutexCreate(); + } + ~SslEntry() { + } + SSL_CTX *ctx; + TSSslVConnOp op; + // If the CTX is not already created, use these + // files to load things up + std::string certFileName; + std::string keyFileName; + TSMutex mutex; + std::deque<TSVConn> waitingVConns; +}; + +std::string ConfigPath; +typedef std::pair<IpAddr, IpAddr> IpRange; +typedef std::deque<IpRange> IpRangeQueue; + +Configuration Config; // global configuration + +void +Parse_Addr_String(ts::ConstBuffer const &text, IpRange &range) { + IpAddr newAddr; + std::string textstr(text._ptr, text._size); + // Is there a hyphen? + size_t hyphen_pos = textstr.find("-"); + if (hyphen_pos != std::string::npos) { + std::string addr1 = textstr.substr(0, hyphen_pos); + std::string addr2 = textstr.substr(hyphen_pos+1); + range.first.load(ts::ConstBuffer(addr1.c_str(), addr1.length())); + range.second.load(ts::ConstBuffer(addr2.c_str(), addr2.length())); + } + else { // Assume it is a single address + newAddr.load(text); + range.first = newAddr; + range.second = newAddr; + } +} + +int +Load_Config_File() { + ts::Rv<Configuration> cv = Configuration::loadFromPath(ConfigPath.c_str()); + if (!cv.isOK()) { + char error_buffer[1024]; + cv._errata.write(error_buffer, sizeof(error_buffer), 0, 0, 0, ""); + TSDebug("skh-cert","Failed to parse %s as TSConfig format", ConfigPath.c_str()); + TSError(PCP "Failed to parse %s as TSConfig format", ConfigPath.c_str()); + TSDebug("skh-cert", "Errors: %s", error_buffer); + return -1; + } + Config = cv; + return 1; +} + +struct ParsedSslValues { + std::string server_priv_key_file; + std::string server_name; + std::string server_cert_name; + std::string action; + IpRangeQueue server_ips; +}; + +void +Parse_Config_Rules(Value &parent, ParsedSslValues &orig_values); + +int +Load_Configuration_Args(int argc, const char *argv[]) { +ts::ConstBuffer text; + std::string s; // temp holder. + TSMgmtString config_path = NULL; + + // get the path to the config file if one was specified + static char const * const CONFIG_ARG = "--config="; + int arg_idx; + for (arg_idx = 0; arg_idx < argc; arg_idx++) { + if (0 == memcmp(argv[arg_idx], CONFIG_ARG, strlen(CONFIG_ARG))) { + config_path = TSstrdup(argv[arg_idx] + strlen(CONFIG_ARG)); + TSDebug(PN, "Found config path %s", config_path); + } + } + if (NULL == config_path) { + static char const * const DEFAULT_CONFIG_PATH = "ssl_start.cfg"; + config_path = TSstrdup(DEFAULT_CONFIG_PATH); + TSDebug(PN, "No config path set in arguments, using default: %s", DEFAULT_CONFIG_PATH); + } + + // translate relative paths to absolute + if (config_path[0] != '/') { + ConfigPath = std::string(TSConfigDirGet()) + '/' + std::string(config_path); + } else { + ConfigPath = config_path; + } + + TSDebug("skh-cert", "Load from %s", ConfigPath.c_str()); + // free up the path + TSfree(config_path); + return 0; +} + +int +Load_Configuration() { + int ret = Load_Config_File(); + if (ret != 0) { + TSError(PCP "Failed to load the config file, check debug output for errata"); + } + + Value root = Config.getRoot(); + Value val = root["runtime-table-size"]; + if (val.isLiteral()) { + // Not evicting yet + } + val = root["ssl-server-match"]; + if (val.isContainer()) { + ParsedSslValues values; + Parse_Config_Rules(val, values); + } + + // Test values + DomainNameTree::DomainNameNode *node = Lookup.tree.findFirstMatch("calendar.google.com"); + TSDebug("skh-cert", "Found node with key=%s and order=%d", node->key.c_str(), node->order); + node = Lookup.tree.findFirstMatch("www.buseyil.com"); + TSDebug("skh-cert", "Found node with key=%s and order=%d", node->key.c_str(), node->order); + + IpAddr key_ip; + key_ip.load(ts::ConstBuffer("107.23.60.186", strlen("107.23.60.186"))); + IpEndpoint key_endpoint; + key_endpoint.assign(key_ip); + void *payload; + if (Lookup.ipmap.contains(&key_endpoint, &payload)) { + TSDebug("skh-cert", "Found %p for 107.23.60.186", payload); + } + else { + TSDebug("skh-cert", "Found nothing for 107.23.60.186"); + } + + return 0; +} + +SSL_CTX * +Load_Certificate(SslEntry const *entry, std::deque<std::string> &names) { + const SSL_METHOD *meth = SSLv23_client_method(); + SSL_CTX *retval = SSL_CTX_new(meth); + X509* cert = NULL; + if (entry->certFileName.length() > 0) { + // Must load the cert file to fetch the names out later + BIO *cert_bio = BIO_new_file(entry->certFileName.c_str(), "r"); + cert = PEM_read_bio_X509_AUX(cert_bio, NULL, NULL, NULL); + BIO_free(cert_bio); + + if (SSL_CTX_use_certificate(retval, cert) < 1) { + TSDebug("skh-cert", "Failed to load cert file %s", entry->certFileName.c_str()); + SSL_CTX_free(retval); + return NULL; + } + } + if (entry->keyFileName.length() > 0) { + if (!SSL_CTX_use_PrivateKey_file(retval, entry->keyFileName.c_str(), SSL_FILETYPE_PEM)) { + TSDebug("skh-cert", "Failed to load priv key file %s", entry->keyFileName.c_str()); + SSL_CTX_free(retval); + return NULL; + } + } + + // Fetch out the names associated with the certificate + if (cert != NULL) { + X509_NAME *name = X509_get_subject_name(cert); + char subjectCn[256]; + if (X509_NAME_get_text_by_NID(name, NID_commonName, subjectCn, sizeof(subjectCn)) >= 0) { + std::string tmp_name(subjectCn); + names.push_back(tmp_name); + } + // Look for alt names + GENERAL_NAMES *alt_names = (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if (alt_names) { + unsigned count = sk_GENERAL_NAME_num(alt_names); + for (unsigned i = 0; i < count; i++) { + GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, i); + + if (alt_name->type == GEN_DNS) { + // Current name is a DNS name, let's check it + char *name_ptr = (char *) ASN1_STRING_data(alt_name->d.dNSName); + std::string tmp_name(name_ptr); + names.push_back(tmp_name); + } + } + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); + } + } + + // Do we need to free cert? Did assigning to SSL_CTX increment its ref count + return retval; +} + +/* + * Load the config information about the terminal config. + * Only load the certificate if no server name or ip is specified + */ +SslEntry * +Load_Certificate_Entry(ParsedSslValues const &values, std::deque<std::string> &names) { + SslEntry *retval = NULL; + std::string cert_file_path; + std::string priv_file_path; + retval = new SslEntry(); + if (values.server_cert_name.length() > 0) { + if (values.server_cert_name[0] != '/') { + cert_file_path = std::string(TSConfigDirGet()) + '/' + values.server_cert_name; + } + else { + cert_file_path = values.server_cert_name; + } + retval->certFileName = cert_file_path; + } + if (values.server_priv_key_file.length() > 0) { + if (values.server_priv_key_file[0] != '/') { + priv_file_path = std::string(TSConfigDirGet()) + '/' + values.server_priv_key_file; + } + else { + priv_file_path = values.server_priv_key_file; + } + retval->keyFileName = priv_file_path; + } + // Must go ahead and load the cert to get the names + if (values.server_name.length() == 0 && + values.server_ips.size() == 0) { + retval->ctx = Load_Certificate(retval, names); + } + if (values.action.length() > 0) { + if (values.action == "tunnel") { + retval->op = TS_SSL_HOOK_OP_TUNNEL; + } + else if (values.action == "teriminate") { + retval-> op = TS_SSL_HOOK_OP_TERMINATE; + } + } + return retval; +} + +int Parse_order = 0; + +void +Parse_Config(Value &parent, ParsedSslValues &orig_values) { + ParsedSslValues cur_values(orig_values); + Value val = parent.find("ssl-key-name"); + if (val.hasValue()) { + cur_values.server_priv_key_file = std::string(val.getText()._ptr, val.getText()._size); + } + val = parent.find("server-ip"); + if (val) { + IpRange ipRange; + Parse_Addr_String(val.getText(), ipRange); + cur_values.server_ips.push_back(ipRange); + } + val = parent.find("server-name"); + if (val) { + cur_values.server_name = std::string(val.getText()._ptr, val.getText()._size); + } + val = parent.find("server-cert-name"); + if (val) { + cur_values.server_cert_name = std::string(val.getText()._ptr, val.getText()._size); + } + val = parent.find("action"); + if (val) { + cur_values.action = std::string(val.getText()._ptr, val.getText()._size); + } + + val = parent.find("child-match"); + if (val) { + Parse_Config_Rules(val, cur_values); + } + else { // We are terminal, enter a match case + TSDebug("skh-cert", "Terminal SSL Config: server_priv_key_file=%s server_name=%s server_cert_name=%s action=%s", + cur_values.server_priv_key_file.c_str(), + cur_values.server_name.c_str(), + cur_values.server_cert_name.c_str(), + cur_values.action.c_str() + ); + // Load the certificate and create a context if appropriate + std::deque<std::string> cert_names; + SslEntry *entry = Load_Certificate_Entry(cur_values, cert_names); + + // Store in appropriate table + if (cur_values.server_name.length() > 0) { + Lookup.tree.insert(cur_values.server_name, entry, Parse_order++); + } + if (cur_values.server_ips.size() > 0) { + size_t i; + for (i = 0; i < cur_values.server_ips.size(); i++) { + IpEndpoint first, second; + first.assign(cur_values.server_ips[i].first); + second.assign(cur_values.server_ips[i].second); + Lookup.ipmap.fill(&first, &second, entry); + char val1[256], val2[256]; + cur_values.server_ips[i].first.toString(val1, sizeof(val1)); + cur_values.server_ips[i].second.toString(val2, sizeof(val2)); + } + } + if (entry != NULL) { + size_t i; + for (i = 0; i < cert_names.size(); i++) { + Lookup.tree.insert(cert_names[i], entry, Parse_order++); + } + } + } +} + +void +Parse_Config_Rules(Value &parent, ParsedSslValues &orig_values) { + size_t i; + for (i = 0; i < parent.childCount(); i++) { + Value child = parent[i]; + Parse_Config(child, orig_values); + } +} + +void * +Load_Certificate_Thread(void *arg) { + SslEntry *entry = reinterpret_cast<SslEntry*>(arg); + + TSMutexLock(entry->mutex); + if (entry->ctx == NULL) { + // Must load certificate + std::deque<std::string> cert_names; + entry->ctx = Load_Certificate(entry, cert_names); + while (entry->waitingVConns.begin() != entry->waitingVConns.end()) { + TSVConn vc = entry->waitingVConns.back(); + entry->waitingVConns.pop_back(); + TSSslConnection sslobj = TSVConnSSLConnectionGet(vc); + SSL *ssl = reinterpret_cast<SSL *>(sslobj); + SSL_set_SSL_CTX(ssl, entry->ctx); + TSVConnReenable(vc); + } + TSMutexUnlock(entry->mutex); + size_t i; + for (i = 0; i < cert_names.size(); i++) { + Lookup.tree.insert(cert_names[i], entry, Parse_order++); + } + } + else { + TSMutexUnlock(entry->mutex); + } + return (void *)1; +} + +int +CB_Life_Cycle(TSCont , TSEvent , void *) { + // By now the SSL library should have been initialized, + // We can safely parse the config file and load the ctx tables + Load_Configuration(); + return TS_SUCCESS; +} + +int +CB_Pre_Accept(TSCont /*contp*/, TSEvent event, void *edata) { + TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata); + IpAddr ip(TSNetVConnLocalAddrGet(ssl_vc)); + char buff[INET6_ADDRSTRLEN]; + IpAddr ip_client(TSNetVConnRemoteAddrGet(ssl_vc)); + char buff2[INET6_ADDRSTRLEN]; + + TSDebug("skh-cert", "Pre accept callback %p - event is %s, target address %s, client address %s" + , ssl_vc + , event == TS_EVENT_VCONN_PRE_ACCEPT ? "good" : "bad" + , ip.toString(buff, sizeof(buff)) + , ip_client.toString(buff2, sizeof(buff2)) + ); + + // Is there a cert already defined for this IP? + // + IpEndpoint key_endpoint; + key_endpoint.assign(ip); + void *payload; + if (Lookup.ipmap.contains(&key_endpoint, &payload)) { + // Set the stored cert on this SSL object + TSSslConnection sslobj = TSVConnSSLConnectionGet(ssl_vc); + SSL *ssl = reinterpret_cast<SSL *>(sslobj); + SslEntry *entry = reinterpret_cast<SslEntry *>(payload); + TSMutexLock(entry->mutex); + if (entry->op == TS_SSL_HOOK_OP_TUNNEL || + entry->op == TS_SSL_HOOK_OP_TERMINATE) { + // Push everything to blind tunnel, or terminate + if (entry->op == TS_SSL_HOOK_OP_TUNNEL) { + TSVConnTunnel(ssl_vc); + } + TSMutexUnlock(entry->mutex); + } + else { + if (entry->ctx == NULL) { + if (entry->waitingVConns.begin() == entry->waitingVConns.end()) { + entry->waitingVConns.push_back(ssl_vc); + TSMutexUnlock(entry->mutex); + + TSThreadCreate(Load_Certificate_Thread, entry); + } + else { // Just add yourself to the queue + entry->waitingVConns.push_back(ssl_vc); + TSMutexUnlock(entry->mutex); + } + // Return before we reenable + return TS_SUCCESS; + } + else { // if (entry->ctx != NULL) { + SSL_set_SSL_CTX(ssl, entry->ctx); + TSDebug("skh-cert", "Replace cert based on IP"); + TSMutexUnlock(entry->mutex); + } + } + } + + // All done, reactivate things + TSVConnReenable(ssl_vc); + return TS_SUCCESS; +} + +int +CB_servername(TSCont /*contp*/, TSEvent /*event*/, void *edata) { + TSVConn ssl_vc = reinterpret_cast<TSVConn>(edata); + TSSslConnection sslobj = TSVConnSSLConnectionGet(ssl_vc); + SSL *ssl = reinterpret_cast<SSL *>(sslobj); + const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (servername != NULL) { + // Is there a certificated loaded up for this name + DomainNameTree::DomainNameNode *node = Lookup.tree.findFirstMatch(servername); + if (node != NULL && node->payload != NULL) { + SslEntry *entry = reinterpret_cast<SslEntry *>(node->payload); + if (entry->op == TS_SSL_HOOK_OP_TUNNEL || + entry->op == TS_SSL_HOOK_OP_TERMINATE) { + // Push everything to blind tunnel + if (entry->op == TS_SSL_HOOK_OP_TUNNEL) { + TSVConnTunnel(ssl_vc); + } + // Make sure we stop out of the SNI callback + // So return before re-enabling the SSL connection + return TS_SUCCESS; + } + TSMutexLock(entry->mutex); + if (entry->ctx == NULL) { + // Spawn off a thread to load a potentially expensive certificate + if (entry->waitingVConns.begin() == entry->waitingVConns.end()) { + entry->waitingVConns.push_back(ssl_vc); + TSMutexUnlock(entry->mutex); + TSThreadCreate(Load_Certificate_Thread, entry); + } + else { // Just add yourself to the queue + entry->waitingVConns.push_back(ssl_vc); + TSMutexUnlock(entry->mutex); + } + // Won't reenable until the certificate has been loaded + return TS_SUCCESS; + } + else { //if (entry->ctx != NULL) { + SSL_set_SSL_CTX(ssl, entry->ctx); + TSDebug("skh-cert", "Replace cert based on name %s", servername); + } + TSMutexUnlock(entry->mutex); + } + } + // All done, reactivate things + TSVConnReenable(ssl_vc); + return TS_SUCCESS; +} + +} // Anon namespace + +// Called by ATS as our initialization point +void +TSPluginInit(int argc, const char *argv[]) { + bool success = false; + TSPluginRegistrationInfo info; + TSCont cb_pa = 0; // pre-accept callback continuation + TSCont cb_lc = 0; // life cycle callback continuuation + TSCont cb_sni = 0; // SNI callback continuuation + + info.plugin_name = const_cast<char*>("SSL Certificate Loader"); + info.vendor_name = const_cast<char*>("Network Geographics"); + info.support_email = const_cast<char*>("[email protected]"); + + if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_2_0, &info)) { + TSError(PCP "registration failed."); + } else if (TSTrafficServerVersionGetMajor() < 2) { + TSError(PCP "requires Traffic Server 2.0 or later."); + } else if (0 > Load_Configuration_Args(argc, argv)) { + TSError(PCP "Failed to load config file."); + } else if (0 == (cb_pa = TSContCreate(&CB_Pre_Accept, TSMutexCreate()))) { + TSError(PCP "Failed to pre-accept callback."); + } else if (0 == (cb_lc = TSContCreate(&CB_Life_Cycle, TSMutexCreate()))) { + TSError(PCP "Failed to lifecycle callback."); + } else if (0 == (cb_sni = TSContCreate(&CB_servername, TSMutexCreate()))) { + TSError(PCP "Failed to create SNI callback."); + } else { + TSLifecycleHookAdd(TS_LIFECYCLE_PORTS_INITIALIZED_HOOK, cb_lc); + TSHttpHookAdd(TS_VCONN_PRE_ACCEPT_HOOK, cb_pa); + TSHttpHookAdd(TS_SSL_SNI_HOOK, cb_sni); + success = true; + } + + if (!success) { + if (cb_pa) TSContDestroy(cb_pa); + if (cb_lc) TSContDestroy(cb_lc); + TSError(PCP "not initialized"); + } + TSDebug(PN, "Plugin %s", success ? "online" : "offline"); + + return; +} + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/ssl_cert_loader.cfg ---------------------------------------------------------------------- diff --git a/plugins/experimental/ssl_cert_loader/ssl_cert_loader.cfg b/plugins/experimental/ssl_cert_loader/ssl_cert_loader.cfg new file mode 100644 index 0000000..542d83b --- /dev/null +++ b/plugins/experimental/ssl_cert_loader/ssl_cert_loader.cfg @@ -0,0 +1,135 @@ +// Describe how proxy should deal with the handshake process of SSL +// connections passing though. +version = "0.1"; + +// Once the certificate load table surpasses this limit +// Start evicting already loaded certificates +runtime-table-size = "10000"; + +// By default the proxy will participate in the handshake, so it needs +// to have a certificate that it uses to handshake with the client, and +// potentially another certificate to handshake with the origin server +// In this case, the config must specify both the matching traffic conditions +// and the connection attributes (certificates, tickets, etc). + +// Alternatively the proxy could be directed to get out of the way and +// blind tunnel the SSL connection. There are no options to set in this +// case. Only specify the matching traffic. + +// There are two rule lists. One that matches on characteristics of the +// origin server. +// Another that matches on characteristics of the client. + +// The attributes specified by matches in both rule lists will be used +// to process the connection. If either rule list specifies a "tunnel" action +// the connection will be blind tunneled. If either rule specifies a "deny" +// action, the handshake processing will stop + +// Each rule list will be evaluated in order and stop on a match + +ssl-server-match = +( + { server-ip = "192.168.56.0-192.168.56.255"; + action="proxy"; /* Default if no action is specified */ + server-cert = "server1.pem"; + ssl-key-name = "privkey.pem"; + ssl-ticket-key-name = ( "ticket-key.blob", "ticket-key-old.blob" ); + child-match = + // Doing something different for a couple servers in the range + { server-ip = "192.168.56.45"; + server-cert = "server11.pem"; + }, + { server-ip = "192.168.56.46"; + server-cert = "server12.pem"; + }, + ); + }, + { server-name = "example.com"; + server-cert = "server2.pem"; + ssl-key-name = "privkey.pem"; + }, + { + /* Could include characteristics of the offered ciphers in + evaluating the actions */ + offered-cipher-constraint = { + compare-op = "not-include"; + ciphers = ("3DES", "null"); + }; + server-cert = "server6.pem"; + ssl-key-name = "privkey.pem"; + }, + + { + /* Default, if no server-ip or server-name is specified, + use the name in the certificate */ + server-name = "@"; + action="proxy"; + server-cert = "server3.pem"; + ssl-key-name = "privkey.pem"; + }, + { + /* With the defaults, devolves to the base case of the + ssl_multicert.config */ + server-cert = "server5.pem"; + ssl-key-name = "privkey.pem"; + }, + /* You can specify the SSL options (non matching state) + in a separate entry and refer to it. Anything specified + directly in the match rule will override the ssl-options + */ + { server-ip = "192.168.100.0-192.168.100.100"; + ssl-options = <ssl-actions.ssl-info-1>; + }, + { server-ip = "192.168.110.0-192.168.110.100"; + ssl-options = <ssl-actions.ssl-info-1>; + }, + { server-name = "*.yahoo.com"; + action="tunnel"; + }, + /* All other traffic should be blind tunneled */ + { server-ip = "0.0.0.0-255.255.255.255"; + action = "tunnel"; + } +); + +ssl-client-match: +( + // In all cases, use the same cipher-suites + { use-client-cipher-suite = < cipher-suites.ecdhe >; + child-match = + ( + /* Traffic from lab should be blind tunneled */ + { client-ip = ("10.10.10.0-10.10.10.255", "10.10.50.152"); + action = "tunnel"; + } + /* Clients from the office must use an authorized client certificate + Proxy must verify. Must specify cert to use with origin server */ + { client-ip = "10.10.20.0-10.10.20.255"; + client-cert = "valid"; + /* Specify, or rely on your system settings for cert verification */ + ssl-signer-cert-name = "signers.pem"; + client-cert-name = "client1.pem"; + priv-key-name = "client-priv-key.pem"; + }, + /* Clients from the other office are not yet set up with client + certs. But must provide client certs for the origin server */ + { client-ip = "10.10.30.0-10.10.30.255"; + /* Could be present or not, don't care, this is the default + Other options are valid, present, or none + */ + client-cert = "unknown"; + client-cert-name = "client1.pem"; + priv-key-name = "client-priv-key.pem"; + }, + } +); + + +ssl-actions = +{ + ssl-info-1 = { + server-cert = "server4.pem"; + ssl-key-name = "privkey.pem"; + }; +}; + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/plugins/experimental/ssl_cert_loader/ssl_start.cfg ---------------------------------------------------------------------- diff --git a/plugins/experimental/ssl_cert_loader/ssl_start.cfg b/plugins/experimental/ssl_cert_loader/ssl_start.cfg new file mode 100644 index 0000000..daff753 --- /dev/null +++ b/plugins/experimental/ssl_cert_loader/ssl_start.cfg @@ -0,0 +1,55 @@ +// Describe how proxy should deal with the handshake process of SSL +// connections passing though. +version = "0.1" + +// Once the certificate load table surpasses this limit +// Start evicting already loaded certificates +runtime-table-size = "10000" + +// By default the proxy will participate in the handshake, so it needs +// to have a certificate that it uses to handshake with the client, and +// potentially another certificate to handshake with the origin server +// In this case, the config must specify both the matching traffic conditions +// and the connection attributes (certificates, tickets, etc). + +// Alternatively the proxy could be directed to get out of the way and +// blind tunnel the SSL connection. There are no options to set in this +// case. Only specify the matching traffic. + +// There are two rule lists. One that matches on characteristics of the +// origin server. +// Another that matches on characteristics of the client. + +// The attributes specified by matches in both rule lists will be used +// to process the connection. If either rule list specifies a "tunnel" action +// the connection will be blind tunneled. If either rule specifies a "deny" +// action, the handshake processing will stop + +// Each rule list will be evaluated in order and stop on a match + +ssl-server-match: +( + // Using the same private key for all of my certs + { ssl-key-name = "privkey.pem"; + child-match: + ( + { server-ip = "107.23.60.186"; + action = "tunnel"; + }, + { server-cert-name = "safelyfiled.pem; + }, + { server-cert-name = "asba.pem; + }, + { server-name = "www.yahoo.com"; + server-cert-name = "asba.pem; + }, + { server-cert-name = "buseyil.pem; + }, + { server-cert-name = "busey.pem; + }, + { server-cert-name = "wildgoogle.pem"; + } + ); + } +) + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/InkAPI.cc ---------------------------------------------------------------------- diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc index f03e848..f7c8927 100644 --- a/proxy/InkAPI.cc +++ b/proxy/InkAPI.cc @@ -363,6 +363,7 @@ tsapi int TS_HTTP_LEN_PUSH; tsapi const TSMLoc TS_NULL_MLOC = (TSMLoc)NULL; HttpAPIHooks *http_global_hooks = NULL; +SslAPIHooks *ssl_hooks = NULL; LifecycleAPIHooks* lifecycle_hooks = NULL; ConfigUpdateCbTable *global_config_cbs = NULL; @@ -644,6 +645,14 @@ sdk_sanity_check_lifecycle_hook_id(TSLifecycleHookID id) } TSReturnCode +sdk_sanity_check_ssl_hook_id(TSHttpHookID id) +{ + if (id<TS_SSL_FIRST_HOOK || id> TS_SSL_LAST_HOOK) + return TS_ERROR; + return TS_SUCCESS; +} + +TSReturnCode sdk_sanity_check_null_ptr(void *ptr) { if (ptr == NULL) @@ -1589,6 +1598,7 @@ api_init() TS_HTTP_LEN_S_MAXAGE = HTTP_LEN_S_MAXAGE; http_global_hooks = new HttpAPIHooks; + ssl_hooks = new SslAPIHooks; lifecycle_hooks = new LifecycleAPIHooks; global_config_cbs = new ConfigUpdateCbTable; @@ -4378,10 +4388,19 @@ TSContMutexGet(TSCont contp) void TSHttpHookAdd(TSHttpHookID id, TSCont contp) { + INKContInternal *icontp; sdk_assert(sdk_sanity_check_continuation(contp) == TS_SUCCESS); sdk_assert(sdk_sanity_check_hook_id(id) == TS_SUCCESS); - http_global_hooks->append(id, (INKContInternal *)contp); + icontp = reinterpret_cast<INKContInternal*>(contp); + + if (id >= TS_SSL_FIRST_HOOK && id <= TS_SSL_LAST_HOOK) { + TSSslHookInternalID internalId = static_cast<TSSslHookInternalID>(id - TS_SSL_FIRST_HOOK); + ssl_hooks->append(internalId , icontp); + } + else { // Follow through the regular HTTP hook framework + http_global_hooks->append(id, icontp); + } } void @@ -8632,3 +8651,111 @@ TSHttpEventNameLookup(TSEvent event) { return HttpDebugNames::get_event_name(static_cast<int>(event)); } + +/// Re-enable SSL VC. +class TSSslCallback : public Continuation +{ +public: + TSSslCallback(SSLNetVConnection *vc) + : Continuation(vc->mutex), m_vc(vc) + { + SET_HANDLER(&TSSslCallback::event_handler); + } + + int event_handler(int, void*) + { + m_vc->reenable(m_vc->nh); + delete this; + return 0; + } + +private: + SSLNetVConnection* m_vc; +}; + + +/// SSL Hooks +TSReturnCode +TSVConnTunnel(TSVConn sslp) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection*>(sslp); + SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection*>(vc); + TSReturnCode zret = TS_SUCCESS; + if (0 != ssl_vc) { + ssl_vc->hookOpRequested = TS_SSL_HOOK_OP_TUNNEL; + } else { + zret = TS_ERROR; + } + return zret; +} + +TSSslConnection +TSVConnSSLConnectionGet(TSVConn sslp) +{ + TSSslConnection ssl = NULL; + NetVConnection *vc = reinterpret_cast<NetVConnection*>(sslp); + SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection*>(vc); + if (ssl_vc != NULL) { + ssl = reinterpret_cast<TSSslConnection>(ssl_vc->ssl); + } + return ssl; +} + +tsapi TSSslContext TSSslContextFindByName(const char *name) +{ + TSSslContext ret = NULL; + SSLCertLookup *lookup = SSLCertificateConfig::acquire(); + if (lookup != NULL) { + SSLCertContext *cc = lookup->find(name); + if (cc && cc->ctx) { + ret = reinterpret_cast<TSSslContext>(cc->ctx); + } + SSLCertificateConfig::release(lookup); + } + return ret; +} +tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const* addr) +{ + TSSslContext ret = NULL; + SSLCertLookup *lookup = SSLCertificateConfig::acquire(); + if (lookup != NULL) { + IpEndpoint ip; + ip.assign(addr); + SSLCertContext *cc = lookup->find(ip); + if (cc && cc->ctx) { + ret = reinterpret_cast<TSSslContext>(cc->ctx); + } + SSLCertificateConfig::release(lookup); + } + return ret; +} + +tsapi int TSVConnIsSsl(TSVConn sslp) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection*>(sslp); + SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection*>(vc); + return ssl_vc != NULL; +} + +void +TSVConnReenable(TSVConn vconn) +{ + NetVConnection *vc = reinterpret_cast<NetVConnection *>(vconn); + SSLNetVConnection *ssl_vc = dynamic_cast<SSLNetVConnection*>(vc); + // We really only deal with a SSLNetVConnection at the moment + if (ssl_vc != NULL) { + EThread *eth = this_ethread(); + + // We use the VC mutex so we don't need to reschedule again if we + // can't get the lock. For this reason we need to execute the + // callback on the VC thread or it doesn't work (not sure why - + // deadlock or it ends up interacting with the wrong NetHandler). + MUTEX_TRY_LOCK(trylock, ssl_vc->mutex, eth); + if (!trylock) { + ssl_vc->thread->schedule_imm(new TSSslCallback(ssl_vc)); + } else { + ssl_vc->reenable(ssl_vc->nh); + } + } +} + http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/InkAPIInternal.h ---------------------------------------------------------------------- diff --git a/proxy/InkAPIInternal.h b/proxy/InkAPIInternal.h index 49725b9..08664f1 100644 --- a/proxy/InkAPIInternal.h +++ b/proxy/InkAPIInternal.h @@ -290,6 +290,17 @@ class HttpAPIHooks : public FeatureAPIHooks<TSHttpHookID, TS_HTTP_LAST_HOOK> { }; +typedef enum { + TS_SSL_INTERNAL_FIRST_HOOK, + TS_VCONN_PRE_ACCEPT_INTERNAL_HOOK = TS_SSL_INTERNAL_FIRST_HOOK, + TS_SSL_SNI_INTERNAL_HOOK, + TS_SSL_INTERNAL_LAST_HOOK +} TSSslHookInternalID; + +class SslAPIHooks : public FeatureAPIHooks<TSSslHookInternalID, TS_SSL_INTERNAL_LAST_HOOK> +{ +}; + class LifecycleAPIHooks : public FeatureAPIHooks<TSLifecycleHookID, TS_LIFECYCLE_LAST_HOOK> { }; @@ -344,6 +355,7 @@ void api_init(); extern HttpAPIHooks *http_global_hooks; extern LifecycleAPIHooks* lifecycle_hooks; +extern SslAPIHooks* ssl_hooks; extern ConfigUpdateCbTable *global_config_cbs; #endif /* __INK_API_INTERNAL_H__ */ http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/api/ts/ts.h ---------------------------------------------------------------------- diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h index d28a7b7..50b7c12 100644 --- a/proxy/api/ts/ts.h +++ b/proxy/api/ts/ts.h @@ -1223,6 +1223,21 @@ extern "C" tsapi int TSHttpSsnTransactionCount(TSHttpSsn ssnp); /* -------------------------------------------------------------------------- + SSL connections */ + /// Re-enable an SSL connection from a hook. + /// This must be called exactly once before the SSL connection will resume. + tsapi void TSVConnReenable(TSVConn sslvcp); + /// Set the connection to go into blind tunnel mode + tsapi TSReturnCode TSVConnTunnel(TSVConn sslp); + // Return the SSL object associated with the connection + tsapi TSSslConnection TSVConnSSLConnectionGet(TSVConn sslp); + // Fetch a SSL context from the global lookup table + tsapi TSSslContext TSSslContextFindByName(const char *name); + tsapi TSSslContext TSSslContextFindByAddr(struct sockaddr const*); + // Returns 1 if the sslp argument refers to a SSL connection + tsapi int TSVConnIsSsl(TSVConn sslp); + + /* -------------------------------------------------------------------------- HTTP transactions */ tsapi void TSHttpTxnHookAdd(TSHttpTxn txnp, TSHttpHookID id, TSCont contp); tsapi TSHttpSsn TSHttpTxnSsnGet(TSHttpTxn txnp); http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/config/ssl_multicert.config.default ---------------------------------------------------------------------- diff --git a/proxy/config/ssl_multicert.config.default b/proxy/config/ssl_multicert.config.default index b6dff3c..648858f 100644 --- a/proxy/config/ssl_multicert.config.default +++ b/proxy/config/ssl_multicert.config.default @@ -5,9 +5,9 @@ # hostname or IP address. At load time, the certificate is parsed to # extract the subject CN and all the DNS subjectAltNames. The # certificate will be presented for connections requesting any of the -# hostnames found in the certificate. Wildcard names are supported, -# but only of the form '*.domain.com', ie. where '*' is the leftmost -# domain component. +# hostnames found in the certificate. Wildcard names in the certificates +# are supported, but only of the form '*.domain.com', ie. where '*' +# is the leftmost domain component. # # The certificate file path, CA path and key path specified in # records.config will be used for all certificates, CAs and keys @@ -47,9 +47,17 @@ # exec: - Executes a program and uses the stdout output for the pass # phrase. # +# action=[tunnel] +# If the tunnel matches this line, traffic server will not participate +# in the handshake. But rather it will blind tunnel the SSL connection. +# If the connection is identified by server name, an openSSL patch must +# be applied to enable this functionality. See TS-3006 for details. +# # Examples: # ssl_cert_name=foo.pem # dest_ip=* ssl_cert_name=bar.pem ssl_key_name=barKey.pem # dest_ip=209.131.48.79 ssl_cert_name=server.pem ssl_key_name=serverKey.pem # dest_ip=10.0.0.1:99 ssl_cert_name=port99.pem # ssl_cert_name=foo.pem ssl_key_dialog="exec:/usr/bin/mypass foo 'ba r'" +# ssl_cert_name=foo.pem action=tunnel +# ssl_cert_name=wildcardcert.pem ssl_key_name=privkey.pem http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpDebugNames.cc ---------------------------------------------------------------------- diff --git a/proxy/http/HttpDebugNames.cc b/proxy/http/HttpDebugNames.cc index 4e907cf..7a85f6e 100644 --- a/proxy/http/HttpDebugNames.cc +++ b/proxy/http/HttpDebugNames.cc @@ -480,6 +480,10 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t) return "TS_HTTP_RESPONSE_CLIENT_HOOK"; case TS_HTTP_LAST_HOOK: return "TS_HTTP_LAST_HOOK"; + case TS_VCONN_PRE_ACCEPT_HOOK: + return "TS_VCONN_PRE_ACCEPT_HOOK"; + case TS_SSL_SNI_HOOK: + return "TS_SSL_SNI_HOOK"; } return "unknown hook"; http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpSM.cc ---------------------------------------------------------------------- diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index deb2daf..166cb7e 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -3610,7 +3610,7 @@ HttpSM::tunnel_handler_ssl_consumer(int event, HttpTunnelConsumer * c) // read side to close so that we don't cut off // pipelined responses with TCP resets // - ink_assert(c->producer->alive == false); + //ink_assert(c->producer->alive == false); c->write_success = true; if (c->self_producer->alive == true) { c->vc->do_io_shutdown(IO_SHUTDOWN_WRITE); http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpSessionAccept.cc ---------------------------------------------------------------------- diff --git a/proxy/http/HttpSessionAccept.cc b/proxy/http/HttpSessionAccept.cc index 9dbbef3..8a81167 100644 --- a/proxy/http/HttpSessionAccept.cc +++ b/proxy/http/HttpSessionAccept.cc @@ -49,7 +49,11 @@ HttpSessionAccept::accept(NetVConnection * netvc, MIOBuffer * iobuf, IOBufferRea return; } - netvc->attributes = transport_type; + // Set the transport type if not already set + if (HttpProxyPort::TRANSPORT_NONE == netvc->attributes) { + netvc->attributes = transport_type; + } + if (is_debug_tag_set("http_seq")) { Debug("http_seq", "[HttpSessionAccept:mainEvent %p] accepted connection from %s transport type = %d", netvc, ats_ip_nptop(client_ip, ipb, sizeof(ipb)), netvc->attributes); http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpSessionAccept.h ---------------------------------------------------------------------- diff --git a/proxy/http/HttpSessionAccept.h b/proxy/http/HttpSessionAccept.h index 05033d3..9f36436 100644 --- a/proxy/http/HttpSessionAccept.h +++ b/proxy/http/HttpSessionAccept.h @@ -25,6 +25,7 @@ #define _HttpSessionAccept_h_ #include "libts.h" +#include "records/I_RecHttp.h" #include "P_EventSystem.h" #include "HttpConfig.h" #include "HTTP.h" http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpTransact.cc ---------------------------------------------------------------------- diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc index 22906d8..d6986b4 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -596,13 +596,17 @@ HttpTransact::BadRequest(State* s) void HttpTransact::HandleBlindTunnel(State* s) { + bool inbound_transparent_p = s->state_machine->ua_session->get_netvc()->get_is_transparent(); + URL u; + IpEndpoint dest_addr; + //ip_text_buffer new_host; + DebugTxn("http_trans", "[HttpTransact::HandleBlindTunnel]"); // We've received a request on a port which we blind forward // For logging purposes we create a fake request s->hdr_info.client_request.create(HTTP_TYPE_REQUEST); s->hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT); - URL u; s->hdr_info.client_request.url_create(&u); u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL); s->hdr_info.client_request.url_set(&u); @@ -647,7 +651,7 @@ HttpTransact::HandleBlindTunnel(State* s) // request was addressed to us to begin with. Remap directs // are something used in the normal reverse proxy and if we // get them here they indicate a very bad misconfiguration! - if (url_remap_success == false || remap_redirect != NULL) { + if (!(inbound_transparent_p || url_remap_success) || remap_redirect != NULL) { // The error message we send back will be suppressed so // the only important thing in selecting the error is what // status code it gets logged as http://git-wip-us.apache.org/repos/asf/trafficserver/blob/044da699/proxy/http/HttpTunnel.cc ---------------------------------------------------------------------- diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc index b7ee202..492ea8e 100644 --- a/proxy/http/HttpTunnel.cc +++ b/proxy/http/HttpTunnel.cc @@ -906,7 +906,6 @@ HttpTunnel::producer_run(HttpTunnelProducer * p) } } } - c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader); ink_assert(c_write > 0); } @@ -969,6 +968,7 @@ HttpTunnel::producer_run(HttpTunnelProducer * p) } } + if (p->alive) { ink_assert(producer_n >= 0); @@ -994,6 +994,7 @@ HttpTunnel::producer_run(HttpTunnelProducer * p) // that it doesn't act like a buffer guard p->read_buffer->dealloc_reader(p->buffer_start); p->buffer_start = NULL; + } int
