forked from microsoft/mssql-jdbc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIOBuffer.java
8226 lines (7001 loc) · 349 KB
/
IOBuffer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
* available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.security.Provider;
import java.security.Security;
import java.sql.Timestamp;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.SocketFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import com.microsoft.sqlserver.jdbc.SQLServerConnection.FedAuthTokenCommand;
import com.microsoft.sqlserver.jdbc.dataclassification.SensitivityClassification;
final class TDS {
// application protocol
static final String PROTOCOL_TDS80 = "tds/8.0"; // TLS-first connections
// TDS versions
static final int VER_TDS80 = 0x08000000; // TDS 8.0
static final int VER_DENALI = 0x74000004; // TDS 7.4
static final int VER_KATMAI = 0x730B0003; // TDS 7.3B(includes null bit compression)
static final int VER_YUKON = 0x72090002; // TDS 7.2
static final int VER_UNKNOWN = 0x00000000; // Unknown/uninitialized
static final int TDS_RET_STAT = 0x79;
static final int TDS_COLMETADATA = 0x81;
static final int TDS_TABNAME = 0xA4;
static final int TDS_COLINFO = 0xA5;
static final int TDS_ORDER = 0xA9;
static final int TDS_ERR = 0xAA;
static final int TDS_MSG = 0xAB;
static final int TDS_RETURN_VALUE = 0xAC;
static final int TDS_LOGIN_ACK = 0xAD;
static final int TDS_FEATURE_EXTENSION_ACK = 0xAE;
static final int TDS_ROW = 0xD1;
static final int TDS_NBCROW = 0xD2;
static final int TDS_ENV_CHG = 0xE3;
static final int TDS_SESSION_STATE = 0xE4;
static final int TDS_SSPI = 0xED;
static final int TDS_DONE = 0xFD;
static final int TDS_DONEPROC = 0xFE;
static final int TDS_DONEINPROC = 0xFF;
static final int TDS_FEDAUTHINFO = 0xEE;
static final int TDS_SQLRESCOLSRCS = 0xa2;
static final int TDS_SQLDATACLASSIFICATION = 0xa3;
// DONE status https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/3c06f110-98bd-4d5b-b836-b1ba66452cb7
static final int DONE_FINAL = 0x0000;
static final int DONE_MORE = 0x0001;
static final int DONE_COUNT = 0x0010;
static final int DONE_ERROR = 0x0002;
static final int DONE_ATTN = 0x0020;
static final int DONE_SRVERROR = 0x0100;
// FedAuth
static final byte TDS_FEATURE_EXT_FEDAUTH = 0x02;
static final int TDS_FEDAUTH_LIBRARY_SECURITYTOKEN = 0x01;
static final int TDS_FEDAUTH_LIBRARY_ADAL = 0x02;
static final int TDS_FEDAUTH_LIBRARY_RESERVED = 0x7F;
// workflows
static final byte ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01;
static final byte ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED = 0x02;
static final byte ADALWORKFLOW_ACTIVEDIRECTORYMANAGEDIDENTITY = 0x03;
static final byte ADALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE = 0x03;
static final byte ADALWORKFLOW_ACTIVEDIRECTORYDEFAULT = 0x03;
static final byte ADALWORKFLOW_ACCESSTOKENCALLBACK = 0x03;
static final byte ADALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL = 0x01; // Using the Password byte as that is the
// closest we have
static final byte ADALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPALCERTIFICATE = 0x01;
static final byte FEDAUTH_INFO_ID_STSURL = 0x01; // FedAuthInfoData is token endpoint URL from which to acquire fed
// auth token
static final byte FEDAUTH_INFO_ID_SPN = 0x02; // FedAuthInfoData is the SPN to use for acquiring fed auth token
// AE constants
// 0x03 is for x_eFeatureExtensionId_Rcs
static final byte TDS_FEATURE_EXT_AE = 0x04;
static final byte COLUMNENCRYPTION_NOT_SUPPORTED = 0x00; // column encryption not supported
static final byte COLUMNENCRYPTION_VERSION1 = 0x01; // column encryption without enclave
static final byte COLUMNENCRYPTION_VERSION2 = 0x02; // column encryption with enclave
static final byte COLUMNENCRYPTION_VERSION3 = 0x03; // column encryption with enclave, with retry
static final int CUSTOM_CIPHER_ALGORITHM_ID = 0; // max version
// 0x06 is for x_eFeatureExtensionId_LoginToken
// 0x07 is for x_eFeatureExtensionId_ClientSideTelemetry
// Data Classification constants
static final byte TDS_FEATURE_EXT_DATACLASSIFICATION = 0x09;
static final byte DATA_CLASSIFICATION_NOT_ENABLED = 0x00;
static final byte MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION = 0x02;
static final byte DATA_CLASSIFICATION_VERSION_ADDED_RANK_SUPPORT = 0x02;
static final int AES_256_CBC = 1;
static final int AEAD_AES_256_CBC_HMAC_SHA256 = 2;
static final int AE_METADATA = 0x08;
static final byte TDS_FEATURE_EXT_UTF8SUPPORT = 0x0A;
static final byte TDS_FEATURE_EXT_AZURESQLDNSCACHING = 0x0B;
static final byte TDS_FEATURE_EXT_SESSIONRECOVERY = 0x01;
static final int TDS_TVP = 0xF3;
static final int TVP_ROW = 0x01;
static final int TVP_NULL_TOKEN = 0xFFFF;
static final int TVP_STATUS_DEFAULT = 0x02;
static final int TVP_ORDER_UNIQUE_TOKEN = 0x10;
// TVP_ORDER_UNIQUE_TOKEN flags
static final byte TVP_ORDERASC_FLAG = 0x1;
static final byte TVP_ORDERDESC_FLAG = 0x2;
static final byte TVP_UNIQUE_FLAG = 0x4;
// TVP flags, may be used in other places
static final int FLAG_NULLABLE = 0x01;
static final int FLAG_TVP_DEFAULT_COLUMN = 0x200;
static final int FEATURE_EXT_TERMINATOR = -1;
// Sql_variant length
static final int SQL_VARIANT_LENGTH = 8009;
static final String getTokenName(int tdsTokenType) {
switch (tdsTokenType) {
case TDS_RET_STAT:
return "TDS_RET_STAT (0x79)";
case TDS_COLMETADATA:
return "TDS_COLMETADATA (0x81)";
case TDS_TABNAME:
return "TDS_TABNAME (0xA4)";
case TDS_COLINFO:
return "TDS_COLINFO (0xA5)";
case TDS_ORDER:
return "TDS_ORDER (0xA9)";
case TDS_ERR:
return "TDS_ERR (0xAA)";
case TDS_MSG:
return "TDS_MSG (0xAB)";
case TDS_RETURN_VALUE:
return "TDS_RETURN_VALUE (0xAC)";
case TDS_LOGIN_ACK:
return "TDS_LOGIN_ACK (0xAD)";
case TDS_FEATURE_EXTENSION_ACK:
return "TDS_FEATURE_EXTENSION_ACK (0xAE)";
case TDS_ROW:
return "TDS_ROW (0xD1)";
case TDS_NBCROW:
return "TDS_NBCROW (0xD2)";
case TDS_ENV_CHG:
return "TDS_ENV_CHG (0xE3)";
case TDS_SESSION_STATE:
return "TDS_SESSION_STATE (0xE4)";
case TDS_SSPI:
return "TDS_SSPI (0xED)";
case TDS_DONE:
return "TDS_DONE (0xFD)";
case TDS_DONEPROC:
return "TDS_DONEPROC (0xFE)";
case TDS_DONEINPROC:
return "TDS_DONEINPROC (0xFF)";
case TDS_FEDAUTHINFO:
return "TDS_FEDAUTHINFO (0xEE)";
case TDS_FEATURE_EXT_DATACLASSIFICATION:
return "TDS_FEATURE_EXT_DATACLASSIFICATION (0x09)";
case TDS_FEATURE_EXT_UTF8SUPPORT:
return "TDS_FEATURE_EXT_UTF8SUPPORT (0x0A)";
case TDS_FEATURE_EXT_AZURESQLDNSCACHING:
return "TDS_FEATURE_EXT_AZURESQLDNSCACHING (0x0B)";
case TDS_FEATURE_EXT_SESSIONRECOVERY:
return "TDS_FEATURE_EXT_SESSIONRECOVERY (0x01)";
default:
return "unknown token (0x" + Integer.toHexString(tdsTokenType).toUpperCase() + ")";
}
}
// RPC ProcIDs for use with RPCRequest (PKT_RPC) calls
static final short PROCID_SP_CURSOR = 1;
static final short PROCID_SP_CURSOROPEN = 2;
static final short PROCID_SP_CURSORPREPARE = 3;
static final short PROCID_SP_CURSOREXECUTE = 4;
static final short PROCID_SP_CURSORPREPEXEC = 5;
static final short PROCID_SP_CURSORUNPREPARE = 6;
static final short PROCID_SP_CURSORFETCH = 7;
static final short PROCID_SP_CURSOROPTION = 8;
static final short PROCID_SP_CURSORCLOSE = 9;
static final short PROCID_SP_EXECUTESQL = 10;
static final short PROCID_SP_PREPARE = 11;
static final short PROCID_SP_EXECUTE = 12;
static final short PROCID_SP_PREPEXEC = 13;
static final short PROCID_SP_PREPEXECRPC = 14;
static final short PROCID_SP_UNPREPARE = 15;
// Constants for use with cursor RPCs
static final short SP_CURSOR_OP_UPDATE = 1;
static final short SP_CURSOR_OP_DELETE = 2;
static final short SP_CURSOR_OP_INSERT = 4;
static final short SP_CURSOR_OP_REFRESH = 8;
static final short SP_CURSOR_OP_LOCK = 16;
static final short SP_CURSOR_OP_SETPOSITION = 32;
static final short SP_CURSOR_OP_ABSOLUTE = 64;
// Constants for server-cursored result sets.
// See the Engine Cursors Functional Specification for details.
static final int FETCH_FIRST = 1;
static final int FETCH_NEXT = 2;
static final int FETCH_PREV = 4;
static final int FETCH_LAST = 8;
static final int FETCH_ABSOLUTE = 16;
static final int FETCH_RELATIVE = 32;
static final int FETCH_REFRESH = 128;
static final int FETCH_INFO = 256;
static final int FETCH_PREV_NOADJUST = 512;
static final byte RPC_OPTION_NO_METADATA = (byte) 0x02;
// Transaction manager request types
static final short TM_GET_DTC_ADDRESS = 0;
static final short TM_PROPAGATE_XACT = 1;
static final short TM_BEGIN_XACT = 5;
static final short TM_PROMOTE_PROMOTABLE_XACT = 6;
static final short TM_COMMIT_XACT = 7;
static final short TM_ROLLBACK_XACT = 8;
static final short TM_SAVE_XACT = 9;
static final byte PKT_QUERY = 1;
static final byte PKT_RPC = 3;
static final byte PKT_REPLY = 4;
static final byte PKT_CANCEL_REQ = 6;
static final byte PKT_BULK = 7;
static final byte PKT_DTC = 14;
static final byte PKT_LOGON70 = 16; // 0x10
static final byte PKT_SSPI = 17;
static final byte PKT_PRELOGIN = 18; // 0x12
static final byte PKT_FEDAUTH_TOKEN_MESSAGE = 8; // Authentication token for federated authentication
static final byte STATUS_NORMAL = 0x00;
static final byte STATUS_BIT_EOM = 0x01;
static final byte STATUS_BIT_ATTENTION = 0x02;// this is called ignore bit in TDS spec
static final byte STATUS_BIT_RESET_CONN = 0x08;
// Various TDS packet size constants
static final int INVALID_PACKET_SIZE = -1;
static final int INITIAL_PACKET_SIZE = 4096;
static final int MIN_PACKET_SIZE = 512;
static final int MAX_PACKET_SIZE = 32767;
static final int DEFAULT_PACKET_SIZE = 8000;
static final int SERVER_PACKET_SIZE = 0; // Accept server's configured packet size
// TDS packet header size and offsets
static final int PACKET_HEADER_SIZE = 8;
static final int PACKET_HEADER_MESSAGE_TYPE = 0;
static final int PACKET_HEADER_MESSAGE_STATUS = 1;
static final int PACKET_HEADER_MESSAGE_LENGTH = 2;
static final int PACKET_HEADER_SPID = 4;
static final int PACKET_HEADER_SEQUENCE_NUM = 6;
static final int PACKET_HEADER_WINDOW = 7; // Reserved/Not used
// MARS header length:
// 2 byte header type
// 8 byte transaction descriptor
// 4 byte outstanding request count
static final int MARS_HEADER_LENGTH = 18; // 2 byte header type, 8 byte transaction descriptor,
static final int TRACE_HEADER_LENGTH = 26; // header length (4) + header type (2) + guid (16) + Sequence number size
// (4)
static final short HEADERTYPE_TRACE = 3; // trace header type
// Message header length
static final int MESSAGE_HEADER_LENGTH = MARS_HEADER_LENGTH + 4; // length includes message header itself
static final byte B_PRELOGIN_OPTION_VERSION = 0x00;
static final byte B_PRELOGIN_OPTION_ENCRYPTION = 0x01;
static final byte B_PRELOGIN_OPTION_INSTOPT = 0x02;
static final byte B_PRELOGIN_OPTION_THREADID = 0x03;
static final byte B_PRELOGIN_OPTION_MARS = 0x04;
static final byte B_PRELOGIN_OPTION_TRACEID = 0x05;
static final byte B_PRELOGIN_OPTION_FEDAUTHREQUIRED = 0x06;
static final byte B_PRELOGIN_OPTION_TERMINATOR = (byte) 0xFF;
// Login option byte 1
static final byte LOGIN_OPTION1_ORDER_X86 = 0x00;
static final byte LOGIN_OPTION1_ORDER_6800 = 0x01;
static final byte LOGIN_OPTION1_CHARSET_ASCII = 0x00;
static final byte LOGIN_OPTION1_CHARSET_EBCDIC = 0x02;
static final byte LOGIN_OPTION1_FLOAT_IEEE_754 = 0x00;
static final byte LOGIN_OPTION1_FLOAT_VAX = 0x04;
static final byte LOGIN_OPTION1_FLOAT_ND5000 = 0x08;
static final byte LOGIN_OPTION1_DUMPLOAD_ON = 0x00;
static final byte LOGIN_OPTION1_DUMPLOAD_OFF = 0x10;
static final byte LOGIN_OPTION1_USE_DB_ON = 0x00;
static final byte LOGIN_OPTION1_USE_DB_OFF = 0x20;
static final byte LOGIN_OPTION1_INIT_DB_WARN = 0x00;
static final byte LOGIN_OPTION1_INIT_DB_FATAL = 0x40;
static final byte LOGIN_OPTION1_SET_LANG_OFF = 0x00;
static final byte LOGIN_OPTION1_SET_LANG_ON = (byte) 0x80;
// Login option byte 2
static final byte LOGIN_OPTION2_INIT_LANG_WARN = 0x00;
static final byte LOGIN_OPTION2_INIT_LANG_FATAL = 0x01;
static final byte LOGIN_OPTION2_ODBC_OFF = 0x00;
static final byte LOGIN_OPTION2_ODBC_ON = 0x02;
static final byte LOGIN_OPTION2_TRAN_BOUNDARY_OFF = 0x00;
static final byte LOGIN_OPTION2_TRAN_BOUNDARY_ON = 0x04;
static final byte LOGIN_OPTION2_CACHE_CONNECTION_OFF = 0x00;
static final byte LOGIN_OPTION2_CACHE_CONNECTION_ON = 0x08;
static final byte LOGIN_OPTION2_USER_NORMAL = 0x00;
static final byte LOGIN_OPTION2_USER_SERVER = 0x10;
static final byte LOGIN_OPTION2_USER_REMUSER = 0x20;
static final byte LOGIN_OPTION2_USER_SQLREPL_OFF = 0x00;
static final byte LOGIN_OPTION2_USER_SQLREPL_ON = 0x30;
static final byte LOGIN_OPTION2_INTEGRATED_SECURITY_OFF = 0x00;
static final byte LOGIN_OPTION2_INTEGRATED_SECURITY_ON = (byte) 0x80;
// Login option byte 3
static final byte LOGIN_OPTION3_DEFAULT = 0x00;
static final byte LOGIN_OPTION3_CHANGE_PASSWORD = 0x01;
static final byte LOGIN_OPTION3_SEND_YUKON_BINARY_XML = 0x02;
static final byte LOGIN_OPTION3_USER_INSTANCE = 0x04;
static final byte LOGIN_OPTION3_UNKNOWN_COLLATION_HANDLING = 0x08;
static final byte LOGIN_OPTION3_FEATURE_EXTENSION = 0x10;
// Login type flag (bits 5 - 7 reserved for future use)
static final byte LOGIN_SQLTYPE_DEFAULT = 0x00;
static final byte LOGIN_SQLTYPE_TSQL = 0x01;
static final byte LOGIN_SQLTYPE_ANSI_V1 = 0x02;
static final byte LOGIN_SQLTYPE_ANSI89_L1 = 0x03;
static final byte LOGIN_SQLTYPE_ANSI89_L2 = 0x04;
static final byte LOGIN_SQLTYPE_ANSI89_IEF = 0x05;
static final byte LOGIN_SQLTYPE_ANSI89_ENTRY = 0x06;
static final byte LOGIN_SQLTYPE_ANSI89_TRANS = 0x07;
static final byte LOGIN_SQLTYPE_ANSI89_INTER = 0x08;
static final byte LOGIN_SQLTYPE_ANSI89_FULL = 0x09;
static final byte LOGIN_OLEDB_OFF = 0x00;
static final byte LOGIN_OLEDB_ON = 0x10;
static final byte LOGIN_READ_ONLY_INTENT = 0x20;
static final byte LOGIN_READ_WRITE_INTENT = 0x00;
static final byte ENCRYPT_OFF = 0x00;
static final byte ENCRYPT_ON = 0x01;
static final byte ENCRYPT_NOT_SUP = 0x02;
static final byte ENCRYPT_REQ = 0x03;
static final byte ENCRYPT_CLIENT_CERT = (byte) 0x80;
static final byte ENCRYPT_INVALID = (byte) 0xFF;
static final String getEncryptionLevel(int level) {
switch (level) {
case ENCRYPT_OFF:
return "OFF";
case ENCRYPT_ON:
return "ON";
case ENCRYPT_NOT_SUP:
return "NOT SUPPORTED";
case ENCRYPT_REQ:
return "REQUIRED";
default:
return "unknown encryption level (0x" + Integer.toHexString(level).toUpperCase() + ")";
}
}
// Prelogin packet length, including the tds header,
// version, encrpytion, and traceid data sessions.
// For detailed info, please check the definition of
// preloginRequest in Prelogin function.
static final byte B_PRELOGIN_MESSAGE_LENGTH = 67;
static final byte B_PRELOGIN_MESSAGE_LENGTH_WITH_FEDAUTH = 73;
// Scroll options and concurrency options lifted out
// of the the Yukon cursors spec for sp_cursoropen.
final static int SCROLLOPT_KEYSET = 1;
final static int SCROLLOPT_DYNAMIC = 2;
final static int SCROLLOPT_FORWARD_ONLY = 4;
final static int SCROLLOPT_STATIC = 8;
final static int SCROLLOPT_FAST_FORWARD = 16;
final static int SCROLLOPT_PARAMETERIZED_STMT = 4096;
final static int SCROLLOPT_AUTO_FETCH = 8192;
final static int SCROLLOPT_AUTO_CLOSE = 16384;
final static int CCOPT_READ_ONLY = 1;
final static int CCOPT_SCROLL_LOCKS = 2;
final static int CCOPT_OPTIMISTIC_CC = 4;
final static int CCOPT_OPTIMISTIC_CCVAL = 8;
final static int CCOPT_ALLOW_DIRECT = 8192;
final static int CCOPT_UPDT_IN_PLACE = 16384;
// Result set rows include an extra, "hidden" ROWSTAT column which indicates
// the overall success or failure of the row fetch operation. With a keyset
// cursor, the value in the ROWSTAT column indicates whether the row has been
// deleted from the database.
static final int ROWSTAT_FETCH_SUCCEEDED = 1;
static final int ROWSTAT_FETCH_MISSING = 2;
// ColumnInfo status
final static int COLINFO_STATUS_EXPRESSION = 0x04;
final static int COLINFO_STATUS_KEY = 0x08;
final static int COLINFO_STATUS_HIDDEN = 0x10;
final static int COLINFO_STATUS_DIFFERENT_NAME = 0x20;
final static int MAX_FRACTIONAL_SECONDS_SCALE = 7;
final static int DEFAULT_FRACTIONAL_SECONDS_SCALE = 3;
final static Timestamp MAX_TIMESTAMP = Timestamp.valueOf("2079-06-06 23:59:59");
final static Timestamp MIN_TIMESTAMP = Timestamp.valueOf("1900-01-01 00:00:00");
static int nanosSinceMidnightLength(int scale) {
final int[] scaledLengths = {3, 3, 3, 4, 4, 5, 5, 5};
assert scale >= 0;
assert scale <= MAX_FRACTIONAL_SECONDS_SCALE;
return scaledLengths[scale];
}
final static int DAYS_INTO_CE_LENGTH = 3;
final static int MINUTES_OFFSET_LENGTH = 2;
// Number of days in a "normal" (non-leap) year according to SQL Server.
final static int DAYS_PER_YEAR = 365;
final static int BASE_YEAR_1900 = 1900;
final static int BASE_YEAR_1970 = 1970;
final static String BASE_DATE_1970 = "1970-01-01";
final static LocalDate BASE_LOCAL_DATE = LocalDate.of(1, 1, 1);
final static LocalDate BASE_LOCAL_DATE_1900 = LocalDate.of(1900, 1, 1);
static int timeValueLength(int scale) {
return nanosSinceMidnightLength(scale);
}
static int datetime2ValueLength(int scale) {
return DAYS_INTO_CE_LENGTH + nanosSinceMidnightLength(scale);
}
static int datetimeoffsetValueLength(int scale) {
return DAYS_INTO_CE_LENGTH + MINUTES_OFFSET_LENGTH + nanosSinceMidnightLength(scale);
}
// TDS is just a namespace - it can't be instantiated.
private TDS() {}
}
class Nanos {
static final int PER_SECOND = 1000000000;
static final int PER_MAX_SCALE_INTERVAL = PER_SECOND / (int) Math.pow(10, TDS.MAX_FRACTIONAL_SECONDS_SCALE);
static final int PER_MILLISECOND = PER_SECOND / 1000;
static final long PER_DAY = 24 * 60 * 60 * (long) PER_SECOND;
private Nanos() {}
}
// Constants relating to the historically accepted Julian-Gregorian calendar cutover date (October 15, 1582).
//
// Used in processing SQL Server temporal data types whose date component may precede that date.
//
// Scoping these constants to a class defers their initialization to first use.
class GregorianChange {
// Cutover date for a pure Gregorian calendar - that is, a proleptic Gregorian calendar with
// Gregorian leap year behavior throughout its entire range. This is the cutover date is used
// with temporal server values, which are represented in terms of number of days relative to a
// base date.
static final java.util.Date PURE_CHANGE_DATE = new java.util.Date(Long.MIN_VALUE);
// The standard Julian to Gregorian cutover date (October 15, 1582) that the JDBC temporal
// classes (Time, Date, Timestamp) assume when converting to and from their UTC milliseconds
// representations.
static final java.util.Date STANDARD_CHANGE_DATE = (new GregorianCalendar(Locale.US)).getGregorianChange();
// A hint as to the number of days since 1/1/0001, past which we do not need to
// not rationalize the difference between SQL Server behavior (pure Gregorian)
// and Java behavior (standard Gregorian).
//
// Not having to rationalize the difference has a substantial (measured) performance benefit
// for temporal getters.
//
// The hint does not need to be exact, as long as it's later than the actual change date.
static final int DAYS_SINCE_BASE_DATE_HINT = DDC.daysSinceBaseDate(1583, 1, 1);
// Extra days that need to added to a pure gregorian date, post the gergorian
// cut over date, to match the default julian-gregorain calendar date of java.
static final int EXTRA_DAYS_TO_BE_ADDED;
static {
// This issue refers to the following bugs in java(same issue).
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7109480
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6459836
// The issue is fixed in JRE 1.7
// and exists in all the older versions.
// Due to the above bug, in older JVM versions(1.6 and before),
// the date calculation is incorrect at the Gregorian cut over date.
// i.e. the next date after Oct 4th 1582 is Oct 17th 1582, where as
// it should have been Oct 15th 1582.
// We intentionally do not make a check based on JRE version.
// If we do so, our code would break if the bug is fixed in a later update
// to an older JRE. So, we check for the existence of the bug instead.
GregorianCalendar cal = new GregorianCalendar(Locale.US);
cal.clear();
cal.set(1, Calendar.FEBRUARY, 577738, 0, 0, 0);// 577738 = 1+577737(no of days since epoch that brings us to oct
// 15th 1582)
if (cal.get(Calendar.DAY_OF_MONTH) == 15) {
// If the date calculation is correct(the above bug is fixed),
// post the default gregorian cut over date, the pure gregorian date
// falls short by two days for all dates compared to julian-gregorian date.
// so, we add two extra days for functional correctness.
// Note: other ways, in which this issue can be fixed instead of
// trying to detect the JVM bug is
// a) use unoptimized code path in the function convertTemporalToObject
// b) use cal.add api instead of cal.set api in the current optimized code path
// In both the above approaches, the code is about 6-8 times slower,
// resulting in an overall perf regression of about (10-30)% for perf test cases
EXTRA_DAYS_TO_BE_ADDED = 2;
} else
EXTRA_DAYS_TO_BE_ADDED = 0;
}
private GregorianChange() {}
}
final class UTC {
// UTC/GMT time zone singleton.
static final TimeZone timeZone = new SimpleTimeZone(0, "UTC");
private UTC() {}
}
/**
* TDS Channel
*/
final class TDSChannel implements Serializable {
/**
* Always update serialVersionUID when prompted.
*/
private static final long serialVersionUID = -866497813437384090L;
private static final Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.TDS.Channel");
final Logger getLogger() {
return logger;
}
private final String traceID;
final public String toString() {
return traceID;
}
private final SQLServerConnection con;
private final transient TDSWriter tdsWriter;
final TDSWriter getWriter() {
return tdsWriter;
}
final TDSReader getReader(TDSCommand command) {
return new TDSReader(this, con, command);
}
// Socket for raw TCP/IP communications with SQL Server
private transient Socket tcpSocket;
// Socket for SSL-encrypted communications with SQL Server
private transient SSLSocket sslSocket;
/*
* Socket providing the communications interface to the driver. For SSL-encrypted connections, this is the SSLSocket
* wrapped around the TCP socket. For unencrypted connections, it is just the TCP socket itself.
*/
@SuppressWarnings("unused")
private transient Socket channelSocket;
// Implementation of a Socket proxy that can switch from TDS-wrapped I/O
// (using the TDSChannel itself) during SSL handshake to raw I/O over
// the TCP/IP socket.
private transient ProxySocket proxySocket = null;
// I/O streams for raw TCP/IP communications with SQL Server
private transient ProxyInputStream tcpInputStream;
private transient OutputStream tcpOutputStream;
// I/O streams providing the communications interface to the driver.
// For SSL-encrypted connections, these are streams obtained from
// the SSL socket above. They wrap the underlying TCP streams.
// For unencrypted connections, they are just the TCP streams themselves.
private transient ProxyInputStream inputStream;
private final transient Lock inputStreamLock = new ReentrantLock();
private transient OutputStream outputStream;
private final transient Lock outputStreamLock = new ReentrantLock();
/** TDS packet payload logger */
private static Logger packetLogger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.TDS.DATA");
private final boolean isLoggingPackets = packetLogger.isLoggable(Level.FINEST);
final boolean isLoggingPackets() {
return isLoggingPackets;
}
// Number of TDS messages sent to and received from the server
int numMsgsSent = 0;
int numMsgsRcvd = 0;
private final transient Lock tdsChannelLock = new ReentrantLock();
// Last SPID received from the server. Used for logging and to tag subsequent outgoing
// packets to facilitate diagnosing problems from the server side.
private int spid = 0;
void setSPID(int spid) {
this.spid = spid;
}
int getSPID() {
return spid;
}
void resetPooledConnection() {
tdsWriter.resetPooledConnection();
}
TDSChannel(SQLServerConnection con) {
this.con = con;
traceID = "TDSChannel (" + con.toString() + ")";
this.tcpSocket = null;
this.sslSocket = null;
this.channelSocket = null;
this.tcpInputStream = null;
this.tcpOutputStream = null;
this.inputStream = null;
this.outputStream = null;
this.tdsWriter = new TDSWriter(this, con);
}
/**
* Opens the physical communications channel (TCP/IP socket and I/O streams) to the SQL Server.
*
* @param iPAddressPreference
* Preferred type of IP address to use first
*
* @return InetSocketAddress of the connection socket.
*/
final InetSocketAddress open(String host, int port, int timeoutMillis, boolean useParallel, boolean useTnir,
boolean isTnirFirstAttempt, int timeoutMillisForFullTimeout,
String iPAddressPreference) throws SQLServerException {
if (logger.isLoggable(Level.FINER))
logger.finer(this.toString() + ": Opening TCP socket...");
SocketFinder socketFinder = new SocketFinder(traceID, con);
channelSocket = tcpSocket = socketFinder.findSocket(host, port, timeoutMillis, useParallel, useTnir,
isTnirFirstAttempt, timeoutMillisForFullTimeout, iPAddressPreference);
try {
// Set socket options
tcpSocket.setTcpNoDelay(true);
tcpSocket.setKeepAlive(true);
setSocketOptions(tcpSocket, this);
int socketTimeout = con.getSocketTimeoutMilliseconds();
// socket timeout should be bounded by loginTimeout before connected
if (!con.isConnected()) {
socketTimeout = Math.min(con.timerRemaining(con.timerExpire), socketTimeout);
}
tcpSocket.setSoTimeout(socketTimeout);
inputStream = tcpInputStream = new ProxyInputStream(tcpSocket.getInputStream());
outputStream = tcpOutputStream = tcpSocket.getOutputStream();
} catch (IOException ex) {
SQLServerException.convertConnectExceptionToSQLServerException(host, port, con, ex);
}
return (InetSocketAddress) channelSocket.getRemoteSocketAddress();
}
/**
* Set TCP keep-alive options for idle connection resiliency
*/
private void setSocketOptions(Socket tcpSocket, TDSChannel channel) {
try {
if (SQLServerDriver.socketSetOptionMethod != null && SQLServerDriver.socketKeepIdleOption != null
&& SQLServerDriver.socketKeepIntervalOption != null) {
if (logger.isLoggable(Level.FINER)) {
logger.finer(channel.toString() + ": Setting KeepAlive extended socket options.");
}
SQLServerDriver.socketSetOptionMethod.invoke(tcpSocket, SQLServerDriver.socketKeepIdleOption, 30); // 30 seconds
SQLServerDriver.socketSetOptionMethod.invoke(tcpSocket, SQLServerDriver.socketKeepIntervalOption, 1); // 1 second
}
} catch (IllegalAccessException | InvocationTargetException e) {
if (logger.isLoggable(Level.FINER)) {
logger.finer(channel.toString() + ": KeepAlive extended socket options not supported on this platform. "
+ e.getMessage());
}
}
}
/**
* Disables SSL on this TDS channel.
*/
void disableSSL() {
if (logger.isLoggable(Level.FINER)) {
logger.finer(toString() + " Disabling SSL...");
}
tdsChannelLock.lock();
try {
// Guard in case of disableSSL being called before enableSSL
if (proxySocket == null) {
if (logger.isLoggable(Level.INFO))
logger.finer(toString() + " proxySocket is null, exit early");
return;
}
/*
* The mission: To close the SSLSocket and release everything that it is holding onto other than the TCP/IP
* socket and streams. The challenge: Simply closing the SSLSocket tries to do additional, unnecessary shutdown
* I/O over the TCP/IP streams that are bound to the socket proxy, resulting in a not responding and confusing
* SQL Server. Solution: Rewire the ProxySocket's input and output streams (one more time) to closed streams.
* SSLSocket sees that the streams are already closed and does not attempt to do any further I/O on them before
* closing itself.
*/
// Create a couple of cheap closed streams
InputStream is = new ByteArrayInputStream(new byte[0]);
try {
is.close();
} catch (IOException e) {
// No reason to expect a brand new ByteArrayInputStream not to close,
// but just in case...
logger.fine("Ignored error closing InputStream: " + e.getMessage());
}
OutputStream os = new ByteArrayOutputStream();
try {
os.close();
} catch (IOException e) {
// No reason to expect a brand new ByteArrayOutputStream not to close,
// but just in case...
logger.fine("Ignored error closing OutputStream: " + e.getMessage());
}
// Rewire the proxy socket to the closed streams
if (logger.isLoggable(Level.FINEST))
logger.finest(toString() + " Rewiring proxy streams for SSL socket close");
proxySocket.setStreams(is, os);
// Now close the SSL socket. It will see that the proxy socket's streams
// are closed and not try to do any further I/O over them.
try {
if (logger.isLoggable(Level.FINER))
logger.finer(toString() + " Closing SSL socket");
sslSocket.close();
} catch (IOException e) {
// Don't care if we can't close the SSL socket. We're done with it anyway.
logger.fine("Ignored error closing SSLSocket: " + e.getMessage());
}
// Do not close the proxy socket. Doing so would close our TCP socket
// to which the proxy socket is bound. Instead, just null out the reference
// to free up the few resources it holds onto.
proxySocket = null;
// Finally, with all of the SSL support out of the way, put the TDSChannel
// back to using the TCP/IP socket and streams directly.
inputStream = tcpInputStream;
outputStream = tcpOutputStream;
channelSocket = tcpSocket;
sslSocket = null;
} finally {
tdsChannelLock.unlock();
}
if (logger.isLoggable(Level.FINER))
logger.finer(toString() + " SSL disabled");
}
/**
* Used during SSL handshake, this class implements an InputStream that reads SSL handshake response data (framed in
* TDS messages) from the TDS channel.
*/
private class SSLHandshakeInputStream extends InputStream {
private final TDSReader tdsReader;
private final SSLHandshakeOutputStream sslHandshakeOutputStream;
private final Logger logger;
private final String logContext;
SSLHandshakeInputStream(TDSChannel tdsChannel, SSLHandshakeOutputStream sslHandshakeOutputStream) {
this.tdsReader = tdsChannel.getReader(null);
this.sslHandshakeOutputStream = sslHandshakeOutputStream;
this.logger = tdsChannel.getLogger();
this.logContext = tdsChannel.toString() + " (SSLHandshakeInputStream):";
}
/**
* If there is no handshake response data available to be read from existing packets then this method ensures
* that the SSL handshake output stream has been flushed to the server, and reads another packet (starting the
* next TDS response message).
*
* Note that simply using TDSReader.ensurePayload isn't sufficient as it does not automatically start the new
* response message.
*/
private void ensureSSLPayload() throws IOException {
if (0 == tdsReader.available()) {
if (logger.isLoggable(Level.FINEST))
logger.finest(logContext
+ " No handshake response bytes available. Flushing SSL handshake output stream.");
try {
sslHandshakeOutputStream.endMessage();
} catch (SQLServerException e) {
logger.finer(logContext + " Ending TDS message threw exception:" + e.getMessage());
throw new IOException(e.getMessage());
}
if (logger.isLoggable(Level.FINEST))
logger.finest(logContext + " Reading first packet of SSL handshake response");
try {
tdsReader.readPacket();
} catch (SQLServerException e) {
logger.finer(logContext + " Reading response packet threw exception:" + e.getMessage());
throw new IOException(e.getMessage());
}
}
}
@Override
public long skip(long n) throws IOException {
if (logger.isLoggable(Level.FINEST))
logger.finest(logContext + " Skipping " + n + " bytes...");
if (n <= 0)
return 0;
if (n > Integer.MAX_VALUE)
n = Integer.MAX_VALUE;
ensureSSLPayload();
try {
tdsReader.skip((int) n);
} catch (SQLServerException e) {
logger.finer(logContext + " Skipping bytes threw exception:" + e.getMessage());
throw new IOException(e.getMessage());
}
return n;
}
private final byte[] oneByte = new byte[1];
@Override
public int read() throws IOException {
int bytesRead;
while (0 == (bytesRead = readInternal(oneByte, 0, oneByte.length)));
assert 1 == bytesRead || -1 == bytesRead;
return 1 == bytesRead ? oneByte[0] : -1;
}
@Override
public int read(byte[] b) throws IOException {
return readInternal(b, 0, b.length);
}
@Override
public int read(byte[] b, int offset, int maxBytes) throws IOException {
return readInternal(b, offset, maxBytes);
}
private int readInternal(byte[] b, int offset, int maxBytes) throws IOException {
if (logger.isLoggable(Level.FINEST))
logger.finest(logContext + " Reading " + maxBytes + " bytes...");
ensureSSLPayload();
try {
tdsReader.readBytes(b, offset, maxBytes);
} catch (SQLServerException e) {
logger.finer(logContext + " Reading bytes threw exception:" + e.getMessage());
throw new IOException(e.getMessage());
}
return maxBytes;
}
}
/**
* Used during SSL handshake, this class implements an OutputStream that writes SSL handshake request data (framed
* in TDS messages) to the TDS channel.
*/
private class SSLHandshakeOutputStream extends OutputStream {
private final TDSWriter tdsWriter;
/** Flag indicating when it is necessary to start a new prelogin TDS message */
private boolean messageStarted;
private final Logger logger;
private final String logContext;
SSLHandshakeOutputStream(TDSChannel tdsChannel) {
this.tdsWriter = tdsChannel.getWriter();
this.messageStarted = false;
this.logger = tdsChannel.getLogger();
this.logContext = tdsChannel.toString() + " (SSLHandshakeOutputStream):";
}
@Override
public void flush() throws IOException {
// It seems that the security provider implementation in some JVMs
// (notably SunJSSE in the 6.0 JVM) likes to add spurious calls to
// flush the SSL handshake output stream during SSL handshaking.
// We need to ignore these calls because the SSL handshake payload
// needs to be completely encapsulated in TDS. The SSL handshake
// input stream always ensures that this output stream has been flushed
// before trying to read the response.
if (logger.isLoggable(Level.FINEST))
logger.finest(logContext + " Ignored a request to flush the stream");
}
void endMessage() throws SQLServerException {
// We should only be asked to end the message if we have started one
assert messageStarted;
if (logger.isLoggable(Level.FINEST))