[or-cvs] r22005: {projects} fixed a few small bugs, added new network package classes fo (in projects/android/trunk/Orbot/src/org/torproject/android: . net service)

Nathan Freitas nathan at freitas.net
Thu Mar 18 03:15:21 UTC 2010


Author: n8fr8
Date: 2010-03-18 03:15:20 +0000 (Thu, 18 Mar 2010)
New Revision: 22005

Added:
   projects/android/trunk/Orbot/src/org/torproject/android/net/ModSSLSocketFactory.java
   projects/android/trunk/Orbot/src/org/torproject/android/net/MyDefaultClientConnectionOperator.java
   projects/android/trunk/Orbot/src/org/torproject/android/net/MyThreadSafeClientConnManager.java
   projects/android/trunk/Orbot/src/org/torproject/android/net/SOCKSHttpClient.java
   projects/android/trunk/Orbot/src/org/torproject/android/net/SocksSocketFactory.java
   projects/android/trunk/Orbot/src/org/torproject/android/service/NanoHTTPD.java
   projects/android/trunk/Orbot/src/org/torproject/android/service/TorWebProxy.java
Modified:
   projects/android/trunk/Orbot/src/org/torproject/android/Orbot.java
   projects/android/trunk/Orbot/src/org/torproject/android/Utils.java
   projects/android/trunk/Orbot/src/org/torproject/android/service/TorService.java
   projects/android/trunk/Orbot/src/org/torproject/android/service/TorServiceUtils.java
   projects/android/trunk/Orbot/src/org/torproject/android/service/TorTransProxy.java
Log:
fixed a few small bugs, added new network package classes for future functionality

Modified: projects/android/trunk/Orbot/src/org/torproject/android/Orbot.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/Orbot.java	2010-03-18 01:05:56 UTC (rev 22004)
+++ projects/android/trunk/Orbot/src/org/torproject/android/Orbot.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -57,6 +57,7 @@
 	private ProgressDialog progressDialog;
 	private ListView listApps;
 	private boolean showingSettings = false;
+	private MenuItem mItemOnOff = null;
 	
 	/* Some tracking bits */
 	private int torStatus = STATUS_READY; //latest status reported from the tor service
@@ -87,15 +88,9 @@
         
         MenuItem mItem = null;
         
-        /*
-        
-        mItem = menu.add(0, 1, Menu.NONE, getString(R.string.menu_home));
-        mItem.setIcon(R.drawable.ic_menu_home);
 
-        mItem = menu.add(0, 2, Menu.NONE, getString(R.string.menu_browse));
-        mItem.setIcon(R.drawable.ic_menu_goto);
-        */
-
+        mItemOnOff = menu.add(0, 1, Menu.NONE, getString(R.string.menu_start));
+        mItemOnOff.setIcon(android.R.drawable.ic_menu_share);
         
         mItem = menu.add(0, 4, Menu.NONE, getString(R.string.menu_settings));
         mItem.setIcon(R.drawable.ic_menu_register);
@@ -132,7 +127,32 @@
 		
 		if (item.getItemId() == 1)
 		{
-			this.showMain();
+			
+			try
+			{
+				
+				if (mService == null)
+				{
+				
+				}
+				else if (mService.getStatus() == STATUS_READY)
+				{
+					mItemOnOff.setTitle(R.string.menu_stop);
+					startTor();
+					
+				}
+				else
+				{
+					mItemOnOff.setTitle(R.string.menu_start);
+					stopTor();
+					
+				}
+				
+			}
+			catch (RemoteException re)
+			{
+				Log.w(TAG, "Unable to start/top Tor from menu UI", re);
+			}
 		}
 		else if (item.getItemId() == 4)
 		{
@@ -706,6 +726,29 @@
         
     }
   
+    private void startTor () throws RemoteException
+    {
+    	mService.setProfile(PROFILE_ON); //this means turn on
+		
+		imgStatus.setImageResource(R.drawable.torstarting);
+		lblStatus.setText(getString(R.string.status_starting_up));
+		
+		Message msg = mHandler.obtainMessage(ENABLE_TOR_MSG);
+    	mHandler.sendMessage(msg);
+    	
+    	updateStatus("");
+    }
+    
+    private void stopTor () throws RemoteException
+    {
+    	mService.setProfile(PROFILE_ONDEMAND);	//these means turn off
+		
+		Message msg = mHandler.obtainMessage(DISABLE_TOR_MSG);
+    	mHandler.sendMessage(msg);
+    	
+    	updateStatus("");
+    }
+    
     /*
      * (non-Javadoc)
      * @see android.view.View.OnClickListener#onClick(android.view.View)
@@ -725,27 +768,14 @@
 				else if (mService.getStatus() == STATUS_READY)
 				{
 					
-					mService.setProfile(PROFILE_ON); //this means turn on
+					startTor();
 					
-					imgStatus.setImageResource(R.drawable.torstarting);
-		    		lblStatus.setText(getString(R.string.status_starting_up));
-		    		
-					Message msg = mHandler.obtainMessage(ENABLE_TOR_MSG);
-		        	mHandler.sendMessage(msg);
-		        	
-		        	updateStatus("");
-					
 				}
 				else
 				{
 					
-					mService.setProfile(PROFILE_ONDEMAND);	//these means turn off
+					stopTor();
 					
-					Message msg = mHandler.obtainMessage(DISABLE_TOR_MSG);
-		        	mHandler.sendMessage(msg);
-		        	
-		        	updateStatus("");
-					
 				}
 				
 			}

Modified: projects/android/trunk/Orbot/src/org/torproject/android/Utils.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/Utils.java	2010-03-18 01:05:56 UTC (rev 22004)
+++ projects/android/trunk/Orbot/src/org/torproject/android/Utils.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -9,10 +9,35 @@
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 
 public class Utils {
 
 
+	 public static String readString (InputStream stream)
+	    {
+	    	String line = null;
+	    
+	    	StringBuffer out = new StringBuffer();
+	    	
+	    	try {
+		    	BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
+
+				while ((line = reader.readLine()) != null)
+				{
+					out.append(line);
+					out.append('\n');
+					
+				}
+			} catch (IOException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+			
+			return out.toString();
+	    	
+	    }
 	/*
 	 * Load the log file text
 	 */

