/// <summary> /// Synchronously reads a packet from a Rendezvous proxy connection /// </summary> private RendezvousProxyPacket ReadProxyPacket() { int bytesreceived = 0; byte[] header = new byte[12]; while (bytesreceived < header.Length) { bytesreceived += socket.Read(header, bytesreceived, header.Length - bytesreceived); } Logging.DumpFLAP(header, "Rendezvous proxy read packet header"); RendezvousProxyPacket retval = new RendezvousProxyPacket(); using (ByteStream bstream = new ByteStream(header)) { retval.Data = new byte[bstream.ReadUshort() - 10]; bstream.AdvanceToPosition(4); retval.Command = (RendezvousProxyCommand)bstream.ReadUshort(); } bytesreceived = 0; while (bytesreceived < retval.Data.Length) { bytesreceived += socket.Read(retval.Data, bytesreceived, retval.Data.Length - bytesreceived); } Logging.DumpFLAP(retval.Data, "Rendezvous proxy read packet data"); return(retval); }
public void TestByteStreamReading() { // Test bytes using (ByteStream stream = new ByteStream(testData)) { for (int i = 0; i < testData.Length; i++) { Assert.AreEqual(testData[i], stream.ReadByte(), "ReadByte returned incorrect value"); } } // Test ushorts using (ByteStream stream = new ByteStream(testData)) { Assert.AreEqual(0x0841, stream.ReadUshort()); Assert.AreEqual(0x6472, stream.ReadUshort()); Assert.AreEqual(0x6965, stream.ReadUshort()); Assert.AreEqual(0x6e6e, stream.ReadUshort()); } // Test uints using (ByteStream stream = new ByteStream(testData)) { Assert.AreEqual(0x08416472, stream.ReadUint()); Assert.AreEqual(0x69656e6e, stream.ReadUint()); } // Test string using (ByteStream stream = new ByteStream(testData)) { Assert.AreEqual("Adrienne", stream.ReadString(stream.ReadByte(), Encoding.ASCII)); } }
/// <summary> /// Completes the initial connection to the AOL proxy server /// </summary> /// <remarks>This method is used to complete the proxy server transaction for both /// Stage 1 sending and Stage 2 receiver-redirect proxy scenarios</remarks> private void InitAolProxyConnectFinished() { try { ProxyInitializeSend(); RendezvousProxyPacket rpp = ReadProxyPacket(); if (rpp.Command == RendezvousProxyCommand.Acknowledge) { using (ByteStream bstream = new ByteStream(rpp.Data)) { Port = bstream.ReadUshort(); aolProxyIP = (new IPAddress(bstream.ReadByteArray(4))).ToString(); } // Send the "send file" request on SNAC(04,06):02 parent.Messages.RequestDirectConnectionInvite(this); // Wait for the proxy to send its 12 byte READY sequence lock (socket) { socket.BeginRead(new byte[12], 0, 12, new AsyncCallback(ProxyReceivedReady), null); //socket.BeginReceive(new byte[12], 0, 12, SocketFlags.None, new AsyncCallback(ProxyReceivedReady), null); } } else { ushort error = (ushort)((rpp.Data[0] << 8) | rpp.Data[1]); if (error == 0x0004) { throw new Exception("Recipient not logged in"); } else if (error == 0x000D) { throw new Exception("Client sent bad request"); } throw new Exception("AOL proxy sent unknown error"); } } catch (Exception ex) { if (DirectConnectionFailed != null) { DirectConnectionFailed(ex.Message); } } }
/// <summary> /// Processes the parameter information sent by the server -- SNAC(13,03) /// </summary> /// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,03)</param> public static void ProcessParametersList(DataPacket dp) { List <ushort> maximums = new List <ushort>(); using (TlvBlock tlvs = new TlvBlock(dp.Data.ReadByteArrayToEnd())) { using (ByteStream stream = new ByteStream(tlvs.ReadByteArray(0x0004))) { while (stream.HasMoreData) { maximums.Add(stream.ReadUshort()); } } } // Do something with these capabilities dp.ParentSession.Limits.MaxBuddies = maximums[0]; dp.ParentSession.Limits.MaxGroups = maximums[1]; dp.ParentSession.Limits.MaxPermits = maximums[2]; dp.ParentSession.Limits.MaxDenys = maximums[3]; dp.ParentSession.ParameterSetArrived(); }
/// <summary> /// Parses a byte buffer into an <see cref="BartID"/> object /// </summary> private void ReadIconInfo(byte[] buffer, UserInfo userinfo) { using (ByteStream iconStream = new ByteStream(buffer)) { int iconStreamSize = iconStream.GetByteCount(); while (iconStream.CurrentPosition + 4 <= iconStreamSize) { BartID item = new BartID(iconStream); // Find the end of the current data item in the stream int endDataPosition = iconStream.CurrentPosition + item.Data.Length; switch (item.Type) { case BartTypeId.BuddyIcon: if (!GraphicsManager.IsBlankIcon(item.Data)) { userinfo.Icon = item; } break; case BartTypeId.StatusString: // Available message using (ByteStream messageStream = new ByteStream(item.Data)) { Encoding encoding = Encoding.UTF8; byte[] amessage = new byte[0]; if (messageStream.HasMoreData) { // Pull the message to a byte array, assume at first that the encoding // is UTF-8. If existing encoding information exists, use that instead amessage = messageStream.ReadByteArray(messageStream.ReadByte()); // Check if there's encoding information available if (messageStream.HasMoreData) { // Check to see if the encoding's been specified if (messageStream.ReadUshort() == 0x0001) { messageStream.AdvanceOffset(2); string encodingStr = messageStream.ReadString(messageStream.ReadUshort(), Encoding.ASCII); // Try to use the encoding from the byte stream try { encoding = Encoding.GetEncoding(encodingStr); } catch (ArgumentException) { Logging.WriteString( "ReadIconInfo: Got unknown encoding for available message (" + encodingStr + "), falling back to UTF-8"); encoding = Encoding.UTF8; } } } } userinfo.AvailableMessage = Encoding.Unicode.GetString( Encoding.Convert(encoding, Encoding.Unicode, amessage)); } break; default: break; } } } }
/// <summary> /// Synchronously reads a packet from a Rendezvous proxy connection /// </summary> private RendezvousProxyPacket ReadProxyPacket() { int bytesreceived = 0; byte[] header = new byte[12]; while (bytesreceived < header.Length) { bytesreceived += socket.Read(header, bytesreceived, header.Length - bytesreceived); } Logging.DumpFLAP(header, "Rendezvous proxy read packet header"); RendezvousProxyPacket retval = new RendezvousProxyPacket(); using (ByteStream bstream = new ByteStream(header)) { retval.Data = new byte[bstream.ReadUshort() - 10]; bstream.AdvanceToPosition(4); retval.Command = (RendezvousProxyCommand)bstream.ReadUshort(); } bytesreceived = 0; while (bytesreceived < retval.Data.Length) { bytesreceived += socket.Read(retval.Data, bytesreceived, retval.Data.Length - bytesreceived); } Logging.DumpFLAP(retval.Data, "Rendezvous proxy read packet data"); return retval; }
/// <summary> /// Completes the initial connection to the AOL proxy server /// </summary> /// <remarks>This method is used to complete the proxy server transaction for both /// Stage 1 sending and Stage 2 receiver-redirect proxy scenarios</remarks> private void InitAolProxyConnectFinished() { try { ProxyInitializeSend(); RendezvousProxyPacket rpp = ReadProxyPacket(); if (rpp.Command == RendezvousProxyCommand.Acknowledge) { using (ByteStream bstream = new ByteStream(rpp.Data)) { Port = bstream.ReadUshort(); aolProxyIP = (new IPAddress(bstream.ReadByteArray(4))).ToString(); } // Send the "send file" request on SNAC(04,06):02 parent.Messages.RequestDirectConnectionInvite(this); // Wait for the proxy to send its 12 byte READY sequence lock (socket) { socket.BeginRead(new byte[12], 0, 12, new AsyncCallback(ProxyReceivedReady), null); //socket.BeginReceive(new byte[12], 0, 12, SocketFlags.None, new AsyncCallback(ProxyReceivedReady), null); } } else { ushort error = (ushort)((rpp.Data[0] << 8) | rpp.Data[1]); if (error == 0x0004) { throw new Exception("Recipient not logged in"); } else if (error == 0x000D) { throw new Exception("Client sent bad request"); } throw new Exception("AOL proxy sent unknown error"); } } catch (Exception ex) { if (DirectConnectionFailed != null) { DirectConnectionFailed(ex.Message); } } }
/// <summary> /// Verifies that a JPEG File Interchange Format image falls inside the /// AOL-required dimensions: between 48x48 and 50x50 /// </summary> /// <param name="fs"> /// An opened <see cref="FileStream"/> two bytes into the JFIF. /// </param> /// <returns> /// <c>true</c> if the JFIF fits in the required dimensions, /// <c>false</c> otherwise. /// </returns> private static bool VerifyJFIF(FileStream fs) { // Scroll to the Start Frame 0 position in the image bool foundframe0 = false; while (!foundframe0 && fs.Position < fs.Length) { if (fs.ReadByte() == 0xFF && fs.ReadByte() == 0xC0) break; } if (fs.Position == fs.Length) return false; // Three skip bytes, then the next four are the width and height (in NBO format) byte[] size = new byte[7]; if (fs.Read(size, 0, size.Length) != size.Length) return false; ushort height = 0, width = 0; using (ByteStream bstream = new ByteStream(size)) { bstream.AdvanceToPosition(3); height = bstream.ReadUshort(); width = bstream.ReadUshort(); } return (width >= 48 && width <= 50) && (height >= 48 && height <= 50); }
/// <summary> /// Initializes a new BartID from a byte stream /// </summary> internal BartID(ByteStream stream) { type = (BartTypeId)stream.ReadUshort(); flags = (BartFlags)stream.ReadByte(); byte dataLength = stream.ReadByte(); data = stream.ReadByteArray(dataLength); }
/// <summary> /// Processes the parameter information sent by the server -- SNAC(13,03) /// </summary> /// <param name="dp">A <see cref="DataPacket"/> object with a buffer containing SNAC(13,03)</param> public static void ProcessParametersList(DataPacket dp) { List<ushort> maximums = new List<ushort>(); using (TlvBlock tlvs = new TlvBlock(dp.Data.ReadByteArrayToEnd())) { using (ByteStream stream = new ByteStream(tlvs.ReadByteArray(0x0004))) { while (stream.HasMoreData) { maximums.Add(stream.ReadUshort()); } } } // Do something with these capabilities dp.ParentSession.Limits.MaxBuddies = maximums[0]; dp.ParentSession.Limits.MaxGroups = maximums[1]; dp.ParentSession.Limits.MaxPermits = maximums[2]; dp.ParentSession.Limits.MaxDenys = maximums[3]; dp.ParentSession.ParameterSetArrived(); }
/// <summary> /// Processes the received message /// </summary> private void EndReadHeader(IAsyncResult res) { byte[] odcheader = null; try { odcheader = (byte[])res.AsyncState; socket.EndRead(res); } catch (Exception ex) { Logging.WriteString(String.Format("Exception in DirectIMConnection.EndReadHeader: {0}", ex)); DisconnectFromServer(true); } // Verify that this is an ODC header if (Encoding.ASCII.GetString(odcheader, 0, 4) != "ODC2") { // Huh... return; } ushort datalen = (ushort)((odcheader[4] << 8) | odcheader[5]); if (datalen < 6) { // Oh return; } ByteStream messageheader = ReadPacket(datalen - 6); if (messageheader == null) { // Tum ta tiddily tumpa turr return; } // Extract various members from the message header messageheader.AdvanceToPosition(6); byte[] cookie = messageheader.ReadByteArray(8); messageheader.AdvanceToPosition(22); uint datalength = messageheader.ReadUint(); ushort charset = messageheader.ReadUshort(); ushort subcharset = messageheader.ReadUshort(); DirectIMFlags flags = (DirectIMFlags)messageheader.ReadUint(); messageheader.AdvanceToPosition(38); string screenname = messageheader.ReadString(16, Encoding.ASCII); if ((flags & DirectIMFlags.TypingPacket) != 0) { // Determine the type of typing packet this is if ((flags & DirectIMFlags.UserTyping) != 0) { //_parent.OnTypingNotification(screenname, TypingNotification.TypingStarted); } else if ((flags & DirectIMFlags.UserTyped) != 0) { //_parent.OnTypingNotification(screenname, TypingNotification.TextTyped); } else { // TODO: restore these // _parent.OnTypingNotification(screenname, TypingNotification.TypingFinished); } // Probably no data, but read it in anyway to make sure we're not missing anything ReadPacket((int)datalength); } else if ((flags & DirectIMFlags.ConfirmationPacket) != 0 && datalength == 0) { // Do we really do anything here? I don't think so. } else { // Create a new instant message DirectIM dim = new DirectIM(Other.ScreenName, this); dim.Cookie = csammisrun.OscarLib.Cookie.GetReceivedCookie(cookie); dim.IsAutoResponse = ((flags & DirectIMFlags.AutoResponse) != 0); dim.Encoding = IM.GetEncodingFromCharset(charset, subcharset); // Create a spooler to incrementally read in a DirectIM packet, // then restart the read sequence when it's done DirectIMDataReader reader = new DirectIMDataReader(this, dim); reader.DataReaderComplete += new DataReaderCompleteHandler(delegate { parent.OnDirectIMReceived( reader.Message); reader.Dispose(); ReadHeader(); }); reader.Read(datalength); return; } // Restart the read sequence ReadHeader(); }
/// <summary> /// Performs TLV 0x2711 processing for direct connect (sendfiles, DirectIM) negotiation /// </summary> private void ProcessDirectConnectionRequest(DirectConnection conn, ByteStream stream) { if (conn.Type == RendezvousType.Accept) { // They're accepting, which means we're going to get a connection on the // listener socket set up in FileTransferManager. Do nothing here } else if (conn.Type == RendezvousType.Cancel) { DirectConnection cancelled = parent.Connections.GetDirectConnectionByCookie(conn.Cookie); if (cancelled != null) { if (cancelled is FileTransferConnection) { (cancelled as FileTransferConnection).CancelFileTransfer( "Remote user cancelled direct connection"); } else if (cancelled is DirectIMConnection) { parent.OnDirectIMSessionCancelled(cancelled, "Remote user cancelled direct connection"); } } return; } else if (conn.Type == RendezvousType.Invite) { // AIM sends a type 0x0000 when accepting...huh. if (conn.Sequence == RendezvousSequence.DirectOrStage1) { if (stream.HasMoreData && conn is FileTransferConnection) { FileTransferConnection ftconn = conn as FileTransferConnection; ftconn.SubType = stream.ReadUshort(); ftconn.TotalFiles = stream.ReadUshort(); ftconn.TotalFileSize = stream.ReadUint(); int strlen = 0; byte[] servicedata = stream.ReadByteArrayToEnd(); // The filename in an RD invite is null-terminated ASCII while (servicedata[strlen++] != 0x00) ; ftconn.FileHeader.Name = Encoding.ASCII.GetString(servicedata, 0, strlen - 1); } parent.OnDirectConnectionRequestReceived(conn.Cookie); } else if (conn.Sequence == RendezvousSequence.Stage2) { // The receipient of a previous invite wants a stage 2 proxy redirection // Shut down the server socket, we won't be getting a connection on it conn.StopListeningSocket(); SendDirectConnectionAccept(conn); conn.StartSendThroughStage2AolProxy(); } else if (conn.Sequence == RendezvousSequence.Stage3) { // Direct connection and proxy 2 failed, the sender's trying to proxy it now conn.Method = DirectConnectionMethod.Proxied; conn.ConnectToServer(); } } }
/// <summary> /// Processes the inner TLV list in TLV 0x0005 and returns a new DirectConnection /// </summary> private DirectConnection ProcessChannel2Tlv05(ByteStream stream, UserInfo ui, DataPacket dp) { byte[] invitemessage; Encoding encoding = Encoding.ASCII; string language = "en"; DirectConnection directconn = null; // Pull the type, cookie, and capability array RendezvousType rtype = RendezvousData.TypeFromUshort(stream.ReadUshort()); Cookie cookie = Cookie.GetReceivedCookie(stream.ReadByteArray(8)); byte[] capabilitiesArray = stream.ReadByteArray(16); Capabilities capabilities = CapabilityProcessor.ProcessCLSIDList(capabilitiesArray); // Create the correct type of connection based on the capability if (capabilities == Capabilities.SendFiles) { if ((directconn = parent.Connections.GetDirectConnectionByCookie(cookie)) == null) { directconn = parent.Connections.CreateNewFileTransferConnection( DirectConnectionMethod.Direct, DirectConnectRole.Receiver); } } else if (capabilities == Capabilities.DirectIM) { if ((directconn = parent.Connections.GetDirectConnectionByCookie(cookie)) == null) { directconn = parent.Connections.CreateNewDirectIMConnection( DirectConnectionMethod.Direct, DirectConnectRole.Receiver); } } else if (capabilities == Capabilities.Chat) { directconn = parent.Connections.CreateNewChatInvitationConnection(DirectConnectRole.Receiver); } else { // Currently unsupported parent.OnWarning(ServerErrorCode.UnknownRendezvousChannel, dp); return null; } directconn.Other = ui; directconn.Cookie = cookie; directconn.Type = rtype; ByteStream serviceData = null; Encoding serviceDataEncoding = Encoding.ASCII; // Process the inner TLV list using (TlvBlock tlvs = new TlvBlock(stream.ReadByteArrayToEnd())) { directconn.AOLProxyIP = tlvs.ReadIPAddress(DC_PROXY_IP_ADDRESS); // proxy ip directconn.ClientIP = tlvs.ReadIPAddress(DC_CLIENT_IP_ADDRESS); // internal ip directconn.VerifiedIP = tlvs.ReadIPAddress(0x0004); // external ip directconn.Port = tlvs.ReadUshort(DC_PORT); directconn.Sequence = RendezvousData.SequenceFromUshort(tlvs.ReadUshort(DC_SEQUENCE_NUMBER)); invitemessage = tlvs.ReadByteArray(DC_MESSAGE); if (tlvs.HasTlv(0x000D)) { encoding = Encoding.GetEncoding(tlvs.ReadString(0x000D, Encoding.ASCII)); } language = tlvs.ReadString(0x000E, Encoding.ASCII); directconn.Method = (tlvs.HasTlv(DC_USE_PROXY_FLAG)) ? DirectConnectionMethod.Proxied : DirectConnectionMethod.Direct; serviceData = new ByteStream(tlvs.ReadByteArray(0x2711)); if (tlvs.HasTlv(0x2712)) { serviceDataEncoding = Encoding.GetEncoding(tlvs.ReadString(0x2712, Encoding.ASCII)); } } if (invitemessage != null) { directconn.Message = encoding.GetString(invitemessage, 0, invitemessage.Length); } // Process the extra data, if necessary if (directconn is FileTransferConnection || directconn is DirectIMConnection) { ProcessDirectConnectionRequest(directconn, serviceData); } else if (directconn is ChatInvitationConnection) { ChatInvitationConnection cic = directconn as ChatInvitationConnection; cic.ChatInvite = new ChatInvitation(); cic.ChatInvite.Message = directconn.Message; cic.ChatInvite.Encoding = encoding; cic.ChatInvite.Language = language; ProcessChatInvitationRequest(cic, serviceData); } return directconn; }
/// <summary> /// Retrieves the message text from SNAC(04,07) TLV 02 /// </summary> /// <param name="stream">A received <see cref="ByteStream"/></param> /// <param name="message">An <see cref="IM"/> object to be populated</param> private void GetChannelOneMessage(ByteStream stream, ref IM message) { while (stream.HasMoreData) { byte identifier = stream.ReadByte(); byte version = stream.ReadByte(); ushort length = stream.ReadUshort(); switch (identifier) { case 0x01: // Message text ushort charset = stream.ReadUshort(); ushort language = stream.ReadUshort(); Encoding incoming = IM.GetEncodingFromCharset(charset, language); message.Message = Encoding.Unicode.GetString( Encoding.Convert(incoming, Encoding.Unicode, stream.ReadByteArray(length - 4))); break; default: // Unhandled case stream.AdvanceOffset(length); break; } } }
/// <summary> /// Retrieves the message text from SNAC(04,07) TLV 05 /// </summary> /// <param name="stream">A received <see cref="ByteStream"/></param> /// <param name="message">An <see cref="IM"/> object to be populated</param> private void GetChannelfourMessage(ByteStream stream, ref IM message) { stream.ReadByteArray(46); //fixed unknon part ushort length = stream.ReadUshort(); ushort charset = stream.ReadUshort(); stream.ReadByte(); ushort charsubset = (ushort)0;//stream.ReadUshort(); Encoding incoming = IM.GetEncodingFromCharset(charset, charsubset); string xml = Encoding.Unicode.GetString( Encoding.Convert(incoming, Encoding.Unicode, stream.ReadByteArray(length))); // parse xml System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); doc.LoadXml(xml); System.Xml.XmlNode node = doc.SelectSingleNode("/sms_message/text"); if (node != null) { message.Message = node.InnerText; } node = doc.SelectSingleNode("/sms_message/sender"); if (node != null) { message.ScreenName = node.InnerText; } }
/// <summary> /// Parses a byte buffer into an <see cref="BartID"/> object /// </summary> private void ReadIconInfo(byte[] buffer, UserInfo userinfo) { using (ByteStream iconStream = new ByteStream(buffer)) { int iconStreamSize = iconStream.GetByteCount(); while (iconStream.CurrentPosition + 4 <= iconStreamSize) { BartID item = new BartID(iconStream); // Find the end of the current data item in the stream int endDataPosition = iconStream.CurrentPosition + item.Data.Length; switch (item.Type) { case BartTypeId.BuddyIcon: if (!GraphicsManager.IsBlankIcon(item.Data)) { userinfo.Icon = item; } break; case BartTypeId.StatusString: // Available message using (ByteStream messageStream = new ByteStream(item.Data)) { Encoding encoding = Encoding.UTF8; byte[] amessage = new byte[0]; if (messageStream.HasMoreData) { // Pull the message to a byte array, assume at first that the encoding // is UTF-8. If existing encoding information exists, use that instead amessage = messageStream.ReadByteArray(messageStream.ReadByte()); // Check if there's encoding information available if (messageStream.HasMoreData) { // Check to see if the encoding's been specified if (messageStream.ReadUshort() == 0x0001) { messageStream.AdvanceOffset(2); string encodingStr = messageStream.ReadString(messageStream.ReadUshort(), Encoding.ASCII); // Try to use the encoding from the byte stream try { encoding = Encoding.GetEncoding(encodingStr); } catch (ArgumentException) { Logging.WriteString( "ReadIconInfo: Got unknown encoding for available message (" + encodingStr + "), falling back to UTF-8"); encoding = Encoding.UTF8; } } } } userinfo.AvailableMessage = Encoding.Unicode.GetString( Encoding.Convert(encoding, Encoding.Unicode, amessage)); } break; default: break; } } } }