/// <summary> /// <para>Creates an array of commands, that will be sent to the server</para> /// </summary> protected override PipelineEntry[] BuildCommandsList(WebRequest req) { bool resetLoggedInState = false; FtpWebRequest request = (FtpWebRequest)req; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this); } _responseUri = request.RequestUri; ArrayList commandList = new ArrayList(); if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS"))); // According to RFC we need to re-authorize with USER/PASS after we re-authenticate. resetLoggedInState = true; } if (resetLoggedInState) { _loginDirectory = null; _establishedServerDirectory = null; _requestedServerDirectory = null; _currentTypeSetting = string.Empty; if (_loginState == FtpLoginState.LoggedIn) { _loginState = FtpLoginState.LoggedInButNeedsRelogin; } } if (_loginState != FtpLoginState.LoggedIn) { Credentials = request.Credentials !.GetCredential(request.RequestUri, "basic"); _welcomeMessage = new StringBuilder(); _exitMessage = new StringBuilder(); string domainUserName = string.Empty; string password = string.Empty; if (Credentials != null) { domainUserName = Credentials.UserName; string domain = Credentials.Domain; if (!string.IsNullOrEmpty(domain)) { domainUserName = domain + "\\" + domainUserName; } password = Credentials.Password; } if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "******"; // [SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Suppression approved. Anonymous FTP credential in production code.")] password = "******"; } commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName))); commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter)); // If SSL, always configure data channel encryption after authentication to maximum RFC compatibility. The RFC allows for // PBSZ/PROT commands to come either before or after the USER/PASS, but some servers require USER/PASS immediately after // the AUTH TLS command. if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P"))); } commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null))); } GetPathOption getPathOption = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { getPathOption = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { getPathOption = GetPathOption.AssumeFilename; } string requestPath; string requestDirectory; string requestFilename; GetPathInfo(getPathOption, request.RequestUri, out requestPath, out requestDirectory, out requestFilename); if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) { throw new WebException(SR.net_ftp_invalid_uri); } // We optimize for having the current working directory staying at the login directory. This ensure that // our relative paths work right and reduces unnecessary CWD commands. // Usually, we don't change the working directory except for some FTP commands. If necessary, // we need to reset our working directory back to the login directory. if (_establishedServerDirectory != null && _loginDirectory != null && _establishedServerDirectory != _loginDirectory) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", _loginDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = _loginDirectory; } // For most commands, we don't need to navigate to the directory since we pass in the full // path as part of the FTP protocol command. However, some commands require it. if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && requestDirectory.Length > 0) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", requestDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = requestDirectory; } if (!request.MethodInfo.IsCommandOnly) { string requestedTypeSetting = request.UseBinary ? "I" : "A"; if (_currentTypeSetting != requestedTypeSetting) { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", requestedTypeSetting))); _currentTypeSetting = requestedTypeSetting; } if (request.UsePassive) { string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork || ServerAddress.IsIPv4MappedToIPv6) ? "PASV" : "EPSV"; commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection)); } else { string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork || ServerAddress.IsIPv4MappedToIPv6) ? "PORT" : "EPRT"; CreateFtpListenerSocket(request); commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request)))); } if (request.ContentOffset > 0) { // REST command must always be the last sent before the main file command is sent. commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } PipelineEntryFlags flags = PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { flags |= PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) { flags |= PipelineEntryFlags.CreateDataConnection; } } if (request.MethodInfo.Operation == FtpOperation.Rename) { string baseDir = (requestDirectory.Length == 0) ? string.Empty : requestDirectory + "/"; commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags)); string renameTo; if (!string.IsNullOrEmpty(request.RenameTo) && request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { renameTo = request.RenameTo; // Absolute path } else { renameTo = baseDir + request.RenameTo; // Relative path } commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", renameTo), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, string.Empty), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags)); } else { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestPath), flags)); } commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null))); return((PipelineEntry[])commandList.ToArray(typeof(PipelineEntry))); }
private PipelineInstruction QueueOrCreateDataConection(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream?stream, out bool isSocketReady) { isSocketReady = false; if (_dataHandshakeStarted) { isSocketReady = true; return(PipelineInstruction.Pause); //if we already started then this is re-entering into the callback where we proceed with the stream } _dataHandshakeStarted = true; // Handle passive responses by parsing the port and later doing a Connect(...) bool isPassive = false; int port = -1; if (entry.Command == "PASV\r\n" || entry.Command == "EPSV\r\n") { if (!response.PositiveCompletion) { _abortReason = SR.Format(SR.net_ftp_server_failed_passive, response.Status); return(PipelineInstruction.Abort); } if (entry.Command == "PASV\r\n") { port = GetPortV4(response.StatusDescription !); } else { port = GetPortV6(response.StatusDescription !); } isPassive = true; } if (isPassive) { Debug.Assert(port != -1, "'port' not set."); try { _dataSocket = CreateFtpDataSocket((FtpWebRequest)_request !, Socket); } catch (ObjectDisposedException) { throw ExceptionHelper.RequestAbortedException; } IPEndPoint localEndPoint = new IPEndPoint(((IPEndPoint)Socket.LocalEndPoint !).Address, 0); _dataSocket.Bind(localEndPoint); _passiveEndPoint = new IPEndPoint(ServerAddress, port); } PipelineInstruction result; if (_passiveEndPoint != null) { IPEndPoint passiveEndPoint = _passiveEndPoint; _passiveEndPoint = null; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "starting Connect()"); } if (_isAsync) { _dataSocket !.BeginConnect(passiveEndPoint, s_connectCallbackDelegate, this); result = PipelineInstruction.Pause; } else { _dataSocket !.Connect(passiveEndPoint); result = PipelineInstruction.Advance; // for passive mode we end up going to the next command } } else { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "starting Accept()"); } if (_isAsync) { _dataSocket !.BeginAccept(s_acceptCallbackDelegate, this); result = PipelineInstruction.Pause; } else { Socket listenSocket = _dataSocket !; try { _dataSocket = _dataSocket !.Accept(); if (!ServerAddress.Equals(((IPEndPoint)_dataSocket.RemoteEndPoint !).Address)) { _dataSocket.Close(); throw new WebException(SR.net_ftp_active_address_different, WebExceptionStatus.ProtocolError); } isSocketReady = true; // for active mode we end up creating a stream before advancing the pipeline result = PipelineInstruction.Pause; } finally { listenSocket.Close(); } } } return(result); }
protected override bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, $"CheckValid({response.StatusBuffer})"); } // If the response is less than 4 bytes long, it is too short to tell, so return true, valid so far. if (response.StatusBuffer.Length < 4) { return(true); } string responseString = response.StatusBuffer.ToString(); // Otherwise, if there is no status code for this response yet, get one. if (response.Status == ResponseDescription.NoStatus) { // If the response does not start with three digits, then it is not a valid response from an FTP server. if (!(char.IsDigit(responseString[0]) && char.IsDigit(responseString[1]) && char.IsDigit(responseString[2]) && (responseString[3] == ' ' || responseString[3] == '-'))) { return(false); } else { response.StatusCodeString = responseString.Substring(0, 3); response.Status = Convert.ToInt16(response.StatusCodeString, NumberFormatInfo.InvariantInfo); } // IF a hyphen follows the status code on the first line of the response, then we have a multiline response coming. if (responseString[3] == '-') { response.Multiline = true; } } // If a complete line of response has been received from the server, then see if the // overall response is complete. // If this was not a multiline response, then the response is complete at the end of the line. // If this was a multiline response (indicated by three digits followed by a '-' in the first line), // then we see if the last line received started with the same three digits followed by a space. // If it did, then this is the sign of a complete multiline response. // If the line contained three other digits followed by the response, then this is a violation of the // FTP protocol for multiline responses. // All other cases indicate that the response is not yet complete. int index = 0; while ((index = responseString.IndexOf("\r\n", validThrough, StringComparison.Ordinal)) != -1) // gets the end line. { int lineStart = validThrough; validThrough = index + 2; // validThrough now marks the end of the line being examined. if (!response.Multiline) { completeLength = validThrough; return(true); } if (responseString.Length > lineStart + 4) { // If the first three characters of the response line currently being examined // match the status code, then if they are followed by a space, then we // have reached the end of the reply. if (responseString.Substring(lineStart, 3) == response.StatusCodeString) { if (responseString[lineStart + 3] == ' ') { completeLength = validThrough; return(true); } } } } return(true); }
// This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the setting up of a data socket/connection, and of saving off the server responses. protected override PipelineInstruction PipelineCallback(PipelineEntry?entry, ResponseDescription?response, bool timeout, ref Stream?stream) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}"); } // null response is not expected if (response == null) { return(PipelineInstruction.Abort); } FtpStatusCode status = (FtpStatusCode)response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) { throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError); } // Update the banner message if any, this is a little hack because the "entry" param is null if (_index == -1) { if (status == FtpStatusCode.SendUserCommand) { _bannerMessage = new StringBuilder(); _bannerMessage.Append(StatusLine); return(PipelineInstruction.Advance); } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return(PipelineInstruction.Reread); } else { throw GenerateException(status, response.StatusDescription, null); } } // // Check for the result of our attempt to use UTF8 // if (entry !.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return(PipelineInstruction.Advance); } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER", StringComparison.Ordinal) != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; _index++; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status, response.StatusDescription, null); } if (_loginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS", StringComparison.Ordinal) != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; } else { throw GenerateException(status, response.StatusDescription, null); } } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) { return(result); } // otherwise we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (_dataSocket == null) { return(PipelineInstruction.Abort); } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command); return(PipelineInstruction.Abort); } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription !); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest)_request !; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription !, request); } return(QueueOrCreateFtpDataStream(ref stream)); } // // Parse responses by status code exclusivelly // // Update welcome message if (status == FtpStatusCode.LoggedInProceed) { _welcomeMessage !.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { _exitMessage !.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) { FtpWebRequest request = (FtpWebRequest)_request !; TlsStream tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates); if (_isAsync) { tlsStream.BeginAuthenticateAsClient(ar => { try { tlsStream.EndAuthenticateAsClient(ar); NetworkStream = tlsStream; this.ContinueCommandPipeline(); } catch (Exception e) { this.CloseSocket(); this.InvokeRequestCallback(e); } }, null); return(PipelineInstruction.Pause); } else { tlsStream.AuthenticateAsClient(); NetworkStream = tlsStream; } } } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { if (entry.Command.StartsWith("SIZE ", StringComparison.Ordinal)) { _contentLength = GetContentLengthFrom213Response(response.StatusDescription !); } else if (entry.Command.StartsWith("MDTM ", StringComparison.Ordinal)) { _lastModified = GetLastModifiedFrom213Response(response.StatusDescription !); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { _loginDirectory = GetLoginDirectory(response.StatusDescription !); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD", StringComparison.Ordinal) != -1) { _establishedServerDirectory = _requestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return(PipelineInstruction.Reread); } return(PipelineInstruction.Advance); }
private bool CaptureOrComplete(ref ExecutionContext?cachedContext, bool returnContext) { if ((_flags & StateFlags.PostBlockStarted) == 0) { NetEventSource.Fail(this, "Called without calling StartPostingAsyncOp."); } // See if we're going to need to capture the context. bool capturingContext = AsyncCallback != null || (_flags & StateFlags.CaptureContext) != 0; // Peek if we've already completed, but don't fix CompletedSynchronously yet // Capture the identity if requested, unless we're going to capture the context anyway, unless // capturing the context won't be sufficient. if ((_flags & StateFlags.CaptureIdentity) != 0 && !InternalPeekCompleted && (!capturingContext)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "starting identity capture"); } SafeCaptureIdentity(); } // No need to flow if there's no callback, unless it's been specifically requested. // Note that Capture() can return null, for example if SuppressFlow() is in effect. if (capturingContext && !InternalPeekCompleted) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "starting capture"); } if (cachedContext == null) { cachedContext = ExecutionContext.Capture(); } if (cachedContext != null) { if (!returnContext) { _context = cachedContext; cachedContext = null; } else { _context = cachedContext; } } if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"_context:{_context}"); } } else { // Otherwise we have to have completed synchronously, or not needed the context. if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Skipping capture"); } cachedContext = null; if (AsyncCallback != null && !CompletedSynchronously) { NetEventSource.Fail(this, "Didn't capture context, but didn't complete synchronously!"); } } // Now we want to see for sure what to do. We might have just captured the context for no reason. // This has to be the first time the state has been queried "for real" (apart from InvokeCallback) // to guarantee synchronization with Complete() (otherwise, Complete() could try to call the // callback without the context having been gotten). DebugProtectState(false); if (CompletedSynchronously) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Completing synchronously"); } base.Complete(IntPtr.Zero); return(true); } return(false); }
/// Pipelined command resolution. /// How this works: /// A list of commands that need to be sent to the FTP server are spliced together into an array, /// each command such STOR, PORT, etc, is sent to the server, then the response is parsed into a string, /// with the response, the delegate is called, which returns an instruction (either continue, stop, or read additional /// responses from server). protected Stream?ContinueCommandPipeline() { // In async case, The BeginWrite can actually result in a // series of synchronous completions that eventually close // the connection. So we need to save the members that // we need to access, since they may not be valid after // BeginWrite returns bool isAsync = _isAsync; while (_index < _commands !.Length) { if (_doSend) { if (_index < 0) { throw new InternalException(); } byte[] sendBuffer = Encoding.GetBytes(_commands[_index].Command); if (NetEventSource.Log.IsEnabled()) { string sendCommand = _commands[_index].Command.Substring(0, _commands[_index].Command.Length - 2); if (_commands[_index].HasFlag(PipelineEntryFlags.DontLogParameter)) { int index = sendCommand.IndexOf(' '); if (index != -1) { sendCommand = string.Concat(sendCommand.AsSpan(0, index), " ********"); } } if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, $"Sending command {sendCommand}"); } } try { if (isAsync) { BeginWrite(sendBuffer, 0, sendBuffer.Length, s_writeCallbackDelegate, this); } else { Write(sendBuffer, 0, sendBuffer.Length); } } catch (IOException) { MarkAsRecoverableFailure(); throw; } catch { throw; } if (isAsync) { return(null); } } Stream?stream = null; bool isReturn = PostSendCommandProcessing(ref stream); if (isReturn) { return(stream); } } lock (this) { Close(); } return(null); }
// IO COMPLETION CALLBACK // // This callback is responsible for getting the complete protocol frame. // 1. it reads the header. // 2. it determines the frame size. // 3. loops while not all frame received or an error. // private void ReadFrameComplete(IAsyncResult transportResult) { do { if (!(transportResult.AsyncState is WorkerAsyncResult)) { NetEventSource.Fail(this, $"The state expected to be WorkerAsyncResult, received {transportResult}."); } WorkerAsyncResult workerResult = (WorkerAsyncResult)transportResult.AsyncState !; int bytesRead = TaskToApm.End <int>(transportResult); workerResult.Offset += bytesRead; if (!(workerResult.Offset <= workerResult.End)) { NetEventSource.Fail(this, $"WRONG: offset - end = {workerResult.Offset - workerResult.End}"); } if (bytesRead <= 0) { // (by design) This indicates the stream has receives EOF // If we are in the middle of a Frame - fail, otherwise - produce EOF object?result = null; if (!workerResult.HeaderDone && workerResult.Offset == 0) { result = (object)-1; } else { result = new System.IO.IOException(SR.net_frame_read_io); } workerResult.InvokeCallback(result); return; } if (workerResult.Offset >= workerResult.End) { if (!workerResult.HeaderDone) { workerResult.HeaderDone = true; // This indicates the header has been read successfully _curReadHeader.CopyFrom(workerResult.Buffer !, 0, _readVerifier); int payloadSize = _curReadHeader.PayloadSize; if (payloadSize < 0) { // Let's call user callback and they call us back and we will throw workerResult.InvokeCallback(new System.IO.IOException(SR.net_frame_read_size)); } if (payloadSize == 0) { // report empty frame (NOT eof!) to the caller, he might be interested in workerResult.InvokeCallback(0); return; } if (payloadSize > _curReadHeader.MaxMessageSize) { throw new InvalidOperationException(SR.Format(SR.net_frame_size, _curReadHeader.MaxMessageSize.ToString(NumberFormatInfo.InvariantInfo), payloadSize.ToString(NumberFormatInfo.InvariantInfo))); } // Start reading the remaining frame data (note header does not count). byte[] frame = new byte[payloadSize]; // Save the ref of the data block workerResult.Buffer = frame; workerResult.End = frame.Length; workerResult.Offset = 0; // Transport.ReadAsync below will pickup those changes. } else { workerResult.HeaderDone = false; // Reset for optional object reuse. workerResult.InvokeCallback(workerResult.End); return; } } // This means we need more data to complete the data block. transportResult = TaskToApm.Begin(_transport.ReadAsync(workerResult.Buffer !, workerResult.Offset, workerResult.End - workerResult.Offset), _readFrameCallback, workerResult); } while (transportResult.CompletedSynchronously); }
private void Initialize(bool isServer, string package, NetworkCredential credential, string spn, Interop.SspiCli.ContextFlags requestedContextFlags, ChannelBinding channelBinding) { if (GlobalLog.IsEnabled) { GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::.ctor() package:" + LoggingHash.ObjectToString(package) + " spn:" + LoggingHash.ObjectToString(spn) + " flags :" + requestedContextFlags.ToString()); } _tokenSize = SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true).MaxToken; _isServer = isServer; _spn = spn; _securityContext = null; _requestedContextFlags = requestedContextFlags; _package = package; _channelBinding = channelBinding; if (GlobalLog.IsEnabled) { GlobalLog.Print("Peer SPN-> '" + _spn + "'"); } // // Check if we're using DefaultCredentials. // Debug.Assert(CredentialCache.DefaultCredentials == CredentialCache.DefaultNetworkCredentials); if (credential == CredentialCache.DefaultCredentials) { if (GlobalLog.IsEnabled) { GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::.ctor(): using DefaultCredentials"); } _credentialsHandle = SSPIWrapper.AcquireDefaultCredential( GlobalSSPI.SSPIAuth, package, (_isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound)); } else { unsafe { SafeSspiAuthDataHandle authData = null; try { Interop.SecurityStatus result = Interop.SspiCli.SspiEncodeStringsAsAuthIdentity( credential.UserName, credential.Domain, credential.Password, out authData); if (result != Interop.SecurityStatus.OK) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.PrintError( NetEventSource.ComponentType.Security, SR.Format( SR.net_log_operation_failed_with_error, "SspiEncodeStringsAsAuthIdentity()", String.Format(CultureInfo.CurrentCulture, "0x{0:X}", (int)result))); } throw new Win32Exception((int)result); } _credentialsHandle = SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth, package, (_isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound), ref authData); } finally { if (authData != null) { authData.Dispose(); } } } } }
internal HttpResponseStreamAsyncResult(object asyncObject, object?userState, AsyncCallback?callback, byte[] buffer, int offset, int size, bool chunked, bool sentHeaders, ThreadPoolBoundHandle boundHandle) : base(asyncObject, userState, callback) { _boundHandle = boundHandle; _sentHeaders = sentHeaders; if (size == 0) { _dataChunks = null; _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: null); } else { _dataChunks = new Interop.HttpApi.HTTP_DATA_CHUNK[chunked ? 3 : 1]; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, "m_pOverlapped:0x" + ((IntPtr)_pOverlapped).ToString("x8")); } object[] objectsToPin = new object[1 + _dataChunks.Length]; objectsToPin[_dataChunks.Length] = _dataChunks; int chunkHeaderOffset = 0; byte[]? chunkHeaderBuffer = null; if (chunked) { chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset); _dataChunks[0] = default; _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)(chunkHeaderBuffer.Length - chunkHeaderOffset); objectsToPin[0] = chunkHeaderBuffer; _dataChunks[1] = default; _dataChunks[1].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[1].BufferLength = (uint)size; objectsToPin[1] = buffer; _dataChunks[2] = default; _dataChunks[2].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[2].BufferLength = (uint)s_CRLFArray.Length; objectsToPin[2] = s_CRLFArray; } else { _dataChunks[0] = default; _dataChunks[0].DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; _dataChunks[0].BufferLength = (uint)size; objectsToPin[0] = buffer; } // This call will pin needed memory _pOverlapped = boundHandle.AllocateNativeOverlapped(s_IOCallback, state: this, pinData: objectsToPin); if (chunked) { _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(chunkHeaderBuffer !, chunkHeaderOffset)); _dataChunks[1].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); _dataChunks[2].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(s_CRLFArray, 0)); } else { _dataChunks[0].pBuffer = (byte *)(Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); } } }