|
@@ -0,0 +1,993 @@
|
|
|
+/*************************************************************************
|
|
|
+ *
|
|
|
+ * Copyright (C) 2018-2023 Ruilin Peng (Nick) <[email protected]>.
|
|
|
+ *
|
|
|
+ * smartdns is free software: you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * smartdns is distributed in the hope that it will be useful,
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+ * GNU General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef _GNU_SOURCE
|
|
|
+#define _GNU_SOURCE
|
|
|
+#endif
|
|
|
+#include "proxy.h"
|
|
|
+#include "dns_conf.h"
|
|
|
+#include "hashtable.h"
|
|
|
+#include "http_parse.h"
|
|
|
+#include "list.h"
|
|
|
+#include "tlog.h"
|
|
|
+#include "util.h"
|
|
|
+#include <arpa/inet.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <pthread.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <sys/epoll.h>
|
|
|
+
|
|
|
+#define PROXY_SOCKS5_VERSION 0x05
|
|
|
+#define PROXY_SOCKS5_NO_AUTH 0x00
|
|
|
+#define PROXY_SOCKS5_AUTH_USER_PASS 0x02
|
|
|
+#define PROXY_SOCKS5_AUTH_NONE 0xFF
|
|
|
+
|
|
|
+#define PROXY_SOCKS5_TYPE_IPV4 0x01
|
|
|
+#define PROXY_SOCKS5_TYPE_DOMAIN 0x03
|
|
|
+#define PROXY_SOCKS5_TYPE_IPV6 0x04
|
|
|
+
|
|
|
+#define PROXY_SOCKS5_CONNECT_TCP 0x01
|
|
|
+#define PROXY_SOCKS5_CONNECT_UDP 0x03
|
|
|
+
|
|
|
+#define PROXY_MAX_EVENTS 64
|
|
|
+#define PROXY_BUFFER_SIZE (1024 * 8)
|
|
|
+#define PROXY_MAX_HOSTNAME_LEN 256
|
|
|
+
|
|
|
+typedef enum PROXY_CONN_STATE {
|
|
|
+ PROXY_CONN_INIT = 0,
|
|
|
+ PROXY_CONN_INIT_ACK = 1,
|
|
|
+ PROXY_CONN_AUTH = 2,
|
|
|
+ PROXY_CONN_AUTH_ACK = 3,
|
|
|
+ PROXY_CONN_CONNECTING = 4,
|
|
|
+ PROXY_CONN_CONNECTED = 5,
|
|
|
+} PROXY_CONN_STATE;
|
|
|
+
|
|
|
+struct proxy_conn {
|
|
|
+ proxy_type_t type;
|
|
|
+ PROXY_CONN_STATE state;
|
|
|
+ char host[DNS_MAX_CNAME_LEN];
|
|
|
+ unsigned short port;
|
|
|
+ int fd;
|
|
|
+ int udp_fd;
|
|
|
+ int buffer_len;
|
|
|
+ int is_udp;
|
|
|
+ struct sockaddr_storage udp_dest_addr;
|
|
|
+ socklen_t udp_dest_addrlen;
|
|
|
+ struct proxy_server_info *server_info;
|
|
|
+};
|
|
|
+
|
|
|
+/* upstream server groups */
|
|
|
+struct proxy_server_info {
|
|
|
+ struct hlist_node node;
|
|
|
+ char proxy_name[PROXY_NAME_LEN];
|
|
|
+ struct sockaddr_storage server_addr;
|
|
|
+ socklen_t server_addrlen;
|
|
|
+ struct proxy_info info;
|
|
|
+};
|
|
|
+
|
|
|
+struct proxy_struct {
|
|
|
+ int run;
|
|
|
+ int epoll_fd;
|
|
|
+ pthread_t tid;
|
|
|
+ pthread_mutex_t proxy_lock;
|
|
|
+ DECLARE_HASHTABLE(proxy_server, 4);
|
|
|
+};
|
|
|
+
|
|
|
+static struct proxy_struct proxy;
|
|
|
+
|
|
|
+const char *proxy_socks5_status_code[] = {
|
|
|
+ "success",
|
|
|
+ "general SOCKS server failure",
|
|
|
+ "connection not allowed by ruleset",
|
|
|
+ "Network unreachable",
|
|
|
+ "Host unreachable",
|
|
|
+ "Connection refused",
|
|
|
+ "TTL expired",
|
|
|
+ "Command not supported",
|
|
|
+ "Address type not supported",
|
|
|
+};
|
|
|
+
|
|
|
+/* get server group by name */
|
|
|
+static struct proxy_server_info *_proxy_get_server_info(const char *proxy_name)
|
|
|
+{
|
|
|
+ unsigned long key;
|
|
|
+ struct proxy_server_info *server_info = NULL;
|
|
|
+ struct hlist_node *tmp = NULL;
|
|
|
+
|
|
|
+ if (proxy_name == NULL) {
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ key = hash_string(proxy_name);
|
|
|
+ hash_for_each_possible_safe(proxy.proxy_server, server_info, tmp, node, key)
|
|
|
+ {
|
|
|
+ if (strncmp(server_info->proxy_name, proxy_name, DNS_GROUP_NAME_LEN) != 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ return server_info;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct addrinfo *_proxy_getaddr(const char *host, int port, int type, int protocol)
|
|
|
+{
|
|
|
+ struct addrinfo hints;
|
|
|
+ struct addrinfo *result = NULL;
|
|
|
+ int ret = 0;
|
|
|
+ char port_str[32];
|
|
|
+
|
|
|
+ snprintf(port_str, sizeof(port_str), "%d", port);
|
|
|
+
|
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
|
+ hints.ai_family = AF_UNSPEC;
|
|
|
+ hints.ai_socktype = type;
|
|
|
+ hints.ai_protocol = protocol;
|
|
|
+
|
|
|
+ ret = getaddrinfo(host, port_str, &hints, &result);
|
|
|
+ if (ret != 0) {
|
|
|
+ tlog(TLOG_ERROR, "get addr info failed. %s\n", gai_strerror(ret));
|
|
|
+ tlog(TLOG_ERROR, "host = %s, port = %d, type = %d, protocol = %d", host, port, type, protocol);
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+errout:
|
|
|
+ if (result) {
|
|
|
+ freeaddrinfo(result);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_add(const char *proxy_name, struct proxy_info *info)
|
|
|
+{
|
|
|
+ unsigned long key;
|
|
|
+ char ip_str[PROXY_MAX_IPLEN];
|
|
|
+ int port = 0;
|
|
|
+ struct addrinfo *gai = NULL;
|
|
|
+ struct proxy_server_info *server_info = _proxy_get_server_info(proxy_name);
|
|
|
+
|
|
|
+ if (server_info) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ server_info = malloc(sizeof(*server_info));
|
|
|
+ if (server_info == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(server_info, 0, sizeof(*server_info));
|
|
|
+ memcpy(&server_info->info, info, sizeof(struct proxy_info));
|
|
|
+
|
|
|
+ if (parse_ip(info->server, ip_str, &port) != 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ port = info->port;
|
|
|
+ gai = _proxy_getaddr(info->server, port, SOCK_STREAM, 0);
|
|
|
+ if (gai == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ server_info->server_addrlen = gai->ai_addrlen;
|
|
|
+ memcpy(&server_info->server_addr, gai->ai_addr, gai->ai_addrlen);
|
|
|
+
|
|
|
+ safe_strncpy(server_info->proxy_name, proxy_name, PROXY_NAME_LEN);
|
|
|
+ key = hash_string(server_info->proxy_name);
|
|
|
+ hash_add(proxy.proxy_server, &server_info->node, key);
|
|
|
+
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ return 0;
|
|
|
+errout:
|
|
|
+ if (server_info) {
|
|
|
+ free(server_info);
|
|
|
+ server_info = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gai) {
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static int _proxy_remove(struct proxy_server_info *server_info)
|
|
|
+{
|
|
|
+ hash_del(&server_info->node);
|
|
|
+ free(server_info);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_remove(const char *proxy_name)
|
|
|
+{
|
|
|
+ struct proxy_server_info *server_info = _proxy_get_server_info(proxy_name);
|
|
|
+ if (server_info == NULL) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ _proxy_remove(server_info);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void _proxy_remove_all(void)
|
|
|
+{
|
|
|
+ struct proxy_server_info *server_info;
|
|
|
+ struct hlist_node *tmp = NULL;
|
|
|
+ unsigned int i = 0;
|
|
|
+
|
|
|
+ hash_for_each_safe(proxy.proxy_server, i, tmp, server_info, node)
|
|
|
+ {
|
|
|
+ _proxy_remove(server_info);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct proxy_conn *proxy_conn_new(const char *proxy_name, const char *host, int port, int is_udp)
|
|
|
+{
|
|
|
+ struct proxy_conn *proxy_conn = NULL;
|
|
|
+ struct proxy_server_info *server_info = NULL;
|
|
|
+ struct addrinfo *gai = NULL;
|
|
|
+ int fd = -1;
|
|
|
+
|
|
|
+ server_info = _proxy_get_server_info(proxy_name);
|
|
|
+ if (server_info == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_udp == 1 && server_info->info.type != PROXY_SOCKS5) {
|
|
|
+ tlog(TLOG_WARN, "only socks5 support udp");
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ fd = socket(server_info->server_addr.ss_family, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
|
|
+ if (fd < 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn = malloc(sizeof(*proxy_conn));
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(proxy_conn, 0, sizeof(*proxy_conn));
|
|
|
+ safe_strncpy(proxy_conn->host, host, DNS_MAX_CNAME_LEN);
|
|
|
+ proxy_conn->port = port;
|
|
|
+ proxy_conn->type = server_info->info.type;
|
|
|
+ proxy_conn->state = PROXY_CONN_INIT;
|
|
|
+ proxy_conn->server_info = server_info;
|
|
|
+ proxy_conn->fd = fd;
|
|
|
+ proxy_conn->udp_fd = -1;
|
|
|
+ proxy_conn->is_udp = is_udp;
|
|
|
+
|
|
|
+ return proxy_conn;
|
|
|
+errout:
|
|
|
+ if (proxy_conn) {
|
|
|
+ free(proxy_conn);
|
|
|
+ proxy_conn = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fd >= 0) {
|
|
|
+ close(fd);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (gai) {
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void proxy_conn_free(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (proxy_conn->fd >= 0) {
|
|
|
+ close(proxy_conn->fd);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (proxy_conn->udp_fd >= 0) {
|
|
|
+ close(proxy_conn->udp_fd);
|
|
|
+ }
|
|
|
+
|
|
|
+ free(proxy_conn);
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_connect(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return connect(proxy_conn->fd, (struct sockaddr *)&proxy_conn->server_info->server_addr,
|
|
|
+ proxy_conn->server_info->server_addrlen);
|
|
|
+}
|
|
|
+
|
|
|
+static int _proxy_handshake_socks5_create_udp_fd(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ int ret = 0;
|
|
|
+ char *gai_host = NULL;
|
|
|
+ int udp_fd = -1;
|
|
|
+ struct addrinfo *gai = NULL;
|
|
|
+
|
|
|
+ switch (proxy_conn->udp_dest_addr.ss_family) {
|
|
|
+ case AF_INET:
|
|
|
+ gai_host = "0.0.0.0";
|
|
|
+ break;
|
|
|
+ case AF_INET6:
|
|
|
+ gai_host = "::";
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ goto errout;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ gai = _proxy_getaddr(gai_host, 0, SOCK_DGRAM, 0);
|
|
|
+ udp_fd = socket(gai->ai_family, gai->ai_socktype | SOCK_CLOEXEC, 0);
|
|
|
+ if (udp_fd < 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = bind(udp_fd, gai->ai_addr, gai->ai_addrlen);
|
|
|
+ if (ret < 0) {
|
|
|
+ goto errout;
|
|
|
+ }
|
|
|
+
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ return udp_fd;
|
|
|
+errout:
|
|
|
+ if (gai) {
|
|
|
+ freeaddrinfo(gai);
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static int _proxy_handshake_socks5_connect_udp(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ int udp_fd = -1;
|
|
|
+
|
|
|
+ if (proxy_conn->is_udp == 0) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (proxy_conn->udp_fd < 0) {
|
|
|
+ udp_fd = _proxy_handshake_socks5_create_udp_fd(proxy_conn);
|
|
|
+ if (udp_fd < 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->udp_fd = udp_fd;
|
|
|
+ }
|
|
|
+
|
|
|
+ return connect(proxy_conn->udp_fd, (struct sockaddr *)&proxy_conn->udp_dest_addr, proxy_conn->udp_dest_addrlen);
|
|
|
+}
|
|
|
+
|
|
|
+static proxy_handshake_state _proxy_handshake_socks5_reply_connect_addr(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ char buff[DNS_MAX_CNAME_LEN * 2];
|
|
|
+ int len = 0;
|
|
|
+ memset(buff, 0, sizeof(buff));
|
|
|
+ struct sockaddr_storage addr;
|
|
|
+ char *ptr = buff;
|
|
|
+ socklen_t addr_len = sizeof(addr);
|
|
|
+
|
|
|
+ buff[0] = PROXY_SOCKS5_VERSION;
|
|
|
+ if (proxy_conn->is_udp) {
|
|
|
+ buff[1] = PROXY_SOCKS5_CONNECT_UDP;
|
|
|
+ } else {
|
|
|
+ buff[1] = PROXY_SOCKS5_CONNECT_TCP;
|
|
|
+ }
|
|
|
+
|
|
|
+ buff[2] = 0x0;
|
|
|
+ ptr = buff + 3;
|
|
|
+ if (proxy_conn->server_info->info.use_domain) {
|
|
|
+ *ptr = PROXY_SOCKS5_TYPE_DOMAIN;
|
|
|
+ ptr++;
|
|
|
+
|
|
|
+ int domainlen = strnlen(proxy_conn->host, DNS_MAX_CNAME_LEN);
|
|
|
+ *ptr = domainlen;
|
|
|
+ ptr++;
|
|
|
+ memcpy(ptr, proxy_conn->host, domainlen);
|
|
|
+ ptr += domainlen;
|
|
|
+ } else {
|
|
|
+ if (proxy_conn->is_udp) {
|
|
|
+ memset(&addr, 0, proxy_conn->server_info->server_addrlen);
|
|
|
+ addr_len = proxy_conn->server_info->server_addrlen;
|
|
|
+ addr.ss_family = proxy_conn->server_info->server_addr.ss_family;
|
|
|
+ } else {
|
|
|
+ getaddr_by_host(proxy_conn->host, (struct sockaddr *)&addr, &addr_len);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (addr.ss_family) {
|
|
|
+ case AF_INET: {
|
|
|
+ struct sockaddr_in *addr_in = NULL;
|
|
|
+ addr_in = (struct sockaddr_in *)&addr;
|
|
|
+ *ptr = PROXY_SOCKS5_TYPE_IPV4;
|
|
|
+ ptr++;
|
|
|
+ memcpy(ptr, &addr_in->sin_addr.s_addr, 4);
|
|
|
+ ptr += 4;
|
|
|
+ } break;
|
|
|
+ case AF_INET6: {
|
|
|
+ struct sockaddr_in6 *addr_in6 = NULL;
|
|
|
+ addr_in6 = (struct sockaddr_in6 *)&addr;
|
|
|
+ if (IN6_IS_ADDR_V4MAPPED(&addr_in6->sin6_addr)) {
|
|
|
+ *ptr = PROXY_SOCKS5_TYPE_IPV4;
|
|
|
+ ptr++;
|
|
|
+ memcpy(ptr, addr_in6->sin6_addr.s6_addr + 12, 4);
|
|
|
+ ptr += 4;
|
|
|
+ } else {
|
|
|
+ *ptr = PROXY_SOCKS5_TYPE_IPV6;
|
|
|
+ ptr++;
|
|
|
+ memcpy(ptr, addr_in6->sin6_addr.s6_addr, 16);
|
|
|
+ ptr += 16;
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ *((short *)(ptr)) = htons(proxy_conn->port);
|
|
|
+ ptr += 2;
|
|
|
+
|
|
|
+ len = send(proxy_conn->fd, buff, ptr - buff, MSG_NOSIGNAL);
|
|
|
+ if (len != ptr - buff) {
|
|
|
+ tlog(TLOG_ERROR, "Send proxy request failed.");
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+ proxy_conn->state = PROXY_CONN_CONNECTING;
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+}
|
|
|
+
|
|
|
+static proxy_handshake_state _proxy_handshake_socks5_send_auth(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ char buff[DNS_MAX_CNAME_LEN * 2];
|
|
|
+ int len = 0;
|
|
|
+ int offset = 0;
|
|
|
+ memset(buff, 0, sizeof(buff));
|
|
|
+
|
|
|
+ buff[0] = 0x1;
|
|
|
+ buff[1] = strnlen(proxy_conn->server_info->info.username, PROXY_MAX_NAMELEN);
|
|
|
+ safe_strncpy(buff + 2, proxy_conn->server_info->info.username, buff[1] + 1);
|
|
|
+ offset = buff[1] + 2;
|
|
|
+ buff[offset] = strnlen(proxy_conn->server_info->info.password, PROXY_MAX_NAMELEN);
|
|
|
+ safe_strncpy(buff + offset + 1, proxy_conn->server_info->info.password, buff[offset] + 1);
|
|
|
+ offset += buff[offset] + 1;
|
|
|
+ len = send(proxy_conn->fd, buff, offset, MSG_NOSIGNAL);
|
|
|
+ if (len != offset) {
|
|
|
+ tlog(TLOG_ERROR, "send auth failed, len = %d, errno = %s", len, strerror(errno));
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->state = PROXY_CONN_AUTH_ACK;
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+}
|
|
|
+
|
|
|
+static proxy_handshake_state _proxy_handshake_socks5(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ int len = 0;
|
|
|
+ char buff[DNS_MAX_CNAME_LEN * 2];
|
|
|
+ memset(buff, 0, sizeof(buff));
|
|
|
+
|
|
|
+ switch (proxy_conn->state) {
|
|
|
+ case PROXY_CONN_INIT: {
|
|
|
+ buff[0] = PROXY_SOCKS5_VERSION;
|
|
|
+ buff[1] = 0x2; // 2 auth methods
|
|
|
+ buff[2] = PROXY_SOCKS5_NO_AUTH;
|
|
|
+ buff[3] = PROXY_SOCKS5_AUTH_USER_PASS;
|
|
|
+ len = send(proxy_conn->fd, buff, 4, MSG_NOSIGNAL);
|
|
|
+ if (len != 4) {
|
|
|
+ tlog(TLOG_ERROR, "init socks5 failed, errno = %s", strerror(errno));
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->state = PROXY_CONN_INIT_ACK;
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+ } break;
|
|
|
+ case PROXY_CONN_INIT_ACK:
|
|
|
+ len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
|
|
|
+ if (len <= 0) {
|
|
|
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_ERROR, "recv socks5 init ack failed, errno = %s", strerror(errno));
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len != 2) {
|
|
|
+ tlog(TLOG_ERROR, "recv socks5 init ack failed, len = %d", len);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff[0] != PROXY_SOCKS5_VERSION) {
|
|
|
+ tlog(TLOG_ERROR, "Server not support socks5");
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((unsigned char)buff[1] == PROXY_SOCKS5_AUTH_NONE) {
|
|
|
+ tlog(TLOG_ERROR, "Server not support auth methods");
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_INFO, "Server select auth method is %d", buff[1]);
|
|
|
+ if (buff[1] == PROXY_SOCKS5_AUTH_USER_PASS) {
|
|
|
+ return _proxy_handshake_socks5_send_auth(proxy_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff[1] == PROXY_SOCKS5_NO_AUTH) {
|
|
|
+ return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_ERROR, "Server select invalid auth method %d", buff[1]);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ break;
|
|
|
+ case PROXY_CONN_AUTH_ACK:
|
|
|
+ len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
|
|
|
+ if (len <= 0) {
|
|
|
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_ERROR, "recv socks5 auth ack failed, errno = %s", strerror(errno));
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len != 2) {
|
|
|
+ tlog(TLOG_ERROR, "recv socks5 auth ack failed, len = %d", len);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff[0] != 0x1) {
|
|
|
+ tlog(TLOG_ERROR, "Server not support socks5");
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff[1] != 0x0) {
|
|
|
+ tlog(TLOG_ERROR, "Server auth failed, code = %d", buff[1]);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_INFO, "Server auth success");
|
|
|
+ proxy_conn->state = PROXY_CONN_CONNECTING;
|
|
|
+ return _proxy_handshake_socks5_reply_connect_addr(proxy_conn);
|
|
|
+ case PROXY_CONN_CONNECTING: {
|
|
|
+ unsigned char addr[16];
|
|
|
+ unsigned short port = 0;
|
|
|
+ int use_dest_ip = 0;
|
|
|
+
|
|
|
+ int addr_len = 0;
|
|
|
+ len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
|
|
|
+ if (len <= 0) {
|
|
|
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
+ return PROXY_HANDSHAKE_WANT_READ;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_ERROR, "recv socks5 connect ack failed, errno = %s", strerror(errno));
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len < 10) {
|
|
|
+ tlog(TLOG_ERROR, "Server reply connect addr failed, len = %d", len);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buff[0] != PROXY_SOCKS5_VERSION) {
|
|
|
+ tlog(TLOG_ERROR, "Server not support socks5");
|
|
|
+ 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]]);
|
|
|
+ } else {
|
|
|
+ tlog(TLOG_ERROR, "Server replay failed, error code %x", buff[1]);
|
|
|
+ }
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (buff[3]) {
|
|
|
+ case PROXY_SOCKS5_TYPE_IPV4: {
|
|
|
+ struct sockaddr_in *addr_in = NULL;
|
|
|
+ addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
|
|
|
+ proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in);
|
|
|
+ if (len != 10) {
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ addr_len = 4;
|
|
|
+ memcpy(addr, buff + 4, addr_len);
|
|
|
+ port = ntohs(*((short *)(buff + 4 + addr_len)));
|
|
|
+ addr_in->sin_family = AF_INET;
|
|
|
+ addr_in->sin_addr.s_addr = *((int *)addr);
|
|
|
+ addr_in->sin_port = *((short *)(buff + 4 + addr_len));
|
|
|
+ if (addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0) {
|
|
|
+ use_dest_ip = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_DEBUG, "proxy dest: %d.%d.%d.%d:%d\n", addr[0], addr[1], addr[2], addr[3], port);
|
|
|
+ } break;
|
|
|
+ case PROXY_SOCKS5_TYPE_IPV6: {
|
|
|
+ struct sockaddr_in6 *addr_in6 = NULL;
|
|
|
+ addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
|
|
|
+ proxy_conn->udp_dest_addrlen = sizeof(struct sockaddr_in6);
|
|
|
+ if (len != 22) {
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ addr_len = 16;
|
|
|
+ memcpy(addr, buff + 4, addr_len);
|
|
|
+ port = ntohs(*((short *)(buff + 4 + addr_len)));
|
|
|
+ addr_in6->sin6_family = AF_INET6;
|
|
|
+ memcpy(addr_in6->sin6_addr.s6_addr, addr, addr_len);
|
|
|
+ addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
|
|
|
+
|
|
|
+ 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[12] == 0 && addr[13] == 0 && addr[14] == 0 && addr[15] == 0) {
|
|
|
+ use_dest_ip = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_DEBUG, "proxy dest: [%x:%x:%x:%x:%x:%x:%x:%x]:%d\n", ntohs(*((short *)addr)),
|
|
|
+ ntohs(*((short *)(addr + 2))), ntohs(*((short *)(addr + 4))), ntohs(*((short *)(addr + 6))),
|
|
|
+ ntohs(*((short *)(addr + 8))), ntohs(*((short *)(addr + 10))), ntohs(*((short *)(addr + 12))),
|
|
|
+ ntohs(*((short *)(addr + 14))), port);
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (use_dest_ip && proxy_conn->is_udp) {
|
|
|
+ memcpy(&proxy_conn->udp_dest_addr, &proxy_conn->server_info->server_addr,
|
|
|
+ proxy_conn->server_info->server_addrlen);
|
|
|
+ proxy_conn->udp_dest_addrlen = proxy_conn->server_info->server_addrlen;
|
|
|
+ switch (proxy_conn->udp_dest_addr.ss_family) {
|
|
|
+ case AF_INET: {
|
|
|
+ struct sockaddr_in *addr_in = NULL;
|
|
|
+ addr_in = (struct sockaddr_in *)&proxy_conn->udp_dest_addr;
|
|
|
+ addr_in->sin_port = *((short *)(buff + 4 + addr_len));
|
|
|
+ } break;
|
|
|
+ case AF_INET6: {
|
|
|
+ struct sockaddr_in6 *addr_in6 = NULL;
|
|
|
+ addr_in6 = (struct sockaddr_in6 *)&proxy_conn->udp_dest_addr;
|
|
|
+ addr_in6->sin6_port = *((short *)(buff + 4 + addr_len));
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_proxy_handshake_socks5_connect_udp(proxy_conn) != 0) {
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->state = PROXY_CONN_CONNECTED;
|
|
|
+ tlog(TLOG_INFO, "connected to socks proxy server.");
|
|
|
+ return PROXY_HANDSHAKE_CONNECTED;
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ tlog(TLOG_ERROR, "client socks status %d is invalid", proxy_conn->state);
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+}
|
|
|
+
|
|
|
+static int _proxy_handshake_http(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ int len = 0;
|
|
|
+ proxy_handshake_state ret = PROXY_HANDSHAKE_ERR;
|
|
|
+ char buff[4096];
|
|
|
+ struct http_head *http_head = NULL;
|
|
|
+
|
|
|
+ switch (proxy_conn->state) {
|
|
|
+ case PROXY_CONN_INIT: {
|
|
|
+ char connecthost[DNS_MAX_CNAME_LEN * 2];
|
|
|
+ struct sockaddr_storage addr;
|
|
|
+
|
|
|
+ socklen_t addr_len = sizeof(addr);
|
|
|
+ getaddr_by_host(proxy_conn->host, (struct sockaddr *)&addr, &addr_len);
|
|
|
+
|
|
|
+ if (proxy_conn->server_info->info.use_domain) {
|
|
|
+ snprintf(connecthost, sizeof(connecthost), "%s:%d", proxy_conn->host, proxy_conn->port);
|
|
|
+ } else {
|
|
|
+ struct sockaddr_in *addr_in;
|
|
|
+ addr_in = (struct sockaddr_in *)&addr;
|
|
|
+ unsigned char *paddr = (unsigned char *)&addr_in->sin_addr.s_addr;
|
|
|
+ snprintf(connecthost, sizeof(connecthost), "%d.%d.%d.%d:%d", paddr[0], paddr[1], paddr[2], paddr[3],
|
|
|
+ proxy_conn->port);
|
|
|
+ }
|
|
|
+
|
|
|
+ int msglen = 0;
|
|
|
+
|
|
|
+ if (proxy_conn->server_info->info.username[0] == '\0') {
|
|
|
+ msglen = snprintf(buff, sizeof(buff),
|
|
|
+ "CONNECT %s HTTP/1.1\r\n"
|
|
|
+ "Host: %s\r\n"
|
|
|
+ "Proxy-Connection: Keep-Alive\r\n\r\n",
|
|
|
+ connecthost, connecthost);
|
|
|
+ } else {
|
|
|
+ char auth[256];
|
|
|
+ char base64_auth[256 * 2];
|
|
|
+ snprintf(auth, sizeof(auth), "%s:%s", proxy_conn->server_info->info.username,
|
|
|
+ proxy_conn->server_info->info.password);
|
|
|
+ SSL_base64_encode(auth, strlen(auth), base64_auth);
|
|
|
+
|
|
|
+ msglen = snprintf(buff, sizeof(buff),
|
|
|
+ "CONNECT %s HTTP/1.1\r\n"
|
|
|
+ "Host: %s\r\n"
|
|
|
+ "Proxy-Authorization: Basic %s\r\n"
|
|
|
+ "Proxy-Connection: Keep-Alive\r\n\r\n",
|
|
|
+ connecthost, connecthost, base64_auth);
|
|
|
+ }
|
|
|
+
|
|
|
+ len = send(proxy_conn->fd, buff, msglen, MSG_NOSIGNAL);
|
|
|
+ if (len != msglen) {
|
|
|
+ tlog(TLOG_ERROR, "init https failed, len = %d, errno = %s", len, strerror(errno));
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->state = PROXY_CONN_CONNECTING;
|
|
|
+ ret = PROXY_HANDSHAKE_WANT_READ;
|
|
|
+ goto out;
|
|
|
+ } break;
|
|
|
+ case PROXY_CONN_CONNECTING: {
|
|
|
+ http_head = http_head_init(4096);
|
|
|
+ if (http_head == NULL) {
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = recv(proxy_conn->fd, buff, sizeof(buff), 0);
|
|
|
+ if (len <= 0) {
|
|
|
+ if (len == 0) {
|
|
|
+ tlog(TLOG_ERROR, "remote server closed.");
|
|
|
+ } else {
|
|
|
+ tlog(TLOG_ERROR, "recv failed, errno = %s", strerror(errno));
|
|
|
+ }
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = http_head_parse(http_head, buff, len);
|
|
|
+ if (len < 0) {
|
|
|
+ if (len == -1) {
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ tlog(TLOG_DEBUG, "remote server not supported.");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (http_head_get_httpcode(http_head) != 200) {
|
|
|
+ tlog(TLOG_WARN, "http server query failed, server return http code : %d, %s",
|
|
|
+ http_head_get_httpcode(http_head), http_head_get_httpcode_msg(http_head));
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ proxy_conn->state = PROXY_CONN_CONNECTED;
|
|
|
+ ret = PROXY_HANDSHAKE_CONNECTED;
|
|
|
+ goto out;
|
|
|
+ } break;
|
|
|
+ default:
|
|
|
+ goto out;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (http_head) {
|
|
|
+ http_head_destroy(http_head);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+proxy_handshake_state proxy_conn_handshake(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (proxy_conn->state == PROXY_CONN_CONNECTED) {
|
|
|
+ return PROXY_HANDSHAKE_OK;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (proxy_conn->type) {
|
|
|
+ case PROXY_SOCKS5:
|
|
|
+ return _proxy_handshake_socks5(proxy_conn);
|
|
|
+ case PROXY_HTTP:
|
|
|
+ return _proxy_handshake_http(proxy_conn);
|
|
|
+ default:
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ return PROXY_HANDSHAKE_ERR;
|
|
|
+}
|
|
|
+
|
|
|
+static int _proxy_is_tcp_connected(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ char buff[1];
|
|
|
+ int ret = 0;
|
|
|
+ ret = recv(proxy_conn->fd, buff, 1, MSG_PEEK | MSG_DONTWAIT);
|
|
|
+ if (ret < 0) {
|
|
|
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_sendto(struct proxy_conn *proxy_conn, const void *buf, size_t len, int flags,
|
|
|
+ const struct sockaddr *dest_addr, socklen_t addrlen)
|
|
|
+{
|
|
|
+ char buffer[PROXY_BUFFER_SIZE];
|
|
|
+ int buffer_len = 0;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_proxy_is_tcp_connected(proxy_conn) == 0) {
|
|
|
+ errno = ECONNRESET;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer[0] = 0x00;
|
|
|
+ buffer[1] = 0x00;
|
|
|
+ buffer[2] = 0x00;
|
|
|
+ buffer_len += 3;
|
|
|
+
|
|
|
+ switch (dest_addr->sa_family) {
|
|
|
+ case AF_INET:
|
|
|
+ buffer[3] = PROXY_SOCKS5_TYPE_IPV4;
|
|
|
+ memcpy(buffer + 4, &((struct sockaddr_in *)dest_addr)->sin_addr.s_addr, 4);
|
|
|
+ memcpy(buffer + 8, &((struct sockaddr_in *)dest_addr)->sin_port, 2);
|
|
|
+ buffer_len += 7;
|
|
|
+ break;
|
|
|
+ case AF_INET6:
|
|
|
+ buffer[3] = PROXY_SOCKS5_TYPE_IPV6;
|
|
|
+ memcpy(buffer + 4, &((struct sockaddr_in6 *)dest_addr)->sin6_addr.s6_addr, 16);
|
|
|
+ memcpy(buffer + 20, &((struct sockaddr_in6 *)dest_addr)->sin6_port, 2);
|
|
|
+ buffer_len += 19;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(buffer + buffer_len, buf, len);
|
|
|
+ buffer_len += len;
|
|
|
+
|
|
|
+ ret = sendto(proxy_conn->udp_fd, buffer, buffer_len, MSG_NOSIGNAL, (struct sockaddr *)&proxy_conn->udp_dest_addr,
|
|
|
+ proxy_conn->udp_dest_addrlen);
|
|
|
+ if (ret != buffer_len) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return len;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_recvfrom(struct proxy_conn *proxy_conn, void *buf, size_t len, int flags, struct sockaddr *src_addr,
|
|
|
+ socklen_t *addrlen)
|
|
|
+{
|
|
|
+ char buffer[PROXY_BUFFER_SIZE];
|
|
|
+ int buffer_len = 0;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = recvfrom(proxy_conn->udp_fd, buffer, sizeof(buffer), MSG_NOSIGNAL, NULL, 0);
|
|
|
+ if (ret <= 0) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buffer[0] != 0x00 || buffer[1] != 0x00 || buffer[2] != 0x00) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (buffer[3]) {
|
|
|
+ case PROXY_SOCKS5_TYPE_IPV4:
|
|
|
+ if (ret < 10) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (src_addr) {
|
|
|
+ memset(src_addr, 0, sizeof(struct sockaddr_in));
|
|
|
+ ((struct sockaddr_in *)src_addr)->sin_family = AF_INET;
|
|
|
+ memcpy(&((struct sockaddr_in *)src_addr)->sin_addr.s_addr, buffer + 4, 4);
|
|
|
+ memcpy(&((struct sockaddr_in *)src_addr)->sin_port, buffer + 8, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addrlen) {
|
|
|
+ *addrlen = sizeof(struct sockaddr_in);
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer_len = 10;
|
|
|
+ break;
|
|
|
+ case PROXY_SOCKS5_TYPE_IPV6:
|
|
|
+ if (ret < 22) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (src_addr) {
|
|
|
+ memset(src_addr, 0, sizeof(struct sockaddr_in6));
|
|
|
+ ((struct sockaddr_in6 *)src_addr)->sin6_family = AF_INET6;
|
|
|
+ memcpy(&((struct sockaddr_in6 *)src_addr)->sin6_addr.s6_addr, buffer + 4, 16);
|
|
|
+ memcpy(&((struct sockaddr_in6 *)src_addr)->sin6_port, buffer + 20, 2);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (addrlen) {
|
|
|
+ *addrlen = sizeof(struct sockaddr_in6);
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer_len = 22;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret - buffer_len > (int)len) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(buf, buffer + buffer_len, ret - buffer_len);
|
|
|
+ return ret - buffer_len;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_get_fd(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return proxy_conn->fd;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_get_udpfd(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return proxy_conn->udp_fd;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_conn_is_udp(struct proxy_conn *proxy_conn)
|
|
|
+{
|
|
|
+ if (proxy_conn == NULL) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return proxy_conn->is_udp;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_init()
|
|
|
+{
|
|
|
+ memset(&proxy, 0, sizeof(proxy));
|
|
|
+ hash_init(proxy.proxy_server);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int proxy_exit()
|
|
|
+{
|
|
|
+ _proxy_remove_all();
|
|
|
+ return 0;
|
|
|
+}
|