1#!/usr/bin/perl 2# 3# Linksys ADSL2MUE Flash utility. 4# 5# Copyright (C) 2008 Alexandre Lissy <alexandrelissy@free.fr> 6# based on D-Link DSL-G6x4T flash utility by Felix Fietkau <mailto@nbd.name> 7# based on fbox recovery util by Enrik Berkhan 8# 9# This program is free software; you can redistribute it and/or modify 10# it under the terms of the GNU General Public License as published by 11# the Free Software Foundation; either version 2 of the License, or 12# (at your option) any later version. 13# 14# This program is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17# GNU General Public License for more details. 18# 19# You should have received a copy of the GNU General Public License 20# along with this program; if not, write to the Free Software 21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22# 23 24use IO::Socket::INET; 25use Socket; 26use strict; 27use warnings; 28 29sub usage() { 30 print STDERR "Usage: $0 <ip> [firmware.bin] [partition]\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 $probe = IO::Socket::INET->new(Proto => 'udp', 38 Broadcast => 1, 39 LocalPort => 5035) or die "socket: $!"; 40my $setip = unpack("N", inet_aton($ip)); 41$setip > 0 or usage(); 42 43my @packets; 44foreach my $ver ([18, 1], [22, 2]) { 45 push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0); 46} 47print STDERR "Looking for device: "; 48my $broadcast = sockaddr_in(5035, INADDR_BROADCAST); 49my $scanning; 50my $box; 51 52$SIG{"ALRM"} = sub { 53 return if --$scanning <= 0; 54 foreach my $packet (@packets) { 55 $probe->send($packet, 0, $broadcast); 56 } 57 print STDERR "."; 58}; 59 60$scanning = 10; 61foreach my $packet (@packets) { 62 $probe->send($packet, 0, $broadcast); 63} 64print STDERR "."; 65 66while($scanning) { 67 my $reply; 68 69 alarm(1); 70 if (my $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("vCCVN", $reply); 74 $addr2 = pack("N", $addr2); 75 if ($code == 1) { 76 $scanning = 0; 77 printf STDERR " found!\nADAM2 version $major.$minor1.$minor2 at %s (%s)\n", inet_ntoa($addr2), inet_ntoa($addr); 78 $box = inet_ntoa($addr2); 79 } 80 } 81} 82 83$box or die " not found!\n"; 84 85{ 86 package ADAM2FTP; 87 use base qw(Net::FTP); 88 89 # ADAM2 requires upper case commands, some brain dead firewall doesn't ;-) 90 sub _USER { 91 shift->command("USER",@_)->response() 92 } 93 94 sub _GETENV { 95 my $ftp = shift; 96 my ($ok, $name, $value); 97 98 $ftp->command("GETENV",@_); 99 while(length($ok = $ftp->response()) < 1) { 100 my $line = $ftp->getline(); 101 unless (defined($value)) { 102 chomp($line); 103 ($name, $value) = split(/\s+/, $line, 2); 104 } 105 } 106 $ftp->debug_print(0, "getenv: $value\n") 107 if $ftp->debug(); 108 return $value; 109 } 110 111 sub getenv { 112 my $ftp = shift; 113 my $name = shift; 114 return $ftp->_GETENV($name); 115 } 116 117 sub _REBOOT { 118 shift->command("REBOOT")->response() == Net::FTP::CMD_OK 119 } 120 121 sub reboot { 122 my $ftp = shift; 123 $ftp->_REBOOT; 124 $ftp->close; 125 } 126} 127 128my $file = shift @ARGV; 129my $part = shift @ARGV; 130$file || exit 0; 131$part || exit 0; 132 133open FILE, "<$file" or die "can't open firmware file\n"; 134my $ftp = ADAM2FTP->new($box, Debug => 0, Timeout => 600) or die "can't open control connection\n"; 135$ftp->login("adam2", "adam2") or die "can't login\n"; 136 137# my $mtd0 = $ftp->getenv("mtd0"); 138# my $mtd1 = $ftp->getenv("mtd1"); 139my $mtd4 = $ftp->getenv($part); 140# my ($ksize, $fssize); 141my ($ossize, $mtd_start, $mtd_end); 142 143# $mtd1 =~ /^(0x\w+),(0x\w+)$/ and $ksize = hex($2) - hex($1); 144# $mtd0 =~ /^(0x\w+),(0x\w+)$/ and $fssize = hex($2) - hex($1); 145$mtd4 =~ /^(0x\w+),(0x\w+)$/; 146$ossize = hex($2) - hex($1); 147$mtd_start = hex($1); 148$mtd_end = hex($2); 149$ossize and $mtd_start and $mtd_end or die 'cannot read partition offsets'; 150printf STDERR "Available flash space: 0x%08x ($part: 0x%08x to 0x%08x)\n", $ossize, $mtd_start, $mtd_end; 151 152$ftp->command("MEDIA FLSH")->response(); 153$ftp->binary(); 154 155print STDERR "Writing to $part ...\n"; 156my $dc = $ftp->stor("data $part"); 157$dc or die "can't open data connection\n"; 158my $rbytes = 1; 159 160while (($ossize > 0) and ($rbytes > 0)) { 161 my $buffer; 162 my $len = ($ossize > 1024 ? 1024 : $ossize); 163 $rbytes = read FILE, $buffer, $len; 164 printf STDERR "."; 165 $rbytes and $ossize -= $dc->write($buffer, $rbytes, 600); 166} 167 168printf STDERR "\nDone.\n"; 169 170$dc->close(); 171