176 lines
3.7 KiB
C
176 lines
3.7 KiB
C
/*
|
|
* Copyright (c) 2019 Antmicro Ltd
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(net_socks, CONFIG_SOCKS_LOG_LEVEL);
|
|
|
|
#include <zephyr.h>
|
|
#include <net/socket.h>
|
|
#include <net/socks.h>
|
|
|
|
#include "socks_internal.h"
|
|
|
|
static int socks5_tcp_send(int fd, u8_t *data, u32_t len)
|
|
{
|
|
u32_t offset = 0U;
|
|
int ret;
|
|
|
|
while (offset < len) {
|
|
ret = send(fd, data + offset, len - offset, 0);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
offset += ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int socks5_client_tcp_connect(const struct sockaddr *proxy,
|
|
const struct sockaddr *destination)
|
|
{
|
|
struct socks5_method_request mthd_req;
|
|
struct socks5_method_response mthd_rep;
|
|
struct socks5_command_request cmd_req;
|
|
struct socks5_command_response cmd_rep;
|
|
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;
|
|
|
|
/* We only support NOAUTH at the moment */
|
|
mthd_req.r.nmethods = 1U;
|
|
mthd_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);
|
|
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));
|
|
if (ret < 0) {
|
|
LOG_ERR("Could not receive negotiation response");
|
|
(void)close(fd);
|
|
return ret;
|
|
}
|
|
|
|
if (mthd_rep.ver != SOCKS5_PKT_MAGIC) {
|
|
LOG_ERR("Invalid negotiation response magic");
|
|
(void)close(fd);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (mthd_rep.method != SOCKS5_AUTH_METHOD_NOAUTH) {
|
|
LOG_ERR("Invalid negotiation response");
|
|
(void)close(fd);
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* Negotiation complete - now connect to destination */
|
|
cmd_req.r.ver = SOCKS5_PKT_MAGIC;
|
|
cmd_req.r.cmd = SOCKS5_CMD_CONNECT;
|
|
cmd_req.r.rsv = SOCKS5_PKT_RSV;
|
|
|
|
if (proxy->sa_family == AF_INET) {
|
|
const struct sockaddr_in *d4 =
|
|
(struct sockaddr_in *)destination;
|
|
|
|
cmd_req.r.atyp = SOCKS5_ATYP_IPV4;
|
|
|
|
memcpy(&cmd_req.ipv4_addr.addr,
|
|
(u8_t *)&d4->sin_addr,
|
|
sizeof(cmd_req.ipv4_addr.addr));
|
|
|
|
cmd_req.ipv4_addr.port = d4->sin_port;
|
|
|
|
size = sizeof(struct socks5_command_request_common)
|
|
+ sizeof(struct socks5_ipv4_addr);
|
|
} else if (proxy->sa_family == AF_INET6) {
|
|
const struct sockaddr_in6 *d6 =
|
|
(struct sockaddr_in6 *)destination;
|
|
|
|
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;
|
|
|
|
size = sizeof(struct socks5_command_request_common)
|
|
+ sizeof(struct socks5_ipv6_addr);
|
|
}
|
|
|
|
ret = socks5_tcp_send(fd, (u8_t *)&cmd_req, size);
|
|
if (ret < 0) {
|
|
LOG_ERR("Could not send CONNECT command");
|
|
(void)close(fd);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = socks5_tcp_recv(fd, (u8_t *)&cmd_rep, size);
|
|
if (ret < 0) {
|
|
LOG_ERR("Could not receive CONNECT response");
|
|
(void)close(fd);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cmd_rep.r.ver != SOCKS5_PKT_MAGIC) {
|
|
LOG_ERR("Invalid CONNECT response");
|
|
(void)close(fd);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cmd_rep.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;
|
|
}
|