1 //------------------------------------------------------------------------------ 2 // <copyright file="HttpListenerRequest.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 //------------------------------------------------------------------------------ 6 7 namespace System.Net { 8 using System; 9 using System.Collections; 10 using System.Collections.Generic; 11 using System.Collections.Specialized; 12 using System.IO; 13 using System.Net.Sockets; 14 using System.Net.WebSockets; 15 using System.Runtime.InteropServices; 16 using System.Security.Authentication.ExtendedProtection; 17 using System.Security.Permissions; 18 using System.Globalization; 19 using System.Text; 20 using System.Threading; 21 using System.Threading.Tasks; 22 using System.ComponentModel; 23 using System.Diagnostics; 24 using System.Security; 25 using System.Security.Principal; 26 using System.Security.Cryptography; 27 using System.Security.Cryptography.X509Certificates; 28 29 30 internal enum ListenerClientCertState { 31 NotInitialized, 32 InProgress, 33 Completed 34 } 35 36 unsafe internal class ListenerClientCertAsyncResult : LazyAsyncResult { 37 private NativeOverlapped* m_pOverlapped; 38 private byte[] m_BackingBuffer; 39 private UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* m_MemoryBlob; 40 private uint m_Size; 41 42 internal NativeOverlapped* NativeOverlapped 43 { 44 get 45 { 46 return m_pOverlapped; 47 } 48 } 49 50 internal UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* RequestBlob 51 { 52 get 53 { 54 return m_MemoryBlob; 55 } 56 } 57 58 private static readonly IOCompletionCallback s_IOCallback = new IOCompletionCallback(WaitCallback); 59 ListenerClientCertAsyncResult(object asyncObject, object userState, AsyncCallback callback, uint size)60 internal ListenerClientCertAsyncResult(object asyncObject, object userState, AsyncCallback callback, uint size) : base(asyncObject, userState, callback) { 61 // we will use this overlapped structure to issue async IO to ul 62 // the event handle will be put in by the BeginHttpApi2.ERROR_SUCCESS() method 63 Reset(size); 64 } 65 Reset(uint size)66 internal void Reset(uint size) { 67 if (size == m_Size) 68 { 69 return; 70 } 71 if (m_Size != 0) 72 { 73 Overlapped.Free(m_pOverlapped); 74 } 75 m_Size = size; 76 if (size == 0) 77 { 78 m_pOverlapped = null; 79 m_MemoryBlob = null; 80 m_BackingBuffer = null; 81 return; 82 } 83 m_BackingBuffer = new byte[checked((int) size)]; 84 Overlapped overlapped = new Overlapped(); 85 overlapped.AsyncResult = this; 86 m_pOverlapped = overlapped.Pack(s_IOCallback, m_BackingBuffer); 87 m_MemoryBlob = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) Marshal.UnsafeAddrOfPinnedArrayElement(m_BackingBuffer, 0); 88 } 89 IOCompleted(uint errorCode, uint numBytes)90 internal unsafe void IOCompleted(uint errorCode, uint numBytes) 91 { 92 IOCompleted(this, errorCode, numBytes); 93 } 94 IOCompleted(ListenerClientCertAsyncResult asyncResult, uint errorCode, uint numBytes)95 private static unsafe void IOCompleted(ListenerClientCertAsyncResult asyncResult, uint errorCode, uint numBytes) 96 { 97 HttpListenerRequest httpListenerRequest = (HttpListenerRequest) asyncResult.AsyncObject; 98 object result = null; 99 try { 100 if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) 101 { 102 //There is a bug that has existed in http.sys since w2k3. Bytesreceived will only 103 //return the size of the inital cert structure. To get the full size, 104 //we need to add the certificate encoding size as well. 105 106 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; 107 asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize); 108 109 uint bytesReceived = 0; 110 errorCode = 111 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( 112 httpListenerRequest.HttpListenerContext.RequestQueueHandle, 113 httpListenerRequest.m_ConnectionId, 114 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE, 115 asyncResult.m_MemoryBlob, 116 asyncResult.m_Size, 117 &bytesReceived, 118 asyncResult.m_pOverlapped); 119 120 if(errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING || 121 (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpListener.SkipIOCPCallbackOnSuccess)) 122 { 123 return; 124 } 125 } 126 127 if (errorCode!=UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 128 asyncResult.ErrorCode = (int)errorCode; 129 result = new HttpListenerException((int)errorCode); 130 } 131 else { 132 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.m_MemoryBlob; 133 if (pClientCertInfo!=null) { 134 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() pClientCertInfo:" + ValidationHelper.ToString((IntPtr)pClientCertInfo) 135 + " pClientCertInfo->CertFlags:" + ValidationHelper.ToString(pClientCertInfo->CertFlags) 136 + " pClientCertInfo->CertEncodedSize:" + ValidationHelper.ToString(pClientCertInfo->CertEncodedSize) 137 + " pClientCertInfo->pCertEncoded:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->pCertEncoded) 138 + " pClientCertInfo->Token:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->Token) 139 + " pClientCertInfo->CertDeniedByMapper:" + ValidationHelper.ToString(pClientCertInfo->CertDeniedByMapper)); 140 if (pClientCertInfo->pCertEncoded!=null) { 141 try { 142 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; 143 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); 144 result = httpListenerRequest.ClientCertificate = new X509Certificate2(certEncoded); 145 } 146 catch (CryptographicException exception) { 147 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception)); 148 result = exception; 149 } 150 catch (SecurityException exception) { 151 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(httpListenerRequest) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception)); 152 result = exception; 153 } 154 } 155 httpListenerRequest.SetClientCertificateError((int)pClientCertInfo->CertFlags); 156 } 157 158 } 159 160 // complete the async IO and invoke the callback 161 GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() calling Complete()"); 162 } 163 catch (Exception exception) 164 { 165 if (NclUtilities.IsFatal(exception)) throw; 166 result = exception; 167 } 168 finally { 169 if(errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING){ 170 httpListenerRequest.ClientCertState = ListenerClientCertState.Completed; 171 } 172 } 173 174 asyncResult.InvokeCallback(result); 175 } 176 WaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)177 private static unsafe void WaitCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped) { 178 // take the ListenerClientCertAsyncResult object from the state 179 Overlapped callbackOverlapped = Overlapped.Unpack(nativeOverlapped); 180 ListenerClientCertAsyncResult asyncResult = (ListenerClientCertAsyncResult) callbackOverlapped.AsyncResult; 181 182 GlobalLog.Print("ListenerClientCertAsyncResult#" + ValidationHelper.HashString(asyncResult) + "::WaitCallback() errorCode:[" + errorCode.ToString() + "] numBytes:[" + numBytes.ToString() + "] nativeOverlapped:[" + ((long)nativeOverlapped).ToString() + "]"); 183 184 IOCompleted(asyncResult, errorCode, numBytes); 185 } 186 187 // Will be called from the base class upon InvokeCallback() Cleanup()188 protected override void Cleanup() 189 { 190 if (m_pOverlapped != null) 191 { 192 m_MemoryBlob = null; 193 Overlapped.Free(m_pOverlapped); 194 m_pOverlapped = null; 195 } 196 GC.SuppressFinalize(this); 197 base.Cleanup(); 198 } 199 ~ListenerClientCertAsyncResult()200 ~ListenerClientCertAsyncResult() 201 { 202 if (m_pOverlapped != null && !NclUtilities.HasShutdownStarted) 203 { 204 Overlapped.Free(m_pOverlapped); 205 m_pOverlapped = null; // Must do this in case application calls GC.ReRegisterForFinalize(). 206 } 207 } 208 } 209 210 211 public sealed unsafe class HttpListenerRequest/* BaseHttpRequest, */ { 212 213 private Uri m_RequestUri; 214 private ulong m_RequestId; 215 internal ulong m_ConnectionId; 216 private SslStatus m_SslStatus; 217 private string m_RawUrl; 218 private string m_CookedUrlHost; 219 private string m_CookedUrlPath; 220 private string m_CookedUrlQuery; 221 private long m_ContentLength; 222 private Stream m_RequestStream; 223 private string m_HttpMethod; 224 private TriState m_KeepAlive; 225 private Version m_Version; 226 private WebHeaderCollection m_WebHeaders; 227 private IPEndPoint m_LocalEndPoint; 228 private IPEndPoint m_RemoteEndPoint; 229 private BoundaryType m_BoundaryType; 230 private ListenerClientCertState m_ClientCertState; 231 private X509Certificate2 m_ClientCertificate; 232 private int m_ClientCertificateError; 233 private RequestContextBase m_MemoryBlob; 234 private CookieCollection m_Cookies; 235 private HttpListenerContext m_HttpContext; 236 private bool m_IsDisposed = false; 237 internal const uint CertBoblSize = 1500; 238 private string m_ServiceName; 239 private object m_Lock = new object(); 240 private List<TokenBinding> m_TokenBindings = null; 241 private int m_TokenBindingVerifyMessageStatus = 0; 242 243 private enum SslStatus : byte 244 { 245 Insecure, 246 NoClientCert, 247 ClientCert 248 } 249 HttpListenerRequest(HttpListenerContext httpContext, RequestContextBase memoryBlob)250 internal HttpListenerRequest(HttpListenerContext httpContext, RequestContextBase memoryBlob) 251 { 252 if (Logging.On) Logging.PrintInfo(Logging.HttpListener, this, ".ctor", "httpContext#" + ValidationHelper.HashString(httpContext) + " memoryBlob# " + ValidationHelper.HashString((IntPtr) memoryBlob.RequestBlob)); 253 if(Logging.On)Logging.Associate(Logging.HttpListener, this, httpContext); 254 m_HttpContext = httpContext; 255 m_MemoryBlob = memoryBlob; 256 m_BoundaryType = BoundaryType.None; 257 258 // Set up some of these now to avoid refcounting on memory blob later. 259 m_RequestId = memoryBlob.RequestBlob->RequestId; 260 m_ConnectionId = memoryBlob.RequestBlob->ConnectionId; 261 m_SslStatus = memoryBlob.RequestBlob->pSslInfo == null ? SslStatus.Insecure : 262 memoryBlob.RequestBlob->pSslInfo->SslClientCertNegotiated == 0 ? SslStatus.NoClientCert : 263 SslStatus.ClientCert; 264 if (memoryBlob.RequestBlob->pRawUrl != null && memoryBlob.RequestBlob->RawUrlLength > 0) { 265 m_RawUrl = Marshal.PtrToStringAnsi((IntPtr) memoryBlob.RequestBlob->pRawUrl, memoryBlob.RequestBlob->RawUrlLength); 266 } 267 268 UnsafeNclNativeMethods.HttpApi.HTTP_COOKED_URL cookedUrl = memoryBlob.RequestBlob->CookedUrl; 269 if (cookedUrl.pHost != null && cookedUrl.HostLength > 0) { 270 m_CookedUrlHost = Marshal.PtrToStringUni((IntPtr)cookedUrl.pHost, cookedUrl.HostLength / 2); 271 } 272 if (cookedUrl.pAbsPath != null && cookedUrl.AbsPathLength > 0) { 273 m_CookedUrlPath = Marshal.PtrToStringUni((IntPtr)cookedUrl.pAbsPath, cookedUrl.AbsPathLength / 2); 274 } 275 if (cookedUrl.pQueryString != null && cookedUrl.QueryStringLength > 0) { 276 m_CookedUrlQuery = Marshal.PtrToStringUni((IntPtr)cookedUrl.pQueryString, cookedUrl.QueryStringLength / 2); 277 } 278 m_Version = new Version(memoryBlob.RequestBlob->Version.MajorVersion, memoryBlob.RequestBlob->Version.MinorVersion); 279 m_ClientCertState = ListenerClientCertState.NotInitialized; 280 m_KeepAlive = TriState.Unspecified; 281 GlobalLog.Print("HttpListenerContext#" + ValidationHelper.HashString(this) + "::.ctor() RequestId:" + RequestId + " ConnectionId:" + m_ConnectionId + " RawConnectionId:" + memoryBlob.RequestBlob->RawConnectionId + " UrlContext:" + memoryBlob.RequestBlob->UrlContext + " RawUrl:" + m_RawUrl + " Version:" + m_Version.ToString() + " Secure:" + m_SslStatus.ToString()); 282 if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, ".ctor", "httpContext#"+ ValidationHelper.HashString(httpContext)+ " RequestUri:" + ValidationHelper.ToString(RequestUri) + " Content-Length:" + ValidationHelper.ToString(ContentLength64) + " HTTP Method:" + ValidationHelper.ToString(HttpMethod)); 283 // Log headers 284 if(Logging.On) { 285 StringBuilder sb = new StringBuilder("HttpListenerRequest Headers:\n"); 286 for (int i=0; i<Headers.Count; i++) { 287 sb.Append("\t"); 288 sb.Append(Headers.GetKey(i)); 289 sb.Append(" : "); 290 sb.Append(Headers.Get(i)); 291 sb.Append("\n"); 292 } 293 Logging.PrintInfo(Logging.HttpListener, this, ".ctor", sb.ToString()); 294 } 295 } 296 297 internal HttpListenerContext HttpListenerContext { 298 get { 299 return m_HttpContext; 300 } 301 } 302 303 // Note: RequestBuffer may get moved in memory. If you dereference a pointer from inside the RequestBuffer, 304 // you must use 'OriginalBlobAddress' below to adjust the location of the pointer to match the location of 305 // RequestBuffer. 306 // 307 308 internal byte[] RequestBuffer 309 { 310 get 311 { 312 CheckDisposed(); 313 return m_MemoryBlob.RequestBuffer; 314 } 315 } 316 317 internal IntPtr OriginalBlobAddress 318 { 319 get 320 { 321 CheckDisposed(); 322 return m_MemoryBlob.OriginalBlobAddress; 323 } 324 } 325 326 // Use this to save the blob from dispose if this object was never used (never given to a user) and is about to be 327 // disposed. DetachBlob(RequestContextBase memoryBlob)328 internal void DetachBlob(RequestContextBase memoryBlob) 329 { 330 if (memoryBlob != null && (object) memoryBlob == (object) m_MemoryBlob) 331 { 332 m_MemoryBlob = null; 333 } 334 } 335 336 // Finalizes ownership of the memory blob. DetachBlob can't be called after this. ReleasePins()337 internal void ReleasePins() 338 { 339 m_MemoryBlob.ReleasePins(); 340 } 341 342 public Guid RequestTraceIdentifier { 343 get { 344 Guid guid = new Guid(); 345 *(1+ (ulong *) &guid) = RequestId; 346 return guid; 347 } 348 } 349 350 internal ulong RequestId { 351 get { 352 return m_RequestId; 353 } 354 } 355 356 public /* override */ string[] AcceptTypes { 357 get { 358 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.Accept)); 359 } 360 } 361 362 public /* override */ Encoding ContentEncoding { 363 get { 364 if (UserAgent!=null && CultureInfo.InvariantCulture.CompareInfo.IsPrefix(UserAgent, "UP")) { 365 string postDataCharset = Headers["x-up-devcap-post-charset"]; 366 if (postDataCharset!=null && postDataCharset.Length>0) { 367 try { 368 return Encoding.GetEncoding(postDataCharset); 369 } 370 catch (ArgumentException) { 371 } 372 } 373 } 374 if (HasEntityBody) { 375 if (ContentType!=null) { 376 string charSet = Helpers.GetAttributeFromHeader(ContentType, "charset"); 377 if (charSet!=null) { 378 try { 379 return Encoding.GetEncoding(charSet); 380 } 381 catch (ArgumentException) { 382 } 383 } 384 } 385 } 386 return Encoding.Default; 387 } 388 } 389 390 public /* override */ long ContentLength64 { 391 get { 392 if (m_BoundaryType==BoundaryType.None) { 393 if (HttpWebRequest.ChunkedHeader.Equals(GetKnownHeader(HttpRequestHeader.TransferEncoding), 394 StringComparison.OrdinalIgnoreCase)) { 395 m_BoundaryType = BoundaryType.Chunked; 396 m_ContentLength = -1; 397 } 398 else { 399 m_ContentLength = 0; 400 m_BoundaryType = BoundaryType.ContentLength; 401 string length = GetKnownHeader(HttpRequestHeader.ContentLength); 402 if (length!=null) { 403 bool success = long.TryParse(length, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out m_ContentLength); 404 if (!success) { 405 m_ContentLength = 0; 406 m_BoundaryType = BoundaryType.Invalid; 407 } 408 } 409 } 410 } 411 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ContentLength_get() returning m_ContentLength:" + m_ContentLength + " m_BoundaryType:" + m_BoundaryType); 412 return m_ContentLength; 413 } 414 } 415 416 public /* override */ string ContentType { 417 get { 418 return GetKnownHeader(HttpRequestHeader.ContentType); 419 } 420 } 421 422 public /* override */ NameValueCollection Headers { 423 get { 424 if (m_WebHeaders==null) { 425 m_WebHeaders = UnsafeNclNativeMethods.HttpApi.GetHeaders(RequestBuffer, OriginalBlobAddress); 426 } 427 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::Headers_get() returning#" + ValidationHelper.HashString(m_WebHeaders)); 428 return m_WebHeaders; 429 } 430 } 431 432 public /* override */ string HttpMethod { 433 get { 434 if (m_HttpMethod==null) { 435 m_HttpMethod = UnsafeNclNativeMethods.HttpApi.GetVerb(RequestBuffer, OriginalBlobAddress); 436 } 437 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::HttpMethod_get() returning m_HttpMethod:" + ValidationHelper.ToString(m_HttpMethod)); 438 return m_HttpMethod; 439 } 440 } 441 442 public /* override */ Stream InputStream { 443 get { 444 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "InputStream_get", ""); 445 if (m_RequestStream==null) { 446 m_RequestStream = HasEntityBody ? new HttpRequestStream(HttpListenerContext) : Stream.Null; 447 } 448 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "InputStream_get", ""); 449 return m_RequestStream; 450 } 451 } 452 453 // Requires ControlPrincipal permission if the request was authenticated with Negotiate, NTLM, or Digest. 454 public /* override */ bool IsAuthenticated { 455 get { 456 IPrincipal user = HttpListenerContext.User; 457 return user != null && user.Identity != null && user.Identity.IsAuthenticated; 458 } 459 } 460 461 public /* override */ bool IsLocal { 462 get { 463 return LocalEndPoint.Address.Equals(RemoteEndPoint.Address); 464 } 465 } 466 467 public /* override */ bool IsSecureConnection { 468 get { 469 return m_SslStatus != SslStatus.Insecure; 470 } 471 } 472 473 public bool IsWebSocketRequest 474 { 475 get 476 { 477 if (!WebSocketProtocolComponent.IsSupported) 478 { 479 return false; 480 } 481 482 bool foundConnectionUpgradeHeader = false; 483 if (string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Connection]) || string.IsNullOrEmpty(this.Headers[HttpKnownHeaderNames.Upgrade])) 484 { 485 return false; 486 } 487 488 foreach (string connection in this.Headers.GetValues(HttpKnownHeaderNames.Connection)) 489 { 490 if (string.Compare(connection, HttpKnownHeaderNames.Upgrade, StringComparison.OrdinalIgnoreCase) == 0) 491 { 492 foundConnectionUpgradeHeader = true; 493 break; 494 } 495 } 496 497 if (!foundConnectionUpgradeHeader) 498 { 499 return false; 500 } 501 502 foreach (string upgrade in this.Headers.GetValues(HttpKnownHeaderNames.Upgrade)) 503 { 504 if (string.Compare(upgrade, WebSocketHelpers.WebSocketUpgradeToken, StringComparison.OrdinalIgnoreCase) == 0) 505 { 506 return true; 507 } 508 } 509 510 return false; 511 } 512 } 513 514 public /* override */ NameValueCollection QueryString { 515 get { 516 NameValueCollection queryString = new NameValueCollection(); 517 Helpers.FillFromString(queryString, Url.Query, true, ContentEncoding); 518 return queryString; 519 } 520 } 521 522 public /* override */ string RawUrl { 523 get { 524 return m_RawUrl; 525 } 526 } 527 528 public string ServiceName 529 { 530 get { return m_ServiceName; } 531 internal set { m_ServiceName = value; } 532 } 533 534 public /* override */ Uri Url { 535 get { 536 return RequestUri; 537 } 538 } 539 540 public /* override */ Uri UrlReferrer { 541 get { 542 string referrer = GetKnownHeader(HttpRequestHeader.Referer); 543 if (referrer==null) { 544 return null; 545 } 546 Uri urlReferrer; 547 bool success = Uri.TryCreate(referrer, UriKind.RelativeOrAbsolute, out urlReferrer); 548 return success ? urlReferrer : null; 549 } 550 } 551 552 public /* override */ string UserAgent { 553 get { 554 return GetKnownHeader(HttpRequestHeader.UserAgent); 555 } 556 } 557 558 public /* override */ string UserHostAddress { 559 get { 560 return LocalEndPoint.ToString(); 561 } 562 } 563 564 public /* override */ string UserHostName { 565 get { 566 return GetKnownHeader(HttpRequestHeader.Host); 567 } 568 } 569 570 public /* override */ string[] UserLanguages { 571 get { 572 return Helpers.ParseMultivalueHeader(GetKnownHeader(HttpRequestHeader.AcceptLanguage)); 573 } 574 } 575 576 public int ClientCertificateError { 577 get { 578 if (m_ClientCertState == ListenerClientCertState.NotInitialized) 579 throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcall, "GetClientCertificate()/BeginGetClientCertificate()")); 580 else if (m_ClientCertState == ListenerClientCertState.InProgress) 581 throw new InvalidOperationException(SR.GetString(SR.net_listener_mustcompletecall, "GetClientCertificate()/BeginGetClientCertificate()")); 582 583 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ClientCertificateError_get() returning ClientCertificateError:" + ValidationHelper.ToString(m_ClientCertificateError)); 584 return m_ClientCertificateError; 585 } 586 } 587 588 internal X509Certificate2 ClientCertificate { 589 set { 590 m_ClientCertificate = value; 591 } 592 } 593 594 internal ListenerClientCertState ClientCertState { 595 set { 596 m_ClientCertState = value; 597 } 598 } 599 SetClientCertificateError(int clientCertificateError)600 internal void SetClientCertificateError(int clientCertificateError) 601 { 602 m_ClientCertificateError = clientCertificateError; 603 } 604 GetClientCertificate()605 public X509Certificate2 GetClientCertificate() { 606 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "GetClientCertificate", ""); 607 try { 608 ProcessClientCertificate(); 609 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::GetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate)); 610 } finally { 611 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "GetClientCertificate", ValidationHelper.ToString(m_ClientCertificate)); 612 } 613 return m_ClientCertificate; 614 } 615 BeginGetClientCertificate(AsyncCallback requestCallback, object state)616 public IAsyncResult BeginGetClientCertificate(AsyncCallback requestCallback, object state) { 617 if(Logging.On)Logging.PrintInfo(Logging.HttpListener, this, "BeginGetClientCertificate", ""); 618 return AsyncProcessClientCertificate(requestCallback, state); 619 } 620 EndGetClientCertificate(IAsyncResult asyncResult)621 public X509Certificate2 EndGetClientCertificate(IAsyncResult asyncResult) { 622 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "EndGetClientCertificate", ""); 623 X509Certificate2 clientCertificate = null; 624 try { 625 if (asyncResult==null) { 626 throw new ArgumentNullException("asyncResult"); 627 } 628 ListenerClientCertAsyncResult clientCertAsyncResult = asyncResult as ListenerClientCertAsyncResult; 629 if (clientCertAsyncResult==null || clientCertAsyncResult.AsyncObject!=this) { 630 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult"); 631 } 632 if (clientCertAsyncResult.EndCalled) { 633 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndGetClientCertificate")); 634 } 635 clientCertAsyncResult.EndCalled = true; 636 clientCertificate = clientCertAsyncResult.InternalWaitForCompletion() as X509Certificate2; 637 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::EndGetClientCertificate() returning m_ClientCertificate:" + ValidationHelper.ToString(m_ClientCertificate)); 638 } finally { 639 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "EndGetClientCertificate", ValidationHelper.HashString(clientCertificate)); 640 } 641 return clientCertificate; 642 } 643 644 //************* Task-based async public methods ************************* 645 [HostProtection(ExternalThreading = true)] GetClientCertificateAsync()646 public Task<X509Certificate2> GetClientCertificateAsync() 647 { 648 return Task<X509Certificate2>.Factory.FromAsync(BeginGetClientCertificate, EndGetClientCertificate, null); 649 } 650 651 652 public TransportContext TransportContext 653 { 654 get 655 { 656 return new HttpListenerRequestContext(this); 657 } 658 } 659 ParseCookies(Uri uri, string setCookieHeader)660 private CookieCollection ParseCookies(Uri uri, string setCookieHeader) { 661 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() uri:" + uri + " setCookieHeader:" + setCookieHeader); 662 CookieCollection cookies = new CookieCollection(); 663 CookieParser parser = new CookieParser(setCookieHeader); 664 for (;;) { 665 Cookie cookie = parser.GetServer(); 666 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ParseCookies() CookieParser returned cookie:" + ValidationHelper.ToString(cookie)); 667 if (cookie==null) { 668 // EOF, done. 669 break; 670 } 671 if (cookie.Name.Length==0) { 672 continue; 673 } 674 cookies.InternalAdd(cookie, true); 675 } 676 return cookies; 677 } 678 679 public CookieCollection Cookies { 680 get { 681 if (m_Cookies==null) { 682 string cookieString = GetKnownHeader(HttpRequestHeader.Cookie); 683 if (cookieString!=null && cookieString.Length>0) { 684 m_Cookies = ParseCookies(RequestUri, cookieString); 685 } 686 if (m_Cookies==null) { 687 m_Cookies = new CookieCollection(); 688 } 689 if (HttpListenerContext.PromoteCookiesToRfc2965) { 690 for (int index=0; index<m_Cookies.Count; index++) { 691 if (m_Cookies[index].Variant==CookieVariant.Rfc2109) { 692 m_Cookies[index].Variant = CookieVariant.Rfc2965; 693 } 694 } 695 } 696 } 697 return m_Cookies; 698 } 699 } 700 701 public Version ProtocolVersion { 702 get { 703 return m_Version; 704 } 705 } 706 707 public /* override */ bool HasEntityBody { 708 get { 709 // accessing the ContentLength property delay creates m_BoundaryType 710 return (ContentLength64 > 0 && m_BoundaryType == BoundaryType.ContentLength) || 711 m_BoundaryType == BoundaryType.Chunked || m_BoundaryType == BoundaryType.Multipart; 712 } 713 } 714 715 public /* override */ bool KeepAlive 716 { 717 get 718 { 719 if (m_KeepAlive == TriState.Unspecified) 720 { 721 string header = Headers[HttpKnownHeaderNames.ProxyConnection]; 722 if (string.IsNullOrEmpty(header)) 723 { 724 header = GetKnownHeader(HttpRequestHeader.Connection); 725 } 726 if (string.IsNullOrEmpty(header)) 727 { 728 if (ProtocolVersion >= HttpVersion.Version11) 729 { 730 m_KeepAlive = TriState.True; 731 } 732 else 733 { 734 header = GetKnownHeader(HttpRequestHeader.KeepAlive); 735 m_KeepAlive = string.IsNullOrEmpty(header) ? TriState.False : TriState.True; 736 } 737 } 738 else 739 { 740 header = header.ToLower(CultureInfo.InvariantCulture); 741 m_KeepAlive = header.IndexOf("close") < 0 || header.IndexOf("keep-alive") >= 0 ? TriState.True : TriState.False; 742 } 743 } 744 745 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::KeepAlive_get() returning:" + m_KeepAlive); 746 return m_KeepAlive == TriState.True; 747 } 748 } 749 750 public /* override */ IPEndPoint RemoteEndPoint { 751 get { 752 if (m_RemoteEndPoint==null) { 753 m_RemoteEndPoint = UnsafeNclNativeMethods.HttpApi.GetRemoteEndPoint(RequestBuffer, OriginalBlobAddress); 754 } 755 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RemoteEndPoint_get() returning:" + m_RemoteEndPoint); 756 return m_RemoteEndPoint; 757 } 758 } 759 760 public /* override */ IPEndPoint LocalEndPoint { 761 get { 762 if (m_LocalEndPoint==null) { 763 m_LocalEndPoint = UnsafeNclNativeMethods.HttpApi.GetLocalEndPoint(RequestBuffer, OriginalBlobAddress); 764 } 765 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::LocalEndPoint_get() returning:" + m_LocalEndPoint); 766 return m_LocalEndPoint; 767 } 768 } 769 770 //should only be called from httplistenercontext Close()771 internal void Close() { 772 if(Logging.On)Logging.Enter(Logging.HttpListener, this, "Close", ""); 773 RequestContextBase memoryBlob = m_MemoryBlob; 774 if (memoryBlob != null) 775 { 776 memoryBlob.Close(); 777 m_MemoryBlob = null; 778 } 779 m_IsDisposed = true; 780 if(Logging.On)Logging.Exit(Logging.HttpListener, this, "Close", ""); 781 } 782 783 AsyncProcessClientCertificate(AsyncCallback requestCallback, object state)784 private ListenerClientCertAsyncResult AsyncProcessClientCertificate(AsyncCallback requestCallback, object state) { 785 if (m_ClientCertState == ListenerClientCertState.InProgress) 786 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()")); 787 m_ClientCertState = ListenerClientCertState.InProgress; 788 HttpListenerContext.EnsureBoundHandle(); 789 790 ListenerClientCertAsyncResult asyncResult = null; 791 //-------------------------------------------------------------------- 792 //When you configure the HTTP.SYS with a flag value 2 793 //which means require client certificates, when the client makes the 794 //initial SSL connection, server (HTTP.SYS) demands the client certificate 795 // 796 //Some apps may not want to demand the client cert at the beginning 797 //perhaps server the default.htm. In this case the HTTP.SYS is configured 798 //with a flag value other than 2, whcih means that the client certificate is 799 //optional.So intially when SSL is established HTTP.SYS won't ask for client 800 //certificate. This works fine for the default.htm in the case above 801 //However, if the app wants to demand a client certficate at a later time 802 //perhaps showing "YOUR ORDERS" page, then the server wans to demand 803 //Client certs. this will inturn makes HTTP.SYS to do the 804 //SEC_I_RENOGOTIATE through which the client cert demand is made 805 // 806 //THE 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 if (m_SslStatus != SslStatus.Insecure) 822 { 823 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate) 824 // the cert, though might or might not be there. try to retrieve it 825 // this number is the same that IIS decided to use 826 uint size = CertBoblSize; 827 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, size); 828 try { 829 while (true) 830 { 831 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size); 832 uint bytesReceived = 0; 833 834 uint statusCode = 835 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( 836 HttpListenerContext.RequestQueueHandle, 837 m_ConnectionId, 838 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE, 839 asyncResult.RequestBlob, 840 size, 841 &bytesReceived, 842 asyncResult.NativeOverlapped); 843 844 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived); 845 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) 846 { 847 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; 848 size = bytesReceived + pClientCertInfo->CertEncodedSize; 849 asyncResult.Reset(size); 850 continue; 851 } 852 if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && 853 statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { 854 // someother bad error, possible(?) return values are: 855 // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED 856 // Also ERROR_BAD_DATA if we got it twice or it reported smaller size buffer required. 857 throw new HttpListenerException((int)statusCode); 858 } 859 860 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && 861 HttpListener.SkipIOCPCallbackOnSuccess) 862 { 863 asyncResult.IOCompleted(statusCode, bytesReceived); 864 } 865 break; 866 } 867 } 868 catch { 869 if (asyncResult!=null) { 870 asyncResult.InternalCleanup(); 871 } 872 throw; 873 } 874 } else { 875 asyncResult = new ListenerClientCertAsyncResult(this, state, requestCallback, 0); 876 asyncResult.InvokeCallback(); 877 } 878 return asyncResult; 879 } 880 ProcessClientCertificate()881 private void ProcessClientCertificate() { 882 if (m_ClientCertState == ListenerClientCertState.InProgress) 883 throw new InvalidOperationException(SR.GetString(SR.net_listener_callinprogress, "GetClientCertificate()/BeginGetClientCertificate()")); 884 m_ClientCertState = ListenerClientCertState.InProgress; 885 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate()"); 886 //-------------------------------------------------------------------- 887 //When you configure the HTTP.SYS with a flag value 2 888 //which means require client certificates, when the client makes the 889 //initial SSL connection, server (HTTP.SYS) demands the client certificate 890 // 891 //Some apps may not want to demand the client cert at the beginning 892 //perhaps server the default.htm. In this case the HTTP.SYS is configured 893 //with a flag value other than 2, whcih means that the client certificate is 894 //optional.So intially when SSL is established HTTP.SYS won't ask for client 895 //certificate. This works fine for the default.htm in the case above 896 //However, if the app wants to demand a client certficate at a later time 897 //perhaps showing "YOUR ORDERS" page, then the server wans to demand 898 //Client certs. this will inturn makes HTTP.SYS to do the 899 //SEC_I_RENOGOTIATE through which the client cert demand is made 900 // 901 //THE 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 if (m_SslStatus != SslStatus.Insecure) 917 { 918 // at this point we know that DefaultFlags has the 2 bit set (Negotiate Client certificate) 919 // the cert, though might or might not be there. try to retrieve it 920 // this number is the same that IIS decided to use 921 uint size = CertBoblSize; 922 while (true) 923 { 924 byte[] clientCertInfoBlob = new byte[checked((int) size)]; 925 fixed (byte* pClientCertInfoBlob = clientCertInfoBlob) 926 { 927 UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = (UnsafeNclNativeMethods.HttpApi.HTTP_SSL_CLIENT_CERT_INFO*) pClientCertInfoBlob; 928 929 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() calling UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate size:" + size); 930 uint bytesReceived = 0; 931 932 uint statusCode = 933 UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate( 934 HttpListenerContext.RequestQueueHandle, 935 m_ConnectionId, 936 (uint)UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.NONE, 937 pClientCertInfo, 938 size, 939 &bytesReceived, 940 null); 941 942 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate returned:" + statusCode + " bytesReceived:" + bytesReceived); 943 if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) { 944 size = bytesReceived + pClientCertInfo->CertEncodedSize; 945 continue; 946 } 947 else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { 948 if (pClientCertInfo!=null) { 949 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() pClientCertInfo:" + ValidationHelper.ToString((IntPtr)pClientCertInfo) 950 + " pClientCertInfo->CertFlags:" + ValidationHelper.ToString(pClientCertInfo->CertFlags) 951 + " pClientCertInfo->CertEncodedSize:" + ValidationHelper.ToString(pClientCertInfo->CertEncodedSize) 952 + " pClientCertInfo->pCertEncoded:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->pCertEncoded) 953 + " pClientCertInfo->Token:" + ValidationHelper.ToString((IntPtr)pClientCertInfo->Token) 954 + " pClientCertInfo->CertDeniedByMapper:" + ValidationHelper.ToString(pClientCertInfo->CertDeniedByMapper)); 955 if (pClientCertInfo->pCertEncoded!=null) { 956 try { 957 byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; 958 Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); 959 m_ClientCertificate = new X509Certificate2(certEncoded); 960 } 961 catch (CryptographicException exception) { 962 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught CryptographicException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception)); 963 } 964 catch (SecurityException exception) { 965 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::ProcessClientCertificate() caught SecurityException in X509Certificate2..ctor():" + ValidationHelper.ToString(exception)); 966 } 967 } 968 m_ClientCertificateError = (int)pClientCertInfo->CertFlags; 969 } 970 } 971 else { 972 GlobalLog.Assert(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND, "HttpListenerRequest#{0}::ProcessClientCertificate()|Call to UnsafeNclNativeMethods.HttpApi.HttpReceiveClientCertificate() failed with statusCode {1}.", ValidationHelper.HashString(this), statusCode); 973 } 974 } 975 break; 976 } 977 } 978 m_ClientCertState = ListenerClientCertState.Completed; 979 } 980 981 private string RequestScheme { 982 get { 983 return IsSecureConnection ? "https" : "http"; 984 } 985 } 986 987 private Uri RequestUri { 988 get { 989 if (m_RequestUri == null) { 990 991 m_RequestUri = HttpListenerRequestUriBuilder.GetRequestUri( 992 m_RawUrl, RequestScheme, m_CookedUrlHost, m_CookedUrlPath, m_CookedUrlQuery); 993 } 994 995 GlobalLog.Print("HttpListenerRequest#" + ValidationHelper.HashString(this) + "::RequestUri_get() returning m_RequestUri:" + ValidationHelper.ToString(m_RequestUri)); 996 return m_RequestUri; 997 } 998 } 999 1000 /* 1001 private DateTime IfModifiedSince { 1002 get { 1003 string headerValue = GetKnownHeader(HttpRequestHeader.IfModifiedSince); 1004 if (headerValue==null) { 1005 return DateTime.Now; 1006 } 1007 return DateTime.Parse(headerValue, CultureInfo.InvariantCulture); 1008 } 1009 } 1010 */ 1011 GetKnownHeader(HttpRequestHeader header)1012 private string GetKnownHeader(HttpRequestHeader header) { 1013 return UnsafeNclNativeMethods.HttpApi.GetKnownHeader(RequestBuffer, OriginalBlobAddress, (int) header); 1014 } 1015 GetChannelBinding()1016 internal ChannelBinding GetChannelBinding() 1017 { 1018 return HttpListenerContext.Listener.GetChannelBindingFromTls(m_ConnectionId); 1019 } 1020 GetTlsTokenBindings()1021 internal IEnumerable<TokenBinding> GetTlsTokenBindings() { 1022 1023 // Try to get the token binding if not created. 1024 if (Volatile.Read(ref m_TokenBindings) == null) 1025 { 1026 lock (m_Lock) 1027 { 1028 if (Volatile.Read(ref m_TokenBindings) == null) 1029 { 1030 // If token binding is supported on the machine get it else create empty list. 1031 if (UnsafeNclNativeMethods.TokenBindingOSHelper.SupportsTokenBinding) 1032 { 1033 ProcessTlsTokenBindings(); 1034 } 1035 else 1036 { 1037 m_TokenBindings = new List<TokenBinding>(); 1038 } 1039 } 1040 } 1041 } 1042 1043 // If the cached status is not success throw exception, else return the token binding 1044 if (0 != m_TokenBindingVerifyMessageStatus) 1045 { 1046 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus); 1047 } 1048 else 1049 { 1050 return m_TokenBindings.AsReadOnly(); 1051 } 1052 } 1053 1054 /// <summary> 1055 /// Process the token binding information in the request and populate m_TokenBindings 1056 /// This method should be called once only as token binding information is cached in m_TokenBindings for further use. 1057 /// </summary> ProcessTlsTokenBindings()1058 private void ProcessTlsTokenBindings() { 1059 1060 Debug.Assert(m_TokenBindings == null); 1061 1062 if (m_TokenBindings != null) 1063 { 1064 return; 1065 } 1066 1067 m_TokenBindings = new List<TokenBinding>(); 1068 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo(RequestBuffer, OriginalBlobAddress); 1069 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_TOKEN_BINDING_INFO_V1* pTokenBindingInfo_V1 = null; 1070 bool useV1TokenBinding = false; 1071 1072 // Only try to collect the old binding information if there is no V2 binding information available 1073 if (pTokenBindingInfo == null) 1074 { 1075 pTokenBindingInfo_V1 = UnsafeNclNativeMethods.HttpApi.GetTlsTokenBindingRequestInfo_V1(RequestBuffer, OriginalBlobAddress); 1076 useV1TokenBinding = true; 1077 } 1078 1079 if (pTokenBindingInfo == null && pTokenBindingInfo_V1 == null) 1080 { 1081 // The current request isn't over TLS or the client or server doesn't support the token binding 1082 // protocol. This isn't an error; just return "nothing here". 1083 return; 1084 } 1085 1086 UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle = null; 1087 m_TokenBindingVerifyMessageStatus = -1; 1088 1089 fixed (byte* pMemoryBlob = RequestBuffer){ 1090 UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2* request = (UnsafeNclNativeMethods.HttpApi.HTTP_REQUEST_V2*)pMemoryBlob; 1091 long fixup = pMemoryBlob - (byte*) OriginalBlobAddress; 1092 1093 if (useV1TokenBinding && pTokenBindingInfo_V1 != null) 1094 { 1095 // Old V1 Token Binding protocol is still being used, so we need to verify the binding message using the old API 1096 m_TokenBindingVerifyMessageStatus = UnsafeNclNativeMethods.HttpApi.TokenBindingVerifyMessage_V1( 1097 pTokenBindingInfo_V1->TokenBinding + fixup, 1098 pTokenBindingInfo_V1->TokenBindingSize, 1099 (IntPtr)((byte*)(pTokenBindingInfo_V1->KeyType) + fixup), 1100 pTokenBindingInfo_V1->TlsUnique + fixup, 1101 pTokenBindingInfo_V1->TlsUniqueSize, 1102 out handle); 1103 } 1104 else 1105 { 1106 // Use the V2 token binding behavior 1107 m_TokenBindingVerifyMessageStatus = 1108 UnsafeNclNativeMethods.HttpApi.TokenBindingVerifyMessage( 1109 pTokenBindingInfo->TokenBinding + fixup, 1110 pTokenBindingInfo->TokenBindingSize, 1111 pTokenBindingInfo->KeyType, 1112 pTokenBindingInfo->TlsUnique + fixup, 1113 pTokenBindingInfo->TlsUniqueSize, 1114 out handle); 1115 } 1116 } 1117 1118 if (m_TokenBindingVerifyMessageStatus != 0) 1119 { 1120 throw new HttpListenerException(m_TokenBindingVerifyMessageStatus); 1121 } 1122 1123 Debug.Assert(handle != null); 1124 Debug.Assert(!handle.IsInvalid); 1125 1126 using (handle) 1127 { 1128 // If we have an old binding, use the old binding behavior 1129 if (useV1TokenBinding) 1130 { 1131 GenerateTokenBindings_V1(handle); 1132 } 1133 else 1134 { 1135 GenerateTokenBindings(handle); 1136 } 1137 } 1138 } 1139 1140 /// <summary> 1141 /// Method to allow current bindings to be returned 1142 /// </summary> 1143 /// <param name="handle"></param> GenerateTokenBindings(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)1144 private void GenerateTokenBindings(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle) 1145 { 1146 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle(); 1147 for (int i = 0; i < pResultList->resultCount; i++) 1148 { 1149 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i]; 1150 1151 if (pThisResultData != null) 1152 { 1153 byte[] retVal = new byte[pThisResultData->identifierSize]; 1154 Marshal.Copy((IntPtr)(pThisResultData->identifierData), retVal, 0, retVal.Length); 1155 1156 if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED) 1157 { 1158 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal)); 1159 } 1160 else if (pThisResultData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED) 1161 { 1162 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal)); 1163 } 1164 } 1165 } 1166 } 1167 1168 /// <summary> 1169 /// Compat method to allow V1 bindings to be returned 1170 /// </summary> 1171 /// <param name="handle"></param> GenerateTokenBindings_V1(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle)1172 private void GenerateTokenBindings_V1(UnsafeNclNativeMethods.HttpApi.HeapAllocHandle handle) 1173 { 1174 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST_V1* pResultList = (UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_LIST_V1*)handle.DangerousGetHandle(); 1175 for (int i = 0; i < pResultList->resultCount; i++) 1176 { 1177 UnsafeNclNativeMethods.HttpApi.TOKENBINDING_RESULT_DATA_V1* pThisResultData = &pResultList->resultData[i]; 1178 1179 if (pThisResultData != null) 1180 { 1181 // Old V1 Token Binding protocol is still being used, so we need modify the binding message using the old behavior 1182 1183 // Per http://tools.ietf.org/html/draft-ietf-tokbind-protocol-00, Sec. 4, 1184 // We'll strip off the token binding type and return the remainder as an opaque blob. 1185 Debug.Assert((long)(&pThisResultData->identifierData->hashAlgorithm) == (long)(pThisResultData->identifierData) + 1 ); 1186 byte[] retVal = new byte[pThisResultData->identifierSize - 1]; 1187 Marshal.Copy((IntPtr)(&pThisResultData->identifierData->hashAlgorithm), retVal, 0, retVal.Length); 1188 1189 if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED) 1190 { 1191 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Provided, retVal)); 1192 } 1193 else if (pThisResultData->identifierData->bindingType == UnsafeNclNativeMethods.HttpApi.TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED) 1194 { 1195 m_TokenBindings.Add(new TokenBinding(TokenBindingType.Referred, retVal)); 1196 } 1197 } 1198 } 1199 } 1200 CheckDisposed()1201 internal void CheckDisposed() { 1202 if (m_IsDisposed) { 1203 throw new ObjectDisposedException(this.GetType().FullName); 1204 } 1205 } 1206 1207 // < 1208 1209 1210 1211 1212 static class Helpers { 1213 // 1214 // Get attribute off header value 1215 // GetAttributeFromHeader(String headerValue, String attrName)1216 internal static String GetAttributeFromHeader(String headerValue, String attrName) { 1217 if (headerValue == null) 1218 return null; 1219 1220 int l = headerValue.Length; 1221 int k = attrName.Length; 1222 1223 // find properly separated attribute name 1224 int i = 1; // start searching from 1 1225 1226 while (i < l) { 1227 i = CultureInfo.InvariantCulture.CompareInfo.IndexOf(headerValue, attrName, i, CompareOptions.IgnoreCase); 1228 if (i < 0) 1229 break; 1230 if (i+k >= l) 1231 break; 1232 1233 char chPrev = headerValue[i-1]; 1234 char chNext = headerValue[i+k]; 1235 if ((chPrev == ';' || chPrev == ',' || Char.IsWhiteSpace(chPrev)) && (chNext == '=' || Char.IsWhiteSpace(chNext))) 1236 break; 1237 1238 i += k; 1239 } 1240 1241 if (i < 0 || i >= l) 1242 return null; 1243 1244 // skip to '=' and the following whitespaces 1245 i += k; 1246 while (i < l && Char.IsWhiteSpace(headerValue[i])) 1247 i++; 1248 if (i >= l || headerValue[i] != '=') 1249 return null; 1250 i++; 1251 while (i < l && Char.IsWhiteSpace(headerValue[i])) 1252 i++; 1253 if (i >= l) 1254 return null; 1255 1256 // parse the value 1257 String attrValue = null; 1258 1259 int j; 1260 1261 if (i < l && headerValue[i] == '"') { 1262 if (i == l-1) 1263 return null; 1264 j = headerValue.IndexOf('"', i+1); 1265 if (j < 0 || j == i+1) 1266 return null; 1267 1268 attrValue = headerValue.Substring(i+1, j-i-1).Trim(); 1269 } 1270 else { 1271 for (j = i; j < l; j++) { 1272 if (headerValue[j] == ' ' || headerValue[j] == ',') 1273 break; 1274 } 1275 1276 if (j == i) 1277 return null; 1278 1279 attrValue = headerValue.Substring(i, j-i).Trim(); 1280 } 1281 1282 return attrValue; 1283 } 1284 ParseMultivalueHeader(String s)1285 internal static String[] ParseMultivalueHeader(String s) { 1286 if (s == null) 1287 return null; 1288 1289 int l = s.Length; 1290 1291 // collect comma-separated values into list 1292 1293 ArrayList values = new ArrayList(); 1294 int i = 0; 1295 1296 while (i < l) { 1297 // find next , 1298 int ci = s.IndexOf(',', i); 1299 if (ci < 0) 1300 ci = l; 1301 1302 // append corresponding server value 1303 values.Add(s.Substring(i, ci-i)); 1304 1305 // move to next 1306 i = ci+1; 1307 1308 // skip leading space 1309 if (i < l && s[i] == ' ') 1310 i++; 1311 } 1312 1313 // return list as array of strings 1314 1315 int n = values.Count; 1316 String[] strings; 1317 1318 // if n is 0 that means s was empty string 1319 1320 if (n == 0) { 1321 strings = new String[1]; 1322 strings[0] = String.Empty; 1323 } 1324 else { 1325 strings = new String[n]; 1326 values.CopyTo(0, strings, 0, n); 1327 } 1328 return strings; 1329 } 1330 1331 UrlDecodeStringFromStringInternal(string s, Encoding e)1332 private static string UrlDecodeStringFromStringInternal(string s, Encoding e) { 1333 int count = s.Length; 1334 UrlDecoder helper = new UrlDecoder(count, e); 1335 1336 // go through the string's chars collapsing %XX and %uXXXX and 1337 // appending each char as char, with exception of %XX constructs 1338 // that are appended as bytes 1339 1340 for (int pos = 0; pos < count; pos++) { 1341 char ch = s[pos]; 1342 1343 if (ch == '+') { 1344 ch = ' '; 1345 } 1346 else if (ch == '%' && pos < count-2) { 1347 if (s[pos+1] == 'u' && pos < count-5) { 1348 int h1 = HexToInt(s[pos+2]); 1349 int h2 = HexToInt(s[pos+3]); 1350 int h3 = HexToInt(s[pos+4]); 1351 int h4 = HexToInt(s[pos+5]); 1352 1353 if (h1 >= 0 && h2 >= 0 && h3 >= 0 && h4 >= 0) { // valid 4 hex chars 1354 ch = (char)((h1 << 12) | (h2 << 8) | (h3 << 4) | h4); 1355 pos += 5; 1356 1357 // only add as char 1358 helper.AddChar(ch); 1359 continue; 1360 } 1361 } 1362 else { 1363 int h1 = HexToInt(s[pos+1]); 1364 int h2 = HexToInt(s[pos+2]); 1365 1366 if (h1 >= 0 && h2 >= 0) { // valid 2 hex chars 1367 byte b = (byte)((h1 << 4) | h2); 1368 pos += 2; 1369 1370 // don't add as char 1371 helper.AddByte(b); 1372 continue; 1373 } 1374 } 1375 } 1376 1377 if ((ch & 0xFF80) == 0) 1378 helper.AddByte((byte)ch); // 7 bit have to go as bytes because of Unicode 1379 else 1380 helper.AddChar(ch); 1381 } 1382 1383 return helper.GetString(); 1384 } 1385 HexToInt(char h)1386 private static int HexToInt(char h) { 1387 return( h >= '0' && h <= '9' ) ? h - '0' : 1388 ( h >= 'a' && h <= 'f' ) ? h - 'a' + 10 : 1389 ( h >= 'A' && h <= 'F' ) ? h - 'A' + 10 : 1390 -1; 1391 } 1392 1393 private class UrlDecoder { 1394 private int _bufferSize; 1395 1396 // Accumulate characters in a special array 1397 private int _numChars; 1398 private char[] _charBuffer; 1399 1400 // Accumulate bytes for decoding into characters in a special array 1401 private int _numBytes; 1402 private byte[] _byteBuffer; 1403 1404 // Encoding to convert chars to bytes 1405 private Encoding _encoding; 1406 FlushBytes()1407 private void FlushBytes() { 1408 if (_numBytes > 0) { 1409 _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars); 1410 _numBytes = 0; 1411 } 1412 } 1413 UrlDecoder(int bufferSize, Encoding encoding)1414 internal UrlDecoder(int bufferSize, Encoding encoding) { 1415 _bufferSize = bufferSize; 1416 _encoding = encoding; 1417 1418 _charBuffer = new char[bufferSize]; 1419 // byte buffer created on demand 1420 } 1421 AddChar(char ch)1422 internal void AddChar(char ch) { 1423 if (_numBytes > 0) 1424 FlushBytes(); 1425 1426 _charBuffer[_numChars++] = ch; 1427 } 1428 AddByte(byte b)1429 internal void AddByte(byte b) { 1430 // if there are no pending bytes treat 7 bit bytes as characters 1431 // this optimization is temp disable as it doesn't work for some encodings 1432 /* 1433 if (_numBytes == 0 && ((b & 0x80) == 0)) { 1434 AddChar((char)b); 1435 } 1436 else 1437 */ 1438 { 1439 if (_byteBuffer == null) 1440 _byteBuffer = new byte[_bufferSize]; 1441 1442 _byteBuffer[_numBytes++] = b; 1443 } 1444 } 1445 GetString()1446 internal String GetString() { 1447 if (_numBytes > 0) 1448 FlushBytes(); 1449 1450 if (_numChars > 0) 1451 return new String(_charBuffer, 0, _numChars); 1452 else 1453 return String.Empty; 1454 } 1455 } 1456 1457 FillFromString(NameValueCollection nvc, String s, bool urlencoded, Encoding encoding)1458 internal static void FillFromString(NameValueCollection nvc, String s, bool urlencoded, Encoding encoding) { 1459 int l = (s != null) ? s.Length : 0; 1460 int i = (s.Length>0 && s[0]=='?') ? 1 : 0; 1461 1462 while (i < l) { 1463 // find next & while noting first = on the way (and if there are more) 1464 1465 int si = i; 1466 int ti = -1; 1467 1468 while (i < l) { 1469 char ch = s[i]; 1470 1471 if (ch == '=') { 1472 if (ti < 0) 1473 ti = i; 1474 } 1475 else if (ch == '&') { 1476 break; 1477 } 1478 1479 i++; 1480 } 1481 1482 // extract the name / value pair 1483 1484 String name = null; 1485 String value = null; 1486 1487 if (ti >= 0) { 1488 name = s.Substring(si, ti-si); 1489 value = s.Substring(ti+1, i-ti-1); 1490 } 1491 else { 1492 value = s.Substring(si, i-si); 1493 } 1494 1495 // add name / value pair to the collection 1496 1497 if (urlencoded) 1498 nvc.Add( 1499 name == null ? null : UrlDecodeStringFromStringInternal(name, encoding), 1500 UrlDecodeStringFromStringInternal(value, encoding)); 1501 else 1502 nvc.Add(name, value); 1503 1504 // trailing '&' 1505 1506 if (i == l-1 && s[i] == '&') 1507 nvc.Add(null, ""); 1508 1509 i++; 1510 } 1511 } 1512 } 1513 } 1514 } 1515