using System; using System.Runtime.InteropServices; using Vanara.Extensions; using Vanara.InteropServices; using static Vanara.PInvoke.Ws2_32; #pragma warning disable IDE1006 // Naming Styles namespace Vanara.PInvoke; public static partial class Ws2_32 { /// public const int IN6ADDR_LINKLOCALPREFIX_LENGTH = 64; /// public const int IN6ADDR_MULTICASTPREFIX_LENGTH = 8; /// public const int IN6ADDR_SOLICITEDNODEMULTICASTPREFIX_LENGTH = 104; /// public const int IN6ADDR_V4MAPPEDPREFIX_LENGTH = 96; /// public const int IN6ADDR_6TO4PREFIX_LENGTH = 16; /// public const int IN6ADDR_TEREDOPREFIX_LENGTH = 32; /// public const int INET_ADDRSTRLEN = 22; /// public const int INET6_ADDRSTRLEN = 65; /// public const uint IOC_IN = 0x80000000; /// public const uint IOC_INOUT = IOC_IN | IOC_OUT; /// public const uint IOC_OUT = 0x40000000; /// public const uint IOC_VOID = 0x20000000; /// public const uint IOCPARM_MASK = 0x7f; /// /// It is used to return information about each interface on the local machine. Nothing is required on input, but on output, an array of INTERFACE_INFO structures is returned. /// public static readonly uint SIO_GET_INTERFACE_LIST = _IOR('t', 127, typeof(uint)); /// /// This ioctl is the same as SIO_GET_INTERFACE_LIST except the structure returned contains embedded SOCKET_ADDRESS structure to describe each local interface, as opposed to SOCKADDR_GEN structure. This removes the dependency the size of the socket address structure. /// public static readonly uint SIO_GET_INTERFACE_LIST_EX = _IOR('t', 126, typeof(uint)); /// /// This ioctl retrieves the multicast filter set on a given socket. The multicast state is set with the SIO_SET_MULTICAST_FILTER ioctl. This ioctl requires an IGMPv3-enabled network and is supported in only Windows XP. /// public static readonly uint SIO_GET_MULTICAST_FILTER = _IOW('t', 124 | IOC_IN, typeof(uint)); /// /// This ioctl sets the multicast state. The input parameter is a struct ip_msfilter. /// public static readonly uint SIO_SET_MULTICAST_FILTER = _IOW('t', 125, typeof(uint)); /// /// This ioctl retrieves the multicast filter set on a given socket. The multicast state is set with the SIO_SET_MULTICAST_FILTER ioctl. This ioctl requires an IGMPv3-enabled network and is supported in only Windows XP. /// public static readonly uint SIOCGIPMSFILTER = SIO_GET_MULTICAST_FILTER; /// /// Enables an application to retrieve a list of the IPv4 or IPv6 source addresses that comprise the source filter along with the current mode on a given interface index and a multicast group for a socket. The source filter may either include or exclude the set of source address, depending on the filter mode (MCAST_INCLUDE or MCAST_EXCLUDE), which is defined in the GROUP_FILTER structure. /// public static readonly uint SIOCGMSFILTER = _IOW('t', 127 | IOC_IN, typeof(uint)); /// /// This ioctl sets the multicast state. The input parameter is a struct ip_msfilter. /// public static readonly uint SIOCSIPMSFILTER = SIO_SET_MULTICAST_FILTER; /// /// Enables an application to specify or modify a list of IPv4 or IPv6 source addresses on a given interface index and to specify or modify a multicast group for a socket. The source filter can include or exclude the set of source address, depending on the filter mode (MCAST_INCLUDE or MCAST_EXCLUDE), which is defined in the GROUP_FILTER structure of the BPXYIOCC macro. The application can join multiple source multicast groups on a single socket; it also can join the same group on multiple interfaces on the same socket. However, there is a maximum limit of 20 groups per single UDP socket and there is a maximum limit of 256 groups per single RAW socket. /// public static readonly uint SIOCSMSFILTER = _IOW('t', 126, typeof(uint)); /// IPPROTO_IPV6 socket options [PInvokeData("ws2ipdef.h")] public enum IPV6 { /// Adds an interface index to the IFLIST associated with the IP_IFLIST option. [CorrespondingType(typeof(uint))] IPV6_ADD_IFLIST = 29, /// /// Join the socket to the supplied multicast group on the specified interface. This option is only valid on datagram and raw sockets /// (the socket type must be SOCK_DGRAM or SOCK_RAW). /// //[CorrespondingType(typeof(ipv6_mreq))] IPV6_ADD_MEMBERSHIP = 12, /// IPV6_CHECKSUM = 26, /// /// Removes an interface index from the IFLIST associated with the IP_IFLIST option. Entries can be removed only by the application, /// so be aware that entries might go stale once an interface is removed. /// [CorrespondingType(typeof(uint))] IPV6_DEL_IFLIST = 30, /// Don't fragment IP datagrams. IPV6_DONTFRAG = 14, /// /// Leave the supplied multicast group from the given interface. This option is only valid on datagram and raw sockets (the socket /// type must be SOCK_DGRAM or SOCK_RAW). /// //[CorrespondingType(typeof(ipv6_mreq))] IPV6_DROP_MEMBERSHIP = 13, /// IPV6_ECN = 50, /// Gets the current IFLIST associated with the IP_IFLIST option. Returns error if IP_IFLIST is not enabled. [CorrespondingType(typeof(uint[]))] IPV6_GET_IFLIST = 33, /// /// Indicates the application provides the IPv6 header on all outgoing data. If the optval parameter is set to 1 on the call to /// setsockopt, the option is enabled. If optval is set to 0, the option is disabled. The default value is disabled. This option is /// only valid for datagram and raw sockets (the socket type must be SOCK_DGRAM or SOCK_RAW). A TCP/IP service provider that supports /// SOCK_RAW should also support IPV6_HDRINCL. /// [CorrespondingType(typeof(BOOL))] IPV6_HDRINCL = 2, /// /// Indicates that hop (TTL) information should be returned in the LPFN_WSARECVMSG (WSARecvMsg) function. If optval is set to 1 on /// the call to setsockopt, the option is enabled. If set to 0, the option is disabled. This option is only valid for datagram and /// raw sockets (the socket type must be SOCK_DGRAM or SOCK_RAW). /// [CorrespondingType(typeof(BOOL))] IPV6_HOPLIMIT = 21, /// IPV6_HOPOPTS = 1, /// /// Gets or sets the IP_IFLIST state of the socket. When this option is set to true, Datagram reception is restricted to interfaces /// that are in the IFLIST. Datagrams received on any other interfaces are ignored. IFLIST starts empty. Use IP_ADD_IFLIST and /// IP_DEL_IFLIST to edit the IFLIST. /// [CorrespondingType(typeof(BOOL))] IPV6_IFLIST = 28, /// Same as IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP = IPV6_ADD_MEMBERSHIP, /// Same as IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP = IPV6_DROP_MEMBERSHIP, /// Gets the system's estimate of the path MTU. Socket must be connected. [CorrespondingType(typeof(uint))] IPV6_MTU = 72, /// /// Gets or sets the path MTU discovery state for the socket. The default value is IP_PMTUDISC_NOT_SET. For stream sockets, /// IP_PMTUDISC_NOT_SET and IP_PMTUDISC_DO will perform path MTU discovery. IP_PMTUDISC_DONT and IP_PMTUDISC_PROBE will turn off path /// MTU discovery. For datagram sockets, if set to IP_PMTUDISC_DO , attempts to send packets larger than path MTU will result in an /// error. If set to IP_PMTUDISC_DONT, packets will be fragmented according to interface MTU. If set to IP_PMTUDISC_PROBE, attempts /// to send packets larger than interface MTU will result in an error. /// [CorrespondingType(typeof(uint))] IPV6_MTU_DISCOVER = 71, /// /// Gets or sets the TTL value associated with IPv6 multicast traffic on the socket. It is illegal to set the TTL to a value greater /// than 255. This option is only valid for datagram and raw sockets (the socket type must be SOCK_DGRAM or SOCK_RAW). /// [CorrespondingType(typeof(uint))] IPV6_MULTICAST_HOPS = 10, /// /// Gets or sets the outgoing interface for sending IPv6 multicast traffic. This option does not change the default interface for /// receiving IPv6 multicast traffic. This option is important for multihomed computers. The input value for setting this option is a /// 4-byte interface index of the desired outgoing interface in host byte order. The GetAdaptersAddresses function can be used to /// obtain the interface index information. If optval is set to NULL on call to setsockopt, the default IPv6 interface is used. If /// optval is zero , the default interface for receiving multicast is specified for sending multicast traffic. When getting this /// option, the optval returns the current default interface index for sending multicast IPv6 traffic in host byte order. /// [CorrespondingType(typeof(uint))] IPV6_MULTICAST_IF = 9, /// /// Indicates multicast data sent on the socket will be echoed to the sockets receive buffer if it is also joined on the destination /// multicast group. If optval is set to 1 on the call to setsockopt, the option is enabled. If set to 0, the option is disabled. /// This option is only valid for datagram and raw sockets (the socket type must be SOCK_DGRAM or SOCK_RAW). /// [CorrespondingType(typeof(BOOL))] IPV6_MULTICAST_LOOP = 11, /// IPV6_NRT_INTERFACE = 74, /// Indicates that packet information should be returned by the LPFN_WSARECVMSG (WSARecvMsg) function. [CorrespondingType(typeof(BOOL))] IPV6_PKTINFO = 19, /// IPV6_PKTINFO_EX = 51, /// /// Enables restriction of a socket to a specified scope, such as addresses with the same link local or site local prefix. Provides /// various restriction levels and default settings. See IPV6_PROTECTION_LEVEL for more information. /// [CorrespondingType(typeof(int))] IPV6_PROTECTION_LEVEL = 23, /// IPV6_RECVDSTADDR = 25, /// IPV6_RECVECN = 50, /// IPV6_RECVERR = 75, /// /// Indicates whether the IP stack should populate the control buffer with details about which interface received a packet with a /// datagram socket. When this value is true, the LPFN_WSARECVMSG (WSARecvMsg) function will return optional control data containing /// the interface where the packet was received for datagram sockets. This option allows the IPv6 interface where the packet was /// received to be returned in the WSAMSG structure. This option is only valid for datagram and raw sockets (the socket type must be /// SOCK_DGRAM or SOCK_RAW). /// [CorrespondingType(typeof(BOOL))] IPV6_RECVIF = 24, /// IPV6_RECVRTHDR = 38, /// /// Indicates whether the IP stack should populate the control buffer with a message containing the Traffic Class IPv6 header field /// on a received datagram. When this value is true, the LPFN_WSARECVMSG (WSARecvMsg) function will return optional control data /// containing the Traffic Class IPv6 header field value of the received datagram. This option allows the Traffic Class IPv6 header /// field of the received datagram to be returned in the WSAMSG structure. The returned message type will be IPV6_TCLASS. All DSCP /// and ECN bits of the Traffic Class field will be returned. This option is only valid on datagram sockets (the socket type must be SOCK_DGRAM). /// [CorrespondingType(typeof(BOOL))] IPV6_RECVTCLASS = 40, /// IPV6_RTHDR = 32, /// IPV6_TCLASS = 39, /// /// Gets or sets the current TTL value associated with IPv6 socket for unicast traffic. It is illegal to set the TTL to a value /// greater than 255. /// [CorrespondingType(typeof(uint))] IPV6_UNICAST_HOPS = 4, /// /// Gets or sets the outgoing interface for sending IPv6 traffic. This option does not change the default interface for receiving /// IPv6 traffic. This option is important for multihomed computers. The input value for setting this option is a 4-byte interface /// index of the desired outgoing interface in host byte order. The GetAdaptersAddresses function can be used to obtain the interface /// index information. If optval is zero, the default interface for sending IPv6 traffic is set to unspecified. When getting this /// option, the optval returns the current default interface index for sending IPv6 traffic in host byte order. /// [CorrespondingType(typeof(uint))] IPV6_UNICAST_IF = 31, /// /// Gets or sets an upper bound on the IP layer MTU (in bytes) for the given socket. If the value is higher than the system's /// estimate of the path MTU (which you can retrieve on a connected socket by querying the IPV6_MTU socket option), then the option /// has no effect. If the value is lower, then outbound packets larger than this will be fragmented, or will fail to send, depending /// on the value of IPV6_DONTFRAG. Default value is IP_UNSPECIFIED_USER_MTU (MAXULONG). For type-safety, you should use the /// WSAGetIPUserMtu and WSASetIPUserMtu functions instead of using the socket option directly. /// [CorrespondingType(typeof(uint))] IPV6_USER_MTU = 76, /// /// Indicates if a socket created for the AF_INET6 address family is restricted to IPv6 communications only. Sockets created for the /// AF_INET6 address family may be used for both IPv6 and IPv4 communications. Some applications may want to restrict their use of a /// socket created for the AF_INET6 address family to IPv6 communications only. When this value is nonzero (the default on Windows), /// a socket created for the AF_INET6 address family can be used to send and receive IPv6 packets only. When this value is zero, a /// socket created for the AF_INET6 address family can be used to send and receive packets to and from an IPv6 address or an IPv4 /// address. Note that the ability to interact with an IPv4 address requires the use of IPv4 mapped addresses. This socket option is /// supported on Windows Vista or later. /// [CorrespondingType(typeof(BOOL))] IPV6_V6ONLY = 27, /// IPV6_WFP_REDIRECT_CONTEXT = 70, /// IPV6_WFP_REDIRECT_RECORDS = 60, } /// The MULTICAST_MODE_TYPE enumeration specifies the filter mode for multicast group addresses. /// /// This enumeration is supported on Windows Vistaand later. /// /// The MULTICAST_MODE_TYPE enumeration is used in the gf_fmode member of the GROUP_SOURCE_REQ structure to determine if a /// list of IP addresses should included or excluded. The values from this enumeration can also be used in the imsf_fmode member /// of the ip_msfilter structure. /// /// /// The MULTICAST_MODE_TYPE enumeration is defined in the Ws2ipdef.h header file which is automatically included in the Ws2tcpip.h /// header file. The Ws2ipdef.h header files should never be used directly. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ne-ws2ipdef-multicast_mode_type typedef enum { MCAST_INCLUDE, // MCAST_EXCLUDE } MULTICAST_MODE_TYPE; [PInvokeData("ws2ipdef.h", MSDNShortId = "7ca9cb9b-618a-4e73-9e2a-18e55e5c00c0")] public enum MULTICAST_MODE_TYPE { /// The filter contains a list of IP addresses to include. MCAST_INCLUDE, /// The filter contains a list of IP addresses to exclude. MCAST_EXCLUDE, } /// /// Used for an ioctl that reads data from the device driver. The driver will be allowed to return sizeof(data_type) bytes to the user. /// /// The type. /// The value. /// The type from which a size is extracted. /// IOCtrl value. public static uint _IOR(char x, uint y, Type t) => IOC_OUT | (((uint)Marshal.SizeOf(t) & IOCPARM_MASK) << 16) | ((uint)x) << 8 | (y); /// Used for an ioctl that writes data to the device driver. /// The type. /// The value. /// The type from which a size is extracted. /// IOCtrl value. public static uint _IOW(char x, uint y, Type t) => IOC_IN | (((uint)Marshal.SizeOf(t) & IOCPARM_MASK) << 16) | ((uint)x) << 8 | (y); /// Gets the size, in bytes, of an GROUP_FILTER with a number of SOCKADDR_STORAGE items. /// The number of SOCKADDR_STORAGE sources. /// Size, in bytes. [PInvokeData("ws2ipdef.h")] public static int GROUP_FILTER_SIZE(int numsrc) => Marshal.SizeOf(typeof(GROUP_FILTER)) - Marshal.SizeOf(typeof(SOCKADDR_STORAGE)) + numsrc * Marshal.SizeOf(typeof(SOCKADDR_STORAGE)); /// Gets the size, in bytes, of an IP_MSFILTER with a number of IN_ADDR items. /// The number of IN_ADDR sources. /// Size, in bytes. [PInvokeData("ws2ipdef.h")] public static int IP_MSFILTER_SIZE(int NumSources) => Marshal.SizeOf(typeof(IP_MSFILTER)) - Marshal.SizeOf(typeof(IN_ADDR)) + NumSources * Marshal.SizeOf(typeof(IN_ADDR)); /// The GROUP_FILTER structure provides multicast filtering parameters for multicast IPv6 or IPv4 addresses. /// /// /// The GROUP_FILTER structure is used with either IPv6 or IPv4 multicast addresses. The GROUP_FILTER structure is /// passed as an argument for the SIOCGMSFILTER and SIOCSMSFILTER IOCTLs. /// /// /// The GROUP_FILTER structure and related structures used for multicast programming are based on IETF recommendations in /// sections 5 and 8.2 of RFC 3768. For more information, see http://www.ietf.org/rfc/rfc3678.txt. /// /// /// On Windows Vista and later, a set of socket options are available for multicast programming that support IPv6 and IPv4 /// addresses. These socket options are IP agnostic and can be used on both IPv6 and IPv4. These IP agnostic options use the /// GROUP_REQ and the GROUP_SOURCE_REQ structures and are the preferred socket options for multicast programming on Windows Vista /// and later. /// /// The GetAdaptersAddresses function can be used to obtain interface index information required for the gf_interface member. /// /// The GROUP_FILTER structure and the Ioctls that use this structure are only valid on datagram and raw sockets (the socket /// type must be SOCK_DGRAM or SOCK_RAW). /// /// /// The GROUP_FILTER structure is defined in the Ws2ipdef.h header file which is automatically included in the Ws2tcpip.h /// header file. The Ws2ipdef.h header files should never be used directly. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-group_filter typedef struct group_filter { ULONG // gf_interface; SOCKADDR_STORAGE gf_group; MULTICAST_MODE_TYPE gf_fmode; ULONG gf_numsrc; SOCKADDR_STORAGE gf_slist[1]; } // GROUP_FILTER, *PGROUP_FILTER; [PInvokeData("ws2ipdef.h", MSDNShortId = "09aa1f67-c858-4bef-9a98-ce25ebcc1d4e")] [VanaraMarshaler(typeof(SafeAnysizeStructMarshaler), "gf_numsrc")] [StructLayout(LayoutKind.Sequential)] public struct GROUP_FILTER { /// The interface index of the local interface for the multicast group to filter. public uint gf_interface; /// The multicast address group that should be filtered. This may be either an IPv6 or IPv4 multicast address. public SOCKADDR_STORAGE gf_group; /// /// The multicast filter mode. /// /// This member can be one of the values from the MULTICAST_MODE_TYPE enumeration type defined in the Ws2ipdef.h header file. /// This member determines if the list of IP addresses in the gf_numsrc member should be included or excluded. /// /// /// /// Value /// Meaning /// /// /// MCAST_INCLUDE /// The filter contains a list of IP addresses to include. /// /// /// MCAST_EXCLUDE /// The filter contains a list of IP addresses to exclude. /// /// /// public MULTICAST_MODE_TYPE gf_fmode; /// The number of multicast filter source address entries in the gf_slist member. public uint gf_numsrc; /// /// An array of SOCKADDR_STORAGE structures specifying the multicast source addresses to include or exclude. These IP addresses /// may be either IPv6 or IPv4 addresses, but they must be the same address family (IPv6 or IPv4) as the address specified in /// the gf_group member.. /// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public SOCKADDR_STORAGE[] gf_slist; } /// /// The in_pktinfo structure is used to store received packet address information, and is used by Windows to return /// information about received packets and also allows specifying the local IPv4 address to use for sending packets. /// /// /// /// If the IP_PKTINFO socket option is set on a socket of type SOCK_DGRAM or SOCK_RAW, one of the control data objects /// returned by the LPFN_WSARECVMSG (WSARecvMsg) function will contain an in_pktinfo structure used to store received packet /// address information. /// /// /// On an IPv4 socket of type SOCK_DGRAM or SOCK_RAW, an application can specific the local IP address to use for /// sending with the WSASendMsg function. One of the control data objects passed in the WSAMSG structure to the WSASendMsg /// function may contain an in_pktinfo structure used to specify the local IPv4 address to use for sending. /// /// /// On the Microsoft Windows Software Development Kit (SDK) released for Windows Vista and later, the organization of header files /// has changed and the in_pktinfo structure is defined in the Ws2ipdef.h header file which is automatically included /// in the Ws2tcpip.h header file. The Ws2ipdef.h header files should never be used directly. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-in_pktinfo typedef struct in_pktinfo { IN_ADDR ipi_addr; // ULONG ipi_ifindex; } IN_PKTINFO, *PIN_PKTINFO; [PInvokeData("ws2ipdef.h", MSDNShortId = "NS:ws2ipdef.in_pktinfo")] [StructLayout(LayoutKind.Sequential)] public struct IN_PKTINFO { /// /// The destination IPv4 address from the IP header of the received packet when used with the LPFN_WSARECVMSG (WSARecvMsg) /// function. The local source IPv4 address to set in the IP header when used with the WSASendMsg function. /// public IN_ADDR ipi_addr; /// /// The interface on which the packet was received when used with the LPFN_WSARECVMSG (WSARecvMsg) function. The interface on /// which the packet should be sent when used with the WSASendMsg function. /// public uint ipi_ifindex; } /// /// The in6_pktinfo structure is used to store received IPv6 packet address information, and is used by Windows to return /// information about received packets and also allows specifying the local IPv6 address to use for sending packets. /// /// /// /// If the IPV6_PKTINFO socket option is set on a socket of type SOCK_DGRAM or SOCK_RAW, one of the control data /// objects returned by the LPFN_WSARECVMSG (WSARecvMsg) function will contain an in6_pktinfo structure used to store received /// packet address information. /// /// /// On an IPv6 socket of type SOCK_DGRAM or SOCK_RAW, an application can specific the local IP source address to use /// for sending with the WSASendMsg function. One of the control data objects passed in the WSAMSG structure to the WSASendMsg /// function may contain an in6_pktinfo structure used to specify the local IPv6 address to use for sending. /// /// /// On the Microsoft Windows Software Development Kit (SDK) released for Windows Vista and later, the organization of header files /// has changed and the in6_pktinfo structure is defined in the Ws2ipdef.h header file which is automatically included /// in the Ws2tcpip.h header file. The Ws2ipdef.h header files should never be used directly. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-in6_pktinfo typedef struct in6_pktinfo { IN6_ADDR // ipi6_addr; ULONG ipi6_ifindex; } IN6_PKTINFO, *PIN6_PKTINFO; [PInvokeData("ws2ipdef.h", MSDNShortId = "NS:ws2ipdef.in6_pktinfo")] [StructLayout(LayoutKind.Sequential)] public struct IN6_PKTINFO { /// /// The destination IPv6 address from the IP header of the received packet when used with the LPFN_WSARECVMSG (WSARecvMsg) /// function. The local source IPv6 address to set in the IP header when used with the WSASendMsg function. /// public IN6_ADDR ipi6_addr; /// /// The interface on which the packet was received when used with the LPFN_WSARECVMSG (WSARecvMsg) function. The interface on /// which the packet should be sent when used with the WSASendMsg function. /// public uint ipi6_ifindex; } /// The ip_mreq structure provides multicast group information for IPv4 addresses. /// /// /// The ip_mreq structure is used with IPv4 addresses. The ip_mreq structure is used with the IP_ADD_MEMBERSHIP and /// IP_DROP_MEMBERSHIP socket options. /// /// /// The ip_mreq structure and related structures used for IPv4 multicast programming are based on IETF recommendations in /// sections 4 and 8.1 of RFC 3768. For more information, see http://www.ietf.org/rfc/rfc3678.txt. /// /// /// For more configurable multicast capabilities with IPv4, use the ip_mreq_source structure. See Multicast Programming for more information. /// /// /// On Windows Vista and later, a set of socket options are available for multicast programming that support IPv6 and IPv4 addresses. /// These socket options are IP agnostic and can be used on both IPv6 and IPv4. These IP agnostic options use the GROUP_REQ and the /// GROUP_SOURCE_REQ structures and are the preferred socket options for multicast programming on Windows Vista and later. /// /// The ip_mreq structure is the IPv4 equivalent of the IPv6-based ipv6_mreq structure. /// /// The imr_interface member can be an interface index. Any IP address in the 0.x.x.x block (first octet of 0) except for the /// IP address of 0.0.0.0 is treated as an interface index. An interface index is a 24-bit number. The 0.0.0.0/8 IPv4 address block /// is not used (this range is reserved). The GetAdaptersAddresses function can be used to obtain interface index information to use /// for the imr_interface member. /// /// /// It is recommended that a local IPv4 address or interface index always be specified in the imr_interface member of the /// ip_mreq structure, rather than use the default interface. This is particularly important on computers with multiple /// network interfaces and multiple public IPv4 addresses. /// /// /// The default interface used for IPv4 multicast is determined by the networking stack in Windows. An application can determine the /// default interface used for IPv4 multicast using the GetIpForwardTable function to retrieve the IPv4 routing table. The network /// interface with the lowest value for the routing metric for a destination IP address of 224.0.0.0 is the default interface for /// IPv4 multicast. The routing table can also be displayed from the command prompt with the following command: /// /// route print /// /// The IP_MULTICAST_IF socket option can be used to set the default interface to send IPv4 multicast packets. This socket option /// does not change the default interface used to receive IPv4 multicast packets. /// /// /// A typical IPv4 multicast application would use the IP_ADD_MEMBERSHIP socket option with the ip_mreq structure to join a /// multicast group and listen for multicast packets on a specific interface. The IP_MULTICAST_IF socket option would be used /// to set the interface to send IPv4 multicast packets to the multicast group. The most common scenario would be a multicast /// application that listens and sends on the same interface for a multicast group. Multiple sockets might be used by a multicast /// application with one socket for listening and one or more sockets for sending. /// /// /// On the Microsoft Windows Software Development Kit (SDK) released for Windows Vista and later, the organization of header files /// has changed and the ip_mreq structure is defined in the Ws2ipdef.h header file which is automatically included in /// the Ws2tcpip.h header file. The Ws2ipdef.h header files should never be used directly. /// /// /// Note The IP_MREQ and PIP_MREQ derived structures are only defined on the Windows SDK released with Windows /// Vista and later. The ip_mreq structure should be used on earlier versions of the Windows SDK. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_mreq typedef struct ip_mreq { IN_ADDR imr_multiaddr; // IN_ADDR imr_interface; } IP_MREQ, *PIP_MREQ; [PInvokeData("ws2ipdef.h", MSDNShortId = "NS:ws2ipdef.ip_mreq")] [StructLayout(LayoutKind.Sequential)] public struct IP_MREQ { /// The address of the IPv4 multicast group. public IN_ADDR imr_multiaddr; /// /// /// The local IPv4 address of the interface or the interface index on which the multicast group should be joined or dropped. This /// value is in network byte order. If this member specifies an IPv4 address of 0.0.0.0, the default IPv4 multicast interface is used. /// /// To use an interface index of 1 would be the same as an IP address of 0.0.0.1. /// public IN_ADDR imr_interface; } /// The ip_msfilter structure provides multicast filtering parameters for IPv4 addresses. /// /// /// The ip_msfilter structure is used with IPv4 addresses. The ip_msfilter structure is passed as an argument for the /// SIO_GET_MULTICAST_FILTER and SIO_SET_MULTICAST_FILTER IOCTLs. /// /// /// The ip_msfilter structure and related structures used for IPv4 multicast programming are based on IETF recommendations in /// sections 4 and 8.1 of RFC 3768. For more information, see http://www.ietf.org/rfc/rfc3678.txt. /// /// /// On Windows Vista and later, a set of socket options are available for multicast programming that support IPv6 and IPv4 /// addresses. These socket options are IP agnostic and can be used on both IPv6 and IPv4. These IP agnostic options use the /// GROUP_REQ and the GROUP_SOURCE_REQ structures and the SIOCSMSFILTER and SIOCGMSFILTER IOCTLs. These are the /// preferred socket options and IOCTLs for multicast programming on Windows Vista and later. /// /// /// The imsf_interface member can be an interface index. Any IPv4 address in the 0.x.x.x block (first octet of 0) except for /// the IPv4 address of 0.0.0.0 is treated as an interface index. An interface index is a 24-bit number. The 0.0.0.0/8 IPv4 address /// block is not used (this range is reserved). The GetAdaptersAddresses function can be used to obtain interface index information /// to use for the imsf_interface member. /// /// /// It is recommended that a local IPv4 address or interface index always be specified in the imsf_interface member of the /// ip_msfilter structure, rather than use the default interface. This is particularly important on computers with multiple /// network interfaces and multiple public IPv4 addresses. /// /// /// The default interface used for IPv4 multicast is determined by the networking stack in Windows. An application can determine the /// default interface used for IPv4 multicast using the GetIpForwardTable function to retrieve the IPv4 routing table. The network /// interface with the lowest value for the routing metric for a destination IP address of 224.0.0.0 is the default interface for /// IPv4 multicast. The routing table can also be displayed from the command prompt with the following command: /// /// route print /// /// The IP_MULTICAST_IF socket option can be used to set the default interface to send IPv4 multicast packets. This socket option /// does not change the default interface used to receive IPv4 multicast packets. /// /// /// A typical IPv4 multicast application would use the IP_ADD_MEMBERSHIP socket option with the ip_mreq structure or the /// IP_ADD_SOURCE_MEMBERSHIP socket option with the ip_mreq_source structure to join a multicast group and listen for /// multicast packets on a specific interface. The IP_MULTICAST_IF socket option would be used to set the interface to send /// IPv4 multicast packets to the multicast group. The most common scenario would be a multicast application that listens and sends /// on the same interface for a multicast group. Multiple sockets might be used by a multicast application with one socket for /// listening and one or more sockets for sending. /// /// /// On the Microsoft Windows Software Development Kit (SDK) released for Windows Vista and later, the organization of header files /// has changed and the ip_msfilter structure is defined in the Ws2ipdef.h header file which is automatically included in the /// Ws2tcpip.h header file. The Ws2ipdef.h header files should never be used directly. /// /// /// Note The IP_MSFILTER and PIP_MSFILTER derived structures are only defined on the Windows SDK released with /// Windows Vista and later. The ip_msfilter structure should be used on earlier versions of the Windows SDK. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ip_msfilter typedef struct ip_msfilter { IN_ADDR // imsf_multiaddr; IN_ADDR imsf_interface; MULTICAST_MODE_TYPE imsf_fmode; ULONG imsf_numsrc; IN_ADDR imsf_slist[1]; } IP_MSFILTER, *PIP_MSFILTER; [PInvokeData("ws2ipdef.h", MSDNShortId = "8d9d515e-9369-4d71-9614-6cbeb5557a5d")] [VanaraMarshaler(typeof(SafeAnysizeStructMarshaler), "imsf_numsrc")] [StructLayout(LayoutKind.Sequential)] public struct IP_MSFILTER { /// The IPv4 address of the multicast group. public IN_ADDR imsf_multiaddr; /// /// /// The local IPv4 address of the interface or the interface index on which the multicast group should be filtered. This value /// is in network byte order. If this member specifies an IPv4 address of 0.0.0.0, the default IPv4 multicast interface is used. /// /// To use an interface index of 1 would be the same as an IP address of 0.0.0.1. /// public IN_ADDR imsf_interface; /// /// /// The multicast filter mode to be used. This parameter can be either MCAST_INCLUDE (value of 0) to include particular /// multicast sources, or MCAST_EXCLUDE (value of 1) to exclude traffic from specified sources. /// /// On Windows Server 2003 and Windows XP, these values are defined in the Ws2tcpip.h header file. /// /// On Windows Vistaand later, these values are defined as enumeration values in the MULTICAST_MODE_TYPE enumeration defined in /// the Ws2ipdef.h header file. /// /// public MULTICAST_MODE_TYPE imsf_fmode; /// The number of sources in the imsf_slist member. public uint imsf_numsrc; /// An array of in_addr structures that specify the IPv4 multicast source addresses to include or exclude. [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] public IN_ADDR[] imsf_slist; } /// The ipv6_mreq structure provides multicast group information for IPv6 addresses. /// /// /// The ipv6_mreq structure is used with IPv6 addresses. The ipv6_mreq structure is used with the IPV6_ADD_MEMBERSHIP, /// IPV6_DROP_MEMBERSHIP, IPV6_JOIN_GROUP, and IPV6_LEAVE_GROUP socket options. The IPV6_JOIN_GROUP and /// IPV6_ADD_MEMBERSHIP socket options are defined to be the same. The IPV6_LEAVE_GROUP and IPV6_DROP_MEMBERSHIP /// socket options are defined to be the same. /// /// /// On Windows Vista and later, a set of socket options are available for multicast programming that support IPv6 and IPv4 addresses. /// These socket options are IP agnostic and can be used on both IPv6 and IPv4. These IP agnostic options use the GROUP_REQ and the /// GROUP_SOURCE_REQ structures and are the preferred socket options for multicast programming on Windows Vista and later. /// /// The ipv6_mreq structure is the IPv6 equivalent of the IPv4-based ip_mreq structure. /// /// The GetAdaptersAddresses function can be used to obtain interface index information required for the ipv6mr_interface member. /// /// /// The ipv6_mreq structure and the IPPROTO_IPV6 level socket options that use this structure are only valid on /// datagram and raw sockets (the socket type must be SOCK_DGRAM or SOCK_RAW). /// /// /// It is recommended that a local IPv6 interface index always be specified in the ipv6mr_interface member of the /// ipv6_mreq structure, rather than use the default interface. This is particularly important on computers with multiple /// network interfaces and multiple public IPv6 addresses. /// /// /// The default interface used for IPv6 multicast is determined by the networking stack in Windows. On Windows Vista and later, an /// application can determine the default interface used for IPv6 multicast using the GetIpForwardTable2 function to retrieve the /// IPv6 routing table. The network interface with the lowest value for the routing metric for a destination IPv6 multicast address /// (the FF00::/8 IPv6 address block) is the default interface for IPv6 multicast. The routing table can also be displayed from the /// command prompt with the following command: /// /// route print /// /// The IPV6_MULTICAST_IF socket option can be used to set the default interface to send IPv6 multicast packets. This socket option /// does not change the default interface used to receive IPv6 multicast packets. /// /// /// A typical IPv6 multicast application would use the IPV6_ADD_MEMBERSHIP or IPV6_JOIN_GROUP socket option with the /// ipv6_mreq structure to join a multicast group and listen for multicast packets on a specific interface. The /// IPV6_MULTICAST_IF socket option would be used to set the interface to send IPv6 multicast packets to the multicast group. /// The most common scenario would be a multicast application that listens and sends on the same interface for a multicast group. /// Multiple sockets might be used by a multicast application with one socket for listening and one or more sockets for sending. /// /// /// On the Microsoft Windows Software Development Kit (SDK) released for Windows Vista and later, the organization of header files /// has changed and the ipv6_mreq structure is defined in the Ws2ipdef.h header file which is automatically included in /// the Ws2tcpip.h header file. The Ws2ipdef.h header files should never be used directly. /// /// /// Note The PIP6_MREQ derived structure is only defined on the Windows SDK released with Windows Vista and later. The /// GROUP_REQ and the GROUP_SOURCE_REQ structures and are the preferred socket options for multicast programming on Windows Vista and later. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-ipv6_mreq typedef struct ipv6_mreq { IN6_ADDR // ipv6mr_multiaddr; ULONG ipv6mr_interface; } IPV6_MREQ, *PIPV6_MREQ; [PInvokeData("ws2ipdef.h", MSDNShortId = "NS:ws2ipdef.ipv6_mreq")] [StructLayout(LayoutKind.Sequential)] public struct IPV6_MREQ { /// The address of the IPv6 multicast group. public IN6_ADDR ipv6mr_multiaddr; /// /// The interface index of the local interface on which the multicast group should be joined or dropped. If this member specifies /// an interface index of 0, the default multicast interface is used. /// public uint ipv6mr_interface; } /// The SOCKADDR_IN6 structure specifies a transport address and port for the AF_INET6 address family. /// /// /// All of the data in the SOCKADDR_IN6 structure, except for the address family, must be specified in network-byte-order (big-endian). /// /// /// The size of the SOCKADDR_IN6 structure is too large to fit in the memory space that is provided by a SOCKADDR structure. For a /// structure that is guaranteed to be large enough to contain a transport address for all possible address families, see SOCKADDR_STORAGEa>. /// /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_in6_lh typedef struct sockaddr_in6 { // ADDRESS_FAMILY sin6_family; USHORT sin6_port; ULONG sin6_flowinfo; IN6_ADDR sin6_addr; union { ULONG sin6_scope_id; SCOPE_ID // sin6_scope_struct; }; } SOCKADDR_IN6_LH, *PSOCKADDR_IN6_LH, *LPSOCKADDR_IN6_LH; [PInvokeData("ws2ipdef.h", MSDNShortId = "ef2955d2-5dc1-420b-a9e0-32a584059d5a")] [StructLayout(LayoutKind.Sequential, Pack = 2)] public struct SOCKADDR_IN6 { /// The address family for the transport address. This member should always be set to AF_INET6. public ADDRESS_FAMILY sin6_family; /// A transport protocol port number. public ushort sin6_port; /// The IPv6 flow information. public uint sin6_flowinfo; /// An IN6_ADDR structure that contains an IPv6 transport address. public IN6_ADDR sin6_addr; /// A ULONG representation of the IPv6 scope identifier that is defined in the sin6_scope_struct member. public uint sin6_scope_id; /// Initializes a new instance of the struct. /// A byte array that contains an IPv6 transport address. /// /// A ULONG representation of the IPv6 scope identifier that is defined in the sin6_scope_struct member. /// /// A transport protocol port number. public SOCKADDR_IN6(byte[] addr, uint scope_id, ushort port = 0) : this(new IN6_ADDR(addr), scope_id, port) { } /// Initializes a new instance of the struct. /// An IN6_ADDR structure that contains an IPv6 transport address. /// /// A ULONG representation of the IPv6 scope identifier that is defined in the sin6_scope_struct member. /// /// A transport protocol port number. public SOCKADDR_IN6(IN6_ADDR addr, uint scope_id, ushort port = 0) { sin6_family = ADDRESS_FAMILY.AF_INET6; sin6_port = port; sin6_flowinfo = 0; sin6_addr = addr; sin6_scope_id = scope_id; } /// Initializes a new instance of the struct from a . /// The IPv4 socket address. public SOCKADDR_IN6(SOCKADDR_IN v4) : this(new IN6_ADDR(v4.sin_addr), 0, v4.sin_port) { } /// Performs an implicit conversion from to . /// The address. /// The resulting instance from the conversion. public static implicit operator SOCKADDR_IN6(IN6_ADDR addr) => new(addr, 0); /// Represents an IPv6 anycast socket address. public static readonly SOCKADDR_IN6 IN6ADDR_ANY = new() { sin6_family = ADDRESS_FAMILY.AF_INET6, sin6_addr = IN6_ADDR.Unspecified }; /// Represents an IPv6 LOOPBACK socket address. public static readonly SOCKADDR_IN6 IN6ADDR_LOOPBACK = new() { sin6_family = ADDRESS_FAMILY.AF_INET6, sin6_addr = IN6_ADDR.Loopback }; /// public override string ToString() => $"{sin6_addr}" + (sin6_scope_id == 0 ? "" : "%" + sin6_scope_id.ToString()) + $":{sin6_port}"; } /// /// The SOCKADDR_IN6_PAIR structure contains pointers to a pair of IP addresses that represent a source and destination /// address pair. /// /// /// The SOCKADDR_IN6_PAIR structure is defined on Windows Vista and later. /// /// Any IPv4 addresses in the SOCKADDR_IN6_PAIR structure must be represented in the IPv4-mapped IPv6 address format which /// enables an IPv6 only application to communicate with an IPv4 node. For more information on the IPv4-mapped IPv6 address format, /// see Dual-Stack Sockets. /// /// The SOCKADDR_IN6_PAIR structure is used by the CreateSortedAddressPairs function. /// Note that the Ws2ipdef.h header file is automatically included in Ws2tcpip.h header file, and should never be used directly. /// // https://docs.microsoft.com/en-us/windows/desktop/api/ws2ipdef/ns-ws2ipdef-_sockaddr_in6_pair typedef struct _sockaddr_in6_pair { // PSOCKADDR_IN6 SourceAddress; PSOCKADDR_IN6 DestinationAddress; } SOCKADDR_IN6_PAIR, *PSOCKADDR_IN6_PAIR; [PInvokeData("ws2ipdef.h", MSDNShortId = "0265f8e0-8b35-4d9d-bf22-e98e9ff36a17")] [StructLayout(LayoutKind.Sequential)] public struct SOCKADDR_IN6_PAIR { /// The source address private readonly IntPtr _SourceAddress; /// The destination address private readonly IntPtr _DestinationAddress; /// /// A pointer to an IP source address represented as a SOCKADDR_IN6 structure. The address family is in host byte order and the /// IPv6 address, port, flow information, and zone ID are in network byte order. /// /// The source address. public SOCKADDR_IN6 SourceAddress => _SourceAddress.ToStructure(); /// /// A pointer to an IP source address represented as a SOCKADDR_IN6 structure. The address family is in host byte order and the /// IPv6 address, port, flow information, and zone ID are in network byte order. /// /// The destination address. public SOCKADDR_IN6 DestinationAddress => _DestinationAddress.ToStructure(); /// Performs an implicit conversion from to . /// The unmanaged value. /// The resulting instance from the conversion. public static implicit operator SOCKADDR_IN6_PAIR_NATIVE(SOCKADDR_IN6_PAIR unmgd) => new() { SourceAddress = unmgd.SourceAddress, DestinationAddress = unmgd.DestinationAddress }; /// Converts to string. /// A that represents this instance. /// public override string ToString() => $"{SourceAddress} : {DestinationAddress}"; } /// /// The SOCKADDR_IN6_PAIR structure contains pointers to a pair of IP addresses that represent a source and destination /// address pair. /// /// /// The SOCKADDR_IN6_PAIR structure is defined on Windows Vista and later. /// /// Any IPv4 addresses in the SOCKADDR_IN6_PAIR structure must be represented in the IPv4-mapped IPv6 address format which /// enables an IPv6 only application to communicate with an IPv4 node. For more information on the IPv4-mapped IPv6 address format, /// see Dual-Stack Sockets. /// /// The SOCKADDR_IN6_PAIR structure is used by the CreateSortedAddressPairs function. /// Note that the Ws2ipdef.h header file is automatically included in Ws2tcpip.h header file, and should never be used directly. /// // https://docs.microsoft.com/en-us/windows/desktop/api/ws2ipdef/ns-ws2ipdef-_sockaddr_in6_pair typedef struct _sockaddr_in6_pair { // PSOCKADDR_IN6 SourceAddress; PSOCKADDR_IN6 DestinationAddress; } SOCKADDR_IN6_PAIR, *PSOCKADDR_IN6_PAIR; [PInvokeData("ws2ipdef.h", MSDNShortId = "0265f8e0-8b35-4d9d-bf22-e98e9ff36a17")] [StructLayout(LayoutKind.Sequential)] public struct SOCKADDR_IN6_PAIR_NATIVE { /// /// A pointer to an IP source address represented as a SOCKADDR_IN6 structure. The address family is in host byte order and the /// IPv6 address, port, flow information, and zone ID are in network byte order. /// public SOCKADDR_IN6 SourceAddress; /// /// A pointer to an IP source address represented as a SOCKADDR_IN6 structure. The address family is in host byte order and the /// IPv6 address, port, flow information, and zone ID are in network byte order. /// public SOCKADDR_IN6 DestinationAddress; /// Converts to string. /// A that represents this instance. /// public override string ToString() => $"{SourceAddress} : {DestinationAddress}"; } /// The SOCKADDR_INET union contains an IPv4, an IPv6 address, or an address family. /// /// The SOCKADDR_INET union is defined on Windows Vista and later. /// /// The SOCKADDR_INET union is a convenience structure for accessing an IPv4 address, an IPv6 address, or the IP address /// family without having to cast the sockaddr structure. /// /// The SOCKADDR_INET union is the data type of the Prefix member in the IP_ADDRESS_PREFIX structure /// Note that the Ws2ipdef.h header file is automatically included in Ws2tcpip.h header file, and should never be used directly. /// // https://docs.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_inet typedef union _SOCKADDR_INET { SOCKADDR_IN // Ipv4; SOCKADDR_IN6 Ipv6; ADDRESS_FAMILY si_family; } SOCKADDR_INET, *PSOCKADDR_INET; [PInvokeData("ws2ipdef.h", MSDNShortId = "7278dcb4-65c6-4aea-a474-cb7fae4d7672")] [StructLayout(LayoutKind.Explicit)] public struct SOCKADDR_INET : IEquatable, IEquatable, IEquatable { /// /// An IPv4 address represented as a SOCKADDR_IN structure containing the address family and the IPv4 address. The address /// family is in host byte order and the IPv4 address is in network byte order. /// /// On the Windows SDK released for Windows Vista and later, the organization of header files has changed and the SOCKADDR_IN /// structure is defined in the Ws2def.h header file. Note that the Ws2def.h header file is automatically included in /// Winsock2.h, and should never be used directly. /// /// [FieldOffset(0)] public SOCKADDR_IN Ipv4; /// /// An IPv6 address represented as a SOCKADDR_IN6 structure containing the address family and the IPv6 address. The address /// family is in host byte order and the IPv6 address is in network byte order. /// /// On the Windows SDK released for Windows Vista and later, the organization of header files has changed and the SOCKADDR_IN6 /// structure is defined in the Ws2def.h header file. Note that the Ws2def.h header file is automatically included in /// Winsock2.h, and should never be used directly. /// /// [FieldOffset(0)] public SOCKADDR_IN6 Ipv6; /// /// The address family. /// /// Possible values for the address family are listed in the Ws2def.h header file. Note that the values for the AF_ address /// family and PF_ protocol family constants are identical (for example, AF_INET and PF_INET), so either constant can be /// used.The Ws2def.h header file is automatically included in Winsock2.h, and should never be used directly. /// /// [FieldOffset(0)] public ADDRESS_FAMILY si_family; /// Gets the size of the actual address (not necessarily the size of the structure). public int Size => si_family == ADDRESS_FAMILY.AF_INET ? Marshal.SizeOf(typeof(SOCKADDR_IN)) : Marshal.SizeOf(typeof(SOCKADDR_IN6)); /// Specifies whether this instance is equal to the specified object. /// The object to test for equality. /// if is equal to this instance. public bool Equals(SOCKADDR_INET other) => (si_family == ADDRESS_FAMILY.AF_INET && Ipv4.Equals(other.Ipv4)) || (si_family == ADDRESS_FAMILY.AF_INET6 && Ipv6.Equals(other.Ipv6)); /// Specifies whether this instance is equal to the specified object. /// The object to test for equality. /// if is equal to this instance. public bool Equals(SOCKADDR_IN other) => si_family == ADDRESS_FAMILY.AF_INET && Ipv4.Equals(other); /// Specifies whether this instance is equal to the specified object. /// The object to test for equality. /// if is equal to this instance. public bool Equals(SOCKADDR_IN6 other) => si_family == ADDRESS_FAMILY.AF_INET6 && Ipv6.Equals(other); /// Performs an implicit conversion from to . /// The address. /// The resulting instance from the conversion. public static implicit operator SOCKADDR_INET(SOCKADDR_IN address) => new() { Ipv4 = address }; /// Performs an implicit conversion from to . /// The address. /// The resulting instance from the conversion. public static implicit operator SOCKADDR_INET(SOCKADDR_IN6 address) => new() { Ipv6 = address }; /// Performs an explicit conversion from to . /// The address. /// The result of the conversion. public static explicit operator SOCKADDR_INET(System.Net.IPEndPoint address) { if (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) return new() { Ipv4 = new SOCKADDR_IN(new(address.Address.GetAddressBytes()), (ushort)address.Port) }; return new() { Ipv6 = new SOCKADDR_IN6(address.Address.GetAddressBytes(), (uint)address.Address.ScopeId, (ushort)address.Port) }; } /// Converts to string. /// A that represents this instance. public override string ToString() { var sb = new System.Text.StringBuilder($"{si_family}"); if (si_family == ADDRESS_FAMILY.AF_INET) sb.Append(":").Append(Ipv4); else if (si_family == ADDRESS_FAMILY.AF_INET6) sb.Append(":").Append(Ipv6); return sb.ToString(); } } }