33import os
44from collections import defaultdict
55
6+ # RFC1918 networks
7+ RFC1918_NETWORKS = [
8+ ipaddress .ip_network ("10.0.0.0/8" ),
9+ ipaddress .ip_network ("172.16.0.0/12" ),
10+ ipaddress .ip_network ("192.168.0.0/16" ),
11+ ]
12+
13+ def is_rfc1918 (ip_str ):
14+ try :
15+ ip = ipaddress .ip_address (ip_str )
16+ return any (ip in net for net in RFC1918_NETWORKS )
17+ except ValueError :
18+ return False
19+
620def extract_unique_hosts_from_file (file_path ):
721 unique_hosts = set ()
822
@@ -37,6 +51,8 @@ def main():
3751 parser .add_argument ("-d" , "--directory" , help = "Directory of files to process." )
3852 parser .add_argument ("-D" , "--duplicates" , action = "store_true" ,
3953 help = "When processing a directory, list hosts that appear in more than one file and which files they appear in." )
54+ parser .add_argument ("-r" , "--rfc1918" , action = "store_true" ,
55+ help = "Optionally show how many hosts are in RFC1918 space vs not (per-file and totals)." )
4056 args = parser .parse_args ()
4157
4258 if args .directory :
@@ -52,28 +68,49 @@ def main():
5268 grand_total_hosts .update (file_hosts )
5369 print (f"\n Total unique hosts across all files: { len (grand_total_hosts )} " )
5470
55- if args .duplicates :
56- # Build host -> set(files) mapping
57- host_to_files = defaultdict (set )
58- for fname , hosts in file_hosts_map .items ():
59- for h in hosts :
60- host_to_files [h ].add (fname )
71+ # Build host -> set(files) mapping once for both -D output and the duplicate note
72+ host_to_files = defaultdict (set )
73+ for fname , hosts in file_hosts_map .items ():
74+ for h in hosts :
75+ host_to_files [h ].add (fname )
76+ duplicates_map = {host : sorted (list (files )) for host , files in host_to_files .items () if len (files ) > 1 }
77+ duplicate_count = len (duplicates_map )
6178
62- # Find hosts present in more than one file
63- duplicates = {host : sorted (list (files )) for host , files in host_to_files .items () if len (files ) > 1 }
79+ # If -D not used, but duplicates exist, print a concise note that duplicates were found and are only counted once
80+ if not args .duplicates and duplicate_count > 0 :
81+ print (f"\n Note: { duplicate_count } host(s) were found in more than one file but are only counted once in the total unique host count. Use -D to list them." )
6482
65- if not duplicates :
83+ if args .rfc1918 :
84+ print ("\n RFC1918 breakdown per file:" )
85+ for fname in sorted (file_hosts_map .keys ()):
86+ hosts = file_hosts_map [fname ]
87+ rfc_count = sum (1 for h in hosts if is_rfc1918 (h ))
88+ non_rfc = len (hosts ) - rfc_count
89+ print (f"{ fname } : { rfc_count } RFC1918, { non_rfc } non-RFC1918" )
90+
91+ total_rfc = sum (1 for h in grand_total_hosts if is_rfc1918 (h ))
92+ total_non_rfc = len (grand_total_hosts ) - total_rfc
93+ print (f"\n Totals across all files: { total_rfc } RFC1918, { total_non_rfc } non-RFC1918" )
94+
95+ if args .duplicates :
96+ # Use the mapping we already built to list duplicates
97+ if not duplicates_map :
6698 print ("\n No duplicate hosts found between files." )
6799 else :
68- print (f"\n Duplicate hosts found between files: { len (duplicates )} hosts\n " )
69- for host in sorted (duplicates .keys (), key = lambda x : tuple (int (p ) for p in x .split ('.' ))):
70- files_list = ", " .join (duplicates [host ])
100+ print (f"\n Duplicate hosts found between files: { len (duplicates_map )} hosts\n " )
101+ for host in sorted (duplicates_map .keys (), key = lambda x : tuple (int (p ) for p in x .split ('.' ))):
102+ files_list = ", " .join (duplicates_map [host ])
71103 print (f"{ host } : { files_list } " )
72104
73105 elif args .file :
74106 unique_hosts = extract_unique_hosts_from_file (args .file )
75107 print (f"\n Total unique hosts in { args .file } (excluding network and broadcast): { len (unique_hosts )} " )
76108
109+ if args .rfc1918 :
110+ rfc_count = sum (1 for h in unique_hosts if is_rfc1918 (h ))
111+ non_rfc = len (unique_hosts ) - rfc_count
112+ print (f"{ args .file } : { rfc_count } RFC1918, { non_rfc } non-RFC1918" )
113+
77114 else :
78115 print ("ERROR: Either a file or a directory must be specified." )
79116 parser .print_help ()
0 commit comments