protected override void ClientConnected(int clientNumber) { Traces.NntpServerTraceEvent(TraceEventType.Verbose, string.Format("[Client {0}] ClientConnected", clientNumber)); lock (_clients) { _clients.Add(clientNumber, new Client(clientNumber)); } // log connection PerfCounters.IncrementCounter(PerfCounterName.CurrentConnections); PerfCounters.IncrementCounter(PerfCounterName.TotalConnections); if (_postingAllowed) { SendData(GeneralResponses.ServerReadyPostingAllowed, clientNumber); } else { SendData(GeneralResponses.ServerReadyPostingNotAllowed, clientNumber); } }
protected override void DataReceived(string data, int clientNumber) { try { Stopwatch sw = new Stopwatch(); sw.Start(); NntpCommand nntpCommand = null; var client = GetClient(clientNumber); string response; if (client == null) { throw new Exception("Client object for [" + clientNumber + "] is not available"); } //LogMessage(clientNumber, client.AuthenticatedClientUsername, data, NO_LOGGING, string.Empty); //LogMessage(clientNumber, string.Empty, data, NO_LOGGING, string.Empty); // log receive data size PerfCounters.IncrementCounterBy(PerfCounterName.BytesReceived, data.Length); bool parseCommand = false; string additionalData = string.Empty; if (client.PreviousCommand == Command.POST) { Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "DataReceived: Client is posting data"); //System.Diagnostics.Debug.WriteLine("Buffer: {0}", client.Buffer); //System.Diagnostics.Debug.WriteLine("Recv: {0}", data); // client is posting data, // keep buffering until dotted terminator reached string postData = data; /* * string ascSeq = string.Empty; * for (int i = 0; i < postData.Length; i++) * { * ascSeq += (int)postData[i] + " "; * } * //System.Diagnostics.Trace.WriteLine("ascSeq - " + ascSeq); */ // Append the data to the internal buffer client.Buffer.Append(postData); // Then extract (at least) the last 5 chars (or min the number of currently received bytes) to determine the termination... string lastChars = string.Empty; if (client.Buffer.Length >= 5) { int len = Math.Max(postData.Length, 5); lastChars = client.Buffer.ToString(client.Buffer.Length - len, len); } // Check for "termination" signal... if (lastChars.IndexOf("\r\n.\r\n") >= 0) { //System.Diagnostics.Trace.WriteLine("Termination sequence sent"); var buf = client.Buffer.ToString(); // extract post data up to the dot terminator client.Buffer = new StringBuilder(buf.Substring(0, buf.IndexOf("\r\n.\r\n"))); // grab any data received after the terminator additionalData = buf.Substring(buf.IndexOf("\r\n.\r\n") + 5); // flag to say we wish to process the received data parseCommand = true; } //else if (lastChars.StartsWith(".\r\n")) //{ // // WHAT does this mean??? // // no post data in this request // postData = string.Empty; // // grab any data received after the terminator // additionalData = data.Substring(3); // // flag to say we wish to process the received data // parseCommand = true; //} //client.Buffer.Append(postData); if (parseCommand) { //System.Diagnostics.Trace.WriteLine("Posting article"); // create a new command nntpCommand = new NntpCommandPostData { Provider = _dataProvider }; // TODO: nntpCommand.ClientUsername = client.AuthenticatedClientUsername; // log command PerfCounters.IncrementCounter(PerfCounterName.TotalNntpCommandPostData); // ensure command executed in context of authenticated user // TODO: client.ImpersonateClient(); Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "Received: {0}: {1}", nntpCommand.Command, client.Buffer.ToString()); // post the article response = nntpCommand.Parse(client.Buffer.ToString(), null, client); Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "Response: {0}", response); // revert to service security context // TODO: client.RevertImpersonation(); // send back success/fail message SendData(response, clientNumber); PerfCounters.IncrementCounter(PerfCounterName.CommandsProcessedPerSecond); // clear down buffer & reset command client.Buffer = new StringBuilder(); client.PreviousCommand = Command.NOTRECOGNISED; nntpCommand = null; } } else { //System.Diagnostics.Trace.WriteLine("Client not posting data"); // client is not posting data, // wait until CR-LF pair is reached string postData = data; if (postData.IndexOf("\r\n") >= 0) { //System.Diagnostics.Trace.WriteLine("CR-LF pair received"); // extract up to end of line postData = postData.Substring(0, postData.IndexOf("\r\n")); // obtain further data (should not normally be present) additionalData = data.Substring(data.IndexOf("\r\n") + 2); parseCommand = true; } client.Buffer.Append(postData); if (parseCommand) { // determine type of command and extract parameters nntpCommand = ClassifyCommand(client); // clear down buffer client.Buffer = new StringBuilder(); } } if (nntpCommand != null) { // ensure command executed in context of authenticated user // TODO: client.ImpersonateClient(); // execute the command var dataSendInAction = false; Action <string> streamWriter = p => { SendData(p, client.ClientNumber); dataSendInAction = true; Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "Received: Response: {0}", p); }; Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "Received: Command: {0}, Parameters: {1}", nntpCommand.Command, client.CommandParameters); response = nntpCommand.Parse(client.CommandParameters, streamWriter, client); // revert to service security context // TODO: client.RevertImpersonation(); // log command PerfCounterName perfCounterNameForCommand = GetTotalCounterFromCommand(client.PreviousCommand); PerfCounters.IncrementCounter(perfCounterNameForCommand); // client attempting to authenticate more than once // then re-authenticate if (client.PreviousCommand == Command.AUTHINFO && nntpCommand.AuthToken == null) { if (client.Authenticated) { // TODO: client.AuthServerContext.Dispose(); // TODO: client.AuthServerContext = null; client.Authenticated = false; } } // steps 2 and 3 of the authentication process if (client.PreviousCommand == Command.AUTHINFO && nntpCommand.AuthToken != null) { try { // TODO: ServerCredential serverCredential = new ServerCredential(Credential.Package.NTLM); //if (client.AuthServerContext == null) //{ // client.AuthServerContext = new ServerContext(serverCredential, nntpCommand.AuthToken); // response = response.Replace("<token>", Convert.ToBase64String(client.AuthServerContext.Token)); //} //else //{ //client.AuthServerContext.Accept(nntpCommand.AuthToken); client.Authenticated = true; response = GeneralResponses.AuthenticationAccepted; // PerfCounters.IncrementCounter(PerfCounterName.ResponseAuthenticationAccepted); //} } catch (Exception ex) { // error during authentication (e.g. access denied) // return response to client Traces.NntpServerTraceEvent(TraceEventType.Critical, client, "Failed to authenticate: {0}", Traces.ExceptionToString((ex))); // TODO: client.AuthServerContext.Dispose(); // TODO: client.AuthServerContext = null; client.Authenticated = false; response = GeneralResponses.AccessDenied; PerfCounters.IncrementCounter(PerfCounterName.ResponseAccessDenied); } } //// if user not logged in, send back authenticated required //// message //if (client.PreviousCommand != Command.MODE && // client.PreviousCommand != Command.AUTHINFO && // (!client.Authenticated || // (client.Authenticated && // client.AuthenticatedClientUsername.Length == 0))) //{ // response = GeneralResponses.AuthenticationRequired; // PerfCounters.IncrementCounter(PerfCounterName.ResponseAuthenticationRequired); //} // send response to client if (dataSendInAction == false) { if (string.IsNullOrEmpty(response) == false) { SendData(response, clientNumber); Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "Received: Response: {0}", response); PerfCounters.IncrementCounter(PerfCounterName.CommandsProcessedPerSecond); // log sent data size PerfCounters.IncrementCounterBy(PerfCounterName.BytesSent, response.Length); } else { throw new Exception("No data returned from parsed command [" + client.PreviousCommand + "]"); } } // adjust client state after command execution switch (client.PreviousCommand) { case Command.POST: if (nntpCommand.PostCancelled) { Trace.WriteLine("Posting is not allowed, reset status on client object"); client.PreviousCommand = Command.NOTRECOGNISED; } break; case Command.QUIT: DisconnectClient(clientNumber); break; } } // continue processing if needed // additional commands may follow the command // or post data just processed if (additionalData.Trim().Length > 0) { DataReceived(additionalData, clientNumber); } sw.Stop(); lock (_perfCounterLock) { PerfCounters.IncrementCounterBy(PerfCounterName.AverageProcessingTime, sw.ElapsedTicks); PerfCounters.IncrementCounter(PerfCounterName.AverageProcessingTimeBase); } } catch (Exception ex) { Traces.NntpServerTraceEvent(TraceEventType.Critical, "[Client {0}] DataReceived failed: {1}", clientNumber, Traces.ExceptionToString(ex)); if (clientNumber > 0) { PerfCounters.IncrementCounter(PerfCounterName.ResponseProgramFault); // INFO: Reply with more specific error message: //if (DetailedErrorResponse) //{ var resp = string.Format( "503 program fault - command not performed {0}\r\n", GetErrorResponseFromExeption(ex)); SendData(resp, clientNumber); //} //else //{ // SendData(GeneralResponses.ProgramFault, clientNumber); //} } } }