blob: aafdf477d25e74fefaa077c2b4e1abe122248b36 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "wpi/UDPClient.h"
9
10#ifdef _WIN32
11#include <WinSock2.h>
12#include <Ws2tcpip.h>
13#pragma comment(lib, "Ws2_32.lib")
14#else
15#include <arpa/inet.h>
16#include <fcntl.h>
17#include <netinet/in.h>
18#include <unistd.h>
19#endif
20
21#include "wpi/Logger.h"
22#include "wpi/SocketError.h"
23
24using namespace wpi;
25
26UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
27
28UDPClient::UDPClient(const Twine& address, Logger& logger)
29 : m_lsd(0), m_port(0), m_address(address.str()), m_logger(logger) {}
30
31UDPClient::UDPClient(UDPClient&& other)
32 : m_lsd(other.m_lsd),
33 m_port(other.m_port),
34 m_address(std::move(other.m_address)),
35 m_logger(other.m_logger) {
36 other.m_lsd = 0;
37 other.m_port = 0;
38}
39
40UDPClient::~UDPClient() {
41 if (m_lsd > 0) {
42 shutdown();
43 }
44}
45
46UDPClient& UDPClient::operator=(UDPClient&& other) {
47 if (this == &other) return *this;
48 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
58int UDPClient::start() { return start(0); }
59
60int UDPClient::start(int port) {
61 if (m_lsd > 0) return 0;
62
63#ifdef _WIN32
64 WSAData wsaData;
65 WORD wVersionRequested = MAKEWORD(2, 2);
66 WSAStartup(wVersionRequested, &wsaData);
67#endif
68
69 m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
70
71 if (m_lsd < 0) {
72 WPI_ERROR(m_logger, "could not create socket");
73 return -1;
74 }
75
76 struct sockaddr_in addr;
77 std::memset(&addr, 0, sizeof(addr));
78 addr.sin_family = AF_INET;
79 if (m_address.size() > 0) {
80#ifdef _WIN32
81 SmallString<128> addr_copy(m_address);
82 addr_copy.push_back('\0');
83 int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
84#else
85 int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
86#endif
87 if (res != 1) {
88 WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
89 return -1;
90 }
91 } else {
92 addr.sin_addr.s_addr = INADDR_ANY;
93 }
94 addr.sin_port = htons(port);
95
96 if (port != 0) {
97#ifdef _WIN32
98 int optval = 1;
99 setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
100 reinterpret_cast<char*>(&optval), sizeof optval);
101#else
102 int optval = 1;
103 setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR,
104 reinterpret_cast<char*>(&optval), sizeof optval);
105#endif
106 }
107
108 int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
109 if (result != 0) {
110 WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror());
111 return result;
112 }
113 m_port = port;
114 return 0;
115}
116
117void UDPClient::shutdown() {
118 if (m_lsd > 0) {
119#ifdef _WIN32
120 ::shutdown(m_lsd, SD_BOTH);
121 closesocket(m_lsd);
122 WSACleanup();
123#else
124 ::shutdown(m_lsd, SHUT_RDWR);
125 close(m_lsd);
126#endif
127 m_lsd = 0;
128 m_port = 0;
129 }
130}
131
132int UDPClient::send(ArrayRef<uint8_t> data, const Twine& server, int port) {
133 // server must be a resolvable IP address
134 struct sockaddr_in addr;
135 std::memset(&addr, 0, sizeof(addr));
136 addr.sin_family = AF_INET;
137 SmallVector<char, 128> addr_store;
138 StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
139 if (remoteAddr.empty()) {
140 WPI_ERROR(m_logger, "server must be passed");
141 return -1;
142 }
143
144#ifdef _WIN32
145 int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
146#else
147 int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
148#endif
149 if (res != 1) {
150 WPI_ERROR(m_logger, "could not resolve " << server << " address");
151 return -1;
152 }
153 addr.sin_port = htons(port);
154
155 // sendto should not block
156 int result =
157 sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
158 reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
159 return result;
160}
161
162int UDPClient::send(StringRef data, const Twine& server, int port) {
163 // server must be a resolvable IP address
164 struct sockaddr_in addr;
165 std::memset(&addr, 0, sizeof(addr));
166 addr.sin_family = AF_INET;
167 SmallVector<char, 128> addr_store;
168 StringRef remoteAddr = server.toNullTerminatedStringRef(addr_store);
169 if (remoteAddr.empty()) {
170 WPI_ERROR(m_logger, "server must be passed");
171 return -1;
172 }
173
174#ifdef _WIN32
175 int res = InetPton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
176#else
177 int res = inet_pton(AF_INET, remoteAddr.data(), &(addr.sin_addr));
178#endif
179 if (res != 1) {
180 WPI_ERROR(m_logger, "could not resolve " << server << " address");
181 return -1;
182 }
183 addr.sin_port = htons(port);
184
185 // sendto should not block
186 int result = sendto(m_lsd, data.data(), data.size(), 0,
187 reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
188 return result;
189}
190
191int UDPClient::receive(uint8_t* data_received, int receive_len) {
192 if (m_port == 0) return -1; // return if not receiving
193 return recv(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0);
194}
195
196int UDPClient::receive(uint8_t* data_received, int receive_len,
197 SmallVectorImpl<char>* addr_received,
198 int* port_received) {
199 if (m_port == 0) return -1; // return if not receiving
200
201 struct sockaddr_in remote;
202 socklen_t remote_len = sizeof(remote);
203 std::memset(&remote, 0, sizeof(remote));
204
205 int result =
206 recvfrom(m_lsd, reinterpret_cast<char*>(data_received), receive_len, 0,
207 reinterpret_cast<sockaddr*>(&remote), &remote_len);
208
209 char ip[50];
210#ifdef _WIN32
211 InetNtop(PF_INET, &(remote.sin_addr.s_addr), ip, sizeof(ip) - 1);
212#else
213 inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(remote.sin_addr.s_addr)), ip,
214 sizeof(ip) - 1);
215#endif
216
217 ip[49] = '\0';
218 int addr_len = std::strlen(ip);
219 addr_received->clear();
220 addr_received->append(&ip[0], &ip[addr_len]);
221
222 *port_received = ntohs(remote.sin_port);
223
224 return result;
225}
226
227int UDPClient::set_timeout(double timeout) {
228 if (timeout < 0) return -1;
229 struct timeval tv;
230 tv.tv_sec = timeout; // truncating will give seconds
231 timeout -= tv.tv_sec; // remove seconds portion
232 tv.tv_usec = timeout * 1000000; // fractions of a second to us
233 int ret = setsockopt(m_lsd, SOL_SOCKET, SO_RCVTIMEO,
234 reinterpret_cast<char*>(&tv), sizeof(tv));
235 if (ret < 0) WPI_ERROR(m_logger, "set timeout failed");
236 return ret;
237}