1#!/usr/bin/env perl 2# 3# D-Link DSL-G6x4T flash utility 4# 5# Copyright (C) 2005 Felix Fietkau <mailto@nbd.name> 6# based on fbox recovery util by Enrik Berkhan 7# 8# This program is free software; you can redistribute it and/or modify 9# it under the terms of the GNU General Public License as published by 10# the Free Software Foundation; either version 2 of the License, or 11# (at your option) any later version. 12# 13# This program is distributed in the hope that it will be useful, 14# but WITHOUT ANY WARRANTY; without even the implied warranty of 15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16# GNU General Public License for more details. 17# 18# You should have received a copy of the GNU General Public License 19# along with this program; if not, write to the Free Software 20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21# 22 23use IO::Socket::INET; 24use IO::Select; 25use Socket; 26use strict; 27use warnings; 28 29sub usage() { 30 print STDERR "Usage: $0 <ip> [firmware.bin]\n\n"; 31 exit 0; 32} 33 34my $ip = shift @ARGV; 35$ip and $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or usage(); 36 37my $setip = unpack("N", inet_aton($ip)); 38$setip > 0 or usage(); 39 40my @packets; 41foreach my $ver ([18, 1], [22, 2]) { 42 push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0); 43} 44print STDERR "Looking for device: "; 45my $scanning; 46my $box; 47 48my $probe = IO::Socket::INET->new(Proto => 'udp', 49 Broadcast => 1, 50 LocalAddr => $ip, 51 LocalPort => 5035) or die "socket: $!"; 52my $sel = IO::Select->new($probe); 53my $packet = pack("vCCVNV", 0, 18, 1, 1, 0, 0); 54my $broadcast = sockaddr_in(5035, INADDR_BROADCAST); 55 56$probe->send($packet, 0, $broadcast); 57 58 59scan_again: 60print "Looking for Fritz!Box "; 61my @boxes = (); 62my $peer; 63$scanning = 100; 64print "o"; 65while($scanning) { 66 my $reply; 67 my @ready; 68 69 if (@ready = $sel->can_read(0.2)) { 70 $peer = $probe->recv($reply, 16); 71 next if (length($reply) < 16); 72 my ($port, $addr) = sockaddr_in($peer); 73 my ($major, $minor1, $minor2, $code, $addr2) = unpack("vCCVV", $reply); 74 $addr2 = pack("N", $addr2); 75 if ($code == 2) { 76 print "O"; 77 push @boxes, [$major, $minor1, $minor2, $addr, $addr2]; 78 $scanning = 2 if ($scanning > 2); 79 } 80 } else { 81 $scanning--; 82 if (scalar @boxes == 0) { 83 $probe->send($packet, 0, $broadcast); 84 print "o"; 85 } else { 86 print "."; 87 } 88 } 89} 90 91if (scalar @boxes == 0) { 92 print " none found, giving up.\n"; 93 exit 1; 94} else { 95 print " found!\n"; 96} 97 98{ 99 package ADAM2FTP; 100 use base qw(Net::FTP); 101 # ADAM2 requires upper case commands, some brain dead firewall doesn't ;-) 102 sub _USER { shift->command("USER",@_)->response() } 103 sub _PASV { shift->command("P\@SW")->response() == Net::FTP::CMD_OK } 104 sub _GETENV { 105 my $ftp = shift; 106 my ($ok, $name, $value); 107 108 $ftp->command("GETENV",@_); 109 while(length($ok = $ftp->response()) < 1) { 110 my $line = $ftp->getline(); 111 unless (defined($value)) { 112 chomp($line); 113 ($name, $value) = split(/\s+/, $line, 2); 114 } 115 } 116 $ftp->debug_print(0, "getenv: $value\n") 117 if $ftp->debug(); 118 return $value; 119 } 120 sub getenv { 121 my $ftp = shift; 122 my $name = shift; 123 return $ftp->_GETENV($name); 124 } 125 sub _REBOOT { shift->command("REBOOT")->response() == Net::FTP::CMD_OK } 126 sub reboot { 127 my $ftp = shift; 128 $ftp->_REBOOT; 129 $ftp->close; 130 } 131 sub check { 132 my $ftp = shift; 133 134 delete ${*$ftp}{'net_ftp_port'}; 135 delete ${*$ftp}{'net_ftp_pasv'}; 136 137 my $data = $ftp->_data_cmd('CHECK' ,@_) or return undef; 138 my $sum; 139 if (${${*$ftp}{'net_cmd_resp'}}[0] =~ /^Flash check 0x([0-9A-F]{8})/) { 140 $sum = hex($1); 141 } 142 $data->_close(); 143 return $sum; 144 } 145} 146 147# passive mode geht mit Net::FTP nicht, connected zu spaet fuer ADAM2! 148my $ftp = ADAM2FTP->new($ip, Passive => 0, Debug => 0, Timeout => 600) 149 or die "can't FTP ADAM2"; 150$ftp->login("adam2", "adam2") or die "can't login adam2"; 151$ftp->binary(); 152my $pid = $ftp->getenv('ProductID'); 153my $hwrev = $ftp->getenv('HWRevision'); 154my $fwrev = $ftp->getenv('firmware_info'); 155my $ulrev = $ftp->getenv('urlader-version'); 156 157print "Product ID: $pid\n"; 158print "Hardware Revision: $hwrev\n"; 159print "Urlader Revision: $ulrev\n"; 160print "Firmware Revision: $fwrev\n"; 161 162$ftp->hash(\*STDOUT, 64 * 1024); 163 164my $file = shift @ARGV; 165$file || exit 0; 166 167open FILE, "<$file" or die "can't open firmware file\n"; 168 169my $mtd0 = $ftp->getenv("mtd0"); 170my $mtd1 = $ftp->getenv("mtd1"); 171my ($ksize, $fssize); 172 173$mtd1 =~ /^(0x\w+),(0x\w+)$/ and $ksize = hex($2) - hex($1); 174$mtd0 =~ /^(0x\w+),(0x\w+)$/ and $fssize = hex($2) - hex($1); 175$ksize and $fssize or die 'cannot read partition offsets'; 176printf STDERR "Available flash space: 0x%08x (0x%08x + 0x%08x)\n", $ksize + $fssize, $ksize, $fssize; 177 178$ftp->command("MEDIA FLSH")->response(); 179$ftp->binary(); 180print STDERR "Writing to mtd1...\n"; 181 182my $dc = $ftp->stor("fs mtd1"); 183$dc or die "can't open data connection\n"; 184my $rbytes = 1; 185 186while (($ksize > 0) and ($rbytes > 0)) { 187 my $buffer; 188 my $len = ($ksize > 1024 ? 1024 : $ksize); 189 $rbytes = read FILE, $buffer, $len; 190 $rbytes and $ksize -= $dc->write($buffer, $rbytes, 600); 191} 192 193$dc->close(); 194$rbytes or die "no more data left to write\n"; 195 196print STDERR "Writing to mtd0...\n"; 197 198$dc = $ftp->stor("fs mtd0"); 199$dc or die "can't open data connection\n"; 200 201while (($fssize > 0) and ($rbytes > 0)) { 202 my $buffer; 203 my $len = ($fssize > 1024 ? 1024 : $fssize); 204 $rbytes = read FILE, $buffer, $len; 205 $rbytes and $fssize -= $dc->write($buffer, $rbytes, 600); 206} 207 208$dc->close(); 209$ftp->reboot(); 210