source: trunk/libtransmission/blocklist.c @ 14320

Last change on this file since 14320 was 14320, checked in by jordan, 8 years ago

(trunk, libT) #4160 'foreign character support' -- merge mike.dld's 4160-02b-path.patch, which updates the codebase to use the new tr_sys_path_*() portability wrappers introduced in 4160-02a

  • Property svn:keywords set to Date Rev Author Id
File size: 9.1 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: blocklist.c 14320 2014-07-08 00:08:43Z jordan $
8 */
9
10#include <assert.h>
11#include <errno.h>
12#include <stdio.h>
13#include <stdlib.h> /* bsearch (), qsort () */
14#include <string.h>
15
16#include <unistd.h> /* close () */
17
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
25
26#ifndef _WIN32
27 #include <sys/mman.h>
28#endif
29#include <fcntl.h>
30
31#include "transmission.h"
32#include "blocklist.h"
33#include "file.h"
34#include "log.h"
35#include "net.h"
36#include "utils.h"
37
38#ifndef O_BINARY
39 #define O_BINARY 0
40#endif
41
42
43/***
44****  PRIVATE
45***/
46
47struct tr_ipv4_range
48{
49  uint32_t begin;
50  uint32_t end;
51};
52
53struct tr_blocklistFile
54{
55  bool                   isEnabled;
56  int                    fd;
57  size_t                 ruleCount;
58  size_t                 byteCount;
59  char *                 filename;
60  struct tr_ipv4_range * rules;
61};
62
63static void
64blocklistClose (tr_blocklistFile * b)
65{
66  if (b->rules != NULL)
67    {
68      munmap (b->rules, b->byteCount);
69      close (b->fd);
70      b->rules = NULL;
71      b->ruleCount = 0;
72      b->byteCount = 0;
73      b->fd = -1;
74    }
75}
76
77static void
78blocklistLoad (tr_blocklistFile * b)
79{
80  int fd;
81  size_t byteCount;
82  tr_sys_path_info info;
83  char * base;
84  const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
85
86  blocklistClose (b);
87
88  if (!tr_sys_path_get_info (b->filename, 0, &info, NULL))
89    return;
90
91  byteCount = (size_t) info.size;
92  if (byteCount == 0)
93    return;
94
95  fd = open (b->filename, O_RDONLY | O_BINARY);
96  if (fd == -1)
97    {
98      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
99      return;
100    }
101
102  b->rules = mmap (NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0);
103  if (!b->rules)
104    {
105      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
106      close (fd);
107      return;
108    }
109
110  b->fd = fd;
111  b->byteCount = byteCount;
112  b->ruleCount = byteCount / sizeof (struct tr_ipv4_range);
113
114  base = tr_sys_path_basename (b->filename, NULL);
115  tr_logAddInfo (_("Blocklist \"%s\" contains %"TR_PRIuSIZE" entries"), base, b->ruleCount);
116  tr_free (base);
117}
118
119static void
120blocklistEnsureLoaded (tr_blocklistFile * b)
121{
122  if (b->rules == NULL)
123    blocklistLoad (b);
124}
125
126static int
127compareAddressToRange (const void * va, const void * vb)
128{
129  const uint32_t * a = va;
130  const struct tr_ipv4_range * b = vb;
131
132  if (*a < b->begin) return -1;
133  if (*a > b->end) return 1;
134  return 0;
135}
136
137static void
138blocklistDelete (tr_blocklistFile * b)
139{
140  blocklistClose (b);
141  tr_sys_path_remove (b->filename, NULL);
142}
143
144/***
145****  PACKAGE-VISIBLE
146***/
147
148tr_blocklistFile *
149tr_blocklistFileNew (const char * filename, bool isEnabled)
150{
151  tr_blocklistFile * b;
152
153  b = tr_new0 (tr_blocklistFile, 1);
154  b->fd = -1;
155  b->filename = tr_strdup (filename);
156  b->isEnabled = isEnabled;
157
158  return b;
159}
160
161const char*
162tr_blocklistFileGetFilename (const tr_blocklistFile * b)
163{
164  return b->filename;
165}
166
167void
168tr_blocklistFileFree (tr_blocklistFile * b)
169{
170  blocklistClose (b);
171  tr_free (b->filename);
172  tr_free (b);
173}
174
175bool
176tr_blocklistFileExists (const tr_blocklistFile * b)
177{
178  return tr_sys_path_exists (b->filename, NULL);
179}
180
181int
182tr_blocklistFileGetRuleCount (const tr_blocklistFile * b)
183{
184  blocklistEnsureLoaded ((tr_blocklistFile*)b);
185
186  return b->ruleCount;
187}
188
189bool
190tr_blocklistFileIsEnabled (tr_blocklistFile * b)
191{
192  return b->isEnabled;
193}
194
195void
196tr_blocklistFileSetEnabled (tr_blocklistFile * b, bool isEnabled)
197{
198  assert (b != NULL);
199  assert (tr_isBool (isEnabled));
200
201  b->isEnabled = isEnabled;
202}
203
204bool
205tr_blocklistFileHasAddress (tr_blocklistFile * b, const tr_address * addr)
206{
207  uint32_t needle;
208  const struct tr_ipv4_range * range;
209
210  assert (tr_address_is_valid (addr));
211
212  if (!b->isEnabled || addr->type == TR_AF_INET6)
213    return false;
214
215  blocklistEnsureLoaded (b);
216
217  if (!b->rules || !b->ruleCount)
218    return false;
219
220  needle = ntohl (addr->addr.addr4.s_addr);
221
222  range = bsearch (&needle,
223                   b->rules,
224                   b->ruleCount,
225                   sizeof (struct tr_ipv4_range),
226                   compareAddressToRange);
227
228  return range != NULL;
229}
230
231/*
232 * P2P plaintext format: "comment:x.x.x.x-y.y.y.y"
233 * http://wiki.phoenixlabs.org/wiki/P2P_Format
234 * http://en.wikipedia.org/wiki/PeerGuardian#P2P_plaintext_format
235 */
236static bool
237parseLine1 (const char * line, struct tr_ipv4_range * range)
238{
239  char * walk;
240  int b[4];
241  int e[4];
242  char str[64];
243  tr_address addr;
244
245  walk = strrchr (line, ':');
246  if (!walk)
247    return false;
248  ++walk; /* walk past the colon */
249
250  if (sscanf (walk, "%d.%d.%d.%d-%d.%d.%d.%d",
251              &b[0], &b[1], &b[2], &b[3],
252              &e[0], &e[1], &e[2], &e[3]) != 8)
253    return false;
254
255  tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
256  if (!tr_address_from_string (&addr, str))
257    return false;
258  range->begin = ntohl (addr.addr.addr4.s_addr);
259
260  tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", e[0], e[1], e[2], e[3]);
261  if (!tr_address_from_string (&addr, str))
262    return false;
263  range->end = ntohl (addr.addr.addr4.s_addr);
264
265  return true;
266}
267
268/*
269 * DAT format: "000.000.000.000 - 000.255.255.255 , 000 , invalid ip"
270 * http://wiki.phoenixlabs.org/wiki/DAT_Format
271 */
272static bool
273parseLine2 (const char * line, struct tr_ipv4_range * range)
274{
275  int unk;
276  int a[4];
277  int b[4];
278  char str[32];
279  tr_address addr;
280
281  if (sscanf (line, "%3d.%3d.%3d.%3d - %3d.%3d.%3d.%3d , %3d , ",
282              &a[0], &a[1], &a[2], &a[3],
283              &b[0], &b[1], &b[2], &b[3],
284              &unk) != 9)
285    return false;
286
287  tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
288  if (!tr_address_from_string (&addr, str))
289    return false;
290  range->begin = ntohl (addr.addr.addr4.s_addr);
291
292  tr_snprintf (str, sizeof (str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
293  if (!tr_address_from_string (&addr, str))
294    return false;
295  range->end = ntohl (addr.addr.addr4.s_addr);
296
297  return true;
298}
299
300static bool
301parseLine (const char * line, struct tr_ipv4_range * range)
302{
303  return parseLine1 (line, range)
304      || parseLine2 (line, range);
305}
306
307static int
308compareAddressRangesByFirstAddress (const void * va, const void * vb)
309{
310  const struct tr_ipv4_range * a = va;
311  const struct tr_ipv4_range * b = vb;
312  if (a->begin != b->begin)
313    return a->begin < b->begin ? -1 : 1;
314  return 0;
315}
316
317int
318tr_blocklistFileSetContent (tr_blocklistFile * b, const char * filename)
319{
320  FILE * in;
321  FILE * out;
322  int inCount = 0;
323  char line[2048];
324  const char * err_fmt = _("Couldn't read \"%1$s\": %2$s");
325  struct tr_ipv4_range * ranges = NULL;
326  size_t ranges_alloc = 0;
327  size_t ranges_count = 0;
328
329  if (!filename)
330    {
331      blocklistDelete (b);
332      return 0;
333    }
334
335  in = fopen (filename, "rb");
336  if (in == NULL)
337    {
338      tr_logAddError (err_fmt, filename, tr_strerror (errno));
339      return 0;
340    }
341
342  blocklistClose (b);
343
344  out = fopen (b->filename, "wb+");
345  if (out == NULL)
346    {
347      tr_logAddError (err_fmt, b->filename, tr_strerror (errno));
348      fclose (in);
349      return 0;
350    }
351
352  /* load the rules into memory */
353  while (fgets (line, sizeof (line), in) != NULL)
354    {
355      char * walk;
356      struct tr_ipv4_range range;
357
358      ++inCount;
359
360      /* zap the linefeed */
361      if ((walk = strchr (line, '\r'))) *walk = '\0';
362      if ((walk = strchr (line, '\n'))) *walk = '\0';
363
364      if (!parseLine (line, &range))
365        {
366          /* don't try to display the actual lines - it causes issues */
367          tr_logAddError (_("blocklist skipped invalid address at line %d"), inCount);
368          continue;
369        }
370
371      if (ranges_alloc == ranges_count)
372        {
373          ranges_alloc += 4096; /* arbitrary */
374          ranges = tr_renew (struct tr_ipv4_range, ranges, ranges_alloc);
375        }
376
377      ranges[ranges_count++] = range;
378    }
379
380  if (ranges_count > 0) /* sort and merge */
381    {
382      struct tr_ipv4_range * r;
383      struct tr_ipv4_range * keep = ranges;
384      const struct tr_ipv4_range * end;
385
386      /* sort */
387      qsort (ranges, ranges_count, sizeof (struct tr_ipv4_range),
388             compareAddressRangesByFirstAddress);
389
390      /* merge */
391      for (r=ranges+1, end=ranges+ranges_count; r!=end; ++r) {
392        if (keep->end < r->begin)
393          *++keep = *r;
394        else if (keep->end < r->end)
395          keep->end = r->end;
396      }
397
398      ranges_count = keep + 1 - ranges;
399
400#ifndef NDEBUG
401      /* sanity checks: make sure the rules are sorted
402       * in ascending order and don't overlap */
403      {
404        size_t i;
405
406        for (i=0; i<ranges_count; ++i)
407          assert (ranges[i].begin <= ranges[i].end);
408
409        for (i=1; i<ranges_count; ++i)
410          assert (ranges[i-1].end < ranges[i].begin);
411      }
412#endif
413    }
414
415  if (fwrite (ranges, sizeof (struct tr_ipv4_range), ranges_count, out) != ranges_count)
416    {
417      tr_logAddError (_("Couldn't save file \"%1$s\": %2$s"), b->filename, tr_strerror (errno));
418    }
419  else
420    {
421      char * base = tr_sys_path_basename (b->filename, NULL);
422      tr_logAddInfo (_("Blocklist \"%s\" updated with %"TR_PRIuSIZE" entries"), base, ranges_count);
423      tr_free (base);
424    }
425
426  tr_free (ranges);
427  fclose (out);
428  fclose (in);
429
430  blocklistLoad (b);
431
432  return ranges_count;
433}
Note: See TracBrowser for help on using the repository browser.