Added: projects/android/trunk/Orbot/src/org/torproject/android/net/ModSSLSocketFactory.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/net/ModSSLSocketFactory.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/net/ModSSLSocketFactory.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,453 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java $
+ * $Revision: 659194 $
+ * $Date: 2008-05-22 11:33:47 -0700 (Thu, 22 May 2008) $
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.torproject.android.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.http.conn.scheme.HostNameResolver;
+import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import android.util.Log;
+
+
+
+/**
+ * Layered socket factory for TLS/SSL connections, based on JSSE.
+ *.
+ * <p>
+ * SSLSocketFactory can be used to validate the identity of the HTTPS 
+ * server against a list of trusted certificates and to authenticate to
+ * the HTTPS server using a private key. 
+ * </p>
+ * 
+ * <p>
+ * SSLSocketFactory will enable server authentication when supplied with
+ * a {@link KeyStore truststore} file containg one or several trusted
+ * certificates. The client secure socket will reject the connection during
+ * the SSL session handshake if the target HTTPS server attempts to
+ * authenticate itself with a non-trusted certificate.
+ * </p>
+ * 
+ * <p>
+ * Use JDK keytool utility to import a trusted certificate and generate a truststore file:    
+ *    <pre>
+ *     keytool -import -alias "my server cert" -file server.crt -keystore my.truststore
+ *    </pre>
+ * </p>
+ * 
+ * <p>
+ * SSLSocketFactory will enable client authentication when supplied with
+ * a {@link KeyStore keystore} file containg a private key/public certificate
+ * pair. The client secure socket will use the private key to authenticate
+ * itself to the target HTTPS server during the SSL session handshake if
+ * requested to do so by the server.
+ * The target HTTPS server will in its turn verify the certificate presented
+ * by the client in order to establish client's authenticity
+ * </p>
+ * 
+ * <p>
+ * Use the following sequence of actions to generate a keystore file
+ * </p>
+ *   <ul>
+ *     <li>
+ *      <p>
+ *      Use JDK keytool utility to generate a new key
+ *      <pre>keytool -genkey -v -alias "my client key" -validity 365 -keystore my.keystore</pre>
+ *      For simplicity use the same password for the key as that of the keystore
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Issue a certificate signing request (CSR)
+ *      <pre>keytool -certreq -alias "my client key" -file mycertreq.csr -keystore my.keystore</pre>
+ *     </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *      Send the certificate request to the trusted Certificate Authority for signature. 
+ *      One may choose to act as her own CA and sign the certificate request using a PKI 
+ *      tool, such as OpenSSL.
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the trusted CA root certificate
+ *       <pre>keytool -import -alias "my trusted ca" -file caroot.crt -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Import the PKCS#7 file containg the complete certificate chain
+ *       <pre>keytool -import -alias "my client key" -file mycert.p7 -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *     <li>
+ *      <p>
+ *       Verify the content the resultant keystore file
+ *       <pre>keytool -list -v -keystore my.keystore</pre> 
+ *      </p>
+ *     </li>
+ *   </ul>
+ * @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
+ * @author Julius Davies
+ */
+
+public class ModSSLSocketFactory implements LayeredSocketFactory {
+
+    public static final String TLS   = "TLS";
+    public static final String SSL   = "SSL";
+    public static final String SSLV2 = "SSLv2";
+    
+    public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER 
+        = new AllowAllHostnameVerifier();
+    
+    public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER 
+        = new BrowserCompatHostnameVerifier();
+    
+    public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER 
+        = new StrictHostnameVerifier();
+    /**
+     * The factory using the default JVM settings for secure connections.
+     */
+    private static ModSSLSocketFactory DEFAULT_FACTORY = null;
+    
+    /**
+     * Gets an singleton instance of the SSLProtocolSocketFactory.
+     * @return a SSLProtocolSocketFactory
+     */
+    public static ModSSLSocketFactory getSocketFactory() {
+    	if (DEFAULT_FACTORY == null) {
+    		DEFAULT_FACTORY = new ModSSLSocketFactory();
+    	}
+		return DEFAULT_FACTORY;
+    }
+    
+    private final SSLContext sslcontext;
+    private final SSLSocketFactory socketfactory;
+    //private final HostNameResolver nameResolver;
+    private X509HostnameVerifier hostnameVerifier = BROWSER_COMPATIBLE_HOSTNAME_VERIFIER;
+    private SocksSocketFactory mSocksSocketFactory = null;
+
+    public ModSSLSocketFactory(
+        String algorithm, 
+        final KeyStore keystore, 
+        final String keystorePassword, 
+        final KeyStore truststore,
+        final SecureRandom random,
+        final HostNameResolver nameResolver) 
+        throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        super();
+        if (algorithm == null) {
+            algorithm = SSL;
+        }
+        KeyManager[] keymanagers = null;
+        if (keystore != null) {
+            keymanagers = createKeyManagers(keystore, keystorePassword);
+        }
+        TrustManager[] trustmanagers = null;
+        if (truststore != null) {
+            trustmanagers = createTrustManagers(truststore);
+        }
+        this.sslcontext = SSLContext.getInstance(algorithm);
+        this.sslcontext.init(keymanagers, trustmanagers, random);
+        this.socketfactory = SSLSocketFactory.getSocketFactory();
+        //this.nameResolver = nameResolver;
+        
+         
+    }
+
+    public ModSSLSocketFactory(
+            final KeyStore keystore, 
+            final String keystorePassword, 
+            final KeyStore truststore) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(SSL, keystore, keystorePassword, truststore, null, null);
+    }
+
+    public ModSSLSocketFactory(final KeyStore keystore, final String keystorePassword) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(SSL, keystore, keystorePassword, null, null, null);
+    }
+
+    public ModSSLSocketFactory(final KeyStore truststore) 
+            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
+    {
+        this(SSL, null, null, truststore, null, null);
+    }
+
+    /**
+     * Constructs an HttpClient SSLSocketFactory backed by the given JSSE
+     * SSLSocketFactory.
+     *
+     * @hide
+     */
+    public ModSSLSocketFactory(SSLSocketFactory socketfactory) {
+        super();
+        this.sslcontext = null;
+        this.socketfactory = socketfactory;
+        //this.nameResolver = null;
+    }
+
+    /**
+     * Creates the default SSL socket factory.
+     * This constructor is used exclusively to instantiate the factory for
+     * {@link #getSocketFactory getSocketFactory}.
+     */
+    private ModSSLSocketFactory() {
+        super();
+        this.sslcontext = null;
+        this.socketfactory = SSLSocketFactory.getSocketFactory();
+        //this.nameResolver = null;
+        
+        //Log.i("TOR_SERVICE","ModSSLSocketFactory: proxied via " + host + ":" + port);
+        
+        this.mSocksSocketFactory = new SocksSocketFactory("127.0.0.1",9050);
+    }
+
+    private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
+        throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
+            KeyManagerFactory.getDefaultAlgorithm());
+        kmfactory.init(keystore, password != null ? password.toCharArray(): null);
+        return kmfactory.getKeyManagers(); 
+    }
+
+    private static TrustManager[] createTrustManagers(final KeyStore keystore)
+        throws KeyStoreException, NoSuchAlgorithmException { 
+        if (keystore == null) {
+            throw new IllegalArgumentException("Keystore may not be null");
+        }
+        TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
+            TrustManagerFactory.getDefaultAlgorithm());
+        tmfactory.init(keystore);
+        return tmfactory.getTrustManagers();
+    }
+
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket createSocket()
+        throws IOException {
+
+    	// SSL Sockets don't work at the moment.
+    	//throw new SSLException("SSL socket functionality broken");
+        // the cast makes sure that the factory is working as expected
+        return (SSLSocket) this.socketfactory.createSocket();
+    	//return new Socket();
+    	//return null;
+        
+    }
+
+
+    // non-javadoc, see interface org.apache.http.conn.SocketFactory
+    public Socket connectSocket(
+        final Socket sock,
+        final String host,
+        final int port,
+        final InetAddress localAddress,
+        int localPort,
+        final HttpParams params
+    ) throws IOException {
+
+        if (host == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null.");
+        }
+
+        //Socket underlying = (Socket)
+        //    ((sock != null) ? sock : createSocket());
+        Socket underlying =  null;
+        
+        /*sock;
+        if (underlying == null)// underlying = new Socket(); 
+        {
+        	underlying = mSocksSocketFactory.createSocket();
+          
+        }*/
+        
+        Log.i("TOR_SERVICE","connecting socks factory");
+        Socket sSocket = mSocksSocketFactory.connectSocket(underlying, host, port, localAddress, localPort, params);
+        Log.i("TOR_SERVICE","creating SSL Socket");
+        
+       // SSLSocket sslsock =  (SSLSocket) socketfactory.connectSocket(sSocket, host, port, localAddress, localPort, params);
+        SSLSocket sslsock = (SSLSocket)socketfactory.createSocket(sSocket, host, port, true);
+        
+        
+        Log.i("TOR_SERVICE","created SSL Socket!");
+        
+        if ((localAddress != null) || (localPort > 0)) {
+
+            // we need to bind explicitly
+            if (localPort < 0)
+                localPort = 0; // indicates "any"
+
+            InetSocketAddress isa =
+                new InetSocketAddress(localAddress, localPort);
+            
+            Log.i("TOR_SERVICE","binding SSL Socket!");
+
+            sslsock.bind(isa);
+        }
+
+        int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
+        int soTimeout = HttpConnectionParams.getSoTimeout(params);
+
+        InetSocketAddress remoteAddress;
+//        if (this.nameResolver != null) {
+//            remoteAddress = new InetSocketAddress(this.nameResolver.resolve(host), port); 
+//        } else {
+         remoteAddress = new InetSocketAddress(host, port);            
+//        }
+//        
+
+         sslsock.connect(remoteAddress, connTimeout);
+
+       // sslsock.setSoTimeout(0);
+        try {
+            hostnameVerifier.verify(host, sslsock);
+            // verifyHostName() didn't blowup - good!
+        } catch (IOException iox) {
+            // close the socket before re-throwing the exception
+            try { sslsock.close(); } catch (Exception x) {  }
+            throw iox;
+        }
+
+        return sslsock;
+       
+    }
+
+
+    /**
+     * Checks whether a socket connection is secure.
+     * This factory creates TLS/SSL socket connections
+     * which, by default, are considered secure.
+     * <br/>
+     * Derived classes may override this method to perform
+     * runtime checks, for example based on the cypher suite.
+     *
+     * @param sock      the connected socket
+     *
+     * @return  <code>true</code>
+     *
+     * @throws IllegalArgumentException if the argument is invalid
+     */
+    public boolean isSecure(Socket sock)
+        throws IllegalArgumentException {
+
+        if (sock == null) {
+            throw new IllegalArgumentException("Socket may not be null.");
+        }
+        // This instanceof check is in line with createSocket() above.
+        if (!(sock instanceof SSLSocket)) {
+            throw new IllegalArgumentException
+                ("Socket not created by this factory.");
+        }
+        // This check is performed last since it calls the argument object.
+        if (sock.isClosed()) {
+            throw new IllegalArgumentException("Socket is closed.");
+        }
+
+        return true;
+
+    } // isSecure
+
+
+    // non-javadoc, see interface LayeredSocketFactory
+    public Socket createSocket(
+        final Socket socket,
+        final String host,
+        final int port,
+        final boolean autoClose
+    ) throws IOException, UnknownHostException {
+        SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
+              socket,
+              host,
+              port,
+              autoClose
+        );
+        hostnameVerifier.verify(host, sslSocket);
+        // verifyHostName() didn't blowup - good!
+        return sslSocket;
+    }
+
+    public void setHostnameVerifier(X509HostnameVerifier hostnameVerifier) {
+        if ( hostnameVerifier == null ) {
+            throw new IllegalArgumentException("Hostname verifier may not be null");
+        }
+        this.hostnameVerifier = hostnameVerifier;
+    }
+
+    public X509HostnameVerifier getHostnameVerifier() {
+        return hostnameVerifier;
+    }
+
+    public class SSLException extends IOException {
+		private static final long serialVersionUID = 1L;
+
+		public SSLException(String msg) {
+			super(msg);
+		}
+    };
+    
+    
+}
\ No newline at end of file

