UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
net.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2016-2024 CESNET
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef _WIN32
39#include <winsock2.h>
40#include <iphlpapi.h>
41#include <tchar.h>
42#include <ws2tcpip.h>
43typedef SOCKET fd_t;
44#else
45#include <arpa/inet.h>
46#include <ifaddrs.h>
47#include <netdb.h>
48#include <netinet/ip.h>
49#include <sys/socket.h>
50#include <sys/types.h>
51#include <unistd.h>
52#define closesocket close
53#define INVALID_SOCKET (-1)
54typedef int fd_t;
55#endif
56
57#include <assert.h>
58#include <ctype.h>
59#include <errno.h>
60#include <limits.h>
61#include <stdint.h>
62#include <stdlib.h>
63#include <string.h>
64
65#include "utils/net.h"
66#include "utils/windows.h"
67
68#include "debug.h"
69
70#define IPV4_LL_PREFIX ((169u<<8u) | 254u)
71
72// MSW does not have the macro defined
73#ifndef IN_LOOPBACKNET
74#define IN_LOOPBACKNET 127
75#endif
76
77#define MOD_NAME "[utils/net] "
78
79bool is_addr_linklocal(struct sockaddr *sa)
80{
81 switch (sa->sa_family) {
82 case AF_INET:
83 {
84 struct sockaddr_in *sin = (struct sockaddr_in *)(void *) sa;
85 uint32_t addr = ntohl(sin->sin_addr.s_addr);
86 if ((addr >> 16u) == IPV4_LL_PREFIX) {
87 return true;
88 }
89 return false;
90 }
91 case AF_INET6:
92 {
93 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)(void *) sa;
94 if (IN6_IS_ADDR_V4MAPPED(&sin->sin6_addr)) {
95 uint32_t v4_addr = ntohl(*((uint32_t*)(void *)(sin->sin6_addr.s6_addr + 12)));
96 if ((v4_addr >> 16u) == IPV4_LL_PREFIX) {
97 return true;
98 }
99 return false;
100 }
101 return IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr);
102 }
103 default:
104 return false;
105 }
106}
107
108bool is_addr_loopback(struct sockaddr *sa)
109{
110 switch (sa->sa_family) {
111 case AF_UNIX:
112 return true;
113 case AF_INET:
114 {
115 struct sockaddr_in *sin = (struct sockaddr_in *)(void *) sa;
116 uint32_t addr = ntohl(sin->sin_addr.s_addr);
117 if ((addr >> 24u) == IN_LOOPBACKNET) {
118 return true;
119 }
120 return false;
121 }
122 case AF_INET6:
123 {
124 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)(void *) sa;
125 if (IN6_IS_ADDR_V4MAPPED(&sin->sin6_addr)) {
126 uint32_t v4_addr = ntohl(*((uint32_t*)(void *)(sin->sin6_addr.s6_addr + 12)));
127 if ((v4_addr >> 24u) == IN_LOOPBACKNET) {
128 return true;
129 }
130 return false;
131 }
132 return IN6_IS_ADDR_LOOPBACK(&sin->sin6_addr);
133 }
134 default:
135 return false;
136 }
137}
138
139bool is_addr_private(struct sockaddr *sa)
140{
141 switch (sa->sa_family) {
142 case AF_UNIX:
143 return true;
144 case AF_INET:
145 {
146 struct sockaddr_in *sin = (struct sockaddr_in *)(void *) sa;
147 uint32_t addr = ntohl(sin->sin_addr.s_addr);
148 return ((addr >> 24U) == 10) || ((addr >> 20U) == 2753) || ((addr >> 16U) == 49320);
149 }
150 case AF_INET6:
151 {
152 struct sockaddr_in6 *sin = (struct sockaddr_in6 *)(void *) sa;
153 return (sin->sin6_addr.s6_addr[0] & 0xFEU) == 0xFCU; // ULA
154 }
155 default:
156 return false;
157 }
158}
159
160static struct addrinfo *resolve_host(const char *hostname, const char *err_prefix)
161{
162 int gai_err = 0;
163 struct addrinfo *ai = NULL;
164 struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_socktype = SOCK_DGRAM };
165
166 if ((gai_err = getaddrinfo(hostname, NULL, &hints, &ai)) != 0) {
167 error_msg("%sgetaddrinfo: %s: %s\n", err_prefix, hostname,
168 ug_gai_strerror(gai_err));
169 return NULL;
170 }
171 return ai;
172}
173
174bool is_host_loopback(const char *hostname)
175{
176 struct addrinfo *ai = resolve_host(hostname, "is_host_loopback: ");
177 if (ai == NULL) {
178 return false;
179 }
180
181 bool ret = is_addr_loopback(ai->ai_addr);
182 freeaddrinfo(ai);
183
184 return ret;
185}
186
187bool is_host_private(const char *hostname)
188{
189 struct addrinfo *ai = resolve_host(hostname, "is_host_private: ");
190 if (ai == NULL) {
191 return false;
192 }
193
194 bool ret = is_addr_loopback(ai->ai_addr);
195 freeaddrinfo(ai);
196
197 return ret;
198}
199
200uint16_t socket_get_recv_port(int fd)
201{
202 struct sockaddr_storage ss;
203 socklen_t len = sizeof(ss);
204 if (getsockname(fd, (struct sockaddr *)&ss, &len) == -1) {
205 perror("getsockname");
206 return 0;
207 }
208 switch (ss.ss_family) {
209 case AF_INET:
210 return ntohs(((struct sockaddr_in *) &ss)->sin_port);
211 case AF_INET6:
212 return ntohs(((struct sockaddr_in6 *) &ss)->sin6_port);
213 default:
214 return 0;
215 }
216}
217
229bool get_local_addresses(struct sockaddr_storage *addrs, size_t *len, int ip_version)
230{
231 assert(ip_version == 0 || ip_version == 4 || ip_version == 6);
232#ifdef _WIN32
233#define WORKING_BUFFER_SIZE 15000
234#define MAX_TRIES 3
235 /* Declare and initialize variables */
236 DWORD dwRetVal = 0;
237 size_t len_remaining = *len;
238 *len = 0;
239
240 PIP_ADAPTER_ADDRESSES pAddresses = NULL;
241 ULONG outBufLen = 0;
242 ULONG Iterations = 0;
243
244 PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
245 PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
246
247 // Allocate a 15 KB buffer to start with.
248 outBufLen = WORKING_BUFFER_SIZE;
249
250 do {
251 pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen);
252 if (pAddresses == NULL) {
253 printf
254 ("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
255 return false;
256 }
257
258 ULONG family = ip_version == 4 ? AF_INET
259 : ip_version == 6 ? AF_INET6
260 : AF_UNSPEC; // both
261 dwRetVal =
262 GetAdaptersAddresses(family, 0, NULL, pAddresses, &outBufLen);
263
264 if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
265 free(pAddresses);
266 pAddresses = NULL;
267 } else {
268 break;
269 }
270 Iterations++;
271 } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
272
273 if (dwRetVal == NO_ERROR) {
274 // If successful, output some information from the data we received
275 pCurrAddresses = pAddresses;
276 while (pCurrAddresses) {
277
278 pUnicast = pCurrAddresses->FirstUnicastAddress;
279 while (pUnicast != NULL) {
280 if (len_remaining < sizeof(addrs[0])) {
281 printf("Warning: insufficient space for all addresses.\n");
282 return true;
283 }
284 size_t sa_len = pUnicast->Address.lpSockaddr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
285 memcpy(addrs, pUnicast->Address.lpSockaddr, sa_len);
286 addrs += 1;
287 *len += sizeof(addrs[0]);
288 len_remaining -= sizeof(addrs[0]);
289 pUnicast = pUnicast->Next;
290 }
291
292 pCurrAddresses = pCurrAddresses->Next;
293 }
294 } else {
295 printf("Call to GetAdaptersAddresses failed with error: %ld\n",
296 dwRetVal);
297 if (dwRetVal == ERROR_NO_DATA)
298 printf("\tNo addresses were found for the requested parameters\n");
299 else {
300 log_msg(LOG_LEVEL_ERROR, "Error: %s\n",
301 get_win32_error(dwRetVal));
302 if (pAddresses)
303 free(pAddresses);
304 return false;
305 }
306 }
307
308 if (pAddresses) {
309 free(pAddresses);
310 }
311
312 return true;
313#else // ! defined _WIN32
314 size_t available_len = *len;
315 *len = 0;
316 struct ifaddrs* a = NULL;
317 getifaddrs(&a);
318 struct ifaddrs* p = a;
319 while (NULL != p) {
320 if (p->ifa_addr == NULL ||
321 (ip_version == 0 && (p->ifa_addr->sa_family != AF_INET &&
322 p->ifa_addr->sa_family != AF_INET6)) ||
323 (ip_version == 4 && p->ifa_addr->sa_family != AF_INET) ||
324 (ip_version == 6 && p->ifa_addr->sa_family != AF_INET6)) {
325 p = p->ifa_next;
326 continue;
327 }
328 if (available_len < sizeof addrs[0]) {
329 MSG(WARNING,
330 "Warning: insufficient space for all addresses.\n");
331 return true;
332 }
333 size_t sa_len = p->ifa_addr->sa_family == AF_INET
334 ? sizeof(struct sockaddr_in)
335 : sizeof(struct sockaddr_in6);
336 memcpy(addrs, p->ifa_addr, sa_len);
337 addrs += 1;
338 *len += sizeof(addrs[0]);
339 available_len -= sizeof(addrs[0]);
340
341 p = p->ifa_next;
342 }
343 if (NULL != a) {
344 freeifaddrs(a);
345 }
346 return true;
347#endif
348}
349
353static bool is_addr4(const char *addr) {
354 // only basic check
355 while (*addr != '\0') {
356 if (!isdigit(*addr) && *addr != '.') {
357 return false;
358 }
359 addr++;
360 }
361 return true;
362}
363
368static bool is_addr6(const char *addr) {
369 while (*addr != '\0') {
370 if (*addr == '%') { // skip zone identification at the end
371 return true;
372 }
373 // only basic check
374 if (!isxdigit(*addr) && *addr != ':') {
375 return false;
376 }
377 addr++;
378 }
379 return true;
380}
381
382bool is_addr_multicast(const char *addr)
383{
384 if (is_addr4(addr)) {
385 unsigned int first_addr_byte = atoi(addr);
386 if ((first_addr_byte >> 4) == 0xe) {
387 return true;
388 }
389 return false;
390
391 }
392 if (is_addr6(addr)) {
393 if (strlen(addr) >= 2 && addr[0] == 'f' && addr[1] == 'f') {
394 return true;
395 }
396 return false;
397
398 }
399 return false;
400}
401
403{
404 fd_t fd = socket(AF_INET6, SOCK_DGRAM, 0);
405 if (fd == INVALID_SOCKET && errno == EAFNOSUPPORT) {
406 return false;
407 }
408 if (fd != INVALID_SOCKET) {
409 closesocket(fd);
410 }
411 return true;
412}
413
414unsigned get_sockaddr_addr_port(struct sockaddr *sa){
415 unsigned port = 0;
416 if (sa->sa_family == AF_INET6) {
417 port = ntohs(((struct sockaddr_in6 *)(void *) sa)->sin6_port);
418 } else if (sa->sa_family == AF_INET) {
419 port = ntohs(((struct sockaddr_in *)(void *) sa)->sin_port);
420 } else {
421 return UINT_MAX;
422 }
423
424 return port;
425}
426
427void get_sockaddr_addr_str(struct sockaddr *sa, char *buf, size_t n){
428 assert(n >= IN6_MAX_ASCII_LEN + 3 /* []: */ + 1 /* \0 */);
429 const void *src = NULL;
430 if (sa->sa_family == AF_INET6) {
431 snprintf(buf, n, "[");
432 src = &((struct sockaddr_in6 *)(void *) sa)->sin6_addr;
433 } else if (sa->sa_family == AF_INET) {
434 src = &((struct sockaddr_in *)(void *) sa)->sin_addr;
435 } else {
436 snprintf(buf, n, "(unknown)");
437 return;
438 }
439 if (inet_ntop(sa->sa_family, src, buf + strlen(buf), n - strlen(buf)) == NULL) {
440 perror("get_sockaddr_str");
441 snprintf(buf, n, "(error)");
442 return;
443 }
444
445 if (sa->sa_family == AF_INET6) {
446 snprintf(buf + strlen(buf), n - strlen(buf), "]");
447 }
448}
449
450const char *get_sockaddr_str(struct sockaddr *sa)
451{
452 enum { ADDR_LEN = IN6_MAX_ASCII_LEN + 3 /* []: */ + 5 /* port */ + 1 /* \0 */ };
453 _Thread_local static char addr[ADDR_LEN] = "";
454 addr[0] = '\0';
455
456 get_sockaddr_addr_str(sa, addr, sizeof(addr));
457
458 unsigned port = get_sockaddr_addr_port(sa);
459 if(port == UINT_MAX)
460 return addr;
461 snprintf(addr + strlen(addr), ADDR_LEN - strlen(addr), ":%u", port);
462
463 return addr;
464}
465
466const char *ug_gai_strerror(int errcode)
467{
468#ifdef _WIN32
469 UNUSED(errcode);
470 // also `win_wstr_to_str(gai_strerrorW(errcode);` would work; it is localized, but with correct diacritics
471 return get_win32_error(WSAGetLastError());
472#else
473 return gai_strerror(errcode);
474#endif // ! defined _WIN32
475}
476
#define UNUSED(x)
int void size_t * len
int fd_t
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define error_msg(...)
Definition debug.h:89
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define MSG(l, fmt,...)
Definition debug.h:96
unsigned get_sockaddr_addr_port(struct sockaddr *sa)
Definition net.c:414
void get_sockaddr_addr_str(struct sockaddr *sa, char *buf, size_t n)
Definition net.c:427
#define INVALID_SOCKET
Definition net.c:53
bool is_addr_multicast(const char *addr)
Definition net.c:382
const char * get_sockaddr_str(struct sockaddr *sa)
Definition net.c:450
#define IN_LOOPBACKNET
Definition net.c:74
static bool is_addr6(const char *addr)
Definition net.c:368
bool is_addr_private(struct sockaddr *sa)
Definition net.c:139
bool is_addr_linklocal(struct sockaddr *sa)
Definition net.c:79
bool is_host_private(const char *hostname)
Definition net.c:187
bool get_local_addresses(struct sockaddr_storage *addrs, size_t *len, int ip_version)
Definition net.c:229
static struct addrinfo * resolve_host(const char *hostname, const char *err_prefix)
Definition net.c:160
#define IPV4_LL_PREFIX
Definition net.c:70
bool is_addr_loopback(struct sockaddr *sa)
Definition net.c:108
static bool is_addr4(const char *addr)
Definition net.c:353
uint16_t socket_get_recv_port(int fd)
Definition net.c:200
bool is_host_loopback(const char *hostname)
Definition net.c:174
const char * ug_gai_strerror(int errcode)
Definition net.c:466
bool is_ipv6_supported(void)
Definition net.c:402
#define closesocket
Definition net.c:52
#define IN6_MAX_ASCII_LEN
Definition net.h:50