noproxy.c (7531B)
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 <curl/curl.h> /* for curl_strnequal() */ 30 #include "curlx/inet_pton.h" 31 #include "noproxy.h" 32 #include "curlx/strparse.h" 33 34 #ifdef HAVE_NETINET_IN_H 35 #include <netinet/in.h> 36 #endif 37 38 #ifdef HAVE_ARPA_INET_H 39 #include <arpa/inet.h> 40 #endif 41 42 /* 43 * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the 44 * specified CIDR address range. 45 */ 46 UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ 47 const char *network, /* 1.2.3.4 address */ 48 unsigned int bits) 49 { 50 unsigned int address = 0; 51 unsigned int check = 0; 52 53 if(bits > 32) 54 /* strange input */ 55 return FALSE; 56 57 if(1 != curlx_inet_pton(AF_INET, ipv4, &address)) 58 return FALSE; 59 if(1 != curlx_inet_pton(AF_INET, network, &check)) 60 return FALSE; 61 62 if(bits && (bits != 32)) { 63 unsigned int mask = 0xffffffff << (32 - bits); 64 unsigned int haddr = htonl(address); 65 unsigned int hcheck = htonl(check); 66 #if 0 67 fprintf(stderr, "Host %s (%x) network %s (%x) bits %u mask %x => %x\n", 68 ipv4, haddr, network, hcheck, bits, mask, 69 (haddr ^ hcheck) & mask); 70 #endif 71 if((haddr ^ hcheck) & mask) 72 return FALSE; 73 return TRUE; 74 } 75 return address == check; 76 } 77 78 UNITTEST bool Curl_cidr6_match(const char *ipv6, 79 const char *network, 80 unsigned int bits) 81 { 82 #ifdef USE_IPV6 83 unsigned int bytes; 84 unsigned int rest; 85 unsigned char address[16]; 86 unsigned char check[16]; 87 88 if(!bits) 89 bits = 128; 90 91 bytes = bits / 8; 92 rest = bits & 0x07; 93 if((bytes > 16) || ((bytes == 16) && rest)) 94 return FALSE; 95 if(1 != curlx_inet_pton(AF_INET6, ipv6, address)) 96 return FALSE; 97 if(1 != curlx_inet_pton(AF_INET6, network, check)) 98 return FALSE; 99 if(bytes && memcmp(address, check, bytes)) 100 return FALSE; 101 if(rest && !((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) 102 return FALSE; 103 104 return TRUE; 105 #else 106 (void)ipv6; 107 (void)network; 108 (void)bits; 109 return FALSE; 110 #endif 111 } 112 113 enum nametype { 114 TYPE_HOST, 115 TYPE_IPV4, 116 TYPE_IPV6 117 }; 118 119 /**************************************************************** 120 * Checks if the host is in the noproxy list. returns TRUE if it matches and 121 * therefore the proxy should NOT be used. 122 ****************************************************************/ 123 bool Curl_check_noproxy(const char *name, const char *no_proxy) 124 { 125 char hostip[128]; 126 127 /* 128 * If we do not 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 was not 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 == curlx_inet_pton(AF_INET, name, &address)) 167 type = TYPE_IPV4; 168 else { 169 /* ignore trailing dots in the hostname */ 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 curlx_str_passblanks(&p); 182 183 token = p; 184 /* pass over the pattern */ 185 while(*p && !ISBLANK(*p) && (*p != ',')) { 186 p++; 187 tokenlen++; 188 } 189 190 if(tokenlen) { 191 switch(type) { 192 case TYPE_HOST: 193 /* ignore trailing dots in the token to check */ 194 if(token[tokenlen - 1] == '.') 195 tokenlen--; 196 197 if(tokenlen && (*token == '.')) { 198 /* ignore leading token dot as well */ 199 token++; 200 tokenlen--; 201 } 202 /* A: example.com matches 'example.com' 203 B: www.example.com matches 'example.com' 204 C: nonexample.com DOES NOT match 'example.com' 205 */ 206 if(tokenlen == namelen) 207 /* case A, exact match */ 208 match = curl_strnequal(token, name, namelen); 209 else if(tokenlen < namelen) { 210 /* case B, tailmatch domain */ 211 match = (name[namelen - tokenlen - 1] == '.') && 212 curl_strnequal(token, name + (namelen - tokenlen), 213 tokenlen); 214 } 215 /* case C passes through, not a match */ 216 break; 217 case TYPE_IPV4: 218 case TYPE_IPV6: { 219 const char *check = token; 220 char *slash; 221 unsigned int bits = 0; 222 char checkip[128]; 223 if(tokenlen >= sizeof(checkip)) 224 /* this cannot match */ 225 break; 226 /* copy the check name to a temp buffer */ 227 memcpy(checkip, check, tokenlen); 228 checkip[tokenlen] = 0; 229 check = checkip; 230 231 slash = strchr(check, '/'); 232 /* if the slash is part of this token, use it */ 233 if(slash) { 234 /* if the bits variable gets a crazy value here, that is fine as 235 the value will then be rejected in the cidr function */ 236 bits = (unsigned int)atoi(slash + 1); 237 *slash = 0; /* null-terminate there */ 238 } 239 if(type == TYPE_IPV6) 240 match = Curl_cidr6_match(name, check, bits); 241 else 242 match = Curl_cidr4_match(name, check, bits); 243 break; 244 } 245 } 246 if(match) 247 return TRUE; 248 } /* if(tokenlen) */ 249 /* pass blanks after pattern */ 250 curlx_str_passblanks(&p); 251 /* if not a comma, this ends the loop */ 252 if(*p != ',') 253 break; 254 /* pass any number of commas */ 255 while(*p == ',') 256 p++; 257 } /* while(*p) */ 258 } /* NO_PROXY was specified and it was not just an asterisk */ 259 260 return FALSE; 261 } 262 263 #endif /* CURL_DISABLE_PROXY */