Skip to content
This repository has been archived by the owner on Jan 4, 2019. It is now read-only.

Commit

Permalink
Merge pull request #470 from riastradh-brave/riastradh-socks5-auth
Browse files Browse the repository at this point in the history
SOCKS5 authentication support
  • Loading branch information
bridiver authored Mar 29, 2018
2 parents 677557f + b9e3f89 commit abba206
Show file tree
Hide file tree
Showing 7 changed files with 630 additions and 0 deletions.
15 changes: 15 additions & 0 deletions chromium_src/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ source_set("browser") {
":favicon",
":history",
":metrics",
":net",
":password_manager",
":sessions",
":web_data",
Expand Down Expand Up @@ -1196,3 +1197,17 @@ source_set("sessions") {
"//third_party/protobuf:protobuf_lite",
]
}

source_set("net") {
configs += [ "//electron/build:electron_config" ]
configs += [ ":chromium_src_config" ]

sources = [
"net/base/host_port_pair_auth.cc",
"net/base/url_auth_util.cc",
"net/socket/socks5_client_socket_auth.cc",
]
deps = [
"//net",
]
}
50 changes: 50 additions & 0 deletions chromium_src/net/base/host_port_pair_auth.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2017 The Brave 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 "net/base/host_port_pair.h"

#include "base/strings/string_number_conversions.h"

namespace net {

HostPortPair::HostPortPair(const std::string& username,
const std::string& password,
const std::string& in_host, uint16_t in_port)
: username_(username), password_(password),
host_(in_host), port_(in_port) {
}

HostPortPair::HostPortPair(const std::string& up_host, uint16_t in_port)
: port_(in_port) {
std::string::const_iterator begin = up_host.begin();
std::string::const_iterator end = up_host.end();
std::string::const_iterator at = std::find(begin, end, '@');
if (at == end) {
host_ = up_host;
} else {
std::string::const_iterator colon = std::find(begin, at, ':');
username_ = std::string(begin, colon);
if (colon != at)
password_ = std::string(colon + 1, at);
host_ = std::string(at + 1, end);
}
}

std::string HostPortPair::ToString() const {
std::string ret;
if (username_.size() != 0 || password_.size() != 0) {
ret += username_;
if (password_.size() != 0) {
ret += ':';
ret += password_;
}
ret += '@';
}
ret += HostForURL();
ret += ':';
ret += base::UintToString(port_);
return ret;
}

} // namespace net
96 changes: 96 additions & 0 deletions chromium_src/net/base/url_auth_util.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2013 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 <iostream>
#include <string>

#include "net/base/url_auth_util.h"
#include "url/third_party/mozilla/url_parse.h"
#include "url/url_canon_ip.h"

namespace net {

// Copypasta of ParseHostAndPort that extracts the username and
// password instead of rejecting them.
bool ParseAuthHostAndPort(std::string::const_iterator host_and_port_begin,
std::string::const_iterator host_and_port_end,
std::string* up_host_ret,
int* port) {
if (host_and_port_begin >= host_and_port_end)
return false;

// When using url, we use char*.
const char* auth_begin = &(*host_and_port_begin);
int auth_len = host_and_port_end - host_and_port_begin;

url::Component auth_component(0, auth_len);
url::Component username_component;
url::Component password_component;
url::Component hostname_component;
url::Component port_component;

url::ParseAuthority(auth_begin, auth_component, &username_component,
&password_component, &hostname_component, &port_component);

if (!hostname_component.is_nonempty())
return false; // Failed parsing.

int parsed_port_number = -1;
if (port_component.is_nonempty()) {
parsed_port_number = url::ParsePort(auth_begin, port_component);

// If parsing failed, port_number will be either PORT_INVALID or
// PORT_UNSPECIFIED, both of which are negative.
if (parsed_port_number < 0)
return false; // Failed parsing the port number.
}

if (port_component.len == 0)
return false; // Reject inputs like "foo:"

unsigned char tmp_ipv6_addr[16];

// If the hostname starts with a bracket, it is either an IPv6 literal or
// invalid. If it is an IPv6 literal then strip the brackets.
if (hostname_component.len > 0 &&
auth_begin[hostname_component.begin] == '[') {
if (auth_begin[hostname_component.end() - 1] == ']' &&
url::IPv6AddressToNumber(
auth_begin, hostname_component, tmp_ipv6_addr)) {
// Strip the brackets.
hostname_component.begin++;
hostname_component.len -= 2;
} else {
return false;
}
}

// Reassemble user:pass@host as up_host.
std::string up_host;
if (username_component.is_valid() || password_component.is_valid()) {
if (username_component.is_valid()) {
std::string username(auth_begin + username_component.begin,
username_component.len);
up_host += username;
}
if (password_component.is_valid()) {
std::string password(auth_begin + password_component.begin,
password_component.len);
up_host += ":";
up_host += password;
}
up_host += "@";
}
std::string hostname(auth_begin + hostname_component.begin,
hostname_component.len);
up_host += hostname;

// Pass results back to caller.
*up_host_ret = up_host;
*port = parsed_port_number;

return true; // Success.
}

}
20 changes: 20 additions & 0 deletions chromium_src/net/base/url_auth_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2013 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.

