net: socks: Make SOCKS5 implementation transparent
Current SOCKS5 implementation is above socket level and every higher layer protocol or application level needs to have SOCKS5 related changes. This solution is based on socket setsockopt(). Application caller has to set proxy details through setsockopt() and socket:connect() will take care creating connection. Signed-off-by: Ravi kumar Veeramally <ravikumar.veeramally@linux.intel.com>
This commit is contained in:
parent
c8fa169294
commit
39ed77e438
3 changed files with 135 additions and 80 deletions
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Antmicro Ltd
|
* Copyright (c) 2019 Antmicro Ltd
|
||||||
*
|
*
|
||||||
|
* Copyright (c) 2019 Intel Corporation
|
||||||
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -11,23 +13,26 @@
|
||||||
|
|
||||||
/**@brief Connects to destination through a SOCKS5 proxy server.
|
/**@brief Connects to destination through a SOCKS5 proxy server.
|
||||||
*
|
*
|
||||||
* @param[in] proxy Address of the proxy server.
|
* @param[in] ctx Network context.
|
||||||
* @param[in] destination Address of the destination server.
|
* @param[in] dest Address of the destination server.
|
||||||
|
* @param[in] dest_len Address length of the destination server.
|
||||||
*
|
*
|
||||||
* @retval File descriptor of the opened connection or an error code if it was
|
* @retval 0 or an error code if it was unsuccessful.
|
||||||
* unsuccessful.
|
|
||||||
*/
|
*/
|
||||||
#if defined(CONFIG_SOCKS)
|
#if defined(CONFIG_SOCKS)
|
||||||
int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
int net_socks5_connect(struct net_context *ctx,
|
||||||
const struct sockaddr *destination);
|
const struct sockaddr *dest,
|
||||||
|
socklen_t dest_len);
|
||||||
#else
|
#else
|
||||||
inline int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
inline int net_socks5_connect(struct net_context *ctx,
|
||||||
const struct sockaddr *destination)
|
const struct sockaddr *dest,
|
||||||
|
socklen_t dest_len)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(proxy);
|
ARG_UNUSED(ctx);
|
||||||
ARG_UNUSED(destination);
|
ARG_UNUSED(dest);
|
||||||
|
ARG_UNUSED(dest_len);
|
||||||
|
|
||||||
return 0;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ LOG_MODULE_REGISTER(net_sock, CONFIG_NET_SOCKETS_LOG_LEVEL);
|
||||||
#include <syscall_handler.h>
|
#include <syscall_handler.h>
|
||||||
#include <sys/fdtable.h>
|
#include <sys/fdtable.h>
|
||||||
#include <sys/math_extras.h>
|
#include <sys/math_extras.h>
|
||||||
|
#include <net/socks.h>
|
||||||
|
|
||||||
#include "sockets_internal.h"
|
#include "sockets_internal.h"
|
||||||
|
|
||||||
|
@ -318,6 +319,14 @@ Z_SYSCALL_HANDLER(zsock_bind, sock, addr, addrlen)
|
||||||
int zsock_connect_ctx(struct net_context *ctx, const struct sockaddr *addr,
|
int zsock_connect_ctx(struct net_context *ctx, const struct sockaddr *addr,
|
||||||
socklen_t addrlen)
|
socklen_t addrlen)
|
||||||
{
|
{
|
||||||
|
#if defined(CONFIG_SOCKS)
|
||||||
|
if (net_context_is_proxy_enabled(ctx)) {
|
||||||
|
SET_ERRNO(net_socks5_connect(ctx, addr, addrlen));
|
||||||
|
SET_ERRNO(net_context_recv(ctx, zsock_received_cb,
|
||||||
|
K_NO_WAIT, ctx->user_data));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
SET_ERRNO(net_context_connect(ctx, addr, addrlen, NULL,
|
SET_ERRNO(net_context_connect(ctx, addr, addrlen, NULL,
|
||||||
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
|
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
|
||||||
NULL));
|
NULL));
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2019 Antmicro Ltd
|
* Copyright (c) 2019 Antmicro Ltd
|
||||||
*
|
*
|
||||||
|
* Copyright (c) 2019 Intel Corporation
|
||||||
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -10,99 +12,108 @@ LOG_MODULE_REGISTER(net_socks, CONFIG_SOCKS_LOG_LEVEL);
|
||||||
#include <zephyr.h>
|
#include <zephyr.h>
|
||||||
#include <net/socket.h>
|
#include <net/socket.h>
|
||||||
#include <net/socks.h>
|
#include <net/socks.h>
|
||||||
|
#include <net/net_pkt.h>
|
||||||
|
|
||||||
#include "socks_internal.h"
|
#include "socks_internal.h"
|
||||||
|
|
||||||
static int socks5_tcp_send(int fd, u8_t *data, u32_t len)
|
static void 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)
|
||||||
{
|
{
|
||||||
u32_t offset = 0U;
|
struct socks5_method_response *method_rsp =
|
||||||
int ret;
|
(struct socks5_method_response *)user_data;
|
||||||
|
|
||||||
while (offset < len) {
|
if (!pkt || status) {
|
||||||
ret = send(fd, data + offset, len - offset, 0);
|
memset(method_rsp, 0, sizeof(struct socks5_method_response));
|
||||||
if (ret < 0) {
|
goto end;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (net_pkt_read(pkt, (u8_t *)method_rsp,
|
||||||
}
|
sizeof(struct socks5_method_response))) {
|
||||||
|
memset(method_rsp, 0, sizeof(struct socks5_method_response));
|
||||||
static int socks5_tcp_recv(int fd, u8_t *data, u32_t len)
|
|
||||||
{
|
|
||||||
u32_t offset = 0U;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
while (offset < len) {
|
|
||||||
ret = recv(fd, data + offset, len - offset, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
end:
|
||||||
|
net_pkt_unref(pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
static void socks5_cmd_rsp_cb(struct net_context *ctx,
|
||||||
const struct sockaddr *destination)
|
struct net_pkt *pkt,
|
||||||
|
union net_ip_header *ip_hdr,
|
||||||
|
union net_proto_header *proto_hdr,
|
||||||
|
int status,
|
||||||
|
void *user_data)
|
||||||
{
|
{
|
||||||
struct socks5_method_request mthd_req;
|
struct socks5_command_response *cmd_rsp =
|
||||||
struct socks5_method_response mthd_rep;
|
(struct socks5_command_response *)user_data;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
if (!pkt || status) {
|
||||||
|
memset(cmd_rsp, 0,
|
||||||
|
sizeof(struct socks5_command_request_common));
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = sizeof(struct socks5_command_request_common);
|
||||||
|
|
||||||
|
if (net_pkt_read(pkt, (u8_t *)cmd_rsp, size)) {
|
||||||
|
memset(cmd_rsp, 0,
|
||||||
|
sizeof(struct socks5_command_request_common));
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
net_pkt_unref(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int socks5_tcp_connect(struct net_context *ctx,
|
||||||
|
const struct sockaddr *proxy,
|
||||||
|
socklen_t proxy_len,
|
||||||
|
const struct sockaddr *dest,
|
||||||
|
socklen_t dest_len)
|
||||||
|
{
|
||||||
|
struct socks5_method_request method_req;
|
||||||
|
struct socks5_method_response method_rsp;
|
||||||
struct socks5_command_request cmd_req;
|
struct socks5_command_request cmd_req;
|
||||||
struct socks5_command_response cmd_rep;
|
struct socks5_command_response cmd_rsp;
|
||||||
int size;
|
int size;
|
||||||
int ret;
|
int ret;
|
||||||
int fd;
|
|
||||||
|
|
||||||
fd = socket(proxy->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
if (fd < 0) {
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = connect(fd, proxy, sizeof(struct sockaddr_in));
|
|
||||||
if (ret < 0) {
|
|
||||||
LOG_ERR("Unable to connect to the proxy server");
|
|
||||||
(void)close(fd);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Negotiate authentication method */
|
/* Negotiate authentication method */
|
||||||
mthd_req.r.ver = SOCKS5_PKT_MAGIC;
|
method_req.r.ver = SOCKS5_PKT_MAGIC;
|
||||||
|
|
||||||
/* We only support NOAUTH at the moment */
|
/* We only support NOAUTH at the moment */
|
||||||
mthd_req.r.nmethods = 1U;
|
method_req.r.nmethods = 1U;
|
||||||
mthd_req.methods[0] = SOCKS5_AUTH_METHOD_NOAUTH;
|
method_req.methods[0] = SOCKS5_AUTH_METHOD_NOAUTH;
|
||||||
|
|
||||||
/* size + 1 because just one method is supported */
|
/* size + 1 because just one method is supported */
|
||||||
size = sizeof(struct socks5_method_request_common) + 1;
|
size = sizeof(struct socks5_method_request_common) + 1;
|
||||||
|
|
||||||
ret = socks5_tcp_send(fd, (u8_t *)&mthd_req, size);
|
ret = net_context_sendto(ctx, (u8_t *)&method_req, size,
|
||||||
|
proxy, proxy_len, NULL, K_NO_WAIT,
|
||||||
|
ctx->user_data);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
(void)close(fd);
|
|
||||||
LOG_ERR("Could not send negotiation packet");
|
LOG_ERR("Could not send negotiation packet");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = socks5_tcp_recv(fd, (u8_t *)&mthd_rep, sizeof(mthd_rep));
|
ret = net_context_recv(ctx, socks5_method_rsp_cb,
|
||||||
|
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
|
||||||
|
&method_rsp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("Could not receive negotiation response");
|
LOG_ERR("Could not receive negotiation response");
|
||||||
(void)close(fd);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mthd_rep.ver != SOCKS5_PKT_MAGIC) {
|
if (method_rsp.ver != SOCKS5_PKT_MAGIC) {
|
||||||
LOG_ERR("Invalid negotiation response magic");
|
LOG_ERR("Invalid negotiation response magic");
|
||||||
(void)close(fd);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mthd_rep.method != SOCKS5_AUTH_METHOD_NOAUTH) {
|
if (method_rsp.method != SOCKS5_AUTH_METHOD_NOAUTH) {
|
||||||
LOG_ERR("Invalid negotiation response");
|
LOG_ERR("Invalid negotiation response");
|
||||||
(void)close(fd);
|
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +124,7 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
||||||
|
|
||||||
if (proxy->sa_family == AF_INET) {
|
if (proxy->sa_family == AF_INET) {
|
||||||
const struct sockaddr_in *d4 =
|
const struct sockaddr_in *d4 =
|
||||||
(struct sockaddr_in *)destination;
|
(struct sockaddr_in *)dest;
|
||||||
|
|
||||||
cmd_req.r.atyp = SOCKS5_ATYP_IPV4;
|
cmd_req.r.atyp = SOCKS5_ATYP_IPV4;
|
||||||
|
|
||||||
|
@ -127,7 +138,7 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
||||||
+ sizeof(struct socks5_ipv4_addr);
|
+ sizeof(struct socks5_ipv4_addr);
|
||||||
} else if (proxy->sa_family == AF_INET6) {
|
} else if (proxy->sa_family == AF_INET6) {
|
||||||
const struct sockaddr_in6 *d6 =
|
const struct sockaddr_in6 *d6 =
|
||||||
(struct sockaddr_in6 *)destination;
|
(struct sockaddr_in6 *)dest;
|
||||||
|
|
||||||
cmd_req.r.atyp = SOCKS5_ATYP_IPV6;
|
cmd_req.r.atyp = SOCKS5_ATYP_IPV6;
|
||||||
|
|
||||||
|
@ -135,35 +146,35 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
||||||
(u8_t *)&d6->sin6_addr,
|
(u8_t *)&d6->sin6_addr,
|
||||||
sizeof(cmd_req.ipv6_addr.addr));
|
sizeof(cmd_req.ipv6_addr.addr));
|
||||||
|
|
||||||
cmd_req.ipv4_addr.port = d6->sin6_port;
|
cmd_req.ipv6_addr.port = d6->sin6_port;
|
||||||
|
|
||||||
size = sizeof(struct socks5_command_request_common)
|
size = sizeof(struct socks5_command_request_common)
|
||||||
+ sizeof(struct socks5_ipv6_addr);
|
+ sizeof(struct socks5_ipv6_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = socks5_tcp_send(fd, (u8_t *)&cmd_req, size);
|
ret = net_context_sendto(ctx, (u8_t *)&cmd_req, size,
|
||||||
|
proxy, proxy_len, NULL, K_NO_WAIT,
|
||||||
|
ctx->user_data);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("Could not send CONNECT command");
|
LOG_ERR("Could not send CONNECT command");
|
||||||
(void)close(fd);
|
return ret;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = socks5_tcp_recv(fd, (u8_t *)&cmd_rep, size);
|
ret = net_context_recv(ctx, socks5_cmd_rsp_cb,
|
||||||
|
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
|
||||||
|
&cmd_rsp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOG_ERR("Could not receive CONNECT response");
|
LOG_ERR("Could not receive CONNECT response");
|
||||||
(void)close(fd);
|
return ret;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd_rep.r.ver != SOCKS5_PKT_MAGIC) {
|
if (cmd_rsp.r.ver != SOCKS5_PKT_MAGIC) {
|
||||||
LOG_ERR("Invalid CONNECT response");
|
LOG_ERR("Invalid CONNECT response");
|
||||||
(void)close(fd);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd_rep.r.rep != SOCKS5_CMD_RESP_SUCCESS) {
|
if (cmd_rsp.r.rep != SOCKS5_CMD_RESP_SUCCESS) {
|
||||||
LOG_ERR("Unable to connect to destination");
|
LOG_ERR("Unable to connect to destination");
|
||||||
(void)close(fd);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,5 +182,35 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
||||||
|
|
||||||
LOG_DBG("Connection through SOCKS5 proxy successful");
|
LOG_DBG("Connection through SOCKS5 proxy successful");
|
||||||
|
|
||||||
return fd;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int net_socks5_connect(struct net_context *ctx, const struct sockaddr *addr,
|
||||||
|
socklen_t addrlen)
|
||||||
|
{
|
||||||
|
struct sockaddr proxy;
|
||||||
|
socklen_t proxy_len;
|
||||||
|
int type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
type = net_context_get_type(ctx);
|
||||||
|
/* TODO: Only TCP and TLS supported, UDP and DTLS yet to support. */
|
||||||
|
if (type != SOCK_STREAM) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = net_context_get_option(ctx, NET_OPT_SOCKS5, &proxy, &proxy_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connect to Proxy Server */
|
||||||
|
ret = net_context_connect(ctx, &proxy, proxy_len, NULL,
|
||||||
|
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
|
||||||
|
NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return socks5_tcp_connect(ctx, &proxy, proxy_len, addr, addrlen);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue