head	1.1;
access;
symbols;
locks; strict;
comment	@# @;


1.1
date	2008.10.15.09.17.36;	author martin;	state Exp;
branches;
next	;
commitid	5c4d48f5b52b4567;


desc
@@


1.1
log
@first version of Stefan's ejip changes
@
text
@package ejip123;

/** UDP see RFC 768. */
public class Udp{

private static final Object mutex = new Object();
public static final int PROTOCOL = 17;
/**
 @@deprecated  */
public static final int DATA = 7;	// offset of data in words
/** Offset of udp payload in ip packets w/o options (in 32b words). */
public static final int OFFSET = Ip.OFFSET + 2;

private static int MAX_HANDLER = 0;
private static UdpHandler[] list = null;
private static int[] ports = null;

private Udp(){
}

public static void init(int handlerCnt){
	MAX_HANDLER = handlerCnt <= 0 ? 4 : handlerCnt;
	list = new UdpHandler[MAX_HANDLER];
	ports = new int[MAX_HANDLER];
}

/**
 Adds a handler for UDP requests.

 @@param port The port the handler wants to listens on.
 @@param h    The handler.
 @@return true, if h gets added, false if the list is full or there is already one handler for that port. */
public static boolean addHandler(int port, UdpHandler h){
	/* DONE should we check if there is already someone listening on the port?
	* According to the code, that calls the handler, we should. */

	synchronized(mutex){
		int n = -1;
		// search for a free place and check that the port is not taken yet
		for(int i = 0; i < MAX_HANDLER; ++i){
			if(list[i] == null){
				n = i;
			} else if(ports[i] == port){
				return false;
			}
		}
		if(n >= 0){
			ports[n] = port;
			list[n] = h;
			return true;
		}
	}
	return false;
}

/** Removes an UDP handler.

 @@param port The port the handler is currently registered to.
 @@return False, if there was no handler registered for that port. True, otherwise.
 */
public static boolean removeHandler(int port){
	synchronized(mutex){
		for(int i = 0; i < MAX_HANDLER; ++i){
			if(list[i] != null){
				if(ports[i] == port){
					list[i] = null;
					return true;
				}
			}
		}
	}
	return false;
}

/* process packet and generate reply if necessary. */
static void process(Packet p, int off){
	int[] buf = p.buf;
	int port = buf[off];
//	int remPort = port>>>16;
	port &= 0xffff;

	synchronized(mutex){
		for(int i = 0; i < MAX_HANDLER; ++i){
			// searching for the handler registered at this port
			if(list[i] != null && ports[i] == port){
				// a checksum of zero indicates disabled checksumming
				if((buf[off + 1]&0xffff) != 0){
					// inject pseudo header for udp checksum into original packet
					// e.g. for packets w/o options: ttl, prot and ip checksum are overwritten
					buf[off - 1] = buf[4]; // dst ip
					buf[off - 2] = buf[3]; // src ip
					buf[off - 3] = (PROTOCOL<<16) + p.len() - (off<<2);
					if(Ip.chkSum(buf, off - 3, p.len() - ((off - 3)<<2)) != 0){
						p.free();	// mark packet free
						return;
					}
				}
				p.setStatus(Packet.APP);
				list[i].request(p, off + 2);
				p.freeIfApp();
				return;
			}
		}
	}
	Icmp.sendPortUnreachable(buf, off);
	p.free();	// mark packet free
}

/**
 Sends an UDP packet. Source IP and port will be chosen by the implementation.

 @@param p          The packet to be sent. The length of the packet needs to be set correctly.
 @@param dstIp      Destination IP.
 @@param dstPort    Destination port.
 @@return true if a packet was sent to a link layer. */
public static boolean send(Packet p, int dstIp, int dstPort){
	return send(p, 0, dstIp, dstPort, dstPort, true);
}

/**
 Sends an UDP packet. Source port will be chosen by the implementation.

 @@param p          The packet to be sent. The length of the packet needs to be set correctly.
 @@param srcIp      Source IP. If 0, the IP of the used interface will be used.
 @@param dstIp      Destination IP.
 @@param dstPort    Destination port.
 @@return true if a packet was sent to a link layer. */
public static boolean send(Packet p, int srcIp, int dstIp, int dstPort){
	return send(p, srcIp, dstIp, dstPort, dstPort, true);
}

/**
 Sends an UDP packet.

 @@param p          The packet to be sent. The length of the packet needs to be set correctly.
 @@param srcIp      Source IP. If 0, the IP of the used interface will be used.
 @@param dstIp      Destination IP.
 @@param srcPort    Source port.
 @@param dstPort    Destination port.
 @@param calcChksum If true the UDP checksum will be calculated and stored in the packet, else the calculation will be
 skipped and the field be set to 0.
 @@return true if a packet was sent to a link layer. */
public static boolean send(Packet p, int srcIp, int dstIp, int srcPort, int dstPort, boolean calcChksum){
	if(p==null)
		return false;
	int off = Ip.OFFSET;
	// 1. UDP header
	int[] buf = p.buf;
	buf[off] = (srcPort<<16) + dstPort;
	buf[off + 1] = (p.len() - 20)<<16;

	if(calcChksum){
		// (optional) 2. UDP pseudo header in front to calc UDP checksum
		if(srcIp == 0){
			LinkLayer ll = Router.getIf(dstIp);
			if(ll == null){
				p.free();
				return false;
			}
			p.setLinkLayer(ll);
			srcIp = ll.getIp();
		}

		buf[off - 1] = dstIp;
		buf[off - 2] = srcIp;
		buf[off - 3] = (PROTOCOL<<16) + p.len() - (off<<2);

		int i = Ip.chkSum(buf, off - 3, p.len() - ((off - 3)<<2));
		buf[off + 1] |= (i == 0) ? 0xffff : i; // special case in UDP: no checksum -> 0, calculated checksum=0 -> 0xffff
	}

	// 3. remains of the IP header
	return Ip.send(p, srcIp, dstIp, PROTOCOL);
}
}
@
