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