utils: fix memory leak in network_get_endpoint()
[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 goto out;
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 free_ai;
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 free_ai:
71 freeaddrinfo(ai_cur);
72
73 out:
74 free(buf);
75 return ret;
76 }
77
78 int network_get_subnet(int af, union network_addr *addr, int *mask, const char *str)
79 {
80 char *buf = strdup(str);
81 char *sep, *end;
82 int ret = -1;
83
84 if (af == AF_INET6)
85 *mask = 128;
86 else
87 *mask = 32;
88
89 sep = strchr(buf, '/');
90 if (sep) {
91 unsigned long val;
92
93 *(sep++) = 0;
94
95 val = strtoul(sep, &end, 0);
96 if ((end && *end) || val > *mask)
97 goto out;
98
99 *mask = val;
100 }
101
102 if (inet_pton(af, buf, addr) == 1)
103 ret = 0;
104
105 out:
106 free(buf);
107 return ret;
108 }
109
110 int network_get_local_addr(void *local, const union network_endpoint *target)
111 {
112 union network_endpoint ep = {};
113 socklen_t len;
114 int ret = -1;
115 int fd;
116
117 memset(local, 0, sizeof(union network_addr));
118 if (target->sa.sa_family == AF_INET6)
119 len = sizeof(ep.in6);
120 else
121 len = sizeof(ep.in);
122
123 fd = socket(target->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
124 if (fd < 0)
125 return -1;
126
127 if (connect(fd, (const struct sockaddr *)target, len))
128 goto out;
129
130 len = sizeof(ep);
131 if (getsockname(fd, &ep.sa, &len))
132 goto out;
133
134 if (ep.sa.sa_family == AF_INET6)
135 memcpy(local, &ep.in6.sin6_addr, sizeof(ep.in6.sin6_addr));
136 else
137 memcpy(local, &ep.in.sin_addr, sizeof(ep.in.sin_addr));
138 ret = 0;
139
140 out:
141 close(fd);
142 return ret;
143 }