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 * https://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 */ 020package org.apache.directory.api.ldap.codec.api; 021 022 023import java.nio.ByteBuffer; 024import java.util.Iterator; 025import java.util.Map; 026 027import org.apache.directory.api.asn1.EncoderException; 028import org.apache.directory.api.asn1.ber.tlv.BerValue; 029import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 030import org.apache.directory.api.asn1.util.Asn1Buffer; 031import org.apache.directory.api.i18n.I18n; 032import org.apache.directory.api.ldap.codec.factory.AbandonRequestFactory; 033import org.apache.directory.api.ldap.codec.factory.AddRequestFactory; 034import org.apache.directory.api.ldap.codec.factory.AddResponseFactory; 035import org.apache.directory.api.ldap.codec.factory.BindRequestFactory; 036import org.apache.directory.api.ldap.codec.factory.BindResponseFactory; 037import org.apache.directory.api.ldap.codec.factory.CompareRequestFactory; 038import org.apache.directory.api.ldap.codec.factory.CompareResponseFactory; 039import org.apache.directory.api.ldap.codec.factory.DeleteRequestFactory; 040import org.apache.directory.api.ldap.codec.factory.DeleteResponseFactory; 041import org.apache.directory.api.ldap.codec.factory.ExtendedRequestFactory; 042import org.apache.directory.api.ldap.codec.factory.ExtendedResponseFactory; 043import org.apache.directory.api.ldap.codec.factory.IntermediateResponseFactory; 044import org.apache.directory.api.ldap.codec.factory.ModifyDnRequestFactory; 045import org.apache.directory.api.ldap.codec.factory.ModifyDnResponseFactory; 046import org.apache.directory.api.ldap.codec.factory.ModifyRequestFactory; 047import org.apache.directory.api.ldap.codec.factory.ModifyResponseFactory; 048import org.apache.directory.api.ldap.codec.factory.SearchRequestFactory; 049import org.apache.directory.api.ldap.codec.factory.SearchResultDoneFactory; 050import org.apache.directory.api.ldap.codec.factory.SearchResultEntryFactory; 051import org.apache.directory.api.ldap.codec.factory.SearchResultReferenceFactory; 052import org.apache.directory.api.ldap.codec.factory.UnbindRequestFactory; 053import org.apache.directory.api.ldap.model.message.Control; 054import org.apache.directory.api.ldap.model.message.Message; 055import org.apache.directory.api.ldap.model.message.Request; 056 057 058/** 059 * LDAP BER encoder. 060 * 061 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 062 */ 063public final class LdapEncoder 064{ 065 /** 066 * Make this final class impossible to instaciate from teh outside 067 */ 068 private LdapEncoder() 069 { 070 // Nothing to do 071 } 072 073 074 /** 075 * Encode a control to a byte[]. The controls are encoded recursively, to start with the last 076 * control first. 077 * <br> 078 * A control is encoded as: 079 * <pre> 080 * 0x30 LL 081 * 0x04 LL abcd control OID 082 * [0x01 0x01 0x00/0xFF] control criticality 083 * [0x04 LL value] control value 084 * </pre> 085 * 086 * 087 * @param buffer The buffer that will contain the encoded control 088 * @param codec The LdapApiService instance 089 * @param controls The controls to encode 090 * @param iterator The Iterator instance we are processing 091 * @param isRequest A flag set to <tt>true</tt> if the LdapMessage is a request 092 * @throws EncoderException If the encoding failed 093 */ 094 private static void encodeControls( Asn1Buffer buffer, LdapApiService codec, 095 Map<String, Control> controls, Iterator<String> iterator, boolean isRequest ) throws EncoderException 096 { 097 if ( iterator.hasNext() ) 098 { 099 // Get the Control from its OID 100 Control control = controls.get( iterator.next() ); 101 102 // Encode the remaining controls recursively 103 encodeControls( buffer, codec, controls, iterator, isRequest ); 104 105 // Fetch the control's factory from the LdapApiService 106 ControlFactory<?> controlFactory; 107 108 if ( isRequest ) 109 { 110 controlFactory = codec.getRequestControlFactories().get( control.getOid() ); 111 } 112 else 113 { 114 controlFactory = codec.getResponseControlFactories().get( control.getOid() ); 115 } 116 117 if ( controlFactory == null ) 118 { 119 throw new EncoderException( I18n.err( I18n.ERR_08002_CANNOT_FIND_CONTROL_FACTORY, control.getOid() ) ); 120 } 121 122 int start = buffer.getPos(); 123 124 // the value, if any 125 controlFactory.encodeValue( buffer, control ); 126 127 if ( buffer.getPos() != start ) 128 { 129 // The control value sequence, as an OctetString 130 BerValue.encodeSequence( buffer, ( byte ) UniversalTag.OCTET_STRING.getValue(), start ); 131 } 132 133 // The criticality 134 if ( control.isCritical() ) 135 { 136 BerValue.encodeBoolean( buffer, control.isCritical() ); 137 } 138 139 // The OID 140 BerValue.encodeOctetString( buffer, control.getOid() ); 141 142 // The Control Sequence 143 BerValue.encodeSequence( buffer, start ); 144 } 145 } 146 147 148 /** 149 * Encode the protocolOp part of a message 150 * 151 * @param buffer The buffer that will contain the encoded control 152 * @param codec The LdapApiService instance 153 * @param message The message to encode 154 */ 155 private static void encodeProtocolOp( Asn1Buffer buffer, LdapApiService codec, Message message ) 156 { 157 switch ( message.getType() ) 158 { 159 case ABANDON_REQUEST : 160 AbandonRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 161 return; 162 163 case ADD_REQUEST : 164 AddRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 165 return; 166 167 case ADD_RESPONSE: 168 AddResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 169 return; 170 171 case BIND_REQUEST : 172 BindRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 173 return; 174 175 case BIND_RESPONSE : 176 BindResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 177 return; 178 179 case COMPARE_REQUEST : 180 CompareRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 181 return; 182 183 case COMPARE_RESPONSE : 184 CompareResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 185 return; 186 187 case DEL_REQUEST : 188 DeleteRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 189 return; 190 191 case DEL_RESPONSE : 192 DeleteResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 193 return; 194 195 case EXTENDED_REQUEST : 196 ExtendedRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 197 return; 198 199 case EXTENDED_RESPONSE : 200 ExtendedResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 201 return; 202 203 case INTERMEDIATE_RESPONSE : 204 IntermediateResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 205 return; 206 207 case MODIFY_REQUEST : 208 ModifyRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 209 return; 210 211 case MODIFY_RESPONSE : 212 ModifyResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 213 return; 214 215 case MODIFYDN_REQUEST : 216 ModifyDnRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 217 return; 218 219 case MODIFYDN_RESPONSE : 220 ModifyDnResponseFactory.INSTANCE.encodeReverse( codec, buffer, message ); 221 return; 222 223 case SEARCH_REQUEST : 224 SearchRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 225 return; 226 227 case SEARCH_RESULT_DONE : 228 SearchResultDoneFactory.INSTANCE.encodeReverse( codec, buffer, message ); 229 return; 230 231 case SEARCH_RESULT_ENTRY : 232 SearchResultEntryFactory.INSTANCE.encodeReverse( codec, buffer, message ); 233 return; 234 235 case SEARCH_RESULT_REFERENCE : 236 SearchResultReferenceFactory.INSTANCE.encodeReverse( codec, buffer, message ); 237 return; 238 239 case UNBIND_REQUEST : 240 UnbindRequestFactory.INSTANCE.encodeReverse( codec, buffer, message ); 241 return; 242 243 default: 244 // Nothing to do 245 } 246 } 247 248 249 /** 250 * Generate the PDU which contains the encoded object. 251 * 252 * The generation is done in two phases : 253 * - first, we compute the length of each part and the 254 * global PDU length 255 * - second, we produce the PDU. 256 * 257 * <pre> 258 * 0x30 L1 259 * | 260 * +--> 0x02 L2 MessageId 261 * +--> ProtocolOp 262 * +--> Controls 263 * 264 * L2 = Length(MessageId) 265 * L1 = Length(0x02) + Length(L2) + L2 + Length(ProtocolOp) + Length(Controls) 266 * LdapMessageLength = Length(0x30) + Length(L1) + L1 267 * </pre> 268 * 269 * @param buffer The Asn1Buffer instance in which we store the temporary result 270 * @param codec The LdapApiService instance 271 * @param message The message to encode 272 * @return A ByteBuffer that contains the PDU 273 * @throws EncoderException If anything goes wrong. 274 */ 275 public static ByteBuffer encodeMessage( Asn1Buffer buffer, LdapApiService codec, Message message ) throws EncoderException 276 { 277 int start = buffer.getPos(); 278 279 // The controls, if any 280 Map<String, Control> controls = message.getControls(); 281 282 if ( ( controls != null ) && ( controls.size() > 0 ) ) 283 { 284 encodeControls( buffer, codec, controls, controls.keySet().iterator(), message instanceof Request ); 285 286 // The controls tag 287 BerValue.encodeSequence( buffer, ( byte ) LdapCodecConstants.CONTROLS_TAG, start ); 288 } 289 290 // The protocolOp part 291 encodeProtocolOp( buffer, codec, message ); 292 293 // The message Id 294 BerValue.encodeInteger( buffer, message.getMessageId() ); 295 296 // The LdapMessage Sequence 297 BerValue.encodeSequence( buffer ); 298 299 return buffer.getBytes(); 300 } 301}