You've already forked linux-packaging-mono
Imported Upstream version 5.12.0.220
Former-commit-id: c477e03582759447177c6d4bf412cd2355aad476
This commit is contained in:
parent
8bd104cef2
commit
8fc30896db
271
mcs/class/System/System.Net/WebConnectionTunnel.cs
Normal file
271
mcs/class/System/System.Net/WebConnectionTunnel.cs
Normal file
@ -0,0 +1,271 @@
|
||||
//
|
||||
// System.Net.WebConnectionTunnel
|
||||
//
|
||||
// Authors:
|
||||
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
|
||||
// Martin Baulig <mabaul@microsoft.com>
|
||||
//
|
||||
// (C) 2003 Ximian, Inc (http://www.ximian.com)
|
||||
// Copyright (c) 2017 Xamarin Inc. (http://www.xamarin.com)
|
||||
//
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Diagnostics;
|
||||
using Mono.Net.Security;
|
||||
|
||||
namespace System.Net
|
||||
{
|
||||
class WebConnectionTunnel
|
||||
{
|
||||
public HttpWebRequest Request {
|
||||
get;
|
||||
}
|
||||
|
||||
public Uri ConnectUri {
|
||||
get;
|
||||
}
|
||||
|
||||
public WebConnectionTunnel (HttpWebRequest request, Uri connectUri)
|
||||
{
|
||||
Request = request;
|
||||
ConnectUri = connectUri;
|
||||
}
|
||||
|
||||
enum NtlmAuthState
|
||||
{
|
||||
None,
|
||||
Challenge,
|
||||
Response
|
||||
}
|
||||
|
||||
HttpWebRequest connectRequest;
|
||||
NtlmAuthState ntlmAuthState;
|
||||
|
||||
public bool Success {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool CloseConnection {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public int StatusCode {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string StatusDescription {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string[] Challenge {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public WebHeaderCollection Headers {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Version ProxyVersion {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public byte[] Data {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
internal async Task Initialize (Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder ();
|
||||
sb.Append ("CONNECT ");
|
||||
sb.Append (Request.Address.Host);
|
||||
sb.Append (':');
|
||||
sb.Append (Request.Address.Port);
|
||||
sb.Append (" HTTP/");
|
||||
if (Request.ProtocolVersion == HttpVersion.Version11)
|
||||
sb.Append ("1.1");
|
||||
else
|
||||
sb.Append ("1.0");
|
||||
|
||||
sb.Append ("\r\nHost: ");
|
||||
sb.Append (Request.Address.Authority);
|
||||
|
||||
bool ntlm = false;
|
||||
var challenge = Challenge;
|
||||
Challenge = null;
|
||||
var auth_header = Request.Headers["Proxy-Authorization"];
|
||||
bool have_auth = auth_header != null;
|
||||
if (have_auth) {
|
||||
sb.Append ("\r\nProxy-Authorization: ");
|
||||
sb.Append (auth_header);
|
||||
ntlm = auth_header.ToUpper ().Contains ("NTLM");
|
||||
} else if (challenge != null && StatusCode == 407) {
|
||||
ICredentials creds = Request.Proxy.Credentials;
|
||||
have_auth = true;
|
||||
|
||||
if (connectRequest == null) {
|
||||
// create a CONNECT request to use with Authenticate
|
||||
connectRequest = (HttpWebRequest)WebRequest.Create (
|
||||
ConnectUri.Scheme + "://" + ConnectUri.Host + ":" + ConnectUri.Port + "/");
|
||||
connectRequest.Method = "CONNECT";
|
||||
connectRequest.Credentials = creds;
|
||||
}
|
||||
|
||||
if (creds != null) {
|
||||
for (int i = 0; i < challenge.Length; i++) {
|
||||
var auth = AuthenticationManager.Authenticate (challenge[i], connectRequest, creds);
|
||||
if (auth == null)
|
||||
continue;
|
||||
ntlm = (auth.ModuleAuthenticationType == "NTLM");
|
||||
sb.Append ("\r\nProxy-Authorization: ");
|
||||
sb.Append (auth.Message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ntlm) {
|
||||
sb.Append ("\r\nProxy-Connection: keep-alive");
|
||||
ntlmAuthState++;
|
||||
}
|
||||
|
||||
sb.Append ("\r\n\r\n");
|
||||
|
||||
StatusCode = 0;
|
||||
byte[] connectBytes = Encoding.Default.GetBytes (sb.ToString ());
|
||||
await stream.WriteAsync (connectBytes, 0, connectBytes.Length, cancellationToken).ConfigureAwait (false);
|
||||
|
||||
(Headers, Data, StatusCode) = await ReadHeaders (stream, cancellationToken).ConfigureAwait (false);
|
||||
|
||||
if ((!have_auth || ntlmAuthState == NtlmAuthState.Challenge) && Headers != null && StatusCode == 407) { // Needs proxy auth
|
||||
var connectionHeader = Headers["Connection"];
|
||||
if (!string.IsNullOrEmpty (connectionHeader) && connectionHeader.ToLower () == "close") {
|
||||
// The server is requesting that this connection be closed
|
||||
CloseConnection = true;
|
||||
}
|
||||
|
||||
Challenge = Headers.GetValues ("Proxy-Authenticate");
|
||||
Success = false;
|
||||
} else {
|
||||
Success = StatusCode == 200 && Headers != null;
|
||||
}
|
||||
|
||||
if (Challenge == null && (StatusCode == 401 || StatusCode == 407)) {
|
||||
var response = new HttpWebResponse (ConnectUri, "CONNECT", (HttpStatusCode)StatusCode, Headers);
|
||||
throw new WebException (
|
||||
StatusCode == 407 ? "(407) Proxy Authentication Required" : "(401) Unauthorized",
|
||||
null, WebExceptionStatus.ProtocolError, response);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<(WebHeaderCollection, byte[], int)> ReadHeaders (Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
byte[] retBuffer = null;
|
||||
int status = 200;
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
MemoryStream ms = new MemoryStream ();
|
||||
|
||||
while (true) {
|
||||
cancellationToken.ThrowIfCancellationRequested ();
|
||||
int n = await stream.ReadAsync (buffer, 0, 1024, cancellationToken).ConfigureAwait (false);
|
||||
if (n == 0)
|
||||
throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null);
|
||||
|
||||
ms.Write (buffer, 0, n);
|
||||
int start = 0;
|
||||
string str = null;
|
||||
bool gotStatus = false;
|
||||
WebHeaderCollection headers = new WebHeaderCollection ();
|
||||
while (WebConnection.ReadLine (ms.GetBuffer (), ref start, (int)ms.Length, ref str)) {
|
||||
if (str == null) {
|
||||
int contentLen;
|
||||
var clengthHeader = headers["Content-Length"];
|
||||
if (string.IsNullOrEmpty (clengthHeader) || !int.TryParse (clengthHeader, out contentLen))
|
||||
contentLen = 0;
|
||||
|
||||
if (ms.Length - start - contentLen > 0) {
|
||||
// we've read more data than the response header and conents,
|
||||
// give back extra data to the caller
|
||||
retBuffer = new byte[ms.Length - start - contentLen];
|
||||
Buffer.BlockCopy (ms.GetBuffer (), start + contentLen, retBuffer, 0, retBuffer.Length);
|
||||
} else {
|
||||
// haven't read in some or all of the contents for the response, do so now
|
||||
FlushContents (stream, contentLen - (int)(ms.Length - start));
|
||||
}
|
||||
|
||||
return (headers, retBuffer, status);
|
||||
}
|
||||
|
||||
if (gotStatus) {
|
||||
headers.Add (str);
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = str.Split (' ');
|
||||
if (parts.Length < 2)
|
||||
throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null);
|
||||
|
||||
if (String.Compare (parts[0], "HTTP/1.1", true) == 0)
|
||||
ProxyVersion = HttpVersion.Version11;
|
||||
else if (String.Compare (parts[0], "HTTP/1.0", true) == 0)
|
||||
ProxyVersion = HttpVersion.Version10;
|
||||
else
|
||||
throw WebConnection.GetException (WebExceptionStatus.ServerProtocolViolation, null);
|
||||
|
||||
status = (int)UInt32.Parse (parts[1]);
|
||||
if (parts.Length >= 3)
|
||||
StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
|
||||
|
||||
gotStatus = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FlushContents (Stream stream, int contentLength)
|
||||
{
|
||||
while (contentLength > 0) {
|
||||
byte[] contentBuffer = new byte[contentLength];
|
||||
int bytesRead = stream.Read (contentBuffer, 0, contentLength);
|
||||
if (bytesRead > 0) {
|
||||
contentLength -= bytesRead;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user