From c21b2f8a5477d2efb2b06ec2f2cc4b2487171110 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Tue, 20 Jun 2023 14:28:11 -0700 Subject: [PATCH 1/2] localSocketAddress implementation --- .../com/microsoft/sqlserver/jdbc/IOBuffer.java | 7 +++++++ .../sqlserver/jdbc/ISQLServerDataSource.java | 14 ++++++++++++++ .../sqlserver/jdbc/SQLServerDataSource.java | 10 ++++++++++ .../microsoft/sqlserver/jdbc/SQLServerDriver.java | 5 ++++- .../sqlserver/jdbc/SQLServerResource.java | 1 + .../sqlserver/jdbc/SQLServerConnectionTest.java | 3 +++ 6 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 9d0548031..885f00336 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -2890,6 +2890,13 @@ private Socket getConnectedSocket(InetSocketAddress addr, int timeoutInMilliSeco if (addr.isUnresolved()) throw new java.net.UnknownHostException(); selectedSocket = getSocketFactory().createSocket(); + + String localSocketAddress = this.conn.activeConnectionProperties.getProperty(SQLServerDriverStringProperty.LOCAL_SOCKET_ADDRESS.toString()); + + if (null != localSocketAddress && !localSocketAddress.isEmpty()) { + selectedSocket.bind(new InetSocketAddress(InetAddress.getByName(localSocketAddress), 0)); + } + if (!selectedSocket.isConnected()) { selectedSocket.connect(addr, timeoutInMilliSeconds); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java index a435666e1..63d4b94b6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java @@ -1283,4 +1283,18 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource { * @param accessTokenCallbackClass */ void setAccessTokenCallbackClass(String accessTokenCallbackClass); + + /** + * Returns the specified address to explicitly to use on the client side for TCP/IP when connecting. + * + * @return localSocketAddress + */ + String getLocalSocketAddress(); + + /** + * Sets the address to explicitly use on the client side for TCP/IP when connecting. + * + * @param localSocketAddress + */ + void setLocalSocketAddress(String localSocketAddress); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index b7e018112..ff50015ba 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -249,6 +249,16 @@ public String getAccessToken() { return getStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), null); } + @Override + public void setLocalSocketAddress(String localSocketAddress) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.LOCAL_SOCKET_ADDRESS.toString(), localSocketAddress); + } + + @Override + public String getLocalSocketAddress() { + return getStringProperty(connectionProps, SQLServerDriverStringProperty.LOCAL_SOCKET_ADDRESS.toString(), null); + } + /** * Sets the Column Encryption setting. If lastUpdateCount is set to true, the driver will return only the last * update count from all the update counts returned by a batch. The default of false will return all update counts. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 1bd92024c..e49909d41 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -610,7 +610,8 @@ enum SQLServerDriverStringProperty { ENCRYPT("encrypt", EncryptOption.TRUE.toString()), SERVER_CERTIFICATE("serverCertificate", ""), DATETIME_DATATYPE("datetimeParameterType", DatetimeType.DATETIME2.toString()), - ACCESS_TOKEN_CALLBACK_CLASS("accessTokenCallbackClass", ""); + ACCESS_TOKEN_CALLBACK_CLASS("accessTokenCallbackClass", ""), + LOCAL_SOCKET_ADDRESS("localSocketAddress", ""); private final String name; private final String defaultValue; @@ -931,6 +932,8 @@ public final class SQLServerDriver implements java.sql.Driver { SQLServerDriverStringProperty.AAD_SECURE_PRINCIPAL_SECRET.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.MAX_RESULT_BUFFER.toString(), SQLServerDriverStringProperty.MAX_RESULT_BUFFER.getDefaultValue(), false, null), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.LOCAL_SOCKET_ADDRESS.toString(), + SQLServerDriverStringProperty.LOCAL_SOCKET_ADDRESS.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CONNECT_RETRY_COUNT.toString(), Integer.toString(SQLServerDriverIntProperty.CONNECT_RETRY_COUNT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CONNECT_RETRY_INTERVAL.toString(), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index a9047e6bc..100e8ea32 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -242,6 +242,7 @@ protected Object[][] getContents() { {"R_AADSecurePrincipalSecretPropertyDescription", "A Secret defined for a registered application which has been granted permission to the database connected."}, {"R_accessTokenCallbackClassPropertyDescription", "The class to instantiate as the SQLServerAccessTokenCallback for acquiring tokens."}, {"R_accessTokenCallbackPropertyDescription", "A SQLServerAccessTokenCallback object which is used to call a callback method to return an access token."}, + {"R_localSocketAddressPropertyDescription", "A specified IPv4, IPv6 or DNS name to explicitly configure on the client side of TCP/IP when connecting."}, {"R_noParserSupport", "An error occurred while instantiating the required parser. Error: \"{0}\""}, {"R_writeOnlyXML", "Cannot read from this SQLXML instance. This instance is for writing data only."}, {"R_dataHasBeenReadXML", "Cannot read from this SQLXML instance. The data has already been read."}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 52b1413c3..b1cf49182 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -170,6 +170,9 @@ public void testDataSource() throws SQLServerException { ds.setTrustStorePassword(stringPropValue); assertEquals(stringPropValue, ds.getTrustStorePassword(), TestResource.getResource("R_valuesAreDifferent")); + ds.setLocalSocketAddress(stringPropValue); + assertEquals(stringPropValue, ds.getLocalSocketAddress(), TestResource.getResource("R_valuesAreDifferent")); + // verify encrypt=true options ds.setEncrypt(EncryptOption.MANDATORY.toString()); assertEquals("True", EncryptOption.valueOfString(ds.getEncrypt()).toString(), From 3d0fee2027ea4d0a6c332a3b6f3eaaf11abd4433 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Tue, 20 Jun 2023 15:03:26 -0700 Subject: [PATCH 2/2] Added logging --- src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 885f00336..f46432a91 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -2895,6 +2895,10 @@ private Socket getConnectedSocket(InetSocketAddress addr, int timeoutInMilliSeco if (null != localSocketAddress && !localSocketAddress.isEmpty()) { selectedSocket.bind(new InetSocketAddress(InetAddress.getByName(localSocketAddress), 0)); + + if (logger.isLoggable(Level.FINER)) { + logger.finer(this.toString() + " binding socket to: " + localSocketAddress); + } } if (!selectedSocket.isConnected()) {