11 #include <sys/socket.h>
12 #include <sys/types.h>
16 #include <glog/logging.h>
18 #include <fmt/format.h>
22 constexpr auto socks_version
= 5;
25 using octet
= uint8_t;
27 octet
constexpr lo(uint16_t n
) { return octet(n
& 0xFF); }
28 octet
constexpr hi(uint16_t n
) { return octet((n
>> 8) & 0xFF); }
30 enum class auth_method
: octet
{
35 constexpr char const* c_str(auth_method auth
)
38 case auth_method::no_auth
: return "no authentication required";
39 case auth_method::none
: return "no acceptable methods";
41 return "*** unknown auth_method ***";
44 std::ostream
& operator<<(std::ostream
& os
, auth_method
const& auth
)
46 return os
<< c_str(auth
);
50 octet version_
{socks_version
};
52 auth_method method_
{auth_method::no_auth
};
60 auto version() const { return version_
; }
61 auto method() const { return method_
; }
64 enum class command
: octet
{
70 constexpr char const* c_str(command cmd
)
73 case command::connect
: return "connect";
74 case command::bind
: return "bind";
75 case command::udp_associate
: return "UDP associate";
77 return "*** unknown command ***";
80 enum class address_type
: octet
{
86 constexpr char const* c_str(address_type at
)
89 case address_type::ip4_address
: return "IPv4 address";
90 case address_type::domain_name
: return "domain name";
91 case address_type::ip6_address
: return "IPv6 address";
93 return "*** unknown address type ***";
96 std::ostream
& operator<<(std::ostream
& os
, address_type
const& at
)
98 return os
<< c_str(at
);
101 class request_domain
{
102 octet version_
{socks_version
};
103 command cmd_
{command::connect
};
105 address_type typ_
{address_type::domain_name
};
106 octet var_
[258]; // 255 + 1 + 2
109 request_domain(char const* addr
, uint16_t port
)
111 auto const len
= strlen(addr
);
113 var_
[0] = static_cast<octet
>(len
);
114 memcpy(var_
+ 1, addr
, len
);
115 var_
[len
+ 1] = hi(port
);
116 var_
[len
+ 2] = lo(port
);
119 ssize_t
size() const { return offsetof(request_domain
, var_
) + var_
[0] + 3; }
123 octet version_
{socks_version
};
124 command cmd_
{command::connect
};
126 address_type typ_
{address_type::ip4_address
};
131 void addr_(char const* addr
)
133 CHECK_EQ(inet_pton(AF_INET
, addr
, reinterpret_cast<void*>(ip4_
)), 1);
135 void port_(uint16_t port
)
142 request4(char const* addr
, uint16_t port
)
149 enum class reply_field
: octet
{
157 command_not_supported
,
158 address_type_not_supported
,
161 constexpr char const* c_str(reply_field rp
)
164 case reply_field::succeeded
: return "succeeded";
165 case reply_field::server_failure
: return "server_failure";
166 case reply_field::not_allowed
: return "not_allowed";
167 case reply_field::network_unreachable
: return "network_unreachable";
168 case reply_field::host_unreachable
: return "host_unreachable";
169 case reply_field::connection_refused
: return "connection_refused";
170 case reply_field::TTL_expired
: return "TTL_expired";
171 case reply_field::command_not_supported
: return "command_not_supported";
172 case reply_field::address_type_not_supported
:
173 return "address_type_not_supported";
175 return "*** unknown reply field ***";
178 std::ostream
& operator<<(std::ostream
& os
, reply_field
const& rp
)
180 return os
<< c_str(rp
);
193 auto version() const { return version_
; }
194 auto reply() const { return reply_
; }
195 auto type() const { return type_
; }
197 std::string
addr() const
201 CHECK_NOTNULL(inet_ntop(AF_INET
, reinterpret_cast<void const*>(ip4_
), &a
[0],
205 uint16_t port() const { return (port_hi_
<< 8) + port_lo_
; }
209 get_tlsa_rrs(DNS::Resolver
& res
, Domain
const& domain
, uint16_t port
)
211 CHECK(!domain
.ascii().empty());
213 auto const tlsa
{fmt::format("_{:d}._tcp.{}", port
, domain
.ascii())};
215 DNS::Query
q(res
, DNS::RR_type::TLSA
, tlsa
);
218 LOG(INFO
) << "TLSA data not found for " << domain
<< ':' << port
;
221 auto const tlsa_rrs
{q
.get_records()};
223 if (q
.bogus_or_indeterminate()) {
224 LOG(WARNING
) << "TLSA data bogus_or_indeterminate";
231 void read_checked(int fd
, T
& obj
, std::string_view msg
)
233 PCHECK(read(fd
, &obj
, sizeof(obj
)) == sizeof(obj
)) << msg
;
237 void write_checked(int fd
, T
const& obj
, std::string_view msg
)
239 PCHECK(write(fd
, &obj
, sizeof(obj
)) == sizeof(obj
)) << msg
;
243 int main(int argc
, char* argv
[])
245 auto const fd
= socket(AF_INET
, SOCK_STREAM
, 0);
246 PCHECK(fd
>= 0) << "socket() failed";
248 auto constexpr tor_host
{"127.0.0.1"};
249 auto constexpr tor_port
{9050};
251 auto in4
{sockaddr_in
{}};
252 in4
.sin_family
= AF_INET
;
253 in4
.sin_port
= htons(tor_port
);
254 CHECK_EQ(inet_pton(AF_INET
, tor_host
, reinterpret_cast<void*>(&in4
.sin_addr
)),
256 PCHECK(connect(fd
, reinterpret_cast<const sockaddr
*>(&in4
), sizeof(in4
)) == 0)
257 << "connect failed: ";
260 write_checked(fd
, grtng
, "greeting write failed");
263 read_checked(fd
, rspns
, "response read failed");
265 CHECK_EQ(rspns
.version(), socks_version
);
266 CHECK_EQ(rspns
.method(), auth_method::no_auth
);
268 auto constexpr domain
{"digilicious.com"};
269 uint16_t constexpr port
{443};
271 // request4 request("108.83.36.113", port);
272 request_domain
request(domain
, port
);
273 PCHECK(write(fd
, &request
, request
.size()) == request
.size())
274 << "request write failed";
277 read_checked(fd
, reply
, "reply read failed");
279 CHECK_EQ(reply
.version(), socks_version
);
280 CHECK_EQ(reply
.reply(), reply_field::succeeded
);
281 CHECK_EQ(reply
.type(), address_type::ip4_address
);
283 LOG(INFO
) << "connected to " << reply
.addr() << ':' << reply
.port() << '\n';
285 POSIX::set_nonblocking(fd
);
288 auto const config_dir
= osutil::get_config_dir();
289 DNS::Resolver
res(config_dir
);
290 auto tlsa_rrs
= get_tlsa_rrs(res
, Domain(domain
), port
);
292 LOG(INFO
) << "starting TLS";
294 sock
.starttls_client(config_dir
, nullptr, domain
, tlsa_rrs
,
297 sock
.out() << "GET / HTTP/1.1" CRLF
"Host: " << domain
<< CRLF CRLF
301 while (std::getline(sock
.in(), line
)) {
302 std::cout
<< line
<< '\n';
303 if (line
== "</html>")