source: trunk/libtransmission/blocklist.c @ 10754

Last change on this file since 10754 was 10754, checked in by charles, 12 years ago

(trunk libT) #3293 "fggets() is overkill for parsing the blocklist" -- fixed in trunk for 2.00

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