001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009-2010 Sun Microsystems, Inc. 015 * Portions copyright 2011-2013 ForgeRock AS. 016 */ 017 018package org.forgerock.opendj.io; 019 020import java.io.IOException; 021import java.util.List; 022 023import org.forgerock.i18n.slf4j.LocalizedLogger; 024import org.forgerock.opendj.ldap.ByteString; 025import org.forgerock.opendj.ldap.DN; 026import org.forgerock.opendj.ldap.Modification; 027import org.forgerock.opendj.ldap.controls.Control; 028import org.forgerock.opendj.ldap.requests.AbandonRequest; 029import org.forgerock.opendj.ldap.requests.AddRequest; 030import org.forgerock.opendj.ldap.requests.CompareRequest; 031import org.forgerock.opendj.ldap.requests.DeleteRequest; 032import org.forgerock.opendj.ldap.requests.ExtendedRequest; 033import org.forgerock.opendj.ldap.requests.GenericBindRequest; 034import org.forgerock.opendj.ldap.requests.ModifyDNRequest; 035import org.forgerock.opendj.ldap.requests.ModifyRequest; 036import org.forgerock.opendj.ldap.requests.SearchRequest; 037import org.forgerock.opendj.ldap.requests.UnbindRequest; 038import org.forgerock.opendj.ldap.responses.BindResult; 039import org.forgerock.opendj.ldap.responses.CompareResult; 040import org.forgerock.opendj.ldap.responses.ExtendedResult; 041import org.forgerock.opendj.ldap.responses.IntermediateResponse; 042import org.forgerock.opendj.ldap.responses.Result; 043import org.forgerock.opendj.ldap.responses.SearchResultEntry; 044import org.forgerock.opendj.ldap.responses.SearchResultReference; 045 046/** 047 * Writes LDAP messages to an underlying ASN.1 writer. 048 * <p> 049 * Methods for creating {@link LDAPWriter}s are provided in the {@link LDAP} 050 * class. 051 * 052 * @param <W> 053 * The type of ASN.1 writer used for encoding elements. 054 */ 055public final class LDAPWriter<W extends ASN1Writer> { 056 /** @Checkstyle:ignore AvoidNestedBlocks */ 057 058 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 059 private final W writer; 060 061 LDAPWriter(final W asn1Writer) { 062 this.writer = asn1Writer; 063 } 064 065 /** 066 * Returns the ASN.1 writer to which LDAP messages will be written. 067 * 068 * @return The ASN.1 writer to which LDAP messages will be written. 069 */ 070 public W getASN1Writer() { 071 return writer; 072 } 073 074 /** 075 * Writes the provided abandon request. 076 * 077 * @param messageID 078 * The LDAP message ID. 079 * @param request 080 * The request. 081 * @throws IOException 082 * If an unexpected IO error occurred. 083 */ 084 public void writeAbandonRequest(final int messageID, final AbandonRequest request) 085 throws IOException { 086 logger.trace("ENCODE LDAP ABANDON REQUEST(messageID=%d, request=%s)", messageID, request); 087 writeMessageHeader(messageID); 088 { 089 writer.writeInteger(LDAP.OP_TYPE_ABANDON_REQUEST, request.getRequestID()); 090 } 091 writeMessageFooter(request.getControls()); 092 } 093 094 /** 095 * Writes the provided add request. 096 * 097 * @param messageID 098 * The LDAP message ID. 099 * @param request 100 * The request. 101 * @throws IOException 102 * If an unexpected IO error occurred. 103 */ 104 public void writeAddRequest(final int messageID, final AddRequest request) throws IOException { 105 logger.trace("ENCODE LDAP ADD REQUEST(messageID=%d, request=%s)", messageID, request); 106 writeMessageHeader(messageID); 107 { 108 LDAP.writeEntry(writer, LDAP.OP_TYPE_ADD_REQUEST, request); 109 } 110 writeMessageFooter(request.getControls()); 111 } 112 113 /** 114 * Writes the provided add result. 115 * 116 * @param messageID 117 * The LDAP message ID. 118 * @param result 119 * The result. 120 * @throws IOException 121 * If an unexpected IO error occurred. 122 */ 123 public void writeAddResult(final int messageID, final Result result) throws IOException { 124 logger.trace("ENCODE LDAP ADD RESULT(messageID=%d, result=%s)", messageID, result); 125 writeMessageHeader(messageID); 126 { 127 writeResultHeader(LDAP.OP_TYPE_ADD_RESPONSE, result); 128 writeResultFooter(writer); 129 } 130 writeMessageFooter(result.getControls()); 131 } 132 133 /** 134 * Writes the provided bind request. 135 * 136 * @param messageID 137 * The LDAP message ID. 138 * @param version 139 * The requested LDAP protocol version. 140 * @param request 141 * The request. 142 * @throws IOException 143 * If an unexpected IO error occurred. 144 */ 145 public void writeBindRequest(final int messageID, final int version, 146 final GenericBindRequest request) throws IOException { 147 logger.trace("ENCODE LDAP BIND REQUEST(messageID=%d, auth=0x%x, request=%s)", 148 messageID, request.getAuthenticationType(), request); 149 writeMessageHeader(messageID); 150 { 151 writer.writeStartSequence(LDAP.OP_TYPE_BIND_REQUEST); 152 { 153 writer.writeInteger(version); 154 writer.writeOctetString(request.getName()); 155 writer.writeOctetString(request.getAuthenticationType(), request 156 .getAuthenticationValue()); 157 } 158 writer.writeEndSequence(); 159 } 160 writeMessageFooter(request.getControls()); 161 } 162 163 /** 164 * Writes the provided bind result. 165 * 166 * @param messageID 167 * The LDAP message ID. 168 * @param result 169 * The result. 170 * @throws IOException 171 * If an unexpected IO error occurred. 172 */ 173 public void writeBindResult(final int messageID, final BindResult result) throws IOException { 174 logger.trace("ENCODE LDAP BIND RESULT(messageID=%d, result=%s)", messageID, result); 175 writeMessageHeader(messageID); 176 { 177 writeResultHeader(LDAP.OP_TYPE_BIND_RESPONSE, result); 178 { 179 final ByteString saslCredentials = result.getServerSASLCredentials(); 180 if (saslCredentials != null && saslCredentials.length() > 0) { 181 writer.writeOctetString(LDAP.TYPE_SERVER_SASL_CREDENTIALS, result 182 .getServerSASLCredentials()); 183 } 184 } 185 writeResultFooter(writer); 186 } 187 writeMessageFooter(result.getControls()); 188 } 189 190 /** 191 * Writes the provided compare request. 192 * 193 * @param messageID 194 * The LDAP message ID. 195 * @param request 196 * The request. 197 * @throws IOException 198 * If an unexpected IO error occurred. 199 */ 200 public void writeCompareRequest(final int messageID, final CompareRequest request) 201 throws IOException { 202 logger.trace("ENCODE LDAP COMPARE REQUEST(messageID=%d, request=%s)", messageID, request); 203 writeMessageHeader(messageID); 204 { 205 writer.writeStartSequence(LDAP.OP_TYPE_COMPARE_REQUEST); 206 { 207 writer.writeOctetString(request.getName().toString()); 208 writer.writeStartSequence(); 209 { 210 writer.writeOctetString(request.getAttributeDescription().toString()); 211 writer.writeOctetString(request.getAssertionValue()); 212 } 213 writer.writeEndSequence(); 214 } 215 writer.writeEndSequence(); 216 } 217 writeMessageFooter(request.getControls()); 218 } 219 220 /** 221 * Writes the provided compare result. 222 * 223 * @param messageID 224 * The LDAP message ID. 225 * @param result 226 * The result. 227 * @throws IOException 228 * If an unexpected IO error occurred. 229 */ 230 public void writeCompareResult(final int messageID, final CompareResult result) 231 throws IOException { 232 logger.trace("ENCODE LDAP COMPARE RESULT(messageID=%d, result=%s)", messageID, result); 233 writeMessageHeader(messageID); 234 { 235 writeResultHeader(LDAP.OP_TYPE_COMPARE_RESPONSE, result); 236 writeResultFooter(writer); 237 } 238 writeMessageFooter(result.getControls()); 239 } 240 241 /** 242 * Writes the provided control. 243 * 244 * @param control 245 * The control. 246 * @throws IOException 247 * If an unexpected IO error occurred. 248 */ 249 public void writeControl(final Control control) throws IOException { 250 writer.writeStartSequence(); 251 { 252 writer.writeOctetString(control.getOID()); 253 if (control.isCritical()) { 254 writer.writeBoolean(control.isCritical()); 255 } 256 if (control.getValue() != null) { 257 writer.writeOctetString(control.getValue()); 258 } 259 } 260 writer.writeEndSequence(); 261 } 262 263 /** 264 * Writes the provided delete request. 265 * 266 * @param messageID 267 * The LDAP message ID. 268 * @param request 269 * The request. 270 * @throws IOException 271 * If an unexpected IO error occurred. 272 */ 273 public void writeDeleteRequest(final int messageID, final DeleteRequest request) 274 throws IOException { 275 logger.trace("ENCODE LDAP DELETE REQUEST(messageID=%d, request=%s)", messageID, request); 276 writeMessageHeader(messageID); 277 { 278 writer.writeOctetString(LDAP.OP_TYPE_DELETE_REQUEST, request.getName().toString()); 279 } 280 writeMessageFooter(request.getControls()); 281 } 282 283 /** 284 * Writes the provided delete result. 285 * 286 * @param messageID 287 * The LDAP message ID. 288 * @param result 289 * The result. 290 * @throws IOException 291 * If an unexpected IO error occurred. 292 */ 293 public void writeDeleteResult(final int messageID, final Result result) throws IOException { 294 logger.trace("ENCODE LDAP DELETE RESULT(messageID=%d, result=%s)", messageID, result); 295 writeMessageHeader(messageID); 296 { 297 writeResultHeader(LDAP.OP_TYPE_DELETE_RESPONSE, result); 298 writeResultFooter(writer); 299 } 300 writeMessageFooter(result.getControls()); 301 } 302 303 /** 304 * Writes the provided extended request. 305 * 306 * @param messageID 307 * The LDAP message ID. 308 * @param request 309 * The request. 310 * @throws IOException 311 * If an unexpected IO error occurred. 312 */ 313 public void writeExtendedRequest(final int messageID, final ExtendedRequest<?> request) 314 throws IOException { 315 logger.trace("ENCODE LDAP EXTENDED REQUEST(messageID=%d, request=%s)", messageID, request); 316 writeMessageHeader(messageID); 317 { 318 writer.writeStartSequence(LDAP.OP_TYPE_EXTENDED_REQUEST); 319 { 320 writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_OID, request.getOID()); 321 final ByteString requestValue = request.getValue(); 322 if (requestValue != null) { 323 writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_VALUE, requestValue); 324 } 325 } 326 writer.writeEndSequence(); 327 } 328 writeMessageFooter(request.getControls()); 329 } 330 331 /** 332 * Writes the provided extended result. 333 * 334 * @param messageID 335 * The LDAP message ID. 336 * @param result 337 * The result. 338 * @throws IOException 339 * If an unexpected IO error occurred. 340 */ 341 public void writeExtendedResult(final int messageID, final ExtendedResult result) 342 throws IOException { 343 logger.trace("ENCODE LDAP EXTENDED RESULT(messageID=%d, result=%s)", messageID, result); 344 writeMessageHeader(messageID); 345 { 346 writeResultHeader(LDAP.OP_TYPE_EXTENDED_RESPONSE, result); 347 { 348 final String responseName = result.getOID(); 349 if (responseName != null) { 350 writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_OID, responseName); 351 } 352 final ByteString responseValue = result.getValue(); 353 if (responseValue != null) { 354 writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_VALUE, responseValue); 355 } 356 } 357 writeResultFooter(writer); 358 } 359 writeMessageFooter(result.getControls()); 360 } 361 362 /** 363 * Writes the provided intermediate response. 364 * 365 * @param messageID 366 * The LDAP message ID. 367 * @param response 368 * The response. 369 * @throws IOException 370 * If an unexpected IO error occurred. 371 */ 372 public void writeIntermediateResponse(final int messageID, final IntermediateResponse response) 373 throws IOException { 374 logger.trace("ENCODE LDAP INTERMEDIATE RESPONSE(messageID=%d, response=%s)", messageID, response); 375 writeMessageHeader(messageID); 376 { 377 writer.writeStartSequence(LDAP.OP_TYPE_INTERMEDIATE_RESPONSE); 378 { 379 final String responseName = response.getOID(); 380 if (responseName != null) { 381 writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_OID, response.getOID()); 382 } 383 final ByteString responseValue = response.getValue(); 384 if (responseValue != null) { 385 writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE, response 386 .getValue()); 387 } 388 } 389 writer.writeEndSequence(); 390 } 391 writeMessageFooter(response.getControls()); 392 } 393 394 /** 395 * Writes the provided modify DN request. 396 * 397 * @param messageID 398 * The LDAP message ID. 399 * @param request 400 * The request. 401 * @throws IOException 402 * If an unexpected IO error occurred. 403 */ 404 public void writeModifyDNRequest(final int messageID, final ModifyDNRequest request) 405 throws IOException { 406 logger.trace("ENCODE LDAP MODIFY DN REQUEST(messageID=%d, request=%s)", messageID, request); 407 writeMessageHeader(messageID); 408 { 409 writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_DN_REQUEST); 410 { 411 writer.writeOctetString(request.getName().toString()); 412 writer.writeOctetString(request.getNewRDN().toString()); 413 writer.writeBoolean(request.isDeleteOldRDN()); 414 final DN newSuperior = request.getNewSuperior(); 415 if (newSuperior != null) { 416 writer.writeOctetString(LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR, newSuperior 417 .toString()); 418 } 419 } 420 writer.writeEndSequence(); 421 } 422 writeMessageFooter(request.getControls()); 423 } 424 425 /** 426 * Writes the provided modify DN result. 427 * 428 * @param messageID 429 * The LDAP message ID. 430 * @param result 431 * The result. 432 * @throws IOException 433 * If an unexpected IO error occurred. 434 */ 435 public void writeModifyDNResult(final int messageID, final Result result) throws IOException { 436 logger.trace("ENCODE LDAP MODIFY DN RESULT(messageID=%d, result=%s)", messageID, result); 437 writeMessageHeader(messageID); 438 { 439 writeResultHeader(LDAP.OP_TYPE_MODIFY_DN_RESPONSE, result); 440 writeResultFooter(writer); 441 } 442 writeMessageFooter(result.getControls()); 443 } 444 445 /** 446 * Writes the provided modify request. 447 * 448 * @param messageID 449 * The LDAP message ID. 450 * @param request 451 * The request. 452 * @throws IOException 453 * If an unexpected IO error occurred. 454 */ 455 public void writeModifyRequest(final int messageID, final ModifyRequest request) 456 throws IOException { 457 logger.trace("ENCODE LDAP MODIFY REQUEST(messageID=%d, request=%s)", messageID, request); 458 writeMessageHeader(messageID); 459 { 460 writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_REQUEST); 461 { 462 writer.writeOctetString(request.getName().toString()); 463 writer.writeStartSequence(); 464 { 465 for (final Modification change : request.getModifications()) { 466 writeChange(change); 467 } 468 } 469 writer.writeEndSequence(); 470 } 471 writer.writeEndSequence(); 472 } 473 writeMessageFooter(request.getControls()); 474 } 475 476 /** 477 * Writes the provided extended result. 478 * 479 * @param messageID 480 * The LDAP message ID. 481 * @param result 482 * The result. 483 * @throws IOException 484 * If an unexpected IO error occurred. 485 */ 486 public void writeModifyResult(final int messageID, final Result result) throws IOException { 487 logger.trace("ENCODE LDAP MODIFY RESULT(messageID=%d, result=%s)", messageID, result); 488 writeMessageHeader(messageID); 489 { 490 writeResultHeader(LDAP.OP_TYPE_MODIFY_RESPONSE, result); 491 writeResultFooter(writer); 492 } 493 writeMessageFooter(result.getControls()); 494 } 495 496 /** 497 * Writes the provided search request. 498 * 499 * @param messageID 500 * The LDAP message ID. 501 * @param request 502 * The request. 503 * @throws IOException 504 * If an unexpected IO error occurred. 505 */ 506 public void writeSearchRequest(final int messageID, final SearchRequest request) 507 throws IOException { 508 logger.trace("ENCODE LDAP SEARCH REQUEST(messageID=%d, request=%s)", messageID, request); 509 writeMessageHeader(messageID); 510 { 511 writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_REQUEST); 512 { 513 writer.writeOctetString(request.getName().toString()); 514 writer.writeEnumerated(request.getScope().intValue()); 515 writer.writeEnumerated(request.getDereferenceAliasesPolicy().intValue()); 516 writer.writeInteger(request.getSizeLimit()); 517 writer.writeInteger(request.getTimeLimit()); 518 writer.writeBoolean(request.isTypesOnly()); 519 LDAP.writeFilter(writer, request.getFilter()); 520 writer.writeStartSequence(); 521 { 522 for (final String attribute : request.getAttributes()) { 523 writer.writeOctetString(attribute); 524 } 525 } 526 writer.writeEndSequence(); 527 } 528 writer.writeEndSequence(); 529 } 530 writeMessageFooter(request.getControls()); 531 } 532 533 /** 534 * Writes the provided search result. 535 * 536 * @param messageID 537 * The LDAP message ID. 538 * @param result 539 * The result. 540 * @throws IOException 541 * If an unexpected IO error occurred. 542 */ 543 public void writeSearchResult(final int messageID, final Result result) throws IOException { 544 logger.trace("ENCODE LDAP SEARCH RESULT(messageID=%d, result=%s)", messageID, result); 545 writeMessageHeader(messageID); 546 { 547 writeResultHeader(LDAP.OP_TYPE_SEARCH_RESULT_DONE, result); 548 writeResultFooter(writer); 549 } 550 writeMessageFooter(result.getControls()); 551 } 552 553 /** 554 * Writes the provided search result entry. 555 * 556 * @param messageID 557 * The LDAP message ID. 558 * @param entry 559 * The entry. 560 * @throws IOException 561 * If an unexpected IO error occurred. 562 */ 563 public void writeSearchResultEntry(final int messageID, final SearchResultEntry entry) 564 throws IOException { 565 logger.trace("ENCODE LDAP SEARCH RESULT ENTRY(messageID=%d, entry=%s)", messageID, entry); 566 writeMessageHeader(messageID); 567 { 568 LDAP.writeEntry(writer, LDAP.OP_TYPE_SEARCH_RESULT_ENTRY, entry); 569 } 570 writeMessageFooter(entry.getControls()); 571 } 572 573 /** 574 * Writes the provided search result reference. 575 * 576 * @param messageID 577 * The LDAP message ID. 578 * @param reference 579 * The reference. 580 * @throws IOException 581 * If an unexpected IO error occurred. 582 */ 583 public void writeSearchResultReference(final int messageID, 584 final SearchResultReference reference) throws IOException { 585 logger.trace("ENCODE LDAP SEARCH RESULT REFERENCE(messageID=%d, reference=%s)", messageID, reference); 586 writeMessageHeader(messageID); 587 { 588 writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE); 589 { 590 for (final String url : reference.getURIs()) { 591 writer.writeOctetString(url); 592 } 593 } 594 writer.writeEndSequence(); 595 } 596 writeMessageFooter(reference.getControls()); 597 } 598 599 /** 600 * Writes the provided unbind request. 601 * 602 * @param messageID 603 * The LDAP message ID. 604 * @param request 605 * The request. 606 * @throws IOException 607 * If an unexpected IO error occurred. 608 */ 609 public void writeUnbindRequest(final int messageID, final UnbindRequest request) 610 throws IOException { 611 logger.trace("ENCODE LDAP UNBIND REQUEST(messageID=%d, request=%s)", messageID, request); 612 writeMessageHeader(messageID); 613 { 614 writer.writeNull(LDAP.OP_TYPE_UNBIND_REQUEST); 615 } 616 writeMessageFooter(request.getControls()); 617 } 618 619 /** 620 * Writes a message with the provided id, tag and content bytes. 621 * 622 * @param messageID 623 * The LDAP message ID. 624 * @param messageTag 625 * The LDAP message type. 626 * @param messageBytes 627 * The contents of the LDAP message. 628 * @throws IOException 629 * If an unexpected IO error occurred. 630 */ 631 public void writeUnrecognizedMessage(final int messageID, final byte messageTag, 632 final ByteString messageBytes) throws IOException { 633 logger.trace("ENCODE LDAP UNKNOWN MESSAGE(messageID=%d, messageTag=%x, messageBytes=%s)", 634 messageID, messageTag, messageBytes); 635 writeMessageHeader(messageID); 636 { 637 writer.writeOctetString(messageTag, messageBytes); 638 } 639 writer.writeEndSequence(); 640 } 641 642 private void writeChange(final Modification change) throws IOException { 643 writer.writeStartSequence(); 644 { 645 writer.writeEnumerated(change.getModificationType().intValue()); 646 LDAP.writeAttribute(writer, change.getAttribute()); 647 } 648 writer.writeEndSequence(); 649 } 650 651 private void writeMessageFooter(final List<Control> controls) throws IOException { 652 if (!controls.isEmpty()) { 653 writer.writeStartSequence(LDAP.TYPE_CONTROL_SEQUENCE); 654 { 655 for (final Control control : controls) { 656 writeControl(control); 657 } 658 } 659 writer.writeEndSequence(); 660 } 661 writer.writeEndSequence(); 662 } 663 664 private void writeMessageHeader(final int messageID) throws IOException { 665 writer.writeStartSequence(); 666 writer.writeInteger(messageID); 667 } 668 669 private void writeResultFooter(final ASN1Writer writer) throws IOException { 670 writer.writeEndSequence(); 671 } 672 673 private void writeResultHeader(final byte typeTag, final Result rawMessage) throws IOException { 674 writer.writeStartSequence(typeTag); 675 writer.writeEnumerated(rawMessage.getResultCode().intValue()); 676 writer.writeOctetString(rawMessage.getMatchedDN()); 677 writer.writeOctetString(rawMessage.getDiagnosticMessage()); 678 final List<String> referralURIs = rawMessage.getReferralURIs(); 679 if (!referralURIs.isEmpty()) { 680 writer.writeStartSequence(LDAP.TYPE_REFERRAL_SEQUENCE); 681 { 682 for (final String s : referralURIs) { 683 writer.writeOctetString(s); 684 } 685 } 686 writer.writeEndSequence(); 687 } 688 } 689}