123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
- /* <DESC>
- * Show how CURLOPT_OPENSOCKETFUNCTION can be used to block IP addresses.
- * </DESC>
- */
- /* This is an advanced example that defines a whitelist or a blacklist to
- * filter IP addresses.
- */
- #ifdef __AMIGA__
- #include <stdio.h>
- int main(void) { printf("AmigaOS is not supported.\n"); return 1; }
- #else
- #ifdef _WIN32
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #ifndef _CRT_NONSTDC_NO_DEPRECATE
- #define _CRT_NONSTDC_NO_DEPRECATE
- #endif
- #ifndef _WIN32_WINNT
- #define _WIN32_WINNT 0x0600
- #endif
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #include <windows.h>
- #else
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <memory.h>
- #include <curl/curl.h>
- #ifndef TRUE
- #define TRUE 1
- #endif
- #ifndef FALSE
- #define FALSE 0
- #endif
- struct ip {
- /* The user-provided IP address or network (use CIDR) to filter */
- char *str;
- /* IP address family AF_INET (IPv4) or AF_INET6 (IPv6) */
- int family;
- /* IP in network byte format */
- union netaddr {
- struct in_addr ipv4;
- #ifdef AF_INET6
- struct in6_addr ipv6;
- #endif
- } netaddr;
- /* IP bits to match against.
- * This is equal to the CIDR notation or max bits if no CIDR.
- * For example if ip->str is 127.0.0.0/8 then ip->maskbits is 8.
- */
- int maskbits;
- struct ip *next;
- };
- enum connection_filter_t {
- CONNECTION_FILTER_BLACKLIST,
- CONNECTION_FILTER_WHITELIST
- };
- struct connection_filter {
- struct ip *list;
- enum connection_filter_t type;
- int verbose;
- #ifdef AF_INET6
- /* If the address being filtered is an IPv4-mapped IPv6 address then it is
- * checked against IPv4 list entries as well, unless ipv6_v6only is set TRUE.
- */
- int ipv6_v6only;
- #endif
- };
- static struct ip *ip_list_append(struct ip *list, const char *data)
- {
- struct ip *ip, *last;
- char *cidr;
- ip = (struct ip *)calloc(1, sizeof(*ip));
- if(!ip)
- return NULL;
- if(strchr(data, ':')) {
- #ifdef AF_INET6
- ip->family = AF_INET6;
- #else
- free(ip);
- return NULL;
- #endif
- }
- else
- ip->family = AF_INET;
- ip->str = strdup(data);
- if(!ip->str) {
- free(ip);
- return NULL;
- }
- /* determine the number of bits that this IP will match against */
- cidr = strchr(ip->str, '/');
- if(cidr) {
- ip->maskbits = atoi(cidr + 1);
- if(ip->maskbits <= 0 ||
- #ifdef AF_INET6
- (ip->family == AF_INET6 && ip->maskbits > 128) ||
- #endif
- (ip->family == AF_INET && ip->maskbits > 32)) {
- free(ip->str);
- free(ip);
- return NULL;
- }
- /* ignore the CIDR notation when converting ip->str to ip->netaddr */
- *cidr = '\0';
- }
- else if(ip->family == AF_INET)
- ip->maskbits = 32;
- #ifdef AF_INET6
- else if(ip->family == AF_INET6)
- ip->maskbits = 128;
- #endif
- if(1 != inet_pton(ip->family, ip->str, &ip->netaddr)) {
- free(ip->str);
- free(ip);
- return NULL;
- }
- if(cidr)
- *cidr = '/';
- if(!list)
- return ip;
- for(last = list; last->next; last = last->next)
- ;
- last->next = ip;
- return list;
- }
- static void ip_list_free_all(struct ip *list)
- {
- struct ip *next;
- while(list) {
- next = list->next;
- free(list->str);
- free(list);
- list = next;
- }
- }
- static void free_connection_filter(struct connection_filter *filter)
- {
- if(filter) {
- ip_list_free_all(filter->list);
- free(filter);
- }
- }
- static int ip_match(struct ip *ip, void *netaddr)
- {
- int bytes, tailbits;
- const unsigned char *x, *y;
- x = (unsigned char *)&ip->netaddr;
- y = (unsigned char *)netaddr;
- for(bytes = ip->maskbits / 8; bytes; --bytes) {
- if(*x++ != *y++)
- return FALSE;
- }
- tailbits = ip->maskbits % 8;
- if(tailbits) {
- unsigned char tailmask = (unsigned char)((0xFF << (8 - tailbits)) & 0xFF);
- if((*x & tailmask) != (*y & tailmask))
- return FALSE;
- }
- return TRUE;
- }
- #ifdef AF_INET6
- static int is_ipv4_mapped_ipv6_address(int family, void *netaddr)
- {
- if(family == AF_INET6) {
- int i;
- unsigned char *x = (unsigned char *)netaddr;
- for(i = 0; i < 12; ++i) {
- if(x[i])
- break;
- }
- /* support formats ::x.x.x.x (deprecated) and ::ffff:x.x.x.x */
- if((i == 12 && (x[i] || x[i + 1] || x[i + 2] || x[i + 3])) ||
- (i == 10 && (x[i] == 0xFF && x[i + 1] == 0xFF)))
- return TRUE;
- }
- return FALSE;
- }
- #endif /* AF_INET6 */
- static curl_socket_t opensocket(void *clientp,
- curlsocktype purpose,
- struct curl_sockaddr *address)
- {
- /* filter the address */
- if(purpose == CURLSOCKTYPE_IPCXN) {
- void *cinaddr = NULL;
- if(address->family == AF_INET)
- cinaddr = &((struct sockaddr_in *)(void *)&address->addr)->sin_addr;
- #ifdef AF_INET6
- else if(address->family == AF_INET6)
- cinaddr = &((struct sockaddr_in6 *)(void *)&address->addr)->sin6_addr;
- #endif
- if(cinaddr) {
- struct ip *ip;
- struct connection_filter *filter = (struct connection_filter *)clientp;
- #ifdef AF_INET6
- int mapped = !filter->ipv6_v6only &&
- is_ipv4_mapped_ipv6_address(address->family, cinaddr);
- #endif
- for(ip = filter->list; ip; ip = ip->next) {
- if(ip->family == address->family && ip_match(ip, cinaddr))
- break;
- #ifdef AF_INET6
- if(mapped && ip->family == AF_INET && address->family == AF_INET6 &&
- ip_match(ip, (unsigned char *)cinaddr + 12))
- break;
- #endif
- }
- if(ip && filter->type == CONNECTION_FILTER_BLACKLIST) {
- if(filter->verbose) {
- char buf[128] = {0};
- inet_ntop(address->family, cinaddr, buf, sizeof(buf));
- fprintf(stderr, "* Rejecting IP %s due to blacklist entry %s.\n",
- buf, ip->str);
- }
- return CURL_SOCKET_BAD;
- }
- else if(!ip && filter->type == CONNECTION_FILTER_WHITELIST) {
- if(filter->verbose) {
- char buf[128] = {0};
- inet_ntop(address->family, cinaddr, buf, sizeof(buf));
- fprintf(stderr,
- "* Rejecting IP %s due to missing whitelist entry.\n", buf);
- }
- return CURL_SOCKET_BAD;
- }
- }
- }
- return socket(address->family, address->socktype, address->protocol);
- }
- int main(void)
- {
- CURL *curl;
- CURLcode res;
- struct connection_filter *filter;
- filter = (struct connection_filter *)calloc(1, sizeof(*filter));
- if(!filter)
- exit(1);
- if(curl_global_init(CURL_GLOBAL_DEFAULT))
- exit(1);
- curl = curl_easy_init();
- if(!curl)
- exit(1);
- /* Set the target URL */
- curl_easy_setopt(curl, CURLOPT_URL, "http://localhost");
- /* Define an IP connection filter.
- * If an address has CIDR notation then it matches the network.
- * For example 74.6.143.25/24 matches 74.6.143.0 - 74.6.143.255.
- */
- filter->type = CONNECTION_FILTER_BLACKLIST;
- filter->list = ip_list_append(filter->list, "98.137.11.164");
- filter->list = ip_list_append(filter->list, "127.0.0.0/8");
- #ifdef AF_INET6
- filter->list = ip_list_append(filter->list, "::1");
- #endif
- /* Set the socket function which does the filtering */
- curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket);
- curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, filter);
- /* Verbose mode */
- filter->verbose = TRUE;
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
- /* Perform the request */
- res = curl_easy_perform(curl);
- /* Check for errors */
- if(res != CURLE_OK) {
- fprintf(stderr, "curl_easy_perform() failed: %s\n",
- curl_easy_strerror(res));
- }
- /* Clean up */
- curl_easy_cleanup(curl);
- free_connection_filter(filter);
- /* Clean up libcurl */
- curl_global_cleanup();
- return 0;
- }
- #endif
|