浏览代码

Fix SocketReadLine to be able to read empty line
Add support for HTTP Proxy
Improve support for multiple authencation methods

olegkap_cp 13 年之前
父节点
当前提交
628ce8bb37

+ 2 - 2
Renci.SshClient/Renci.SshNet.Tests/ConnectionTest.cs

@@ -173,14 +173,14 @@ namespace Renci.SshNet.Tests
 		[ExpectedException(typeof(ArgumentOutOfRangeException))]
 		public void Test_ConnectionInfo_SmallPortNumber()
 		{
-			var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MinPort - 1, null, null);
+			var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MinPort - 1, Resources.USERNAME, Resources.PASSWORD);
 		}
 
 		[WorkItem(703), TestMethod]
 		[ExpectedException(typeof(ArgumentOutOfRangeException))]
 		public void Test_ConnectionInfo_BigPortNumber()
 		{
-			var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MaxPort + 1, null, null);
+			var connectionInfo = new PasswordConnectionInfo(Resources.HOST, IPEndPoint.MaxPort + 1, Resources.USERNAME, Resources.PASSWORD);
 		}
 
 	}

+ 5 - 0
Renci.SshClient/Renci.SshNet/AuthenticationMethod.cs

@@ -26,6 +26,11 @@ namespace Renci.SshNet
         /// </summary>
         public string ErrorMessage { get; private set; }
 
+        /// <summary>
+        /// Gets list of allowed authentications.
+        /// </summary>
+        public IEnumerable<string> AllowedAuthentications { get; protected set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="AuthenticationMethod"/> class.
         /// </summary>

+ 18 - 6
Renci.SshClient/Renci.SshNet/ConnectionInfo.cs

@@ -355,15 +355,27 @@ namespace Renci.SshNet
 
             authenticated = noneAuthenticationMethod.Authenticate(session);
 
-            if (authenticated != AuthenticationResult.Success)
+            var allowedAuthentications = noneAuthenticationMethod.AllowedAuthentications;
+
+            while (authenticated != AuthenticationResult.Success)
             {
-                foreach (var authenticationMethod in this.AuthenticationMethods.Where((a) => noneAuthenticationMethod.AllowedAuthentications.Contains(a.Name)))
-                {
-                    authenticated = authenticationMethod.Authenticate(session);
+                //  Find first authentication method
+                var method = this.AuthenticationMethods.Where((a) => allowedAuthentications.Contains(a.Name)).FirstOrDefault();
+
+                if (method == null)
+                    throw new SshAuthenticationException("No suitable authentication method found to complete authentication.");
 
-                    if (authenticated == AuthenticationResult.Success)
-                        break;
+                authenticated = method.Authenticate(session);
+
+                if (authenticated == AuthenticationResult.PartialSuccess)
+                {
+                    //  If further authentication is required then continue to try another method
+                    allowedAuthentications = method.AllowedAuthentications;
+                    continue;
                 }
+
+                //  If authentication was successful or failure, exit
+                break;
             }
 
             session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerReceived;

+ 3 - 0
Renci.SshClient/Renci.SshNet/KeyboardInteractiveAuthenticationMethod.cs

@@ -98,6 +98,9 @@ namespace Renci.SshNet
             else
                 this._authenticationResult = AuthenticationResult.Failure;
 
+            //  Copy allowed authentication methods
+            this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList();
+
             this._authenticationCompleted.Set();
         }
 

+ 0 - 6
Renci.SshClient/Renci.SshNet/NoneAuthenticationMethod.cs

@@ -25,11 +25,6 @@ namespace Renci.SshNet
             get { return "none"; }
         }
 
-        /// <summary>
-        /// Gets list of allowed authentications.
-        /// </summary>
-        public IEnumerable<string> AllowedAuthentications { get; private set; }
-
         /// <summary>
         /// Initializes a new instance of the <see cref="KeyboardInteractiveConnectionInfo"/> class.
         /// </summary>
@@ -76,7 +71,6 @@ namespace Renci.SshNet
             else
                 this._authenticationResult = AuthenticationResult.Failure;
 
-
             //  Copy allowed authentication methods
             this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList();
 

+ 3 - 0
Renci.SshClient/Renci.SshNet/PasswordAuthenticationMethod.cs

@@ -103,6 +103,9 @@ namespace Renci.SshNet
             else
                 this._authenticationResult = AuthenticationResult.Failure;
 
+            //  Copy allowed authentication methods
+            this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList();
+
             this._authenticationCompleted.Set();
         }
 

