| /* |
| * Copyright (c) 2019 Antmicro Ltd |
| * |
| * Copyright (c) 2019 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_socks, CONFIG_SOCKS_LOG_LEVEL); |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/net/socket.h> |
| #include <zephyr/net/net_pkt.h> |
| |
| #include "socks.h" |
| #include "socks_internal.h" |
| |
| 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) |
| { |
| struct socks5_method_response *method_rsp = |
| (struct socks5_method_response *)user_data; |
| |
| if (!pkt || status) { |
| memset(method_rsp, 0, sizeof(struct socks5_method_response)); |
| goto end; |
| } |
| |
| if (net_pkt_read(pkt, (uint8_t *)method_rsp, |
| sizeof(struct socks5_method_response))) { |
| memset(method_rsp, 0, sizeof(struct socks5_method_response)); |
| } |
| |
| end: |
| net_pkt_unref(pkt); |
| } |
| |
| 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) |
| { |
| 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; |
| } |
| |
| size = sizeof(struct socks5_command_request_common); |
| |
| if (net_pkt_read(pkt, (uint8_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_response cmd_rsp; |
| int size; |
| int ret; |
| |
| /* Negotiate authentication method */ |
| method_req.r.ver = SOCKS5_PKT_MAGIC; |
| |
| /* We only support NOAUTH at the moment */ |
| 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 = net_context_sendto(ctx, (uint8_t *)&method_req, size, |
| proxy, proxy_len, NULL, K_NO_WAIT, |
| ctx->user_data); |
| if (ret < 0) { |
| LOG_ERR("Could not send negotiation packet"); |
| return ret; |
| } |
| |
| 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"); |
| return ret; |
| } |
| |
| if (method_rsp.ver != SOCKS5_PKT_MAGIC) { |
| LOG_ERR("Invalid negotiation response magic"); |
| return -EINVAL; |
| } |
| |
| if (method_rsp.method != SOCKS5_AUTH_METHOD_NOAUTH) { |
| LOG_ERR("Invalid negotiation response"); |
| 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 *)dest; |
| |
| cmd_req.r.atyp = SOCKS5_ATYP_IPV4; |
| |
| memcpy(&cmd_req.ipv4_addr.addr, |
| (uint8_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 *)dest; |
| |
| cmd_req.r.atyp = SOCKS5_ATYP_IPV6; |
| |
| memcpy(&cmd_req.ipv6_addr.addr, |
| (uint8_t *)&d6->sin6_addr, |
| sizeof(cmd_req.ipv6_addr.addr)); |
| |
| cmd_req.ipv6_addr.port = d6->sin6_port; |
| |
| size = sizeof(struct socks5_command_request_common) |
| + sizeof(struct socks5_ipv6_addr); |
| } |
| |
| ret = net_context_sendto(ctx, (uint8_t *)&cmd_req, size, |
| proxy, proxy_len, NULL, K_NO_WAIT, |
| ctx->user_data); |
| if (ret < 0) { |
| LOG_ERR("Could not send CONNECT command"); |
| return ret; |
| } |
| |
| 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"); |
| return ret; |
| } |
| |
| if (cmd_rsp.r.ver != SOCKS5_PKT_MAGIC) { |
| LOG_ERR("Invalid CONNECT response"); |
| return -EINVAL; |
| } |
| |
| if (cmd_rsp.r.rep != SOCKS5_CMD_RESP_SUCCESS) { |
| LOG_ERR("Unable to connect to destination"); |
| return -EINVAL; |
| } |
| |
| /* Verifying the rest is not required */ |
| |
| LOG_DBG("Connection through SOCKS5 proxy successful"); |
| |
| 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); |
| } |