Coverage Report

Created: 2025-10-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/core/ogs-sockaddr.c
Line
Count
Source
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
2
 * contributor license agreements.  See the NOTICE file distributed with
3
 * this work for additional information regarding copyright ownership.
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
5
 * (the "License"); you may not use this file except in compliance with
6
 * the License.  You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
/*
18
 * Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
19
 *
20
 * This file is part of Open5GS.
21
 *
22
 * Licensed under the Apache License, Version 2.0 (the "License");
23
 * you may not use this file except in compliance with the License.
24
 * You may obtain a copy of the License at
25
 *
26
 *   http://www.apache.org/licenses/LICENSE-2.0
27
 *
28
 * Unless required by applicable law or agreed to in writing, software
29
 * distributed under the License is distributed on an "AS IS" BASIS,
30
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31
 * See the License for the specific language governing permissions and
32
 * limitations under the License.
33
 */
34
35
#include "core-config-private.h"
36
37
#if HAVE_ARPA_INET_H
38
#include <arpa/inet.h>
39
#endif
40
41
#if HAVE_CTYPE_H
42
#include <ctype.h>
43
#endif
44
45
#if HAVE_IFADDRS_H
46
#include <ifaddrs.h>
47
#endif
48
49
#if HAVE_NETDB_H
50
#include <netdb.h>
51
#endif
52
53
#include "ogs-core.h"
54
55
#undef OGS_LOG_DOMAIN
56
0
#define OGS_LOG_DOMAIN __ogs_sock_domain
57
58
static bool ogs_sockaddr_compare(const ogs_sockaddr_t *a,
59
                                 const ogs_sockaddr_t *b,
60
                                 bool compare_port);
61
62
/* If you want to use getnameinfo,
63
 * you need to consider DNS query delay (about 10 seconds) */
64
#if 0
65
int ogs_getnameinfo(
66
    char *hostname, socklen_t hostname_len, ogs_sockaddr_t *addr, int flags)
67
{
68
    ogs_assert(hostname);
69
    ogs_assert(addr);
70
71
    return getnameinfo(&addr->sa, ogs_sockaddr_len(addr),
72
            hostname, hostname_len,
73
            NULL, 0, flags != 0 ? flags : NI_NAMEREQD);
74
}
75
#endif
76
77
int ogs_getaddrinfo(ogs_sockaddr_t **sa_list,
78
        int family, const char *hostname, uint16_t port, int flags)
79
0
{
80
0
    *sa_list = NULL;
81
0
    return ogs_addaddrinfo(sa_list, family, hostname, port, flags);
82
0
}
83
84
int ogs_freeaddrinfo(ogs_sockaddr_t *sa_list)
85
0
{
86
0
    ogs_sockaddr_t *next = NULL, *addr = NULL;
87
88
0
    addr = sa_list;
89
0
    while (addr) {
90
0
        next = addr->next;
91
0
        if (addr->hostname)
92
0
            ogs_free(addr->hostname);
93
0
        ogs_free(addr);
94
0
        addr = next;
95
0
    }
96
97
0
    return OGS_OK;
98
0
}
99
100
int ogs_addaddrinfo(ogs_sockaddr_t **sa_list,
101
        int family, const char *hostname, uint16_t port, int flags)
102
0
{
103
0
    int rc;
104
0
    char service[NI_MAXSERV];
105
0
    struct addrinfo hints, *ai, *ai_list;
106
0
    ogs_sockaddr_t *prev = NULL;
107
108
    /* Last node of original list (for appending) */
109
0
    ogs_sockaddr_t *tail = NULL;
110
    /* First newly added node (for cleanup on error) */
111
0
    ogs_sockaddr_t *first_new = NULL;
112
113
0
    char buf[OGS_ADDRSTRLEN];
114
115
0
    ogs_assert(sa_list);
116
117
    /* Prepare hints for getaddrinfo() */
118
0
    memset(&hints, 0, sizeof(hints));
119
0
    hints.ai_family = family;
120
0
    hints.ai_socktype = SOCK_STREAM;
121
0
    hints.ai_flags = flags;
122
123
0
    ogs_snprintf(service, sizeof(service), "%u", port);
124
125
0
    rc = getaddrinfo(hostname, service, &hints, &ai_list);
126
0
    if (rc != 0) {
127
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
128
0
                        "getaddrinfo(%d:%s:%d:0x%x) failed: %s",
129
0
                        family, hostname ? hostname : "(null)",
130
0
                        port, flags, gai_strerror(rc));
131
        /* Non-fatal: log the error and return */
132
0
        return OGS_ERROR;
133
0
    }
