1 /*
2  * Copyright (c) 2019 Antmicro Ltd
3  *
4  * Copyright (c) 2019 Intel Corporation
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/logging/log.h>
10 LOG_MODULE_REGISTER(net_socks, CONFIG_SOCKS_LOG_LEVEL);
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/net/socket.h>
14 #include <zephyr/net/net_pkt.h>
15 
16 #include "socks.h"
17 #include "socks_internal.h"
18 
socks5_method_rsp_cb(struct net_context * ctx,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,int status,void * user_data)19 static void socks5_method_rsp_cb(struct net_context *ctx,
20 				 struct net_pkt *pkt,
21 				 union net_ip_header *ip_hdr,
22 				 union net_proto_header *proto_hdr,
23 				 int status,
24 				 void *user_data)
25 {
26 	struct socks5_method_response *method_rsp =
27 			(struct socks5_method_response *)user_data;
28 
29 	if (!pkt || status) {
30 		memset(method_rsp, 0, sizeof(struct socks5_method_response));
31 		goto end;
32 	}
33 
34 	if (net_pkt_read(pkt, (uint8_t *)method_rsp,
35 			 sizeof(struct socks5_method_response))) {
36 		memset(method_rsp, 0, sizeof(struct socks5_method_response));
37 	}
38 
39 end:
40 	net_pkt_unref(pkt);
41 }
42 
socks5_cmd_rsp_cb(struct net_context * ctx,struct net_pkt * pkt,union net_ip_header * ip_hdr,union net_proto_header * proto_hdr,int status,void * user_data)43 static void socks5_cmd_rsp_cb(struct net_context *ctx,
44 			      struct net_pkt *pkt,
45 			      union net_ip_header *ip_hdr,
46 			      union net_proto_header *proto_hdr,
47 			      int status,
48 			      void *user_data)
49 {
50 	struct socks5_command_response *cmd_rsp =
51 			(struct socks5_command_response *)user_data;
52 	int size;
53 
54 	if (!pkt || status) {
55 		memset(cmd_rsp, 0,
56 		       sizeof(struct socks5_command_request_common));
57 		goto end;
58 	}
59 
60 	size = sizeof(struct socks5_command_request_common);
61 
62 	if (net_pkt_read(pkt, (uint8_t *)cmd_rsp, size)) {
63 		memset(cmd_rsp, 0,
64 		       sizeof(struct socks5_command_request_common));
65 	}
66 
67 end:
68 	net_pkt_unref(pkt);
69 }
70 
socks5_tcp_connect(struct net_context * ctx,const struct sockaddr * proxy,socklen_t proxy_len,const struct sockaddr * dest,socklen_t dest_len)71 static int socks5_tcp_connect(struct net_context *ctx,
72 			      const struct sockaddr *proxy,
73 			      socklen_t proxy_len,
74 			      const struct sockaddr *dest,
75 			      socklen_t dest_len)
76 {
77 	struct socks5_method_request method_req;
78 	struct socks5_method_response method_rsp;
79 	struct socks5_command_request cmd_req;
80 	struct socks5_command_response cmd_rsp;
81 	int size;
82 	int ret;
83 
84 	/* Negotiate authentication method */
85 	method_req.r.ver = SOCKS5_PKT_MAGIC;
86 
87 	/* We only support NOAUTH at the moment */
88 	method_req.r.nmethods = 1U;
89 	method_req.methods[0] = SOCKS5_AUTH_METHOD_NOAUTH;
90 
91 	/* size + 1 because just one method is supported */
92 	size = sizeof(struct socks5_method_request_common) + 1;
93 
94 	ret = net_context_sendto(ctx, (uint8_t *)&method_req, size,
95 				 proxy, proxy_len, NULL, K_NO_WAIT,
96 				 ctx->user_data);
97 	if (ret < 0) {
98 		LOG_ERR("Could not send negotiation packet");
99 		return ret;
100 	}
101 
102 	ret = net_context_recv(ctx, socks5_method_rsp_cb,
103 			       K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
104 			       &method_rsp);
105 	if (ret < 0) {
106 		LOG_ERR("Could not receive negotiation response");
107 		return ret;
108 	}
109 
110 	if (method_rsp.ver != SOCKS5_PKT_MAGIC) {
111 		LOG_ERR("Invalid negotiation response magic");
112 		return -EINVAL;
113 	}
114 
115 	if (method_rsp.method != SOCKS5_AUTH_METHOD_NOAUTH) {
116 		LOG_ERR("Invalid negotiation response");
117 		return -ENOTSUP;
118 	}
119 
120 	/* Negotiation complete - now connect to destination */
121 	cmd_req.r.ver = SOCKS5_PKT_MAGIC;
122 	cmd_req.r.cmd = SOCKS5_CMD_CONNECT;
123 	cmd_req.r.rsv = SOCKS5_PKT_RSV;
124 
125 	if (proxy->sa_family == AF_INET) {
126 		const struct sockaddr_in *d4 =
127 			(struct sockaddr_in *)dest;
128 
129 		cmd_req.r.atyp = SOCKS5_ATYP_IPV4;
130 
131 		memcpy(&cmd_req.ipv4_addr.addr,
132 		       (uint8_t *)&d4->sin_addr,
133 		       sizeof(cmd_req.ipv4_addr.addr));
134 
135 		cmd_req.ipv4_addr.port = d4->sin_port;
136 
137 		size = sizeof(struct socks5_command_request_common)
138 			+ sizeof(struct socks5_ipv4_addr);
139 	} else if (proxy->sa_family == AF_INET6) {
140 		const struct sockaddr_in6 *d6 =
141 			(struct sockaddr_in6 *)dest;
142 
143 		cmd_req.r.atyp = SOCKS5_ATYP_IPV6;
144 
145 		memcpy(&cmd_req.ipv6_addr.addr,
146 		       (uint8_t *)&d6->sin6_addr,
147 		       sizeof(cmd_req.ipv6_addr.addr));
148 
149 		cmd_req.ipv6_addr.port = d6->sin6_port;
150 
151 		size = sizeof(struct socks5_command_request_common)
152 			+ sizeof(struct socks5_ipv6_addr);
153 	}
154 
155 	ret = net_context_sendto(ctx, (uint8_t *)&cmd_req, size,
156 				 proxy, proxy_len, NULL, K_NO_WAIT,
157 				 ctx->user_data);
158 	if (ret < 0) {
159 		LOG_ERR("Could not send CONNECT command");
160 		return ret;
161 	}
162 
163 	ret = net_context_recv(ctx, socks5_cmd_rsp_cb,
164 			       K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
165 			       &cmd_rsp);
166 	if (ret < 0) {
167 		LOG_ERR("Could not receive CONNECT response");
168 		return ret;
169 	}
170 
171 	if (cmd_rsp.r.ver != SOCKS5_PKT_MAGIC) {
172 		LOG_ERR("Invalid CONNECT response");
173 		return -EINVAL;
174 	}
175 
176 	if (cmd_rsp.r.rep != SOCKS5_CMD_RESP_SUCCESS) {
177 		LOG_ERR("Unable to connect to destination");
178 		return -EINVAL;
179 	}
180 
181 	/* Verifying the rest is not required */
182 
183 	LOG_DBG("Connection through SOCKS5 proxy successful");
184 
185 	return 0;
186 }
187 
net_socks5_connect(struct net_context * ctx,const struct sockaddr * addr,socklen_t addrlen)188 int net_socks5_connect(struct net_context *ctx, const struct sockaddr *addr,
189 		       socklen_t addrlen)
190 {
191 	struct sockaddr proxy;
192 	socklen_t proxy_len;
193 	int type;
194 	int ret;
195 
196 	type = net_context_get_type(ctx);
197 	/* TODO: Only TCP and TLS supported, UDP and DTLS yet to support. */
198 	if (type != SOCK_STREAM) {
199 		return -ENOTSUP;
200 	}
201 
202 	ret = net_context_get_option(ctx, NET_OPT_SOCKS5, &proxy, &proxy_len);
203 	if (ret < 0) {
204 		return ret;
205 	}
206 
207 	/* Connect to Proxy Server */
208 	ret = net_context_connect(ctx, &proxy, proxy_len, NULL,
209 				  K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
210 				  NULL);
211 	if (ret < 0) {
212 		return ret;
213 	}
214 
215 	return socks5_tcp_connect(ctx, &proxy, proxy_len, addr, addrlen);
216 }
217