소스 검색

proxy: Configure proxy options using URI scheme

Nick Peng 2 년 전
부모
커밋
5392857539
8개의 변경된 파일213개의 추가작업 그리고 130개의 파일을 삭제
  1. 2 3
      ReadMe.md
  2. 2 3
      ReadMe_en.md
  3. 10 4
      etc/smartdns/smartdns.conf
  4. 48 71
      src/dns_conf.c
  5. 0 4
      src/dns_conf.h
  6. 76 38
      src/proxy.c
  7. 71 7
      src/util.c
  8. 4 0
      src/util.h

+ 2 - 3
ReadMe.md

@@ -590,12 +590,11 @@ entware|ipkg update<br>ipkg install smartdns|软件源路径:https://bin.entwa
 | audit-num | 审计归档个数 | 2 | 大于等于 0 的数字 | audit-num 2 |
 | audit-num | 审计归档个数 | 2 | 大于等于 0 的数字 | audit-num 2 |
 | audit-file-mode | 审计归档文件权限 | 0640 | 文件权限 | log-file-mode 644 |
 | audit-file-mode | 审计归档文件权限 | 0640 | 文件权限 | log-file-mode 644 |
 | conf-file | 附加配置文件 | 无 | 合法路径字符串 | conf-file /etc/smartdns/smartdns.more.conf |
 | conf-file | 附加配置文件 | 无 | 合法路径字符串 | conf-file /etc/smartdns/smartdns.more.conf |
-| server | 上游 UDP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果。<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server 8.8.8.8:53 -blacklist-ip -group g1 -proxy proxy|
+| server | 上游 UDP DNS | 无 | 可重复。<br>[ip][:port]\|URL:服务器 IP:端口(可选)或 URL <br>[-blacklist-ip]:配置 IP 过滤结果。<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server 8.8.8.8:53 -blacklist-ip -group g1 -proxy proxy<br> server tls://8.8.8.8|
 | server-tcp | 上游 TCP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围。<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-tcp 8.8.8.8:53 |
 | server-tcp | 上游 TCP DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:指定仅接受参数中配置的 IP 范围。<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-tcp 8.8.8.8:53 |
 | server-tls | 上游 TLS DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]:TLS 合法性校验 SPKI 值,base64 编码的 sha256 SPKI pin 值<br>[-host-name]:TLS SNI 名称, 名称设置为-,表示停用SNI名称<br>[-tls-host-verify]:TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-tls 8.8.8.8:853 |
 | server-tls | 上游 TLS DNS | 无 | 可重复。<br>[ip][:port]:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]:TLS 合法性校验 SPKI 值,base64 编码的 sha256 SPKI pin 值<br>[-host-name]:TLS SNI 名称, 名称设置为-,表示停用SNI名称<br>[-tls-host-verify]:TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-tls 8.8.8.8:853 |
 | server-https | 上游 HTTPS DNS | 无 | 可重复。<br>https://[host][:port]/path:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]:TLS 合法性校验 SPKI 值,base64 编码的 sha256 SPKI pin 值<br>[-host-name]:TLS SNI 名称<br>[-http-host]:http 协议头主机名<br>[-tls-host-verify]:TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围。<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-https https://cloudflare-dns.com/dns-query |
 | server-https | 上游 HTTPS DNS | 无 | 可重复。<br>https://[host][:port]/path:服务器 IP:端口(可选)<br>[-spki-pin [sha256-pin]]:TLS 合法性校验 SPKI 值,base64 编码的 sha256 SPKI pin 值<br>[-host-name]:TLS SNI 名称<br>[-http-host]:http 协议头主机名<br>[-tls-host-verify]:TLS 证书主机名校验<br> [-no-check-certificate]:跳过证书校验<br>[-blacklist-ip]:配置 IP 过滤结果<br>[-whitelist-ip]:仅接受参数中配置的 IP 范围。<br>[-group [group] ...]:DNS 服务器所属组,比如 office 和 foreign,和 nameserver 配套使用<br>[-exclude-default-group]:将 DNS 服务器从默认组中排除。<br>[-set-mark]:设置数据包标记so-mark。<br>[-proxy name]:设置代理服务器。 | server-https https://cloudflare-dns.com/dns-query |