134
135
    /* Find the end of the existing list, so new entries can be appended */
136
0
    if (*sa_list) {
137
0
        tail = *sa_list;
138
0
        while (tail->next)
139
0
            tail = tail->next;
140
0
        prev = tail;
141
0
    }
142
143
    /* Iterate over each result from getaddrinfo and add to the linked list */
144
0
    for (ai = ai_list; ai; ai = ai->ai_next) {
145
0
        ogs_sockaddr_t *new, tmp;
146
0
        if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
147
0
            continue; /* Skip unsupported address families */
148
149
0
        new = ogs_calloc(1, sizeof(ogs_sockaddr_t));
150
0
        if (!new) {
151
0
            ogs_error("ogs_calloc() failed");
152
            /* Clean up any partially added entries on memory failure */
153
0
            if (first_new) {
154
0
                if (tail) {
155
                    /* detach new sub-list from original list */
156
0
                    tail->next = NULL;
157
0
                } else {
158
0
                    *sa_list = NULL;    /* no original list, reset head */
159
0
                }
160
0
                ogs_freeaddrinfo(first_new);
161
0
            }
162
0
            freeaddrinfo(ai_list);
163
0
            return OGS_ERROR;
164
0
        }
165
0
        memcpy(&new->sa, ai->ai_addr, ai->ai_addrlen);
166
0
        new->ogs_sin_port = htobe16(port);
167
168
0
        if (hostname) {
169
0
            if (ogs_inet_pton(ai->ai_family, hostname, &tmp) == OGS_OK) {
170
                /* Input string is a valid numeric IP address */
171
0
                ogs_debug("addr:%s, port:%d", OGS_ADDR(new, buf), port);
172
0
            } else {
173
                /* Input string is not a numeric IP; treat it as a hostname */
174
0
                new->hostname = ogs_strdup(hostname);
175
0
                if (!new->hostname) {
176
0
                    ogs_error("ogs_strdup() failed");
177
                    /* Free the new node and any previously added nodes */
178
0
                    ogs_free(new);
179
0
                    if (first_new) {
180
0
                        if (tail) {
181
0
                            tail->next = NULL;
182
0
                        } else {
183
0
                            *sa_list = NULL;
184
0
                        }
185
0
                        ogs_freeaddrinfo(first_new);
186
0
                    }
187
0
                    freeaddrinfo(ai_list);
188
0
                    return OGS_ERROR;
189
0
                }
190
0
                ogs_debug("name:%s, port:%d", new->hostname, port);
191
0
            }
192
0
        }
193
194
        /* Link the new node into the list */
195
0
        if (!prev) {
196
0
            *sa_list = new;
197
0
        } else {
198
0
            prev->next = new;
199
0
        }
200
0
        prev = new;
201
0
        if (!first_new) {
202
0
            first_new = new;  /* mark the first new node added */
203
0
        }
204
0
    }
205
206
0
    freeaddrinfo(ai_list);
207
208
0
    if (first_new == NULL) {
209
        /* No addresses were added (e.g., no AF_INET/AF_INET6 results) */
210
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
211
0
                        "ogs_addaddrinfo(%d:%s:%d:0x%x) returned no addresses",
212
0
                        family, hostname ? hostname : "(null)", port, flags);
213
0
        return OGS_ERROR;
214
0
    }
215
216
0
    return OGS_OK;
