Coverage Report

Created: 2026-01-10 06:51

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
0
{
63
0
  if(sa->sa_family == AF_INET6) {
64
0
    const struct sockaddr_in6 *sa6 =
65
0
      (const struct sockaddr_in6 *)(const void *)sa;
66
0
    const unsigned char *b = sa6->sin6_addr.s6_addr;
67
0
    unsigned short w = (unsigned short)((b[0] << 8) | b[1]);
68
69
0
    if((b[0] & 0xFE) == 0xFC) /* Handle ULAs */
70
0
      return IPV6_SCOPE_UNIQUELOCAL;
71
0
    switch(w & 0xFFC0) {
72
0
    case 0xFE80:
73
0
      return IPV6_SCOPE_LINKLOCAL;
74
0
    case 0xFEC0:
75
0
      return IPV6_SCOPE_SITELOCAL;
76
0
    case 0x0000:
77
0
      w = b[1] | b[2] | b[3] | b[4] | b[5] | b[6] | b[7] | b[8] | b[9] |
78
0
          b[10] | b[11] | b[12] | b[13] | b[14];
79
0
      if(w || b[15] != 0x01)
80
0
        break;
81
0
      return IPV6_SCOPE_NODELOCAL;
82
0
    default:
83
0
      break;
84
0
    }
85
0
  }
86
0
  return IPV6_SCOPE_GLOBAL;
87
0
}
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
0
{
102
0
  struct ifaddrs *iface, *head;
103
0
  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
0
  if(getifaddrs(&head) >= 0) {
110
0
    for(iface = head; iface != NULL; iface = iface->ifa_next) {
111
0
      if(iface->ifa_addr) {
112
0
        if(iface->ifa_addr->sa_family == af) {
113
0
          if(curl_strequal(iface->ifa_name, interf)) {
114
0
            void *addr;
115
0
            const char *ip;
116
0
            char scope[12] = "";
117
0
            char ipstr[64];
118
0
#ifdef USE_IPV6
119
0
            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
0
            else
154
0
#endif
155
0
              addr =
156
0
                &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
157
0
            res = IF2IP_FOUND;
158
0
            ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr));
159
0
            curl_msnprintf(buf, buf_size, "%s%s", ip, scope);
160
0
            break;
161
0
          }
162
0
        }
163
0
        else if((res == IF2IP_NOT_FOUND) &&
164
0
                curl_strequal(iface->ifa_name, interf)) {
165
0
          res = IF2IP_AF_NOT_SUPPORTED;
166
0
        }
167
0
      }
168
0
    }
169
170
0
    freeifaddrs(head);
171
0
  }
172
173
0
  return res;
174
0
}
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 */