001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.directory.server.dhcp.io; 022 023 024import java.io.UnsupportedEncodingException; 025import java.net.InetAddress; 026import java.nio.ByteBuffer; 027import java.util.Iterator; 028 029import org.apache.directory.server.dhcp.messages.DhcpMessage; 030import org.apache.directory.server.dhcp.messages.HardwareAddress; 031import org.apache.directory.server.dhcp.options.DhcpOption; 032import org.apache.directory.server.dhcp.options.OptionsField; 033import org.apache.directory.server.dhcp.options.dhcp.DhcpMessageType; 034import org.apache.directory.server.i18n.I18n; 035 036 037/** 038 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 039 */ 040public class DhcpMessageEncoder 041{ 042 /** 043 * Converts a DhcpMessage object into a byte buffer. 044 * 045 * @param byteBuffer ByteBuffer to put DhcpMessage into 046 * @param message DhcpMessage to encode into ByteBuffer 047 */ 048 public void encode( ByteBuffer byteBuffer, DhcpMessage message ) 049 { 050 byteBuffer.put( message.getOp() ); 051 052 HardwareAddress hardwareAddress = message.getHardwareAddress(); 053 054 byteBuffer.put( ( byte ) ( null != hardwareAddress ? hardwareAddress.getType() : 0 ) ); 055 byteBuffer.put( ( byte ) ( null != hardwareAddress ? hardwareAddress.getLength() : 0 ) ); 056 byteBuffer.put( ( byte ) message.getHopCount() ); 057 byteBuffer.putInt( message.getTransactionId() ); 058 byteBuffer.putShort( ( short ) message.getSeconds() ); 059 byteBuffer.putShort( message.getFlags() ); 060 061 writeAddress( byteBuffer, message.getCurrentClientAddress() ); 062 writeAddress( byteBuffer, message.getAssignedClientAddress() ); 063 writeAddress( byteBuffer, message.getNextServerAddress() ); 064 writeAddress( byteBuffer, message.getRelayAgentAddress() ); 065 066 writeBytes( byteBuffer, ( null != hardwareAddress ? hardwareAddress.getAddress() : new byte[] 067 {} ), 16 ); 068 069 writeString( byteBuffer, message.getServerHostname(), 64 ); 070 writeString( byteBuffer, message.getBootFileName(), 128 ); 071 072 OptionsField options = message.getOptions(); 073 074 // update message type option (if set) 075 if ( null != message.getMessageType() ) 076 { 077 options.add( new DhcpMessageType( message.getMessageType() ) ); 078 } 079 080 encodeOptions( options, byteBuffer ); 081 } 082 083 084 /** 085 * Write a zero-terminated string to a field of len bytes. 086 * 087 * @param byteBuffer 088 * @param serverHostname 089 * @param i 090 */ 091 private void writeString( ByteBuffer byteBuffer, String string, int len ) 092 { 093 if ( null == string ) 094 { 095 string = ""; 096 } 097 098 try 099 { 100 byte[] sbytes = string.getBytes( "ASCII" ); 101 102 // writeBytes will automatically zero-pad and thus terminate the 103 // string. 104 writeBytes( byteBuffer, sbytes, len ); 105 } 106 catch ( UnsupportedEncodingException e ) 107 { 108 // should not happen 109 throw new RuntimeException( I18n.err( I18n.ERR_635 ), e ); 110 } 111 } 112 113 114 /** 115 * Write an InetAddress to the byte buffer. 116 * 117 * @param byteBuffer 118 * @param currentClientAddress 119 */ 120 private void writeAddress( ByteBuffer byteBuffer, InetAddress currentClientAddress ) 121 { 122 if ( null == currentClientAddress ) 123 { 124 byte[] emptyAddress = 125 { 0, 0, 0, 0 }; 126 byteBuffer.put( emptyAddress ); 127 } 128 else 129 { 130 byte[] addressBytes = currentClientAddress.getAddress(); 131 byteBuffer.put( addressBytes ); 132 } 133 } 134 135 136 /** 137 * Write an array of bytes to the buffer. Write exactly len bytes, 138 * truncating if more than len, padding if less than len bytes are 139 * available. 140 * 141 * @param byteBuffer 142 * @param currentClientAddress 143 */ 144 private void writeBytes( ByteBuffer byteBuffer, byte[] bytes, int len ) 145 { 146 if ( null == bytes ) 147 { 148 bytes = new byte[] 149 {}; 150 } 151 152 byteBuffer.put( bytes, 0, Math.min( len, bytes.length ) ); 153 154 // pad as necessary 155 int remain = len - bytes.length; 156 157 while ( remain-- > 0 ) 158 { 159 byteBuffer.put( ( byte ) 0 ); 160 } 161 } 162 163 private static final byte[] VENDOR_MAGIC_COOKIE = 164 { ( byte ) 99, ( byte ) 130, ( byte ) 83, ( byte ) 99 }; 165 166 167 public void encodeOptions( OptionsField options, ByteBuffer message ) 168 { 169 message.put( VENDOR_MAGIC_COOKIE ); 170 171 for ( Iterator i = options.iterator(); i.hasNext(); ) 172 { 173 DhcpOption option = ( DhcpOption ) i.next(); 174 option.writeTo( message ); 175 } 176 177 // add end option 178 message.put( ( byte ) 0xff ); 179 } 180}