blob: bdff9946352d66cf567ea9074118d159cffa02a3 [file] [log] [blame]
/*
* 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;
}