Based on a patch by Sam.
Closes Patch#141 and solves TODO#105.
<listitem>
<para>
<replaceable class="parameter">target_pattern</replaceable>
- <replaceable class="parameter">socks_proxy</replaceable>[:<replaceable class="parameter">port</replaceable>]
+ [<replaceable class="parameter">user</replaceable>:<replaceable class="parameter">pass</replaceable>@]<replaceable class="parameter">socks_proxy</replaceable>[:<replaceable class="parameter">port</replaceable>]
<replaceable class="parameter">http_parent</replaceable>[:<replaceable class="parameter">port</replaceable>]
</para>
<para>
(<replaceable class="parameter">http_parent</replaceable>
may be <quote>.</quote> to denote <quote>no HTTP forwarding</quote>), and the optional
<replaceable class="parameter">port</replaceable> parameters are TCP ports,
- i.e. integer values from 1 to 65535
+ i.e. integer values from 1 to 65535. <replaceable class="parameter">user</replaceable> and
+ <replaceable class="parameter">pass</replaceable> can be used for SOCKS5 authentication if required.
</para>
</listitem>
</varlistentry>
forward-socks4 / socks-gw.example.com:1080 .
</screen>
+ <para>
+ To connect SOCKS5 proxy which requires username/password authentication:
+ </para>
+ <screen>
+ forward-socks5 / user:pass@socks-gw.example.com:1080 .
+</screen>
+
<para>
To chain Privoxy and Tor, both running on the same system, you would use
something like:
if (NULL != socks_proxy)
{
- /* Parse the SOCKS proxy host[:port] */
+ /* Parse the SOCKS proxy [user:pass@]host[:port] */
fwd->gateway_port = 1080;
parse_forwarder_address(socks_proxy,
- &fwd->gateway_host, &fwd->gateway_port);
+ &fwd->gateway_host, &fwd->gateway_port,
+ &fwd->auth_username, &fwd->auth_password);
http_parent = vec[2];
}
{
fwd->forward_port = 8000;
parse_forwarder_address(http_parent,
- &fwd->forward_host, &fwd->forward_port);
+ &fwd->forward_host, &fwd->forward_port,
+ NULL, NULL);
}
assert (NULL != fwd);
client_pos = 0;
cbuf[client_pos++] = '\x05'; /* Version */
- cbuf[client_pos++] = '\x01'; /* One authentication method supported */
+
+ if (fwd->auth_username && fwd->auth_password)
+ {
+ cbuf[client_pos++] = '\x02'; /* Two authentication methods supported */
+ cbuf[client_pos++] = '\x02'; /* Username/password */
+ }
+ else
+ {
+ cbuf[client_pos++] = '\x01'; /* One authentication method supported */
+ }
cbuf[client_pos++] = '\x00'; /* The no authentication authentication method */
if (write_socket(sfd, cbuf, client_pos))
err = 1;
}
- if (!err && (sbuf[1] != '\x00'))
+ if (!err && (sbuf[1] == '\x02'))
+ {
+ /* check cbuf overflow */
+ size_t auth_len = strlen(fwd->auth_username) + strlen(fwd->auth_password) + 3;
+ if (auth_len > sizeof(cbuf))
+ {
+ errstr = "SOCKS5 username and/or password too long";
+ err = 1;
+ }
+
+ if (!err)
+ {
+ client_pos = 0;
+ cbuf[client_pos++] = '\x01'; /* Version */
+ cbuf[client_pos++] = (char)strlen(fwd->auth_username);
+
+ memcpy(cbuf + client_pos, fwd->auth_username, strlen(fwd->auth_username));
+ client_pos += strlen(fwd->auth_username);
+ cbuf[client_pos++] = (char)strlen(fwd->auth_password);
+ memcpy(cbuf + client_pos, fwd->auth_password, strlen(fwd->auth_password));
+ client_pos += strlen(fwd->auth_password);
+
+ if (write_socket(sfd, cbuf, client_pos))
+ {
+ errstr = "SOCKS5 negotiation auth write failed";
+ csp->error_message = strdup(errstr);
+ log_error(LOG_LEVEL_CONNECT, "%s", errstr);
+ close_socket(sfd);
+ return(JB_INVALID_SOCKET);
+ }
+
+ if (read_socket(sfd, sbuf, sizeof(sbuf)) != 2)
+ {
+ errstr = "SOCKS5 negotiation auth read failed";
+ err = 1;
+ }
+ }
+
+ if (!err && (sbuf[1] != '\x00'))
+ {
+ errstr = "SOCKS5 authentication failed";
+ err = 1;
+ }
+ }
+ else if (!err && (sbuf[1] != '\x00'))
{
errstr = "SOCKS5 negotiation protocol error";
err = 1;
if (strcmp(p, ".") != 0)
{
cur_fwd->forward_port = 8000;
- parse_forwarder_address(p, &cur_fwd->forward_host,
- &cur_fwd->forward_port);
+ parse_forwarder_address(p,
+ &cur_fwd->forward_host, &cur_fwd->forward_port,
+ NULL, NULL);
}
/* Add to list. */
if (strcmp(p, ".") != 0)
{
cur_fwd->gateway_port = 1080;
- parse_forwarder_address(p, &cur_fwd->gateway_host,
- &cur_fwd->gateway_port);
+ parse_forwarder_address(p,
+ &cur_fwd->gateway_host, &cur_fwd->gateway_port,
+ NULL, NULL);
}
/* Parse the parent HTTP proxy host[:port] */
if (strcmp(p, ".") != 0)
{
cur_fwd->forward_port = 8000;
- parse_forwarder_address(p, &cur_fwd->forward_host,
- &cur_fwd->forward_port);
+ parse_forwarder_address(p,
+ &cur_fwd->forward_host, &cur_fwd->forward_port,
+ NULL, NULL);
}
/* Add to list. */
break;
}
- /* Parse the SOCKS proxy host[:port] */
+ /* Parse the SOCKS proxy [user:pass@]host[:port] */
p = vec[1];
cur_fwd->gateway_port = 1080;
- parse_forwarder_address(p, &cur_fwd->gateway_host,
- &cur_fwd->gateway_port);
+ parse_forwarder_address(p,
+ &cur_fwd->gateway_host, &cur_fwd->gateway_port,
+ &cur_fwd->auth_username, &cur_fwd->auth_password);
/* Parse the parent HTTP proxy host[:port] */
p = vec[2];
if (strcmp(p, ".") != 0)
{
cur_fwd->forward_port = 8000;
- parse_forwarder_address(p, &cur_fwd->forward_host,
- &cur_fwd->forward_port);
+ parse_forwarder_address(p,
+ &cur_fwd->forward_host, &cur_fwd->forward_port,
+ NULL, NULL);
}
/* Add to list. */
/** SOCKS server port. */
int gateway_port;
+ /** SOCKS5 username. */
+ char *auth_username;
+
+ /** SOCKS5 password. */
+ char *auth_password;
+
/** Parent HTTP proxy hostname, or NULL for none. */
char *forward_host;
*
* Function : parse_forwarder_address
*
- * Description : Parse out the host and port from a forwarder address.
+ * Description : Parse out the username, password, host and port from
+ * a forwarder address.
*
* Parameters :
* 1 : address = The forwarder address to parse.
* 2 : hostname = Used to return the hostname. NULL on error.
* 3 : port = Used to return the port. Untouched if no port
* is specified.
+ * 4 : username = Used to return the username if any.
+ * 5 : password = Used to return the password if any.
*
* Returns : JB_ERR_OK on success
* JB_ERR_MEMORY on out of memory
* JB_ERR_PARSE on malformed address.
*
*********************************************************************/
-jb_err parse_forwarder_address(char *address, char **hostname, int *port)
+jb_err parse_forwarder_address(char *address, char **hostname, int *port,
+ char **username, char **password)
{
- char *p = address;
+ char *p;
+ *hostname = strdup_or_die(address);
- if ((*address == '[') && (NULL == strchr(address, ']')))
+ /* Parse username and password */
+ if (username && password && (NULL != (p = strchr(*hostname, '@'))))
+ {
+ *p++ = '\0';
+ *username = *hostname;
+ *hostname = p;
+
+ if (NULL != (p = strchr(*username, ':')))
+ {
+ *p++ = '\0';
+ *password = strdup_or_die(p);
+ }
+ }
+
+ /* Parse hostname and port */
+ p = *hostname;
+ if ((*p == '[') && (NULL == strchr(p, ']')))
{
/* XXX: Should do some more validity checks here. */
return JB_ERR_PARSE;
}
- *hostname = strdup_or_die(address);
-
if ((**hostname == '[') && (NULL != (p = strchr(*hostname, ']'))))
{
*p++ = '\0';
extern jb_err create_pattern_spec(struct pattern_spec *url, char *buf);
extern void free_pattern_spec(struct pattern_spec *url);
extern int match_portlist(const char *portlist, int port);
-extern jb_err parse_forwarder_address(char *address, char **hostname, int *port);
+extern jb_err parse_forwarder_address(char *address, char **hostname, int *port,
+ char **username, char **password);
#endif /* ndef URLMATCH_H_INCLUDED */