/*
* 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_old.CS8900;
import ejip_old.Net;
import ejip.jtcpip.util.Debug;
import ejip.jtcpip.util.NumFunctions;
/**
* Implements a basic DHCP client
*
* @author Ulrich Feichter
* @author Tobias Kellner
* @author Christof Rath
* @version $Rev: 975 $ $Date: 2009/01/12 23:00:13 $
*/
public class DHCPClient
{
/**
* UDP connection to the DHCP server to the server port 67 from the server
* port 68
*/
private static UDPConnection conn;
/** buffer for the datagram */
private static byte[] buffer;
/** datagram for the communication */
private static Datagram dg;
/** transaction id */
private static int transID;
/** Timeout when we have to renew the lease */
private static long renewalTimeout;
/** IP address lease time */
private static int leaseTimeSec;
/** Server identifier (ip address) */
private static int serverID;
/** Time when we got the lease (required for the lease timeout) */
private static long ackTime;
/** time in seconds between two trys */
public static int retryTimeout = 15;
public static void init(){
buffer = new byte[StackParameters.UDP_DATA_SIZE];
conn = UDPConnection.newConnection(68, 0xFFFFFFFF, 67);
dg = new DatagramPacket(buffer, buffer.length, "//255.255.255.255:67");
}
/**
* forbids instantiation
*/
private DHCPClient()
{
}
/**
* Creates a new transaction ID and resets the datagram
*/
private static void start()
{
transID = NumFunctions.rand.nextInt();
renewalTimeout = 0;
leaseTimeSec = 0;
serverID = 0;
ackTime = 0;
Net.linkLayer.gateway = 0;
Net.linkLayer.netmask = 0;
}
/**
* Sends a DHCP discover datagram.
*
* @return the datagram with the DHCP server answer
* @throws IOException
*/
private static Datagram discover() throws IOException
{
dg.reset();
dg.write(1); // OP: request
dg.write(1); // hw_type: ethernet
dg.write(6); // hw_addr_len: 6 for ethernet
dg.write(0); // hops
dg.writeInt(transID); // transaction ID
dg.writeShort(0); // secs: time elapsed since start of DHCP request
dg.writeShort(0); // flags: MSB set rest must be zero
dg.writeInt(0); // client ip address
dg.writeInt(0); // your (client) ip address
dg.writeInt(0); // server ip address
dg.writeInt(0); // relay agent ip address
dg.write(CS8900.eth[0]); // client hardware address
dg.write(CS8900.eth[1]); // s.a.a.
dg.write(CS8900.eth[2]); // s.a.a.
dg.write(CS8900.eth[3]); // s.a.a.
dg.write(CS8900.eth[4]); // s.a.a.
dg.write(CS8900.eth[5]); // s.a.a.
dg.writeShort(0); // s.a.a.
dg.writeInt(0); // s.a.a.
dg.writeInt(0); // s.a.a.
dg.writeLong(0); // server host name
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0); // boot file name
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
// Options (1 byte option type, 1 byte option length in bytes, n bytes
// option value)
dg.writeInt(0x63825363); // Magic cookie
dg.write(53); // DHCP Msg type
dg.write(1); // only one byte of data
dg.write(1); // DHCP discover
dg.write(61); // Client identifier
dg.write(7); // 7 bytes of data
dg.write(1); // 1 for Ethernet addr
dg.write(CS8900.eth[0]); // client hardware address
dg.write(CS8900.eth[1]); // s.a.a.
dg.write(CS8900.eth[2]); // s.a.a.
dg.write(CS8900.eth[3]); // s.a.a.
dg.write(CS8900.eth[4]); // s.a.a.
dg.write(CS8900.eth[5]); // s.a.a.
dg.write(12); // Host name
dg.write(6); // 6 bytes of data
dg.writeChar('j'); // jcpip
dg.writeChar('t');
dg.writeChar('c');
dg.writeChar('p');
dg.writeChar('i');
dg.writeChar('p');
dg.write(55); // Parameter Request List
dg.write(2); // 2 bytes of data
dg.write(1); // Subnetmask
dg.write(3); // Gateway
dg.write(0xFF); // End of Options
conn.send(dg);
conn.receive(dg);
if (dg.getLength() != 0)
{
dg.skipBytes(28); //jump to chaddr (client hardware address)
//and check if the received datagram is for our MAC address
boolean isOurs = true;
for (int i = 0; i < 6; i++)
if (dg.readUnsignedByte() != CS8900.eth[i])
{
isOurs = false;
break;
}
if (!isOurs)
dg.reset();
else dg.skipBytes(-34);
}
return dg;
}
/**
* Sends a DHCP request for a given IP address
*
* @param ipAddr
* @return DHCP server answer
* @throws IOException
*/
private static Datagram request(int ipAddr) throws IOException
{
dg.reset();
dg.write(1); // OP: request
dg.write(1); // hw_type: ethernet
dg.write(6); // hw_addr_len: 6 for ethernet
dg.write(0); // hops
dg.writeInt(transID); // transaction ID
dg.writeShort(0); // secs: time elapsed since start of DHCP request
// but must be the same as at discover!!!!
dg.writeShort(0x0000); // flags: MSB = uni/broadcast; rest must be zero
dg.writeInt(ipAddr); // client ip address
dg.writeInt(0); // your (client) ip address
dg.writeInt(0); // server ip address
dg.writeInt(0); // relay agent ip address
dg.write(CS8900.eth[0]); // client hardware address
dg.write(CS8900.eth[1]); // s.a.a.
dg.write(CS8900.eth[2]); // s.a.a.
dg.write(CS8900.eth[3]); // s.a.a.
dg.write(CS8900.eth[4]); // s.a.a.
dg.write(CS8900.eth[5]); // s.a.a.
dg.writeShort(0); // s.a.a.
dg.writeInt(0); // s.a.a.
dg.writeInt(0); // s.a.a.
dg.writeLong(0); // server host name
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0); // boot file name
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
dg.writeLong(0);
// Options (1 byte option type, 1 byte option length in bytes, n bytes
// option value)
dg.writeInt(0x63825363); // Magic cookie
dg.write(53); // DHCP Msg type
dg.write(1); // only one byte of data
dg.write(3); // DHCP request
dg.write(61); // Client identifier
dg.write(7); // 7 bytes of data
dg.write(1); // 1 for Ethernet addr
dg.write(CS8900.eth[0]); // client hardware address
dg.write(CS8900.eth[1]); // s.a.a.
dg.write(CS8900.eth[2]); // s.a.a.
dg.write(CS8900.eth[3]); // s.a.a.
dg.write(CS8900.eth[4]); // s.a.a.
dg.write(CS8900.eth[5]); // s.a.a.
dg.write(12); // Host name
dg.write(6); // 6 bytes of data
dg.writeChar('j'); // jcpip
dg.writeChar('t');
dg.writeChar('c');
dg.writeChar('p');
dg.writeChar('i');
dg.writeChar('p');
dg.write(55); // Parameter Request List
dg.write(3); // 2 bytes of data
dg.write(1); // Subnetmask
dg.write(3); // Gateway
dg.write(51); // Lease time
dg.write(0xFF); // End of Options
conn.send(dg);
conn.receive(dg);
if (dg.getLength() != 0)
{
dg.skipBytes(28); //jump to chaddr (client hardware address)
//and check if the received datagram is for our MAC address
boolean isOurs = true;
for (int i = 0; i < 6; i++)
if (dg.readUnsignedByte() != CS8900.eth[i])
{
isOurs = false;
break;
}
if (!isOurs)
dg.reset();
else dg.skipBytes(-34);
}
return dg;
}
/**
* Sets the IP address, the Subnet mask and the Gateway
*
* @return true if the DHCP ACK has been received
*/
public static boolean setNetParams()
{
try
{
int offeredIP = 0;
start();
discover();
if (dg.getLength() == 0)
{
return false;
}
dg.setAddress("//255.255.255.255:67"); // send again a broadcast
dg.skipBytes(16); // jump to yiaddr
offeredIP = dg.readInt();
dg.skipBytes(8); // jump to chaddr (client MAC)
request(offeredIP);
if (dg.getLength() == 0)
{
return false;
}
return processOptions();
} catch (IOException e)
{
e.printStackTrace();
return false;
}
}
/**
* Checks if the renewalTimeout has been reached and send a DHCP request in
* case
*
* @return true if everything went right
*/
public static boolean renewIfNecessary()
{
if (renewalTimeout > System.currentTimeMillis())
return true;
try
{
start();
request(Net.linkLayer.ip);
if (dg.getLength() == 0)
{
return false;
}
return processOptions();
} catch (IOException e)
{
e.printStackTrace();
return false;
}
}
/**
* @return true if the processed datagram was a DHCP ACK packet
* @throws IOException
*/
private static boolean processOptions() throws IOException
{
int offeredIP;
dg.skipBytes(16); // jump to yiaddr
offeredIP = dg.readInt(); // read yiaddr
Net.linkLayer.ip = offeredIP;
dg.skipBytes(220); // jump to options (after magic cookie)
int opt; // DHCP option
int cnt; // count of option value bytes
int value = 0; // option value;
boolean result = false;
while ((opt = dg.readUnsignedByte()) != 0xFF)
{
cnt = dg.readUnsignedByte();
switch (cnt)
{
case 1:
value = dg.readUnsignedByte();
break;
case 2:
value = dg.readUnsignedShort();
break;
case 4:
value = dg.readInt();
break;
default:
for (int i = 0; i < cnt; i++)
dg.readByte();
}
// if (Debug.enabled)
// Debug.println("DHCP opt: " + opt + " cnt: " + cnt + " val: " + value, Debug.DBG_OTHER);
if (Debug.enabled)
Debug.println("DHCP opt", Debug.DBG_OTHER);
// interpret the options
switch (opt)
{
case 53: // DHCP Msg type
result = (value == 5); // 5: DHCP ACK
if (result)
ackTime = System.currentTimeMillis();
break;
case 58: // renewal time
renewalTimeout = value;
break;
case 51: // address lease time
leaseTimeSec = value;
break;
case 54: // server identifier
serverID = value;
break;
case 1: // subnet mask
Net.linkLayer.netmask = value;
break;
case 3: // gateway
Net.linkLayer.gateway = value;
break;
}
}
if (renewalTimeout > 0) // there has been a 'renewal time'-option
renewalTimeout = ackTime + renewalTimeout * 1000; // as the
// ackTime is in
// seconds
else if (leaseTimeSec > 0) // at least there has been an 'address lease
// time'-option
renewalTimeout = ackTime + leaseTimeSec * 500; // according to RFC
// if not given by
// the server it
// should be 0.5 of
// the leas time
else
renewalTimeout = ackTime + 3600000; // send renewal every hour
return result;
}
/**
* @return the address lease time in hours
*/
public static int getLeaseTime()
{
return leaseTimeSec / 3600;
}
/**
* @return the renewal time in hours
*/
public static int getRenewalTime()
{
return (int) (renewalTimeout - ackTime) / 3600000;
}
/**
* @return the server IP address
*/
public static int getServerIP()
{
return serverID;
}
}