Coverage Report

Created: 2025-12-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/if2ip.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifdef HAVE_NETINET_IN_H
28
#  include <netinet/in.h>
29
#endif
30
#ifdef HAVE_ARPA_INET_H
31
#  include <arpa/inet.h>
32
#endif
33
#ifdef HAVE_NET_IF_H
34
#  include <net/if.h>
35
#endif
36
#ifdef HAVE_SYS_IOCTL_H
37
#  include <sys/ioctl.h>
38
#endif
39
#ifdef HAVE_NETDB_H
40
#  include <netdb.h>
41
#endif
42
#ifdef HAVE_SYS_SOCKIO_H
43
#  include <sys/sockio.h>
44
#endif
45
#ifdef HAVE_IFADDRS_H
46
#  include <ifaddrs.h>
47
#endif
48
#ifdef HAVE_STROPTS_H
49
#  include <stropts.h>
50
#endif
51
#ifdef __VMS
52
#  include <inet.h>
53
#endif
54
55
#include "curlx/inet_ntop.h"
56
#include "if2ip.h"
57
58
/* ------------------------------------------------------------------ */
59
60
#ifdef USE_IPV6
61
/* Return the scope of the given address. */
62
unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
63
0
{
64
0
  if(sa->sa_family == AF_INET6) {
65
0
    const struct sockaddr_in6 * sa6 =
66
0
      (const struct sockaddr_in6 *)(const void *) sa;
67
0
    const unsigned char *b = sa6->sin6_addr.s6_addr;
68
0
    unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
69
70
0
    if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */
71
0
      return IPV6_SCOPE_UNIQUELOCAL;
72
0
    switch(w & 0xFFC0) {
73
0
    case 0xFE80:
74
0
      return IPV6_SCOPE_LINKLOCAL;
75
0
    case 0xFEC0:
76
0
      return IPV6_SCOPE_SITELOCAL;
77
0
    case 0x0000:
78
0
      w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
79
0
          b[10] | b[11] | b[12] | b[13] | b[14];
80
0
      if(w || b[15] != 0x01)
81
0
        break;
82
0
      return IPV6_SCOPE_NODELOCAL;
83
0
    default:
84
0
      break;
85
0
    }
86
0
  }
87
0
  return IPV6_SCOPE_GLOBAL;
88
0
}
89
#endif
90
91
#if !defined(CURL_DISABLE_BINDLOCAL) || !defined(CURL_DISABLE_FTP)
92
93
#ifdef HAVE_GETIFADDRS
94
95
if2ip_result_t Curl_if2ip(int af,
96
#ifdef USE_IPV6
97
                          unsigned int remote_scope,
98
                          unsigned int local_scope_id,
99
#endif
100
                          const char *interf,
101
                          char *buf, size_t buf_size)
102
0
{
103
0
  struct ifaddrs *iface, *head;
104
0
  if2ip_result_t res = IF2IP_NOT_FOUND;
105
106
#if defined(USE_IPV6) && \
107
    !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
108
  (void)local_scope_id;
109
#endif
110
111
0
  if(getifaddrs(&head) >= 0) {
112
0
    for(iface = head; iface != NULL; iface = iface->ifa_next) {
113
0
      if(iface->ifa_addr) {
114
0
        if(iface->ifa_addr->sa_family == af) {
115
0
          if(curl_strequal(iface->ifa_name, interf)) {
116
0
            void *addr;
117
0
            const char *ip;
118
0
            char scope[12] = "";
119
0
            char ipstr[64];
120
0
#ifdef USE_IPV6
121
0
            if(af == AF_INET6) {
122
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
123
0
              unsigned int scopeid = 0;
124
0
#endif
125
0
              unsigned int ifscope = Curl_ipv6_scope(iface->ifa_addr);
126
127
0
              if(ifscope != remote_scope) {
128
                /* We are interested only in interface addresses whose scope
129
                   matches the remote address we want to connect to: global
130
                   for global, link-local for link-local, etc... */
131
0
                if(res == IF2IP_NOT_FOUND)
132
0
                  res = IF2IP_AF_NOT_SUPPORTED;
133
0
                continue;
134
0
              }
135
136
0
              addr =
137
0
                &((struct sockaddr_in6 *)(void *)iface->ifa_addr)->sin6_addr;
138
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
139
              /* Include the scope of this interface as part of the address */
140
0
              scopeid = ((struct sockaddr_in6 *)(void *)iface->ifa_addr)
141
0
                            ->sin6_scope_id;
142
143
              /* If given, scope id should match. */
144
0
              if(local_scope_id && scopeid != local_scope_id) {
145
0
                if(res == IF2IP_NOT_FOUND)
146
0
                  res = IF2IP_AF_NOT_SUPPORTED;
147
148
0
                continue;
149
0
              }
150
151
0
              if(scopeid)
152
0
                curl_msnprintf(scope, sizeof(scope), "%%%u", scopeid);
153
0
#endif
154
0
            }
155
0
            else
156
0
#endif
157
0
              addr =
158
0
                &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
159
0
            res = IF2IP_FOUND;
160
0
            ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr));
161
0
            curl_msnprintf(buf, buf_size, "%s%s", ip, scope);
162
0
            break;
163
0
          }
164
0
        }
165
0
        else if((res == IF2IP_NOT_FOUND) &&
166
0
                curl_strequal(iface->ifa_name, interf)) {
167
0
          res = IF2IP_AF_NOT_SUPPORTED;
168
0
        }
169
0
      }
170
0
    }
