4
4
* Copyright (C) 2003 Gabriel L. Somlo
5
5
*/
6
6
#include <iprange.h>
7
+ #include <sys/stat.h>
8
+ #include <dirent.h>
7
9
8
10
char * PROG ;
9
11
int debug ;
@@ -236,6 +238,8 @@ static void usage(const char *me) {
236
238
"Other options:\n"
237
239
" --has-compare\n"
238
240
" --has-reduce\n"
241
+ " --has-filelist-loading\n"
242
+ " --has-directory-loading\n"
239
243
" Exits with 0,\n"
240
244
" other versions of iprange will exit with 1.\n"
241
245
" Use this option in scripts to find if this\n"
@@ -262,6 +266,23 @@ static void usage(const char *me) {
262
266
" to change its name in the CSV output.\n"
263
267
" If no filename is given, stdin is assumed.\n"
264
268
"\n"
269
+ " > @filename\n"
270
+ " A file list containing filenames, one per line.\n"
271
+ " Each file in the list is loaded as an individual ipset.\n"
272
+ " Comments starting with # or ; are ignored.\n"
273
+ " Empty lines are ignored.\n"
274
+ " Multiple @filename parameters can be used.\n"
275
+ " @filename works with all modes and respects the positional\n"
276
+ " nature of the parameters.\n"
277
+ "\n"
278
+ " > @directory\n"
279
+ " If @filename refers to a directory, all files in that directory\n"
280
+ " will be loaded, each as an individual ipset.\n"
281
+ " Subdirectories are ignored.\n"
282
+ " Multiple @directory parameters can be used.\n"
283
+ " @directory works with all modes and respects the positional\n"
284
+ " nature of the parameters.\n"
285
+ "\n"
265
286
" Files may contain any or all of the following:\n"
266
287
" (1) comments starting with hashes (#) or semicolons (;);\n"
267
288
" (2) one IP per line (without mask);\n"
@@ -537,43 +558,226 @@ int main(int argc, char **argv) {
537
558
fprintf (stderr , "yes, compare and reduce is present.\n" );
538
559
exit (0 );
539
560
}
561
+ else if (!strcmp (argv [i ], "--has-filelist-loading" )
562
+ || !strcmp (argv [i ], "--has-directory-loading" )) {
563
+ fprintf (stderr , "yes, @filename and @directory support is present.\n" );
564
+ exit (0 );
565
+ }
540
566
else {
541
- if (!strcmp (argv [i ], "-" ))
567
+ if (!strcmp (argv [i ], "-" )) {
542
568
ips = ipset_load (NULL );
543
- else
544
- ips = ipset_load (argv [i ]);
545
-
546
- if (!ips ) {
547
- fprintf (stderr , "%s: Cannot load ipset: %s\n" , PROG , argv [i ]);
548
- exit (1 );
569
+
570
+ if (!ips ) {
571
+ fprintf (stderr , "%s: Cannot load ipset from stdin\n" , PROG );
572
+ exit (1 );
573
+ }
574
+
575
+ if (read_second ) {
576
+ ips -> next = second ;
577
+ second = ips ;
578
+ if (ips -> next ) ips -> next -> prev = ips ;
579
+ }
580
+ else {
581
+ if (!first ) first = ips ;
582
+ ips -> next = root ;
583
+ root = ips ;
584
+ if (ips -> next ) ips -> next -> prev = ips ;
585
+ }
549
586
}
550
-
551
- if (read_second ) {
552
- ips -> next = second ;
553
- second = ips ;
554
- if (ips -> next ) ips -> next -> prev = ips ;
587
+ else if (argv [i ][0 ] == '@' ) {
588
+ /* Handle @filename as a file list or directory */
589
+ const char * listname = argv [i ] + 1 ; /* Skip the @ character */
590
+ struct stat st ;
591
+
592
+ if (stat (listname , & st ) != 0 ) {
593
+ fprintf (stderr , "%s: Cannot access %s: %s\n" , PROG , listname , strerror (errno ));
594
+ exit (1 );
595
+ }
596
+
597
+ /* Check if it's a directory */
598
+ if (S_ISDIR (st .st_mode )) {
599
+ DIR * dir ;
600
+ struct dirent * entry ;
601
+
602
+ if (unlikely (debug ))
603
+ fprintf (stderr , "%s: Loading files from directory %s\n" , PROG , listname );
604
+
605
+ dir = opendir (listname );
606
+ if (!dir ) {
607
+ fprintf (stderr , "%s: Cannot open directory: %s - %s\n" , PROG , listname , strerror (errno ));
608
+ exit (1 );
609
+ }
610
+
611
+ /* Flag to track if we loaded any files */
612
+ int files_loaded = 0 ;
613
+
614
+ /* Read all files from the directory */
615
+ while ((entry = readdir (dir ))) {
616
+ /* Skip "." and ".." */
617
+ if (!strcmp (entry -> d_name , "." ) || !strcmp (entry -> d_name , ".." ))
618
+ continue ;
619
+
620
+ /* Create full path */
621
+ char filepath [FILENAME_MAX + 1 ];
622
+ snprintf (filepath , FILENAME_MAX , "%s/%s" , listname , entry -> d_name );
623
+
624
+ /* Skip subdirectories */
625
+ if (stat (filepath , & st ) != 0 || S_ISDIR (st .st_mode ))
626
+ continue ;
627
+
628
+ if (unlikely (debug ))
629
+ fprintf (stderr , "%s: Loading file %s from directory %s\n" , PROG , entry -> d_name , listname );
630
+
631
+ /* Load the file as an independent ipset */
632
+ ips = ipset_load (filepath );
633
+ if (!ips ) {
634
+ fprintf (stderr , "%s: Cannot load file %s from directory %s\n" ,
635
+ PROG , filepath , listname );
636
+ continue ;
637
+ }
638
+
639
+ files_loaded = 1 ;
640
+
641
+ /* Add the ipset to the appropriate chain */
642
+ if (read_second ) {
643
+ ips -> next = second ;
644
+ second = ips ;
645
+ if (ips -> next ) ips -> next -> prev = ips ;
646
+ }
647
+ else {
648
+ if (!first ) first = ips ;
649
+ ips -> next = root ;
650
+ root = ips ;
651
+ if (ips -> next ) ips -> next -> prev = ips ;
652
+ }
653
+ }
654
+
655
+ closedir (dir );
656
+
657
+ /* Handle empty directory case */
658
+ if (!files_loaded ) {
659
+ if (unlikely (debug ))
660
+ fprintf (stderr , "%s: Directory %s is empty or contains no valid files\n" , PROG , listname );
661
+
662
+ /* Report an error for empty directory */
663
+ fprintf (stderr , "%s: No valid files found in directory: %s\n" , PROG , listname );
664
+ }
665
+ }
666
+ else {
667
+ /* Handle as a file list */
668
+ FILE * fp ;
669
+ char line [MAX_LINE + 1 ];
670
+ int lineid = 0 ;
671
+
672
+ if (unlikely (debug ))
673
+ fprintf (stderr , "%s: Loading files from list %s\n" , PROG , listname );
674
+
675
+ fp = fopen (listname , "r" );
676
+ if (!fp ) {
677
+ fprintf (stderr , "%s: Cannot open file list: %s - %s\n" , PROG , listname , strerror (errno ));
678
+ exit (1 );
679
+ }
680
+
681
+ /* Flag to track if we loaded any files */
682
+ int files_loaded = 0 ;
683
+
684
+ /* Read each line and load the corresponding file */
685
+ while (fgets (line , MAX_LINE , fp )) {
686
+ lineid ++ ;
687
+
688
+ /* Skip empty lines and comments */
689
+ char * s = line ;
690
+ while (* s && (* s == ' ' || * s == '\t' )) s ++ ;
691
+ if (* s == '\n' || * s == '\r' || * s == '\0' || * s == '#' || * s == ';' )
692
+ continue ;
693
+
694
+ /* Remove trailing newlines/whitespace */
695
+ char * end = s + strlen (s ) - 1 ;
696
+ while (end > s && (* end == '\n' || * end == '\r' || * end == ' ' || * end == '\t' ))
697
+ * end -- = '\0' ;
698
+
699
+ if (unlikely (debug ))
700
+ fprintf (stderr , "%s: Loading file %s from list (line %d)\n" , PROG , s , lineid );
701
+
702
+ /* Load the file as an independent ipset */
703
+ ips = ipset_load (s );
704
+ if (!ips ) {
705
+ fprintf (stderr , "%s: Cannot load file %s from list %s (line %d)\n" ,
706
+ PROG , s , listname , lineid );
707
+ continue ;
708
+ }
709
+
710
+ files_loaded = 1 ;
711
+
712
+ /* Add the ipset to the appropriate chain */
713
+ if (read_second ) {
714
+ ips -> next = second ;
715
+ second = ips ;
716
+ if (ips -> next ) ips -> next -> prev = ips ;
717
+ }
718
+ else {
719
+ if (!first ) first = ips ;
720
+ ips -> next = root ;
721
+ root = ips ;
722
+ if (ips -> next ) ips -> next -> prev = ips ;
723
+ }
724
+ }
725
+
726
+ fclose (fp );
727
+
728
+ /* Handle empty file list case */
729
+ if (!files_loaded ) {
730
+ if (unlikely (debug ))
731
+ fprintf (stderr , "%s: File list %s is empty or contains no valid entries\n" , PROG , listname );
732
+
733
+ /* Report an error for empty file list */
734
+ fprintf (stderr , "%s: No valid files found in file list: %s\n" , PROG , listname );
735
+ }
736
+ }
555
737
}
556
738
else {
557
- if (!first ) first = ips ;
558
- ips -> next = root ;
559
- root = ips ;
560
- if (ips -> next ) ips -> next -> prev = ips ;
739
+ ips = ipset_load (argv [i ]);
740
+
741
+ if (!ips ) {
742
+ fprintf (stderr , "%s: Cannot load ipset: %s\n" , PROG , argv [i ]);
743
+ continue ; /* Continue with other arguments instead of exiting */
744
+ }
745
+
746
+ if (read_second ) {
747
+ ips -> next = second ;
748
+ second = ips ;
749
+ if (ips -> next ) ips -> next -> prev = ips ;
750
+ }
751
+ else {
752
+ if (!first ) first = ips ;
753
+ ips -> next = root ;
754
+ root = ips ;
755
+ if (ips -> next ) ips -> next -> prev = ips ;
756
+ }
561
757
}
562
758
}
563
759
}
564
760
565
761
/*
566
762
* if no ipset was given on the command line
567
- * assume stdin
763
+ * assume stdin, but only if no other filenames were specified
568
764
*/
569
765
570
- if (!root ) {
766
+ if (!root && argc <= 1 ) {
767
+ if (unlikely (debug ))
768
+ fprintf (stderr , "%s: No inputs provided, reading from stdin\n" , PROG );
769
+
571
770
first = root = ipset_load (NULL );
572
771
if (!root ) {
573
772
fprintf (stderr , "%s: No ipsets to merge.\n" , PROG );
574
773
exit (1 );
575
774
}
576
775
}
776
+ else if (!root ) {
777
+ /* We had parameters but still ended up with no valid ipsets */
778
+ fprintf (stderr , "%s: No valid ipsets to merge from the provided inputs.\n" , PROG );
779
+ exit (1 );
780
+ }
577
781
578
782
gettimeofday (& load_dt , NULL );
579
783
0 commit comments