├── tests.d ├── 28-binary-empty │ ├── empty │ ├── output │ └── cmd.sh ├── 15-empty-inputs │ ├── input1 │ ├── valid_dir │ │ └── input2 │ ├── output │ └── cmd.sh ├── 23-empty-input-count │ ├── output │ ├── empty_file │ └── cmd.sh ├── 12-directory-list │ ├── ipdir │ │ ├── file1 │ │ ├── file3 │ │ ├── file2 │ │ └── subdir │ │ │ └── file4 │ ├── output │ └── cmd.sh ├── 16-recursive-directory │ ├── dir │ │ ├── file1 │ │ └── subdir │ │ │ └── file2 │ ├── output │ └── cmd.sh ├── 19-nonexistent-paths │ ├── input1 │ ├── output │ └── cmd.sh ├── 21-special-chars │ ├── output │ ├── weird_filename with spaces.txt │ └── cmd.sh ├── 26-empty-file-in-list-count │ ├── empty_file │ ├── output │ ├── filelist │ └── cmd.sh ├── 02-common │ ├── output │ ├── input1 │ ├── input2 │ └── cmd.sh ├── 05-file-list │ ├── filelist │ ├── input1 │ ├── input2 │ ├── input3 │ ├── cmd.sh │ └── output ├── 18-invalid-input │ ├── output │ ├── input1 │ └── cmd.sh ├── 24-nonexistent-file-count │ ├── output │ └── cmd.sh ├── 29-mixed-text-binary │ ├── input2 │ ├── input1 │ ├── output │ └── cmd.sh ├── 25-missing-file-in-list-count │ ├── filelist │ ├── output │ └── cmd.sh ├── 07-file-list-compare-next │ ├── filelist1 │ ├── filelist2 │ ├── input1 │ ├── input2 │ ├── output │ └── cmd.sh ├── 13-complex-compare-next │ ├── filelist1 │ ├── filelist2 │ ├── input10 │ ├── input11 │ ├── input5 │ ├── input6 │ ├── dir1 │ │ ├── input5 │ │ └── input6 │ ├── dir2 │ │ ├── input10 │ │ └── input11 │ ├── input2 │ ├── input3 │ ├── input4 │ ├── input8 │ ├── input9 │ ├── input1 │ ├── input7 │ ├── cmd.sh │ └── output ├── 22-binary-to-text │ ├── input1 │ ├── output │ └── cmd.sh ├── 03-exclude │ ├── input2 │ ├── input1 │ ├── cmd.sh │ └── output ├── 06-file-list-compare │ ├── filelist │ ├── input1 │ ├── input2 │ ├── input3 │ ├── cmd.sh │ └── output ├── 11-diff-no-difference │ ├── output │ ├── input1 │ ├── input2 │ └── cmd.sh ├── 04-diff │ ├── input1 │ ├── input2 │ ├── output │ └── cmd.sh ├── 09-prefix-suffix │ ├── input1 │ ├── output │ └── cmd.sh ├── 14-has-directory-loading │ ├── output │ └── cmd.sh ├── 01-basic-merge │ ├── input1 │ ├── input2 │ ├── cmd.sh │ └── output ├── 10-dont-fix-network │ ├── input1 │ ├── cmd.sh │ └── output ├── 08-print-ranges │ ├── input1 │ ├── cmd.sh │ └── output ├── 27-binary-save-load │ ├── input1 │ ├── output │ └── cmd.sh ├── 17-mixed-input-formats │ ├── cmd.sh │ ├── input1 │ └── output ├── 20-large-scale │ ├── cmd.sh │ └── output └── README.md ├── autogen.sh ├── hooks ├── prepare-commit-msg ├── post-commit └── pre-commit ├── .github └── workflows │ ├── setup.sh │ └── publish.yml ├── ipset_copy.h ├── ipset_diff.h ├── ipset_merge.h ├── ipset_common.h ├── ipset_combine.h ├── ipset_exclude.h ├── ipset_reduce.h ├── ipset_optimize.h ├── ipset_load.h ├── ipset_binary.h ├── .gitignore ├── iprange-9999.ebuild ├── README.md ├── CMakeLists.txt ├── ipset_merge.c ├── ipset_copy.c ├── ipset_print.h ├── ipset_combine.c ├── COPYING ├── iprange.spec.in ├── packaging ├── release-msg ├── git-build ├── gpg-recv-key ├── README.md ├── tar-compare ├── check-files ├── update-tags └── packaging.functions ├── m4 ├── ax_need_prog.m4 └── ax_pthread.m4 ├── Makefile.am ├── ChangeLog ├── ipset_common.c ├── configure.ac ├── ipset_exclude.c ├── ipset_diff.c ├── run-tests.sh ├── ipset_optimize.c ├── ipset.c ├── ipset.h ├── ipset_reduce.c ├── ipset_binary.c ├── iprange.h ├── ipset_print.c └── ipset_load.c /tests.d/28-binary-empty/empty: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests.d/28-binary-empty/output: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests.d/15-empty-inputs/input1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 -------------------------------------------------------------------------------- /tests.d/23-empty-input-count/output: -------------------------------------------------------------------------------- 1 | 0,0 2 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | autoreconf -ivf 3 | -------------------------------------------------------------------------------- /tests.d/12-directory-list/ipdir/file1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 -------------------------------------------------------------------------------- /tests.d/12-directory-list/ipdir/file3: -------------------------------------------------------------------------------- 1 | 10.0.0.1 -------------------------------------------------------------------------------- /tests.d/16-recursive-directory/dir/file1: -------------------------------------------------------------------------------- 1 | 10.0.0.1 -------------------------------------------------------------------------------- /tests.d/19-nonexistent-paths/input1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 -------------------------------------------------------------------------------- /tests.d/21-special-chars/output: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | -------------------------------------------------------------------------------- /tests.d/23-empty-input-count/empty_file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests.d/12-directory-list/ipdir/file2: -------------------------------------------------------------------------------- 1 | 192.168.1.2/31 -------------------------------------------------------------------------------- /tests.d/12-directory-list/ipdir/subdir/file4: -------------------------------------------------------------------------------- 1 | 8.8.8.8 -------------------------------------------------------------------------------- /tests.d/15-empty-inputs/valid_dir/input2: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | -------------------------------------------------------------------------------- /tests.d/26-empty-file-in-list-count/empty_file: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests.d/26-empty-file-in-list-count/output: -------------------------------------------------------------------------------- 1 | 0,0 2 | -------------------------------------------------------------------------------- /tests.d/16-recursive-directory/dir/subdir/file2: -------------------------------------------------------------------------------- 1 | 10.0.0.2 -------------------------------------------------------------------------------- /tests.d/02-common/output: -------------------------------------------------------------------------------- 1 | 10.0.0.2/31 2 | 192.168.1.0/28 3 | -------------------------------------------------------------------------------- /tests.d/05-file-list/filelist: -------------------------------------------------------------------------------- 1 | # File list 2 | input1 3 | input2 -------------------------------------------------------------------------------- /tests.d/18-invalid-input/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 192.168.1.1 3 | -------------------------------------------------------------------------------- /tests.d/24-nonexistent-file-count/output: -------------------------------------------------------------------------------- 1 | FAILED AS EXPECTED 2 | -------------------------------------------------------------------------------- /tests.d/26-empty-file-in-list-count/filelist: -------------------------------------------------------------------------------- 1 | empty_file 2 | -------------------------------------------------------------------------------- /tests.d/29-mixed-text-binary/input2: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 10.0.0.2 3 | -------------------------------------------------------------------------------- /tests.d/21-special-chars/weird_filename with spaces.txt: -------------------------------------------------------------------------------- 1 | 192.168.1.1 -------------------------------------------------------------------------------- /tests.d/25-missing-file-in-list-count/filelist: -------------------------------------------------------------------------------- 1 | non_existent_file 2 | -------------------------------------------------------------------------------- /tests.d/25-missing-file-in-list-count/output: -------------------------------------------------------------------------------- 1 | FAILED AS EXPECTED 2 | -------------------------------------------------------------------------------- /tests.d/29-mixed-text-binary/input1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | 192.168.1.2 3 | -------------------------------------------------------------------------------- /tests.d/07-file-list-compare-next/filelist1: -------------------------------------------------------------------------------- 1 | # First file list 2 | input1 -------------------------------------------------------------------------------- /tests.d/07-file-list-compare-next/filelist2: -------------------------------------------------------------------------------- 1 | # Second file list 2 | input2 -------------------------------------------------------------------------------- /tests.d/16-recursive-directory/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 250.250.250.250 3 | -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/filelist1: -------------------------------------------------------------------------------- 1 | # Filelist 1 2 | input3 3 | input4 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/filelist2: -------------------------------------------------------------------------------- 1 | # Filelist 2 2 | input8 3 | input9 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input10: -------------------------------------------------------------------------------- 1 | # input10 - in dir2 2 | 172.17.1.0/24 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input11: -------------------------------------------------------------------------------- 1 | # input11 - in dir2 2 | 172.18.1.0/24 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input5: -------------------------------------------------------------------------------- 1 | # input5 - in dir1 2 | 172.17.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input6: -------------------------------------------------------------------------------- 1 | # input6 - in dir1 2 | 172.18.0.0/16 -------------------------------------------------------------------------------- /tests.d/22-binary-to-text/input1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | 192.168.1.2 3 | 10.0.0.1 4 | -------------------------------------------------------------------------------- /tests.d/22-binary-to-text/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 192.168.1.1 3 | 192.168.1.2 4 | -------------------------------------------------------------------------------- /tests.d/03-exclude/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 10.0.0.0/28 3 | 192.168.1.128/25 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/dir1/input5: -------------------------------------------------------------------------------- 1 | # input5 - in dir1 2 | 172.17.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/dir1/input6: -------------------------------------------------------------------------------- 1 | # input6 - in dir1 2 | 172.18.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/dir2/input10: -------------------------------------------------------------------------------- 1 | # input10 - in dir2 2 | 172.17.1.0/24 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/dir2/input11: -------------------------------------------------------------------------------- 1 | # input11 - in dir2 2 | 172.18.1.0/24 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input2: -------------------------------------------------------------------------------- 1 | # input2 - direct file 2 | 192.168.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input3: -------------------------------------------------------------------------------- 1 | # input3 - from filelist1 2 | 10.0.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input4: -------------------------------------------------------------------------------- 1 | # input4 - from filelist1 2 | 10.1.0.0/16 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input8: -------------------------------------------------------------------------------- 1 | # input8 - from filelist2 2 | 10.0.1.0/24 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input9: -------------------------------------------------------------------------------- 1 | # input9 - from filelist2 2 | 10.1.1.0/24 -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/filelist: -------------------------------------------------------------------------------- 1 | # File list for comparison 2 | input1 3 | input2 -------------------------------------------------------------------------------- /tests.d/19-nonexistent-paths/output: -------------------------------------------------------------------------------- 1 | FAILED AS EXPECTED 1 2 | FAILED AS EXPECTED 2 3 | -------------------------------------------------------------------------------- /tests.d/05-file-list/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.1 3 | 10.0.0.1 4 | 172.16.1.1 -------------------------------------------------------------------------------- /tests.d/05-file-list/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.2 3 | 10.0.0.2 4 | 172.16.1.2 -------------------------------------------------------------------------------- /tests.d/05-file-list/input3: -------------------------------------------------------------------------------- 1 | # Third input file 2 | 192.168.1.3 3 | 10.0.0.3 4 | 172.16.1.3 -------------------------------------------------------------------------------- /tests.d/11-diff-no-difference/output: -------------------------------------------------------------------------------- 1 | # OK: No differences found (exit code 0, no output) 2 | -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input1: -------------------------------------------------------------------------------- 1 | # input1 - will be read from stdin 2 | 172.16.0.0/16 -------------------------------------------------------------------------------- /tests.d/29-mixed-text-binary/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 10.0.0.2 3 | 192.168.1.1 4 | 192.168.1.2 5 | -------------------------------------------------------------------------------- /tests.d/03-exclude/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.0/24 3 | 10.0.0.0/24 4 | 172.16.1.0/24 -------------------------------------------------------------------------------- /tests.d/12-directory-list/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 192.168.1.1 3 | 192.168.1.2/31 4 | 250.250.250.250 -------------------------------------------------------------------------------- /tests.d/15-empty-inputs/output: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | 10.0.0.1 3 | 192.168.5.0/24 4 | 192.168.6.0/24 5 | -------------------------------------------------------------------------------- /tests.d/04-diff/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.0/27 3 | 10.0.0.1 4 | 10.0.0.2 5 | 172.16.1.0/28 -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/input7: -------------------------------------------------------------------------------- 1 | # input7 - direct file after --compare-next 2 | 192.168.1.0/24 -------------------------------------------------------------------------------- /tests.d/04-diff/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.0/28 3 | 10.0.0.2 4 | 10.0.0.3 5 | 172.16.2.0/28 -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.0/28 3 | 10.0.0.1/32 4 | 172.16.1.0/28 -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.0/28 3 | 10.0.0.2/32 4 | 172.16.2.0/28 -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/input3: -------------------------------------------------------------------------------- 1 | # Third input file 2 | 192.168.1.16/28 3 | 10.0.0.3/32 4 | 172.16.3.0/28 -------------------------------------------------------------------------------- /hooks/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | test -x ./packaging/release-msg && exec ./packaging/release-msg "$@" 3 | -------------------------------------------------------------------------------- /tests.d/07-file-list-compare-next/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.0/28 3 | 10.0.0.1/32 4 | 172.16.1.0/28 -------------------------------------------------------------------------------- /tests.d/07-file-list-compare-next/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.0/28 3 | 10.0.0.2/32 4 | 172.16.2.0/28 -------------------------------------------------------------------------------- /tests.d/09-prefix-suffix/input1: -------------------------------------------------------------------------------- 1 | # Test prefixes and suffixes 2 | 192.168.1.0/24 3 | 10.0.0.1 4 | 172.16.1.0/28 -------------------------------------------------------------------------------- /.github/workflows/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | sudo apt install gnupg help2man fakeroot 7 | -------------------------------------------------------------------------------- /tests.d/02-common/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.0/24 3 | 10.0.0.1 4 | 10.0.0.2 5 | 10.0.0.3 6 | 172.16.1.0/24 -------------------------------------------------------------------------------- /tests.d/14-has-directory-loading/output: -------------------------------------------------------------------------------- 1 | iprange --has-directory-loading OK 2 | iprange --has-filelist-loading OK 3 | -------------------------------------------------------------------------------- /tests.d/01-basic-merge/input1: -------------------------------------------------------------------------------- 1 | # First input file 2 | 192.168.1.1 3 | 192.168.1.2 4 | 10.0.0.1 5 | 10.0.0.2 6 | 172.16.1.0/24 -------------------------------------------------------------------------------- /tests.d/02-common/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.0/28 3 | 10.0.0.2 4 | 10.0.0.3 5 | 10.0.0.4 6 | 172.16.2.0/24 -------------------------------------------------------------------------------- /tests.d/01-basic-merge/input2: -------------------------------------------------------------------------------- 1 | # Second input file 2 | 192.168.1.3 3 | 192.168.1.4 4 | 10.0.0.3 5 | 10.0.0.4 6 | 172.16.2.0/24 -------------------------------------------------------------------------------- /tests.d/10-dont-fix-network/input1: -------------------------------------------------------------------------------- 1 | # Test CIDRs with non-network addresses 2 | 192.168.1.5/24 3 | 10.0.0.10/28 4 | 172.16.1.7/28 -------------------------------------------------------------------------------- /tests.d/18-invalid-input/input1: -------------------------------------------------------------------------------- 1 | # Valid IP 2 | 192.168.1.1 3 | # Invalid IP 4 | 999.999.999.999 5 | # Valid IP again 6 | 10.0.0.1 -------------------------------------------------------------------------------- /tests.d/08-print-ranges/input1: -------------------------------------------------------------------------------- 1 | # Test ranges 2 | 192.168.1.0/24 3 | 10.0.0.1 4 | 10.0.0.2 5 | 10.0.0.3 6 | 10.0.0.4 7 | 172.16.1.0/28 -------------------------------------------------------------------------------- /tests.d/12-directory-list/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test the @directory feature 3 | echo "250.250.250.250" | ../../iprange - @ipdir -------------------------------------------------------------------------------- /tests.d/05-file-list/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test the @filename feature 3 | echo "250.250.250.250" | ../../iprange - @filelist input3 4 | -------------------------------------------------------------------------------- /tests.d/27-binary-save-load/input1: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | 192.168.1.2 3 | 192.168.1.5 4 | 192.168.1.6/31 5 | 192.168.1.8/31 6 | 192.168.1.10 7 | -------------------------------------------------------------------------------- /tests.d/27-binary-save-load/output: -------------------------------------------------------------------------------- 1 | 192.168.1.1 2 | 192.168.1.2 3 | 192.168.1.5 4 | 192.168.1.6/31 5 | 192.168.1.8/31 6 | 192.168.1.10 7 | -------------------------------------------------------------------------------- /hooks/post-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -x ./packaging/update-tags ] 3 | then 4 | exec git diff HEAD^ | ./packaging/update-tags - 5 | fi 6 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -x ./packaging/check-files ] 3 | then 4 | exec git diff --cached | ./packaging/check-files - 5 | fi 6 | -------------------------------------------------------------------------------- /tests.d/01-basic-merge/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test basic merging of IP sets 3 | echo "250.250.250.250" | ../../iprange input1 input2 - 4 | -------------------------------------------------------------------------------- /tests.d/03-exclude/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test excluding IPs from a set 3 | echo "250.250.250.250" | ../../iprange - input1 --except input2 4 | -------------------------------------------------------------------------------- /tests.d/05-file-list/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 10.0.0.2/31 3 | 172.16.1.1 4 | 172.16.1.2/31 5 | 192.168.1.1 6 | 192.168.1.2/31 7 | 250.250.250.250 8 | -------------------------------------------------------------------------------- /tests.d/08-print-ranges/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test printing IP ranges instead of CIDRs 3 | echo "250.250.250.250" | ../../iprange -j - input1 4 | -------------------------------------------------------------------------------- /tests.d/08-print-ranges/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1-10.0.0.4 2 | 172.16.1.0-172.16.1.15 3 | 192.168.1.0-192.168.1.255 4 | 250.250.250.250-250.250.250.250 5 | -------------------------------------------------------------------------------- /tests.d/11-diff-no-difference/input1: -------------------------------------------------------------------------------- 1 | # First input file - included into input2 2 | 192.168.1.0/28 3 | 10.0.0.1 4 | 10.0.0.2 5 | 172.16.1.0/28 6 | -------------------------------------------------------------------------------- /tests.d/23-empty-input-count/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Input is an empty set (file) 4 | echo >empty_file 5 | ../../iprange empty_file -C 6 | -------------------------------------------------------------------------------- /tests.d/03-exclude/output: -------------------------------------------------------------------------------- 1 | 10.0.0.16/28 2 | 10.0.0.32/27 3 | 10.0.0.64/26 4 | 10.0.0.128/25 5 | 172.16.1.0/24 6 | 192.168.1.0/25 7 | 250.250.250.250 8 | -------------------------------------------------------------------------------- /tests.d/09-prefix-suffix/output: -------------------------------------------------------------------------------- 1 | add 10.0.0.1 nomatch 2 | add 172.16.1.0/28 nomatch 3 | add 192.168.1.0/24 nomatch 4 | add 250.250.250.250 nomatch 5 | -------------------------------------------------------------------------------- /tests.d/10-dont-fix-network/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test the --dont-fix-network option 3 | echo "250.250.250.250" | ../../iprange --dont-fix-network - input1 4 | -------------------------------------------------------------------------------- /ipset_copy.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_COPY_H 2 | #define IPRANGE_IPSET_COPY_H 3 | 4 | extern ipset *ipset_copy(ipset *ips1); 5 | 6 | #endif //IPRANGE_IPSET_COPY_H 7 | -------------------------------------------------------------------------------- /tests.d/04-diff/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 10.0.0.3 3 | 172.16.1.0/28 4 | 172.16.2.0/28 5 | 192.168.1.16/28 6 | 250.250.250.250 7 | # OK: Differences found (exit code 1) 8 | -------------------------------------------------------------------------------- /tests.d/07-file-list-compare-next/output: -------------------------------------------------------------------------------- 1 | name1,name2,entries1,entries2,ips1,ips2,combined_ips,common_ips 2 | input1,input2,3,3,33,33,50,16 3 | hello,input2,1,3,1,33,34,0 -------------------------------------------------------------------------------- /tests.d/11-diff-no-difference/input2: -------------------------------------------------------------------------------- 1 | # Second input file - identical to input1 + stdin 2 | 192.168.1.0/28 3 | 10.0.0.1 4 | 10.0.0.2 5 | 172.16.1.0/28 6 | 250.250.250.250 7 | -------------------------------------------------------------------------------- /ipset_diff.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_DIFF_H 2 | #define IPRANGE_IPSET_DIFF_H 3 | 4 | extern ipset *ipset_diff(ipset *ips1, ipset *ips2); 5 | 6 | #endif //IPRANGE_IPSET_DIFF_H 7 | -------------------------------------------------------------------------------- /ipset_merge.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_MERGE_H 2 | #define IPRANGE_IPSET_MERGE_H 3 | 4 | extern void ipset_merge(ipset *to, ipset *add); 5 | 6 | #endif //IPRANGE_IPSET_MERGE_H 7 | -------------------------------------------------------------------------------- /tests.d/22-binary-to-text/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Convert to binary and back to text 4 | echo "250.250.250.250" | ../../iprange input1 --print-binary | ../../iprange 5 | -------------------------------------------------------------------------------- /ipset_common.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_COMMON_H 2 | #define IPRANGE_IPSET_COMMON_H 3 | 4 | extern ipset *ipset_common(ipset *ips1, ipset *ips2); 5 | 6 | #endif //IPRANGE_IPSET_COMMON_H 7 | -------------------------------------------------------------------------------- /tests.d/01-basic-merge/output: -------------------------------------------------------------------------------- 1 | 10.0.0.1 2 | 10.0.0.2/31 3 | 10.0.0.4 4 | 172.16.1.0/24 5 | 172.16.2.0/24 6 | 192.168.1.1 7 | 192.168.1.2/31 8 | 192.168.1.4 9 | 250.250.250.250 10 | -------------------------------------------------------------------------------- /tests.d/09-prefix-suffix/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test printing with prefix and suffix 3 | echo "250.250.250.250" | ../../iprange --print-prefix "add " --print-suffix " nomatch" input1 - -------------------------------------------------------------------------------- /tests.d/17-mixed-input-formats/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test handling of mixed input formats in the same file 3 | 4 | echo "250.250.250.250 - 250.250.250.251" | ../../iprange - input1 5 | -------------------------------------------------------------------------------- /ipset_combine.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_COMBINE_H 2 | #define IPRANGE_IPSET_COMBINE_H 3 | 4 | extern ipset *ipset_combine(ipset *ips1, ipset *ips2); 5 | 6 | #endif //IPRANGE_IPSET_COMBINE_H 7 | -------------------------------------------------------------------------------- /ipset_exclude.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_EXCLUDE_H 2 | #define IPRANGE_IPSET_EXCLUDE_H 3 | 4 | extern ipset *ipset_exclude(ipset *ips1, ipset *ips2); 5 | 6 | #endif //IPRANGE_IPSET_EXCLUDE_H 7 | -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test the @filename feature with compare mode 3 | echo "250.250.250.250" | ../../iprange --compare - as stdin @filelist input3 --header 4 | -------------------------------------------------------------------------------- /tests.d/28-binary-empty/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create binary file from empty input 4 | echo >empty 5 | echo "250.250.250.250" | ../../iprange empty --print-binary | ../../iprange 6 | -------------------------------------------------------------------------------- /tests.d/02-common/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test finding common IPs between sets 3 | cat </dev/null || echo "FAILED AS EXPECTED" 6 | -------------------------------------------------------------------------------- /tests.d/27-binary-save-load/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Binary files: read a plain text file with IPs and write a binary file 4 | 5 | echo "172.16.99.1" | ../../iprange input1 --print-binary | ../../iprange 6 | -------------------------------------------------------------------------------- /ipset_reduce.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_REDUCE_H 2 | #define IPRANGE_IPSET_REDUCE_H 3 | 4 | extern void ipset_reduce(ipset *ips, size_t acceptable_increase, size_t min_accepted); 5 | 6 | #endif //IPRANGE_IPSET_REDUCE_H 7 | -------------------------------------------------------------------------------- /tests.d/17-mixed-input-formats/output: -------------------------------------------------------------------------------- 1 | 10.0.0.0/24 2 | 10.1.0.1 3 | 172.16.1.1 4 | 172.16.1.2/31 5 | 172.16.1.4/30 6 | 172.16.1.8/31 7 | 172.16.1.10 8 | 192.168.1.1 9 | 192.168.2.0/24 10 | 250.250.250.250/31 11 | -------------------------------------------------------------------------------- /ipset_optimize.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_OPTIMIZE_H 2 | #define IPRANGE_IPSET_OPTIMIZE_H 3 | 4 | extern void ipset_optimize(ipset *ips); 5 | extern void ipset_optimize_all(ipset *root); 6 | 7 | #endif //IPRANGE_IPSET_OPTIMIZE_H 8 | -------------------------------------------------------------------------------- /tests.d/26-empty-file-in-list-count/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Input file list exists, but a file is empty, -C should output 0,0 and exit code should be 0 4 | 5 | echo >empty_file 6 | echo "empty_file" > filelist 7 | ../../iprange @filelist -C 8 | -------------------------------------------------------------------------------- /tests.d/20-large-scale/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test handling of large input files 3 | 4 | for x in {0..127}; do 5 | for y in {128..255}; do 6 | for z in 10 30 50; do 7 | echo "10.$z.$x.$y" 8 | done 9 | done 10 | done | ../../iprange - 11 | -------------------------------------------------------------------------------- /tests.d/10-dont-fix-network/output: -------------------------------------------------------------------------------- 1 | 10.0.0.10/31 2 | 10.0.0.12/30 3 | 172.16.1.7 4 | 172.16.1.8/29 5 | 192.168.1.5 6 | 192.168.1.6/31 7 | 192.168.1.8/29 8 | 192.168.1.16/28 9 | 192.168.1.32/27 10 | 192.168.1.64/26 11 | 192.168.1.128/25 12 | 250.250.250.250 13 | -------------------------------------------------------------------------------- /tests.d/25-missing-file-in-list-count/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Input file list exists, but a file in the list does not - -C should fail with exit code 1 3 | 4 | echo "non_existent_file" > filelist 5 | ../../iprange @filelist -C 2>/dev/null || echo "FAILED AS EXPECTED" 6 | -------------------------------------------------------------------------------- /ipset_load.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_LOAD_H 2 | #define IPRANGE_IPSET_LOAD_H 3 | 4 | extern int dns_threads_max; 5 | extern int dns_silent; 6 | extern int dns_progress; 7 | 8 | extern ipset *ipset_load(const char *filename); 9 | 10 | #endif //IPRANGE_IPSET_LOAD_H 11 | -------------------------------------------------------------------------------- /tests.d/06-file-list-compare/output: -------------------------------------------------------------------------------- 1 | name1,name2,entries1,entries2,ips1,ips2,combined_ips,common_ips 2 | input3,input2,3,3,33,33,66,0 3 | input3,input1,3,3,33,33,66,0 4 | input3,stdin,3,1,33,1,34,0 5 | input2,input1,3,3,33,33,50,16 6 | input2,stdin,3,1,33,1,34,0 7 | input1,stdin,3,1,33,1,34,0 8 | -------------------------------------------------------------------------------- /tests.d/18-invalid-input/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test handling of invalid input 3 | # iprange should ignore invalid IPs and continue with valid ones 4 | 5 | cat </dev/null 6 | 12345678901234567890 7 | 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20 8 | 1.2.3.123456 9 | 1.23456.7.8 10 | EOF 11 | -------------------------------------------------------------------------------- /tests.d/19-nonexistent-paths/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test handling of non-existent files and directories 3 | # The program should gracefully handle these cases 4 | 5 | ../../iprange nonexistent_file input1 2>/dev/null || echo "FAILED AS EXPECTED 1" 6 | ../../iprange input1 @nonexistent_file 2>/dev/null || echo "FAILED AS EXPECTED 2" 7 | -------------------------------------------------------------------------------- /ipset_binary.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_LOAD_SAVE_H 2 | #define IPRANGE_IPSET_LOAD_SAVE_H 3 | 4 | #define BINARY_HEADER_V10 "iprange binary format v1.0\n" 5 | 6 | extern int ipset_load_binary_v10(FILE *fp, ipset *ips, int first_line_missing); 7 | extern void ipset_save_binary_v10(ipset *ips); 8 | 9 | #endif //IPRANGE_IPSET_LOAD_SAVE_H 10 | -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test all input methods with --compare-next 3 | # First set: stdin(input1), input2, @filelist1(input3,input4), @dir1(input5,input6) 4 | # Second set: input7, @filelist2(input8,input9), @dir2(input10,input11) 5 | 6 | # Add header to the output 7 | ../../iprange --header - input2 @filelist1 @dir1 --compare-next input7 @filelist2 @dir2 < input1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .libs 3 | 4 | *.o 5 | *.in 6 | *~ 7 | *.1 8 | 9 | Makefile 10 | aclocal.m4 11 | autom4te.cache 12 | compile 13 | config.guess 14 | config.h 15 | config.log 16 | config.status 17 | config.sub 18 | configure 19 | depcomp 20 | install-sh 21 | libtool 22 | ltmain.sh 23 | missing 24 | stamp-h1 25 | 26 | iprange 27 | iprange.spec 28 | 29 | *.tar.gz 30 | *.tar.bz2 31 | *.tar.xz 32 | -------------------------------------------------------------------------------- /iprange-9999.ebuild: -------------------------------------------------------------------------------- 1 | # Copyright 1999-2015 Gentoo Foundation 2 | # Distributed under the terms of the GNU General Public License v2 3 | # $Id$ 4 | 5 | EAPI=5 6 | 7 | inherit autotools git-2 8 | 9 | DESCRIPTION="manage IP ranges" 10 | HOMEPAGE="https://github.com/firehol/iprange" 11 | EGIT_REPO_URI="https://github.com/firehol/iprange" 12 | 13 | LICENSE="GPL-2+" 14 | SLOT="0" 15 | KEYWORDS="" 16 | IUSE="" 17 | 18 | RDEPEND="" 19 | DEPEND="${RDEPEND}" 20 | 21 | src_prepare() { 22 | eautoreconf 23 | } 24 | -------------------------------------------------------------------------------- /tests.d/29-mixed-text-binary/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Mixing text and binary files: have input1 as text and pipe the output of another iprange that creates a binary version of input2 4 | 5 | cat >input1 <input2 </dev/null 4 | if [ $? -eq 0 ]; then 5 | echo "iprange --has-directory-loading OK" 6 | else 7 | echo "iprange --has-directory-loading FAILED" 8 | fi 9 | 10 | # Also test the --has-filelist-loading flag 11 | ../../iprange --has-filelist-loading 2>/dev/null 12 | if [ $? -eq 0 ]; then 13 | echo "iprange --has-filelist-loading OK" 14 | else 15 | echo "iprange --has-filelist-loading FAILED" 16 | fi 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iprange - manage IP ranges 2 | ========================== 3 | 4 | Getting help 5 | ------------ 6 | 7 | ~~~~ 8 | iprange --help 2>&1 | more 9 | ~~~~ 10 | 11 | Installation from tar-file 12 | -------------------------- 13 | 14 | ~~~~ 15 | ./configure && make && make install 16 | ~~~~ 17 | 18 | 19 | Installation from git 20 | --------------------- 21 | 22 | ~~~~ 23 | ./autogen.sh 24 | ./configure && make && make install 25 | ~~~~ 26 | 27 | When working with git, copy the hooks to the cloned folder: 28 | 29 | ~~~~ 30 | cp hooks/* .git/hooks 31 | ~~~~ 32 | -------------------------------------------------------------------------------- /tests.d/04-diff/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test symmetric difference between sets 3 | # The diff command should return exit code 1 when differences are found 4 | 5 | # Run the diff command 6 | echo "250.250.250.250" | ../../iprange - input1 --diff input2 7 | DIFF_EXIT=$? 8 | 9 | # Check that the exit code is 1 (differences found) 10 | if [ $DIFF_EXIT -ne 1 ]; then 11 | echo "# ERROR: Expected exit code 1 (differences found), got $DIFF_EXIT" 12 | exit 1 13 | fi 14 | 15 | # If we get here, test passed 16 | echo "# OK: Differences found (exit code 1)" 17 | exit 0 18 | -------------------------------------------------------------------------------- /tests.d/11-diff-no-difference/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Test diff mode with identical files (should return exit code 0) 3 | # This tests the case where there are no differences 4 | 5 | # Run the diff command 6 | echo "250.250.250.250" | ../../iprange - input1 --diff input2 7 | DIFF_EXIT=$? 8 | 9 | # Check that the exit code is 0 (no differences found) 10 | if [ $DIFF_EXIT -ne 0 ]; then 11 | echo "# ERROR: Expected exit code 0 (no differences), got $DIFF_EXIT" 12 | exit 1 13 | fi 14 | 15 | # If we get here, test passed 16 | echo "# OK: No differences found (exit code 0, no output)" 17 | exit 0 -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | project(iprange C) 3 | 4 | find_package (Threads) 5 | 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat-signedness -Werror=format-security") 7 | 8 | set(SOURCE_FILES 9 | config.h 10 | iprange.c ipset.c ipset.h iprange.h ipset_binary.c ipset_binary.h ipset_load.c ipset_load.h ipset_reduce.c ipset_print.c ipset_print.h ipset_optimize.c ipset_optimize.h ipset_reduce.h ipset_diff.c ipset_diff.h ipset_common.c ipset_common.h ipset_exclude.c ipset_exclude.h ipset_merge.c ipset_merge.h ipset_copy.c ipset_copy.h ipset_combine.c ipset_combine.h) 11 | 12 | include_directories(AFTER .) 13 | add_definitions("-DHAVE_CONFIG_H") 14 | 15 | add_executable(iprange_git ${SOURCE_FILES}) -------------------------------------------------------------------------------- /ipset_merge.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_merge() 5 | * 6 | * merges the second ipset (add) to the first ipset (to) 7 | * they may not be optimized 8 | * the result is never optimized (even if the sources are) 9 | * to optimize it call ipset_optimize() 10 | * 11 | */ 12 | 13 | inline void ipset_merge(ipset *to, ipset *add) { 14 | if(unlikely(debug)) fprintf(stderr, "%s: Merging %s to %s\n", PROG, add->filename, to->filename); 15 | 16 | ipset_grow(to, add->entries); 17 | 18 | memcpy(&to->netaddrs[to->entries], &add->netaddrs[0], add->entries * sizeof(network_addr_t)); 19 | 20 | to->entries = to->entries + add->entries; 21 | to->lines += add->lines; 22 | to->flags &= ~IPSET_FLAG_OPTIMIZED; 23 | } 24 | -------------------------------------------------------------------------------- /ipset_copy.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_copy() 5 | * 6 | * it returns a new ipset that is an exact copy of the ipset given 7 | * 8 | */ 9 | 10 | inline ipset *ipset_copy(ipset *ips1) { 11 | ipset *ips; 12 | 13 | if(unlikely(debug)) fprintf(stderr, "%s: Copying %s\n", PROG, ips1->filename); 14 | 15 | ips = ipset_create(ips1->filename, ips1->entries); 16 | if(unlikely(!ips)) return NULL; 17 | 18 | /*strcpy(ips->name, ips1->name); */ 19 | memcpy(&ips->netaddrs[0], &ips1->netaddrs[0], ips1->entries * sizeof(network_addr_t)); 20 | 21 | ips->entries = ips1->entries; 22 | ips->unique_ips = ips1->unique_ips; 23 | ips->lines = ips1->lines; 24 | ips->flags = ips1->flags; 25 | 26 | return ips; 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /ipset_print.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_PRINT_H 2 | #define IPRANGE_IPSET_PRINT_H 3 | 4 | typedef enum ipset_print_cmd { 5 | PRINT_RANGE = 1, 6 | PRINT_CIDR = 2, 7 | PRINT_SINGLE_IPS = 3, 8 | PRINT_BINARY = 4 9 | } IPSET_PRINT_CMD; 10 | 11 | extern uint8_t prefix_enabled[]; 12 | 13 | extern char *print_prefix_ips; 14 | extern char *print_prefix_nets; 15 | extern char *print_suffix_ips; 16 | extern char *print_suffix_nets; 17 | 18 | extern void ipset_print(ipset *ips, IPSET_PRINT_CMD print); 19 | 20 | extern void prefix_update_counters(in_addr_t addr, int prefix); 21 | extern void print_addr(in_addr_t addr, int prefix); 22 | extern void print_addr_range(in_addr_t lo, in_addr_t hi); 23 | extern void print_addr_single(in_addr_t x); 24 | 25 | extern int split_range(in_addr_t addr, int prefix, in_addr_t lo, in_addr_t hi, void (*print)(in_addr_t, int)); 26 | 27 | #endif //IPRANGE_IPSET_PRINT_H 28 | -------------------------------------------------------------------------------- /ipset_combine.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_combine() 5 | * 6 | * it returns a new ipset that has all the entries of both ipsets given 7 | * the result is never optimized, even when the source ipsets are 8 | * 9 | */ 10 | 11 | inline ipset *ipset_combine(ipset *ips1, ipset *ips2) { 12 | ipset *ips; 13 | 14 | if(unlikely(debug)) fprintf(stderr, "%s: Combining %s and %s\n", PROG, ips1->filename, ips2->filename); 15 | 16 | ips = ipset_create("combined", ips1->entries + ips2->entries); 17 | if(unlikely(!ips)) return NULL; 18 | 19 | memcpy(&ips->netaddrs[0], &ips1->netaddrs[0], ips1->entries * sizeof(network_addr_t)); 20 | memcpy(&ips->netaddrs[ips1->entries], &ips2->netaddrs[0], ips2->entries * sizeof(network_addr_t)); 21 | 22 | ips->entries = ips1->entries + ips2->entries; 23 | ips->lines = ips1->lines + ips2->lines; 24 | 25 | return ips; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests.d/15-empty-inputs/cmd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Create test files and directories 3 | touch empty_file 4 | touch empty_list 5 | mkdir -p empty_dir 6 | 7 | # Run all the test cases, piping different marker IPs to each 8 | # If any 172.16.x.x IPs appear in output, it means stdin was incorrectly read 9 | 10 | # Test cases that should NOT read from stdin 11 | echo "172.16.99.1" | ../../iprange empty_file 2>/dev/null 12 | echo "172.16.99.2" | ../../iprange @empty_list 2>/dev/null 13 | echo "172.16.99.3" | ../../iprange @empty_dir 2>/dev/null 14 | echo "172.16.99.4" | ../../iprange non_existent_file 2>/dev/null 15 | echo "172.16.99.5" | ../../iprange @non_existent_dir 2>/dev/null 16 | echo "172.16.99.6" | ../../iprange input1 17 | echo "172.16.99.7" | ../../iprange @valid_dir 18 | 19 | # Test cases that SHOULD read from stdin 20 | echo "192.168.5.0/24" | ../../iprange - 2>/dev/null 21 | echo "192.168.6.0/24" | ../../iprange 2>/dev/null 22 | 23 | # Cleanup 24 | rm -rf empty_file empty_list empty_dir 25 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | FireHOL Copyright 2 | 3 | Copyright (C) 2003-2015 Costa Tsaousis 4 | Copyright (C) 2012-2015 Phil Whineray 5 | 6 | Original iprange.c Copyright: 7 | 8 | Copyright (C) 2003 Gabriel L. Somlo 9 | 10 | comment by Costa Tsaousis: 11 | An excellent work by Gabriel Somlo for loading and merging CIDRs. 12 | I have built all the features this tool provides on top of the 13 | original work of Gabriel. 14 | 15 | License 16 | 17 | This program is free software; you can redistribute it and/or modify 18 | it under the terms of the GNU General Public License as published by 19 | the Free Software Foundation; either version 2 of the License, or 20 | (at your option) any later version. 21 | 22 | This program is distributed in the hope that it will be useful, 23 | but WITHOUT ANY WARRANTY; without even the implied warranty of 24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 | GNU General Public License for more details. 26 | 27 | You should have received a copy of the GNU General Public License 28 | along with this program. If not, see . 29 | -------------------------------------------------------------------------------- /iprange.spec.in: -------------------------------------------------------------------------------- 1 | Summary: Manage ip ranges 2 | Name: @PACKAGE_NAME@ 3 | Version: @PACKAGE_RPM_VERSION@ 4 | Release: @PACKAGE_RPM_RELEASE@%{?release_suffix}%{?dist} 5 | License: GPLv2+ 6 | URL: http://firehol.org 7 | Source: %{name}-@PACKAGE_VERSION@.tar.bz2 8 | 9 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 10 | 11 | %description 12 | Manage ip ranges. 13 | 14 | %prep 15 | %setup -q -n %{name}-@PACKAGE_VERSION@ 16 | 17 | %build 18 | %configure \ 19 | --docdir="%{_docdir}/%{name}-%{version}" \ 20 | %{?conf} 21 | make %{?_smp_mflags} 22 | 23 | %install 24 | rm -rf "%{buildroot}" 25 | make %{?_smp_mflags} install DESTDIR="%{buildroot}" 26 | 27 | %files 28 | %{_sbindir}/iprange 29 | 30 | %changelog 31 | * Sat Sep 16 2017 Phil Whineray - 1.0.4-1 32 | - Bugfix release 33 | 34 | * Sat Oct 16 2016 Costa Tsaousis - 1.0.3-1 35 | - speedup release 36 | 37 | * Sat Nov 28 2015 Phil Whineray - 1.0.2-1 38 | - Bugfix release 39 | 40 | * Sat Nov 28 2015 Phil Whineray - 1.0.1-1 41 | - Release new version 42 | 43 | * Sun Nov 15 2015 Alon Bar-Lev - 1.0.0-1 44 | - Initial add. 45 | -------------------------------------------------------------------------------- /packaging/release-msg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # release-msg 5 | # 6 | scriptname=release-msg 7 | if ! MYTMP=$(mktemp -d -t $scriptname-XXXXXX) 8 | then 9 | echo >&2 10 | echo >&2 11 | echo >&2 "Cannot create temporary directory." 12 | echo >&2 13 | exit 1 14 | fi 15 | 16 | cleanup() { 17 | status=$? 18 | rm -rf "${MYTMP}" 19 | exit $status 20 | } 21 | 22 | # clean up if we get stopped by Crtl-C or forced logout or normal exit 23 | trap cleanup INT 24 | trap cleanup HUP 25 | trap cleanup 0 26 | 27 | set -e 28 | if [ "$1" = "--debug" ] 29 | then 30 | set -x 31 | shift 32 | fi 33 | 34 | if [ $# -lt 1 ] 35 | then 36 | echo "Use only from .git/hooks/prepare-commit-msg" 37 | exit 1 38 | fi 39 | 40 | if [ ! -x packaging/release-msg ] 41 | then 42 | echo "Must be run from base directory" 43 | exit 1 44 | fi 45 | 46 | dirname="${0%/*}" 47 | if [ "$dirname" = "$0" ]; then dirname="."; fi 48 | source $dirname/packaging.functions 49 | 50 | mkdir -p $MYTMP/files 51 | git show :configure.ac > $MYTMP/files/configure.ac 52 | version=`get_configure_ac_version` 53 | case "$(match_version $version)" in 54 | prerelease|candidate|release) 55 | sed -i -e "1s/.*/Prepare release $version/" "$1" 56 | ;; 57 | esac 58 | 59 | exit 0 60 | -------------------------------------------------------------------------------- /m4/ax_need_prog.m4: -------------------------------------------------------------------------------- 1 | # 2 | # SYNOPSIS 3 | # 4 | # AX_NEED_PROG([VARIABLE],[program],[OPTIONS-IF-FOUND],[PATH]) 5 | # 6 | # DESCRIPTION 7 | # 8 | # Checks for an installed program binary, placing the PATH and 9 | # OPTIONS-IF-FOUND in the precious variable VARIABLE if so. 10 | # Uses AC_PATH_PROG, adding a test for success and bailing out if not. 11 | # 12 | # LICENSE 13 | # 14 | # Copyright (c) 2015 Phil Whineray 15 | # 16 | # Copying and distribution of this file, with or without modification, are 17 | # permitted in any medium without royalty provided the copyright notice 18 | # and this notice are preserved. This file is offered as-is, without any 19 | # warranty. 20 | 21 | AC_DEFUN([AX_NEED_PROG],[ 22 | pushdef([VARIABLE],$1) 23 | pushdef([EXECUTABLE],$2) 24 | pushdef([OPTIONS_IF_FOUND],$3) 25 | pushdef([PATH_PROG],$4) 26 | 27 | AS_IF([test "x$VARIABLE" = "x"],[ 28 | AC_PATH_PROG([]VARIABLE[], []EXECUTABLE[], [], []PATH_PROG[]) 29 | 30 | AS_IF([test "x$VARIABLE" = "x"],[ 31 | AC_MSG_ERROR([cannot find required executable, bailing out]) 32 | ],[ 33 | AS_IF([test x"OPTIONS_IF_FOUND" = "x"],[], 34 | [VARIABLE="$VARIABLE OPTIONS_IF_FOUND"]) 35 | ]) 36 | ]) 37 | 38 | popdef([PATH_PROG]) 39 | popdef([OPTIONS_IF_FOUND]) 40 | popdef([EXECUTABLE]) 41 | popdef([VARIABLE]) 42 | ]) 43 | -------------------------------------------------------------------------------- /tests.d/13-complex-compare-next/output: -------------------------------------------------------------------------------- 1 | name1,name2,entries1,entries2,ips1,ips2,combined_ips,common_ips 2 | dir1/input5,dir2/input11,1,1,65536,256,65792,0 3 | dir1/input5,dir2/input10,1,1,65536,256,65536,256 4 | dir1/input5,input9,1,1,65536,256,65792,0 5 | dir1/input5,input8,1,1,65536,256,65792,0 6 | dir1/input5,input7,1,1,65536,256,65792,0 7 | dir1/input6,dir2/input11,1,1,65536,256,65536,256 8 | dir1/input6,dir2/input10,1,1,65536,256,65792,0 9 | dir1/input6,input9,1,1,65536,256,65792,0 10 | dir1/input6,input8,1,1,65536,256,65792,0 11 | dir1/input6,input7,1,1,65536,256,65792,0 12 | input4,dir2/input11,1,1,65536,256,65792,0 13 | input4,dir2/input10,1,1,65536,256,65792,0 14 | input4,input9,1,1,65536,256,65536,256 15 | input4,input8,1,1,65536,256,65792,0 16 | input4,input7,1,1,65536,256,65792,0 17 | input3,dir2/input11,1,1,65536,256,65792,0 18 | input3,dir2/input10,1,1,65536,256,65792,0 19 | input3,input9,1,1,65536,256,65792,0 20 | input3,input8,1,1,65536,256,65536,256 21 | input3,input7,1,1,65536,256,65792,0 22 | input2,dir2/input11,1,1,65536,256,65792,0 23 | input2,dir2/input10,1,1,65536,256,65792,0 24 | input2,input9,1,1,65536,256,65792,0 25 | input2,input8,1,1,65536,256,65792,0 26 | input2,input7,1,1,65536,256,65536,256 27 | stdin,dir2/input11,1,1,65536,256,65792,0 28 | stdin,dir2/input10,1,1,65536,256,65792,0 29 | stdin,input9,1,1,65536,256,65792,0 30 | stdin,input8,1,1,65536,256,65792,0 31 | stdin,input7,1,1,65536,256,65792,0 32 | -------------------------------------------------------------------------------- /packaging/git-build: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # When run from the top-level repository, performs a complete clean 4 | # and maintainer-mode rebuild of the FireHOL package. 5 | 6 | if [ ! -f .gitignore -o ! -f configure.ac -o ! -x autogen.sh ] 7 | then 8 | echo "Run as ./packaging/git-build from an autotools git repository" 9 | exit 1 10 | fi 11 | 12 | # If we are genuinely in a git repo, try to clean it up, otherwise 13 | # just make the assumption 14 | if [ -d .git ] 15 | then 16 | if [ -n "$TRAVIS_TAG" ] 17 | then 18 | echo "Checking we have a good signature during CI build..." 19 | echo "Checking tag: $TRAVIS_TAG" 20 | git tag -v "$TRAVIS_TAG" 2>&1 | tee /tmp/tagcheck 21 | grep -iq "gpg. good signature" /tmp/tagcheck 22 | status=$? 23 | rm -f /tmp/tagcheck 24 | if [ $status -ne 0 ] 25 | then 26 | exit $status 27 | fi 28 | fi 29 | 30 | clean=$(git status -s | grep "^?") 31 | 32 | if [ "$clean" ] 33 | then 34 | if [ "$1" != "-ok" ] 35 | then 36 | echo "Warning: this script runs: git clean -d -f -x" 37 | echo " ensure all required ?? files are added, then re-run with '-ok'" 38 | git status -s | grep '^?' 39 | exit 1 40 | fi 41 | fi 42 | 43 | set -e 44 | git clean -d -f -x 45 | set +e 46 | fi 47 | 48 | set -e 49 | ./autogen.sh 50 | ./configure --enable-maintainer-mode 51 | set +e 52 | make dist 53 | status=$? 54 | exit $status 55 | -------------------------------------------------------------------------------- /packaging/gpg-recv-key: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! "$KEYURL" ] 4 | then 5 | echo "No KEYURL environment set, e.g.:" 6 | echo " export KEYURL='https://keyserver.ubuntu.com/pks/lookup?exact=on&op=get&search='" 7 | exit 1 8 | fi 9 | 10 | debug="" 11 | if [ "$debug" ] 12 | then 13 | if [ ! "$GNUPGHOME" ] 14 | then 15 | echo "No GNUPGHOME environment set, e.g.:" 16 | echo " export GNUPGHOME=$HOME/.gnupg" 17 | exit 1 18 | fi 19 | 20 | if [ ! -d "$GNUPGHOME" ] 21 | then 22 | mkdir -p "$GNUPGHOME" 23 | chmod 700 "$GNUPGHOME" 24 | fi 25 | fi 26 | 27 | keyuid="$1" 28 | fingerprint="$2" 29 | 30 | # Modern GPG can import key by fingerprint but the version available 31 | # within travis currently does not, so we extract the short version and 32 | # check it matches manually 33 | key=`echo $fingerprint | tr -d ' '` 34 | 35 | curl -o/tmp/keydata.asc "${KEYURL}0x$key" || exit 1 36 | gpg --import /tmp/keydata.asc || exit 1 37 | gpg --fingerprint "$key" > /tmp/keystatus.$$ 38 | status=$? 39 | 40 | cat /tmp/keystatus.$$ 41 | if [ $status -ne 0 ] 42 | then 43 | rm -f /tmp/keystatus.$$ 44 | exit 2 45 | fi 46 | 47 | if ! grep -q "^uid.*<$keyuid>" /tmp/keystatus.$$ 48 | then 49 | rm -f /tmp/keystatus.$$ 50 | echo "Did not find expected uid $keyuid" 51 | exit 3 52 | fi 53 | 54 | echo "uid looks good" 55 | 56 | if ! grep -q " $fingerprint$" /tmp/keystatus.$$ 57 | then 58 | rm -f /tmp/keystatus.$$ 59 | echo "Did not find expected fingerprint $fingerprint" 60 | exit 3 61 | fi 62 | 63 | echo "Fingerprint looks good" 64 | 65 | rm -f /tmp/keystatus.$$ 66 | exit 0 67 | -------------------------------------------------------------------------------- /tests.d/README.md: -------------------------------------------------------------------------------- 1 | # iprange Test Suite 2 | 3 | This directory contains tests for the iprange utility. 4 | 5 | ## Test Structure 6 | 7 | Each test is in its own subdirectory and contains: 8 | 9 | - `inputX` files (where X is a number) used as test inputs 10 | - An `output` file with the expected output 11 | - A `cmd.sh` script that runs the specific test case 12 | 13 | ## Running Tests 14 | 15 | To run all tests, use the master test script from the main directory: 16 | 17 | ``` 18 | ./run-tests.sh 19 | ``` 20 | 21 | The script will: 22 | 1. Run each test's `cmd.sh` script 23 | 2. Capture the output 24 | 3. Compare it with the expected output in the `output` file 25 | 4. Check exit codes 26 | 5. Report differences and failures 27 | 28 | ## Adding New Tests 29 | 30 | To add a new test: 31 | 32 | 1. Create a new directory in `tests.d` with a descriptive name 33 | 2. Create the necessary input files 34 | 3. Create a `cmd.sh` script that runs iprange with the desired options 35 | 4. Generate the expected output file by running `cmd.sh` and redirecting to `output` 36 | 5. Make `cmd.sh` executable with `chmod +x cmd.sh` 37 | 38 | ## Test Cases 39 | 40 | The test suite includes coverage for: 41 | 42 | 01. Basic merging of IP sets 43 | 02. Finding common IPs between sets 44 | 03. Excluding IPs from a set 45 | 04. Symmetric difference between sets (with differences - exit code 1) 46 | 05. Using @filename feature for file lists 47 | 06. Using @filename with compare mode 48 | 07. Using @filename with compare-next mode 49 | 08. Printing IP ranges instead of CIDRs 50 | 09. Using prefix and suffix for output 51 | 10. Using --dont-fix-network option 52 | 11. Symmetric difference with no differences (exit code 0) 53 | 54 | Each test verifies a specific feature or mode of the iprange utility. -------------------------------------------------------------------------------- /packaging/README.md: -------------------------------------------------------------------------------- 1 | Packaging Tools 2 | =============== 3 | 4 | The programs in this folder are used when packaging from within git 5 | and are not included in source or binary packages. 6 | 7 | For the most part they are used from the git commit hooks (copy 8 | `../hooks/*` to `../.git/hooks` to automate checking and the release 9 | process. 10 | 11 | The check-files script pulls in `*.functions` and `*/*.functions` to 12 | do the actual work. 13 | 14 | `packaging.functions` contains generic checks on e.g `ChangeLog` 15 | and `configure.ac` and automates release version, checking, tagging 16 | and post-release update. 17 | 18 | Programs and packages with specific needs should create extra 19 | `whatever.functions` and supporting scripts in a subdirectory. 20 | 21 | 22 | Making a release 23 | ---------------- 24 | ` 25 | Just update ChangeLog and configure.ac to specify a suitable version 26 | suffix: 27 | 28 | empty - final release 29 | pre.# - pre-release candidate 30 | rc.# - pre-release candidate 31 | 32 | If it is a final release and there is a package.spec.in, add a new 33 | entry to the top of the %changelog section and update: 34 | PACKAGE_RPM_RELEASE="1" 35 | 36 | The hooks will take over and if everything is OK will tag the release 37 | (you will be asked to sign the tag) and then update the files ready 38 | for further development. 39 | 40 | The release is not pushed out automatically, so if you want to undo 41 | it, run: 42 | 43 | ~~~~ 44 | git reset --hard HEAD^^ 45 | git tag -d vx.y.z 46 | ~~~~ 47 | 48 | Otherwise you can just push the results; the script outputs the required 49 | instructions upon success. 50 | 51 | Once pushed the infrastructure will build a set of tar-files on the server. 52 | For information on how to verify, sign and make these available, see: 53 | 54 | https://github.com/firehol/infrastructure/raw/master/doc/release.txt 55 | -------------------------------------------------------------------------------- /packaging/tar-compare: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # When provided with a git repo, which has been used to produce a 4 | # distribution tar.gz (with make dist) and the resultant tar-file, 5 | # lists files which appear in one or the other only, to help check 6 | # for missing EXTRA_DIST entries in Makefile.am files. 7 | 8 | scriptname=tar-compare 9 | if ! MYTMP=$(mktemp -d -t $scriptname-XXXXXX) 10 | then 11 | echo >&2 12 | echo >&2 13 | echo >&2 "Cannot create temporary directory." 14 | echo >&2 15 | exit 1 16 | fi 17 | 18 | cleanup() { 19 | status=$? 20 | rm -rf "${MYTMP}" 21 | exit $status 22 | } 23 | 24 | # clean up if we get stopped by Crtl-C or forced logout or normal exit 25 | trap cleanup INT 26 | trap cleanup HUP 27 | trap cleanup 0 28 | 29 | if [ $# -ne 2 ] 30 | then 31 | echo "tar-compare git-dir tar-gz-file" 32 | exit 1 33 | fi 34 | 35 | mkdir $MYTMP/unpack 36 | tar xfzC "$2" $MYTMP/unpack 37 | diff -r "$1" $MYTMP/unpack/* | grep "^Only" | sed \ 38 | -e '/: autom4te\.cache$/d' \ 39 | -e '/: \.deps$/d' \ 40 | -e '/: \.git$/d' \ 41 | -e '/: \.gitattributes$/d' \ 42 | -e '/: \.gitignore$/d' \ 43 | -e '/: \.github$/d' \ 44 | -e '/: \.travis$/d' \ 45 | -e '/: \.travis.yml$/d' \ 46 | -e '/: config\.log$/d' \ 47 | -e '/: config\.status$/d' \ 48 | -e '/: config\.h.*$/d' \ 49 | -e '/: CMakeLists.txt$/d' \ 50 | -e '/: Makefile$/d' \ 51 | -e '/: hooks$/d' \ 52 | -e '/: packaging$/d' \ 53 | -e '/: stamp-h1$/d' \ 54 | -e '/: README\.md$/d' \ 55 | -e '/: tmp-anchor-links$/d' \ 56 | -e '/: tmp-manproc$/d' \ 57 | -e '/: .*\.tar\.\(gz\|bz2\|xz\)$/d' \ 58 | -e '/: unittest$/d' \ 59 | -e '/: iprange$/d' \ 60 | -e '/: .*\.o$/d' \ 61 | -e '/sbin: \(firehol\|fireqos\|link-balancer\)$/d' \ 62 | -e '/sbin: \(update-ipsets\|vnetbuild\|commands.sed\)$/d' > $MYTMP/out 63 | 64 | cat $MYTMP/out 65 | test -s $MYTMP/out && exit 1 66 | exit 0 67 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2015 Alon Bar-Lev 3 | # 4 | AUTOMAKE_OPTIONS=foreign dist-xz dist-bzip2 1.10 5 | ACLOCAL_AMFLAGS = -I m4 6 | 7 | MAINTAINERCLEANFILES= \ 8 | config.log config.status \ 9 | $(srcdir)/Makefile.in \ 10 | $(srcdir)/config.h.in $(srcdir)/config.h.in~ $(srcdir)/configure \ 11 | $(srcdir)/install-sh $(srcdir)/ltmain.sh $(srcdir)/missing \ 12 | $(srcdir)/compile $(srcdir)/depcomp $(srcdir)/aclocal.m4 \ 13 | $(srcdir)/config.guess $(srcdir)/config.sub \ 14 | $(srcdir)/m4/ltsugar.m4 $(srcdir)/m4/libtool.m4 \ 15 | $(srcdir)/m4/ltversion.m4 $(srcdir)/m4/lt~obsolete.m4 \ 16 | $(srcdir)/m4/ltoptions.m4 \ 17 | $(srcdir)/pkcs11-helper.spec $(srcdir)/config-w32-vc.h \ 18 | iprange.1 19 | 20 | AM_H2MFLAGS = \ 21 | --no-info 22 | 23 | iprange_DESCRIPTION = "manage IP ranges" 24 | 25 | bin_PROGRAMS = iprange 26 | dist_noinst_DATA = iprange.spec 27 | 28 | if ENABLE_MAN 29 | man_MANS = iprange.1 30 | endif 31 | 32 | iprange_SOURCES = \ 33 | iprange.c \ 34 | iprange.h \ 35 | ipset.c \ 36 | ipset.h \ 37 | ipset_binary.c \ 38 | ipset_binary.h \ 39 | ipset_combine.c \ 40 | ipset_combine.h \ 41 | ipset_common.c \ 42 | ipset_common.h \ 43 | ipset_copy.c \ 44 | ipset_copy.h \ 45 | ipset_diff.c \ 46 | ipset_diff.h \ 47 | ipset_exclude.c \ 48 | ipset_exclude.h \ 49 | ipset_load.c \ 50 | ipset_load.h \ 51 | ipset_merge.c \ 52 | ipset_merge.h \ 53 | ipset_optimize.c \ 54 | ipset_optimize.h \ 55 | ipset_print.c \ 56 | ipset_print.h \ 57 | ipset_reduce.c \ 58 | ipset_reduce.h \ 59 | $(NULL) 60 | 61 | EXTRA_DIST = \ 62 | .gitignore \ 63 | README.md \ 64 | iprange-9999.ebuild \ 65 | autogen.sh \ 66 | $(man_MANS) \ 67 | $(NULL) 68 | 69 | if MAINTAINER_MODE 70 | %.1: % 71 | $(HELP2MAN) \ 72 | -s 1 \ 73 | $(AM_H2MFLAGS) \ 74 | -I $(top_srcdir)/$*.h2m \ 75 | -n $(if $($(subst -,_,$*)_DESCRIPTION), $($(subst -,_,$*)_DESCRIPTION), "manual page for $*") \ 76 | -o $@ \ 77 | $(top_builddir)/$< 78 | endif 79 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | iprange (1.0.4) - 2017-09-16 2 | 3 | - bugfix: fix invalid size of ipset while reducing non-optimized ipsets #18 4 | 5 | iprange (1.0.3) - 2016-10-16 6 | 7 | - speedup: used high speed mutex instead of read/write locks, for speed 8 | 9 | iprange (1.0.2) - 2015-12-23 10 | 11 | - bugfix: last batch of dns host resolutions were being lost from results 12 | 13 | iprange (1.0.1) - 2015-11-28 14 | 15 | - resolved a case with dns resolution, where all threads were waiting 16 | without resolving the IPs 17 | - packaging improvements 18 | 19 | iprange (1.0.0) - 2015-11-15 20 | 21 | 2015-11-12 Costa Tsaousis (costa@tsaousis.gr) 22 | - added logic to retry temporary DNS failures 23 | 2015-11-11 Costa Tsaousis (costa@tsaousis.gr) 24 | - added --diff to find the differences between ipsets 25 | 2015-11-05 Costa Tsaousis (costa@tsaousis.gr) 26 | - added threaded DNS resolution of hostnames 27 | 2015-11-05 Costa Tsaousis (costa@tsaousis.gr) 28 | - better error handling when parsing input files 29 | - optimized printing using internal ip2str() implementation 30 | - added DNS resolution of hostnames 31 | 2015-06-06 Costa Tsaousis (costa@tsaousis.gr) 32 | - added support for loading multiple sets 33 | - added support for merging multiple files 34 | - added support for comparing ipsets (all-to-all, one-to-all) 35 | - added support for parsing IP ranges from the input file 36 | (much like -s did for a single range) 37 | - added support for parsing netmasks 38 | - added support for min prefix generated 39 | - added support for generated only specific prefixes 40 | - added support for reducing the prefixes for iptables ipsets 41 | - the output is now always optimized (reduced / merged) 42 | - removed option -s (convert a single IP range to CIDR) 43 | - added support for finding the common IPs in multiple files 44 | - added timings 45 | - added verbose output 46 | 2015-05-31 Costa Tsaousis (costa@tsaousis.gr) 47 | - added -C option to report count of unique IPs 48 | - some optimizations to speed it up by 10% - 20% 49 | 2004-10-16 Paul Townsend (alpha alpha beta at purdue dot edu) 50 | - more general input/output formatting 51 | 2003 Gabriel L. Somlo, the original author of iprange.c core 52 | - found at http://www.cs.colostate.edu/~somlo/iprange.c 53 | -------------------------------------------------------------------------------- /ipset_common.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_common() 5 | * 6 | * it takes 2 ipsets 7 | * it returns 1 new ipset having all the IPs common to both ipset given 8 | * 9 | * the result is optimized 10 | */ 11 | 12 | inline ipset *ipset_common(ipset *ips1, ipset *ips2) { 13 | ipset *ips; 14 | unsigned long int n1, n2, i1 = 0, i2 = 0; 15 | in_addr_t lo1, lo2, hi1, hi2, lo, hi; 16 | 17 | if(unlikely(!(ips1->flags & IPSET_FLAG_OPTIMIZED))) 18 | ipset_optimize(ips1); 19 | 20 | if(unlikely(!(ips2->flags & IPSET_FLAG_OPTIMIZED))) 21 | ipset_optimize(ips2); 22 | 23 | if(unlikely(debug)) fprintf(stderr, "%s: Finding common IPs in %s and %s\n", PROG, ips1->filename, ips2->filename); 24 | 25 | ips = ipset_create("common", 0); 26 | if(unlikely(!ips)) return NULL; 27 | 28 | n1 = ips1->entries; 29 | n2 = ips2->entries; 30 | 31 | lo1 = ips1->netaddrs[0].addr; 32 | lo2 = ips2->netaddrs[0].addr; 33 | hi1 = ips1->netaddrs[0].broadcast; 34 | hi2 = ips2->netaddrs[0].broadcast; 35 | 36 | while(i1 < n1 && i2 < n2) { 37 | if(lo1 > hi2) { 38 | i2++; 39 | if(i2 < n2) { 40 | lo2 = ips2->netaddrs[i2].addr; 41 | hi2 = ips2->netaddrs[i2].broadcast; 42 | } 43 | continue; 44 | } 45 | 46 | if(lo2 > hi1) { 47 | i1++; 48 | if(i1 < n1) { 49 | lo1 = ips1->netaddrs[i1].addr; 50 | hi1 = ips1->netaddrs[i1].broadcast; 51 | } 52 | continue; 53 | } 54 | 55 | /* they overlap */ 56 | 57 | if(lo1 > lo2) lo = lo1; 58 | else lo = lo2; 59 | 60 | if(hi1 < hi2) { 61 | hi = hi1; 62 | i1++; 63 | if(i1 < n1) { 64 | lo1 = ips1->netaddrs[i1].addr; 65 | hi1 = ips1->netaddrs[i1].broadcast; 66 | } 67 | } 68 | else { 69 | hi = hi2; 70 | i2++; 71 | if(i2 < n2) { 72 | lo2 = ips2->netaddrs[i2].addr; 73 | hi2 = ips2->netaddrs[i2].broadcast; 74 | } 75 | } 76 | 77 | ipset_add_ip_range(ips, lo, hi); 78 | } 79 | 80 | ips->lines = ips1->lines + ips2->lines; 81 | ips->flags |= IPSET_FLAG_OPTIMIZED; 82 | 83 | return ips; 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /packaging/check-files: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # check-files 5 | # 6 | scriptname=check-files 7 | if ! MYTMP=$(mktemp -d -t $scriptname-XXXXXX) 8 | then 9 | echo >&2 10 | echo >&2 11 | echo >&2 "Cannot create temporary directory." 12 | echo >&2 13 | exit 1 14 | fi 15 | 16 | cleanup() { 17 | status=$? 18 | rm -rf "${MYTMP}" 19 | exit $status 20 | } 21 | 22 | # clean up if we get stopped by Crtl-C or forced logout or normal exit 23 | trap cleanup INT 24 | trap cleanup HUP 25 | trap cleanup 0 26 | 27 | set -e 28 | if [ "$1" = "--debug" ] 29 | then 30 | set -x 31 | shift 32 | fi 33 | 34 | if [ $# -lt 1 ] 35 | then 36 | echo "check-files [--debug] -|filenames" 37 | echo "e.g." 38 | echo " git diff | ./packaging/check-files -" 39 | echo "for a complete check (v.s. empty repo):" 40 | echo " git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 | ./packaging/check-files -" 41 | echo "or in .git/hooks/pre-commit:" 42 | echo " exec git diff --cached | ./packaging/check-files -" 43 | exit 1 44 | fi 45 | 46 | if [ ! -x packaging/check-files ] 47 | then 48 | echo "Must be run from base directory" 49 | exit 1 50 | fi 51 | 52 | if [ "$1" = "-" ] 53 | then 54 | from_cache=Y 55 | f="" 56 | else 57 | from_cache= 58 | for f in "$@" 59 | do 60 | if [ ! -f "$f" ] 61 | then 62 | echo "$f: no such file" 63 | exit 1 64 | fi 65 | done 66 | 67 | git status --porcelain "$@" | grep "^?" | cut -c4- > $MYTMP/missing.lst 68 | 69 | while read missing 70 | do 71 | git update-index --add --cacheinfo \ 72 | 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 $missing 73 | done < $MYTMP/missing.lst 74 | 75 | empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 76 | git diff $empty_tree -- "$@" > $MYTMP/diff.full 77 | f=$MYTMP/diff.full 78 | 79 | while read missing 80 | do 81 | git update-index --force-remove $missing 82 | done < $MYTMP/missing.lst 83 | fi 84 | 85 | > $MYTMP/diff.lst sed -e "/^+++ b/{p;s:^+++ b/::;w $MYTMP/files.lst" -e "d;}" $f 86 | 87 | #cat $MYTMP/diff.lst 88 | #cat $MYTMP/files.lst 89 | 90 | dirname="${0%/*}" 91 | if [ "$dirname" = "$0" ]; then dirname="."; fi 92 | 93 | for i in $dirname/*.functions $dirname/*/*.functions 94 | do 95 | if [ -f "$i" ] 96 | then 97 | source $i 98 | echo $i | sed -e 's:.*/::' -e 's/\.functions$//' -e 's/\./_/g' >> $MYTMP/fns 99 | fi 100 | done 101 | 102 | status=0 103 | while read fn 104 | do 105 | "${fn}_check_init" $filename || status=1 106 | done < $MYTMP/fns 107 | 108 | while read filename 109 | do 110 | #echo Checking $filename 111 | while read fn 112 | do 113 | if [ $status -eq 0 ] 114 | then 115 | "${fn}_check_file" $filename || status=1 116 | fi 117 | done < $MYTMP/fns 118 | done < $MYTMP/files.lst 119 | 120 | if [ $status -eq 0 ] 121 | then 122 | while read fn 123 | do 124 | "${fn}_check_fin" $filename || status=1 125 | done < $MYTMP/fns 126 | fi 127 | 128 | exit $status 129 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2015 Alon Bar-Lev 3 | # 4 | AC_PREREQ(2.60) 5 | 6 | define([VERSION_MAJOR], [1]) 7 | define([VERSION_MINOR], [0]) 8 | define([VERSION_FIX], [5]) 9 | define([VERSION_NUMBER], VERSION_MAJOR[.]VERSION_MINOR[.]VERSION_FIX) 10 | define([VERSION_SUFFIX], [_master]) 11 | 12 | dnl Set to "1" for a first RPM release of a new version 13 | PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed s/^_//)" 14 | 15 | AC_INIT([iprange], VERSION_NUMBER[]VERSION_SUFFIX) 16 | 17 | AC_ARG_ENABLE([man], 18 | [AS_HELP_STRING([--disable-man], [disable manpage installation @<:@enabled@:>@])], 19 | , 20 | [enable_man="yes"]) 21 | AM_CONDITIONAL([ENABLE_MAN], [test "${enable_man}" = "yes"]) 22 | 23 | AM_MAINTAINER_MODE([disable]) 24 | if test x"$USE_MAINTAINER_MODE" = xyes; then 25 | AC_MSG_NOTICE(***************** MAINTAINER MODE *****************) 26 | PACKAGE_BUILT_DATE=$(date '+%d %b %Y') 27 | 28 | AX_NEED_PROG([HELP2MAN],[help2man],[]) 29 | else 30 | if test ! -f iprange.1; then 31 | if test x"$enable_man" = xyes; then 32 | AC_MSG_ERROR([docs not built, use '--disable-man' or --enable-maintainer-mode]) 33 | fi 34 | fi 35 | fi 36 | 37 | PACKAGE_RPM_VERSION="VERSION_NUMBER" 38 | AC_SUBST([PACKAGE_RPM_VERSION]) 39 | AC_SUBST([PACKAGE_RPM_RELEASE]) 40 | 41 | AC_CONFIG_AUX_DIR([.]) 42 | AC_CONFIG_HEADERS([config.h]) 43 | AC_CONFIG_MACRO_DIR([m4]) 44 | AC_CONFIG_SRCDIR([iprange.c]) 45 | AM_INIT_AUTOMAKE 46 | AC_CANONICAL_HOST 47 | AC_PROG_CC 48 | AC_PROG_INSTALL 49 | AC_ARG_ENABLE( 50 | [pedantic], 51 | [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])], 52 | , 53 | [enable_pedantic="no"] 54 | ) 55 | AC_ARG_WITH( 56 | [compare-with-common], 57 | [AS_HELP_STRING([--without-compare-with-common], [if set, use MODE_COMMON to compare files this is 20 times faster than MODE COMBINE])], 58 | , 59 | [with_compare_with_common="yes"] 60 | ) 61 | AC_ARG_WITH( 62 | [system-ip2str], 63 | [AS_HELP_STRING([--with-system-ip2str], [if set, use slower inet_ntoa])], 64 | , 65 | [with_system_ip2str="no"] 66 | ) 67 | 68 | ACX_PTHREAD(, [AC_MSG_ERROR([Cannot initialize pthread environment])]) 69 | LIBS="${PTHREAD_LIBS} ${LIBS}" 70 | CFLAGS="${CFLAGS} ${PTHREAD_CFLAGS}" 71 | CC="${PTHREAD_CC}" 72 | 73 | AC_TYPE_UINT32_T 74 | AC_C_INLINE 75 | 76 | test "${with_compare_with_common}" = "yes" && AC_DEFINE([COMPARE_WITH_COMMON], [1], [compare settings]) 77 | test "${with_system_ip2str}" = "yes" && AC_DEFINE([SYSTEM_IP2STR], [1], [ip2str settings]) 78 | 79 | if test "${GCC}" = "yes"; then 80 | AC_DEFINE_UNQUOTED([likely(x)], [__builtin_expect(!!(x), 1)], [gcc branch optimization]) 81 | AC_DEFINE_UNQUOTED([unlikely(x)], [__builtin_expect(!!(x), 0)], [gcc branch optimization]) 82 | else 83 | AC_DEFINE_UNQUOTED([likely(x)], [(x)], [gcc branch optimization]) 84 | AC_DEFINE_UNQUOTED([unlikely(x)], [(x)], [gcc branch optimization]) 85 | fi 86 | 87 | if test "${enable_pedantic}" = "yes"; then 88 | enable_strict="yes" 89 | CFLAGS="${CFLAGS} -pedantic -Wall -Wextra" 90 | fi 91 | AC_CONFIG_FILES([ 92 | Makefile 93 | iprange.spec 94 | ]) 95 | AC_OUTPUT 96 | 97 | test "${with_compare_with_common}" != "yes" && AC_MSG_WARN([compare-with-common is not enabled. You should enable it, as it is a lot faster.]) || : 98 | -------------------------------------------------------------------------------- /ipset_exclude.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_exclude() 5 | * 6 | * it takes 2 ipsets (ips1, ips2) 7 | * it returns 1 new ipset having all the IPs of ips1, excluding the IPs of ips2 8 | * 9 | * the result is optimized 10 | */ 11 | 12 | inline ipset *ipset_exclude(ipset *ips1, ipset *ips2) { 13 | ipset *ips; 14 | unsigned long int n1, n2, i1 = 0, i2 = 0; 15 | in_addr_t lo1, lo2, hi1, hi2; 16 | 17 | if(unlikely(!(ips1->flags & IPSET_FLAG_OPTIMIZED))) 18 | ipset_optimize(ips1); 19 | 20 | if(unlikely(!(ips2->flags & IPSET_FLAG_OPTIMIZED))) 21 | ipset_optimize(ips2); 22 | 23 | if(unlikely(debug)) fprintf(stderr, "%s: Removing IPs in %s from %s\n", PROG, ips2->filename, ips1->filename); 24 | 25 | ips = ipset_create(ips1->filename, 0); 26 | if(unlikely(!ips)) return NULL; 27 | 28 | n1 = ips1->entries; 29 | n2 = ips2->entries; 30 | 31 | lo1 = ips1->netaddrs[0].addr; 32 | lo2 = ips2->netaddrs[0].addr; 33 | hi1 = ips1->netaddrs[0].broadcast; 34 | hi2 = ips2->netaddrs[0].broadcast; 35 | 36 | while(i1 < n1 && i2 < n2) { 37 | if(lo1 > hi2) { 38 | i2++; 39 | if(i2 < n2) { 40 | lo2 = ips2->netaddrs[i2].addr; 41 | hi2 = ips2->netaddrs[i2].broadcast; 42 | } 43 | continue; 44 | } 45 | 46 | if(lo2 > hi1) { 47 | ipset_add_ip_range(ips, lo1, hi1); 48 | 49 | i1++; 50 | if(i1 < n1) { 51 | lo1 = ips1->netaddrs[i1].addr; 52 | hi1 = ips1->netaddrs[i1].broadcast; 53 | } 54 | continue; 55 | } 56 | 57 | /* they overlap */ 58 | 59 | if(lo1 < lo2) { 60 | ipset_add_ip_range(ips, lo1, lo2 - 1); 61 | lo1 = lo2; 62 | } 63 | 64 | if(hi1 == hi2) { 65 | i1++; 66 | if(i1 < n1) { 67 | lo1 = ips1->netaddrs[i1].addr; 68 | hi1 = ips1->netaddrs[i1].broadcast; 69 | } 70 | 71 | i2++; 72 | if(i2 < n2) { 73 | lo2 = ips2->netaddrs[i2].addr; 74 | hi2 = ips2->netaddrs[i2].broadcast; 75 | } 76 | } 77 | else if(hi1 < hi2) { 78 | i1++; 79 | if(i1 < n1) { 80 | lo1 = ips1->netaddrs[i1].addr; 81 | hi1 = ips1->netaddrs[i1].broadcast; 82 | } 83 | } 84 | else if(hi1 > hi2) { 85 | lo1 = hi2 + 1; 86 | i2++; 87 | if(i2 < n2) { 88 | lo2 = ips2->netaddrs[i2].addr; 89 | hi2 = ips2->netaddrs[i2].broadcast; 90 | } 91 | } 92 | } 93 | 94 | if(i1 < n1) { 95 | ipset_add_ip_range(ips, lo1, hi1); 96 | i1++; 97 | 98 | /* if there are entries left in ips1, copy them */ 99 | while(i1 < n1) { 100 | ipset_add_ip_range(ips, ips1->netaddrs[i1].addr, ips1->netaddrs[i1].broadcast); 101 | i1++; 102 | } 103 | } 104 | 105 | ips->lines = ips1->lines + ips2->lines; 106 | ips->flags |= IPSET_FLAG_OPTIMIZED; 107 | return ips; 108 | } 109 | 110 | 111 | -------------------------------------------------------------------------------- /ipset_diff.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* ---------------------------------------------------------------------------- 4 | * ipset_diff() 5 | * 6 | * it takes 2 ipsets 7 | * it returns 1 new ipset having all the IPs that do not exist in either 8 | * 9 | * the result is optimized 10 | */ 11 | 12 | inline ipset *ipset_diff(ipset *ips1, ipset *ips2) { 13 | ipset *ips; 14 | unsigned long int n1, n2, i1 = 0, i2 = 0; 15 | in_addr_t lo1, lo2, hi1, hi2; 16 | 17 | if(unlikely(!(ips1->flags & IPSET_FLAG_OPTIMIZED))) 18 | ipset_optimize(ips1); 19 | 20 | if(unlikely(!(ips2->flags & IPSET_FLAG_OPTIMIZED))) 21 | ipset_optimize(ips2); 22 | 23 | if(unlikely(debug)) fprintf(stderr, "%s: Finding diff IPs in %s and %s\n", PROG, ips1->filename, ips2->filename); 24 | 25 | ips = ipset_create("diff", 0); 26 | if(unlikely(!ips)) return NULL; 27 | 28 | n1 = ips1->entries; 29 | n2 = ips2->entries; 30 | 31 | lo1 = ips1->netaddrs[0].addr; 32 | lo2 = ips2->netaddrs[0].addr; 33 | hi1 = ips1->netaddrs[0].broadcast; 34 | hi2 = ips2->netaddrs[0].broadcast; 35 | 36 | while(i1 < n1 && i2 < n2) { 37 | if(lo1 > hi2) { 38 | ipset_add_ip_range(ips, lo2, hi2); 39 | i2++; 40 | if(i2 < n2) { 41 | lo2 = ips2->netaddrs[i2].addr; 42 | hi2 = ips2->netaddrs[i2].broadcast; 43 | } 44 | continue; 45 | } 46 | if(lo2 > hi1) { 47 | ipset_add_ip_range(ips, lo1, hi1); 48 | i1++; 49 | if(i1 < n1) { 50 | lo1 = ips1->netaddrs[i1].addr; 51 | hi1 = ips1->netaddrs[i1].broadcast; 52 | } 53 | continue; 54 | } 55 | 56 | /* they overlap */ 57 | 58 | /* add the first part */ 59 | if(lo1 > lo2) { 60 | ipset_add_ip_range(ips, lo2, lo1 - 1); 61 | } 62 | else if(lo2 > lo1) { 63 | ipset_add_ip_range(ips, lo1, lo2 - 1); 64 | } 65 | 66 | /* find the second part */ 67 | if(hi1 > hi2) { 68 | lo1 = hi2 + 1; 69 | i2++; 70 | if(i2 < n2) { 71 | lo2 = ips2->netaddrs[i2].addr; 72 | hi2 = ips2->netaddrs[i2].broadcast; 73 | } 74 | continue; 75 | } 76 | 77 | else if(hi2 > hi1) { 78 | lo2 = hi1 + 1; 79 | i1++; 80 | if(i1 < n1) { 81 | lo1 = ips1->netaddrs[i1].addr; 82 | hi1 = ips1->netaddrs[i1].broadcast; 83 | } 84 | continue; 85 | } 86 | 87 | else { /* if(h1 == h2) */ 88 | i1++; 89 | if(i1 < n1) { 90 | lo1 = ips1->netaddrs[i1].addr; 91 | hi1 = ips1->netaddrs[i1].broadcast; 92 | } 93 | 94 | i2++; 95 | if(i2 < n2) { 96 | lo2 = ips2->netaddrs[i2].addr; 97 | hi2 = ips2->netaddrs[i2].broadcast; 98 | } 99 | } 100 | } 101 | while(i1 < n1) { 102 | ipset_add_ip_range(ips, lo1, hi1); 103 | i1++; 104 | if(i1 < n1) { 105 | lo1 = ips1->netaddrs[i1].addr; 106 | hi1 = ips1->netaddrs[i1].broadcast; 107 | } 108 | } 109 | while(i2 < n2) { 110 | ipset_add_ip_range(ips, lo2, hi2); 111 | i2++; 112 | if(i2 < n2) { 113 | lo2 = ips2->netaddrs[i2].addr; 114 | hi2 = ips2->netaddrs[i2].broadcast; 115 | } 116 | } 117 | 118 | ips->lines = ips1->lines + ips2->lines; 119 | ips->flags |= IPSET_FLAG_OPTIMIZED; 120 | 121 | return ips; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Master test script for iprange 4 | # 5 | # This script runs all the tests in the tests.d directory 6 | # Each test is in its own subdirectory with: 7 | # - inputX files (X is a number) 8 | # - output file with expected output 9 | # - cmd.sh script that runs iprange with appropriate arguments 10 | 11 | # Colorizing output 12 | RED='\033[0;31m' 13 | GREEN='\033[0;32m' 14 | YELLOW='\033[1;33m' 15 | NC='\033[0m' # No Color 16 | 17 | # Test directory 18 | TEST_DIR="tests.d" 19 | TEMP_DIR=$(mktemp -d) 20 | IPRANGE="./iprange" 21 | 22 | # Check if iprange exists and is executable 23 | if [ ! -x "$IPRANGE" ]; then 24 | echo -e "${RED}Error: iprange executable not found or not executable${NC}" 25 | echo "Make sure you have built the iprange binary." 26 | exit 1 27 | fi 28 | 29 | # Function to run a single test 30 | run_test() { 31 | local test_dir="$1" 32 | local test_name=$(basename "$test_dir") 33 | local cmd_script="$test_dir/cmd.sh" 34 | local expected_output="$test_dir/output" 35 | local temp_output="$TEMP_DIR/$test_name.output" 36 | 37 | echo -e "${YELLOW}Running test: $test_name${NC}" 38 | 39 | # Check if cmd.sh exists and is executable 40 | if [ ! -x "$cmd_script" ]; then 41 | echo -e "${RED}Error: $cmd_script not found or not executable${NC}" 42 | return 1 43 | fi 44 | 45 | # Check if expected output file exists 46 | if [ ! -f "$expected_output" ]; then 47 | echo -e "${RED}Error: Expected output file $expected_output not found${NC}" 48 | return 1 49 | fi 50 | 51 | # Run the test 52 | (cd "$test_dir" && ./cmd.sh > "$temp_output" 2>&1) 53 | local exit_code=$? 54 | 55 | # Some test scripts generate their own output files 56 | if [ -f "$test_dir/output1.tmp" ]; then 57 | # If temporary output exists, the test script handles its own verification 58 | if [ $exit_code -eq 0 ]; then 59 | # Test passed, copy the expected output for the test harness 60 | cp "$test_dir/output" "$temp_output" 61 | fi 62 | fi 63 | 64 | # Check exit code 65 | if [ $exit_code -ne 0 ]; then 66 | echo -e "${RED}Test failed: Exit code $exit_code${NC}" 67 | echo -e "${RED}Output:${NC}" 68 | cat "$temp_output" 69 | return 1 70 | fi 71 | 72 | # Compare output 73 | diff --ignore-all-space --ignore-blank-lines --text -u "$expected_output" "$temp_output" > "$TEMP_DIR/$test_name.diff" 74 | if [ $? -ne 0 ]; then 75 | echo -e "${RED}Test failed: Output does not match expected output${NC}" 76 | echo -e "${RED}Diff:${NC}" 77 | cat "$TEMP_DIR/$test_name.diff" 78 | return 1 79 | fi 80 | 81 | echo -e "${GREEN}Test passed${NC}" 82 | return 0 83 | } 84 | 85 | # Find all test directories 86 | test_dirs=$(find "$TEST_DIR" -mindepth 1 -maxdepth 1 -type d | sort) 87 | 88 | if [ -z "$test_dirs" ]; then 89 | echo -e "${RED}No test directories found in $TEST_DIR${NC}" 90 | exit 1 91 | fi 92 | 93 | # Run all tests 94 | total=0 95 | passed=0 96 | failed=0 97 | 98 | for test_dir in $test_dirs; do 99 | total=$((total + 1)) 100 | if run_test "$test_dir"; then 101 | passed=$((passed + 1)) 102 | else 103 | failed=$((failed + 1)) 104 | fi 105 | echo "" 106 | done 107 | 108 | # Print summary 109 | echo -e "${YELLOW}Test Summary:${NC}" 110 | echo -e "Total tests: $total" 111 | if [ $passed -gt 0 ]; then 112 | echo -e "${GREEN}Passed tests: $passed${NC}" 113 | fi 114 | if [ $failed -gt 0 ]; then 115 | echo -e "${RED}Failed tests: $failed${NC}" 116 | fi 117 | 118 | # Clean up 119 | rm -rf "$TEMP_DIR" 120 | 121 | # Return non-zero if any test failed 122 | [ $failed -eq 0 ] -------------------------------------------------------------------------------- /ipset_optimize.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /*----------------------------------------------------------*/ 4 | /* compare two network_addr_t structures; used with qsort() */ 5 | /* sort in increasing order by address, then by prefix. */ 6 | /*----------------------------------------------------------*/ 7 | int compar_netaddr(const void *p1, const void *p2) { 8 | 9 | network_addr_t *na1 = (network_addr_t *) p1, *na2 = (network_addr_t *) p2; 10 | 11 | if (na1->addr < na2->addr) 12 | return (-1); 13 | if (na1->addr > na2->addr) 14 | return (1); 15 | if (na1->broadcast > na2->broadcast) 16 | return (-1); 17 | if (na1->broadcast < na2->broadcast) 18 | return (1); 19 | 20 | return (0); 21 | 22 | } /* compar_netaddr() */ 23 | 24 | /* ---------------------------------------------------------------------------- 25 | * ipset_optimize() 26 | * 27 | * takes an ipset with any number of entries (lo-hi pairs) in any order and 28 | * it optimizes it in place 29 | * after this optimization, all entries in the ipset are sorted (ascending) 30 | * and non-overlapping (it returns less or equal number of entries) 31 | * 32 | */ 33 | 34 | inline void ipset_optimize(ipset *ips) { 35 | network_addr_t *naddrs; 36 | size_t i, n = ips->entries, lines = ips->lines; 37 | network_addr_t *oaddrs = ips->netaddrs; 38 | in_addr_t lo, hi; 39 | 40 | if(unlikely(ips->flags & IPSET_FLAG_OPTIMIZED)) { 41 | fprintf(stderr, "%s: Is already optimized %s\n", PROG, ips->filename); 42 | return; 43 | } 44 | 45 | if(unlikely(debug)) fprintf(stderr, "%s: Optimizing %s\n", PROG, ips->filename); 46 | 47 | /* sort it */ 48 | qsort((void *)ips->netaddrs, ips->entries, sizeof(network_addr_t), compar_netaddr); 49 | 50 | /* optimize it in a new space */ 51 | naddrs = malloc(ips->entries * sizeof(network_addr_t)); 52 | if(unlikely(!naddrs)) { 53 | ipset_free(ips); 54 | fprintf(stderr, "%s: Cannot allocate memory (%zu bytes)\n", PROG, ips->entries * sizeof(network_addr_t)); 55 | exit(1); 56 | } 57 | 58 | ips->netaddrs = naddrs; 59 | ips->entries = 0; 60 | ips->unique_ips = 0; 61 | ips->lines = 0; 62 | 63 | if(!n) return; 64 | 65 | lo = oaddrs[0].addr; 66 | hi = oaddrs[0].broadcast; 67 | for (i = 1; i < n; i++) { 68 | /* 69 | * if the broadcast of this 70 | * is before the broadcast of the last 71 | * then skip it = it fits entirely inside the current 72 | */ 73 | if (oaddrs[i].broadcast <= hi) 74 | continue; 75 | 76 | /* 77 | * if the network addr of this 78 | * overlaps or is adjustent to the last 79 | * then merge it = extent the broadcast of the last 80 | */ 81 | if (oaddrs[i].addr <= hi + 1) { 82 | hi = oaddrs[i].broadcast; 83 | continue; 84 | } 85 | 86 | /* 87 | * at this point we are sure the old lo, hi 88 | * do not overlap and are not adjustent to the current 89 | * so, add the last to the new set 90 | */ 91 | ipset_add_ip_range(ips, lo, hi); 92 | 93 | /* prepare for the next loop */ 94 | lo = oaddrs[i].addr; 95 | hi = oaddrs[i].broadcast; 96 | } 97 | ipset_add_ip_range(ips, lo, hi); 98 | ips->lines = lines; 99 | 100 | ips->flags |= IPSET_FLAG_OPTIMIZED; 101 | 102 | free(oaddrs); 103 | } 104 | 105 | /* ---------------------------------------------------------------------------- 106 | * ipset_optimize_all() 107 | * 108 | * it calls ipset_optimize() for all ipsets linked to 'next' to the given 109 | * 110 | */ 111 | 112 | inline void ipset_optimize_all(ipset *root) { 113 | ipset *ips; 114 | 115 | for(ips = root; ips ;ips = ips->next) 116 | ipset_optimize(ips); 117 | } 118 | -------------------------------------------------------------------------------- /packaging/update-tags: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # update-tags 5 | # 6 | scriptname=update-tags 7 | if ! MYTMP=$(mktemp -d -t $scriptname-XXXXXX) 8 | then 9 | echo >&2 10 | echo >&2 11 | echo >&2 "Cannot create temporary directory." 12 | echo >&2 13 | exit 1 14 | fi 15 | 16 | cleanup() { 17 | status=$? 18 | if [ $status -ne 0 ] 19 | then 20 | echo "FAILED." 21 | if [ "$TAGVER" ] 22 | then 23 | git tag -d "$TAGVER" 24 | fi 25 | echo "To re-run manually:" 26 | echo " git diff HEAD^ | ./packaging/update-tags -" 27 | echo "To undo commit:" 28 | echo " git reset HEAD^" 29 | fi 30 | rm -rf "${MYTMP}" 31 | exit $status 32 | } 33 | 34 | # clean up if we get stopped by Crtl-C or forced logout or normal exit 35 | trap cleanup INT 36 | trap cleanup HUP 37 | trap cleanup 0 38 | 39 | set -e 40 | if [ "$1" = "--debug" ] 41 | then 42 | set -x 43 | shift 44 | fi 45 | 46 | if [ $# -lt 1 ] 47 | then 48 | echo "Use only from .git/hooks/post-commit" 49 | exit 1 50 | fi 51 | 52 | if [ ! -x packaging/update-tags ] 53 | then 54 | echo "Must be run from base directory" 55 | exit 1 56 | fi 57 | 58 | if [ "$1" = "-" ] 59 | then 60 | from_cache=Y 61 | f="" 62 | else 63 | from_cache= 64 | for f in "$@" 65 | do 66 | if [ ! -f "$f" ] 67 | then 68 | echo "$f: no such file" 69 | exit 1 70 | fi 71 | done 72 | 73 | git status --porcelain "$@" | grep "^?" | cut -c4- > $MYTMP/missing.lst 74 | 75 | while read missing 76 | do 77 | git update-index --add --cacheinfo \ 78 | 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 $missing 79 | done < $MYTMP/missing.lst 80 | 81 | empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 82 | git diff $empty_tree -- "$@" > $MYTMP/diff.full 83 | f=$MYTMP/diff.full 84 | 85 | while read missing 86 | do 87 | git update-index --force-remove $missing 88 | done < $MYTMP/missing.lst 89 | fi 90 | 91 | > $MYTMP/diff.lst sed -e "/^+++ b/{p;s:^+++ b/::;w $MYTMP/files.lst" -e "d;}" $f 92 | 93 | #cat $MYTMP/diff.lst 94 | #cat $MYTMP/files.lst 95 | 96 | dirname="${0%/*}" 97 | if [ "$dirname" = "$0" ]; then dirname="."; fi 98 | source $dirname/packaging.functions 99 | 100 | status=0 101 | while read filename 102 | do 103 | #echo Checking $filename 104 | case $filename in 105 | configure.ac|ChangeLog) 106 | mkdir -p $MYTMP/files 107 | git show HEAD:configure.ac > $MYTMP/files/configure.ac 108 | version=`get_configure_ac_version` 109 | case "$(match_version $version)" in 110 | prerelease|candidate|release) 111 | do_release=Y 112 | ;; 113 | esac 114 | ;; 115 | *) 116 | #echo "No checks found for $filename" 117 | : 118 | ;; 119 | esac 120 | done < $MYTMP/files.lst 121 | 122 | if [ "$do_release" ] 123 | then 124 | echo "Tagging new release with:" 125 | echo " git tag -s \"v$version\" -m \"Release version $version\"" 126 | git tag -s "v$version" -m "Release version $version" 127 | TAGVER="v$version" 128 | 129 | splitver confmaj confmin conffix confsfx "$version" 130 | if [ ! "$confsfx" ] 131 | then 132 | echo "Incrementing version in configure.ac:" 133 | conffix=`expr $conffix + 1` 134 | sed -i -e "s/define(\[VERSION_FIX\], \[.*])/define([VERSION_FIX], [$conffix])/" configure.ac 135 | fi 136 | 137 | echo "Resetting suffix in configure.ac:" 138 | sed -i -e 's/define(\[VERSION_SUFFIX\], \[.*])/define([VERSION_SUFFIX], [_master])/' configure.ac 139 | sed -i -e 's:^PACKAGE_RPM_RELEASE=.*:PACKAGE_RPM_RELEASE="0.0.$(echo VERSION_SUFFIX | sed s/^_//)":' configure.ac 140 | 141 | echo "Committing new configure.ac:" 142 | git commit --no-verify -m "Post release $version" -- configure.ac 143 | echo "" 144 | echo "Verify, then:" 145 | echo " git push origin" 146 | echo " git push origin tag $TAGVER" 147 | fi 148 | 149 | exit $status 150 | -------------------------------------------------------------------------------- /ipset.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | #define IPSET_ENTRIES_INCREASE_STEP 1024 4 | 5 | /* ---------------------------------------------------------------------------- 6 | * ipset_create() 7 | * 8 | * create an empty ipset with the given name and free entries in its array 9 | * 10 | */ 11 | 12 | ipset *ipset_create(const char *filename, size_t entries) { 13 | ipset *ips = malloc(sizeof(ipset)); 14 | if(!ips) return NULL; 15 | 16 | if(entries < IPSET_ENTRIES_INCREASE_STEP) entries = IPSET_ENTRIES_INCREASE_STEP; 17 | 18 | ips->netaddrs = malloc(entries * sizeof(network_addr_t)); 19 | if(!ips->netaddrs) { 20 | free(ips); 21 | return NULL; 22 | } 23 | 24 | ips->lines = 0; 25 | ips->entries = 0; 26 | ips->entries_max = entries; 27 | ips->unique_ips = 0; 28 | ips->next = NULL; 29 | ips->prev = NULL; 30 | ips->flags = 0; 31 | 32 | strncpy(ips->filename, (filename && *filename)?filename:"stdin", FILENAME_MAX); 33 | ips->filename[FILENAME_MAX] = '\0'; 34 | 35 | /* strcpy(ips->name, ips->filename); */ 36 | 37 | return ips; 38 | } 39 | 40 | 41 | /* ---------------------------------------------------------------------------- 42 | * ipset_free() 43 | * 44 | * release the memory of an ipset and re-link its siblings so that lingage will 45 | * be consistent 46 | * 47 | */ 48 | 49 | void ipset_free(ipset *ips) { 50 | if(ips->next) ips->next->prev = ips->prev; 51 | if(ips->prev) ips->prev->next = ips->next; 52 | 53 | free(ips->netaddrs); 54 | free(ips); 55 | } 56 | 57 | 58 | /* ---------------------------------------------------------------------------- 59 | * ipset_free_all() 60 | * 61 | * release all the memory occupied by all ipsets linked together (prev, next) 62 | * 63 | */ 64 | 65 | void ipset_free_all(ipset *ips) { 66 | if(ips->prev) { 67 | ips->prev->next = NULL; 68 | ipset_free_all(ips->prev); 69 | } 70 | 71 | if(ips->next) { 72 | ips->next->prev = NULL; 73 | ipset_free_all(ips->next); 74 | } 75 | 76 | ipset_free(ips); 77 | } 78 | 79 | 80 | void ipset_grow_internal(ipset *ips, size_t free_entries_needed) { 81 | 82 | // make sure we allocate at least IPSET_ENTRIES_INCREASE_STEP entries 83 | ips->entries_max += (free_entries_needed < IPSET_ENTRIES_INCREASE_STEP)?IPSET_ENTRIES_INCREASE_STEP:free_entries_needed; 84 | 85 | ips->netaddrs = realloc(ips->netaddrs, ips->entries_max * sizeof(network_addr_t)); 86 | if(unlikely(!ips->netaddrs)) { 87 | fprintf(stderr, "%s: Cannot re-allocate memory (%zu bytes)\n", PROG, ips->entries_max * sizeof(network_addr_t)); 88 | exit(1); 89 | } 90 | } 91 | 92 | 93 | inline size_t ipset_unique_ips(ipset *ips) { 94 | if(unlikely(!(ips->flags & IPSET_FLAG_OPTIMIZED))) 95 | ipset_optimize(ips); 96 | 97 | return(ips->unique_ips); 98 | } 99 | 100 | /* ---------------------------------------------------------------------------- 101 | * ipset_histogram() 102 | * 103 | * generate histogram for ipset 104 | * 105 | */ 106 | 107 | /* 108 | int ipset_histogram(ipset *ips, const char *path) { 109 | make sure the path exists 110 | if this is the first time: 111 | - create a directory for this ipset, in path 112 | - create the 'new' directory inside this ipset path 113 | - assume the 'latest' is empty 114 | - keep the starting date 115 | - print an empty histogram 116 | save in 'new' the IPs of current excluding the 'latest' 117 | save 'current' as 'latest' 118 | assume the histogram is complete 119 | for each file in 'new' 120 | - if the file is <= to histogram start date, the histogram is incomplete 121 | - calculate the hours passed to the 'current' 122 | - find the IPs in this file common to 'current' = 'stillthere' 123 | - find the IPs in this file not in 'stillthere' = 'removed' 124 | - if there are IPs in 'removed', add an entry to the retention histogram 125 | - if there are no IPs in 'stillthere', delete the file 126 | - else replace the file with the contents of 'stillthere' 127 | return 0; 128 | } 129 | */ 130 | 131 | 132 | -------------------------------------------------------------------------------- /ipset.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPSET_H 2 | #define IPRANGE_IPSET_H 3 | 4 | #define MAX_LINE 1024 5 | 6 | #define IPSET_FLAG_OPTIMIZED 0x00000001 7 | 8 | typedef struct ipset { 9 | char filename[FILENAME_MAX+1]; 10 | /* char name[FILENAME_MAX+1]; */ 11 | 12 | size_t lines; 13 | size_t entries; 14 | size_t entries_max; 15 | size_t unique_ips; /* this is updated only after calling ipset_optimize() */ 16 | 17 | uint32_t flags; 18 | 19 | struct ipset *next; 20 | struct ipset *prev; 21 | 22 | network_addr_t *netaddrs; 23 | } ipset; 24 | 25 | extern ipset *ipset_create(const char *filename, size_t entries); 26 | extern void ipset_free(ipset *ips); 27 | extern void ipset_free_all(ipset *ips); 28 | 29 | extern size_t prefix_counters[33]; 30 | 31 | extern size_t ipset_unique_ips(ipset *ips); 32 | 33 | 34 | /* ---------------------------------------------------------------------------- 35 | * ipset_grow() 36 | * 37 | * exprand the ipset so that it will have at least the given number of free 38 | * entries in its internal array 39 | * 40 | */ 41 | 42 | extern void ipset_grow_internal(ipset *ips, size_t free_entries_needed); 43 | 44 | static inline void ipset_grow(ipset *ips, size_t free_entries_needed) { 45 | if(unlikely(!ips)) return; 46 | 47 | if(unlikely(!free_entries_needed)) 48 | free_entries_needed = 1; 49 | 50 | if(unlikely((ips->entries_max - ips->entries) < free_entries_needed)) 51 | ipset_grow_internal(ips, free_entries_needed); 52 | } 53 | 54 | /* ---------------------------------------------------------------------------- 55 | * ipset_added_entry() 56 | * 57 | * validate and check the ipset, after appending one more entry 58 | * 59 | */ 60 | 61 | static inline void ipset_added_entry(ipset *ips) { 62 | size_t entries = ips->entries; 63 | 64 | ips->lines++; 65 | ips->unique_ips += ips->netaddrs[entries].broadcast - ips->netaddrs[entries].addr + 1; 66 | 67 | if(likely(ips->flags & IPSET_FLAG_OPTIMIZED && entries > 0)) { 68 | // the new is just next to the last 69 | if(unlikely(ips->netaddrs[entries].addr == (ips->netaddrs[entries - 1].broadcast + 1))) { 70 | ips->netaddrs[entries - 1].broadcast = ips->netaddrs[entries].broadcast; 71 | return; 72 | } 73 | 74 | // the new is after the end of the last 75 | if(likely(ips->netaddrs[entries].addr > ips->netaddrs[entries - 1].broadcast)) { 76 | ips->entries++; 77 | return; 78 | } 79 | 80 | // the new is before the beginning of the last 81 | ips->flags &= ~IPSET_FLAG_OPTIMIZED; 82 | 83 | if(unlikely(debug)) { 84 | in_addr_t new_from = ips->netaddrs[ips->entries].addr; 85 | in_addr_t new_to = ips->netaddrs[ips->entries].broadcast; 86 | 87 | in_addr_t last_from = ips->netaddrs[ips->entries - 1].addr; 88 | in_addr_t last_to = ips->netaddrs[ips->entries - 1].broadcast; 89 | 90 | char buf[IP2STR_MAX_LEN + 1]; 91 | fprintf(stderr, "%s: NON-OPTIMIZED %s at line %zu, entry %zu, last was %s (%u) - ", PROG, ips->filename, ips->lines, ips->entries, ip2str_r(buf, last_from), last_from); 92 | fprintf(stderr, "%s (%u), new is ", ip2str_r(buf, last_to), last_to); 93 | fprintf(stderr, "%s (%u) - ", ip2str_r(buf, new_from), new_from); 94 | fprintf(stderr, "%s (%u)\n", ip2str_r(buf, new_to), new_to); 95 | } 96 | } 97 | 98 | ips->entries++; 99 | } 100 | 101 | 102 | /* ---------------------------------------------------------------------------- 103 | * ipset_add_ip_range() 104 | * 105 | * add an IP entry (from - to) to the ipset given 106 | * 107 | */ 108 | 109 | static inline void ipset_add_ip_range(ipset *ips, in_addr_t from, in_addr_t to) { 110 | ipset_grow(ips, 1); 111 | 112 | ips->netaddrs[ips->entries].addr = from; 113 | ips->netaddrs[ips->entries].broadcast = to; 114 | ipset_added_entry(ips); 115 | } 116 | 117 | 118 | /* ---------------------------------------------------------------------------- 119 | * ipset_add_ipstr() 120 | * 121 | * add a single IP entry to an ipset, by parsing the given IP string 122 | * 123 | */ 124 | 125 | static inline int ipset_add_ipstr(ipset *ips, char *ipstr) { 126 | int err = 0; 127 | 128 | ipset_grow(ips, 1); 129 | 130 | ips->netaddrs[ips->entries] = str2netaddr(ipstr, &err); 131 | if(!err) ipset_added_entry(ips); 132 | return !err; 133 | 134 | } 135 | 136 | #endif //IPRANGE_IPSET_H 137 | -------------------------------------------------------------------------------- /ipset_reduce.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | size_t prefix_counters[33]; 4 | 5 | /* ---------------------------------------------------------------------------- 6 | * ipset_reduce() 7 | * 8 | * takes an ipset, an acceptable increase % and a minimum accepted entries 9 | * and disables entries in the global prefix_enabled[] array, so that once 10 | * the ipset is printed, only the enabled prefixes will be used 11 | * 12 | * prefix_enable[] is not reset before use, so that it can be initialized with 13 | * some of the prefixes enabled and others disabled already (user driven) 14 | * 15 | * this function does not alter the given ipset and it does not print it 16 | */ 17 | 18 | void ipset_reduce(ipset *ips, size_t acceptable_increase, size_t min_accepted) { 19 | size_t i, n, total = 0, acceptable, iterations = 0, initial = 0, eliminated = 0; 20 | 21 | if(unlikely(!(ips->flags & IPSET_FLAG_OPTIMIZED))) 22 | ipset_optimize(ips); 23 | 24 | n = ips->entries; 25 | 26 | /* reset the prefix counters */ 27 | for(i = 0; i <= 32; i++) 28 | prefix_counters[i] = 0; 29 | 30 | /* find how many prefixes are there */ 31 | if(unlikely(debug)) fprintf(stderr, "\nCounting prefixes in %s\n", ips->filename); 32 | for(i = 0; i < n ;i++) 33 | split_range(0, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, prefix_update_counters); 34 | 35 | /* count them */ 36 | if(unlikely(debug)) fprintf(stderr, "Break down by prefix:\n"); 37 | total = 0; 38 | for(i = 0; i <= 32 ;i++) { 39 | if(prefix_counters[i]) { 40 | if(unlikely(debug)) fprintf(stderr, " - prefix /%zu counts %zu entries\n", i, prefix_counters[i]); 41 | total += prefix_counters[i]; 42 | initial++; 43 | } 44 | else prefix_enabled[i] = 0; 45 | } 46 | if(unlikely(debug)) fprintf(stderr, "Total %zu entries generated\n", total); 47 | 48 | /* find the upper limit */ 49 | acceptable = total * acceptable_increase / 100; 50 | if(acceptable < min_accepted) acceptable = min_accepted; 51 | if(unlikely(debug)) fprintf(stderr, "Acceptable is to reach %zu entries by reducing prefixes\n", acceptable); 52 | 53 | /* reduce the possible prefixes */ 54 | while(total < acceptable) { 55 | ssize_t min = -1, to = -1; 56 | size_t j, min_increase = acceptable * 10, multiplier, increase, old_to_counters; 57 | 58 | iterations++; 59 | 60 | /* find the prefix with the least increase */ 61 | for(i = 0; i <= 31 ;i++) { 62 | if(!prefix_counters[i] || !prefix_enabled[i]) continue; 63 | 64 | for(j = i + 1, multiplier = 2; j <= 32 ; j++, multiplier *= 2) { 65 | if(!prefix_counters[j]) continue; 66 | 67 | increase = prefix_counters[i] * (multiplier - 1); 68 | if(unlikely(debug)) fprintf(stderr, " > Examining merging prefix %zu to %zu (increase by %zu)\n", i, j, increase); 69 | 70 | if(increase < min_increase) { 71 | min_increase = increase; 72 | min = i; 73 | to = j; 74 | } 75 | break; 76 | } 77 | } 78 | 79 | if(min == -1 || to == -1 || min == to) { 80 | if(unlikely(debug)) fprintf(stderr, " Nothing more to reduce\n"); 81 | break; 82 | } 83 | 84 | multiplier = 1; 85 | ssize_t x; 86 | for(x = min; x < to; x++) multiplier *= 2; 87 | 88 | increase = prefix_counters[min] * multiplier - prefix_counters[min]; 89 | if(unlikely(debug)) fprintf(stderr, " > Selected prefix %zd (%zu entries) to be merged in %zd (total increase by %zu)\n", min, prefix_counters[min], to, increase); 90 | 91 | if(total + increase > acceptable) { 92 | if(unlikely(debug)) fprintf(stderr, " Cannot proceed to increase total %zu by %zu, above acceptable %zu.\n", total, increase, acceptable); 93 | break; 94 | } 95 | 96 | old_to_counters = prefix_counters[to]; 97 | 98 | total += increase; 99 | prefix_counters[to] += increase + prefix_counters[min]; 100 | prefix_counters[min] = 0; 101 | prefix_enabled[min] = 0; 102 | eliminated++; 103 | if(unlikely(debug)) fprintf(stderr, " Eliminating prefix %zd in %zd (had %zu, now has %zu entries), total is now %zu (increased by %zu)\n", min, to, old_to_counters, prefix_counters[to], total, increase); 104 | } 105 | 106 | if(unlikely(debug)) fprintf(stderr, "\nEliminated %zu out of %zu prefixes (%zu remain in the final set).\n\n", eliminated, initial, initial - eliminated); 107 | 108 | /* reset the prefix counters */ 109 | for(i = 0; i <= 32; i++) 110 | prefix_counters[i] = 0; 111 | } 112 | -------------------------------------------------------------------------------- /ipset_binary.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | static uint32_t endianness = 0x1A2B3C4D; 4 | 5 | /* ---------------------------------------------------------------------------- 6 | * binary files v1.0 7 | * 8 | */ 9 | 10 | int ipset_load_binary_v10(FILE *fp, ipset *ips, int first_line_missing) { 11 | char buffer[MAX_LINE + 1], *s; 12 | unsigned long entries, bytes, lines, unique_ips; 13 | uint32_t endian; 14 | size_t loaded; 15 | 16 | if(!first_line_missing) { 17 | s = fgets(buffer, MAX_LINE, fp); 18 | buffer[MAX_LINE] = '\0'; 19 | if(!s || strcmp(s, BINARY_HEADER_V10)) { 20 | fprintf(stderr, "%s: %s expecting binary header but found '%s'.\n", PROG, ips->filename, s?s:""); 21 | return 1; 22 | } 23 | } 24 | 25 | s = fgets(buffer, MAX_LINE, fp); 26 | buffer[MAX_LINE] = '\0'; 27 | if(!s || ( strcmp(s, "optimized\n") && strcmp(s, "non-optimized\n") )) { 28 | fprintf(stderr, "%s: %s 2nd line should be the optimized flag, but found '%s'.\n", PROG, ips->filename, s?s:""); 29 | return 1; 30 | } 31 | if(!strcmp(s, "optimized\n")) ips->flags |= IPSET_FLAG_OPTIMIZED; 32 | else ips->flags &= ~IPSET_FLAG_OPTIMIZED; 33 | 34 | s = fgets(buffer, MAX_LINE, fp); 35 | buffer[MAX_LINE] = '\0'; 36 | if(!s || strncmp(s, "record size ", 12)) { 37 | fprintf(stderr, "%s: %s 3rd line should be the record size, but found '%s'.\n", PROG, ips->filename, s?s:""); 38 | return 1; 39 | } 40 | if(atol(&s[12]) != sizeof(network_addr_t)) { 41 | fprintf(stderr, "%s: %s: invalid record size %ld (expected %lu)\n", PROG, ips->filename, atol(&s[12]), (unsigned long)sizeof(network_addr_t)); 42 | return 1; 43 | } 44 | 45 | s = fgets(buffer, MAX_LINE, fp); 46 | buffer[MAX_LINE] = '\0'; 47 | if(!s || strncmp(s, "records ", 8)) { 48 | fprintf(stderr, "%s: %s 4th line should be the number of records, but found '%s'.\n", PROG, ips->filename, s?s:""); 49 | return 1; 50 | } 51 | entries = strtoul(&s[8], NULL, 10); 52 | 53 | s = fgets(buffer, MAX_LINE, fp); 54 | buffer[MAX_LINE] = '\0'; 55 | if(!s || strncmp(s, "bytes ", 6)) { 56 | fprintf(stderr, "%s: %s 5th line should be the number of bytes, but found '%s'.\n", PROG, ips->filename, s?s:""); 57 | return 1; 58 | } 59 | bytes = strtoul(&s[6], NULL, 10); 60 | 61 | s = fgets(buffer, MAX_LINE, fp); 62 | buffer[MAX_LINE] = '\0'; 63 | if(!s || strncmp(s, "lines ", 6)) { 64 | fprintf(stderr, "%s: %s 6th line should be the number of lines read, but found '%s'.\n", PROG, ips->filename, s?s:""); 65 | return 1; 66 | } 67 | lines = strtoul(&s[6], NULL, 10); 68 | 69 | s = fgets(buffer, MAX_LINE, fp); 70 | buffer[MAX_LINE] = '\0'; 71 | if(!s || strncmp(s, "unique ips ", 11)) { 72 | fprintf(stderr, "%s: %s 7th line should be the number of unique IPs, but found '%s'.\n", PROG, ips->filename, s?s:""); 73 | return 1; 74 | } 75 | unique_ips = strtoul(&s[11], NULL, 10); 76 | 77 | if(bytes != ((sizeof(network_addr_t) * entries) + sizeof(uint32_t))) { 78 | fprintf(stderr, "%s: %s invalid number of bytes, found %lu, expected %lu.\n", PROG, ips->filename, bytes, ((sizeof(network_addr_t) * entries) + sizeof(uint32_t))); 79 | return 1; 80 | } 81 | 82 | loaded = fread(&endian, sizeof(uint32_t), 1, fp); 83 | if(loaded != 1) { 84 | fprintf(stderr, "%s: %s: cannot load ipset header\n", PROG, ips->filename); 85 | return 1; 86 | } 87 | 88 | if(endian != endianness) { 89 | fprintf(stderr, "%s: %s: incompatible endianness\n", PROG, ips->filename); 90 | return 1; 91 | } 92 | 93 | if(unique_ips < entries) { 94 | fprintf(stderr, "%s: %s: unique IPs (%lu) cannot be less than entries (%lu)\n", PROG, ips->filename, unique_ips, entries); 95 | return 1; 96 | } 97 | 98 | if(lines < entries) { 99 | fprintf(stderr, "%s: %s: lines (%lu) cannot be less than entries (%lu)\n", PROG, ips->filename, lines, entries); 100 | return 1; 101 | } 102 | 103 | ipset_grow(ips, entries); 104 | 105 | loaded = fread(&ips->netaddrs[ips->entries], sizeof(network_addr_t), entries, fp); 106 | 107 | if(loaded != entries) { 108 | fprintf(stderr, "%s: %s: expected to load %lu entries, loaded %zu\n", PROG, ips->filename, entries, loaded); 109 | return 1; 110 | } 111 | 112 | ips->entries += loaded; 113 | ips->lines += lines; 114 | ips->unique_ips += unique_ips; 115 | 116 | return 0; 117 | } 118 | 119 | void ipset_save_binary_v10(ipset *ips) { 120 | // it is crucial not to generate any output 121 | // if the ipset is empty: 122 | // the caller may do 'test -s file' to check it 123 | if(!ips->entries) return; 124 | 125 | fprintf(stdout, BINARY_HEADER_V10); 126 | if(ips->flags & IPSET_FLAG_OPTIMIZED) fprintf(stdout, "optimized\n"); 127 | else fprintf(stdout, "non-optimized\n"); 128 | fprintf(stdout, "record size %zu\n", sizeof(network_addr_t)); 129 | fprintf(stdout, "records %zu\n", ips->entries); 130 | fprintf(stdout, "bytes %zu\n", (sizeof(network_addr_t) * ips->entries) + sizeof(uint32_t)); 131 | fprintf(stdout, "lines %zu\n", ips->entries); 132 | fprintf(stdout, "unique ips %zu\n", ips->unique_ips); 133 | fwrite(&endianness, sizeof(uint32_t), 1, stdout); 134 | fwrite(ips->netaddrs, sizeof(network_addr_t), ips->entries, stdout); 135 | } 136 | 137 | 138 | -------------------------------------------------------------------------------- /iprange.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRANGE_IPRANGE_H 2 | #define IPRANGE_IPRANGE_H 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | #if defined(HAVE_INTTYPES_H) 8 | #include 9 | #elif defined(HAVE_STDINT_H) 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | extern int cidr_use_network; 26 | extern int default_prefix; 27 | extern char *PROG; 28 | extern int debug; 29 | 30 | /*---------------------------------------------------------------------*/ 31 | /* network address type: one field for the net address, one for prefix */ 32 | /*---------------------------------------------------------------------*/ 33 | typedef struct network_addr { 34 | in_addr_t addr; 35 | in_addr_t broadcast; 36 | } network_addr_t; 37 | 38 | /*------------------------------------------------------------------*/ 39 | /* Set a bit to a given value (0 or 1); MSB is bit 1, LSB is bit 32 */ 40 | /*------------------------------------------------------------------*/ 41 | static inline in_addr_t set_bit(in_addr_t addr, int bitno, int val) { 42 | 43 | if (val) 44 | return (addr | (1 << (32 - bitno))); 45 | else 46 | return (addr & ~(1 << (32 - bitno))); 47 | 48 | } /* set_bit() */ 49 | 50 | /*--------------------------------------*/ 51 | /* Compute netmask address given prefix */ 52 | /*--------------------------------------*/ 53 | static inline in_addr_t netmask(int prefix) { 54 | 55 | if (prefix == 0) 56 | return (~((in_addr_t) - 1)); 57 | else 58 | return (in_addr_t)(~((1 << (32 - prefix)) - 1)); 59 | 60 | } /* netmask() */ 61 | 62 | /*----------------------------------------------------*/ 63 | /* Compute broadcast address given address and prefix */ 64 | /*----------------------------------------------------*/ 65 | static inline in_addr_t broadcast(in_addr_t addr, int prefix) { 66 | 67 | return (addr | ~netmask(prefix)); 68 | 69 | } /* broadcast() */ 70 | 71 | /*--------------------------------------------------*/ 72 | /* Compute network address given address and prefix */ 73 | /*--------------------------------------------------*/ 74 | static inline in_addr_t network(in_addr_t addr, int prefix) { 75 | 76 | return (addr & netmask(prefix)); 77 | 78 | } /* network() */ 79 | 80 | /*-----------------------------------------------------------*/ 81 | /* Convert an A.B.C.D address into a 32-bit host-order value */ 82 | /*-----------------------------------------------------------*/ 83 | static inline in_addr_t a_to_hl(char *ipstr, int *err) { 84 | struct in_addr in; 85 | 86 | if (unlikely(!inet_aton(ipstr, &in))) { 87 | fprintf(stderr, "%s: Invalid address %s.\n", PROG, ipstr); 88 | in.s_addr = 0; 89 | if(err) (*err)++; 90 | return (ntohl(in.s_addr)); 91 | } 92 | 93 | return (ntohl(in.s_addr)); 94 | 95 | } /* a_to_hl() */ 96 | 97 | /*-----------------------------------------------------------------*/ 98 | /* convert a network address char string into a host-order network */ 99 | /* address and an integer prefix value */ 100 | /*-----------------------------------------------------------------*/ 101 | static inline network_addr_t str2netaddr(char *ipstr, int *err) { 102 | 103 | int prefix = default_prefix; 104 | char *prefixstr; 105 | network_addr_t netaddr; 106 | 107 | if ((prefixstr = strchr(ipstr, '/'))) { 108 | *prefixstr = '\0'; 109 | prefixstr++; 110 | errno = 0; 111 | prefix = atoi(prefixstr); 112 | if (unlikely(errno || (*prefixstr == '\0') || (prefix < 0) || (prefix > 32))) { 113 | /* try the netmask format */ 114 | in_addr_t mask = ~a_to_hl(prefixstr, err); 115 | /*fprintf(stderr, "mask is %u (0x%08x)\n", mask, mask);*/ 116 | prefix = 32; 117 | while((likely(mask & 0x00000001))) { 118 | mask >>= 1; 119 | prefix--; 120 | } 121 | 122 | if(unlikely(mask)) { 123 | if(err) (*err)++; 124 | fprintf(stderr, "%s: Invalid netmask %s\n", PROG, prefixstr); 125 | netaddr.addr = 0; 126 | netaddr.broadcast = 0; 127 | return (netaddr); 128 | } 129 | } 130 | } 131 | 132 | if(likely(cidr_use_network)) 133 | netaddr.addr = network(a_to_hl(ipstr, err), prefix); 134 | else 135 | netaddr.addr = a_to_hl(ipstr, err); 136 | 137 | netaddr.broadcast = broadcast(netaddr.addr, prefix); 138 | 139 | return (netaddr); 140 | 141 | } 142 | 143 | // ---------------------------------------------------------------------------- 144 | // Print out a 32-bit address in A.B.C.D/M format 145 | // 146 | // very fast implementation of IP address printing 147 | // this is 30% faster than the system default (inet_ntoa() based) 148 | // http://stackoverflow.com/questions/1680365/integer-to-ip-address-c 149 | 150 | static inline char *ip2str_r(char *buf, in_addr_t IP) { 151 | int i, k; 152 | for(i = 0, k = 0; i < 4; i++) { 153 | char c0 = (char)(((((IP & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) / 100) + 0x30); 154 | if(c0 != '0') *(buf + k++) = c0; 155 | 156 | char c1 = (char)((((((IP & (0xff << ((3 - i) * 8))) >> ((3 - i) * 8))) % 100) / 10) + 0x30); 157 | if(!(c1 == '0' && c0 == '0')) *(buf + k++) = c1; 158 | 159 | *(buf + k) = (char)((((((IP & (0xff << ((3 - i) * 8)))) >> ((3 - i) * 8))) % 10) + 0x30); 160 | k++; 161 | 162 | if(i < 3) *(buf + k++) = '.'; 163 | } 164 | *(buf + k) = 0; 165 | 166 | return buf; 167 | } 168 | 169 | #define IP2STR_MAX_LEN 20 170 | 171 | #include "ipset.h" 172 | #include "ipset_binary.h" 173 | #include "ipset_combine.h" 174 | #include "ipset_common.h" 175 | #include "ipset_copy.h" 176 | #include "ipset_diff.h" 177 | #include "ipset_exclude.h" 178 | #include "ipset_load.h" 179 | #include "ipset_merge.h" 180 | #include "ipset_optimize.h" 181 | #include "ipset_print.h" 182 | #include "ipset_reduce.h" 183 | 184 | #endif //IPRANGE_IPRANGE_H 185 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: [ push, pull_request, workflow_dispatch ] 2 | 3 | jobs: 4 | prep: 5 | runs-on: ubuntu-latest 6 | name: Prepare build 7 | steps: 8 | - name: Extract tag/branch variables 9 | shell: bash 10 | run: | 11 | echo "tag=$(echo ${GITHUB_REF#refs/tags/}|grep -v '/')" >> $GITHUB_OUTPUT 12 | echo "branch=$(echo ${GITHUB_REF#refs/heads/}|grep -v '/')" >> $GITHUB_OUTPUT 13 | id: extract 14 | outputs: 15 | tag: ${{ steps.extract.outputs.tag }} 16 | branch: ${{ steps.extract.outputs.branch }} 17 | 18 | build: 19 | runs-on: ubuntu-latest 20 | name: Build package 21 | needs: prep 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Set up build tools 25 | run: ./.github/workflows/setup.sh 26 | - name: Server-side run of commit hooks in case developer skipped them 27 | run: git diff 4b825dc642cb6eb9a060e54bf8d69288fbee4904 | ./packaging/check-files - 28 | env: 29 | TRAVIS_TAG: ${{ needs.prep.outputs.tag }} 30 | - name: Obtain GPG keys to validate tag signature 31 | if: ${{ needs.prep.outputs.tag != '' }} 32 | run: | 33 | ./packaging/gpg-recv-key phil@firehol.org "0762 9FF7 89EA 6156 012F 9F50 C406 9602 1359 9237" 34 | ./packaging/gpg-recv-key costa@tsaousis.gr "4DFF 624A E564 3B51 2872 1F40 29CA 3358 89B9 A863" 35 | git fetch --tags -f 36 | env: 37 | KEYURL: https://keyserver.ubuntu.com/pks/lookup?exact=on&op=get&search= 38 | - name: Run build 39 | run: fakeroot ./packaging/git-build && ./configure && make check 40 | env: 41 | TRAVIS_TAG: ${{ needs.prep.outputs.tag }} 42 | - name: Create checksums 43 | run: | 44 | for i in *.tar.* 45 | do 46 | md5sum -b $i > $i.md5 47 | sha512sum -b $i > $i.sha 48 | done 49 | - name: Upload build artifacts 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: build-artifacts 53 | path: | 54 | *.tar.* 55 | 56 | publish_branch: 57 | runs-on: ubuntu-latest 58 | name: Publish to website if branch 59 | needs: [ prep, build ] 60 | env: 61 | DEPLOY_ARTIFACTS: "*.tar.*" 62 | DEPLOY_SERVER: travis@firehol.org 63 | DEPLOY_DIR: uploads/iprange/${{needs.prep.outputs.branch}} 64 | SERVER_DEPLOY_LOG: https://firehol.org/travis-project.log 65 | SERVER_DEPLOY_TIMEOUT: 300 66 | if: >- 67 | ${{ ( needs.prep.outputs.branch == 'main' 68 | || needs.prep.outputs.branch == 'master' 69 | || startsWith( needs.prep.outputs.branch, 'stable-' ) ) }} 70 | steps: 71 | - name: Download artifacts 72 | uses: actions/download-artifact@v4.1.7 73 | with: 74 | name: build-artifacts 75 | - name: Setup SSH 76 | id: ssh 77 | run: | 78 | echo "$FIREHOL_ORG_PUBLISH_SSH" > firehol_org_publish_key 79 | chmod 600 firehol_org_publish_key 80 | eval "$(ssh-agent)" 81 | if ssh-add firehol_org_publish_key; then 82 | echo "Key added: setting agent environment" 83 | echo "ssh_agent_pid=$SSH_AGENT_PID" >> $GITHUB_OUTPUT 84 | echo "ssh_auth_sock=$SSH_AUTH_SOCK" >> $GITHUB_OUTPUT 85 | mkdir -p $HOME/.ssh 86 | chmod 700 $HOME/.ssh 87 | echo PasswordAuthentication=no >> $HOME/.ssh/config 88 | chmod 644 $HOME/.ssh/config 89 | else 90 | echo "Key not added: skipping ssh-agent environment" 91 | fi 92 | rm -f firehol_org_publish_key 93 | env: 94 | FIREHOL_ORG_PUBLISH_SSH: ${{secrets.FIREHOL_ORG_PUBLISH_SSH}} 95 | - name: Prepare deployment check 96 | if: ${{ steps.ssh.outputs.ssh_agent_pid != '' }} 97 | run: curl -s -oresult.orig $SERVER_DEPLOY_LOG 98 | - name: Deploy to website ${{needs.prep.outputs.branch}} 99 | if: ${{ steps.ssh.outputs.ssh_agent_pid != '' }} 100 | run: | 101 | ssh-keyscan -H firehol.org >> ~/.ssh/known_hosts 102 | ssh $DEPLOY_SERVER mkdir -p "$DEPLOY_DIR" 103 | rsync -a $DEPLOY_ARTIFACTS "$DEPLOY_SERVER:$DEPLOY_DIR/" 104 | ssh $DEPLOY_SERVER touch "$DEPLOY_DIR/complete.txt" 105 | env: 106 | SSH_AGENT_PID: ${{ steps.ssh.outputs.ssh_agent_pid }} 107 | SSH_AUTH_SOCK: ${{ steps.ssh.outputs.ssh_auth_sock }} 108 | - name: Check deployment 109 | if: ${{ steps.ssh.outputs.ssh_agent_pid != '' }} 110 | run: | 111 | pause=10 112 | attempts=$(( $SERVER_DEPLOY_TIMEOUT / $pause )) 113 | while [ $attempts -gt 0 ] 114 | do 115 | sleep $pause 116 | attempts=$((attempts - 1)) 117 | curl -s -o result $SERVER_DEPLOY_LOG 118 | if ! cmp -s result result.orig 119 | then 120 | cat result 121 | if grep -q "not deploying" result 122 | then 123 | exit 2 124 | else 125 | exit 0 126 | fi 127 | fi 128 | done 129 | exit 1 130 | 131 | publish_tag: 132 | runs-on: ubuntu-latest 133 | name: Publish to github if tag 134 | needs: [ prep, build ] 135 | if: ${{ needs.prep.outputs.tag != '' }} 136 | steps: 137 | - name: Download artifacts 138 | uses: actions/download-artifact@v4.1.7 139 | with: 140 | name: build-artifacts 141 | - name: Create Release 142 | id: create_release 143 | uses: actions/create-release@v1 144 | env: 145 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 146 | with: 147 | tag_name: ${{ github.ref }} 148 | release_name: Release ${{ github.ref }} 149 | draft: true 150 | - name: Upload 151 | uses: actions/github-script@v3 152 | with: 153 | github-token: ${{secrets.GITHUB_TOKEN}} 154 | script: | 155 | const path = require('path'); 156 | const fs = require('fs'); 157 | const release_id = '${{ steps.create_release.outputs.id }}'; 158 | for (let file of await fs.readdirSync('./')) { 159 | console.log('uploadReleaseAsset', file); 160 | await github.repos.uploadReleaseAsset({ 161 | owner: context.repo.owner, 162 | repo: context.repo.repo, 163 | release_id: release_id, 164 | name: file, 165 | data: await fs.readFileSync(`./${file}`) 166 | }); 167 | } 168 | -------------------------------------------------------------------------------- /ipset_print.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | uint8_t prefix_enabled[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 4 | 5 | char *print_prefix_ips = ""; 6 | char *print_prefix_nets = ""; 7 | char *print_suffix_ips = ""; 8 | char *print_suffix_nets = ""; 9 | 10 | inline void prefix_update_counters(in_addr_t addr, int prefix) { 11 | (void)addr; 12 | 13 | if(likely(prefix >= 0 && prefix <= 32)) 14 | prefix_counters[prefix]++; 15 | } 16 | 17 | inline void print_addr(in_addr_t addr, int prefix) { 18 | prefix_update_counters(addr, prefix); 19 | 20 | char buf[IP2STR_MAX_LEN + 1]; 21 | 22 | if (prefix < 32) 23 | printf("%s%s/%d%s\n", print_prefix_nets, ip2str_r(buf, addr), prefix, print_suffix_nets); 24 | else 25 | printf("%s%s%s\n", print_prefix_ips, ip2str_r(buf, addr), print_suffix_ips); 26 | 27 | } /* print_addr() */ 28 | 29 | /*------------------------------------------------------*/ 30 | /* Print out an address range in a.b.c.d-A.B.C.D format */ 31 | /*------------------------------------------------------*/ 32 | inline void print_addr_range(in_addr_t lo, in_addr_t hi) { 33 | char buf[IP2STR_MAX_LEN + 1]; 34 | 35 | if(unlikely(lo > hi)) { 36 | /* 37 | * it should never happen 38 | * give a log for the user to see 39 | */ 40 | in_addr_t t = hi; 41 | fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip2str_r(buf, lo)); 42 | fprintf(stderr, " end=%s\n", ip2str_r(buf, hi)); 43 | hi = lo; 44 | lo = t; 45 | } 46 | 47 | if(lo == hi) { 48 | printf("%s%s-", print_prefix_ips, ip2str_r(buf, lo)); 49 | printf("%s%s\n", ip2str_r(buf, hi), print_suffix_ips); 50 | } 51 | else { 52 | printf("%s%s-", print_prefix_nets, ip2str_r(buf, lo)); 53 | printf("%s%s\n", ip2str_r(buf, hi), print_suffix_nets); 54 | } 55 | 56 | } 57 | 58 | inline void print_addr_single(in_addr_t x) { 59 | char buf[IP2STR_MAX_LEN + 1]; 60 | printf("%s%s%s\n", print_prefix_ips, ip2str_r(buf, x), print_suffix_ips); 61 | 62 | } 63 | 64 | /*------------------------------------------------------------*/ 65 | /* Recursively compute network addresses to cover range lo-hi */ 66 | /*------------------------------------------------------------*/ 67 | /* Note: Worst case scenario is when lo=0.0.0.1 and hi=255.255.255.254 68 | * We then have 62 CIDR blocks to cover this interval, and 125 69 | * calls to split_range(); 70 | * The maximum possible recursion depth is 32. 71 | */ 72 | 73 | inline int split_range(in_addr_t addr, int prefix, in_addr_t lo, in_addr_t hi, void (*print)(in_addr_t, int)) { 74 | in_addr_t bc, lower_half, upper_half; 75 | 76 | if(unlikely(lo > hi)) { 77 | /* 78 | * it should never happen 79 | * give a log for the user to see 80 | */ 81 | in_addr_t t = hi; 82 | char buf[IP2STR_MAX_LEN + 1]; 83 | fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip2str_r(buf, lo)); 84 | fprintf(stderr, " end=%s\n", ip2str_r(buf, hi)); 85 | hi = lo; 86 | lo = t; 87 | } 88 | 89 | if (unlikely((prefix < 0) || (prefix > 32))) { 90 | fprintf(stderr, "%s: Invalid netmask %d!\n", PROG, prefix); 91 | return 0; 92 | } 93 | 94 | bc = broadcast(addr, prefix); 95 | 96 | if (unlikely((lo < addr) || (hi > bc))) { 97 | fprintf(stderr, "%s: Out of range limits: %x, %x for " 98 | "network %x/%d, broadcast: %x!\n", PROG, lo, hi, addr, prefix, bc); 99 | return 0; 100 | } 101 | 102 | if ((lo == addr) && (hi == bc) && prefix_enabled[prefix]) { 103 | print(addr, prefix); 104 | return 1; 105 | } 106 | 107 | prefix++; 108 | lower_half = addr; 109 | upper_half = set_bit(addr, prefix, 1); 110 | 111 | if (hi < upper_half) 112 | return split_range(lower_half, prefix, lo, hi, print); 113 | else if (lo >= upper_half) 114 | return split_range(upper_half, prefix, lo, hi, print); 115 | else 116 | return ( 117 | split_range(lower_half, prefix, lo, broadcast(lower_half, prefix), print) + 118 | split_range(upper_half, prefix, upper_half, hi, print) 119 | ); 120 | } 121 | 122 | 123 | /* ---------------------------------------------------------------------------- 124 | * ipset_print() 125 | * 126 | * print the ipset given to stdout 127 | * 128 | */ 129 | 130 | void ipset_print(ipset *ips, IPSET_PRINT_CMD print) { 131 | size_t i, n, total = 0; 132 | 133 | if(unlikely(!(ips->flags & IPSET_FLAG_OPTIMIZED))) 134 | ipset_optimize(ips); 135 | 136 | if(print == PRINT_BINARY) { 137 | ipset_save_binary_v10(ips); 138 | return; 139 | } 140 | 141 | if(unlikely(debug)) fprintf(stderr, "%s: Printing %s with %zu ranges, %zu unique IPs\n", PROG, ips->filename, ips->entries, ips->unique_ips); 142 | 143 | switch(print) { 144 | case PRINT_CIDR: 145 | /* reset the prefix counters */ 146 | for(i = 0; i <= 32; i++) 147 | prefix_counters[i] = 0; 148 | 149 | n = ips->entries; 150 | for(i = 0; i < n ;i++) 151 | total += split_range(0, 0, ips->netaddrs[i].addr, ips->netaddrs[i].broadcast, print_addr); 152 | 153 | break; 154 | 155 | case PRINT_SINGLE_IPS: 156 | n = ips->entries; 157 | for(i = 0; i < n ;i++) { 158 | in_addr_t x, start = ips->netaddrs[i].addr, end = ips->netaddrs[i].broadcast; 159 | if(unlikely(start > end)) { 160 | char buf[IP2STR_MAX_LEN + 1]; 161 | fprintf(stderr, "%s: WARNING: invalid range reversed start=%s", PROG, ip2str_r(buf, start)); 162 | fprintf(stderr, " end=%s\n", ip2str_r(buf, end)); 163 | x = end; 164 | end = start; 165 | start = x; 166 | } 167 | if(unlikely(end - start > (256 * 256 * 256))) { 168 | char buf[IP2STR_MAX_LEN + 1]; 169 | fprintf(stderr, "%s: too big range eliminated start=%s", PROG, ip2str_r(buf, start)); 170 | fprintf(stderr, " end=%s gives %lu IPs\n", ip2str_r(buf, end), (unsigned long)(end - start)); 171 | continue; 172 | } 173 | for( x = start ; x >= start && x <= end ; x++ ) { 174 | print_addr_single(x); 175 | total++; 176 | } 177 | } 178 | break; 179 | 180 | default: 181 | n = ips->entries; 182 | for(i = 0; i < n ;i++) { 183 | print_addr_range(ips->netaddrs[i].addr, ips->netaddrs[i].broadcast); 184 | total++; 185 | } 186 | break; 187 | } 188 | 189 | /* print prefix break down */ 190 | if(unlikely(debug)) { 191 | int prefixes = 0; 192 | 193 | if (print == PRINT_CIDR) { 194 | 195 | fprintf(stderr, "\n%zu printed CIDRs, break down by prefix:\n", total); 196 | 197 | total = 0; 198 | for(i = 0; i <= 32 ;i++) { 199 | if(prefix_counters[i]) { 200 | fprintf(stderr, " - prefix /%zu counts %zu entries\n", i, prefix_counters[i]); 201 | total += prefix_counters[i]; 202 | prefixes++; 203 | } 204 | } 205 | } 206 | else if (print == PRINT_SINGLE_IPS) prefixes = 1; 207 | 208 | { 209 | char *units; 210 | if (print == PRINT_CIDR) units = "CIDRs"; 211 | else if (print == PRINT_SINGLE_IPS) units = "IPs"; 212 | else units = "ranges"; 213 | 214 | fprintf(stderr, "\ntotals: %zu lines read, %zu distinct IP ranges found, %d CIDR prefixes, %zu %s printed, %zu unique IPs\n", ips->lines, ips->entries, prefixes, total, units, ips->unique_ips); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /tests.d/20-large-scale/output: -------------------------------------------------------------------------------- 1 | 10.10.0.128/25 2 | 10.10.1.128/25 3 | 10.10.2.128/25 4 | 10.10.3.128/25 5 | 10.10.4.128/25 6 | 10.10.5.128/25 7 | 10.10.6.128/25 8 | 10.10.7.128/25 9 | 10.10.8.128/25 10 | 10.10.9.128/25 11 | 10.10.10.128/25 12 | 10.10.11.128/25 13 | 10.10.12.128/25 14 | 10.10.13.128/25 15 | 10.10.14.128/25 16 | 10.10.15.128/25 17 | 10.10.16.128/25 18 | 10.10.17.128/25 19 | 10.10.18.128/25 20 | 10.10.19.128/25 21 | 10.10.20.128/25 22 | 10.10.21.128/25 23 | 10.10.22.128/25 24 | 10.10.23.128/25 25 | 10.10.24.128/25 26 | 10.10.25.128/25 27 | 10.10.26.128/25 28 | 10.10.27.128/25 29 | 10.10.28.128/25 30 | 10.10.29.128/25 31 | 10.10.30.128/25 32 | 10.10.31.128/25 33 | 10.10.32.128/25 34 | 10.10.33.128/25 35 | 10.10.34.128/25 36 | 10.10.35.128/25 37 | 10.10.36.128/25 38 | 10.10.37.128/25 39 | 10.10.38.128/25 40 | 10.10.39.128/25 41 | 10.10.40.128/25 42 | 10.10.41.128/25 43 | 10.10.42.128/25 44 | 10.10.43.128/25 45 | 10.10.44.128/25 46 | 10.10.45.128/25 47 | 10.10.46.128/25 48 | 10.10.47.128/25 49 | 10.10.48.128/25 50 | 10.10.49.128/25 51 | 10.10.50.128/25 52 | 10.10.51.128/25 53 | 10.10.52.128/25 54 | 10.10.53.128/25 55 | 10.10.54.128/25 56 | 10.10.55.128/25 57 | 10.10.56.128/25 58 | 10.10.57.128/25 59 | 10.10.58.128/25 60 | 10.10.59.128/25 61 | 10.10.60.128/25 62 | 10.10.61.128/25 63 | 10.10.62.128/25 64 | 10.10.63.128/25 65 | 10.10.64.128/25 66 | 10.10.65.128/25 67 | 10.10.66.128/25 68 | 10.10.67.128/25 69 | 10.10.68.128/25 70 | 10.10.69.128/25 71 | 10.10.70.128/25 72 | 10.10.71.128/25 73 | 10.10.72.128/25 74 | 10.10.73.128/25 75 | 10.10.74.128/25 76 | 10.10.75.128/25 77 | 10.10.76.128/25 78 | 10.10.77.128/25 79 | 10.10.78.128/25 80 | 10.10.79.128/25 81 | 10.10.80.128/25 82 | 10.10.81.128/25 83 | 10.10.82.128/25 84 | 10.10.83.128/25 85 | 10.10.84.128/25 86 | 10.10.85.128/25 87 | 10.10.86.128/25 88 | 10.10.87.128/25 89 | 10.10.88.128/25 90 | 10.10.89.128/25 91 | 10.10.90.128/25 92 | 10.10.91.128/25 93 | 10.10.92.128/25 94 | 10.10.93.128/25 95 | 10.10.94.128/25 96 | 10.10.95.128/25 97 | 10.10.96.128/25 98 | 10.10.97.128/25 99 | 10.10.98.128/25 100 | 10.10.99.128/25 101 | 10.10.100.128/25 102 | 10.10.101.128/25 103 | 10.10.102.128/25 104 | 10.10.103.128/25 105 | 10.10.104.128/25 106 | 10.10.105.128/25 107 | 10.10.106.128/25 108 | 10.10.107.128/25 109 | 10.10.108.128/25 110 | 10.10.109.128/25 111 | 10.10.110.128/25 112 | 10.10.111.128/25 113 | 10.10.112.128/25 114 | 10.10.113.128/25 115 | 10.10.114.128/25 116 | 10.10.115.128/25 117 | 10.10.116.128/25 118 | 10.10.117.128/25 119 | 10.10.118.128/25 120 | 10.10.119.128/25 121 | 10.10.120.128/25 122 | 10.10.121.128/25 123 | 10.10.122.128/25 124 | 10.10.123.128/25 125 | 10.10.124.128/25 126 | 10.10.125.128/25 127 | 10.10.126.128/25 128 | 10.10.127.128/25 129 | 10.30.0.128/25 130 | 10.30.1.128/25 131 | 10.30.2.128/25 132 | 10.30.3.128/25 133 | 10.30.4.128/25 134 | 10.30.5.128/25 135 | 10.30.6.128/25 136 | 10.30.7.128/25 137 | 10.30.8.128/25 138 | 10.30.9.128/25 139 | 10.30.10.128/25 140 | 10.30.11.128/25 141 | 10.30.12.128/25 142 | 10.30.13.128/25 143 | 10.30.14.128/25 144 | 10.30.15.128/25 145 | 10.30.16.128/25 146 | 10.30.17.128/25 147 | 10.30.18.128/25 148 | 10.30.19.128/25 149 | 10.30.20.128/25 150 | 10.30.21.128/25 151 | 10.30.22.128/25 152 | 10.30.23.128/25 153 | 10.30.24.128/25 154 | 10.30.25.128/25 155 | 10.30.26.128/25 156 | 10.30.27.128/25 157 | 10.30.28.128/25 158 | 10.30.29.128/25 159 | 10.30.30.128/25 160 | 10.30.31.128/25 161 | 10.30.32.128/25 162 | 10.30.33.128/25 163 | 10.30.34.128/25 164 | 10.30.35.128/25 165 | 10.30.36.128/25 166 | 10.30.37.128/25 167 | 10.30.38.128/25 168 | 10.30.39.128/25 169 | 10.30.40.128/25 170 | 10.30.41.128/25 171 | 10.30.42.128/25 172 | 10.30.43.128/25 173 | 10.30.44.128/25 174 | 10.30.45.128/25 175 | 10.30.46.128/25 176 | 10.30.47.128/25 177 | 10.30.48.128/25 178 | 10.30.49.128/25 179 | 10.30.50.128/25 180 | 10.30.51.128/25 181 | 10.30.52.128/25 182 | 10.30.53.128/25 183 | 10.30.54.128/25 184 | 10.30.55.128/25 185 | 10.30.56.128/25 186 | 10.30.57.128/25 187 | 10.30.58.128/25 188 | 10.30.59.128/25 189 | 10.30.60.128/25 190 | 10.30.61.128/25 191 | 10.30.62.128/25 192 | 10.30.63.128/25 193 | 10.30.64.128/25 194 | 10.30.65.128/25 195 | 10.30.66.128/25 196 | 10.30.67.128/25 197 | 10.30.68.128/25 198 | 10.30.69.128/25 199 | 10.30.70.128/25 200 | 10.30.71.128/25 201 | 10.30.72.128/25 202 | 10.30.73.128/25 203 | 10.30.74.128/25 204 | 10.30.75.128/25 205 | 10.30.76.128/25 206 | 10.30.77.128/25 207 | 10.30.78.128/25 208 | 10.30.79.128/25 209 | 10.30.80.128/25 210 | 10.30.81.128/25 211 | 10.30.82.128/25 212 | 10.30.83.128/25 213 | 10.30.84.128/25 214 | 10.30.85.128/25 215 | 10.30.86.128/25 216 | 10.30.87.128/25 217 | 10.30.88.128/25 218 | 10.30.89.128/25 219 | 10.30.90.128/25 220 | 10.30.91.128/25 221 | 10.30.92.128/25 222 | 10.30.93.128/25 223 | 10.30.94.128/25 224 | 10.30.95.128/25 225 | 10.30.96.128/25 226 | 10.30.97.128/25 227 | 10.30.98.128/25 228 | 10.30.99.128/25 229 | 10.30.100.128/25 230 | 10.30.101.128/25 231 | 10.30.102.128/25 232 | 10.30.103.128/25 233 | 10.30.104.128/25 234 | 10.30.105.128/25 235 | 10.30.106.128/25 236 | 10.30.107.128/25 237 | 10.30.108.128/25 238 | 10.30.109.128/25 239 | 10.30.110.128/25 240 | 10.30.111.128/25 241 | 10.30.112.128/25 242 | 10.30.113.128/25 243 | 10.30.114.128/25 244 | 10.30.115.128/25 245 | 10.30.116.128/25 246 | 10.30.117.128/25 247 | 10.30.118.128/25 248 | 10.30.119.128/25 249 | 10.30.120.128/25 250 | 10.30.121.128/25 251 | 10.30.122.128/25 252 | 10.30.123.128/25 253 | 10.30.124.128/25 254 | 10.30.125.128/25 255 | 10.30.126.128/25 256 | 10.30.127.128/25 257 | 10.50.0.128/25 258 | 10.50.1.128/25 259 | 10.50.2.128/25 260 | 10.50.3.128/25 261 | 10.50.4.128/25 262 | 10.50.5.128/25 263 | 10.50.6.128/25 264 | 10.50.7.128/25 265 | 10.50.8.128/25 266 | 10.50.9.128/25 267 | 10.50.10.128/25 268 | 10.50.11.128/25 269 | 10.50.12.128/25 270 | 10.50.13.128/25 271 | 10.50.14.128/25 272 | 10.50.15.128/25 273 | 10.50.16.128/25 274 | 10.50.17.128/25 275 | 10.50.18.128/25 276 | 10.50.19.128/25 277 | 10.50.20.128/25 278 | 10.50.21.128/25 279 | 10.50.22.128/25 280 | 10.50.23.128/25 281 | 10.50.24.128/25 282 | 10.50.25.128/25 283 | 10.50.26.128/25 284 | 10.50.27.128/25 285 | 10.50.28.128/25 286 | 10.50.29.128/25 287 | 10.50.30.128/25 288 | 10.50.31.128/25 289 | 10.50.32.128/25 290 | 10.50.33.128/25 291 | 10.50.34.128/25 292 | 10.50.35.128/25 293 | 10.50.36.128/25 294 | 10.50.37.128/25 295 | 10.50.38.128/25 296 | 10.50.39.128/25 297 | 10.50.40.128/25 298 | 10.50.41.128/25 299 | 10.50.42.128/25 300 | 10.50.43.128/25 301 | 10.50.44.128/25 302 | 10.50.45.128/25 303 | 10.50.46.128/25 304 | 10.50.47.128/25 305 | 10.50.48.128/25 306 | 10.50.49.128/25 307 | 10.50.50.128/25 308 | 10.50.51.128/25 309 | 10.50.52.128/25 310 | 10.50.53.128/25 311 | 10.50.54.128/25 312 | 10.50.55.128/25 313 | 10.50.56.128/25 314 | 10.50.57.128/25 315 | 10.50.58.128/25 316 | 10.50.59.128/25 317 | 10.50.60.128/25 318 | 10.50.61.128/25 319 | 10.50.62.128/25 320 | 10.50.63.128/25 321 | 10.50.64.128/25 322 | 10.50.65.128/25 323 | 10.50.66.128/25 324 | 10.50.67.128/25 325 | 10.50.68.128/25 326 | 10.50.69.128/25 327 | 10.50.70.128/25 328 | 10.50.71.128/25 329 | 10.50.72.128/25 330 | 10.50.73.128/25 331 | 10.50.74.128/25 332 | 10.50.75.128/25 333 | 10.50.76.128/25 334 | 10.50.77.128/25 335 | 10.50.78.128/25 336 | 10.50.79.128/25 337 | 10.50.80.128/25 338 | 10.50.81.128/25 339 | 10.50.82.128/25 340 | 10.50.83.128/25 341 | 10.50.84.128/25 342 | 10.50.85.128/25 343 | 10.50.86.128/25 344 | 10.50.87.128/25 345 | 10.50.88.128/25 346 | 10.50.89.128/25 347 | 10.50.90.128/25 348 | 10.50.91.128/25 349 | 10.50.92.128/25 350 | 10.50.93.128/25 351 | 10.50.94.128/25 352 | 10.50.95.128/25 353 | 10.50.96.128/25 354 | 10.50.97.128/25 355 | 10.50.98.128/25 356 | 10.50.99.128/25 357 | 10.50.100.128/25 358 | 10.50.101.128/25 359 | 10.50.102.128/25 360 | 10.50.103.128/25 361 | 10.50.104.128/25 362 | 10.50.105.128/25 363 | 10.50.106.128/25 364 | 10.50.107.128/25 365 | 10.50.108.128/25 366 | 10.50.109.128/25 367 | 10.50.110.128/25 368 | 10.50.111.128/25 369 | 10.50.112.128/25 370 | 10.50.113.128/25 371 | 10.50.114.128/25 372 | 10.50.115.128/25 373 | 10.50.116.128/25 374 | 10.50.117.128/25 375 | 10.50.118.128/25 376 | 10.50.119.128/25 377 | 10.50.120.128/25 378 | 10.50.121.128/25 379 | 10.50.122.128/25 380 | 10.50.123.128/25 381 | 10.50.124.128/25 382 | 10.50.125.128/25 383 | 10.50.126.128/25 384 | 10.50.127.128/25 385 | -------------------------------------------------------------------------------- /packaging/packaging.functions: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | packaging_check_init() { 4 | version_check= 5 | } 6 | 7 | packaging_check_file() { 8 | local filename="$1" status=0 9 | 10 | case $filename in 11 | configure.ac) 12 | check_versions || status=1 13 | version_check=Y 14 | ;; 15 | *.spec.in) 16 | check_versions || status=1 17 | version_check=Y 18 | ;; 19 | ChangeLog) 20 | check_versions || status=1 21 | version_check=Y 22 | if [ $status -eq 0 ] 23 | then 24 | check_changelog || status=1 25 | fi 26 | ;; 27 | *) 28 | #echo "No checks found for $filename" 29 | : 30 | ;; 31 | esac 32 | return $status 33 | } 34 | 35 | packaging_check_fin() { 36 | if [ "$version_check" ] 37 | then 38 | version=`get_configure_ac_version` 39 | case "$(match_version $version)" in 40 | prerelease|candidate|release) 41 | try_build || status=1 42 | if [ $status -eq 0 ] 43 | then 44 | cp $MYTMP/build/*.tar.* . 45 | echo "Check $(cd $MYTMP/build && ls *.tar.gz) before pushing tags" 46 | fi 47 | ;; 48 | esac 49 | fi 50 | } 51 | 52 | get_staged_file() { 53 | local optional=0 54 | if [ "$1" = "-o" ] 55 | then 56 | optional=1 57 | shift 58 | fi 59 | 60 | local dir="${1%/*}" 61 | if [ "$dir" = "$1" ]; then dir="."; fi 62 | mkdir -p $MYTMP/files/$dir 63 | 64 | test -f $MYTMP/files/$1 && return 0 65 | 66 | if [ "$from_cache" ] 67 | then 68 | if [ $optional -eq 1 ] 69 | then 70 | git show :$1 > $MYTMP/files/$1 2> /dev/null || rm -f $MYTMP/files/$1 71 | else 72 | git show :$1 > $MYTMP/files/$1 73 | fi 74 | else 75 | if [ $optional -eq 0 -o -f $1 ] 76 | then 77 | cp $1 $MYTMP/files/$1 78 | fi 79 | fi 80 | } 81 | 82 | try_build() { 83 | if [ -f $MYTMP/success ]; then return 0; fi 84 | mkdir -p $MYTMP/build 85 | git archive HEAD | tar -xf - -C "$MYTMP/build" 86 | git diff --staged | patch -p1 -d "$MYTMP/build" 87 | (cd $MYTMP/build; ./packaging/git-build || touch $MYTMP/fail) 88 | if [ -f $MYTMP/fail ]; then return 1; fi 89 | (cd $MYTMP/build; ./packaging/tar-compare . *.tar.gz || touch $MYTMP/fail) 90 | if [ -f $MYTMP/fail ]; then return 1; fi 91 | (cd $MYTMP/build; make check || touch $MYTMP/fail) 92 | if [ -f $MYTMP/fail ]; then return 1; fi 93 | touch $MYTMP/success 94 | return 0 95 | } 96 | 97 | get_changelog_version() { 98 | get_staged_file ChangeLog 99 | local v=`sed -ne '1s/.*(\(.*\)).*/\1/p' $MYTMP/files/ChangeLog` 100 | if [ ! "$v" ]; then v="No version in ChangeLog!"; fi 101 | echo "$v" 102 | } 103 | 104 | get_configure_ac_version() { 105 | get_staged_file configure.ac 106 | local v=`sed -n \ 107 | -e '/define(\[VERSION_\(MINOR\|FIX\)/s/.*\[\([^[]*\)\].*/.\1/p' \ 108 | -e '/define(\[VERSION_\(MAJOR\|SUFFIX\)/s/.*\[\([^[]*\)\].*/\1/p' \ 109 | $MYTMP/files/configure.ac | tr -d '\n'` 110 | if [ ! "$v" ]; then v="No version in configure.ac!"; fi 111 | echo "$v" 112 | } 113 | 114 | get_configure_ac_package() { 115 | get_staged_file configure.ac 116 | local v=`sed -n -e 's/AC_INIT(\[\([^]]*\)\].*/\1/p' configure.ac` 117 | if [ ! "$v" ]; then v="noname"; fi 118 | echo "$v" 119 | } 120 | 121 | get_configure_ac_rpmrel() { 122 | get_staged_file configure.ac 123 | local v=`sed -n -e 's/PACKAGE_RPM_RELEASE="\([^"]*\)".*/\1/p' configure.ac` 124 | if [ ! "$v" ]; then v="norpmrel"; fi 125 | echo "$v" 126 | } 127 | 128 | get_spec_version() { 129 | get_staged_file -o "$1".spec.in 130 | test -f $MYTMP/files/"$1".spec.in || return 0 # Spec file is optional 131 | sed -n -e '1,/^%changelog/d' -e '/^*/{s/.*- \([0-9].*\)/\1/p;q}' "$1".spec.in 132 | } 133 | 134 | splitver() { 135 | local maj min fix sfx IFS=.-_ 136 | 137 | maj=$1 138 | min=$2 139 | fix=$3 140 | sfx=$4 141 | 142 | set -- $5 143 | eval $maj=\$1 $min=\$2 $fix=\$3 $sfx=\$4 144 | } 145 | 146 | match_version() { 147 | case "$1" in 148 | [0-9]*.[0-9]*.[0-9]*_*) 149 | # x.y.z_ZZZZ = development branch (can be before or after pre/rc) 150 | echo "development" 151 | ;; 152 | [0-9]*.[0-9]*.[0-9]-pre[0-9]*) 153 | echo "prerelease" 154 | ;; 155 | [0-9]*.[0-9]*.[0-9]-rc.[0-9]*) 156 | echo "candidate" 157 | ;; 158 | [0-9]*.[0-9]*.[0-9]*) 159 | echo "release" 160 | ;; 161 | *) 162 | # Unknown 163 | : 164 | ;; 165 | esac 166 | } 167 | 168 | check_versions() { 169 | local status=0 exact=0 prerelease=0 170 | 171 | if [ -f $MYTMP/version-checked ] 172 | then 173 | read status < $MYTMP/version-checked 174 | return $status 175 | fi 176 | 177 | local confver=`get_configure_ac_version` 178 | case "$(match_version $confver)" in 179 | development) 180 | : 181 | ;; 182 | prerelease|candidate|release) 183 | exact=1 184 | ;; 185 | *) 186 | echo "Unrecognised version in configure.ac ($confver)" 187 | status=1 188 | ;; 189 | esac 190 | 191 | local clogver=`get_changelog_version` 192 | case "$(match_version $clogver)" in 193 | development) 194 | echo "Do not include development branch version in ChangeLog ($clogver)" 195 | status=1 196 | ;; 197 | prerelease|candidate) 198 | prerelease=1 199 | ;; 200 | release) 201 | : 202 | ;; 203 | *) 204 | echo "Unrecognised version format in ChangeLog ($clogver)" 205 | status=1 206 | ;; 207 | esac 208 | 209 | local package=`get_configure_ac_package` 210 | local specver=`get_spec_version $package` 211 | 212 | local clogmaj clogmin clogfix clogsfx 213 | local confmaj confmin conffix confsfx 214 | local specmaj specmin specfix specsfx 215 | 216 | splitver clogmaj clogmin clogfix clogsfx "$clogver" 217 | splitver confmaj confmin conffix confsfx "$confver" 218 | splitver specmaj specmin specfix specsfx "$specver" 219 | 220 | if [ "$specver" ] 221 | then 222 | if [ $specmaj -ne $clogmaj \ 223 | -o $specmin -ne $clogmin \ 224 | -o $specfix -ne $clogfix ] 225 | then 226 | echo "Main version of $package.spec.in ($specver) differs from ChangeLog ($clogver)" 227 | status=1 228 | fi 229 | fi 230 | 231 | if [ $status -eq 0 -a $exact -eq 0 ] 232 | then 233 | 234 | if [ $confmaj -gt $clogmaj \ 235 | -o $confmin -gt $clogmin \ 236 | -o $conffix -gt $clogfix ] 237 | then 238 | : 239 | elif [ $confmaj -eq $clogmaj \ 240 | -a $confmin -eq $clogmin \ 241 | -a $conffix -eq $clogfix \ 242 | -a $prerelease -eq 1 ] 243 | then 244 | : 245 | else 246 | echo "Version in configure.ac ($confver) lesser than ChangeLog ($clogver)" 247 | status=1 248 | fi 249 | fi 250 | 251 | if [ $exact -eq 1 ] 252 | then 253 | echo "Running additional release checks" 254 | 255 | if [ "$confver" != "$clogver" ] 256 | then 257 | echo "Version in configure.ac ($confver) differs from ChangeLog ($clogver)" 258 | status=1 259 | fi 260 | 261 | if [ "$specver" ] 262 | then 263 | local confrpmrel=`get_configure_ac_rpmrel` 264 | if [ "$specsfx" != "$confrpmrel" ] 265 | then 266 | echo "%changelog suffix in $package.spec.in ($specsfx) differs from configure.ac PACKAGE_RPM_RELEASE ($confrpmrel)" 267 | status=1 268 | fi 269 | fi 270 | 271 | if [ ! "$TRAVIS_TAG" ] 272 | then 273 | if [ "$(git tag -l v$confver)" ] 274 | then 275 | echo "Tag v$confver already exists" 276 | status=1 277 | fi 278 | 279 | if [ "$(git config user.signingkey)" = "" ] 280 | then 281 | echo "You need to set up a PGP signing key e.g.:" 282 | echo " gpg --list-keys" 283 | echo "and" 284 | echo " git config user.signingkey SHORTID" 285 | echo "or" 286 | echo " git config --global user.signingkey SHORTID" 287 | status=1 288 | fi 289 | 290 | git status -s | grep "^?" > $MYTMP/needclean 291 | if [ -s $MYTMP/needclean ] 292 | then 293 | echo "The following files must be dealt with before commit:" 294 | cat $MYTMP/needclean 295 | echo "e.g. add them to .gitignore or remove with 'git clean -fdx'" 296 | status=1 297 | fi 298 | fi 299 | fi 300 | 301 | echo $status > $MYTMP/version-checked 302 | return $status 303 | } 304 | 305 | check_changelog() { 306 | local status=0 307 | 308 | if [ -f $MYTMP/changelog-checked ] 309 | then 310 | read status < $MYTMP/changelog-checked 311 | return $status 312 | fi 313 | 314 | local version=`sed -ne '1s/.*(\(.*\)).*/\1/p' $filename` 315 | if [ "`echo $version | grep '[[:space:]]'`" != "" ] 316 | then 317 | echo "ChangeLog version contains whitespace! Fix it!" 318 | status=1 319 | fi 320 | 321 | echo $status > $MYTMP/changelog-checked 322 | return $status 323 | } 324 | -------------------------------------------------------------------------------- /m4/ax_pthread.m4: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # http://www.gnu.org/software/autoconf-archive/ax_pthread.html 3 | # =========================================================================== 4 | # 5 | # SYNOPSIS 6 | # 7 | # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) 8 | # 9 | # DESCRIPTION 10 | # 11 | # This macro figures out how to build C programs using POSIX threads. It 12 | # sets the PTHREAD_LIBS output variable to the threads library and linker 13 | # flags, and the PTHREAD_CFLAGS output variable to any special C compiler 14 | # flags that are needed. (The user can also force certain compiler 15 | # flags/libs to be tested by setting these environment variables.) 16 | # 17 | # Also sets PTHREAD_CC to any special C compiler that is needed for 18 | # multi-threaded programs (defaults to the value of CC otherwise). (This 19 | # is necessary on AIX to use the special cc_r compiler alias.) 20 | # 21 | # NOTE: You are assumed to not only compile your program with these flags, 22 | # but also link it with them as well. e.g. you should link with 23 | # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS 24 | # 25 | # If you are only building threads programs, you may wish to use these 26 | # variables in your default LIBS, CFLAGS, and CC: 27 | # 28 | # LIBS="$PTHREAD_LIBS $LIBS" 29 | # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 30 | # CC="$PTHREAD_CC" 31 | # 32 | # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant 33 | # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name 34 | # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). 35 | # 36 | # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the 37 | # PTHREAD_PRIO_INHERIT symbol is defined when compiling with 38 | # PTHREAD_CFLAGS. 39 | # 40 | # ACTION-IF-FOUND is a list of shell commands to run if a threads library 41 | # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it 42 | # is not found. If ACTION-IF-FOUND is not specified, the default action 43 | # will define HAVE_PTHREAD. 44 | # 45 | # Please let the authors know if this macro fails on any platform, or if 46 | # you have any other suggestions or comments. This macro was based on work 47 | # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help 48 | # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by 49 | # Alejandro Forero Cuervo to the autoconf macro repository. We are also 50 | # grateful for the helpful feedback of numerous users. 51 | # 52 | # Updated for Autoconf 2.68 by Daniel Richard G. 53 | # 54 | # LICENSE 55 | # 56 | # Copyright (c) 2008 Steven G. Johnson 57 | # Copyright (c) 2011 Daniel Richard G. 58 | # 59 | # This program is free software: you can redistribute it and/or modify it 60 | # under the terms of the GNU General Public License as published by the 61 | # Free Software Foundation, either version 3 of the License, or (at your 62 | # option) any later version. 63 | # 64 | # This program is distributed in the hope that it will be useful, but 65 | # WITHOUT ANY WARRANTY; without even the implied warranty of 66 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 67 | # Public License for more details. 68 | # 69 | # You should have received a copy of the GNU General Public License along 70 | # with this program. If not, see . 71 | # 72 | # As a special exception, the respective Autoconf Macro's copyright owner 73 | # gives unlimited permission to copy, distribute and modify the configure 74 | # scripts that are the output of Autoconf when processing the Macro. You 75 | # need not follow the terms of the GNU General Public License when using 76 | # or distributing such scripts, even though portions of the text of the 77 | # Macro appear in them. The GNU General Public License (GPL) does govern 78 | # all other use of the material that constitutes the Autoconf Macro. 79 | # 80 | # This special exception to the GPL applies to versions of the Autoconf 81 | # Macro released by the Autoconf Archive. When you make and distribute a 82 | # modified version of the Autoconf Macro, you may extend this special 83 | # exception to the GPL to apply to your modified version as well. 84 | 85 | #serial 21 86 | 87 | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) 88 | AC_DEFUN([AX_PTHREAD], [ 89 | AC_REQUIRE([AC_CANONICAL_HOST]) 90 | AC_LANG_PUSH([C]) 91 | ax_pthread_ok=no 92 | 93 | # We used to check for pthread.h first, but this fails if pthread.h 94 | # requires special compiler flags (e.g. on True64 or Sequent). 95 | # It gets checked for in the link test anyway. 96 | 97 | # First of all, check if the user has set any of the PTHREAD_LIBS, 98 | # etcetera environment variables, and if threads linking works using 99 | # them: 100 | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then 101 | save_CFLAGS="$CFLAGS" 102 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 103 | save_LIBS="$LIBS" 104 | LIBS="$PTHREAD_LIBS $LIBS" 105 | AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) 106 | AC_TRY_LINK_FUNC([pthread_join], [ax_pthread_ok=yes]) 107 | AC_MSG_RESULT([$ax_pthread_ok]) 108 | if test x"$ax_pthread_ok" = xno; then 109 | PTHREAD_LIBS="" 110 | PTHREAD_CFLAGS="" 111 | fi 112 | LIBS="$save_LIBS" 113 | CFLAGS="$save_CFLAGS" 114 | fi 115 | 116 | # We must check for the threads library under a number of different 117 | # names; the ordering is very important because some systems 118 | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the 119 | # libraries is broken (non-POSIX). 120 | 121 | # Create a list of thread flags to try. Items starting with a "-" are 122 | # C compiler flags, and other items are library names, except for "none" 123 | # which indicates that we try without any flags at all, and "pthread-config" 124 | # which is a program returning the flags for the Pth emulation library. 125 | 126 | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" 127 | 128 | # The ordering *is* (sometimes) important. Some notes on the 129 | # individual items follow: 130 | 131 | # pthreads: AIX (must check this before -lpthread) 132 | # none: in case threads are in libc; should be tried before -Kthread and 133 | # other compiler flags to prevent continual compiler warnings 134 | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) 135 | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) 136 | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) 137 | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) 138 | # -pthreads: Solaris/gcc 139 | # -mthreads: Mingw32/gcc, Lynx/gcc 140 | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it 141 | # doesn't hurt to check since this sometimes defines pthreads too; 142 | # also defines -D_REENTRANT) 143 | # ... -mt is also the pthreads flag for HP/aCC 144 | # pthread: Linux, etcetera 145 | # --thread-safe: KAI C++ 146 | # pthread-config: use pthread-config program (for GNU Pth library) 147 | 148 | case ${host_os} in 149 | solaris*) 150 | 151 | # On Solaris (at least, for some versions), libc contains stubbed 152 | # (non-functional) versions of the pthreads routines, so link-based 153 | # tests will erroneously succeed. (We need to link with -pthreads/-mt/ 154 | # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather 155 | # a function called by this macro, so we could check for that, but 156 | # who knows whether they'll stub that too in a future libc.) So, 157 | # we'll just look for -pthreads and -lpthread first: 158 | 159 | ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" 160 | ;; 161 | 162 | darwin*) 163 | ax_pthread_flags="-pthread $ax_pthread_flags" 164 | ;; 165 | esac 166 | 167 | # Clang doesn't consider unrecognized options an error unless we specify 168 | # -Werror. We throw in some extra Clang-specific options to ensure that 169 | # this doesn't happen for GCC, which also accepts -Werror. 170 | 171 | AC_MSG_CHECKING([if compiler needs -Werror to reject unknown flags]) 172 | save_CFLAGS="$CFLAGS" 173 | ax_pthread_extra_flags="-Werror" 174 | CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wunknown-warning-option -Wsizeof-array-argument" 175 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])], 176 | [AC_MSG_RESULT([yes])], 177 | [ax_pthread_extra_flags= 178 | AC_MSG_RESULT([no])]) 179 | CFLAGS="$save_CFLAGS" 180 | 181 | if test x"$ax_pthread_ok" = xno; then 182 | for flag in $ax_pthread_flags; do 183 | 184 | case $flag in 185 | none) 186 | AC_MSG_CHECKING([whether pthreads work without any flags]) 187 | ;; 188 | 189 | -*) 190 | AC_MSG_CHECKING([whether pthreads work with $flag]) 191 | PTHREAD_CFLAGS="$flag" 192 | ;; 193 | 194 | pthread-config) 195 | AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) 196 | if test x"$ax_pthread_config" = xno; then continue; fi 197 | PTHREAD_CFLAGS="`pthread-config --cflags`" 198 | PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" 199 | ;; 200 | 201 | *) 202 | AC_MSG_CHECKING([for the pthreads library -l$flag]) 203 | PTHREAD_LIBS="-l$flag" 204 | ;; 205 | esac 206 | 207 | save_LIBS="$LIBS" 208 | save_CFLAGS="$CFLAGS" 209 | LIBS="$PTHREAD_LIBS $LIBS" 210 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags" 211 | 212 | # Check for various functions. We must include pthread.h, 213 | # since some functions may be macros. (On the Sequent, we 214 | # need a special flag -Kthread to make this header compile.) 215 | # We check for pthread_join because it is in -lpthread on IRIX 216 | # while pthread_create is in libc. We check for pthread_attr_init 217 | # due to DEC craziness with -lpthreads. We check for 218 | # pthread_cleanup_push because it is one of the few pthread 219 | # functions on Solaris that doesn't have a non-functional libc stub. 220 | # We try pthread_create on general principles. 221 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include 222 | static void routine(void *a) { a = 0; } 223 | static void *start_routine(void *a) { return a; }], 224 | [pthread_t th; pthread_attr_t attr; 225 | pthread_create(&th, 0, start_routine, 0); 226 | pthread_join(th, 0); 227 | pthread_attr_init(&attr); 228 | pthread_cleanup_push(routine, 0); 229 | pthread_cleanup_pop(0) /* ; */])], 230 | [ax_pthread_ok=yes], 231 | []) 232 | 233 | LIBS="$save_LIBS" 234 | CFLAGS="$save_CFLAGS" 235 | 236 | AC_MSG_RESULT([$ax_pthread_ok]) 237 | if test "x$ax_pthread_ok" = xyes; then 238 | break; 239 | fi 240 | 241 | PTHREAD_LIBS="" 242 | PTHREAD_CFLAGS="" 243 | done 244 | fi 245 | 246 | # Various other checks: 247 | if test "x$ax_pthread_ok" = xyes; then 248 | save_LIBS="$LIBS" 249 | LIBS="$PTHREAD_LIBS $LIBS" 250 | save_CFLAGS="$CFLAGS" 251 | CFLAGS="$CFLAGS $PTHREAD_CFLAGS" 252 | 253 | # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. 254 | AC_MSG_CHECKING([for joinable pthread attribute]) 255 | attr_name=unknown 256 | for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do 257 | AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], 258 | [int attr = $attr; return attr /* ; */])], 259 | [attr_name=$attr; break], 260 | []) 261 | done 262 | AC_MSG_RESULT([$attr_name]) 263 | if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then 264 | AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], [$attr_name], 265 | [Define to necessary symbol if this constant 266 | uses a non-standard name on your system.]) 267 | fi 268 | 269 | AC_MSG_CHECKING([if more special flags are required for pthreads]) 270 | flag=no 271 | case ${host_os} in 272 | aix* | freebsd* | darwin*) flag="-D_THREAD_SAFE";; 273 | osf* | hpux*) flag="-D_REENTRANT";; 274 | solaris*) 275 | if test "$GCC" = "yes"; then 276 | flag="-D_REENTRANT" 277 | else 278 | # TODO: What about Clang on Solaris? 279 | flag="-mt -D_REENTRANT" 280 | fi 281 | ;; 282 | esac 283 | AC_MSG_RESULT([$flag]) 284 | if test "x$flag" != xno; then 285 | PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" 286 | fi 287 | 288 | AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], 289 | [ax_cv_PTHREAD_PRIO_INHERIT], [ 290 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], 291 | [[int i = PTHREAD_PRIO_INHERIT;]])], 292 | [ax_cv_PTHREAD_PRIO_INHERIT=yes], 293 | [ax_cv_PTHREAD_PRIO_INHERIT=no]) 294 | ]) 295 | AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"], 296 | [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])]) 297 | 298 | LIBS="$save_LIBS" 299 | CFLAGS="$save_CFLAGS" 300 | 301 | # More AIX lossage: compile with *_r variant 302 | if test "x$GCC" != xyes; then 303 | case $host_os in 304 | aix*) 305 | AS_CASE(["x/$CC"], 306 | [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], 307 | [#handle absolute path differently from PATH based program lookup 308 | AS_CASE(["x$CC"], 309 | [x/*], 310 | [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], 311 | [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) 312 | ;; 313 | esac 314 | fi 315 | fi 316 | 317 | test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" 318 | 319 | AC_SUBST([PTHREAD_LIBS]) 320 | AC_SUBST([PTHREAD_CFLAGS]) 321 | AC_SUBST([PTHREAD_CC]) 322 | 323 | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: 324 | if test x"$ax_pthread_ok" = xyes; then 325 | ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) 326 | : 327 | else 328 | ax_pthread_ok=no 329 | $2 330 | fi 331 | AC_LANG_POP 332 | ])dnl AX_PTHREAD 333 | -------------------------------------------------------------------------------- /ipset_load.c: -------------------------------------------------------------------------------- 1 | #include "iprange.h" 2 | 3 | /* 4 | * the maximum line element to read in input files 5 | * normally the elements are IP, IP/MASK, HOSTNAME 6 | */ 7 | #define MAX_INPUT_ELEMENT 255 8 | 9 | 10 | /* ---------------------------------------------------------------------------- 11 | * parse_line() 12 | * 13 | * it parses a single line of input 14 | * returns 15 | * -1 = cannot parse line 16 | * 0 = skip line - nothing useful here 17 | * 1 = parsed 1 ip address 18 | * 2 = parsed 2 ip addresses 19 | * 3 = parsed 1 hostname 20 | * 21 | */ 22 | 23 | typedef enum ipset_line_type { 24 | LINE_IS_INVALID = -1, 25 | LINE_IS_EMPTY = 0, 26 | LINE_HAS_1_IP = 1, 27 | LINE_HAS_2_IPS = 2, 28 | LINE_HAS_1_HOSTNAME = 3 29 | } IPSET_LINE_TYPE; 30 | 31 | static inline IPSET_LINE_TYPE parse_hostname(char *line, int lineid, char *ipstr, char *ipstr2, int len) { 32 | char *s = line; 33 | int i = 0; 34 | 35 | if(ipstr2) { ; } 36 | 37 | /* skip all spaces */ 38 | while(unlikely(*s == ' ' || *s == '\t')) s++; 39 | 40 | while(likely(i < len && ( 41 | (*s >= '0' && *s <= '9') 42 | || (*s >= 'a' && *s <= 'z') 43 | || (*s >= 'A' && *s <= 'Z') 44 | || *s == '_' 45 | || *s == '-' 46 | || *s == '.' 47 | ))) ipstr[i++] = *s++; 48 | 49 | /* terminate ipstr */ 50 | ipstr[i] = '\0'; 51 | 52 | /* skip all spaces */ 53 | while(unlikely(*s == ' ' || *s == '\t')) s++; 54 | 55 | /* the rest is comment */ 56 | if(unlikely(*s == '#' || *s == ';')) return LINE_HAS_1_HOSTNAME; 57 | 58 | /* if we reached the end of line */ 59 | if(likely(*s == '\r' || *s == '\n' || *s == '\0')) return LINE_HAS_1_HOSTNAME; 60 | 61 | fprintf(stderr, "%s: Ignoring text after hostname '%s' on line %d: '%s'\n", PROG, ipstr, lineid, s); 62 | 63 | return LINE_HAS_1_HOSTNAME; 64 | } 65 | 66 | static inline IPSET_LINE_TYPE parse_line(char *line, int lineid, char *ipstr, char *ipstr2, int len) { 67 | char *s = line; 68 | int i = 0; 69 | 70 | /* skip all spaces */ 71 | while(unlikely(*s == ' ' || *s == '\t')) s++; 72 | 73 | /* skip a line of comment */ 74 | if(unlikely(*s == '#' || *s == ';')) return LINE_IS_EMPTY; 75 | 76 | /* if we reached the end of line */ 77 | if(unlikely(*s == '\r' || *s == '\n' || *s == '\0')) return LINE_IS_EMPTY; 78 | 79 | /* get the ip address */ 80 | while(likely(i < len && ((*s >= '0' && *s <= '9') || *s == '.' || *s == '/'))) 81 | ipstr[i++] = *s++; 82 | 83 | if(unlikely(!i)) return parse_hostname(line, lineid, ipstr, ipstr2, len); 84 | 85 | /* terminate ipstr */ 86 | ipstr[i] = '\0'; 87 | 88 | /* skip all spaces */ 89 | while(unlikely(*s == ' ' || *s == '\t')) s++; 90 | 91 | /* the rest is comment */ 92 | if(unlikely(*s == '#' || *s == ';')) return LINE_HAS_1_IP; 93 | 94 | /* if we reached the end of line */ 95 | if(likely(*s == '\r' || *s == '\n' || *s == '\0')) return LINE_HAS_1_IP; 96 | 97 | if(unlikely(*s != '-')) { 98 | /*fprintf(stderr, "%s: Ignoring text on line %d, expected a - after %s, but found '%s'\n", PROG, lineid, ipstr, s);*/ 99 | /*return LINE_HAS_1_IP;*/ 100 | return parse_hostname(line, lineid, ipstr, ipstr2, len); 101 | } 102 | 103 | /* skip the - */ 104 | s++; 105 | 106 | /* skip all spaces */ 107 | while(unlikely(*s == ' ' || *s == '\t')) s++; 108 | 109 | /* the rest is comment */ 110 | if(unlikely(*s == '#' || *s == ';')) { 111 | fprintf(stderr, "%s: Ignoring text on line %d, expected an ip address after -, but found '%s'\n", PROG, lineid, s); 112 | return LINE_HAS_1_IP; 113 | } 114 | 115 | /* if we reached the end of line */ 116 | if(unlikely(*s == '\r' || *s == '\n' || *s == '\0')) { 117 | fprintf(stderr, "%s: Incomplete range on line %d, expected an ip address after -, but line ended\n", PROG, lineid); 118 | return LINE_HAS_1_IP; 119 | } 120 | 121 | /* get the ip 2nd address */ 122 | i = 0; 123 | while(likely(i < len && ((*s >= '0' && *s <= '9') || *s == '.' || *s == '/'))) 124 | ipstr2[i++] = *s++; 125 | 126 | if(unlikely(!i)) { 127 | /*fprintf(stderr, "%s: Incomplete range on line %d, expected an ip address after -, but line ended\n", PROG, lineid); */ 128 | /*return LINE_HAS_1_IP; */ 129 | return parse_hostname(line, lineid, ipstr, ipstr2, len); 130 | } 131 | 132 | /* terminate ipstr */ 133 | ipstr2[i] = '\0'; 134 | 135 | /* skip all spaces */ 136 | while(unlikely(*s == ' ' || *s == '\t')) s++; 137 | 138 | /* the rest is comment */ 139 | if(unlikely(*s == '#' || *s == ';')) return LINE_HAS_2_IPS; 140 | 141 | /* if we reached the end of line */ 142 | if(likely(*s == '\r' || *s == '\n' || *s == '\0')) return LINE_HAS_2_IPS; 143 | 144 | /*fprintf(stderr, "%s: Ignoring text on line %d, after the second ip address: '%s'\n", PROG, lineid, s); */ 145 | /*return LINE_HAS_2_IPS; */ 146 | return parse_hostname(line, lineid, ipstr, ipstr2, len); 147 | } 148 | 149 | /* ---------------------------------------------------------------------------- 150 | * hostname resolution 151 | */ 152 | 153 | typedef struct dnsreq { 154 | struct dnsreq *next; 155 | char tries; 156 | char hostname[]; 157 | } DNSREQ; 158 | 159 | typedef struct dnsrep { 160 | in_addr_t ip; 161 | struct dnsrep *next; 162 | } DNSREP; 163 | 164 | static DNSREQ *dns_requests; 165 | static DNSREP *dns_replies; 166 | static int dns_threads; 167 | int dns_threads_max = 5; 168 | int dns_silent; 169 | int dns_progress; 170 | static unsigned long dns_requests_pending; 171 | static unsigned long dns_requests_made; 172 | static unsigned long dns_requests_finished; 173 | static unsigned long dns_requests_retries; 174 | static unsigned long dns_replies_found; 175 | static unsigned long dns_replies_failed; 176 | 177 | static pthread_mutex_t dns_mut = PTHREAD_MUTEX_INITIALIZER; 178 | static pthread_cond_t dns_cond = PTHREAD_COND_INITIALIZER; 179 | static pthread_mutex_t dns_requests_mut = PTHREAD_MUTEX_INITIALIZER; 180 | static pthread_mutex_t dns_replies_mut = PTHREAD_MUTEX_INITIALIZER; 181 | 182 | void dns_lock_requests(void) { pthread_mutex_lock(&dns_requests_mut); } 183 | void dns_unlock_requests(void) { pthread_mutex_unlock(&dns_requests_mut); } 184 | void dns_lock_replies(void) { pthread_mutex_lock(&dns_replies_mut); } 185 | void dns_unlock_replies(void) { pthread_mutex_unlock(&dns_replies_mut); } 186 | 187 | // the threads waiting for requests 188 | void dns_thread_wait_for_requests(void) { 189 | pthread_mutex_lock(&dns_mut); 190 | while(!dns_requests) 191 | pthread_cond_wait(&dns_cond, &dns_mut); 192 | pthread_mutex_unlock(&dns_mut); 193 | } 194 | 195 | // the master signals the threads for new requests 196 | static void dns_signal_threads(void) 197 | { 198 | /* signal the childs we have a new request for them */ 199 | pthread_mutex_lock(&dns_mut); 200 | pthread_cond_signal(&dns_cond); 201 | pthread_mutex_unlock(&dns_mut); 202 | } 203 | 204 | 205 | static void *dns_thread_resolve(void *ptr); 206 | 207 | /* ---------------------------------------------------------------------------- 208 | * dns_request_add() 209 | * 210 | * add a new DNS resolution request to the queue 211 | * 212 | */ 213 | 214 | static void dns_request_add(DNSREQ *d) 215 | { 216 | unsigned long pending; 217 | 218 | dns_lock_requests(); 219 | d->next = dns_requests; 220 | dns_requests = d; 221 | dns_requests_pending++; 222 | dns_requests_made++; 223 | 224 | pending = dns_requests_pending; 225 | dns_unlock_requests(); 226 | 227 | /* do we have start a new thread? */ 228 | if(pending > (unsigned long)dns_threads && dns_threads < dns_threads_max) { 229 | pthread_t thread; 230 | 231 | if(unlikely(debug)) 232 | fprintf(stderr, "%s: Creating new DNS thread\n", PROG); 233 | 234 | if(pthread_create(&thread, NULL, dns_thread_resolve, NULL)) { 235 | fprintf(stderr, "%s: Cannot create DNS thread.\n", PROG); 236 | return; 237 | } 238 | else if(pthread_detach(thread)) { 239 | fprintf(stderr, "%s: Cannot detach DNS thread.\n", PROG); 240 | return; 241 | } 242 | 243 | dns_threads++; 244 | } 245 | 246 | dns_signal_threads(); 247 | } 248 | 249 | 250 | /* ---------------------------------------------------------------------------- 251 | * dns_request_done() 252 | * 253 | * to be called by a worker thread 254 | * let the main thread know a DNS resolution has been completed 255 | * 256 | */ 257 | 258 | static void dns_request_done(DNSREQ *d, int added) 259 | { 260 | dns_lock_requests(); 261 | dns_requests_pending--; 262 | dns_requests_finished++; 263 | 264 | if(!added) dns_replies_failed++; 265 | else dns_replies_found += added; 266 | 267 | dns_unlock_requests(); 268 | 269 | free(d); 270 | } 271 | 272 | 273 | /* ---------------------------------------------------------------------------- 274 | * dns_request_failed() 275 | * 276 | * to be called by a worker thread 277 | * handle a DNS failure (mainly for retries) 278 | * 279 | */ 280 | 281 | static void dns_request_failed(DNSREQ *d, int added, int gai_error) 282 | { 283 | switch(gai_error) { 284 | case EAI_AGAIN: /* The name server returned a temporary failure indication. Try again later. */ 285 | if(d->tries > 0) { 286 | if(!dns_silent) 287 | fprintf(stderr, "%s: DNS: '%s' will be retried: %s\n", PROG, d->hostname, gai_strerror(gai_error)); 288 | 289 | d->tries--; 290 | 291 | dns_lock_requests(); 292 | d->next = dns_requests; 293 | dns_requests = d; 294 | dns_requests_retries++; 295 | dns_replies_found += added; 296 | dns_unlock_requests(); 297 | return; 298 | } 299 | dns_request_done(d, added); 300 | return; 301 | 302 | case EAI_SYSTEM: 303 | fprintf(stderr, "%s: DNS: '%s' system error: %s\n", PROG, d->hostname, strerror(errno)); 304 | dns_request_done(d, added); 305 | return; 306 | 307 | case EAI_SOCKTYPE: /* The requested socket type is not supported. */ 308 | case EAI_SERVICE: /* The requested service is not available for the requested socket type. */ 309 | case EAI_MEMORY: /* Out of memory. */ 310 | case EAI_BADFLAGS: /* hints.ai_flags contains invalid flags; or, hints.ai_flags included AI_CANONNAME and name was NULL. */ 311 | fprintf(stderr, "%s: DNS: '%s' error: %s\n", PROG, d->hostname, gai_strerror(gai_error)); 312 | dns_request_done(d, added); 313 | return; 314 | 315 | case EAI_NONAME: /* The node or service is not known */ 316 | case EAI_FAIL: /* The name server returned a permanent failure indication. */ 317 | case EAI_FAMILY: /* The requested address family is not supported. */ 318 | default: 319 | if(!dns_silent) 320 | fprintf(stderr, "%s: DNS: '%s' failed permanently: %s\n", PROG, d->hostname, gai_strerror(gai_error)); 321 | dns_request_done(d, added); 322 | return; 323 | } 324 | } 325 | 326 | 327 | /* ---------------------------------------------------------------------------- 328 | * dns_request_get() 329 | * 330 | * to be called by a worker thread 331 | * get a request from the requests queue 332 | * 333 | */ 334 | 335 | static DNSREQ *dns_request_get(void) 336 | { 337 | DNSREQ *ret = NULL; 338 | 339 | /* 340 | * if(unlikely(debug)) 341 | * fprintf(stderr, "%s: DNS THREAD waiting for DNS REQUEST\n", PROG); 342 | */ 343 | 344 | while(!ret) { 345 | if(dns_requests) { 346 | dns_lock_requests(); 347 | if(dns_requests) { 348 | ret = dns_requests; 349 | dns_requests = dns_requests->next; 350 | ret->next = NULL; 351 | } 352 | dns_unlock_requests(); 353 | if(ret) continue; 354 | } 355 | 356 | dns_thread_wait_for_requests(); 357 | } 358 | 359 | return ret; 360 | } 361 | 362 | 363 | /* ---------------------------------------------------------------------------- 364 | * dns_thread_resolve() 365 | * 366 | * a pthread worker to get requests and generate replies 367 | * 368 | */ 369 | 370 | static void *dns_thread_resolve(void *ptr) 371 | { 372 | DNSREQ *d; 373 | 374 | if(ptr) { ; } 375 | 376 | /* 377 | * if(unlikely(debug)) 378 | * fprintf(stderr, "%s: DNS THREAD created\n", PROG); 379 | */ 380 | 381 | while((d = dns_request_get())) { 382 | int added = 0; 383 | 384 | /* 385 | * if(unlikely(debug)) 386 | * fprintf(stderr, "%s: DNS THREAD resolving DNS REQUEST for '%s'\n", PROG, d->hostname); 387 | */ 388 | 389 | int r; 390 | struct addrinfo *result, *rp, hints; 391 | 392 | hints.ai_family = AF_INET; 393 | hints.ai_socktype = SOCK_DGRAM; 394 | hints.ai_flags = 0; 395 | hints.ai_protocol = 0; 396 | 397 | r = getaddrinfo(d->hostname, "80", &hints, &result); 398 | if(r != 0) { 399 | dns_request_failed(d, 0, r); 400 | continue; 401 | } 402 | 403 | for (rp = result; rp != NULL; rp = rp->ai_next) { 404 | char host[MAX_INPUT_ELEMENT + 1] = ""; 405 | network_addr_t net; 406 | int err = 0; 407 | DNSREP *p; 408 | 409 | r = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST); 410 | if (r != 0) { 411 | fprintf(stderr, "%s: DNS: '%s' failed to get IP string: %s\n", PROG, d->hostname, gai_strerror(r)); 412 | continue; 413 | } 414 | 415 | net = str2netaddr(host, &err); 416 | if(err) { 417 | fprintf(stderr, "%s: DNS: '%s' cannot parse the IP '%s': %s\n", PROG, d->hostname, host, gai_strerror(r)); 418 | continue; 419 | } 420 | 421 | p = malloc(sizeof(DNSREP)); 422 | if(!p) { 423 | fprintf(stderr, "%s: DNS: out of memory while resolving host '%s'\n", PROG, d->hostname); 424 | continue; 425 | } 426 | 427 | if(unlikely(debug)) { 428 | char buf[IP2STR_MAX_LEN + 1]; 429 | fprintf(stderr, "%s: DNS: '%s' = %s\n", PROG, d->hostname, ip2str_r(buf, net.addr)); 430 | } 431 | 432 | p->ip = net.addr; 433 | dns_lock_replies(); 434 | p->next = dns_replies; 435 | dns_replies = p; 436 | added++; 437 | dns_unlock_replies(); 438 | } 439 | 440 | freeaddrinfo(result); 441 | dns_request_done(d, added); 442 | } 443 | 444 | return NULL; 445 | } 446 | 447 | /* ---------------------------------------------------------------------------- 448 | * dns_process_replies() 449 | * 450 | * dequeue the resolved hostnames by adding them to the ipset 451 | * 452 | */ 453 | 454 | static void dns_process_replies(ipset *ips) 455 | { 456 | if(!dns_replies) return; 457 | 458 | dns_lock_replies(); 459 | while(dns_replies) { 460 | DNSREP *p; 461 | 462 | /* 463 | * if(unlikely(debug)) 464 | * char buf[IP2STR_MAX_LEN + 1]; 465 | * fprintf(stderr, "%s: Got DNS REPLY '%s'\n", PROG, ip2str_r(buf, dns_replies->ip)); 466 | */ 467 | 468 | ipset_add_ip_range(ips, dns_replies->ip, dns_replies->ip); 469 | 470 | p = dns_replies->next; 471 | free(dns_replies); 472 | dns_replies = p; 473 | } 474 | dns_unlock_replies(); 475 | } 476 | 477 | 478 | /* ---------------------------------------------------------------------------- 479 | * dns_request() 480 | * 481 | * attempt to resolv a hostname 482 | * the result (one or more) will be appended to the ipset supplied 483 | * 484 | * this is asynchronous - it will just queue the request and spawn worker 485 | * threads to do the DNS resolution. 486 | * 487 | * the IPs will be added to the ipset, either at the next call to this 488 | * function, or by calling dns_done(). 489 | * 490 | * So, to use it: 491 | * 1. call dns_request() to request dns resolutions (any number) 492 | * 2. call dns_done() when you finish requesting hostnames 493 | * 3. the resolved IPs are in the ipset you supplied 494 | * 495 | * All ipset manipulation is done at this thread, so if control is 496 | * outside the above 2 functions, you are free to do whatever you like 497 | * with the ipset. 498 | * 499 | * Important: you cannot use dns_request() and dns_done() with more 500 | * than 1 ipset at the same time. The resulting IPs will be multiplexed. 501 | * When you call dns_done() on one ipset, you can proceed with the next. 502 | * 503 | */ 504 | 505 | static void dns_request(ipset *ips, char *hostname) 506 | { 507 | DNSREQ *d; 508 | 509 | /* dequeue if possible */ 510 | dns_process_replies(ips); 511 | 512 | /* 513 | * if(unlikely(debug)) 514 | * fprintf(stderr, "%s: Adding DNS REQUEST for '%s'\n", PROG, hostname); 515 | */ 516 | 517 | d = malloc(sizeof(DNSREQ) + strlen(hostname) + 1); 518 | if(!d) goto cleanup; 519 | 520 | strcpy(d->hostname, hostname); 521 | d->tries = 20; 522 | 523 | /* add the request to the queue */ 524 | dns_request_add(d); 525 | 526 | return; 527 | 528 | cleanup: 529 | fprintf(stderr, "%s: out of memory, while trying to resolv '%s'\n", PROG, hostname); 530 | } 531 | 532 | 533 | /* ---------------------------------------------------------------------------- 534 | * dns_done() 535 | * 536 | * wait for the DNS requests made to finish. 537 | * 538 | */ 539 | 540 | static void dns_done(ipset *ips) 541 | { 542 | unsigned long dots = 40, shown = 0, should_show = 0; 543 | 544 | if(ips) { ; } 545 | 546 | if(!dns_requests_made) return; 547 | 548 | while(dns_requests_pending) { 549 | if(unlikely(debug)) 550 | fprintf(stderr, "%s: DNS: waiting %lu DNS resolutions to finish...\n", PROG, dns_requests_pending); 551 | else if(dns_progress) { 552 | should_show = dots * dns_requests_finished / dns_requests_made; 553 | for(; shown < should_show; shown++) { 554 | if(!(shown % 10)) fprintf(stderr, "%lu%%", shown * 100 / dots); 555 | else fprintf(stderr, "."); 556 | } 557 | } 558 | 559 | dns_process_replies(ips); 560 | 561 | if(dns_requests_pending) { 562 | dns_signal_threads(); 563 | sleep(1); 564 | } 565 | } 566 | dns_process_replies(ips); 567 | 568 | if(unlikely(debug)) 569 | fprintf(stderr, "%s: DNS: made %lu DNS requests, failed %lu, retries: %lu, IPs got %lu, threads used %d of %d\n", PROG, dns_requests_made, dns_replies_failed, dns_requests_retries, dns_replies_found, dns_threads, dns_threads_max); 570 | else if(dns_progress) { 571 | for(; shown <= dots; shown++) { 572 | if(!(shown % 10)) fprintf(stderr, "%lu%%", shown * 100 / dots); 573 | else fprintf(stderr, "."); 574 | } 575 | fprintf(stderr, "\n"); 576 | } 577 | } 578 | 579 | /* ---------------------------------------------------------------------------- 580 | * ipset_load() 581 | * 582 | * loads a file and stores all entries it finds to a new ipset it creates 583 | * if the filename is NULL, stdin is used 584 | * 585 | * the result is not optimized. To optimize it call ipset_optimize(). 586 | * 587 | */ 588 | 589 | ipset *ipset_load(const char *filename) { 590 | FILE *fp = stdin; 591 | int lineid = 0; 592 | char line[MAX_LINE + 1], ipstr[MAX_INPUT_ELEMENT + 1], ipstr2[MAX_INPUT_ELEMENT + 1]; 593 | ipset *ips = ipset_create((filename && *filename)?filename:"stdin", 0); 594 | 595 | if(unlikely(!ips)) return NULL; 596 | 597 | if (likely(filename && *filename)) { 598 | fp = fopen(filename, "r"); 599 | if (unlikely(!fp)) { 600 | fprintf(stderr, "%s: %s - %s\n", PROG, filename, strerror(errno)); 601 | ipset_free(ips); 602 | return NULL; 603 | } 604 | } 605 | 606 | /* load it */ 607 | if(unlikely(debug)) fprintf(stderr, "%s: Loading from %s\n", PROG, ips->filename); 608 | 609 | /* it will be removed, if the loaded ipset is not optimized on disk */ 610 | ips->flags |= IPSET_FLAG_OPTIMIZED; 611 | 612 | if(!fgets(line, MAX_LINE, fp)) { 613 | if(likely(fp != stdin)) 614 | fclose(fp); 615 | 616 | /* For normal files, an empty file is valid too (return empty ipset) */ 617 | if(unlikely(debug)) 618 | fprintf(stderr, "%s: %s is empty\n", PROG, filename && *filename?filename:"stdin"); 619 | 620 | return ips; 621 | } 622 | 623 | if(unlikely(!strcmp(line, BINARY_HEADER_V10))) { 624 | if(ipset_load_binary_v10(fp, ips, 1)) { 625 | fprintf(stderr, "%s: Cannot fast load %s\n", PROG, filename); 626 | ipset_free(ips); 627 | ips = NULL; 628 | } 629 | 630 | if(likely(fp != stdin)) fclose(fp); 631 | if(unlikely(debug)) if(ips) fprintf(stderr, "%s: Binary loaded %s %s\n", PROG, (ips->flags & IPSET_FLAG_OPTIMIZED)?"optimized":"non-optimized", ips->filename); 632 | 633 | return ips; 634 | } 635 | 636 | do { 637 | lineid++; 638 | 639 | switch(parse_line(line, lineid, ipstr, ipstr2, MAX_INPUT_ELEMENT)) { 640 | case LINE_IS_INVALID: 641 | /* cannot read line */ 642 | fprintf(stderr, "%s: Cannot understand line No %d from %s: %s\n", PROG, lineid, ips->filename, line); 643 | break; 644 | 645 | case LINE_IS_EMPTY: 646 | /* nothing on this line */ 647 | break; 648 | 649 | case LINE_HAS_1_IP: 650 | /* 1 IP on this line */ 651 | if(unlikely(!ipset_add_ipstr(ips, ipstr))) 652 | fprintf(stderr, "%s: Cannot understand line No %d from %s: %s\n", PROG, lineid, ips->filename, line); 653 | break; 654 | 655 | case LINE_HAS_2_IPS: 656 | /* 2 IPs in range on this line */ 657 | { 658 | int err = 0; 659 | in_addr_t lo, hi; 660 | network_addr_t netaddr1, netaddr2; 661 | netaddr1 = str2netaddr(ipstr, &err); 662 | if(likely(!err)) netaddr2 = str2netaddr(ipstr2, &err); 663 | if(unlikely(err)) { 664 | fprintf(stderr, "%s: Cannot understand line No %d from %s: %s\n", PROG, lineid, ips->filename, line); 665 | continue; 666 | } 667 | 668 | lo = (netaddr1.addr < netaddr2.addr)?netaddr1.addr:netaddr2.addr; 669 | hi = (netaddr1.broadcast > netaddr2.broadcast)?netaddr1.broadcast:netaddr2.broadcast; 670 | ipset_add_ip_range(ips, lo, hi); 671 | } 672 | break; 673 | 674 | case LINE_HAS_1_HOSTNAME: 675 | if(unlikely(debug)) 676 | fprintf(stderr, "%s: DNS resolution for hostname '%s' from line %d of file %s.\n", PROG, ipstr, lineid, ips->filename); 677 | 678 | /* resolve_hostname(ips, ipstr); */ 679 | dns_request(ips, ipstr); 680 | break; 681 | 682 | default: 683 | fprintf(stderr, "%s: Cannot understand result code. This is an internal error.\n", PROG); 684 | exit(1); 685 | } 686 | } while(likely(ips && fgets(line, MAX_LINE, fp))); 687 | 688 | if(likely(fp != stdin)) fclose(fp); 689 | 690 | dns_done(ips); 691 | 692 | if(unlikely(!ips)) return NULL; 693 | 694 | if(unlikely(debug)) fprintf(stderr, "%s: Loaded %s %s\n", PROG, (ips->flags & IPSET_FLAG_OPTIMIZED)?"optimized":"non-optimized", ips->filename); 695 | 696 | /* 697 | * if(unlikely(!ips->entries)) { 698 | * free(ips); 699 | * return NULL; 700 | * } 701 | */ 702 | 703 | return ips; 704 | } 705 | 706 | --------------------------------------------------------------------------------