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}