1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2008-2009, Motorola, Inc. 4 * 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * - Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * 13 * - Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * - Neither the name of the Motorola, Inc. nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 package javax.obex; 35 36 import java.io.ByteArrayOutputStream; 37 import java.io.IOException; 38 import java.io.UnsupportedEncodingException; 39 import java.security.MessageDigest; 40 import java.security.NoSuchAlgorithmException; 41 import java.util.Calendar; 42 import java.util.Date; 43 import java.util.TimeZone; 44 45 /** 46 * This class defines a set of helper methods for the implementation of Obex. 47 * @hide 48 */ 49 public final class ObexHelper { 50 51 /** 52 * Defines the basic packet length used by OBEX. Every OBEX packet has the 53 * same basic format:<BR> 54 * Byte 0: Request or Response Code Byte 1&2: Length of the packet. 55 */ 56 public static final int BASE_PACKET_LENGTH = 3; 57 58 /** Prevent object construction of helper class */ 59 private ObexHelper() { 60 } 61 62 /** 63 * The maximum packet size for OBEX packets that this client can handle. At 64 * present, this must be changed for each port. TODO: The max packet size 65 * should be the Max incoming MTU minus TODO: L2CAP package headers and 66 * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO: 67 * LocalDevice.getProperty(). 68 */ 69 /* 70 * android note set as 0xFFFE to match remote MPS 71 */ 72 public static final int MAX_PACKET_SIZE_INT = 0xFFFE; 73 74 /** 75 * Temporary workaround to be able to push files to Windows 7. 76 * TODO: Should be removed as soon as Microsoft updates their driver. 77 */ 78 public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00; 79 80 public static final int OBEX_OPCODE_CONNECT = 0x80; 81 82 public static final int OBEX_OPCODE_DISCONNECT = 0x81; 83 84 public static final int OBEX_OPCODE_PUT = 0x02; 85 86 public static final int OBEX_OPCODE_PUT_FINAL = 0x82; 87 88 public static final int OBEX_OPCODE_GET = 0x03; 89 90 public static final int OBEX_OPCODE_GET_FINAL = 0x83; 91 92 public static final int OBEX_OPCODE_RESERVED = 0x04; 93 94 public static final int OBEX_OPCODE_RESERVED_FINAL = 0x84; 95 96 public static final int OBEX_OPCODE_SETPATH = 0x85; 97 98 public static final int OBEX_OPCODE_ABORT = 0xFF; 99 100 public static final int OBEX_AUTH_REALM_CHARSET_ASCII = 0x00; 101 102 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_1 = 0x01; 103 104 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_2 = 0x02; 105 106 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_3 = 0x03; 107 108 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_4 = 0x04; 109 110 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_5 = 0x05; 111 112 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_6 = 0x06; 113 114 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_7 = 0x07; 115 116 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_8 = 0x08; 117 118 public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_9 = 0x09; 119 120 public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF; 121 122 /** 123 * Updates the HeaderSet with the headers received in the byte array 124 * provided. Invalid headers are ignored. 125 * <P> 126 * The first two bits of an OBEX Header specifies the type of object that is 127 * being sent. The table below specifies the meaning of the high bits. 128 * <TABLE> 129 * <TR> 130 * <TH>Bits 8 and 7</TH> 131 * <TH>Value</TH> 132 * <TH>Description</TH> 133 * </TR> 134 * <TR> 135 * <TD>00</TD> 136 * <TD>0x00</TD> 137 * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD> 138 * </TR> 139 * <TR> 140 * <TD>01</TD> 141 * <TD>0x40</TD> 142 * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD> 143 * </TR> 144 * <TR> 145 * <TD>10</TD> 146 * <TD>0x80</TD> 147 * <TD>1 byte quantity</TD> 148 * </TR> 149 * <TR> 150 * <TD>11</TD> 151 * <TD>0xC0</TD> 152 * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD> 153 * </TR> 154 * </TABLE> 155 * This method uses the information in this table to determine the type of 156 * Java object to create and passes that object with the full header to 157 * setHeader() to update the HeaderSet object. Invalid headers will cause an 158 * exception to be thrown. When it is thrown, it is ignored. 159 * @param header the HeaderSet to update 160 * @param headerArray the byte array containing headers 161 * @return the result of the last start body or end body header provided; 162 * the first byte in the result will specify if a body or end of 163 * body is received 164 * @throws IOException if an invalid header was found 165 */ 166 public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException { 167 int index = 0; 168 int length = 0; 169 int headerID; 170 byte[] value = null; 171 byte[] body = null; 172 HeaderSet headerImpl = header; 173 try { 174 while (index < headerArray.length) { 175 headerID = 0xFF & headerArray[index]; 176 switch (headerID & (0xC0)) { 177 178 /* 179 * 0x00 is a unicode null terminate string with the first 180 * two bytes after the header identifier being the length 181 */ 182 case 0x00: 183 // Fall through 184 /* 185 * 0x40 is a byte sequence with the first 186 * two bytes after the header identifier being the length 187 */ 188 case 0x40: 189 boolean trimTail = true; 190 index++; 191 length = 0xFF & headerArray[index]; 192 length = length << 8; 193 index++; 194 length += 0xFF & headerArray[index]; 195 length -= 3; 196 index++; 197 value = new byte[length]; 198 System.arraycopy(headerArray, index, value, 0, length); 199 if (length == 0 || (length > 0 && (value[length - 1] != 0))) { 200 trimTail = false; 201 } 202 switch (headerID) { 203 case HeaderSet.TYPE: 204 try { 205 // Remove trailing null 206 if (trimTail == false) { 207 headerImpl.setHeader(headerID, new String(value, 0, 208 value.length, "ISO8859_1")); 209 } else { 210 headerImpl.setHeader(headerID, new String(value, 0, 211 value.length - 1, "ISO8859_1")); 212 } 213 } catch (UnsupportedEncodingException e) { 214 throw e; 215 } 216 break; 217 218 case HeaderSet.AUTH_CHALLENGE: 219 headerImpl.mAuthChall = new byte[length]; 220 System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0, 221 length); 222 break; 223 224 case HeaderSet.AUTH_RESPONSE: 225 headerImpl.mAuthResp = new byte[length]; 226 System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0, 227 length); 228 break; 229 230 case HeaderSet.BODY: 231 /* Fall Through */ 232 case HeaderSet.END_OF_BODY: 233 body = new byte[length + 1]; 234 body[0] = (byte)headerID; 235 System.arraycopy(headerArray, index, body, 1, length); 236 break; 237 238 case HeaderSet.TIME_ISO_8601: 239 try { 240 String dateString = new String(value, "ISO8859_1"); 241 Calendar temp = Calendar.getInstance(); 242 if ((dateString.length() == 16) 243 && (dateString.charAt(15) == 'Z')) { 244 temp.setTimeZone(TimeZone.getTimeZone("UTC")); 245 } 246 temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring( 247 0, 4))); 248 temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring( 249 4, 6))); 250 temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString 251 .substring(6, 8))); 252 temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString 253 .substring(9, 11))); 254 temp.set(Calendar.MINUTE, Integer.parseInt(dateString 255 .substring(11, 13))); 256 temp.set(Calendar.SECOND, Integer.parseInt(dateString 257 .substring(13, 15))); 258 headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp); 259 } catch (UnsupportedEncodingException e) { 260 throw e; 261 } 262 break; 263 264 default: 265 if ((headerID & 0xC0) == 0x00) { 266 headerImpl.setHeader(headerID, ObexHelper.convertToUnicode( 267 value, true)); 268 } else { 269 headerImpl.setHeader(headerID, value); 270 } 271 } 272 273 index += length; 274 break; 275 276 /* 277 * 0x80 is a byte header. The only valid byte headers are 278 * the 16 user defined byte headers. 279 */ 280 case 0x80: 281 index++; 282 try { 283 headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index])); 284 } catch (Exception e) { 285 // Not a valid header so ignore 286 } 287 index++; 288 break; 289 290 /* 291 * 0xC0 is a 4 byte unsigned integer header and with the 292 * exception of TIME_4_BYTE will be converted to a Long 293 * and added. 294 */ 295 case 0xC0: 296 index++; 297 value = new byte[4]; 298 System.arraycopy(headerArray, index, value, 0, 4); 299 try { 300 if (headerID != HeaderSet.TIME_4_BYTE) { 301 // Determine if it is a connection ID. These 302 // need to be handled differently 303 if (headerID == HeaderSet.CONNECTION_ID) { 304 headerImpl.mConnectionID = new byte[4]; 305 System.arraycopy(value, 0, headerImpl.mConnectionID, 0, 4); 306 } else { 307 headerImpl.setHeader(headerID, Long 308 .valueOf(convertToLong(value))); 309 } 310 } else { 311 Calendar temp = Calendar.getInstance(); 312 temp.setTime(new Date(convertToLong(value) * 1000L)); 313 headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp); 314 } 315 } catch (Exception e) { 316 // Not a valid header so ignore 317 throw new IOException("Header was not formatted properly"); 318 } 319 index += 4; 320 break; 321 } 322 323 } 324 } catch (IOException e) { 325 throw new IOException("Header was not formatted properly"); 326 } 327 328 return body; 329 } 330 331 /** 332 * Creates the header part of OBEX packet based on the header provided. 333 * TODO: Could use getHeaderList() to get the array of headers to include 334 * and then use the high two bits to determine the the type of the object 335 * and construct the byte array from that. This will make the size smaller. 336 * @param head the header used to construct the byte array 337 * @param nullOut <code>true</code> if the header should be set to 338 * <code>null</code> once it is added to the array or 339 * <code>false</code> if it should not be nulled out 340 * @return the header of an OBEX packet 341 */ 342 public static byte[] createHeader(HeaderSet head, boolean nullOut) { 343 Long intHeader = null; 344 String stringHeader = null; 345 Calendar dateHeader = null; 346 Byte byteHeader = null; 347 StringBuffer buffer = null; 348 byte[] value = null; 349 byte[] result = null; 350 byte[] lengthArray = new byte[2]; 351 int length; 352 HeaderSet headImpl = null; 353 ByteArrayOutputStream out = new ByteArrayOutputStream(); 354 headImpl = head; 355 356 try { 357 /* 358 * Determine if there is a connection ID to send. If there is, 359 * then it should be the first header in the packet. 360 */ 361 if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) { 362 363 out.write((byte)HeaderSet.CONNECTION_ID); 364 out.write(headImpl.mConnectionID); 365 } 366 367 // Count Header 368 intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT); 369 if (intHeader != null) { 370 out.write((byte)HeaderSet.COUNT); 371 value = ObexHelper.convertToByteArray(intHeader.longValue()); 372 out.write(value); 373 if (nullOut) { 374 headImpl.setHeader(HeaderSet.COUNT, null); 375 } 376 } 377 378 // Name Header 379 stringHeader = (String)headImpl.getHeader(HeaderSet.NAME); 380 if (stringHeader != null) { 381 out.write((byte)HeaderSet.NAME); 382 value = ObexHelper.convertToUnicodeByteArray(stringHeader); 383 length = value.length + 3; 384 lengthArray[0] = (byte)(0xFF & (length >> 8)); 385 lengthArray[1] = (byte)(0xFF & length); 386 out.write(lengthArray); 387 out.write(value); 388 if (nullOut) { 389 headImpl.setHeader(HeaderSet.NAME, null); 390 } 391 } else if (headImpl.getEmptyNameHeader()) { 392 out.write((byte) HeaderSet.NAME); 393 lengthArray[0] = (byte) 0x00; 394 lengthArray[1] = (byte) 0x03; 395 out.write(lengthArray); 396 } 397 398 // Type Header 399 stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE); 400 if (stringHeader != null) { 401 out.write((byte)HeaderSet.TYPE); 402 try { 403 value = stringHeader.getBytes("ISO8859_1"); 404 } catch (UnsupportedEncodingException e) { 405 throw e; 406 } 407 408 length = value.length + 4; 409 lengthArray[0] = (byte)(255 & (length >> 8)); 410 lengthArray[1] = (byte)(255 & length); 411 out.write(lengthArray); 412 out.write(value); 413 out.write(0x00); 414 if (nullOut) { 415 headImpl.setHeader(HeaderSet.TYPE, null); 416 } 417 } 418 419 // Length Header 420 intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH); 421 if (intHeader != null) { 422 out.write((byte)HeaderSet.LENGTH); 423 value = ObexHelper.convertToByteArray(intHeader.longValue()); 424 out.write(value); 425 if (nullOut) { 426 headImpl.setHeader(HeaderSet.LENGTH, null); 427 } 428 } 429 430 // Time ISO Header 431 dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601); 432 if (dateHeader != null) { 433 434 /* 435 * The ISO Header should take the form YYYYMMDDTHHMMSSZ. The 436 * 'Z' will only be included if it is a UTC time. 437 */ 438 buffer = new StringBuffer(); 439 int temp = dateHeader.get(Calendar.YEAR); 440 for (int i = temp; i < 1000; i = i * 10) { 441 buffer.append("0"); 442 } 443 buffer.append(temp); 444 temp = dateHeader.get(Calendar.MONTH); 445 if (temp < 10) { 446 buffer.append("0"); 447 } 448 buffer.append(temp); 449 temp = dateHeader.get(Calendar.DAY_OF_MONTH); 450 if (temp < 10) { 451 buffer.append("0"); 452 } 453 buffer.append(temp); 454 buffer.append("T"); 455 temp = dateHeader.get(Calendar.HOUR_OF_DAY); 456 if (temp < 10) { 457 buffer.append("0"); 458 } 459 buffer.append(temp); 460 temp = dateHeader.get(Calendar.MINUTE); 461 if (temp < 10) { 462 buffer.append("0"); 463 } 464 buffer.append(temp); 465 temp = dateHeader.get(Calendar.SECOND); 466 if (temp < 10) { 467 buffer.append("0"); 468 } 469 buffer.append(temp); 470 471 if (dateHeader.getTimeZone().getID().equals("UTC")) { 472 buffer.append("Z"); 473 } 474 475 try { 476 value = buffer.toString().getBytes("ISO8859_1"); 477 } catch (UnsupportedEncodingException e) { 478 throw e; 479 } 480 481 length = value.length + 3; 482 lengthArray[0] = (byte)(255 & (length >> 8)); 483 lengthArray[1] = (byte)(255 & length); 484 out.write(HeaderSet.TIME_ISO_8601); 485 out.write(lengthArray); 486 out.write(value); 487 if (nullOut) { 488 headImpl.setHeader(HeaderSet.TIME_ISO_8601, null); 489 } 490 } 491 492 // Time 4 Byte Header 493 dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE); 494 if (dateHeader != null) { 495 out.write(HeaderSet.TIME_4_BYTE); 496 497 /* 498 * Need to call getTime() twice. The first call will return 499 * a java.util.Date object. The second call returns the number 500 * of milliseconds since January 1, 1970. We need to convert 501 * it to seconds since the TIME_4_BYTE expects the number of 502 * seconds since January 1, 1970. 503 */ 504 value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L); 505 out.write(value); 506 if (nullOut) { 507 headImpl.setHeader(HeaderSet.TIME_4_BYTE, null); 508 } 509 } 510 511 // Description Header 512 stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION); 513 if (stringHeader != null) { 514 out.write((byte)HeaderSet.DESCRIPTION); 515 value = ObexHelper.convertToUnicodeByteArray(stringHeader); 516 length = value.length + 3; 517 lengthArray[0] = (byte)(255 & (length >> 8)); 518 lengthArray[1] = (byte)(255 & length); 519 out.write(lengthArray); 520 out.write(value); 521 if (nullOut) { 522 headImpl.setHeader(HeaderSet.DESCRIPTION, null); 523 } 524 } 525 526 // Target Header 527 value = (byte[])headImpl.getHeader(HeaderSet.TARGET); 528 if (value != null) { 529 out.write((byte)HeaderSet.TARGET); 530 length = value.length + 3; 531 lengthArray[0] = (byte)(255 & (length >> 8)); 532 lengthArray[1] = (byte)(255 & length); 533 out.write(lengthArray); 534 out.write(value); 535 if (nullOut) { 536 headImpl.setHeader(HeaderSet.TARGET, null); 537 } 538 } 539 540 // HTTP Header 541 value = (byte[])headImpl.getHeader(HeaderSet.HTTP); 542 if (value != null) { 543 out.write((byte)HeaderSet.HTTP); 544 length = value.length + 3; 545 lengthArray[0] = (byte)(255 & (length >> 8)); 546 lengthArray[1] = (byte)(255 & length); 547 out.write(lengthArray); 548 out.write(value); 549 if (nullOut) { 550 headImpl.setHeader(HeaderSet.HTTP, null); 551 } 552 } 553 554 // Who Header 555 value = (byte[])headImpl.getHeader(HeaderSet.WHO); 556 if (value != null) { 557 out.write((byte)HeaderSet.WHO); 558 length = value.length + 3; 559 lengthArray[0] = (byte)(255 & (length >> 8)); 560 lengthArray[1] = (byte)(255 & length); 561 out.write(lengthArray); 562 out.write(value); 563 if (nullOut) { 564 headImpl.setHeader(HeaderSet.WHO, null); 565 } 566 } 567 568 // Connection ID Header 569 value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER); 570 if (value != null) { 571 out.write((byte)HeaderSet.APPLICATION_PARAMETER); 572 length = value.length + 3; 573 lengthArray[0] = (byte)(255 & (length >> 8)); 574 lengthArray[1] = (byte)(255 & length); 575 out.write(lengthArray); 576 out.write(value); 577 if (nullOut) { 578 headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null); 579 } 580 } 581 582 // Object Class Header 583 value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS); 584 if (value != null) { 585 out.write((byte)HeaderSet.OBJECT_CLASS); 586 length = value.length + 3; 587 lengthArray[0] = (byte)(255 & (length >> 8)); 588 lengthArray[1] = (byte)(255 & length); 589 out.write(lengthArray); 590 out.write(value); 591 if (nullOut) { 592 headImpl.setHeader(HeaderSet.OBJECT_CLASS, null); 593 } 594 } 595 596 // Check User Defined Headers 597 for (int i = 0; i < 16; i++) { 598 599 //Unicode String Header 600 stringHeader = (String)headImpl.getHeader(i + 0x30); 601 if (stringHeader != null) { 602 out.write((byte)i + 0x30); 603 value = ObexHelper.convertToUnicodeByteArray(stringHeader); 604 length = value.length + 3; 605 lengthArray[0] = (byte)(255 & (length >> 8)); 606 lengthArray[1] = (byte)(255 & length); 607 out.write(lengthArray); 608 out.write(value); 609 if (nullOut) { 610 headImpl.setHeader(i + 0x30, null); 611 } 612 } 613 614 // Byte Sequence Header 615 value = (byte[])headImpl.getHeader(i + 0x70); 616 if (value != null) { 617 out.write((byte)i + 0x70); 618 length = value.length + 3; 619 lengthArray[0] = (byte)(255 & (length >> 8)); 620 lengthArray[1] = (byte)(255 & length); 621 out.write(lengthArray); 622 out.write(value); 623 if (nullOut) { 624 headImpl.setHeader(i + 0x70, null); 625 } 626 } 627 628 // Byte Header 629 byteHeader = (Byte)headImpl.getHeader(i + 0xB0); 630 if (byteHeader != null) { 631 out.write((byte)i + 0xB0); 632 out.write(byteHeader.byteValue()); 633 if (nullOut) { 634 headImpl.setHeader(i + 0xB0, null); 635 } 636 } 637 638 // Integer header 639 intHeader = (Long)headImpl.getHeader(i + 0xF0); 640 if (intHeader != null) { 641 out.write((byte)i + 0xF0); 642 out.write(ObexHelper.convertToByteArray(intHeader.longValue())); 643 if (nullOut) { 644 headImpl.setHeader(i + 0xF0, null); 645 } 646 } 647 } 648 649 // Add the authentication challenge header 650 if (headImpl.mAuthChall != null) { 651 out.write((byte)HeaderSet.AUTH_CHALLENGE); 652 length = headImpl.mAuthChall.length + 3; 653 lengthArray[0] = (byte)(255 & (length >> 8)); 654 lengthArray[1] = (byte)(255 & length); 655 out.write(lengthArray); 656 out.write(headImpl.mAuthChall); 657 if (nullOut) { 658 headImpl.mAuthChall = null; 659 } 660 } 661 662 // Add the authentication response header 663 if (headImpl.mAuthResp != null) { 664 out.write((byte)HeaderSet.AUTH_RESPONSE); 665 length = headImpl.mAuthResp.length + 3; 666 lengthArray[0] = (byte)(255 & (length >> 8)); 667 lengthArray[1] = (byte)(255 & length); 668 out.write(lengthArray); 669 out.write(headImpl.mAuthResp); 670 if (nullOut) { 671 headImpl.mAuthResp = null; 672 } 673 } 674 675 } catch (IOException e) { 676 } finally { 677 result = out.toByteArray(); 678 try { 679 out.close(); 680 } catch (Exception ex) { 681 } 682 } 683 684 return result; 685 686 } 687 688 /** 689 * Determines where the maximum divide is between headers. This method is 690 * used by put and get operations to separate headers to a size that meets 691 * the max packet size allowed. 692 * @param headerArray the headers to separate 693 * @param start the starting index to search 694 * @param maxSize the maximum size of a packet 695 * @return the index of the end of the header block to send or -1 if the 696 * header could not be divided because the header is too large 697 */ 698 public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) { 699 700 int fullLength = 0; 701 int lastLength = -1; 702 int index = start; 703 int length = 0; 704 705 while ((fullLength < maxSize) && (index < headerArray.length)) { 706 int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]); 707 lastLength = fullLength; 708 709 switch (headerID & (0xC0)) { 710 711 case 0x00: 712 // Fall through 713 case 0x40: 714 715 index++; 716 length = (headerArray[index] < 0 ? headerArray[index] + 256 717 : headerArray[index]); 718 length = length << 8; 719 index++; 720 length += (headerArray[index] < 0 ? headerArray[index] + 256 721 : headerArray[index]); 722 length -= 3; 723 index++; 724 index += length; 725 fullLength += length + 3; 726 break; 727 728 case 0x80: 729 730 index++; 731 index++; 732 fullLength += 2; 733 break; 734 735 case 0xC0: 736 737 index += 5; 738 fullLength += 5; 739 break; 740 741 } 742 743 } 744 745 /* 746 * Determine if this is the last header or not 747 */ 748 if (lastLength == 0) { 749 /* 750 * Since this is the last header, check to see if the size of this 751 * header is less then maxSize. If it is, return the length of the 752 * header, otherwise return -1. The length of the header is 753 * returned since it would be the start of the next header 754 */ 755 if (fullLength < maxSize) { 756 return headerArray.length; 757 } else { 758 return -1; 759 } 760 } else { 761 return lastLength + start; 762 } 763 } 764 765 /** 766 * Converts the byte array to a long. 767 * @param b the byte array to convert to a long 768 * @return the byte array as a long 769 */ 770 public static long convertToLong(byte[] b) { 771 long result = 0; 772 long value = 0; 773 long power = 0; 774 775 for (int i = (b.length - 1); i >= 0; i--) { 776 value = b[i]; 777 if (value < 0) { 778 value += 256; 779 } 780 781 result = result | (value << power); 782 power += 8; 783 } 784 785 return result; 786 } 787 788 /** 789 * Converts the long to a 4 byte array. The long must be non negative. 790 * @param l the long to convert 791 * @return a byte array that is the same as the long 792 */ 793 public static byte[] convertToByteArray(long l) { 794 byte[] b = new byte[4]; 795 796 b[0] = (byte)(255 & (l >> 24)); 797 b[1] = (byte)(255 & (l >> 16)); 798 b[2] = (byte)(255 & (l >> 8)); 799 b[3] = (byte)(255 & l); 800 801 return b; 802 } 803 804 /** 805 * Converts the String to a UNICODE byte array. It will also add the ending 806 * null characters to the end of the string. 807 * @param s the string to convert 808 * @return the unicode byte array of the string 809 */ 810 public static byte[] convertToUnicodeByteArray(String s) { 811 if (s == null) { 812 return null; 813 } 814 815 char c[] = s.toCharArray(); 816 byte[] result = new byte[(c.length * 2) + 2]; 817 for (int i = 0; i < c.length; i++) { 818 result[(i * 2)] = (byte)(c[i] >> 8); 819 result[((i * 2) + 1)] = (byte)c[i]; 820 } 821 822 // Add the UNICODE null character 823 result[result.length - 2] = 0; 824 result[result.length - 1] = 0; 825 826 return result; 827 } 828 829 /** 830 * Retrieves the value from the byte array for the tag value specified. The 831 * array should be of the form Tag - Length - Value triplet. 832 * @param tag the tag to retrieve from the byte array 833 * @param triplet the byte sequence containing the tag length value form 834 * @return the value of the specified tag 835 */ 836 public static byte[] getTagValue(byte tag, byte[] triplet) { 837 838 int index = findTag(tag, triplet); 839 if (index == -1) { 840 return null; 841 } 842 843 index++; 844 int length = triplet[index] & 0xFF; 845 846 byte[] result = new byte[length]; 847 index++; 848 System.arraycopy(triplet, index, result, 0, length); 849 850 return result; 851 } 852 853 /** 854 * Finds the index that starts the tag value pair in the byte array provide. 855 * @param tag the tag to look for 856 * @param value the byte array to search 857 * @return the starting index of the tag or -1 if the tag could not be found 858 */ 859 public static int findTag(byte tag, byte[] value) { 860 int length = 0; 861 862 if (value == null) { 863 return -1; 864 } 865 866 int index = 0; 867 868 while ((index < value.length) && (value[index] != tag)) { 869 length = value[index + 1] & 0xFF; 870 index += length + 2; 871 } 872 873 if (index >= value.length) { 874 return -1; 875 } 876 877 return index; 878 } 879 880 /** 881 * Converts the byte array provided to a unicode string. 882 * @param b the byte array to convert to a string 883 * @param includesNull determine if the byte string provided contains the 884 * UNICODE null character at the end or not; if it does, it will be 885 * removed 886 * @return a Unicode string 887 * @throws IllegalArgumentException if the byte array has an odd length 888 */ 889 public static String convertToUnicode(byte[] b, boolean includesNull) { 890 if (b == null || b.length == 0) { 891 return null; 892 } 893 int arrayLength = b.length; 894 if (!((arrayLength % 2) == 0)) { 895 throw new IllegalArgumentException("Byte array not of a valid form"); 896 } 897 arrayLength = (arrayLength >> 1); 898 if (includesNull) { 899 arrayLength -= 1; 900 } 901 902 char[] c = new char[arrayLength]; 903 for (int i = 0; i < arrayLength; i++) { 904 int upper = b[2 * i]; 905 int lower = b[(2 * i) + 1]; 906 if (upper < 0) { 907 upper += 256; 908 } 909 if (lower < 0) { 910 lower += 256; 911 } 912 // If upper and lower both equal 0, it should be the end of string. 913 // Ignore left bytes from array to avoid potential issues 914 if (upper == 0 && lower == 0) { 915 return new String(c, 0, i); 916 } 917 918 c[i] = (char)((upper << 8) | lower); 919 } 920 921 return new String(c); 922 } 923 924 /** 925 * Compute the MD5 hash of the byte array provided. Does not accumulate 926 * input. 927 * @param in the byte array to hash 928 * @return the MD5 hash of the byte array 929 */ 930 public static byte[] computeMd5Hash(byte[] in) { 931 try { 932 MessageDigest md5 = MessageDigest.getInstance("MD5"); 933 return md5.digest(in); 934 } catch (NoSuchAlgorithmException e) { 935 throw new RuntimeException(e); 936 } 937 } 938 939 /** 940 * Computes an authentication challenge header. 941 * @param nonce the challenge that will be provided to the peer; the 942 * challenge must be 16 bytes long 943 * @param realm a short description that describes what password to use 944 * @param access if <code>true</code> then full access will be granted if 945 * successful; if <code>false</code> then read only access will be 946 * granted if successful 947 * @param userID if <code>true</code>, a user ID is required in the reply; 948 * if <code>false</code>, no user ID is required 949 * @throws IllegalArgumentException if the challenge is not 16 bytes long; 950 * if the realm can not be encoded in less then 255 bytes 951 * @throws IOException if the encoding scheme ISO 8859-1 is not supported 952 */ 953 public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access, 954 boolean userID) throws IOException { 955 byte[] authChall = null; 956 957 if (nonce.length != 16) { 958 throw new IllegalArgumentException("Nonce must be 16 bytes long"); 959 } 960 961 /* 962 * The authentication challenge is a byte sequence of the following form 963 * byte 0: 0x00 - the tag for the challenge 964 * byte 1: 0x10 - the length of the challenge; must be 16 965 * byte 2-17: the authentication challenge 966 * byte 18: 0x01 - the options tag; this is optional in the spec, but 967 * we are going to include it in every message 968 * byte 19: 0x01 - length of the options; must be 1 969 * byte 20: the value of the options; bit 0 is set if user ID is 970 * required; bit 1 is set if access mode is read only 971 * byte 21: 0x02 - the tag for authentication realm; only included if 972 * an authentication realm is specified 973 * byte 22: the length of the authentication realm; only included if 974 * the authentication realm is specified 975 * byte 23: the encoding scheme of the authentication realm; we will use 976 * the ISO 8859-1 encoding scheme since it is part of the KVM 977 * byte 24 & up: the realm if one is specified. 978 */ 979 if (realm == null) { 980 authChall = new byte[21]; 981 } else { 982 if (realm.length() >= 255) { 983 throw new IllegalArgumentException("Realm must be less then 255 bytes"); 984 } 985 authChall = new byte[24 + realm.length()]; 986 authChall[21] = 0x02; 987 authChall[22] = (byte)(realm.length() + 1); 988 authChall[23] = 0x01; // ISO 8859-1 Encoding 989 System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length()); 990 } 991 992 // Include the nonce field in the header 993 authChall[0] = 0x00; 994 authChall[1] = 0x10; 995 System.arraycopy(nonce, 0, authChall, 2, 16); 996 997 // Include the options header 998 authChall[18] = 0x01; 999 authChall[19] = 0x01; 1000 authChall[20] = 0x00; 1001 1002 if (!access) { 1003 authChall[20] = (byte)(authChall[20] | 0x02); 1004 } 1005 if (userID) { 1006 authChall[20] = (byte)(authChall[20] | 0x01); 1007 } 1008 1009 return authChall; 1010 } 1011 } 1012