Coverage Report

Created: 2023-09-25 07:12

/src/open5gs/lib/core/ogs-sockaddr.c
Line
Count
Source (jump to first uncovered line)
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
/* If you want to use getnameinfo,
59
 * you need to consider DNS query delay (about 10 seconds) */
60
#if 0
61
int ogs_getnameinfo(
62
    char *hostname, socklen_t hostname_len, ogs_sockaddr_t *addr, int flags)
63
{
64
    ogs_assert(hostname);
65
    ogs_assert(addr);
66
67
    return getnameinfo(&addr->sa, ogs_sockaddr_len(addr),
68
            hostname, hostname_len,
69
            NULL, 0, flags != 0 ? flags : NI_NAMEREQD);
70
}
71
#endif
72
73
int ogs_getaddrinfo(ogs_sockaddr_t **sa_list,
74
        int family, const char *hostname, uint16_t port, int flags)
75
0
{
76
0
    *sa_list = NULL;
77
0
    return ogs_addaddrinfo(sa_list, family, hostname, port, flags);
78
0
}
79
80
int ogs_freeaddrinfo(ogs_sockaddr_t *sa_list)
81
0
{
82
0
    ogs_sockaddr_t *next = NULL, *addr = NULL;
83
84
0
    addr = sa_list;
85
0
    while (addr) {
86
0
        next = addr->next;
87
0
        if (addr->hostname)
88
0
            ogs_free(addr->hostname);
89
0
        ogs_free(addr);
90
0
        addr = next;
91
0
    }
92
93
0
    return OGS_OK;
94
0
}
95
96
int ogs_addaddrinfo(ogs_sockaddr_t **sa_list,
97
        int family, const char *hostname, uint16_t port, int flags)
98
0
{
99
0
    int rc;
100
0
    char service[NI_MAXSERV];
101
0
    struct addrinfo hints, *ai, *ai_list;
102
0
    ogs_sockaddr_t *prev;
103
0
    char buf[OGS_ADDRSTRLEN];
104
105
0
    ogs_assert(sa_list);
106
107
0
    memset(&hints, 0, sizeof(hints));
108
0
    hints.ai_family = family;
109
0
    hints.ai_socktype = SOCK_STREAM;
110
0
    hints.ai_flags = flags;
111
112
0
    ogs_snprintf(service, sizeof(service), "%u", port);
113
114
0
    rc = getaddrinfo(hostname, service, &hints, &ai_list);
115
0
    if (rc != 0) {
116
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
117
0
                "getaddrinfo(%d:%s:%d:0x%x) failed",
118
0
                family, hostname, port, flags);
119
0
        return OGS_ERROR;
120
0
    }
121
122
0
    prev = NULL;
123
0
    if (*sa_list) {
124
0
        prev = *sa_list;
125
0
        while(prev->next) prev = prev->next;
126
0
    }
127
0
    for (ai = ai_list; ai; ai = ai->ai_next) {
128
0
        ogs_sockaddr_t *new, tmp;
129
0
        if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
130
0
            continue;
131
132
0
        new = ogs_calloc(1, sizeof(ogs_sockaddr_t));
133
0
        if (!new) {
134
0
            ogs_error("ogs_calloc() failed");
135
0
            return OGS_ERROR;
136
0
        }
137
0
        memcpy(&new->sa, ai->ai_addr, ai->ai_addrlen);
138
0
        new->ogs_sin_port = htobe16(port);
139
140
0
        if (hostname) {
141
0
            if (ogs_inet_pton(ai->ai_family, hostname, &tmp) == OGS_OK) {
142
                /* It's a valid IP address */
143
0
                ogs_debug("addr:%s, port:%d", OGS_ADDR(new, buf), port);
144
0
            } else {
145
                /* INVALID IP address! We assume it is a hostname */
146
0
                new->hostname = ogs_strdup(hostname);
147
0
                ogs_assert(new->hostname);
148
0
                ogs_debug("name:%s, port:%d", new->hostname, port);
149
0
            }
150
0
        }
151
152
0
        if (!prev)
153
0
            *sa_list = new;
154
0
        else
155
0
            prev->next = new;
156
157
0
        prev = new;
158
0
    }
