Skip to content
Permalink
Browse files

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>
  • Loading branch information...
rveerama1 authored and jukkar committed Aug 1, 2019
1 parent c8fa169 commit 39ed77e4387eb00ce3b799d2e24d46cb1af222f1
Showing with 129 additions and 74 deletions.
  1. +16 −11 include/net/socks.h
  2. +9 −0 subsys/net/lib/sockets/sockets.c
  3. +104 −63 subsys/net/lib/socks/socks.c
@@ -1,6 +1,8 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

@@ -11,23 +13,26 @@

/**@brief Connects to destination through a SOCKS5 proxy server.
*
* @param[in] proxy Address of the proxy server.
* @param[in] destination Address of the destination server.
* @param[in] ctx Network context.
* @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
* unsuccessful.
* @retval 0 or an error code if it was unsuccessful.
*/
#if defined(CONFIG_SOCKS)
int socks5_client_tcp_connect(const struct sockaddr *proxy,
const struct sockaddr *destination);
int net_socks5_connect(struct net_context *ctx,
const struct sockaddr *dest,
socklen_t dest_len);
#else
inline int socks5_client_tcp_connect(const struct sockaddr *proxy,
const struct sockaddr *destination)
inline int net_socks5_connect(struct net_context *ctx,
const struct sockaddr *dest,
socklen_t dest_len)
{
ARG_UNUSED(proxy);
ARG_UNUSED(destination);
ARG_UNUSED(ctx);
ARG_UNUSED(dest);
ARG_UNUSED(dest_len);

return 0;
return -ENOTSUP;
}
#endif

@@ -18,6 +18,7 @@ LOG_MODULE_REGISTER(net_sock, CONFIG_NET_SOCKETS_LOG_LEVEL);
#include <syscall_handler.h>
#include <sys/fdtable.h>
#include <sys/math_extras.h>
#include <net/socks.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,
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,
K_MSEC(CONFIG_NET_SOCKETS_CONNECT_TIMEOUT),
NULL));
@@ -1,6 +1,8 @@
/*
* Copyright (c) 2019 Antmicro Ltd
*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

@@ -10,99 +12,108 @@ LOG_MODULE_REGISTER(net_socks, CONFIG_SOCKS_LOG_LEVEL);
#include <zephyr.h>
#include <net/socket.h>
#include <net/socks.h>
#include <net/net_pkt.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;
int ret;
struct socks5_method_response *method_rsp =
(struct socks5_method_response *)user_data;

while (offset < len) {
ret = send(fd, data + offset, len - offset, 0);
if (ret < 0) {
return ret;
}
if (!pkt || status) {
memset(method_rsp, 0, sizeof(struct socks5_method_response));
goto end;
}

offset += ret;
if (net_pkt_read(pkt, (u8_t *)method_rsp,
sizeof(struct socks5_method_response))) {
memset(method_rsp, 0, sizeof(struct socks5_method_response));
}

return 0;
end:
net_pkt_unref(pkt);
}

static int socks5_tcp_recv(int fd, u8_t *data, u32_t len)
static void 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)
{
u32_t offset = 0U;
int ret;
struct socks5_command_response *cmd_rsp =
(struct socks5_command_response *)user_data;
int size;

if (!pkt || status) {
memset(cmd_rsp, 0,
sizeof(struct socks5_command_request_common));
goto end;
}

while (offset < len) {
ret = recv(fd, data + offset, len - offset, 0);
if (ret < 0) {
return ret;
}
size = sizeof(struct socks5_command_request_common);

offset += ret;
if (net_pkt_read(pkt, (u8_t *)cmd_rsp, size)) {
memset(cmd_rsp, 0,
sizeof(struct socks5_command_request_common));
}

return 0;
end:
net_pkt_unref(pkt);
}

int socks5_client_tcp_connect(const struct sockaddr *proxy,
const struct sockaddr *destination)
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 mthd_req;
struct socks5_method_response mthd_rep;
struct socks5_method_request method_req;
struct socks5_method_response method_rsp;
struct socks5_command_request cmd_req;
struct socks5_command_response cmd_rep;
struct socks5_command_response cmd_rsp;
int size;
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 */
mthd_req.r.ver = SOCKS5_PKT_MAGIC;
method_req.r.ver = SOCKS5_PKT_MAGIC;

/* We only support NOAUTH at the moment */
mthd_req.r.nmethods = 1U;
mthd_req.methods[0] = SOCKS5_AUTH_METHOD_NOAUTH;
method_req.r.nmethods = 1U;
method_req.methods[0] = SOCKS5_AUTH_METHOD_NOAUTH;

/* size + 1 because just one method is supported */
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) {
(void)close(fd);
LOG_ERR("Could not send negotiation packet");
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) {
LOG_ERR("Could not receive negotiation response");
(void)close(fd);
return ret;
}

if (mthd_rep.ver != SOCKS5_PKT_MAGIC) {
if (method_rsp.ver != SOCKS5_PKT_MAGIC) {
LOG_ERR("Invalid negotiation response magic");
(void)close(fd);
return -EINVAL;
}

if (mthd_rep.method != SOCKS5_AUTH_METHOD_NOAUTH) {
if (method_rsp.method != SOCKS5_AUTH_METHOD_NOAUTH) {
LOG_ERR("Invalid negotiation response");
(void)close(fd);
return -ENOTSUP;
}

@@ -113,7 +124,7 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,

if (proxy->sa_family == AF_INET) {
const struct sockaddr_in *d4 =
(struct sockaddr_in *)destination;
(struct sockaddr_in *)dest;

cmd_req.r.atyp = SOCKS5_ATYP_IPV4;

@@ -127,49 +138,79 @@ int socks5_client_tcp_connect(const struct sockaddr *proxy,
+ sizeof(struct socks5_ipv4_addr);
} else if (proxy->sa_family == AF_INET6) {
const struct sockaddr_in6 *d6 =
(struct sockaddr_in6 *)destination;
(struct sockaddr_in6 *)dest;

cmd_req.r.atyp = SOCKS5_ATYP_IPV6;

memcpy(&cmd_req.ipv6_addr.addr,
(u8_t *)&d6->sin6_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)
+ 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) {
LOG_ERR("Could not send CONNECT command");
(void)close(fd);
return -EINVAL;
return ret;
}

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) {
LOG_ERR("Could not receive CONNECT response");
(void)close(fd);
return -EINVAL;
return ret;
}

if (cmd_rep.r.ver != SOCKS5_PKT_MAGIC) {
if (cmd_rsp.r.ver != SOCKS5_PKT_MAGIC) {
LOG_ERR("Invalid CONNECT response");
(void)close(fd);
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");
(void)close(fd);
return -EINVAL;
}

/* Verifying the rest is not required */

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);
}

0 comments on commit 39ed77e

Please sign in to comment.
You can’t perform that action at this time.