| 1 19 package com.mysql.jdbc; 20 21 import java.io.BufferedInputStream ; 22 import java.io.BufferedOutputStream ; 23 import java.io.ByteArrayOutputStream ; 24 import java.io.EOFException ; 25 import java.io.FileInputStream ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.io.OutputStreamWriter ; 29 30 import java.lang.ref.SoftReference ; 31 32 import java.net.Socket ; 33 34 import java.security.NoSuchAlgorithmException ; 35 36 import java.sql.SQLException ; 37 import java.sql.SQLWarning ; 38 39 import java.util.ArrayList ; 40 import java.util.Properties ; 41 import java.util.zip.Deflater ; 42 import java.util.zip.Inflater ; 43 44 45 53 public class MysqlIO { 54 static final int NULL_LENGTH = ~0; 55 static final int COMP_HEADER_LENGTH = 3; 56 static final int MIN_COMPRESS_LEN = 50; 57 static final int HEADER_LENGTH = 4; 58 private static int maxBufferSize = 65535; 59 private static final int CLIENT_COMPRESS = 32; 61 private static final int CLIENT_CONNECT_WITH_DB = 8; 62 private static final int CLIENT_FOUND_ROWS = 2; 63 private static final int CLIENT_IGNORE_SPACE = 256; 65 private static final int CLIENT_LOCAL_FILES = 128; 67 68 70 private static final int CLIENT_LONG_FLAG = 4; 71 private static final int CLIENT_LONG_PASSWORD = 1; 73 private static final int CLIENT_PROTOCOL_41 = 512; private static final int CLIENT_INTERACTIVE = 1024; 75 private static final int CLIENT_SSL = 2048; 76 private static final int CLIENT_RESERVED = 16384; private static final int CLIENT_SECURE_CONNECTION = 32768; 78 private static final String FALSE_SCRAMBLE = "xxxxxxxx"; 79 80 84 private static String jvmPlatformCharset = null; 85 86 static { 87 OutputStreamWriter outWriter = null; 88 89 try { 95 outWriter = new OutputStreamWriter (new ByteArrayOutputStream ()); 96 jvmPlatformCharset = outWriter.getEncoding(); 97 } finally { 98 try { 99 outWriter.close(); 100 } catch (IOException ioEx) { 101 } 103 } 104 } 105 106 private Buffer reusablePacket = null; 112 private Buffer sendPacket = null; 113 private Buffer sharedSendPacket = null; 114 115 116 117 private BufferedOutputStream mysqlOutput = null; 119 private com.mysql.jdbc.Connection connection; 120 private Deflater deflater = null; 121 private Inflater inflater = null; 122 123 124 125 127 128 129 131 132 133 private InputStream mysqlInput = null; 135 private RowData streamingData = null; 136 137 private SQLWarning warningChain = null; 141 142 143 private Socket mysqlConnection = null; 144 private SocketFactory socketFactory = null; 145 146 private SoftReference loadFileBufRef; 153 154 private SoftReference splitBufRef; 160 private String host = null; 161 private String seed; 162 private String serverVersion = null; 163 private String socketFactoryClassName = null; 164 private byte[] packetHeaderBuf = new byte[4]; 165 private boolean clearStreamBeforeEachQuery = false; 166 private boolean colDecimalNeedsBump = false; private boolean has41NewNewProt = false; 168 169 170 private boolean hasLongColumnInfo = false; 171 private boolean isInteractiveClient = false; 172 173 177 private boolean platformDbCharsetMatches = true; 178 private boolean profileSql = false; 179 180 181 private boolean use41Extensions = false; 182 private boolean useCompression = false; 183 private boolean useNewLargePackets = false; 184 private boolean useNewUpdateCounts = false; private byte packetSequence = 0; 186 private byte protocolVersion = 0; 187 private int clientParam = 0; 188 189 private int maxAllowedPacket = 1024 * 1024; 191 private int maxThreeBytes = 255 * 255 * 255; 192 private int port = 3306; 193 private int serverMajorVersion = 0; 194 private int serverMinorVersion = 0; 195 private int serverSubMinorVersion = 0; 196 197 211 protected MysqlIO(String host, int port, String socketFactoryClassName, 212 Properties props, com.mysql.jdbc.Connection conn, int socketTimeout) 213 throws IOException , java.sql.SQLException { 214 this.connection = conn; 215 this.reusablePacket = new Buffer(this.connection.getNetBufferLength()); 216 this.port = port; 217 this.host = host; 218 this.socketFactoryClassName = socketFactoryClassName; 219 this.socketFactory = createSocketFactory(); 220 this.mysqlConnection = socketFactory.connect(this.host, props); 221 this.clearStreamBeforeEachQuery = this.connection.alwaysClearStream(); 222 223 if (socketTimeout != 0) { 224 try { 225 this.mysqlConnection.setSoTimeout(socketTimeout); 226 } catch (Exception ex) { 227 228 } 229 } 230 231 this.mysqlConnection = this.socketFactory.beforeHandshake(); 232 233 if (!this.connection.isUsingUnbufferedInput()) { 234 this.mysqlInput = new BufferedInputStream (this.mysqlConnection 235 .getInputStream(), 16384); 236 } else { 237 this.mysqlInput = this.mysqlConnection.getInputStream(); 238 } 239 240 this.mysqlOutput = new BufferedOutputStream (this.mysqlConnection 241 .getOutputStream(), 16384); 242 this.isInteractiveClient = this.connection.isInteractiveClient(); 243 } 244 245 250 protected void setProfileSql(boolean flag) { 251 this.profileSql = flag; 252 } 253 254 271 protected ResultSet getResultSet(long columnCount, int maxRows, 272 int resultSetType, boolean streamResults, String catalog) 273 throws Exception { 274 Buffer packet; Field[] fields = new Field[(int) columnCount]; 276 277 for (int i = 0; i < columnCount; i++) { 279 packet = readPacket(); 280 fields[i] = unpackField(packet, false); 281 } 282 283 packet = reuseAndReadPacket(this.reusablePacket); 284 285 RowData rowData = null; 286 287 if (!streamResults) { 288 ArrayList rows = new ArrayList (); 289 290 byte[][] rowBytes = nextRow((int) columnCount); 292 int rowCount = 0; 293 294 if (rowBytes != null) { 295 rows.add(rowBytes); 296 rowCount = 1; 297 } 298 299 while ((rowBytes != null) && (rowCount < maxRows)) { 300 rowBytes = nextRow((int) columnCount); 301 302 if (rowBytes != null) { 303 rows.add(rowBytes); 304 rowCount++; 305 } else { 306 if (Driver.TRACE) { 307 Debug.msg(this, "* NULL Row *"); 308 } 309 } 310 } 311 312 if (rowCount <= maxRows) { 318 clearInputStream(); 319 } 320 321 if (Driver.TRACE) { 322 Debug.msg(this, 323 "* Fetched " + rows.size() + " rows from server *"); 324 } 325 326 rowData = new RowDataStatic(rows); 327 reclaimLargeReusablePacket(); 328 } else { 329 rowData = new RowDataDynamic(this, (int) columnCount); 330 this.streamingData = rowData; 331 } 332 333 return buildResultSetWithRows(catalog, fields, rowData, resultSetType); 334 } 335 336 339 protected final void forceClose() { 340 try { 341 if (this.mysqlInput != null) { 342 this.mysqlInput.close(); 343 } 344 } catch (IOException ioEx) { 345 this.mysqlInput = null; 348 } 349 350 try { 351 if (this.mysqlOutput != null) { 352 this.mysqlOutput.close(); 353 } 354 } catch (IOException ioEx) { 355 this.mysqlOutput = null; 358 } 359 360 try { 361 if (this.mysqlConnection != null) { 362 this.mysqlConnection.close(); 363 } 364 } catch (IOException ioEx) { 365 this.mysqlConnection = null; 368 } 369 } 370 371 376 protected boolean hasLongColumnInfo() { 377 return this.hasLongColumnInfo; 378 } 379 380 389 protected final Field unpackField(Buffer packet, 390 boolean extractDefaultValues) { 391 if (this.use41Extensions) { 392 if (this.has41NewNewProt) { 395 int catalogNameStart = packet.getPosition() + 1; 396 int catalogNameLength = packet.fastSkipLenString(); 397 } 398 399 int databaseNameStart = packet.getPosition() + 1; 400 int databaseNameLength = packet.fastSkipLenString(); 401 402 int tableNameStart = packet.getPosition() + 1; 403 int tableNameLength = packet.fastSkipLenString(); 404 405 int originalTableNameStart = packet.getPosition() + 1; 407 int originalTableNameLength = packet.fastSkipLenString(); 408 409 int nameStart = packet.getPosition() + 1; 411 int nameLength = packet.fastSkipLenString(); 412 413 int originalColumnNameStart = packet.getPosition() + 1; 415 int originalColumnNameLength = packet.fastSkipLenString(); 416 417 packet.readByte(); 418 419 int charSetNumber = packet.readInt(); 420 421 int colLength = 0; 422 423 if (this.has41NewNewProt) { 424 colLength = (int) packet.readLong(); 426 } else { 427 colLength = packet.readLongInt(); 428 } 429 430 int colType = packet.readByte() & 0xff; 431 432 short colFlag = 0; 433 434 if (this.hasLongColumnInfo) { 435 colFlag = (short) (packet.readInt()); 436 } else { 437 colFlag = (short) (packet.readByte() & 0xff); 438 } 439 440 int colDecimals = packet.readByte() & 0xff; 441 442 int defaultValueStart = -1; 443 int defaultValueLength = -1; 444 445 if (extractDefaultValues) { 446 defaultValueStart = packet.getPosition() + 1; 447 defaultValueLength = packet.fastSkipLenString(); 448 } 449 450 Field field = new Field(this.connection, packet.getByteBuffer(), 451 databaseNameStart, databaseNameLength, tableNameStart, 452 tableNameLength, originalTableNameStart, 453 originalTableNameLength, nameStart, nameLength, 454 originalColumnNameStart, originalColumnNameLength, 455 colLength, colType, colFlag, colDecimals, 456 defaultValueStart, defaultValueLength, charSetNumber); 457 458 return field; 459 } else { 460 int tableNameStart = packet.getPosition() + 1; 461 int tableNameLength = packet.fastSkipLenString(); 462 int nameStart = packet.getPosition() + 1; 463 int nameLength = packet.fastSkipLenString(); 464 int colLength = packet.readnBytes(); 465 int colType = packet.readnBytes(); 466 packet.readByte(); 468 short colFlag = 0; 469 470 if (this.hasLongColumnInfo) { 471 colFlag = (short) (packet.readInt()); 472 } else { 473 colFlag = (short) (packet.readByte() & 0xff); 474 } 475 476 int colDecimals = (packet.readByte() & 0xff); 477 478 if (this.colDecimalNeedsBump) { 479 colDecimals++; 480 } 481 482 Field field = new Field(this.connection, packet.getBufferSource(), 483 nameStart, nameLength, tableNameStart, tableNameLength, 484 colLength, colType, colFlag, colDecimals); 485 486 return field; 487 } 488 } 489 490 493 protected void checkForCharsetMismatch() { 494 if (this.connection.useUnicode() 495 && (this.connection.getEncoding() != null)) { 496 String encodingToCheck = jvmPlatformCharset; 497 498 if (encodingToCheck == null) { 499 encodingToCheck = System.getProperty("file.encoding"); 500 } 501 502 if (encodingToCheck == null) { 503 this.platformDbCharsetMatches = false; 504 } else { 505 this.platformDbCharsetMatches = encodingToCheck.equals(this.connection 506 .getEncoding()); 507 } 508 } 509 } 510 511 static int getMaxBuf() { 512 return maxBufferSize; 513 } 514 515 520 final int getServerMajorVersion() { 521 return this.serverMajorVersion; 522 } 523 524 529 final int getServerMinorVersion() { 530 return this.serverMinorVersion; 531 } 532 533 538 final int getServerSubMinorVersion() { 539 return this.serverSubMinorVersion; 540 } 541 542 547 String getServerVersion() { 548 return this.serverVersion; 549 } 550 551 562 void doHandshake(String user, String password, String database) 563 throws java.sql.SQLException { 564 Buffer buf = readPacket(); 566 567 this.protocolVersion = buf.readByte(); 569 570 if (this.protocolVersion == -1) { 571 try { 572 this.mysqlConnection.close(); 573 } catch (Exception e) { 574 ; } 576 577 int errno = 2000; 578 579 errno = buf.readInt(); 580 581 String serverErrorMessage = buf.readString(); 582 583 StringBuffer errorBuf = new StringBuffer (" message from server: \""); 584 errorBuf.append(serverErrorMessage); 585 errorBuf.append("\""); 586 587 String xOpen = SQLError.mysqlToXOpen(errno); 588 589 throw new SQLException (SQLError.get(xOpen) + ", " 590 + errorBuf.toString(), xOpen, errno); 591 } 592 593 this.serverVersion = buf.readString(); 594 595 int point = this.serverVersion.indexOf("."); 597 598 if (point != -1) { 599 try { 600 int n = Integer.parseInt(this.serverVersion.substring(0, point)); 601 this.serverMajorVersion = n; 602 } catch (NumberFormatException NFE1) { 603 ; 604 } 605 606 String remaining = this.serverVersion.substring(point + 1, 607 this.serverVersion.length()); 608 point = remaining.indexOf("."); 609 610 if (point != -1) { 611 try { 612 int n = Integer.parseInt(remaining.substring(0, point)); 613 this.serverMinorVersion = n; 614 } catch (NumberFormatException nfe) { 615 ; 616 } 617 618 remaining = remaining.substring(point + 1, remaining.length()); 619 620 int pos = 0; 621 622 while (pos < remaining.length()) { 623 if ((remaining.charAt(pos) < '0') 624 || (remaining.charAt(pos) > '9')) { 625 break; 626 } 627 628 pos++; 629 } 630 631 try { 632 int n = Integer.parseInt(remaining.substring(0, pos)); 633 this.serverSubMinorVersion = n; 634 } catch (NumberFormatException nfe) { 635 ; 636 } 637 } 638 } 639 640 if (versionMeetsMinimum(4, 0, 8)) { 641 this.maxThreeBytes = (256 * 256 * 256) - 1; 642 this.useNewLargePackets = true; 643 } else { 644 this.maxThreeBytes = 255 * 255 * 255; 645 this.useNewLargePackets = false; 646 } 647 648 this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0); 649 this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5); 651 652 long threadId = buf.readLong(); 653 seed = buf.readString(); 654 655 if (Driver.TRACE) { 656 Debug.msg(this, "Protocol Version: " + (int) this.protocolVersion); 657 Debug.msg(this, "Server Version: " + this.serverVersion); 658 Debug.msg(this, "Thread ID: " + threadId); 659 Debug.msg(this, "Crypt Seed: " + seed); 660 } 661 662 int serverCapabilities = 0; 663 664 if (buf.getPosition() < buf.getBufLength()) { 665 serverCapabilities = buf.readInt(); 666 } 667 668 if (versionMeetsMinimum(4, 1, 1)) { 669 int position = buf.getPosition(); 670 671 672 int serverLanguage = buf.readInt(); buf.readInt(); 674 buf.setPosition(position + 16); 675 676 String seedPart2 = buf.readString(); 677 StringBuffer newSeed = new StringBuffer (20); 678 newSeed.append(seed); 679 newSeed.append(seedPart2); 680 this.seed = newSeed.toString(); 681 } 682 683 if (((serverCapabilities & CLIENT_COMPRESS) != 0) 684 && this.connection.useCompression()) { 685 clientParam |= CLIENT_COMPRESS; 686 } 687 688 if ((database != null) && (database.length() > 0)) { 689 clientParam |= CLIENT_CONNECT_WITH_DB; 690 } 691 692 if (((serverCapabilities & CLIENT_SSL) == 0) 693 && this.connection.useSSL()) { 694 this.connection.setUseSSL(false); 695 } 696 697 if ((serverCapabilities & CLIENT_LONG_FLAG) != 0) { 698 clientParam |= CLIENT_LONG_FLAG; 700 this.hasLongColumnInfo = true; 701 } 702 703 clientParam |= CLIENT_FOUND_ROWS; 705 706 if (this.connection.allowLoadLocalInfile()) { 707 clientParam |= CLIENT_LOCAL_FILES; 708 } 709 710 if (isInteractiveClient) { 711 clientParam |= CLIENT_INTERACTIVE; 712 } 713 714 if (this.protocolVersion > 9) { 716 clientParam |= CLIENT_LONG_PASSWORD; } else { 718 clientParam &= ~CLIENT_LONG_PASSWORD; 719 } 720 721 if (versionMeetsMinimum(4, 1, 0)) { 725 if (versionMeetsMinimum(4, 1, 1)) { 726 clientParam |= CLIENT_PROTOCOL_41; 727 this.has41NewNewProt = true; 728 } else { 729 clientParam |= CLIENT_RESERVED; 730 this.has41NewNewProt = false; 731 } 732 733 this.use41Extensions = true; 734 } 735 736 int passwordLength = 16; 737 int userLength = 0; 738 int databaseLength = 0; 739 740 if (user != null) { 741 userLength = user.length(); 742 } 743 744 if (database != null) { 745 databaseLength = database.length(); 746 } 747 748 int packLength = (userLength + passwordLength + databaseLength) + 7 749 + HEADER_LENGTH; 750 Buffer packet = null; 751 752 if (!connection.useSSL()) { 753 if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { 754 clientParam |= CLIENT_SECURE_CONNECTION; 755 756 if (versionMeetsMinimum(4, 1, 1)) { 757 secureAuth411(packLength, serverCapabilities, clientParam, 758 user, password, database); 759 } else { 760 secureAuth(packLength, serverCapabilities, clientParam, 761 user, password, database); 762 } 763 } else { 764 packet = new Buffer(packLength); 765 766 if ((clientParam & CLIENT_RESERVED) != 0) { 767 if (versionMeetsMinimum(4, 1, 1)) { 768 packet.writeLong(clientParam); 769 packet.writeLong(this.maxThreeBytes); 770 771 packet.writeByte((byte) 8); 775 776 packet.writeBytesNoNull(new byte[23]); 778 } else { 779 packet.writeLong(clientParam); 780 packet.writeLong(this.maxThreeBytes); 781 } 782 } else { 783 packet.writeInt((int) clientParam); 784 packet.writeLongInt(this.maxThreeBytes); 785 } 786 787 packet.writeString(user); 789 790 if (this.protocolVersion > 9) { 791 packet.writeString(Util.newCrypt(password, this.seed)); 792 } else { 793 packet.writeString(Util.oldCrypt(password, this.seed)); 794 } 795 796 if (((serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0) 797 && (database != null) && (database.length() > 0)) { 798 packet.writeString(database); 799 } 800 801 send(packet); 802 } 803 } else { 804 boolean doSecureAuth = false; 805 806 if ((serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) { 807 clientParam |= CLIENT_SECURE_CONNECTION; 808 doSecureAuth = true; 809 } 810 811 clientParam |= CLIENT_SSL; 812 packet = new Buffer(packLength); 813 814 if ((clientParam & CLIENT_RESERVED) != 0) { 815 packet.writeLong(clientParam); 816 } else { 817 packet.writeInt((int) clientParam); 818 } 819 820 send(packet); 821 822 javax.net.ssl.SSLSocketFactory sslFact = (javax.net.ssl.SSLSocketFactory) javax.net.ssl.SSLSocketFactory 823 .getDefault(); 824 825 try { 826 this.mysqlConnection = sslFact.createSocket(this.mysqlConnection, 827 |