217
0
}
218
219
int ogs_filteraddrinfo(ogs_sockaddr_t **sa_list, int family)
220
0
{
221
0
    ogs_sockaddr_t *addr = NULL, *prev = NULL, *next = NULL;
222
223
0
    ogs_assert(sa_list);
224
225
0
    prev = NULL;
226
0
    addr = *sa_list;
227
0
    while (addr) {
228
0
        next = addr->next;
229
230
0
        if (addr->ogs_sa_family != family) {
231
0
            if (prev)
232
0
                prev->next = addr->next;
233
0
            else
234
0
                *sa_list = addr->next;
235
0
            if (addr->hostname)
236
0
                ogs_free(addr->hostname);
237
0
            ogs_free(addr);
238
239
0
        } else {
240
0
            prev = addr;
241
0
        }
242
243
0
        addr = next;
244
0
    }
245
246
0
    return OGS_OK;
247
0
}
248
249
int ogs_copyaddrinfo(ogs_sockaddr_t **dst, const ogs_sockaddr_t *src)
250
0
{
251
0
    ogs_sockaddr_t *d;
252
0
    const ogs_sockaddr_t *s;
253
254
0
    for (*dst = d = NULL, s = src; s; s = s->next) {
255
0
        if (!d) {
256
0
            *dst = d = ogs_memdup(s, sizeof *s);
257
0
            if (!(*dst)) {
258
0
                ogs_error("ogs_memdup() failed");
259
0
                return OGS_ERROR;
260
0
            }
261
0
        } else {
262
0
            d = d->next = ogs_memdup(s, sizeof *s);
263
0
            if (!d) {
264
0
                ogs_error("ogs_memdup() failed");
265
0
                return OGS_ERROR;
266
0
            }
267
0
        }
268
0
        if (s->hostname) {
269
0
            if (s == src || s->hostname != src->hostname) {
270
0
                d->hostname = ogs_strdup(s->hostname);
271
0
                if (!d->hostname) {
272
0
                    ogs_error("ogs_memdup() failed");
273
0
                    return OGS_ERROR;
274
0
                }
275
0
            } else {
276
0
                d->hostname = (*dst)->hostname;
277
0
            }
278
0
        }
279
0
    }
280
281
0
    return OGS_OK;
282
0
}
283
284
int ogs_sortaddrinfo(ogs_sockaddr_t **sa_list, int family)
285
0
{
286
0
    ogs_sockaddr_t *head = NULL, *addr = NULL, *new = NULL, *old = NULL;
287
288
0
    ogs_assert(sa_list);
289
290
0
    old = *sa_list;
291
0
    while (old) {
292
0
        addr = old;
293
294
0
        old = old->next;
295
296
0
        if (head == NULL || addr->ogs_sa_family == family) {
297
0
            addr->next = head;
298
0
            head = addr;
299
0
        } else {
300
0
            new = head;
301
0
            while(new->next != NULL && new->next->ogs_sa_family != family) {
302
0
                new = new->next;
303
0
            }
304
0
            addr->next = new->next;
305
0
            new->next = addr;
306
0
        }
307
0
    }
308
309
0
    *sa_list = head;
310
311
0
    return OGS_OK;
312
0
}
313
314
/*--------------------------------------------------------------------------
315
 * Merge a single node if not already in "dest" list
316
 *--------------------------------------------------------------------------
317
 */
318
void ogs_merge_single_addrinfo(
319
        ogs_sockaddr_t **dest, const ogs_sockaddr_t *item)
320
0
{
321
0
    ogs_sockaddr_t *p;
322
0
    ogs_sockaddr_t *new_sa;
323
324
0
    ogs_assert(dest);
325
0
    ogs_assert(item);
326
327
0
    p = *dest;
328
329
0
    while (p) {
330
0
        if (ogs_sockaddr_is_equal(p, item)) {
331
            /* Already exists */
332
0
            return;
333
0
        }
334
0
        p = p->next;
335
0
    }
336
0
    new_sa = (ogs_sockaddr_t *)ogs_malloc(sizeof(*new_sa));
337
0
    ogs_assert(new_sa);
338
0
    memcpy(new_sa, item, sizeof(*new_sa));
339
0
    if (item->hostname) {
340
0
        new_sa->hostname = ogs_strdup(item->hostname);
341
0
        ogs_assert(new_sa->hostname);
342
0
    }
343
0
    new_sa->next = NULL;
344
0
    if (!(*dest)) {
345
0
        *dest = new_sa;
346
0
    } else {
347
0
        p = *dest;
348
0
        while (p->next)
349
0
            p = p->next;
350
0
        p->next = new_sa;
351
0
    }
352
0
}
353
354
/*--------------------------------------------------------------------------
355
 * Merge an entire src list into dest
356
 *--------------------------------------------------------------------------
357
 */