Added: projects/android/trunk/Orbot/src/org/torproject/android/net/MyDefaultClientConnectionOperator.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/net/MyDefaultClientConnectionOperator.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/net/MyDefaultClientConnectionOperator.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,70 @@
+package org.torproject.android.net;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.impl.conn.DefaultClientConnectionOperator;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+public class MyDefaultClientConnectionOperator extends
+		DefaultClientConnectionOperator {
+
+	public MyDefaultClientConnectionOperator(SchemeRegistry schemes) {
+		super(schemes);
+	}
+	
+	@Override
+	public void openConnection(OperatedClientConnection conn, HttpHost target,
+			InetAddress local, HttpContext context, HttpParams params)
+			throws IOException {
+		if (conn == null) {
+            throw new IllegalArgumentException
+                ("Connection must not be null.");
+        }
+        if (target == null) {
+            throw new IllegalArgumentException
+                ("Target host must not be null.");
+        }
+        // local address may be null
+        //@@@ is context allowed to be null?
+        if (params == null) {
+            throw new IllegalArgumentException
+                ("Parameters must not be null.");
+        }
+        if (conn.isOpen()) {
+            throw new IllegalArgumentException
+                ("Connection must not be open.");
+        }
+
+        final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+        final SocketFactory sf = schm.getSocketFactory();
+
+        Socket sock = sf.createSocket();
+        conn.opening(sock, target);
+	
+        try {
+        	Socket connsock = sf.connectSocket(sock, target.getHostName(),
+                    schm.resolvePort(target.getPort()),
+                    local, 0, params);
+        	
+        			if (sock != connsock) {
+		                sock = connsock;
+		                conn.opening(sock, target);
+		            }
+        } catch (ConnectException ex) {
+            throw new HttpHostConnectException(target, ex);
+        }
+        prepareSocket(sock, context, params);
+        conn.openCompleted(sf.isSecure(sock), params);
+	}
+
+}

Added: projects/android/trunk/Orbot/src/org/torproject/android/net/MyThreadSafeClientConnManager.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/net/MyThreadSafeClientConnManager.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/net/MyThreadSafeClientConnManager.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,21 @@
+package org.torproject.android.net;
+
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.HttpParams;
+
+
+public class MyThreadSafeClientConnManager extends ThreadSafeClientConnManager {
+
+	public MyThreadSafeClientConnManager(HttpParams params, SchemeRegistry schreg) {
+		super(params, schreg);
+		
+	}
+
+	@Override
+	protected ClientConnectionOperator createConnectionOperator(
+			SchemeRegistry schreg) {
+		return new MyDefaultClientConnectionOperator(schreg);
+	}
+}

Added: projects/android/trunk/Orbot/src/org/torproject/android/net/SOCKSHttpClient.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/net/SOCKSHttpClient.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/net/SOCKSHttpClient.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,71 @@
+package org.torproject.android.net;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+
+public class SOCKSHttpClient extends DefaultHttpClient {
+
+	private final static String DEFAULT_HOST = "127.0.0.1";
+	private final static int DEFAULT_PORT = 9050;
+	
+	private static ClientConnectionManager ccm = null;
+	private static HttpParams params = null;
+	
+	public SOCKSHttpClient ()
+	{
+		
+       super(initConnectionManager(), initParams());
+
+
+	}
+	
+	private void setSystemProperties ()
+	{
+//       System.getProperties().put("socks.proxySet","true");
+  //     System.getProperties().put("socks.proxyHost",DEFAULT_HOST);
+	//	System.getProperties().put("socks.proxyPort", DEFAULT_PORT+"");
+
+	}
+	
+	private static ClientConnectionManager initConnectionManager ()
+	{
+		if (ccm == null)
+		{
+		SchemeRegistry supportedSchemes = new SchemeRegistry();
+		
+		
+		 supportedSchemes.register(new Scheme("http", 
+	                SocksSocketFactory.getSocketFactory(), 80));
+	    
+		 supportedSchemes.register(new Scheme("https", 
+	                ModSSLSocketFactory.getSocketFactory(), 443));
+	
+			ccm = new MyThreadSafeClientConnManager(initParams(), supportedSchemes);
+		}
+		
+      return ccm;
+	}
+	
+	private static HttpParams initParams ()
+	{
+	    if (params == null)
+	    {
+	      // prepare parameters
+	      params = new BasicHttpParams();
+	      HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+	      HttpProtocolParams.setContentCharset(params, "UTF-8");
+	      HttpProtocolParams.setUseExpectContinue(params, true);
+	    }
+	    
+	    return params;
+	}
+}

