private async Task <PingReply> SendIcmpEchoRequestOverRawSocketAsync(IPAddress address, byte[] buffer, int timeout, PingOptions?options) { SocketConfig socketConfig = GetSocketConfig(address, buffer, timeout, options); using (Socket socket = GetRawSocket(socketConfig)) { int ipHeaderLength = socketConfig.IsIpv4 ? MinIpHeaderLengthInBytes : 0; CancellationTokenSource timeoutTokenSource = new CancellationTokenSource(timeout); try { await socket.SendToAsync( new ArraySegment <byte>(socketConfig.SendBuffer), SocketFlags.None, socketConfig.EndPoint, timeoutTokenSource.Token) .ConfigureAwait(false); byte[] receiveBuffer = new byte[2 * (MaxIpHeaderLengthInBytes + IcmpHeaderLengthInBytes) + buffer.Length]; // Read from the socket in a loop. We may receive messages that are not echo replies, or that are not in response // to the echo request we just sent. We need to filter such messages out, and continue reading until our timeout. // For example, when pinging the local host, we need to filter out our own echo requests that the socket reads. long startingTimestamp = Stopwatch.GetTimestamp(); while (!timeoutTokenSource.IsCancellationRequested) { SocketReceiveFromResult receiveResult = await socket.ReceiveFromAsync( new ArraySegment <byte>(receiveBuffer), SocketFlags.None, socketConfig.EndPoint, timeoutTokenSource.Token) .ConfigureAwait(false); int bytesReceived = receiveResult.ReceivedBytes; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { continue; // Not enough bytes to reconstruct IP header + ICMP header. } if (TryGetPingReply(socketConfig, receiveBuffer, bytesReceived, startingTimestamp, ref ipHeaderLength, out PingReply? reply)) { return(reply); } } } catch (SocketException ex) when(ex.SocketErrorCode == SocketError.TimedOut) { } catch (SocketException ex) when(ex.SocketErrorCode == SocketError.MessageSize) { return(CreatePingReply(IPStatus.PacketTooBig)); } catch (OperationCanceledException) { } finally { timeoutTokenSource.Dispose(); } // We have exceeded our timeout duration, and no reply has been received. return(CreatePingReply(IPStatus.TimedOut)); } }
private async Task <PingReply> SendIcmpEchoRequestOverRawSocket(IPAddress address, byte[] buffer, int timeout, PingOptions options) { EndPoint endPoint = new IPEndPoint(address, 0); bool isIpv4 = address.AddressFamily == AddressFamily.InterNetwork; ProtocolType protocolType = isIpv4 ? ProtocolType.Icmp : ProtocolType.IcmpV6; // Use the current thread's ID as the identifier. ushort identifier = (ushort)Environment.CurrentManagedThreadId; IcmpHeader header = new IcmpHeader() { Type = isIpv4 ? (byte)IcmpV4MessageType.EchoRequest : (byte)IcmpV6MessageType.EchoRequest, Code = 0, HeaderChecksum = 0, Identifier = identifier, SequenceNumber = 0, }; byte[] sendBuffer = CreateSendMessageBuffer(header, buffer); using (Socket socket = new Socket(address.AddressFamily, SocketType.Raw, protocolType)) { socket.ReceiveTimeout = timeout; socket.SendTimeout = timeout; // Setting Socket.DontFragment and .Ttl is not supported on Unix, so ignore the PingOptions parameter. int ipHeaderLength = isIpv4 ? IpHeaderLengthInBytes : 0; await socket.SendToAsync(new ArraySegment <byte>(sendBuffer), SocketFlags.None, endPoint).ConfigureAwait(false); byte[] receiveBuffer = new byte[ipHeaderLength + IcmpHeaderLengthInBytes + buffer.Length]; long elapsed; Stopwatch sw = Stopwatch.StartNew(); // Read from the socket in a loop. We may receive messages that are not echo replies, or that are not in response // to the echo request we just sent. We need to filter such messages out, and continue reading until our timeout. // For example, when pinging the local host, we need to filter out our own echo requests that the socket reads. while ((elapsed = sw.ElapsedMilliseconds) < timeout) { Task <SocketReceiveFromResult> receiveTask = socket.ReceiveFromAsync( new ArraySegment <byte>(receiveBuffer), SocketFlags.None, endPoint); var cts = new CancellationTokenSource(); Task finished = await Task.WhenAny(receiveTask, Task.Delay(timeout - (int)elapsed, cts.Token)).ConfigureAwait(false); cts.Cancel(); if (finished != receiveTask) { sw.Stop(); return(CreateTimedOutPingReply()); } SocketReceiveFromResult receiveResult = receiveTask.GetAwaiter().GetResult(); int bytesReceived = receiveResult.ReceivedBytes; if (bytesReceived - ipHeaderLength < IcmpHeaderLengthInBytes) { continue; // Not enough bytes to reconstruct IP header + ICMP header. } byte type, code; unsafe { fixed(byte *bytesPtr = receiveBuffer) { int icmpHeaderOffset = ipHeaderLength; IcmpHeader receivedHeader = *((IcmpHeader *)(bytesPtr + icmpHeaderOffset)); // Skip IP header. type = receivedHeader.Type; code = receivedHeader.Code; if (identifier != receivedHeader.Identifier || type == (byte)IcmpV4MessageType.EchoRequest || type == (byte)IcmpV6MessageType.EchoRequest) // Echo Request, ignore { continue; } } } sw.Stop(); long roundTripTime = sw.ElapsedMilliseconds; int dataOffset = ipHeaderLength + IcmpHeaderLengthInBytes; // We want to return a buffer with the actual data we sent out, not including the header data. byte[] dataBuffer = new byte[bytesReceived - dataOffset]; Buffer.BlockCopy(receiveBuffer, dataOffset, dataBuffer, 0, dataBuffer.Length); IPStatus status = isIpv4 ? IcmpV4MessageConstants.MapV4TypeToIPStatus(type, code) : IcmpV6MessageConstants.MapV6TypeToIPStatus(type, code); return(new PingReply(address, options, status, roundTripTime, dataBuffer)); } // We have exceeded our timeout duration, and no reply has been received. sw.Stop(); return(CreateTimedOutPingReply()); } }
public static async Task <int> UdpQueryAsync(this Socket socket, ArraySegment <byte> request, ArraySegment <byte> response, IPEndPoint remoteEP, int timeout = 2000, int retries = 1, bool expBackoffTimeout = false, CancellationToken cancellationToken = default) { Task <SocketReceiveFromResult> recvTask = null; EndPoint epAny = GetEndPointAnyFor(remoteEP.AddressFamily); int timeoutValue = timeout; int retry = 0; while (retry < retries) //retry loop { if (expBackoffTimeout) { timeoutValue = timeout * (2 ^ retry); } retry++; if (cancellationToken.IsCancellationRequested) { return(await Task.FromCanceled <int>(cancellationToken)); //task cancelled } //send request await socket.SendToAsync(request, SocketFlags.None, remoteEP); while (true) { //receive request if (recvTask == null) { recvTask = socket.ReceiveFromAsync(response, SocketFlags.None, epAny); } //receive with timeout using (CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource()) { using (CancellationTokenRegistration ctr = cancellationToken.Register(delegate() { timeoutCancellationTokenSource.Cancel(); })) { if (await Task.WhenAny(recvTask, Task.Delay(timeoutValue, timeoutCancellationTokenSource.Token)) != recvTask) { break; //recv timed out } } timeoutCancellationTokenSource.Cancel(); //to stop delay task } SocketReceiveFromResult result = await recvTask; if (remoteEP.Equals(result.RemoteEndPoint)) { //got response return(result.ReceivedBytes); } //recv task is complete; set recvTask to null so that another task is used to read next response packet recvTask = null; } } socket.Dispose(); throw new SocketException((int)SocketError.TimedOut); }
public async Task SendToRecvFrom_Datagram_UDP(IPAddress loopbackAddress) { IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; // TODO #5185: Harden against packet loss const int DatagramSize = 256; const int DatagramsToSend = 256; const int AckTimeout = 1000; const int TestTimeout = 30000; var left = new Socket(leftAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); left.BindToAnonymousPort(leftAddress); var right = new Socket(rightAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); right.BindToAnonymousPort(rightAddress); var leftEndpoint = (IPEndPoint)left.LocalEndPoint; var rightEndpoint = (IPEndPoint)right.LocalEndPoint; var receiverAck = new SemaphoreSlim(0); var senderAck = new SemaphoreSlim(0); var receivedChecksums = new uint?[DatagramsToSend]; Task leftThread = Task.Run(async() => { using (left) { EndPoint remote = leftEndpoint.Create(leftEndpoint.Serialize()); var recvBuffer = new byte[DatagramSize]; for (int i = 0; i < DatagramsToSend; i++) { SocketReceiveFromResult result = await ReceiveFromAsync( left, new ArraySegment <byte>(recvBuffer), remote); Assert.Equal(DatagramSize, result.ReceivedBytes); Assert.Equal(rightEndpoint, result.RemoteEndPoint); int datagramId = recvBuffer[0]; Assert.Null(receivedChecksums[datagramId]); receivedChecksums[datagramId] = Fletcher32.Checksum(recvBuffer, 0, result.ReceivedBytes); receiverAck.Release(); Assert.True(await senderAck.WaitAsync(TestTimeout)); } } }); var sentChecksums = new uint[DatagramsToSend]; using (right) { var random = new Random(); var sendBuffer = new byte[DatagramSize]; for (int i = 0; i < DatagramsToSend; i++) { random.NextBytes(sendBuffer); sendBuffer[0] = (byte)i; int sent = await SendToAsync(right, new ArraySegment <byte>(sendBuffer), leftEndpoint); Assert.True(await receiverAck.WaitAsync(AckTimeout)); senderAck.Release(); Assert.Equal(DatagramSize, sent); sentChecksums[i] = Fletcher32.Checksum(sendBuffer, 0, sent); } } await leftThread; for (int i = 0; i < DatagramsToSend; i++) { Assert.NotNull(receivedChecksums[i]); Assert.Equal(sentChecksums[i], (uint)receivedChecksums[i]); } }
public async Task SendToRecvFrom_Datagram_UDP(IPAddress loopbackAddress, bool useClone) { IPAddress leftAddress = loopbackAddress, rightAddress = loopbackAddress; const int DatagramSize = 256; const int DatagramsToSend = 256; const int ReceiverAckTimeout = 5000; const int SenderAckTimeout = 10000; using var origLeft = new Socket(leftAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); using var origRight = new Socket(rightAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); origLeft.BindToAnonymousPort(leftAddress); origRight.BindToAnonymousPort(rightAddress); using var left = useClone ? new Socket(origLeft.SafeHandle) : origLeft; using var right = useClone ? new Socket(origRight.SafeHandle) : origRight; // Force non-blocking mode in ...SyncForceNonBlocking variants of the test: ConfigureNonBlocking(left); ConfigureNonBlocking(right); var leftEndpoint = (IPEndPoint)left.LocalEndPoint; var rightEndpoint = (IPEndPoint)right.LocalEndPoint; var receiverAck = new SemaphoreSlim(0); var senderAck = new SemaphoreSlim(0); _output.WriteLine($"{DateTime.Now}: Sending data from {rightEndpoint} to {leftEndpoint}"); var receivedChecksums = new uint?[DatagramsToSend]; Task leftThread = Task.Run(async() => { EndPoint remote = leftEndpoint.Create(leftEndpoint.Serialize()); var recvBuffer = new byte[DatagramSize]; for (int i = 0; i < DatagramsToSend; i++) { SocketReceiveFromResult result = await ReceiveFromAsync( left, new ArraySegment <byte>(recvBuffer), remote); Assert.Equal(DatagramSize, result.ReceivedBytes); Assert.Equal(rightEndpoint, result.RemoteEndPoint); int datagramId = recvBuffer[0]; Assert.Null(receivedChecksums[datagramId]); receivedChecksums[datagramId] = Fletcher32.Checksum(recvBuffer, 0, result.ReceivedBytes); receiverAck.Release(); bool gotAck = await senderAck.WaitAsync(SenderAckTimeout); Assert.True(gotAck, $"{DateTime.Now}: Timeout waiting {SenderAckTimeout} for senderAck in iteration {i}"); } }); var sentChecksums = new uint[DatagramsToSend]; using (right) { var random = new Random(); var sendBuffer = new byte[DatagramSize]; for (int i = 0; i < DatagramsToSend; i++) { random.NextBytes(sendBuffer); sendBuffer[0] = (byte)i; int sent = await SendToAsync(right, new ArraySegment <byte>(sendBuffer), leftEndpoint); bool gotAck = await receiverAck.WaitAsync(ReceiverAckTimeout); Assert.True(gotAck, $"{DateTime.Now}: Timeout waiting {ReceiverAckTimeout} for receiverAck in iteration {i} after sending {sent}. Receiver is in {leftThread.Status}"); senderAck.Release(); Assert.Equal(DatagramSize, sent); sentChecksums[i] = Fletcher32.Checksum(sendBuffer, 0, sent); } } await leftThread; for (int i = 0; i < DatagramsToSend; i++) { Assert.NotNull(receivedChecksums[i]); Assert.Equal(sentChecksums[i], (uint)receivedChecksums[i]); } }
public async Task <int> UdpQueryAsync(ArraySegment <byte> request, ArraySegment <byte> response, EndPoint remoteEP, int timeout = 10000, int retries = 1, bool expBackoffTimeout = false, CancellationToken cancellationToken = default) { Task <SocketReceiveFromResult> recvTask = null; int timeoutValue = timeout; int retry = 0; while (retry < retries) //retry loop { if (expBackoffTimeout) { timeoutValue = timeout * (2 ^ retry); } retry++; if (cancellationToken.IsCancellationRequested) { return(await Task.FromCanceled <int>(cancellationToken)); //task cancelled } //send request await SendToAsync(request, remoteEP); while (true) { //receive request if (recvTask == null) { recvTask = ReceiveFromAsync(response); } //receive with timeout using (CancellationTokenSource timeoutCancellationTokenSource = new CancellationTokenSource()) { using (CancellationTokenRegistration ctr = cancellationToken.Register(delegate() { timeoutCancellationTokenSource.Cancel(); })) { if (await Task.WhenAny(recvTask, Task.Delay(timeoutValue, timeoutCancellationTokenSource.Token)) != recvTask) { break; //recv timed out } } timeoutCancellationTokenSource.Cancel(); //to stop delay task } SocketReceiveFromResult result = await recvTask; if ((remoteEP is DomainEndPoint) || remoteEP.Equals(result.RemoteEndPoint)) //in case remoteEP is domain end point then returned response will contain the resolved IP address so cant compare it together { //got response return(result.ReceivedBytes); } //recv task is complete; set recvTask to null so that another task is used to read next response packet recvTask = null; } } _udpSocket.Dispose(); throw new SocketException((int)SocketError.TimedOut); }
public async Task <SocketReceiveFromResult> ReceiveFromAsync(ArraySegment <byte> buffer) { byte[] datagram = new byte[262 + buffer.Count]; SocketReceiveFromResult result = await _udpSocket.ReceiveFromAsync(datagram, SocketFlags.None, SocketExtension.GetEndPointAnyFor(_udpSocket.AddressFamily)); if (result.ReceivedBytes < 10) { throw new SocksProxyException("Incomplete SOCKS5 datagram was received."); } EndPoint remoteEP; switch ((SocksAddressType)datagram[3]) { case SocksAddressType.IPv4Address: { byte[] address = new byte[4]; Buffer.BlockCopy(datagram, 3 + 1, address, 0, 4); byte[] port = new byte[2]; Buffer.BlockCopy(datagram, 3 + 1 + 4, port, 0, 2); Array.Reverse(port); remoteEP = new IPEndPoint(new IPAddress(address), BitConverter.ToUInt16(port, 0)); } break; case SocksAddressType.IPv6Address: { byte[] address = new byte[16]; Buffer.BlockCopy(datagram, 3 + 1, address, 0, 16); byte[] port = new byte[2]; Buffer.BlockCopy(datagram, 3 + 1 + 16, port, 0, 2); Array.Reverse(port); remoteEP = new IPEndPoint(new IPAddress(address), BitConverter.ToUInt16(port, 0)); } break; case SocksAddressType.DomainName: { int length = datagram[3 + 1]; byte[] address = new byte[length]; Buffer.BlockCopy(datagram, 3 + 1 + 1, address, 0, length); byte[] port = new byte[2]; Buffer.BlockCopy(datagram, 3 + 1 + 1 + length, port, 0, 2); Array.Reverse(port); remoteEP = new DomainEndPoint(Encoding.ASCII.GetString(address), BitConverter.ToUInt16(port, 0)); } break; default: throw new NotSupportedException("SocksAddressType not supported."); } int addressSize; switch (remoteEP.AddressFamily) { case AddressFamily.InterNetwork: addressSize = 4; break; case AddressFamily.InterNetworkV6: addressSize = 16; break; case AddressFamily.Unspecified: addressSize = 1 + (remoteEP as DomainEndPoint).Address.Length; break; default: throw new NotSupportedException("AddressFamily not supported."); } int dataOffset = 6 + addressSize; int dataSize = result.ReceivedBytes - dataOffset; if (dataSize > buffer.Count) { dataSize = buffer.Count; } ArraySegment <byte> recvData = new ArraySegment <byte>(datagram, dataOffset, dataSize); recvData.CopyTo(buffer); if (_relayEP == null) { _relayEP = result.RemoteEndPoint; //set new relay ep } return(new SocketReceiveFromResult() { ReceivedBytes = dataSize, RemoteEndPoint = remoteEP }); }
private async Task RunAcceptLoopAsync() { try { while (true) { for (var schedulerIndex = 0; schedulerIndex < _numSchedulers; schedulerIndex++) { try { ArraySegment <byte> buffer = new ArraySegment <byte>(new byte[4096]); IPEndPoint sender = new IPEndPoint(_listenSocket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0); SocketReceiveFromResult receiveResult = await _listenSocket.ReceiveFromAsync(buffer, SocketFlags.None, sender); if (receiveResult.ReceivedBytes > 0) { AbstractPacketBase packet = AbstractPacketBase.Parse(buffer.AsSpan()); System.Console.WriteLine($"rcv {receiveResult.RemoteEndPoint} " + packet.ToString()); //var response = new RegularPacket(connectionID, 2, null); //await _listenSocket.SendToAsync(new ArraySegment<byte>(response.PadAndNullEncrypt()), SocketFlags.None, receiveResult.RemoteEndPoint); /* * if (_connections.TryGetValue(connectionID, out QuicConnection connection)) * { * await connection.Input.WriteAsync(buffer); * } * else * { * connection = new QuicConnection(connectionID.ToString(), _listenSocket, _memoryPool, _schedulers[schedulerIndex], _logger); * _connections.Add(connectionID, connection); * await connection.StartAsync(_dispatcher); * } */ } } catch (SocketException ex) when(ex.SocketErrorCode == SocketError.ConnectionReset) { // REVIEW: Should there be a separate log message for a connection reset this early? _logger.LogDebug($"Connection Reset: connectionId: ({null})"); } catch (SocketException ex) when(!_unbinding) { _logger.LogError(ex, $"Connection Error: connectionId: ({null})"); } } } } catch (Exception ex) { if (_unbinding) { // Means we must be unbinding. Eat the exception. } else { _logger.LogCritical(ex, $"Unexpected exeption in {nameof(QuicTransport)}.{nameof(RunAcceptLoopAsync)}."); _listenException = ex; // Request shutdown so we can rethrow this exception // in Stop which should be observable. _applicationLifetime.StopApplication(); } } }
public void StartReceive() { _socket = CreateSocket(); var thread = new Thread(async() => { while (true) { try { //Start receiving data ArraySegment <byte> buff = new ArraySegment <byte>(new byte[2500]); var end = new IPEndPoint(IPAddress.Any, 0); SocketReceiveFromResult v = await _socket.ReceiveFromAsync(buff, SocketFlags.None, end); // Check if we can accept it if (!_accept) { continue; } AtemServerConnection conn = Connections.FindOrCreateConnection(v.RemoteEndPoint, out _); if (conn == null) { continue; } byte[] buffer = buff.Array; var packet = new ReceivedPacket(buffer); if (packet.CommandCode.HasFlag(ReceivedPacket.CommandCodeFlags.Handshake)) { conn.ResetConnStatsInfo(); // send handshake back byte[] test = { buffer[0], buffer[1], // flags + length buffer[2], buffer[3], // session id 0x00, 0x00, // acked pkt id 0x00, 0x00, // retransmit request buffer[8], buffer[9], // unknown2 0x00, 0x00, // server pkt id 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }; var sendThread = new Thread(o => { while (!conn.HasTimedOut) { conn.TrySendQueued(_socket); Task.Delay(1).Wait(); } Console.WriteLine("send finished"); }); sendThread.Name = $"To {conn.Endpoint}"; sendThread.Start(); await _socket.SendToAsync(new ArraySegment <byte>(test, 0, 20), SocketFlags.None, v.RemoteEndPoint); continue; } if (!conn.IsOpened) { var recvThread = new Thread(o => { while (!conn.HasTimedOut || conn.HasCommandsToProcess) { List <ICommand> cmds = conn.GetNextCommands(); Log.DebugFormat("Recieved {0} commands", cmds.Count); //conn.HandleInner(_state, connection, cmds); } }); recvThread.Name = $"Receive {conn.Endpoint}"; recvThread.Start(); } conn.Receive(_socket, packet); if (conn.ReadyForData) { QueueDataDumps(conn); } } catch (SocketException) { // Reinit the socket as it is now unavailable //_socket = CreateSocket(); } } }); thread.Name = "AtemServer"; thread.Start(); }