/// <summary> /// Validates the specified <paramref name="connectionString"/>. /// </summary> /// <param name="connectionString">Connection string to be validated.</param> /// <exception cref="ArgumentException">Server property is missing.</exception> /// <exception cref="FormatException">Server property is invalid.</exception> /// <exception cref="ArgumentOutOfRangeException">Server port value is not between <see cref="Transport.PortRangeLow"/> and <see cref="Transport.PortRangeHigh"/>.</exception> protected override void ValidateConnectionString(string connectionString) { m_connectData = connectionString.ParseKeyValuePairs(); // Derive desired IP stack based on specified "interface" setting, adding setting if it's not defined m_ipStack = Transport.GetInterfaceIPStack(m_connectData); // Check if 'server' property is missing. if (!m_connectData.ContainsKey("server")) throw new ArgumentException(string.Format("Server property is missing (Example: {0})", DefaultConnectionString)); // Backwards compatibility adjustments. // New Format: Server=localhost:8888 // Old Format: Server=localhost; Port=8888 if (m_connectData.ContainsKey("port")) m_connectData["server"] = string.Format("{0}:{1}", m_connectData["server"], m_connectData["port"]); // Check if 'server' property is valid. Match endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex); if (endpoint == Match.Empty) throw new FormatException(string.Format("Server property is invalid (Example: {0})", DefaultConnectionString)); if (!Transport.IsPortNumberValid(endpoint.Groups["port"].Value)) throw new ArgumentOutOfRangeException("connectionString", string.Format("Server port must between {0} and {1}", Transport.PortRangeLow, Transport.PortRangeHigh)); }
/// <summary> /// Creates an <see cref="IPEndPoint"/> for the specified host name and port number. /// </summary> /// <param name="hostNameOrAddress">The host name or IP address to resolve.</param> /// <param name="port">The port number to be associated with the address.</param> /// <param name="stack">Desired IP stack to use.</param> /// <returns>An <see cref="IPEndPoint"/> object.</returns> public static IPEndPoint CreateEndPoint(string hostNameOrAddress, int port, IPStack stack) { // Determine system's default IP stack if the default stack was requested if (stack == IPStack.Default) stack = GetDefaultIPStack(); // Make sure system can support specified stack if (stack == IPStack.IPv6 && !Socket.OSSupportsIPv6) throw new NotSupportedException(string.Format("IPv6 stack is not available for socket creation on {0}:{1}", hostNameOrAddress.ToNonNullNorWhiteSpace("localhost"), port)); #if !MONO else if (stack == IPStack.IPv4 && !Socket.OSSupportsIPv4) throw new NotSupportedException(string.Format("IPv4 stack is not available for socket creation on {0}:{1}", hostNameOrAddress.ToNonNullNorWhiteSpace("localhost"), port)); #endif if (string.IsNullOrWhiteSpace(hostNameOrAddress)) { // No host name or IP address was specified, use local IPs if (stack == IPStack.IPv6) return new IPEndPoint(IPAddress.IPv6Any, port); return new IPEndPoint(IPAddress.Any, port); } else { IPAddress address; bool ipStackMismatch = false; // Attempt to parse provided address name as a literal IP address if (IPAddress.TryParse(hostNameOrAddress, out address)) { // As long as desired IP stack matches format of specified IP address, return end point for address if ((stack == IPStack.IPv6 && address.AddressFamily == AddressFamily.InterNetworkV6) || (stack == IPStack.IPv4 && address.AddressFamily == AddressFamily.InterNetwork)) return new IPEndPoint(address, port); // User specified an IP address that is mismatch with the desired IP stack. If the DNS server // responds to this IP, we can attempt to see if an IP is defined for the desired IP stack, // otherwise this is an exception ipStackMismatch = true; } try { // Handle "localhost" as a special case, returning proper loopback address for the desired IP stack if (string.Compare(hostNameOrAddress, "localhost", true) == 0) return new IPEndPoint(stack == IPStack.IPv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback, port); // Failed to parse an IP address for the deisred stack - this may simply be that a host name was provided // so we attempt a DNS lookup. Note that exceptions will occur if DNS lookup fails. IPAddress[] dnsAddressList = Dns.GetHostEntry(hostNameOrAddress).AddressList; if (dnsAddressList.Length > 0) { // Traverse address list looking for first match on desired IP stack foreach (IPAddress dnsAddress in dnsAddressList) { if ((stack == IPStack.IPv6 && dnsAddress.AddressFamily == AddressFamily.InterNetworkV6) || (stack == IPStack.IPv4 && dnsAddress.AddressFamily == AddressFamily.InterNetwork)) return new IPEndPoint(dnsAddress, port); } // If no available matching address was found for desired IP stack, this is an IP stack mismatch ipStackMismatch = true; } throw new InvalidOperationException(string.Format("No valid {0} addresses could be found for \"{1}\"", stack, hostNameOrAddress)); } catch { // Spell out a specific error message for ip stack mismatches if (ipStackMismatch) throw new InvalidOperationException(string.Format("IP address mismatch: unable to find an {0} address for \"{1}\"", stack, hostNameOrAddress)); // Otherwise report original exception throw; } } }
/// <summary> /// Creates a <see cref="Socket"/> for the specified <paramref name="port"/> and <paramref name="protocol"/>. /// </summary> /// <param name="address">The local address where the <see cref="Socket"/> will be bound.</param> /// <param name="port">The port number at which the <see cref="Socket"/> will be bound.</param> /// <param name="protocol">One of the <see cref="ProtocolType"/> values.</param> /// <param name="stack">Desired IP stack to use.</param> /// <param name="allowDualStackSocket">Determines if dual-mode socket is allowed when endpoint address is IPv6.</param> /// <returns>An <see cref="Socket"/> object.</returns> public static Socket CreateSocket(string address, int port, ProtocolType protocol, IPStack stack, bool allowDualStackSocket = true) { Socket socket = null; IPEndPoint endpoint = null; switch (protocol) { case ProtocolType.Tcp: endpoint = CreateEndPoint(address, port, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); // If allowDualModeSocket is true and the enpoint is IPv6, we setup a dual-mode socket // by setting the IPv6Only socket option to false if (allowDualStackSocket && endpoint.AddressFamily == AddressFamily.InterNetworkV6 && Environment.OSVersion.Version.Major > 5) socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); // Associate the socket with the local endpoint socket.Bind(endpoint); break; case ProtocolType.Udp: // Allow negative port number to be specified for unbound socket. if (port >= 0) { endpoint = CreateEndPoint(address, port, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); // If allowDualModeSocket is true and the endpoint is IPv6, we setup a dual-mode socket // by setting the IPv6Only socket option to false if (allowDualStackSocket && endpoint.AddressFamily == AddressFamily.InterNetworkV6 && Environment.OSVersion.Version.Major > 5) socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); socket.Bind(endpoint); } else { // Create a socket with no binding when -1 is used for port number endpoint = CreateEndPoint(address, 0, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); } break; default: throw new NotSupportedException("Communications library does not support socket creation for protocol " + protocol); } return socket; }
/// <summary> /// Validates the specified <paramref name="configurationString"/>. /// </summary> /// <param name="configurationString">Configuration string to be validated.</param> /// <exception cref="ArgumentException">Port property is missing.</exception> /// <exception cref="ArgumentOutOfRangeException">Port property value is not between <see cref="Transport.PortRangeLow"/> and <see cref="Transport.PortRangeHigh"/>.</exception> protected override void ValidateConfigurationString(string configurationString) { m_configData = configurationString.ParseKeyValuePairs(); // Derive desired IP stack based on specified "interface" setting, adding setting if it's not defined m_ipStack = Transport.GetInterfaceIPStack(m_configData); if (!m_configData.ContainsKey("port")) throw new ArgumentException($"Port property is missing (Example: {DefaultConfigurationString})"); if (!Transport.IsPortNumberValid(m_configData["port"])) throw new ArgumentOutOfRangeException(nameof(configurationString), $"Port number must be between {Transport.PortRangeLow} and {Transport.PortRangeHigh}"); }
/// <summary> /// Creates a <see cref="Socket"/> for the specified <paramref name="port"/> and <paramref name="protocol"/>. /// </summary> /// <param name="address">The local address where the <see cref="Socket"/> will be bound.</param> /// <param name="port">The port number at which the <see cref="Socket"/> will be bound.</param> /// <param name="protocol">One of the <see cref="ProtocolType"/> values.</param> /// <param name="stack">Desired IP stack to use.</param> /// <param name="allowDualStackSocket">Determines if dual-mode socket is allowed when endpoint address is IPv6.</param> /// <returns>An <see cref="Socket"/> object.</returns> public static Socket CreateSocket(string address, int port, ProtocolType protocol, IPStack stack, bool allowDualStackSocket = true) { Socket socket; IPEndPoint endpoint; switch (protocol) { case ProtocolType.Tcp: endpoint = CreateEndPoint(address, port, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); #if !MONO // If allowDualModeSocket is true and the endpoint is IPv6, we setup a dual-mode socket // by setting the IPv6Only socket option to false if (allowDualStackSocket && endpoint.AddressFamily == AddressFamily.InterNetworkV6 && Environment.OSVersion.Version.Major > 5) { socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); } #endif // Associate the socket with the local endpoint socket.Bind(endpoint); break; case ProtocolType.Udp: // Allow negative port number to be specified for unbound socket. if (port >= 0) { endpoint = CreateEndPoint(address, port, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); #if !MONO // If allowDualModeSocket is true and the endpoint is IPv6, we setup a dual-mode socket // by setting the IPv6Only socket option to false if (allowDualStackSocket && endpoint.AddressFamily == AddressFamily.InterNetworkV6 && Environment.OSVersion.Version.Major > 5) { socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); } #endif socket.Bind(endpoint); } else { // Create a socket with no binding when -1 is used for port number endpoint = CreateEndPoint(address, 0, stack); socket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); } break; default: throw new NotSupportedException($"Communications library does not support socket creation for protocol {protocol}"); } return(socket); }
/// <summary> /// Creates an <see cref="IPEndPoint"/> for the specified host name and port number. /// </summary> /// <param name="hostNameOrAddress">The host name or IP address to resolve.</param> /// <param name="port">The port number to be associated with the address.</param> /// <param name="stack">Desired IP stack to use.</param> /// <returns>An <see cref="IPEndPoint"/> object.</returns> public static IPEndPoint CreateEndPoint(string hostNameOrAddress, int port, IPStack stack) { // Determine system's default IP stack if the default stack was requested if (stack == IPStack.Default) { stack = GetDefaultIPStack(); } // Make sure system can support specified stack if (stack == IPStack.IPv6 && !Socket.OSSupportsIPv6) { throw new NotSupportedException($"IPv6 stack is not available for socket creation on {hostNameOrAddress.ToNonNullNorWhiteSpace("localhost")}:{port}"); } #if !MONO if (stack == IPStack.IPv4 && !Socket.OSSupportsIPv4) { throw new NotSupportedException($"IPv4 stack is not available for socket creation on {hostNameOrAddress.ToNonNullNorWhiteSpace("localhost")}:{port}"); } #endif // No host name or IP address was specified, use local IPs if (string.IsNullOrWhiteSpace(hostNameOrAddress)) { return(stack == IPStack.IPv6 ? new IPEndPoint(IPAddress.IPv6Any, port) : new IPEndPoint(IPAddress.Any, port)); } bool ipStackMismatch = false; // Attempt to parse provided address name as a literal IP address if (IPAddress.TryParse(hostNameOrAddress, out IPAddress address)) { // As long as desired IP stack matches format of specified IP address, return end point for address if (stack == IPStack.IPv6 && address.AddressFamily == AddressFamily.InterNetworkV6 || stack == IPStack.IPv4 && address.AddressFamily == AddressFamily.InterNetwork) { return(new IPEndPoint(address, port)); } // User specified an IP address that is mismatch with the desired IP stack. If the DNS server // responds to this IP, we can attempt to see if an IP is defined for the desired IP stack, // otherwise this is an exception ipStackMismatch = true; } try { // Handle "localhost" as a special case, returning proper loopback address for the desired IP stack if (string.Compare(hostNameOrAddress, "localhost", StringComparison.OrdinalIgnoreCase) == 0) { return(new IPEndPoint(stack == IPStack.IPv6 ? IPAddress.IPv6Loopback : IPAddress.Loopback, port)); } // Failed to parse an IP address for the desired stack - this may simply be that a host name was provided // so we attempt a DNS lookup. Note that exceptions will occur if DNS lookup fails. IPAddress[] dnsAddressList = Dns.GetHostEntry(hostNameOrAddress).AddressList; if (dnsAddressList.Length > 0) { // Traverse address list looking for first match on desired IP stack foreach (IPAddress dnsAddress in dnsAddressList) { if (stack == IPStack.IPv6 && dnsAddress.AddressFamily == AddressFamily.InterNetworkV6 || stack == IPStack.IPv4 && dnsAddress.AddressFamily == AddressFamily.InterNetwork) { return(new IPEndPoint(dnsAddress, port)); } } // If no available matching address was found for desired IP stack, this is an IP stack mismatch ipStackMismatch = true; } throw new InvalidOperationException($"No valid {stack} addresses could be found for \"{hostNameOrAddress}\""); } catch { // Spell out a specific error message for IP stack mismatches if (ipStackMismatch) { throw new InvalidOperationException($"IP address mismatch: unable to find an {stack} address for \"{hostNameOrAddress}\""); } // Otherwise report original exception throw; } }
public static Socket CreateSocket(string address, int port, ProtocolType protocol, IPStack stack) { return(CreateSocket(address, port, protocol, stack, true)); }
/// <summary> /// Validates the specified <paramref name="configurationString"/>. /// </summary> /// <param name="configurationString">Configuration string to be validated.</param> /// <exception cref="ArgumentException">Port property is missing.</exception> /// <exception cref="ArgumentOutOfRangeException">Port property value is not between <see cref="Transport.PortRangeLow"/> and <see cref="Transport.PortRangeHigh"/>.</exception> protected override void ValidateConfigurationString(string configurationString) { string setting; int value; m_configData = configurationString.ParseKeyValuePairs(); // Derive desired IP stack based on specified "interface" setting, adding setting if it's not defined m_ipStack = Transport.GetInterfaceIPStack(m_configData); if (!m_configData.ContainsKey("port")) throw new ArgumentException(string.Format("Port is missing (Example: {0})", DefaultConfigurationString)); if (!Transport.IsPortNumberValid(m_configData["port"]) && int.Parse(m_configData["port"]) != -1) throw new ArgumentOutOfRangeException("configurationString", string.Format("Port number must be {0} or between {1} and {2}", -1, Transport.PortRangeLow, Transport.PortRangeHigh)); if (!m_configData.ContainsKey("multicastTimeToLive")) m_configData.Add("multicastTimeToLive", "10"); // Make sure a valid multi-cast time-to-live value is defined in the configuration data if (!(m_configData.TryGetValue("multicastTimeToLive", out setting) && int.TryParse(setting, out value))) m_configData["multicastTimeToLive"] = "10"; }