E-MailRelay

gsocks.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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{
29 if( location.socks() )
30 {
31 unsigned int far_port = location.socksFarPort() ;
32 if( !Address::validPort(far_port) || far_port > 0xffffU )
33 throw SocksError( "invalid port" ) ;
34
35 m_request = buildPdu( location.socksFarHost() , far_port ) ;
36 }
37}
38
39std::string GNet::Socks::buildPdu( const std::string & far_host , unsigned int far_port )
40{
41 g_port_t far_port_n = htons( static_cast<g_port_t>(far_port) ) ;
42 g_port_t far_port_lo = far_port_n & 0xffU ;
43 g_port_t far_port_hi = (far_port_n>>8U) & g_port_t(0xffU) ;
44
45 std::string userid ; // TODO - socks userid
46 std::string data ;
47 data.reserve( far_host.size() + 10U ) ;
48 data.append( 1U , 4 ) ; // version 4
49 data.append( 1U , 1 ) ; // connect request
50 data.append( 1U , static_cast<char>(far_port_lo) ) ;
51 data.append( 1U , static_cast<char>(far_port_hi) ) ;
52 data.append( 1U , 0 ) ; // invalid ipv4 (signals 4A protocol extension)
53 data.append( 1U , 0 ) ; // invalid ipv4
54 data.append( 1U , 0 ) ; // invalid ipv4
55 data.append( 1U , 1 ) ; // invalid ipv4
56 data.append( userid ) ;
57 data.append( 1U , 0 ) ; // NUL terminator
58 data.append( far_host ) ; // 4A protocol extension: get the socks server to do dns
59 data.append( 1U , 0 ) ; // NUL terminator
60
61 return data ;
62}
63
64 bool GNet::Socks::send( G::ReadWrite & io )
65{
66 if( m_request_offset >= m_request.size() )
67 return true ;
68
69 const char * p = m_request.data() + m_request_offset ;
70 std::size_t n = m_request.size() - m_request_offset ;
71
72 ssize_t rc = io.write( p , n ) ;
73 if( rc < 0 && !io.eWouldBlock() )
74 {
75 throw SocksError( "socket write error" ) ;
76 }
77 else if( rc < 0 || static_cast<std::size_t>(rc) < n )
78 {
79 std::size_t nsent = rc < 0 ? std::size_t(0U) : static_cast<std::size_t>(rc) ;
80 m_request_offset += nsent ;
81 return false ;
82 }
83 else
84 {
85 m_request_offset = m_request.size() ;
86 return true ;
87 }
88}
89
90 bool GNet::Socks::read( G::ReadWrite & io )
91{
92 std::array<char,8U> buffer {} ;
93 ssize_t rc = io.read( buffer.data() , buffer.size() ) ;
94 if( rc == 0 )
95 {
96 throw SocksError( "disconnected" ) ;
97 }
98 else if( rc == -1 && !io.eWouldBlock() )
99 {
100 throw SocksError( "socket read error" ) ;
101 }
102 else if( rc < 0 )
103 {
104 return false ; // go again
105 }
106 else
107 {
108 G_ASSERT( rc >= 1 && rc <= 8 ) ;
109 std::size_t n = std::min( buffer.size() , static_cast<std::size_t>(rc) ) ;
110 m_response.append( buffer.data() , n ) ;
111 }
112
113 if( m_response.size() >= 8U )
114 {
115 G_ASSERT( m_response.size() == 8U ) ;
116 if( m_response[0] != 0 )
117 {
118 throw SocksError( "invalid response" ) ;
119 }
120 else if( m_response[1] != 'Z' )
121 {
122 throw SocksError( "request rejected" ) ;
123 }
124 return true ;
125 }
126 else
127 {
128 return false ;
129 }
130}
131
GNet::Address::validPort
static bool validPort(unsigned int n)
Returns true if the port number is within the valid range.
Definition: gaddress.cpp:453
GNet::Location
A class that represents the remote target for out-going client connections.
Definition: glocation.h:70
GNet::Location::socksFarHost
std::string socksFarHost() const
Returns the port for the socks far server.
Definition: glocation.cpp:217
GNet::Location::socksFarPort
unsigned int socksFarPort() const
Returns the port number for the socks far server.
Definition: glocation.cpp:211
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:83
GNet::Socks::send
bool send(G::ReadWrite &)
Sends the connect-request pdu using the given file descriptor.
Definition: gsocks.cpp:64
GNet::Socks::read
bool read(G::ReadWrite &)
Reads the response using the given file descriptor.
Definition: gsocks.cpp:90
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:39
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