mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 22:00:47 +01:00

There is code in socket_adapters.cc that was trying to display UI by invoking the MessageBoxA API. This causes a linker failure when building apps for versions of Windows that do not have the MessageBoxA API. The text message that the socket code tries display also does not seem right. It references Google Talk and provides a HTTP URI that is invalid. The message is only in English instead of being localized in all the languages supported by the app. I am fixing this by replacing the call to MessageBoxA with a call to RTC_LOG(LS_ERROR). I am also attempting to clean up the text of the message by removing the invalid URL and removing references to Google products. I am trying to make the logging message more matter-of-fact about what is going on. As I understand it, the message is displayed when a HTTP proxy sends a Proxy-Authenticate HTTP response header that specifies an unsupported authentication scheme. I changed the text of the logging message to state this. Bug: webrtc:11187 Change-Id: I14df32943b62130ac623f72fe901e8f2bb1e8f24 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161475 Reviewed-by: Tommi <tommi@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Henrik Andreassson <henrika@webrtc.org> Commit-Queue: Tommi <tommi@webrtc.org> Cr-Commit-Position: refs/heads/master@{#30046}
657 lines
19 KiB
C++
657 lines
19 KiB
C++
/*
|
|
* Copyright 2004 The WebRTC Project Authors. All rights reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300
|
|
#pragma warning(disable : 4786)
|
|
#endif
|
|
|
|
#include <algorithm>
|
|
|
|
#include "absl/strings/match.h"
|
|
#include "rtc_base/buffer.h"
|
|
#include "rtc_base/byte_buffer.h"
|
|
#include "rtc_base/checks.h"
|
|
#include "rtc_base/http_common.h"
|
|
#include "rtc_base/logging.h"
|
|
#include "rtc_base/socket_adapters.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
#include "rtc_base/zero_memory.h"
|
|
|
|
namespace rtc {
|
|
|
|
BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t size)
|
|
: AsyncSocketAdapter(socket),
|
|
buffer_size_(size),
|
|
data_len_(0),
|
|
buffering_(false) {
|
|
buffer_ = new char[buffer_size_];
|
|
}
|
|
|
|
BufferedReadAdapter::~BufferedReadAdapter() {
|
|
delete[] buffer_;
|
|
}
|
|
|
|
int BufferedReadAdapter::Send(const void* pv, size_t cb) {
|
|
if (buffering_) {
|
|
// TODO: Spoof error better; Signal Writeable
|
|
socket_->SetError(EWOULDBLOCK);
|
|
return -1;
|
|
}
|
|
return AsyncSocketAdapter::Send(pv, cb);
|
|
}
|
|
|
|
int BufferedReadAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) {
|
|
if (buffering_) {
|
|
socket_->SetError(EWOULDBLOCK);
|
|
return -1;
|
|
}
|
|
|
|
size_t read = 0;
|
|
|
|
if (data_len_) {
|
|
read = std::min(cb, data_len_);
|
|
memcpy(pv, buffer_, read);
|
|
data_len_ -= read;
|
|
if (data_len_ > 0) {
|
|
memmove(buffer_, buffer_ + read, data_len_);
|
|
}
|
|
pv = static_cast<char*>(pv) + read;
|
|
cb -= read;
|
|
}
|
|
|
|
// FIX: If cb == 0, we won't generate another read event
|
|
|
|
int res = AsyncSocketAdapter::Recv(pv, cb, timestamp);
|
|
if (res >= 0) {
|
|
// Read from socket and possibly buffer; return combined length
|
|
return res + static_cast<int>(read);
|
|
}
|
|
|
|
if (read > 0) {
|
|
// Failed to read from socket, but still read something from buffer
|
|
return static_cast<int>(read);
|
|
}
|
|
|
|
// Didn't read anything; return error from socket
|
|
return res;
|
|
}
|
|
|
|
void BufferedReadAdapter::BufferInput(bool on) {
|
|
buffering_ = on;
|
|
}
|
|
|
|
void BufferedReadAdapter::OnReadEvent(AsyncSocket* socket) {
|
|
RTC_DCHECK(socket == socket_);
|
|
|
|
if (!buffering_) {
|
|
AsyncSocketAdapter::OnReadEvent(socket);
|
|
return;
|
|
}
|
|
|
|
if (data_len_ >= buffer_size_) {
|
|
RTC_LOG(LS_ERROR) << "Input buffer overflow";
|
|
RTC_NOTREACHED();
|
|
data_len_ = 0;
|
|
}
|
|
|
|
int len =
|
|
socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_, nullptr);
|
|
if (len < 0) {
|
|
// TODO: Do something better like forwarding the error to the user.
|
|
RTC_LOG_ERR(INFO) << "Recv";
|
|
return;
|
|
}
|
|
|
|
data_len_ += len;
|
|
|
|
ProcessInput(buffer_, &data_len_);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// This is a SSL v2 CLIENT_HELLO message.
|
|
// TODO: Should this have a session id? The response doesn't have a
|
|
// certificate, so the hello should have a session id.
|
|
static const uint8_t kSslClientHello[] = {
|
|
0x80, 0x46, // msg len
|
|
0x01, // CLIENT_HELLO
|
|
0x03, 0x01, // SSL 3.1
|
|
0x00, 0x2d, // ciphersuite len
|
|
0x00, 0x00, // session id len
|
|
0x00, 0x10, // challenge len
|
|
0x01, 0x00, 0x80, 0x03, 0x00, 0x80, 0x07, 0x00, 0xc0, // ciphersuites
|
|
0x06, 0x00, 0x40, 0x02, 0x00, 0x80, 0x04, 0x00, 0x80, //
|
|
0x00, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x00, 0x00, 0x0a, //
|
|
0x00, 0xfe, 0xfe, 0x00, 0x00, 0x09, 0x00, 0x00, 0x64, //
|
|
0x00, 0x00, 0x62, 0x00, 0x00, 0x03, 0x00, 0x00, 0x06, //
|
|
0x1f, 0x17, 0x0c, 0xa6, 0x2f, 0x00, 0x78, 0xfc, // challenge
|
|
0x46, 0x55, 0x2e, 0xb1, 0x83, 0x39, 0xf1, 0xea //
|
|
};
|
|
|
|
// static
|
|
ArrayView<const uint8_t> AsyncSSLSocket::SslClientHello() {
|
|
// Implicit conversion directly from kSslClientHello to ArrayView fails when
|
|
// built with gcc.
|
|
return {kSslClientHello, sizeof(kSslClientHello)};
|
|
}
|
|
|
|
// This is a TLSv1 SERVER_HELLO message.
|
|
static const uint8_t kSslServerHello[] = {
|
|
0x16, // handshake message
|
|
0x03, 0x01, // SSL 3.1
|
|
0x00, 0x4a, // message len
|
|
0x02, // SERVER_HELLO
|
|
0x00, 0x00, 0x46, // handshake len
|
|
0x03, 0x01, // SSL 3.1
|
|
0x42, 0x85, 0x45, 0xa7, 0x27, 0xa9, 0x5d, 0xa0, // server random
|
|
0xb3, 0xc5, 0xe7, 0x53, 0xda, 0x48, 0x2b, 0x3f, //
|
|
0xc6, 0x5a, 0xca, 0x89, 0xc1, 0x58, 0x52, 0xa1, //
|
|
0x78, 0x3c, 0x5b, 0x17, 0x46, 0x00, 0x85, 0x3f, //
|
|
0x20, // session id len
|
|
0x0e, 0xd3, 0x06, 0x72, 0x5b, 0x5b, 0x1b, 0x5f, // session id
|
|
0x15, 0xac, 0x13, 0xf9, 0x88, 0x53, 0x9d, 0x9b, //
|
|
0xe8, 0x3d, 0x7b, 0x0c, 0x30, 0x32, 0x6e, 0x38, //
|
|
0x4d, 0xa2, 0x75, 0x57, 0x41, 0x6c, 0x34, 0x5c, //
|
|
0x00, 0x04, // RSA/RC4-128/MD5
|
|
0x00 // null compression
|
|
};
|
|
|
|
// static
|
|
ArrayView<const uint8_t> AsyncSSLSocket::SslServerHello() {
|
|
return {kSslServerHello, sizeof(kSslServerHello)};
|
|
}
|
|
|
|
AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket)
|
|
: BufferedReadAdapter(socket, 1024) {}
|
|
|
|
int AsyncSSLSocket::Connect(const SocketAddress& addr) {
|
|
// Begin buffering before we connect, so that there isn't a race condition
|
|
// between potential senders and receiving the OnConnectEvent signal
|
|
BufferInput(true);
|
|
return BufferedReadAdapter::Connect(addr);
|
|
}
|
|
|
|
void AsyncSSLSocket::OnConnectEvent(AsyncSocket* socket) {
|
|
RTC_DCHECK(socket == socket_);
|
|
// TODO: we could buffer output too...
|
|
const int res = DirectSend(kSslClientHello, sizeof(kSslClientHello));
|
|
RTC_DCHECK_EQ(sizeof(kSslClientHello), res);
|
|
}
|
|
|
|
void AsyncSSLSocket::ProcessInput(char* data, size_t* len) {
|
|
if (*len < sizeof(kSslServerHello))
|
|
return;
|
|
|
|
if (memcmp(kSslServerHello, data, sizeof(kSslServerHello)) != 0) {
|
|
Close();
|
|
SignalCloseEvent(this, 0); // TODO: error code?
|
|
return;
|
|
}
|
|
|
|
*len -= sizeof(kSslServerHello);
|
|
if (*len > 0) {
|
|
memmove(data, data + sizeof(kSslServerHello), *len);
|
|
}
|
|
|
|
bool remainder = (*len > 0);
|
|
BufferInput(false);
|
|
SignalConnectEvent(this);
|
|
|
|
// FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
|
|
if (remainder)
|
|
SignalReadEvent(this);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket,
|
|
const std::string& user_agent,
|
|
const SocketAddress& proxy,
|
|
const std::string& username,
|
|
const CryptString& password)
|
|
: BufferedReadAdapter(socket, 1024),
|
|
proxy_(proxy),
|
|
agent_(user_agent),
|
|
user_(username),
|
|
pass_(password),
|
|
force_connect_(false),
|
|
state_(PS_ERROR),
|
|
context_(0) {}
|
|
|
|
AsyncHttpsProxySocket::~AsyncHttpsProxySocket() {
|
|
delete context_;
|
|
}
|
|
|
|
int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) {
|
|
int ret;
|
|
RTC_LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect("
|
|
<< proxy_.ToSensitiveString() << ")";
|
|
dest_ = addr;
|
|
state_ = PS_INIT;
|
|
if (ShouldIssueConnect()) {
|
|
BufferInput(true);
|
|
}
|
|
ret = BufferedReadAdapter::Connect(proxy_);
|
|
// TODO: Set state_ appropriately if Connect fails.
|
|
return ret;
|
|
}
|
|
|
|
SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const {
|
|
return dest_;
|
|
}
|
|
|
|
int AsyncHttpsProxySocket::Close() {
|
|
headers_.clear();
|
|
state_ = PS_ERROR;
|
|
dest_.Clear();
|
|
delete context_;
|
|
context_ = nullptr;
|
|
return BufferedReadAdapter::Close();
|
|
}
|
|
|
|
Socket::ConnState AsyncHttpsProxySocket::GetState() const {
|
|
if (state_ < PS_TUNNEL) {
|
|
return CS_CONNECTING;
|
|
} else if (state_ == PS_TUNNEL) {
|
|
return CS_CONNECTED;
|
|
} else {
|
|
return CS_CLOSED;
|
|
}
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket* socket) {
|
|
RTC_LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent";
|
|
if (!ShouldIssueConnect()) {
|
|
state_ = PS_TUNNEL;
|
|
BufferedReadAdapter::OnConnectEvent(socket);
|
|
return;
|
|
}
|
|
SendRequest();
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket* socket, int err) {
|
|
RTC_LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")";
|
|
if ((state_ == PS_WAIT_CLOSE) && (err == 0)) {
|
|
state_ = PS_ERROR;
|
|
Connect(dest_);
|
|
} else {
|
|
BufferedReadAdapter::OnCloseEvent(socket, err);
|
|
}
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::ProcessInput(char* data, size_t* len) {
|
|
size_t start = 0;
|
|
for (size_t pos = start; state_ < PS_TUNNEL && pos < *len;) {
|
|
if (state_ == PS_SKIP_BODY) {
|
|
size_t consume = std::min(*len - pos, content_length_);
|
|
pos += consume;
|
|
start = pos;
|
|
content_length_ -= consume;
|
|
if (content_length_ == 0) {
|
|
EndResponse();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (data[pos++] != '\n')
|
|
continue;
|
|
|
|
size_t len = pos - start - 1;
|
|
if ((len > 0) && (data[start + len - 1] == '\r'))
|
|
--len;
|
|
|
|
data[start + len] = 0;
|
|
ProcessLine(data + start, len);
|
|
start = pos;
|
|
}
|
|
|
|
*len -= start;
|
|
if (*len > 0) {
|
|
memmove(data, data + start, *len);
|
|
}
|
|
|
|
if (state_ != PS_TUNNEL)
|
|
return;
|
|
|
|
bool remainder = (*len > 0);
|
|
BufferInput(false);
|
|
SignalConnectEvent(this);
|
|
|
|
// FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
|
|
if (remainder)
|
|
SignalReadEvent(this); // TODO: signal this??
|
|
}
|
|
|
|
bool AsyncHttpsProxySocket::ShouldIssueConnect() const {
|
|
// TODO: Think about whether a more sophisticated test
|
|
// than dest port == 80 is needed.
|
|
return force_connect_ || (dest_.port() != 80);
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::SendRequest() {
|
|
rtc::StringBuilder ss;
|
|
ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n";
|
|
ss << "User-Agent: " << agent_ << "\r\n";
|
|
ss << "Host: " << dest_.HostAsURIString() << "\r\n";
|
|
ss << "Content-Length: 0\r\n";
|
|
ss << "Proxy-Connection: Keep-Alive\r\n";
|
|
ss << headers_;
|
|
ss << "\r\n";
|
|
std::string str = ss.str();
|
|
DirectSend(str.c_str(), str.size());
|
|
state_ = PS_LEADER;
|
|
expect_close_ = true;
|
|
content_length_ = 0;
|
|
headers_.clear();
|
|
|
|
RTC_LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str;
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::ProcessLine(char* data, size_t len) {
|
|
RTC_LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data;
|
|
|
|
if (len == 0) {
|
|
if (state_ == PS_TUNNEL_HEADERS) {
|
|
state_ = PS_TUNNEL;
|
|
} else if (state_ == PS_ERROR_HEADERS) {
|
|
Error(defer_error_);
|
|
return;
|
|
} else if (state_ == PS_SKIP_HEADERS) {
|
|
if (content_length_) {
|
|
state_ = PS_SKIP_BODY;
|
|
} else {
|
|
EndResponse();
|
|
return;
|
|
}
|
|
} else {
|
|
if (!unknown_mechanisms_.empty()) {
|
|
RTC_LOG(LS_ERROR) << "Unsupported authentication methods: "
|
|
<< unknown_mechanisms_;
|
|
}
|
|
// Unexpected end of headers
|
|
Error(0);
|
|
return;
|
|
}
|
|
} else if (state_ == PS_LEADER) {
|
|
unsigned int code;
|
|
if (sscanf(data, "HTTP/%*u.%*u %u", &code) != 1) {
|
|
Error(0);
|
|
return;
|
|
}
|
|
switch (code) {
|
|
case 200:
|
|
// connection good!
|
|
state_ = PS_TUNNEL_HEADERS;
|
|
return;
|
|
#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407)
|
|
#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ
|
|
#endif
|
|
case 407: // HTTP_STATUS_PROXY_AUTH_REQ
|
|
state_ = PS_AUTHENTICATE;
|
|
return;
|
|
default:
|
|
defer_error_ = 0;
|
|
state_ = PS_ERROR_HEADERS;
|
|
return;
|
|
}
|
|
} else if ((state_ == PS_AUTHENTICATE) &&
|
|
absl::StartsWithIgnoreCase(data, "Proxy-Authenticate:")) {
|
|
std::string response, auth_method;
|
|
switch (HttpAuthenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_,
|
|
pass_, context_, response, auth_method)) {
|
|
case HAR_IGNORE:
|
|
RTC_LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method;
|
|
if (!unknown_mechanisms_.empty())
|
|
unknown_mechanisms_.append(", ");
|
|
unknown_mechanisms_.append(auth_method);
|
|
break;
|
|
case HAR_RESPONSE:
|
|
headers_ = "Proxy-Authorization: ";
|
|
headers_.append(response);
|
|
headers_.append("\r\n");
|
|
state_ = PS_SKIP_HEADERS;
|
|
unknown_mechanisms_.clear();
|
|
break;
|
|
case HAR_CREDENTIALS:
|
|
defer_error_ = SOCKET_EACCES;
|
|
state_ = PS_ERROR_HEADERS;
|
|
unknown_mechanisms_.clear();
|
|
break;
|
|
case HAR_ERROR:
|
|
defer_error_ = 0;
|
|
state_ = PS_ERROR_HEADERS;
|
|
unknown_mechanisms_.clear();
|
|
break;
|
|
}
|
|
} else if (absl::StartsWithIgnoreCase(data, "Content-Length:")) {
|
|
content_length_ = strtoul(data + 15, 0, 0);
|
|
} else if (absl::StartsWithIgnoreCase(data, "Proxy-Connection: Keep-Alive")) {
|
|
expect_close_ = false;
|
|
/*
|
|
} else if (absl::StartsWithIgnoreCase(data, "Connection: close") {
|
|
expect_close_ = true;
|
|
*/
|
|
}
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::EndResponse() {
|
|
if (!expect_close_) {
|
|
SendRequest();
|
|
return;
|
|
}
|
|
|
|
// No point in waiting for the server to close... let's close now
|
|
// TODO: Refactor out PS_WAIT_CLOSE
|
|
state_ = PS_WAIT_CLOSE;
|
|
BufferedReadAdapter::Close();
|
|
OnCloseEvent(this, 0);
|
|
}
|
|
|
|
void AsyncHttpsProxySocket::Error(int error) {
|
|
BufferInput(false);
|
|
Close();
|
|
SetError(error);
|
|
SignalCloseEvent(this, error);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket,
|
|
const SocketAddress& proxy,
|
|
const std::string& username,
|
|
const CryptString& password)
|
|
: BufferedReadAdapter(socket, 1024),
|
|
state_(SS_ERROR),
|
|
proxy_(proxy),
|
|
user_(username),
|
|
pass_(password) {}
|
|
|
|
AsyncSocksProxySocket::~AsyncSocksProxySocket() = default;
|
|
|
|
int AsyncSocksProxySocket::Connect(const SocketAddress& addr) {
|
|
int ret;
|
|
dest_ = addr;
|
|
state_ = SS_INIT;
|
|
BufferInput(true);
|
|
ret = BufferedReadAdapter::Connect(proxy_);
|
|
// TODO: Set state_ appropriately if Connect fails.
|
|
return ret;
|
|
}
|
|
|
|
SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const {
|
|
return dest_;
|
|
}
|
|
|
|
int AsyncSocksProxySocket::Close() {
|
|
state_ = SS_ERROR;
|
|
dest_.Clear();
|
|
return BufferedReadAdapter::Close();
|
|
}
|
|
|
|
Socket::ConnState AsyncSocksProxySocket::GetState() const {
|
|
if (state_ < SS_TUNNEL) {
|
|
return CS_CONNECTING;
|
|
} else if (state_ == SS_TUNNEL) {
|
|
return CS_CONNECTED;
|
|
} else {
|
|
return CS_CLOSED;
|
|
}
|
|
}
|
|
|
|
void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket* socket) {
|
|
SendHello();
|
|
}
|
|
|
|
void AsyncSocksProxySocket::ProcessInput(char* data, size_t* len) {
|
|
RTC_DCHECK(state_ < SS_TUNNEL);
|
|
|
|
ByteBufferReader response(data, *len);
|
|
|
|
if (state_ == SS_HELLO) {
|
|
uint8_t ver, method;
|
|
if (!response.ReadUInt8(&ver) || !response.ReadUInt8(&method))
|
|
return;
|
|
|
|
if (ver != 5) {
|
|
Error(0);
|
|
return;
|
|
}
|
|
|
|
if (method == 0) {
|
|
SendConnect();
|
|
} else if (method == 2) {
|
|
SendAuth();
|
|
} else {
|
|
Error(0);
|
|
return;
|
|
}
|
|
} else if (state_ == SS_AUTH) {
|
|
uint8_t ver, status;
|
|
if (!response.ReadUInt8(&ver) || !response.ReadUInt8(&status))
|
|
return;
|
|
|
|
if ((ver != 1) || (status != 0)) {
|
|
Error(SOCKET_EACCES);
|
|
return;
|
|
}
|
|
|
|
SendConnect();
|
|
} else if (state_ == SS_CONNECT) {
|
|
uint8_t ver, rep, rsv, atyp;
|
|
if (!response.ReadUInt8(&ver) || !response.ReadUInt8(&rep) ||
|
|
!response.ReadUInt8(&rsv) || !response.ReadUInt8(&atyp))
|
|
return;
|
|
|
|
if ((ver != 5) || (rep != 0)) {
|
|
Error(0);
|
|
return;
|
|
}
|
|
|
|
uint16_t port;
|
|
if (atyp == 1) {
|
|
uint32_t addr;
|
|
if (!response.ReadUInt32(&addr) || !response.ReadUInt16(&port))
|
|
return;
|
|
RTC_LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
|
|
} else if (atyp == 3) {
|
|
uint8_t len;
|
|
std::string addr;
|
|
if (!response.ReadUInt8(&len) || !response.ReadString(&addr, len) ||
|
|
!response.ReadUInt16(&port))
|
|
return;
|
|
RTC_LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
|
|
} else if (atyp == 4) {
|
|
std::string addr;
|
|
if (!response.ReadString(&addr, 16) || !response.ReadUInt16(&port))
|
|
return;
|
|
RTC_LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port;
|
|
} else {
|
|
Error(0);
|
|
return;
|
|
}
|
|
|
|
state_ = SS_TUNNEL;
|
|
}
|
|
|
|
// Consume parsed data
|
|
*len = response.Length();
|
|
memmove(data, response.Data(), *len);
|
|
|
|
if (state_ != SS_TUNNEL)
|
|
return;
|
|
|
|
bool remainder = (*len > 0);
|
|
BufferInput(false);
|
|
SignalConnectEvent(this);
|
|
|
|
// FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
|
|
if (remainder)
|
|
SignalReadEvent(this); // TODO: signal this??
|
|
}
|
|
|
|
void AsyncSocksProxySocket::SendHello() {
|
|
ByteBufferWriter request;
|
|
request.WriteUInt8(5); // Socks Version
|
|
if (user_.empty()) {
|
|
request.WriteUInt8(1); // Authentication Mechanisms
|
|
request.WriteUInt8(0); // No authentication
|
|
} else {
|
|
request.WriteUInt8(2); // Authentication Mechanisms
|
|
request.WriteUInt8(0); // No authentication
|
|
request.WriteUInt8(2); // Username/Password
|
|
}
|
|
DirectSend(request.Data(), request.Length());
|
|
state_ = SS_HELLO;
|
|
}
|
|
|
|
void AsyncSocksProxySocket::SendAuth() {
|
|
ByteBufferWriterT<ZeroOnFreeBuffer<char>> request;
|
|
request.WriteUInt8(1); // Negotiation Version
|
|
request.WriteUInt8(static_cast<uint8_t>(user_.size()));
|
|
request.WriteString(user_); // Username
|
|
request.WriteUInt8(static_cast<uint8_t>(pass_.GetLength()));
|
|
size_t len = pass_.GetLength() + 1;
|
|
char* sensitive = new char[len];
|
|
pass_.CopyTo(sensitive, true);
|
|
request.WriteBytes(sensitive, pass_.GetLength()); // Password
|
|
ExplicitZeroMemory(sensitive, len);
|
|
delete[] sensitive;
|
|
DirectSend(request.Data(), request.Length());
|
|
state_ = SS_AUTH;
|
|
}
|
|
|
|
void AsyncSocksProxySocket::SendConnect() {
|
|
ByteBufferWriter request;
|
|
request.WriteUInt8(5); // Socks Version
|
|
request.WriteUInt8(1); // CONNECT
|
|
request.WriteUInt8(0); // Reserved
|
|
if (dest_.IsUnresolvedIP()) {
|
|
std::string hostname = dest_.hostname();
|
|
request.WriteUInt8(3); // DOMAINNAME
|
|
request.WriteUInt8(static_cast<uint8_t>(hostname.size()));
|
|
request.WriteString(hostname); // Destination Hostname
|
|
} else {
|
|
request.WriteUInt8(1); // IPV4
|
|
request.WriteUInt32(dest_.ip()); // Destination IP
|
|
}
|
|
request.WriteUInt16(dest_.port()); // Destination Port
|
|
DirectSend(request.Data(), request.Length());
|
|
state_ = SS_CONNECT;
|
|
}
|
|
|
|
void AsyncSocksProxySocket::Error(int error) {
|
|
state_ = SS_ERROR;
|
|
BufferInput(false);
|
|
Close();
|
|
SetError(SOCKET_EACCES);
|
|
SignalCloseEvent(this, error);
|
|
}
|
|
|
|
} // namespace rtc
|