#!/usr/bin/perl

# what if netstat was written in perl
# MIT-Licensed, ed <irc.rizon.net>, 2019-01-22
# https://ocv.me/dev/?netstat.pl

use strict;
use warnings;
use Socket qw(inet_ntop AF_INET6);

my %stab;

sub dec_ep4 {
	my @ep = split /:/, $_[0];
	my $port = hex($ep[1]);
	my @aip = map{pack("C",hex($_))} ($ep[0] =~ /(..)/g);
	my @dip = unpack("C*", reverse @aip);
	return sprintf "%s:\033[1m%5s", join(".",@dip), $port;
}

sub dec_ep6 {
	my @ep = split /:/, $_[0];
	my $port = hex($ep[1]);
	my @aip = map{pack("C",hex($_))} ($ep[0] =~ /(..)/g);
	@aip = unpack("L>*", join("", @aip));
	@aip = pack("L<*", @aip);
	my $ip = inet_ntop(AF_INET6, join("", @aip));
	return sprintf "%s:\033[1m%5s", substr($ip,0,22), $port;
}

sub parse_tab4 {
	my $proto = $_[0];
	open my $fh, "/proc/net/".$proto;
	my $ln = <$fh>;
	while ($ln = <$fh>) {
		$ln =~ s/^ +//;
		my @lw = split / +/, $ln;
		$stab{$lw[9]} = sprintf "%-4s \033[0;33m%32s \033[0;35m%32s", $proto, dec_ep4($lw[1]), dec_ep4($lw[2]);
	}
	close $fh;
}

sub parse_tab6 {
	my $proto = $_[0];
	open my $fh, "/proc/net/".$proto;
	my $ln = <$fh>;
	while ($ln = <$fh>) {
		$ln =~ s/^ +//;
		my @lw = split / +/, $ln;
		#printf "\n%s\n", $ln;
		$stab{$lw[9]} = sprintf "%-4s \033[0;33m%32s \033[0;35m%32s", $proto, dec_ep6($lw[1]), dec_ep6($lw[2]);
	}
	close $fh
}

parse_tab4("tcp");
parse_tab6("tcp6");

parse_tab4("udp");
parse_tab6("udp6");

printf "\033[36mproto\033[0;33m%32s \033[0;35m%32s \033[0;36m%5s %s\n", "local-ad\033[1mdress", "foreign-ad\033[1mdress", "pid", "process";

my @pids;
foreach my $proc (</proc/*>) {
	my ($pid) = $proc =~ m/.*\/(.*)/;
	$pid =~ /^[0-9]+$/ or next;
	push @pids, int $pid;
}
@pids = sort {$a <=> $b} @pids;

#print join(", ", @pids);

my @out;
foreach my $pid (@pids) {
	my $exe = readlink("/proc/".$pid."/exe") or next;
	foreach my $fd (</proc/$pid/fd/*>) {
		my $lfd = readlink($fd) or next;
		$lfd =~ /^socket/ or next;
		$lfd =~ s/.*:\[(.*)\]/$1/;
		next if (not exists $stab{$lfd});
		push @out, sprintf "%s \033[0;36m%5s %s\n", $stab{$lfd}, $pid, substr($exe,0,32);
		# $lfd = socket id
	}
}

@out = sort {
	substr($a,0,3) cmp substr($b,0,3)
} @out;

foreach (@out) {
	print $_;
}
