예제 #1
0
        /// <summary>
        /// Sends a Netbios Name Service request to a unicast address and returns the respective response
        /// </summary>
        /// <param name="packet">Netbios Name Service packet to send</param>
        /// <param name="address">Target address to send the packet to</param>
        /// <param name="timeOutMilliseconds">
        /// If after this timeout following sending the request packet no response has been received, the request is sent again.
        /// This is tried <see cref="numRetries"/> times. We intentionally use the "wrong" timeout value here. For unicast
        /// requests RFC 1002 defines a UCAST_REQ_RETRY_TIMEOUT of 5 seconds. However, with the default retry count of 3, this
        /// results in a maximum time of 15 seconds until we can be sure that no response was sent. This is extremely long and
        /// tests have shown that even in a WLan, computers respond to requests withing less than 100 ms. We therefore by default
        /// use the BCAST_REQ_RETRY_TIMEOUT of 250ms, which is meant for broadcast requests, also for unicast requests.
        /// </param>
        /// <param name="numRetries">Number of retries. Default is 3.</param>
        /// <returns>
        /// A <see cref="Task"/> that completes when either (a) the respective response was received (in which case the result value
        /// of the Task contains the response packet) or (b) when the send operation was not successful or (c) when after <see cref="numRetries"/>
        /// retries no response was received (in the latter two cases the result value of the Task is <c>null</c>).
        /// </returns>
        public async Task <NbNsPacketBase> SendUnicastRequestAsync(NbNsPacketBase packet, IPAddress address, int timeOutMilliseconds = BCAST_REQ_RETRY_TIMEOUT, int numRetries = BCAST_REQ_RETRY_COUNT)
        {
            if (packet == null)
            {
                throw new ArgumentNullException("packet");
            }
            if (address == null)
            {
                throw new ArgumentNullException("address");
            }
            var identifier = new PacketIdentifier(packet.Header.NameTrnId, address);

            // If there is a request pending with the same transaction Id and the same target IP address, we do not
            // send the request again, but simply return the Task representing the already pending request.
            var newTcs = new TaskCompletionSource <NbNsPacketBase>();
            var tcs    = _pendingUnicastRequests.GetOrAdd(identifier, newTcs);

            if (tcs != newTcs)
            {
                return(await tcs.Task);
            }

            for (var retryCount = 1; retryCount <= numRetries; retryCount++)
            {
                // If the send operation was not successful (most likely the given IP address does not exist in the local subnet)
                // we immediately return null.
                if (!await SendPacketAsync(packet, address))
                {
                    _pendingUnicastRequests.TryRemove(identifier, out newTcs);
                    return(null);
                }

                // If we received a response within the given timeout, we return the response; if not, we resend the request
                if (await Task.WhenAny(tcs.Task, Task.Delay(timeOutMilliseconds)) == tcs.Task)
                {
                    _pendingUnicastRequests.TryRemove(identifier, out newTcs);
                    return(await tcs.Task);
                }
            }

            _pendingUnicastRequests.TryRemove(identifier, out newTcs);
            return(null);
        }
예제 #2
0
        /// <summary>
        /// Sends a Netbios Name Service packet asynchronously
        /// </summary>
        /// <param name="packet">Netbios Name Service packet to send</param>
        /// <param name="address">Target address to send the packet to</param>
        /// <param name="timeOutMilliseconds">
        /// Maximum time in milliseconds to wait for the send operation to finish. If it takes longer, the target IP address
        /// most likely does not exist in a local subnet (although the address is part of a local subnet) so that the ARP
        /// protocol is not able to find the MAC address for the given IP address.
        /// </param>
        /// <returns>
        /// A <see cref="Task"/> that completes with result = <c>true</c> when the packet was successfully sent or
        /// with result = <c>false</c> if the send operation was not successful witin the given timeout.
        /// </returns>
        public async Task <bool> SendPacketAsync(NbNsPacketBase packet, IPAddress address, int timeOutMilliseconds = SEND_TIMEOUT)
        {
            if (packet == null)
            {
                throw new ArgumentNullException("packet");
            }
            if (address == null)
            {
                throw new ArgumentNullException("address");
            }
            var endPoint = new IPEndPoint(address, TARGET_PORT);
            var tcs      = new TaskCompletionSource <int>();

            try
            {
                _socket.BeginSendTo(packet, 0, packet.Length, SocketFlags.None, endPoint, asynchronousResult =>
                {
                    var t = (TaskCompletionSource <int>)asynchronousResult.AsyncState;
                    try
                    {
                        t.TrySetResult(_socket.EndSendTo(asynchronousResult));
                    }
                    catch (Exception)
                    {
                        t.TrySetResult(0);
                    }
                }, tcs);
            }
            catch (Exception)
            {
                return(false);
            }
            if (await Task.WhenAny(tcs.Task, Task.Delay(timeOutMilliseconds)) != tcs.Task)
            {
                return(false);
            }

            // The send operation was successful if as many bytes as are contained in the packet were actually sent.
            return(packet.Length == await tcs.Task);
        }
