Index: net/socket/socks_client_socket.cc |
=================================================================== |
--- net/socket/socks_client_socket.cc (revision 0) |
+++ net/socket/socks_client_socket.cc (revision 0) |
@@ -0,0 +1,390 @@ |
+// Copyright (c) 2009 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 "net/socket/socks_client_socket.h" |
+ |
+#include "base/basictypes.h" |
+#include "build/build_config.h" |
+#if defined(OS_WIN) |
+#include <ws2tcpip.h> |
+#else |
+#include <netdb.h> |
+#endif |
+#include "base/compiler_specific.h" |
+#include "base/trace_event.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_util.h" |
+ |
+namespace net { |
+ |
+// Every SOCKS server requests a user-id from the client. It is optional |
+// and we send an empty string. |
+static const char kEmptyUserId[] = ""; |
+ |
+// The SOCKS4a implementation suggests to use an invalid IP in case the DNS |
+// resolution at client fails. |
+static const uint8 kInvalidIp[] = { 0, 0, 0, 127 }; |
+ |
+// For SOCKS4, the client sends 8 bytes plus the size of the user-id. |
+// For SOCKS4A, this increases to accomodate the unresolved hostname. |
+static const int kWriteHeaderSize = 8; |
+ |
+// For SOCKS4 and SOCKS4a, the server sends 8 bytes for acknowledgement. |
+static const int kReadHeaderSize = 8; |
+ |
+// Server Response codes for SOCKS. |
+static const uint8 kServerResponseOk = 0x5A; |
+static const uint8 kServerResponseRejected = 0x5B; |
+static const uint8 kServerResponseNotReachable = 0x5C; |
+static const uint8 kServerResponseMismatchedUserId = 0x5D; |
+ |
+static const uint8 kSOCKSVersion4 = 0x04; |
+static const uint8 kSOCKSStreamRequest = 0x01; |
+ |
+// A struct holding the essential details of the SOCKS4/4a Server Request. |
+// The port in the header is stored in network byte order. |
+struct SOCKS4ServerRequest { |
+ uint8 version; |
+ uint8 command; |
+ uint16 nw_port; |
+ uint8 ip[4]; |
+}; |
+COMPILE_ASSERT(sizeof(SOCKS4ServerRequest) == kWriteHeaderSize, |
+ socks4_server_request_struct_wrong_size); |
+ |
+// A struct holding details of the SOCKS4/4a Server Response. |
+struct SOCKS4ServerResponse { |
+ uint8 reserved_null; |
+ uint8 code; |
+ uint16 port; |
+ uint8 ip[4]; |
+}; |
+COMPILE_ASSERT(sizeof(SOCKS4ServerResponse) == kReadHeaderSize, |
+ socks4_server_response_struct_wrong_size); |
+ |
+SOCKSClientSocket::SOCKSClientSocket(ClientSocket* transport_socket, |
+ const HostResolver::RequestInfo& req_info, |
+ HostResolver* host_resolver) |
+ : ALLOW_THIS_IN_INITIALIZER_LIST( |
+ io_callback_(this, &SOCKSClientSocket::OnIOComplete)), |
+ transport_(transport_socket), |
+ next_state_(STATE_NONE), |
+ socks_version_(kSOCKS4Unresolved), |
+ user_callback_(NULL), |
+ handshake_buf_len_(0), |
+ buffer_(NULL), |
+ buffer_len_(0), |
+ completed_handshake_(false), |
+ bytes_sent_(0), |
+ bytes_received_(0), |
+ resolver_(host_resolver), |
+ host_request_info_(req_info) { |
+} |
+ |
+SOCKSClientSocket::~SOCKSClientSocket() { |
+ Disconnect(); |
+} |
+ |
+int SOCKSClientSocket::Connect(CompletionCallback* callback) { |
+ DCHECK(transport_.get()); |
+ DCHECK(transport_->IsConnected()); |
+ DCHECK_EQ(STATE_NONE, next_state_); |
+ DCHECK(!user_callback_); |
+ |
+ // If already connected, then just return OK. |
+ if (completed_handshake_) |
+ return OK; |
+ |
+ next_state_ = STATE_RESOLVE_HOST; |
+ |
+ int rv = DoLoop(OK); |
+ if (rv == ERR_IO_PENDING) |
+ user_callback_ = callback; |
+ return rv; |
+} |
+ |
+void SOCKSClientSocket::Disconnect() { |
+ completed_handshake_ = false; |
+ transport_->Disconnect(); |
+} |
+ |
+bool SOCKSClientSocket::IsConnected() const { |
+ return completed_handshake_ && transport_->IsConnected(); |
+} |
+ |
+bool SOCKSClientSocket::IsConnectedAndIdle() const { |
+ return completed_handshake_ && transport_->IsConnectedAndIdle(); |
+} |
+ |
+// Read is called by the transport layer above to read. This can only be done |
+// if the SOCKS handshake is complete. |
+int SOCKSClientSocket::Read(IOBuffer* buf, int buf_len, |
+ CompletionCallback* callback) { |
+ DCHECK(completed_handshake_); |
+ DCHECK_EQ(STATE_NONE, next_state_); |
+ DCHECK(!user_callback_); |
+ |
+ return transport_->Read(buf, buf_len, callback); |
+} |
+ |
+// Write is called by the transport layer. This can only be done if the |
+// SOCKS handshake is complete. |
+int SOCKSClientSocket::Write(IOBuffer* buf, int buf_len, |
+ CompletionCallback* callback) { |
+ DCHECK(completed_handshake_); |
+ DCHECK_EQ(STATE_NONE, next_state_); |
+ DCHECK(!user_callback_); |
+ |
+ return transport_->Write(buf, buf_len, callback); |
+} |
+ |
+void SOCKSClientSocket::DoCallback(int result) { |
+ DCHECK_NE(ERR_IO_PENDING, result); |
+ DCHECK(user_callback_); |
+ |
+ // Since Run() may result in Read being called, |
+ // clear user_callback_ up front. |
+ CompletionCallback* c = user_callback_; |
+ user_callback_ = NULL; |
+ DLOG(INFO) << "Finished setting up SOCKS handshake"; |
+ c->Run(result); |
+} |
+ |
+void SOCKSClientSocket::OnIOComplete(int result) { |
+ DCHECK_NE(STATE_NONE, next_state_); |
+ int rv = DoLoop(result); |
+ if (rv != ERR_IO_PENDING) |
+ DoCallback(rv); |
+} |
+ |
+int SOCKSClientSocket::DoLoop(int last_io_result) { |
+ DCHECK_NE(next_state_, STATE_NONE); |
+ int rv = last_io_result; |
+ do { |
+ State state = next_state_; |
+ next_state_ = STATE_NONE; |
+ switch (state) { |
+ case STATE_RESOLVE_HOST: |
+ DCHECK_EQ(OK, rv); |
+ rv = DoResolveHost(); |
+ break; |
+ case STATE_RESOLVE_HOST_COMPLETE: |
+ rv = DoResolveHostComplete(rv); |
+ break; |
+ case STATE_HANDSHAKE_WRITE: |
+ DCHECK_EQ(OK, rv); |
+ rv = DoHandshakeWrite(); |
+ break; |
+ case STATE_HANDSHAKE_WRITE_COMPLETE: |
+ rv = DoHandshakeWriteComplete(rv); |
+ break; |
+ case STATE_HANDSHAKE_READ: |
+ DCHECK_EQ(OK, rv); |
+ rv = DoHandshakeRead(); |
+ break; |
+ case STATE_HANDSHAKE_READ_COMPLETE: |
+ rv = DoHandshakeReadComplete(rv); |
+ break; |
+ default: |
+ NOTREACHED() << "bad state"; |
+ rv = ERR_UNEXPECTED; |
+ break; |
+ } |
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); |
+ return rv; |
+} |
+ |
+int SOCKSClientSocket::DoResolveHost() { |
+ DCHECK_EQ(kSOCKS4Unresolved, socks_version_); |
+ |
+ next_state_ = STATE_RESOLVE_HOST_COMPLETE; |
+ return resolver_.Resolve(host_request_info_, &addresses_, &io_callback_); |
+} |
+ |
+int SOCKSClientSocket::DoResolveHostComplete(int result) { |
+ DCHECK_EQ(kSOCKS4Unresolved, socks_version_); |
+ |
+ bool ok = (result == OK); |
+ next_state_ = STATE_HANDSHAKE_WRITE; |
+ if (ok) { |
+ DCHECK(addresses_.head()); |
+ |
+ // If the host is resolved to an IPv6 address, we revert to SOCKS4a |
+ // since IPv6 is unsupported by SOCKS4/4a protocol. |
+ struct sockaddr *host_info = addresses_.head()->ai_addr; |
+ if (host_info->sa_family == AF_INET) { |
+ DLOG(INFO) << "Resolved host. Using SOCKS4 to communicate"; |
+ socks_version_ = kSOCKS4; |
+ } else { |
+ DLOG(INFO) << "Resolved host but to IPv6. Using SOCKS4a to communicate"; |
+ socks_version_ = kSOCKS4a; |
+ } |
+ } else { |
+ DLOG(INFO) << "Could not resolve host. Using SOCKS4a to communicate"; |
+ socks_version_ = kSOCKS4a; |
+ } |
+ |
+ // Even if DNS resolution fails, we send OK since the server |
+ // resolves the domain. |
+ return OK; |
+} |
+ |
+// Builds the buffer that is to be sent to the server. |
+// We check whether the SOCKS proxy is 4 or 4A. |
+// In case it is 4A, the record size increases by size of the hostname. |
+void SOCKSClientSocket::BuildHandshakeWriteBuffer() { |
+ DCHECK_NE(kSOCKS4Unresolved, socks_version_); |
+ |
+ int record_size = kWriteHeaderSize + arraysize(kEmptyUserId); |
+ if (socks_version_ == kSOCKS4a) { |
+ record_size += host_request_info_.hostname().size() + 1; |
+ } |
+ |
+ buffer_len_ = record_size; |
+ buffer_.reset(new char[buffer_len_]); |
+ |
+ SOCKS4ServerRequest* request = |
+ reinterpret_cast<SOCKS4ServerRequest*>(buffer_.get()); |
+ |
+ request->version = kSOCKSVersion4; |
+ request->command = kSOCKSStreamRequest; |
+ request->nw_port = htons(host_request_info_.port()); |
+ |
+ if (socks_version_ == kSOCKS4) { |
+ const struct addrinfo* ai = addresses_.head(); |
+ DCHECK(ai); |
+ // If the sockaddr is IPv6, we have already marked the version to socks4a |
+ // and so this step does not get hit. |
+ struct sockaddr_in *ipv4_host = |
+ reinterpret_cast<struct sockaddr_in*>(ai->ai_addr); |
+ memcpy(&request->ip, &(ipv4_host->sin_addr), sizeof(ipv4_host->sin_addr)); |
+ |
+ DLOG(INFO) << "Resolved Host is : " << NetAddressToString(ai); |
+ } else if (socks_version_ == kSOCKS4a) { |
+ // invalid IP of the form 0.0.0.127 |
+ memcpy(&request->ip, kInvalidIp, arraysize(kInvalidIp)); |
+ } else { |
+ NOTREACHED(); |
+ } |
+ |
+ memcpy(&buffer_[kWriteHeaderSize], kEmptyUserId, arraysize(kEmptyUserId)); |
+ |
+ if (socks_version_ == kSOCKS4a) { |
+ memcpy(&buffer_[kWriteHeaderSize + arraysize(kEmptyUserId)], |
+ host_request_info_.hostname().c_str(), |
+ host_request_info_.hostname().size() + 1); |
+ } |
+} |
+ |
+// Writes the SOCKS handshake data to the underlying socket connection. |
+int SOCKSClientSocket::DoHandshakeWrite() { |
+ next_state_ = STATE_HANDSHAKE_WRITE_COMPLETE; |
+ |
+ if (!buffer_.get()) { |
+ BuildHandshakeWriteBuffer(); |
+ bytes_sent_ = 0; |
+ } |
+ |
+ handshake_buf_len_ = buffer_len_ - bytes_sent_; |
+ DCHECK_GT(handshake_buf_len_, 0); |
+ handshake_buf_ = new IOBuffer(handshake_buf_len_); |
+ memcpy(handshake_buf_.get()->data(), &buffer_[bytes_sent_], |
+ handshake_buf_len_); |
+ return transport_->Write(handshake_buf_, handshake_buf_len_, &io_callback_); |
+} |
+ |
+int SOCKSClientSocket::DoHandshakeWriteComplete(int result) { |
+ DCHECK_NE(kSOCKS4Unresolved, socks_version_); |
+ |
+ if (result < 0) |
+ return result; |
+ |
+ bytes_sent_ += result; |
+ if (bytes_sent_ == buffer_len_) { |
+ next_state_ = STATE_HANDSHAKE_READ; |
+ buffer_.reset(NULL); |
+ } else if (bytes_sent_ < buffer_len_) { |
+ next_state_ = STATE_HANDSHAKE_WRITE; |
+ } else { |
+ return ERR_UNEXPECTED; |
+ } |
+ |
+ return OK; |
+} |
+ |
+int SOCKSClientSocket::DoHandshakeRead() { |
+ DCHECK_NE(kSOCKS4Unresolved, socks_version_); |
+ |
+ next_state_ = STATE_HANDSHAKE_READ_COMPLETE; |
+ |
+ if (!buffer_.get()) { |
+ buffer_.reset(new char[kReadHeaderSize]); |
+ buffer_len_ = kReadHeaderSize; |
+ bytes_received_ = 0; |
+ } |
+ |
+ handshake_buf_len_ = buffer_len_ - bytes_received_; |
+ handshake_buf_ = new IOBuffer(handshake_buf_len_); |
+ return transport_->Read(handshake_buf_, handshake_buf_len_, &io_callback_); |
+} |
+ |
+int SOCKSClientSocket::DoHandshakeReadComplete(int result) { |
+ DCHECK_NE(kSOCKS4Unresolved, socks_version_); |
+ |
+ if (result < 0) |
+ return result; |
+ if (bytes_received_ + result > buffer_len_) |
+ return ERR_INVALID_RESPONSE; |
+ |
+ memcpy(buffer_.get() + bytes_received_, handshake_buf_->data(), result); |
+ bytes_received_ += result; |
+ if (bytes_received_ < buffer_len_) { |
+ next_state_ = STATE_HANDSHAKE_READ; |
+ return OK; |
+ } |
+ |
+ SOCKS4ServerResponse* response = |
+ reinterpret_cast<SOCKS4ServerResponse*>(buffer_.get()); |
+ |
+ if (response->reserved_null != 0x00) { |
+ LOG(ERROR) << "Unknown response from SOCKS server."; |
+ return ERR_INVALID_RESPONSE; |
+ } |
+ |
+ // TODO(arindam): Add SOCKS specific failure codes in net_error_list.h |
+ switch (response->code) { |
+ case kServerResponseOk: |
+ completed_handshake_ = true; |
+ return OK; |
+ case kServerResponseRejected: |
+ LOG(ERROR) << "SOCKS request rejected or failed"; |
+ return ERR_FAILED; |
+ case kServerResponseNotReachable: |
+ LOG(ERROR) << "SOCKS request failed because client is not running " |
+ << "identd (or not reachable from the server)"; |
+ return ERR_NAME_NOT_RESOLVED; |
+ case kServerResponseMismatchedUserId: |
+ LOG(ERROR) << "SOCKS request failed because client's identd could " |
+ << "not confirm the user ID string in the request"; |
+ return ERR_FAILED; |
+ default: |
+ LOG(ERROR) << "SOCKS server sent unknown response"; |
+ return ERR_INVALID_RESPONSE; |
+ } |
+ |
+ // Note: we ignore the last 6 bytes as specified by the SOCKS protocol |
+} |
+ |
+#if defined(OS_LINUX) |
+// Identical to posix system call getpeername(). |
+// Needed by ssl_client_socket_nss. |
+int SOCKSClientSocket::GetPeerName(struct sockaddr *name, socklen_t *namelen) { |
+ // Default implementation just permits some unit tests to link. |
+ NOTREACHED(); |
+ return ERR_UNEXPECTED; |
+} |
+#endif |
+ |
+} // namespace net |
+ |
Property changes on: net\socket\socks_client_socket.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |