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