/// <summary> /// Queries the STUN server with the specified IP address for the public IP /// address of the requesting host. /// </summary> /// <param name="address">The IP address of the STUN server to query.</param> /// <param name="port">The port on which the STUN service is running at /// the specified host.</param> /// <param name="timeout">The maximum number of milliseconds to wait for /// a server response before returning to the caller.</param> /// <returns>The public IP address of the requesting host.</returns> /// <exception cref="ArgumentNullException">The address parameter is /// null.</exception> /// <exception cref="ArgumentOutOfRangeException">The port is not between /// 0 and 65535.</exception> /// <exception cref="ProtocolViolationException">The specified STUN /// server returned an erroneous response.</exception> /// <exception cref="SocketException">The specified hostname could not be /// resolved, or an error occurred while sending the request or while /// retrieving the response, or the specified STUN server could not be /// reached.</exception> /// <exception cref="TimeoutException">The specified timeout has /// expired.</exception> public static IPAddress Query(IPAddress address, int port = 3478, int timeout = Int32.MaxValue) { address.ThrowIfNull("address"); port.ThrowIfOutOfRange("port", 0, 65535); IPEndPoint IpEp = new IPEndPoint(address, port); var request = new BindingRequest().Serialize(); int rto = initialRto; using (UdpClient udp = new UdpClient()) { // The timeout mechanism is similar to TCP. For details, // refer to RFC 5389, Section 7.2.1. Sending over UDP. for (int tries = 0; tries < rc; tries++) { // Transmit the datagram. udp.Send(request, request.Length, IpEp); // Set the timeout value on the socket. udp.Client.ReceiveTimeout = rto; try { byte[] datagram = udp.Receive(ref IpEp); return(BindingResponse.Deserialize(datagram).Address); } catch (SocketException e) { if (e.ErrorCode != connectionTimeout) { throw; } timeout = timeout - rto; if (timeout <= 0) { throw new TimeoutException("The timeout has expired."); } } catch (SerializationException) { throw new ProtocolViolationException("The STUN " + "Binding Response is invalid."); } // Increase the timeout value. if (tries < (rc - 1)) { rto = rto * 2; } else { rto = initialRto * rm; } if (timeout < rto) { rto = timeout; } } // Give up. throw new SocketException(connectionTimeout); } }
public static IPAddress Query(IPAddress address, int port = 0xd96, int timeout = 0x7fffffff) { address.ThrowIfNull <IPAddress>("address"); port.ThrowIfOutOfRange("port", 0, 0xffff); IPEndPoint endPoint = new IPEndPoint(address, port); byte[] dgram = new BindingRequest(null).Serialize(); int num = 500; using (UdpClient client = new UdpClient()) { for (int i = 0; i < 7; i++) { client.Send(dgram, dgram.Length, endPoint); client.Client.ReceiveTimeout = num; try { return(BindingResponse.Deserialize(client.Receive(ref endPoint)).Address); } catch (SocketException exception) { if (exception.ErrorCode != 0x274c) { throw; } timeout -= num; if (timeout <= 0) { throw new TimeoutException("The timeout has expired."); } } catch (SerializationException) { throw new ProtocolViolationException("The STUN Binding Response is invalid."); } if (i < 6) { num *= 2; } else { num = 0x1f40; } if (timeout < num) { num = timeout; } } throw new SocketException(0x274c); } }