358
void ogs_merge_addrinfo(ogs_sockaddr_t **dest, const ogs_sockaddr_t *src)
359
0
{
360
0
    const ogs_sockaddr_t *cur;
361
0
    cur = src;
362
0
    while (cur) {
363
0
        ogs_merge_single_addrinfo(dest, cur);
364
0
        cur = cur->next;
365
0
    }
366
0
}
367
368
ogs_sockaddr_t *ogs_link_local_addr(const char *dev, const ogs_sockaddr_t *sa)
369
0
{
370
0
#if defined(HAVE_GETIFADDRS)
371
0
    struct ifaddrs *iflist, *cur;
372
0
    int rc;
373
374
0
    rc = getifaddrs(&iflist);
375
0
    if (rc != 0) {
376
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "getifaddrs failed");
377
0
        return NULL;
378
0
    }
379
380
0
    for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
381
0
        ogs_sockaddr_t *ifa_addr = NULL;
382
0
        ogs_sockaddr_t *addr = NULL;
383
384
0
        ifa_addr = (ogs_sockaddr_t *)cur->ifa_addr;
385
386
0
        if (ifa_addr == NULL) /* may happen with ppp interfaces */
387
0
            continue;
388
389
0
        if (ifa_addr->ogs_sa_family == AF_INET)
390
0
            continue;
391
392
0
        if (!IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6.sin6_addr))
393
0
            continue;
394
395
0
        if (dev && strcmp(dev, cur->ifa_name) != 0)
396
0
            continue;
397
398
0
        if (sa && memcmp(&sa->sin6.sin6_addr,
399
0
                &ifa_addr->sin6.sin6_addr, sizeof(struct in6_addr)) != 0)
400
0
            continue;
401
402
0
        addr = ogs_calloc(1, sizeof(ogs_sockaddr_t));
403
0
        if (!addr) {
404
0
            ogs_error("ogs_calloc() failed");
405
0
            return NULL;
406
0
        }
407
0
        ogs_assert(addr);
408
0
        memcpy(&addr->sa, cur->ifa_addr, ogs_sockaddr_len(cur->ifa_addr));
409
410
0
        freeifaddrs(iflist);
411
0
        return addr;
412
0
    }
413
414
0
    freeifaddrs(iflist);
415
0
#endif
416
0
    return NULL;
417
0
}
418
419
ogs_sockaddr_t *ogs_link_local_addr_by_dev(const char *dev)
420
0
{
421
0
    ogs_assert(dev);
422
0
    return ogs_link_local_addr(dev, NULL);
423
0
}
424
425
ogs_sockaddr_t *ogs_link_local_addr_by_sa(const ogs_sockaddr_t *sa)
426
0
{
427
0
    ogs_assert(sa);
428
0
    return ogs_link_local_addr(NULL, sa);
429
0
}
430
431
int ogs_filter_ip_version(ogs_sockaddr_t **addr,
432
        int no_ipv4, int no_ipv6, int prefer_ipv4)
