<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">So I invested some time in this today,
      and came up with this patch that implements SOCKS5 support. Can
      anyone suggest improvements?<br>
      <br>
      Sample config :<br>
      <br>
          upstream backend {<br>
              server 127.0.0.1:9050 socks=ip4.me;<br>
          }<br>
      <br>
          server {<br>
              listen       1234;<br>
              server_name  localhost;<br>
      <br>
              location / {<br>
                  proxy_pass <a class="moz-txt-link-freetext" href="http://backend">http://backend</a>;<br>
                  proxy_connect_timeout 5s;<br>
      <br>
                  proxy_set_header Host ip4.me;<br>
              }<br>
          }<br>
      <br>
      No DNS lookups are done at nginx, which is proper behavior.<br>
      <br>
      <br>
      <br>
      Index: src/http/ngx_http_upstream_round_robin.c<br>
===================================================================<br>
      --- src/http/ngx_http_upstream_round_robin.c    (revision 5017)<br>
      +++ src/http/ngx_http_upstream_round_robin.c    (working copy)<br>
      @@ -87,6 +87,12 @@<br>
                       peers->peer[n].weight = server[i].weight;<br>
                       peers->peer[n].effective_weight =
      server[i].weight;<br>
                       peers->peer[n].current_weight = 0;<br>
      +<br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +                peers->peer[n].socks = server[i].socks;<br>
      +                peers->peer[n].socks_port =
      server[i].socks_port;<br>
      +                peers->peer[n].socks_hostname =
      server[i].socks_hostname;<br>
      +#endif<br>
                       n++;<br>
                   }<br>
               }<br>
      @@ -145,6 +151,12 @@<br>
                       backup->peer[n].max_fails =
      server[i].max_fails;<br>
                       backup->peer[n].fail_timeout =
      server[i].fail_timeout;<br>
                       backup->peer[n].down = server[i].down;<br>
      +<br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +                backup->peer[n].socks = server[i].socks;<br>
      +                backup->peer[n].socks_port =
      server[i].socks_port;<br>
      +                backup->peer[n].socks_hostname =
      server[i].socks_hostname;<br>
      +#endif<br>
                       n++;<br>
                   }<br>
               }<br>
      @@ -453,6 +465,12 @@<br>
           pc->socklen = peer->socklen;<br>
           pc->name = &peer->name;<br>
       <br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +    pc->socks = peer->socks;<br>
      +    pc->socks_port = peer->socks_port;<br>
      +    pc->socks_hostname = peer->socks_hostname;<br>
      +#endif<br>
      +<br>
           /* ngx_unlock_mutex(rrp->peers->mutex); */<br>
       <br>
           if (pc->tries == 1 && rrp->peers->next) {<br>
      Index: src/http/ngx_http_upstream.c<br>
===================================================================<br>
      --- src/http/ngx_http_upstream.c    (revision 5017)<br>
      +++ src/http/ngx_http_upstream.c    (working copy)<br>
      @@ -146,7 +146,12 @@<br>
       static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);<br>
       #endif<br>
       <br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +void ngx_upstream_socks_init_handshake(ngx_http_request_t *,<br>
      +    ngx_http_upstream_t *);<br>
      +#endif<br>
       <br>
      +<br>
       ngx_http_upstream_header_t  ngx_http_upstream_headers_in[] = {<br>
       <br>
           { ngx_string("Status"),<br>
      @@ -1228,6 +1233,15 @@<br>
       <br>
           u->request_sent = 0;<br>
       <br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +<br>
      +    if (u->peer.socks) {<br>
      +        ngx_upstream_socks_init_handshake(r, u);<br>
      +        return;<br>
      +    }<br>
      +<br>
      +#endif<br>
      +<br>
           if (rc == NGX_AGAIN) {<br>
               ngx_add_timer(c->write,
      u->conf->connect_timeout);<br>
               return;<br>
      @@ -4131,7 +4145,8 @@<br>
                                               
      |NGX_HTTP_UPSTREAM_MAX_FAILS<br>
                                               
      |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT<br>
                                                |NGX_HTTP_UPSTREAM_DOWN<br>
      -                                        
      |NGX_HTTP_UPSTREAM_BACKUP);<br>
      +                                        
      |NGX_HTTP_UPSTREAM_BACKUP<br>
      +                                        
      |NGX_HTTP_UPSTREAM_SOCKS_FLAG);<br>
           if (uscf == NULL) {<br>
               return NGX_CONF_ERROR;<br>
           }<br>
      @@ -4334,6 +4349,29 @@<br>
                   continue;<br>
               }<br>
       <br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +        if (ngx_strncmp(value[i].data, "socks=", 6) == 0) {<br>
      +<br>
      +            if (!(uscf->flags &
      NGX_HTTP_UPSTREAM_SOCKS_FLAG)) {<br>
      +                goto invalid;<br>
      +            }<br>
      +<br>
      +            if (us->socks) {<br>
      +                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,<br>
      +                                   "duplicate socks proxy");<br>
      +<br>
      +                goto invalid;<br>
      +            }<br>
      +<br>
      +            us->socks = 1;<br>
      +            us->socks_port = 80;<br>
      +            us->socks_hostname.len = value[i].len - 6;<br>
      +            us->socks_hostname.data = value[i].data + 6;<br>
      +<br>
      +            continue;<br>
      +        }<br>
      +#endif<br>
      +<br>
               goto invalid;<br>
           }<br>
       <br>
      Index: src/http/ngx_http_upstream.h<br>
