From b6c9d9a880f6f1a6908a3c62dfccdce3e68dad80 Mon Sep 17 00:00:00 2001 From: Benoit Foucher Date: Wed, 10 Sep 2014 08:47:19 +0200 Subject: ICE-5582 (SOCKs test), ICE-5314 (HTTP proxies), major refactoring of networking code (addition of StreamSocket class abstraction) --- cpp/src/Ice/NetworkProxy.cpp | 325 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 cpp/src/Ice/NetworkProxy.cpp (limited to 'cpp/src/Ice/NetworkProxy.cpp') diff --git a/cpp/src/Ice/NetworkProxy.cpp b/cpp/src/Ice/NetworkProxy.cpp new file mode 100644 index 00000000000..c20417b018b --- /dev/null +++ b/cpp/src/Ice/NetworkProxy.cpp @@ -0,0 +1,325 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2014 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include +#include +#include +#include + +using namespace std; +using namespace IceInternal; + +IceUtil::Shared* IceInternal::upCast(NetworkProxy* p) { return p; } + +#ifndef ICE_OS_WINRT + +namespace +{ + +class SOCKSNetworkProxy : public NetworkProxy +{ +public: + + SOCKSNetworkProxy(const string&, int); + SOCKSNetworkProxy(const Address&); + + virtual void beginWrite(const Address&, Buffer&); + virtual SocketOperation endWrite(Buffer&); + virtual void beginRead(Buffer&); + virtual SocketOperation endRead(Buffer&); + virtual void finish(Buffer&, Buffer&); + virtual NetworkProxyPtr resolveHost(ProtocolSupport) const; + virtual Address getAddress() const; + virtual string getName() const; + virtual ProtocolSupport getProtocolSupport() const; + +private: + + string _host; + int _port; + Address _address; +}; + +class HTTPNetworkProxy : public NetworkProxy +{ +public: + + HTTPNetworkProxy(const string&, int); + HTTPNetworkProxy(const Address&, ProtocolSupport); + + virtual void beginWrite(const Address&, Buffer&); + virtual SocketOperation endWrite(Buffer&); + virtual void beginRead(Buffer&); + virtual SocketOperation endRead(Buffer&); + virtual void finish(Buffer&, Buffer&); + virtual NetworkProxyPtr resolveHost(ProtocolSupport) const; + virtual Address getAddress() const; + virtual string getName() const; + virtual ProtocolSupport getProtocolSupport() const; + +private: + + string _host; + int _port; + Address _address; + ProtocolSupport _protocol; +}; + +} + +SOCKSNetworkProxy::SOCKSNetworkProxy(const string& host, int port) : _host(host), _port(port) +{ + assert(!host.empty()); + memset(&_address, 0, sizeof(_address)); +} + +SOCKSNetworkProxy::SOCKSNetworkProxy(const Address& addr) : _port(0), _address(addr) +{ +} + +void +SOCKSNetworkProxy::beginWrite(const Address& addr, Buffer& buf) +{ + // + // SOCKS connect request + // + buf.b.resize(9); + buf.i = buf.b.begin(); + Ice::Byte* dest = &buf.b[0]; + *dest++ = 0x04; // SOCKS version 4. + *dest++ = 0x01; // Command, establish a TCP/IP stream connection + + const Ice::Byte* src; + + // + // Port (already in big-endian order) + // + src = reinterpret_cast(&addr.saIn.sin_port); + *dest++ = *src++; + *dest++ = *src; + + // + // IPv4 address (already in big-endian order) + // + src = reinterpret_cast(&addr.saIn.sin_addr.s_addr); + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src++; + *dest++ = *src; + + *dest = 0x00; // User ID. +} + +SocketOperation +SOCKSNetworkProxy::endWrite(Buffer& buf) +{ + // Once the request is sent, read the response + return buf.i != buf.b.end() ? SocketOperationWrite : SocketOperationRead; +} + +void +SOCKSNetworkProxy::beginRead(Buffer& buf) +{ + // + // Read the SOCKS4 response whose size is 8 bytes. + // + buf.b.resize(8); + buf.i = buf.b.begin(); +} + +SocketOperation +SOCKSNetworkProxy::endRead(Buffer& buf) +{ + // We're done once we read the response + return buf.i != buf.b.end() ? SocketOperationRead : SocketOperationNone; +} + +void +SOCKSNetworkProxy::finish(Buffer& readBuffer, Buffer&) +{ + readBuffer.i = readBuffer.b.begin(); + + if(readBuffer.b.end() - readBuffer.i < 2) + { + throw Ice::UnmarshalOutOfBoundsException(__FILE__, __LINE__); + } + + const Ice::Byte* src = &(*readBuffer.i); + const Ice::Byte b1 = *src++; + const Ice::Byte b2 = *src++; + if(b1 != 0x00 || b2 != 0x5a) + { + throw Ice::ConnectFailedException(__FILE__, __LINE__); + } +} + +NetworkProxyPtr +SOCKSNetworkProxy::resolveHost(ProtocolSupport protocol) const +{ + assert(!_host.empty()); + return new SOCKSNetworkProxy(getAddresses(_host, _port, protocol, Ice::Random, false, true)[0]); +} + +Address +SOCKSNetworkProxy::getAddress() const +{ + assert(_host.empty()); // Host must be resolved. + return _address; +} + +string +SOCKSNetworkProxy::getName() const +{ + return "SOCKS"; +} + +ProtocolSupport +SOCKSNetworkProxy::getProtocolSupport() const +{ + return EnableIPv4; +} + +HTTPNetworkProxy::HTTPNetworkProxy(const string& host, int port) : + _host(host), _port(port), _protocol(EnableBoth) +{ + assert(!host.empty()); + memset(&_address, 0, sizeof(_address)); +} + +HTTPNetworkProxy::HTTPNetworkProxy(const Address& addr, ProtocolSupport protocol) : + _port(0), _address(addr), _protocol(protocol) +{ +} + +void +HTTPNetworkProxy::beginWrite(const Address& addr, Buffer& buf) +{ + // + // HTTP connect request + // + ostringstream out; + out << "CONNECT " << addrToString(addr) << " HTTP/1.1\r\n" << "Host: " << addrToString(addr) << "\r\n\r\n"; + string str = out.str(); + buf.b.resize(str.size()); + memcpy(&buf.b[0], str.c_str(), str.size()); + buf.i = buf.b.begin(); +} + +SocketOperation +HTTPNetworkProxy::endWrite(Buffer& buf) +{ + // Once the request is sent, read the response + return buf.i != buf.b.end() ? SocketOperationWrite : SocketOperationRead; +} + +void +HTTPNetworkProxy::beginRead(Buffer& buf) +{ + // + // Read the Http response + // + buf.b.resize(7); // Enough space for reading at least HTTP1.1 + buf.i = buf.b.begin(); +} + +SocketOperation +HTTPNetworkProxy::endRead(Buffer& buf) +{ + // + // Check if we received the full HTTP response, if not, continue + // reading otherwise we're done. + // + const Ice::Byte* end = HttpParser().isCompleteMessage(buf.b.begin(), buf.i); + if(!end && buf.i == buf.b.end()) + { + // + // Read one more byte, we can't easily read bytes in advance + // since the transport implenentation might be be able to read + // the data from the memory instead of the socket. This is for + // instance the case with the OpenSSL transport (or we would + // have to use a buffering BIO). + // + buf.b.resize(buf.b.size() + 1); + buf.i = buf.b.begin() + buf.b.size() - 1; + return SocketOperationRead; + } + return SocketOperationNone; +} + +void +HTTPNetworkProxy::finish(Buffer& readBuffer, Buffer&) +{ + HttpParser parser; + parser.parse(readBuffer.b.begin(), readBuffer.b.end()); + if(parser.status() != 200) + { + throw Ice::ConnectFailedException(__FILE__, __LINE__); + } +} + +NetworkProxyPtr +HTTPNetworkProxy::resolveHost(ProtocolSupport protocol) const +{ + assert(!_host.empty()); + return new HTTPNetworkProxy(getAddresses(_host, _port, protocol, Ice::Random, false, true)[0], protocol); +} + +Address +HTTPNetworkProxy::getAddress() const +{ + assert(_host.empty()); // Host must be resolved. + return _address; +} + +string +HTTPNetworkProxy::getName() const +{ + return "HTTP"; +} + +ProtocolSupport +HTTPNetworkProxy::getProtocolSupport() const +{ + return _protocol; +} + +#endif + +NetworkProxyPtr +IceInternal::createNetworkProxy(const Ice::PropertiesPtr& properties, ProtocolSupport protocolSupport) +{ + string proxyHost; + + proxyHost = properties->getProperty("Ice.SOCKSProxyHost"); + if(!proxyHost.empty()) + { +#ifdef ICE_OS_WINRT + throw Ice::InitializationException(__FILE__, __LINE__, "SOCKS proxy not supported with WinRT"); +#else + if(protocolSupport == EnableIPv6) + { + throw Ice::InitializationException(__FILE__, __LINE__, "IPv6 only is not supported with SOCKS4 proxies"); + } + int proxyPort = properties->getPropertyAsIntWithDefault("Ice.SOCKSProxyPort", 1080); + return new SOCKSNetworkProxy(proxyHost, proxyPort); +#endif + } + + proxyHost = properties->getProperty("Ice.HTTPProxyHost"); + if(!proxyHost.empty()) + { +#ifdef ICE_OS_WINRT + throw Ice::InitializationException(__FILE__, __LINE__, "HTTP proxy not supported with WinRT"); +#else + return new HTTPNetworkProxy(proxyHost, properties->getPropertyAsIntWithDefault("Ice.HTTPProxyPort", 1080)); +#endif + } + + return 0; +} -- cgit v1.2.3