#ifndef NET_BASE_URL_AUTH_UTIL_H_
#define NET_BASE_URL_AUTH_UTIL_H_

#include "net/base/net_export.h"

namespace net {

NET_EXPORT bool ParseAuthHostAndPort(
std::string::const_iterator host_and_port_begin,
std::string::const_iterator host_and_port_end,
std::string* up_host_ret,
int* port);

}

#endif // NET_BASE_URL_AUTH_UTIL_H_
148 changes: 148 additions & 0 deletions chromium_src/net/socket/socks5_client_socket_auth.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2017 The Brave 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 "net/base/io_buffer.h"
#include "net/socket/socks5_client_socket_auth.h"

namespace net {

SOCKS5ClientSocketAuth::SOCKS5ClientSocketAuth(
std::unique_ptr<ClientSocketHandle> transport_socket,
const HostResolver::RequestInfo& req_info,
const NetworkTrafficAnnotationTag& traffic_annotation,
const HostPortPair& proxy_host_port)
: SOCKS5ClientSocket(std::move(transport_socket), req_info,
traffic_annotation),
proxy_host_port_(proxy_host_port),
next_state_(STATE_INIT_WRITE) {
}

SOCKS5ClientSocketAuth::~SOCKS5ClientSocketAuth() = default;

const std::string& SOCKS5ClientSocketAuth::username() {
return proxy_host_port_.username();
}

const std::string& SOCKS5ClientSocketAuth::password() {
return proxy_host_port_.password();
}

bool SOCKS5ClientSocketAuth::do_auth() {
return username().size() != 0 || password().size() != 0;
}

uint8_t SOCKS5ClientSocketAuth::auth_method() {
if (!do_auth())
return 0x00;
return 0x02;
}

static const size_t kSOCKSAuthUsernamePasswordResponseLen = 2;

int SOCKS5ClientSocketAuth::Authenticate(
int rv, ClientSocketHandle& transport, NetLogWithSource& net_log,
CompletionCallback& callback) {
if (!do_auth()) {
DCHECK_EQ(OK, rv);
return OK;
}
do {
switch (next_state_) {
case STATE_INIT_WRITE: {
DCHECK_EQ(OK, rv);
// Initialize the buffer with 
// 0x01, usernamelen, username, passwordlen, password
size_t usernamelen = username().size();
size_t passwordlen = password().size();
buffer_ = std::string(1 + 1 + usernamelen + 1 + passwordlen, 0);
buffer_[0] = 0x01;
buffer_[1] = usernamelen;
buffer_.replace(2, usernamelen, username());
buffer_[2 + usernamelen] = passwordlen;
buffer_.replace(2 + usernamelen + 1, passwordlen, password());
DCHECK_EQ(buffer_.size(), 2 + usernamelen + 1 + passwordlen);
buffer_left_ = buffer_.size();
next_state_ = STATE_WRITE;
rv = OK;
break;
}
case STATE_WRITE:
DCHECK_EQ(OK, rv);
DCHECK_LT(0, buffer_left_);
iobuf_ = new IOBuffer(buffer_left_);
memcpy(iobuf_->data(),
&buffer_.data()[buffer_.size() - buffer_left_],
buffer_left_);
next_state_ = STATE_WRITE_COMPLETE;
net_log.BeginEvent(NetLogEventType::SOCKS5_AUTH_WRITE);
rv = transport.socket()->Write(iobuf_.get(), buffer_left_, callback);
break;

case STATE_WRITE_COMPLETE:
// TODO(riastradh): Zero iobuf? Zero buffer?
net_log.EndEventWithNetErrorCode(NetLogEventType::SOCKS5_AUTH_WRITE,
std::max(rv, 0));
if (rv < 0) {
next_state_ = STATE_BAD;
return rv;
}
DCHECK_LE(static_cast<size_t>(rv), buffer_left_);
buffer_left_ -= rv;
next_state_ = (buffer_left_ == 0 ? STATE_INIT_READ : STATE_WRITE);
rv = OK;
break;

case STATE_INIT_READ:
DCHECK_EQ(OK, rv);
buffer_.clear();
buffer_left_ = kSOCKSAuthUsernamePasswordResponseLen;
iobuf_ = new IOBuffer(buffer_left_);
next_state_ = STATE_READ;
rv = OK;
break;

case STATE_READ:
DCHECK_EQ(OK, rv);
iobuf_ = new IOBuffer(buffer_left_);
next_state_ = STATE_READ_COMPLETE;
net_log.BeginEvent(NetLogEventType::SOCKS5_AUTH_READ);
rv = transport.socket()->Read(iobuf_.get(), buffer_left_, callback);
break;

case STATE_READ_COMPLETE:
net_log.EndEventWithNetErrorCode(NetLogEventType::SOCKS5_AUTH_READ,
std::max(rv, 0));
if (rv < 0) {
next_state_ = STATE_BAD;
return rv;
}
DCHECK_LE(static_cast<size_t>(rv), buffer_left_);
buffer_.append(iobuf_->data(), rv);
buffer_left_ -= rv;
next_state_ = (buffer_left_ == 0 ? STATE_DONE : STATE_READ);
rv = OK;
break;

case STATE_DONE: {
DCHECK_EQ(OK, rv);
DCHECK_EQ(buffer_.size(), kSOCKSAuthUsernamePasswordResponseLen);
static_assert(kSOCKSAuthUsernamePasswordResponseLen == 2, "bad size");
uint8_t ver = buffer_[0];
uint8_t status = buffer_[1];
next_state_ = STATE_BAD; // Caller had better stop here.
if (ver != 0x01 || status != 0x00)
return ERR_FAILED;
return OK;
}

case STATE_BAD:
default:
NOTREACHED() << "bad state";
return ERR_UNEXPECTED;
}
} while (rv != ERR_IO_PENDING);
return rv;
}

} // namespace net
46 changes: 46 additions & 0 deletions chromium_src/net/socket/socks5_client_socket_auth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2017 The Brave Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_SOCKET_SOCKS5_CLIENT_SOCKET_AUTH_H_
#define NET_SOCKET_SOCKS5_CLIENT_SOCKET_AUTH_H_

