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