E-MailRelay

gsocks.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gsocks.cpp
19///
20
21#include "gdef.h"
22#include "gsocks.h"
23#include "gassert.h"
24#include <array>
25#include <algorithm>
26
27 GNet::Socks::Socks( const Location & location ) :
28 m_request_offset(0U)
29{
30 if( location.socks() )
31 {
32 unsigned int far_port = location.socksFarPort() ;
33 if( !Address::validPort(far_port) || far_port > 0xffffU )
34 throw SocksError( "invalid port" ) ;
35
36 m_request = buildPdu( location.socksFarHost() , far_port ) ;
37 m_request_offset = 0U ;
38 }
39}
40
41std::string GNet::Socks::buildPdu( const std::string & far_host , unsigned int far_port )
42{
43 g_port_t far_port_n = htons( static_cast<g_port_t>(far_port) ) ;
44 g_port_t far_port_lo = far_port_n & 0xffU ;
45 g_port_t far_port_hi = (far_port_n>>8U) & g_port_t(0xffU) ;
46
47 std::string userid ; // TODO - socks userid
48 std::string data ;
49 data.reserve( far_host.size() + 10U ) ;
50 data.append( 1U , 4 ) ; // version 4
51 data.append( 1U , 1 ) ; // connect request
52 data.append( 1U , static_cast<char>(far_port_lo) ) ;
53 data.append( 1U , static_cast<char>(far_port_hi) ) ;
54 data.append( 1U , 0 ) ; // invalid ipv4 (signals 4A protocol extension)
55 data.append( 1U , 0 ) ; // invalid ipv4
56 data.append( 1U , 0 ) ; // invalid ipv4
57 data.append( 1U , 1 ) ; // invalid ipv4
58 data.append( userid ) ;
59 data.append( 1U , 0 ) ; // NUL terminator
60 data.append( far_host ) ; // 4A protocol extension: get the socks server to do dns
61 data.append( 1U , 0 ) ; // NUL terminator
62
63 return data ;
64}
65
66 bool GNet::Socks::send( G::ReadWrite & io )
67{
68 if( m_request_offset >= m_request.size() )
69 return true ;
70
71 const char * p = m_request.data() + m_request_offset ;
72 std::size_t n = m_request.size() - m_request_offset ;
73
74 ssize_t rc = io.write( p , n ) ;
75 if( rc < 0 && !io.eWouldBlock() )
76 {
77 throw SocksError( "socket write error" ) ;
78 }
79 else if( rc < 0 || static_cast<std::size_t>(rc) < n )
80 {
81 std::size_t nsent = rc < 0 ? std::size_t(0U) : static_cast<std::size_t>(rc) ;
82 m_request_offset += nsent ;
83 return false ;
84 }
85 else
86 {
87 m_request_offset = m_request.size() ;
88 return true ;
89 }
90}
91
92 bool GNet::Socks::read( G::ReadWrite & io )
93{
94 std::array<char,8U> buffer {} ;
95 ssize_t rc = io.read( &buffer[0] , buffer.size() ) ;
96 if( rc == 0 )
97 {
98 throw SocksError( "disconnected" ) ;
99 }
100 else if( rc == -1 && !io.eWouldBlock() )
101 {
102 throw SocksError( "socket read error" ) ;
103 }
104 else if( rc < 0 )
105 {
106 return false ; // go again
107 }
108 else
109 {
110 G_ASSERT( rc >= 1 && rc <= 8 ) ;
111 std::size_t n = std::min( buffer.size() , static_cast<std::size_t>(rc) ) ;
112 m_response.append( &buffer[0] , n ) ;
113 }
114
115 if( m_response.size() >= 8U )
116 {
117 G_ASSERT( m_response.size() == 8U ) ;
118 if( m_response[0] != 0 )
119 {
120 throw SocksError( "invalid response" ) ;
121 }
122 else if( m_response[1] != 'Z' )
123 {
124 throw SocksError( "request rejected" ) ;
125 }
126 return true ;
127 }
128 else
129 {
130 return false ;
131 }
132}
133
GNet::Address::validPort
static bool validPort(unsigned int n)
Returns true if the port number is within the valid range.
Definition: gaddress.cpp:470
GNet::Location
A class that represents the remote target for out-going client connections.
Definition: glocation.h:71
GNet::Location::socksFarHost
std::string socksFarHost() const
Returns the port for the socks far server.
Definition: glocation.cpp:215
GNet::Location::socksFarPort
unsigned int socksFarPort() const
Returns the port number for the socks far server.
Definition: glocation.cpp:209
GNet::Location::socks
static Location socks(const std::string &socks_server, const std::string &far_server)
Factory function for a remote location explicitly accessed via socks.
Definition: glocation.cpp:77
GNet::Socks::send
bool send(G::ReadWrite &)
Sends the connect-request pdu using the given file descriptor.
Definition: gsocks.cpp:66
GNet::Socks::read
bool read(G::ReadWrite &)
Reads the response using the given file descriptor.
Definition: gsocks.cpp:92
GNet::Socks::Socks
Socks(const Location &)
Constructor.
Definition: gsocks.cpp:27
GNet::Socks::buildPdu
static std::string buildPdu(const std::string &far_host, unsigned int far_port)
Builds a SOCKS4a connect request pdu.
Definition: gsocks.cpp:41
G::ReadWrite
An abstract interface for reading and writing from a non-blocking i/o channel.
Definition: greadwrite.h:50
G::ReadWrite::write
virtual ssize_type write(const char *buf, size_type len)=0
Sends data.
G::ReadWrite::eWouldBlock
virtual bool eWouldBlock() const =0
See read() and write().
G::ReadWrite::read
virtual ssize_type read(char *buffer, size_type buffer_length)=0
Reads data.
gassert.h
gsocks.h