Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1027)

Unified Diff: net/web2socket_proxy/web2socket_conn.cc

Issue 5484001: Web2socket proxy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: additional files Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/web2socket_proxy/web2socket_conn.cc
diff --git a/net/web2socket_proxy/web2socket_conn.cc b/net/web2socket_proxy/web2socket_conn.cc
new file mode 100644
index 0000000000000000000000000000000000000000..b0c86b39de9aa5080b70841b7425a0b4457b173b
--- /dev/null
+++ b/net/web2socket_proxy/web2socket_conn.cc
@@ -0,0 +1,779 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "web2socket_conn.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "base/base64.h"
+#include "base/logging.h"
+#include "third_party/libevent/evdns.h"
+
+static int CountSpaces(const std::string& s) {
+ int rv = 0;
+ for (size_t i = 0; i < s.size(); ++i) {
+ rv += (isascii(s[i]) && isspace(s[i]));
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 If you don't have to keep this code independent fr
Denis Lagno 2010/12/03 16:28:50 actually we don't need to be locale-independent he
tyoshino (SeeGerritForStatus) 2010/12/06 10:02:51 e.g. isdigit may recognize some characters other t
+ }
+ return rv;
+}
+
+static std::string FetchLowerCasedASCIISnippet(uint8_t* begin, uint8_t* end) {
+ std::string rv;
+ for (; begin < end; ++begin) {
+ if (!isascii(*begin)) {
+ return rv;
+ }
+ rv += tolower(*begin);
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 ditto. ToLowerASCII
Denis Lagno 2010/12/03 16:28:50 Done.
+ }
+ return rv;
+}
+
+// Returns true on success.
+static bool FetchDecimalDigits(const std::string& s, uint32_t* result) {
+ *result = 0;
+ bool got_something = false;
+ for (size_t i = 0; i < s.size(); ++i) {
+ if (isascii(s[i]) && isdigit(s[i])) {
+ got_something = true;
+ if (*result > std::numeric_limits<uint32_t>::max() / 10) {
+ return false;
+ }
+ *result *= 10;
+ int digit = s[i] - '0';
+ if (*result > std::numeric_limits<uint32_t>::max() - digit) {
+ return false;
+ }
+ *result += digit;
+ }
+ }
+ return got_something;
+}
+
+// Returns true on success.
+static bool FetchNamePort(uint8_t* begin, uint8_t* end,
+ std::string* name, uint32_t* port) {
+ *name = std::string();
+ *port = 0;
+ if (begin == end) {
+ return false;
+ }
+ for (uint8_t* pos = end; --pos != begin;) {
+ if (!isascii(*pos) || !isdigit(*pos)) {
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 How about isascii(*pos) && isdigit(*pos)? But if s
Denis Lagno 2010/12/03 16:28:50 actually !x || !y is !(x && y), not just (x && y).
tyoshino (SeeGerritForStatus) 2010/12/06 10:02:51 Oops, I put wrong comment. You're right. Anyway, t
+ if (*pos == ':') {
+ name->assign(begin, pos);
+ if (end - pos <= 1 ||
+ !FetchDecimalDigits(std::string(pos + 1, end), port)) {
+ *port = 0;
+ }
+ }
+ break;
+ }
+ }
+ if (*port <= 0 || *port >= (2 << 16)) {
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 Up to your design, but how about handling erroneou
Denis Lagno 2010/12/03 16:28:50 Done.
+ name->assign(begin, end);
+ *port = 22;
+ }
+ return !name->empty();
+}
+
+Conn::Conn(Web2SocketServ* master)
+ : master_(master),
+ phase_(PHASE_WAIT_HANDSHAKE),
+ destresolution_ipv4_failure_(false),
+ destresolution_ipv6_failure_(false) {
+ while (token_map_.find(last_token_) != token_map_.end()) {
+ token_ = last_token_ =
+ reinterpret_cast<Token>(reinterpret_cast<size_t>(last_token_) + 1);
+ }
+ token_map_[token_] = this;
+ // Schedule timeout for initial phase of connection.
+ evtimer_set(&destconnect_timeout_event_,
+ &OnDestConnectTimeout, token_);
+ event_base_set(master_->evbase_,
+ &destconnect_timeout_event_);
+ struct timeval tv;
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ evtimer_add(&destconnect_timeout_event_, &tv);
+}
+
+Conn::~Conn() {
+ phase_ = PHASE_DEFUNCT;
+ event_del(&destconnect_timeout_event_);
+ if (token_map_[token_] == this) {
+ token_map_.erase(token_);
+ }
+}
+
+Conn* Conn::Get(Token token) {
+ TokenMap::iterator it = token_map_.find(token);
+ if (it == token_map_.end()) {
+ return NULL;
+ }
+ Conn* cs = it->second;
+ if (cs == NULL ||
+ cs->token_ != token ||
+ cs->master_ == NULL ||
+ cs->phase_ < 0 ||
+ cs->phase_ > PHASE_SHUT ||
+ !cs->master_->IsConnSane(cs)) {
+ return NULL;
+ }
+ return cs;
+}
+
+void Conn::Shut() {
+ if (phase_ >= PHASE_SHUT) {
+ return;
+ }
+ master_->MarkConnImportance(this, false);
+ if (primchan_.sock_ >= 0 && primchan_.bev_ != NULL) {
+ static const uint8_t closing_handshake[9] = { 0 };
+ bufferevent_write(primchan_.bev_,
+ closing_handshake, sizeof(closing_handshake));
+ }
+ phase_ = PHASE_SHUT;
+}
+
+// For our purposes equivalent to strlen(reinterpret_cast<const char*>(X)).
+#define RAWSIZEOF(X) (sizeof(X) - 1)
+
+Conn::Status Conn::ConsumeHeader(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ static const uint8_t kCRLF[] = "\r\n";
+ static const uint8_t kCRLFCRLF[] = "\r\n\r\n";
+ static const uint8_t kGetMagic[] = "GET /echo ";
+ static const uint8_t kDelimiter[] = ": ";
+
+ if (buf_size <= 0) {
+ return STATUS_INCOMPLETE;
+ }
+ if (!buf) {
+ return STATUS_ABORT;
+ }
+ if (!std::equal(buf, buf + std::min(buf_size, RAWSIZEOF(kGetMagic)),
+ kGetMagic)) {
+ // Data head does not match what is expected.
+ return STATUS_ABORT;
+ }
+
+ if (buf_size >= Web2SocketServ::kReadBufferLimit) {
+ return STATUS_ABORT;
+ }
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::search(buf, buf_end, kCRLFCRLF,
+ kCRLFCRLF + RAWSIZEOF(kCRLFCRLF));
+ if (term_pos == buf_end) {
+ return STATUS_INCOMPLETE;
+ }
+ uint8_t key3[8]; // Notation (key3) matches websocket RFC.
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 Two space before inline comment
Denis Lagno 2010/12/03 16:28:50 Done.
+ if (buf_end - term_pos <
+ static_cast<ptrdiff_t>(RAWSIZEOF(kCRLFCRLF) + sizeof(key3))) {
+ return STATUS_INCOMPLETE;
+ }
+ term_pos += RAWSIZEOF(kCRLFCRLF) + sizeof(key3);
+ memcpy(key3, term_pos - sizeof(key3), sizeof(key3));
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 how about swapping these lines to remove "- sizeof
Denis Lagno 2010/12/03 16:28:50 Done.
+ // First line is "GET /echo" line, so we skip it.
+ uint8_t* pos = std::search(buf, term_pos, kCRLF, kCRLF + RAWSIZEOF(kCRLF));
+ if (pos == term_pos) {
+ return STATUS_ABORT;
+ }
+ for (;;) {
+ pos += RAWSIZEOF(kCRLF);
+ if (term_pos - pos <
+ static_cast<ptrdiff_t>(sizeof(key3) + RAWSIZEOF(kCRLF))) {
+ return STATUS_ABORT;
+ }
+ if (term_pos - pos ==
+ static_cast<ptrdiff_t>(sizeof(key3) + RAWSIZEOF(kCRLF))) {
+ break;
+ }
+ uint8_t* npos = std::search(pos, term_pos, kDelimiter,
+ kDelimiter + RAWSIZEOF(kDelimiter));
+ if (npos == term_pos) {
+ return STATUS_ABORT;
+ }
+ std::string key = FetchLowerCasedASCIISnippet(pos, npos);
+ pos = std::search(npos += RAWSIZEOF(kDelimiter), term_pos,
+ kCRLF, kCRLF + RAWSIZEOF(kCRLF));
+ if (pos == term_pos) {
+ return STATUS_ABORT;
+ }
+ if (!key.empty()) {
+ header_fields_[key] = FetchLowerCasedASCIISnippet(npos, pos);
+ }
+ }
+
+ // Values of Upgrade and Connection fields are hardcoded in the protocol.
+ if (header_fields_["upgrade"] != "websocket" ||
+ header_fields_["connection"] != "upgrade") {
+ return STATUS_ABORT;
+ }
+
+ static const std::string kAllowedHost = "127.0.0.1:";
+ if (header_fields_["host"].substr(0, kAllowedHost.size()) !=
+ kAllowedHost) {
+ return STATUS_ABORT;
+ }
+
+ if (!master_->origin_.empty() &&
+ master_->origin_ != "any") {
+ if (header_fields_["origin"] != master_->origin_) {
+ return STATUS_ABORT;
+ }
+ }
+
+ static const std::string kSecKey1 = "sec-websocket-key1";
+ static const std::string kSecKey2 = "sec-websocket-key2";
+ uint32_t key_number1, key_number2;
+ if (!FetchDecimalDigits(header_fields_[kSecKey1],
+ &key_number1) ||
+ !FetchDecimalDigits(header_fields_[kSecKey2],
+ &key_number2)) {
+ return STATUS_ABORT;
+ }
+
+ // We limit incoming header size so following numbers shall not be too high.
+ int spaces1 = CountSpaces(header_fields_[kSecKey1]);
+ int spaces2 = CountSpaces(header_fields_[kSecKey2]);
+ if (spaces1 == 0 ||
+ spaces2 == 0 ||
+ key_number1 % spaces1 != 0 ||
+ key_number2 % spaces2 != 0) {
+ return STATUS_ABORT;
+ }
+
+ uint8_t challenge[4 + 4 + sizeof(key3)];
+ uint32_t part1 = htonl(key_number1 / spaces1);
+ uint32_t part2 = htonl(key_number2 / spaces2);
+ memcpy(challenge, &part1, 4);
+ memcpy(challenge + 4, &part2, 4);
+ memcpy(challenge + sizeof(challenge) - sizeof(key3), key3, sizeof(key3));
+ MD5Sum(challenge, sizeof(challenge), &handshake_response_);
+
+ evbuffer_drain(evb, term_pos - buf);
+ return STATUS_OK;
+}
+
+bool Conn::EmitHandshake(struct bufferevent* bev) {
+ std::vector<std::string> boilerplate;
+ boilerplate.push_back("HTTP/1.1 101 WebSocket Protocol Handshake");
+ boilerplate.push_back("Upgrade: WebSocket");
+ boilerplate.push_back("Connection: Upgrade");
+
+ {
+ // Take care of Location field.
+ char buf[128];
+ snprintf(buf, sizeof(buf),
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 check return value?
Denis Lagno 2010/12/03 16:28:50 Done.
+ "Sec-WebSocket-Location: ws://%s/echo",
+ header_fields_["host"].c_str());
+ boilerplate.push_back(buf);
+ }
+ {
+ // Take care of Origin field.
+ if (header_fields_.find("origin") != header_fields_.end()) {
+ char buf[128];
+ snprintf(buf, sizeof(buf),
+ "Sec-WebSocket-Origin: %s", header_fields_["origin"].c_str());
+ boilerplate.push_back(buf);
+ }
+ }
+
+ boilerplate.push_back("");
+ static const uint8_t kCRLF[] = "\r\n";
+ for (size_t i = 0; i < boilerplate.size(); ++i) {
+ if (bufferevent_write(bev, boilerplate[i].c_str(),
+ boilerplate[i].size()) ||
+ bufferevent_write(bev, kCRLF, RAWSIZEOF(kCRLF))) {
+ return false;
+ }
+ }
+ return !bufferevent_write(bev, &handshake_response_,
+ sizeof(handshake_response_));
+}
+
+Conn::Status Conn::ConsumeDestframe(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1) {
+ return STATUS_INCOMPLETE;
+ }
+ if (buf[0] != 0) {
+ return STATUS_ABORT;
+ }
+ if (buf_size < 1 + 1) {
+ return STATUS_INCOMPLETE;
+ }
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::find(buf + 1, buf_end, 0xff);
+ if (term_pos == buf_end) {
+ if (buf_size >= Web2SocketServ::kReadBufferLimit) {
+ // So big and still worth nothing.
+ return STATUS_ABORT;
+ }
+ return STATUS_INCOMPLETE;
+ }
+ if (!FetchNamePort(buf + 1, term_pos, &destname_, &destport_)) {
+ return STATUS_ABORT;
+ }
+ evbuffer_drain(evb, term_pos - buf + 1);
+ return STATUS_OK;
+}
+
+Conn::Status Conn::ConsumeFrameHeader(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1) {
+ return STATUS_INCOMPLETE;
+ }
+ if (buf[0] != 0) {
+ return STATUS_ABORT;
+ }
+ evbuffer_drain(evb, 1);
+ return STATUS_OK;
+}
+
+Conn::Status Conn::ProcessFrameData(struct evbuffer* evb) {
+ uint8_t* buf = EVBUFFER_DATA(evb);
+ size_t buf_size = EVBUFFER_LENGTH(evb);
+
+ if (buf_size < 1) {
tyoshino (SeeGerritForStatus) 2010/12/03 07:53:33 We can remove if-clause?
Denis Lagno 2010/12/03 16:28:50 It shouldn't happen in practice, but just a safety
+ return STATUS_INCOMPLETE;
+ }
+ uint8_t* buf_end = buf + buf_size;
+ uint8_t* term_pos = std::find(buf, buf_end, 0xff);
+ bool term_detected = (term_pos != buf_end);
+ if (term_detected) {
+ buf_size = term_pos - buf;
+ }
+ switch (phase_) {
+ case PHASE_INSIDE_FRAME_BASE64: {
+ if (term_detected && buf_size % 4) {
+ // base64 is encoded in chunks of 4 bytes.
+ return STATUS_ABORT;
+ }
+ if (buf_size < 4) {
+ DCHECK(!term_detected);
+ return STATUS_INCOMPLETE;
+ }
+ size_t bytes_to_process_atm = (buf_size / 4) * 4;
+ std::string out_bytes;
+ base::Base64Decode(std::string(buf, buf + bytes_to_process_atm),
+ &out_bytes);
+ evbuffer_drain(evb, bytes_to_process_atm);
+ DCHECK(destchan_.bev_ != NULL);
+ if (bufferevent_write(destchan_.bev_,
+ out_bytes.c_str(), out_bytes.size())) {
+ return STATUS_ABORT;
+ }
+ break;
+ }
+ case PHASE_INSIDE_FRAME_SKIP: {
+ evbuffer_drain(evb, buf_size);
+ break;
+ }
+ default: {
+ return STATUS_ABORT;
+ }
+ }
+ if (term_detected) {
+ evbuffer_drain(evb, 1);
+ return STATUS_OK;
+ }
+ return STATUS_INCOMPLETE;
+}
+
+bool Conn::TryConnectDest(const struct sockaddr* addr,
+ socklen_t addrlen) {
+ if (destchan_.sock_ >= 0 ||
+ destchan_.bev_ != NULL) {
+ return false;
+ }
+ destchan_.sock_ = socket(addr->sa_family, SOCK_STREAM, 0);
+ if (destchan_.sock_ < 0) {
+ return false;
+ }
+ if (!Web2SocketServ::SetNonBlock(destchan_.sock_)) {
+ return false;
+ }
+ if (connect(destchan_.sock_, addr, addrlen)) {
+ if (errno != EINPROGRESS) {
+ return false;
+ }
+ }
+ destchan_.bev_ = bufferevent_new(destchan_.sock_,
+ &OnDestchanRead,
+ &OnDestchanWrite,
+ &OnDestchanError,
+ token_);
+ if (destchan_.bev_ == NULL) {
+ return false;
+ }
+ if (bufferevent_base_set(master_->evbase_, destchan_.bev_)) {
+ return false;
+ }
+ bufferevent_setwatermark(destchan_.bev_, EV_READ,
+ 0, Web2SocketServ::kReadBufferLimit);
+ if (bufferevent_enable(destchan_.bev_, EV_READ | EV_WRITE)) {
+ return false;
+ }
+ return true;
+}
+
+void Conn::OnPrimchanRead(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0) {
+ return;
+ }
+ cs->master_->MarkConnImportance(cs, true);
+ for (;;) {
+ switch (cs->phase_) {
+ case PHASE_WAIT_HANDSHAKE: {
+ switch (cs->ConsumeHeader(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ }
+ // Header consumed OK. Do respond.
+ if (!cs->EmitHandshake(bev)) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ cs->phase_ = PHASE_WAIT_DESTFRAME;
+ return;
+ }
+ case PHASE_WAIT_DESTFRAME: {
+ switch (cs->ConsumeDestframe(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_port = htons(cs->destport_);
+ if (inet_pton(sa.sin_family = AF_INET,
+ cs->destname_.c_str(),
+ &sa.sin_addr) == 1) {
+ // valid IPv4 address supplied.
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ }
+ }
+ {
+ if (cs->destname_.size() >= 2 &&
+ cs->destname_[0] == '[' &&
+ cs->destname_[cs->destname_.size() - 1] == ']') {
+ // Literal IPv6 address in brackets.
+ cs->destname_ =
+ cs->destname_.substr(1, cs->destname_.size() - 2);
+ }
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_port = htons(cs->destport_);
+ if (inet_pton(sa.sin6_family = AF_INET6,
+ cs->destname_.c_str(),
+ &sa.sin6_addr) == 1) {
+ // valid IPv6 address supplied.
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ }
+ }
+ // Try to asynchronously perform DNS resolution.
+ evdns_resolve_ipv4(cs->destname_.c_str(), 0,
+ &OnDestResolutionIPv4, token);
+ evdns_resolve_ipv6(cs->destname_.c_str(), 0,
+ &OnDestResolutionIPv6, token);
+ cs->phase_ = PHASE_WAIT_DESTCONNECT;
+ return;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ }
+ case PHASE_WAIT_DESTCONNECT: {
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) >=
+ Web2SocketServ::kReadBufferLimit) {
+ cs->Shut();
+ }
+ return;
+ }
+ case PHASE_OUTSIDE_FRAME: {
+ switch (cs->ConsumeFrameHeader(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ cs->phase_ = PHASE_INSIDE_FRAME_BASE64;
+ // Process remaining data if any.
+ break;
+ }
+ case STATUS_SKIP: {
+ cs->phase_ = PHASE_INSIDE_FRAME_SKIP;
+ // Process remaining data if any.
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ break;
+ }
+ case PHASE_INSIDE_FRAME_BASE64:
+ case PHASE_INSIDE_FRAME_SKIP: {
+ switch (cs->ProcessFrameData(EVBUFFER_INPUT(bev))) {
+ case STATUS_OK: {
+ cs->phase_ = PHASE_OUTSIDE_FRAME;
+ // Handle remaining data if any.
+ break;
+ }
+ case STATUS_INCOMPLETE: {
+ return;
+ }
+ case STATUS_ABORT:
+ default: {
+ cs->Shut();
+ return;
+ }
+ }
+ break;
+ }
+ case PHASE_SHUT: {
+ evbuffer_drain(EVBUFFER_INPUT(bev),
+ EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
+ return;
+ }
+ case PHASE_DEFUNCT:
+ default: {
+ // Must not reach here.
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ }
+ }
+}
+
+void Conn::OnPrimchanWrite(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ if (cs->phase_ > PHASE_WAIT_DESTCONNECT) {
+ OnDestchanRead(cs->destchan_.bev_, token);
+ }
+}
+
+void Conn::OnPrimchanError(struct bufferevent* bev,
+ short what, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->primchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT) {
+ cs->master_->ZapConn(cs);
+ } else {
+ cs->Shut();
+ }
+}
+
+void Conn::OnDestResolutionIPv4(int result, char type,
+ int count, int ttl,
+ void* addr_list, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ != PHASE_WAIT_DESTCONNECT) {
+ return;
+ }
+ if (result == DNS_ERR_NONE &&
+ count >= 1 &&
+ addr_list != NULL &&
+ type == DNS_IPv4_A) {
+ for (int i = 0; i < count; ++i) {
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(cs->destport_);
+ DCHECK(sizeof(sa.sin_addr) == sizeof(struct in_addr));
+ memcpy(&sa.sin_addr,
+ static_cast<struct in_addr*>(addr_list) + i,
+ sizeof(sa.sin_addr));
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ return;
+ }
+ }
+ }
+ cs->destresolution_ipv4_failure_ = true;
+ if (cs->destresolution_ipv4_failure_ && cs->destresolution_ipv6_failure_) {
+ cs->Shut();
+ }
+}
+
+void Conn::OnDestResolutionIPv6(int result, char type,
+ int count, int ttl,
+ void* addr_list, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ != PHASE_WAIT_DESTCONNECT) {
+ return;
+ }
+ if (result == DNS_ERR_NONE &&
+ count >= 1 &&
+ addr_list != NULL &&
+ type == DNS_IPv6_AAAA) {
+ for (int i = 0; i < count; ++i) {
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(cs->destport_);
+ DCHECK(sizeof(sa.sin6_addr) == sizeof(struct in6_addr));
+ memcpy(&sa.sin6_addr,
+ static_cast<struct in6_addr*>(addr_list) + i,
+ sizeof(sa.sin6_addr));
+ if (cs->TryConnectDest((struct sockaddr*)&sa, sizeof(sa))) {
+ return;
+ }
+ }
+ }
+ cs->destresolution_ipv6_failure_ = true;
+ if (cs->destresolution_ipv4_failure_ && cs->destresolution_ipv6_failure_) {
+ cs->Shut();
+ }
+}
+
+void Conn::OnDestConnectTimeout(int, short, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (cs == NULL) {
+ // Sanity check failed.
+ }
+ if (cs->phase_ > PHASE_WAIT_DESTCONNECT) {
+ return;
+ }
+ cs->Shut();
+}
+
+void Conn::OnDestchanRead(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) <= 0) {
+ return;
+ }
+ if (cs->primchan_.bev_ == NULL) {
+ cs->master_->ZapConn(cs);
+ return;
+ }
+ cs->master_->MarkConnImportance(cs, true);
+ std::string out_bytes;
+ base::Base64Encode(
+ std::string(
+ static_cast<const char*>(static_cast<void*>(
+ EVBUFFER_DATA(EVBUFFER_INPUT(bev)))),
+ EVBUFFER_LENGTH(EVBUFFER_INPUT(bev))),
+ &out_bytes);
+ evbuffer_drain(EVBUFFER_INPUT(bev), EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)));
+ static const uint8_t frame_header[] = { 0x00 };
+ static const uint8_t frame_terminator[] = { 0xff };
+ if (bufferevent_write(cs->primchan_.bev_,
+ frame_header, sizeof(frame_header)) ||
+ bufferevent_write(cs->primchan_.bev_,
+ out_bytes.c_str(), out_bytes.size()) ||
+ bufferevent_write(cs->primchan_.bev_,
+ frame_terminator, sizeof(frame_terminator))) {
+ cs->Shut();
+ }
+}
+
+
+void Conn::OnDestchanWrite(struct bufferevent* bev, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ == PHASE_WAIT_DESTCONNECT) {
+ cs->phase_ = PHASE_OUTSIDE_FRAME;
+ }
+ OnPrimchanRead(cs->primchan_.bev_, token);
+}
+
+void Conn::OnDestchanError(struct bufferevent* bev,
+ short what, Token token) {
+ Conn* cs = Conn::Get(token);
+ if (bev == NULL ||
+ cs == NULL ||
+ bev != cs->destchan_.bev_) {
+ // Sanity check failed.
+ return;
+ }
+ if (cs->phase_ >= PHASE_SHUT) {
+ cs->master_->ZapConn(cs);
+ } else {
+ cs->Shut();
+ }
+}
+
+Conn::Token Conn::last_token_ = 0;
+Conn::TokenMap Conn::token_map_;

Powered by Google App Engine
This is Rietveld 408576698