159
160
0
    freeaddrinfo(ai_list);
161
162
0
    if (prev == NULL) {
163
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
164
0
                "ogs_getaddrinfo(%d:%s:%d:%d) failed",
165
0
                family, hostname, port, flags);
166
0
        return OGS_ERROR;
167
0
    }
168
169
0
    return OGS_OK;
170
0
}
171
172
int ogs_filteraddrinfo(ogs_sockaddr_t **sa_list, int family)
173
0
{
174
0
    ogs_sockaddr_t *addr = NULL, *prev = NULL, *next = NULL;
175
176
0
    ogs_assert(sa_list);
177
178
0
    prev = NULL;
179
0
    addr = *sa_list;
180
0
    while (addr) {
181
0
        next = addr->next;
182
183
0
        if (addr->ogs_sa_family != family) {
184
0
            if (prev)
185
0
                prev->next = addr->next;
186
0
            else
187
0
                *sa_list = addr->next;
188
0
            if (addr->hostname)
189
0
                ogs_free(addr->hostname);
190
0
            ogs_free(addr);
191
192
0
        } else {
193
0
            prev = addr;
194
0
        }
195
196
0
        addr = next;
197
0
    }
198
199
0
    return OGS_OK;
200
0
}
201
202
int ogs_copyaddrinfo(ogs_sockaddr_t **dst, const ogs_sockaddr_t *src)
203
0
{
204
0
    ogs_sockaddr_t *d;
205
0
    const ogs_sockaddr_t *s;
206
207
0
    for (*dst = d = NULL, s = src; s; s = s->next) {
208
0
        if (!d) {
209
0
            *dst = d = ogs_memdup(s, sizeof *s);
210
0
            if (!(*dst)) {
211
0
                ogs_error("ogs_memdup() failed");
212
0
                return OGS_ERROR;
213
0
            }
214
0
        } else {
215
0
            d = d->next = ogs_memdup(s, sizeof *s);
216
0
            if (!d) {
217
0
                ogs_error("ogs_memdup() failed");
218
0
                return OGS_ERROR;
219
0
            }
220
0
        }
221
0
        if (s->hostname) {
222
0
            if (s == src || s->hostname != src->hostname) {
223
0
                d->hostname = ogs_strdup(s->hostname);
224
0
                if (!d->hostname) {
225
0
                    ogs_error("ogs_memdup() failed");
226
0
                    return OGS_ERROR;
227
0
                }
228
0
            } else {
229
0
                d->hostname = (*dst)->hostname;
230
0
            }
231
0
        }
232
0
    }
233
234
0
    return OGS_OK;
235
0
}
236
237
int ogs_sortaddrinfo(ogs_sockaddr_t **sa_list, int family)
238
0
{
239
0
    ogs_sockaddr_t *head = NULL, *addr = NULL, *new = NULL, *old = NULL;
240
241
0
    ogs_assert(sa_list);
242
243
0
    old = *sa_list;
244
0
    while (old) {
245
0
        addr = old;
246
247
0
        old = old->next;
248
249
0
        if (head == NULL || addr->ogs_sa_family == family) {
250
0
            addr->next = head;
251
0
            head = addr;
252
0
        } else {
253
0
            new = head;
254
0
            while(new->next != NULL && new->next->ogs_sa_family != family) {
255
0
                new = new->next;
256
0
            }
257
0
            addr->next = new->next;
258
0
            new->next = addr;
259
0
        }
260
0
    }
261
262
0
    *sa_list = head;
263
264
0
    return OGS_OK;
265
0
}
266
267
ogs_sockaddr_t *ogs_link_local_addr(const char *dev, const ogs_sockaddr_t *sa)
268
0
{
269
0
#if defined(HAVE_GETIFADDRS)
270
0
    struct ifaddrs *iflist, *cur;
271
0
    int rc;
272
273
0
    rc = getifaddrs(&iflist);
274
0
    if (rc != 0) {
275
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "getifaddrs failed");
276
0
        return NULL;
277
0
    }