Added: projects/android/trunk/Orbot/src/org/torproject/android/net/SocksSocketFactory.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/net/SocksSocketFactory.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/net/SocksSocketFactory.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,144 @@
+/**
+ * Shadow - Anonymous web browser for Android devices
+ * Copyright (C) 2009 Connell Gauld
+ * 
+ * Thanks to University of Cambridge,
+ * 		Alastair Beresford and Andrew Rice
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ * 
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+package org.torproject.android.net;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import net.sourceforge.jsocks.socks.Socks5Proxy;
+import net.sourceforge.jsocks.socks.SocksException;
+import net.sourceforge.jsocks.socks.SocksSocket;
+
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.scheme.SocketFactory;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+
+import android.util.Log;
+
+
+/**
+ * Provides sockets for an HttpClient connection.
+ * @author cmg47
+ *
+ */
+public class SocksSocketFactory implements SocketFactory {
+
+	SocksSocket server = null;
+	private static Socks5Proxy sProxy = null;
+
+	private final static String DEFAULT_HOST = "127.0.0.1";
+	private final static int DEFAULT_PORT = 9050;
+	
+	/**
+	 * Construct a SocksSocketFactory that uses the provided SOCKS proxy.
+	 * @param proxyaddress the IP address of the SOCKS proxy
+	 * @param proxyport the port of the SOCKS proxy
+	 */
+	public SocksSocketFactory(String proxyaddress, int proxyport) {
+		
+
+		try {
+			sProxy = new Socks5Proxy(proxyaddress, proxyport);
+		} catch (UnknownHostException e) {
+			// TODO Auto-generated catch block
+			Log.i("TOR_SERVICE","SocksSF couldn't connect",e);
+		}
+		
+		sProxy.resolveAddrLocally(false);
+	
+
+	}
+	
+	@Override
+	public Socket connectSocket(Socket sock, String host, int port,
+			InetAddress localAddress, int localPort, HttpParams params) throws IOException,
+			UnknownHostException, ConnectTimeoutException {
+		
+		Log.i("TOR_SERVICE","SocksSocketFactory: connectSocket: " + host + ":" + port);
+		
+		if (host == null) {
+            throw new IllegalArgumentException("Target host may not be null.");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("Parameters may not be null.");
+        }
+
+     //   int timeout = HttpConnectionParams.getConnectionTimeout(params);
+        
+        // Pipe this socket over the proxy
+      //  sock = mSocksProxy.connectSocksProxy(sock, host, port, timeout);
+        
+        
+    
+		try {
+			sock =  new SocksSocket(sProxy,host, port);
+			
+
+			
+			sock.setSoTimeout(0); //indef
+			
+			
+			 if ((localAddress != null) || (localPort > 0)) {
+
+		            // we need to bind explicitly
+		            if (localPort < 0)
+		                localPort = 0; // indicates "any"
+
+		            InetSocketAddress isa =
+		                new InetSocketAddress(localAddress, localPort);
+		            sock.bind(isa);
+		        }
+
+			
+		} catch (SocksException e) {
+			Log.e("TOR_SERVICE","error connecting socks to" + host + ":" + port,e);
+		} catch (UnknownHostException e) {
+			Log.e("TOR_SERVICE","error connecting socks to" + host + ":" + port,e);
+		}
+		
+        return sock;
+		
+	}
+	
+    
+    
+	@Override
+	public Socket createSocket() throws IOException {
+		return new Socket();
+	}
+
+	@Override
+	public boolean isSecure(Socket sock) throws IllegalArgumentException {
+		return false;
+	}
+	
+	public static SocketFactory getSocketFactory ()
+	{
+		return new SocksSocketFactory (DEFAULT_HOST, DEFAULT_PORT);
+	}
+
+}

