/*
* 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.examples;
import com.jopdesign.sys.Const;
import ejip123.*;
import joprt.RtThread;
import ejip123.util.Dbg;
import ejip123.util.DbgUdp;
import ejip123.util.Serial;
import util.Timer;
/**
A small telnet server. This example shows how a TCP server can be implemented with the packet orientated TcpHandler
interface.
*/
public class Telnetd implements TcpHandler, PingReplyHandler{
private StringBuffer sb = new StringBuffer(32);
private TcpConnection tc;
private final int IDLE = -1;
private final int GREET = 0;
private final int ESTABLISHED = 1;
private final int PING = 2;
private final int PINGDONE = 3;
private static final int PROCESSING = 4;
private int state = IDLE;
private Packet curPacket;
private int curOff;
//private final Object this = new Object();
public static void main(String[] args){
PacketPool.init(10, 1500);
// Dbg.initSerWait(); // serial debug output
DbgUdp.init(); // sends debug output over the network to 192.168.2.1:10000 (see init method)
Router.init(3); // initializes a routing table with 3 routes
// Serial ser = new Serial(10, 1000, Const.IO_UART_BG_MODEM_BASE); // simulator
Serial ser = new Serial(10, 1000, Const.IO_UART1_BASE); // one byte every ~400us at 19200 baud
LinkLayer slip = Slip.init(9, 10000, ser, Ip.Ip(192, 168, 2, 2), 1500);
Ip.init(6, 50000); // ip (and therefore icmp and tcp) loop thread: period 50ms
Router.addRoute(new Route(Ip.Ip(192, 168, 2, 0), Ip.Ip(255, 255, 255, 0), slip));
Router.setDefaultInterface(slip); // where should packets go which are not matched by a route?
Tcp.init(3, 4); // inits tcp with slots for 3 handlers and 4 connections
Telnetd th = new Telnetd();
Tcp.addHandler(23, th);
RtThread.startMission();
Router.print(); // like route print on windows or ip addr on linux
th.forever();
}
/**
@noinspection NonPrivateFieldAccessedInSynchronizedContext */
private void forever(){
for(; ;){
Timer.wd();
synchronized(this){
Packet p;
switch(state){
case PROCESSING:
Dbg.wr("processing...\n");
processCmd();
break;
case GREET:
if(tc != null && (p = PacketPool.getFreshPacket()) != null){
p.setData(Tcp.OFFSET<<2, "Welcome to JOP\r\n> ");
if(tc.send(p, true))
state = ESTABLISHED;
}
break;
case PINGDONE:
if(tc != null && (p = PacketPool.getFreshPacket()) != null){
p.setData(Tcp.OFFSET<<2, sb);
if(tc.send(p, true)){
state = ESTABLISHED;
}
}
break;
default:
break;
}
}
}
}
private void processCmd(){
int cmdlen = curPacket.getData(curOff, sb);
int tmpState = ESTABLISHED;
if(cmdlen > 0){
Dbg.wr("cli: ");
Dbg.wr(sb);
if(Util.CharSequenceStartsWith(sb, "hello")){
sb.setLength(0);
sb.append("Hello from JOP\r\n> ");
} else if(Util.CharSequenceStartsWith(sb, "ping")){
int ip = Ip.parseIp(sb, 5);
if(ip != 0){
sb.setLength(0);
sb.append("PING ");
Util.appendIp(sb, ip);
sb.append("\r\n");
tmpState = PING;
Icmp.ping(ip, this);
} else{
sb.setLength(0);
sb.append("parsing cmd failed\r\n> ");
}
} else{
sb.setLength(0);
sb.append("unknown command\r\n> ");
}
}
if(sb.length() > 0){
curPacket.setLen(0);
curPacket.setData(Tcp.OFFSET<<2, sb);
tc.send(curPacket, true);
}
state = tmpState;
}
public boolean isBusy(TcpConnection newCon){
synchronized(this){
return state != IDLE;
}
}
public boolean request(TcpConnection con, Packet p, int off){
synchronized(this){
if(state != ESTABLISHED)
return false;
curOff = off;
curPacket = p;
curPacket.setStatus(Packet.ALLOC);
state = PROCESSING;
return true;
}
}
public void pingReply(int ms){
synchronized(this){
sb.setLength(0);
sb.append("got reply in ");
sb.append(ms);
sb.append("ms\r\n> ");
state = PINGDONE;
}
}
public void pingTimeout(){
synchronized(this){
sb.setLength(0);
sb.append("timed out\r\n> ");
state = PINGDONE;
}
}
public void established(TcpConnection newCon){
synchronized(this){
if(state == IDLE){
tc = newCon;
state = GREET;
} else{
newCon.close();
}
}
}
public void closed(TcpConnection closedCon){
synchronized(this){
if(tc == closedCon){
tc = null;
state = IDLE;
Dbg.wr("connection closed, ready for more!\n");
} else{
Dbg.wr("Con ");
Dbg.wr(closedCon.toString());
Dbg.wr(" we support only one concurrent connection. this one was not the first and was therefore closed\n");
}
}
}
public void reset(TcpConnection closedCon){
synchronized(this){
if(tc == closedCon){
tc = null;
state = IDLE;
}
}
}
}