/* * Copyright (c) 2006-2007 Graz University of Technology. 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. The names "Graz University of Technology" and "IAIK of Graz University of * Technology" must not be used to endorse or promote products derived from * this software without prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED 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 LICENSOR 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 ejip.jtcpip; import java.io.IOException; import javax.microedition.io.Datagram; import ejip.jtcpip.util.Debug; import ejip.jtcpip.util.NumFunctions; import ejip.jtcpip.util.StringFunctions; /** * Handles a UDP connection (port and IO-stream combination) If the remoteIP == * 0 the first source IP address and port will be set as remoteIP and remotePort * and we can't sent befor receive. * * If remoteIP == 255.255.255.255 we accept UDP packets from everyone and can * send broadcasts * * @author Ulrich Feichter * @author Tobias Kellner * @author Christof Rath * @version $Rev: 984 $ $Date: 2007/09/11 00:16:50 $ */ public class UDPConnection { /** Free <code>UDPConnection</code> */ protected final static byte UDP_CONN_FREE = 0x01; /** Used <code>UDPConnection</code> */ protected final static byte UDP_CONN_USED = 0x02; /** The <code>UDPConnection</code> pool */ protected static UDPConnection[] pool ; /** Stores the status of a certain connection */ protected byte status; /** port of the local host */ private int localPort; /** * If incomingDatagram != null there is a blocked receive() call. Fill the * DatagramPacket not the stream! */ private Datagram incomingDatagram = null; public static void init(){ pool = new UDPConnection[StackParameters.UDP_CONNECTION_POOL_SIZE]; } /** * Look for a UDPConnection with matching source IP and port and destination * port. Returns null if no matching connection was found. * * @param pay * The payload where we seek an connection for * @return <code>UDPConnection</code> object if available or null if no * connection was found */ protected static UDPConnection getConnection(Payload pay) { for (int i = 0; i < StackParameters.UDP_CONNECTION_POOL_SIZE; i++) if ((pool[i] != null) && pool[i].isIncomingPayPartOfConn(pay)) return pool[i]; return null; } /** * Look for a UDPConnection with matching source IP and port and destination * port. Returns null if no matching connection was found. * * @param destPort * The destination port to look for * @param srcIP * The source IP to look for * @param srcPort * The source port to look for * @return <code>UDPConnection</code> object if available or null if no * connection was found */ protected static UDPConnection getConnection(short destPort, int srcIP, short srcPort) { for (int i = 0; i < StackParameters.UDP_CONNECTION_POOL_SIZE; i++) if (pool[i] != null && pool[i].localPort == destPort) { /* * if (pool[i].remoteIP == 0) { pool[i].remoteIP = srcIP; * pool[i].remotePort = srcPort; } * * * //if remoteIP == 255.255.255.255 accept every payload if * (pool[i].remoteIP == 0xFFFFFFFF || (pool[i].remoteIP == srcIP && * pool[i].remotePort == srcPort)) */return pool[i]; } return null; } /** * Create a new <code>UDPConnection</code>. Tries to create a new * UDPConnection. If the connection pool is full, or there is already a * listening connection at the specified port, null is returned. * * @param localPort * @return The new <code>UDPConnection</code> or null if unsuccessful */ public static UDPConnection newConnection(int localPort) { if (localPort < 0 || localPort > 0xFFFF) return null; for (byte i = 0; i < StackParameters.UDP_CONNECTION_POOL_SIZE; i++) { if (pool[i] == null) pool[i] = new UDPConnection(localPort); if (pool[i].status == UDP_CONN_USED) { if (pool[i].localPort == localPort) return null; continue; } // Found the first free connection // Now test if the rest of the connections do not use the same port for (int j = i + 1; j < StackParameters.UDP_CONNECTION_POOL_SIZE; j++) if ((pool[j] != null) && (pool[j].status == UDP_CONN_USED) && (pool[j].localPort == localPort)) return null; // The connection at position i is free and no one else is // using the same port pool[i].status = UDP_CONN_USED; cleanUpConnection(pool[i]); pool[i].localPort = localPort; return pool[i]; } return null; } /** * Create a new <code>UDPConnection</code>. Tries to create a new * UDPConnection. If the connection pool is full, or there is already a * listening connection at the specified port, null is returned. * * @param localPort * @param remoteIP * @param remotePort * * @return The new <code>UDPConnection</code> or null if unsuccessful */ public static UDPConnection newConnection(int localPort, int remoteIP, int remotePort) { if (localPort < 0 || localPort > 0xFFFF || remotePort < 0 || remotePort > 0xFFFF) return null; UDPConnection conn = newConnection(localPort); /* * if (conn != null) { conn.remoteIP = remoteIP; conn.remotePort = * remotePort; } */ return conn; } /** * Prepares a connection to remove possible artefacts of an old connection. * * @param conn */ private static void cleanUpConnection(UDPConnection conn) { /* * conn.remoteIP = 0; conn.remotePort = 0; */conn.localPort = 0; } /** * Create a <code>UDPConnection</code>. This constructor is private, * because <code>UDPConnection</code>s can only be got through * {@link #newConnection(int)}. Initializes some internal variables. * * @param port */ private UDPConnection(int port) { status = UDP_CONN_FREE; // iStream = new // UDPInputStream(StackParameters.UDP_CONNECTION_RCV_BUFFER_SIZE); // oStream = new // UDPOutputStream(StackParameters.UDP_CONNECTION_RCV_BUFFER_SIZE); cleanUpConnection(this); localPort = port; } /** * Returns true if a given Payload is part of this connection * * @param pay * @return boolean */ protected boolean isIncomingPayPartOfConn(Payload pay) { return UDPPacket.getDestPort(pay) == localPort; } /** * Actively closes the UDP Connection. */ public void close() { status = UDP_CONN_FREE; cleanUpConnection(this); } /** * Sends one datagram * * @param p * @throws IOException */ public void send(Datagram p) throws IOException { Payload pay = null; int dgRemPort = StringFunctions.getPortFromConnectorStr(p.getAddress()); int dgRemAddr = dgRemPort > -1 ? StringFunctions.getAddrFromConnectorStr(p.getAddress()) : 0; while (true) { pay = UDP.preparePayload((short) dgRemPort, (short) localPort); if (pay != null) break; try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } UDPPacket.setData(pay, ((DatagramPacket) p).getBuffer(), 0, p.getLength()); UDPPacket.setLength(pay, (short) pay.length); // Sets the length in // the UDP header if (Debug.enabled) Debug.println("Trying to send a UDP connection portion", Debug.DBG_UDP); IP.asyncSendPayload(pay, dgRemAddr, IP.PROT_UDP); } /** * Adds the content of the payload to the UDPInputStream * * @param pay */ synchronized protected void receivePayload(Payload pay) { if (incomingDatagram != null) { try { int i = 0; for (i = UDPPacket.getDataOffset(pay); i < pay.length / 4; i++) incomingDatagram.writeInt(pay.payload[i]); switch (pay.length % 4) { case 1: incomingDatagram.write(pay.payload[i] >> 24); break; case 2: incomingDatagram.writeShort(pay.payload[i] >> 16); break; case 3: incomingDatagram.writeShort(pay.payload[i] >> 16); incomingDatagram.write(pay.payload[i] >> 8); break; } incomingDatagram.setAddress(IP.ipIntToString(IPPacket.getSrcAddr(pay)) + ":" + UDPPacket.getSourcePort(pay)); incomingDatagram = null; //TODO //notifyAll(); } catch (IOException e) { } } } /** * Receives a datagram packet from this socket. When this method returns, * the DatagramPacket's buffer is filled with the data received. The * datagram packet also contains the sender's IP address, and the port * number on the sender's machine. * <p> * This method blocks until a datagram is received. The length field of the * datagram packet object contains the length of the received message. If * the message is longer than the packet's length, the message is truncated. * * @param datagram * the DatagramPacket into which to place the incoming data. */ synchronized public void receive(Datagram datagram) { incomingDatagram = datagram; incomingDatagram.reset(); //TODO // wait(); //wait(StackParameters.UDP_RECV_TIMEOUT); } /** * @return a random unused port number */ public static short newLocalPort() { int randPort = NumFunctions.rand.nextInt() & 0xFFFF; for (int i = 0; i < StackParameters.UDP_CONNECTION_POOL_SIZE; i++) if (pool[i] != null && pool[i].status == UDP_CONN_USED && pool[i].localPort == randPort) return newLocalPort(); return (short) randPort; } }