@Override public void onCreate(Bundle icicle) { super.onCreate(icicle); Utils.showMenu(this); initialize(); if (connection != null && connection.isReadyForConnection()) continueConnecting(); }
void initializeBitmap(int dx, int dy) throws IOException { Log.i(TAG, "Desktop name is " + rfbconn.desktopName()); Log.i( TAG, "Desktop size is " + rfbconn.framebufferWidth() + " x " + rfbconn.framebufferHeight()); int fbsize = rfbconn.framebufferWidth() * rfbconn.framebufferHeight(); capacity = BCFactory.getInstance() .getBCActivityManager() .getMemoryClass(Utils.getActivityManager(getContext())); if (connection.getForceFull() == BitmapImplHint.AUTO) { if (fbsize * CompactBitmapData.CAPACITY_MULTIPLIER <= capacity * 1024 * 1024) { useFull = true; compact = true; } else if (fbsize * FullBufferBitmapData.CAPACITY_MULTIPLIER <= capacity * 1024 * 1024) { useFull = true; } else { useFull = false; } } else useFull = (connection.getForceFull() == BitmapImplHint.FULL); if (!useFull) { bitmapData = new LargeBitmapData(rfbconn, this, dx, dy, capacity); android.util.Log.i(TAG, "Using LargeBitmapData."); } else { try { // TODO: Remove this if Android 4.2 receives a fix which causes it to stop drawing // the bitmap in CompactBitmapData when under load (say playing a video over VNC). if (!compact || android.os.Build.VERSION.SDK_INT == 17) { bitmapData = new FullBufferBitmapData(rfbconn, this, capacity); android.util.Log.i(TAG, "Using FullBufferBitmapData."); } else { bitmapData = new CompactBitmapData(rfbconn, this); android.util.Log.i(TAG, "Using CompactBufferBitmapData."); } } catch (Throwable e) { // If despite our efforts we fail to allocate memory, use LBBM. if (bitmapData != null) bitmapData.dispose(); bitmapData = null; System.gc(); useFull = false; bitmapData = new LargeBitmapData(rfbconn, this, dx, dy, capacity); android.util.Log.i(TAG, "Using LargeBitmapData."); } } decoder.setBitmapData(bitmapData); }
void initialize() { if (android.os.Build.VERSION.SDK_INT >= 9) { android.os.StrictMode.ThreadPolicy policy = new android.os.StrictMode.ThreadPolicy.Builder().permitAll().build(); android.os.StrictMode.setThreadPolicy(policy); } handler = new Handler(); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow() .setFlags( WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); database = new Database(this); Intent i = getIntent(); connection = null; Uri data = i.getData(); boolean isSupportedScheme = false; if (data != null) { String s = data.getScheme(); isSupportedScheme = s.equals("rdp") || s.equals("spice") || s.equals("vnc"); } if (isSupportedScheme || !Utils.isNullOrEmptry(i.getType())) { if (isMasterPasswordEnabled()) { Utils.showFatalErrorMessage( this, getResources().getString(R.string.master_password_error_intents_not_supported)); return; } connection = ConnectionBean.createLoadFromUri(data, this); String host = data.getHost(); if (!host.startsWith(Constants.CONNECTION)) { connection.parseFromUri(data); } if (connection.isSaved()) { connection.saveAndWriteRecent(false); } // we need to save the connection to display the loading screen, so otherwise we should exit if (!connection.isReadyForConnection()) { if (!connection.isSaved()) { Log.i(TAG, "Exiting - Insufficent information to connect and connection was not saved."); Toast.makeText(this, getString(R.string.error_uri_noinfo_nosave), Toast.LENGTH_LONG) .show(); ; } else { // launch bVNC activity Log.i(TAG, "Insufficent information to connect, showing connection dialog."); Intent bVncIntent = new Intent(this, bVNC.class); startActivity(bVncIntent); } finish(); return; } } else { connection = new ConnectionBean(this); Bundle extras = i.getExtras(); if (extras != null) { connection.Gen_populate((ContentValues) extras.getParcelable(Constants.CONNECTION)); } // Parse a HOST:PORT entry String host = connection.getAddress(); if (host.indexOf(':') > -1) { String p = host.substring(host.indexOf(':') + 1); try { connection.setPort(Integer.parseInt(p)); } catch (Exception e) { } connection.setAddress(host.substring(0, host.indexOf(':'))); } if (connection.getPort() == 0) connection.setPort(Constants.DEFAULT_VNC_PORT); if (connection.getSshPort() == 0) connection.setSshPort(Constants.DEFAULT_SSH_PORT); } }
void parseFromUri(Uri dataUri) { Log.i(TAG, "Parsing VNC URI."); if (dataUri == null) { m_isReadyForConnection = false; m_saved = true; return; } String host = dataUri.getHost(); if (host != null) { setAddress(host); // by default, the connection name is the host name String nickName = getNickname(); if (Utils.isNullOrEmptry(nickName)) { setNickname(host); } // default to use same host for ssh if (Utils.isNullOrEmptry(getSshServer())) { setSshServer(host); } } final int PORT_NONE = -1; int port = dataUri.getPort(); if (port != PORT_NONE) { if (!isValidPort(port)) throw new IllegalArgumentException("The specified VNC port is not valid."); setPort(port); } // handle legacy android-vnc-viewer parsing vnc://host:port/colormodel/password List<String> path = dataUri.getPathSegments(); if (path.size() >= 1) { setColorModel(path.get(0)); } if (path.size() >= 2) { setPassword(path.get(1)); } // query based parameters String connectionName = dataUri.getQueryParameter(Constants.PARAM_CONN_NAME); if (connectionName != null) { setNickname(connectionName); } ArrayList<String> supportedUserParams = new ArrayList<String>() { { add(Constants.PARAM_RDP_USER); add(Constants.PARAM_SPICE_USER); add(Constants.PARAM_VNC_USER); } }; for (String userParam : supportedUserParams) { String username = dataUri.getQueryParameter(userParam); if (username != null) { setUserName(username); break; } } ArrayList<String> supportedPwdParams = new ArrayList<String>() { { add(Constants.PARAM_RDP_PWD); add(Constants.PARAM_SPICE_PWD); add(Constants.PARAM_VNC_PWD); } }; for (String pwdParam : supportedPwdParams) { String password = dataUri.getQueryParameter(pwdParam); if (password != null) { setPassword(password); break; } } setKeepPassword(false); // we should not store the password unless it is encrypted String securityTypeParam = dataUri.getQueryParameter(Constants.PARAM_SECTYPE); int secType = 0; // invalid if (securityTypeParam != null) { secType = Integer.parseInt(securityTypeParam); // throw if invalid switch (secType) { case Constants.SECTYPE_NONE: case Constants.SECTYPE_VNC: setConnectionType(Constants.CONN_TYPE_PLAIN); break; case Constants.SECTYPE_INTEGRATED_SSH: setConnectionType(Constants.CONN_TYPE_SSH); break; case Constants.SECTYPE_ULTRA: setConnectionType(Constants.CONN_TYPE_ULTRAVNC); break; case Constants.SECTYPE_TLS: setConnectionType(Constants.CONN_TYPE_ANONTLS); break; case Constants.SECTYPE_VENCRYPT: setConnectionType(Constants.CONN_TYPE_VENCRYPT); break; case Constants.SECTYPE_TUNNEL: setConnectionType(Constants.CONN_TYPE_STUNNEL); break; default: throw new IllegalArgumentException( "The specified security type is invalid or unsupported."); } } // ssh parameters String sshHost = dataUri.getQueryParameter(Constants.PARAM_SSH_HOST); if (sshHost != null) { setSshServer(sshHost); } String sshPortParam = dataUri.getQueryParameter(Constants.PARAM_SSH_PORT); if (sshPortParam != null) { int sshPort = Integer.parseInt(sshPortParam); if (!isValidPort(sshPort)) throw new IllegalArgumentException("The specified SSH port is not valid."); setSshPort(sshPort); } String sshUser = dataUri.getQueryParameter(Constants.PARAM_SSH_USER); if (sshUser != null) { setSshUser(sshUser); } String sshPassword = dataUri.getQueryParameter(Constants.PARAM_SSH_PWD); if (sshPassword != null) { setSshPassword(sshPassword); } // security hashes String idHashAlgParam = dataUri.getQueryParameter(Constants.PARAM_ID_HASH_ALG); if (idHashAlgParam != null) { int idHashAlg = Integer.parseInt(idHashAlgParam); // throw if invalid switch (idHashAlg) { case Constants.ID_HASH_MD5: case Constants.ID_HASH_SHA1: case Constants.ID_HASH_SHA256: setIdHashAlgorithm(idHashAlg); break; default: // we are given a bad parameter throw new IllegalArgumentException( "The specified hash algorithm is invalid or unsupported."); } } String idHash = dataUri.getQueryParameter(Constants.PARAM_ID_HASH); if (idHash != null) { setIdHash(idHash); } // color model String colorModelParam = dataUri.getQueryParameter(Constants.PARAM_COLORMODEL); if (colorModelParam != null) { int colorModel = Integer.parseInt(colorModelParam); // throw if invalid switch (colorModel) { case Constants.COLORMODEL_BLACK_AND_WHITE: setColorModel(COLORMODEL.C2.nameString()); break; case Constants.COLORMODEL_GREYSCALE: setColorModel(COLORMODEL.C4.nameString()); break; case Constants.COLORMODEL_8_COLORS: setColorModel(COLORMODEL.C8.nameString()); break; case Constants.COLORMODEL_64_COLORS: setColorModel(COLORMODEL.C64.nameString()); break; case Constants.COLORMODEL_256_COLORS: setColorModel(COLORMODEL.C256.nameString()); break; // use the best currently available model case Constants.COLORMODEL_16BIT: setColorModel(COLORMODEL.C24bit.nameString()); break; case Constants.COLORMODEL_24BIT: setColorModel(COLORMODEL.C24bit.nameString()); break; case Constants.COLORMODEL_32BIT: setColorModel(COLORMODEL.C24bit.nameString()); break; default: // we are given a bad parameter throw new IllegalArgumentException( "The specified color model is invalid or unsupported."); } } String saveConnectionParam = dataUri.getQueryParameter(Constants.PARAM_SAVE_CONN); boolean saveConnection = true; if (saveConnectionParam != null) { saveConnection = Boolean.parseBoolean(saveConnectionParam); // throw if invalid } // if we are going to save the connection, we will do so here // it may make sense to confirm overwriting data but is probably unnecessary if (saveConnection) { Database database = new Database(c); save(database.getWritableDatabase()); database.close(); m_saved = true; } // we do not currently use API keys // check if we need to show data-entry screen // it may be possible to prompt for data later m_isReadyForConnection = true; if (Utils.isNullOrEmptry(getAddress())) { m_isReadyForConnection = false; Log.i(TAG, "URI missing remote address."); } int connType = getConnectionType(); if (secType == Constants.SECTYPE_VNC || connType == Constants.CONN_TYPE_STUNNEL || connType == Constants.CONN_TYPE_SSH) { // we can infer a password is required // while we could have implemented tunnel/ssh without one // the user can supply a blank value and the server will not // request it and it is better to support the common case if (Utils.isNullOrEmptry(getPassword())) { m_isReadyForConnection = false; Log.i(TAG, "URI missing VNC password."); } } if (connType == Constants.CONN_TYPE_SSH) { // the below should not occur if (Utils.isNullOrEmptry(getSshServer())) m_isReadyForConnection = false; // we probably need either a username/password or a key // however the main screen doesn't validate this } // some other types probably require a username/password // however main screen doesn't validate this }
@Override public void handleMessage(Message msg) { switch (msg.what) { case VncConstants.DIALOG_X509_CERT: final X509Certificate cert = (X509Certificate) msg.obj; if (connection.getSshHostKey().equals("")) { // Show a dialog with the key signature for approval. DialogInterface.OnClickListener signatureNo = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // We were told not to continue, so stop the activity closeConnection(); ((Activity) getContext()).finish(); } }; DialogInterface.OnClickListener signatureYes = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // We were told to go ahead with the connection, so save the key into the // database. String certificate = null; try { certificate = Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT); } catch (CertificateEncodingException e) { e.printStackTrace(); showFatalMessageAndQuit("Certificate encoding could not be generated."); } connection.setSshHostKey(certificate); connection.save(database.getWritableDatabase()); database.close(); // Indicate the certificate was accepted. certificateAccepted = true; } }; // Generate a sha1 signature of the certificate. MessageDigest sha1; MessageDigest md5; try { sha1 = MessageDigest.getInstance("SHA1"); md5 = MessageDigest.getInstance("MD5"); sha1.update(cert.getEncoded()); Utils.showYesNoPrompt( getContext(), "Continue connecting to " + connection.getAddress() + "?", "The x509 certificate signatures are:" + "\nSHA1: " + Utils.toHexString(sha1.digest()) + "\nMD5: " + Utils.toHexString(md5.digest()) + "\nYou can ensure they are identical to the known signatures of the server certificate to prevent a man-in-the-middle attack.", signatureYes, signatureNo); } catch (NoSuchAlgorithmException e2) { e2.printStackTrace(); showFatalMessageAndQuit( "Could not generate SHA1 or MD5 signature of certificate. No SHA1/MD5 algorithm found."); } catch (CertificateEncodingException e) { e.printStackTrace(); showFatalMessageAndQuit("Certificate encoding could not be generated."); } } else { // Compare saved with obtained certificate and quit if they don't match. try { if (!connection .getSshHostKey() .equals(Base64.encodeToString(cert.getEncoded(), Base64.DEFAULT))) { showFatalMessageAndQuit( "ERROR: The saved x509 certificate does not match the current server certificate! " + "This could be a man-in-the-middle attack. If you are aware of the key change, delete and recreate the connection."); } else { // In case we need to display information about the certificate, we can // reconstruct it like this: // CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); // ByteArrayInputStream in = new // ByteArrayInputStream(Base64.decode(connection.getSshHostKey(), // Base64.DEFAULT)); // X509Certificate c = (X509Certificate)certFactory.generateCertificate(in); // android.util.Log.e(" Subject ", c.getSubjectDN().toString()); // android.util.Log.e(" Issuer ", c.getIssuerDN().toString()); // The certificate matches, so we proceed. certificateAccepted = true; } } catch (CertificateEncodingException e) { e.printStackTrace(); showFatalMessageAndQuit("Certificate encoding could not be generated."); } } break; } }