===================================================================<br>
      --- src/http/ngx_http_upstream.h    (revision 5017)<br>
      +++ src/http/ngx_http_upstream.h    (working copy)<br>
      @@ -93,6 +93,12 @@<br>
       <br>
           unsigned                         down:1;<br>
           unsigned                         backup:1;<br>
      +<br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +    unsigned                         socks:1;<br>
      +    in_port_t                        socks_port;<br>
      +    ngx_str_t                        socks_hostname;<br>
      +#endif<br>
       } ngx_http_upstream_server_t;<br>
       <br>
       <br>
      @@ -102,6 +108,7 @@<br>
       #define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT  0x0008<br>
       #define NGX_HTTP_UPSTREAM_DOWN          0x0010<br>
       #define NGX_HTTP_UPSTREAM_BACKUP        0x0020<br>
      +#define NGX_HTTP_UPSTREAM_SOCKS_FLAG    0x0040<br>
       <br>
       <br>
       struct ngx_http_upstream_srv_conf_s {<br>
      @@ -332,6 +339,11 @@<br>
       <br>
           unsigned                         request_sent:1;<br>
           unsigned                         header_sent:1;<br>
      +<br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +    ngx_http_upstream_handler_pt     next_read_event_handler;<br>
      +    ngx_http_upstream_handler_pt     next_write_event_handler;<br>
      +#endif<br>
       };<br>
       <br>
       <br>
      Index: src/http/ngx_http_upstream_round_robin.h<br>
===================================================================<br>
      --- src/http/ngx_http_upstream_round_robin.h    (revision 5017)<br>
      +++ src/http/ngx_http_upstream_round_robin.h    (working copy)<br>
      @@ -35,6 +35,12 @@<br>
       #if (NGX_HTTP_SSL)<br>
           ngx_ssl_session_t              *ssl_session;   /* local to a
      process */<br>
       #endif<br>
      +<br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +    unsigned                        socks:1;<br>
      +    in_port_t                       socks_port;<br>
      +    ngx_str_t                       socks_hostname;<br>
      +#endif<br>
       } ngx_http_upstream_rr_peer_t;<br>
       <br>
       <br>
      Index: src/http/modules/ngx_http_upstream_socks_module.c<br>
