transmission 2.83
[tomato.git] / release / src-rt-6.x.4708 / router / transmission / libtransmission / blocklist.c
blobf4b07bf7bc60222f5aa2506ca565e05f4a95cbe6
1 /*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: blocklist.c 14241 2014-01-21 03:10:30Z jordan $
8 */
10 #include <assert.h>
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h> /* bsearch (), qsort () */
14 #include <string.h>
16 #include <unistd.h> /* close () */
18 #ifdef WIN32
19 #include <w32api.h>
20 #define WINVER WindowsXP
21 #include <windows.h>
22 #define PROT_READ PAGE_READONLY
23 #define MAP_PRIVATE FILE_MAP_COPY
24 #endif
26 #ifndef WIN32
27 #include <sys/mman.h>
28 #endif
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
33 #include "transmission.h"
34 #include "blocklist.h"
35 #include "log.h"
36 #include "net.h"
37 #include "utils.h"
39 #ifndef O_BINARY
40 #define O_BINARY 0
41 #endif
44 /***
45 **** PRIVATE
46 ***/
48 struct tr_ipv4_range
50 uint32_t begin;
51 uint32_t end;
54 struct tr_blocklistFile
56 bool isEnabled;
57 int fd;
58 size_t ruleCount;
59 size_t byteCount;
60 char * filename;
61 struct tr_ipv4_range * rules;
64 static void
65 blocklistClose (tr_blocklistFile * b)
67 if (b->rules != NULL)
69 munmap (b->rules, b->byteCount);
70 close (b->fd);
71 b->rules = NULL;
72 b->ruleCount = 0;
73 b->byteCount = 0;
74 b->fd = -1;
78 static void
79 blocklistLoad (tr_blocklistFile * b)
81 int fd;
82 size_t byteCount;
83 struct stat st;
84 char * base;
85 const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
87 blocklistClose (b);
89 if (stat (b->filename, &st) == -1)
90 return;
92 fd = open (b->filename, O_RDONLY | O_BINARY);
93 if (fd == -1)
95 tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
96 return;
99 byteCount = (size_t) st.st_size;
100 b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0);
101 if (!b->rules)
103 tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
104 close (fd);
105 return;
108 b->fd = fd;
109 b->byteCount = byteCount;
110 b->ruleCount = byteCount / sizeof (struct tr_ipv4_range);
112 base = tr_basename (b->filename);
113 tr_logAddInfo (_("Blocklist \"%s\" contains %"TR_PRIuSIZE" entries"), base, b->ruleCount);
114 tr_free (base);
117 static void
118 blocklistEnsureLoaded (tr_blocklistFile * b)
120 if (b->rules == NULL)
121 blocklistLoad (b);
124 static int
125 compareAddressToRange (const void * va, const void * vb)
127 const uint32_t * a = va;
128 const struct tr_ipv4_range * b = vb;
130 if (*a < b->begin) return -1;
131 if (*a > b->end) return 1;
132 return 0;
135 static void
136 blocklistDelete (tr_blocklistFile * b)
138 blocklistClose (b);
139 tr_remove (b->filename);
142 /***
143 **** PACKAGE-VISIBLE
144 ***/
146 tr_blocklistFile *
147 tr_blocklistFileNew (const char * filename, bool isEnabled)
149 tr_blocklistFile * b;
151 b = tr_new0 (tr_blocklistFile, 1);
152 b->fd = -1;
153 b->filename = tr_strdup (filename);
154 b->isEnabled = isEnabled;
156 return b;
159 const char*
160 tr_blocklistFileGetFilename (const tr_blocklistFile * b)
162 return b->filename;
165 void
166 tr_blocklistFileFree (tr_blocklistFile * b)
168 blocklistClose (b);
169 tr_free (b->filename);
170 tr_free (b);
173 bool
174 tr_blocklistFileExists (const tr_blocklistFile * b)
176 return tr_fileExists (b->filename, NULL);
180 tr_blocklistFileGetRuleCount (const tr_blocklistFile * b)
182 blocklistEnsureLoaded ((tr_blocklistFile*)b);
184 return b->ruleCount;
187 bool
188 tr_blocklistFileIsEnabled (tr_blocklistFile * b)
190 return b->isEnabled;
193 void
194 tr_blocklistFileSetEnabled (tr_blocklistFile * b, bool isEnabled)
196 assert (b != NULL);
197 assert (tr_isBool (isEnabled));
199 b->isEnabled = isEnabled;
202 bool
203 tr_blocklistFileHasAddress (tr_blocklistFile * b, const tr_address * addr)
205 uint32_t needle;
206 const struct tr_ipv4_range * range;
208 assert (tr_address_is_valid (addr));
210 if (!b->isEnabled || addr->type == TR_AF_INET6)
211 return false;
213 blocklistEnsureLoaded (b);
215 if (!b->rules || !b->ruleCount)
216 return false;
218 needle = ntohl (addr->addr.addr4.s_addr);
220 range = bsearch (&needle,
221 b->rules,
222 b->ruleCount,
223 sizeof (struct tr_ipv4_range),
224 compareAddressToRange);
226 return range != NULL;
230 * P2P plaintext format: "comment:x.x.x.x-y.y.y.y"
231 * http://wiki.phoenixlabs.org/wiki/P2P_Format
232 * http://en.wikipedia.org/wiki/PeerGuardian#P2P_plaintext_format
234 static bool
235 parseLine1 (const char * line, struct tr_ipv4_range * range)
237 char * walk;
238 int b[4];
239 int e[4];
240 char str[64];
241 tr_address addr;
243 walk = strrchr (line, ':');
244 if (!walk)
245 return false;
246 ++walk; /* walk past the colon */
248 if (sscanf (walk, "%d.%d.%d.%d-%d.%d.%d.%d",
249 &b[0], &b[1], &b[2], &b[3],
250 &e[0], &e[1], &e[2], &e[3]) != 8)
251 return false;
253 tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
254 if (!tr_address_from_string (&addr, str))
255 return false;
256 range->begin = ntohl (addr.addr.addr4.s_addr);
258 tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", e[0], e[1], e[2], e[3]);
259 if (!tr_address_from_string (&addr, str))
260 return false;
261 range->end = ntohl (addr.addr.addr4.s_addr);
263 return true;
267 * DAT format: "000.000.000.000 - 000.255.255.255 , 000 , invalid ip"
268 * http://wiki.phoenixlabs.org/wiki/DAT_Format
270 static bool
271 parseLine2 (const char * line, struct tr_ipv4_range * range)
273 int unk;
274 int a[4];
275 int b[4];
276 char str[32];
277 tr_address addr;
279 if (sscanf (line, "%3d.%3d.%3d.%3d - %3d.%3d.%3d.%3d , %3d , ",
280 &a[0], &a[1], &a[2], &a[3],
281 &b[0], &b[1], &b[2], &b[3],
282 &unk) != 9)
283 return false;
285 tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
286 if (!tr_address_from_string (&addr, str))
287 return false;
288 range->begin = ntohl (addr.addr.addr4.s_addr);
290 tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
291 if (!tr_address_from_string (&addr, str))
292 return false;
293 range->end = ntohl (addr.addr.addr4.s_addr);
295 return true;
298 static bool
299 parseLine (const char * line, struct tr_ipv4_range * range)
301 return parseLine1 (line, range)
302 || parseLine2 (line, range);
305 static int
306 compareAddressRangesByFirstAddress (const void * va, const void * vb)
308 const struct tr_ipv4_range * a = va;
309 const struct tr_ipv4_range * b = vb;
310 if (a->begin != b->begin)
311 return a->begin < b->begin ? -1 : 1;
312 return 0;
316 tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename)
318 FILE * in;
319 FILE * out;
320 int inCount = 0;
321 char line[2048];
322 const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
323 struct tr_ipv4_range * ranges = NULL;
324 size_t ranges_alloc = 0;
325 size_t ranges_count = 0;
327 if (!filename)
329 blocklistDelete (b);
330 return 0;
333 in = fopen (filename, "rb");
334 if (in == NULL)
336 tr_logAddError (err_fmt, filename, tr_strerror (errno));
337 return 0;
340 blocklistClose (b);
342 out = fopen (b->filename, "wb+");
343 if (out == NULL)
345 tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
346 fclose (in);
347 return 0;
350 /* load the rules into memory */
351 while (fgets (line, sizeof (line), in) != NULL)
353 char * walk;
354 struct tr_ipv4_range range;
356 ++inCount;
358 /* zap the linefeed */
359 if ((walk = strchr (line, '\r'))) *walk = '\0';
360 if ((walk = strchr (line, '\n'))) *walk = '\0';
362 if (!parseLine (line, &range))
364 /* don't try to display the actual lines - it causes issues */
365 tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount);
366 continue;
369 if (ranges_alloc == ranges_count)
371 ranges_alloc += 4096; /* arbitrary */
372 ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc);
375 ranges[ranges_count++] = range;
378 if (ranges_count > 0) /* sort and merge */
380 struct tr_ipv4_range * r;
381 struct tr_ipv4_range * keep = ranges;
382 const struct tr_ipv4_range * end;
384 /* sort */
385 qsort (ranges, ranges_count, sizeof (struct tr_ipv4_range),
386 compareAddressRangesByFirstAddress);
388 /* merge */
389 for (r=ranges+1, end=ranges+ranges_count; r!=end; ++r) {
390 if (keep->end < r->begin)
391 *++keep = *r;
392 else if (keep->end < r->end)
393 keep->end = r->end;
396 ranges_count = keep + 1 - ranges;
398 #ifndef NDEBUG
399 /* sanity checks: make sure the rules are sorted
400 * in ascending order and don't overlap */
402 size_t i;
404 for (i=0; i<ranges_count; ++i)
405 assert (ranges[i].begin <= ranges[i].end);
407 for (i=1; i<ranges_count; ++i)
408 assert (ranges[i-1].end < ranges[i].begin);
410 #endif
413 if (fwrite (ranges, sizeof (struct tr_ipv4_range), ranges_count, out) != ranges_count)
415 tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), b->filename, tr_strerror (errno));
417 else
419 char * base = tr_basename (b->filename);
420 tr_logAddInfo (_("Blocklist \"%s\" updated with %"TR_PRIuSIZE" entries"), base, ranges_count);
421 tr_free (base);
424 tr_free (ranges);
425 fclose (out);
426 fclose (in);
428 blocklistLoad (b);
430 return ranges_count;