Coverage Report

Created: 2026-01-09 07:25

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