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(PW_ERROR_BAD_ADDRESS_FAMILY));
51 return false;
52 }
53 if (rc == -1) {
54 pw_set_status(PwStatus(PW_ERROR_BAD_ADDRESS_FAMILY));
55 return false;
56 }
57 if (rc != 1) {
58 pw_set_status(PwStatus(PW_ERROR_BAD_IP_ADDRESS));
59 return false;
60 }
61 return true;
62}
63
64[[nodiscard]] bool _pw_parse_inet_address(PwValuePtr addr, PwValuePtr result)
65{
66 if (!pw_validate(addr, PwTypeId_String)) {
67 return false;
68 }
69
70 // try to separate address and port parts
71 PwValue parts = PW_NULL;
72 if (!pw_string_rsplit_chr(addr, ':', 1, &parts)) {
73 return false;
74 }
75 if (pw_array_length(&parts) == 1) {
76 // addr without port
77 return parse_addr(AF_UNSPEC, addr, 0, result);
78 }
79
80 PwValue addr_part = PW_NULL;
81 if (!pw_array_item(&parts, 0, &addr_part)) {
82 return false;
83 }
84 PwValue port_part = PW_NULL;
85 if (!pw_array_item(&parts, 1, &port_part)) {
86 return false;
87 }
88 if (pw_strchr(&addr_part, ':', 0, nullptr)) {
89 // IPv6 address?
90 if (pw_startswith(&addr_part, '[') && pw_endswith(&addr_part, ']')) {
91 // yes, with port
92 } else {
93 // yes, without port
94 // parse original addr as IPv6
95 return parse_addr(AF_INET6, addr, 0, result);
96 }
97 }
98
99 // parse port
100 PwValue port = PW_NULL;
101 if (!pw_parse_number(&port_part, &port)) {
102 return false;
103 }
104 if ( ! (pw_is_signed(&port) && port.signed_value > 0 && port.signed_value < 65536)) {
105 pw_set_status(PwStatus(PW_ERROR_BAD_PORT));
106 return false;
107 }
108
109 // parse address
110 return parse_addr(AF_UNSPEC, &addr_part, port.signed_value, result);
111}
112
113[[nodiscard]] bool pw_parse_subnet(PwValuePtr subnet, PwValuePtr netmask, PwValuePtr result)
114{
115 if (!pw_validate(subnet, PwTypeId_String)) {
116 return false;
117 }
118
119 // check CIDR notation
120 PwValue parts = PW_NULL;
121 if (!pw_string_split_chr(subnet, '/', 0, &parts)) {
122 return false;
123 }
124
125 unsigned num_parts = pw_array_length(&parts);
126 if (num_parts > 2) {
127 pw_set_status(PwStatus(PW_ERROR_BAD_NETMASK));
128 return false;
129 }
130
131 // parse subnet address
132 PwValue subnet_addr = PW_NULL;
133 if (!pw_array_item(&parts, 0, &subnet_addr)) {
134 return false;
135 }
136 if (!pw_parse_inet_address(&subnet_addr, result)) {
137 return false;
138 }
139
140 if (num_parts > 1) {
141
142 // try CIDR netmask
143 PwValue cidr_netmask = PW_NULL;
144 if (!pw_array_item(&parts, 1, &cidr_netmask)) {
145 return false;
146 }
147 PwValue n = PW_NULL;
148 if (!pw_parse_number(&cidr_netmask, &n)) {
149 return false;
150 }
151 if (!pw_is_signed(&n)) {
152 pw_set_status(PwStatus(PW_ERROR_BAD_NETMASK));
153 return false;
154 }
155 if (n.signed_value == 0) {
156 pw_set_status(PwStatus(PW_ERROR_BAD_NETMASK));
157 return false;
158 }
159
160 // check upper range depending on address family
161 _PwSockAddrData* sa = _pw_get_struct_ptr(result, PwTypeId_SockAddr);
162 int max_bits;
163 if (sa->addr.ss_family == AF_INET) {
164 max_bits = 32;
165 } else {
166 max_bits = 128;
167 }
168 if (n.signed_value >= max_bits) {
169 pw_set_status(PwStatus(PW_ERROR_BAD_NETMASK));
170 return false;
171 }
172
173 // set netmask in the result
174 sa->netmask = n.signed_value;
175
176 } else {
177 // not CIDR notation, parse netmask parameter
178
179 if (pw_is_null(netmask)) {
180 pw_set_status(PwStatus(PW_ERROR_MISSING_NETMASK));
181 return false;
182 }
183
184 if (!pw_validate(netmask, PwTypeId_String)) {
185 return false;
186 }
187
188 PwValue parsed_netmask = PW_NULL;
189 if (!pw_parse_inet_address(netmask, &parsed_netmask)) {
190 return false;
191 }
192
193 // check address families are same
194 _PwSockAddrData* sa_result = _pw_get_struct_ptr(result, PwTypeId_SockAddr);
195 _PwSockAddrData* sa_netmask = _pw_get_struct_ptr(&parsed_netmask, PwTypeId_SockAddr);
196
197 if (sa_result->addr.ss_family != sa_netmask->addr.ss_family) {
198 pw_set_status(PwStatus(PW_ERROR_BAD_NETMASK));
199 return false;
200 }
201
202 // convert netmask to the number of bits
203 if (sa_result->addr.ss_family == AF_INET) {
204 unsigned n = ntohl(((struct sockaddr_in*) &sa_netmask->addr)->sin_addr.s_addr);
205 static_assert(sizeof(n) >= 4);
206 if (n == 0) {
207 sa_result->netmask = 0;
208 } else {
209 // XXX use stdc_trailing_zeros
210 sa_result->netmask = 32 - __builtin_ctz(n);
211 }
212 } else {
213 uint8_t* addr = ((struct sockaddr_in6*) &sa_netmask->addr)->sin6_addr.s6_addr;
214 sa_result->netmask = 128;
215 addr += 15;
216 for (unsigned i = 0; i < 16; i++) {
217 uint8_t n = *addr--;
218 if (n) {
219 // XXX use stdc_trailing_zeros
220 sa_result->netmask -= __builtin_ctz(n);
221 break;
222 }
223 sa_result->netmask -= 8;
224 }
225 }
226 }
227 return true;
228}
229
230[[nodiscard]] bool _pw_split_addr_port(PwValuePtr addr_port, PwValuePtr addr, PwValuePtr port)
231{
232 PwValue emptystr = PW_STRING("");
233 PwValue parts = PW_NULL;
234 if (!pw_string_rsplit_chr(addr_port, ':', 1, &parts)) {
235 return false;
236 }
237 if (pw_array_length(&parts) == 1) {
238 // Assume addr_port contains port (or service name) only.
239 pw_move(addr, &emptystr);
240 if (!pw_array_item(&parts, 0, port)) {
241 return false;
242 }
243 } else {
244 if (!pw_array_item(&parts, 0, addr)) {
245 return false;
246 }
247 if (!pw_array_item(&parts, 1, port)) {
248 return false;
249 }
250 if (pw_strchr(addr, ':', 0, nullptr)) {
251 // IPv6 address?
252 if (pw_startswith(addr, '[') && pw_endswith(addr, ']')) {
253 // address is in square brackets, so port is okay
254 } else {
255 // port is missing
256 pw_clone2(addr, addr_port);
257 pw_clone2(port, &emptystr);
258 }
259 }
260 }
261 return true;
262}