block_ip.c 8.5 KB

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