private PeerQueryData ByteArrayToPeerData(byte[] bytes, int offset)
        {
            if (offset + 100 > bytes.Length)
            {
                return(null);
            }

            PeerQueryData data = new PeerQueryData
            {
                Number           = BitConverter.ToUInt32(bytes, offset).ToString(),
                LongName         = Encoding.ASCII.GetString(bytes, offset + 4, 40).Trim(new char[] { '\x00' }),
                SpecialAttribute = BitConverter.ToUInt16(bytes, offset + 44),
                PeerType         = bytes[offset + 46],
                HostName         = Encoding.ASCII.GetString(bytes, offset + 47, 40).Trim(new char[] { '\x00' }),
                IpAddress        = $"{bytes[offset + 87]}.{bytes[offset + 88]}.{bytes[offset + 89]}.{bytes[offset + 90]}",
                PortNumber       = BitConverter.ToUInt16(bytes, offset + 91),
                ExtensionNumber  = bytes[offset + 93],
                Pin = BitConverter.ToUInt16(bytes, offset + 94)
            };

            UInt32   timestamp = BitConverter.ToUInt32(bytes, offset + 96);
            DateTime dt        = new DateTime(1900, 1, 1, 0, 0, 0, 0);

            data.LastChange = dt.AddSeconds(timestamp);

            return(data);
        }
        /// <summary>
        /// Query for search string
        /// </summary>
        /// <param name="name"></param>
        /// <returns>search reply with list of peers</returns>
        private PeerSearchReply SendPeerSearch(string name)
        {
            Logging.Instance.Log(LogTypes.Debug, TAG, nameof(SendPeerSearch), $"name='{name}'");
            PeerSearchReply reply = new PeerSearchReply();

            if (client == null)
            {
                Logging.Instance.Error(TAG, nameof(SendPeerSearch), "no server connection");
                reply.Error = "no server connection";
                return(reply);
            }

            if (string.IsNullOrEmpty(name))
            {
                reply.Error = "no search name";
                return(reply);
            }

            byte[] sendData = new byte[43];
            sendData[0] = 0x0A;             // Peer_search
            sendData[1] = 0x29;             // length
            sendData[2] = 0x01;;            // version 1
            byte[] txt = Encoding.ASCII.GetBytes(name);
            Buffer.BlockCopy(txt, 0, sendData, 3, txt.Length);
            try
            {
                stream.Write(sendData, 0, sendData.Length);
            }
            catch (Exception ex)
            {
                Message?.Invoke(LngText(LngKeys.Message_SubscribeServerError));
                Logging.Instance.Error(TAG, nameof(SendPeerQuery), $"error sending data to subscribe server", ex);
                reply.Valid = false;
                reply.Error = "reply server error";
                return(reply);
            }

            byte[] ack = new byte[] { 0x08, 0x00 };
            List <PeerQueryData> list = new List <PeerQueryData>();

            while (true)
            {
                byte[] recvData = new byte[102];
                int    recvLen  = 0;
                try
                {
                    recvLen = stream.Read(recvData, 0, recvData.Length);
                }
                catch (Exception ex)
                {
                    Logging.Instance.Error(TAG, nameof(SendPeerQuery), $"error receiving data from subscribe server", ex);
                    reply.Valid = false;
                    reply.Error = "reply server error";
                    return(reply);
                }
                //Logging.Instance.Log(LogTypes.Debug, TAG, nameof(SendPeerSearch), $"recvLen={recvLen}");

                if (recvLen == 0)
                {
                    Logging.Instance.Log(LogTypes.Error, TAG, nameof(SendPeerSearch), $"recvLen=0");
                    reply.Error = $"no data received";
                    return(reply);
                }

                if (recvData[0] == 0x09)
                {
                    // end of list
                    break;
                }

                if (recvLen < 2 + 0x64)
                {
                    Logging.Instance.Log(LogTypes.Warn, TAG, nameof(SendPeerSearch), $"received data to short ({recvLen} bytes)");
                    reply.Error = $"received data to short ({recvLen} bytes)";
                    continue;
                }

                if (recvData[1] != 0x64)
                {
                    Logging.Instance.Log(LogTypes.Warn, TAG, nameof(SendPeerSearch), $"invalid length value ({recvData[1]})");
                    reply.Error = $"invalid length value ({recvData[1]})";
                    continue;
                }

                PeerQueryData data = ByteArrayToPeerData(recvData, 2);
                Logging.Instance.Log(LogTypes.Debug, TAG, nameof(SendPeerSearch), $"found {data}");

                list.Add(data);

                // send ack
                try
                {
                    stream.Write(ack, 0, ack.Length);
                }
                catch (Exception ex)
                {
                    Logging.Instance.Error(TAG, nameof(SendPeerQuery), $"error sending data to subscribe server", ex);
                    return(null);
                }
            }

            list.Sort(new PeerQueryDataSorter(PeerQueryDataSorter.Sort.Number));
            reply.List  = list.ToArray();
            reply.Valid = true;
            reply.Error = "ok";

            return(reply);
        }