2009-04-21 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / System / System.Net / EndPointListener.cs
index b2bc1d9523eb56e9ae60deabd6e62ff05d9d0a13..23aff2518c2a1f74121932824735d4518fa9dfa4 100644 (file)
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
+
+#if NET_2_0 && SECURITY_DEP
+
 using System.IO;
 using System.Net.Sockets;
-using System.Collections.Generic;
+using System.Collections;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Mono.Security.Authenticode;
+
 namespace System.Net {
        sealed class EndPointListener
        {
                IPEndPoint endpoint;
                Socket sock;
-               Dictionary<ListenerPrefix, HttpListener> prefixes;
-               List<ListenerPrefix> unhandled; // host = '*'
-               List<ListenerPrefix> all; // host = '+'
-               bool secure; // Can a port have listeners for secure and not secure at the same time?
+               Hashtable prefixes;  // Dictionary <ListenerPrefix, HttpListener>
+               ArrayList unhandled; // List<ListenerPrefix> unhandled; host = '*'
+               ArrayList all;       // List<ListenerPrefix> all;  host = '+'
+               X509Certificate2 cert;
+               AsymmetricAlgorithm key;
+               bool secure;
 
                public EndPointListener (IPAddress addr, int port, bool secure)
                {
+                       if (secure) {
+                               this.secure = secure;
+                               LoadCertificateAndKey (addr, port);
+                       }
+
                        endpoint = new IPEndPoint (addr, port);
                        sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                        sock.Bind (endpoint);
                        sock.Listen (500);
                        sock.BeginAccept (OnAccept, this);
-                       prefixes = new Dictionary<ListenerPrefix, HttpListener> ();
-                       this.secure = secure;
+                       prefixes = new Hashtable ();
+               }
+
+               void LoadCertificateAndKey (IPAddress addr, int port)
+               {
+                       // Actually load the certificate
+                       try {
+                               string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
+                               string path = Path.Combine (dirname, ".mono");
+                               path = Path.Combine (path, "httplistener");
+                               string cert_file = Path.Combine (path, String.Format ("{0}.cer", port));
+                               string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port));
+                               cert = new X509Certificate2 (cert_file);
+                               key = PrivateKey.CreateFromFile (pvk_file).RSA;
+                       } catch {
+                               // ignore errors
+                       }
                }
 
                static void OnAccept (IAsyncResult ares)
@@ -59,10 +87,26 @@ namespace System.Net {
                        } catch {
                                // Anything to do here?
                        } finally {
-                               epl.sock.BeginAccept (OnAccept, epl);
+                               try {
+                                       epl.sock.BeginAccept (OnAccept, epl);
+                               } catch {
+                                       if (accepted != null) {
+                                               try {
+                                                       accepted.Close ();
+                                               } catch {}
+                                               accepted = null;
+                                       }
+                               } 
                        }
 
-                       HttpConnection conn = new HttpConnection (accepted, epl, epl.secure);
+                       if (accepted == null)
+                               return;
+
+                       if (epl.secure && (epl.cert == null || epl.key == null)) {
+                               accepted.Close ();
+                               return;
+                       }
+                       HttpConnection conn = new HttpConnection (accepted, epl, epl.secure, epl.cert, epl.key);
                        conn.BeginReadRequest ();
                }
 
@@ -99,30 +143,41 @@ namespace System.Net {
                                return null;
 
                        //TODO: We should use a ReaderWriterLock between this and the add/remove operations.
-                       int colon = host.IndexOf (':');
-                       if (colon >= 0)
-                               host = host.Substring (0, colon);
+                       if (host != null) {
+                               int colon = host.IndexOf (':');
+                               if (colon >= 0)
+                                       host = host.Substring (0, colon);
+                       }
 
-                       string path = HttpUtility.UrlDecode (raw_url);
+                       string path;
+                       Uri raw_uri;
+                       if (Uri.MaybeUri (raw_url) && Uri.TryCreate (raw_url, UriKind.Absolute, out raw_uri))
+                               path = raw_uri.PathAndQuery;
+                       else
+                               path = HttpUtility.UrlDecode (raw_url);
+                       
+                       string path_slash = path [path.Length - 1] == '/' ? path : path + "/";
+                       
                        HttpListener best_match = null;
                        int best_length = -1;
 
                        lock (prefixes) {
-                               foreach (ListenerPrefix p in prefixes.Keys) {
-                                       string ppath = p.Path;
-                                       if (ppath.Length < best_length)
-                                               continue;
-
-                                       if (p.Host == host && path.StartsWith (ppath)) {
-                                               best_length = ppath.Length;
-                                               best_match = prefixes [p];
-                                               prefix = p;
+                               if (host != null && host != "") {
+                                       foreach (ListenerPrefix p in prefixes.Keys) {
+                                               string ppath = p.Path;
+                                               if (ppath.Length < best_length)
+                                                       continue;
+
+                                               if (p.Host == host && (path.StartsWith (ppath) || path_slash.StartsWith (ppath))) {
+                                                       best_length = ppath.Length;
+                                                       best_match = (HttpListener) prefixes [p];
+                                                       prefix = p;
+                                               }
                                        }
+                                       if (best_length != -1)
+                                               return best_match;
                                }
 
-                               if (best_length != -1)
-                                       return best_match;
-
                                best_match = MatchFromList (host, path, unhandled, out prefix);
                                if (best_match != null)
                                        return best_match;
@@ -134,7 +189,7 @@ namespace System.Net {
                        return null;
                }
 
-               HttpListener MatchFromList (string host, string path, List<ListenerPrefix> list, out ListenerPrefix prefix)
+               HttpListener MatchFromList (string host, string path, ArrayList list, out ListenerPrefix prefix)
                {
                        prefix = null;
                        if (list == null)
@@ -158,7 +213,7 @@ namespace System.Net {
                        return best_match;
                }
 
-               void AddSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
+               void AddSpecial (ArrayList coll, ListenerPrefix prefix)
                {
                        if (coll == null)
                                return;
@@ -171,14 +226,14 @@ namespace System.Net {
                        coll.Add (prefix);
                }
 
-               void RemoveSpecial (List<ListenerPrefix> coll, ListenerPrefix prefix)
+               void RemoveSpecial (ArrayList coll, ListenerPrefix prefix)
                {
                        if (coll == null)
                                return;
 
                        int c = coll.Count;
                        for (int i = 0; i < c; i++) {
-                               ListenerPrefix p = coll [i];
+                               ListenerPrefix p = (ListenerPrefix) coll [i];
                                if (p.Path == prefix.Path) {
                                        coll.RemoveAt (i);
                                        CheckIfRemove ();
@@ -211,7 +266,7 @@ namespace System.Net {
                        lock (prefixes) {
                                if (prefix.Host == "*") {
                                        if (unhandled == null)
-                                               unhandled = new List<ListenerPrefix> ();
+                                               unhandled = new ArrayList ();
 
                                        prefix.Listener = listener;
                                        AddSpecial (unhandled, prefix);
@@ -220,14 +275,14 @@ namespace System.Net {
 
                                if (prefix.Host == "+") {
                                        if (all == null)
-                                               all = new List<ListenerPrefix> ();
+                                               all = new ArrayList ();
                                        prefix.Listener = listener;
                                        AddSpecial (all, prefix);
                                        return;
                                }
 
                                if (prefixes.ContainsKey (prefix)) {
-                                       HttpListener other = prefixes [prefix];
+                                       HttpListener other = (HttpListener) prefixes [prefix];
                                        if (other != listener) // TODO: code.
                                                throw new HttpListenerException (400, "There's another listener for " + prefix);
                                        return;