Added: projects/android/trunk/Orbot/src/org/torproject/android/service/NanoHTTPD.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/service/NanoHTTPD.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/service/NanoHTTPD.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,733 @@
+package org.torproject.android.service;
+
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+/**
+ * A simple, tiny, nicely embeddable HTTP 1.0 server in Java
+ *
+ * <p> NanoHTTPD version 1.12,
+ * Copyright &copy; 2001,2005-2010 Jarno Elonen (elonen at iki.fi, http://iki.fi/elonen/)
+ *
+ * <p><b>Features + limitations: </b><ul>
+ *
+ *    <li> Only one Java file </li>
+ *    <li> Java 1.1 compatible </li>
+ *    <li> Released as open source, Modified BSD licence </li>
+ *    <li> No fixed config files, logging, authorization etc. (Implement yourself if you need them.) </li>
+ *    <li> Supports parameter parsing of GET and POST methods </li>
+ *    <li> Supports both dynamic content and file serving </li>
+ *    <li> Never caches anything </li>
+ *    <li> Doesn't limit bandwidth, request time or simultaneous connections </li>
+ *    <li> Default code serves files and shows all HTTP parameters and headers</li>
+ *    <li> File server supports directory listing, index.html and index.htm </li>
+ *    <li> File server does the 301 redirection trick for directories without '/'</li>
+ *    <li> File server supports simple skipping for files (continue download) </li>
+ *    <li> File server uses current directory as a web root </li>
+ *    <li> File server serves also very long files without memory overhead </li>
+ *    <li> Contains a built-in list of most common mime types </li>
+ *    <li> All header names are converted lowercase so they don't vary between browsers/clients </li>
+ *
+ * </ul>
+ *
+ * <p><b>Ways to use: </b><ul>
+ *
+ *    <li> Run as a standalone app, serves files from current directory and shows requests</li>
+ *    <li> Subclass serve() and embed to your own program </li>
+ *    <li> Call serveFile() from serve() with your own base directory </li>
+ *
+ * </ul>
+ *
+ * See the end of the source file for distribution license
+ * (Modified BSD licence)
+ */
+public class NanoHTTPD
+{
+	// ==================================================
+	// API parts
+	// ==================================================
+	
+	private boolean keepRunning = true;
+	private Thread t = null;
+	
+	public void stop ()
+	{
+		keepRunning = false;
+		t.interrupt();
+	}
+
+	/**
+	 * Override this to customize the server.<p>
+	 *
+	 * (By default, this delegates to serveFile() and allows directory listing.)
+	 *
+	 * @parm uri	Percent-decoded URI without parameters, for example "/index.cgi"
+	 * @parm method	"GET", "POST" etc.
+	 * @parm parms	Parsed, percent decoded parameters from URI and, in case of POST, data.
+	 * @parm header	Header entries, percent decoded
+	 * @return HTTP response, see class Response for details
+	 */
+	public Response serve( String uri, String method, Properties header, Properties parms )
+	{
+		System.out.println( method + " '" + uri + "' " );
+
+		Enumeration e = header.propertyNames();
+		while ( e.hasMoreElements())
+		{
+			String value = (String)e.nextElement();
+			System.out.println( "  HDR: '" + value + "' = '" +
+								header.getProperty( value ) + "'" );
+		}
+		e = parms.propertyNames();
+		while ( e.hasMoreElements())
+		{
+			String value = (String)e.nextElement();
+			System.out.println( "  PRM: '" + value + "' = '" +
+								parms.getProperty( value ) + "'" );
+		}
+
+		return serveFile( uri, header, new File("."), true );
+	}
+
+	/**
+	 * HTTP response.
+	 * Return one of these from serve().
+	 */
+	public class Response
+	{
+		/**
+		 * Default constructor: response = HTTP_OK, data = mime = 'null'
+		 */
+		public Response()
+		{
+			this.status = HTTP_OK;
+		}
+
+		/**
+		 * Basic constructor.
+		 */
+		public Response( String status, String mimeType, InputStream data )
+		{
+			this.status = status;
+			this.mimeType = mimeType;
+			this.data = data;
+		}
+
+		/**
+		 * Convenience method that makes an InputStream out of
+		 * given text.
+		 */
+		public Response( String status, String mimeType, String txt )
+		{
+			this.status = status;
+			this.mimeType = mimeType;
+			this.data = new ByteArrayInputStream( txt.getBytes());
+		}
+
+		/**
+		 * Adds given line to the header.
+		 */
+		public void addHeader( String name, String value )
+		{
+			header.put( name, value );
+		}
+
+		/**
+		 * HTTP status code after processing, e.g. "200 OK", HTTP_OK
+		 */
+		public String status;
+
+		/**
+		 * MIME type of content, e.g. "text/html"
+		 */
+		public String mimeType;
+
+		/**
+		 * Data of the response, may be null.
+		 */
+		public InputStream data;
+
+		/**
+		 * Headers for the HTTP response. Use addHeader()
+		 * to add lines.
+		 */
+		public Properties header = new Properties();
+	}
+
+	/**
+	 * Some HTTP response status codes
+	 */
+	public static final String
+		HTTP_OK = "200 OK",
+		HTTP_REDIRECT = "301 Moved Permanently",
+		HTTP_FORBIDDEN = "403 Forbidden",
+		HTTP_NOTFOUND = "404 Not Found",
+		HTTP_BADREQUEST = "400 Bad Request",
+		HTTP_INTERNALERROR = "500 Internal Server Error",
+		HTTP_NOTIMPLEMENTED = "501 Not Implemented";
+
+	/**
+	 * Common mime types for dynamic content
+	 */
+	public static final String
+		MIME_PLAINTEXT = "text/plain",
+		MIME_HTML = "text/html",
+		MIME_DEFAULT_BINARY = "application/octet-stream";
+
+	// ==================================================
+	// Socket & server code
+	// ==================================================
+
+	/**
+	 * Starts a HTTP server to given port.<p>
+	 * Throws an IOException if the socket is already in use
+	 */
+	public NanoHTTPD( int port ) throws IOException
+	{
+		myTcpPort = port;
+
+		final ServerSocket ss = new ServerSocket( myTcpPort );
+		t = new Thread( new Runnable()
+			{
+				public void run()
+				{
+					try
+					{
+						while( keepRunning )
+							new HTTPSession( ss.accept());
+					}
+					catch ( IOException ioe )
+					{}
+				}
+			});
+		t.setDaemon( true );
+		t.start();
+	}
+
+	/**
+	 * Starts as a standalone file server and waits for Enter.
+	 */
+	public static void main( String[] args )
+	{
+		System.out.println( "NanoHTTPD 1.12 (C) 2001,2005-2010 Jarno Elonen\n" +
+							"(Command line options: [port] [--licence])\n" );
+
+		// Show licence if requested
+		int lopt = -1;
+		for ( int i=0; i<args.length; ++i )
+		if ( args[i].toLowerCase().endsWith( "licence" ))
+		{
+			lopt = i;
+			System.out.println( LICENCE + "\n" );
+		}
+
+		// Change port if requested
+		int port = 80;
+		if ( args.length > 0 && lopt != 0 )
+			port = Integer.parseInt( args[0] );
+
+		if ( args.length > 1 &&
+			 args[1].toLowerCase().endsWith( "licence" ))
+				System.out.println( LICENCE + "\n" );
+
+		NanoHTTPD nh = null;
+		try
+		{
+			nh = new NanoHTTPD( port );
+		}
+		catch( IOException ioe )
+		{
+			System.err.println( "Couldn't start server:\n" + ioe );
+			System.exit( -1 );
+		}
+		nh.myFileDir = new File("");
+
+		System.out.println( "Now serving files in port " + port + " from \"" +
+							new File("").getAbsolutePath() + "\"" );
+		System.out.println( "Hit Enter to stop.\n" );
+
+		try { System.in.read(); } catch( Throwable t ) {};
+	}
+
+	/**
+	 * Handles one session, i.e. parses the HTTP request
+	 * and returns the response.
+	 */
+	private class HTTPSession implements Runnable
+	{
+		public HTTPSession( Socket s )
+		{
+			mySocket = s;
+			Thread t = new Thread( this );
+			t.setDaemon( true );
+			t.start();
+		}
+
+		public void run()
+		{
+			try
+			{
+				InputStream is = mySocket.getInputStream();
+				if ( is == null) return;
+				BufferedReader in = new BufferedReader( new InputStreamReader( is ));
+
+				// Read the request line
+				String inLine = in.readLine();
+				if (inLine == null) return;
+				StringTokenizer st = new StringTokenizer( inLine );
+				if ( !st.hasMoreTokens())
+					sendError( HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html" );
+
+				String method = st.nextToken();
+
+				if ( !st.hasMoreTokens())
+					sendError( HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html" );
+
+				String uri = st.nextToken();
+
+				// Decode parameters from the URI
+				Properties parms = new Properties();
+				int qmi = uri.indexOf( '?' );
+				if ( qmi >= 0 )
+				{
+					decodeParms( uri.substring( qmi+1 ), parms );
+					uri = decodePercent( uri.substring( 0, qmi ));
+				}
+				else uri = decodePercent(uri);
+
+
+				// If there's another token, it's protocol version,
+				// followed by HTTP headers. Ignore version but parse headers.
+				// NOTE: this now forces header names uppercase since they are
+				// case insensitive and vary by client.
+				Properties header = new Properties();
+				if ( st.hasMoreTokens())
+				{
+					String line = in.readLine();
+					while ( line.trim().length() > 0 )
+					{
+						int p = line.indexOf( ':' );
+						header.put( line.substring(0,p).trim().toLowerCase(), line.substring(p+1).trim());
+						line = in.readLine();
+					}
+				}
+
+				// If the method is POST, there may be parameters
+				// in data section, too, read it:
+				if ( method.equalsIgnoreCase( "POST" ))
+				{
+					long size = 0x7FFFFFFFFFFFFFFFl;
+					String contentLength = header.getProperty("content-length");
+					if (contentLength != null)
+					{
+						try { size = Integer.parseInt(contentLength); }
+						catch (NumberFormatException ex) {}
+					}
+					String postLine = "";
+					char buf[] = new char[512];
+					int read = in.read(buf);
+					while ( read >= 0 && size > 0 && !postLine.endsWith("\r\n") )
+					{
+						size -= read;
+						postLine += String.valueOf(buf, 0, read);
+						if ( size > 0 )
+							read = in.read(buf);
+					}
+					postLine = postLine.trim();
+					decodeParms( postLine, parms );
+				}
+
+				// Ok, now do the serve()
+				Response r = serve( uri, method, header, parms );
+				if ( r == null )
+					sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: Serve() returned a null response." );
+				else
+					sendResponse( r.status, r.mimeType, r.header, r.data );
+
+				in.close();
+			}
+			catch ( IOException ioe )
+			{
+				try
+				{
+					sendError( HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
+				}
+				catch ( Throwable t ) {}
+			}
+			catch ( InterruptedException ie )
+			{
+				// Thrown by sendError, ignore and exit the thread.
+			}
+		}
+
+		/**
+		 * Decodes the percent encoding scheme. <br/>
+		 * For example: "an+example%20string" -> "an example string"
+		 */
+		private String decodePercent( String str ) throws InterruptedException
+		{
+			try
+			{
+				StringBuffer sb = new StringBuffer();
+				for( int i=0; i<str.length(); i++ )
+				{
+				    char c = str.charAt( i );
+				    switch ( c )
+					{
+				        case '+':
+				            sb.append( ' ' );
+				            break;
+				        case '%':
+			                sb.append((char)Integer.parseInt( str.substring(i+1,i+3), 16 ));
+				            i += 2;
+				            break;
+				        default:
+				            sb.append( c );
+				            break;
+				    }
+				}
+				return new String( sb.toString().getBytes());
+			}
+			catch( Exception e )
+			{
+				sendError( HTTP_BADREQUEST, "BAD REQUEST: Bad percent-encoding." );
+				return null;
+			}
+		}
+
+		/**
+		 * Decodes parameters in percent-encoded URI-format
+		 * ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and
+		 * adds them to given Properties. NOTE: this doesn't support multiple
+		 * identical keys due to the simplicity of Properties -- if you need multiples,
+		 * you might want to replace the Properties with a Hastable of Vectors or such.
+		 */
+		private void decodeParms( String parms, Properties p )
+			throws InterruptedException
+		{
+			if ( parms == null )
+				return;
+
+			StringTokenizer st = new StringTokenizer( parms, "&" );
+			while ( st.hasMoreTokens())
+			{
+				String e = st.nextToken();
+				int sep = e.indexOf( '=' );
+				if ( sep >= 0 )
+					p.put( decodePercent( e.substring( 0, sep )).trim(),
+						   decodePercent( e.substring( sep+1 )));
+			}
+		}
+
+		/**
+		 * Returns an error message as a HTTP response and
+		 * throws InterruptedException to stop furhter request processing.
+		 */
+		private void sendError( String status, String msg ) throws InterruptedException
+		{
+			sendResponse( status, MIME_PLAINTEXT, null, new ByteArrayInputStream( msg.getBytes()));
+			throw new InterruptedException();
+		}
+
+		/**
+		 * Sends given response to the socket.
+		 */
+		private void sendResponse( String status, String mime, Properties header, InputStream data )
+		{
+			try
+			{
+				if ( status == null )
+					throw new Error( "sendResponse(): Status can't be null." );
+
+				OutputStream out = mySocket.getOutputStream();
+				PrintWriter pw = new PrintWriter( out );
+				pw.print("HTTP/1.0 " + status + " \r\n");
+
+				if ( mime != null )
+					pw.print("Content-Type: " + mime + "\r\n");
+
+				if ( header == null || header.getProperty( "Date" ) == null )
+					pw.print( "Date: " + gmtFrmt.format( new Date()) + "\r\n");
+
+				if ( header != null )
+				{
+					Enumeration e = header.keys();
+					while ( e.hasMoreElements())
+					{
+						String key = (String)e.nextElement();
+						String value = header.getProperty( key );
+						pw.print( key + ": " + value + "\r\n");
+					}
+				}
+
+				pw.print("\r\n");
+				pw.flush();
+
+				if ( data != null )
+				{
+					byte[] buff = new byte[2048];
+					while (true)
+					{
+						int read = data.read( buff, 0, 2048 );
+						if (read <= 0)
+							break;
+						out.write( buff, 0, read );
+					}
+				}
+				out.flush();
+				out.close();
+				if ( data != null )
+					data.close();
+			}
+			catch( IOException ioe )
+			{
+				// Couldn't write? No can do.
+				try { mySocket.close(); } catch( Throwable t ) {}
+			}
+		}
+
+		private Socket mySocket;
+	};
+
+	/**
+	 * URL-encodes everything between "/"-characters.
+	 * Encodes spaces as '%20' instead of '+'.
+	 */
+	private String encodeUri( String uri )
+	{
+		String newUri = "";
+		StringTokenizer st = new StringTokenizer( uri, "/ ", true );
+		while ( st.hasMoreTokens())
+		{
+			String tok = st.nextToken();
+			if ( tok.equals( "/" ))
+				newUri += "/";
+			else if ( tok.equals( " " ))
+				newUri += "%20";
+			else
+			{
+				newUri += URLEncoder.encode( tok );
+				// For Java 1.4 you'll want to use this instead:
+				// try { newUri += URLEncoder.encode( tok, "UTF-8" ); } catch ( UnsupportedEncodingException uee )
+			}
+		}
+		return newUri;
+	}
+
+	private int myTcpPort;
+	File myFileDir;
+
+	// ==================================================
+	// File server code
+	// ==================================================
+
+	/**
+	 * Serves file from homeDir and its' subdirectories (only).
+	 * Uses only URI, ignores all headers and HTTP parameters.
+	 */
+	public Response serveFile( String uri, Properties header, File homeDir,
+							   boolean allowDirectoryListing )
+	{
+		// Make sure we won't die of an exception later
+		if ( !homeDir.isDirectory())
+			return new Response( HTTP_INTERNALERROR, MIME_PLAINTEXT,
+								 "INTERNAL ERRROR: serveFile(): given homeDir is not a directory." );
+
+		// Remove URL arguments
+		uri = uri.trim().replace( File.separatorChar, '/' );
+		if ( uri.indexOf( '?' ) >= 0 )
+			uri = uri.substring(0, uri.indexOf( '?' ));
+
+		// Prohibit getting out of current directory
+		if ( uri.startsWith( ".." ) || uri.endsWith( ".." ) || uri.indexOf( "../" ) >= 0 )
+			return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
+								 "FORBIDDEN: Won't serve ../ for security reasons." );
+
+		File f = new File( homeDir, uri );
+		if ( !f.exists())
+			return new Response( HTTP_NOTFOUND, MIME_PLAINTEXT,
+								 "Error 404, file not found." );
+
+		// List the directory, if necessary
+		if ( f.isDirectory())
+		{
+			// Browsers get confused without '/' after the
+			// directory, send a redirect.
+			if ( !uri.endsWith( "/" ))
+			{
+				uri += "/";
+				Response r = new Response( HTTP_REDIRECT, MIME_HTML,
+										   "<html><body>Redirected: <a href=\"" + uri + "\">" +
+										   uri + "</a></body></html>");
+				r.addHeader( "Location", uri );
+				return r;
+			}
+
+			// First try index.html and index.htm
+			if ( new File( f, "index.html" ).exists())
+				f = new File( homeDir, uri + "/index.html" );
+			else if ( new File( f, "index.htm" ).exists())
+				f = new File( homeDir, uri + "/index.htm" );
+
+			// No index file, list the directory
+			else if ( allowDirectoryListing )
+			{
+				String[] files = f.list();
+				String msg = "<html><body><h1>Directory " + uri + "</h1><br/>";
+
+				if ( uri.length() > 1 )
+				{
+					String u = uri.substring( 0, uri.length()-1 );
+					int slash = u.lastIndexOf( '/' );
+					if ( slash >= 0 && slash  < u.length())
+						msg += "<b><a href=\"" + uri.substring(0, slash+1) + "\">..</a></b><br/>";
+				}
+
+				for ( int i=0; i<files.length; ++i )
+				{
+					File curFile = new File( f, files[i] );
+					boolean dir = curFile.isDirectory();
+					if ( dir )
+					{
+						msg += "<b>";
+						files[i] += "/";
+					}
+
+					msg += "<a href=\"" + encodeUri( uri + files[i] ) + "\">" +
+						   files[i] + "</a>";
+
+					// Show file size
+					if ( curFile.isFile())
+					{
+						long len = curFile.length();
+						msg += " &nbsp;<font size=2>(";
+						if ( len < 1024 )
+							msg += curFile.length() + " bytes";
+						else if ( len < 1024 * 1024 )
+							msg += curFile.length()/1024 + "." + (curFile.length()%1024/10%100) + " KB";
+						else
+							msg += curFile.length()/(1024*1024) + "." + curFile.length()%(1024*1024)/10%100 + " MB";
+
+						msg += ")</font>";
+					}
+					msg += "<br/>";
+					if ( dir ) msg += "</b>";
+				}
+				return new Response( HTTP_OK, MIME_HTML, msg );
+			}
+			else
+			{
+				return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT,
+								 "FORBIDDEN: No directory listing." );
+			}
+		}
+
+		try
+		{
+			// Get MIME type from file name extension, if possible
+			String mime = null;
+			int dot = f.getCanonicalPath().lastIndexOf( '.' );
+			if ( dot >= 0 )
+				mime = (String)theMimeTypes.get( f.getCanonicalPath().substring( dot + 1 ).toLowerCase());
+			if ( mime == null )
+				mime = MIME_DEFAULT_BINARY;
+
+			// Support (simple) skipping:
+			long startFrom = 0;
+			String range = header.getProperty( "Range" );
+			if ( range != null )
+			{
+				if ( range.startsWith( "bytes=" ))
+				{
+					range = range.substring( "bytes=".length());
+					int minus = range.indexOf( '-' );
+					if ( minus > 0 )
+						range = range.substring( 0, minus );
+					try	{
+						startFrom = Long.parseLong( range );
+					}
+					catch ( NumberFormatException nfe ) {}
+				}
+			}
+
+			FileInputStream fis = new FileInputStream( f );
+			fis.skip( startFrom );
+			Response r = new Response( HTTP_OK, mime, fis );
+			r.addHeader( "Content-length", "" + (f.length() - startFrom));
+			r.addHeader( "Content-range", "" + startFrom + "-" +
+						(f.length()-1) + "/" + f.length());
+			return r;
+		}
+		catch( IOException ioe )
+		{
+			return new Response( HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed." );
+		}
+	}
+
+	/**
+	 * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE
+	 */
+	private static Hashtable theMimeTypes = new Hashtable();
+	static
+	{
+		StringTokenizer st = new StringTokenizer(
+			"htm		text/html "+
+			"html		text/html "+
+			"txt		text/plain "+
+			"asc		text/plain "+
+			"gif		image/gif "+
+			"jpg		image/jpeg "+
+			"jpeg		image/jpeg "+
+			"png		image/png "+
+			"mp3		audio/mpeg "+
+			"m3u		audio/mpeg-url " +
+			"pdf		application/pdf "+
+			"doc		application/msword "+
+			"ogg		application/x-ogg "+
+			"zip		application/octet-stream "+
+			"exe		application/octet-stream "+
+			"class		application/octet-stream " );
+		while ( st.hasMoreTokens())
+			theMimeTypes.put( st.nextToken(), st.nextToken());
+	}
+
+	/**
+	 * GMT date formatter
+	 */
+    private static java.text.SimpleDateFormat gmtFrmt;
+	static
+	{
+		gmtFrmt = new java.text.SimpleDateFormat( "E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
+		gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
+	}
+
+	/**
+	 * The distribution licence
+	 */
+	private static final String LICENCE =
+		"Copyright (C) 2001,2005-2008 by Jarno Elonen <elonen at iki.fi>\n"+
+		"\n"+
+		"Redistribution and use in source and binary forms, with or without\n"+
+		"modification, are permitted provided that the following conditions\n"+
+		"are met:\n"+
+		"\n"+
+		"Redistributions of source code must retain the above copyright notice,\n"+
+		"this list of conditions and the following disclaimer. Redistributions in\n"+
+		"binary form must reproduce the above copyright notice, this list of\n"+
+		"conditions and the following disclaimer in the documentation and/or other\n"+
+		"materials provided with the distribution. The name of the author may not\n"+
+		"be used to endorse or promote products derived from this software without\n"+
+		"specific prior written permission. \n"+
+		" \n"+
+		"THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"+
+		"IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n"+
+		"OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n"+
+		"IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n"+
+		"INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n"+
+		"NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"+
+		"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"+
+		"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"+
+		"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"+
+		"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.";
+}
\ No newline at end of file

Modified: projects/android/trunk/Orbot/src/org/torproject/android/service/TorService.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/service/TorService.java	2010-03-18 01:05:56 UTC (rev 22004)
+++ projects/android/trunk/Orbot/src/org/torproject/android/service/TorService.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -42,6 +42,8 @@
 	
 	private static final int NOTIFY_ID = 1;
 	
+	/* removing this for now - it is a work in progress and a security risk - 3/17/2010 */
+	//private static TorWebProxy _webProxy;
 	
 	
     /** Called when the activity is first created. */
@@ -307,6 +309,22 @@
 			procId = TorServiceUtils.findProcessId(TorServiceConstants.PRIVOXY_INSTALL_PATH);
 		}
 		
+		/*
+		 //removing this for now
+		if (_webProxy != null)
+		{
+			try
+			{
+				//shutdown web proxy
+				_webProxy.stop();
+				_webProxy = null;
+			}
+			catch (Exception e)
+			{
+				Log.i(TAG,"error stopping web proxy",e);
+			}
+		}*/
+		
     }
    
     private void logNotice (String msg)
@@ -400,6 +418,13 @@
     			}
     		}.start();
     		
