@@ -313,6 +313,72 @@ public String toString() {
313313     **/ 
314314    private  static  final  Lock  sLock  = new  ReentrantLock ();
315315
316+     static  final  String  USER_AGENT_TEMPLATE  = "{\" driver\" :\" %s\" ,\" version\" :\" %s\" ,\" os\" :{\" type\" :\" %s\" ,\" details\" :\" %s\" },\" arch\" :\" %s\" ,\" runtime\" :\" %s\" }" ;
317+     static  final  String  userAgentStr ;
318+ 
319+     static  {
320+         userAgentStr  = getUserAgent ();
321+     }
322+ 
323+     static  String  getUserAgent () {
324+         try  {
325+             return  String .format (
326+                     USER_AGENT_TEMPLATE ,
327+                     "MS-JDBC" ,
328+                     getJDBCVersion (),
329+                     getOSType (),
330+                     getOSDetails (),
331+                     getArchitecture (),
332+                     getRuntimeDetails ()
333+                     );
334+         } catch (Exception  e ) {
335+             return  "{\" driver\" :\" MS-JDBC\" }" ;
336+         }
337+     }
338+ 
339+     static  String  getJDBCVersion () {
340+         return  sanitizeField (SQLJdbcVersion .MAJOR  + "."  + SQLJdbcVersion .MINOR  + "."  + SQLJdbcVersion .PATCH  + "."  + SQLJdbcVersion .BUILD  + SQLJdbcVersion .RELEASE_EXT , 16 );
341+     }
342+ 
343+     static  String  getOSType () {
344+         String  osName  = System .getProperty ("os.name" , "Unknown" ).trim ();
345+         String  osNameToReturn  = "Unknown" ;
346+         if  (osName .startsWith ("Windows" )) {
347+             osNameToReturn  = "Windows" ;
348+         } else  if  (osName .startsWith ("Linux" )) {
349+             osNameToReturn  = "Linux" ;
350+         } else  if  (osName .startsWith ("Mac" )) { 
351+             osNameToReturn  = "macOS" ;
352+         } else  if  (osName .startsWith ("FreeBSD" )) {
353+             osNameToReturn  = "FreeBSD" ;
354+         } else  if  (osName .startsWith ("Android" )) {
355+             osNameToReturn  = "Android" ;
356+         }
357+         return  sanitizeField (osNameToReturn , 16 );
358+     }
359+ 
360+     static  String  getArchitecture () {
361+         return  sanitizeField (System .getProperty ("os.arch" , "Unknown" ).trim (), 16 );
362+     }
363+ 
364+     static  String  getOSDetails () {
365+         String  osName  = System .getProperty ("os.name" , "" ).trim ();
366+         String  osVersion  = System .getProperty ("os.version" , "" ).trim ();
367+         if  (osName .isEmpty () && osVersion .isEmpty ()) return  "Unknown" ;
368+         return  sanitizeField (osName  + " "  + osVersion , 128 );
369+     }
370+ 
371+     static  String  getRuntimeDetails () {
372+         String  javaVmName  = System .getProperty ("java.vm.name" , "" ).trim ();
373+         String  javaVmVersion  = System .getProperty ("java.vm.version" , "" ).trim ();
374+         if  (javaVmName .isEmpty () && javaVmVersion .isEmpty ()) return  "Unknown" ;
375+         return  sanitizeField (javaVmName  + " "  + javaVmVersion , 128 );
376+     }
377+ 
378+     static  String  sanitizeField (String  field , int  maxLength ) {
379+         return  (field  == null  || field .isEmpty ()) ? "Unknown"  : field .substring (0 , Math .min (field .length (), maxLength ));
380+     }
381+ 
316382    /** 
317383     * Generate a 6 byte random array for netAddress 
318384     * As per TDS spec this is a unique clientID (MAC address) used to identify the client. 
@@ -5685,6 +5751,28 @@ int writeDNSCacheFeatureRequest(boolean write, /* if false just calculates the l
56855751        return  len ;
56865752    }
56875753
5754+     /** 
5755+      * Writes the user agent telemetry feature request  
5756+      * @param write 
5757+      * If true, writes the feature request to the physical state object. 
5758+      * @param tdsWriter 
5759+      * @return 
5760+      * The length of the feature request in bytes, or 0 if vectorTypeSupport is "off". 
5761+      * @throws SQLServerException 
5762+      */ 
5763+     int  writeUserAgentFeatureRequest (boolean  write , /* if false just calculates the length */ 
5764+             TDSWriter  tdsWriter ) throws  SQLServerException  {
5765+         byte [] userAgentToSendBytes  = toUCS16 (userAgentStr );
5766+         int  len  = userAgentToSendBytes .length  + 6 ; // 1byte = featureID, 1byte = version, 4byte = feature data length in bytes, remaining bytes: feature data 
5767+         if  (write ) {
5768+             tdsWriter .writeByte (TDS .TDS_FEATURE_EXT_USERAGENT );
5769+             tdsWriter .writeInt (userAgentToSendBytes .length  + 1 );
5770+             tdsWriter .writeByte (TDS .MAX_USERAGENT_VERSION );
5771+             tdsWriter .writeBytes (userAgentToSendBytes );
5772+          }
5773+ 	     return  len ;
5774+     }
5775+     
56885776    /** 
56895777     * Writes the Vector Support feature request to the physical state object, 
56905778     * unless vectorTypeSupport is "off". The request includes the feature ID, 
@@ -6903,6 +6991,14 @@ private void onFeatureExtAck(byte featureId, byte[] data) throws SQLServerExcept
69036991                break ;
69046992            }
69056993
6994+             case  TDS .TDS_FEATURE_EXT_USERAGENT : {
6995+                 if  (connectionlogger .isLoggable (Level .FINER )) {
6996+                     connectionlogger .fine (
6997+                             toString () + " Received feature extension acknowledgement for User agent feature extension. Received byte: "  + data [0 ]);
6998+                 }
6999+                 break ;
7000+             }
7001+ 
69067002            default : {
69077003                // Unknown feature ack 
69087004                throw  new  SQLServerException (SQLServerException .getErrString ("R_UnknownFeatureAck" ), null );
@@ -7190,6 +7286,9 @@ final boolean complete(LogonCommand logonCommand, TDSReader tdsReader) throws SQ
71907286        }
71917287
71927288        int  aeOffset  = len ;
7289+ 
7290+         len  += writeUserAgentFeatureRequest (false , tdsWriter );
7291+ 
71937292        // AE is always ON 
71947293        len  += writeAEFeatureRequest (false , tdsWriter );
71957294        if  (federatedAuthenticationInfoRequested  || federatedAuthenticationRequested ) {
@@ -7394,6 +7493,8 @@ final boolean complete(LogonCommand logonCommand, TDSReader tdsReader) throws SQ
73947493            tdsWriter .writeBytes (secBlob , 0 , secBlob .length );
73957494        }
73967495
7496+         writeUserAgentFeatureRequest (true , tdsWriter );
7497+ 
73977498        // AE is always ON 
73987499        writeAEFeatureRequest (true , tdsWriter );
73997500
0 commit comments