@@ -749,6 +749,130 @@ private:
749
749
}
750
750
};
751
751
752
+ class Socks5ProxyNetwork final : public kj::Network {
753
+ public:
754
+ Socks5ProxyNetwork (kj::StringPtr proxyHostname,
755
+ kj::Network& inner,
756
+ kj::Maybe<kj::TlsContext&> tls = kj::none,
757
+ kj::Maybe<kj::Own<kj::NetworkAddress>> resolvedProxyAddr = kj::none)
758
+ : inner(inner), proxyHostname(kj::mv(proxyHostname)),
759
+ proxyAddr (kj::mv(resolvedProxyAddr)), tls(kj::mv(tls)) {}
760
+
761
+ kj::Promise<kj::Own<kj::NetworkAddress>> parseAddress (kj::StringPtr addr, uint portHint) override {
762
+ co_return kj::heap<Socks5NetworkAddress>(co_await resolveProxyAddr (), addr, portHint, tls);
763
+ }
764
+
765
+ kj::Own<kj::NetworkAddress> getSockaddr (const void * sockaddr, uint len) override {
766
+ KJ_UNIMPLEMENTED (" Socks5NetworkAddress::getSockaddr() not implemented" );
767
+ }
768
+
769
+ kj::Own<Network> restrictPeers (
770
+ kj::ArrayPtr<const kj::StringPtr > allow,
771
+ kj::ArrayPtr<const kj::StringPtr > deny = nullptr ) override {
772
+ auto addr = proxyAddr.map ([](auto & addr) -> kj::Own<kj::NetworkAddress> { return addr->clone (); });
773
+ auto restricted = inner.restrictPeers (allow, deny);
774
+ return kj::heap<Socks5ProxyNetwork>(
775
+ proxyHostname, *restricted, tls, kj::mv (addr)).attach (kj::mv (restricted));
776
+ }
777
+
778
+ private:
779
+ kj::Network& inner;
780
+ kj::StringPtr proxyHostname;
781
+ kj::Maybe<kj::Own<kj::NetworkAddress>> proxyAddr = kj::none;
782
+ kj::Maybe<kj::TlsContext&> tls = kj::none;
783
+
784
+ kj::Promise<kj::Own<kj::NetworkAddress>> resolveProxyAddr () {
785
+ KJ_IF_SOME (p, proxyAddr) {
786
+ co_return p->clone ();
787
+ } else {
788
+ kj::Own<kj::NetworkAddress> parsed = co_await inner.parseAddress (proxyHostname);
789
+ proxyAddr = parsed->clone ();
790
+ co_return parsed;
791
+ }
792
+ }
793
+
794
+ class Socks5NetworkAddress final : public kj::NetworkAddress {
795
+ public:
796
+ Socks5NetworkAddress (kj::Own<kj::NetworkAddress> proxy, kj::StringPtr upstream, uint portHint,
797
+ kj::Maybe<kj::TlsContext&> tls = kj::none)
798
+ : proxy(kj::mv(proxy)), upstream(upstream), portHint(portHint), tls(tls) {}
799
+
800
+ kj::Promise<kj::Own<kj::AsyncIoStream>> connect () override {
801
+ KJ_REQUIRE (upstream.size () < 253 , " socks5: proxied host is too long" );
802
+ const kj::byte RESERVED = 0 ;
803
+ const kj::byte SOCKS5_VER = 5 ;
804
+ const kj::byte AUTH_METHOD_NONE = 0 ;
805
+ const kj::byte CMD_CONNECT = 1 ;
806
+
807
+ const kj::byte ADDR_IP4 = 1 ;
808
+ const kj::byte ADDR_FQDN = 3 ;
809
+ const kj::byte ADDR_IP6 = 4 ;
810
+
811
+ // 1. Send auth request
812
+ auto stream = co_await proxy->connect ();
813
+ kj::byte buf[7 + 253 ];
814
+ buf[0 ] = SOCKS5_VER;
815
+ buf[1 ] = 1 ; // one authentication method
816
+ buf[2 ] = AUTH_METHOD_NONE;
817
+ co_await stream->write (buf, 3 );
818
+
819
+ // 2. handle auth response
820
+ co_await stream->read (buf, 2 );
821
+ KJ_REQUIRE (buf[0 ] == SOCKS5_VER, " socks5: unsupported version" );
822
+ KJ_REQUIRE (buf[1 ] == AUTH_METHOD_NONE, " socks5: unsupported auth method" );
823
+
824
+ // 3. send connect request
825
+ buf[0 ] = SOCKS5_VER;
826
+ buf[1 ] = CMD_CONNECT;
827
+ buf[2 ] = RESERVED;
828
+ buf[3 ] = ADDR_FQDN;
829
+ buf[4 ] = upstream.size ();
830
+ memcpy (buf + 5 , upstream.begin (), upstream.size ());
831
+ uint16_t cmdReqSize = upstream.size () + 5 + 2 ;
832
+ buf[cmdReqSize - 2 ] = portHint >> 8 ;
833
+ buf[cmdReqSize - 1 ] = portHint & 0xff ;
834
+ co_await stream->write (buf, cmdReqSize);
835
+
836
+ // 4. handle connect respond
837
+ co_await stream->read (buf, 5 );
838
+ KJ_REQUIRE (buf[0 ] == SOCKS5_VER, " socks5: unsupported version" );
839
+ KJ_REQUIRE (buf[1 ] == 0 , " socks5: failed to connect to upstream" );
840
+ KJ_REQUIRE (buf[2 ] == RESERVED, " socks5: invalid connect response reserved byte" );
841
+ switch (buf[3 ]) {
842
+ case ADDR_IP4: co_await stream->read (buf, 4 + 2 - 1 ); break ;
843
+ case ADDR_IP6: co_await stream->read (buf, 16 + 2 - 1 ); break ;
844
+ case ADDR_FQDN: co_await stream->read (buf, buf[4 ] + 2 ); break ;
845
+ default : throw KJ_EXCEPTION (FAILED, " socks5: invalid bound address type" );
846
+ }
847
+
848
+ // 5. Return connected stream
849
+ KJ_IF_SOME (tlsContext, tls) {
850
+ co_return co_await tlsContext.wrapClient (kj::mv (stream), upstream);
851
+ } else {
852
+ co_return stream;
853
+ }
854
+ }
855
+
856
+ kj::Own<kj::NetworkAddress> clone () override {
857
+ return kj::heap<Socks5NetworkAddress>(proxy->clone (), upstream, portHint, tls);
858
+ }
859
+
860
+ // We don't use any other methods, and they seem kinda annoying to implement.
861
+ kj::Own<kj::ConnectionReceiver> listen () override {
862
+ KJ_UNIMPLEMENTED (" Socks5NetworkAddress::listen() not implemented" );
863
+ }
864
+ kj::String toString () override {
865
+ KJ_UNIMPLEMENTED (" Socks5NetworkAddress::toString() not implemented" );
866
+ }
867
+
868
+ private:
869
+ kj::Own<kj::NetworkAddress> proxy;
870
+ kj::StringPtr upstream;
871
+ uint portHint;
872
+ kj::Maybe<kj::TlsContext&> tls = kj::none;
873
+ };
874
+ };
875
+
752
876
kj::Own<Server::Service> Server::makeNetworkService (config::Network::Reader conf) {
753
877
TRACE_EVENT (" workerd" , " Server::makeNetworkService()" );
754
878
auto restrictedNetwork = network.restrictPeers (
@@ -757,7 +881,18 @@ kj::Own<Server::Service> Server::makeNetworkService(config::Network::Reader conf
757
881
758
882
kj::Maybe<kj::Own<kj::Network>> tlsNetwork;
759
883
kj::Maybe<kj::SecureNetworkWrapper&> tlsContext;
760
- if (conf.hasTlsOptions ()) {
884
+
885
+
886
+ if (conf.hasProxy ()) {
887
+ auto proxyConf = conf.getProxy ();
888
+ if (conf.hasTlsOptions ()) {
889
+ auto ownedTlsContext = makeTlsContext (conf.getTlsOptions ());
890
+ tlsNetwork = kj::heap<Socks5ProxyNetwork>(proxyConf.getAddress (), *restrictedNetwork, *ownedTlsContext)
891
+ .attach (kj::mv (ownedTlsContext));
892
+ }
893
+ restrictedNetwork = kj::heap<Socks5ProxyNetwork>(proxyConf.getAddress (), *restrictedNetwork)
894
+ .attach (kj::mv (restrictedNetwork));
895
+ } else if (conf.hasTlsOptions ()) {
761
896
auto ownedTlsContext = makeTlsContext (conf.getTlsOptions ());
762
897
tlsContext = ownedTlsContext;
763
898
tlsNetwork = ownedTlsContext->wrapNetwork (*restrictedNetwork).attach (kj::mv (ownedTlsContext));
0 commit comments