+    		/*
+    		//removing this for now - nf - 3/17/2010
+    		if (_webProxy == null)
+    		{
+    			_webProxy = new TorWebProxy();
+    			
+    		}*/
 			
     }
     

Modified: projects/android/trunk/Orbot/src/org/torproject/android/service/TorServiceUtils.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/service/TorServiceUtils.java	2010-03-18 01:05:56 UTC (rev 22004)
+++ projects/android/trunk/Orbot/src/org/torproject/android/service/TorServiceUtils.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -209,7 +209,7 @@
 			}
             
         } catch (Exception e) {
-            Log.e(TAG, "Error executing shell cmd: " + e.getMessage(),e);
+            Log.w(TAG, "Error executing shell cmd: " + e.getMessage());
         }
         
         return exitCode;

Modified: projects/android/trunk/Orbot/src/org/torproject/android/service/TorTransProxy.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/service/TorTransProxy.java	2010-03-18 01:05:56 UTC (rev 22004)
+++ projects/android/trunk/Orbot/src/org/torproject/android/service/TorTransProxy.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -97,12 +97,21 @@
 				{
 					Log.i(TAG,"enabling transproxy for app: " + apps[i].getUsername() + "(" + apps[i].getUid() + ")");
 				 
+					//TCP
 					script.append("iptables -t nat");
 					script.append(command);
 					script.append("OUTPUT -p tcp -m owner --uid-owner ");
 					script.append(apps[i].getUid());
 					script.append(" -j DNAT --to 127.0.0.1:9040");
 					script.append(" || exit\n");
+					
+					//UDP
+					script.append("iptables -t nat");
+					script.append(command);
+					script.append("OUTPUT -p udp -m owner --uid-owner ");
+					script.append(apps[i].getUid());
+					script.append(" -j DNAT --to 127.0.0.1:9040");
+					script.append(" || exit\n");
 				}		
 			}
 			
