quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

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