This repository has been archived by the owner on Jan 4, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #470 from riastradh-brave/riastradh-socks5-auth
SOCKS5 authentication support
- Loading branch information
Showing
7 changed files
with
630 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_ |
Oops, something went wrong.