===================================================================<br>
      --- src/http/modules/ngx_http_upstream_socks_module.c    (revision
      0)<br>
      +++ src/http/modules/ngx_http_upstream_socks_module.c    (revision
      0)<br>
      @@ -0,0 +1,133 @@<br>
      +<br>
      +#include <ngx_config.h><br>
      +#include <ngx_core.h><br>
      +#include <ngx_http.h><br>
      +<br>
      +static void ngx_upstream_socks_write_handler(ngx_http_request_t
      *r,<br>
      +    ngx_http_upstream_t *u);<br>
      +static void ngx_upstream_socks_read_handler(ngx_http_request_t
      *r,<br>
      +    ngx_http_upstream_t *u);<br>
      +<br>
      +void<br>
      +ngx_upstream_socks_init_handshake(ngx_http_request_t *r,<br>
      +    ngx_http_upstream_t *u)<br>
      +{<br>
      +    ngx_connection_t *c;<br>
      +<br>
      +    c = u->peer.connection;<br>
      +<br>
      +    u->next_write_event_handler = u->write_event_handler;<br>
      +    u->next_read_event_handler = u->read_event_handler;<br>
      +<br>
      +    u->write_event_handler = ngx_upstream_socks_write_handler;<br>
      +    u->read_event_handler = ngx_upstream_socks_read_handler;<br>
      +}<br>
      +<br>
      +static void<br>
      +ngx_upstream_socks_write_handler(ngx_http_request_t *r,<br>
      +    ngx_http_upstream_t *u)<br>
      +{<br>
      +    ngx_connection_t *c;<br>
      +<br>
      +    c = u->peer.connection;<br>
      +<br>
      +    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log,
      0,<br>
      +                   "http socks upstream handshake handler");<br>
      +<br>
      +    if (c->write->timedout) {<br>
      +        ngx_http_finalize_request(r, NGX_HTTP_GATEWAY_TIME_OUT);<br>
      +        return;<br>
      +    }<br>
      +<br>
      +    if (!u->peer.socks_handshake1_sent) {<br>
      +        u->peer.socks_handshake1_sent = 1;<br>
      +<br>
      +        // TODO, this is ugly<br>
      +        u_char handshake[] = {0x05, 0x01, 0x00};<br>
      +        c->send(c, handshake, 3);<br>
      +<br>
      +    } else if (u->peer.socks_handshake2_recv &&
      !u->peer.socks_handshake3_sent) {<br>
      +        u_char *handshake;<br>
      +        ngx_uint_t len;<br>
      +<br>
      +        len = 7 + u->peer.socks_hostname.len;<br>
      +<br>
      +        handshake = ngx_pnalloc(r->pool, len);<br>
      +<br>
      +        handshake[0] = 5; // version<br>
      +        handshake[1] = 1; // connect<br>
      +        handshake[2] = 0;<br>
      +        handshake[3] = 3; // specify dns<br>
      +        handshake[4] = (u_char)u->peer.socks_hostname.len;<br>
      +<br>
      +        // port<br>
      +        *(u_short*)(handshake+len-2) =
      ntohs(u->peer.socks_port);<br>
      +<br>
      +        ngx_memcpy(handshake+5, u->peer.socks_hostname.data,
      u->peer.socks_hostname.len);<br>
      +<br>
      +        c->send(c, handshake, len);<br>
      +<br>
      +        ngx_pfree(r->pool, handshake);<br>
      +<br>
      +        u->peer.socks_handshake3_sent = 1;<br>
      +    }<br>
      +}<br>
      +<br>
      +static void<br>
      +ngx_upstream_socks_read_handler(ngx_http_request_t *r,<br>
      +    ngx_http_upstream_t *u)<br>
      +{<br>
      +    ngx_connection_t *c;<br>
      +<br>
      +    c = u->peer.connection;<br>
      +<br>
      +    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log,
      0,<br>
      +                   "http socks upstream handshake recv handler");<br>
      +<br>
      +    if (c->read->timedout) {<br>
      +        ngx_http_finalize_request(r, NGX_HTTP_GATEWAY_TIME_OUT);<br>
      +        return;<br>
      +    }<br>
      +<br>
      +    if (!u->peer.socks_handshake2_recv) {<br>
      +        u_char buf[2];<br>
      +<br>
      +        u->peer.socks_handshake2_recv = 1;<br>
      +        c->recv(c, buf, 2);<br>
      +<br>
      +        if (buf[0] != 5 || buf[1] != 0) {<br>
      +            ngx_http_finalize_request(r, NGX_HTTP_BAD_GATEWAY);<br>
      +            return;<br>
      +        }<br>
      +<br>
      +    } else if (u->peer.socks_handshake3_sent &&
      !u->peer.socks_handshake4_recv) {<br>
      +        u_char buf[22];<br>
      +<br>
      +        c->recv(c, buf, 5);<br>
      +<br>
      +        if (buf[0] != 5 || buf[1] != 0 || buf[2] != 0) {<br>
      +            ngx_http_finalize_request(r, NGX_HTTP_BAD_GATEWAY);<br>
      +            return;<br>
      +        }<br>
      +<br>
      +        if (buf[3] == 1) {<br>
      +            c->recv(c, buf+5, 5);<br>
      +<br>
      +        } else if (buf[3] == 4) {<br>
      +            c->recv(c, buf+5, 17);<br>
      +        <br>
      +        } else if (buf[3] == 3) {<br>
      +            u_char *hostname_and_port = ngx_pnalloc(r->pool,
      ((size_t)buf[4]) + 2);<br>
      +            c->recv(c, hostname_and_port, ((size_t)buf[4]) +
      2);<br>
      +            ngx_pfree(r->pool, hostname_and_port);<br>
      +        }<br>
      +<br>
      +        u->peer.socks_handshake4_recv = 1;<br>
      +<br>
      +        // restore previous handlers<br>
      +        u->write_event_handler =
      u->next_write_event_handler;<br>
      +        u->read_event_handler = u->next_read_event_handler;<br>
      +<br>
      +        u->write_event_handler(r, u);<br>
      +    }<br>
      +}<br>
      Index: src/event/ngx_event_connect.h<br>