278
279
0
    for (cur = iflist; cur != NULL; cur = cur->ifa_next) {
280
0
        ogs_sockaddr_t *ifa_addr = NULL;
281
0
        ogs_sockaddr_t *addr = NULL;
282
283
0
        ifa_addr = (ogs_sockaddr_t *)cur->ifa_addr;
284
285
0
        if (ifa_addr == NULL) /* may happen with ppp interfaces */
286
0
            continue;
287
288
0
        if (ifa_addr->ogs_sa_family == AF_INET)
289
0
            continue;
290
291
0
        if (!IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6.sin6_addr))
292
0
            continue;
293
294
0
        if (dev && strcmp(dev, cur->ifa_name) != 0)
295
0
            continue;
296
297
0
        if (sa && memcmp(&sa->sin6.sin6_addr,
298
0
                &ifa_addr->sin6.sin6_addr, sizeof(struct in6_addr)) != 0)
299
0
            continue;
300
301
0
        addr = ogs_calloc(1, sizeof(ogs_sockaddr_t));
302
0
        if (!addr) {
303
0
            ogs_error("ogs_calloc() failed");
304
0
            return NULL;
305
0
        }
306
0
        ogs_assert(addr);
307
0
        memcpy(&addr->sa, cur->ifa_addr, ogs_sockaddr_len(cur->ifa_addr));
308
309
0
        freeifaddrs(iflist);
310
0
        return addr;
311
0
    }
312
313
0
    freeifaddrs(iflist);
314
0
#endif
315
0
    return NULL;
316
0
}
317
318
ogs_sockaddr_t *ogs_link_local_addr_by_dev(const char *dev)
319
0
{
320
0
    ogs_assert(dev);
321
0
    return ogs_link_local_addr(dev, NULL);
322
0
}
323
324
ogs_sockaddr_t *ogs_link_local_addr_by_sa(const ogs_sockaddr_t *sa)
325
0
{
326
0
    ogs_assert(sa);
327
0
    return ogs_link_local_addr(NULL, sa);
328
0
}
329
330
int ogs_filter_ip_version(ogs_sockaddr_t **addr,
331
        int no_ipv4, int no_ipv6, int prefer_ipv4)
