blob: 108ef5424c9ac4adc634d31e61b719300244977f [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "wpi/UDPClient.h"
6
7#ifdef _WIN32
8#include <WinSock2.h>
9#include <Ws2tcpip.h>
10#pragma comment(lib, "Ws2_32.lib")
11#else
12#include <arpa/inet.h>
13#include <fcntl.h>
14#include <netinet/in.h>
15#include <unistd.h>
16#endif
17
18#include "wpi/Logger.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070019#include "wpi/SmallString.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080020#include "wpi/SocketError.h"
21
22using namespace wpi;
23
24UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
25
Austin Schuh812d0d12021-11-04 20:16:48 -070026UDPClient::UDPClient(std::string_view address, Logger& logger)
27 : m_lsd(0), m_port(0), m_address(address), m_logger(logger) {}
Brian Silverman8fce7482020-01-05 13:18:21 -080028
29UDPClient::UDPClient(UDPClient&& other)
30 : m_lsd(other.m_lsd),
31 m_port(other.m_port),
32 m_address(std::move(other.m_address)),
33 m_logger(other.m_logger) {
34 other.m_lsd = 0;
35 other.m_port = 0;
36}
37
38UDPClient::~UDPClient() {
39 if (m_lsd > 0) {
40 shutdown();
41 }
42}
43
44UDPClient& UDPClient::operator=(UDPClient&& other) {
Austin Schuh812d0d12021-11-04 20:16:48 -070045 if (this == &other) {
46 return *this;
47 }
Brian Silverman8fce7482020-01-05 13:18:21 -080048 shutdown();
49 m_logger = other.m_logger;
50 m_lsd = other.m_lsd;
51 m_address = std::move(other.m_address);
52 m_port = other.m_port;
53 other.m_lsd = 0;
54 other.m_port = 0;
55 return *this;
56}
57
Austin Schuh812d0d12021-11-04 20:16:48 -070058int UDPClient::start() {
59 return start(0);
60}
Brian Silverman8fce7482020-01-05 13:18:21 -080061
62int UDPClient::start(int port) {
Austin Schuh812d0d12021-11-04 20:16:48 -070063 if (m_lsd > 0) {
64 return 0;
65 }
Brian Silverman8fce7482020-01-05 13:18:21 -080066
67#ifdef _WIN32
68 WSAData wsaData;
69 WORD wVersionRequested = MAKEWORD(2, 2);
70 WSAStartup(wVersionRequested, &wsaData);
71#endif
72
73 m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
74
75 if (m_lsd < 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -070076 WPI_ERROR(m_logger, "{}", "could not create socket");
Brian Silverman8fce7482020-01-05 13:18:21 -080077 return -1;
78 }
79
80 struct sockaddr_in addr;
81 std::memset(&addr, 0, sizeof(addr));
82 addr.sin_family = AF_INET;
83 if (m_address.size() > 0) {
84#ifdef _WIN32
85 SmallString<128> addr_copy(m_address);
86 addr_copy.push_back('\0');
87 int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
88#else
89 int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
90#endif
91 if (res != 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -070092 WPI_ERROR(m_logger, "could not resolve {} address", m_address);
Brian Silverman8fce7482020-01-05 13:18:21 -080093 return -1;
94 }
95 } else {
96 addr.sin_addr.s_addr = INADDR_ANY;
97 }
98 addr.sin_port = htons(port);
99
100 if (port != 0) {
101#ifdef _WIN32
102 int optval = 1;
103 setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
104 reinterpret_cast<char*>(&optval), sizeof optval);
105#else
106 int optval = 1;
107 setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR,
108 reinterpret_cast<char*>(&optval), sizeof optval);
109#endif
110 }
111
112 int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
113 if (result != 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700114 WPI_ERROR(m_logger, "bind() failed: {}", SocketStrerror());
Brian Silverman8fce7482020-01-05 13:18:21 -0800115 return result;
116 }
117 m_port = port;
118 return 0;
119}
120
121void UDPClient::shutdown() {
122 if (m_lsd > 0) {
123#ifdef _WIN32
124 ::shutdown(m_lsd, SD_BOTH);
125 closesocket(m_lsd);
126 WSACleanup();
127#else
128 ::shutdown(m_lsd, SHUT_RDWR);
129 close(m_lsd);
130#endif
131 m_lsd = 0;
132 m_port = 0;
133 }
134}
135
Austin Schuh812d0d12021-11-04 20:16:48 -0700136int UDPClient::send(span<const uint8_t> data, std::string_view server,
137 int port) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800138 // server must be a resolvable IP address
139 struct sockaddr_in addr;
140 std::memset(&addr, 0, sizeof(addr));
141 addr.sin_family = AF_INET;
Austin Schuh812d0d12021-11-04 20:16:48 -0700142 SmallString<128> remoteAddr{server};
Brian Silverman8fce7482020-01-05 13:18:21 -0800143 if (remoteAddr.empty()) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 WPI_ERROR(m_logger, "{}", "server must be passed");
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 return -1;
146 }
147
148#ifdef _WIN32
Austin Schuh812d0d12021-11-04 20:16:48 -0700149 int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
Brian Silverman8fce7482020-01-05 13:18:21 -0800150#else
Austin Schuh812d0d12021-11-04 20:16:48 -0700151 int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
Brian Silverman8fce7482020-01-05 13:18:21 -0800152#endif
153 if (res != 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700154 WPI_ERROR(m_logger, "could not resolve {} address", server);
Brian Silverman8fce7482020-01-05 13:18:21 -0800155 return -1;
156 }
157 addr.sin_port = htons(port);
158
159 // sendto should not block
160 int result =
161 sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
162 reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
163 return result;
164}
165
Austin Schuh812d0d12021-11-04 20:16:48 -0700166int UDPClient::send(std::string_view data, std::string_view server, int port) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800167 // server must be a resolvable IP address
168 struct sockaddr_in addr;
169 std::memset(&addr, 0, sizeof(addr));
170 addr.sin_family = AF_INET;
Austin Schuh812d0d12021-11-04 20:16:48 -0700171 SmallString<128> remoteAddr{server};
Brian Silverman8fce7482020-01-05 13:18:21 -0800172 if (remoteAddr.empty()) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700173 WPI_ERROR(m_logger, "{}", "server must be passed");
Brian Silverman8fce7482020-01-05 13:18:21 -0800174 return -1;
175 }
176
177#ifdef _WIN32
Austin Schuh812d0d12021-11-04 20:16:48 -0700178 int res = InetPton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
Brian Silverman8fce7482020-01-05 13:18:21 -0800179#else
Austin Schuh812d0d12021-11-04 20:16:48 -0700180 int res = inet_pton(AF_INET, remoteAddr.c_str(), &(addr.sin_addr));
Brian Silverman8fce7482020-01-05 13:18:21 -0800181#endif
182 if (res != 1) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700183 WPI_ERROR(m_logger, "could not resolve {} address", server);
Brian Silverman8fce7482020-01-05 13:18:21 -0800184 return -1;
185 }
186 addr.sin_port = htons(port);
187
188 // sendto should not block
189 int result = sendto(m_lsd, data.data(), data.size(), 0,
190 reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
191 return result;
192}
193
194int UDPClient::receive(uint8_t* data_received, int receive_len) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700195 if (m_port == 0) {
196 return -1; // return if not receiving
197 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800198 return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
199}
200
201int UDPClient::receive(uint8_t* data_received, int receive_len,
202 SmallVectorImpl<char>* addr_received,
203 int* port_received) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700204 if (m_port == 0) {
205 return -1; // return if not receiving
206 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800207
208 struct sockaddr_in remote;
209 socklen_t remote_len = sizeof(remote);
210 std::memset(&remote, 0, sizeof(remote));
211
212 int result =
213 recvfrom(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0,
214 reinterpret_cast<sockaddr*>(&remote), &remote_len);
215
216 char ip[50];
217#ifdef _WIN32
218 InetNtop(PF_INET, &(remote.sin_addr.s_addr), ip, sizeof(ip) - 1);
219#else
220 inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(remote.sin_addr.s_addr)), ip,
221 sizeof(ip) - 1);
222#endif
223
224 ip[49] = '\0';
225 int addr_len = std::strlen(ip);
226 addr_received->clear();
227 addr_received->append(&ip[0], &ip[addr_len]);
228
229 *port_received = ntohs(remote.sin_port);
230
231 return result;
232}
233
234int UDPClient::set_timeout(double timeout) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700235 if (timeout < 0) {
236 return -1;
237 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800238 struct timeval tv;
239 tv.tv_sec = timeout; // truncating will give seconds
240 timeout -= tv.tv_sec; // remove seconds portion
241 tv.tv_usec = timeout * 1000000; // fractions of a second to us
242 int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
243 reinterpret_cast<char*>(&tv), sizeof(tv));
Austin Schuh812d0d12021-11-04 20:16:48 -0700244 if (ret < 0) {
245 WPI_ERROR(m_logger, "{}", "set timeout failed");
246 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800247 return ret;
248}