/* * Copyright (c) Martin Schoeberl, martin@jopdesign.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Martin Schoeberl * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ package ejip123.legacy; /* * Changelog: * 2002-10-24 creation. * * * TODO: use a source port as TID. * timeout and resend or cancel connection. * */ import util.Amd; import ejip123.util.Dbg; import util.Timer; import ejip123.UdpHandler; import ejip123.Packet; import ejip123.PacketPool; import ejip123.Udp; /** * Tftp.java: A simple TFTP Server. see rfc1350. */ public class Tftp implements UdpHandler{ public static final int PORT = 69; private static final int IDLE = 0; private static final int RRQ = 1; private static final int WRQ = 2; private static final int DAT = 3; private static final int ACK = 4; // private static final int ERR = 5; private static int state; private static int fn; private static int endBlock; private static int block; private static int srcIp, dstIp, dstPort; // private static LinkLayer ipLink; Tftp() { tftpInit(); } private static void tftpInit() { state = IDLE; block = 0; fn = 0; block_out = 0; timeout = 0; } /* not used private static void error(Packet p) { p.buf[Udp.DATA] = (ERR<<16) + 4711; p.buf[Udp.DATA+1] = 'x'<<24; p.len = Udp.DATA+6; tftpInit(); } */ /** drop the packet end reset state */ private static void discard(Packet p) { p.setLen(0); tftpInit(); } private static int block_out; private static int timeout; private static int time; private static void onTheFly(int block) { block_out = block; time = 4; timeout = Timer.getTimeoutSec(time); } public void loop() { if (block_out != 0) { if (Timer.timeout(timeout)) { resend(); } } } private static void resend() { time <<= 1; if (time > 64) { Dbg.wr("TFTP give up"); tftpInit(); return; } Dbg.wr("TFTP resend "); Dbg.intVal(block_out); // retransmit DATA timeout = Timer.getTimeoutSec(time); Packet p = PacketPool.getFreshPacket(); // Packet p = PacketPool.getPacket(Packet.FREE, Packet.ALLOC, ipLink); if (p == null) { // got no free buffer! Dbg.wr('!'); Dbg.wr('b'); return; } p.buf[Udp.DATA] = (DAT<<16)+block_out; if (block_out==endBlock) { p.setLen(Udp.DATA*4+4); // last block is zero length } else { read(p.buf, block_out); p.setLen(Udp.DATA*4+4+512); } Udp.send(p, srcIp, dstIp, dstPort); } /** * handle the TFTP packets. * * file name is fixed length (2): * 'ix' internal memory (read only) * 'f0'..'f8' flash sector (64 KB) * */ public void request(Packet p, int offset) { // TODO use parameter offset instead of Udp.DATA int i, j; int[] buf = p.buf; Dbg.wr('F'); Dbg.hexVal(buf[Udp.DATA]); int op = buf[Udp.DATA]>>>16; /* ++simerr; if (simerr%23==0) { Dbg.wr(" tftp dropped"); Dbg.lf(); p.freePacket(); // mark packet free return; } */ if (op==RRQ) { state = RRQ; fn = buf[Udp.DATA]&0xffff; i = fn>>8; if (i=='i') { endBlock = 2+1; // (256*4)/512 } else if (i=='f') { endBlock = 128+1; // 64K/512 } else { endBlock = 1+1; } block = 1; buf[Udp.DATA] = (DAT<<16)+block; read(buf, block); p.setLen(Udp.DATA*4+4+512); onTheFly(block); } else if (op==ACK) { i = (buf[Udp.DATA] & 0xffff); // get block number if (i < block) { // a ACK for an already sent package // drop it p.free(); // mark packet free return; } block = i+1; // use one higher then last acked block if (block>endBlock) { // ACK of last block discard(p); } else { buf[Udp.DATA] = (DAT<<16)+block; if (block==endBlock) { p.setLen(Udp.DATA*4+4); // last block is zero length } else { read(buf, block); p.setLen(Udp.DATA*4+4+512); } onTheFly(block); /* ++simerr; if (simerr%23==0) { Dbg.wr(" simulate wrong data on read "); Dbg.lf(); buf[Udp.DATA+13] = 0x12345678; } */ } } else if (op==WRQ) { state = WRQ; fn = buf[Udp.DATA]&0xffff; block = 1; buf[Udp.DATA] = (ACK<<16); p.setLen(Udp.DATA*4+4); } else if (op==DAT) { i = (buf[Udp.DATA] & 0xffff); // get block number if (state==IDLE) { // ACK of last data block got lost, // but we received the data and finished programming buf[Udp.DATA] = (ACK<<16)+i; // just ack it p.setLen(Udp.DATA*4+4); // we have allready received it before } else if (state!=WRQ) { discard(p); } else if (block != i) { // not the expected block // is it a second write with the old block number? if (block-1 == i) { buf[Udp.DATA] = (ACK<<16)+i; // just ack it p.setLen(Udp.DATA*4+4); // we have allready received it before } else { p.setLen(0); // else just discarde paket } } else { // Dbg.wr('a'); // Dbg.intVal(block); if (p.len() > Udp.DATA*4+4) { program(buf, block); } buf[Udp.DATA] = (ACK<<16)+block; boolean last = p.len() != Udp.DATA*4+4+512; p.setLen(Udp.DATA*4+4); ++block; if (last) { // end of write tftpInit(); } } } else { p.setLen(0); tftpInit(); Dbg.wr("error "); } if (p.len() ==0) { p.free(); // mark packet free } else { reply(p); } } // TODO: insert reply back to request or split in smaller methods private static void reply(Packet p) { /* ++simerr; if (simerr%23==0) { Dbg.wr("reply dropped "); Dbg.lf(); p.freePacket(); // mark packet free return; } */ int[] buf = p.buf; Dbg.wr("tftp reply: "); Dbg.intVal(buf[Udp.DATA] & 0xffff); // generate a reply with IP src/dst exchanged dstPort = buf[5]>>>16; // TODO: static offset srcIp = buf[4]; dstIp = buf[3]; // ipLink = p.linkLayer(); Udp.send(p, srcIp, dstIp, dstPort); } /** * program flash. */ private static void program(int[] buf, int block) { int i, j; int base; block--; i = fn>>8; Timer.wd(); // toggle for each block? if (i=='f') { // program flash base = (block<<9); base += ((fn&0xff)-'0')<<16; // 64 KB sector if ((base & 0xffff) ==0) { Amd.erase(base); } progloop(base, buf); } } private static void progloop(int base, int[] buf) { int i, w; for (i=1; i<129; ++i) { w = buf[Udp.DATA+i]; Amd.program(base, w>>>24); Amd.program(base+1, w>>>16); Amd.program(base+2, w>>>8); Amd.program(base+3, w); base += 4; } } /** * read internal memory or flash. */ private static void read(int[] buf, int block) { int i, j, k; int base; block--; i = fn>>8; if (i=='i') { // read internal memory base = block<<7; for (i=0; i<128; ++i) { buf[Udp.DATA+1+i] = com.jopdesign.sys.Native.rdIntMem(base+i); } } else if (i=='f') { // read flash base = (block<<9) + 0x80000; // add offset because we use Native.rdMem! base += ((fn&0xff)-'0')<<16; // 64 KB sector k = 0; for (i=0; i<128; ++i) { for (j=0; j<4; ++j) { k <<= 8; k += com.jopdesign.sys.Native.rdMem(base+(i<<2)+j); } buf[Udp.DATA+1+i] = k; } } else { // read nothing for (i=0; i<128; ++i) { buf[Udp.DATA+1+i] = 0; } } } }