===================================================================<br>
      --- src/event/ngx_event_connect.h    (revision 5017)<br>
      +++ src/event/ngx_event_connect.h    (working copy)<br>
      @@ -52,6 +52,16 @@<br>
           ngx_event_save_peer_session_pt   save_session;<br>
       #endif<br>
       <br>
      +#if (NGX_HTTP_UPSTREAM_SOCKS)<br>
      +    unsigned                         socks:1;<br>
      +    unsigned                         socks_handshake1_sent:1;<br>
      +    unsigned                         socks_handshake2_recv:1;<br>
      +    unsigned                         socks_handshake3_sent:1;<br>
      +    unsigned                         socks_handshake4_recv:1;<br>
      +    in_port_t                        socks_port;<br>
      +    ngx_str_t                        socks_hostname;<br>
      +#endif<br>
      +<br>
       #if (NGX_THREADS)<br>
           ngx_atomic_t                    *lock;<br>
       #endif<br>
      Index: auto/options<br>
===================================================================<br>
      --- auto/options    (revision 5017)<br>
      +++ auto/options    (working copy)<br>
      @@ -99,6 +99,7 @@<br>
       HTTP_UPSTREAM_IP_HASH=YES<br>
       HTTP_UPSTREAM_LEAST_CONN=YES<br>
       HTTP_UPSTREAM_KEEPALIVE=YES<br>
      +HTTP_UPSTREAM_SOCKS=NO<br>
       <br>
       # STUB<br>
       HTTP_STUB_STATUS=NO<br>
      @@ -216,6 +217,8 @@<br>
               --with-http_random_index_module)
      HTTP_RANDOM_INDEX=YES      ;;<br>
               --with-http_secure_link_module) 
      HTTP_SECURE_LINK=YES       ;;<br>
               --with-http_degradation_module) 
      HTTP_DEGRADATION=YES       ;;<br>
      +        --with-http_upstream_socks_module)<br>
      +                                        
      HTTP_UPSTREAM_SOCKS=YES    ;;<br>
       <br>
               --without-http_charset_module)  
      HTTP_CHARSET=NO            ;;<br>
               --without-http_gzip_module)     
      HTTP_GZIP=NO               ;;<br>
      @@ -364,6 +367,7 @@<br>
         --with-http_secure_link_module     enable
      ngx_http_secure_link_module<br>
         --with-http_degradation_module     enable
      ngx_http_degradation_module<br>
         --with-http_stub_status_module     enable
      ngx_http_stub_status_module<br>
      +  --with-http_upstream_socks_module  enable SOCKS5 support for
      upstreams<br>
       <br>
         --without-http_charset_module      disable
      ngx_http_charset_module<br>
         --without-http_gzip_module         disable ngx_http_gzip_module<br>
      Index: auto/modules<br>