332
0
{
333
0
    int rv;
334
335
0
    if (no_ipv4 == 1) {
336
0
        rv = ogs_filteraddrinfo(addr, AF_INET6);
337
0
        ogs_assert(rv == OGS_OK);
338
0
    }
339
0
    if (no_ipv6 == 1) {
340
0
        rv = ogs_filteraddrinfo(addr, AF_INET);
341
0
        ogs_assert(rv == OGS_OK);
342
0
    }
343
344
0
    if (prefer_ipv4 == 1) {
345
0
        rv = ogs_sortaddrinfo(addr, AF_INET);
346
0
        ogs_assert(rv == OGS_OK);
347
0
    } else {
348
0
        rv = ogs_sortaddrinfo(addr, AF_INET6);
349
0
        ogs_assert(rv == OGS_OK);
350
0
    }
351
352
0
    return OGS_OK;
353
0
}
354
355
356
const char *ogs_inet_ntop(void *sa, char *buf, int buflen)
357
0
{
358
0
    int family;
359
0
    ogs_sockaddr_t *sockaddr = NULL;
360
361
0
    sockaddr = sa;
362
0
    ogs_assert(sockaddr);
363
0
    ogs_assert(buf);
364
0
    ogs_assert(buflen >= OGS_ADDRSTRLEN);
365
366
0
    family = sockaddr->ogs_sa_family;
367
0
    switch (family) {
368
0
    case AF_INET:
369
0
        return inet_ntop(family, &sockaddr->sin.sin_addr, buf,
370
0
                INET_ADDRSTRLEN);
371
0
    case AF_INET6:
372
0
        return inet_ntop(family, &sockaddr->sin6.sin6_addr, buf,
373
0
                INET6_ADDRSTRLEN);
374
0
    default:
375
0
        ogs_fatal("Unknown family(%d)", family);
376
0
        ogs_abort();
377
0
        return NULL;
378
0
    }
379
0
}
380
381
int ogs_inet_pton(int family, const char *src, void *sa)
382
0
{
383
0
    ogs_sockaddr_t *dst = NULL;
384
385
0
    ogs_assert(src);
386
0
    dst = sa;
387
0
    ogs_assert(dst);
388
389
0
    dst->ogs_sa_family = family;
390
0
    switch(family) {
391
0
    case AF_INET:
392
0
        return inet_pton(family, src, &dst->sin.sin_addr) == 1 ?
393
0
            OGS_OK : OGS_ERROR;
394
0
    case AF_INET6:
395
0
        return inet_pton(family, src, &dst->sin6.sin6_addr) == 1 ?
396
0
             OGS_OK : OGS_ERROR;
397
0
    default:
398
0
        ogs_fatal("Unknown family(%d)", family);
399
0
        ogs_abort();
400
0
        return OGS_ERROR;
401
0
    }
402
0
}
403
404
socklen_t ogs_sockaddr_len(const void *sa)
405
0
{
406
0
    const ogs_sockaddr_t *sockaddr = sa;
407
408
0
    ogs_assert(sa);
409
410
0
    switch(sockaddr->ogs_sa_family) {
411
0
    case AF_INET:
412
0
        return sizeof(struct sockaddr_in);
413
0
    case AF_INET6:
414
0
        return sizeof(struct sockaddr_in6);
415
0
    default:
416
0
        ogs_fatal("Unknown family(%d)", sockaddr->ogs_sa_family);
417
0
        ogs_abort();
418
0
        return OGS_ERROR;
419
0
    }
420
0
}
421
422
bool ogs_sockaddr_is_equal(void *p, void *q)
423
0
{
424
0
    ogs_sockaddr_t *a, *b;
425
426
0
    a = p;
427
0
    ogs_assert(a);
428
0
    b = q;
429
0
    ogs_assert(b);
430
431
0
    if (a->ogs_sa_family != b->ogs_sa_family)
432
0
        return false;
433
434
0
    switch (a->ogs_sa_family) {
435
0
    case AF_INET:
436
0
        if (a->sin.sin_port != b->sin.sin_port)
437
0
            return false;
438
0
        if (memcmp(&a->sin.sin_addr, &b->sin.sin_addr, sizeof(struct in_addr)) != 0)
439
0
            return false;
440
0
        return true;
441
0
    case AF_INET6:
442
0
        if (a->sin6.sin6_port != b->sin6.sin6_port)
443
0
            return false;
444
0
        if (memcmp(&a->sin6.sin6_addr, &b->sin6.sin6_addr, sizeof(struct in6_addr)) != 0)
445
0
            return false;
446
0
        return true;
447
0
    default:
448
0
        ogs_error("Unexpected address faimily %u", a->ogs_sa_family);
449
0
        ogs_abort();
450
0
    }
451
0
}
452
453
static int parse_network(ogs_ipsubnet_t *ipsub, const char *network)
454
0
{
455
    /* legacy syntax for ip addrs: a.b.c. ==> a.b.c.0/24 for example */
456
0
    int shift;
457
0
    char *s, *t;
458
0
    int octet;
459
0
    char buf[sizeof "255.255.255.255"];
460
461
0
    if (strlen(network) < sizeof buf)
462
0
        strcpy(buf, network);
463
0
    else
464
0
        return OGS_ERROR;
465
466
    /* parse components */
467
0
    s = buf;
468
0
    ipsub->sub[0] = 0;
469
0
    ipsub->mask[0] = 0;
470
0
    shift = 24;
471
0
    while (*s) {
472
0
        t = s;
473
0
        if (!isdigit(*t))
474
0
            return OGS_ERROR;
475
476
0
        while (isdigit(*t))
477
0
            ++t;
478
479
0
        if (*t == '.')
480
0
            *t++ = 0;
481
0
        else if (*t)
482
0
            return OGS_ERROR;
483
484
0
        if (shift < 0)
485
0
            return OGS_ERROR;
486
487
0
        octet = atoi(s);
488
0
        if (octet < 0 || octet > 255)
489
0
            return OGS_ERROR;
490
491
0
        ipsub->sub[0] |= octet << shift;
492
0
        ipsub->mask[0] |= 0xFFUL << shift;
493
0
        s = t;
494
0
        shift -= 8;
495
0
    }
496
0
    ipsub->sub[0] = be32toh(ipsub->sub[0]);
497
0
    ipsub->mask[0] = be32toh(ipsub->mask[0]);
498
0
    ipsub->family = AF_INET;
499
500
0
    return OGS_OK;
501
0
}
502
503
/* return values:
504
 * CORE_EINVAL     not an IP address; caller should see
505
 *                 if it is something else
506
 * CORE_BADIP      IP address portion is is not valid
507
 * CORE_BADMASK    mask portion is not valid
508
 */