433
0
{
434
0
    int rv;
435
436
0
    if (no_ipv4 == 1) {
437
0
        rv = ogs_filteraddrinfo(addr, AF_INET6);
438
0
        ogs_assert(rv == OGS_OK);
439
0
    }
440
0
    if (no_ipv6 == 1) {
441
0
        rv = ogs_filteraddrinfo(addr, AF_INET);
442
0
        ogs_assert(rv == OGS_OK);
443
0
    }
444
445
0
    if (prefer_ipv4 == 1) {
446
0
        rv = ogs_sortaddrinfo(addr, AF_INET);
447
0
        ogs_assert(rv == OGS_OK);
448
0
    } else {
449
0
        rv = ogs_sortaddrinfo(addr, AF_INET6);
450
0
        ogs_assert(rv == OGS_OK);
451
0
    }
452
453
0
    return OGS_OK;
454
0
}
455
456
457
const char *ogs_inet_ntop(void *sa, char *buf, int buflen)
458
0
{
459
0
    int family;
460
0
    ogs_sockaddr_t *sockaddr = NULL;
461
462
0
    sockaddr = sa;
463
0
    ogs_assert(sockaddr);
464
0
    ogs_assert(buf);
465
0
    ogs_assert(buflen >= OGS_ADDRSTRLEN);
466
467
0
    family = sockaddr->ogs_sa_family;
468
0
    switch (family) {
469
0
    case AF_INET:
470
0
        return inet_ntop(family, &sockaddr->sin.sin_addr, buf,
471
0
                INET_ADDRSTRLEN);
472
0
    case AF_INET6:
473
0
        return inet_ntop(family, &sockaddr->sin6.sin6_addr, buf,
474
0
                INET6_ADDRSTRLEN);
475
0
    default:
476
0
        ogs_fatal("Unknown family(%d)", family);
477
0
        ogs_abort();
478
0
        return NULL;
479
0
    }
480
0
}
481
482
int ogs_inet_pton(int family, const char *src, void *sa)
483
0
{
484
0
    ogs_sockaddr_t *dst = NULL;
485
486
0
    ogs_assert(src);
487
0
    dst = sa;
488
0
    ogs_assert(dst);
489
490
0
    dst->ogs_sa_family = family;
491
0
    switch(family) {
492
0
    case AF_INET:
493
0
        return inet_pton(family, src, &dst->sin.sin_addr) == 1 ?
494
0
            OGS_OK : OGS_ERROR;
495
0
    case AF_INET6:
496
0
        return inet_pton(family, src, &dst->sin6.sin6_addr) == 1 ?
497
0
             OGS_OK : OGS_ERROR;
498
0
    default:
499
0
        ogs_fatal("Unknown family(%d)", family);
500
0
        ogs_abort();
501
0
        return OGS_ERROR;
502
0
    }
503
0
}
504
505
socklen_t ogs_sockaddr_len(const void *sa)
506
0
{
507
0
    const ogs_sockaddr_t *sockaddr = sa;
508
509
0
    ogs_assert(sa);
510
511
0
    switch(sockaddr->ogs_sa_family) {
512
0
    case AF_INET:
513
0
        return sizeof(struct sockaddr_in);
514
0
    case AF_INET6:
515
0
        return sizeof(struct sockaddr_in6);
516
0
    default:
517
0
        ogs_fatal("Unknown family(%d)", sockaddr->ogs_sa_family);
518
0
        ogs_abort();
519
0
        return OGS_ERROR;
520
0
    }
521
0
}
522
523
/*
524
 * Helper function to compare two addresses.
525
 * If compare_port is true, compare both port and address.
526
 * Otherwise, compare address only.
527
 */
528
static bool ogs_sockaddr_compare(const ogs_sockaddr_t *a,
529
                                 const ogs_sockaddr_t *b,
530
                                 bool compare_port)
531
0
{
532
0
    ogs_assert(a);
533
0
    ogs_assert(b);
534
535
0
    if (a->ogs_sa_family != b->ogs_sa_family)
536
0
        return false;
537
538
0
    switch (a->ogs_sa_family) {
539
0
    case AF_INET:
540
0
        if (compare_port && (a->sin.sin_port != b->sin.sin_port))
541
0
            return false;
542
0
        if (memcmp(&a->sin.sin_addr, &b->sin.sin_addr,
543
0
                   sizeof(struct in_addr)) != 0)
544
0
            return false;
545
0
        return true;
546
0
    case AF_INET6:
547
0
        if (compare_port && (a->sin6.sin6_port != b->sin6.sin6_port))
548
0
            return false;
549
0
        if (memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr,
550
0
                   sizeof(struct in6_addr)) != 0)
551
0
            return false;
552
0
        return true;
553
0
    default:
554
0
        ogs_error("Unexpected address family %u", a->ogs_sa_family);
555
0
        ogs_abort();
556
0
        return false; /* Defensive return */
557
0
    }
