Coverage Report

Created: 2026-10-15 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/curlx/inet_ntop.c
Line
Count
Source
1
/*
2
 * Copyright (C) 1996-2022  Internet Software Consortium.
3
 *
4
 * Permission to use, copy, modify, and distribute this software for any
5
 * purpose with or without fee is hereby granted, provided that the above
6
 * copyright notice and this permission notice appear in all copies.
7
 *
8
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
9
 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
10
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
11
 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
12
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
13
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
14
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
15
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 *
17
 * SPDX-License-Identifier: ISC
18
 */
19
#include "curl_setup.h"
20
21
#ifdef HAVE_SYS_PARAM_H
22
#include <sys/param.h>
23
#endif
24
#ifdef HAVE_NETINET_IN_H
25
#include <netinet/in.h>
26
#endif
27
#ifdef HAVE_ARPA_INET_H
28
#include <arpa/inet.h>
29
#endif
30
31
#include "curlx/inet_ntop.h"
32
#include "curlx/snprintf.h"
33
#include "curlx/strcopy.h"
34
35
3.55k
#define IN6ADDRSZ       16
36
/* #define INADDRSZ         4 */
37
1.85k
#define INT16SZ          2
38
39
/*
40
 * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
41
 * sure we have _some_ value for AF_INET6 without polluting our fake value
42
 * everywhere.
43
 */
44
#if !defined(USE_IPV6) && !defined(AF_INET6)
45
#define AF_INET6 (AF_INET + 1)
46
#endif
47
48
/*
49
 * Format an IPv4 address, more or less like inet_ntop().
50
 *
51
 * Returns CURLcode.
52
 * Note:
53
 *  - uses no static variables
54
 *  - takes an unsigned char* not an in_addr as input
55
 */
56
static CURLcode inet_ntop4(const unsigned char *src, char *dst, size_t size)
57
16
{
58
16
  char tmp[sizeof("255.255.255.255")];
59
16
  size_t len;
60
61
16
  DEBUGASSERT(size >= 16);
62
63
  /* this snprintf() does not overflow the buffer. */
64
16
  SNPRINTF(tmp, sizeof(tmp), "%d.%d.%d.%d",
65
16
           ((int)((unsigned char)src[0])) & 0xff,
66
16
           ((int)((unsigned char)src[1])) & 0xff,
67
16
           ((int)((unsigned char)src[2])) & 0xff,
68
16
           ((int)((unsigned char)src[3])) & 0xff);
69
70
16
  len = strlen(tmp);
71
16
  if(len == 0 || len >= size)
72
0
    return CURLE_TOO_LARGE;
73
16
  curlx_strcopy(dst, size, tmp, len);
74
16
  return CURLE_OK;
75
16
}
76
77
/*
78
 * Convert IPv6 binary address into presentation (printable) format.
79
 */
80
static CURLcode inet_ntop6(const unsigned char *src, char *dst, size_t size)
81
100
{
82
  /*
83
   * Note that int32_t and int16_t need only be "at least" large enough
84
   * to contain a value of the specified size. On some systems, like
85
   * Crays, there is no such thing as an integer variable with 16 bits.
86
   * Keep this in mind if you think this function should have been coded
87
   * to use pointer overlays. All the world's not a VAX.
88
   */
89
100
  char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
90
100
  char *tp;
91
100
  struct {
92
100
    int base;
93
100
    int len;
94
100
  } best, cur;
95
100
  unsigned int words[IN6ADDRSZ / INT16SZ];
96
100
  int i;
97
98
  /* Preprocess:
99
   *  Copy the input (bytewise) array into a wordwise array.
100
   *  Find the longest run of 0x00's in src[] for :: shorthanding.
101
   */
102
100
  memset(words, '\0', sizeof(words));
103
1.70k
  for(i = 0; i < IN6ADDRSZ; i++)
104
1.60k
    words[i / 2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3));
105
106
100
  best.base = -1;
107
100
  cur.base = -1;
108
100
  best.len = 0;
109
100
  cur.len = 0;
110
111
900
  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
112
800
    if(words[i] == 0) {
113
527
      if(cur.base == -1) {
114
126
        cur.base = i;
115
126
        cur.len = 1;
116
126
      }
117
401
      else
118
401
        cur.len++;
119
527
    }
120
273
    else if(cur.base != -1) {
121
98
      if(best.base == -1 || cur.len > best.len)
122
83
        best = cur;
123
98
      cur.base = -1;
124
98
    }
125
800
  }
126
100
  if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
127
19
    best = cur;
128
100
  if(best.base != -1 && best.len < 2)
129
3
    best.base = -1;
130
  /* Format the result. */
131
100
  tp = tmp;
132
868
  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
133
    /* Are we inside the best run of 0x00's? */
134
784
    if(best.base != -1 && i >= best.base && i < (best.base + best.len)) {
135
483
      if(i == best.base)
136
88
        *tp++ = ':';
137
483
      continue;
138
483
    }
139
140
    /* Are we following an initial run of 0x00s or any real hex?
141
     */
142
301
    if(i)
143
250
      *tp++ = ':';
144
145
    /* Is this address an encapsulated IPv4?
146
     */
147
301
    if(i == 6 && best.base == 0 &&
148
31
       (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
149
16
      CURLcode result = inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp));
150
16
      if(result)
151
0
        return result;
152
16
      tp += strlen(tp);
153
16
      break;
154
16
    }
155
285
    else {
156
      /* Lower-case digits. Cannot use the set from mprintf.c since this
157
         needs to work as a curlx function */
158
285
      static const unsigned char ldigits[] = "0123456789abcdef";
159
160
285
      unsigned int w = words[i];
161
      /* output lowercase 16-bit hex number but ignore leading zeroes */
162
285
      if(w & 0xf000)
163
58
        *tp++ = ldigits[(w & 0xf000) >> 12];
164
285
      if(w & 0xff00)
165
87
        *tp++ = ldigits[(w & 0x0f00) >> 8];
166
285
      if(w & 0xfff0)
167
113
        *tp++ = ldigits[(w & 0x00f0) >> 4];
168
285
      *tp++ = ldigits[(w & 0x000f)];
169
285
    }
170
301
  }
171
172
  /* Was it a trailing run of 0x00's?
173
   */
174
100
  if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
175
18
    *tp++ = ':';
176
177
  /* Check for overflow, copy, and we are done. */
178
100
  if((size_t)(tp - tmp) >= size)
179
18
    return CURLE_TOO_LARGE;
180
82
  curlx_strcopy(dst, size, tmp, tp - tmp);
181
82
  return CURLE_OK;
182
100
}
183
184
/*
185
 * Convert a network format address to presentation format.
186
 *
187
 * Copies result to 'buf' and returns CURLcode.
188
 */
189
CURLcode curlx_inet_ntop(int af, const void *src, char *buf, size_t size)
190
100
{
191
100
  switch(af) {
192
0
  case AF_INET:
193
0
    return inet_ntop4((const unsigned char *)src, buf, size);
194
100
  case AF_INET6:
195
100
    return inet_ntop6((const unsigned char *)src, buf, size);
196
0
  default:
197
0
    return CURLE_UNSUPPORTED_PROTOCOL;
198
100
  }
199
100
}