build: move some code to libunet
[project/unetd.git] / utils.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <arpa/inet.h>
8 #include <netdb.h>
9 #include "unetd.h"
10
11 int network_get_endpoint(union network_endpoint *dest, const char *str,
12 int default_port, int idx)
13 {
14 struct addrinfo hints = {
15 .ai_flags = AI_ADDRCONFIG,
16 .ai_family = AF_UNSPEC,
17 };
18 char *buf = strdup(str);
19 char *host = buf, *port;
20 struct addrinfo *ai, *ai_cur;
21 int n_res;
22 int ret = -1;
23
24 memset(dest, 0, sizeof(*dest));
25
26 if (*host == '[') {
27 host++;
28 port = strchr(host, ']');
29 if (!port)
30 return -1;
31
32 *(port++) = 0;
33 if (!*port)
34 port = NULL;
35 else if (*port == ':')
36 port++;
37 else
38 goto out;
39 hints.ai_family = AF_INET6;
40 hints.ai_flags |= AI_NUMERICHOST;
41 } else {
42 host = buf;
43
44 port = strchr(host, ':');
45 if (port)
46 *(port++) = 0;
47 }
48
49 if (getaddrinfo(host, port, &hints, &ai) || !ai)
50 goto out;
51
52 while (1) {
53 ai_cur = ai;
54 for (n_res = 0; ai_cur; ai_cur = ai_cur->ai_next, n_res++)
55 if (!idx--)
56 goto found;
57
58 idx %= n_res;
59 }
60
61 found:
62 if (ai_cur->ai_addrlen > sizeof(*dest))
63 goto out;
64
65 memcpy(dest, ai_cur->ai_addr, ai_cur->ai_addrlen);
66 if (!port)
67 dest->in.sin_port = htons(default_port);
68 ret = 0;
69
70 out:
71 free(buf);
72 return ret;
73 }
74
75 int network_get_subnet(int af, union network_addr *addr, int *mask, const char *str)
76 {
77 char *buf = strdup(str);
78 char *sep, *end;
79 int ret = -1;
80
81 if (af == AF_INET6)
82 *mask = 128;
83 else
84 *mask = 32;
85
86 sep = strchr(buf, '/');
87 if (sep) {
88 unsigned long val;
89
90 *(sep++) = 0;
91
92 val = strtoul(sep, &end, 0);
93 if ((end && *end) || val > *mask)
94 goto out;
95
96 *mask = val;
97 }
98
99 if (inet_pton(af, buf, addr) == 1)
100 ret = 0;
101
102 out:
103 free(buf);
104 return ret;
105 }
106
107 int network_get_local_addr(void *local, const union network_endpoint *target)
108 {
109 union network_endpoint ep = {};
110 socklen_t len;
111 int ret = -1;
112 int fd;
113
114 memset(local, 0, sizeof(union network_addr));
115 if (target->sa.sa_family == AF_INET6)
116 len = sizeof(ep.in6);
117 else
118 len = sizeof(ep.in);
119
120 fd = socket(target->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
121 if (fd < 0)
122 return -1;
123
124 if (connect(fd, (const struct sockaddr *)target, len))
125 goto out;
126
127 len = sizeof(ep);
128 if (getsockname(fd, &ep.sa, &len))
129 goto out;
130
131 if (ep.sa.sa_family == AF_INET6)
132 memcpy(local, &ep.in6.sin6_addr, sizeof(ep.in6.sin6_addr));
133 else
134 memcpy(local, &ep.in.sin_addr, sizeof(ep.in.sin_addr));
135 ret = 0;
136
137 out:
138 close(fd);
139 return ret;
140 }