1#include <arpa/inet.h>
2
3#include "include/pwlib/netutils.h"
4#include "include/pwlib/parsers.h"
5#include "include/pwlib/socket.h"
6
7
8[[nodiscard]] static bool parse_addr(int domain, PwValuePtr addr, in_port_t port, PwValuePtr result)
9{
10 if (!pw_create(PwTypeId_SockAddr, result)) {
11 return false;
12 }
13
14 _PwSockAddrData* sa = _pw_get_struct_ptr(result, PwTypeId_SockAddr);
15
16 PwValue a = PW_NULL;
17
18 if (pw_startswith(addr, '[') && pw_endswith(addr, ']')) {
19 if (!pw_substr(addr, 1, pw_strlen(addr) - 1, &a)) {
20 return false;
21 }
22 } else {
23 __pw_clone(&a, addr);
24 }
25
26 if (domain == AF_UNSPEC && pw_strchr(&a, ':', 0, nullptr)) {
27 domain = AF_INET6;
28 } else {
29 domain = AF_INET;
30 }
31
32 PW_CSTRING(c_addr, &a);
33 int rc;
34 switch (domain) {
35 case AF_INET: {
36 struct sockaddr_in* sin = (struct sockaddr_in*) &sa->addr;
37 sin->sin_family = domain;
38 sin->sin_port = htons(port);
39 rc = inet_pton(AF_INET, c_addr, &sin->sin_addr);
40 break;
41 }
42 case AF_INET6: {
43 struct sockaddr_in6* sin = (struct sockaddr_in6*) &sa->addr;
44 sin->sin6_family = domain;
45 sin->sin6_port = htons(port);
46 rc = inet_pton(AF_INET, c_addr, &sin->sin6_addr);
47 break;
48 }
49 default:
50 pw_set_status(PwStatus(PweBadAddressFamily));
51 return false;
52 }
53 if (rc == -1) {
54 pw_set_status(PwStatus(PweBadAddressFamily));
55 return false;
56 }
57 if (rc != 1) {
58 pw_set_status(PwStatus(PweBadIpAddress));
59 return false;
60 }
61 return true;
62}
63
64[[nodiscard]] bool _pw_parse_inet_address(PwValuePtr addr, PwValuePtr result)
65{
66 pw_assert(pw_is_string(addr));
67
68 // try to separate address and port parts
69 PwValue parts = PW_NULL;
70 if (!pw_string_rsplit_chr(addr, ':', 1, &parts)) {
71 return false;
72 }
73 if (pw_array_length(&parts) == 1) {
74 // addr without port
75 return parse_addr(AF_UNSPEC, addr, 0, result);
76 }
77
78 PwValue addr_part = PW_NULL;
79 if (!pw_array_item(&parts, 0, &addr_part)) {
80 return false;
81 }
82 PwValue port_part = PW_NULL;
83 if (!pw_array_item(&parts, 1, &port_part)) {
84 return false;
85 }
86 if (pw_strchr(&addr_part, ':', 0, nullptr)) {
87 // IPv6 address?
88 if (pw_startswith(&addr_part, '[') && pw_endswith(&addr_part, ']')) {
89 // yes, with port
90 } else {
91 // yes, without port
92 // parse original addr as IPv6
93 return parse_addr(AF_INET6, addr, 0, result);
94 }
95 }
96
97 // parse port
98 PwValue port = PW_NULL;
99 if (!pw_parse_number(&port_part, &port)) {
100 return false;
101 }
102 if ( ! (pw_is_signed(&port) && port.signed_value > 0 && port.signed_value < 65536)) {
103 pw_set_status(PwStatus(PweBadPort));
104 return false;
105 }
106
107 // parse address
108 return parse_addr(AF_UNSPEC, &addr_part, port.signed_value, result);
109}
110
111[[nodiscard]] bool pw_parse_subnet(PwValuePtr subnet, PwValuePtr netmask, PwValuePtr result)
112{
113 pw_assert(pw_is_string(subnet));
114
115 // check CIDR notation
116 PwValue parts = PW_NULL;
117 if (!pw_string_split_chr(subnet, '/', 0, &parts)) {
118 return false;
119 }
120
121 unsigned num_parts = pw_array_length(&parts);
122 if (num_parts > 2) {
123 pw_set_status(PwStatus(PweBadNetmask));
124 return false;
125 }
126
127 // parse subnet address
128 PwValue subnet_addr = PW_NULL;
129 if (!pw_array_item(&parts, 0, &subnet_addr)) {
130 return false;
131 }
132 if (!pw_parse_inet_address(&subnet_addr, result)) {
133 return false;
134 }
135
136 if (num_parts > 1) {
137
138 // try CIDR netmask
139 PwValue cidr_netmask = PW_NULL;
140 if (!pw_array_item(&parts, 1, &cidr_netmask)) {
141 return false;
142 }
143 PwValue n = PW_NULL;
144 if (!pw_parse_number(&cidr_netmask, &n)) {
145 return false;
146 }
147 if (!pw_is_signed(&n)) {
148 pw_set_status(PwStatus(PweBadNetmask));
149 return false;
150 }
151 if (n.signed_value == 0) {
152 pw_set_status(PwStatus(PweBadNetmask));
153 return false;
154 }
155
156 // check upper range depending on address family
157 _PwSockAddrData* sa = _pw_get_struct_ptr(result, PwTypeId_SockAddr);
158 int max_bits;
159 if (sa->addr.ss_family == AF_INET) {
160 max_bits = 32;
161 } else {
162 max_bits = 128;
163 }
164 if (n.signed_value >= max_bits) {
165 pw_set_status(PwStatus(PweBadNetmask));
166 return false;
167 }
168
169 // set netmask in the result
170 sa->netmask = n.signed_value;
171
172 } else {
173 // not CIDR notation, parse netmask parameter
174
175 if (pw_is_null(netmask)) {
176 pw_set_status(PwStatus(PweMissingNetmask));
177 return false;
178 }
179
180 pw_assert(pw_is_string(netmask));
181
182 PwValue parsed_netmask = PW_NULL;
183 if (!pw_parse_inet_address(netmask, &parsed_netmask)) {
184 return false;
185 }
186
187 // check address families are same
188 _PwSockAddrData* sa_result = _pw_get_struct_ptr(result, PwTypeId_SockAddr);
189 _PwSockAddrData* sa_netmask = _pw_get_struct_ptr(&parsed_netmask, PwTypeId_SockAddr);
190
191 if (sa_result->addr.ss_family != sa_netmask->addr.ss_family) {
192 pw_set_status(PwStatus(PweBadNetmask));
193 return false;
194 }
195
196 // convert netmask to the number of bits
197 if (sa_result->addr.ss_family == AF_INET) {
198 unsigned n = ntohl(((struct sockaddr_in*) &sa_netmask->addr)->sin_addr.s_addr);
199 static_assert(sizeof(n) >= 4);
200 if (n == 0) {
201 sa_result->netmask = 0;
202 } else {
203 // XXX use stdc_trailing_zeros
204 sa_result->netmask = 32 - __builtin_ctz(n);
205 }
206 } else {
207 uint8_t* addr = ((struct sockaddr_in6*) &sa_netmask->addr)->sin6_addr.s6_addr;
208 sa_result->netmask = 128;
209 addr += 15;
210 for (unsigned i = 0; i < 16; i++) {
211 uint8_t n = *addr--;
212 if (n) {
213 // XXX use stdc_trailing_zeros
214 sa_result->netmask -= __builtin_ctz(n);
215 break;
216 }
217 sa_result->netmask -= 8;
218 }
219 }
220 }
221 return true;
222}
223
224[[nodiscard]] bool _pw_split_addr_port(PwValuePtr addr_port, PwValuePtr addr, PwValuePtr port)
225{
226 PwValue emptystr = PW_STRING();
227 PwValue parts = PW_NULL;
228 if (!pw_string_rsplit_chr(addr_port, ':', 1, &parts)) {
229 return false;
230 }
231 if (pw_array_length(&parts) == 1) {
232 // Assume addr_port contains port (or service name) only.
233 pw_move(addr, &emptystr);
234 if (!pw_array_item(&parts, 0, port)) {
235 return false;
236 }
237 } else {
238 if (!pw_array_item(&parts, 0, addr)) {
239 return false;
240 }
241 if (!pw_array_item(&parts, 1, port)) {
242 return false;
243 }
244 if (pw_strchr(addr, ':', 0, nullptr)) {
245 // IPv6 address?
246 if (pw_startswith(addr, '[') && pw_endswith(addr, ']')) {
247 // address is in square brackets, so port is okay
248 } else {
249 // port is missing
250 pw_clone2(addr, addr_port);
251 pw_clone2(port, &emptystr);
252 }
253 }
254 }
255 return true;
256}