558
0
}
559
560
/* Compare addresses including port */
561
bool ogs_sockaddr_is_equal(const void *p, const void *q)
562
0
{
563
0
    const ogs_sockaddr_t *a = (const ogs_sockaddr_t *)p;
564
0
    const ogs_sockaddr_t *b = (const ogs_sockaddr_t *)q;
565
0
    return ogs_sockaddr_compare(a, b, true);
566
0
}
567
568
/* Compare addresses without considering port */
569
bool ogs_sockaddr_is_equal_addr(const void *p, const void *q)
570
0
{
571
0
    const ogs_sockaddr_t *a = (const ogs_sockaddr_t *)p;
572
0
    const ogs_sockaddr_t *b = (const ogs_sockaddr_t *)q;
573
0
    return ogs_sockaddr_compare(a, b, false);
574
0
}
575
576
bool ogs_sockaddr_check_any_match(
577
        ogs_sockaddr_t *base,
578
        ogs_sockaddr_t *list, const ogs_sockaddr_t *single, bool compare_port)
579
0
{
580
0
    ogs_sockaddr_t *p = NULL;
581
582
0
    while (list) {
583
0
        p = base;
584
0
        while (p) {
585
0
            if (ogs_sockaddr_compare(p, list, compare_port) == true)
586
0
                return true;
587
0
            p = p->next;
588
0
        }
589
0
        list = list->next;
590
0
    }
591
0
    if (single) {
592
0
        p = base;
593
0
        while (p) {
594
0
            if (ogs_sockaddr_compare(p, single, compare_port) == true)
595
0
                return true;
596
0
            p = p->next;
597
0
        }
598
0
    }
599
0
    return false;
600
0
}
601
602
static int parse_network(ogs_ipsubnet_t *ipsub, const char *network)
603
0
{
604
    /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
605
0
    int shift;
606
0
    char *s, *t;
607
0
    int octet;
608
0
    char buf[sizeof "255.255.255.255"];
609
610
0
    if (strlen(network) < sizeof buf)
611
0
        strcpy(buf, network);
612
0
    else
613
0
        return OGS_ERROR;
614
615
    /* parse components */
616
0
    s = buf;
617
0
    ipsub->sub[0] = 0;
618
0
    ipsub->mask[0] = 0;
619
0
    shift = 24;
620
0
    while (*s) {
621
0
        t = s;
622
0
        if (!isdigit(*t))
623
0
            return OGS_ERROR;
624
625
0
        while (isdigit(*t))
626
0
            ++t;
627
628
0
        if (*t == '.')
629
0
            *t++ = 0;
630
0
        else if (*t)
631
0
            return OGS_ERROR;
632
633
0
        if (shift < 0)
634
0
            return OGS_ERROR;
635
636
0
        octet = atoi(s);
637
0
        if (octet < 0 || octet > 255)
638
0
            return OGS_ERROR;
639
640
0
        ipsub->sub[0] |= octet << shift;
641
0
        ipsub->mask[0] |= 0xFFUL << shift;
642
0
        s = t;
643
0
        shift -= 8;
644
0
    }
645
0
    ipsub->sub[0] = be32toh(ipsub->sub[0]);
646
0
    ipsub->mask[0] = be32toh(ipsub->mask[0]);
647
0
    ipsub->family = AF_INET;
648
649
0
    return OGS_OK;
650
0
}
651
652
/* return values:
653
 * CORE_EINVAL     not an IP address; caller should see
654
 *                 if it is something else
655
 * CORE_BADIP      IP address portion is is not valid
656
 * CORE_BADMASK    mask portion is not valid
657
 */