예제 #3
0
        /// <summary>
        /// Tries to parse a Netbios Name Service packet from a buffer of bytes
        /// </summary>
        /// <param name="buffer">Byte array containing the NbNs packet</param>
        /// <param name="packet">Parsed NbNs packet if successful, else null</param>
        /// <returns><c>true</c> if parsing was successful, else <c>false</c></returns>
        /// <remarks>
        /// This method is the entry point for parsing Netbios Name Service packets. It returns an object of a class
        /// derived from <see cref="NbNsPacketBase"/>, which can then be casted based on <see cref="PacketType"/> to
        /// the respective derived class.
        /// </remarks>
        public static bool TryParse(byte[] buffer, out NbNsPacketBase packet)
        {
            packet = null;
            if (buffer == null || buffer.Length < NbNsHeader.NETBIOS_HEADER_LENGTH)
            {
                return(false);
            }

            NbNsHeader header;

            if (!NbNsHeader.TryParse(buffer, out header))
            {
                return(false);
            }

            if (header.Opcode == NbNsHeader.OpcodeSpecifier.Query && header.IsResponse == false && header.IsRecursionDesired == false)
            {
                // Must be a Netbios Node Status Request
                NbNsNodeStatusRequest result;
                if (!NbNsNodeStatusRequest.TryParse(header, buffer, out result))
                {
                    return(false);
                }
                packet = result;
            }
            else if (header.Opcode == NbNsHeader.OpcodeSpecifier.Query && header.IsRecursionDesired == false)
            {
                // Must be a Netbios Node Status Response
                NbNsNodeStatusResponse result;
                if (!NbNsNodeStatusResponse.TryParse(header, buffer, out result))
                {
                    return(false);
                }
                packet = result;
            }

            // ToDo: Parse Further Netbios Name Service packets

            return(true);
        }
예제 #4
0
    /// <summary>
    /// Tries to parse a Netbios Name Service packet from a buffer of bytes
    /// </summary>
    /// <param name="buffer">Byte array containing the NbNs packet</param>
    /// <param name="packet">Parsed NbNs packet if successful, else null</param>
    /// <returns><c>true</c> if parsing was successful, else <c>false</c></returns>
    /// <remarks>
    /// This method is the entry point for parsing Netbios Name Service packets. It returns an object of a class
    /// derived from <see cref="NbNsPacketBase"/>, which can then be casted based on <see cref="PacketType"/> to
    /// the respective derived class.
    /// </remarks>
    public static bool TryParse(byte[] buffer, out NbNsPacketBase packet)
    {
      packet = null;
      if (buffer == null || buffer.Length < NbNsHeader.NETBIOS_HEADER_LENGTH)
        return false;

      NbNsHeader header;
      if (!NbNsHeader.TryParse(buffer, out header))
        return false;

      if (header.Opcode == NbNsHeader.OpcodeSpecifier.Query && header.IsResponse == false && header.IsRecursionDesired == false)
      {
        // Must be a Netbios Node Status Request
        NbNsNodeStatusRequest result;
        if (!NbNsNodeStatusRequest.TryParse(header, buffer, out result))
          return false;
        packet = result;
      }
      else if (header.Opcode == NbNsHeader.OpcodeSpecifier.Query && header.IsRecursionDesired == false)
      {
        // Must be a Netbios Node Status Response
        NbNsNodeStatusResponse result;
        if (!NbNsNodeStatusResponse.TryParse(header, buffer, out result))
          return false;
        packet = result;
      }

      // ToDo: Parse Further Netbios Name Service packets
      
      return true;
    }
예제 #5
0
        /// <summary>
        /// Method used in <see cref="_receiverTask"/> to receive Netbios Name Service packets
        /// </summary>
        /// <returns>Task that completes when the <see cref="_receiverTask"/> stops receiving packets</returns>
        private async Task Receive()
        {
            var      buffer        = new byte[RECEIVE_BUFFER_SIZE];
            EndPoint localEndPoint = new IPEndPoint(0, 0);

            while (true)
            {
                var tcs = new TaskCompletionSource <EndPoint>();
                try
                {
                    _socket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref localEndPoint, asynchronousResult =>
                    {
                        var t       = (TaskCompletionSource <EndPoint>)asynchronousResult.AsyncState;
                        EndPoint ep = new IPEndPoint(0, 0);
                        try
                        {
                            _socket.EndReceiveFrom(asynchronousResult, ref ep);
                            t.TrySetResult(ep);
                        }
                        catch (Exception)
                        {
                            t.TrySetResult(null);
                        }
                    }, tcs);
                }
                catch (Exception)
                {
                    if (_endReceive.Task.IsCompleted)
                    {
                        return;
                    }
                    continue;
                }

                // Wait until a new packet has been received or NbNsClient is disposed
                if (await Task.WhenAny(tcs.Task, _endReceive.Task) == _endReceive.Task)
                {
                    return;
                }

                // RemoteEndPoint is null when there was an exception in EndReceive. In that case, discard the packet.
                var remoteEndPoint = (await tcs.Task) as IPEndPoint;
                if (remoteEndPoint == null)
                {
                    continue;
                }

                // If we cannot parse the received packet, discard it.
                NbNsPacketBase packet;
                if (!NbNsPacketBase.TryParse(buffer, out packet))
                {
                    continue;
                }

                // If the received packet is the response to a known request, set the request task result to the received packet.
                var identifier = new PacketIdentifier(packet.Header.NameTrnId, remoteEndPoint.Address);
                TaskCompletionSource <NbNsPacketBase> result;
                if (_pendingUnicastRequests.TryGetValue(identifier, out result))
                {
                    result.TrySetResult(packet);
                }
            }
        }