package com.microsoft.sqlserver.jdbc;
import static java.nio.charset.StandardCharsets.UTF_16LE;
import java.io.IOException;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLPermission;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import javax.sql.XAConnection;
import org.ietf.jgss.GSSCredential;
import mssql.googlecode.cityhash.CityHash;
import mssql.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import mssql.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap.Builder;
import mssql.googlecode.concurrentlinkedhashmap.EvictionListener;
public class SQLServerConnection implements ISQLServerConnection, java.io.Serializable {
private static final long serialVersionUID = 1965647556064751510L;
long timerExpire;
boolean attemptRefreshTokenLocked = false;
static final int DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD = 10;
private int serverPreparedStatementDiscardThreshold = -1;
static final boolean DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL = false;
private Boolean enablePrepareOnFirstPreparedStatementCall = null;
private ConcurrentLinkedQueue<PreparedStatementHandle> discardedPreparedStatementHandles = new ConcurrentLinkedQueue<>();
private AtomicInteger discardedPreparedStatementHandleCount = new AtomicInteger(0);
private SQLServerColumnEncryptionKeyStoreProvider keystoreProvider = null;
private boolean fedAuthRequiredByUser = false;
private boolean fedAuthRequiredPreLoginResponse = false;
private boolean federatedAuthenticationRequested = false;
private boolean federatedAuthenticationInfoRequested = false;
private FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData = null;
private String authenticationString = null;
private byte[] accessTokenInByte = null;
private SqlFedAuthToken fedAuthToken = null;
private String originalHostNameInCertificate = null;
private String clientCertificate = null;
private String clientKey = null;
private String clientKeyPassword = "";
private String aadPrincipalID = "";
private String aadPrincipalSecret = "";
private boolean sendTemporalDataTypesAsStringForBulkCopy = true;
final int ENGINE_EDITION_FOR_SQL_AZURE = 5;
final int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6;
final int ENGINE_EDITION_FOR_SQL_AZURE_MI = 8;
private Boolean isAzure = null;
private Boolean isAzureDW = null;
private Boolean isAzureMI = null;
private SharedTimer sharedTimer;
SharedTimer getSharedTimer() throws SQLServerException {
if (state == State.Closed) {
SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"),
SQLServerException.EXCEPTION_XOPEN_CONNECTION_FAILURE, false);
}
if (null == sharedTimer) {
this.sharedTimer = SharedTimer.getTimer();
}
return this.sharedTimer;
}
String getServerNameString(String serverName) {
String serverNameFromConnectionStr = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SERVER_NAME.toString());
if (null == serverName || serverName.equals(serverNameFromConnectionStr)) {
return serverName;
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_redirectedFrom"));
Object[] msgArgs = {serverName, serverNameFromConnectionStr};
return form.format(msgArgs);
}
static class CityHash128Key implements java.io.Serializable {
private static final long serialVersionUID = 166788428640603097L;
String unhashedString;
private long[] segments;
private int hashCode;
CityHash128Key(String sql, String parametersDefinition) {
this(sql + parametersDefinition);
}
@SuppressWarnings("deprecation")
CityHash128Key(String s) {
unhashedString = s;
byte[] bytes = new byte[s.length()];
s.getBytes(0, s.length(), bytes, 0);
segments = CityHash.cityHash128(bytes, 0, bytes.length);
}
public boolean equals(Object obj) {
if (!(obj instanceof CityHash128Key))
return false;
return (java.util.Arrays.equals(segments, ((CityHash128Key) obj).segments)
&& this.unhashedString.equals(((CityHash128Key) obj).unhashedString));
}
public int hashCode() {
if (0 == hashCode) {
hashCode = java.util.Arrays.hashCode(segments);
}
return hashCode;
}
}
class PreparedStatementHandle {
private int handle = 0;
private final AtomicInteger handleRefCount = new AtomicInteger();
private boolean isDirectSql;
private volatile boolean evictedFromCache;
private volatile boolean explicitlyDiscarded;
private CityHash128Key key;
PreparedStatementHandle(CityHash128Key key, int handle, boolean isDirectSql, boolean isEvictedFromCache) {
this.key = key;
this.handle = handle;
this.isDirectSql = isDirectSql;
this.setIsEvictedFromCache(isEvictedFromCache);
handleRefCount.set(1);
}
private boolean isEvictedFromCache() {
return evictedFromCache;
}
private void setIsEvictedFromCache(boolean isEvictedFromCache) {
this.evictedFromCache = isEvictedFromCache;
}
void setIsExplicitlyDiscarded() {
this.explicitlyDiscarded = true;
evictCachedPreparedStatementHandle(this);
}
private boolean isExplicitlyDiscarded() {
return explicitlyDiscarded;
}
int getHandle() {
return handle;
}
CityHash128Key getKey() {
return key;
}
boolean isDirectSql() {
return isDirectSql;
}
private boolean tryDiscardHandle() {
return handleRefCount.compareAndSet(0, -999);
}
private boolean isDiscarded() {
return 0 > handleRefCount.intValue();
}
boolean tryAddReference() {
return (isDiscarded() || isExplicitlyDiscarded()) ? false : handleRefCount.incrementAndGet() > 0;
}
void removeReference() {
handleRefCount.decrementAndGet();
}
}
static final private int PARSED_SQL_CACHE_SIZE = 100;
static private ConcurrentLinkedHashMap<CityHash128Key, ParsedSQLCacheItem> parsedSQLCache;
static {
parsedSQLCache = new Builder<CityHash128Key, ParsedSQLCacheItem>()
.maximumWeightedCapacity(PARSED_SQL_CACHE_SIZE).build();
}
static ParsedSQLCacheItem getCachedParsedSQL(CityHash128Key key) {
return parsedSQLCache.get(key);
}
static ParsedSQLCacheItem parseAndCacheSQL(CityHash128Key key, String sql) throws SQLServerException {
JDBCSyntaxTranslator translator = new JDBCSyntaxTranslator();
String parsedSql = translator.translate(sql);
String procName = translator.getProcedureName();
boolean returnValueSyntax = translator.hasReturnValueSyntax();
int[] parameterPositions = locateParams(parsedSql);
ParsedSQLCacheItem cacheItem = new ParsedSQLCacheItem(parsedSql, parameterPositions, procName,
returnValueSyntax);
parsedSQLCache.putIfAbsent(key, cacheItem);
return cacheItem;
}
static final int DEFAULT_STATEMENT_POOLING_CACHE_SIZE = 0;
private int statementPoolingCacheSize = DEFAULT_STATEMENT_POOLING_CACHE_SIZE;
private ConcurrentLinkedHashMap<CityHash128Key, PreparedStatementHandle> preparedStatementHandleCache;
private ConcurrentLinkedHashMap<CityHash128Key, SQLServerParameterMetaData> parameterMetadataCache;
private boolean disableStatementPooling = true;
private static int[] locateParams(String sql) {
LinkedList<Integer> parameterPositions = new LinkedList<>();
int offset = -1;
while ((offset = ParameterUtils.scanSQLForChar('?', sql, ++offset)) < sql.length()) {
parameterPositions.add(offset);
}
return parameterPositions.stream().mapToInt(Integer::valueOf).toArray();
}
class FederatedAuthenticationFeatureExtensionData implements Serializable {
private static final long serialVersionUID = -6709861741957202475L;
boolean fedAuthRequiredPreLoginResponse;
int libraryType = -1;
byte[] accessToken = null;
SqlAuthentication authentication = null;
FederatedAuthenticationFeatureExtensionData(int libraryType, String authenticationString,
boolean fedAuthRequiredPreLoginResponse) throws SQLServerException {
this.libraryType = libraryType;
this.fedAuthRequiredPreLoginResponse = fedAuthRequiredPreLoginResponse;
switch (authenticationString.toUpperCase(Locale.ENGLISH)) {
case "ACTIVEDIRECTORYPASSWORD":
this.authentication = SqlAuthentication.ActiveDirectoryPassword;
break;
case "ACTIVEDIRECTORYINTEGRATED":
this.authentication = SqlAuthentication.ActiveDirectoryIntegrated;
break;
case "ACTIVEDIRECTORYMSI":
this.authentication = SqlAuthentication.ActiveDirectoryMSI;
break;
case "ACTIVEDIRECTORYSERVICEPRINCIPAL":
this.authentication = SqlAuthentication.ActiveDirectoryServicePrincipal;
break;
case "ACTIVEDIRECTORYINTERACTIVE":
this.authentication = SqlAuthentication.ActiveDirectoryInteractive;
break;
default:
assert (false);
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_InvalidConnectionSetting"));
Object[] msgArgs = {"authentication", authenticationString};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
}
FederatedAuthenticationFeatureExtensionData(int libraryType, boolean fedAuthRequiredPreLoginResponse,
byte[] accessToken) {
this.libraryType = libraryType;
this.fedAuthRequiredPreLoginResponse = fedAuthRequiredPreLoginResponse;
this.accessToken = accessToken;
}
}
class SqlFedAuthInfo {
String spn;
String stsurl;
@Override
public String toString() {
return "STSURL: " + stsurl + ", SPN: " + spn;
}
}
class ActiveDirectoryAuthentication {
static final String JDBC_FEDAUTH_CLIENT_ID = "7f98cb04-cd1e-40df-9140-3bf7e2cea4db";
static final String AZURE_REST_MSI_URL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01";
static final String ACCESS_TOKEN_IDENTIFIER = "\"access_token\":\"";
static final String ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER = "\"expires_in\":\"";
static final String ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER = "\"expires_on\":\"";
static final String ACCESS_TOKEN_EXPIRES_ON_DATE_FORMAT = "M/d/yyyy h:mm:ss a X";
static final int GET_ACCESS_TOKEN_SUCCESS = 0;
static final int GET_ACCESS_TOKEN_INVALID_GRANT = 1;
static final int GET_ACCESS_TOKEN_TANSISENT_ERROR = 2;
static final int GET_ACCESS_TOKEN_OTHER_ERROR = 3;
}
private enum State {
Initialized,
Connected,
Opened,
Closed
}
private final static float TIMEOUTSTEP = 0.08F;
private final static float TIMEOUTSTEP_TNIR = 0.125F;
final static int TnirFirstAttemptTimeoutMs = 500;
private final static int INTERMITTENT_TLS_MAX_RETRY = 5;
private boolean isRoutedInCurrentAttempt = false;
private ServerPortPlaceHolder routingInfo = null;
ServerPortPlaceHolder getRoutingInfo() {
return routingInfo;
}
private static final String callAbortPerm = "callAbort";
private static final String SET_NETWORK_TIMEOUT_PERM = "setNetworkTimeout";
private boolean sendStringParametersAsUnicode = SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE
.getDefaultValue();
private String hostName = null;
boolean sendStringParametersAsUnicode() {
return sendStringParametersAsUnicode;
}
private boolean lastUpdateCount;
final boolean useLastUpdateCount() {
return lastUpdateCount;
}
private boolean serverNameAsACE = SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.getDefaultValue();
boolean serverNameAsACE() {
return serverNameAsACE;
}
private boolean multiSubnetFailover;
final boolean getMultiSubnetFailover() {
return multiSubnetFailover;
}
private boolean transparentNetworkIPResolution;
final boolean getTransparentNetworkIPResolution() {
return transparentNetworkIPResolution;
}
private ApplicationIntent applicationIntent = null;
final ApplicationIntent getApplicationIntent() {
return applicationIntent;
}
private int nLockTimeout;
private String selectMethod;
final String getSelectMethod() {
return selectMethod;
}
private String responseBuffering;
final String getResponseBuffering() {
return responseBuffering;
}
private int queryTimeoutSeconds;
final int getQueryTimeoutSeconds() {
return queryTimeoutSeconds;
}
private int cancelQueryTimeoutSeconds;
final int getCancelQueryTimeoutSeconds() {
return cancelQueryTimeoutSeconds;
}
private int socketTimeoutMilliseconds;
final int getSocketTimeoutMilliseconds() {
return socketTimeoutMilliseconds;
}
private boolean useBulkCopyForBatchInsert;
public boolean getUseBulkCopyForBatchInsert() {
return useBulkCopyForBatchInsert;
}
public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert) {
this.useBulkCopyForBatchInsert = useBulkCopyForBatchInsert;
}
boolean userSetTNIR = true;
private boolean sendTimeAsDatetime = SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue();
private boolean useFmtOnly = SQLServerDriverBooleanProperty.USE_FMT_ONLY.getDefaultValue();
@Override
public final boolean getSendTimeAsDatetime() {
return !isKatmaiOrLater() || sendTimeAsDatetime;
}
final int baseYear() {
return getSendTimeAsDatetime() ? TDS.BASE_YEAR_1970 : TDS.BASE_YEAR_1900;
}
private byte requestedEncryptionLevel = TDS.ENCRYPT_INVALID;
final byte getRequestedEncryptionLevel() {
assert TDS.ENCRYPT_INVALID != requestedEncryptionLevel;
return requestedEncryptionLevel;
}
private boolean trustServerCertificate;
final boolean trustServerCertificate() {
return trustServerCertificate;
}
private byte negotiatedEncryptionLevel = TDS.ENCRYPT_INVALID;
final byte getNegotiatedEncryptionLevel() {
assert TDS.ENCRYPT_INVALID != negotiatedEncryptionLevel;
return negotiatedEncryptionLevel;
}
private String socketFactoryClass = null;
final String getSocketFactoryClass() {
return socketFactoryClass;
}
private String socketFactoryConstructorArg = null;
final String getSocketFactoryConstructorArg() {
return socketFactoryConstructorArg;
}
private String trustManagerClass = null;
final String getTrustManagerClass() {
assert TDS.ENCRYPT_INVALID != requestedEncryptionLevel;
return trustManagerClass;
}
private String trustManagerConstructorArg = null;
final String getTrustManagerConstructorArg() {
assert TDS.ENCRYPT_INVALID != requestedEncryptionLevel;
return trustManagerConstructorArg;
}
static final String RESERVED_PROVIDER_NAME_PREFIX = "MSSQL_";
String columnEncryptionSetting = null;
boolean isColumnEncryptionSettingEnabled() {
return (columnEncryptionSetting.equalsIgnoreCase(ColumnEncryptionSetting.Enabled.toString()));
}
boolean getSendTemporalDataTypesAsStringForBulkCopy() {
return sendTemporalDataTypesAsStringForBulkCopy;
}
String enclaveAttestationUrl = null;
String enclaveAttestationProtocol = null;
String keyStoreAuthentication = null;
String keyStoreSecret = null;
String keyStoreLocation = null;
String keyStorePrincipalId = null;
private ColumnEncryptionVersion serverColumnEncryptionVersion = ColumnEncryptionVersion.AE_NotSupported;
private String enclaveType = null;
boolean getServerSupportsColumnEncryption() {
return (serverColumnEncryptionVersion.value() > ColumnEncryptionVersion.AE_NotSupported.value());
}
ColumnEncryptionVersion getServerColumnEncryptionVersion() {
return serverColumnEncryptionVersion;
}
private boolean serverSupportsDataClassification = false;
private byte serverSupportedDataClassificationVersion = TDS.DATA_CLASSIFICATION_NOT_ENABLED;
boolean getServerSupportsDataClassification() {
return serverSupportsDataClassification;
}
private boolean serverSupportsDNSCaching = false;
private static ConcurrentHashMap<String, InetSocketAddress> dnsCache = null;
static InetSocketAddress getDNSEntry(String key) {
return (null != dnsCache) ? dnsCache.get(key) : null;
}
byte getServerSupportedDataClassificationVersion() {
return serverSupportedDataClassificationVersion;
}
private boolean delayLoadingLobs = SQLServerDriverBooleanProperty.DELAY_LOADING_LOBS.getDefaultValue();
@Override
public boolean getDelayLoadingLobs() {
return delayLoadingLobs;
}
@Override
public void setDelayLoadingLobs(boolean b) {
delayLoadingLobs = b;
}
static Map<String, SQLServerColumnEncryptionKeyStoreProvider> globalSystemColumnEncryptionKeyStoreProviders = new HashMap<>();
static {
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")) {
SQLServerColumnEncryptionCertificateStoreProvider provider = new SQLServerColumnEncryptionCertificateStoreProvider();
globalSystemColumnEncryptionKeyStoreProviders.put(provider.getName(), provider);
}
}
static Map<String, SQLServerColumnEncryptionKeyStoreProvider> globalCustomColumnEncryptionKeyStoreProviders = null;
Map<String, SQLServerColumnEncryptionKeyStoreProvider> systemColumnEncryptionKeyStoreProvider = new HashMap<>();
public static synchronized void registerColumnEncryptionKeyStoreProviders(
Map<String, SQLServerColumnEncryptionKeyStoreProvider> clientKeyStoreProviders) throws SQLServerException {
loggerExternal.entering(loggingClassName, "registerColumnEncryptionKeyStoreProviders",
"Registering Column Encryption Key Store Providers");
if (null == clientKeyStoreProviders) {
throw new SQLServerException(null, SQLServerException.getErrString("R_CustomKeyStoreProviderMapNull"), null,
0, false);
}
if (null != globalCustomColumnEncryptionKeyStoreProviders
&& !globalCustomColumnEncryptionKeyStoreProviders.isEmpty()) {
throw new SQLServerException(null, SQLServerException.getErrString("R_CustomKeyStoreProviderSetOnce"), null,
0, false);
}
globalCustomColumnEncryptionKeyStoreProviders = new HashMap<>();
for (Map.Entry<String, SQLServerColumnEncryptionKeyStoreProvider> entry : clientKeyStoreProviders.entrySet()) {
String providerName = entry.getKey();
if (null == providerName || 0 == providerName.length()) {
throw new SQLServerException(null, SQLServerException.getErrString("R_EmptyCustomKeyStoreProviderName"),
null, 0, false);
}
if ((providerName.substring(0, 6).equalsIgnoreCase(RESERVED_PROVIDER_NAME_PREFIX))) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_InvalidCustomKeyStoreProviderName"));
Object[] msgArgs = {providerName, RESERVED_PROVIDER_NAME_PREFIX};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
if (null == entry.getValue()) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_CustomKeyStoreProviderValueNull"));
Object[] msgArgs = {providerName, RESERVED_PROVIDER_NAME_PREFIX};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
globalCustomColumnEncryptionKeyStoreProviders.put(entry.getKey(), entry.getValue());
}
loggerExternal.exiting(loggingClassName, "registerColumnEncryptionKeyStoreProviders",
"Number of Key store providers that are registered:"
+ globalCustomColumnEncryptionKeyStoreProviders.size());
}
public static synchronized void unregisterColumnEncryptionKeyStoreProviders() {
loggerExternal.entering(loggingClassName, "unregisterColumnEncryptionKeyStoreProviders",
"Removing Column Encryption Key Store Provider");
if (null != globalCustomColumnEncryptionKeyStoreProviders) {
globalCustomColumnEncryptionKeyStoreProviders.clear();
globalCustomColumnEncryptionKeyStoreProviders = null;
}
loggerExternal.exiting(loggingClassName, "unregisterColumnEncryptionKeyStoreProviders",
"Number of Key store providers that are registered: 0");
}
synchronized SQLServerColumnEncryptionKeyStoreProvider getGlobalSystemColumnEncryptionKeyStoreProvider(
String providerName) {
return (null != globalSystemColumnEncryptionKeyStoreProviders && globalSystemColumnEncryptionKeyStoreProviders
.containsKey(providerName)) ? globalSystemColumnEncryptionKeyStoreProviders.get(providerName) : null;
}
synchronized String getAllGlobalCustomSystemColumnEncryptionKeyStoreProviders() {
return (null != globalCustomColumnEncryptionKeyStoreProviders) ? globalCustomColumnEncryptionKeyStoreProviders
.keySet().toString() : null;
}
synchronized String getAllSystemColumnEncryptionKeyStoreProviders() {
String keyStores = "";
if (0 != systemColumnEncryptionKeyStoreProvider.size())
keyStores = systemColumnEncryptionKeyStoreProvider.keySet().toString();
if (0 != SQLServerConnection.globalSystemColumnEncryptionKeyStoreProviders.size())
keyStores += "," + SQLServerConnection.globalSystemColumnEncryptionKeyStoreProviders.keySet().toString();
return keyStores;
}
synchronized SQLServerColumnEncryptionKeyStoreProvider getGlobalCustomColumnEncryptionKeyStoreProvider(
String providerName) {
return (null != globalCustomColumnEncryptionKeyStoreProviders && globalCustomColumnEncryptionKeyStoreProviders
.containsKey(providerName)) ? globalCustomColumnEncryptionKeyStoreProviders.get(providerName) : null;
}
synchronized SQLServerColumnEncryptionKeyStoreProvider getSystemColumnEncryptionKeyStoreProvider(
String providerName) {
return (null != systemColumnEncryptionKeyStoreProvider && systemColumnEncryptionKeyStoreProvider
.containsKey(providerName)) ? systemColumnEncryptionKeyStoreProvider.get(providerName) : null;
}
synchronized SQLServerColumnEncryptionKeyStoreProvider getColumnEncryptionKeyStoreProvider(
String providerName) throws SQLServerException {
keystoreProvider = getSystemColumnEncryptionKeyStoreProvider(providerName);
if (null == keystoreProvider) {
keystoreProvider = getGlobalSystemColumnEncryptionKeyStoreProvider(providerName);
}
if (null == keystoreProvider) {
keystoreProvider = getGlobalCustomColumnEncryptionKeyStoreProvider(providerName);
}
if (null == keystoreProvider) {
String systemProviders = getAllSystemColumnEncryptionKeyStoreProviders();
String customProviders = getAllGlobalCustomSystemColumnEncryptionKeyStoreProviders();
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_UnrecognizedKeyStoreProviderName"));
Object[] msgArgs = {providerName, systemProviders, customProviders};
throw new SQLServerException(form.format(msgArgs), null);
}
return keystoreProvider;
}
private String trustedServerNameAE = null;
private static Map<String, List<String>> columnEncryptionTrustedMasterKeyPaths = new HashMap<>();
public static synchronized void setColumnEncryptionTrustedMasterKeyPaths(
Map<String, List<String>> trustedKeyPaths) {
loggerExternal.entering(loggingClassName, "setColumnEncryptionTrustedMasterKeyPaths",
"Setting Trusted Master Key Paths");
columnEncryptionTrustedMasterKeyPaths.clear();
for (Map.Entry<String, List<String>> entry : trustedKeyPaths.entrySet()) {
columnEncryptionTrustedMasterKeyPaths.put(entry.getKey().toUpperCase(), entry.getValue());
}
loggerExternal.exiting(loggingClassName, "setColumnEncryptionTrustedMasterKeyPaths",
"Number of Trusted Master Key Paths: " + columnEncryptionTrustedMasterKeyPaths.size());
}
public static synchronized void updateColumnEncryptionTrustedMasterKeyPaths(String server,
List<String> trustedKeyPaths) {
loggerExternal.entering(loggingClassName, "updateColumnEncryptionTrustedMasterKeyPaths",
"Updating Trusted Master Key Paths");
columnEncryptionTrustedMasterKeyPaths.put(server.toUpperCase(), trustedKeyPaths);
loggerExternal.exiting(loggingClassName, "updateColumnEncryptionTrustedMasterKeyPaths",
"Number of Trusted Master Key Paths: " + columnEncryptionTrustedMasterKeyPaths.size());
}
public static synchronized void removeColumnEncryptionTrustedMasterKeyPaths(String server) {
loggerExternal.entering(loggingClassName, "removeColumnEncryptionTrustedMasterKeyPaths",
"Removing Trusted Master Key Paths");
columnEncryptionTrustedMasterKeyPaths.remove(server.toUpperCase());
loggerExternal.exiting(loggingClassName, "removeColumnEncryptionTrustedMasterKeyPaths",
"Number of Trusted Master Key Paths: " + columnEncryptionTrustedMasterKeyPaths.size());
}
public static synchronized Map<String, List<String>> getColumnEncryptionTrustedMasterKeyPaths() {
loggerExternal.entering(loggingClassName, "getColumnEncryptionTrustedMasterKeyPaths",
"Getting Trusted Master Key Paths");
Map<String, List<String>> masterKeyPathCopy = new HashMap<>();
for (Map.Entry<String, List<String>> entry : columnEncryptionTrustedMasterKeyPaths.entrySet()) {
masterKeyPathCopy.put(entry.getKey(), entry.getValue());
}
loggerExternal.exiting(loggingClassName, "getColumnEncryptionTrustedMasterKeyPaths",
"Number of Trusted Master Key Paths: " + masterKeyPathCopy.size());
return masterKeyPathCopy;
}
static synchronized List<String> getColumnEncryptionTrustedMasterKeyPaths(String server, Boolean[] hasEntry) {
if (columnEncryptionTrustedMasterKeyPaths.containsKey(server)) {
hasEntry[0] = true;
return columnEncryptionTrustedMasterKeyPaths.get(server);
} else {
hasEntry[0] = false;
return null;
}
}
public static synchronized void clearUserTokenCache() {
PersistentTokenCacheAccessAspect.clearUserTokenCache();
}
Properties activeConnectionProperties;
private boolean integratedSecurity = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue();
private boolean ntlmAuthentication = false;
private byte[] ntlmPasswordHash = null;
private AuthenticationScheme intAuthScheme = AuthenticationScheme.nativeAuthentication;
private GSSCredential impersonatedUserCred;
private boolean isUserCreatedCredential;
ServerPortPlaceHolder currentConnectPlaceHolder = null;
String sqlServerVersion;
boolean xopenStates;
private boolean databaseAutoCommitMode;
private boolean inXATransaction = false;
private byte[] transactionDescriptor = new byte[8];
private boolean rolledBackTransaction;
final boolean rolledBackTransaction() {
return rolledBackTransaction;
}
private volatile State state = State.Initialized;
private void setState(State state) {
this.state = state;
}
final boolean isSessionUnAvailable() {
return !(state.equals(State.Opened));
}
final static int maxDecimalPrecision = 38;
final static int defaultDecimalPrecision = 18;
final String traceID;
private int maxFieldSize;
final void setMaxFieldSize(int limit) throws SQLServerException {
if (maxFieldSize != limit) {
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
connectionCommand("SET TEXTSIZE " + ((0 == limit) ? Integer.MAX_VALUE : limit), "setMaxFieldSize");
maxFieldSize = limit;
}
}
final void initResettableValues() {
rolledBackTransaction = false;
transactionIsolationLevel = Connection.TRANSACTION_READ_COMMITTED;
maxFieldSize = 0;
maxRows = 0;
nLockTimeout = -1;
databaseAutoCommitMode = true;
holdability = ResultSet.HOLD_CURSORS_OVER_COMMIT;
sqlWarnings = null;
sCatalog = originalCatalog;
databaseMetaData = null;
}
private int maxRows;
final void setMaxRows(int limit) throws SQLServerException {
if (maxRows != limit) {
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
connectionCommand("SET ROWCOUNT " + limit, "setMaxRows");
maxRows = limit;
}
}
private SQLCollation databaseCollation;
final SQLCollation getDatabaseCollation() {
return databaseCollation;
}
static private final AtomicInteger baseConnectionID = new AtomicInteger(0);
private String sCatalog = "master";
private String originalCatalog = "master";
private int transactionIsolationLevel;
private SQLServerPooledConnection pooledConnectionParent;
private SQLServerDatabaseMetaData databaseMetaData;
private int nNextSavePointId = 10000;
static final private java.util.logging.Logger connectionlogger = java.util.logging.Logger
.getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerConnection");
static final private java.util.logging.Logger loggerExternal = java.util.logging.Logger
.getLogger("com.microsoft.sqlserver.jdbc.Connection");
private static String loggingClassName = "com.microsoft.sqlserver.jdbc.SQLServerConnection:";
private String failoverPartnerServerProvided = null;
private int holdability;
final int getHoldabilityInternal() {
return holdability;
}
private int tdsPacketSize = TDS.INITIAL_PACKET_SIZE;
private int requestedPacketSize = TDS.DEFAULT_PACKET_SIZE;
final int getTDSPacketSize() {
return tdsPacketSize;
}
private TDSChannel tdsChannel;
private TDSCommand currentCommand = null;
private int tdsVersion = TDS.VER_UNKNOWN;
final boolean isKatmaiOrLater() {
assert TDS.VER_UNKNOWN != tdsVersion;
assert tdsVersion >= TDS.VER_YUKON;
return tdsVersion >= TDS.VER_KATMAI;
}
final boolean isDenaliOrLater() {
return tdsVersion >= TDS.VER_DENALI;
}
private int serverMajorVersion;
int getServerMajorVersion() {
return serverMajorVersion;
}
private SQLServerConnectionPoolProxy proxy;
private UUID clientConnectionId = null;
@Override
public UUID getClientConnectionId() throws SQLServerException {
checkClosed();
return clientConnectionId;
}
final UUID getClientConIdInternal() {
return clientConnectionId;
}
final boolean attachConnId() {
return state.equals(State.Connected);
}
SQLServerConnection(String parentInfo) throws SQLServerException {
int connectionID = nextConnectionID();
traceID = "ConnectionID:" + connectionID;
loggingClassName += connectionID;
if (connectionlogger.isLoggable(Level.FINE))
connectionlogger.fine(toString() + " created by (" + parentInfo + ")");
initResettableValues();
if (!this.getDisableStatementPooling() && 0 < this.getStatementPoolingCacheSize()) {
prepareCache();
}
}
void setFailoverPartnerServerProvided(String partner) {
failoverPartnerServerProvided = partner;
}
final void setAssociatedProxy(SQLServerConnectionPoolProxy proxy) {
this.proxy = proxy;
}
final Connection getConnection() {
if (null != proxy)
return proxy;
else
return this;
}
final void resetPooledConnection() {
tdsChannel.resetPooledConnection();
initResettableValues();
}
private static int nextConnectionID() {
return baseConnectionID.incrementAndGet();
}
java.util.logging.Logger getConnectionLogger() {
return connectionlogger;
}
@Override
public String toString() {
if (null != clientConnectionId)
return traceID + " ClientConnectionId: " + clientConnectionId.toString();
else
return traceID;
}
void checkClosed() throws SQLServerException {
if (isSessionUnAvailable()) {
SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"),
SQLServerException.EXCEPTION_XOPEN_CONNECTION_FAILURE, false);
}
}
protected boolean needsReconnect() {
return (null != fedAuthToken && Util.checkIfNeedNewAccessToken(this, fedAuthToken.expiresOn));
}
private boolean isBooleanPropertyOn(String propName, String propValue) throws SQLServerException {
if (null == propValue)
return false;
if ("true".equalsIgnoreCase(propValue)) {
return true;
} else if ("false".equalsIgnoreCase(propValue)) {
return false;
} else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidBooleanValue"));
Object[] msgArgs = {propName};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
return false;
}
}
final static int MAX_SQL_LOGIN_NAME_WCHARS = 128;
void validateMaxSQLLoginName(String propName, String propValue) throws SQLServerException {
if (propValue != null && propValue.length() > MAX_SQL_LOGIN_NAME_WCHARS) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_propertyMaximumExceedsChars"));
Object[] msgArgs = {propName, Integer.toString(MAX_SQL_LOGIN_NAME_WCHARS)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
Connection connect(Properties propsIn, SQLServerPooledConnection pooledConnection) throws SQLServerException {
int loginTimeoutSeconds = 0;
long start = System.currentTimeMillis();
for (int retryAttempt = 0;;) {
try {
return connectInternal(propsIn, pooledConnection);
} catch (SQLServerException e) {
if (SQLServerException.DRIVER_ERROR_INTERMITTENT_TLS_FAILED != e.getDriverErrorCode()) {
throw e;
} else {
if (0 == retryAttempt) {
loginTimeoutSeconds = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue();
String sPropValue = propsIn.getProperty(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString());
if (null != sPropValue && sPropValue.length() > 0) {
int sPropValueInt = Integer.parseInt(sPropValue);
if (0 != sPropValueInt) {
loginTimeoutSeconds = sPropValueInt;
}
}
}
retryAttempt++;
long elapsedSeconds = ((System.currentTimeMillis() - start) / 1000L);
if (INTERMITTENT_TLS_MAX_RETRY < retryAttempt) {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine("Connection failed during SSL handshake. Maximum retry attempt ("
+ INTERMITTENT_TLS_MAX_RETRY + ") reached. ");
}
throw e;
} else if (elapsedSeconds >= loginTimeoutSeconds) {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger
.fine("Connection failed during SSL handshake. Not retrying as timeout expired.");
}
throw e;
} else {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine(
"Connection failed during SSL handshake. Retrying due to an intermittent TLS 1.2 failure issue. Retry attempt = "
+ retryAttempt + ".");
}
}
}
}
}
}
private void registerKeyStoreProviderOnConnection(String keyStoreAuth, String keyStoreSecret,
String keyStoreLocation) throws SQLServerException {
if (null == keyStoreAuth) {
if ((null != keyStoreSecret)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_keyStoreAuthenticationNotSet"));
Object[] msgArgs = {"keyStoreSecret"};
throw new SQLServerException(form.format(msgArgs), null);
}
if (null != keyStoreLocation) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_keyStoreAuthenticationNotSet"));
Object[] msgArgs = {"keyStoreLocation"};
throw new SQLServerException(form.format(msgArgs), null);
}
if (null != keyStorePrincipalId) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_keyStoreAuthenticationNotSet"));
Object[] msgArgs = {"keyStorePrincipalId"};
throw new SQLServerException(form.format(msgArgs), null);
}
} else {
KeyStoreAuthentication keyStoreAuthentication = KeyStoreAuthentication.valueOfString(keyStoreAuth);
switch (keyStoreAuthentication) {
case JavaKeyStorePassword:
if ((null == keyStoreSecret) || (null == keyStoreLocation)) {
throw new SQLServerException(
SQLServerException.getErrString("R_keyStoreSecretOrLocationNotSet"), null);
} else {
SQLServerColumnEncryptionJavaKeyStoreProvider provider = new SQLServerColumnEncryptionJavaKeyStoreProvider(
keyStoreLocation, keyStoreSecret.toCharArray());
systemColumnEncryptionKeyStoreProvider.put(provider.getName(), provider);
}
break;
case KeyVaultClientSecret:
if (null == keyStoreSecret) {
throw new SQLServerException(SQLServerException.getErrString("R_keyStoreSecretNotSet"), null);
}
registerKeyVaultProvider(keyStorePrincipalId, keyStoreSecret);
break;
case KeyVaultManagedIdentity:
SQLServerColumnEncryptionAzureKeyVaultProvider provider;
if (null != keyStorePrincipalId) {
provider = new SQLServerColumnEncryptionAzureKeyVaultProvider(keyStorePrincipalId);
} else {
provider = new SQLServerColumnEncryptionAzureKeyVaultProvider();
}
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<>();
keyStoreMap.put(provider.getName(), provider);
registerColumnEncryptionKeyStoreProviders(keyStoreMap);
break;
default:
break;
}
}
}
private void registerKeyVaultProvider(String clientId, String clientKey) throws SQLServerException {
SQLServerColumnEncryptionAzureKeyVaultProvider provider = new SQLServerColumnEncryptionAzureKeyVaultProvider(
clientId, clientKey);
Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap = new HashMap<>();
keyStoreMap.put(provider.getName(), provider);
registerColumnEncryptionKeyStoreProviders(keyStoreMap);
}
Connection connectInternal(Properties propsIn,
SQLServerPooledConnection pooledConnection) throws SQLServerException {
try {
activeConnectionProperties = (Properties) propsIn.clone();
pooledConnectionParent = pooledConnection;
String hostNameInCertificate = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString());
if (null == originalHostNameInCertificate && null != hostNameInCertificate
&& !hostNameInCertificate.isEmpty()) {
originalHostNameInCertificate = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString());
}
if (null != originalHostNameInCertificate && !originalHostNameInCertificate.isEmpty()) {
activeConnectionProperties.setProperty(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(),
originalHostNameInCertificate);
}
String sPropKey;
String sPropValue;
sPropKey = SQLServerDriverStringProperty.USER.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.USER.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
validateMaxSQLLoginName(sPropKey, sPropValue);
sPropKey = SQLServerDriverStringProperty.PASSWORD.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.PASSWORD.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
validateMaxSQLLoginName(sPropKey, sPropValue);
sPropKey = SQLServerDriverStringProperty.DATABASE_NAME.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
validateMaxSQLLoginName(sPropKey, sPropValue);
int loginTimeoutSeconds = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue();
sPropValue = activeConnectionProperties.getProperty(SQLServerDriverIntProperty.LOGIN_TIMEOUT.toString());
if (null != sPropValue && sPropValue.length() > 0) {
try {
loginTimeoutSeconds = Integer.parseInt(sPropValue);
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidTimeOut"));
Object[] msgArgs = {sPropValue};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
if (loginTimeoutSeconds < 0 || loginTimeoutSeconds > 65535) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidTimeOut"));
Object[] msgArgs = {sPropValue};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.SERVER_NAME_AS_ACE.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
serverNameAsACE = isBooleanPropertyOn(sPropKey, sPropValue);
sPropKey = SQLServerDriverStringProperty.SERVER_NAME.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = "localhost";
}
String sPropKeyPort = SQLServerDriverIntProperty.PORT_NUMBER.toString();
String sPropValuePort = activeConnectionProperties.getProperty(sPropKeyPort);
int px = sPropValue.indexOf('\\');
String instanceValue = null;
String instanceNameProperty = SQLServerDriverStringProperty.INSTANCE_NAME.toString();
if (px >= 0) {
instanceValue = sPropValue.substring(px + 1, sPropValue.length());
validateMaxSQLLoginName(instanceNameProperty, instanceValue);
sPropValue = sPropValue.substring(0, px);
}
trustedServerNameAE = sPropValue;
if (serverNameAsACE) {
try {
sPropValue = java.net.IDN.toASCII(sPropValue);
} catch (IllegalArgumentException ex) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_InvalidConnectionSetting"));
Object[] msgArgs = {"serverNameAsACE", sPropValue};
throw new SQLServerException(form.format(msgArgs), ex);
}
}
activeConnectionProperties.setProperty(sPropKey, sPropValue);
String instanceValueFromProp = activeConnectionProperties.getProperty(instanceNameProperty);
if (null != instanceValueFromProp)
instanceValue = instanceValueFromProp;
if (instanceValue != null) {
validateMaxSQLLoginName(instanceNameProperty, instanceValue);
activeConnectionProperties.setProperty(instanceNameProperty, instanceValue);
trustedServerNameAE += "\\" + instanceValue;
}
if (null != sPropValuePort) {
trustedServerNameAE += ":" + sPropValuePort;
}
sPropKey = SQLServerDriverStringProperty.APPLICATION_NAME.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue)
validateMaxSQLLoginName(sPropKey, sPropValue);
else
activeConnectionProperties.setProperty(sPropKey, SQLServerDriver.DEFAULT_APP_NAME);
sPropKey = SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
sPropKey = SQLServerDriverStringProperty.COLUMN_ENCRYPTION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.COLUMN_ENCRYPTION.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
columnEncryptionSetting = ColumnEncryptionSetting.valueOfString(sPropValue).toString();
sPropKey = SQLServerDriverStringProperty.ENCLAVE_ATTESTATION_URL.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
enclaveAttestationUrl = sPropValue;
}
sPropKey = SQLServerDriverStringProperty.ENCLAVE_ATTESTATION_PROTOCOL.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
enclaveAttestationProtocol = sPropValue;
if (!AttestationProtocol.isValidAttestationProtocol(enclaveAttestationProtocol)) {
throw new SQLServerException(SQLServerException.getErrString("R_enclaveInvalidAttestationProtocol"),
null);
}
if (enclaveAttestationProtocol.equalsIgnoreCase(AttestationProtocol.HGS.toString())) {
this.enclaveProvider = new SQLServerVSMEnclaveProvider();
} else {
this.enclaveProvider = new SQLServerAASEnclaveProvider();
}
}
if ((null != enclaveAttestationUrl && !enclaveAttestationUrl.isEmpty()
&& (null == enclaveAttestationProtocol || enclaveAttestationProtocol.isEmpty()))
|| (null != enclaveAttestationProtocol && !enclaveAttestationProtocol.isEmpty()
&& (null == enclaveAttestationUrl || enclaveAttestationUrl.isEmpty()))
|| (null != enclaveAttestationUrl && !enclaveAttestationUrl.isEmpty()
&& (null != enclaveAttestationProtocol || !enclaveAttestationProtocol.isEmpty())
&& (null == columnEncryptionSetting || !isColumnEncryptionSettingEnabled()))) {
throw new SQLServerException(SQLServerException.getErrString("R_enclavePropertiesError"), null);
}
sPropKey = SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
keyStoreAuthentication = KeyStoreAuthentication.valueOfString(sPropValue).toString();
}
sPropKey = SQLServerDriverStringProperty.KEY_STORE_SECRET.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
keyStoreSecret = sPropValue;
}
sPropKey = SQLServerDriverStringProperty.KEY_STORE_LOCATION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
keyStoreLocation = sPropValue;
}
sPropKey = SQLServerDriverStringProperty.KEY_STORE_PRINCIPAL_ID.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
keyStorePrincipalId = sPropValue;
}
registerKeyStoreProviderOnConnection(keyStoreAuthentication, keyStoreSecret, keyStoreLocation);
if (null == globalCustomColumnEncryptionKeyStoreProviders) {
sPropKey = SQLServerDriverStringProperty.KEY_VAULT_PROVIDER_CLIENT_ID.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
String keyVaultColumnEncryptionProviderClientId = sPropValue;
sPropKey = SQLServerDriverStringProperty.KEY_VAULT_PROVIDER_CLIENT_KEY.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
String keyVaultColumnEncryptionProviderClientKey = sPropValue;
registerKeyVaultProvider(keyVaultColumnEncryptionProviderClientId,
keyVaultColumnEncryptionProviderClientKey);
}
}
}
sPropKey = SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.MULTI_SUBNET_FAILOVER.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
multiSubnetFailover = isBooleanPropertyOn(sPropKey, sPropValue);
sPropKey = SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
userSetTNIR = false;
sPropValue = Boolean
.toString(SQLServerDriverBooleanProperty.TRANSPARENT_NETWORK_IP_RESOLUTION.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
transparentNetworkIPResolution = isBooleanPropertyOn(sPropKey, sPropValue);
sPropKey = SQLServerDriverBooleanProperty.ENCRYPT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.ENCRYPT.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
socketFactoryClass = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CLASS.toString());
socketFactoryConstructorArg = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.SOCKET_FACTORY_CONSTRUCTOR_ARG.toString());
requestedEncryptionLevel = isBooleanPropertyOn(sPropKey, sPropValue) ? TDS.ENCRYPT_ON : TDS.ENCRYPT_OFF;
sPropKey = SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean
.toString(SQLServerDriverBooleanProperty.TRUST_SERVER_CERTIFICATE.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
trustServerCertificate = isBooleanPropertyOn(sPropKey, sPropValue);
trustManagerClass = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CLASS.toString());
trustManagerConstructorArg = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.TRUST_MANAGER_CONSTRUCTOR_ARG.toString());
sPropKey = SQLServerDriverStringProperty.SELECT_METHOD.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.SELECT_METHOD.getDefaultValue();
}
if ("cursor".equalsIgnoreCase(sPropValue) || "direct".equalsIgnoreCase(sPropValue)) {
sPropValue = sPropValue.toLowerCase(Locale.ENGLISH);
activeConnectionProperties.setProperty(sPropKey, sPropValue);
selectMethod = sPropValue;
} else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidselectMethod"));
Object[] msgArgs = {sPropValue};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
sPropKey = SQLServerDriverStringProperty.RESPONSE_BUFFERING.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.RESPONSE_BUFFERING.getDefaultValue();
}
if ("full".equalsIgnoreCase(sPropValue) || "adaptive".equalsIgnoreCase(sPropValue)) {
activeConnectionProperties.setProperty(sPropKey, sPropValue.toLowerCase(Locale.ENGLISH));
} else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidresponseBuffering"));
Object[] msgArgs = {sPropValue};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
sPropKey = SQLServerDriverStringProperty.APPLICATION_INTENT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.APPLICATION_INTENT.getDefaultValue();
}
applicationIntent = ApplicationIntent.valueOfString(sPropValue);
activeConnectionProperties.setProperty(sPropKey, applicationIntent.toString());
sPropKey = SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
sendTimeAsDatetime = isBooleanPropertyOn(sPropKey, sPropValue);
sPropKey = SQLServerDriverBooleanProperty.USE_FMT_ONLY.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.USE_FMT_ONLY.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
useFmtOnly = isBooleanPropertyOn(sPropKey, sPropValue);
sPropKey = SQLServerDriverIntProperty.STATEMENT_POOLING_CACHE_SIZE.toString();
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
this.setStatementPoolingCacheSize(n);
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_statementPoolingCacheSize"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_ID.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_ID.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
aadPrincipalID = sPropValue;
sPropKey = SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_SECRET.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_SECRET.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
aadPrincipalSecret = sPropValue;
sPropKey = SQLServerDriverBooleanProperty.DISABLE_STATEMENT_POOLING.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
setDisableStatementPooling(isBooleanPropertyOn(sPropKey, sPropValue));
}
sPropKey = SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
integratedSecurity = isBooleanPropertyOn(sPropKey, sPropValue);
}
if (integratedSecurity) {
sPropKey = SQLServerDriverStringProperty.AUTHENTICATION_SCHEME.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
intAuthScheme = AuthenticationScheme.valueOfString(sPropValue);
}
}
if (intAuthScheme == AuthenticationScheme.javaKerberos) {
sPropKey = SQLServerDriverObjectProperty.GSS_CREDENTIAL.toString();
if (activeConnectionProperties.containsKey(sPropKey)) {
impersonatedUserCred = (GSSCredential) activeConnectionProperties.get(sPropKey);
isUserCreatedCredential = true;
}
} else if (intAuthScheme == AuthenticationScheme.ntlm) {
String sPropKeyDomain = SQLServerDriverStringProperty.DOMAIN.toString();
String sPropValueDomain = activeConnectionProperties.getProperty(sPropKeyDomain);
if (null == sPropValueDomain) {
activeConnectionProperties.setProperty(sPropKeyDomain,
SQLServerDriverStringProperty.DOMAIN.getDefaultValue());
}
if (activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()).isEmpty()
|| activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString())
.isEmpty()) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " " + SQLServerException.getErrString("R_NtlmNoUserPasswordDomain"));
}
throw new SQLServerException(SQLServerException.getErrString("R_NtlmNoUserPasswordDomain"), null);
}
ntlmAuthentication = true;
}
sPropKey = SQLServerDriverStringProperty.AUTHENTICATION.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.AUTHENTICATION.getDefaultValue();
}
authenticationString = SqlAuthentication.valueOfString(sPropValue).toString().trim();
if (integratedSecurity
&& !authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " "
+ SQLServerException.getErrString("R_SetAuthenticationWhenIntegratedSecurityTrue"));
}
throw new SQLServerException(
SQLServerException.getErrString("R_SetAuthenticationWhenIntegratedSecurityTrue"), null);
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())
&& ((!activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString())
.isEmpty())
|| (!activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()).isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " "
+ SQLServerException.getErrString("R_IntegratedAuthenticationWithUserPassword"));
}
throw new SQLServerException(
SQLServerException.getErrString("R_IntegratedAuthenticationWithUserPassword"), null);
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())
&& ((activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString())
.isEmpty())
|| (activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()).isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " " + SQLServerException.getErrString("R_NoUserPasswordForActivePassword"));
}
throw new SQLServerException(SQLServerException.getErrString("R_NoUserPasswordForActivePassword"),
null);
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())
&& ((!activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString())
.isEmpty())
|| (!activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()).isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " " + SQLServerException.getErrString("R_MSIAuthenticationWithUserPassword"));
}
throw new SQLServerException(SQLServerException.getErrString("R_MSIAuthenticationWithUserPassword"),
null);
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryServicePrincipal.toString())
&& ((activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_ID.toString()).isEmpty())
|| (activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_SECRET.toString())
.isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " "
+ SQLServerException.getErrString("R_NoUserPasswordForActiveServicePrincipal"));
}
throw new SQLServerException(
SQLServerException.getErrString("R_NoUserPasswordForActiveServicePrincipal"), null);
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.SqlPassword.toString())
&& ((activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString())
.isEmpty())
|| (activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()).isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " " + SQLServerException.getErrString("R_NoUserPasswordForSqlPassword"));
}
throw new SQLServerException(SQLServerException.getErrString("R_NoUserPasswordForSqlPassword"), null);
}
sPropKey = SQLServerDriverStringProperty.ACCESS_TOKEN.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
accessTokenInByte = sPropValue.getBytes(UTF_16LE);
}
if ((null != accessTokenInByte) && 0 == accessTokenInByte.length) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger
.severe(toString() + " " + SQLServerException.getErrString("R_AccessTokenCannotBeEmpty"));
}
throw new SQLServerException(SQLServerException.getErrString("R_AccessTokenCannotBeEmpty"), null);
}
if (integratedSecurity && (null != accessTokenInByte)) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " "
+ SQLServerException.getErrString("R_SetAccesstokenWhenIntegratedSecurityTrue"));
}
throw new SQLServerException(
SQLServerException.getErrString("R_SetAccesstokenWhenIntegratedSecurityTrue"), null);
}
if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString()))
&& (null != accessTokenInByte)) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " "
+ SQLServerException.getErrString("R_SetBothAuthenticationAndAccessToken"));
}
throw new SQLServerException(SQLServerException.getErrString("R_SetBothAuthenticationAndAccessToken"),
null);
}
if ((null != accessTokenInByte) && ((!activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.USER.toString()).isEmpty())
|| (!activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString())
.isEmpty()))) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " " + SQLServerException.getErrString("R_AccessTokenWithUserPassword"));
}
throw new SQLServerException(SQLServerException.getErrString("R_AccessTokenWithUserPassword"), null);
}
if (!userSetTNIR && (!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())
|| null != accessTokenInByte)) {
transparentNetworkIPResolution = false;
}
sPropKey = SQLServerDriverStringProperty.WORKSTATION_ID.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
validateMaxSQLLoginName(sPropKey, sPropValue);
int nPort = 0;
sPropKey = SQLServerDriverIntProperty.PORT_NUMBER.toString();
try {
String strPort = activeConnectionProperties.getProperty(sPropKey);
if (null != strPort) {
nPort = Integer.parseInt(strPort);
if ((nPort < 0) || (nPort > 65535)) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber"));
Object[] msgArgs = {Integer.toString(nPort)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
sPropKey = SQLServerDriverIntProperty.PACKET_SIZE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue && sPropValue.length() > 0) {
try {
requestedPacketSize = Integer.parseInt(sPropValue);
if (-1 == requestedPacketSize)
requestedPacketSize = TDS.SERVER_PACKET_SIZE;
else if (0 == requestedPacketSize)
requestedPacketSize = TDS.MAX_PACKET_SIZE;
} catch (NumberFormatException e) {
requestedPacketSize = TDS.INVALID_PACKET_SIZE;
}
if (TDS.SERVER_PACKET_SIZE != requestedPacketSize) {
if (requestedPacketSize < TDS.MIN_PACKET_SIZE || requestedPacketSize > TDS.MAX_PACKET_SIZE) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPacketSize"));
Object[] msgArgs = {sPropValue};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
}
sPropKey = SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.toString();
sendStringParametersAsUnicode = (null == activeConnectionProperties.getProperty(
sPropKey)) ? SQLServerDriverBooleanProperty.SEND_STRING_PARAMETERS_AS_UNICODE.getDefaultValue()
: isBooleanPropertyOn(sPropKey, activeConnectionProperties.getProperty(sPropKey));
sPropKey = SQLServerDriverBooleanProperty.LAST_UPDATE_COUNT.toString();
lastUpdateCount = isBooleanPropertyOn(sPropKey, activeConnectionProperties.getProperty(sPropKey));
sPropKey = SQLServerDriverBooleanProperty.XOPEN_STATES.toString();
xopenStates = isBooleanPropertyOn(sPropKey, activeConnectionProperties.getProperty(sPropKey));
sPropKey = SQLServerDriverStringProperty.RESPONSE_BUFFERING.toString();
responseBuffering = (null != activeConnectionProperties.getProperty(sPropKey)
&& activeConnectionProperties.getProperty(sPropKey).length() > 0)
? activeConnectionProperties
.getProperty(sPropKey)
: null;
sPropKey = SQLServerDriverIntProperty.LOCK_TIMEOUT.toString();
int defaultLockTimeOut = SQLServerDriverIntProperty.LOCK_TIMEOUT.getDefaultValue();
nLockTimeout = defaultLockTimeOut;
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
if (n >= defaultLockTimeOut)
nLockTimeout = n;
else {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLockTimeOut"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidLockTimeOut"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverIntProperty.QUERY_TIMEOUT.toString();
int defaultQueryTimeout = SQLServerDriverIntProperty.QUERY_TIMEOUT.getDefaultValue();
queryTimeoutSeconds = defaultQueryTimeout;
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
if (n >= defaultQueryTimeout) {
queryTimeoutSeconds = n;
} else {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidQueryTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidQueryTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString();
int defaultSocketTimeout = SQLServerDriverIntProperty.SOCKET_TIMEOUT.getDefaultValue();
socketTimeoutMilliseconds = defaultSocketTimeout;
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
if (n >= defaultSocketTimeout) {
socketTimeoutMilliseconds = n;
} else {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidSocketTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidSocketTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString();
int cancelQueryTimeout = SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue();
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
if (n >= cancelQueryTimeout) {
if (queryTimeoutSeconds > defaultQueryTimeout) {
cancelQueryTimeoutSeconds = n;
}
} else {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidCancelQueryTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidCancelQueryTimeout"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverIntProperty.SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD.toString();
if (activeConnectionProperties.getProperty(sPropKey) != null
&& activeConnectionProperties.getProperty(sPropKey).length() > 0) {
try {
int n = Integer.parseInt(activeConnectionProperties.getProperty(sPropKey));
setServerPreparedStatementDiscardThreshold(n);
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_serverPreparedStatementDiscardThreshold"));
Object[] msgArgs = {activeConnectionProperties.getProperty(sPropKey)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
sPropKey = SQLServerDriverBooleanProperty.ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
setEnablePrepareOnFirstPreparedStatementCall(isBooleanPropertyOn(sPropKey, sPropValue));
}
sPropKey = SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
useBulkCopyForBatchInsert = isBooleanPropertyOn(sPropKey, sPropValue);
}
sPropKey = SQLServerDriverStringProperty.SSL_PROTOCOL.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue();
activeConnectionProperties.setProperty(sPropKey, sPropValue);
} else {
activeConnectionProperties.setProperty(sPropKey, SSLProtocol.valueOfString(sPropValue).toString());
}
sPropKey = SQLServerDriverStringProperty.MSI_CLIENT_ID.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
sPropKey = SQLServerDriverStringProperty.CLIENT_CERTIFICATE.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
activeConnectionProperties.setProperty(sPropKey, sPropValue);
clientCertificate = sPropValue;
}
sPropKey = SQLServerDriverStringProperty.CLIENT_KEY.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
activeConnectionProperties.setProperty(sPropKey, sPropValue);
clientKey = sPropValue;
}
sPropKey = SQLServerDriverStringProperty.CLIENT_KEY_PASSWORD.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
activeConnectionProperties.setProperty(sPropKey, sPropValue);
clientKeyPassword = sPropValue;
}
sPropKey = SQLServerDriverBooleanProperty.SEND_TEMPORAL_DATATYPES_AS_STRING_FOR_BULK_COPY.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null != sPropValue) {
sendTemporalDataTypesAsStringForBulkCopy = isBooleanPropertyOn(sPropKey, sPropValue);
}
sPropKey = SQLServerDriverStringProperty.MAX_RESULT_BUFFER.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
activeConnectionProperties.setProperty(sPropKey,
String.valueOf(MaxResultBufferParser.validateMaxResultBuffer(sPropValue)));
sPropKey = SQLServerDriverBooleanProperty.DELAY_LOADING_LOBS.toString();
sPropValue = activeConnectionProperties.getProperty(sPropKey);
if (null == sPropValue) {
sPropValue = Boolean.toString(SQLServerDriverBooleanProperty.DELAY_LOADING_LOBS.getDefaultValue());
activeConnectionProperties.setProperty(sPropKey, sPropValue);
}
delayLoadingLobs = isBooleanPropertyOn(sPropKey, sPropValue);
FailoverInfo fo = null;
String databaseNameProperty = SQLServerDriverStringProperty.DATABASE_NAME.toString();
String serverNameProperty = SQLServerDriverStringProperty.SERVER_NAME.toString();
String failOverPartnerProperty = SQLServerDriverStringProperty.FAILOVER_PARTNER.toString();
String failOverPartnerPropertyValue = activeConnectionProperties.getProperty(failOverPartnerProperty);
if (multiSubnetFailover && failOverPartnerPropertyValue != null) {
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_dbMirroringWithMultiSubnetFailover"), null, false);
}
if ((multiSubnetFailover || null != failOverPartnerPropertyValue) && !userSetTNIR) {
transparentNetworkIPResolution = false;
}
if ((applicationIntent != null) && applicationIntent.equals(ApplicationIntent.READ_ONLY)
&& failOverPartnerPropertyValue != null) {
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_dbMirroringWithReadOnlyIntent"), null, false);
}
if (null != activeConnectionProperties.getProperty(databaseNameProperty)) {
fo = FailoverMapSingleton.getFailoverInfo(this,
activeConnectionProperties.getProperty(serverNameProperty),
activeConnectionProperties.getProperty(instanceNameProperty),
activeConnectionProperties.getProperty(databaseNameProperty));
} else {
if (null != failOverPartnerPropertyValue)
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_failoverPartnerWithoutDB"), null, true);
}
String mirror = (null == fo) ? failOverPartnerPropertyValue : null;
long startTime = System.currentTimeMillis();
login(activeConnectionProperties.getProperty(serverNameProperty), instanceValue, nPort, mirror, fo,
loginTimeoutSeconds, startTime);
if (TDS.ENCRYPT_ON == negotiatedEncryptionLevel || TDS.ENCRYPT_REQ == negotiatedEncryptionLevel) {
int sslRecordSize = Util.isIBM() ? 8192 : 16384;
if (tdsPacketSize > sslRecordSize) {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " Negotiated tdsPacketSize " + tdsPacketSize
+ " is too large for SSL with JRE " + Util.SYSTEM_JRE + " (max size is " + sslRecordSize
+ ")");
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_packetSizeTooBigForSSL"));
Object[] msgArgs = {Integer.toString(sslRecordSize)};
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, form.format(msgArgs));
}
}
state = State.Opened;
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " End of connect");
}
} finally {
if (!state.equals(State.Opened)) {
if (!state.equals(State.Closed))
this.close();
}
}
return this;
}
private void login(String primary, String primaryInstanceName, int primaryPortNumber, String mirror,
FailoverInfo foActual, int timeout, long timerStart) throws SQLServerException {
final boolean isDBMirroring = null != mirror || null != foActual;
int sleepInterval = 100;
long timeoutUnitInterval;
boolean useFailoverHost = false;
FailoverInfo tempFailover = null;
ServerPortPlaceHolder currentFOPlaceHolder = null;
ServerPortPlaceHolder currentPrimaryPlaceHolder = null;
if (null != foActual) {
tempFailover = foActual;
useFailoverHost = foActual.getUseFailoverPartner();
} else {
if (isDBMirroring) {
tempFailover = new FailoverInfo(mirror, this, false);
}
}
boolean useParallel = getMultiSubnetFailover();
boolean useTnir = getTransparentNetworkIPResolution();
long intervalExpire;
if (0 == timeout) {
timeout = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue();
}
long timerTimeout = timeout * 1000L;
timerExpire = timerStart + timerTimeout;
if (isDBMirroring || useParallel) {
timeoutUnitInterval = (long) (TIMEOUTSTEP * timerTimeout);
} else if (useTnir) {
timeoutUnitInterval = (long) (TIMEOUTSTEP_TNIR * timerTimeout);
} else {
timeoutUnitInterval = timerTimeout;
}
intervalExpire = timerStart + timeoutUnitInterval;
long intervalExpireFullTimeout = timerStart + timerTimeout;
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " Start time: " + timerStart + " Time out time: " + timerExpire
+ " Timeout Unit Interval: " + timeoutUnitInterval);
}
boolean isInteractive = SqlAuthentication.ActiveDirectoryInteractive.toString()
.equalsIgnoreCase(authenticationString);
int attemptNumber = 0;
int noOfRedirections = 0;
while (true) {
clientConnectionId = null;
state = State.Initialized;
try {
if (isDBMirroring && useFailoverHost) {
if (null == currentFOPlaceHolder) {
currentFOPlaceHolder = tempFailover.failoverPermissionCheck(this, integratedSecurity);
}
currentConnectPlaceHolder = currentFOPlaceHolder;
} else {
if (routingInfo != null) {
currentPrimaryPlaceHolder = routingInfo;
routingInfo = null;
} else if (null == currentPrimaryPlaceHolder) {
currentPrimaryPlaceHolder = primaryPermissionCheck(primary, primaryInstanceName,
primaryPortNumber);
}
currentConnectPlaceHolder = currentPrimaryPlaceHolder;
}
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger
.fine(toString() + " This attempt server name: " + currentConnectPlaceHolder.getServerName()
+ " port: " + currentConnectPlaceHolder.getPortNumber() + " InstanceName: "
+ currentConnectPlaceHolder.getInstanceName() + " useParallel: " + useParallel);
connectionlogger.fine(toString() + " This attempt endtime: " + intervalExpire);
connectionlogger.fine(toString() + " This attempt No: " + attemptNumber);
}
InetSocketAddress inetSocketAddress = connectHelper(currentConnectPlaceHolder,
timerRemaining(intervalExpire), timeout, useParallel, useTnir, (0 == attemptNumber),
timerRemaining(intervalExpireFullTimeout));
if (serverSupportsDNSCaching) {
dnsCache.put(currentConnectPlaceHolder.getServerName(), inetSocketAddress);
}
if (isRoutedInCurrentAttempt) {
if (isDBMirroring) {
String msg = SQLServerException.getErrString("R_invalidRoutingInfo");
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, msg);
}
noOfRedirections++;
if (noOfRedirections > 1) {
String msg = SQLServerException.getErrString("R_multipleRedirections");
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, msg);
}
if (tdsChannel != null)
tdsChannel.close();
initResettableValues();
resetNonRoutingEnvchangeValues();
attemptNumber++;
useParallel = false;
useTnir = false;
intervalExpire = timerExpire;
if (timerHasExpired(timerExpire)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {getServerNameString(currentConnectPlaceHolder.getServerName()),
Integer.toString(currentConnectPlaceHolder.getPortNumber()),
SQLServerException.getErrString("R_timedOutBeforeRouting")};
String msg = form.format(msgArgs);
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, msg);
} else {
isRoutedInCurrentAttempt = false;
continue;
}
} else
break;
} catch (SQLServerException sqlex) {
int errorCode = sqlex.getErrorCode();
int driverErrorCode = sqlex.getDriverErrorCode();
if (SQLServerException.LOGON_FAILED == errorCode
|| SQLServerException.PASSWORD_EXPIRED == errorCode
|| SQLServerException.USER_ACCOUNT_LOCKED == errorCode
|| SQLServerException.DRIVER_ERROR_INVALID_TDS == driverErrorCode
|| SQLServerException.DRIVER_ERROR_SSL_FAILED == driverErrorCode
|| SQLServerException.DRIVER_ERROR_INTERMITTENT_TLS_FAILED == driverErrorCode
|| SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG == driverErrorCode
|| SQLServerException.ERROR_SOCKET_TIMEOUT == driverErrorCode
|| (timerHasExpired(timerExpire) && !isInteractive)
|| (state.equals(State.Connected) && !isDBMirroring)) {
close();
throw sqlex;
} else {
if (null != tdsChannel)
tdsChannel.close();
}
if (!isDBMirroring || 1 == attemptNumber % 2) {
long remainingMilliseconds = timerRemaining(timerExpire);
if (remainingMilliseconds <= sleepInterval && !isInteractive) {
throw sqlex;
}
}
}
if (!isDBMirroring || (1 == attemptNumber % 2)) {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine(toString() + " sleeping milisec: " + sleepInterval);
}
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
sleepInterval = (sleepInterval < 500) ? sleepInterval * 2 : 1000;
}
attemptNumber++;
if (useParallel) {
intervalExpire = System.currentTimeMillis() + (timeoutUnitInterval * (attemptNumber + 1));
} else if (isDBMirroring) {
intervalExpire = System.currentTimeMillis() + (timeoutUnitInterval * ((attemptNumber / 2) + 1));
} else if (isInteractive) {
timerStart = System.currentTimeMillis();
timeout = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue();
timerTimeout = timeout * 1000L;
timerExpire = timerStart + timerTimeout;
intervalExpire = timerStart + timeoutUnitInterval;
intervalExpireFullTimeout = timerStart + timerTimeout;
} else if (useTnir) {
long timeSlice = timeoutUnitInterval * (1 << attemptNumber);
if ((1 == attemptNumber) && (500 > timeSlice)) {
timeSlice = 500;
}
intervalExpire = System.currentTimeMillis() + timeSlice;
} else
intervalExpire = timerExpire;
if (intervalExpire > timerExpire) {
intervalExpire = timerExpire;
}
if (isDBMirroring) {
useFailoverHost = !useFailoverHost;
}
}
if (useFailoverHost && null == failoverPartnerServerProvided) {
String curserverinfo = currentConnectPlaceHolder.getServerName();
if (null != currentFOPlaceHolder.getInstanceName()) {
curserverinfo = curserverinfo + "\\";
curserverinfo = curserverinfo + currentFOPlaceHolder.getInstanceName();
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPartnerConfiguration"));
Object[] msgArgs = {
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.DATABASE_NAME.toString()),
curserverinfo};
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, form.format(msgArgs));
}
if (null != failoverPartnerServerProvided) {
if (multiSubnetFailover) {
String msg = SQLServerException.getErrString("R_dbMirroringWithMultiSubnetFailover");
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, msg);
}
if ((applicationIntent != null) && applicationIntent.equals(ApplicationIntent.READ_ONLY)) {
String msg = SQLServerException.getErrString("R_dbMirroringWithReadOnlyIntent");
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, msg);
}
if (null == tempFailover)
tempFailover = new FailoverInfo(failoverPartnerServerProvided, this, false);
if (null != foActual) {
foActual.failoverAdd(this, useFailoverHost, failoverPartnerServerProvided);
} else {
String databaseNameProperty = SQLServerDriverStringProperty.DATABASE_NAME.toString();
String instanceNameProperty = SQLServerDriverStringProperty.INSTANCE_NAME.toString();
String serverNameProperty = SQLServerDriverStringProperty.SERVER_NAME.toString();
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine(toString() + " adding new failover info server: "
+ activeConnectionProperties.getProperty(serverNameProperty) + " instance: "
+ activeConnectionProperties.getProperty(instanceNameProperty) + " database: "
+ activeConnectionProperties.getProperty(databaseNameProperty)
+ " server provided failover: " + failoverPartnerServerProvided);
}
tempFailover.failoverAdd(this, useFailoverHost, failoverPartnerServerProvided);
FailoverMapSingleton.putFailoverInfo(this, primary,
activeConnectionProperties.getProperty(instanceNameProperty),
activeConnectionProperties.getProperty(databaseNameProperty), tempFailover, useFailoverHost,
failoverPartnerServerProvided);
}
}
}
void resetNonRoutingEnvchangeValues() {
tdsPacketSize = TDS.INITIAL_PACKET_SIZE;
databaseCollation = null;
rolledBackTransaction = false;
Arrays.fill(getTransactionDescriptor(), (byte) 0);
sCatalog = originalCatalog;
failoverPartnerServerProvided = null;
}
static final int DEFAULTPORT = SQLServerDriverIntProperty.PORT_NUMBER.getDefaultValue();
ServerPortPlaceHolder primaryPermissionCheck(String primary, String primaryInstanceName,
int primaryPortNumber) throws SQLServerException {
String instancePort;
if (0 == primaryPortNumber) {
if (null != primaryInstanceName) {
instancePort = getInstancePort(primary, primaryInstanceName);
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.fine(toString() + " SQL Server port returned by SQL Browser: " + instancePort);
try {
if (null != instancePort) {
primaryPortNumber = Integer.parseInt(instancePort);
if ((primaryPortNumber < 0) || (primaryPortNumber > 65535)) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_invalidPortNumber"));
Object[] msgArgs = {Integer.toString(primaryPortNumber)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} else
primaryPortNumber = DEFAULTPORT;
} catch (NumberFormatException e) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidPortNumber"));
Object[] msgArgs = {primaryPortNumber};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
} else
primaryPortNumber = DEFAULTPORT;
}
activeConnectionProperties.setProperty(SQLServerDriverIntProperty.PORT_NUMBER.toString(),
String.valueOf(primaryPortNumber));
return new ServerPortPlaceHolder(primary, primaryPortNumber, primaryInstanceName, integratedSecurity);
}
static boolean timerHasExpired(long timerExpire) {
return (System.currentTimeMillis() > timerExpire);
}
static int timerRemaining(long timerExpire) {
long remaining = timerExpire - System.currentTimeMillis();
return (int) ((remaining > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (remaining <= 0) ? 1 : remaining);
}
private InetSocketAddress connectHelper(ServerPortPlaceHolder serverInfo, int timeOutSliceInMillis,
int timeOutFullInSeconds, boolean useParallel, boolean useTnir, boolean isTnirFirstAttempt,
int timeOutsliceInMillisForFullTimeout) throws SQLServerException {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine(toString() + " Connecting with server: " + serverInfo.getServerName() + " port: "
+ serverInfo.getPortNumber() + " Timeout slice: " + timeOutSliceInMillis + " Timeout Full: "
+ timeOutFullInSeconds);
}
hostName = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.WORKSTATION_ID.toString());
if (StringUtils.isEmpty(hostName)) {
hostName = Util.lookupHostName();
}
tdsChannel = new TDSChannel(this);
InetSocketAddress inetSocketAddress = tdsChannel.open(serverInfo.getServerName(), serverInfo.getPortNumber(),
(0 == timeOutFullInSeconds) ? 0 : timeOutSliceInMillis, useParallel, useTnir, isTnirFirstAttempt,
timeOutsliceInMillisForFullTimeout);
setState(State.Connected);
clientConnectionId = UUID.randomUUID();
assert null != clientConnectionId;
Prelogin(serverInfo.getServerName(), serverInfo.getPortNumber());
if (TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
tdsChannel.enableSSL(serverInfo.getServerName(), serverInfo.getPortNumber(), clientCertificate, clientKey,
clientKeyPassword);
clientKeyPassword = "";
}
activeConnectionProperties.remove(SQLServerDriverStringProperty.CLIENT_KEY_PASSWORD.toString());
executeCommand(new LogonCommand());
return inetSocketAddress;
}
void Prelogin(String serverName, int portNumber) throws SQLServerException {
if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString()))
|| (null != accessTokenInByte)) {
fedAuthRequiredByUser = true;
}
final byte messageLength;
final byte fedAuthOffset;
if (fedAuthRequiredByUser) {
messageLength = TDS.B_PRELOGIN_MESSAGE_LENGTH_WITH_FEDAUTH;
requestedEncryptionLevel = TDS.ENCRYPT_ON;
fedAuthOffset = 5;
} else {
messageLength = TDS.B_PRELOGIN_MESSAGE_LENGTH;
fedAuthOffset = 0;
}
final byte[] preloginRequest = new byte[messageLength];
int preloginRequestOffset = 0;
byte[] bufferHeader = {
TDS.PKT_PRELOGIN,
TDS.STATUS_BIT_EOM, 0, messageLength, 0, 0,
0,
0,
};
System.arraycopy(bufferHeader, 0, preloginRequest, preloginRequestOffset, bufferHeader.length);
preloginRequestOffset = preloginRequestOffset + bufferHeader.length;
byte[] preloginOptionsBeforeFedAuth = {
TDS.B_PRELOGIN_OPTION_VERSION, 0, (byte) (16 + fedAuthOffset), 0, 6,
TDS.B_PRELOGIN_OPTION_ENCRYPTION, 0, (byte) (22 + fedAuthOffset), 0, 1,
TDS.B_PRELOGIN_OPTION_TRACEID, 0, (byte) (23 + fedAuthOffset), 0, 36,
};
System.arraycopy(preloginOptionsBeforeFedAuth, 0, preloginRequest, preloginRequestOffset,
preloginOptionsBeforeFedAuth.length);
preloginRequestOffset = preloginRequestOffset + preloginOptionsBeforeFedAuth.length;
if (fedAuthRequiredByUser) {
byte[] preloginOptions2 = {TDS.B_PRELOGIN_OPTION_FEDAUTHREQUIRED, 0, 64, 0, 1,};
System.arraycopy(preloginOptions2, 0, preloginRequest, preloginRequestOffset, preloginOptions2.length);
preloginRequestOffset = preloginRequestOffset + preloginOptions2.length;
}
preloginRequest[preloginRequestOffset] = TDS.B_PRELOGIN_OPTION_TERMINATOR;
preloginRequestOffset++;
byte[] preloginOptionData = {
0, 0, 0, 0, 0, 0,
(null == clientCertificate) ? requestedEncryptionLevel
: (byte) (requestedEncryptionLevel | TDS.ENCRYPT_CLIENT_CERT),
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,};
System.arraycopy(preloginOptionData, 0, preloginRequest, preloginRequestOffset, preloginOptionData.length);
preloginRequestOffset = preloginRequestOffset + preloginOptionData.length;
if (fedAuthRequiredByUser) {
preloginRequest[preloginRequestOffset] = 1;
preloginRequestOffset = preloginRequestOffset + 1;
}
final byte[] preloginResponse = new byte[TDS.INITIAL_PACKET_SIZE];
String preloginErrorLogString = " Prelogin error: host " + serverName + " port " + portNumber;
final byte[] conIdByteArray = Util.asGuidByteArray(clientConnectionId);
int offset;
if (fedAuthRequiredByUser) {
offset = preloginRequest.length - 36 - 1;
} else {
offset = preloginRequest.length - 36;
}
System.arraycopy(conIdByteArray, 0, preloginRequest, offset, conIdByteArray.length);
offset += conIdByteArray.length;
if (Util.isActivityTraceOn()) {
ActivityId activityId = ActivityCorrelator.getNext();
final byte[] actIdByteArray = Util.asGuidByteArray(activityId.getId());
System.arraycopy(actIdByteArray, 0, preloginRequest, offset, actIdByteArray.length);
offset += actIdByteArray.length;
long seqNum = activityId.getSequence();
Util.writeInt((int) seqNum, preloginRequest, offset);
offset += 4;
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " ActivityId " + activityId.toString());
}
}
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(
toString() + " Requesting encryption level:" + TDS.getEncryptionLevel(requestedEncryptionLevel));
}
if (tdsChannel.isLoggingPackets())
tdsChannel.logPacket(preloginRequest, 0, preloginRequest.length, toString() + " Prelogin request");
try {
tdsChannel.write(preloginRequest, 0, preloginRequest.length);
tdsChannel.flush();
} catch (SQLServerException e) {
connectionlogger.warning(
toString() + preloginErrorLogString + " Error sending prelogin request: " + e.getMessage());
throw e;
}
if (Util.isActivityTraceOn()) {
ActivityCorrelator.setCurrentActivityIdSentFlag();
}
int responseLength = preloginResponse.length;
int responseBytesRead = 0;
boolean processedResponseHeader = false;
while (responseBytesRead < responseLength) {
int bytesRead;
try {
bytesRead = tdsChannel.read(preloginResponse, responseBytesRead, responseLength - responseBytesRead);
} catch (SQLServerException e) {
connectionlogger.warning(
toString() + preloginErrorLogString + " Error reading prelogin response: " + e.getMessage());
throw e;
}
if (-1 == bytesRead) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + preloginErrorLogString
+ " Unexpected end of prelogin response after " + responseBytesRead + " bytes read");
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {getServerNameString(serverName), Integer.toString(portNumber),
SQLServerException.getErrString("R_notSQLServer")};
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, form.format(msgArgs));
}
assert bytesRead >= 0;
assert bytesRead <= responseLength - responseBytesRead;
if (tdsChannel.isLoggingPackets())
tdsChannel.logPacket(preloginResponse, responseBytesRead, bytesRead, toString() + " Prelogin response");
responseBytesRead += bytesRead;
if (!processedResponseHeader && responseBytesRead >= TDS.PACKET_HEADER_SIZE) {
if (TDS.PKT_REPLY != preloginResponse[0]) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + preloginErrorLogString + " Unexpected response type:"
+ preloginResponse[0]);
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {getServerNameString(serverName), Integer.toString(portNumber),
SQLServerException.getErrString("R_notSQLServer")};
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, form.format(msgArgs));
}
if (TDS.STATUS_BIT_EOM != (TDS.STATUS_BIT_EOM & preloginResponse[1])) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + preloginErrorLogString + " Unexpected response status:"
+ preloginResponse[1]);
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {getServerNameString(serverName), Integer.toString(portNumber),
SQLServerException.getErrString("R_notSQLServer")};
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, form.format(msgArgs));
}
responseLength = Util.readUnsignedShortBigEndian(preloginResponse, 2);
assert responseLength >= 0;
if (responseLength >= preloginResponse.length) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + preloginErrorLogString + " Response length:"
+ responseLength + " is greater than allowed length:" + preloginResponse.length);
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_tcpipConnectionFailed"));
Object[] msgArgs = {getServerNameString(serverName), Integer.toString(portNumber),
SQLServerException.getErrString("R_notSQLServer")};
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, form.format(msgArgs));
}
processedResponseHeader = true;
}
}
boolean receivedVersionOption = false;
negotiatedEncryptionLevel = TDS.ENCRYPT_INVALID;
int responseIndex = TDS.PACKET_HEADER_SIZE;
while (true) {
if (responseIndex >= responseLength) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Option token not found");
}
throwInvalidTDS();
}
byte optionToken = preloginResponse[responseIndex++];
if (TDS.B_PRELOGIN_OPTION_TERMINATOR == optionToken)
break;
if (responseIndex + 4 >= responseLength) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Offset/Length not found for option:" + optionToken);
}
throwInvalidTDS();
}
int optionOffset = Util.readUnsignedShortBigEndian(preloginResponse, responseIndex)
+ TDS.PACKET_HEADER_SIZE;
responseIndex += 2;
assert optionOffset >= 0;
int optionLength = Util.readUnsignedShortBigEndian(preloginResponse, responseIndex);
responseIndex += 2;
assert optionLength >= 0;
if (optionOffset + optionLength > responseLength) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Offset:" + optionOffset + " and length:" + optionLength
+ " exceed response length:" + responseLength);
}
throwInvalidTDS();
}
switch (optionToken) {
case TDS.B_PRELOGIN_OPTION_VERSION:
if (receivedVersionOption) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Version option already received");
}
throwInvalidTDS();
}
if (6 != optionLength) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Version option length:" + optionLength
+ " is incorrect. Correct value is 6.");
}
throwInvalidTDS();
}
serverMajorVersion = preloginResponse[optionOffset];
if (serverMajorVersion < 9) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Server major version:" + serverMajorVersion
+ " is not supported by this driver.");
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_unsupportedServerVersion"));
Object[] msgArgs = {Integer.toString(preloginResponse[optionOffset])};
terminate(SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG, form.format(msgArgs));
}
if (connectionlogger.isLoggable(Level.FINE))
connectionlogger
.fine(toString() + " Server returned major version:" + preloginResponse[optionOffset]);
receivedVersionOption = true;
break;
case TDS.B_PRELOGIN_OPTION_ENCRYPTION:
if (TDS.ENCRYPT_INVALID != negotiatedEncryptionLevel) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Encryption option already received");
}
throwInvalidTDS();
}
if (1 != optionLength) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Encryption option length:" + optionLength
+ " is incorrect. Correct value is 1.");
}
throwInvalidTDS();
}
negotiatedEncryptionLevel = preloginResponse[optionOffset];
if (TDS.ENCRYPT_OFF != negotiatedEncryptionLevel && TDS.ENCRYPT_ON != negotiatedEncryptionLevel
&& TDS.ENCRYPT_REQ != negotiatedEncryptionLevel
&& TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Server returned "
+ TDS.getEncryptionLevel(negotiatedEncryptionLevel));
}
throwInvalidTDS();
}
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " Negotiated encryption level:"
+ TDS.getEncryptionLevel(negotiatedEncryptionLevel));
if (TDS.ENCRYPT_ON == requestedEncryptionLevel && TDS.ENCRYPT_ON != negotiatedEncryptionLevel
&& TDS.ENCRYPT_REQ != negotiatedEncryptionLevel) {
terminate(SQLServerException.DRIVER_ERROR_SSL_FAILED,
SQLServerException.getErrString("R_sslRequiredNoServerSupport"));
}
if (TDS.ENCRYPT_NOT_SUP == requestedEncryptionLevel
&& TDS.ENCRYPT_NOT_SUP != negotiatedEncryptionLevel) {
if (TDS.ENCRYPT_REQ == negotiatedEncryptionLevel)
terminate(SQLServerException.DRIVER_ERROR_SSL_FAILED,
SQLServerException.getErrString("R_sslRequiredByServer"));
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Client requested encryption level: "
+ TDS.getEncryptionLevel(requestedEncryptionLevel)
+ " Server returned unexpected encryption level: "
+ TDS.getEncryptionLevel(negotiatedEncryptionLevel));
}
throwInvalidTDS();
}
break;
case TDS.B_PRELOGIN_OPTION_FEDAUTHREQUIRED:
if (0 != preloginResponse[optionOffset] && 1 != preloginResponse[optionOffset]) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString()
+ " Server sent an unexpected value for FedAuthRequired PreLogin Option. Value was "
+ preloginResponse[optionOffset]);
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_FedAuthRequiredPreLoginResponseInvalidValue"));
throw new SQLServerException(form.format(new Object[] {preloginResponse[optionOffset]}), null);
}
if (((null != authenticationString)
&& (!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())))
|| (null != accessTokenInByte)) {
fedAuthRequiredPreLoginResponse = (preloginResponse[optionOffset] == 1);
}
break;
default:
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " Ignoring prelogin response option:" + optionToken);
break;
}
}
if (!receivedVersionOption || TDS.ENCRYPT_INVALID == negotiatedEncryptionLevel) {
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger
.warning(toString() + " Prelogin response is missing version and/or encryption option.");
}
throwInvalidTDS();
}
}
final void throwInvalidTDS() throws SQLServerException {
terminate(SQLServerException.DRIVER_ERROR_INVALID_TDS, SQLServerException.getErrString("R_invalidTDS"));
}
final void throwInvalidTDSToken(String tokenName) throws SQLServerException {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unexpectedToken"));
Object[] msgArgs = {tokenName};
String message = SQLServerException.getErrString("R_invalidTDS") + form.format(msgArgs);
terminate(SQLServerException.DRIVER_ERROR_INVALID_TDS, message);
}
final void terminate(int driverErrorCode, String message) throws SQLServerException {
terminate(driverErrorCode, message, null);
}
final void terminate(int driverErrorCode, String message, Throwable throwable) throws SQLServerException {
String state = this.state.equals(State.Opened) ? SQLServerException.EXCEPTION_XOPEN_CONNECTION_FAILURE
: SQLServerException.EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH;
if (!xopenStates)
state = SQLServerException.mapFromXopen(state);
SQLServerException ex = new SQLServerException(this,
SQLServerException.checkAndAppendClientConnId(message, this), state,
0,
true);
if (null != throwable)
ex.initCause(throwable);
ex.setDriverErrorCode(driverErrorCode);
notifyPooledConnection(ex);
close();
throw ex;
}
private final transient Object schedulerLock = new Object();
boolean executeCommand(TDSCommand newCommand) throws SQLServerException {
synchronized (schedulerLock) {
ICounter previousCounter = null;
if (null != currentCommand) {
try {
currentCommand.getCounter().resetCounter();
currentCommand.detach();
} catch (SQLServerException e) {
if (connectionlogger.isLoggable(Level.FINE)) {
connectionlogger.fine("Failed to detach current command : " + e.getMessage());
}
} finally {
previousCounter = currentCommand.getCounter();
currentCommand = null;
}
}
newCommand.createCounter(previousCounter, activeConnectionProperties);
boolean commandComplete = false;
try {
commandComplete = newCommand.execute(tdsChannel.getWriter(), tdsChannel.getReader(newCommand));
} finally {
if (!commandComplete && !isSessionUnAvailable())
currentCommand = newCommand;
}
return commandComplete;
}
}
void resetCurrentCommand() throws SQLServerException {
if (null != currentCommand) {
currentCommand.detach();
currentCommand = null;
}
}
private void connectionCommand(String sql, String logContext) throws SQLServerException {
final class ConnectionCommand extends UninterruptableTDSCommand {
private static final long serialVersionUID = 1L;
final String sql;
ConnectionCommand(String sql, String logContext) {
super(logContext);
this.sql = sql;
}
final boolean doExecute() throws SQLServerException {
TDSWriter tdsWriter = startRequest(TDS.PKT_QUERY);
tdsWriter.sendEnclavePackage(null, null);
tdsWriter.writeString(sql);
TDSParser.parse(startResponse(), getLogContext());
return true;
}
}
executeCommand(new ConnectionCommand(sql, logContext));
}
private String sqlStatementToInitialize() {
String s = null;
if (nLockTimeout > -1)
s = " set lock_timeout " + nLockTimeout;
return s;
}
void setCatalogName(String sDB) {
if (sDB != null) {
if (sDB.length() > 0) {
sCatalog = sDB;
}
}
}
String sqlStatementToSetTransactionIsolationLevel() throws SQLServerException {
String sql = "set transaction isolation level ";
switch (transactionIsolationLevel) {
case Connection.TRANSACTION_READ_UNCOMMITTED: {
sql = sql + " read uncommitted ";
break;
}
case Connection.TRANSACTION_READ_COMMITTED: {
sql = sql + " read committed ";
break;
}
case Connection.TRANSACTION_REPEATABLE_READ: {
sql = sql + " repeatable read ";
break;
}
case Connection.TRANSACTION_SERIALIZABLE: {
sql = sql + " serializable ";
break;
}
case SQLServerConnection.TRANSACTION_SNAPSHOT: {
sql = sql + " snapshot ";
break;
}
default: {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidTransactionLevel"));
Object[] msgArgs = {Integer.toString(transactionIsolationLevel)};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
}
return sql;
}
static String sqlStatementToSetCommit(boolean autoCommit) {
return autoCommit ? "set implicit_transactions off " : "set implicit_transactions on ";
}
@Override
public Statement createStatement() throws SQLServerException {
loggerExternal.entering(loggingClassName, "createStatement");
Statement st = createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
loggerExternal.exiting(loggingClassName, "createStatement", st);
return st;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement", sql);
PreparedStatement pst = prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
loggerExternal.exiting(loggingClassName, "prepareStatement", pst);
return pst;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareCall", sql);
CallableStatement st = prepareCall(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
loggerExternal.exiting(loggingClassName, "prepareCall", st);
return st;
}
@Override
public String nativeSQL(String sql) throws SQLServerException {
loggerExternal.entering(loggingClassName, "nativeSQL", sql);
checkClosed();
loggerExternal.exiting(loggingClassName, "nativeSQL", sql);
return sql;
}
@Override
public void setAutoCommit(boolean newAutoCommitMode) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER)) {
loggerExternal.entering(loggingClassName, "setAutoCommit", newAutoCommitMode);
if (Util.isActivityTraceOn())
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
String commitPendingTransaction = "";
checkClosed();
if (newAutoCommitMode == databaseAutoCommitMode)
return;
if (newAutoCommitMode)
commitPendingTransaction = "IF @@TRANCOUNT > 0 COMMIT TRAN ";
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(
toString() + " Autocommitmode current :" + databaseAutoCommitMode + " new: " + newAutoCommitMode);
}
rolledBackTransaction = false;
connectionCommand(sqlStatementToSetCommit(newAutoCommitMode) + commitPendingTransaction, "setAutoCommit");
databaseAutoCommitMode = newAutoCommitMode;
loggerExternal.exiting(loggingClassName, "setAutoCommit");
}
@Override
public boolean getAutoCommit() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getAutoCommit");
checkClosed();
boolean res = !inXATransaction && databaseAutoCommitMode;
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.exiting(loggingClassName, "getAutoCommit", res);
return res;
}
final byte[] getTransactionDescriptor() {
return transactionDescriptor;
}
@Override
public void commit() throws SQLServerException {
commit(false);
}
public void commit(boolean delayedDurability) throws SQLServerException {
loggerExternal.entering(loggingClassName, "commit");
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
if (!databaseAutoCommitMode) {
if (!delayedDurability)
connectionCommand("IF @@TRANCOUNT > 0 COMMIT TRAN", "Connection.commit");
else
connectionCommand("IF @@TRANCOUNT > 0 COMMIT TRAN WITH ( DELAYED_DURABILITY = ON )",
"Connection.commit");
}
loggerExternal.exiting(loggingClassName, "commit");
}
@Override
public void rollback() throws SQLServerException {
loggerExternal.entering(loggingClassName, "rollback");
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
if (databaseAutoCommitMode) {
SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_cantInvokeRollback"),
null, true);
} else
connectionCommand("IF @@TRANCOUNT > 0 ROLLBACK TRAN", "Connection.rollback");
loggerExternal.exiting(loggingClassName, "rollback");
}
@Override
public void abort(Executor executor) throws SQLException {
loggerExternal.entering(loggingClassName, "abort", executor);
if (isClosed())
return;
SecurityManager secMgr = System.getSecurityManager();
if (secMgr != null) {
try {
SQLPermission perm = new SQLPermission(callAbortPerm);
secMgr.checkPermission(perm);
} catch (SecurityException ex) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_permissionDenied"));
Object[] msgArgs = {callAbortPerm};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, true);
}
}
if (null == executor) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidArgument"));
Object[] msgArgs = {"executor"};
SQLServerException.makeFromDriverError(null, null, form.format(msgArgs), null, false);
} else {
setState(State.Closed);
executor.execute(() -> clearConnectionResources());
}
loggerExternal.exiting(loggingClassName, "abort");
}
@Override
public void close() throws SQLServerException {
loggerExternal.entering(loggingClassName, "close");
setState(State.Closed);
clearConnectionResources();
loggerExternal.exiting(loggingClassName, "close");
}
private void clearConnectionResources() {
if (sharedTimer != null) {
sharedTimer.removeRef();
sharedTimer = null;
}
if (null != tdsChannel) {
tdsChannel.close();
}
if (null != preparedStatementHandleCache)
preparedStatementHandleCache.clear();
if (null != parameterMetadataCache)
parameterMetadataCache.clear();
cleanupPreparedStatementDiscardActions();
if (Util.isActivityTraceOn()) {
ActivityCorrelator.cleanupActivityId();
}
}
final void poolCloseEventNotify() throws SQLServerException {
if (state.equals(State.Opened) && null != pooledConnectionParent) {
if (!databaseAutoCommitMode && !(pooledConnectionParent instanceof XAConnection)) {
connectionCommand("IF @@TRANCOUNT > 0 ROLLBACK TRAN", "close connection");
}
notifyPooledConnection(null);
if (Util.isActivityTraceOn()) {
ActivityCorrelator.cleanupActivityId();
}
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " Connection closed and returned to connection pool");
}
}
}
@Override
public boolean isClosed() throws SQLServerException {
loggerExternal.entering(loggingClassName, "isClosed");
loggerExternal.exiting(loggingClassName, "isClosed", isSessionUnAvailable());
return isSessionUnAvailable();
}
@Override
public DatabaseMetaData getMetaData() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getMetaData");
checkClosed();
if (databaseMetaData == null) {
databaseMetaData = new SQLServerDatabaseMetaData(this);
}
loggerExternal.exiting(loggingClassName, "getMetaData", databaseMetaData);
return databaseMetaData;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.entering(loggingClassName, "setReadOnly", readOnly);
checkClosed();
loggerExternal.exiting(loggingClassName, "setReadOnly");
}
@Override
public boolean isReadOnly() throws SQLServerException {
loggerExternal.entering(loggingClassName, "isReadOnly");
checkClosed();
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.exiting(loggingClassName, "isReadOnly", Boolean.FALSE);
return false;
}
@Override
public void setCatalog(String catalog) throws SQLServerException {
loggerExternal.entering(loggingClassName, "setCatalog", catalog);
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
if (catalog != null) {
connectionCommand("use " + Util.escapeSQLId(catalog), "setCatalog");
sCatalog = catalog;
}
loggerExternal.exiting(loggingClassName, "setCatalog");
}
@Override
public String getCatalog() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getCatalog");
checkClosed();
loggerExternal.exiting(loggingClassName, "getCatalog", sCatalog);
return sCatalog;
}
@Override
public void setTransactionIsolation(int level) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER)) {
loggerExternal.entering(loggingClassName, "setTransactionIsolation", level);
if (Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
}
checkClosed();
if (level == Connection.TRANSACTION_NONE)
return;
String sql;
transactionIsolationLevel = level;
sql = sqlStatementToSetTransactionIsolationLevel();
connectionCommand(sql, "setTransactionIsolation");
loggerExternal.exiting(loggingClassName, "setTransactionIsolation");
}
@Override
public int getTransactionIsolation() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getTransactionIsolation");
checkClosed();
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.exiting(loggingClassName, "getTransactionIsolation", transactionIsolationLevel);
return transactionIsolationLevel;
}
volatile SQLWarning sqlWarnings;
private final Object warningSynchronization = new Object();
@Override
public SQLWarning getWarnings() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getWarnings");
checkClosed();
loggerExternal.exiting(loggingClassName, "getWarnings", sqlWarnings);
return sqlWarnings;
}
void addWarning(String warningString) {
synchronized (warningSynchronization) {
SQLWarning warning = new SQLWarning(warningString);
if (null == sqlWarnings) {
sqlWarnings = warning;
} else {
sqlWarnings.setNextWarning(warning);
}
}
}
@Override
public void clearWarnings() throws SQLServerException {
synchronized (warningSynchronization) {
loggerExternal.entering(loggingClassName, "clearWarnings");
checkClosed();
sqlWarnings = null;
loggerExternal.exiting(loggingClassName, "clearWarnings");
}
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.entering(loggingClassName, "createStatement",
new Object[] {resultSetType, resultSetConcurrency});
checkClosed();
SQLServerStatement st = new SQLServerStatement(this, resultSetType, resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
if (requestStarted) {
addOpenStatement(st);
}
loggerExternal.exiting(loggingClassName, "createStatement", st);
return st;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {sql, resultSetType, resultSetConcurrency});
checkClosed();
SQLServerPreparedStatement st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
if (requestStarted) {
addOpenStatement(st);
}
loggerExternal.exiting(loggingClassName, "prepareStatement", st);
return st;
}
private PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {sql, resultSetType, resultSetConcurrency, stmtColEncSetting});
checkClosed();
SQLServerPreparedStatement st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency,
stmtColEncSetting);
if (requestStarted) {
addOpenStatement(st);
}
loggerExternal.exiting(loggingClassName, "prepareStatement", st);
return st;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLServerException {
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.entering(loggingClassName, "prepareCall",
new Object[] {sql, resultSetType, resultSetConcurrency});
checkClosed();
SQLServerCallableStatement st = new SQLServerCallableStatement(this, sql, resultSetType, resultSetConcurrency,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
if (requestStarted) {
addOpenStatement(st);
}
loggerExternal.exiting(loggingClassName, "prepareCall", st);
return st;
}
@Override
public void setTypeMap(java.util.Map<String, Class<?>> map) throws SQLException {
loggerExternal.entering(loggingClassName, "setTypeMap", map);
checkClosed();
if (map != null && (map instanceof java.util.HashMap)) {
if (map.isEmpty()) {
loggerExternal.exiting(loggingClassName, "setTypeMap");
return;
}
}
SQLServerException.throwNotSupportedException(this, null);
}
@Override
public java.util.Map<String, Class<?>> getTypeMap() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getTypeMap");
checkClosed();
java.util.Map<String, Class<?>> mp = new java.util.HashMap<>();
loggerExternal.exiting(loggingClassName, "getTypeMap", mp);
return mp;
}
int writeAEFeatureRequest(boolean write,
TDSWriter tdsWriter) throws SQLServerException {
int len = 6;
if (write) {
tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_AE);
tdsWriter.writeInt(1);
if (null == enclaveAttestationUrl || enclaveAttestationUrl.isEmpty()) {
tdsWriter.writeByte(TDS.COLUMNENCRYPTION_VERSION1);
} else {
tdsWriter.writeByte(TDS.COLUMNENCRYPTION_VERSION2);
}
}
return len;
}
int writeFedAuthFeatureRequest(boolean write,
TDSWriter tdsWriter,
FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData) throws SQLServerException {
assert (fedAuthFeatureExtensionData.libraryType == TDS.TDS_FEDAUTH_LIBRARY_ADAL
|| fedAuthFeatureExtensionData.libraryType == TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN);
int dataLen = 0;
switch (fedAuthFeatureExtensionData.libraryType) {
case TDS.TDS_FEDAUTH_LIBRARY_ADAL:
dataLen = 2;
break;
case TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN:
assert null != fedAuthFeatureExtensionData.accessToken;
dataLen = 1 + 4 + fedAuthFeatureExtensionData.accessToken.length;
break;
default:
assert (false);
break;
}
int totalLen = dataLen + 5;
if (write) {
tdsWriter.writeByte((byte) TDS.TDS_FEATURE_EXT_FEDAUTH);
byte options = 0x00;
switch (fedAuthFeatureExtensionData.libraryType) {
case TDS.TDS_FEDAUTH_LIBRARY_ADAL:
assert federatedAuthenticationInfoRequested;
options |= TDS.TDS_FEDAUTH_LIBRARY_ADAL << 1;
break;
case TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN:
assert federatedAuthenticationRequested;
options |= TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN << 1;
break;
default:
assert (false);
break;
}
options |= (byte) (fedAuthFeatureExtensionData.fedAuthRequiredPreLoginResponse ? 0x01 : 0x00);
tdsWriter.writeInt(dataLen);
tdsWriter.writeByte(options);
switch (fedAuthFeatureExtensionData.libraryType) {
case TDS.TDS_FEDAUTH_LIBRARY_ADAL:
byte workflow = 0x00;
switch (fedAuthFeatureExtensionData.authentication) {
case ActiveDirectoryPassword:
workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD;
break;
case ActiveDirectoryIntegrated:
workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED;
break;
case ActiveDirectoryMSI:
workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYMSI;
break;
case ActiveDirectoryInteractive:
workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYINTERACTIVE;
break;
case ActiveDirectoryServicePrincipal:
workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYSERVICEPRINCIPAL;
break;
default:
assert (false);
break;
}
tdsWriter.writeByte(workflow);
break;
case TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN:
tdsWriter.writeInt(fedAuthFeatureExtensionData.accessToken.length);
tdsWriter.writeBytes(fedAuthFeatureExtensionData.accessToken, 0,
fedAuthFeatureExtensionData.accessToken.length);
break;
default:
assert (false);
break;
}
}
return totalLen;
}
int writeDataClassificationFeatureRequest(boolean write ,
TDSWriter tdsWriter) throws SQLServerException {
int len = 6;
if (write) {
tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_DATACLASSIFICATION);
tdsWriter.writeInt(1);
tdsWriter.writeByte(TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION);
}
return len;
}
int writeUTF8SupportFeatureRequest(boolean write,
TDSWriter tdsWriter) throws SQLServerException {
int len = 5;
if (write) {
tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_UTF8SUPPORT);
tdsWriter.writeInt(0);
}
return len;
}
int writeDNSCacheFeatureRequest(boolean write,
TDSWriter tdsWriter) throws SQLServerException {
int len = 5;
if (write) {
tdsWriter.writeByte(TDS.TDS_FEATURE_EXT_AZURESQLDNSCACHING);
tdsWriter.writeInt(0);
}
return len;
}
private final class LogonCommand extends UninterruptableTDSCommand {
private static final long serialVersionUID = 1L;
LogonCommand() {
super("logon");
}
final boolean doExecute() throws SQLServerException {
logon(this);
return true;
}
}
private void logon(LogonCommand command) throws SQLServerException {
SSPIAuthentication authentication = null;
if (integratedSecurity) {
if (AuthenticationScheme.nativeAuthentication == intAuthScheme) {
authentication = new AuthenticationJNI(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber());
} else if (AuthenticationScheme.javaKerberos == intAuthScheme) {
if (null != impersonatedUserCred) {
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber(), impersonatedUserCred, isUserCreatedCredential);
} else {
authentication = new KerbAuthentication(this, currentConnectPlaceHolder.getServerName(),
currentConnectPlaceHolder.getPortNumber());
}
} else if (ntlmAuthentication) {
if (null == ntlmPasswordHash) {
ntlmPasswordHash = NTLMAuthentication.getNtlmPasswordHash(
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()));
activeConnectionProperties.remove(SQLServerDriverStringProperty.PASSWORD.toString());
}
authentication = new NTLMAuthentication(this,
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.DOMAIN.toString()),
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()),
ntlmPasswordHash, hostName);
}
}
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())
|| ((authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())
|| authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())
|| authenticationString
.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryServicePrincipal.toString())
|| authenticationString
.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryInteractive.toString()))
&& fedAuthRequiredPreLoginResponse)) {
federatedAuthenticationInfoRequested = true;
fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData(TDS.TDS_FEDAUTH_LIBRARY_ADAL,
authenticationString, fedAuthRequiredPreLoginResponse);
}
if (null != accessTokenInByte) {
fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData(
TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN, fedAuthRequiredPreLoginResponse, accessTokenInByte);
federatedAuthenticationRequested = true;
}
try {
sendLogon(command, authentication, fedAuthFeatureExtensionData);
if (!isRoutedInCurrentAttempt) {
originalCatalog = sCatalog;
String sqlStmt = sqlStatementToInitialize();
if (sqlStmt != null) {
connectionCommand(sqlStmt, "Change Settings");
}
}
} finally {
if (integratedSecurity) {
if (null != authentication) {
authentication.releaseClientContext();
authentication = null;
}
if (null != impersonatedUserCred) {
impersonatedUserCred = null;
}
}
}
}
private static final int ENVCHANGE_DATABASE = 1;
private static final int ENVCHANGE_LANGUAGE = 2;
private static final int ENVCHANGE_CHARSET = 3;
private static final int ENVCHANGE_PACKETSIZE = 4;
private static final int ENVCHANGE_SORTLOCALEID = 5;
private static final int ENVCHANGE_SORTFLAGS = 6;
private static final int ENVCHANGE_SQLCOLLATION = 7;
private static final int ENVCHANGE_XACT_BEGIN = 8;
private static final int ENVCHANGE_XACT_COMMIT = 9;
private static final int ENVCHANGE_XACT_ROLLBACK = 10;
private static final int ENVCHANGE_DTC_ENLIST = 11;
private static final int ENVCHANGE_DTC_DEFECT = 12;
private static final int ENVCHANGE_CHANGE_MIRROR = 13;
@SuppressWarnings("unused")
private static final int ENVCHANGE_UNUSED_14 = 14;
private static final int ENVCHANGE_DTC_PROMOTE = 15;
private static final int ENVCHANGE_DTC_MGR_ADDR = 16;
private static final int ENVCHANGE_XACT_ENDED = 17;
private static final int ENVCHANGE_RESET_COMPLETE = 18;
private static final int ENVCHANGE_USER_INFO = 19;
private static final int ENVCHANGE_ROUTING = 20;
final void processEnvChange(TDSReader tdsReader) throws SQLServerException {
tdsReader.readUnsignedByte();
final int envValueLength = tdsReader.readUnsignedShort();
TDSReaderMark mark = tdsReader.mark();
int envchange = tdsReader.readUnsignedByte();
switch (envchange) {
case ENVCHANGE_PACKETSIZE:
try {
tdsPacketSize = Integer.parseInt(tdsReader.readUnicodeString(tdsReader.readUnsignedByte()));
} catch (NumberFormatException e) {
tdsReader.throwInvalidTDS();
}
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " Network packet size is " + tdsPacketSize + " bytes");
break;
case ENVCHANGE_SQLCOLLATION:
if (SQLCollation.tdsLength() != tdsReader.readUnsignedByte())
tdsReader.throwInvalidTDS();
try {
databaseCollation = new SQLCollation(tdsReader);
} catch (java.io.UnsupportedEncodingException e) {
terminate(SQLServerException.DRIVER_ERROR_INVALID_TDS, e.getMessage(), e);
}
break;
case ENVCHANGE_DTC_ENLIST:
case ENVCHANGE_XACT_BEGIN:
rolledBackTransaction = false;
byte[] transactionDescriptor = getTransactionDescriptor();
if (transactionDescriptor.length != tdsReader.readUnsignedByte())
tdsReader.throwInvalidTDS();
tdsReader.readBytes(transactionDescriptor, 0, transactionDescriptor.length);
if (connectionlogger.isLoggable(Level.FINER)) {
String op;
if (ENVCHANGE_XACT_BEGIN == envchange)
op = " started";
else
op = " enlisted";
connectionlogger.finer(toString() + op);
}
break;
case ENVCHANGE_XACT_ROLLBACK:
rolledBackTransaction = true;
if (inXATransaction) {
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " rolled back. (DTC)");
} else {
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " rolled back");
Arrays.fill(getTransactionDescriptor(), (byte) 0);
}
break;
case ENVCHANGE_XACT_COMMIT:
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " committed");
Arrays.fill(getTransactionDescriptor(), (byte) 0);
break;
case ENVCHANGE_DTC_DEFECT:
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " defected");
Arrays.fill(getTransactionDescriptor(), (byte) 0);
break;
case ENVCHANGE_DATABASE:
setCatalogName(tdsReader.readUnicodeString(tdsReader.readUnsignedByte()));
break;
case ENVCHANGE_CHANGE_MIRROR:
setFailoverPartnerServerProvided(tdsReader.readUnicodeString(tdsReader.readUnsignedByte()));
break;
case ENVCHANGE_LANGUAGE:
case ENVCHANGE_CHARSET:
case ENVCHANGE_SORTLOCALEID:
case ENVCHANGE_SORTFLAGS:
case ENVCHANGE_DTC_PROMOTE:
case ENVCHANGE_DTC_MGR_ADDR:
case ENVCHANGE_XACT_ENDED:
case ENVCHANGE_RESET_COMPLETE:
case ENVCHANGE_USER_INFO:
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.finer(toString() + " Ignored env change: " + envchange);
break;
case ENVCHANGE_ROUTING:
int routingDataValueLength, routingProtocol, routingPortNumber, routingServerNameLength;
routingDataValueLength = routingProtocol = routingPortNumber = routingServerNameLength = -1;
String routingServerName = null;
try {
routingDataValueLength = tdsReader.readUnsignedShort();
if (routingDataValueLength <= 5)
{
throwInvalidTDS();
}
routingProtocol = tdsReader.readUnsignedByte();
if (routingProtocol != 0) {
throwInvalidTDS();
}
routingPortNumber = tdsReader.readUnsignedShort();
if (routingPortNumber <= 0 || routingPortNumber > 65535) {
throwInvalidTDS();
}
routingServerNameLength = tdsReader.readUnsignedShort();
if (routingServerNameLength <= 0 || routingServerNameLength > 1024) {
throwInvalidTDS();
}
routingServerName = tdsReader.readUnicodeString(routingServerNameLength);
assert routingServerName != null;
} finally {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + " Received routing ENVCHANGE with the following values."
+ " routingDataValueLength:" + routingDataValueLength + " protocol:" + routingProtocol
+ " portNumber:" + routingPortNumber + " serverNameLength:" + routingServerNameLength
+ " serverName:" + ((routingServerName != null) ? routingServerName : "null"));
}
}
String currentHostName = activeConnectionProperties.getProperty("hostNameInCertificate");
if (null != currentHostName && currentHostName.startsWith("*") && (null != routingServerName)
&& routingServerName.indexOf('.') != -1) {
char[] currentHostNameCharArray = currentHostName.toCharArray();
char[] routingServerNameCharArray = routingServerName.toCharArray();
boolean hostNameNeedsUpdate = true;
for (int i = currentHostName.length() - 1, j = routingServerName.length() - 1; i > 0 && j > 0;
i--, j--) {
if (routingServerNameCharArray[j] != currentHostNameCharArray[i]) {
hostNameNeedsUpdate = false;
break;
}
}
if (hostNameNeedsUpdate) {
String newHostName = "*" + routingServerName.substring(routingServerName.indexOf('.'));
activeConnectionProperties.setProperty("hostNameInCertificate", newHostName);
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.finer(toString() + "Using new host to validate the SSL certificate");
}
}
}
isRoutedInCurrentAttempt = true;
routingInfo = new ServerPortPlaceHolder(routingServerName, routingPortNumber, null, integratedSecurity);
break;
default:
if (connectionlogger.isLoggable(Level.WARNING)) {
connectionlogger.warning(toString() + " Unknown environment change: " + envchange);
}
throwInvalidTDS();
break;
}
tdsReader.reset(mark);
tdsReader.readBytes(new byte[envValueLength], 0, envValueLength);
}
final void processFedAuthInfo(TDSReader tdsReader, TDSTokenHandler tdsTokenHandler) throws SQLServerException {
SqlFedAuthInfo sqlFedAuthInfo = new SqlFedAuthInfo();
tdsReader.readUnsignedByte();
int tokenLen = tdsReader.readInt();
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " FEDAUTHINFO token stream length = " + tokenLen);
}
if (tokenLen < 4) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + "FEDAUTHINFO token stream length too short for CountOfInfoIDs.");
}
throw new SQLServerException(
SQLServerException.getErrString("R_FedAuthInfoLengthTooShortForCountOfInfoIds"), null);
}
int optionsCount = tdsReader.readInt();
tokenLen = tokenLen - 4;
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " CountOfInfoIDs = " + optionsCount);
}
if (tokenLen > 0) {
byte[] tokenData = new byte[tokenLen];
tdsReader.readBytes(tokenData, 0, tokenLen);
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger
.fine(toString() + " Read rest of FEDAUTHINFO token stream: " + Arrays.toString(tokenData));
}
final int optionSize = 9;
int totalOptionsSize = optionsCount * optionSize;
for (int i = 0; i < optionsCount; i++) {
int currentOptionOffset = i * optionSize;
byte id = tokenData[currentOptionOffset];
byte[] buffer = new byte[4];
buffer[3] = tokenData[currentOptionOffset + 1];
buffer[2] = tokenData[currentOptionOffset + 2];
buffer[1] = tokenData[currentOptionOffset + 3];
buffer[0] = tokenData[currentOptionOffset + 4];
java.nio.ByteBuffer wrapped = java.nio.ByteBuffer.wrap(buffer);
int dataLen = wrapped.getInt();
buffer = new byte[4];
buffer[3] = tokenData[currentOptionOffset + 5];
buffer[2] = tokenData[currentOptionOffset + 6];
buffer[1] = tokenData[currentOptionOffset + 7];
buffer[0] = tokenData[currentOptionOffset + 8];
wrapped = java.nio.ByteBuffer.wrap(buffer);
int dataOffset = wrapped.getInt();
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " FedAuthInfoOpt: ID=" + id + ", DataLen=" + dataLen
+ ", Offset=" + dataOffset);
}
dataOffset = dataOffset - 4;
if (dataOffset < totalOptionsSize || dataOffset >= tokenLen) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + "FedAuthInfoDataOffset points to an invalid location.");
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_FedAuthInfoInvalidOffset"));
throw new SQLServerException(form.format(new Object[] {dataOffset}), null);
}
String data = null;
try {
byte[] dataArray = new byte[dataLen];
System.arraycopy(tokenData, dataOffset, dataArray, 0, dataLen);
data = new String(dataArray, UTF_16LE);
} catch (Exception e) {
connectionlogger.severe(toString() + "Failed to read FedAuthInfoData.");
throw new SQLServerException(SQLServerException.getErrString("R_FedAuthInfoFailedToReadData"), e);
}
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " FedAuthInfoData: " + data);
}
switch (id) {
case TDS.FEDAUTH_INFO_ID_SPN:
sqlFedAuthInfo.spn = data;
break;
case TDS.FEDAUTH_INFO_ID_STSURL:
sqlFedAuthInfo.stsurl = data;
break;
default:
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger
.fine(toString() + " Ignoring unknown federated authentication info option: " + id);
}
break;
}
}
} else {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + "FEDAUTHINFO token stream is not long enough to contain the data it claims to.");
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_FedAuthInfoLengthTooShortForData"));
throw new SQLServerException(form.format(new Object[] {tokenLen}), null);
}
if (null == sqlFedAuthInfo.spn || null == sqlFedAuthInfo.stsurl || sqlFedAuthInfo.spn.trim().isEmpty()
|| sqlFedAuthInfo.stsurl.trim().isEmpty()) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + "FEDAUTHINFO token stream does not contain both STSURL and SPN.");
}
throw new SQLServerException(SQLServerException.getErrString("R_FedAuthInfoDoesNotContainStsurlAndSpn"),
null);
}
onFedAuthInfo(sqlFedAuthInfo, tdsTokenHandler);
aadPrincipalSecret = "";
activeConnectionProperties.remove(SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_SECRET.toString());
}
final class FedAuthTokenCommand extends UninterruptableTDSCommand {
private static final long serialVersionUID = 1L;
TDSTokenHandler tdsTokenHandler = null;
SqlFedAuthToken sqlFedAuthToken = null;
FedAuthTokenCommand(SqlFedAuthToken sqlFedAuthToken, TDSTokenHandler tdsTokenHandler) {
super("FedAuth");
this.tdsTokenHandler = tdsTokenHandler;
this.sqlFedAuthToken = sqlFedAuthToken;
}
final boolean doExecute() throws SQLServerException {
sendFedAuthToken(this, sqlFedAuthToken, tdsTokenHandler);
return true;
}
}
void onFedAuthInfo(SqlFedAuthInfo fedAuthInfo, TDSTokenHandler tdsTokenHandler) throws SQLServerException {
assert (null != activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString())
&& null != activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()))
|| (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())
|| authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())
|| authenticationString
.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryInteractive.toString())
&& fedAuthRequiredPreLoginResponse);
assert null != fedAuthInfo;
attemptRefreshTokenLocked = true;
fedAuthToken = getFedAuthToken(fedAuthInfo);
attemptRefreshTokenLocked = false;
assert null != fedAuthToken;
TDSCommand fedAuthCommand = new FedAuthTokenCommand(fedAuthToken, tdsTokenHandler);
fedAuthCommand.execute(tdsChannel.getWriter(), tdsChannel.getReader(fedAuthCommand));
}
private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLServerException {
SqlFedAuthToken fedAuthToken = null;
assert null != fedAuthInfo;
String user = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString());
int sleepInterval = 100;
while (true) {
if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) {
if (!msalContextExists()) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_MSALMissing"));
throw new SQLServerException(form.format(new Object[] {authenticationString}), null, 0, null);
}
fedAuthToken = SQLServerMSAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user,
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString()),
authenticationString);
break;
} else if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())) {
fedAuthToken = SQLServerSecurityUtility.getMSIAuthToken(fedAuthInfo.spn,
activeConnectionProperties.getProperty(SQLServerDriverStringProperty.MSI_CLIENT_ID.toString()));
break;
} else if (authenticationString
.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryServicePrincipal.toString())) {
fedAuthToken = SQLServerMSAL4JUtils.getSqlFedAuthTokenPrincipal(fedAuthInfo, aadPrincipalID,
aadPrincipalSecret, authenticationString);
break;
} else if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) {
if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows")
&& AuthenticationJNI.isDllLoaded()) {
try {
FedAuthDllInfo dllInfo = AuthenticationJNI.getAccessTokenForWindowsIntegrated(
fedAuthInfo.stsurl, fedAuthInfo.spn, clientConnectionId.toString(),
ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, 0);
assert null != dllInfo.accessTokenBytes;
byte[] accessTokenFromDLL = dllInfo.accessTokenBytes;
String accessToken = new String(accessTokenFromDLL, UTF_16LE);
fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn);
break;
} catch (DLLException adalException) {
int errorCategory = adalException.GetCategory();
if (-1 == errorCategory) {
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_UnableLoadADALSqlDll"));
Object[] msgArgs = {Integer.toHexString(adalException.GetState())};
throw new SQLServerException(form.format(msgArgs), null);
}
int millisecondsRemaining = timerRemaining(timerExpire);
if (ActiveDirectoryAuthentication.GET_ACCESS_TOKEN_TANSISENT_ERROR != errorCategory
|| timerHasExpired(timerExpire) || (sleepInterval >= millisecondsRemaining)) {
String errorStatus = Integer.toHexString(adalException.GetStatus());
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(
toString() + " SQLServerConnection.getFedAuthToken.AdalException category:"
+ errorCategory + " error: " + errorStatus);
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_ADALAuthenticationMiddleErrorMessage"));
String errorCode = Integer.toHexString(adalException.GetStatus()).toUpperCase();
Object[] msgArgs1 = {errorCode, adalException.GetState()};
SQLServerException middleException = new SQLServerException(form.format(msgArgs1),
adalException);
form = new MessageFormat(SQLServerException.getErrString("R_MSALExecution"));
Object[] msgArgs = {user, authenticationString};
throw new SQLServerException(form.format(msgArgs), null, 0, middleException);
}
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken sleeping: "
+ sleepInterval + " milliseconds.");
connectionlogger.fine(toString() + " SQLServerConnection.getFedAuthToken remaining: "
+ millisecondsRemaining + " milliseconds.");
}
try {
Thread.sleep(sleepInterval);
} catch (InterruptedException e1) {
Thread.currentThread().interrupt();
}
sleepInterval = sleepInterval * 2;
}
}
else {
if (!msalContextExists()) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DLLandMSALMissing"));
Object[] msgArgs = {SQLServerDriver.AUTH_DLL_NAME, authenticationString};
throw new SQLServerException(form.format(msgArgs), null, 0, null);
}
fedAuthToken = SQLServerMSAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString);
}
break;
} else if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryInteractive.toString())) {
if (!msalContextExists()) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_MSALMissing"));
throw new SQLServerException(form.format(new Object[] {authenticationString}), null, 0, null);
}
fedAuthToken = SQLServerMSAL4JUtils.getSqlFedAuthTokenInteractive(fedAuthInfo, user,
authenticationString);
break;
}
}
return fedAuthToken;
}
private boolean msalContextExists() {
try {
Class.forName("com.microsoft.aad.msal4j.PublicClientApplication");
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
private void sendFedAuthToken(FedAuthTokenCommand fedAuthCommand, SqlFedAuthToken fedAuthToken,
TDSTokenHandler tdsTokenHandler) throws SQLServerException {
assert null != fedAuthToken;
assert null != fedAuthToken.accessToken;
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " Sending federated authentication token.");
}
TDSWriter tdsWriter = fedAuthCommand.startRequest(TDS.PKT_FEDAUTH_TOKEN_MESSAGE);
byte[] accessToken = fedAuthToken.accessToken.getBytes(UTF_16LE);
tdsWriter.writeInt(accessToken.length + 4);
tdsWriter.writeInt(accessToken.length);
tdsWriter.writeBytes(accessToken, 0, accessToken.length);
TDSReader tdsReader;
tdsReader = fedAuthCommand.startResponse();
federatedAuthenticationRequested = true;
TDSParser.parse(tdsReader, tdsTokenHandler);
}
final void processFeatureExtAck(TDSReader tdsReader) throws SQLServerException {
tdsReader.readUnsignedByte();
byte featureId;
do {
featureId = (byte) tdsReader.readUnsignedByte();
if (featureId != TDS.FEATURE_EXT_TERMINATOR) {
int dataLen;
dataLen = tdsReader.readInt();
byte[] data = new byte[dataLen];
if (dataLen > 0) {
tdsReader.readBytes(data, 0, dataLen);
}
onFeatureExtAck(featureId, data);
}
} while (featureId != TDS.FEATURE_EXT_TERMINATOR);
}
private void onFeatureExtAck(byte featureId, byte[] data) throws SQLServerException {
if (null != routingInfo && TDS.TDS_FEATURE_EXT_AZURESQLDNSCACHING != featureId) {
return;
}
switch (featureId) {
case TDS.TDS_FEATURE_EXT_FEDAUTH: {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(
toString() + " Received feature extension acknowledgement for federated authentication.");
}
if (!federatedAuthenticationRequested) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString() + " Did not request federated authentication.");
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_UnrequestedFeatureAckReceived"));
Object[] msgArgs = {featureId};
throw new SQLServerException(form.format(msgArgs), null);
}
assert null != fedAuthFeatureExtensionData;
switch (fedAuthFeatureExtensionData.libraryType) {
case TDS.TDS_FEDAUTH_LIBRARY_ADAL:
case TDS.TDS_FEDAUTH_LIBRARY_SECURITYTOKEN:
if (0 != data.length) {
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(toString()
+ " Federated authentication feature extension ack for ADAL and Security Token includes extra data.");
}
throw new SQLServerException(
SQLServerException.getErrString("R_FedAuthFeatureAckContainsExtraData"), null);
}
break;
default:
assert false;
if (connectionlogger.isLoggable(Level.SEVERE)) {
connectionlogger.severe(
toString() + " Attempting to use unknown federated authentication library.");
}
MessageFormat form = new MessageFormat(
SQLServerException.getErrString("R_FedAuthFeatureAckUnknownLibraryType"));
Object[] msgArgs = {fedAuthFeatureExtensionData.libraryType};
throw new SQLServerException(form.format(msgArgs), null);
}
break;
}
case TDS.TDS_FEATURE_EXT_AE: {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " Received feature extension acknowledgement for AE.");
}
if (1 > data.length) {
throw new SQLServerException(SQLServerException.getErrString("R_InvalidAEVersionNumber"), null);
}
aeVersion = data[0];
if (TDS.COLUMNENCRYPTION_NOT_SUPPORTED == aeVersion || aeVersion > TDS.COLUMNENCRYPTION_VERSION2) {
throw new SQLServerException(SQLServerException.getErrString("R_InvalidAEVersionNumber"), null);
}
serverColumnEncryptionVersion = ColumnEncryptionVersion.AE_v1;
if (null != enclaveAttestationUrl) {
if (aeVersion < TDS.COLUMNENCRYPTION_VERSION2) {
throw new SQLServerException(SQLServerException.getErrString("R_enclaveNotSupported"), null);
} else {
serverColumnEncryptionVersion = ColumnEncryptionVersion.AE_v2;
enclaveType = new String(data, 2, data.length - 2, UTF_16LE);
}
if (!EnclaveType.isValidEnclaveType(enclaveType)) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_enclaveTypeInvalid"));
Object[] msgArgs = {enclaveType};
throw new SQLServerException(null, form.format(msgArgs), null, 0, false);
}
}
break;
}
case TDS.TDS_FEATURE_EXT_DATACLASSIFICATION: {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger
.fine(toString() + " Received feature extension acknowledgement for Data Classification.");
}
if (2 != data.length) {
throw new SQLServerException(SQLServerException.getErrString("R_UnknownDataClsTokenNumber"), null);
}
serverSupportedDataClassificationVersion = data[0];
if ((0 == serverSupportedDataClassificationVersion)
|| (serverSupportedDataClassificationVersion > TDS.MAX_SUPPORTED_DATA_CLASSIFICATION_VERSION)) {
throw new SQLServerException(SQLServerException.getErrString("R_InvalidDataClsVersionNumber"),
null);
}
byte enabled = data[1];
serverSupportsDataClassification = enabled != 0;
break;
}
case TDS.TDS_FEATURE_EXT_UTF8SUPPORT: {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(toString() + " Received feature extension acknowledgement for UTF8 support.");
}
if (1 > data.length) {
throw new SQLServerException(SQLServerException.getErrString("R_unknownUTF8SupportValue"), null);
}
break;
}
case TDS.TDS_FEATURE_EXT_AZURESQLDNSCACHING: {
if (connectionlogger.isLoggable(Level.FINER)) {
connectionlogger.fine(
toString() + " Received feature extension acknowledgement for Azure SQL DNS Caching.");
}
if (1 > data.length) {
throw new SQLServerException(SQLServerException.getErrString("R_unknownAzureSQLDNSCachingValue"),
null);
}
if (1 == data[0]) {
serverSupportsDNSCaching = true;
if (null == dnsCache) {
dnsCache = new ConcurrentHashMap<String, InetSocketAddress>();
}
} else {
serverSupportsDNSCaching = false;
if (null != dnsCache) {
dnsCache.remove(currentConnectPlaceHolder.getServerName());
}
}
break;
}
default: {
throw new SQLServerException(SQLServerException.getErrString("R_UnknownFeatureAck"), null);
}
}
}
private void executeDTCCommand(int requestType, byte[] payload, String logContext) throws SQLServerException {
final class DTCCommand extends UninterruptableTDSCommand {
private static final long serialVersionUID = 1L;
private final int requestType;
private final byte[] payload;
DTCCommand(int requestType, byte[] payload, String logContext) {
super(logContext);
this.requestType = requestType;
this.payload = payload;
}
final boolean doExecute() throws SQLServerException {
TDSWriter tdsWriter = startRequest(TDS.PKT_DTC);
tdsWriter.sendEnclavePackage(null, null);
tdsWriter.writeShort((short) requestType);
if (null == payload) {
tdsWriter.writeShort((short) 0);
} else {
assert payload.length <= Short.MAX_VALUE;
tdsWriter.writeShort((short) payload.length);
tdsWriter.writeBytes(payload);
}
TDSParser.parse(startResponse(), getLogContext());
return true;
}
}
executeCommand(new DTCCommand(requestType, payload, logContext));
}
final void JTAUnenlistConnection() throws SQLServerException {
executeDTCCommand(TDS.TM_PROPAGATE_XACT, null, "MS_DTC delist connection");
inXATransaction = false;
}
final void JTAEnlistConnection(byte cookie[]) throws SQLServerException {
executeDTCCommand(TDS.TM_PROPAGATE_XACT, cookie, "MS_DTC enlist connection");
connectionCommand(sqlStatementToSetTransactionIsolationLevel(), "JTAEnlistConnection");
inXATransaction = true;
}
private byte[] toUCS16(String s) {
if (s == null)
return new byte[0];
int l = s.length();
byte data[] = new byte[l * 2];
int offset = 0;
for (int i = 0; i < l; i++) {
int c = s.charAt(i);
byte b1 = (byte) (c & 0xFF);
data[offset++] = b1;
data[offset++] = (byte) ((c >> 8) & 0xFF);
}
return data;
}
private byte[] encryptPassword(String pwd) {
if (pwd == null)
pwd = "";
int len = pwd.length();
byte data[] = new byte[len * 2];
for (int i1 = 0; i1 < len; i1++) {
int j1 = pwd.charAt(i1) ^ 0x5a5a;
j1 = (j1 & 0xf) << 4 | (j1 & 0xf0) >> 4 | (j1 & 0xf00) << 4 | (j1 & 0xf000) >> 4;
byte b1 = (byte) ((j1 & 0xFF00) >> 8);
data[(i1 * 2) + 1] = b1;
byte b2 = (byte) ((j1 & 0x00FF));
data[(i1 * 2) + 0] = b2;
}
return data;
}
private void sendLogon(LogonCommand logonCommand, SSPIAuthentication authentication,
FederatedAuthenticationFeatureExtensionData fedAuthFeatureExtensionData) throws SQLServerException {
final class LogonProcessor extends TDSTokenHandler {
private final SSPIAuthentication auth;
private byte[] secBlobOut = null;
StreamLoginAck loginAckToken;
LogonProcessor(SSPIAuthentication auth) {
super("logon");
this.auth = auth;
this.loginAckToken = null;
}
boolean onSSPI(TDSReader tdsReader) throws SQLServerException {
StreamSSPI ack = new StreamSSPI();
ack.setFromTDS(tdsReader);
boolean[] done = {false};
secBlobOut = auth.generateClientContext(ack.sspiBlob, done);
return true;
}
boolean onLoginAck(TDSReader tdsReader) throws SQLServerException {
loginAckToken = new StreamLoginAck();
loginAckToken.setFromTDS(tdsReader);
sqlServerVersion = loginAckToken.sSQLServerVersion;
tdsVersion = loginAckToken.tdsVersion;
return true;
}
final boolean complete(LogonCommand logonCommand, TDSReader tdsReader) throws SQLServerException {
if (null != loginAckToken)
return true;
if (null != secBlobOut && 0 != secBlobOut.length) {
logonCommand.startRequest(TDS.PKT_SSPI).writeBytes(secBlobOut, 0, secBlobOut.length);
return false;
}
logonCommand.startRequest(TDS.PKT_SSPI);
logonCommand.onRequestComplete();
++tdsChannel.numMsgsSent;
TDSParser.parse(tdsReader, this);
return true;
}
}
assert !(integratedSecurity && fedAuthRequiredPreLoginResponse);
assert (!integratedSecurity) || !(federatedAuthenticationInfoRequested || federatedAuthenticationRequested);
assert (null == fedAuthFeatureExtensionData)
|| (federatedAuthenticationInfoRequested || federatedAuthenticationRequested);
assert (null != fedAuthFeatureExtensionData
|| !(federatedAuthenticationInfoRequested || federatedAuthenticationRequested));
String sUser = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString());
String sPwd = activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString());
String appName = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.APPLICATION_NAME.toString());
String interfaceLibName = "Microsoft JDBC Driver " + SQLJdbcVersion.major + "." + SQLJdbcVersion.minor;
String databaseName = activeConnectionProperties
.getProperty(SQLServerDriverStringProperty.DATABASE_NAME.toString());
String serverName = (null != currentConnectPlaceHolder) ? currentConnectPlaceHolder.getServerName()
: activeConnectionProperties.getProperty(
SQLServerDriverStringProperty.SERVER_NAME
.toString());
if (null != serverName && serverName.length() > 128) {
serverName = serverName.substring(0, 128);
}
byte[] secBlob = new byte[0];
boolean[] done = {false};
if (null != authentication) {
secBlob = authentication.generateClientContext(secBlob, done);
sUser = null;
sPwd = null;
}
byte hostnameBytes[] = toUCS16(hostName);
byte userBytes[] = toUCS16(sUser);
byte passwordBytes[] = encryptPassword(sPwd);
int passwordLen = (null != passwordBytes) ? passwordBytes.length : 0;
byte appNameBytes[] = toUCS16(appName);
byte serverNameBytes[] = toUCS16(serverName);
byte interfaceLibNameBytes[] = toUCS16(interfaceLibName);
byte interfaceLibVersionBytes[] = {(byte) SQLJdbcVersion.build, (byte) SQLJdbcVersion.patch,
(byte) SQLJdbcVersion.minor, (byte) SQLJdbcVersion.major};
byte databaseNameBytes[] = toUCS16(databaseName);
byte netAddress[] = new byte[6];
int dataLen = 0;
if (serverMajorVersion >= 11) {
tdsVersion = TDS.VER_DENALI;
} else if (serverMajorVersion >= 10) {
tdsVersion = TDS.VER_KATMAI;
} else if (serverMajorVersion >= 9) {
tdsVersion = TDS.VER_YUKON;
} else {
assert false : "prelogin did not disconnect for the old version: " + serverMajorVersion;
}
final int tdsLoginRequestBaseLength = 94;
TDSWriter tdsWriter = logonCommand.startRequest(TDS.PKT_LOGON70);
int len = tdsLoginRequestBaseLength + hostnameBytes.length + appNameBytes.length + serverNameBytes.length
+ interfaceLibNameBytes.length + databaseNameBytes.length + ((secBlob != null) ? secBlob.length : 0)
+ 4;
if (!integratedSecurity && !(federatedAuthenticationInfoRequested || federatedAuthenticationRequested)
&& null == clientCertificate) {
len = len + passwordLen + userBytes.length;
}
int aeOffset = len;
len += writeAEFeatureRequest(false, tdsWriter);
if (federatedAuthenticationInfoRequested || federatedAuthenticationRequested) {
len = len + writeFedAuthFeatureRequest(false, tdsWriter, fedAuthFeatureExtensionData);
}
len += writeDataClassificationFeatureRequest(false, tdsWriter);
len = len + writeUTF8SupportFeatureRequest(false, tdsWriter);
len = len + writeDNSCacheFeatureRequest(false, tdsWriter);
len = len + 1;
tdsWriter.writeInt(len);
tdsWriter.writeInt(tdsVersion);
tdsWriter.writeInt(requestedPacketSize);
tdsWriter.writeBytes(interfaceLibVersionBytes);
tdsWriter.writeInt(0);
tdsWriter.writeInt(0);
tdsWriter.writeByte((byte) (
TDS.LOGIN_OPTION1_ORDER_X86 |
TDS.LOGIN_OPTION1_CHARSET_ASCII |
TDS.LOGIN_OPTION1_FLOAT_IEEE_754 |
TDS.LOGIN_OPTION1_DUMPLOAD_ON |
TDS.LOGIN_OPTION1_USE_DB_OFF |
TDS.LOGIN_OPTION1_INIT_DB_FATAL |
TDS.LOGIN_OPTION1_SET_LANG_ON
));
tdsWriter.writeByte((byte) (TDS.LOGIN_OPTION2_INIT_LANG_FATAL |
TDS.LOGIN_OPTION2_ODBC_ON |
(integratedSecurity ?
TDS.LOGIN_OPTION2_INTEGRATED_SECURITY_ON
: TDS.LOGIN_OPTION2_INTEGRATED_SECURITY_OFF)));
tdsWriter.writeByte((byte) (TDS.LOGIN_SQLTYPE_DEFAULT | (applicationIntent != null
&& applicationIntent.equals(ApplicationIntent.READ_ONLY) ? TDS.LOGIN_READ_ONLY_INTENT
: TDS.LOGIN_READ_WRITE_INTENT)));
byte colEncSetting;
{
colEncSetting = TDS.LOGIN_OPTION3_FEATURE_EXTENSION;
}
tdsWriter.writeByte((byte) (TDS.LOGIN_OPTION3_DEFAULT | colEncSetting
| ((serverMajorVersion >= 10) ? TDS.LOGIN_OPTION3_UNKNOWN_COLLATION_HANDLING : 0)));
tdsWriter.writeInt((byte) 0);
tdsWriter.writeInt((byte) 0);
tdsWriter.writeShort((short) tdsLoginRequestBaseLength);
tdsWriter.writeShort((short) ((hostName != null && !hostName.isEmpty()) ? hostName.length() : 0));
dataLen += hostnameBytes.length;
if (ntlmAuthentication) {
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (0));
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (0));
} else if (!integratedSecurity && !(federatedAuthenticationInfoRequested || federatedAuthenticationRequested)
&& null == clientCertificate) {
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (sUser == null ? 0 : sUser.length()));
dataLen += userBytes.length;
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (sPwd == null ? 0 : sPwd.length()));
dataLen += passwordLen;
} else {
tdsWriter.writeShort((short) (0));
tdsWriter.writeShort((short) (0));
tdsWriter.writeShort((short) (0));
tdsWriter.writeShort((short) (0));
}
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (appName == null ? 0 : appName.length()));
dataLen += appNameBytes.length;
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (serverName == null ? 0 : serverName.length()));
dataLen += serverNameBytes.length;
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
{
tdsWriter.writeShort((short) 4);
dataLen += 4;
}
assert null != interfaceLibName;
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (interfaceLibName.length()));
dataLen += interfaceLibNameBytes.length;
tdsWriter.writeShort((short) 0);
tdsWriter.writeShort((short) 0);
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
tdsWriter.writeShort((short) (databaseName == null ? 0 : databaseName.length()));
dataLen += databaseNameBytes.length;
tdsWriter.writeBytes(netAddress);
final int uShortMax = 65535;
if (!integratedSecurity) {
tdsWriter.writeShort((short) 0);
tdsWriter.writeShort((short) 0);
} else {
tdsWriter.writeShort((short) (tdsLoginRequestBaseLength + dataLen));
if (uShortMax <= secBlob.length) {
tdsWriter.writeShort((short) (uShortMax));
} else {
tdsWriter.writeShort((short) (secBlob.length));
}
}
tdsWriter.writeShort((short) 0);
tdsWriter.writeShort((short) 0);
if (tdsVersion >= TDS.VER_YUKON) {
tdsWriter.writeShort((short) 0);
tdsWriter.writeShort((short) 0);
if (null != secBlob && uShortMax <= secBlob.length) {
tdsWriter.writeInt(secBlob.length);
} else {
tdsWriter.writeInt((short) 0);
}
}
tdsWriter.writeBytes(hostnameBytes);
tdsWriter.setDataLoggable(false);
if (!integratedSecurity && !(federatedAuthenticationInfoRequested || federatedAuthenticationRequested)
&& null == clientCertificate) {
tdsWriter.writeBytes(userBytes);
tdsWriter.writeBytes(passwordBytes);
}
tdsWriter.setDataLoggable(true);
tdsWriter.writeBytes(appNameBytes);
tdsWriter.writeBytes(serverNameBytes);
tdsWriter.writeInt(aeOffset);
tdsWriter.writeBytes(interfaceLibNameBytes);
tdsWriter.writeBytes(databaseNameBytes);
tdsWriter.setDataLoggable(false);
if (integratedSecurity) {
tdsWriter.writeBytes(secBlob, 0, secBlob.length);
}
writeAEFeatureRequest(true, tdsWriter);
if (federatedAuthenticationInfoRequested || federatedAuthenticationRequested) {
writeFedAuthFeatureRequest(true, tdsWriter, fedAuthFeatureExtensionData);
}
writeDataClassificationFeatureRequest(true, tdsWriter);
writeUTF8SupportFeatureRequest(true, tdsWriter);
writeDNSCacheFeatureRequest(true, tdsWriter);
tdsWriter.writeByte((byte) TDS.FEATURE_EXT_TERMINATOR);
tdsWriter.setDataLoggable(true);
LogonProcessor logonProcessor = new LogonProcessor(authentication);
TDSReader tdsReader;
do {
tdsReader = logonCommand.startResponse();
TDSParser.parse(tdsReader, logonProcessor);
} while (!logonProcessor.complete(logonCommand, tdsReader));
}
private void checkValidHoldability(int holdability) throws SQLServerException {
if (holdability != ResultSet.HOLD_CURSORS_OVER_COMMIT && holdability != ResultSet.CLOSE_CURSORS_AT_COMMIT) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidHoldability"));
SQLServerException.makeFromDriverError(this, this, form.format(new Object[] {holdability}), null, true);
}
}
private void checkMatchesCurrentHoldability(int resultSetHoldability) throws SQLServerException {
if (resultSetHoldability != this.holdability) {
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_sqlServerHoldability"), null, false);
}
}
@Override
public Statement createStatement(int nType, int nConcur, int resultSetHoldability) throws SQLServerException {
loggerExternal.entering(loggingClassName, "createStatement",
new Object[] {nType, nConcur, resultSetHoldability});
Statement st = createStatement(nType, nConcur, resultSetHoldability,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "createStatement", st);
return st;
}
@Override
public Statement createStatement(int nType, int nConcur, int resultSetHoldability,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
loggerExternal.entering(loggingClassName, "createStatement",
new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting});
checkClosed();
checkValidHoldability(resultSetHoldability);
checkMatchesCurrentHoldability(resultSetHoldability);
Statement st = new SQLServerStatement(this, nType, nConcur, stmtColEncSetting);
if (requestStarted) {
addOpenStatement((ISQLServerStatement) st);
}
loggerExternal.exiting(loggingClassName, "createStatement", st);
return st;
}
@Override
public PreparedStatement prepareStatement(java.lang.String sql, int nType, int nConcur,
int resultSetHoldability) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {nType, nConcur, resultSetHoldability});
PreparedStatement st = prepareStatement(sql, nType, nConcur, resultSetHoldability,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "prepareStatement", st);
return st;
}
@Override
public PreparedStatement prepareStatement(java.lang.String sql, int nType, int nConcur, int resultSetHoldability,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetting});
checkClosed();
checkValidHoldability(resultSetHoldability);
checkMatchesCurrentHoldability(resultSetHoldability);
PreparedStatement st = new SQLServerPreparedStatement(this, sql, nType, nConcur, stmtColEncSetting);
if (requestStarted) {
addOpenStatement((ISQLServerStatement) st);
}
loggerExternal.exiting(loggingClassName, "prepareStatement", st);
return st;
}
@Override
public CallableStatement prepareCall(String sql, int nType, int nConcur,
int resultSetHoldability) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {nType, nConcur, resultSetHoldability});
CallableStatement st = prepareCall(sql, nType, nConcur, resultSetHoldability,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "prepareCall", st);
return st;
}
@Override
public CallableStatement prepareCall(String sql, int nType, int nConcur, int resultSetHoldability,
SQLServerStatementColumnEncryptionSetting stmtColEncSetiing) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {nType, nConcur, resultSetHoldability, stmtColEncSetiing});
checkClosed();
checkValidHoldability(resultSetHoldability);
checkMatchesCurrentHoldability(resultSetHoldability);
CallableStatement st = new SQLServerCallableStatement(this, sql, nType, nConcur, stmtColEncSetiing);
if (requestStarted) {
addOpenStatement((ISQLServerStatement) st);
}
loggerExternal.exiting(loggingClassName, "prepareCall", st);
return st;
}
@Override
public PreparedStatement prepareStatement(String sql, int flag) throws SQLServerException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggingClassName, "prepareStatement", new Object[] {sql, flag});
}
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, flag,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public PreparedStatement prepareStatement(String sql, int flag,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggingClassName, "prepareStatement", new Object[] {sql, flag, stmtColEncSetting});
}
checkClosed();
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, stmtColEncSetting);
ps.bRequestedGeneratedKeys = (flag == Statement.RETURN_GENERATED_KEYS);
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLServerException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggingClassName, "prepareStatement", new Object[] {sql, columnIndexes});
}
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, columnIndexes,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {sql, columnIndexes, stmtColEncSetting});
checkClosed();
if (columnIndexes == null || columnIndexes.length != 1) {
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false);
}
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, stmtColEncSetting);
ps.bRequestedGeneratedKeys = true;
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLServerException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggingClassName, "prepareStatement", new Object[] {sql, columnNames});
}
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, columnNames,
SQLServerStatementColumnEncryptionSetting.UseConnectionSetting);
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames,
SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException {
loggerExternal.entering(loggingClassName, "prepareStatement",
new Object[] {sql, columnNames, stmtColEncSetting});
checkClosed();
if (columnNames == null || columnNames.length != 1) {
SQLServerException.makeFromDriverError(this, this,
SQLServerException.getErrString("R_invalidColumnArrayLength"), null, false);
}
SQLServerPreparedStatement ps = (SQLServerPreparedStatement) prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, stmtColEncSetting);
ps.bRequestedGeneratedKeys = true;
loggerExternal.exiting(loggingClassName, "prepareStatement", ps);
return ps;
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
loggerExternal.entering(loggingClassName, "releaseSavepoint", savepoint);
SQLServerException.throwNotSupportedException(this, null);
}
final private Savepoint setNamedSavepoint(String sName) throws SQLServerException {
if (databaseAutoCommitMode) {
SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_cantSetSavepoint"),
null, false);
}
SQLServerSavepoint s = new SQLServerSavepoint(this, sName);
connectionCommand("IF @@TRANCOUNT = 0 BEGIN BEGIN TRAN IF @@TRANCOUNT = 2 COMMIT TRAN END SAVE TRAN "
+ Util.escapeSQLId(s.getLabel()), "setSavepoint");
return s;
}
@Override
public Savepoint setSavepoint(String sName) throws SQLServerException {
loggerExternal.entering(loggingClassName, "setSavepoint", sName);
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
Savepoint pt = setNamedSavepoint(sName);
loggerExternal.exiting(loggingClassName, "setSavepoint", pt);
return pt;
}
@Override
public Savepoint setSavepoint() throws SQLServerException {
loggerExternal.entering(loggingClassName, "setSavepoint");
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
Savepoint pt = setNamedSavepoint(null);
loggerExternal.exiting(loggingClassName, "setSavepoint", pt);
return pt;
}
@Override
public void rollback(Savepoint s) throws SQLServerException {
loggerExternal.entering(loggingClassName, "rollback", s);
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkClosed();
if (databaseAutoCommitMode) {
SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_cantInvokeRollback"),
null, false);
}
connectionCommand("IF @@TRANCOUNT > 0 ROLLBACK TRAN " + Util.escapeSQLId(((SQLServerSavepoint) s).getLabel()),
"rollbackSavepoint");
loggerExternal.exiting(loggingClassName, "rollback");
}
@Override
public int getHoldability() throws SQLServerException {
loggerExternal.entering(loggingClassName, "getHoldability");
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.exiting(loggingClassName, "getHoldability", holdability);
return holdability;
}
@Override
public void setHoldability(int holdability) throws SQLServerException {
loggerExternal.entering(loggingClassName, "setHoldability", holdability);
if (loggerExternal.isLoggable(Level.FINER) && Util.isActivityTraceOn()) {
loggerExternal.finer(toString() + " ActivityId: " + ActivityCorrelator.getNext().toString());
}
checkValidHoldability(holdability);
checkClosed();
if (this.holdability != holdability) {
assert ResultSet.HOLD_CURSORS_OVER_COMMIT == holdability
|| ResultSet.CLOSE_CURSORS_AT_COMMIT == holdability : "invalid holdability " + holdability;
connectionCommand(
(holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT) ? "SET CURSOR_CLOSE_ON_COMMIT ON"
: "SET CURSOR_CLOSE_ON_COMMIT OFF",
"setHoldability");
this.holdability = holdability;
}
loggerExternal.exiting(loggingClassName, "setHoldability");
}
@Override
public int getNetworkTimeout() throws SQLException {
loggerExternal.entering(loggingClassName, "getNetworkTimeout");
checkClosed();
int timeout = 0;
try {
timeout = tdsChannel.getNetworkTimeout();
} catch (IOException ioe) {
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, ioe.getMessage(), ioe);
}
loggerExternal.exiting(loggingClassName, "getNetworkTimeout");
return timeout;
}
@Override
public void setNetworkTimeout(Executor executor, int timeout) throws SQLException {
loggerExternal.entering(loggingClassName, "setNetworkTimeout", timeout);
if (timeout < 0) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidSocketTimeout"));
Object[] msgArgs = {timeout};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, false);
}
checkClosed();
SecurityManager secMgr = System.getSecurityManager();
if (secMgr != null) {
try {
SQLPermission perm = new SQLPermission(SET_NETWORK_TIMEOUT_PERM);
secMgr.checkPermission(perm);
} catch (SecurityException ex) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_permissionDenied"));
Object[] msgArgs = {SET_NETWORK_TIMEOUT_PERM};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, true);
}
}
try {
tdsChannel.setNetworkTimeout(timeout);
} catch (IOException ioe) {
terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, ioe.getMessage(), ioe);
}
loggerExternal.exiting(loggingClassName, "setNetworkTimeout");
}
@Override
public String getSchema() throws SQLException {
loggerExternal.entering(loggingClassName, "getSchema");
checkClosed();
try (SQLServerStatement stmt = (SQLServerStatement) this.createStatement();
SQLServerResultSet resultSet = stmt.executeQueryInternal("SELECT SCHEMA_NAME()")) {
if (resultSet != null) {
resultSet.next();
return resultSet.getString(1);
} else {
SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_getSchemaError"),
null, true);
}
} catch (SQLException e) {
if (isSessionUnAvailable()) {
throw e;
}
SQLServerException.makeFromDriverError(this, this, SQLServerException.getErrString("R_getSchemaError"),
null, true);
}
loggerExternal.exiting(loggingClassName, "getSchema");
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
loggerExternal.entering(loggingClassName, "setSchema", schema);
checkClosed();
addWarning(SQLServerException.getErrString("R_setSchemaWarning"));
loggerExternal.exiting(loggingClassName, "setSchema");
}
@Override
public void setSendTimeAsDatetime(boolean sendTimeAsDateTimeValue) {
sendTimeAsDatetime = sendTimeAsDateTimeValue;
}
@Override
public void setUseFmtOnly(boolean useFmtOnly) {
this.useFmtOnly = useFmtOnly;
}
@Override
public final boolean getUseFmtOnly() {
return useFmtOnly;
}
@Override
public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
SQLServerException.throwNotSupportedException(this, null);
return null;
}
@Override
public java.sql.Blob createBlob() throws SQLException {
checkClosed();
return new SQLServerBlob(this);
}
@Override
public java.sql.Clob createClob() throws SQLException {
checkClosed();
return new SQLServerClob(this);
}
@Override
public java.sql.NClob createNClob() throws SQLException {
checkClosed();
return new SQLServerNClob(this);
}
@Override
public SQLXML createSQLXML() throws SQLException {
loggerExternal.entering(loggingClassName, "createSQLXML");
SQLXML sqlxml = new SQLServerSQLXML(this);
if (loggerExternal.isLoggable(Level.FINER))
loggerExternal.exiting(loggingClassName, "createSQLXML", sqlxml);
return sqlxml;
}
@Override
public java.sql.Struct createStruct(String typeName, Object[] attributes) throws SQLException {
SQLServerException.throwNotSupportedException(this, null);
return null;
}
String getTrustedServerNameAE() throws SQLServerException {
return trustedServerNameAE.toUpperCase();
}
@Override
public Properties getClientInfo() throws SQLException {
loggerExternal.entering(loggingClassName, "getClientInfo");
checkClosed();
Properties p = new Properties();
loggerExternal.exiting(loggingClassName, "getClientInfo", p);
return p;
}
@Override
public String getClientInfo(String name) throws SQLException {
loggerExternal.entering(loggingClassName, "getClientInfo", name);
checkClosed();
loggerExternal.exiting(loggingClassName, "getClientInfo", null);
return null;
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
loggerExternal.entering(loggingClassName, "setClientInfo", properties);
try {
checkClosed();
} catch (SQLServerException ex) {
SQLClientInfoException info = new SQLClientInfoException();
info.initCause(ex);
throw info;
}
if (!properties.isEmpty()) {
Enumeration<?> e = properties.keys();
while (e.hasMoreElements()) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProperty"));
Object[] msgArgs = {e.nextElement()};
addWarning(form.format(msgArgs));
}
}
loggerExternal.exiting(loggingClassName, "setClientInfo");
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) {
loggerExternal.entering(loggingClassName, "setClientInfo", new Object[] {name, value});
}
try {
checkClosed();
} catch (SQLServerException ex) {
SQLClientInfoException info = new SQLClientInfoException();
info.initCause(ex);
throw info;
}
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidProperty"));
Object[] msgArgs = {name};
addWarning(form.format(msgArgs));
loggerExternal.exiting(loggingClassName, "setClientInfo");
}
@Override
public boolean isValid(int timeout) throws SQLException {
loggerExternal.entering(loggingClassName, "isValid", timeout);
if (timeout < 0) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidQueryTimeOutValue"));
Object[] msgArgs = {timeout};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs), null, true);
}
if (isSessionUnAvailable())
return false;
boolean isValid = true;
try (SQLServerStatement stmt = new SQLServerStatement(this, ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_READ_ONLY, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting)) {
if (0 != timeout)
stmt.setQueryTimeout(timeout);
stmt.executeQueryInternal("SELECT 1");
} catch (SQLException e) {
isValid = false;
connectionlogger.fine(toString() + " Exception checking connection validity: " + e.getMessage());
}
loggerExternal.exiting(loggingClassName, "isValid", isValid);
return isValid;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
loggerExternal.entering(loggingClassName, "isWrapperFor", iface);
boolean f = iface.isInstance(this);
loggerExternal.exiting(loggingClassName, "isWrapperFor", f);
return f;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
loggerExternal.entering(loggingClassName, "unwrap", iface);
T t;
try {
t = iface.cast(this);
} catch (ClassCastException e) {
SQLServerException newe = new SQLServerException(e.getMessage(), e);
throw newe;
}
loggerExternal.exiting(loggingClassName, "unwrap", t);
return t;
}
private boolean requestStarted = false;
private boolean originalDatabaseAutoCommitMode;
private int originalTransactionIsolationLevel;
private int originalNetworkTimeout;
private int originalHoldability;
private boolean originalSendTimeAsDatetime;
private int originalStatementPoolingCacheSize;
private boolean originalDisableStatementPooling;
private int originalServerPreparedStatementDiscardThreshold;
private Boolean originalEnablePrepareOnFirstPreparedStatementCall;
private String originalSCatalog;
private boolean originalUseBulkCopyForBatchInsert;
private volatile SQLWarning originalSqlWarnings;
private List<ISQLServerStatement> openStatements;
private boolean originalUseFmtOnly;
private boolean originalDelayLoadingLobs;
int aeVersion = TDS.COLUMNENCRYPTION_NOT_SUPPORTED;
protected void beginRequestInternal() throws SQLException {
loggerExternal.entering(loggingClassName, "beginRequest", this);
synchronized (this) {
if (!requestStarted) {
originalDatabaseAutoCommitMode = databaseAutoCommitMode;
originalTransactionIsolationLevel = transactionIsolationLevel;
originalNetworkTimeout = getNetworkTimeout();
originalHoldability = holdability;
originalSendTimeAsDatetime = sendTimeAsDatetime;
originalStatementPoolingCacheSize = statementPoolingCacheSize;
originalDisableStatementPooling = disableStatementPooling;
originalServerPreparedStatementDiscardThreshold = getServerPreparedStatementDiscardThreshold();
originalEnablePrepareOnFirstPreparedStatementCall = getEnablePrepareOnFirstPreparedStatementCall();
originalSCatalog = sCatalog;
originalUseBulkCopyForBatchInsert = getUseBulkCopyForBatchInsert();
originalSqlWarnings = sqlWarnings;
openStatements = new LinkedList<ISQLServerStatement>();
originalUseFmtOnly = useFmtOnly;
originalDelayLoadingLobs = delayLoadingLobs;
requestStarted = true;
}
}
loggerExternal.exiting(loggingClassName, "beginRequest", this);
}
protected void endRequestInternal() throws SQLException {
loggerExternal.entering(loggingClassName, "endRequest", this);
synchronized (this) {
if (requestStarted) {
if (!databaseAutoCommitMode) {
rollback();
}
if (databaseAutoCommitMode != originalDatabaseAutoCommitMode) {
setAutoCommit(originalDatabaseAutoCommitMode);
}
if (transactionIsolationLevel != originalTransactionIsolationLevel) {
setTransactionIsolation(originalTransactionIsolationLevel);
}
if (getNetworkTimeout() != originalNetworkTimeout) {
setNetworkTimeout(null, originalNetworkTimeout);
}
if (holdability != originalHoldability) {
setHoldability(originalHoldability);
}
if (sendTimeAsDatetime != originalSendTimeAsDatetime) {
setSendTimeAsDatetime(originalSendTimeAsDatetime);
}
if (useFmtOnly != originalUseFmtOnly) {
setUseFmtOnly(originalUseFmtOnly);
}
if (statementPoolingCacheSize != originalStatementPoolingCacheSize) {
setStatementPoolingCacheSize(originalStatementPoolingCacheSize);
}
if (disableStatementPooling != originalDisableStatementPooling) {
setDisableStatementPooling(originalDisableStatementPooling);
}
if (getServerPreparedStatementDiscardThreshold() != originalServerPreparedStatementDiscardThreshold) {
setServerPreparedStatementDiscardThreshold(originalServerPreparedStatementDiscardThreshold);
}
if (getEnablePrepareOnFirstPreparedStatementCall() != originalEnablePrepareOnFirstPreparedStatementCall) {
setEnablePrepareOnFirstPreparedStatementCall(originalEnablePrepareOnFirstPreparedStatementCall);
}
if (!sCatalog.equals(originalSCatalog)) {
setCatalog(originalSCatalog);
}
if (getUseBulkCopyForBatchInsert() != originalUseBulkCopyForBatchInsert) {
setUseBulkCopyForBatchInsert(originalUseBulkCopyForBatchInsert);
}
if (delayLoadingLobs != originalDelayLoadingLobs) {
setDelayLoadingLobs(originalDelayLoadingLobs);
}
sqlWarnings = originalSqlWarnings;
if (null != openStatements) {
while (!openStatements.isEmpty()) {
try (Statement st = openStatements.get(0)) {}
}
openStatements.clear();
}
requestStarted = false;
}
}
loggerExternal.exiting(loggingClassName, "endRequest", this);
}
static final char[] OUT = {' ', 'O', 'U', 'T'};
String replaceParameterMarkers(String sqlSrc, int[] paramPositions, Parameter[] params,
boolean isReturnValueSyntax) throws SQLServerException {
final int MAX_PARAM_NAME_LEN = 6;
char[] sqlDst = new char[sqlSrc.length() + params.length * (MAX_PARAM_NAME_LEN + OUT.length)];
int dstBegin = 0;
int srcBegin = 0;
int nParam = 0;
int paramIndex = 0;
while (true) {
int srcEnd = (paramIndex >= paramPositions.length) ? sqlSrc.length() : paramPositions[paramIndex];
sqlSrc.getChars(srcBegin, srcEnd, sqlDst, dstBegin);
dstBegin += srcEnd - srcBegin;
if (sqlSrc.length() == srcEnd)
break;
dstBegin += makeParamName(nParam++, sqlDst, dstBegin);
srcBegin = srcEnd + 1;
if (params[paramIndex++].isOutput()) {
if (!isReturnValueSyntax || paramIndex > 1) {
System.arraycopy(OUT, 0, sqlDst, dstBegin, OUT.length);
dstBegin += OUT.length;
}
}
}
return new String(sqlDst, 0, dstBegin);
}
static int makeParamName(int nParam, char[] name, int offset) {
name[offset + 0] = '@';
name[offset + 1] = 'P';
if (nParam < 10) {
name[offset + 2] = (char) ('0' + nParam);
return 3;
} else {
if (nParam < 100) {
int nBase = 2;
while (true) {
if (nParam < nBase * 10) {
name[offset + 2] = (char) ('0' + (nBase - 1));
name[offset + 3] = (char) ('0' + (nParam - ((nBase - 1) * 10)));
return 4;
}
nBase++;
}
} else {
String sParam = "" + nParam;
sParam.getChars(0, sParam.length(), name, offset + 2);
return 2 + sParam.length();
}
}
}
void notifyPooledConnection(SQLServerException e) {
synchronized (this) {
if (null != pooledConnectionParent) {
pooledConnectionParent.notifyEvent(e);
}
}
}
void DetachFromPool() {
synchronized (this) {
pooledConnectionParent = null;
}
}
private static final int BROWSER_PORT = 1434;
String getInstancePort(String server, String instanceName) throws SQLServerException {
String browserResult = null;
DatagramSocket datagramSocket = null;
String lastErrorMessage = null;
try {
lastErrorMessage = "Failed to determine instance for the : " + server + " instance:" + instanceName;
try {
datagramSocket = new DatagramSocket();
datagramSocket.setSoTimeout(1000);
} catch (SocketException socketException) {
lastErrorMessage = "Unable to create local datagram socket";
throw socketException;
}
assert null != datagramSocket;
try {
if (multiSubnetFailover) {
InetAddress[] inetAddrs = InetAddress.getAllByName(server);
assert null != inetAddrs;
for (InetAddress inetAddr : inetAddrs) {
try {
byte sendBuffer[] = (" " + instanceName).getBytes();
sendBuffer[0] = 4;
DatagramPacket udpRequest = new DatagramPacket(sendBuffer, sendBuffer.length, inetAddr,
BROWSER_PORT);
datagramSocket.send(udpRequest);
} catch (IOException ioException) {
lastErrorMessage = "Error sending SQL Server Browser Service UDP request to address: "
+ inetAddr + ", port: " + BROWSER_PORT;
throw ioException;
}
}
} else {
InetAddress inetAddr = InetAddress.getByName(server);
assert null != inetAddr;
try {
byte sendBuffer[] = (" " + instanceName).getBytes();
sendBuffer[0] = 4;
DatagramPacket udpRequest = new DatagramPacket(sendBuffer, sendBuffer.length, inetAddr,
BROWSER_PORT);
datagramSocket.send(udpRequest);
} catch (IOException ioException) {
lastErrorMessage = "Error sending SQL Server Browser Service UDP request to address: "
+ inetAddr + ", port: " + BROWSER_PORT;
throw ioException;
}
}
} catch (UnknownHostException unknownHostException) {
lastErrorMessage = "Unable to determine IP address of host: " + server;
throw unknownHostException;
}
try {
byte receiveBuffer[] = new byte[4096];
DatagramPacket udpResponse = new DatagramPacket(receiveBuffer, receiveBuffer.length);
datagramSocket.receive(udpResponse);
browserResult = new String(receiveBuffer, 3, receiveBuffer.length - 3);
if (connectionlogger.isLoggable(Level.FINER))
connectionlogger.fine(toString() + " Received SSRP UDP response from IP address: "
+ udpResponse.getAddress().getHostAddress());
} catch (IOException ioException) {
lastErrorMessage = "Error receiving SQL Server Browser Service UDP response from server: " + server;
throw ioException;
}
} catch (IOException ioException) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_sqlBrowserFailed"));
Object[] msgArgs = {server, instanceName, ioException.toString()};
connectionlogger.log(Level.FINE, toString() + " " + lastErrorMessage, ioException);
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs),
SQLServerException.EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH, false);
} finally {
if (null != datagramSocket)
datagramSocket.close();
}
assert null != browserResult;
int p = browserResult.indexOf("tcp;");
if (-1 == p) {
MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_notConfiguredToListentcpip"));
Object[] msgArgs = {instanceName};
SQLServerException.makeFromDriverError(this, this, form.format(msgArgs),
SQLServerException.EXCEPTION_XOPEN_CONNECTION_CANT_ESTABLISH, false);
}
int p1 = p + 4;
int p2 = browserResult.indexOf(';', p1);
return browserResult.substring(p1, p2);
}
int getNextSavepointId() {
nNextSavePointId++;
return nNextSavePointId;
}
void doSecurityCheck() {
assert null != currentConnectPlaceHolder;
currentConnectPlaceHolder.doSecurityCheck();
}
private static long columnEncryptionKeyCacheTtl = TimeUnit.SECONDS.convert(2, TimeUnit.HOURS);
public static synchronized void setColumnEncryptionKeyCacheTtl(int columnEncryptionKeyCacheTTL,
TimeUnit unit) throws SQLServerException {
if (columnEncryptionKeyCacheTTL < 0 || unit.equals(TimeUnit.MILLISECONDS) || unit.equals(TimeUnit.MICROSECONDS)
|| unit.equals(TimeUnit.NANOSECONDS)) {
throw new SQLServerException(null, SQLServerException.getErrString("R_invalidCEKCacheTtl"), null, 0, false);
}
columnEncryptionKeyCacheTtl = TimeUnit.SECONDS.convert(columnEncryptionKeyCacheTTL, unit);
}
static synchronized long getColumnEncryptionKeyCacheTtl() {
return columnEncryptionKeyCacheTtl;
}
final void enqueueUnprepareStatementHandle(PreparedStatementHandle statementHandle) {
if (null == statementHandle)
return;
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal
.finer(this + ": Adding PreparedHandle to queue for un-prepare:" + statementHandle.getHandle());
this.discardedPreparedStatementHandles.add(statementHandle);
this.discardedPreparedStatementHandleCount.incrementAndGet();
}
@Override
public int getDiscardedServerPreparedStatementCount() {
return this.discardedPreparedStatementHandleCount.get();
}
@Override
public void closeUnreferencedPreparedStatementHandles() {
this.unprepareUnreferencedPreparedStatementHandles(true);
}
private final void cleanupPreparedStatementDiscardActions() {
discardedPreparedStatementHandles.clear();
discardedPreparedStatementHandleCount.set(0);
}
@Override
public boolean getEnablePrepareOnFirstPreparedStatementCall() {
if (null == this.enablePrepareOnFirstPreparedStatementCall)
return DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL;
else
return this.enablePrepareOnFirstPreparedStatementCall;
}
@Override
public void setEnablePrepareOnFirstPreparedStatementCall(boolean value) {
this.enablePrepareOnFirstPreparedStatementCall = value;
}
@Override
public int getServerPreparedStatementDiscardThreshold() {
if (0 > this.serverPreparedStatementDiscardThreshold)
return DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD;
else
return this.serverPreparedStatementDiscardThreshold;
}
@Override
public void setServerPreparedStatementDiscardThreshold(int value) {
this.serverPreparedStatementDiscardThreshold = Math.max(0, value);
}
final boolean isPreparedStatementUnprepareBatchingEnabled() {
return 1 < getServerPreparedStatementDiscardThreshold();
}
final void unprepareUnreferencedPreparedStatementHandles(boolean force) {
if (isSessionUnAvailable())
return;
final int threshold = getServerPreparedStatementDiscardThreshold();
if (force || threshold < getDiscardedServerPreparedStatementCount()) {
StringBuilder sql = new StringBuilder(threshold * 32);
int handlesRemoved = 0;
PreparedStatementHandle statementHandle = null;
while (null != (statementHandle = discardedPreparedStatementHandles.poll())) {
++handlesRemoved;
sql.append(statementHandle.isDirectSql() ? "EXEC sp_unprepare " : "EXEC sp_cursorunprepare ")
.append(statementHandle.getHandle()).append(';');
}
try {
try (SQLServerStatement stmt = (SQLServerStatement) this.createStatement()) {
stmt.isInternalEncryptionQuery = true;
stmt.execute(sql.toString());
}
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.finer(this + ": Finished un-preparing handle count:" + handlesRemoved);
} catch (SQLException e) {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.log(Level.FINER, this + ": Error batch-closing at least one prepared handle", e);
}
discardedPreparedStatementHandleCount.addAndGet(-handlesRemoved);
}
}
@Override
public boolean getDisableStatementPooling() {
return this.disableStatementPooling;
}
@Override
public void setDisableStatementPooling(boolean value) {
this.disableStatementPooling = value;
if (!value && 0 < this.getStatementPoolingCacheSize()) {
prepareCache();
}
}
@Override
public int getStatementPoolingCacheSize() {
return statementPoolingCacheSize;
}
@Override
public int getStatementHandleCacheEntryCount() {
if (!isStatementPoolingEnabled())
return 0;
else
return this.preparedStatementHandleCache.size();
}
@Override
public boolean isStatementPoolingEnabled() {
return null != preparedStatementHandleCache && 0 < this.getStatementPoolingCacheSize()
&& !this.getDisableStatementPooling();
}
@Override
public void setStatementPoolingCacheSize(int value) {
value = Math.max(0, value);
statementPoolingCacheSize = value;
if (!this.disableStatementPooling && value > 0) {
prepareCache();
}
if (null != preparedStatementHandleCache)
preparedStatementHandleCache.setCapacity(value);
if (null != parameterMetadataCache)
parameterMetadataCache.setCapacity(value);
}
private void prepareCache() {
preparedStatementHandleCache = new Builder<CityHash128Key, PreparedStatementHandle>()
.maximumWeightedCapacity(getStatementPoolingCacheSize())
.listener(new PreparedStatementCacheEvictionListener()).build();
parameterMetadataCache = new Builder<CityHash128Key, SQLServerParameterMetaData>()
.maximumWeightedCapacity(getStatementPoolingCacheSize()).build();
}
final SQLServerParameterMetaData getCachedParameterMetadata(CityHash128Key key) {
if (!isStatementPoolingEnabled())
return null;
return parameterMetadataCache.get(key);
}
final void registerCachedParameterMetadata(CityHash128Key key, SQLServerParameterMetaData pmd) {
if (!isStatementPoolingEnabled() || null == pmd)
return;
parameterMetadataCache.put(key, pmd);
}
final PreparedStatementHandle getCachedPreparedStatementHandle(CityHash128Key key) {
if (!isStatementPoolingEnabled())
return null;
return preparedStatementHandleCache.get(key);
}
final PreparedStatementHandle registerCachedPreparedStatementHandle(CityHash128Key key, int handle,
boolean isDirectSql) {
if (!isStatementPoolingEnabled() || null == key)
return null;
PreparedStatementHandle cacheItem = new PreparedStatementHandle(key, handle, isDirectSql, false);
preparedStatementHandleCache.putIfAbsent(key, cacheItem);
return cacheItem;
}
final void returnCachedPreparedStatementHandle(PreparedStatementHandle handle) {
handle.removeReference();
if (handle.isEvictedFromCache() && handle.tryDiscardHandle())
enqueueUnprepareStatementHandle(handle);
}
final void evictCachedPreparedStatementHandle(PreparedStatementHandle handle) {
if (null == handle || null == handle.getKey())
return;
preparedStatementHandleCache.remove(handle.getKey());
}
final class PreparedStatementCacheEvictionListener
implements EvictionListener<CityHash128Key, PreparedStatementHandle> {
public void onEviction(CityHash128Key key, PreparedStatementHandle handle) {
if (null != handle) {
handle.setIsEvictedFromCache(true);
if (handle.tryDiscardHandle()) {
enqueueUnprepareStatementHandle(handle);
}
}
}
}
boolean isAzure() {
if (null == isAzure) {
try (Statement stmt = this.createStatement();
ResultSet rs = stmt.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) {
rs.next();
int engineEdition = rs.getInt(1);
isAzure = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE
|| engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW
|| engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_MI);
isAzureDW = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW);
isAzureMI = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_MI);
} catch (SQLException e) {
if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
loggerExternal.log(Level.FINER, this + ": Error retrieving server type", e);
isAzure = false;
isAzureDW = false;
isAzureMI = false;
}
return isAzure;
} else {
return isAzure;
}
}
boolean isAzureDW() {
isAzure();
return isAzureDW;
}
boolean isAzureMI() {
isAzure();
return isAzureMI;
}
final synchronized void addOpenStatement(ISQLServerStatement st) {
if (null != openStatements) {
openStatements.add(st);
}
}
final synchronized void removeOpenStatement(ISQLServerStatement st) {
if (null != openStatements) {
openStatements.remove(st);
}
}
boolean isAEv2() {
return (aeVersion >= TDS.COLUMNENCRYPTION_VERSION2);
}
private ISQLServerEnclaveProvider enclaveProvider;
ArrayList<byte[]> initEnclaveParameters(String userSql, String preparedTypeDefinitions, Parameter[] params,
ArrayList<String> parameterNames) throws SQLServerException {
if (!this.enclaveEstablished()) {
enclaveProvider.getAttestationParameters(this.enclaveAttestationUrl);
}
return enclaveProvider.createEnclaveSession(this, userSql, preparedTypeDefinitions, params, parameterNames);
}
boolean enclaveEstablished() {
return (null != enclaveProvider.getEnclaveSession());
}
byte[] generateEnclavePackage(String userSQL, ArrayList<byte[]> enclaveCEKs) throws SQLServerException {
return (enclaveCEKs.size() > 0) ? enclaveProvider.getEnclavePackage(userSQL, enclaveCEKs) : null;
}
String getServerName() {
return this.trustedServerNameAE;
}
}
final class SQLServerConnectionSecurityManager {
static final String dllName = SQLServerDriver.AUTH_DLL_NAME + ".dll";
String serverName;
int portNumber;
SQLServerConnectionSecurityManager(String serverName, int portNumber) {
this.serverName = serverName;
this.portNumber = portNumber;
}
public void checkConnect() throws SecurityException {
SecurityManager security = System.getSecurityManager();
if (null != security) {
security.checkConnect(serverName, portNumber);
}
}
public void checkLink() throws SecurityException {
SecurityManager security = System.getSecurityManager();
if (null != security) {
security.checkLink(dllName);
}
}
}