/*
* Copyright 2011 Future Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.krakenapps.dhcp.server;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.List;
import org.krakenapps.dhcp.DhcpMessage;
import org.krakenapps.dhcp.DhcpOption;
import org.krakenapps.dhcp.DhcpOptionCode;
import org.krakenapps.dhcp.model.DhcpOptionConfig;
import org.krakenapps.dhcp.options.ByteConverter;
import org.krakenapps.dhcp.options.DhcpMessageTypeOption;
import org.krakenapps.dhcp.options.DhcpServerIdentifierOption;
import org.krakenapps.dhcp.options.RawDhcpOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DhcpMessageBuilder {
private static InetAddress ZERO_ADDRESS;
static {
try {
ZERO_ADDRESS = InetAddress.getByName("0.0.0.0");
} catch (UnknownHostException e) {
// not reachable
}
}
public static DhcpMessage newOffer(DhcpMessage msg, List<DhcpOptionConfig> configs, InetAddress yourAddress) {
InetAddress serverIp = DhcpDatabase.getServerIdentifier(configs);
DhcpMessage m = new DhcpMessage();
m.setMessageType(2); // boot reply
m.setHardwareType(1); // ethernet
m.setHardwareAddressLength(6);
m.setHops(0);
m.setTransactionId(msg.getTransactionId());
m.setSecs(0);
m.setFlags(msg.getFlags());
m.setClientAddress(ZERO_ADDRESS);
m.setYourAddress(yourAddress);
m.setNextServerAddress(serverIp);
m.setGatewayAddress(msg.getGatewayAddress());
m.setClientMac(msg.getClientMac());
m.setServerName(null);
m.setBootFileName(null);
List<DhcpOption> options = m.getOptions();
options.add(new DhcpMessageTypeOption(DhcpMessage.Type.Offer));
addOptions(configs, options);
return m;
}
public static DhcpMessage newAck(DhcpMessage msg, List<DhcpOptionConfig> configs, InetAddress yourAddress) {
InetAddress serverIp = DhcpDatabase.getServerIdentifier(configs);
DhcpMessage m = new DhcpMessage();
m.setMessageType(2); // boot reply
m.setHardwareType(1); // ethernet
m.setHardwareAddressLength(6);
m.setHops(0);
m.setTransactionId(msg.getTransactionId());
m.setSecs(0);
m.setFlags(msg.getFlags());
m.setClientAddress(ZERO_ADDRESS);
m.setYourAddress(yourAddress);
m.setNextServerAddress(serverIp);
m.setGatewayAddress(msg.getGatewayAddress());
m.setClientMac(msg.getClientMac());
m.setServerName(null);
m.setBootFileName(null);
// 53, 1, 58, 59, 51, 54, 15, 3, 6
List<DhcpOption> options = m.getOptions();
options.add(new DhcpMessageTypeOption(DhcpMessage.Type.Ack));
addOptions(configs, options);
return m;
}
private static void addOptions(List<DhcpOptionConfig> configs, List<DhcpOption> options) {
for (DhcpOptionConfig config : configs) {
byte[] value = encode(config);
if (value != null)
options.add(new RawDhcpOption(config.getType(), value.length, value));
else
warn("kraken dhcp: cannot encode {}, {}", config.getType(), config.getValue());
}
}
public static DhcpMessage newNak(DhcpMessage msg, InetAddress serverIp) {
DhcpMessage m = new DhcpMessage();
m.setMessageType(2); // boot reply
m.setHardwareType(1); // ethernet
m.setHardwareAddressLength(6);
m.setHops(0);
m.setTransactionId(msg.getTransactionId());
m.setSecs(0);
m.setFlags(msg.getFlags());
m.setClientAddress(ZERO_ADDRESS);
m.setYourAddress(ZERO_ADDRESS);
m.setNextServerAddress(ZERO_ADDRESS);
m.setGatewayAddress(ZERO_ADDRESS);
m.setClientMac(msg.getClientMac());
m.setServerName(null);
m.setBootFileName(null);
List<DhcpOption> options = m.getOptions();
options.add(new DhcpMessageTypeOption(DhcpMessage.Type.Nak));
options.add(new DhcpServerIdentifierOption(serverIp));
return m;
}
private static byte[] encode(DhcpOptionConfig c) {
int type = c.getType();
DhcpOptionCode code = DhcpOptionCode.from(type);
if (code == null)
return null;
Class<?> clazz = code.getValueType();
if (clazz == null)
return null;
try {
if (clazz.equals(Boolean.class))
return new byte[] { (byte) (Boolean.parseBoolean(c.getValue()) ? 1 : 0) };
if (clazz.equals(Byte.class))
return new byte[] { Byte.valueOf(c.getValue()) };
if (clazz.equals(String.class))
return c.getValue().getBytes(Charset.forName("utf-8"));
if (clazz.equals(Integer.class))
return ByteConverter.convert(Integer.valueOf(c.getValue()));
if (clazz.equals(Short.class))
return ByteConverter.convert(Short.valueOf(c.getValue()));
if (clazz.equals(InetAddress.class))
return InetAddress.getByName(c.getValue()).getAddress();
if (clazz.equals(Short[].class)) {
String[] tokens = c.getValue().split(",");
byte[] b = new byte[tokens.length * 2];
ByteBuffer bb = ByteBuffer.wrap(b);
for (String token : tokens)
bb.put(ByteConverter.convert(Short.valueOf(token)));
return b;
}
if (clazz.equals(InetAddress[].class)) {
String[] tokens = c.getValue().split(",");
byte[] b = new byte[tokens.length * 4];
ByteBuffer bb = ByteBuffer.wrap(b);
for (String token : tokens)
bb.put(InetAddress.getByName(token).getAddress());
return b;
}
} catch (Exception e) {
}
return null;
}
public static byte[] encode(DhcpMessage msg) {
int optionLength = 0;
for (DhcpOption o : msg.getOptions())
optionLength += (2 + o.getLength());
byte[] b = new byte[241 + optionLength];
ByteBuffer bb = ByteBuffer.wrap(b);
// 28 byte
bb.put(msg.getMessageType());
bb.put(msg.getHardwareType());
bb.put(msg.getHardwareAddressLength());
bb.put(msg.getHops());
bb.putInt(msg.getTransactionId());
bb.putShort(msg.getSecs());
bb.putShort(msg.getFlags());
bb.put(msg.getClientAddress().getAddress());
bb.put(msg.getYourAddress().getAddress());
bb.put(msg.getNextServerAddress().getAddress());
bb.put(msg.getGatewayAddress().getAddress());
byte[] chaddr = new byte[16];
byte[] mac = msg.getClientMac().getBytes();
for (int i = 0; i < 6; i++)
chaddr[i] = mac[i];
// 212 bytes
bb.put(chaddr);
bb.put(new byte[64]);
bb.put(new byte[128]);
bb.put(new byte[] { 0x63, (byte) 0x82, 0x53, 0x63 });
for (DhcpOption o : msg.getOptions()) {
bb.put(o.getType());
bb.put((byte) o.getLength());
bb.put(o.getValue());
}
// end option
bb.put((byte) 0xff);
return b;
}
private static void warn(String msg, Object... args) {
Logger logger = LoggerFactory.getLogger(DhcpMessageBuilder.class.getName());
logger.warn(msg, args);
}
}