171
172
0
    freeifaddrs(head);
173
0
  }
174
175
0
  return res;
176
0
}
177
178
#elif defined(HAVE_IOCTL_SIOCGIFADDR)
179
180
if2ip_result_t Curl_if2ip(int af,
181
#ifdef USE_IPV6
182
                          unsigned int remote_scope,
183
                          unsigned int local_scope_id,
184
#endif
185
                          const char *interf,
186
                          char *buf, size_t buf_size)
187
{
188
  struct ifreq req;
189
  struct in_addr in;
190
  struct sockaddr_in *s;
191
  curl_socket_t dummy;
192
  size_t len;
193
  const char *r;
194
195
#ifdef USE_IPV6
196
  (void)remote_scope;
197
  (void)local_scope_id;
198
#endif
199
200
  if(!interf || (af != AF_INET))
201
    return IF2IP_NOT_FOUND;
202
203
  len = strlen(interf);
204
  if(len >= sizeof(req.ifr_name))
205
    return IF2IP_NOT_FOUND;
206
207
  dummy = CURL_SOCKET(AF_INET, SOCK_STREAM, 0);
208
  if(CURL_SOCKET_BAD == dummy)
209
    return IF2IP_NOT_FOUND;
210
211
  memset(&req, 0, sizeof(req));
212
  memcpy(req.ifr_name, interf, len + 1);
213
  req.ifr_addr.sa_family = AF_INET;
214
215
#if defined(__GNUC__) && defined(_AIX)
216
/* Suppress warning inside system headers */
217
#pragma GCC diagnostic push
218
#pragma GCC diagnostic ignored "-Wshift-sign-overflow"
219
#endif
220
  if(ioctl(dummy, SIOCGIFADDR, &req) < 0) {
221
#if defined(__GNUC__) && defined(_AIX)
222
#pragma GCC diagnostic pop
223
#endif
224
    sclose(dummy);
225
    /* With SIOCGIFADDR, we cannot tell the difference between an interface
226
       that does not exist and an interface that has no address of the
227
       correct family. Assume the interface does not exist */
228
    return IF2IP_NOT_FOUND;
229
  }
230
231
  s = (struct sockaddr_in *)(void *)&req.ifr_addr;
232
  memcpy(&in, &s->sin_addr, sizeof(in));
233
  r = curlx_inet_ntop(s->sin_family, &in, buf, buf_size);
234
235
  sclose(dummy);
236
  if(!r)
237
    return IF2IP_NOT_FOUND;
238
  return IF2IP_FOUND;
239
}
240
241
#else
242
243
if2ip_result_t Curl_if2ip(int af,
244
#ifdef USE_IPV6
245
                          unsigned int remote_scope,
246
                          unsigned int local_scope_id,
247
#endif
248
                          const char *interf,
249
                          char *buf, size_t buf_size)
250
{
251
    (void)af;
252
#ifdef USE_IPV6
253
    (void)remote_scope;
254
    (void)local_scope_id;
255
#endif
256
    (void)interf;
257
    (void)buf;
258
    (void)buf_size;
259
    return IF2IP_NOT_FOUND;
260
}
261
262
#endif
263
264
#endif /* CURL_DISABLE_BINDLOCAL && CURL_DISABLE_FTP */