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 $
13 #include <stdlib.h> /* bsearch (), qsort () */
16 #include <unistd.h> /* close () */
20 #define WINVER WindowsXP
22 #define PROT_READ PAGE_READONLY
23 #define MAP_PRIVATE FILE_MAP_COPY
29 #include <sys/types.h>
33 #include "transmission.h"
34 #include "blocklist.h"
54 struct tr_blocklistFile
61 struct tr_ipv4_range
* rules
;
65 blocklistClose (tr_blocklistFile
* b
)
69 munmap (b
->rules
, b
->byteCount
);
79 blocklistLoad (tr_blocklistFile
* b
)
85 const char * err_fmt
= _("Couldn't read \"%1$s\": %2$s");
89 if (stat (b
->filename
, &st
) == -1)
92 fd
= open (b
->filename
, O_RDONLY
| O_BINARY
);
95 tr_logAddError (err_fmt
, b
->filename
, tr_strerror (errno
));
99 byteCount
= (size_t) st
.st_size
;
100 b
->rules
= mmap (NULL
, byteCount
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
103 tr_logAddError (err_fmt
, b
->filename
, tr_strerror (errno
));
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
);
118 blocklistEnsureLoaded (tr_blocklistFile
* b
)
120 if (b
->rules
== NULL
)
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;
136 blocklistDelete (tr_blocklistFile
* b
)
139 tr_remove (b
->filename
);
147 tr_blocklistFileNew (const char * filename
, bool isEnabled
)
149 tr_blocklistFile
* b
;
151 b
= tr_new0 (tr_blocklistFile
, 1);
153 b
->filename
= tr_strdup (filename
);
154 b
->isEnabled
= isEnabled
;
160 tr_blocklistFileGetFilename (const tr_blocklistFile
* b
)
166 tr_blocklistFileFree (tr_blocklistFile
* b
)
169 tr_free (b
->filename
);
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
);
188 tr_blocklistFileIsEnabled (tr_blocklistFile
* b
)
194 tr_blocklistFileSetEnabled (tr_blocklistFile
* b
, bool isEnabled
)
197 assert (tr_isBool (isEnabled
));
199 b
->isEnabled
= isEnabled
;
203 tr_blocklistFileHasAddress (tr_blocklistFile
* b
, const tr_address
* addr
)
206 const struct tr_ipv4_range
* range
;
208 assert (tr_address_is_valid (addr
));
210 if (!b
->isEnabled
|| addr
->type
== TR_AF_INET6
)
213 blocklistEnsureLoaded (b
);
215 if (!b
->rules
|| !b
->ruleCount
)
218 needle
= ntohl (addr
->addr
.addr4
.s_addr
);
220 range
= bsearch (&needle
,
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
235 parseLine1 (const char * line
, struct tr_ipv4_range
* range
)
243 walk
= strrchr (line
, ':');
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)
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
))
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
))
261 range
->end
= ntohl (addr
.addr
.addr4
.s_addr
);
267 * DAT format: "000.000.000.000 - 000.255.255.255 , 000 , invalid ip"
268 * http://wiki.phoenixlabs.org/wiki/DAT_Format
271 parseLine2 (const char * line
, struct tr_ipv4_range
* range
)
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],
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
))
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
))
293 range
->end
= ntohl (addr
.addr
.addr4
.s_addr
);
299 parseLine (const char * line
, struct tr_ipv4_range
* range
)
301 return parseLine1 (line
, range
)
302 || parseLine2 (line
, range
);
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;
316 tr_blocklistFileSetContent (tr_blocklistFile
* b
, const char * filename
)
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;
333 in
= fopen (filename
, "rb");
336 tr_logAddError (err_fmt
, filename
, tr_strerror (errno
));
342 out
= fopen (b
->filename
, "wb+");
345 tr_logAddError (err_fmt
, b
->filename
, tr_strerror (errno
));
350 /* load the rules into memory */
351 while (fgets (line
, sizeof (line
), in
) != NULL
)
354 struct tr_ipv4_range range
;
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
);
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
;
385 qsort (ranges
, ranges_count
, sizeof (struct tr_ipv4_range
),
386 compareAddressRangesByFirstAddress
);
389 for (r
=ranges
+1, end
=ranges
+ranges_count
; r
!=end
; ++r
) {
390 if (keep
->end
< r
->begin
)
392 else if (keep
->end
< r
->end
)
396 ranges_count
= keep
+ 1 - ranges
;
399 /* sanity checks: make sure the rules are sorted
400 * in ascending order and don't overlap */
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
);
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
));
419 char * base
= tr_basename (b
->filename
);
420 tr_logAddInfo (_("Blocklist \"%s\" updated with %"TR_PRIuSIZE
" entries"), base
, ranges_count
);