658
static int parse_ip(
659
        ogs_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
660
0
{
661
    /* supported flavors of IP:
662
     *
663
     * . IPv6 numeric address string (e.g., "fe80::1")
664
     *
665
     *   IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address.
666
     *
667
     * . IPv4 numeric address string (e.g., "127.0.0.1")
668
     *
669
     * . IPv4 network string (e.g., "9.67")
670
     *
671
     *   IMPORTANT: This network form is only allowed if network_allowed is on.
672
     */
673
0
    int rc;
674
675
0
    rc = inet_pton(AF_INET6, ipstr, ipsub->sub);
676
0
    if (rc == 1) {
677
0
        if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
678
            /* ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
679
             * addresses; this of course forces the user to specify
680
             * IPv4 addresses in a.b.c.d style instead of ::ffff:a.b.c.d style.
681
             */
682
0
            ogs_error("Cannot support IPv4-mapped IPv6: "
683
0
                    "Use IPv4 address in a.b.c.d style "
684
0
                    "instead of ::ffff:a.b.c.d style");
685
0
            return OGS_ERROR;
686
0
        }
687
0
        ipsub->family = AF_INET6;
688
0
    } else {
689
0
        rc = inet_pton(AF_INET, ipstr, ipsub->sub);
690
0
        if (rc == 1) {
691
0
            ipsub->family = AF_INET;
692
0
        }
693
0
    }
694
695
0
    if (rc != 1) {
696
0
        if (network_allowed)
697
0
            return parse_network(ipsub, ipstr);
698
0
        else
699
0
            return OGS_ERROR;
700
0
    }
701
0
    return OGS_OK;
702
0
}
703
704
static int looks_like_ip(const char *ipstr)
705
0
{
706
0
    if (strlen(ipstr) == 0)
707
0
        return 0;
708
709
0
    if (strchr(ipstr, ':')) {
710
        /* definitely not a hostname;
711
         * assume it is intended to be an IPv6 address */
712
0
        return 1;
713
0
    }
714
715
    /* simple IPv4 address string check */
716
0
    while ((*ipstr == '.') || isdigit(*ipstr))
717
0
        ipstr++;
718
719
0
    return (*ipstr == '\0');
720
0
}
721
722
static void fix_subnet(ogs_ipsubnet_t *ipsub)
723
0
{
724
    /* in case caller specified more bits in network address than are
725
     * valid according to the mask, turn off the extra bits
726
     */
727
0
    int i;
728
729
0
    for (i = 0; i < sizeof ipsub->mask / sizeof(int32_t); i++)
730
0
        ipsub->sub[i] &= ipsub->mask[i];
731
0
}
732
733
/* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
734
int ogs_ipsubnet(ogs_ipsubnet_t *ipsub,
735
        const char *ipstr, const char *mask_or_numbits)
736
0
{
737
0
    int rv;
738
0
    char *endptr;
739
0
    long bits, maxbits = 32;
740
741
0
    ogs_assert(ipsub);
742
0
    ogs_assert(ipstr);
743
744
    /* filter out stuff which doesn't look remotely like an IP address;
745
     * this helps callers like mod_access which have a syntax allowing
746
     * hostname or IP address;
747
     * CORE_EINVAL tells the caller that it was probably not intended
748
     * to be an IP address
749
     */
750
0
    if (!looks_like_ip(ipstr)) {
751
0
        ogs_error("looks_like_ip(%s, %s) failed", ipstr, mask_or_numbits);
752
0
        return OGS_ERROR;
753
0
    }
754
755
    /* assume ipstr is an individual IP address, not a subnet */
756
0
    memset(ipsub->mask, 0xFF, sizeof ipsub->mask);
757
758
0
    rv = parse_ip(ipsub, ipstr, mask_or_numbits == NULL);
759
0
    if (rv != OGS_OK) {
760
0
        ogs_error("parse_ip(%s, %s) failed", ipstr, mask_or_numbits);
761
0
        return rv;
762
0
    }
763
764
0
    if (mask_or_numbits) {
765
0
        if (ipsub->family == AF_INET6) {
766
0
            maxbits = 128;
767
0
        }
768
0
        bits = strtol(mask_or_numbits, &endptr, 10);
769
0
        if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
770
            /* valid num-bits string; fill in mask appropriately */
771
0
            int cur_entry = 0;
772
0
            int32_t cur_bit_value;
773
774
0
            memset(ipsub->mask, 0, sizeof ipsub->mask);
775
0
            while (bits > 32) {
776
0
                ipsub->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
777
0
                bits -= 32;
778
0
                ++cur_entry;
779
0
            }
780
0
            cur_bit_value = 0x80000000;
781
0
            while (bits) {
782
0
                ipsub->mask[cur_entry] |= cur_bit_value;
783
0
                --bits;
784
0
                cur_bit_value /= 2;
785
0
            }
786
0
            ipsub->mask[cur_entry] = htobe32(ipsub->mask[cur_entry]);
787
0
        }