509
static int parse_ip(
510
        ogs_ipsubnet_t *ipsub, const char *ipstr, int network_allowed)
511
0
{
512
    /* supported flavors of IP:
513
     *
514
     * . IPv6 numeric address string (e.g., "fe80::1")
515
     *
516
     *   IMPORTANT: Don't store IPv4-mapped IPv6 address as an IPv6 address.
517
     *
518
     * . IPv4 numeric address string (e.g., "127.0.0.1")
519
     *
520
     * . IPv4 network string (e.g., "9.67")
521
     *
522
     *   IMPORTANT: This network form is only allowed if network_allowed is on.
523
     */
524
0
    int rc;
525
526
0
    rc = inet_pton(AF_INET6, ipstr, ipsub->sub);
527
0
    if (rc == 1) {
528
0
        if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ipsub->sub)) {
529
            /* ipsubnet_test() assumes that we don't create IPv4-mapped IPv6
530
             * addresses; this of course forces the user to specify
531
             * IPv4 addresses in a.b.c.d style instead of ::ffff:a.b.c.d style.
532
             */
533
0
            ogs_error("Cannot support IPv4-mapped IPv6: "
534
0
                    "Use IPv4 address in a.b.c.d style "
535
0
                    "instead of ::ffff:a.b.c.d style");
536
0
            return OGS_ERROR;
537
0
        }
538
0
        ipsub->family = AF_INET6;
539
0
    } else {
540
0
        rc = inet_pton(AF_INET, ipstr, ipsub->sub);
541
0
        if (rc == 1) {
542
0
            ipsub->family = AF_INET;
543
0
        }
544
0
    }
545
546
0
    if (rc != 1) {
547
0
        if (network_allowed)
548
0
            return parse_network(ipsub, ipstr);
549
0
        else
550
0
            return OGS_ERROR;
551
0
    }
552
0
    return OGS_OK;
553
0
}
554
555
static int looks_like_ip(const char *ipstr)
556
0
{
557
0
    if (strlen(ipstr) == 0)
558
0
        return 0;
559
560
0
    if (strchr(ipstr, ':')) {
561
        /* definitely not a hostname;
562
         * assume it is intended to be an IPv6 address */
563
0
        return 1;
564
0
    }
565
566
    /* simple IPv4 address string check */
567
0
    while ((*ipstr == '.') || isdigit(*ipstr))
568
0
        ipstr++;
569
570
0
    return (*ipstr == '\0');
571
0
}
572
573
static void fix_subnet(ogs_ipsubnet_t *ipsub)
574
0
{
575
    /* in case caller specified more bits in network address than are
576
     * valid according to the mask, turn off the extra bits
577
     */
578
0
    int i;
579
580
0
    for (i = 0; i < sizeof ipsub->mask / sizeof(int32_t); i++)
581
0
        ipsub->sub[i] &= ipsub->mask[i];
582
0
}
583
584
/* be sure not to store any IPv4 address as a v4-mapped IPv6 address */
585
int ogs_ipsubnet(ogs_ipsubnet_t *ipsub,
586
        const char *ipstr, const char *mask_or_numbits)
