Coverage Report

Created: 2026-08-31 06:46

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
0
#define IN6ADDRSZ       16
36
/* #define INADDRSZ         4 */
37
0
#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
0
{
58
0
  char tmp[sizeof("255.255.255.255")];
59
0
  size_t len;
60
61
0
  DEBUGASSERT(size >= 16);
62
63
  /* this snprintf() does not overflow the buffer. */
64
0
  SNPRINTF(tmp, sizeof(tmp), "%d.%d.%d.%d",
65
0
           ((int)((unsigned char)src[0])) & 0xff,
66
0
           ((int)((unsigned char)src[1])) & 0xff,
67
0
           ((int)((unsigned char)src[2])) & 0xff,
68
0
           ((int)((unsigned char)src[3])) & 0xff);
69
70
0
  len = strlen(tmp);
71
0
  if(len == 0 || len >= size)
72
0
    return CURLE_TOO_LARGE;
73
0
  curlx_strcopy(dst, size, tmp, len);
74
0
  return CURLE_OK;
75
0
}
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
0
{
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
0
  char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
90
0
  char *tp;
91
0
  struct {
92
0
    int base;
93
0
    int len;
94
0
  } best, cur;
95
0
  unsigned int words[IN6ADDRSZ / INT16SZ];
96
0
  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
0
  memset(words, '\0', sizeof(words));
103
0
  for(i = 0; i < IN6ADDRSZ; i++)
104
0
    words[i / 2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3));
105
106
0
  best.base = -1;
107
0
  cur.base = -1;
108
0
  best.len = 0;
109
0
  cur.len = 0;
110
111
0
  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
112
0
    if(words[i] == 0) {
113
0
      if(cur.base == -1) {
114
0
        cur.base = i;
115
0
        cur.len = 1;
116
0
      }
117
0
      else
118
0
        cur.len++;
119
0
    }
120
0
    else if(cur.base != -1) {
121
0
      if(best.base == -1 || cur.len > best.len)
122
0
        best = cur;
123
0
      cur.base = -1;
124
0
    }
125
0
  }
126
0
  if((cur.base != -1) && (best.base == -1 || cur.len > best.len))
127
0
    best = cur;
128
0
  if(best.base != -1 && best.len < 2)
129
0
    best.base = -1;
130
  /* Format the result. */
131
0
  tp = tmp;
132
0
  for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
133
    /* Are we inside the best run of 0x00's? */
134
0
    if(best.base != -1 && i >= best.base && i < (best.base + best.len)) {
135
0
      if(i == best.base)
136
0
        *tp++ = ':';
137
0
      continue;
138
0
    }
139
140
    /* Are we following an initial run of 0x00s or any real hex?
141
     */
142
0
    if(i)
143
0
      *tp++ = ':';
144
145
    /* Is this address an encapsulated IPv4?
146
     */
147
0
    if(i == 6 && best.base == 0 &&
148
0
       (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
149
0
      CURLcode result = inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp));
150
0
      if(result)
151
0
        return result;
152
0
      tp += strlen(tp);
153
0
      break;
154
0
    }
155
0
    else {
156
      /* Lower-case digits. Cannot use the set from mprintf.c since this
157
         needs to work as a curlx function */
158
0
      static const unsigned char ldigits[] = "0123456789abcdef";
159
160
0
      unsigned int w = words[i];
161
      /* output lowercase 16-bit hex number but ignore leading zeroes */
162
0
      if(w & 0xf000)
163
0
        *tp++ = ldigits[(w & 0xf000) >> 12];
164
0
      if(w & 0xff00)
165
0
        *tp++ = ldigits[(w & 0x0f00) >> 8];
166
0
      if(w & 0xfff0)
167
0
        *tp++ = ldigits[(w & 0x00f0) >> 4];
168
0
      *tp++ = ldigits[(w & 0x000f)];
169
0
    }
170
0
  }
171
172
  /* Was it a trailing run of 0x00's?
173
   */
174
0
  if(best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
175
0
    *tp++ = ':';
176
177
  /* Check for overflow, copy, and we are done. */
178
0
  if((size_t)(tp - tmp) >= size)
179
0
    return CURLE_TOO_LARGE;
180
0
  curlx_strcopy(dst, size, tmp, tp - tmp);
181
0
  return CURLE_OK;
182
0
}
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
0
{
191
0
  switch(af) {
192
0
  case AF_INET:
193
0
    return inet_ntop4((const unsigned char *)src, buf, size);
194
0
  case AF_INET6:
195
0
    return inet_ntop6((const unsigned char *)src, buf, size);
196
0
  default:
197
0
    return CURLE_UNSUPPORTED_PROTOCOL;
198
0
  }
199
0
}