From ef114ab2ba53bc9068c4552f8bd3dc0a8511671e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 22 Jan 2013 17:17:48 +0100 Subject: [PATCH 1/1] Initial checkin for move to Git (no prior version history available). --- access_list.pl | 106 ++++++++++++++++++++++++++++++++++++++++++ mbd.pl | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ nets.pl | 9 ++++ 3 files changed, 237 insertions(+) create mode 100644 access_list.pl create mode 100644 mbd.pl create mode 100644 nets.pl diff --git a/access_list.pl b/access_list.pl new file mode 100644 index 0000000..26ef9ff --- /dev/null +++ b/access_list.pl @@ -0,0 +1,106 @@ + +package Config; + +our @access_list = ( + # half-life - untested (packet dump only) + { + ports => [ 27015 ], + sizes => [ 16 ] + }, + + # cs 1.6 - verified + # (funker muligens for _alle_ source-spill inkl. hl2/cs:s) + { + ports => [ 4242, "26900..26905", "27015..27020" ], + sizes => [ 25 ] + }, + + # doom 3 - verified + { + ports => [ "27666..27673" ], + sizes => [ 14 ] + }, + + # quake 1 - verified + { + ports => [ 26000 ], + sizes => [ 12 ] + }, + + # q3a - tested with demo only + # rtcw: enemy territory - untested (packet dump only) + { + ports => [ "27960..27969" ], + sizes => [ 15 ] + }, + + # bf2 - tested with demo only + # bf2142 reportedly uses same engine + { + ports => [ "29900..29950" ], + sizes => [ 8 ] + }, + + # bf1942 - unverified (packet dump only) + { + ports => [ "22000..22010" ], + sizes => [ 8 ] + }, + + # quake 4 - tested with demo only, MUST select "internet" + { + ports => [ 27950, 28004 ], + sizes => [ 14 ] + }, + + # quake 2 - untested (packet dump only) + { + ports => [ 27910 ], + sizes => [ 11 ] + }, + + # warcraft 3 - untested (packet dump only) + { + ports => [ 6112 ], + sizes => [ 16 ] + }, + + # ut2003/ut2004 - untested (packet dump only) + { + ports => [ 10777 ], + sizes => [ 5 ] + }, + + # soldat - untested (packet dump only) + { + ports => [ 23073 ], + sizes => [ 8 ] + }, + + # starcraft - untested (packet dump only) + { + ports => [ 6111, 6112 ], + sizes => [ 8, 20 ] + }, + + # trackmania nations - untested (packet dump only) + { + ports => [ "2350..2370" ], + sizes => [ 42 ] + }, + + # company of heroes - untested (packet dump only) + { + ports => [ 9100 ], + sizes => [ 39 ] + }, + + # command & conquer 3 - untested (packet dump only, reported to have some kind + # of chat functionality) + { + ports => [ "8086..8093" ], + sizes => [ 476 ] + }, + + # unreal tournament, port 9777? +) diff --git a/mbd.pl b/mbd.pl new file mode 100644 index 0000000..90ee4d6 --- /dev/null +++ b/mbd.pl @@ -0,0 +1,122 @@ +#! /usr/bin/perl +use strict; +use warnings; +use Socket; +use Net::CIDR; +use Net::RawIP; +require './access_list.pl'; +require './nets.pl'; + +sub expand_range { + my $range = shift; + + if ($range =~ /^(\d+)\.\.(\d+)$/) { + return $1..$2; + } else { + return $range; + } +} + +sub match_ranges { + my ($elem, $ranges) = @_; + + for my $range (@$ranges) { + if ($range =~ /^(\d+)\.\.(\d+)$/) { + return 1 if ($elem >= $1 && $elem <= $2); + } else { + return 1 if ($elem == $range); + } + } + + return 0; +} + +sub fhbits { + my $bits = 0; + for my $fh (@_) { + vec($bits, fileno($fh), 1) = 1; + } + return $bits; +} + +# Find what ports we need to listen on +my %port_hash = (); +for my $e (@Config::access_list) { + for my $r (@{$e->{'ports'}}) { + for my $p (expand_range($r)) { + $port_hash{$p} = 1; + } + } +} +my @ports = sort { $a <=> $b } keys %port_hash; + +# Open a socket for each port +my @socks = (); +my $udp = getprotobyname("udp"); +for my $p (@ports) { + my $sock; + socket($sock, PF_INET, SOCK_DGRAM, $udp); + bind($sock, sockaddr_in($p, INADDR_ANY)); + push @socks, $sock; +} + +my $sendsock = Net::RawIP->new({udp => {}}); + +print "Listening on " . scalar @ports . " ports.\n"; + +# Main loop +while (1) { + my $rin = fhbits(@socks); + my $rout; + + my $nfound = select($rout=$rin, undef, undef, undef); + for my $sock (@socks) { + next unless (vec($rout, fileno($sock), 1) == 1); + + my $data; + my $addr = recv($sock, $data, 8192, 0); # jumbo broadcast! :-P + my ($sport, $saddr) = sockaddr_in($addr); + my ($dport, $daddr) = sockaddr_in(getsockname($sock)); + my $size = length($data); + + # We don't get the packet's destination address, but I guess this should do... + # Check against the ACL. + my $pass = 0; + for my $rule (@Config::access_list) { + if (match_ranges($dport, $rule->{'ports'}) && + match_ranges($size, $rule->{'sizes'})) { + $pass = 1; + } + } + + if (!$pass) { + print "$dport, $size bytes => filtered\n"; + } + + next unless $pass; + + for my $net (@Config::networks) { + next if (Net::CIDR::cidrlookup(inet_ntoa($saddr), $net)); + + my ($range) = Net::CIDR::cidr2range($net); + $range =~ /-(.*?)$/; + my $broadcast = $1; + + print inet_ntoa($saddr), ", $dport, $size bytes => $broadcast\n"; + + $sendsock->set({ + ip => { + saddr => inet_ntoa($saddr), + daddr => $broadcast + }, + udp => { + source => $sport, + dest => $dport, + data => $data + } + }); + $sendsock->send; + } + } +} + diff --git a/nets.pl b/nets.pl new file mode 100644 index 0000000..55bec01 --- /dev/null +++ b/nets.pl @@ -0,0 +1,9 @@ + +package Config; + +our @networks = ( + "10.0.10.0/24", + "10.0.11.0/24" +); + +1; -- 2.39.2