1#!/usr/bin/env perl 2 3use strict; 4use warnings; 5use Getopt::Long; 6use Net::Connection::ncnetstat; 7 8sub version{ 9 print "ncnetstat v. 0.3.1\n"; 10} 11 12sub help{ 13 print ' 14-a Show all connections. 15--drp Do not resolve port names. 16--dump Show the Net::Connection::Match filter and exit. 17-i Invert the sort. 18-l Show the listening ports. 19-n Do not resolve the PTRs. 20--nc Do not use colors. 21--pct Show memory and CPU usage percent. 22-S <sort> The Net::Connection::Sort to use. 23-t Show only TCP connections. 24-u Show only UDP connections. 25-W Show the wchan. 26 27-c <CIDRs> A comma seperated list of CIDRs to search for. 28--ci Invert the CIDR search. 29 30-C Show the command to the first space. 31--Cl Show the whole command. 32 33--cmd <cmds> A comma seperated list of commands to search for. 34--cmdi Invert the command search. 35 36--cpu <pct> Show connections belonging to procs matching this CPU usage percent. 37--cpui Invert the CPU search. 38 39--mem <pct> Show connections belonging to procs matching this memory usage percent. 40--memi Invert the memory usage search. 41 42-p <ports> A comma seperated list of ports to search for. 43--pi Invert the port search. 44 45-P <protos> A comma seperated list of protocols to search for. 46--Pi Invert your protocol search. 47 48--pid <pids> A comma separated list of PIDs to search for. 49--pidi Invert the pid search. 50 51--ptrr <rgx> A comma seperated list of regex to use for a PTR search. 52--ptrri Invert the RegexPTR search. 53--lptrr <rgx> A comma seperated list of regex to use for a local PTR search. 54--lptrri Invert the local RegexPTR search. 55--rptrr <rgx> A comma seperated list of regex to use for a remote PTR search. 56--rptrri Invert the remote RegexPTR search. 57 58--ptr <PTRs> A comma seperated list of PTRs to search for. 59--ptri Invert the PTR search. 60--lptr <PTRs> A comma seperated list of local PTRs to search for. 61--lptri Invert the local PTR search. 62--rptr <PTRs> A comma seperated list of remote PTRs to search for. 63--rptri Invert the remote PTR search. 64 65-s <states> A comma seperated list of states to search for. 66--si Invert the state search. 67 68-U <users> A comma seperated list of usernames to search for. 69--Ui Invert the username search. 70 71--uid <uids> A comma separated list of UIDs to search for. 72--uidi Invert the UID search. 73 74-w <rgx> A comma separated list of regexp to use for matching wchan values. 75--wi Invert the wchan search. 76 77The default available sort methods are as below. 78host_f foreign host 79host_fl foreign host, local host 80host_l local host 81host_lf local host, foreign host 82pid process ID 83port_f foreign port, numerically 84port_fa foreign port, alphabetically 85port_l local port, numerically 86port_la local port, alphabetically 87proto protocol 88ptr_f foreign PTR 89ptr_l local PTR 90state state 91uid user ID 92user username 93 94For CPU, memory, PID, and UID searches, the equalities below can be 95used, by directly prepending them to the number. 96< 97<= 98> 99>= 100'; 101} 102 103# command line option holders 104my $tcp=0; 105my $udp=0; 106my $help=0; 107my $version=0; 108my $dont_resolve_ports=0; 109my $sort='host_fl'; 110my $cidr_string; 111my $lcidr_string; 112my $rcidr_string; 113my $ports_string; 114my $states_string; 115my $protocols_string; 116my $all=0; 117my $listening; 118my $invert=0; 119my $ptrs_string; 120my $ptrrs_string; 121my $rptrs_string; 122my $lptrs_string; 123my $rptrrs_string; 124my $lptrrs_string; 125my $ports_invert; 126my $rptrs_invert; 127my $lptrs_invert; 128my $rptrrs_invert; 129my $lptrrs_invert; 130my $ptrs_invert; 131my $ptrrs_invert; 132my $cidr_invert; 133my $lcidr_invert; 134my $rcidr_invert; 135my $states_invert; 136my $protocols_invert; 137my $no_color=0; 138my $no_use_ptr=0; 139my $ptr=1; 140my $command=0; 141my $command_long=0; 142my $uid_string; 143my $uid_invert=0; 144my $users_string; 145my $users_invert=0; 146my $pids_string; 147my $pids_invert=0; 148my $commands_string; 149my $commands_invert; 150my $wchan_string; 151my $wchan_invert=0; 152my $wchan_show=0; 153my $pct_show=0; 154my $cpu_string; 155my $cpu_invert=0; 156my $mem_string; 157my $mem_invert=0; 158my $dump=0; 159 160# get the commandline options 161Getopt::Long::Configure ('no_ignore_case'); 162Getopt::Long::Configure ('bundling'); 163GetOptions( 164 't'=>\$tcp, 165 'u'=>\$udp, 166 'version' => \$version, 167 'v' => \$version, 168 'help' => \$help, 169 'h' => \$help, 170 'a' => \$all, 171 'l' => \$listening, 172 'i' => \$invert, 173 'drp' => \$dont_resolve_ports, 174 'c=s' => \$cidr_string, 175 'ci'=> \$cidr_invert, 176 'lc=s' => \$lcidr_string, 177 'lci'=> \$lcidr_invert, 178 'rc=s' => \$rcidr_string, 179 'rci'=> \$rcidr_invert, 180 'S=s' => \$sort, 181 'p=s' => \$ports_string, 182 'pi' => \$ports_invert, 183 's=s' => \$states_string, 184 'si' => \$states_invert, 185 'P=s' => \$protocols_string, 186 'Pi' => \$protocols_invert, 187 'pid=s' => \$pids_string, 188 'pidi' => \$pids_invert, 189 'ptrr=s' => \$ptrrs_string, 190 'ptr=s' => \$ptrs_string, 191 'ptri' => \$ptrs_invert, 192 'ptrri' => \$ptrrs_invert, 193 'rptrr=s' => \$rptrrs_string, 194 'rptr=s' => \$rptrs_string, 195 'rptrri' => \$rptrrs_invert, 196 'rptri' => \$rptrs_invert, 197 'lptrr=s' => \$lptrrs_string, 198 'lptr=s' => \$lptrs_string, 199 'lptri' => \$lptrs_invert, 200 'lptrri' => \$lptrrs_invert, 201 'nc' => \$no_color, 202 'n' => \$no_use_ptr, 203 'C' => \$command, 204 'Cl' => \$command_long, 205 'uid=s' => \$uid_string, 206 'uidi'=> \$uid_invert, 207 'U=s' => \$users_string, 208 'Ui' => \$users_invert, 209 'cmd=s' => \$commands_string, 210 'cmdi' => \$commands_invert, 211 'w=s' => \$wchan_string, 212 'wi' => \$wchan_invert, 213 'W' => \$wchan_show, 214 'pct' => \$pct_show, 215 'cpu=s' => \$cpu_string, 216 'cpui' => \$cpu_invert, 217 'mem=s' => \$mem_string, 218 'memi' => \$mem_invert, 219 'dump' => \$dump, 220 ); 221 222my @filters; 223 224# print the version info if requested 225if ( $version ){ 226 &version; 227 exit; 228} 229 230if ( $help ){ 231 &version; 232 &help; 233 exit; 234} 235 236# 237# XOR various command line options 238# 239if (defined( $ENV{'ncnetstat_pct'} )) { 240 $pct_show= $pct_show ^ $ENV{'ncnetstat_pct'}; 241} 242if (defined( $ENV{'ncnetstat_C'} )) { 243 $command = $command ^ $ENV{'ncnetstat_C'}; 244} 245if (defined( $ENV{'ncnetstat_Cl'} )) { 246 $command_long = $command_long ^ $ENV{'ncnetstat_Cl'}; 247} 248if (defined( $ENV{'ncnetstat_W'} )) { 249 $wchan_show = $wchan_show ^ $ENV{'ncnetstat_W'}; 250} 251 252 253# add the filters for the -l and -a option 254if ( 255 ( ! $all ) && 256 ( ! $listening ) 257 ){ 258 # If -a is not given, we don't want the listen ports 259 push( @filters, 260 { 261 type=>'Ports', 262 invert=>1, 263 args=>{ 264 ports=>[ 265 '*', 266 ], 267 } 268 }, 269 { 270 type=>'States', 271 invert=>1, 272 args=>{ 273 states=>['LISTEN'] 274 } 275 } 276 ); 277}elsif( 278 $listening && 279 ( ! $all ) 280 ){ 281 # if -l we only want the ports in the LISTEN state 282 push( @filters, { 283 type=>'States', 284 invert=>0, 285 args=>{ 286 states=>['LISTEN'] 287 } 288 } 289 ); 290} 291 292# 293# Handle stats search 294# 295if ( defined( $states_string ) ){ 296 my @states=split(/\,/, $states_string ); 297 push( @filters, { 298 type=>'States', 299 invert=>$states_invert, 300 args=>{ 301 states=>\@states, 302 }, 303 } 304 ); 305} 306 307# 308# handles the protocol search 309# 310if ( defined( $protocols_string ) ){ 311 my @protos=split(/\,/, $protocols_string ); 312 push( @filters, { 313 type=>'Protos', 314 invert=>$protocols_invert, 315 args=>{ 316 protos=>\@protos, 317 }, 318 } 319 ); 320} 321 322# 323# Handle CIDR searches 324# 325if ( defined( $cidr_string ) ){ 326 my @cidrs=split(/\,/, $cidr_string ); 327 push( @filters, { 328 type=>'CIDR', 329 invert=>$cidr_invert, 330 args=>{ 331 cidrs=>\@cidrs, 332 }, 333 } 334 ); 335} 336 337# 338# Handle UID searches 339# 340if ( defined( $uid_string ) ){ 341 my @uids=split(/\,/, $uid_string ); 342 push( @filters, { 343 type=>'UID', 344 invert=>$uid_invert, 345 args=>{ 346 uids=>\@uids, 347 }, 348 } 349 ); 350} 351 352# 353# Handle username searches 354# 355if ( defined( $users_string ) ){ 356 my @users=split(/\,/, $users_string ); 357 push( @filters, { 358 type=>'Username', 359 invert=>$users_invert, 360 args=>{ 361 usernames=>\@users, 362 }, 363 } 364 ); 365} 366 367# 368# Handle local CIDR searches 369# 370#if ( defined( $lcidr_string ) ){ 371# my @cidrs=split(/\,/, $lcidr_string ); 372# push( @filters, { 373# type=>'CIDR', 374# invert=>$cidr_invert, 375# args=>{ 376# lcidrs=>\@cidrs, 377# }, 378# } 379# ); 380#} 381 382# 383# Handle local CIDR searches 384# 385#if ( defined( $fcidr_string ) ){ 386# my @cidrs=split(/\,/, $fcidr_string ); 387# push( @filters, { 388# type=>'CIDR', 389# invert=>$cidr_invert, 390# args=>{ 391# fcidrs=>\@cidrs, 392# }, 393# } 394# ); 395#} 396 397# 398# Handle the ports search. 399# 400if ( defined( $ports_string ) ){ 401 my @ports=split(/\,/, $ports_string); 402 push( @filters, { 403 type=>'Ports', 404 invert=>$ports_invert, 405 args=>{ 406 ports=>\@ports, 407 }, 408 } 409 ); 410} 411 412# 413# Handle the regex PTR searches 414# 415if ( defined( $ptrrs_string ) ){ 416 my @ptrs=split(/\,/, $ptrrs_string); 417 push( @filters, { 418 type=>'RegexPTR', 419 invert=>$ptrrs_invert, 420 args=>{ 421 ptrs=>\@ptrs, 422 }, 423 } 424 ); 425} 426 427# 428# Handle the regex local PTR searches 429# 430if ( defined( $lptrrs_string ) ){ 431 my @ptrs=split(/\,/, $lptrrs_string); 432 push( @filters, { 433 type=>'RegexPTR', 434 invert=>$lptrrs_invert, 435 args=>{ 436 lptrs=>\@ptrs, 437 }, 438 } 439 ); 440} 441 442# 443# Handle the regex remote PTR searches 444# 445if ( defined( $rptrrs_string ) ){ 446 my @ptrs=split(/\,/, $rptrrs_string); 447 push( @filters, { 448 type=>'RegexPTR', 449 invert=>$rptrrs_invert, 450 args=>{ 451 fptrs=>\@ptrs, 452 }, 453 } 454 ); 455 456} 457 458# 459# Handle the ptrs searches 460# 461if ( defined( $ptrs_string ) ){ 462 my @ptrs=split(/\,/, $ptrs_string); 463 push( @filters, { 464 type=>'PTR', 465 invert=>$rptrs_invert, 466 args=>{ 467 ptrs=>\@ptrs, 468 }, 469 } 470 ); 471} 472 473# 474# Handle the remote ptrs searches 475# 476if ( defined( $rptrs_string ) ){ 477 my @ptrs=split(/\,/, $rptrs_string); 478 push( @filters, { 479 type=>'PTR', 480 invert=>$rptrs_invert, 481 args=>{ 482 fptrs=>\@ptrs, 483 }, 484 } 485 ); 486} 487 488# 489# Handle the local ptrs searches 490# 491if ( defined( $lptrs_string ) ){ 492 my @ptrs=split(/\,/, $lptrs_string); 493 push( @filters, { 494 type=>'PTR', 495 invert=>$lptrs_invert, 496 args=>{ 497 lptrs=>\@ptrs, 498 }, 499 } 500 ); 501} 502 503# 504# Handle the PID searches 505# 506if ( defined( $pids_string ) ){ 507 my @pids=split(/\,/, $pids_string); 508 push( @filters, { 509 type=>'PID', 510 invert=>$pids_invert, 511 args=>{ 512 pids=>\@pids, 513 }, 514 } 515 ); 516} 517 518# 519# Handle the command searches 520# 521if ( defined( $commands_string ) ){ 522 my @commands=split(/\,/, $commands_string); 523 push( @filters, { 524 type=>'Command', 525 invert=>$commands_invert, 526 args=>{ 527 commands=>\@commands, 528 }, 529 } 530 ); 531} 532 533# 534# Handle the wait channel searches 535# 536if ( defined( $wchan_string ) ){ 537 my @wchans=split(/\,/, $wchan_string); 538 push( @filters, { 539 type=>'WChan', 540 invert=>$wchan_invert, 541 args=>{ 542 wchans=>\@wchans, 543 }, 544 } 545 ); 546} 547 548# 549# Handle the pctcpu searches 550# 551if ( defined( $cpu_string ) ){ 552 my @cpus=split(/\,/, $cpu_string); 553 push( @filters, { 554 type=>'PctCPU', 555 invert=>$cpu_invert, 556 args=>{ 557 pctcpus=>\@cpus, 558 }, 559 } 560 ); 561} 562 563# 564# Handle the pctmem searches 565# 566if ( defined( $mem_string ) ){ 567 my @mems=split(/\,/, $mem_string); 568 push( @filters, { 569 type=>'PctMem', 570 invert=>$mem_invert, 571 args=>{ 572 pctmems=>\@mems, 573 }, 574 } 575 ); 576} 577 578# handle the -t -u options 579# only add a filter if one is specified... 580# adding both is just pointless 581if ( 582 ( ! $tcp ) && 583 $udp 584 ){ 585 push( @filters, { 586 type=>'Protos', 587 invert=>0, 588 args=>{ 589 protos=>[ 'udp4', 'udp6' ], 590 } 591 }, 592 ); 593}elsif( 594 $tcp && 595 ( ! $udp ) 596 ){ 597 push( @filters, { 598 type=>'Protos', 599 invert=>0, 600 args=>{ 601 protos=>[ 'tcp4', 'tcp6' ], 602 } 603 } 604 ); 605} 606 607if ( $no_use_ptr ){ 608 $ptr=0; 609} 610 611# XOR the -i if needed 612if ( defined( $ENV{NCNETSTAT_invert} ) ){ 613 $invert= $invert ^ $ENV{NCNETSTAT_invert}; 614} 615# XOR the -n value if needed 616if ( defined( $ENV{NCNETSTAT_ptr} ) ){ 617 $ptr = $ptr ^ $ENV{NCNETSTAT_ptr}; 618} 619# same for the no color 620if ( defined( $ENV{NO_COLOR} ) ){ 621 $no_color = $no_color ^ 1; 622} 623# disable the color if requested 624if ( $no_color ){ 625 $ENV{ANSI_COLORS_DISABLED}=1; 626} 627# set C if Cl is set 628if ( $command_long && ! $command ){ 629 $command=1; 630} 631 632#dump the filters if asked 633if ($dump) { 634 use Data::Dumper; 635 print Dumper( \@filters ); 636 exit; 637} 638 639my $ncnetstat=Net::Connection::ncnetstat->new( 640 { 641 ptr=>$ptr, 642 command=>$command, 643 command_long=>$command_long, 644 wchan=>$wchan_show, 645 pct=>$pct_show, 646 sorter=>{ 647 invert=>$invert, 648 type=>$sort, 649 }, 650 match=>{ 651 checks=>\@filters, 652 } 653 } 654 ); 655print $ncnetstat->run; 656 657=head1 NAME 658 659ncnetstat - a netstat like utility that supports color and searching 660 661=head1 SYNOPSIS 662 663ncnetstat [B<-a>] [B<--drp>] [B<-l>] [B<-n>] [B<--nc>] [B<-S <sort>>] [B<-t>] [B<-u>] 664[B<-c <CIDRs>>] [B<--ci -p <ports>>] [B<--pi>] [B<-P <protocols>>] [B<--Pi>] 665[B<--ptr <PTRs>>] [B<--ptri>] [B<--lptr <PTRs>>] [B<--lptri>] [B<--rptr <PTRs>>] [B<--rptri>] 666[B<-s <states>>] [B<--si>] ... 667 668=head1 FLAGS 669 670=head2 -a 671 672Show all connections. 673 674=head2 -c <CIDRs> 675 676A comma seperated list of CIDRs to search for. 677 678=head2 --ci 679 680Invert the CIDR search. 681 682=head2 -C 683 684Show the command to the first space. 685 686=head2 --Cl 687 688Show the whole command. 689 690=head2 --cmd <cmds> 691 692A comma seperated list of commands to search for. 693 694=head2 --cmdi 695 696Invert the command search. 697 698=head2 --cpu <pct> 699 700Show connections belonging to procs matching this CPU usage percent. 701 702=head2--cpui 703 704Invert the CPU search. 705 706=head2 --drp 707 708Don't resolve port names. 709 710=head2 -i 711 712Invert the sort. 713 714=head2 -l 715 716Show the listening ports. 717 718=head2 --mem <pct> 719 720Show connections belonging to procs matching this memory usage percent. 721 722 723=head2 --memi 724 725Invert the memory usage search. 726 727=head2 -n 728 729Don't resolve the PTRs. 730 731=head2 --nc 732 733Don't use colors. 734 735=head2 -p <ports> 736 737A comma seperated list of ports to search for. 738 739=head2 --pct 740 741Show memory and CPU usage of the process that has the connection. 742 743=head2 --pi 744 745Invert the port search. 746 747=head2 -P <protocols> 748 749A comma seperated list of protocols to search for. 750 751=head2 --Pi 752 753Invert your protocol search. 754 755=head2 --pid <pids> 756 757A comma separated list of PIDs to search for. 758 759=head2 --pidi 760 761Invert the pid search. 762 763=head2 --ptr <PTRs> 764 765A comma seperated list of PTRs to search for. 766 767=head2 --ptri 768 769Invert the PTR search. 770 771=head2 --ptrr <rgx> 772 773A comma seperated list of regex to use for a PTR search. 774 775=head2 --ptrri 776 777Invert the RegexPTR search. 778 779=head2 --lptr <PTRs> 780 781A comma seperated list of local PTRs to search for. 782 783=head2 --lptri 784 785Invert the local PTR search. 786 787=head2 --lptrr <rgx> 788 789A comma seperated list of regex to use for a local PTR search. 790 791=head2 --lptrri 792 793Invert the local RegexPTR search. 794 795=head2 --rptr <PTRs> 796 797A comma seperated list of remote PTRs to search for. 798 799=head2 --rptri 800 801Invert the remote PTR search. 802 803=head2 --rptr <PTRs> 804 805A comma seperated list of remote PTRs to search for. 806 807=head2 --rptri 808 809Invert the remote PTR search. 810 811=head2 -s <states> 812 813A comma seperated list of states to search for. 814 815=head2 --si 816 817Invert the state search. 818 819=head2 -S <sort> 820 821The L<Net::Connection::Sort> to use. 822 823The default available sort methods are as below. 824 825 host_f foreign host 826 host_fl foreign host, local host *default* 827 host_l local host 828 host_lf local host, foreign host 829 pid process ID 830 port_f foreign port, numerically 831 port_fa foreign port, alphabetically 832 port_l local port, numerically 833 port_la local port, alphabetically 834 proto protocol 835 ptr_f foreign PTR 836 ptr_l local PTR 837 state state 838 uid user ID 839 user username 840 841=head2 -t 842 843Show only TCP connections. 844 845=head2 -u 846 847Show only UDP connections. 848 849=head2 -U <users> 850 851A comma seperated list of usernames to search for. 852 853=head2 --Ui 854 855Invert the username search. 856 857=head2 --uid <uids> 858 859A comma separated list of UIDs to search for. 860 861=head2 --uidi 862 863Invert the UID search. 864 865=head2 -W 866 867Show the wait channel. 868 869=head2 -w <rgx> 870 871A comma separated list of regexp to use for matching wchan values. 872 873=head2 --wi 874 875Invert the wchan search 876 877=head1 PID/UID/CPU/Mem EQUALITIES 878 879For some searches, the equalities below can be used, by 880directly prepending them to the number. 881 882 < 883 <= 884 > 885 >= 886 887So if you wanted to find every connection from a UID greater than 1000, would 888do '--uid \>1000'. 889 890=head1 ENVIRONMENT VARIABLES 891 892=head2 NCNETSTAT_invert 893 894This is either 0 or 1. If defined it will be used for XORing the -i flag. 895 896 export CNETSTAT_invert=1 897 # run ncnetstat inverted 898 ncnetstat 899 # run it non-inverted, the opposite of what the -i flag normally is 900 ncnetstat -i 901 902=head2 NCNETSTAT_sort 903 904Sets the default sort method. -S overrides this. 905 906=head2 NO_COLOR 907 908If this is set, The output will not be colorized. If this is set, the --nc 909flag is also inverted. 910 911=head2 RES_NAMESERVERS 912 913A space-separated list of nameservers to query used by L<Net::DNS::Resolver>. 914 915There are a few more possible ones, but this is the most useful one and that documentation 916really belongs to that module. 917 918=head2 ncnetstat_C 919 920Used to xor the -C switch. 921 922Set to either 0 or 1, boolean, for setting the default. 923 924=head2 ncnetstat_Cl 925 926Used to xor the --Cl switch. 927 928Set to either 0 or 1, boolean, for setting the default. 929 930=head2 ncnetstat_W 931 932Used to xor the -W switch. 933 934Set to either 0 or 1, boolean, for setting the default. 935 936=head2 ncnetstat_pct 937 938Used to xor the --pct switch. 939 940Set to either 0 or 1, boolean, for setting the default. 941 942=head1 EXAMPLES 943 944 ncnetstat -s established,time_wait 945 946Return a list of connection that are in the established or time_wait state. 947 948 ncnetstat -c ::/0 949 950Return a list of all IPv6 addresses. 951 952 ncnetstat -c ::1/128,127.0.0.1/32 953 954Return all connections to localhost. 955 956 ncnetstat -c 192.168.15.2/32 -l 957 958Display all connections listening explicitly on 192.168.15.2. 959 960 ncnetstat -S host_f -i 961 962Sort the connections by the foreign host and invert the results. 963 964 ncnetstat -c 10.0.0.0/24 --ci 965 966Show connections that are either not locally or remotely part of the 96710.0.0.0/24 subnet. 968 969 ncnetstat --ptr foo.bar 970 971Find connections to/from IPs that have a PTR record of foo.bar. 972 973 ncnetstat --ptr foo.bar --ptri 974 975Find connections to/from IPs that do not have a PTR record of foo.bar. 976 977 ncnetstat -n --uid '>1000' --Cl 978 979Show every connection by a user with a UID greater than 1000, do not resolve 980PTR info and print the whole command. 981 982 ncnetstat -U www -p 80,443 --pi 983 984Show every connecttion by the user www that is not a HTTP or HTTPS connection. 985 986 ncnetstat --cpu '>5' --Cl --pct -W 987 988Search for connections from procs using more than 5% of the CPU time. Show memory 989and CPU usage as well whole command and wait channel. 990 991=cut 992