+ 3 - 0
Renci.SshClient/Renci.SshNet/PrivateKeyAuthenticationMethod.cs

@@ -128,6 +128,9 @@ namespace Renci.SshNet
             else
                 this._authenticationResult = AuthenticationResult.Failure;
 
+            //  Copy allowed authentication methods
+            this.AllowedAuthentications = e.Message.AllowedAuthentications.ToList();
+
             this._authenticationCompleted.Set();
         }
 

+ 2 - 3
Renci.SshClient/Renci.SshNet/Session.NET.cs

@@ -62,11 +62,10 @@ namespace Renci.SshNet
                 this._socket.Receive(data);
 
                 buffer.Add(data[0]);
-                Debug.WriteLine(data[0]);
             }
-            while (!(buffer.Count > 2 && buffer[buffer.Count - 1] == 0x0A));
+            while (!(buffer.Count > 1 && buffer[buffer.Count - 1] == 0x0A));
 
-            if (buffer[buffer.Count - 2] == 0x0D)
+            if (buffer.Count > 1 && buffer[buffer.Count - 2] == 0x0D)
                 response = encoding.GetString(buffer.Take(buffer.Count - 2).ToArray());
             else
                 response = encoding.GetString(buffer.Take(buffer.Count - 1).ToArray());

+ 68 - 2
Renci.SshClient/Renci.SshNet/Session.cs

@@ -407,7 +407,6 @@ namespace Renci.SshNet
             this.ClientVersion = string.Format(CultureInfo.CurrentCulture, "SSH-2.0-Renci.SshNet.SshClient.0.0.1");
         }
 
-
         /// <summary>
         /// Connects to the server.
         /// </summary>
@@ -1834,7 +1833,74 @@ namespace Renci.SshNet
 
         private void ConnectHttp(Socket socket)
         {
-            throw new NotImplementedException();
+            var httpResponseRe = new Regex(@"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$");
+            var httpHeaderRe = new Regex(@"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?");
+
+            var encoding = new Renci.SshNet.Common.ASCIIEncoding();
+
+            this.SocketWrite(encoding.GetBytes(string.Format("CONNECT {0}:{1} HTTP/1.0\r\n", this.ConnectionInfo.Host, this.ConnectionInfo.Port)));
+            
+            //  Sent proxy authorization is specified
+            if (!string.IsNullOrEmpty(this.ConnectionInfo.ProxyUsername))
+            {
+                var authorization = string.Format("Proxy-Authorization: Basic {0}\r\n",
+                    Convert.ToBase64String(encoding.GetBytes(string.Format("{0}:{1}", this.ConnectionInfo.ProxyUsername, this.ConnectionInfo.ProxyPassword)))
+                    );
+                this.SocketWrite(encoding.GetBytes(authorization));
+            }
+
+            this.SocketWrite(encoding.GetBytes("\r\n"));
+
+            var statusCode = 0;
+            var response = string.Empty;
+            var contentLength = 0;
+
+            while (true)
+            {
+                this.SocketReadLine(ref response);
+
+                System.Diagnostics.Debug.WriteLine(response);
+
+                var match = httpResponseRe.Match(response);
+
+                if (match.Success)
+                {
+                    statusCode = int.Parse(match.Result("${statusCode}"));
+                    continue;
+                }
+                else
+                {
+                    match = httpHeaderRe.Match(response);
+                    if (match.Success)
+                    {
+                        var fieldName = match.Result("${fieldName}");
+                        if (fieldName.Equals("Content-Length", StringComparison.InvariantCultureIgnoreCase))
+                        {
+                            contentLength = int.Parse(match.Result("${fieldValue}"));
+                        }
+                        continue;
+                    }
+                }
+
+                //  Read response body if specified
+                if (string.IsNullOrEmpty(response) && contentLength > 0)
+                {
+                    var contentBody = new byte[contentLength];
+                    this.SocketRead(contentLength, ref contentBody);
+                    var text = encoding.GetString(contentBody);
+                }
+
+                if (statusCode == 200 && string.IsNullOrEmpty(response))
+                {
+                    //  Once all HTTP header information is read, exit
+                    break;
+                }
+                else
+                {
+                    var reasonPhrase = match.Result("${reasonPhrase}");
+                    throw new ProxyException(string.Format("HTTP: Status code {0}, Reason \"{1}\"", statusCode, reasonPhrase));
+                }
+            }
         }
 
         /// <summary>