587
0
{
588
0
    int rv;
589
0
    char *endptr;
590
0
    long bits, maxbits = 32;
591
592
0
    ogs_assert(ipsub);
593
0
    ogs_assert(ipstr);
594
595
    /* filter out stuff which doesn't look remotely like an IP address;
596
     * this helps callers like mod_access which have a syntax allowing
597
     * hostname or IP address;
598
     * CORE_EINVAL tells the caller that it was probably not intended
599
     * to be an IP address
600
     */
601
0
    if (!looks_like_ip(ipstr)) {
602
0
        ogs_error("looks_like_ip(%s, %s) failed", ipstr, mask_or_numbits);
603
0
        return OGS_ERROR;
604
0
    }
605
606
    /* assume ipstr is an individual IP address, not a subnet */
607
0
    memset(ipsub->mask, 0xFF, sizeof ipsub->mask);
608
609
0
    rv = parse_ip(ipsub, ipstr, mask_or_numbits == NULL);
610
0
    if (rv != OGS_OK) {
611
0
        ogs_error("parse_ip(%s, %s) failed", ipstr, mask_or_numbits);
612
0
        return rv;
613
0
    }
614
615
0
    if (mask_or_numbits) {
616
0
        if (ipsub->family == AF_INET6) {
617
0
            maxbits = 128;
618
0
        }
619
0
        bits = strtol(mask_or_numbits, &endptr, 10);
620
0
        if (*endptr == '\0' && bits > 0 && bits <= maxbits) {
621
            /* valid num-bits string; fill in mask appropriately */
622
0
            int cur_entry = 0;
623
0
            int32_t cur_bit_value;
624
625
0
            memset(ipsub->mask, 0, sizeof ipsub->mask);
626
0
            while (bits > 32) {
627
0
                ipsub->mask[cur_entry] = 0xFFFFFFFF; /* all 32 bits */
628
0
                bits -= 32;
629
0
                ++cur_entry;
630
0
            }
631
0
            cur_bit_value = 0x80000000;
632
0
            while (bits) {
633
0
                ipsub->mask[cur_entry] |= cur_bit_value;
634
0
                --bits;
635
0
                cur_bit_value /= 2;
636
0
            }
637
0
            ipsub->mask[cur_entry] = htobe32(ipsub->mask[cur_entry]);
638
0
        }
639
0
        else if (inet_pton(AF_INET, mask_or_numbits, ipsub->mask) == 1 &&
640
0
            ipsub->family == AF_INET) {
641
            /* valid IPv4 netmask */
642
0
        } else {
643
0
            ogs_error("Bad netmask %s", mask_or_numbits);
644
0
            return OGS_ERROR;
645
0
        }
646
0
    }
647
648
0
    fix_subnet(ipsub);
649
650
0
    return OGS_OK;
651
0
}
652
653
char *ogs_gethostname(ogs_sockaddr_t *addr)
654
0
{
655
0
    return addr->hostname;
656
0
}
657
658
char *ogs_ipstrdup(ogs_sockaddr_t *addr)
659
0
{
660
0
    char buf[OGS_ADDRSTRLEN + 1];
661
662
0
    ogs_assert(addr);
663
0
    memset(buf, 0, sizeof(buf));
664
665
0
    OGS_ADDR(addr, buf);
666
667
0
    return ogs_strdup(buf);
668
0
}