pex: add support for sending endpoint notification from the wg port via raw socket
[project/unetd.git] / utils.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <sys/stat.h>
8 #include <arpa/inet.h>
9 #include <netdb.h>
10 #include <stdio.h>
11 #include "unetd.h"
12
13 int network_get_endpoint(union network_endpoint *dest, const char *str,
14 int default_port, int idx)
15 {
16 struct addrinfo hints = {
17 .ai_flags = AI_ADDRCONFIG,
18 .ai_family = AF_UNSPEC,
19 };
20 char *buf = strdup(str);
21 char *host = buf, *port;
22 struct addrinfo *ai, *ai_cur;
23 int n_res;
24 int ret = -1;
25
26 memset(dest, 0, sizeof(*dest));
27
28 if (*host == '[') {
29 host++;
30 port = strchr(host, ']');
31 if (!port)
32 goto out;
33
34 *(port++) = 0;
35 if (!*port)
36 port = NULL;
37 else if (*port == ':')
38 port++;
39 else
40 goto out;
41 hints.ai_family = AF_INET6;
42 hints.ai_flags |= AI_NUMERICHOST;
43 } else {
44 host = buf;
45
46 port = strchr(host, ':');
47 if (port)
48 *(port++) = 0;
49 }
50
51 if (getaddrinfo(host, port, &hints, &ai) || !ai)
52 goto out;
53
54 while (1) {
55 ai_cur = ai;
56 for (n_res = 0; ai_cur; ai_cur = ai_cur->ai_next, n_res++)
57 if (!idx--)
58 goto found;
59
60 idx %= n_res;
61 }
62
63 found:
64 if (ai_cur->ai_addrlen > sizeof(*dest))
65 goto free_ai;
66
67 memcpy(dest, ai_cur->ai_addr, ai_cur->ai_addrlen);
68 if (!port)
69 dest->in.sin_port = htons(default_port);
70 ret = 0;
71
72 free_ai:
73 freeaddrinfo(ai);
74
75 out:
76 free(buf);
77 return ret;
78 }
79
80 int network_get_subnet(int af, union network_addr *addr, int *mask, const char *str)
81 {
82 char *buf = strdup(str);
83 char *sep, *end;
84 int ret = -1;
85
86 if (af == AF_INET6)
87 *mask = 128;
88 else
89 *mask = 32;
90
91 sep = strchr(buf, '/');
92 if (sep) {
93 unsigned long val;
94
95 *(sep++) = 0;
96
97 val = strtoul(sep, &end, 0);
98 if ((end && *end) || val > *mask)
99 goto out;
100
101 *mask = val;
102 }
103
104 if (inet_pton(af, buf, addr) == 1)
105 ret = 0;
106
107 out:
108 free(buf);
109 return ret;
110 }
111
112 int network_get_local_addr(void *local, const union network_endpoint *target)
113 {
114 union network_endpoint ep = {};
115 socklen_t len;
116 int ret = -1;
117 int fd;
118
119 memset(local, 0, sizeof(union network_addr));
120 if (target->sa.sa_family == AF_INET6)
121 len = sizeof(ep.in6);
122 else
123 len = sizeof(ep.in);
124
125 fd = socket(target->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
126 if (fd < 0)
127 return -1;
128
129 if (connect(fd, (const struct sockaddr *)target, len))
130 goto out;
131
132 len = sizeof(ep);
133 if (getsockname(fd, &ep.sa, &len))
134 goto out;
135
136 if (ep.sa.sa_family == AF_INET6)
137 memcpy(local, &ep.in6.sin6_addr, sizeof(ep.in6.sin6_addr));
138 else
139 memcpy(local, &ep.in.sin_addr, sizeof(ep.in.sin_addr));
140 ret = 0;
141
142 out:
143 close(fd);
144 return ret;
145 }
146
147 void *unet_read_file(const char *name, size_t *len)
148 {
149 struct stat st;
150 void *data;
151 FILE *f;
152
153 f = fopen(name, "r");
154 if (!f)
155 goto error;
156
157 if (fstat(fileno(f), &st) < 0)
158 goto close;
159
160 if (*len && st.st_size > *len)
161 goto close;
162
163 data = malloc(st.st_size);
164 if (!data)
165 goto close;
166
167 if (fread(data, 1, st.st_size, f) != st.st_size) {
168 free(data);
169 goto close;
170 }
171 fclose(f);
172
173 *len = st.st_size;
174 return data;
175
176 close:
177 fclose(f);
178 error:
179 *len = 0;
180 return NULL;
181 }