788
0
        else if (inet_pton(AF_INET, mask_or_numbits, ipsub->mask) == 1 &&
789
0
            ipsub->family == AF_INET) {
790
            /* valid IPv4 netmask */
791
0
        } else {
792
0
            ogs_error("Bad netmask %s", mask_or_numbits);
793
0
            return OGS_ERROR;
794
0
        }
795
0
    }
796
797
0
    fix_subnet(ipsub);
798
799
0
    return OGS_OK;
800
0
}
801
802
char *ogs_gethostname(ogs_sockaddr_t *addr)
803
0
{
804
0
    return addr->hostname;
805
0
}
806
807
char *ogs_ipstrdup(ogs_sockaddr_t *addr)
808
0
{
809
0
    char buf[OGS_ADDRSTRLEN + 1];
810
811
0
    ogs_assert(addr);
812
0
    memset(buf, 0, sizeof(buf));
813
814
0
    OGS_ADDR(addr, buf);
815
816
0
    return ogs_strdup(buf);
817
0
}
818
819
char *ogs_sockaddr_to_string_static(ogs_sockaddr_t *sa_list)
820
0
{
821
0
    static char dumpstr[OGS_HUGE_LEN] = { 0, };
822
0
    char *p, *last;
823
0
    ogs_sockaddr_t *addr = NULL;
824
825
0
    last = dumpstr + OGS_HUGE_LEN;
826
0
    p = dumpstr;
827
828
0
    addr = (ogs_sockaddr_t *)sa_list;
829
0
    while (addr) {
830
0
        char buf[OGS_ADDRSTRLEN];
831
0
        p = ogs_slprintf(p, last, "[%s]:%d ",
832
0
                OGS_ADDR(addr, buf), OGS_PORT(addr));
833
0
        addr = addr->next;
834
0
    }
835
836
0
    if (p > dumpstr) {
837
        /* If there is more than one addr, remove the last character */
838
0
        *(p-1) = 0;
839
840
0
        return dumpstr;
841
0
    }
842
843
    /* No address */
844
0
    return NULL;
845
0
}
846
847
int ogs_sockaddr_from_ip_or_fqdn(ogs_sockaddr_t **sa_list,
848
        int family, const char *ip_or_fqdn, uint16_t port)
849
0
{
850
0
    int rc;
851
0
    int flags = 0;
852
0
    ogs_sockaddr_t tmp;
853
854
0
    ogs_assert(sa_list);
855
0
    ogs_assert(ip_or_fqdn);
856
857
    /* Determine if the input is an IP literal (numeric address).
858
     * If so, use AI_NUMERICHOST to avoid DNS lookup. */
859
0
    if (ogs_inet_pton(AF_INET, ip_or_fqdn, &tmp) == OGS_OK ||
860
0
        ogs_inet_pton(AF_INET6, ip_or_fqdn, &tmp) == OGS_OK) {
861
0
        flags |= AI_NUMERICHOST;
862
0
    }
863
864
    /* Use ogs_addaddrinfo
865
     * to perform resolution and construct the sockaddr list */
866
0
    *sa_list = NULL;
867
0
    rc = ogs_addaddrinfo(sa_list, family, ip_or_fqdn, port, flags);
868
0
    if (rc != OGS_OK) {
869
0
        ogs_error("Failed to resolve address: %s", ip_or_fqdn);
870
        /* Cleanup: free any nodes that might have been added before failure */
871
0
        if (*sa_list) {
872
0
            ogs_freeaddrinfo(*sa_list);
873
0
            *sa_list = NULL;
874
0
        }
875
0
        return OGS_ERROR;
876
0
    }
877
878
0
    return OGS_OK;
879
0
}