protected override void ClientDisconnected(int clientNumber) { Traces.NntpServerTraceEvent(TraceEventType.Verbose, "[Client {0}] ClientDisconnected", clientNumber); //Client client = GetClient(clientNumber); //if (client != null && // client.AuthServerContext != null) //{ // client.AuthServerContext.Dispose(); //} lock (_clients) { try { _clients.Remove(clientNumber); } catch { // continue Traces.NntpServerTraceEvent(TraceEventType.Warning, "[Client {0}] ClientDisconnected: Unable to remove client", clientNumber); } } // log disconnection PerfCounters.DecrementCounter(PerfCounterName.CurrentConnections); }
protected void CloseSockets() { try { if (_primarySocket != null) { _primarySocket.Close(); } lock (_workerSockets) { foreach (KeyValuePair <int, Socket> key in _workerSockets) { Socket workerSocket = key.Value; if (workerSocket != null) { workerSocket.Close(); } } _workerSockets.Clear(); _clients = 0; } } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Error, "Server.CloseSockets failed: {0}", Traces.ExceptionToString(ex)); } }
public void OnDataReceived(IAsyncResult ar) { var socketPacket = (SocketPacket)ar.AsyncState; try { // complete BeginReceive() - returns number of characters received int charsReceived = socketPacket.ClientSocket.EndReceive(ar); if (charsReceived > 0) { // extract data string data = EncodingRecv.GetString(socketPacket.Buffer, 0, charsReceived); DataReceived(data, socketPacket.ClientNumber); // continue to receive data if still connected WaitForData(socketPacket.ClientSocket, socketPacket.ClientNumber); } else { DisconnectClient(socketPacket.ClientNumber); } } catch (ObjectDisposedException) { //System.Diagnostics.Trace.WriteLine("#" + socketPacket.ClientNumber.ToString() + " - OnDataReceived: Socket has been closed"); return; } catch (SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) // connection reset by peer { // remove worker socket of closed client DisconnectClient(socketPacket.ClientNumber); } else if (se.SocketErrorCode == SocketError.ConnectionAborted) // connection aborted { // remove worker socket of closed client DisconnectClient(socketPacket.ClientNumber); } else { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnDataReceived failed: {1}", socketPacket.ClientNumber, Traces.ExceptionToString(se)); } return; } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnDataReceived failed: {1}", socketPacket.ClientNumber, Traces.ExceptionToString(ex)); } }
protected void DisconnectClient(int clientNumber) { try { Socket workerSocket; try { lock (_workerSockets) { workerSocket = _workerSockets[clientNumber]; } } catch { //System.Diagnostics.Trace.WriteLine("Server::DisconnectClient - unable to access entry [" + clientNumber + "]"); return; } if (workerSocket != null) { workerSocket.Close(); try { lock (_workerSockets) { _workerSockets.Remove(clientNumber); } } // ReSharper disable EmptyGeneralCatchClause catch // ReSharper restore EmptyGeneralCatchClause { // continue //System.Diagnostics.Trace.WriteLine("Server::DisconnectClient - unable to remove entry [" + clientNumber + "]"); } ClientDisconnected(clientNumber); } } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Error, "[Client {0}] Server.DisconnectClient failed: {1}", clientNumber, Traces.ExceptionToString(ex)); } }
public static string DecodeQuotedPrintable(string text) { if (string.IsNullOrEmpty(text)) { return(text); } try { var returnValue = qpRegEx.Replace(text, new MatchEvaluator(ReplaceOctet)); return(returnValue.Replace("=\n", " ")); } catch (Exception exp) { Traces.NntpServerTraceEvent(TraceEventType.Error, "Error converting QP text: {0}\r\n\r\n{1}", text, Traces.ExceptionToString(exp)); } return(text); }
public void WaitForData(Socket socket, int clientNumber) { try { if (_workerReceiveCallBack == null) { _workerReceiveCallBack = new AsyncCallback(OnDataReceived); } var socketPacket = new SocketPacket(socket, clientNumber); socket.BeginReceive(socketPacket.Buffer, 0, socketPacket.Buffer.Length, SocketFlags.None, _workerReceiveCallBack, socketPacket); } catch (ObjectDisposedException) { //System.Diagnostics.Trace.WriteLine("#" + clientNumber.ToString() + " - WaitForData: Socket has been closed"); return; } catch (SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) // connection reset by peer { // remove worker socket of closed client DisconnectClient(clientNumber); } else if (se.SocketErrorCode == SocketError.ConnectionAborted) // connection aborted { // remove worker socket of closed client DisconnectClient(clientNumber); } else { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.WaitForData failed: {1}", clientNumber, Traces.ExceptionToString(se)); } return; } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.WaitForData failed: {1}", clientNumber, Traces.ExceptionToString(ex)); } }
public static string DecodeBase64(string text, string charSet) { if (string.IsNullOrEmpty(text)) { return(text); } try { var enc = Encoding.GetEncoding(charSet); byte[] data = Convert.FromBase64String(text); return(enc.GetString(data)); } catch (Exception exp) { Traces.NntpServerTraceEvent(TraceEventType.Error, "Error converting base64 text: {0}\r\n\r\n{1}", text, Traces.ExceptionToString(exp)); } return(text); }
public void Start(int port, int pendingConnectionsLength, bool bindToWorld, out string errorString) { errorString = null; try { _primarySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint ipLocal; if (bindToWorld) { ipLocal = new IPEndPoint(IPAddress.Any, port); } else { ipLocal = new IPEndPoint(IPAddress.Loopback, port); } // bind to local address _primarySocket.Bind(ipLocal); // start listening _primarySocket.Listen(pendingConnectionsLength); // call back for client connections _primarySocket.BeginAccept(new AsyncCallback(OnClientConnect), null); } catch (SocketException se) { errorString = se.Message; Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "Server.Start failed: {0}", Traces.ExceptionToString(se)); } catch (Exception ex) { // TODO: Logging.Error("Failed to establish master listener on port [" + port.ToString() + "]", ex); Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "Server.Start failed: {0}", Traces.ExceptionToString(ex)); errorString = ex.Message; throw new Exception("Failed to establish master listener", ex); } }
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); } }
private void ReEncode(Article article, string charSet) { try { var enc = Encoding.GetEncoding(charSet); if (enc.CodePage != Server.EncodingRecv.CodePage) { var raw = Server.EncodingRecv.GetBytes(article.Body); var str = enc.GetString(raw); article.Body = str; } } catch (Exception exp) { Traces.NntpServerTraceEvent( TraceEventType.Critical, "PostArticle: Could not convert into the desired charset: {0}, {1}", article.ContentType, Traces.ExceptionToString(exp)); } }
private Client GetClient(int clientNumber) { Client client = null; if (_clients != null) { lock (_clients) { try { client = _clients[clientNumber]; } catch { // continue Traces.NntpServerTraceEvent(TraceEventType.Warning, "[Client {0}] GetClient: Unable to get client", clientNumber); } } } return(client); }
public void OnDataSend(IAsyncResult ar) { var socketPacket = (SocketPacket)ar.AsyncState; try { socketPacket.ClientSocket.EndSend(ar); } catch (ObjectDisposedException) { //System.Diagnostics.Trace.WriteLine("#" + socketPacket.ClientNumber.ToString() + " - OnDataSend: Socket has been closed"); return; } catch (SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) // connection reset by peer { // remove worker socket of closed client DisconnectClient(socketPacket.ClientNumber); } else if (se.SocketErrorCode == SocketError.ConnectionAborted) // connection aborted { // remove worker socket of closed client DisconnectClient(socketPacket.ClientNumber); } else { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnDataSend failed: {1}", socketPacket.ClientNumber, Traces.ExceptionToString(se)); } return; } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnDataSend failed: {1}", socketPacket.ClientNumber, Traces.ExceptionToString(ex)); } }
public void OnClientConnect(IAsyncResult ar) { int currentClient = 0; try { // get the worker socket for this connection Socket workerSocket = _primarySocket.EndAccept(ar); lock (_workerSockets) { Interlocked.Increment(ref _clients); currentClient = _clients; _workerSockets.Add(currentClient, workerSocket); } ClientConnected(currentClient); WaitForData(workerSocket, currentClient); } catch (ObjectDisposedException) { //System.Diagnostics.Trace.WriteLine("#" + currentClient.ToString() + " - OnClientConnection: Socket has been closed"); //Logging.Error("OnClientConnect dispose error", ex); return; } catch (SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) // connection reset by peer { // remove worker socket of closed client if (currentClient > 0) { DisconnectClient(currentClient); } } else if (se.SocketErrorCode == SocketError.ConnectionAborted) // connection aborted { // remove worker socket of closed client if (currentClient > 0) { DisconnectClient(currentClient); } } else { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnClientConnect failed: {1}", currentClient, Traces.ExceptionToString(se)); } } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.OnClientConnect failed: {1}", currentClient, Traces.ExceptionToString(ex)); } finally { try { // register for another connection _primarySocket.BeginAccept(new AsyncCallback(OnClientConnect), null); } catch (ObjectDisposedException) { // This occurs when the main socket will be closed } } }
/// <summary> /// Persists an article to the backing store /// </summary> /// <param name="clientUsername">Authenticated username</param> /// <param name="data">Article data posted from NNTP client</param> /// <returns>String which will be returned to the client</returns> public string PostArticle(string clientUsername, string data) { try { if (data.Length > MaxPostLengthBytes) { //System.Diagnostics.Trace.WriteLine("Posting failed - maximum post length exceeded"); //return PostStatus.FailedExcessiveLength; return(GeneralResponses.PostingFailedExcessiveLength); } var articleBody = new StringBuilder(); var article = new Article { ContentType = string.Empty }; char[] delimiter = { '\r' }; bool isHeader = true; bool isMimePost = false; // undo dot stuffing //data = data.Replace("\r\n..\r\n", "\r\n.\r\n"); //data = data.Replace("\r\n..", "\r\n."); string[] lines = data.Split(delimiter); //Regex extendedChars = new Regex("[^\\u0009-\\u007e]", RegexOptions.Multiline | RegexOptions.IgnoreCase); string lastKeyValue = string.Empty; string line; foreach (var lineIter in lines) { line = lineIter.Replace("\n", string.Empty); //line = extendedChars.Replace(line, string.Empty); //System.Diagnostics.Trace.WriteLine("Processing line -|" + line + "|"); if (isHeader && string.IsNullOrEmpty(line)) { isHeader = false; continue; } if (isHeader) { var nameValuePair = new ArrayList(2); int sepPos = line.IndexOf(": "); if ((sepPos > 0) && (line.IndexOf(" ") > 0)) { nameValuePair.Add(line.Substring(0, sepPos).Trim()); nameValuePair.Add(line.Substring(sepPos + 2).Trim()); lastKeyValue = nameValuePair[0].ToString(); } else { nameValuePair.Add(string.Empty); nameValuePair.Add(line); } string keyValue = nameValuePair[0].ToString(); if (string.Compare(keyValue, HeaderNames.From, StringComparison.InvariantCultureIgnoreCase) == 0) { article.From = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Date, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Date = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Subject, StringComparison.InvariantCultureIgnoreCase) == 0) { // Also support newreaders with post the "subject" in multiple lines article.Subject = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.Newsgroups, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Newsgroups = nameValuePair[1].ToString().Replace("\"", string.Empty).Replace("'", string.Empty); } else if (string.Compare(keyValue, HeaderNames.UserAgent, StringComparison.InvariantCultureIgnoreCase) == 0) { article.UserAgent = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.XNewsreader, StringComparison.InvariantCultureIgnoreCase) == 0) { if (string.IsNullOrEmpty(article.UserAgent)) { article.UserAgent = nameValuePair[1].ToString(); // The "User-Agent" is embedded into the html, so either use "User-Agent" or "X-Newsreader" } article.XNewsreader = nameValuePair[1].ToString(); } // TODO: Also support of "X-Mailer:" !? else if (string.Compare(keyValue, HeaderNames.References, StringComparison.InvariantCultureIgnoreCase) == 0) { article.References = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.ContentType, StringComparison.InvariantCultureIgnoreCase) == 0) { // Also support newreaders with post the "content-type" in multiple lines, like: // Content-Type: text/plain; // format=flowed; // charset="iso-8859-1"; // reply-type=original article.ContentType = nameValuePair[1].ToString(); } else if (string.Compare(keyValue, HeaderNames.ContentTransferEncoding, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentTransferEncoding = nameValuePair[1].ToString(); } else if (keyValue.Length == 0) { // Multi-Line Header: if (string.Compare(lastKeyValue, HeaderNames.From, StringComparison.InvariantCultureIgnoreCase) == 0) { article.From += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Date, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Date += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Subject, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Subject += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.Newsgroups, StringComparison.InvariantCultureIgnoreCase) == 0) { article.Newsgroups += nameValuePair[1].ToString().Replace("\"", string.Empty).Replace("'", string.Empty); } else if (string.Compare(lastKeyValue, HeaderNames.UserAgent, StringComparison.InvariantCultureIgnoreCase) == 0) { article.UserAgent += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.XNewsreader, StringComparison.InvariantCultureIgnoreCase) == 0) { //if (string.IsNullOrEmpty(article.UserAgent)) // article.UserAgent = nameValuePair[1].ToString(); // The "User-Agent" is embedded into the html, so either use "User-Agent" or "X-Newsreader" article.XNewsreader += nameValuePair[1].ToString(); } // TODO: Also support of "X-Mailer:" !? else if (string.Compare(lastKeyValue, HeaderNames.References, StringComparison.InvariantCultureIgnoreCase) == 0) { article.References += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.ContentType, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentType += nameValuePair[1].ToString(); } else if (string.Compare(lastKeyValue, HeaderNames.ContentTransferEncoding, StringComparison.InvariantCultureIgnoreCase) == 0) { article.ContentTransferEncoding += nameValuePair[1].ToString(); } } else { } } // isHeader else { // Body: // undo dot stuff (remove the first dott, if there are two dots... if (line.IndexOf("..", StringComparison.InvariantCultureIgnoreCase) == 0) { line = line.Substring(1); } articleBody.Append(line + "\n"); } } // foreach article.Body = articleBody.ToString(); // Check for mimePostings: if (article.ContentType.IndexOf("multipart", StringComparison.InvariantCultureIgnoreCase) == 0) { isMimePost = true; } if (isMimePost) { var mime = new Mime { Text = article.Body }; // Exract boundary: var m2 = _bondaryRegex.Match(article.ContentType); if (m2.Success) { mime.Boundary = m2.Groups[1].Value; } string textPlain = null; string textPlainContentType = null; string textHtml = null; string textHtmlContentType = null; foreach (var mimePart in mime.MimeParts) { var ct = mimePart.GetPropertyValue("Content-Type"); if (ct.IndexOf("text/plain", StringComparison.InvariantCultureIgnoreCase) >= 0) { textPlainContentType = ct; textPlain = (string)mimePart.Decode(); } if (ct.IndexOf("text/html", StringComparison.InvariantCultureIgnoreCase) >= 0) { textHtmlContentType = ct; textHtml = (string)mimePart.Decode(); } if ((textPlain != null) && (textHtml != null)) { break; } } if ((textPlain == null) && (textHtml == null)) { //System.Diagnostics.Trace.WriteLine("Posting failed - text part not found"); //return PostStatus.FailedTextPartMissingInHtml; return(GeneralResponses.PostingFailedTextPartMissingInMime); } if (InMimeUseHtml && (textHtml != null)) { article.Body = textHtml; article.ContentType = textHtmlContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: HTML selected"); } else { if (textPlain != null) { article.Body = textPlain; article.ContentType = textPlainContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: plain/text selected"); } else { article.Body = textHtml; article.ContentType = textHtmlContentType; Traces.NntpServerTraceEvent(TraceEventType.Verbose, "MIME-Part: HTML selected (no plain/text available)"); } } } // Transcode the "body" according to the "charset", if one is specified: var charSetMatch = _charSetRegex.Match(article.ContentType); string charSet = Server.EncodingRecv.HeaderName; // default if (charSetMatch.Success) { charSet = charSetMatch.Groups[1].Value; } if (isMimePost == false) { if (article.ContentTransferEncoding.IndexOf("quoted-printable", StringComparison.InvariantCultureIgnoreCase) >= 0) { article.Body = MimePart.DecodeQuotedPrintable(article.Body); } else if (article.ContentTransferEncoding.IndexOf("base64", StringComparison.InvariantCultureIgnoreCase) >= 0) { article.Body = MimePart.DecodeBase64(article.Body, charSet); } } // Re-Encode after all data is now in the body... ReEncode(article, charSet); // post must have subject if (article.Subject.Trim().Length == 0) { //System.Diagnostics.Trace.WriteLine("Posting failed - no subject line"); //return PostStatus.FailedSubjectLineBlank; return(GeneralResponses.PostingFailedSubjectLineBlank); } // Decode Subject article.Subject = Mime.DecodeEncodedWordValue(article.Subject); var articles = new List <Article>(); // Disabled cross posting (2010-06-06) //// Cross-Postings (Multiple-Postings) are allowed for "primary messages": //if ( (string.IsNullOrEmpty(article.References)) && (article.Newsgroups.IndexOf(",", StringComparison.InvariantCultureIgnoreCase) > 0) ) //{ // string[] groupNames = article.Newsgroups.Split(','); // foreach (var groupName in groupNames) // { // Newsgroup group = GetNewsgroupFromCacheOrServer(groupName.Trim()); // if (group != null && group.PostingAllowed) // { // // copy the article... // var crossPostArticle = new Article(); // crossPostArticle.From = article.From; // crossPostArticle.Body = article.Body; // crossPostArticle.Date = article.Date; // crossPostArticle.Subject = article.Subject; // crossPostArticle.ParentNewsgroup = group.GroupName; // //crossPostArticle.References = article.References; // crossPostArticle.Newsgroups = article.Newsgroups; // crossPostArticle.UserAgent = article.UserAgent; // crossPostArticle.ContentType = article.ContentType; // crossPostArticle.ContentTransferEncoding = article.ContentTransferEncoding; // crossPostArticle.XNewsreader = article.XNewsreader; // // add cross-posted footnote // //crossPostArticle.Body += "\n\n[X-Posted To: " + article.Newsgroups + "]\n"; // articles.Add(crossPostArticle); // } // else // { // // indicate posting failure // //System.Diagnostics.Trace.WriteLine("Posting failed - user [" + clientUsername + "] does not have permission to post to group [" + groupName + "]"); // return PostStatus.FailedAccessDenied; // } // } // foreach // if (articles.Count <= 0) // { // return PostStatus.FailedGroupNotFound; // } // else // { // SaveArticles(clientUsername, articles); // } //} //else { // Only one group or a reply: if (article.Newsgroups.IndexOf(",", StringComparison.InvariantCultureIgnoreCase) > 0) { // Cross-Post are not allowed fro answers! return(GeneralResponses.PostingFailedAccessDeniedMultipost); } var group = GetNewsgroupFromCacheOrServer(article.Newsgroups); if (group != null && group.PostingAllowed) { article.ParentNewsgroup = group.GroupName; articles.Add(article); SaveArticles(clientUsername, articles); } else { // indicate posting failure if (group != null) { //System.Diagnostics.Trace.WriteLine("Posting failed - user [" + clientUsername + "] does not have permission to post to group [" + group.GroupName + "]"); //return PostStatus.FailedAccessDenied; return(GeneralResponses.PostingFailedAccessDenied); } //System.Diagnostics.Trace.WriteLine("Posting failed - newsgroup [" + article.Newsgroups + "] could not be found"); //return PostStatus.FailedGroupNotFound; return(GeneralResponses.PostingFailedGroupNotFound); } } return(GeneralResponses.ArticlePostedOk); } catch (Exception exp) { //System.Diagnostics.Trace.WriteLine("Error in DataProvider.PostArticle - " + ex.Message + " :: " + ex.StackTrace); Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "Error in DataProvider.PostArticle: {0}", Traces.ExceptionToString(exp)); var resp = string.Format("441 posting failed {0}\r\n", NntpServer.GetErrorResponseFromExeption(exp)); return(resp); } }
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); //} } } }
//public bool DetailedErrorResponse { get; set; } private NntpCommand ClassifyCommand(Client client) { var data = client.Buffer.ToString(); string commandText; var parameterText = string.Empty; NntpCommand nntpCommand; Command command; if (data.Trim().Length == 0) { Traces.NntpServerTraceEvent(TraceEventType.Warning, client, "ClassifyCommand: Command Empty!"); //System.Diagnostics.Trace.WriteLine("Command empty"); return(new NntpCommandNotRecognised()); } Traces.NntpServerTraceEvent(TraceEventType.Verbose, client, "ClassifyCommand: {0}", data); // seperate command name and parameters if (data.IndexOf(" ") > 0) { commandText = data.Substring(0, data.IndexOf(" ")); parameterText = data.Substring(data.IndexOf(" ") + 1); } else { commandText = data.Trim(); } //System.Diagnostics.Trace.WriteLine("Command text is " + commandText); //System.Diagnostics.Trace.WriteLine("Parameter text is " + parameterText); try { command = (Command)Enum.Parse(typeof(Command), commandText, true); } catch { Traces.NntpServerTraceEvent(TraceEventType.Warning, client, "ClassifyCommand: Command unknown: Command: {0}, Parameter: {1}", commandText, parameterText); return(new NntpCommandNotRecognised()); } switch (command) { case Command.ARTICLE: nntpCommand = new NntpCommandArticle(client.GroupName) { ToClientEncoding = this.EncodingSend }; break; case Command.LIST: nntpCommand = new NntpCommandList(); break; case Command.GROUP: nntpCommand = new NntpCommandGroup(); //client.GroupName = parameterText; break; case Command.BODY: nntpCommand = new NntpCommandBody(client.GroupName); break; case Command.HEAD: nntpCommand = new NntpCommandHead(client.GroupName) { ToClientEncoding = this.EncodingSend }; break; case Command.HELP: nntpCommand = new NntpCommandHelp(); break; case Command.IHAVE: nntpCommand = new NntpCommandIHave(); break; case Command.LAST: nntpCommand = new NntpCommandLast(client.GroupName); break; case Command.NEWGROUPS: nntpCommand = new NntpCommandNewGroups(); break; case Command.NEWNEWS: nntpCommand = new NntpCommandNewNews(); break; case Command.NEXT: nntpCommand = new NntpCommandNext(client.GroupName); break; case Command.POST: nntpCommand = new NntpCommandPost(_postingAllowed); break; case Command.QUIT: nntpCommand = new NntpCommandQuit(); break; case Command.SLAVE: nntpCommand = new NntpCommandSlave(); break; case Command.DATE: nntpCommand = new NntpCommandDate(); break; case Command.MODE: nntpCommand = new NntpCommandMode { PostingAllowed = _postingAllowed }; break; case Command.STAT: nntpCommand = new NntpCommandStat(client.GroupName); break; case Command.XHDR: nntpCommand = new NntpCommandXHdr(client.GroupName); break; case Command.XOVER: nntpCommand = new NntpCommandXOver(client.GroupName); break; case Command.LISTGROUP: if (ListGroupDisabled) { return(new NntpCommandNotRecognised()); } nntpCommand = new NntpCommandListGroup(client.GroupName); break; case Command.AUTHINFO: nntpCommand = new NntpCommandAuthInfo(); break; default: nntpCommand = new NntpCommandNotRecognised(); break; } nntpCommand.Provider = _dataProvider; client.CommandParameters = parameterText; client.PreviousCommand = command; return(nntpCommand); }
protected void SendData(string data, int clientNumber) { byte[] buffer; try { buffer = _encodingSend.GetBytes(data); } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.SendData failed to encode data: {1}\r\n{2}", clientNumber, data, Traces.ExceptionToString(ex)); return; } Socket workerSocket; try { lock (_workerSockets) { workerSocket = _workerSockets[clientNumber]; } } catch { // continue //System.Diagnostics.Trace.WriteLine("Server::SendData - unable to access entry [" + clientNumber + "]"); return; } if (workerSocket == null) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Warning, "[Client {0}] Server.SendData failed (client removed from the worker list)", clientNumber); return; } try { if (_workerSendCallBack == null) { _workerSendCallBack = new AsyncCallback(OnDataSend); } var socketPacket = new SocketPacket(workerSocket, clientNumber); workerSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, _workerSendCallBack, socketPacket); } catch (ObjectDisposedException) { //System.Diagnostics.Trace.WriteLine("#" + clientNumber.ToString() + " - OnDataSend: Socket has been closed"); return; } catch (SocketException se) { if (se.SocketErrorCode == SocketError.ConnectionReset) // connection reset by peer { // remove worker socket of closed client DisconnectClient(clientNumber); } else if (se.SocketErrorCode == SocketError.ConnectionAborted) // connection aborted { // remove worker socket of closed client DisconnectClient(clientNumber); } else { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.SendData failed: {1}", clientNumber, Traces.ExceptionToString(se)); } return; } catch (Exception ex) { Traces.NntpServerTraceEvent(System.Diagnostics.TraceEventType.Critical, "[Client {0}] Server.SendData failed: {1}", clientNumber, Traces.ExceptionToString(ex)); } }