===================================================================<br>
      --- auto/modules    (revision 5017)<br>
      +++ auto/modules    (working copy)<br>
      @@ -365,6 +365,11 @@<br>
           HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_KEEPALIVE_SRCS"<br>
       fi<br>
       <br>
      +if [ $HTTP_UPSTREAM_SOCKS = YES ]; then<br>
      +    have=NGX_HTTP_UPSTREAM_SOCKS . auto/have<br>
      +    HTTP_SRCS="$HTTP_SRCS
      src/http/modules/ngx_http_upstream_socks_module.c"<br>
      +fi<br>
      +<br>
       if [ $HTTP_STUB_STATUS = YES ]; then<br>
           have=NGX_STAT_STUB . auto/have<br>
           HTTP_MODULES="$HTTP_MODULES ngx_http_stub_status_module"<br>
      <br>
      <br>
      <br>
      <br>
      Tom<br>
      <br>
      <br>
      Op 1/25/13 1:13 PM, Tom van der Woerdt schreef:<br>
    </div>
    <blockquote cite="mid:510276F7.3020809@tvdw.eu" type="cite">Yes, I
      currently use a proxy like that, but it feels like a performance
      killer to do it like that. If implemented in nginx it could be so
      much faster.
      <br>
      <br>
      About SOCKS implementations: as long as authentication isn't
      required, the handshake is really, really easy, especially version
      4. The lack of a framing protocol makes it behave like any normal
      socket once the handshake is done.
      <br>
      <br>
      Tom
      <br>
      <br>
      <br>
      Op 1/25/13 12:57 PM, Aleksandar Lazic schreef:
      <br>
      <blockquote type="cite">Hi,
        <br>
        <br>
        There are some http2socks proxy out there.
        <br>
        <br>
        <a class="moz-txt-link-freetext" href="http://www.privoxy.org/">http://www.privoxy.org/</a>
        <br>
        <a class="moz-txt-link-freetext" href="http://www.privoxy.org/user-manual/config.html#SOCKS">http://www.privoxy.org/user-manual/config.html#SOCKS</a>
        <br>
        <br>
        <a class="moz-txt-link-freetext" href="http://www.delegate.org/delegate/">http://www.delegate.org/delegate/</a>
        <br>
        <a class="moz-txt-link-freetext" href="http://www.delegate.org/delegate/Manual.htm#SOCKS">http://www.delegate.org/delegate/Manual.htm#SOCKS</a>
        <br>
        <br>
        <a class="moz-txt-link-freetext" href="http://en.wikipedia.org/wiki/SOCKS#Translating_proxies">http://en.wikipedia.org/wiki/SOCKS#Translating_proxies</a>
        <br>
        <br>
        The setup coul looks like
        <br>
        <br>
        client -> nginx  -> http-proxylistener ->
        socks-proxyrequester -> socks-server
        <br>
        <br>
        OT: Sock5 is not so easy if you want to implement the full
        protocol, imho.
        <br>
        <br>
        I Agree with you that this would be a nice upsteam module, even
        that I don't
        <br>
        need it at the moment.
        <br>
        <br>
        Cheers
        <br>
        Aleks
        <br>
        Am 23-01-2013 17:05, schrieb Tom van der Woerdt:
        <br>
        <blockquote type="cite">Hi,
          <br>
          <br>
          A project I'm working on has a backend server that, for
          security
          <br>
          reasons, can only be accessed via a SOCKS4a/SOCKS5 proxy. A
          frontend
          <br>
          server for this project (nginx) has one simple task: to proxy
          all
          <br>
          incoming connections to the backend server.
          <br>
          <br>
          Right now, nginx cannot do this, because it has no support for
          <br>
          proxying upstream connections via a SOCKS proxy. The current
          temporary
          <br>
          workaround is to run another service on the frontend machine
          that acts
          <br>
          like a HTTP server but proxies the data to the backend -
          basically
          <br>
          everything I'd like nginx to do. I cannot use this service as
          my main
          <br>
          frontend, because there are a few other files that also need
          to be
          <br>
          served.
          <br>
          <br>
          SOCKS4a and SOCKS5 are really easy protocols and are basically
          just
          <br>
          sockets but with an alternate handshake (skip the DNS lookup,
          send the
          <br>
          hostname to the socket instead). Since they should be so easy
          to
          <br>
          implement, I'm requesting that on this mailing list.
          <br>
          <br>
          I was thinking of a config file that would look something like
          this :
          <br>
          <br>
              upstream backend {
          <br>
                  server hidden_dns.local socks4=127.0.0.1:1234;
          <br>
              }
          <br>
          <br>
              server {
          <br>
                  location / {
          <br>
                      proxy_pass <a class="moz-txt-link-freetext" href="http://backend">http://backend</a>;
          <br>
                  }
          <br>
              }
          <br>
          <br>
          As far as I'm aware, this feature wouldn't break anything,
          since a
          <br>
          SOCKS connections behaves just like any other normal socket.
          <br>
          <br>
          Thanks for considering,
          <br>
          Tom van der Woerdt
          <br>
          <br>
          <br>
          _______________________________________________
          <br>
          nginx-devel mailing list
          <br>
          <a class="moz-txt-link-abbreviated" href="mailto:nginx-devel@nginx.org">nginx-devel@nginx.org</a>
          <br>
          <a class="moz-txt-link-freetext" href="http://mailman.nginx.org/mailman/listinfo/nginx-devel">http://mailman.nginx.org/mailman/listinfo/nginx-devel</a>
          <br>
        </blockquote>
        <br>
        _______________________________________________
        <br>
        nginx-devel mailing list
        <br>
        <a class="moz-txt-link-abbreviated" href="mailto:nginx-devel@nginx.org">nginx-devel@nginx.org</a>
        <br>
        <a class="moz-txt-link-freetext" href="http://mailman.nginx.org/mailman/listinfo/nginx-devel">http://mailman.nginx.org/mailman/listinfo/nginx-devel</a>
        <br>
      </blockquote>
      <br>
      <br>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <br>
      <pre wrap="">_______________________________________________
nginx-devel mailing list
<a class="moz-txt-link-abbreviated" href="mailto:nginx-devel@nginx.org">nginx-devel@nginx.org</a>
<a class="moz-txt-link-freetext" href="http://mailman.nginx.org/mailman/listinfo/nginx-devel">http://mailman.nginx.org/mailman/listinfo/nginx-devel</a></pre>
    </blockquote>
    <br>
  </body>
</html>