source: trunk/libtransmission/blocklist.c @ 12228

Last change on this file since 12228 was 12228, checked in by jordan, 11 years ago

(trunk libT) still fiddling around with #includes -- this time removing unncecessary libT includes from libT .c files

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