-| proxy-socks5 | socks5代理服务器 | 无 | 可重复。proxy-socks5 ip:port <br>[-name]: 代理服务器名称。<br>[-u\|-user]:用户名。<br>[-p\|-password]:密码。|proxy-socks5 1.2.3.4:1080 -name proxy|
-| proxy-http | http代理服务器 | 无 | 可重复。proxy-http ip:port <br>[-name]: 代理服务器名称。<br>[-u\|-user]:用户名。<br>[-p\|-password]:密码。|proxy-http 1.2.3.4:1080 -name proxy|
+| proxy-server | 代理服务器 | 无 | 可重复。<br>proxy-server URL <br>[URL]: [socks5\|http]://[username:password@]host:port<br>[-name]: 代理服务器名称。 |proxy-server socks5://user:[email protected]:1080 -name proxy|
 | speed-check-mode | 测速模式选择 | 无 | [ping\|tcp:[80]\|none] | speed-check-mode ping,tcp:80,tcp:443 |
 | speed-check-mode | 测速模式选择 | 无 | [ping\|tcp:[80]\|none] | speed-check-mode ping,tcp:80,tcp:443 |
 | response-mode | 首次查询响应模式 | first-ping |模式:[fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: 最快ping响应地址模式,DNS上游最快查询时延+ping时延最短,查询等待与链接体验最佳;<br>[fastest-ip]: 最快IP地址模式,查询到的所有IP地址中ping最短的IP。需等待IP测速; <br>[fastest-response]: 最快响应的DNS结果,DNS查询等待时间最短,返回的IP地址可能不是最快。| response-mode first-ping |
 | response-mode | 首次查询响应模式 | first-ping |模式:[fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: 最快ping响应地址模式,DNS上游最快查询时延+ping时延最短,查询等待与链接体验最佳;<br>[fastest-ip]: 最快IP地址模式,查询到的所有IP地址中ping最短的IP。需等待IP测速; <br>[fastest-response]: 最快响应的DNS结果,DNS查询等待时间最短,返回的IP地址可能不是最快。| response-mode first-ping |
 | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br>- 表示忽略 <br># 表示返回 SOA <br>4 表示 IPv4 <br>6 表示 IPv6 | address /www.example.com/1.2.3.4 |
 | address | 指定域名 IP 地址 | 无 | address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6] <br>- 表示忽略 <br># 表示返回 SOA <br>4 表示 IPv4 <br>6 表示 IPv6 | address /www.example.com/1.2.3.4 |

+ 2 - 3
ReadMe_en.md

@@ -552,12 +552,11 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use
 |audit-num|archived audit log number|2|Integer, 0 means turn off the log|audit-num 2
 |audit-num|archived audit log number|2|Integer, 0 means turn off the log|audit-num 2
 |audit-file-mode|archived audit log file mode|0640|Integer|audit-file-mode 644
 |audit-file-mode|archived audit log file mode|0640|Integer|audit-file-mode 644
 |conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf
 |conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf
-|server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group. <br>`[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server 8.8.8.8:53 -blacklist-ip
+|server|Upstream UDP DNS server|None|Repeatable <br>`[ip][:port]\|URL`: Server IP, port optional OR URL. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group. <br>`[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server 8.8.8.8:53 -blacklist-ip<br>server tls://8.8.8.8
 |server-tcp|Upstream TCP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br>`[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-tcp 8.8.8.8:53
 |server-tcp|Upstream TCP DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br>`[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-tcp 8.8.8.8:53
 |server-tls|Upstream TLS DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name. `-` to disable SNI name.<br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br> `[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-tls 8.8.8.8:853
 |server-tls|Upstream TLS DNS server|None|Repeatable <br>`[ip][:port]`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name. `-` to disable SNI name.<br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br> `[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-tls 8.8.8.8:853
 |server-https|Upstream HTTPS DNS server|None|Repeatable <br>`https://[host][:port]/path`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name<br>`[-http-host]`:http header host. <br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br> `[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-https https://cloudflare-dns.com/dns-query
 |server-https|Upstream HTTPS DNS server|None|Repeatable <br>`https://[host][:port]/path`: Server IP, port optional. <br>`[-spki-pin [sha256-pin]]`: TLS verify SPKI value, a base64 encoded SHA256 hash<br>`[-host-name]`:TLS Server name<br>`[-http-host]`:http header host. <br>`[-tls-host-verify]`: TLS cert hostname to verify. <br>`-no-check-certificate:`: No check certificate. <br>`[-blacklist-ip]`: The "-blacklist-ip" parameter is to filtering IPs which is configured by "blacklist-ip". <br>`[-whitelist-ip]`: whitelist-ip parameter specifies that only the IP range configured in whitelist-ip is accepted. <br>`[-group [group] ...]`: The group to which the DNS server belongs, such as office, foreign, use with nameserver. <br>`[-exclude-default-group]`: Exclude DNS servers from the default group <br> `[-set-mark mark]`:set mark on packets <br> `[-proxy name]`: set proxy server| server-https https://cloudflare-dns.com/dns-query
-|proxy-socks5| socks5 proxy server | None | Repeatable. <br>`proxy-socks5 ip:po`rt <br>[-name]:  proxy server name. <br>[-u\|-user]:user name. <br>[-p\|-password]:password. |proxy-socks5 1.2.3.4:1080 -name proxy|
-|proxy-http|http proxy server | None | Repeatable. <br>`proxy-http ip:port` <br>[-name]:  proxy server name. <br>[-u\|-user]:user name. <br>[-p\|-password]:password. |proxy-http 1.2.3.4:1080 -name proxy|
+|proxy-server| proxy server | None | Repeatable. <br>`proxy-server URL` <br>[URL]: `[socks5\|http]://[username:password@]host:port`<br>[-name]:  proxy server name. |proxy-server socks5://user:[email protected]:1080 -name proxy|
 |speed-check-mode|Speed ​​mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80,tcp:443
 |speed-check-mode|Speed ​​mode|None|[ping\|tcp:[80]\|none]|speed-check-mode ping,tcp:80,tcp:443
 |response-mode|First query response mode|first-ping|Mode: [fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br>[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br>[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping |
 |response-mode|First query response mode|first-ping|Mode: [fisrt-ping\|fastest-ip\|fastest-response]<br> [first-ping]: The fastest dns + ping response mode, DNS query delay + ping delay is the shortest;<br>[fastest-ip]: The fastest IP address mode, return the fastest ip address, may take some time to test speed. <br>[fastest-response]: The fastest response DNS result mode, the DNS query waiting time is the shortest. | response-mode first-ping |
 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4
 |address|Domain IP address|None|address /domain/[ip\|-\|-4\|-6\|#\|#4\|#6], `-` for ignore, `#` for return SOA, `4` for IPV4, `6` for IPV6| address /www.example.com/1.2.3.4

+ 10 - 4
etc/smartdns/smartdns.conf

@@ -165,7 +165,7 @@ log-level info
 # ca-path /etc/ss/certs
 # ca-path /etc/ss/certs
 
 
 # remote udp dns server list
 # remote udp dns server list
-# server [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-check-edns] [-group [group] ...] [-exclude-default-group]
+# server [IP]:[PORT]|URL [-blacklist-ip] [-whitelist-ip] [-check-edns] [-group [group] ...] [-exclude-default-group]
 # default port is 53
 # default port is 53
 #   -blacklist-ip: filter result with blacklist ip
 #   -blacklist-ip: filter result with blacklist ip
 #   -whitelist-ip: filter result whth whitelist ip,  result in whitelist-ip will be accepted.
 #   -whitelist-ip: filter result whth whitelist ip,  result in whitelist-ip will be accepted.
@@ -174,6 +174,8 @@ log-level info
 #   -exclude-default-group: exclude this server from default group.
 #   -exclude-default-group: exclude this server from default group.
 #   -proxy [proxy-name]: use proxy to connect to server.
 #   -proxy [proxy-name]: use proxy to connect to server.
 # server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2
 # server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2
+# server tls://dns.google:853 
+# server https://dns.google/dns-query
 
 
 # remote tcp dns server list
 # remote tcp dns server list
 # server-tcp [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-group [group] ...] [-exclude-default-group]
 # server-tcp [IP]:[PORT] [-blacklist-ip] [-whitelist-ip] [-group [group] ...] [-exclude-default-group]
@@ -205,9 +207,13 @@ log-level info
 # server-https https://cloudflare-dns.com/dns-query
 # server-https https://cloudflare-dns.com/dns-query
 
 
 # socks5 and http proxy list
 # socks5 and http proxy list
-# proxy-http ip[:port] -name [proxy name] [-u|-user username] [-p|-password password]
-# proxy-socks5 ip[:port] -name [proxy name] [-u|-user username] [-p|-password password]
-# proxy-socks5 127.0.0.1:3328 -name proxy-socks5 -u username -p password
+# proxy-server URL -name [proxy name]
+#   URL: socks5://[username:password@]host:port
+#        http://[username:password@]host:port
+#   -name: proxy name, use with server -proxy [proxy-name]
+# example:
+#   proxy-server socks5://user:[email protected]:1080 -name proxy
+#   proxy-server http://user:[email protected]:3128 -name proxy
 
 
 # specific nameserver to domain
 # specific nameserver to domain
 # nameserver /domain/[group|-]
 # nameserver /domain/[group|-]

+ 48 - 71
src/dns_conf.c

@@ -101,10 +101,6 @@ struct dns_domain_check_orders dns_conf_check_orders = {
 };
 };
 static int dns_has_cap_ping = 0;
 static int dns_has_cap_ping = 0;
 
 
-/* proxy servers */
-struct dns_proxy_servers dns_conf_proxy_servers[PROXY_MAX_SERVERS];
-int dns_conf_proxy_server_num;
-
 /* logging */
 /* logging */
 int dns_conf_log_level = TLOG_ERROR;
 int dns_conf_log_level = TLOG_ERROR;
 char dns_conf_log_file[DNS_MAX_PATH];
 char dns_conf_log_file[DNS_MAX_PATH];
@@ -451,6 +447,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 	struct dns_servers *server = NULL;
 	struct dns_servers *server = NULL;
 	int port = -1;
 	int port = -1;
 	char *ip = NULL;
 	char *ip = NULL;
+	char scheme[DNS_MAX_CNAME_LEN] = {0};
 	int opt = 0;
 	int opt = 0;
 	unsigned int result_flag = 0;
 	unsigned int result_flag = 0;
 	unsigned int server_flag = 0;
 	unsigned int server_flag = 0;
@@ -497,20 +494,35 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de
 	server->proxyname[0] = '\0';
 	server->proxyname[0] = '\0';
 	server->set_mark = -1;
 	server->set_mark = -1;
 
 
-	if (type == DNS_SERVER_HTTPS) {
-		if (parse_uri(ip, NULL, server->server, &port, server->path) != 0) {
+	if (parse_uri(ip, scheme, server->server, &port, server->path) != 0) {
+		return -1;
+	}
+
+	if (scheme[0] != '\0') {
+		if (strcasecmp(scheme, "https") == 0) {
+			type = DNS_SERVER_HTTPS;
+			default_port = DEFAULT_DNS_HTTPS_PORT;
+		} else if (strcasecmp(scheme, "tls") == 0) {
+			type = DNS_SERVER_TLS;
+			default_port = DEFAULT_DNS_TLS_PORT;
+		} else if (strcasecmp(scheme, "tcp") == 0) {
+			type = DNS_SERVER_TCP;
+			default_port = DEFAULT_DNS_PORT;
+		} else if (strcasecmp(scheme, "udp") == 0) {
+			type = DNS_SERVER_UDP;
+			default_port = DEFAULT_DNS_PORT;
+		} else {
+			tlog(TLOG_ERROR, "invalid scheme: %s", scheme);
 			return -1;
 			return -1;
 		}
 		}
+	}
+
+	if (type == DNS_SERVER_HTTPS) {
 		safe_strncpy(server->hostname, server->server, sizeof(server->hostname));
 		safe_strncpy(server->hostname, server->server, sizeof(server->hostname));
 		safe_strncpy(server->httphost, server->server, sizeof(server->httphost));
 		safe_strncpy(server->httphost, server->server, sizeof(server->httphost));
 		if (server->path[0] == 0) {
 		if (server->path[0] == 0) {
 			safe_strncpy(server->path, "/", sizeof(server->path));
 			safe_strncpy(server->path, "/", sizeof(server->path));
 		}
 		}
-	} else {
-		/* parse ip, port from ip */
-		if (parse_ip(ip, server->server, &port) != 0) {
-			return -1;
-		}
 	}
 	}
 
 
 	/* if port is not defined, set port to default 53 */
 	/* if port is not defined, set port to default 53 */
@@ -1599,34 +1611,27 @@ errout:
 	return 0;
 	return 0;
 }
 }
 
 
-static int _config_proxy_server(int argc, char *argv[], struct dns_proxy_servers **pserver, proxy_type_t type)
+static int _config_proxy_server(void *data, int argc, char *argv[])
 {
 {
-	int index = dns_conf_proxy_server_num;
-	struct dns_proxy_servers *server = NULL;
 	char *servers_name = NULL;
 	char *servers_name = NULL;
-	int port = -1;
+	struct dns_proxy_servers *server = NULL;
+	proxy_type_t type = PROXY_TYPE_END;
+
 	char *ip = NULL;
 	char *ip = NULL;
 	int opt = 0;
 	int opt = 0;
-	unsigned int server_flag = 0;
 	int use_domain = 0;
 	int use_domain = 0;
+	char scheme[DNS_MAX_CNAME_LEN] = {0};
+	int port = PORT_NOT_DEFINED;
 
 
 	/* clang-format off */
 	/* clang-format off */
 	static struct option long_options[] = {
 	static struct option long_options[] = {
 		{"name", required_argument, NULL, 'n'}, 
 		{"name", required_argument, NULL, 'n'}, 
 		{"use-domain", no_argument, NULL, 'd'},
 		{"use-domain", no_argument, NULL, 'd'},
-		{"user", required_argument, NULL, 'u'},
-		{"password", required_argument, NULL, 'p'},
 		{NULL, no_argument, NULL, 0}
 		{NULL, no_argument, NULL, 0}
 	};
 	};
 	/* clang-format on */
 	/* clang-format on */
-	if (argc <= 1) {
-		tlog(TLOG_ERROR, "invalid parameter.");
-		return -1;
-	}
 
 
-	ip = argv[1];
-	if (index >= PROXY_MAX_SERVERS) {
-		tlog(TLOG_WARN, "exceeds max server number, %s", ip);
+	if (argc <= 1) {
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -1637,10 +1642,15 @@ static int _config_proxy_server(int argc, char *argv[], struct dns_proxy_servers
 	}
 	}
 	memset(server, 0, sizeof(*server));
 	memset(server, 0, sizeof(*server));
 
 
+	ip = argv[1];
+	if (parse_uri_ext(ip, scheme, server->username, server->password, server->server, &port, NULL) != 0) {
+		return -1;
+	}
+
 	/* process extra options */
 	/* process extra options */
 	optind = 1;
 	optind = 1;
 	while (1) {
 	while (1) {
-		opt = getopt_long_only(argc, argv, "n:du:p:", long_options, NULL);
+		opt = getopt_long_only(argc, argv, "n:d", long_options, NULL);
 		if (opt == -1) {
 		if (opt == -1) {
 			break;
 			break;
 		}
 		}
@@ -1654,32 +1664,26 @@ static int _config_proxy_server(int argc, char *argv[], struct dns_proxy_servers
 			use_domain = 1;
 			use_domain = 1;
 			break;
 			break;
 		}
 		}
-		case 'u': {
-			safe_strncpy(server->username, optarg, sizeof(server->username));
-			break;
-		}
-		case 'p': {
-			safe_strncpy(server->password, optarg, sizeof(server->password));
-			break;
-		}
 		default:
 		default:
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
-	ip = argv[optind];
-	if (ip) {
-		/* parse ip, port from ip */
-		if (parse_ip(ip, server->server, &port) != 0) {
-			return -1;
+	if (strcasecmp(scheme, "socks5") == 0) {
+		if (port == PORT_NOT_DEFINED) {
+			port = 1080;
 		}
 		}
 
 
-		/* if port is not defined, set port to default 53 */
+		type = PROXY_SOCKS5;
+	} else if (strcasecmp(scheme, "http") == 0) {
 		if (port == PORT_NOT_DEFINED) {
 		if (port == PORT_NOT_DEFINED) {
-			port = 443;
+			port = 3128;
 		}
 		}
+
+		type = PROXY_HTTP;
 	} else {
 	} else {
-		goto errout;
+		tlog(TLOG_ERROR, "invalid scheme %s", scheme);
+		return -1;
 	}
 	}
 
 
 	if (servers_name == NULL) {
 	if (servers_name == NULL) {
@@ -1695,14 +1699,8 @@ static int _config_proxy_server(int argc, char *argv[], struct dns_proxy_servers
 	/* add new server */
 	/* add new server */
 	server->type = type;
 	server->type = type;
 	server->port = port;
 	server->port = port;
-	server->server_flag = server_flag;
 	server->use_domain = use_domain;
 	server->use_domain = use_domain;
-	dns_conf_proxy_server_num++;
-	tlog(TLOG_DEBUG, "add proxy server %s, flag: %X", ip, server_flag);
-
-	if (pserver) {
-		*pserver = server;
-	}
+	tlog(TLOG_DEBUG, "add proxy server %s", ip);
 
 
 	return 0;
 	return 0;
 
 
@@ -1714,26 +1712,6 @@ errout:
 	return -1;
 	return -1;
 }
 }
 
 
-static int _config_proxy_socks5(void *data, int argc, char *argv[])
-{
-	struct dns_proxy_servers *server = NULL;
-	int ret = _config_proxy_server(argc, argv, &server, PROXY_SOCKS5);
-	if (ret == 0) {
-		server->socks5 = 1;
-	}
-	return ret;
-}
-
-static int _config_proxy_http(void *data, int argc, char *argv[])
-{
-	struct dns_proxy_servers *server = NULL;
-	int ret = _config_proxy_server(argc, argv, &server, PROXY_HTTP);
-	if (ret == 0) {
-		server->https = 1;
-	}
-	return ret;
-}
-
 static radix_node_t *_create_addr_node(char *addr)
 static radix_node_t *_create_addr_node(char *addr)
 {
 {
 	radix_node_t *node = NULL;
 	radix_node_t *node = NULL;
@@ -2645,8 +2623,7 @@ static struct config_item _config_item[] = {
 	CONF_CUSTOM("server-https", _config_server_https, NULL),
 	CONF_CUSTOM("server-https", _config_server_https, NULL),
 	CONF_CUSTOM("nameserver", _config_nameserver, NULL),
 	CONF_CUSTOM("nameserver", _config_nameserver, NULL),
 	CONF_CUSTOM("address", _config_address, NULL),
 	CONF_CUSTOM("address", _config_address, NULL),
-	CONF_CUSTOM("proxy-socks5", _config_proxy_socks5, NULL),
-	CONF_CUSTOM("proxy-http", _config_proxy_http, NULL),
+	CONF_CUSTOM("proxy-server", _config_proxy_server, NULL),
 	CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
 	CONF_YESNO("ipset-timeout", &dns_conf_ipset_timeout_enable),
 	CONF_CUSTOM("ipset", _config_ipset, NULL),
 	CONF_CUSTOM("ipset", _config_ipset, NULL),
 	CONF_YESNO("nftset-timeout", &dns_conf_nftset_timeout_enable),
 	CONF_YESNO("nftset-timeout", &dns_conf_nftset_timeout_enable),

+ 0 - 4
src/dns_conf.h

@@ -263,12 +263,8 @@ struct dns_proxy_servers {
 	char server[DNS_MAX_IPLEN];
 	char server[DNS_MAX_IPLEN];
 	proxy_type_t type;
 	proxy_type_t type;
 	unsigned short port;
 	unsigned short port;
-	unsigned int server_flag;
 	char username[DNS_PROXY_MAX_LEN];
 	char username[DNS_PROXY_MAX_LEN];
 	char password[DNS_PROXY_MAX_LEN];
 	char password[DNS_PROXY_MAX_LEN];
-
-	int socks5;
-	int https;
 	int use_domain;
 	int use_domain;
 };
 };
 
 

+ 76 - 38
src/proxy.c

@@ -45,7 +45,7 @@
 #define PROXY_SOCKS5_CONNECT_UDP 0x03
 #define PROXY_SOCKS5_CONNECT_UDP 0x03
 
 
 #define PROXY_MAX_EVENTS 64
 #define PROXY_MAX_EVENTS 64
-#define PROXY_BUFFER_SIZE (1024 * 8)
+#define PROXY_BUFFER_SIZE (1024 * 4)
 #define PROXY_MAX_HOSTNAME_LEN 256
 #define PROXY_MAX_HOSTNAME_LEN 256
 
 
 typedef enum PROXY_CONN_STATE {
 typedef enum PROXY_CONN_STATE {
@@ -57,6 +57,11 @@ typedef enum PROXY_CONN_STATE {
 	PROXY_CONN_CONNECTED = 5,
 	PROXY_CONN_CONNECTED = 5,
 } PROXY_CONN_STATE;
 } PROXY_CONN_STATE;
 
 
+struct proxy_conn_buffer {
+	int len;
+	char buffer[PROXY_BUFFER_SIZE];
+};
+
 struct proxy_conn {
 struct proxy_conn {
 	proxy_type_t type;
 	proxy_type_t type;
 	PROXY_CONN_STATE state;
 	PROXY_CONN_STATE state;
@@ -68,6 +73,7 @@ struct proxy_conn {
 	int is_udp;
 	int is_udp;
 	struct sockaddr_storage udp_dest_addr;
 	struct sockaddr_storage udp_dest_addr;
 	socklen_t udp_dest_addrlen;
 	socklen_t udp_dest_addrlen;
+	struct proxy_conn_buffer buffer;
 	struct proxy_server_info *server_info;
 	struct proxy_server_info *server_info;
 };
 };
 
 
@@ -501,7 +507,8 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 		return PROXY_HANDSHAKE_WANT_READ;
 		return PROXY_HANDSHAKE_WANT_READ;
 	} break;
 	} break;
 	case PROXY_CONN_INIT_ACK:
 	case PROXY_CONN_INIT_ACK:
-		len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
+		len = recv(proxy_conn->fd, proxy_conn->buffer.buffer + proxy_conn->buffer.len,
+				   sizeof(proxy_conn->buffer.buffer), 0);
 		if (len <= 0) {
 		if (len <= 0) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				return PROXY_HANDSHAKE_WANT_READ;
 				return PROXY_HANDSHAKE_WANT_READ;
@@ -511,35 +518,43 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (len != 2) {
+		proxy_conn->buffer.len += len;
+		if (proxy_conn->buffer.len < 2) {
+			return PROXY_HANDSHAKE_WANT_READ;
+		}
+
+		if (proxy_conn->buffer.len > 2) {
 			tlog(TLOG_ERROR, "recv socks5 init ack failed, len = %d", len);
 			tlog(TLOG_ERROR, "recv socks5 init ack failed, len = %d", len);
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (buff[0] != PROXY_SOCKS5_VERSION) {
+		proxy_conn->buffer.len = 0;
+
+		if (proxy_conn->buffer.buffer[0] != PROXY_SOCKS5_VERSION) {
 			tlog(TLOG_ERROR, "Server not support socks5");
 			tlog(TLOG_ERROR, "Server not support socks5");
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if ((unsigned char)buff[1] == PROXY_SOCKS5_AUTH_NONE) {
+		if ((unsigned char)proxy_conn->buffer.buffer[1] == PROXY_SOCKS5_AUTH_NONE) {
 			tlog(TLOG_ERROR, "Server not support auth methods");
 			tlog(TLOG_ERROR, "Server not support auth methods");
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		tlog(TLOG_INFO, "Server select auth method is %d", buff[1]);
-		if (buff[1] == PROXY_SOCKS5_AUTH_USER_PASS) {
+		tlog(TLOG_INFO, "Server select auth method is %d", proxy_conn->buffer.buffer[1]);
+		if (proxy_conn->buffer.buffer[1] == PROXY_SOCKS5_AUTH_USER_PASS) {
 			return _proxy_handshake_socks5_send_auth(proxy_conn);
 			return _proxy_handshake_socks5_send_auth(proxy_conn);
 		}
 		}
 
 
-		if (buff[1] == PROXY_SOCKS5_NO_AUTH) {
+		if (proxy_conn->buffer.buffer[1] == PROXY_SOCKS5_NO_AUTH) {
 			return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
 			return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
 		}
 		}
 
 
-		tlog(TLOG_ERROR, "Server select invalid auth method %d", buff[1]);
+		tlog(TLOG_ERROR, "Server select invalid auth method %d", proxy_conn->buffer.buffer[1]);
 		return PROXY_HANDSHAKE_ERR;
 		return PROXY_HANDSHAKE_ERR;
 		break;
 		break;
 	case PROXY_CONN_AUTH_ACK:
 	case PROXY_CONN_AUTH_ACK:
-		len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
+		len = recv(proxy_conn->fd, proxy_conn->buffer.buffer + proxy_conn->buffer.len,
+				   sizeof(proxy_conn->buffer.buffer), 0);
 		if (len <= 0) {
 		if (len <= 0) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				return PROXY_HANDSHAKE_WANT_READ;
 				return PROXY_HANDSHAKE_WANT_READ;
@@ -549,18 +564,25 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (len != 2) {
+		proxy_conn->buffer.len += len;
+		if (proxy_conn->buffer.len < 2) {
+			return PROXY_HANDSHAKE_WANT_READ;
+		}
+
+		if (proxy_conn->buffer.len != 2) {
 			tlog(TLOG_ERROR, "recv socks5 auth ack failed, len = %d", len);
 			tlog(TLOG_ERROR, "recv socks5 auth ack failed, len = %d", len);
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (buff[0] != 0x1) {
+		proxy_conn->buffer.len = 0;
+
+		if (proxy_conn->buffer.buffer[0] != 0x1) {
 			tlog(TLOG_ERROR, "Server not support socks5");
 			tlog(TLOG_ERROR, "Server not support socks5");
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (buff[1] != 0x0) {
-			tlog(TLOG_ERROR, "Server auth failed, code = %d", buff[1]);
+		if (proxy_conn->buffer.buffer[1] != 0x0) {
+			tlog(TLOG_ERROR, "Server auth failed, code = %d", proxy_conn->buffer.buffer[1]);
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
@@ -571,9 +593,11 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 		unsigned char addr[16];
 		unsigned char addr[16];
 		unsigned short port = 0;
 		unsigned short port = 0;
 		int use_dest_ip = 0;
 		int use_dest_ip = 0;
+		char *recv_buff = NULL;
 
 
 		int addr_len = 0;
 		int addr_len = 0;
-		len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
+		len = recv(proxy_conn->fd, proxy_conn->buffer.buffer + proxy_conn->buffer.len,
+				   sizeof(proxy_conn->buffer.buffer), 0);
 		if (len <= 0) {
 		if (len <= 0) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 			if (errno == EAGAIN || errno == EWOULDBLOCK) {
 				return PROXY_HANDSHAKE_WANT_READ;
 				return PROXY_HANDSHAKE_WANT_READ;
@@ -583,40 +607,42 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (len < 10) {
-			tlog(TLOG_ERROR, "Server reply connect addr failed, len = %d", len);
-			return PROXY_HANDSHAKE_ERR;
+		proxy_conn->buffer.len += len;
+		if (proxy_conn->buffer.len < 10) {
+			return PROXY_HANDSHAKE_WANT_READ;
 		}
 		}
+		recv_buff = proxy_conn->buffer.buffer;
 
 
-		if (buff[0] != PROXY_SOCKS5_VERSION) {
+		if (recv_buff[0] != PROXY_SOCKS5_VERSION) {
 			tlog(TLOG_ERROR, "Server not support socks5");
 			tlog(TLOG_ERROR, "Server not support socks5");
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		if (buff[1] != 0) {
-			if ((unsigned char)buff[1] <= (sizeof(proxy_socks5_status_code) / sizeof(proxy_socks5_status_code[0]))) {
-				tlog(TLOG_ERROR, "Server replay failed, error code %s", proxy_socks5_status_code[(int)buff[1]]);
+		if (recv_buff[1] != 0) {
+			if ((unsigned char)recv_buff[1] <=
+				(sizeof(proxy_socks5_status_code) / sizeof(proxy_socks5_status_code[0]))) {
+				tlog(TLOG_ERROR, "Server replay failed, error code %s", proxy_socks5_status_code[(int)recv_buff[1]]);
 			} else {
 			} else {
-				tlog(TLOG_ERROR, "Server replay failed, error code %x", buff[1]);
+				tlog(TLOG_ERROR, "Server replay failed, error code %x", recv_buff[1]);
 			}
 			}
 			return PROXY_HANDSHAKE_ERR;
 			return PROXY_HANDSHAKE_ERR;
 		}
 		}
 
 
-		switch (buff[3]) {
+		switch (recv_buff[3]) {
 		case PROXY_SOCKS5_TYPE_IPV4: {
 		case PROXY_SOCKS5_TYPE_IPV4: {
 			struct sockaddr_in *addr_in = NULL;
 			struct sockaddr_in *addr_in = NULL;
 			addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
 			addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
 			proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in);
 			proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in);
-			if (len != 10) {
-				return PROXY_HANDSHAKE_ERR;
+			if (proxy_conn->buffer.len < 10) {
+				return PROXY_HANDSHAKE_WANT_READ;
 			}
 			}
 
 
 			addr_len = 4;
 			addr_len = 4;
-			memcpy(addr, buff + 4, addr_len);
-			port = ntohs(*((short *)(buff + 4 + addr_len)));
+			memcpy(addr, recv_buff + 4, addr_len);
+			port = ntohs(*((short *)(recv_buff + 4 + addr_len)));
 			addr_in->sin_family = AF_INET;
 			addr_in->sin_family = AF_INET;
 			addr_in->sin_addr.s_addr = *((int *)addr);
 			addr_in->sin_addr.s_addr = *((int *)addr);
-			addr_in->sin_port = *((short *)(buff + 4 + addr_len));
+			addr_in->sin_port = *((short *)(recv_buff + 4 + addr_len));
 			if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0) {
 			if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0) {
 				use_dest_ip = 1;
 				use_dest_ip = 1;
 			}
 			}
@@ -627,16 +653,16 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 			struct sockaddr_in6 *addr_in6 = NULL;
 			struct sockaddr_in6 *addr_in6 = NULL;
 			addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
 			addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
 			proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in6);
 			proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in6);
-			if (len != 22) {
-				return PROXY_HANDSHAKE_ERR;
+			if (proxy_conn->buffer.len < 22) {
+				return PROXY_HANDSHAKE_WANT_READ;
 			}
 			}
 
 
 			addr_len = 16;
 			addr_len = 16;
-			memcpy(addr, buff + 4, addr_len);
-			port = ntohs(*((short *)(buff + 4 + addr_len)));
+			memcpy(addr, recv_buff + 4, addr_len);
+			port = ntohs(*((short *)(recv_buff + 4 + addr_len)));
 			addr_in6->sin6_family = AF_INET6;
 			addr_in6->sin6_family = AF_INET6;
 			memcpy(addr_in6->sin6_addr.s6_addr, addr, addr_len);
 			memcpy(addr_in6->sin6_addr.s6_addr, addr, addr_len);
-			addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
+			addr_in6->sin6_port = *((short *)(recv_buff + 4 + addr_len));
 
 
 			if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 &&
 			if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 &&
 				addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 &&
 				addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 &&
@@ -661,12 +687,12 @@ static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_co
 			case AF_INET: {
 			case AF_INET: {
 				struct sockaddr_in *addr_in = NULL;
 				struct sockaddr_in *addr_in = NULL;
 				addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
 				addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
-				addr_in->sin_port = *((short *)(buff + 4 + addr_len));
+				addr_in->sin_port = *((short *)(recv_buff + 4 + addr_len));
 			} break;
 			} break;
 			case AF_INET6: {
 			case AF_INET6: {
 				struct sockaddr_in6 *addr_in6 = NULL;
 				struct sockaddr_in6 *addr_in6 = NULL;
 				addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
 				addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
-				addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
+				addr_in6->sin6_port = *((short *)(recv_buff + 4 + addr_len));
 			} break;
 			} break;
 			default:
 			default:
 				return PROXY_HANDSHAKE_ERR;
 				return PROXY_HANDSHAKE_ERR;
@@ -754,7 +780,8 @@ static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
 			goto out;
 			goto out;
 		}
 		}
 
 
-		len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
+		len = recv(proxy_conn->fd, proxy_conn->buffer.buffer + proxy_conn->buffer.len,
+				   sizeof(proxy_conn->buffer.buffer), 0);
 		if (len <= 0) {
 		if (len <= 0) {
 			if (len == 0) {
 			if (len == 0) {
 				tlog(TLOG_ERROR, "remote server closed.");
 				tlog(TLOG_ERROR, "remote server closed.");
@@ -763,10 +790,12 @@ static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
 			}
 			}
 			goto out;
 			goto out;
 		}
 		}
+		proxy_conn->buffer.len += len;
 
 
-		len = http_head_parse(http_head, buff, len);
+		len = http_head_parse(http_head, proxy_conn->buffer.buffer, proxy_conn->buffer.len);
 		if (len < 0) {
 		if (len < 0) {
 			if (len == -1) {
 			if (len == -1) {
+				ret = PROXY_HANDSHAKE_WANT_READ;
 				goto out;
 				goto out;
 			}
 			}
 
 
@@ -780,6 +809,15 @@ static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
 			goto out;
 			goto out;
 		}
 		}
 
 
+		proxy_conn->buffer.len -= len;
+		if (proxy_conn->buffer.len > 0) {
+			memmove(proxy_conn->buffer.buffer, proxy_conn->buffer.buffer + len, proxy_conn->buffer.len);
+		}
+
+		if (proxy_conn->buffer.len < 0) {
+			proxy_conn->buffer.len = 0;
+		}
+		tlog(TLOG_INFO, "connected to http proxy server.");
 		proxy_conn->state = PROXY_CONN_CONNECTED;
 		proxy_conn->state = PROXY_CONN_CONNECTED;
 		ret = PROXY_HANDSHAKE_CONNECTED;
 		ret = PROXY_HANDSHAKE_CONNECTED;
 		goto out;
 		goto out;

+ 71 - 7
src/util.c

@@ -24,6 +24,7 @@
 #include "tlog.h"
 #include "tlog.h"
 #include "util.h"
 #include "util.h"
 #include <arpa/inet.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <dlfcn.h>
 #include <dlfcn.h>
 #include <errno.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fcntl.h>
@@ -389,11 +390,54 @@ int check_is_ipaddr(const char *ip)
 }
 }
 
 
 int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
 int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
+{
+	return parse_uri_ext(value, scheme, NULL, NULL, host, port, path);
+}
+
+void urldecode(char *dst, const char *src)
+{
+	char a, b;
+	while (*src) {
+		if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) {
+			if (a >= 'a') {
+				a -= 'a' - 'A';
+			}
+
+			if (a >= 'A') {
+				a -= ('A' - 10);
+			} else {
+				a -= '0';
+			}
+
+			if (b >= 'a') {
+				b -= 'a' - 'A';
+			}
+
+			if (b >= 'A') {
+				b -= ('A' - 10);
+			} else {
+				b -= '0';
+			}
+			*dst++ = 16 * a + b;
+			src += 3;
+		} else if (*src == '+') {
+			*dst++ = ' ';
+			src++;
+		} else {
+			*dst++ = *src++;
+		}
+	}
+	*dst++ = '\0';
+}
+
+int parse_uri_ext(char *value, char *scheme, char *user, char *password, char *host, int *port, char *path)
 {
 {
 	char *scheme_end = NULL;
 	char *scheme_end = NULL;
 	int field_len = 0;
 	int field_len = 0;
 	char *process_ptr = value;
 	char *process_ptr = value;
-	char host_name[PATH_MAX];
+	char user_pass_host_part[PATH_MAX];
+	char *user_password = NULL;
+	char *host_part = NULL;
 
 
 	char *host_end = NULL;
 	char *host_end = NULL;
 
 
@@ -413,24 +457,44 @@ int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
 
 
 	host_end = strstr(process_ptr, "/");
 	host_end = strstr(process_ptr, "/");
 	if (host_end == NULL) {
 	if (host_end == NULL) {
-		return parse_ip(process_ptr, host, port);
+		host_end = process_ptr + strlen(process_ptr);
 	};
 	};
 
 
 	field_len = host_end - process_ptr;
 	field_len = host_end - process_ptr;
-	if (field_len >= (int)sizeof(host_name)) {
+	if (field_len >= (int)sizeof(user_pass_host_part)) {
 		return -1;
 		return -1;
 	}
 	}
-	memcpy(host_name, process_ptr, field_len);
-	host_name[field_len] = 0;
+	memcpy(user_pass_host_part, process_ptr, field_len);
+	user_pass_host_part[field_len] = 0;
+
+	host_part = strstr(user_pass_host_part, "@");
+	if (host_part != NULL) {
+		*host_part = '\0';
+		host_part = host_part + 1;
+		user_password = user_pass_host_part;
+		char *sep = strstr(user_password, ":");
+		if (sep != NULL) {
+			*sep = '\0';
+			sep = sep + 1;
+			if (password) {
+				urldecode(password, sep);
+			}
+		}
+		if (user) {
+			urldecode(user, user_password);
+		}
+	} else {
+		host_part = user_pass_host_part;
+	}
 
 
-	if (parse_ip(host_name, host, port) != 0) {
+	if (host != NULL && parse_ip(host_part, host, port) != 0) {
 		return -1;
 		return -1;
 	}
 	}
 
 
 	process_ptr += field_len;
 	process_ptr += field_len;
 
 
 	if (path) {
 	if (path) {
-		strncpy(path, process_ptr, PATH_MAX);
+		strcpy(path, process_ptr);
 	}
 	}
 	return 0;
 	return 0;
 }
 }

+ 4 - 0
src/util.h

@@ -69,6 +69,10 @@ int check_is_ipaddr(const char *ip);
 
 
 int parse_uri(char *value, char *scheme, char *host, int *port, char *path);
 int parse_uri(char *value, char *scheme, char *host, int *port, char *path);
 
 
+int parse_uri_ext(char *value, char *scheme, char *user, char *password, char *host, int *port, char *path);
+
+void urldecode(char *dst, const char *src);
+
 int set_fd_nonblock(int fd, int nonblock);
 int set_fd_nonblock(int fd, int nonblock);
 
 
 char *reverse_string(char *output, const char *input, int len, int to_lower_case);
 char *reverse_string(char *output, const char *input, int len, int to_lower_case);