/// <summary> /// Reads the Flash policy request and returns the policy response if Flash requests are allowed. /// </summary> /// <param name="sender">The <see cref="AsyncSocket"/></param> /// <param name="readBytes">The bytes read from the socket</param> /// <param name="tag">A tag identifying the read request</param> void ReadPolicyRequest(AsyncSocket sender, byte[] readBytes, long tag) { // remove this now that we are done with it this.socket.DidRead -= new AsyncSocket.SocketDidRead(ReadPolicyRequest); if (tag == FLASH_POLICY_REQUEST_TAG) { Data data = new Data(readBytes); this.AlreadyReceivedData.Append(data.ToString()); string request = this.AlreadyReceivedData.ToString(); if (request == FlashPolicy.REQUEST) { if (this.allowFlash) { this.socket.Write(FlashPolicy.ResponseBytes, TIMEOUT_FLASHPOLICYRESPONSE, FLASH_POLICY_RESPONSE_TAG); this.socket.CloseAfterWriting(); } else { OnError(ErrorCode.NOT_AUTHORIZED, ErrorDescription.BROWSER_CONNECTIONS_NOT_ALLOWED); } } else { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_REQUEST); } } }
/// <summary> /// Reads the socket data and handles the request /// </summary> /// <param name="alreadyReadBytes">Any bytes that were already read from the socket</param> public void Read(byte[] alreadyReadBytes) { Data data = new Data(alreadyReadBytes); string s = data.ToString(); this.AlreadyReceivedData.Append(s); this.socket.DidRead += new AsyncSocket.SocketDidRead(ReadPolicyRequest); this.socket.Read(FlashPolicy.REQUEST.Length - s.Length, TIMEOUT_FLASHPOLICYREQUEST, FLASH_POLICY_REQUEST_TAG); }
/// <summary> /// This method automatically increases the length of the data by the proper length, /// and copies the bytes from the given data object into the mutable data array. /// </summary> /// <param name="data"> /// A Data object to copy bytes from. /// </param> public void AppendData(Data data) { // We're not going to bother checking to see if data is null. // The NullReferenceException will automatically get thrown for us if it is. AppendData(data.ByteArray); }
public MutableData(Data data, int offset, int length) : base(data, offset, length) { // Nothing to do here }
public MutableData(Data data, bool copy) : base(data, copy) { // Nothing to do here }
public MutableData(Data data) : base(data, false) { // Nothing to do here }
public static bool IsEqual(Data d1, int d1Offset, Data d2, int d2Offset, int length) { return IsEqual(d1.ByteArray, d1Offset, d2.ByteArray, d2Offset, length); }
/// <summary> /// This method automatically increases the length of the data by the proper length, /// and copies the data from the given byte array into the underlying buffer. /// The data is copied starting at the given offset up to the given length. /// The data is inserted into the underlying buffer at the given index. /// </summary> /// <param name="index"> /// The position in this instance where insertion begins. /// </param> /// <param name="data"> /// The data to insert into the underlying buffer. /// </param> /// <param name="offset"> /// The offset from which to start copying data from the given data. /// </param> /// <param name="length"> /// The amount of data to copy from the given data. /// </param> public void InsertData(int index, Data data, int offset, int length) { InsertData(index, data.ByteArray, offset, length); }
/// <summary> /// Creates a new Data object using the given data. /// If the copy flag is set, this method will create a new buffer, and copy the buffer from the given data into it. /// Thus changes to the given data will not affect this Data object. /// Otherwise the new Data object will simply form a wrapper around the given data (without copying anything). /// /// Note: If you pass a Data object which uses an internal stream (IsStream = true), the data is always copied. /// </summary> /// <param name="buffer"> /// Byte array to use for underlying data. /// </param> /// <param name="copy"> /// Whether or not to copy data from the given buffer into a new buffer. /// </param> public Data(Data data, bool copy) : this(data.ByteArray, copy) { // Nothing to do here }
/// <summary> /// Creates a new Data object using the data. /// The data is not copied. /// That is, the new Data object is simply a wrapper around that same data. /// </summary> /// <param name="buffer"> /// Data to use as underlying data. /// </param> public Data(Data data) : this(data.ByteArray, false) { // Nothing to do here }
/// <summary> /// Reads the entire file, and stores the result in a Data object wrapping the read bytes. /// Warning: This method is only to be used for small files. /// </summary> /// <param name="filePath"> /// Relative or absolute path to file. /// </param> /// <returns> /// A regular Data object, which wraps the read bytes from the file. /// </returns> public static Data ReadFile(string filePath) { Data result = null; FileStream fs = null; try { fs = new FileStream(filePath, FileMode.Open, FileAccess.Read); result = new Data((int)fs.Length); int amountRead = fs.Read(result.ByteArray, 0, result.Length); int totalAmountRead = amountRead; while ((amountRead > 0) && (totalAmountRead < result.Length)) { amountRead = fs.Read(result.ByteArray, totalAmountRead, result.Length - totalAmountRead); totalAmountRead += amountRead; } if (totalAmountRead < result.Length) { result = null; } } catch { result = null; } finally { if (fs != null) fs.Close(); } return result; }
/// <summary> /// Logs the specified data. /// </summary> /// <param name="data">The <see cref="Data"/> to log</param> public void Log(Data data) { try { if (this.loggingEnabled && !String.IsNullOrEmpty(this.logFolder)) { string filename = String.Format(@"GNTP_{0}.txt", this.requestInfo.RequestID); string filepath = PathUtility.Combine(this.logFolder, filename); bool exists = System.IO.File.Exists(filepath); if (!exists) { // log the initial request/response System.IO.StreamWriter w = System.IO.File.CreateText(filepath); using (w) { w.Write("Timestamp: {0}\r\n", DateTime.Now.ToString()); foreach (string item in this.requestInfo.HandlingInfo) { w.Write(String.Format("{0}\r\n", item)); } w.Write(SEPERATOR); if (this.requestReader != null) { w.Write(this.requestReader.ReceivedData); if (!String.IsNullOrEmpty(this.requestReader.DecryptedData)) { w.Write(SEPERATOR); w.Write(this.requestReader.DecryptedData); } } w.Write(SEPERATOR); w.Write(data.ToString()); w.Close(); this.requestInfo.HandlingInfo.Clear(); } } else { // this is for logging callback data System.IO.StreamWriter w = new System.IO.StreamWriter(filepath, true); using (w) { w.Write(SEPERATOR); w.Write("Timestamp: {0}\r\n", DateTime.Now.ToString()); foreach (string item in this.requestInfo.HandlingInfo) { w.Write(String.Format("{0}\r\n", item)); } w.Write(SEPERATOR); w.Write(data.ToString()); w.Close(); } } } } catch { // suppress logging exceptions } }
/// <summary> /// Performs the actual writing of data to the socket. Used by all other Write* methods. /// </summary> /// <param name="socket">The <see cref="AsyncSocket"/> to write the data to</param> /// <param name="bytes">The bytes to write to the socket</param> /// <param name="timeout">The socket write timeout value</param> /// <param name="tag">The tag that will identify the write operation (can be referenced in the socket's DidWrite event)</param> /// <param name="disconnectAfterWriting">Indicates if the server should disconnect the socket after writing the data</param> /// <param name="requestComplete">Indicates if the request is complete once the data is written</param> protected void FinalWrite(AsyncSocket socket, byte[] bytes, int timeout, long tag, bool disconnectAfterWriting, bool requestComplete) { Data data = new Data(bytes); Log(data); // give any custom readers the change to modify the output before we send it (especially useful for WebSockets that need to frame their data) if (this.requestReader != null) this.requestReader.BeforeResponse(ref bytes); // if we are done sending stuff back (all responses and callbacks), we need to initiate an orderly shutdown if (!disconnectAfterWriting && requestComplete) OrderlySocketShutdown(); // send the data socket.Write(bytes, timeout, tag); // if we are the ones disconnecting, do it now. (usually if we are sending back an error response) // if not, we can just leave the socket alone if (disconnectAfterWriting) { OnSocketUsageComplete(socket); socket.CloseAfterWriting(); } }
/// <summary> /// Handles the socket's DidRead event after reading only the first four bytes of data. /// </summary> /// <param name="socket">The <see cref="AsyncSocket"/></param> /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param> /// <param name="tag">The tag identifying the read operation</param> private void SocketDidReadIndicatorBytes(AsyncSocket socket, byte[] readBytes, long tag) { // remove this event handler since we dont need it any more socket.DidRead -= new AsyncSocket.SocketDidRead(this.SocketDidReadIndicatorBytes); Data data = new Data(readBytes); string s = data.ToString(); if (tag == ACCEPT_TAG) { if (s == FlashPolicy.REQUEST_INDICATOR) { GNTPFlashSocketReader gfsr = new GNTPFlashSocketReader(socket, allowBrowserConnections); gfsr.Read(readBytes); } else if (s == WebSocketHandshakeHandler.REQUEST_INDICATOR) { // this is a GNTP over WebSocket request, so we have to do the WebSocket handshake first socket.Tag = readBytes; WebSocketHandshakeHandler wshh = new WebSocketHandshakeHandler(socket, "*", "ws://localhost:23053"); wshh.DoHandshake(delegate() { // now pass off to the GNTPWebSocketReader (which is just a normal GNTPSocketReader that can deal with the WebSocket framing of packets) GNTPWebSocketReader gwsr = new GNTPWebSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo); this.requestReader = gwsr; gwsr.MessageParsed += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed); gwsr.Error += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error); gwsr.Read(readBytes); }); } else { // this is a normal GNTP/TCP connection, so handle it as such GNTPSocketReader gsr = new GNTPSocketReader(socket, passwordManager, passwordRequired, allowNetworkNotifications, allowBrowserConnections, allowSubscriptions, this.requestInfo); this.requestReader = gsr; gsr.MessageParsed += new GNTPRequestReader.GNTPRequestReaderMessageParsedEventHandler(requestReader_MessageParsed); gsr.Error += new GNTPRequestReader.GNTPRequestReaderErrorEventHandler(requestReader_Error); gsr.Read(readBytes); } } else { WriteError(socket, ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST); } }
/// <summary> /// Handles the socket's DidRead event. /// </summary> /// <param name="socket">The <see cref="AsyncSocket"/></param> /// <param name="readBytes">Array of <see cref="byte"/>s that were read</param> /// <param name="tag">The tag identifying the read operation</param> protected virtual void SocketDidRead(AsyncSocket socket, byte[] readBytes, long tag) { try { Data data = new Data(readBytes); this.AlreadyReceivedData.Append(data.ToString()); GNTPParser parser = (GNTPParser)socket.Tag; NextIndicator next = parser.Parse(readBytes); if (next.ShouldContinue) { if (next.UseBytes) socket.Read(next.Bytes, TIMEOUT_GNTP_HEADER, parser.Tag); else socket.Read(next.Length, TIMEOUT_GNTP_BINARY, parser.Tag); } } catch (GrowlException gEx) { OnError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo); } catch (Exception ex) { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message); } }
/// <summary> /// Reads from the given data, starting at the given offset and reading the given length, /// and appends the read data to the underlying buffer. /// The underlying buffer length is automatically increased as needed. /// /// This method properly handles reading from stream data (data.IsStream == true). /// </summary> /// <param name="data"> /// The data to append to the end of the underlying buffer. /// </param> /// <param name="offset"> /// The offset from which to start copying from the given data. /// </param> /// <param name="length"> /// The amount to copy from the given data. /// </param> public void AppendData(Data data, int offset, int length) { AppendData(data.ByteArray, offset, length); }
/// <summary> /// This method automatically increases the length of the data by the proper length, /// and copies the data from the given byte array into the underlying buffer. /// The data is copied starting at the given offset up to the given length. /// The data is inserted into the underlying buffer at the given index. /// </summary> /// <param name="index"> /// The position in this instance where insertion begins. /// </param> /// <param name="data"> /// The data to insert into the underlying buffer. /// </param> public void InsertData(int index, Data data) { InsertData(index, data.ByteArray); }
/// <summary> /// Creates a new Data object using a specified subset of the given data. /// The data must necessarily be copied (otherwise it would be unsafe). /// </summary> /// <param name="buffer"> /// Byte array to use for underlying data. /// </param> /// <param name="offset"> /// The offset within data to start reading from. /// </param> /// <param name="data"> /// The amount to read from data. /// </param> public Data(Data data, int offset, int length) : this(data.ByteArray, offset, length) { // Nothing to do here }
private void ProcessBuffer() { try { Data data = new Data(buffer.ToArray()); string s = data.ToString(); alreadyReceived.Append(s); if (tag == START_TAG) { // start looking for GNTP header tag = GNTP_IDENTIFIER_TAG; ContinueProcessing(); } else if (tag == GNTP_IDENTIFIER_TAG) { string line = s; Match match = ParseGNTPHeaderLine(line, this.passwordRequired); if (match.Success) { this.version = match.Groups["Version"].Value; if (version == MessageParser.GNTP_SUPPORTED_VERSION) { string d = match.Groups["Directive"].Value; if (Enum.IsDefined(typeof(RequestType), d)) { this.directive = (RequestType)Enum.Parse(typeof(RequestType), d); // check for supported but not allowed requests if (this.directive == RequestType.SUBSCRIBE && !this.allowSubscriptions) { OnError(ErrorCode.NOT_AUTHORIZED, ErrorDescription.SUBSCRIPTIONS_NOT_ALLOWED); } else { this.encryptionAlgorithm = Cryptography.GetEncryptionType(match.Groups["EncryptionAlgorithm"].Value); this.ivHex = (match.Groups["IV"] != null ? match.Groups["IV"].Value : null); if (!String.IsNullOrEmpty(this.ivHex)) this.iv = Cryptography.HexUnencode(this.ivHex); string keyHash = match.Groups["KeyHash"].Value.ToUpper(); bool authorized = false; // Any of the following criteria require a password: // 1. the request did not originate on the local machine or LAN // 2. the request came from the LAN, but LAN passwords are required // 2. it is a SUBSCRIBE request (all subscriptions require a password) // 3. the user's preferences require even local requests to supply a password // Additionally, even if a password is not required, it will be validated if the // sending appplication includes one string errorDescription = ErrorDescription.INVALID_KEY; if (this.passwordRequired || this.directive == RequestType.SUBSCRIBE || !String.IsNullOrEmpty(keyHash)) { if (String.IsNullOrEmpty(keyHash)) { errorDescription = ErrorDescription.MISSING_KEY; } else { string keyHashAlgorithmType = match.Groups["KeyHashAlgorithm"].Value; this.keyHashAlgorithm = Cryptography.GetKeyHashType(keyHashAlgorithmType); string salt = match.Groups["Salt"].Value.ToUpper(); authorized = this.passwordManager.IsValid(keyHash, salt, this.keyHashAlgorithm, this.encryptionAlgorithm, out this.key); } } else { authorized = true; this.key = Key.None; } if (authorized) { if (this.encryptionAlgorithm == Cryptography.SymmetricAlgorithmType.PlainText) { tag = HEADER_TAG; ContinueProcessing(); } else { tag = ENCRYPTED_HEADERS_TAG; ContinueProcessing(); } } else { OnError(ErrorCode.NOT_AUTHORIZED, errorDescription); } } } else { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNSUPPORTED_DIRECTIVE, d); } } else { OnError(ErrorCode.UNKNOWN_PROTOCOL_VERSION, ErrorDescription.UNSUPPORTED_VERSION, version); } } else { OnError(ErrorCode.UNKNOWN_PROTOCOL, ErrorDescription.MALFORMED_REQUEST); } } else if (tag == HEADER_TAG) { if (s == MessageParser.BLANK_LINE) { // if this is a REGISTER message, check Notifications-Count value // to see how many notification sections to expect if (this.directive == RequestType.REGISTER) { if (this.expectedNotifications > 0) { tag = NOTIFICATION_TYPE_TAG; ContinueProcessing(); } else { // a REGISTER request with no notifications is not valid OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.NO_NOTIFICATIONS_REGISTERED); } } else { // otherwise, check the number of resource pointers we got and start reading those this.pointersExpected = GetNumberOfPointers(); if (this.pointersExpected > 0) { this.pointersExpectedRemaining = this.pointersExpected; this.currentPointer = 1; tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } else { OnMessageParsed(); } } } else { Header header = Header.ParseHeader(s); if (header != null) { bool addHeader = true; if (header.Name == Header.APPLICATION_NAME) { this.applicationName = header.Value; } if (header.Name == Header.NOTIFICATIONS_COUNT) { this.expectedNotifications = Convert.ToInt32(header.Value); this.expectedNotificationsRemaining = this.expectedNotifications; this.currentNotification = 1; } if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT) { this.callbackData = header.Value; } if (header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TYPE) { this.callbackDataType = header.Value; } if (header.Name == Header.NOTIFICATION_CALLBACK_TARGET || header.Name == Header.NOTIFICATION_CALLBACK_CONTEXT_TARGET) // left in for compatibility { this.callbackUrl = header.Value; } if (header.Name == Header.RECEIVED) { this.requestInfo.PreviousReceivedHeaders.Add(header); addHeader = false; } if (addHeader) this.headers.AddHeader(header); } tag = HEADER_TAG; ContinueProcessing(); } } else if (tag == NOTIFICATION_TYPE_TAG) { if (s == MessageParser.BLANK_LINE) { this.expectedNotificationsRemaining--; if (this.expectedNotificationsRemaining > 0) { this.currentNotification++; tag = NOTIFICATION_TYPE_TAG; ContinueProcessing(); } else { // otherwise, check the number of resource pointers we got and start reading those this.pointersExpected = GetNumberOfPointers(); if (this.pointersExpected > 0) { this.pointersExpectedRemaining = this.pointersExpected; this.currentPointer = 1; tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } else { OnMessageParsed(); } } } else { if (this.notificationsToBeRegistered.Count < this.currentNotification) { this.notificationsToBeRegistered.Add(new HeaderCollection()); } Header header = Header.ParseHeader(s); this.notificationsToBeRegistered[this.currentNotification - 1].AddHeader(header); tag = NOTIFICATION_TYPE_TAG; ContinueProcessing(); } } else if (tag == RESOURCE_HEADER_TAG) { if (s == MessageParser.BLANK_LINE) { // we should have found an Identifier header and Length header, or we are just starting a new section Pointer p = this.pointers[this.currentPointer - 1]; if (p.Identifier != null && p.Length > 0) { // read #of bytes int length = this.pointers[this.currentPointer - 1].Length; tag = RESOURCE_TAG; ContinueProcessing(); } else { tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } } else { Header header = Header.ParseHeader(s); // should be Identifer or Length if (header != null) { bool validHeader = false; if (header.Name == Header.RESOURCE_IDENTIFIER) { this.pointers[this.currentPointer - 1].Identifier = header.Value; validHeader = true; } else if (header.Name == Header.RESOURCE_LENGTH) { this.pointers[this.currentPointer - 1].Length = Convert.ToInt32(header.Value); validHeader = true; } else { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER, header.Name); } if (validHeader) { tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } } else { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.UNRECOGNIZED_RESOURCE_HEADER); } } } else if (tag == RESOURCE_TAG) { // deal with data bytes byte[] bytes = this.key.Decrypt(data.ByteArray, this.iv); Pointer pointer = this.pointers[this.currentPointer - 1]; pointer.ByteArray = bytes; BinaryData binaryData = new BinaryData(pointer.Identifier, pointer.ByteArray); ResourceCache.Add(this.applicationName, binaryData); this.pointersExpectedRemaining--; if (this.pointersExpectedRemaining > 0) { this.currentPointer++; tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } else { OnMessageParsed(); } } else if (tag == ENCRYPTED_HEADERS_TAG) { // see if a length was specified (the original spec did not require the main encrypted headers portion to specify a length) if (s.StartsWith(Header.RESOURCE_LENGTH)) { Header header = Header.ParseHeader(s); if (header != null) { int len = Convert.ToInt32(header.Value); tag = ENCRYPTED_HEADERS_TAG; ContinueProcessing(); } } ParseEncryptedMessage(data.ByteArray); if (this.pointersExpected > 0) { tag = RESOURCE_HEADER_TAG; ContinueProcessing(); } else { OnMessageParsed(); } } else { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST); } } catch (GrowlException gEx) { OnError(gEx.ErrorCode, gEx.Message, gEx.AdditionalInfo); } catch (Exception ex) { OnError(ErrorCode.INVALID_REQUEST, ErrorDescription.MALFORMED_REQUEST, ex.Message); } }
public static bool IsEqual(Data d1, Data d2) { return IsEqual(d1.ByteArray, d2.ByteArray); }