source: trunk/libtransmission/blocklist.c @ 11599

Last change on this file since 11599 was 11599, checked in by charles, 11 years ago

(trunk) Join the 21st century and use only 1 space at the end sentences. This commit is nearly as important as the semi-annual ones that remove trailing spaces from the ends of lines of code... :)

  • Property svn:keywords set to Date Rev Author Id
File size: 8.1 KB
Line 
1/*
2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: blocklist.c 11599 2010-12-27 19:18:17Z charles $
11 */
12
13#include <stdio.h>
14#include <stdlib.h> /* free() */
15#include <string.h>
16
17#ifdef WIN32
18 #include <w32api.h>
19 #define WINVER  WindowsXP
20 #include <windows.h>
21 #define PROT_READ      PAGE_READONLY
22 #define MAP_PRIVATE    FILE_MAP_COPY
23#endif
24
25#ifndef WIN32
26 #include <sys/mman.h>
27#endif
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <unistd.h>
32#include <assert.h>
33
34#include "transmission.h"
35#include "platform.h"
36#include "blocklist.h"
37#include "net.h"
38#include "utils.h"
39
40#ifndef O_BINARY
41 #define O_BINARY 0
42#endif
43
44
45/***
46****  PRIVATE
47***/
48
49struct tr_ip_range
50{
51    uint32_t    begin;
52    uint32_t    end;
53};
54
55struct tr_blocklist
56{
57    tr_bool               isEnabled;
58    int                   fd;
59    size_t                ruleCount;
60    size_t                byteCount;
61    char *                filename;
62    struct tr_ip_range *  rules;
63};
64
65static void
66blocklistClose( tr_blocklist * b )
67{
68    if( b->rules )
69    {
70        munmap( b->rules, b->byteCount );
71        close( b->fd );
72        b->rules = NULL;
73        b->ruleCount = 0;
74        b->byteCount = 0;
75        b->fd = -1;
76    }
77}
78
79static void
80blocklistLoad( tr_blocklist * b )
81{
82    int fd;
83    size_t byteCount;
84    struct stat st;
85    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );
86
87    blocklistClose( b );
88
89    if( stat( b->filename, &st ) == -1 )
90        return;
91
92    fd = open( b->filename, O_RDONLY | O_BINARY );
93    if( fd == -1 )
94    {
95        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
96        return;
97    }
98
99    byteCount = (size_t) st.st_size;
100    b->rules = mmap( NULL, byteCount, PROT_READ, MAP_PRIVATE, fd, 0 );
101    if( !b->rules )
102    {
103        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
104        close( fd );
105        return;
106    }
107
108    b->fd = fd;
109    b->byteCount = byteCount;
110    b->ruleCount = byteCount / sizeof( struct tr_ip_range );
111
112    {
113        char * base = tr_basename( b->filename );
114        tr_inf( _( "Blocklist \"%s\" contains %zu entries" ), base, b->ruleCount );
115        tr_free( base );
116    }
117}
118
119static void
120blocklistEnsureLoaded( tr_blocklist * b )
121{
122    if( !b->rules )
123        blocklistLoad( b );
124}
125
126static int
127compareAddressToRange( const void * va,
128                       const void * vb )
129{
130    const uint32_t *           a = va;
131    const struct tr_ip_range * b = vb;
132
133    if( *a < b->begin ) return -1;
134    if( *a > b->end ) return 1;
135    return 0;
136}
137
138static void
139blocklistDelete( tr_blocklist * b )
140{
141    blocklistClose( b );
142    unlink( b->filename );
143}
144
145/***
146****  PACKAGE-VISIBLE
147***/
148
149tr_blocklist *
150_tr_blocklistNew( const char * filename, tr_bool isEnabled )
151{
152    tr_blocklist * b;
153
154    b = tr_new0( tr_blocklist, 1 );
155    b->fd = -1;
156    b->filename = tr_strdup( filename );
157    b->isEnabled = isEnabled;
158
159    return b;
160}
161
162const char*
163_tr_blocklistGetFilename( const tr_blocklist * b )
164{
165    return b->filename;
166}
167
168void
169_tr_blocklistFree( tr_blocklist * b )
170{
171    blocklistClose( b );
172    tr_free( b->filename );
173    tr_free( b );
174}
175
176int
177_tr_blocklistExists( const tr_blocklist * b )
178{
179    struct stat st;
180
181    return !stat( b->filename, &st );
182}
183
184int
185_tr_blocklistGetRuleCount( const tr_blocklist * b )
186{
187    blocklistEnsureLoaded( (tr_blocklist*)b );
188
189    return b->ruleCount;
190}
191
192int
193_tr_blocklistIsEnabled( tr_blocklist * b )
194{
195    return b->isEnabled;
196}
197
198void
199_tr_blocklistSetEnabled( tr_blocklist * b,
200                         int            isEnabled )
201{
202    b->isEnabled = isEnabled ? 1 : 0;
203}
204
205int
206_tr_blocklistHasAddress( tr_blocklist     * b,
207                         const tr_address * addr )
208{
209    uint32_t                   needle;
210    const struct tr_ip_range * range;
211
212    assert( tr_isAddress( addr ) );
213
214    if( !b->isEnabled || addr->type == TR_AF_INET6 )
215        return 0;
216
217    blocklistEnsureLoaded( b );
218
219    if( !b->rules || !b->ruleCount )
220        return 0;
221
222    needle = ntohl( addr->addr.addr4.s_addr );
223
224    range = bsearch( &needle,
225                     b->rules,
226                     b->ruleCount,
227                     sizeof( struct tr_ip_range ),
228                     compareAddressToRange );
229
230    return range != NULL;
231}
232
233/*
234 * P2P plaintext format: "comment:x.x.x.x-y.y.y.y"
235 * http://wiki.phoenixlabs.org/wiki/P2P_Format
236 * http://en.wikipedia.org/wiki/PeerGuardian#P2P_plaintext_format
237 */
238static tr_bool
239parseLine1( const char * line, struct tr_ip_range * range )
240{
241    char * walk;
242    int b[4];
243    int e[4];
244    char str[64];
245    tr_address addr;
246
247    walk = strrchr( line, ':' );
248    if( !walk )
249        return FALSE;
250    ++walk; /* walk past the colon */
251
252    if( sscanf( walk, "%d.%d.%d.%d-%d.%d.%d.%d",
253                &b[0], &b[1], &b[2], &b[3],
254                &e[0], &e[1], &e[2], &e[3] ) != 8 )
255        return FALSE;
256
257    tr_snprintf( str, sizeof( str ), "%d.%d.%d.%d", b[0], b[1], b[2], b[3] );
258    if( tr_pton( str, &addr ) == NULL )
259        return FALSE;
260    range->begin = ntohl( addr.addr.addr4.s_addr );
261
262    tr_snprintf( str, sizeof( str ), "%d.%d.%d.%d", e[0], e[1], e[2], e[3] );
263    if( tr_pton( str, &addr ) == NULL )
264        return FALSE;
265    range->end = ntohl( addr.addr.addr4.s_addr );
266
267    return TRUE;
268}
269
270/*
271 * DAT format: "000.000.000.000 - 000.255.255.255 , 000 , invalid ip"
272 * http://wiki.phoenixlabs.org/wiki/DAT_Format
273 */
274static tr_bool
275parseLine2( const char * line, struct tr_ip_range * range )
276{
277    int unk;
278    int a[4];
279    int b[4];
280    char str[32];
281    tr_address addr;
282
283    if( sscanf( line, "%3d.%3d.%3d.%3d - %3d.%3d.%3d.%3d , %3d , ",
284                &a[0], &a[1], &a[2], &a[3],
285                &b[0], &b[1], &b[2], &b[3],
286                &unk ) != 9 )
287        return FALSE;
288
289    tr_snprintf( str, sizeof(str), "%d.%d.%d.%d", a[0], a[1], a[2], a[3] );
290    if( tr_pton( str, &addr ) == NULL )
291        return FALSE;
292    range->begin = ntohl( addr.addr.addr4.s_addr );
293
294    tr_snprintf( str, sizeof(str), "%d.%d.%d.%d", b[0], b[1], b[2], b[3] );
295    if( tr_pton( str, &addr ) == NULL )
296        return FALSE;
297    range->end = ntohl( addr.addr.addr4.s_addr );
298
299    return TRUE;
300}
301
302static int
303parseLine( const char * line, struct tr_ip_range * range )
304{
305    return parseLine1( line, range )
306        || parseLine2( line, range );
307}
308
309int
310_tr_blocklistSetContent( tr_blocklist * b,
311                         const char *   filename )
312{
313    FILE * in;
314    FILE * out;
315    int inCount = 0;
316    int outCount = 0;
317    char line[2048];
318    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );
319
320    if( !filename )
321    {
322        blocklistDelete( b );
323        return 0;
324    }
325
326    in = fopen( filename, "rb" );
327    if( !in )
328    {
329        tr_err( err_fmt, filename, tr_strerror( errno ) );
330        return 0;
331    }
332
333    blocklistClose( b );
334
335    out = fopen( b->filename, "wb+" );
336    if( !out )
337    {
338        tr_err( err_fmt, b->filename, tr_strerror( errno ) );
339        fclose( in );
340        return 0;
341    }
342
343    while( fgets( line, sizeof( line ), in ) != NULL )
344    {
345        char * walk;
346        struct tr_ip_range range;
347
348        ++inCount;
349
350        /* zap the linefeed */
351        if(( walk = strchr( line, '\r' ))) *walk = '\0';
352        if(( walk = strchr( line, '\n' ))) *walk = '\0';
353
354        if( !parseLine( line, &range ) )
355        {
356            /* don't try to display the actual lines - it causes issues */
357            tr_err( _( "blocklist skipped invalid address at line %d" ), inCount );
358            continue;
359        }
360
361        if( fwrite( &range, sizeof( struct tr_ip_range ), 1, out ) != 1 )
362        {
363            tr_err( _( "Couldn't save file \"%1$s\": %2$s" ), b->filename,
364                   tr_strerror( errno ) );
365            break;
366        }
367
368        ++outCount;
369    }
370
371    {
372        char * base = tr_basename( b->filename );
373        tr_inf( _( "Blocklist \"%s\" updated with %d entries" ), base, outCount );
374        tr_free( base );
375    }
376
377    fclose( out );
378    fclose( in );
379
380    blocklistLoad( b );
381
382    return outCount;
383}
384
Note: See TracBrowser for help on using the repository browser.