#include "net/socket/socks5_client_socket.h"

namespace net {

class NET_EXPORT_PRIVATE SOCKS5ClientSocketAuth : public SOCKS5ClientSocket {
public:
SOCKS5ClientSocketAuth(std::unique_ptr<ClientSocketHandle> transport_socket,
const HostResolver::RequestInfo& req_info,
const NetworkTrafficAnnotationTag& traffic_annotation,
const HostPortPair& proxy_host_port);
~SOCKS5ClientSocketAuth() override;
private:
bool do_auth();
const std::string& username();
const std::string& password();
uint8_t auth_method() override;
int Authenticate(int rv,
ClientSocketHandle& transport, NetLogWithSource& net_log,
CompletionCallback& callback) override;
const HostPortPair proxy_host_port_;
enum {
STATE_INIT_WRITE = 0,
STATE_WRITE,
STATE_WRITE_COMPLETE,
STATE_INIT_READ,
STATE_READ,
STATE_READ_COMPLETE,
STATE_DONE,
STATE_BAD,
} next_state_;
scoped_refptr<IOBuffer> iobuf_;
std::string buffer_;
size_t buffer_left_;
DISALLOW_COPY_AND_ASSIGN(SOCKS5ClientSocketAuth);
};

} // namespace net

#endif // NET_SOCKET_SOCKS5_CLIENT_SOCKET_AUTH_H_
Loading

0 comments on commit abba206

Please sign in to comment.