@@ -120,5 +129,52 @@
 		}
 		return false;
     }	
+	
 
+	public static boolean setTransparentProxyingByPort(Context context, String[] ports) {
+		
+		String command = null;
+		
+		command = IPTABLES_ADD; //ADD
+		
+    	final StringBuilder script = new StringBuilder();
+    	
+		try {
+			int code;
+			
+			for (int i = 0; i < ports.length; i++)
+			{
+				Log.i(TAG,"enabling transproxy for port: " + ports[i]);
+				 
+				//TCP
+				script.append("iptables -t nat");
+				script.append("-A PREROUTING -p tcp --dport ");
+				script.append(ports[i]);
+				script.append(" -j DNAT --to 127.0.0.1:9040");
+				script.append(" || exit\n");
+				
+				//UDP
+				script.append("iptables -t nat");
+				script.append("-A PREROUTING -p udp --dport ");
+				script.append(ports[i]);
+				script.append(" -j DNAT --to 127.0.0.1:9040");
+				script.append(" || exit\n");
+					
+			}
+			
+	    	StringBuilder res = new StringBuilder();
+	    	
+	    	String[] cmd = {script.toString()};
+	    	
+			code = TorServiceUtils.doShellCommand(cmd, res, true, true);
+			
+				String msg = res.toString();
+				Log.e(TAG, msg);
+			
+		} catch (Exception e) {
+			Log.w(TAG, "error refreshing iptables: " + e);
+		}
+		return false;
+    }
+
 }

