/**
* This file is part of ElasticDroid.
*
* ElasticDroid is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* ElasticDroid is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with ElasticDroid. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by siddhu on 15 Dec 2010
*/
package org.elasticdroid.model;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.http.ConnectionClosedException;
import org.elasticdroid.R;
import org.elasticdroid.model.ds.SerializableIpPermission;
import org.elasticdroid.model.ds.SerializableSecurityGroup;
import org.elasticdroid.model.tpl.GenericModel;
import org.elasticdroid.tpl.GenericActivity;
import org.elasticdroid.tpl.GenericListActivity;
import org.elasticdroid.utils.MiscUtils;
import android.util.Log;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.ec2.model.Filter;
import com.amazonaws.services.ec2.model.IpPermission;
/**
* This class is a model class to get the SSH URI to use to connect to this instance.
*
* <ul>
* <li>It checks if the port on the instance that the user wishes to connect to is available
* (default to-port 22), as well the port to connect from (hard-coded to 22 atm).</li>
* <li>If the port is available, it makes sure that the device's IP is in the IP address ranges
* acceptable on the instance (for that port).</li>
* <li> If this is the case as well, it produces an SSH URI which can be used as a URI to Connect
* Bot</li>
* </ul>
* @author siddhu
*
* 15 Dec 2010
*/
public class SshConnectorModel extends GenericModel<String, Void, Object> {
/**
* the port to connect to: toPort in AWS parlance
* {@see http://docs.amazonwebservices.com/AWSJavaSDK/latest/javadoc/index.html}.
*/
private int toPort;
/** username to connect using */
private String username;
/** hostname to connect to */
private String hostname;
/**
* The connection data
*/
private HashMap <String, String> connectionData;
/**
* The tag for printing out log messages
*/
private static final String TAG = "org.elasticdroid.model.SshConnectorModel";
/**
* Constructor, sets toPort to 22
* @param genericActivity
*/
public SshConnectorModel(GenericActivity genericActivity, HashMap<String, String>
connectionData, String username, String hostname) {
super(genericActivity);
this.connectionData = connectionData;
this.username = username;
this.hostname = hostname;
toPort = 22;
}
/**
* Constructor.
* @param genericActivity The activity that called the model
* @param toPort The port to try to connect to.
*/
public SshConnectorModel(GenericActivity genericActivity, HashMap<String, String>
connectionData, String username, String hostname, int toPort) {
super(genericActivity); //call parent class constructor
this.connectionData = connectionData;
this.toPort = toPort;
this.username = username;
this.hostname = hostname;
}
/**
* Constructor, sets toPort to 22
* @param genericActivity
*/
public SshConnectorModel(GenericListActivity genericListActivity, HashMap<String, String>
connectionData, String username, String hostname) {
super(genericListActivity);
this.connectionData = connectionData;
this.username = username;
this.hostname = hostname;
toPort = 22;
}
/**
* Constructor.
* @param genericActivity The activity that called the model
* @param toPort The port to try to connect to.
*/
public SshConnectorModel(GenericListActivity genericListActivity, HashMap<String, String>
connectionData, String username, String hostname, int toPort) {
super(genericListActivity); //call parent class constructor
this.connectionData = connectionData;
this.toPort = toPort;
this.username = username;
this.hostname = hostname;
}
/* (non-Javadoc)
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected Object doInBackground(String... secGroups) {
return prepareSshUri(secGroups);
}
@SuppressWarnings("unchecked")
public Object prepareSshUri(String... secGroups) {
String sourceIpAddress = null;
//first, get the source IP address
try {
URL ipAddressUri = new URL("http://www.whatismyip.com/automation/n09230945.asp");
HttpURLConnection connection = (HttpURLConnection) ipAddressUri.openConnection();
connection.setConnectTimeout(5000); //time out in 5 seconds
sourceIpAddress = new BufferedReader(new InputStreamReader(connection.getInputStream())).
readLine();
}
catch(Exception exception) {
//if you can't retrieve it, just return a ConnectionClosedexception
sourceIpAddress = null;
}
//just a check in case they change the way whatismyip.org works
if (sourceIpAddress == null) {
if (!listActivityUsed) {
return new ConnectionClosedException(
activity.getString(R.string.sshconnector_cannotretrievehostip));
} else {
return new ConnectionClosedException(
listActivity.getString(R.string.sshconnector_cannotretrievehostip));
}
}
Log.v(TAG, "Your Device's IP address is: " + sourceIpAddress);
//get the information on the security groups in list
ArrayList<Filter> secGroupFilters = new ArrayList<Filter>();
List<SerializableSecurityGroup> securityGroups = null;//initialise it
boolean portFound = false; //set to indicate port was found
//used to identify whether the fail was cuz of the port failing, or cuz of the IP address
//ranges.
for (String secGroup : secGroups) {
//add filters for each of the security groups
secGroupFilters.add(new Filter("group-name").withValues(secGroup));
}
//pass the filters to the SecurityGroupsModel
//do not use the execute method so as to have it run in this thread
Object result;
if (!listActivityUsed) {
result = new SecurityGroupsModel(activity, connectionData).getSecurityGroupData(
secGroupFilters.toArray(new Filter[secGroupFilters.size()]));
} else {
result = new SecurityGroupsModel(listActivity, connectionData).getSecurityGroupData(
secGroupFilters.toArray(new Filter[secGroupFilters.size()]));
}
if (result instanceof List<?>) {
securityGroups = (List<SerializableSecurityGroup>)result;
}
//pass the exception on
else if (result instanceof AmazonServiceException) {
return (AmazonServiceException)result;
}
//pass the exception on
else if (result instanceof AmazonClientException) {
return (AmazonClientException) result;
}
//now scan through each of the security groups, and check if toPort is open in any of them.
//If so, check if this IP address is in the acceptable list
for (SerializableSecurityGroup securityGroup: securityGroups) {
List<SerializableIpPermission> permissions = securityGroup.getIpPermissions();
//loop through the permissions
for (SerializableIpPermission permission : permissions) {
if (permission.getToPort() == toPort) {
//check if the IP address is right
Log.v(this.getClass().getName(), "" + permission.getIpRanges().toString());
portFound = true;
//if source IP address is null, i.e. our WAN IP resolver is down, don't
//check if this port is blocked for our IP address
if (sourceIpAddress != null) {
//loop through the acceptable IP address ranges
for (String sourceCidr : permission.getIpRanges()) {
//split the source IP address along the dots
//split the source CIDR along the / to remove CIDR ignore bits, followed
//by along the dots.
if (MiscUtils.checkIpPermissions(sourceIpAddress.split("\\."),
sourceCidr.split("/")[0].split("\\."),
Integer.valueOf(sourceCidr.split("/")[1]))) {
//success, everything is fine. IP permissions, the works.
String sshUri = "ssh://" + username + "@" + hostname + ":" + toPort;
//add nickname to show on ConnectBot screen
sshUri += "#" + username + "@" + hostname;
return sshUri;
}
}
}
}
}
}
//if we get here, we failed
if (portFound) {
Log.v(TAG, "Val: " + R.string.sshconnector_ipaddressblocked);
//if we did find a port, return the error that the IP address provided is blocked.
if (!listActivityUsed) {
return new ConnectionClosedException(activity.getString(
R.string.sshconnector_ipaddressblocked));
}
else {
return new ConnectionClosedException(listActivity.getString(
R.string.sshconnector_ipaddressblocked));
}
}
else {
if (!listActivityUsed) {
return new ConnectionClosedException(activity.getString(
R.string.sshconnector_portblocked));
}
else {
return new ConnectionClosedException(listActivity.getString(
R.string.sshconnector_portblocked));
}
}
}
}