--- /dev/null
+
+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?
+)
--- /dev/null
+#! /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;
+ }
+ }
+}
+