Added: projects/android/trunk/Orbot/src/org/torproject/android/service/TorWebProxy.java
===================================================================
--- projects/android/trunk/Orbot/src/org/torproject/android/service/TorWebProxy.java	                        (rev 0)
+++ projects/android/trunk/Orbot/src/org/torproject/android/service/TorWebProxy.java	2010-03-18 03:15:20 UTC (rev 22005)
@@ -0,0 +1,254 @@
+package org.torproject.android.service;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Map.Entry;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.torproject.android.Utils;
+
+import android.util.Log;
+
+public class TorWebProxy  extends NanoHTTPD implements TorServiceConstants
+{
+	private final static String BASE_URI = "http://localhost:8888/";
+	
+	private final static String DEFAULT_URI = "https://check.torproject.org/";
+	private java.net.URLDecoder decoder;
+	
+	private String lastPage = null;
+	
+	private final static int PORT = 8888;
+	
+	public TorWebProxy() throws IOException
+	{
+		super(PORT);
+		
+		decoder = new java.net.URLDecoder();
+		
+		Log.i(TAG,"TorWebProxy started on port: " + PORT);
+	}
+
+	public Response serve( String uri, String method, Properties header, Properties params )
+	{
+		Log.i(TAG,"TorWebProxy serve(): " +  method + " '" + uri + "' " );
+		
+		
+		InputStream contentStream = null;
+		
+		String requestUrl = null;
+		
+		if (uri.toLowerCase().startsWith("/http"))
+		{
+			//okay this is cool
+			requestUrl = uri.substring(1);
+			requestUrl = decoder.decode(requestUrl);
+		}
+		else if (params.getProperty("url")!=null)
+		{
+			requestUrl = params.getProperty("url");
+		}
+		else if (uri.equals("/"))
+		{
+			requestUrl = DEFAULT_URI;
+		}
+		else //must be a relative path
+		{
+			if (lastPage != null)
+			{
+				
+				if (!uri.startsWith("/"))
+					uri = "/" + uri;
+				
+				try {
+					URL lastPageUrl = new URL(lastPage);
+					
+					StringBuilder sb = new StringBuilder();
+					sb.append(lastPageUrl.getProtocol());
+					sb.append("://");
+					sb.append(lastPageUrl.getHost());
+					
+					if (lastPageUrl.getPort()!=-1)
+					{
+						sb.append(":");
+						sb.append(lastPageUrl.getPort());
+					}
+					
+					sb.append(uri);
+					
+					requestUrl = sb.toString();
+					
+				} catch (MalformedURLException e) {
+					Log.i(TAG, "TorWebProxy: " + e.getLocalizedMessage(),e);
+					
+					return new NanoHTTPD.Response(NanoHTTPD.HTTP_INTERNALERROR,"text/plain","Something bad happened: " + e.getLocalizedMessage());
+
+				}
+				
+				
+			}
+		}
+		
+		HttpUriRequest request = null;
+		HttpHost host = null;
+		
+
+		URI rURI = null;
+		try {
+			rURI = new URI(requestUrl);
+		} catch (URISyntaxException e) {
+			Log.e(TAG,"error parsing uri: " + requestUrl,e);
+			return new NanoHTTPD.Response(NanoHTTPD.HTTP_INTERNALERROR,"text/plain","error");
+
+		}
+		
+		int port = rURI.getPort();
+		
+		if (port == -1)
+		{
+			if (rURI.getScheme().equalsIgnoreCase("http"))
+				port = 80;
+			else if (rURI.getScheme().equalsIgnoreCase("https"))
+				port = 443;
+		}
+		
+		host = new HttpHost(rURI.getHost(),port, rURI.getScheme());
+		
+		Log.i(TAG,"TorWebProxy server(): host=" + host.getSchemeName() + "://" + host.getHostName() + ":" + host.getPort());
+		
+		if (method.equalsIgnoreCase("get"))
+		{
+			Log.i(TAG,"TorWebProxy serve(): GET: " + rURI.getPath() );
+			request = new HttpGet (rURI.getPath());
+		}
+		else if (method.equalsIgnoreCase("post"))
+		{
+			Log.i(TAG,"TorWebProxy serve(): POST: " + rURI.getPath() );
+
+			request = new HttpPost(rURI.getPath());
+			
+
+			//request = new HttpPost (requestUrl);
+			
+			Iterator<Entry<Object,Object>> itSet = params.entrySet().iterator();
+			
+			Entry<Object,Object> entry = null;
+			
+			HttpParams hParams = request.getParams();
+			
+			while (itSet.hasNext())
+			{
+				entry = itSet.next();
+				
+				hParams.setParameter((String)entry.getKey(), entry.getValue());
+			}
+			
+			request.setParams(hParams);
+			
+		}
+		else
+		{
+			return new NanoHTTPD.Response(NanoHTTPD.HTTP_NOTIMPLEMENTED,"text/plain","No support for the method: " + method);
+
+		}
+		
+	//	SOCKSHttpClient client = new SOCKSHttpClient();
+		
+		HttpHost proxy = new HttpHost("127.0.0.1", 8118, "http");
+		SchemeRegistry supportedSchemes = new SchemeRegistry();
+		// Register the "http" and "https" protocol schemes, they are
+		// required by the default operator to look up socket factories.
+		supportedSchemes.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
+		supportedSchemes.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
+		// prepare parameters
+		HttpParams hparams = new BasicHttpParams();
+		HttpProtocolParams.setVersion(hparams, HttpVersion.HTTP_1_1);
+		HttpProtocolParams.setContentCharset(hparams, "UTF-8");
+		HttpProtocolParams.setUseExpectContinue(hparams, true);
+		ClientConnectionManager ccm = new ThreadSafeClientConnManager(hparams, supportedSchemes);
+
+		DefaultHttpClient client = new DefaultHttpClient(ccm, hparams);
+		client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
+
+		
+		try {
+			HttpResponse response = client.execute(host, request);
+			
+			if (response.getEntity() == null || response.getEntity().getContentType() == null)
+			{
+				return new NanoHTTPD.Response(NanoHTTPD.HTTP_INTERNALERROR,"text/plain","Something bad happened");
+
+			}
+			
+			String contentType = response.getEntity().getContentType().getValue();
+			
+			int respCode = response.getStatusLine().getStatusCode();
+			
+			Log.i(TAG,"TorWebProxy server(): resp=" + respCode + ";" + contentType);
+			
+			contentStream = response.getEntity().getContent();
+			
+			if (contentType.indexOf("text/html")!=-1)
+			{
+				response.getEntity().getContentLength();
+				
+				lastPage = requestUrl;
+				
+				String page = Utils.readString(contentStream);
+				
+				page = page.replace("href=\"", "href=\"" + BASE_URI);
+				page = page.replace("src=\"", "src=\"" + BASE_URI);
+				page = page.replace("action=\"", "action=\"" + BASE_URI);
+				
+				page = page.replace("HREF=\"", "href=\"" + BASE_URI);
+				page = page.replace("SRC=\"", "src=\"" + BASE_URI);
+				page = page.replace("ACTION=\"", "action=\"" + BASE_URI);
+				
+				
+				return new NanoHTTPD.Response( HTTP_OK, contentType, page );
+			}
+			else
+				return new NanoHTTPD.Response( HTTP_OK, contentType, contentStream );
+		
+		} catch (ClientProtocolException e) {
+			Log.w(TAG,"TorWebProxy",e);
+			
+		} catch (IOException e) {
+			Log.w(TAG,"TorWebProxy",e);
+			
+		}
+		catch (NullPointerException e)
+		{
+			Log.w(TAG,"TorWebProxy",e);
+		}
+		
+		return new NanoHTTPD.Response(NanoHTTPD.HTTP_INTERNALERROR,"text/plain","Something bad happened");
+		
+	}
+
+
+	
+}
\ No newline at end of file



More information about the tor-commits mailing list