block_ip.c (8858B)
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 /* <DESC> 25 * Show how CURLOPT_OPENSOCKETFUNCTION can be used to block IP addresses. 26 * </DESC> 27 */ 28 /* This is an advanced example that defines a whitelist or a blacklist to 29 * filter IP addresses. 30 */ 31 32 #if defined(__AMIGA__) || defined(UNDER_CE) 33 #include <stdio.h> 34 int main(void) { printf("Platform not supported.\n"); return 1; } 35 #else 36 37 #ifdef _WIN32 38 #ifndef _CRT_SECURE_NO_WARNINGS 39 #define _CRT_SECURE_NO_WARNINGS 40 #endif 41 #ifndef _CRT_NONSTDC_NO_DEPRECATE 42 #define _CRT_NONSTDC_NO_DEPRECATE 43 #endif 44 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 45 #undef _WIN32_WINNT 46 #define _WIN32_WINNT 0x0600 /* Requires Windows Vista */ 47 #endif 48 #include <winsock2.h> 49 #include <ws2tcpip.h> 50 #include <windows.h> 51 #else 52 #include <sys/types.h> 53 #include <sys/socket.h> 54 #include <netinet/in.h> 55 #include <arpa/inet.h> 56 #endif 57 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 62 #include <curl/curl.h> 63 64 #ifndef TRUE 65 #define TRUE 1 66 #endif 67 68 #ifndef FALSE 69 #define FALSE 0 70 #endif 71 72 struct ip { 73 /* The user-provided IP address or network (use CIDR) to filter */ 74 char *str; 75 /* IP address family AF_INET (IPv4) or AF_INET6 (IPv6) */ 76 int family; 77 /* IP in network byte format */ 78 union netaddr { 79 struct in_addr ipv4; 80 #ifdef AF_INET6 81 struct in6_addr ipv6; 82 #endif 83 } netaddr; 84 /* IP bits to match against. 85 * This is equal to the CIDR notation or max bits if no CIDR. 86 * For example if ip->str is 127.0.0.0/8 then ip->maskbits is 8. 87 */ 88 int maskbits; 89 struct ip *next; 90 }; 91 92 enum connection_filter_t { 93 CONNECTION_FILTER_BLACKLIST, 94 CONNECTION_FILTER_WHITELIST 95 }; 96 97 struct connection_filter { 98 struct ip *list; 99 enum connection_filter_t type; 100 int verbose; 101 #ifdef AF_INET6 102 /* If the address being filtered is an IPv4-mapped IPv6 address then it is 103 * checked against IPv4 list entries as well, unless ipv6_v6only is set TRUE. 104 */ 105 int ipv6_v6only; 106 #endif 107 }; 108 109 static struct ip *ip_list_append(struct ip *list, const char *data) 110 { 111 struct ip *ip, *last; 112 char *cidr; 113 114 ip = (struct ip *)calloc(1, sizeof(*ip)); 115 if(!ip) 116 return NULL; 117 118 if(strchr(data, ':')) { 119 #ifdef AF_INET6 120 ip->family = AF_INET6; 121 #else 122 free(ip); 123 return NULL; 124 #endif 125 } 126 else 127 ip->family = AF_INET; 128 129 ip->str = strdup(data); 130 if(!ip->str) { 131 free(ip); 132 return NULL; 133 } 134 135 /* determine the number of bits that this IP will match against */ 136 cidr = strchr(ip->str, '/'); 137 if(cidr) { 138 ip->maskbits = atoi(cidr + 1); 139 if(ip->maskbits <= 0 || 140 #ifdef AF_INET6 141 (ip->family == AF_INET6 && ip->maskbits > 128) || 142 #endif 143 (ip->family == AF_INET && ip->maskbits > 32)) { 144 free(ip->str); 145 free(ip); 146 return NULL; 147 } 148 /* ignore the CIDR notation when converting ip->str to ip->netaddr */ 149 *cidr = '\0'; 150 } 151 else if(ip->family == AF_INET) 152 ip->maskbits = 32; 153 #ifdef AF_INET6 154 else if(ip->family == AF_INET6) 155 ip->maskbits = 128; 156 #endif 157 158 if(1 != inet_pton(ip->family, ip->str, &ip->netaddr)) { 159 free(ip->str); 160 free(ip); 161 return NULL; 162 } 163 164 if(cidr) 165 *cidr = '/'; 166 167 if(!list) 168 return ip; 169 for(last = list; last->next; last = last->next) 170 ; 171 last->next = ip; 172 return list; 173 } 174 175 static void ip_list_free_all(struct ip *list) 176 { 177 struct ip *next; 178 while(list) { 179 next = list->next; 180 free(list->str); 181 free(list); 182 list = next; 183 } 184 } 185 186 static void free_connection_filter(struct connection_filter *filter) 187 { 188 if(filter) { 189 ip_list_free_all(filter->list); 190 free(filter); 191 } 192 } 193 194 static int ip_match(struct ip *ip, void *netaddr) 195 { 196 int bytes, tailbits; 197 const unsigned char *x, *y; 198 199 x = (unsigned char *)&ip->netaddr; 200 y = (unsigned char *)netaddr; 201 202 for(bytes = ip->maskbits / 8; bytes; --bytes) { 203 if(*x++ != *y++) 204 return FALSE; 205 } 206 207 tailbits = ip->maskbits % 8; 208 if(tailbits) { 209 unsigned char tailmask = (unsigned char)((0xFF << (8 - tailbits)) & 0xFF); 210 if((*x & tailmask) != (*y & tailmask)) 211 return FALSE; 212 } 213 214 return TRUE; 215 } 216 217 #ifdef AF_INET6 218 static int is_ipv4_mapped_ipv6_address(int family, void *netaddr) 219 { 220 if(family == AF_INET6) { 221 int i; 222 unsigned char *x = (unsigned char *)netaddr; 223 for(i = 0; i < 12; ++i) { 224 if(x[i]) 225 break; 226 } 227 /* support formats ::x.x.x.x (deprecated) and ::ffff:x.x.x.x */ 228 if((i == 12 && (x[i] || x[i + 1] || x[i + 2] || x[i + 3])) || 229 (i == 10 && (x[i] == 0xFF && x[i + 1] == 0xFF))) 230 return TRUE; 231 } 232 233 return FALSE; 234 } 235 #endif /* AF_INET6 */ 236 237 static curl_socket_t opensocket(void *clientp, 238 curlsocktype purpose, 239 struct curl_sockaddr *address) 240 { 241 /* filter the address */ 242 if(purpose == CURLSOCKTYPE_IPCXN) { 243 void *cinaddr = NULL; 244 245 if(address->family == AF_INET) 246 cinaddr = &((struct sockaddr_in *)(void *)&address->addr)->sin_addr; 247 #ifdef AF_INET6 248 else if(address->family == AF_INET6) 249 cinaddr = &((struct sockaddr_in6 *)(void *)&address->addr)->sin6_addr; 250 #endif 251 252 if(cinaddr) { 253 struct ip *ip; 254 struct connection_filter *filter = (struct connection_filter *)clientp; 255 #ifdef AF_INET6 256 int mapped = !filter->ipv6_v6only && 257 is_ipv4_mapped_ipv6_address(address->family, cinaddr); 258 #endif 259 260 for(ip = filter->list; ip; ip = ip->next) { 261 if(ip->family == address->family && ip_match(ip, cinaddr)) 262 break; 263 #ifdef AF_INET6 264 if(mapped && ip->family == AF_INET && address->family == AF_INET6 && 265 ip_match(ip, (unsigned char *)cinaddr + 12)) 266 break; 267 #endif 268 } 269 270 if(ip && filter->type == CONNECTION_FILTER_BLACKLIST) { 271 if(filter->verbose) { 272 char buf[128] = {0}; 273 inet_ntop(address->family, cinaddr, buf, sizeof(buf)); 274 fprintf(stderr, "* Rejecting IP %s due to blacklist entry %s.\n", 275 buf, ip->str); 276 } 277 return CURL_SOCKET_BAD; 278 } 279 else if(!ip && filter->type == CONNECTION_FILTER_WHITELIST) { 280 if(filter->verbose) { 281 char buf[128] = {0}; 282 inet_ntop(address->family, cinaddr, buf, sizeof(buf)); 283 fprintf(stderr, 284 "* Rejecting IP %s due to missing whitelist entry.\n", buf); 285 } 286 return CURL_SOCKET_BAD; 287 } 288 } 289 } 290 291 return socket(address->family, address->socktype, address->protocol); 292 } 293 294 int main(void) 295 { 296 CURL *curl; 297 CURLcode res; 298 struct connection_filter *filter; 299 300 filter = (struct connection_filter *)calloc(1, sizeof(*filter)); 301 if(!filter) 302 return 1; 303 304 if(curl_global_init(CURL_GLOBAL_DEFAULT)) { 305 free(filter); 306 return 1; 307 } 308 309 curl = curl_easy_init(); 310 if(!curl) { 311 curl_global_cleanup(); 312 free(filter); 313 return 1; 314 } 315 316 /* Set the target URL */ 317 curl_easy_setopt(curl, CURLOPT_URL, "http://localhost"); 318 319 /* Define an IP connection filter. 320 * If an address has CIDR notation then it matches the network. 321 * For example 74.6.143.25/24 matches 74.6.143.0 - 74.6.143.255. 322 */ 323 filter->type = CONNECTION_FILTER_BLACKLIST; 324 filter->list = ip_list_append(filter->list, "98.137.11.164"); 325 filter->list = ip_list_append(filter->list, "127.0.0.0/8"); 326 #ifdef AF_INET6 327 filter->list = ip_list_append(filter->list, "::1"); 328 #endif 329 330 /* Set the socket function which does the filtering */ 331 curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); 332 curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, filter); 333 334 /* Verbose mode */ 335 filter->verbose = TRUE; 336 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 337 338 /* Perform the request */ 339 res = curl_easy_perform(curl); 340 341 /* Check for errors */ 342 if(res != CURLE_OK) { 343 fprintf(stderr, "curl_easy_perform() failed: %s\n", 344 curl_easy_strerror(res)); 345 } 346 347 /* Clean up */ 348 curl_easy_cleanup(curl); 349 free_connection_filter(filter); 350 351 /* Clean up libcurl */ 352 curl_global_cleanup(); 353 354 return 0; 355 } 356 #endif