package ejip123;
import ejip123.util.Dbg;
/** TCP main class. Used for managing TcpConnections and handlers. */
public class Tcp{
/** TCP protocol number. */
public static final int PROTOCOL = 6;
/** Offset of data in words when no options present. */
public static final int OFFSET = Ip.OFFSET + 5;
private static final Object lock = new Object();
private static int MAX_HANDLER = 0;
private static TcpHandler[] list = null;
private static int[] ports = null;
private static TcpConnection[] connections = null;
/** Maximum number of active TCP connections. */
private static int MAX_CON = 0;
private Tcp(){
}
public static void init(int handlerCnt, int concurrConnCnt){
MAX_HANDLER = handlerCnt <= 0 ? 4 : handlerCnt;
MAX_CON = handlerCnt <= 0 ? MAX_HANDLER<<1 : concurrConnCnt;
list = new TcpHandler[MAX_HANDLER];
ports = new int[MAX_HANDLER];
connections = new TcpConnection[Tcp.MAX_CON];
for(int i = 0; i < Tcp.MAX_CON; ++i){
connections[i] = new TcpConnection();
}
}
/* Called periodic from Net for timeout processing. */
static void loop(int cur){
synchronized(lock){
for(int i = 0; i < MAX_CON; ++i){
TcpConnection tc = connections[i];
tc.loop(cur);
}
}
}
/* Processes packet and generate reply if necessary. */
static void process(Packet p, int off){
int[] buf = p.buf;
int src = buf[3];
int dst = buf[4];
// inject pseudo header for tcp checksum into original packet
// for packets w/o options: ttl, prot and ip checksum are overwritten
buf[off - 1] = dst;
buf[off - 2] = src;
buf[off - 3] = (PROTOCOL<<16) + p.len() - (off<<2);
if(Ip.chkSum(buf, off - 3, p.len() - ((off - 3)<<2)) != 0){
Dbg.wr("tcp checksum failed!\n");
p.free();
return;
}
int dstPort = buf[off];
int srcPort = dstPort>>>16;
dstPort &= 0xffff;
synchronized(lock){
TcpConnection free = null;
for(int i = 0; i < MAX_CON; ++i){
TcpConnection tc = connections[i];
if(tc.isUsed()){
if(tc.processPacket(src, dst, srcPort, dstPort, p, off)){
return;
}
} else{
free = tc;
}
}
// if not found get a new one if possible or drop the packet
if(free != null){
// Dbg.wr("new con\n");
free.newIncoming(src, dst, srcPort, dstPort, p, off);
} else{
// TODO rst? port unreachable? protocol unreachable?
p.free();
}
}
}
static int getMaxPayload(int dstIp){
// int max = Ip.getMaxPayload(dstIp) - ((Tcp.OFFSET - Ip.OFFSET)<<2);
// Dbg.wr("max local link payload=");
// Dbg.intVal(max);
return Ip.getMaxPayload(dstIp) - ((Tcp.OFFSET - Ip.OFFSET)<<2);
}
/**
Opens a client connection. Fetches an available connection from the pool, sets it up and tries to connect to the remote
host. Local IP and port will be chosen by the implementation.
@param dstIp The remote IP.
@param dstPort The remote port.
@param th The TcpHandler, that will handle events for this connection.
@return The fetched connection of null if there was an error. */
public static TcpConnection open(int dstIp, int dstPort, TcpHandler th){
return open(dstIp, dstPort, dstPort, th);
}
/**
Opens a client connection. Fetches an available connection from the pool, sets it up and tries to connect to the remote
host. Local IP will be chosen by the implementation.
@param dstIp The remote IP.
@param srcPort The local port.
@param dstPort The remote port.
@param th The TcpHandler, that will handle events for this connection.
@return The fetched connection of null if there was an error. */
public static TcpConnection open(int dstIp, int srcPort, int dstPort, TcpHandler th){
int srcIp = Ip.getSrcIp(dstIp);
return open(srcIp, dstIp, srcPort, dstPort, th);
}
/**
Opens a client connection. Fetches an available connection from the pool, sets it up and tries to connect to the remote
host.
@param srcIp The local IP.
@param dstIp The remote IP.
@param srcPort The local port.
@param dstPort The remote port.
@param th The TcpHandler, that will handle events for this connection.
@return The fetched connection of null if there was an error. */
public static TcpConnection open(int srcIp, int dstIp, int srcPort, int dstPort, TcpHandler th){
synchronized(lock){
// check that there is no for that port
if(th == null || getHandler(srcPort) != null)
return null;
// check that there is no matching connection yet
TcpConnection free = null;
for(int i = 0; i < MAX_CON; ++i){
TcpConnection tc = connections[i];
if(tc.isUsed()){
if(tc.matches(srcIp, dstIp, srcPort, dstPort)){
return null;
}
} else{
free = tc;
}
}
if(free != null && free.open(dstIp, srcIp, dstPort, srcPort, th))
return free;
}
return null;
}
/**
Registers a TcpHandler for a port.
@param port The port.
@param h The handler.
@return True, if successfully registered. False, if list is full or there is already a handler registered for that
port. */
public static boolean addHandler(int port, TcpHandler h){
synchronized(lock){
int free = -1;
for(int i = 0; i < MAX_HANDLER; ++i){
if(list[i] == null){
free = i;
} else if(ports[i] == port){
return false;
}
}
if(free != -1){
ports[free] = port;
list[free] = h;
return true;
}
return false;
}
}
/**
Removes a TCP handler from the list. No new connections will be forwarded to that handler. Existing connection (even not
yet fully established ones) are not touched by this. A handler has to close all existing non reset connections before it
totally abandons them.
@param port The port the handler wants to be listening to.
@return false if it was not in the list. */
public static boolean removeHandler(int port){
synchronized(lock){
for(int i = 0; i < MAX_HANDLER; ++i){
if(list[i] != null && ports[i] == port){
list[i] = null;
return true;
}
}
}
return false;
}
/**
Fetches a handler.
@param port The TCP port in question.
@return The TcpHandler registered for that port or null if there is none. */
static TcpHandler getHandler(int port){
TcpHandler th = null;
// is a handler registered for that port?
synchronized(lock){
for(int i = 0; i < MAX_HANDLER; ++i){
if(list[i] != null && ports[i] == port){
th = list[i];
break;
}
}
}
return th;
}
}