2
* Created on Dec 20, 2004
3
* Created by Alon Rohter
4
* Copyright (C) 2004-2005 Aelitis, All Rights Reserved.
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
* AELITIS, SAS au capital de 46,603.30 euros
19
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
23
package com.aelitis.azureus.core.versioncheck;
26
import java.io.BufferedInputStream;
27
import java.io.ByteArrayInputStream;
28
import java.io.ByteArrayOutputStream;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.lang.reflect.InvocationTargetException;
33
import java.net.HttpURLConnection;
34
import java.net.InetAddress;
35
import java.net.InetSocketAddress;
36
import java.net.Socket;
38
import java.net.URLEncoder;
41
import org.gudy.azureus2.core3.config.COConfigurationManager;
42
import org.gudy.azureus2.core3.internat.MessageText;
43
import org.gudy.azureus2.core3.logging.*;
44
import org.gudy.azureus2.core3.stats.transfer.*;
45
import org.gudy.azureus2.core3.util.*;
47
import org.gudy.azureus2.plugins.PluginInterface;
49
import com.aelitis.azureus.core.AzureusCoreFactory;
50
import com.aelitis.azureus.core.clientmessageservice.*;
51
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin;
52
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminASNLookup;
53
import com.aelitis.net.udp.uc.PRUDPPacketHandler;
54
import com.aelitis.net.udp.uc.PRUDPPacketHandlerFactory;
55
import com.aelitis.net.udp.uc.PRUDPReleasablePacketHandler;
60
* Client for checking version information from a remote server.
62
public class VersionCheckClient {
63
private static final LogIDs LOGID = LogIDs.CORE;
65
public static final String REASON_UPDATE_CHECK_START = "us";
66
public static final String REASON_UPDATE_CHECK_PERIODIC = "up";
67
public static final String REASON_CHECK_SWT = "sw";
68
public static final String REASON_DHT_EXTENDED_ALLOWED = "dx";
69
public static final String REASON_DHT_ENABLE_ALLOWED = "de";
70
public static final String REASON_EXTERNAL_IP = "ip";
71
public static final String REASON_RECOMMENDED_PLUGINS = "rp";
72
public static final String REASON_SECONDARY_CHECK = "sc";
75
private static final String AZ_MSG_SERVER_ADDRESS = "version.aelitis.com";
76
private static final int AZ_MSG_SERVER_PORT = 27001;
77
private static final String MESSAGE_TYPE_ID = "AZVER";
79
public static final String HTTP_SERVER_ADDRESS = "version.aelitis.com";
80
public static final int HTTP_SERVER_PORT = 2080;
82
public static final String TCP_SERVER_ADDRESS = "version.aelitis.com";
83
public static final int TCP_SERVER_PORT = 2080;
85
public static final String UDP_SERVER_ADDRESS = "version.aelitis.com";
86
public static final int UDP_SERVER_PORT = 2080;
89
private static final long CACHE_PERIOD = 5*60*1000;
90
private static boolean secondary_check_done;
94
VersionCheckClientUDPCodecs.registerCodecs();
97
private static final VersionCheckClient instance = new VersionCheckClient();
98
private Map last_check_data = null;
99
private final AEMonitor check_mon = new AEMonitor( "versioncheckclient" );
100
private long last_check_time = 0;
103
private VersionCheckClient() {
110
* Get the singleton instance of the version check client.
111
* @return version check client
113
public static VersionCheckClient getSingleton() { return instance; }
118
* Get the version check reply info.
119
* @return reply data, possibly cached, if the server was already checked within the last minute
121
public Map getVersionCheckInfo( String reason ) {
122
return( getVersionCheckInfoSupport( reason, false, false ));
127
getVersionCheckInfoSupport(
129
boolean only_if_cached,
132
try { check_mon.enter();
134
long time_diff = SystemTime.getCurrentTime() - last_check_time;
136
force = force || time_diff > CACHE_PERIOD || time_diff < 0;
138
if( last_check_data == null || last_check_data.size() == 0 || force ) {
139
// if we've never checked before then we go ahead even if the "only_if_cached"
140
// flag is set as its had not chance of being cached yet!
141
if ( only_if_cached && last_check_data != null ){
142
return( new HashMap() );
145
last_check_data = performVersionCheck( constructVersionCheckMessage( reason ), true, true );
147
catch( Throwable t ) {
149
last_check_data = new HashMap();
153
Logger.log(new LogEvent(LOGID, "VersionCheckClient is using "
154
+ "cached version check info. Using " + last_check_data.size()
158
finally { check_mon.exit(); }
160
if( last_check_data == null ) last_check_data = new HashMap();
162
return last_check_data;
166
isVersionCheckDataValid()
168
return( last_check_data != null && last_check_data.size() > 0 );
172
* Get the ip address seen by the version check server.
173
* NOTE: This information may be cached, see getVersionCheckInfo().
174
* @return external ip address, or empty string if no address information found
177
getExternalIpAddress(
178
boolean only_if_cached )
180
Map reply = getVersionCheckInfoSupport( REASON_EXTERNAL_IP, only_if_cached, false );
182
byte[] address = (byte[])reply.get( "source_ip_address" );
183
if( address != null ) {
184
return new String( address );
192
* Is the DHT plugin allowed to be enabled.
193
* @return true if DHT can be enabled, false if it should not be enabled
195
public boolean DHTEnableAllowed() {
196
Map reply = getVersionCheckInfo( REASON_DHT_ENABLE_ALLOWED );
200
byte[] value = (byte[])reply.get( "enable_dht" );
202
if( value != null ) {
204
res = new String( value ).equalsIgnoreCase( "true" );
207
// we take the view that if the version check failed then we go ahead
208
// and enable the DHT (i.e. we're being optimistic)
211
res = !isVersionCheckDataValid();
219
* Is the DHT allowed to be used by external plugins.
220
* @return true if extended DHT use is allowed, false if not allowed
222
public boolean DHTExtendedUseAllowed() {
223
Map reply = getVersionCheckInfo( REASON_DHT_EXTENDED_ALLOWED );
227
byte[] value = (byte[])reply.get( "enable_dht_extended_use" );
228
if( value != null ) {
229
res = new String( value ).equalsIgnoreCase( "true" );
232
// be generous and enable extended use if check failed
235
res = !isVersionCheckDataValid();
242
getRecommendedPlugins()
244
Map reply = getVersionCheckInfo( REASON_RECOMMENDED_PLUGINS );
246
List l = (List)reply.get( "recommended_plugins" );
250
return( new String[0] );
253
String[] res = new String[l.size()];
255
for (int i=0;i<l.size();i++){
257
res[i] = new String((byte[])l.get(i));
264
* Perform the actual version check by connecting to the version server.
265
* @param data_to_send version message
266
* @return version reply
267
* @throws Exception if the server check connection fails
272
boolean use_az_message,
277
Exception error = null;
280
if ( use_az_message ){
283
reply = executeAZMessage( data_to_send );
285
reply.put( "protocol_used", "AZMSG" );
287
}catch( Exception e ){
293
if ( reply == null && use_http ){
296
reply = executeHTTP( data_to_send );
298
reply.put( "protocol_used", "HTTP" );
302
}catch( Exception e ){
308
if ( error != null ){
313
if (Logger.isEnabled())
314
Logger.log(new LogEvent(LOGID, "VersionCheckClient server "
315
+ "version check successful. Received " + reply.size()
318
last_check_time = SystemTime.getCurrentTime();
329
if (Logger.isEnabled())
330
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
331
+ "version information from " + AZ_MSG_SERVER_ADDRESS + ":" + AZ_MSG_SERVER_PORT));
333
ClientMessageService msg_service = null;
337
msg_service = ClientMessageServiceClient.getServerService( AZ_MSG_SERVER_ADDRESS, AZ_MSG_SERVER_PORT, MESSAGE_TYPE_ID );
339
msg_service.sendMessage( data_to_send ); //send our version message
341
reply = msg_service.receiveMessage(); //get the server reply
343
preProcessReply( reply );
347
if ( msg_service != null ){
362
if (Logger.isEnabled())
363
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
364
+ "version information from " + HTTP_SERVER_ADDRESS + ":" + HTTP_SERVER_PORT + " via HTTP" ));
366
String url_str = "http://" + HTTP_SERVER_ADDRESS + (HTTP_SERVER_PORT==80?"":(":" + HTTP_SERVER_PORT)) + "/version?";
368
url_str += URLEncoder.encode( new String( BEncoder.encode( data_to_send ), "ISO-8859-1" ), "ISO-8859-1" );
370
URL url = new URL( url_str );
372
HttpURLConnection url_connection = (HttpURLConnection)url.openConnection();
374
url_connection.connect();
377
InputStream is = url_connection.getInputStream();
379
Map reply = BDecoder.decode( new BufferedInputStream( is ));
381
preProcessReply( reply );
387
url_connection.disconnect();
395
return( getHTTPGetString( new HashMap(), for_proxy));
403
String get_str = "GET " + (for_proxy?("http://" + HTTP_SERVER_ADDRESS + ":" + HTTP_SERVER_PORT ):"") +"/version?";
406
get_str += URLEncoder.encode( new String( BEncoder.encode( content ), "ISO-8859-1" ), "ISO-8859-1" );
408
}catch( Throwable e ){
411
get_str +=" HTTP/1.1" + "\015\012" + "\015\012";
424
if (Logger.isEnabled())
425
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
426
+ "version information from " + TCP_SERVER_ADDRESS + ":" + TCP_SERVER_PORT + " via TCP" ));
428
String get_str = getHTTPGetString( data_to_send, false );
430
Socket socket = null;
433
socket = new Socket();
435
if ( bind_ip != null ){
437
socket.bind( new InetSocketAddress( bind_ip, bind_port ));
439
}else if ( bind_port != 0 ){
441
socket.bind( new InetSocketAddress( bind_port ));
444
socket.setSoTimeout( 10000 );
446
socket.connect( new InetSocketAddress( TCP_SERVER_ADDRESS, TCP_SERVER_PORT ), 10000 );
448
OutputStream os = socket.getOutputStream();
450
os.write( get_str.getBytes( "ISO-8859-1" ));
454
InputStream is = socket.getInputStream();
456
ByteArrayOutputStream baos = new ByteArrayOutputStream();
458
byte[] buffer = new byte[1024];
464
int len = is.read( buffer );
473
if ( total_len > 16000 ){
475
throw( new IOException( "reply too large" ));
478
baos.write( buffer, 0, len );
481
byte[] reply_bytes = baos.toByteArray();
483
for (int i=3;i<reply_bytes.length;i++){
485
if ( reply_bytes[i-3]== (byte)'\015' &&
486
reply_bytes[i-2]== (byte)'\012' &&
487
reply_bytes[i-1]== (byte)'\015' &&
488
reply_bytes[i-0]== (byte)'\012' ){
490
Map reply = BDecoder.decode( new BufferedInputStream( new ByteArrayInputStream( reply_bytes, i+1, reply_bytes.length - (i+1 ))));
492
preProcessReply( reply );
498
throw( new Exception( "Invalid reply: " + new String( reply_bytes )));
502
if ( socket != null ){
507
}catch( Throwable e ){
522
PRUDPReleasablePacketHandler handler = PRUDPPacketHandlerFactory.getReleasableHandler( bind_port );
524
PRUDPPacketHandler packet_handler = handler.getHandler();
528
Random random = new Random();
531
Exception last_error = null;
533
packet_handler.setExplicitBindAddress( bind_ip );
535
for (int i=0;i<3;i++){
538
// connection ids for requests must always have their msb set...
539
// apart from the original darn udp tracker spec....
541
long connection_id = 0x8000000000000000L | random.nextLong();
543
VersionCheckClientUDPRequest request_packet = new VersionCheckClientUDPRequest( connection_id );
545
request_packet.setPayload( data_to_send );
547
VersionCheckClientUDPReply reply_packet = (VersionCheckClientUDPReply)packet_handler.sendAndReceive( null, request_packet, new InetSocketAddress( UDP_SERVER_ADDRESS, UDP_SERVER_PORT ), timeout );
549
Map reply = reply_packet.getPayload();
551
preProcessReply( reply );
555
}catch( Exception e){
559
timeout = timeout * 2;
563
if ( last_error != null ){
568
throw( new Exception( "Timeout" ));
572
packet_handler.setExplicitBindAddress( null );
582
// two cases where we automatically attempt to resolve the ASN (to minimise load on ASN
585
// 2) where we have an asn and public IP has changed outside of prefix range
587
final long ASN_MIN_CHECK = 7*24*60*60*1000L;
589
boolean check_asn = false;
591
long now = SystemTime.getCurrentTime();
593
long asn_check_time = COConfigurationManager.getLongParameter( "ASN Autocheck Performed Time" );
595
if ( now < asn_check_time || now - asn_check_time > ASN_MIN_CHECK ){
597
String bgp_prefix = COConfigurationManager.getStringParameter( "ASN BGP", null );
598
String asn = COConfigurationManager.getStringParameter( "ASN ASN", null );
600
if ( asn == null || asn.length() == 0 ||
601
bgp_prefix == null || bgp_prefix.length() == 0 ){
603
// during 2502 introduction we ran DNS based queries as fallback without support
604
// for reading ASN - pick up blank ASNs now and force recheck
611
byte[] address = (byte[])reply.get( "source_ip_address" );
613
InetAddress ip = InetAddress.getByName( new String( address ));
615
// if we've got a prefix only recheck if outside existing range
617
if ( !NetworkAdmin.getSingleton().matchesCIDR( bgp_prefix, ip )){
622
}catch( Throwable e ){
626
Debug.printStackTrace(e);
633
COConfigurationManager.setParameter( "ASN Autocheck Performed Time", now );
636
byte[] address = (byte[])reply.get( "source_ip_address" );
638
InetAddress ip = InetAddress.getByName( new String( address ));
640
NetworkAdminASNLookup asn = NetworkAdmin.getSingleton().lookupASN( ip );
642
COConfigurationManager.setParameter( "ASN AS", asn.getAS());
643
COConfigurationManager.setParameter( "ASN ASN", asn.getASName());
644
COConfigurationManager.setParameter( "ASN BGP", asn.getBGPPrefix());
646
// kick off a secondary version check to communicate the new information
648
if ( !secondary_check_done ){
650
secondary_check_done = true;
652
new AEThread( "Secondary version check", true )
657
getVersionCheckInfoSupport( REASON_SECONDARY_CHECK, false, true );
661
}catch( Throwable e ){
663
Debug.printStackTrace(e);
667
Long as_advice = (Long)reply.get( "as_advice" );
669
if ( as_advice != null ){
671
String asn = COConfigurationManager.getStringParameter( "ASN ASN", null );
675
long advice = as_advice.longValue();
681
String done_asn = COConfigurationManager.getStringParameter( "ASN Advice Followed", "" );
683
if ( !done_asn.equals( asn )){
685
COConfigurationManager.setParameter( "ASN Advice Followed", asn );
687
boolean change = advice == 1 || advice == 2;
688
boolean alert = advice == 1 || advice == 3;
690
if ( !COConfigurationManager.getBooleanParameter( "network.transport.encrypted.require" )){
694
COConfigurationManager.setParameter( "network.transport.encrypted.require", true );
700
MessageText.getString(
701
"crypto.alert.as.warning",
702
new String[]{ asn });
704
Logger.log( new LogAlert( false, LogAlert.AT_WARNING, msg ));
714
getExternalIpAddressHTTP()
718
Map reply = executeHTTP( new HashMap());
720
byte[] address = (byte[])reply.get( "source_ip_address" );
722
return( InetAddress.getByName( new String( address )));
726
getExternalIpAddressTCP(
732
Map reply = executeTCP( new HashMap(), bind_ip, bind_port );
734
byte[] address = (byte[])reply.get( "source_ip_address" );
736
return( InetAddress.getByName( new String( address )));
740
getExternalIpAddressUDP(
746
Map reply = executeUDP( new HashMap(), bind_ip, bind_port );
748
byte[] address = (byte[])reply.get( "source_ip_address" );
750
return( InetAddress.getByName( new String( address )));
754
* Construct the default version check message.
755
* @return message to send
757
private Map constructVersionCheckMessage( String reason ) {
758
Map message = new HashMap();
760
message.put( "appid", SystemProperties.getApplicationIdentifier());
761
message.put( "version", Constants.AZUREUS_VERSION );
763
String id = COConfigurationManager.getStringParameter( "ID", null );
764
boolean send_info = COConfigurationManager.getBooleanParameter( "Send Version Info" );
766
int last_send_time = COConfigurationManager.getIntParameter( "Send Version Info Last Time", -1 );
768
int current_send_time = (int)(SystemTime.getCurrentTime()/1000);
770
COConfigurationManager.setParameter( "Send Version Info Last Time", current_send_time );
772
if( id != null && send_info ) {
774
message.put( "id", id );
775
message.put( "os", Constants.OSName );
777
message.put( "os_version", System.getProperty( "os.version" ) );
778
message.put( "os_arch", System.getProperty( "os.arch" ) ); //see http://lopica.sourceforge.net/os.html
780
if ( last_send_time != -1 && last_send_time < current_send_time ){
784
message.put( "tsl", new Long(current_send_time-last_send_time));
787
message.put( "reason", reason );
789
String java_version = System.getProperty( "java.version" );
790
if ( java_version == null ){ java_version = "unknown"; }
791
message.put( "java", java_version );
794
String java_vendor = System.getProperty( "java.vm.vendor" );
795
if ( java_vendor == null ){ java_vendor = "unknown"; }
796
message.put( "javavendor", java_vendor );
799
long max_mem = Runtime.getRuntime().maxMemory()/(1024*1024);
800
message.put( "javamx", new Long( max_mem ) );
802
OverallStats stats = StatsFactory.getStats();
804
if ( stats != null ){
806
//long total_bytes_downloaded = stats.getDownloadedBytes();
807
//long total_bytes_uploaded = stats.getUploadedBytes();
808
long total_uptime = stats.getTotalUpTime();
810
//removed due to complaints about anonymous stats collection
811
//message.put( "total_bytes_downloaded", new Long( total_bytes_downloaded ) );
812
//message.put( "total_bytes_uploaded", new Long( total_bytes_uploaded ) );
813
message.put( "total_uptime", new Long( total_uptime ) );
814
//message.put( "dlstats", stats.getDownloadStats());
817
String as = COConfigurationManager.getStringParameter( "ASN AS", null );
821
// there was borkage with DNS based queries that left leading " on AS
823
if ( as.startsWith( "\"" )){
825
as = as.substring( 1 ).trim();
827
COConfigurationManager.setParameter( "ASN AS", as );
830
message.put( "ip_as", as );
833
String asn = COConfigurationManager.getStringParameter( "ASN ASN", null );
837
if ( asn.length() > 64 ){
839
asn = asn.substring( 0, 64 );
842
message.put( "ip_asn", asn );
845
String ui = COConfigurationManager.getStringParameter("ui");
846
if (ui.length() > 0) {
847
message.put("ui", ui);
850
if ( AzureusCoreFactory.isCoreAvailable()){
852
//installed plugin IDs
853
PluginInterface[] plugins = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaces();
854
List pids = new ArrayList();
855
for (int i=0;i<plugins.length;i++){
856
String pid = plugins[i].getPluginID();
858
String info = (String)plugins[i].getPluginconfig().getPluginStringParameter( "plugin.info" );
860
// filter out built-in and core ones
861
if ( ( info != null && info.length() > 0 ) ||
862
( !pid.startsWith( "<" ) &&
863
!pid.startsWith( "azbp" ) &&
864
!pid.startsWith( "azupdater" ) &&
865
!pid.startsWith( "azplatform" ) &&
866
!pids.contains( pid ))){
868
if ( info != null && info.length() > 0 ){
870
if( info.length() < 256 ){
876
Debug.out( "Plugin '" + pid + "' reported excessive info string '" + info + "'" );
883
message.put( "plugins", pids );
890
Class c = Class.forName( "org.eclipse.swt.SWT" );
892
String swt_platform = (String)c.getMethod( "getPlatform", new Class[]{} ).invoke( null, new Object[]{} );
893
message.put( "swt_platform", swt_platform );
896
Integer swt_version = (Integer)c.getMethod( "getVersion", new Class[]{} ).invoke( null, new Object[]{} );
897
message.put( "swt_version", new Long( swt_version.longValue() ) );
899
c = Class.forName("org.gudy.azureus2.ui.swt.mainwindow.MainWindow");
901
c.getMethod("addToVersionCheckMessage", new Class[] { Map.class }).invoke(
902
null, new Object[] { message });
906
catch( ClassNotFoundException e ) { /* ignore */ }
907
catch( NoClassDefFoundError er ) { /* ignore */ }
908
catch( InvocationTargetException err ) { /* ignore */ }
909
catch( Throwable t ) { t.printStackTrace(); }
912
boolean using_phe = COConfigurationManager.getBooleanParameter( "network.transport.encrypted.require" );
913
message.put( "using_phe", using_phe ? new Long(1) : new Long(0) );
923
COConfigurationManager.initialise();
925
System.out.println( "UDP: " + getSingleton().getExternalIpAddressUDP(null,0));
926
System.out.println( "TCP: " + getSingleton().getExternalIpAddressTCP(null,0));
927
System.out.println( "HTTP: " + getSingleton().getExternalIpAddressHTTP());
929
}catch( Throwable e){
2
* Created on Dec 20, 2004
3
* Created by Alon Rohter
4
* Copyright (C) 2004-2005 Aelitis, All Rights Reserved.
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License
8
* as published by the Free Software Foundation; either version 2
9
* of the License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
* AELITIS, SAS au capital de 46,603.30 euros
19
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
23
package com.aelitis.azureus.core.versioncheck;
26
import java.io.BufferedInputStream;
27
import java.io.ByteArrayInputStream;
28
import java.io.ByteArrayOutputStream;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.OutputStream;
32
import java.lang.reflect.InvocationTargetException;
36
import org.gudy.azureus2.core3.config.COConfigurationManager;
37
import org.gudy.azureus2.core3.config.ParameterListener;
38
import org.gudy.azureus2.core3.internat.MessageText;
39
import org.gudy.azureus2.core3.logging.*;
40
import org.gudy.azureus2.core3.stats.transfer.*;
41
import org.gudy.azureus2.core3.util.*;
43
import org.gudy.azureus2.plugins.PluginInterface;
45
import com.aelitis.azureus.core.AzureusCoreFactory;
46
import com.aelitis.azureus.core.clientmessageservice.*;
47
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin;
48
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminASN;
49
import com.aelitis.net.udp.uc.PRUDPPacketHandler;
50
import com.aelitis.net.udp.uc.PRUDPPacketHandlerFactory;
51
import com.aelitis.net.udp.uc.PRUDPReleasablePacketHandler;
56
* Client for checking version information from a remote server.
58
public class VersionCheckClient {
59
private static final LogIDs LOGID = LogIDs.CORE;
61
public static final String REASON_UPDATE_CHECK_START = "us";
62
public static final String REASON_UPDATE_CHECK_PERIODIC = "up";
63
public static final String REASON_CHECK_SWT = "sw";
64
public static final String REASON_DHT_EXTENDED_ALLOWED = "dx";
65
public static final String REASON_DHT_ENABLE_ALLOWED = "de";
66
public static final String REASON_EXTERNAL_IP = "ip";
67
public static final String REASON_RECOMMENDED_PLUGINS = "rp";
68
public static final String REASON_SECONDARY_CHECK = "sc";
71
private static final String AZ_MSG_SERVER_ADDRESS_V4 = Constants.VERSION_SERVER_V4;
72
private static final int AZ_MSG_SERVER_PORT = 27001;
73
private static final String MESSAGE_TYPE_ID = "AZVER";
75
public static final String HTTP_SERVER_ADDRESS_V4 = AZ_MSG_SERVER_ADDRESS_V4;
76
public static final int HTTP_SERVER_PORT = 80;
78
public static final String TCP_SERVER_ADDRESS_V4 = AZ_MSG_SERVER_ADDRESS_V4;
79
public static final int TCP_SERVER_PORT = 80;
81
public static final String UDP_SERVER_ADDRESS_V4 = AZ_MSG_SERVER_ADDRESS_V4;
82
public static final int UDP_SERVER_PORT = 2080;
84
public static final String AZ_MSG_SERVER_ADDRESS_V6 = Constants.VERSION_SERVER_V6;
85
public static final String HTTP_SERVER_ADDRESS_V6 = AZ_MSG_SERVER_ADDRESS_V6;
86
public static final String TCP_SERVER_ADDRESS_V6 = AZ_MSG_SERVER_ADDRESS_V6;
87
public static final String UDP_SERVER_ADDRESS_V6 = AZ_MSG_SERVER_ADDRESS_V6;
90
private static final long CACHE_PERIOD = 5*60*1000;
91
private static boolean secondary_check_done;
95
VersionCheckClientUDPCodecs.registerCodecs();
98
private static boolean prefer_v6;
102
COConfigurationManager.addAndFireParameterListener(
103
"IPV6 Prefer Addresses",
104
new ParameterListener()
110
prefer_v6 = COConfigurationManager.getBooleanParameter( name );
115
private static final int AT_V4 = 1;
116
private static final int AT_V6 = 2;
117
private static final int AT_EITHER = 3;
119
private static final VersionCheckClient instance = new VersionCheckClient();
121
private Map last_check_data_v4 = null;
122
private Map last_check_data_v6 = null;
124
private final AEMonitor check_mon = new AEMonitor( "versioncheckclient" );
126
private long last_check_time_v4 = 0;
127
private long last_check_time_v6 = 0;
130
private VersionCheckClient() {
137
* Get the singleton instance of the version check client.
138
* @return version check client
140
public static VersionCheckClient getSingleton() { return instance; }
145
* Get the version check reply info.
146
* @return reply data, possibly cached, if the server was already checked within the last minute
153
return( getVersionCheckInfo( reason, AT_EITHER ));
161
if ( address_type == AT_V4 ){
163
return( getVersionCheckInfoSupport( reason, false, false, false ));
165
}else if ( address_type == AT_V6 ){
167
return( getVersionCheckInfoSupport( reason, false, false, true ));
171
Map reply = getVersionCheckInfoSupport( reason, false, false, prefer_v6 );
173
if ( reply == null || reply.size() == 0 ){
175
reply = getVersionCheckInfoSupport( reason, false, false, !prefer_v6 );
184
getVersionCheckInfoSupport(
186
boolean only_if_cached,
192
try { check_mon.enter();
194
long time_diff = SystemTime.getCurrentTime() - last_check_time_v6;
196
force = force || time_diff > CACHE_PERIOD || time_diff < 0;
198
if( last_check_data_v6 == null || last_check_data_v6.size() == 0 || force ) {
199
// if we've never checked before then we go ahead even if the "only_if_cached"
200
// flag is set as its had not chance of being cached yet!
201
if ( only_if_cached && last_check_data_v6 != null ){
202
return( new HashMap() );
205
last_check_data_v6 = performVersionCheck( constructVersionCheckMessage( reason ), true, true, true );
207
catch( UnknownHostException t ) {
209
Debug.out(t.getClass().getName() + ": " + t.getMessage());
211
catch( Throwable t ) {
213
last_check_data_v6 = new HashMap();
217
Logger.log(new LogEvent(LOGID, "VersionCheckClient is using "
218
+ "cached version check info. Using " + last_check_data_v6.size()
222
finally { check_mon.exit(); }
224
if( last_check_data_v6 == null ) last_check_data_v6 = new HashMap();
226
return last_check_data_v6;
230
try { check_mon.enter();
232
long time_diff = SystemTime.getCurrentTime() - last_check_time_v4;
234
force = force || time_diff > CACHE_PERIOD || time_diff < 0;
236
if( last_check_data_v4 == null || last_check_data_v4.size() == 0 || force ) {
237
// if we've never checked before then we go ahead even if the "only_if_cached"
238
// flag is set as its had not chance of being cached yet!
239
if ( only_if_cached && last_check_data_v4 != null ){
240
return( new HashMap() );
243
last_check_data_v4 = performVersionCheck( constructVersionCheckMessage( reason ), true, true, false );
245
catch( UnknownHostException t ) {
247
Debug.outNoStack("VersionCheckClient - " + t.getClass().getName() + ": " + t.getMessage());
249
catch (IOException t) {
250
// General connection problem.
251
Debug.outNoStack("VersionCheckClient - " + t.getClass().getName() + ": " + t.getMessage());
253
catch( Throwable t ) {
255
last_check_data_v4 = new HashMap();
259
if (Logger.isEnabled())
260
Logger.log(new LogEvent(LOGID, "VersionCheckClient is using "
261
+ "cached version check info. Using " + last_check_data_v4.size()
265
finally { check_mon.exit(); }
267
if( last_check_data_v4 == null ) last_check_data_v4 = new HashMap();
269
return last_check_data_v4;
274
isVersionCheckDataValid(
277
boolean v6_ok = last_check_data_v6 != null && last_check_data_v6.size() > 0;
278
boolean v4_ok = last_check_data_v4 != null && last_check_data_v4.size() > 0;
280
if ( address_type == AT_V4 ){
284
}else if ( address_type == AT_V6 ){
290
return( v4_ok | v6_ok );
298
return( v6?last_check_time_v6:last_check_time_v4);
302
* Get the ip address seen by the version check server.
303
* NOTE: This information may be cached, see getVersionCheckInfo().
304
* @return external ip address, or empty string if no address information found
308
getExternalIpAddress(
309
boolean only_if_cached,
312
Map reply = getVersionCheckInfoSupport( REASON_EXTERNAL_IP, only_if_cached, false, v6 );
314
byte[] address = (byte[])reply.get( "source_ip_address" );
315
if( address != null ) {
316
return new String( address );
324
* Is the DHT plugin allowed to be enabled.
325
* @return true if DHT can be enabled, false if it should not be enabled
327
public boolean DHTEnableAllowed() {
328
Map reply = getVersionCheckInfo( REASON_DHT_ENABLE_ALLOWED, AT_EITHER );
332
byte[] value = (byte[])reply.get( "enable_dht" );
334
if( value != null ) {
336
res = new String( value ).equalsIgnoreCase( "true" );
339
// we take the view that if the version check failed then we go ahead
340
// and enable the DHT (i.e. we're being optimistic)
343
res = !isVersionCheckDataValid( AT_EITHER );
351
* Is the DHT allowed to be used by external plugins.
352
* @return true if extended DHT use is allowed, false if not allowed
354
public boolean DHTExtendedUseAllowed() {
355
Map reply = getVersionCheckInfo( REASON_DHT_EXTENDED_ALLOWED, AT_EITHER );
359
byte[] value = (byte[])reply.get( "enable_dht_extended_use" );
360
if( value != null ) {
361
res = new String( value ).equalsIgnoreCase( "true" );
364
// be generous and enable extended use if check failed
367
res = !isVersionCheckDataValid( AT_EITHER );
374
getRecommendedPlugins()
376
Map reply = getVersionCheckInfo( REASON_RECOMMENDED_PLUGINS, AT_EITHER );
378
List l = (List)reply.get( "recommended_plugins" );
382
return( new String[0] );
385
String[] res = new String[l.size()];
387
for (int i=0;i<l.size();i++){
389
res[i] = new String((byte[])l.get(i));
396
* Perform the actual version check by connecting to the version server.
397
* @param data_to_send version message
398
* @return version reply
399
* @throws Exception if the server check connection fails
404
boolean use_az_message,
410
Exception error = null;
413
if ( use_az_message ){
416
reply = executeAZMessage( data_to_send, v6 );
418
reply.put( "protocol_used", "AZMSG" );
420
catch (IOException e) {
423
catch (Exception e) {
424
Debug.printStackTrace( e );
429
if ( reply == null && use_http ){
432
reply = executeHTTP( data_to_send, v6 );
434
reply.put( "protocol_used", "HTTP" );
438
catch (IOException e) {
442
Debug.printStackTrace(e);
447
if ( error != null ){
452
if (Logger.isEnabled())
453
Logger.log(new LogEvent(LOGID, "VersionCheckClient server "
454
+ "version check successful. Received " + reply.size()
459
last_check_time_v6 = SystemTime.getCurrentTime();
463
last_check_time_v4 = SystemTime.getCurrentTime();
476
String host = v6?AZ_MSG_SERVER_ADDRESS_V6:AZ_MSG_SERVER_ADDRESS_V4;
478
if (Logger.isEnabled())
479
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
480
+ "version information from " + host + ":" + AZ_MSG_SERVER_PORT));
482
ClientMessageService msg_service = null;
486
msg_service = ClientMessageServiceClient.getServerService( host, AZ_MSG_SERVER_PORT, MESSAGE_TYPE_ID );
488
msg_service.sendMessage( data_to_send ); //send our version message
490
reply = msg_service.receiveMessage(); //get the server reply
492
preProcessReply( reply, v6 );
496
if ( msg_service != null ){
512
String host = v6?HTTP_SERVER_ADDRESS_V6:HTTP_SERVER_ADDRESS_V4;
514
if (Logger.isEnabled())
515
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
516
+ "version information from " + host + ":" + HTTP_SERVER_PORT + " via HTTP" ));
518
String url_str = "http://" + host + (HTTP_SERVER_PORT==80?"":(":" + HTTP_SERVER_PORT)) + "/version?";
520
url_str += URLEncoder.encode( new String( BEncoder.encode( data_to_send ), "ISO-8859-1" ), "ISO-8859-1" );
522
URL url = new URL( url_str );
524
HttpURLConnection url_connection = (HttpURLConnection)url.openConnection();
526
url_connection.connect();
529
InputStream is = url_connection.getInputStream();
531
Map reply = BDecoder.decode( new BufferedInputStream( is ));
533
preProcessReply( reply, v6 );
539
url_connection.disconnect();
548
return( getHTTPGetString( new HashMap(), for_proxy, v6 ));
557
String host = v6?HTTP_SERVER_ADDRESS_V6:HTTP_SERVER_ADDRESS_V4;
559
String get_str = "GET " + (for_proxy?("http://" + host + ":" + HTTP_SERVER_PORT ):"") +"/version?";
562
get_str += URLEncoder.encode( new String( BEncoder.encode( content ), "ISO-8859-1" ), "ISO-8859-1" );
564
}catch( Throwable e ){
567
get_str +=" HTTP/1.1" + "\015\012" + "\015\012";
581
String host = v6?TCP_SERVER_ADDRESS_V6:TCP_SERVER_ADDRESS_V4;
583
if (Logger.isEnabled())
584
Logger.log(new LogEvent(LOGID, "VersionCheckClient retrieving "
585
+ "version information from " + host + ":" + TCP_SERVER_PORT + " via TCP" ));
587
String get_str = getHTTPGetString( data_to_send, false, v6 );
589
Socket socket = null;
592
socket = new Socket();
594
if ( bind_ip != null ){
596
socket.bind( new InetSocketAddress( bind_ip, bind_port ));
598
}else if ( bind_port != 0 ){
600
socket.bind( new InetSocketAddress( bind_port ));
603
socket.setSoTimeout( 10000 );
605
socket.connect( new InetSocketAddress( host, TCP_SERVER_PORT ), 10000 );
607
OutputStream os = socket.getOutputStream();
609
os.write( get_str.getBytes( "ISO-8859-1" ));
613
InputStream is = socket.getInputStream();
615
ByteArrayOutputStream baos = new ByteArrayOutputStream();
617
byte[] buffer = new byte[1024];
623
int len = is.read( buffer );
632
if ( total_len > 16000 ){
634
throw( new IOException( "reply too large" ));
637
baos.write( buffer, 0, len );
640
byte[] reply_bytes = baos.toByteArray();
642
for (int i=3;i<reply_bytes.length;i++){
644
if ( reply_bytes[i-3]== (byte)'\015' &&
645
reply_bytes[i-2]== (byte)'\012' &&
646
reply_bytes[i-1]== (byte)'\015' &&
647
reply_bytes[i-0]== (byte)'\012' ){
649
Map reply = BDecoder.decode( new BufferedInputStream( new ByteArrayInputStream( reply_bytes, i+1, reply_bytes.length - (i+1 ))));
651
preProcessReply( reply, v6 );
657
throw( new Exception( "Invalid reply: " + new String( reply_bytes )));
661
if ( socket != null ){
666
}catch( Throwable e ){
682
String host = v6?UDP_SERVER_ADDRESS_V6:UDP_SERVER_ADDRESS_V4;
684
PRUDPReleasablePacketHandler handler = PRUDPPacketHandlerFactory.getReleasableHandler( bind_port );
686
PRUDPPacketHandler packet_handler = handler.getHandler();
690
Random random = new Random();
693
Exception last_error = null;
695
packet_handler.setExplicitBindAddress( bind_ip );
697
for (int i=0;i<3;i++){
700
// connection ids for requests must always have their msb set...
701
// apart from the original darn udp tracker spec....
703
long connection_id = 0x8000000000000000L | random.nextLong();
705
VersionCheckClientUDPRequest request_packet = new VersionCheckClientUDPRequest( connection_id );
707
request_packet.setPayload( data_to_send );
709
VersionCheckClientUDPReply reply_packet = (VersionCheckClientUDPReply)packet_handler.sendAndReceive( null, request_packet, new InetSocketAddress( host, UDP_SERVER_PORT ), timeout );
711
Map reply = reply_packet.getPayload();
713
preProcessReply( reply, v6 );
717
}catch( Exception e){
721
timeout = timeout * 2;
725
if ( last_error != null ){
730
throw( new Exception( "Timeout" ));
734
packet_handler.setExplicitBindAddress( null );
745
NetworkAdmin admin = NetworkAdmin.getSingleton();
748
byte[] address = (byte[])reply.get( "source_ip_address" );
750
InetAddress my_ip = InetAddress.getByName( new String( address ));
752
NetworkAdminASN old_asn = admin.getCurrentASN();
754
NetworkAdminASN new_asn = admin.lookupASN( my_ip );
756
if ( !new_asn.sameAs( old_asn )){
758
// kick off a secondary version check to communicate the new information
760
if ( !secondary_check_done ){
762
secondary_check_done = true;
764
new AEThread( "Secondary version check", true )
769
getVersionCheckInfoSupport( REASON_SECONDARY_CHECK, false, true, v6 );
774
}catch( Throwable e ){
776
Debug.printStackTrace(e);
779
Long as_advice = (Long)reply.get( "as_advice" );
781
if ( as_advice != null ){
783
NetworkAdminASN current_asn = admin.getCurrentASN();
785
String asn = current_asn.getASName();
789
long advice = as_advice.longValue();
795
String done_asn = COConfigurationManager.getStringParameter( "ASN Advice Followed", "" );
797
if ( !done_asn.equals( asn )){
799
COConfigurationManager.setParameter( "ASN Advice Followed", asn );
801
boolean change = advice == 1 || advice == 2;
802
boolean alert = advice == 1 || advice == 3;
804
if ( !COConfigurationManager.getBooleanParameter( "network.transport.encrypted.require" )){
808
COConfigurationManager.setParameter( "network.transport.encrypted.require", true );
814
MessageText.getString(
815
"crypto.alert.as.warning",
816
new String[]{ asn });
818
Logger.log( new LogAlert( false, LogAlert.AT_WARNING, msg ));
826
// set ui.toolbar.uiswitcher based on instructions from tracker
827
// Really shouldn't be in VersionCheck client, but instead have some
828
// listener and have the code elsewhere. Simply calling
829
//getVersionCheckInfo from "code elsewhere" (to get the cached result)
830
//caused a deadlock at startup.
831
Long lEnabledUISwitcher = (Long) reply.get("ui.toolbar.uiswitcher");
832
if (lEnabledUISwitcher != null) {
833
COConfigurationManager.setBooleanDefault("ui.toolbar.uiswitcher",
834
lEnabledUISwitcher.longValue() == 1);
839
getExternalIpAddressHTTP(
844
Map reply = executeHTTP( new HashMap(), v6 );
846
byte[] address = (byte[])reply.get( "source_ip_address" );
848
return( InetAddress.getByName( new String( address )));
852
getExternalIpAddressTCP(
859
Map reply = executeTCP( new HashMap(), bind_ip, bind_port, v6 );
861
byte[] address = (byte[])reply.get( "source_ip_address" );
863
return( InetAddress.getByName( new String( address )));
867
getExternalIpAddressUDP(
874
Map reply = executeUDP( new HashMap(), bind_ip, bind_port, v6 );
876
byte[] address = (byte[])reply.get( "source_ip_address" );
878
return( InetAddress.getByName( new String( address )));
882
* Construct the default version check message.
883
* @return message to send
885
private Map constructVersionCheckMessage( String reason ) {
886
Map message = new HashMap();
888
message.put( "appid", SystemProperties.getApplicationIdentifier());
889
message.put( "version", Constants.AZUREUS_VERSION );
891
String id = COConfigurationManager.getStringParameter( "ID", null );
892
boolean send_info = COConfigurationManager.getBooleanParameter( "Send Version Info" );
894
int last_send_time = COConfigurationManager.getIntParameter( "Send Version Info Last Time", -1 );
896
int current_send_time = (int)(SystemTime.getCurrentTime()/1000);
898
COConfigurationManager.setParameter( "Send Version Info Last Time", current_send_time );
900
if( id != null && send_info ) {
902
message.put( "id", id );
903
message.put( "os", Constants.OSName );
905
message.put( "os_version", System.getProperty( "os.version" ) );
906
message.put( "os_arch", System.getProperty( "os.arch" ) ); //see http://lopica.sourceforge.net/os.html
908
if ( last_send_time != -1 && last_send_time < current_send_time ){
912
message.put( "tsl", new Long(current_send_time-last_send_time));
915
message.put( "reason", reason );
917
String java_version = System.getProperty( "java.version" );
918
if ( java_version == null ){ java_version = "unknown"; }
919
message.put( "java", java_version );
922
String java_vendor = System.getProperty( "java.vm.vendor" );
923
if ( java_vendor == null ){ java_vendor = "unknown"; }
924
message.put( "javavendor", java_vendor );
927
long max_mem = Runtime.getRuntime().maxMemory()/(1024*1024);
928
message.put( "javamx", new Long( max_mem ) );
930
String java_rt_name = System.getProperty("java.runtime.name");
931
if (java_rt_name != null) {
932
message.put( "java_rt_name", java_rt_name);
935
String java_rt_version = System.getProperty("java.runtime.version");
936
if (java_rt_version != null) {
937
message.put( "java_rt_version", java_rt_version);
940
OverallStats stats = StatsFactory.getStats();
942
if ( stats != null ){
944
//long total_bytes_downloaded = stats.getDownloadedBytes();
945
//long total_bytes_uploaded = stats.getUploadedBytes();
946
long total_uptime = stats.getTotalUpTime();
948
//removed due to complaints about anonymous stats collection
949
//message.put( "total_bytes_downloaded", new Long( total_bytes_downloaded ) );
950
//message.put( "total_bytes_uploaded", new Long( total_bytes_uploaded ) );
951
message.put( "total_uptime", new Long( total_uptime ) );
952
//message.put( "dlstats", stats.getDownloadStats());
955
String as = COConfigurationManager.getStringParameter( "ASN AS", null );
959
// there was borkage with DNS based queries that left leading " on AS
961
if ( as.startsWith( "\"" )){
963
as = as.substring( 1 ).trim();
965
COConfigurationManager.setParameter( "ASN AS", as );
968
message.put( "ip_as", as );
971
String asn = COConfigurationManager.getStringParameter( "ASN ASN", null );
975
if ( asn.length() > 64 ){
977
asn = asn.substring( 0, 64 );
980
message.put( "ip_asn", asn );
983
String ui = COConfigurationManager.getStringParameter("ui");
984
if (ui.length() > 0) {
985
message.put("ui", ui);
988
// send locale, so we can determine which languages need attention
989
message.put("locale", Locale.getDefault().toString());
990
String originalLocale = System.getProperty("user.language") + "_"
991
+ System.getProperty("user.country");
992
String variant = System.getProperty("user.variant");
993
if (variant != null && variant.length() > 0) {
994
originalLocale += "_" + variant;
996
message.put("orig_locale", originalLocale);
998
if ( AzureusCoreFactory.isCoreAvailable()){
1000
//installed plugin IDs
1001
PluginInterface[] plugins = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaces();
1002
List pids = new ArrayList();
1003
for (int i=0;i<plugins.length;i++){
1004
String pid = plugins[i].getPluginID();
1006
String info = (String)plugins[i].getPluginconfig().getPluginStringParameter( "plugin.info" );
1008
// filter out built-in and core ones
1009
if ( ( info != null && info.length() > 0 ) ||
1010
( !pid.startsWith( "<" ) &&
1011
!pid.startsWith( "azbp" ) &&
1012
!pid.startsWith( "azupdater" ) &&
1013
!pid.startsWith( "azplatform" ) &&
1014
!pids.contains( pid ))){
1016
if ( info != null && info.length() > 0 ){
1018
if( info.length() < 256 ){
1024
Debug.out( "Plugin '" + pid + "' reported excessive info string '" + info + "'" );
1031
message.put( "plugins", pids );
1038
Class c = Class.forName( "org.eclipse.swt.SWT" );
1040
String swt_platform = (String)c.getMethod( "getPlatform", new Class[]{} ).invoke( null, new Object[]{} );
1041
message.put( "swt_platform", swt_platform );
1044
Integer swt_version = (Integer)c.getMethod( "getVersion", new Class[]{} ).invoke( null, new Object[]{} );
1045
message.put( "swt_version", new Long( swt_version.longValue() ) );
1047
c = Class.forName("org.gudy.azureus2.ui.swt.mainwindow.MainWindow");
1049
c.getMethod("addToVersionCheckMessage", new Class[] { Map.class }).invoke(
1050
null, new Object[] { message });
1054
catch( ClassNotFoundException e ) { /* ignore */ }
1055
catch( NoClassDefFoundError er ) { /* ignore */ }
1056
catch( InvocationTargetException err ) { /* ignore */ }
1057
catch( Throwable t ) { t.printStackTrace(); }
1060
boolean using_phe = COConfigurationManager.getBooleanParameter( "network.transport.encrypted.require" );
1061
message.put( "using_phe", using_phe ? new Long(1) : new Long(0) );
1071
COConfigurationManager.initialise();
1075
System.out.println( "UDP: " + getSingleton().getExternalIpAddressUDP(null,0,v6));
1076
System.out.println( "TCP: " + getSingleton().getExternalIpAddressTCP(null,0,v6));
1077
System.out.println( "HTTP: " + getSingleton().getExternalIpAddressHTTP(v6));
1079
}catch( Throwable e){
1080
e.printStackTrace();