xref: /third_party/curl/lib/noproxy.c (revision 13498266)
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#ifndef CURL_DISABLE_PROXY
28
29#include "inet_pton.h"
30#include "strcase.h"
31#include "noproxy.h"
32
33#ifdef HAVE_NETINET_IN_H
34#include <netinet/in.h>
35#endif
36
37#ifdef HAVE_ARPA_INET_H
38#include <arpa/inet.h>
39#endif
40
41/*
42 * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the
43 * specified CIDR address range.
44 */
45UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
46                               const char *network, /* 1.2.3.4 address */
47                               unsigned int bits)
48{
49  unsigned int address = 0;
50  unsigned int check = 0;
51
52  if(bits > 32)
53    /* strange input */
54    return FALSE;
55
56  if(1 != Curl_inet_pton(AF_INET, ipv4, &address))
57    return FALSE;
58  if(1 != Curl_inet_pton(AF_INET, network, &check))
59    return FALSE;
60
61  if(bits && (bits != 32)) {
62    unsigned int mask = 0xffffffff << (32 - bits);
63    unsigned int haddr = htonl(address);
64    unsigned int hcheck = htonl(check);
65#if 0
66    fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n",
67            ipv4, haddr, network, hcheck, bits, mask,
68            (haddr ^ hcheck) & mask);
69#endif
70    if((haddr ^ hcheck) & mask)
71      return FALSE;
72    return TRUE;
73  }
74  return (address == check);
75}
76
77UNITTEST bool Curl_cidr6_match(const char *ipv6,
78                               const char *network,
79                               unsigned int bits)
80{
81#ifdef ENABLE_IPV6
82  int bytes;
83  int rest;
84  unsigned char address[16];
85  unsigned char check[16];
86
87  if(!bits)
88    bits = 128;
89
90  bytes = bits/8;
91  rest = bits & 0x07;
92  if(1 != Curl_inet_pton(AF_INET6, ipv6, address))
93    return FALSE;
94  if(1 != Curl_inet_pton(AF_INET6, network, check))
95    return FALSE;
96  if((bytes > 16) || ((bytes == 16) && rest))
97    return FALSE;
98  if(bytes && memcmp(address, check, bytes))
99    return FALSE;
100  if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
101    return FALSE;
102
103  return TRUE;
104#else
105  (void)ipv6;
106  (void)network;
107  (void)bits;
108  return FALSE;
109#endif
110}
111
112enum nametype {
113  TYPE_HOST,
114  TYPE_IPV4,
115  TYPE_IPV6
116};
117
118/****************************************************************
119* Checks if the host is in the noproxy list. returns TRUE if it matches and
120* therefore the proxy should NOT be used.
121****************************************************************/
122bool Curl_check_noproxy(const char *name, const char *no_proxy,
123                        bool *spacesep)
124{
125  char hostip[128];
126  *spacesep = FALSE;
127  /*
128   * If we don't have a hostname at all, like for example with a FILE
129   * transfer, we have nothing to interrogate the noproxy list with.
130   */
131  if(!name || name[0] == '\0')
132    return FALSE;
133
134  /* no_proxy=domain1.dom,host.domain2.dom
135   *   (a comma-separated list of hosts which should
136   *   not be proxied, or an asterisk to override
137   *   all proxy variables)
138   */
139  if(no_proxy && no_proxy[0]) {
140    const char *p = no_proxy;
141    size_t namelen;
142    enum nametype type = TYPE_HOST;
143    if(!strcmp("*", no_proxy))
144      return TRUE;
145
146    /* NO_PROXY was specified and it wasn't just an asterisk */
147
148    if(name[0] == '[') {
149      char *endptr;
150      /* IPv6 numerical address */
151      endptr = strchr(name, ']');
152      if(!endptr)
153        return FALSE;
154      name++;
155      namelen = endptr - name;
156      if(namelen >= sizeof(hostip))
157        return FALSE;
158      memcpy(hostip, name, namelen);
159      hostip[namelen] = 0;
160      name = hostip;
161      type = TYPE_IPV6;
162    }
163    else {
164      unsigned int address;
165      namelen = strlen(name);
166      if(1 == Curl_inet_pton(AF_INET, name, &address))
167        type = TYPE_IPV4;
168      else {
169        /* ignore trailing dots in the host name */
170        if(name[namelen - 1] == '.')
171          namelen--;
172      }
173    }
174
175    while(*p) {
176      const char *token;
177      size_t tokenlen = 0;
178      bool match = FALSE;
179
180      /* pass blanks */
181      while(*p && ISBLANK(*p))
182        p++;
183
184      token = p;
185      /* pass over the pattern */
186      while(*p && !ISBLANK(*p) && (*p != ',')) {
187        p++;
188        tokenlen++;
189      }
190
191      if(tokenlen) {
192        switch(type) {
193        case TYPE_HOST:
194          /* ignore trailing dots in the token to check */
195          if(token[tokenlen - 1] == '.')
196            tokenlen--;
197
198          if(tokenlen && (*token == '.')) {
199            /* ignore leading token dot as well */
200            token++;
201            tokenlen--;
202          }
203          /* A: example.com matches 'example.com'
204             B: www.example.com matches 'example.com'
205             C: nonexample.com DOES NOT match 'example.com'
206          */
207          if(tokenlen == namelen)
208            /* case A, exact match */
209            match = strncasecompare(token, name, namelen);
210          else if(tokenlen < namelen) {
211            /* case B, tailmatch domain */
212            match = (name[namelen - tokenlen - 1] == '.') &&
213              strncasecompare(token, name + (namelen - tokenlen),
214                              tokenlen);
215          }
216          /* case C passes through, not a match */
217          break;
218        case TYPE_IPV4:
219        case TYPE_IPV6: {
220          const char *check = token;
221          char *slash;
222          unsigned int bits = 0;
223          char checkip[128];
224          if(tokenlen >= sizeof(checkip))
225            /* this cannot match */
226            break;
227          /* copy the check name to a temp buffer */
228          memcpy(checkip, check, tokenlen);
229          checkip[tokenlen] = 0;
230          check = checkip;
231
232          slash = strchr(check, '/');
233          /* if the slash is part of this token, use it */
234          if(slash) {
235            bits = atoi(slash + 1);
236            *slash = 0; /* null terminate there */
237          }
238          if(type == TYPE_IPV6)
239            match = Curl_cidr6_match(name, check, bits);
240          else
241            match = Curl_cidr4_match(name, check, bits);
242          break;
243        }
244        }
245        if(match)
246          return TRUE;
247      } /* if(tokenlen) */
248      /* pass blanks after pattern */
249      while(ISBLANK(*p))
250        p++;
251      /* if not a comma! */
252      if(*p && (*p != ',')) {
253        *spacesep = TRUE;
254        continue;
255      }
256      /* pass any number of commas */
257      while(*p == ',')
258        p++;
259    } /* while(*p) */
260  } /* NO_PROXY was specified and it wasn't just an asterisk */
261
262  return FALSE;
263}
264
265#endif /* CURL_DISABLE_PROXY */
266