mirror of https://github.com/dahall/Vanara.git
Added new Vanara.Net assembly with support for DNS services and DHCP clients.
parent
ad564035e7
commit
1e24865e5a
|
@ -0,0 +1,578 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vanara.Extensions;
|
||||
using Vanara.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.Dhcp;
|
||||
using static Vanara.PInvoke.Kernel32;
|
||||
|
||||
namespace Vanara.Net;
|
||||
|
||||
/// <summary>Encapsulates the functions and properties for a DHCP client.</summary>
|
||||
/// <seealso cref="System.IDisposable"/>
|
||||
public class DhcpClient : IDisposable
|
||||
{
|
||||
#pragma warning disable IDE0052 // Remove unread private members
|
||||
private static readonly DhcpInit init = new();
|
||||
#pragma warning restore IDE0052 // Remove unread private members
|
||||
|
||||
private readonly SafeEventHandle closing = CreateEvent(default, false, false), updateList = CreateEvent(default, false, false);
|
||||
private readonly SafeHTHREAD hThread;
|
||||
private readonly Dictionary<HEVENT, DHCP_OPTION_ID> paramChgEvents = new();
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DhcpClient"/> class.</summary>
|
||||
public DhcpClient()
|
||||
{
|
||||
var h = GCHandle.Alloc(this, GCHandleType.Normal);
|
||||
hThread = CreateThread(default, 0, ThreadProc, (IntPtr)h, CREATE_THREAD_FLAGS.RUN_IMMEDIATELY, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the value related to a <see cref="DHCP_OPTION_ID"/> has changed.
|
||||
/// <para>Use the <see cref="ChangeEventIds"/> to set the list of identifiers that are watched.</para>
|
||||
/// </summary>
|
||||
public event Action<DHCP_OPTION_ID> ParamChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not the client may assume that all subnets of the IP network to which the client is connected use the same MTU
|
||||
/// as the subnet of that network to which the client is directly connected. A value of true indicates that all subnets share the same
|
||||
/// MTU. A value of false means that the client should assume that some subnets of the directly connected network may have smaller MTUs.
|
||||
/// </summary>
|
||||
public bool AllSubnetsMTU => GetParam<bool>(DHCP_OPTION_ID.OPTION_ALL_SUBNETS_MTU);
|
||||
|
||||
/// <summary>Specifies the timeout in seconds for ARP cache entries.</summary>
|
||||
public TimeSpan ARPCacheTimeout => TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_ARP_CACHE_TIMEOUT));
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not the client should respond to subnet mask requests using ICMP. A value of false indicates that the client
|
||||
/// should not respond. A value of true means that the client should respond.
|
||||
/// </summary>
|
||||
public bool BeAMaskSupplier => GetParam<bool>(DHCP_OPTION_ID.OPTION_BE_A_MASK_SUPPLIER);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not the client should solicit routers using the Router Discovery mechanism defined in RFC 1256. A value of false
|
||||
/// indicates that the client should not perform router discovery. A value of true means that the client should perform router discovery.
|
||||
/// </summary>
|
||||
public bool BeARouter => GetParam<bool>(DHCP_OPTION_ID.OPTION_BE_A_ROUTER);
|
||||
|
||||
/// <summary>
|
||||
/// Identifies a bootstrap file. If supported by the client, it should have the same effect as the filename declaration. BOOTP clients
|
||||
/// are unlikely to support this option. Some DHCP clients will support it, and others actually require it.
|
||||
/// </summary>
|
||||
public string BootfileName => GetParam<string>(DHCP_OPTION_ID.OPTION_BOOTFILE_NAME);
|
||||
|
||||
/// <summary>This option specifies the length in 512-octet blocks of the default boot image for the client.</summary>
|
||||
public ushort BootFileSize => GetParam<ushort>(DHCP_OPTION_ID.OPTION_BOOT_FILE_SIZE);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the broadcast address in use on the client’s subnet. Legal values for broadcast addresses are specified in
|
||||
/// section 3.2.1.3 of STD 3 (RFC1122).
|
||||
/// </summary>
|
||||
public IPAddress BroadcastAddress => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_BROADCAST_ADDRESS));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of <see cref="DHCP_OPTION_ID"/> values that, when changed, will fire the <see cref="ParamChanged"/> event.
|
||||
/// </summary>
|
||||
/// <value>The list of <see cref="DHCP_OPTION_ID"/> values that, when changed, will fire the <see cref="ParamChanged"/> event.</value>
|
||||
public DHCP_OPTION_ID[] ChangeEventIds
|
||||
{
|
||||
get => paramChgEvents.Values.ToArray();
|
||||
set
|
||||
{
|
||||
ClearListeners();
|
||||
updateList.Set();
|
||||
if (value is null || value.Length == 0) return;
|
||||
string adapter = Adapter;
|
||||
foreach (DHCP_OPTION_ID id in value)
|
||||
{
|
||||
if (DhcpRegisterParamChange(DHCPCAPI_REGISTER_HANDLE_EVENT, default, adapter, default, DHCPCAPI_PARAMS_ARRAY.Make(out _, id), out HEVENT hEvt).Succeeded)
|
||||
{
|
||||
paramChgEvents.Add(hEvt, id);
|
||||
}
|
||||
}
|
||||
updateList.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class identifier (ID) that should be used if DHCP INFORM messages are being transmitted onto the network. This value is optional.
|
||||
/// </summary>
|
||||
public byte[] ClassId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This option is used by some DHCP clients as a way for users to specify identifying information to the client. This can be used in a
|
||||
/// similar way to the vendor-class-identifier option, but the value of the option is specified by the user, not the vendor. Most recent
|
||||
/// DHCP clients have a way in the user interface to specify the value for this identifier, usually as a text string.
|
||||
/// </summary>
|
||||
public string ClientClassInfo => GetParam<string>(DHCP_OPTION_ID.OPTION_CLIENT_CLASS_INFO);
|
||||
|
||||
/// <summary>
|
||||
/// This option can be used to specify a DHCP client identifier in a host declaration, so that dhcpd can find the host record by matching
|
||||
/// against the client identifier.
|
||||
/// </summary>
|
||||
public string ClientId => GetParam<string>(DHCP_OPTION_ID.OPTION_CLIENT_ID);
|
||||
|
||||
/// <summary>
|
||||
/// The cookie server option specifies a list of RFC 865 cookie servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] CookieServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_COOKIE_SERVERS));
|
||||
|
||||
/// <summary>This option specifies the default time-to-live that the client should use on outgoing datagrams.</summary>
|
||||
public byte DefaultTTL => GetParam<byte>(DHCP_OPTION_ID.OPTION_DEFAULT_TTL);
|
||||
|
||||
/// <summary>This option specifies the domain name that client should use when resolving hostnames via the Domain Name System.</summary>
|
||||
public string DomainName => GetParam<string>(DHCP_OPTION_ID.OPTION_DOMAIN_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// The domain-name-servers option specifies a list of Domain Name System (STD 13, RFC 1035) name servers available to the client.
|
||||
/// Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] DomainNameServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_DOMAIN_NAME_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies whether or not the client should use Ethernet Version 2 (RFC 894) or IEEE 802.3 (RFC 1042) encapsulation if the
|
||||
/// interface is an Ethernet. A value of false indicates that the client should use RFC 894 encapsulation. A value of true means that the
|
||||
/// client should use RFC 1042 encapsulation.
|
||||
/// </summary>
|
||||
public bool EthernetEncapsulation => GetParam<bool>(DHCP_OPTION_ID.OPTION_ETHERNET_ENCAPSULATION);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the name of a file containing additional options to be interpreted according to the DHCP option format as
|
||||
/// specified in RFC2132.
|
||||
/// </summary>
|
||||
public string ExtensionsPath => GetParam<string>(DHCP_OPTION_ID.OPTION_EXTENSIONS_PATH);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the name of the client. The name may or may not be qualified with the local domain name (it is preferable to
|
||||
/// use the domain-name option to specify the domain name). See RFC 1035 for character set restrictions. This option is only honored by
|
||||
/// dhclient-script(8) if the hostname for the client machine is not set.
|
||||
/// </summary>
|
||||
public string HostName => GetParam<string>(DHCP_OPTION_ID.OPTION_HOST_NAME);
|
||||
|
||||
/// <summary>
|
||||
/// The ien116-name-servers option specifies a list of IEN 116 name servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] IEN116NameServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_IEN116_NAME_SERVERS));
|
||||
|
||||
/// <summary>The Internet Explorer proxy.</summary>
|
||||
public string IEProxy => GetParam<string>(DHCP_OPTION_ID.OPTION_MSFT_IE_PROXY);
|
||||
|
||||
/// <summary>
|
||||
/// The impress-server option specifies a list of Imagen Impress servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] ImpressServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_IMPRESS_SERVERS));
|
||||
|
||||
/// <summary></summary>
|
||||
public uint KeepAliveDataSize => GetParam<uint>(DHCP_OPTION_ID.OPTION_KEEP_ALIVE_DATA_SIZE);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the interval (in seconds) that the client TCP should wait before sending a keepalive message on a TCP
|
||||
/// connection. The time is specified as a 32-bit unsigned integer. A value of zero indicates that the client should not generate
|
||||
/// keepalive messages on connections unless specifically requested by an application.
|
||||
/// </summary>
|
||||
public TimeSpan KeepAliveInterval => TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_KEEP_ALIVE_INTERVAL));
|
||||
|
||||
/// <summary>
|
||||
/// This option is used in a client request (DHCPDISCOVER or DHCPREQUEST) to allow the client to request a lease time for the IP address.
|
||||
/// In a server reply (DHCPOFFER), a DHCP server uses this option to specify the lease time it is willing to offer.
|
||||
/// </summary>
|
||||
public TimeSpan LeaseTime => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_LEASE_TIME));
|
||||
|
||||
/// <summary>
|
||||
/// The log-server option specifies a list of MIT-LCS UDP log servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] LogServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_LOG_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// The LPR server option specifies a list of RFC 1179 line printer servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] LPRServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_LPR_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the maximum size datagram that the client should be prepared to reassemble. The minimum legal value is 576.
|
||||
/// </summary>
|
||||
public ushort MaxReassemblySize => GetParam<ushort>(DHCP_OPTION_ID.OPTION_MAX_REASSEMBLY_SIZE);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the path-name of a file to which the client’s core image should be dumped in the event the client crashes. The
|
||||
/// path is formatted as a character string consisting of characters from the NVT ASCII character set.
|
||||
/// </summary>
|
||||
public string MeritDumpFile => GetParam<string>(DHCP_OPTION_ID.OPTION_MERIT_DUMP_FILE);
|
||||
|
||||
/// <summary>
|
||||
/// This option is used by a DHCP server to provide an error message to a DHCP client in a DHCPNAK message in the event of a failure. A
|
||||
/// client may use this option in a DHCPDECLINE message to indicate why the client declined the offered parameters.
|
||||
/// </summary>
|
||||
public string Message => GetParam<string>(DHCP_OPTION_ID.OPTION_MESSAGE);
|
||||
|
||||
/// <summary>
|
||||
/// This option, when sent by the client, specifies the maximum size of any response that the server sends to the client. When specified
|
||||
/// on the server, if the client did not send a dhcp-max-message-size option, the size specified on the server is used. This works for
|
||||
/// BOOTP as well as DHCP responses.
|
||||
/// </summary>
|
||||
public ushort MessageLength => GetParam<ushort>(DHCP_OPTION_ID.OPTION_MESSAGE_LENGTH);
|
||||
|
||||
/// <summary>This option, sent by both client and server, specifies the type of DHCP message contained in the DHCP packet.</summary>
|
||||
public DhcpMessageType MessageType => GetParam<DhcpMessageType>(DHCP_OPTION_ID.OPTION_MESSAGE_TYPE);
|
||||
|
||||
/// <summary>This option specifies the MTU to use on this interface. The minimum legal value for the MTU is 68.</summary>
|
||||
public ushort MTU => GetParam<ushort>(DHCP_OPTION_ID.OPTION_MTU);
|
||||
|
||||
/// <summary>
|
||||
/// The NetBIOS datagram distribution server (NBDD) option specifies a list of RFC 1001/1002 NBDD servers listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] NetBIOSDatagramServer => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_NETBIOS_DATAGRAM_SERVER));
|
||||
|
||||
/// <summary>
|
||||
/// The NetBIOS name server (NBNS) option specifies a list of RFC 1001/1002 NBNS name servers listed in order of preference. NetBIOS Name
|
||||
/// Service is currently more commonly referred to as WINS. WINS servers can be specified using the netbios-name-servers option.
|
||||
/// </summary>
|
||||
public IPAddress[] NetBIOSNameServer => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_NETBIOS_NAME_SERVER));
|
||||
|
||||
/// <summary>
|
||||
/// The NetBIOS node type option allows NetBIOS over TCP/IP clients which are configurable to be configured as described in RFC
|
||||
/// 1001/1002. The value is specified as a single octet which identifies the client type.
|
||||
/// </summary>
|
||||
public NetBIOSNodeType NetBIOSNodeType => GetParam<NetBIOSNodeType>(DHCP_OPTION_ID.OPTION_NETBIOS_NODE_TYPE);
|
||||
|
||||
/// <summary>
|
||||
/// The NetBIOS scope option specifies the NetBIOS over TCP/IP scope parameter for the client as specified in RFC 1001/1002. See RFC1001,
|
||||
/// RFC1002, and RFC1035 for character-set restrictions.
|
||||
/// </summary>
|
||||
public string NetBIOSScopeOption => GetParam<string>(DHCP_OPTION_ID.OPTION_NETBIOS_SCOPE_OPTION);
|
||||
|
||||
/// <summary>
|
||||
/// The netinfo-server-address option has not been described in any RFC, but has been allocated (and is claimed to be in use) by Apple
|
||||
/// Computers. It’s hard to say if the above is the correct format, or what clients might be expected to do if values were configured.
|
||||
/// Use at your own risk.
|
||||
/// </summary>
|
||||
public IPAddress[] NetworkInfoServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_NETWORK_INFO_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the name of the client’s NIS (Sun Network Information Services) domain. The domain is formatted as a character
|
||||
/// string consisting of characters from the NVT ASCII character set.
|
||||
/// </summary>
|
||||
public string NetworkInfoServiceDomain => GetParam<string>(DHCP_OPTION_ID.OPTION_NETWORK_INFO_SERVICE_DOM);
|
||||
|
||||
/// <summary>
|
||||
/// The NNTP server option specifies a list of NNTP servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] NetworkTimeServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_NETWORK_TIME_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies whether the client should configure its IP layer to allow forwarding of datagrams with non-local source routes
|
||||
/// (see Section 3.3.5 of [4] for a discussion of this topic). A value of false means disallow forwarding of such datagrams, and a value
|
||||
/// of true means allow forwarding.
|
||||
/// </summary>
|
||||
public bool NonLocalSourceRouting => GetParam<bool>(DHCP_OPTION_ID.OPTION_NON_LOCAL_SOURCE_ROUTING);
|
||||
|
||||
/// <summary></summary>
|
||||
public bool OkToOverlay => GetParam<bool>(DHCP_OPTION_ID.OPTION_OK_TO_OVERLAY);
|
||||
|
||||
/// <summary>
|
||||
/// This option, when sent by the client, specifies which options the client wishes the server to return. Normally, in the ISC DHCP
|
||||
/// client, this is done using the request statement. If this option is not specified by the client, the DHCP server will normally return
|
||||
/// every option that is valid in scope and that fits into the reply. When this option is specified on the server, the server returns the
|
||||
/// specified options. This can be used to force a client to take options that it hasn’t requested, and it can also be used to tailor the
|
||||
/// response of the DHCP server for clients that may need a more limited set of options than those the server would normally return.
|
||||
/// </summary>
|
||||
public byte[] ParameterRequestList => GetParam<byte[]>(DHCP_OPTION_ID.OPTION_PARAMETER_REQUEST_LIST);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the timeout (in seconds) to use when aging Path MTU values discovered by the mechanism defined in RFC 1191.
|
||||
/// </summary>
|
||||
public TimeSpan PathMTUAgingTimeout => TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_PMTU_AGING_TIMEOUT));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies a table of MTU sizes to use when performing Path MTU Discovery as defined in RFC 1191. The table is formatted
|
||||
/// as a list of 16-bit unsigned integers, ordered from smallest to largest. The minimum MTU value cannot be smaller than 68.
|
||||
/// </summary>
|
||||
public ushort[] PathMTUPlateauTable => GetParam<ushort[]>(DHCP_OPTION_ID.OPTION_PMTU_PLATEAU_TABLE);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies whether or not the client should perform subnet mask discovery using ICMP. A value of false indicates that the
|
||||
/// client should not perform mask discovery. A value of true means that the client should perform mask discovery.
|
||||
/// </summary>
|
||||
public bool PerformMaskDiscovery => GetParam<bool>(DHCP_OPTION_ID.OPTION_PERFORM_MASK_DISCOVERY);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies whether or not the client should solicit routers using the Router Discovery mechanism defined in RFC 1256. A
|
||||
/// value of false indicates that the client should not perform router discovery. A value of true means that the client should perform
|
||||
/// router discovery.
|
||||
/// </summary>
|
||||
public bool PerformRouterDiscovery => GetParam<bool>(DHCP_OPTION_ID.OPTION_PERFORM_ROUTER_DISCOVERY);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies policy filters for non-local source routing. The filters consist of a list of IP addresses and masks which
|
||||
/// specify destination/mask pairs with which to filter incoming source routes.
|
||||
/// <para>Any source routed datagram whose next-hop address does not match one of the filters should be discarded by the client.</para>
|
||||
/// </summary>
|
||||
public IPAddress[] PolicyFilter => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_POLICY_FILTER_FOR_NLSR));
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This option specifies the number of seconds from the time a client gets an address until the client transitions to the REBINDING state.
|
||||
/// </para>
|
||||
/// <para>This option is user configurable, but it will be ignored if the value is greater than or equal to the lease time.</para>
|
||||
/// <para>
|
||||
/// To make DHCPv4+DHCPv6 migration easier in the future, any value configured in this option is also used as a DHCPv6 "T1" (renew) time.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public TimeSpan RebindTime => TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_REBIND_TIME));
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This option specifies the number of seconds from the time a client gets an address until the client transitions to the RENEWING state.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This option is user configurable, but it will be ignored if the value is greater than or equal to the rebinding time, or lease time.
|
||||
/// </para>
|
||||
/// <para>To make DHCPv4+DHCPv6 migration easier in the future,</para>
|
||||
/// </summary>
|
||||
public TimeSpan RenewalTime => TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_RENEWAL_TIME));
|
||||
|
||||
/// <summary>
|
||||
/// <para>This option is used by the client in a DHCPDISCOVER to request that a particular IP address be assigned.</para>
|
||||
/// <para>This option is not user configurable.</para>
|
||||
/// </summary>
|
||||
public IPAddress RequestedAddress => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_REQUESTED_ADDRESS));
|
||||
|
||||
/// <summary></summary>
|
||||
public IPAddress[] RlpServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_RLP_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies the path-name that contains the client’s root disk. The path is formatted as a character string consisting of
|
||||
/// characters from the NVT ASCII character set.
|
||||
/// </summary>
|
||||
public string RootDisk => GetParam<string>(DHCP_OPTION_ID.OPTION_ROOT_DISK);
|
||||
|
||||
/// <summary>
|
||||
/// The routers option specifies a list of IP addresses for routers on the client’s subnet. Routers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress RouterAddress => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_ROUTER_ADDRESS));
|
||||
|
||||
/// <summary>This option specifies the address to which the client should transmit router solicitation requests.</summary>
|
||||
public IPAddress RouterSolicitationAddress => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_ROUTER_SOLICITATION_ADDR));
|
||||
|
||||
/// <summary>
|
||||
/// GUID of the adapter on which requested data is being made. Must be under 256 characters. If this value is <see langword="null"/>, the
|
||||
/// first adapter with an address supplied via DHCP will be used.
|
||||
/// </summary>
|
||||
public string SelectedAdapterId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This option is used in DHCPOFFER and DHCPREQUEST messages, and may optionally be included in the DHCPACK and DHCPNAK messages. DHCP
|
||||
/// servers include this option in the DHCPOFFER in order to allow the client to distinguish between lease offers. DHCP clients use the
|
||||
/// contents of the ´server identifier´ field as the destination address for any DHCP messages unicast to the DHCP server. DHCP clients
|
||||
/// also indicate which of several lease offers is being accepted by including this option in a DHCPREQUEST message.
|
||||
/// </para>
|
||||
/// <para>The value of this option is the IP address of the server.</para>
|
||||
/// <para>This option is not directly user configurable. See the server-identifier server option in dhcpd.conf(5).</para>
|
||||
/// </summary>
|
||||
public IPAddress ServerIdentifier => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_SERVER_IDENTIFIER));
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This option specifies a list of static routes that the client should install in its routing cache. If multiple routes to the same
|
||||
/// destination are specified, they are listed in descending order of priority.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The routes consist of a list of IP address pairs. The first address is the destination address, and the second address is the router
|
||||
/// for the destination.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The default route (0.0.0.0) is an illegal destination for a static route. To specify the default route, use the routers option. Also,
|
||||
/// please note that this option is not intended for classless IP routing - it does not include a subnet mask. Since classless IP routing
|
||||
/// is now the most widely deployed routing standard, this option is virtually useless, and is not implemented by any of the popular DHCP
|
||||
/// clients, for example the Microsoft DHCP client.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public IPAddress[] StaticRoutes => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_STATIC_ROUTES));
|
||||
|
||||
/// <summary>
|
||||
/// The subnet mask option specifies the client’s subnet mask as per RFC 950. If no subnet mask option is provided anywhere in scope, as
|
||||
/// a last resort dhcpd will use the subnet mask from the subnet declaration for the network on which an address is being assigned.
|
||||
/// However, any subnet-mask option declaration that is in scope for the address being assigned will override the subnet mask specified
|
||||
/// in the subnet declaration.
|
||||
/// </summary>
|
||||
public IPAddress SubnetMask => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_SUBNET_MASK));
|
||||
|
||||
/// <summary>This specifies the IP address of the client’s swap server.</summary>
|
||||
public IPAddress SwapServer => new(GetParam<uint>(DHCP_OPTION_ID.OPTION_SWAP_SERVER));
|
||||
|
||||
/// <summary>
|
||||
/// This option is used to identify a TFTP server and, if supported by the client, should have the same effect as the server-name
|
||||
/// declaration. BOOTP clients are unlikely to support this option. Some DHCP clients will support it, and others actually require it.
|
||||
/// </summary>
|
||||
public string TFTPServerName => GetParam<string>(DHCP_OPTION_ID.OPTION_TFTP_SERVER_NAME);
|
||||
|
||||
/// <summary>The time-offset option specifies the offset of the client’s subnet in seconds from Coordinated Universal Time (UTC).</summary>
|
||||
public DateTimeOffset TimeOffset => new(0, TimeSpan.FromSeconds(GetParam<uint>(DHCP_OPTION_ID.OPTION_TIME_OFFSET)));
|
||||
|
||||
/// <summary>
|
||||
/// The time-server option specifies a list of RFC 868 time servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] TimeServers => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_TIME_SERVERS));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies whether or not the client should negotiate the use of trailers (RFC 893 [14]) when using the ARP protocol. A
|
||||
/// value of false indicates that the client should not attempt to use trailers. A value of true means that the client should attempt to
|
||||
/// use trailers.
|
||||
/// </summary>
|
||||
public bool Trailers => GetParam<bool>(DHCP_OPTION_ID.OPTION_TRAILERS);
|
||||
|
||||
/// <summary>This option specifies the default TTL that the client should use when sending TCP segments. The minimum value is 1.</summary>
|
||||
public byte TTL => GetParam<byte>(DHCP_OPTION_ID.OPTION_TTL);
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// This option is used by some DHCP clients to identify the vendor type and possibly the configuration of a DHCP client. The information
|
||||
/// is a string of bytes whose contents are specific to the vendor and are not specified in a standard. To see what vendor class
|
||||
/// identifier clients are sending, you can write the following in your DHCP server configuration file:
|
||||
/// </para>
|
||||
/// <para>set vendor-string = option vendor-class-identifier;</para>
|
||||
/// <para>
|
||||
/// This will result in all entries in the DHCP server lease database file for clients that sent vendor-class-identifier options having a
|
||||
/// set statement that looks something like this:
|
||||
/// </para>
|
||||
/// <para>set vendor-string = "SUNW.Ultra-5_10";</para>
|
||||
/// <para>
|
||||
/// The vendor-class-identifier option is normally used by the DHCP server to determine the options that are returned in the
|
||||
/// vendor-encapsulated-options option. Please see the VENDOR ENCAPSULATED OPTIONS section later in this manual page for further information.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public string VendorSpecInfo => GetParam<string>(DHCP_OPTION_ID.OPTION_VENDOR_SPEC_INFO);
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies a list of systems that are running the X Window System Display Manager and are available to the client.
|
||||
/// Addresses should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] XwindowDisplayManager => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_XWINDOW_DISPLAY_MANAGER));
|
||||
|
||||
/// <summary>
|
||||
/// This option specifies a list of X Window System Font servers available to the client. Servers should be listed in order of preference.
|
||||
/// </summary>
|
||||
public IPAddress[] XwindowFontServer => ToIP(GetParam<uint[]>(DHCP_OPTION_ID.OPTION_XWINDOW_FONT_SERVER));
|
||||
|
||||
internal static NetworkInterface CurrentAdapter => NetworkInterface.GetAllNetworkInterfaces().
|
||||
Where(i => i.NetworkInterfaceType == NetworkInterfaceType.Ethernet && i.OperationalStatus == OperationalStatus.Up &&
|
||||
i.Supports(NetworkInterfaceComponent.IPv4) && (i.GetIPProperties()?.GetIPv4Properties().IsDhcpEnabled ?? false)).
|
||||
FirstOrDefault();
|
||||
|
||||
internal string Adapter => SelectedAdapterId ?? CurrentAdapter?.Id;
|
||||
|
||||
/// <summary>Gets the original subnet mask.</summary>
|
||||
/// <returns>The retrieved subnet mask.</returns>
|
||||
public IPAddress GetOriginalSubnetMask()
|
||||
{
|
||||
DhcpGetOriginalSubnetMask(Adapter, out DHCP_IP_ADDRESS mask);
|
||||
return new(mask.value);
|
||||
}
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
ClearListeners();
|
||||
closing.Set();
|
||||
hThread.Close();
|
||||
}
|
||||
|
||||
/// <summary>Gets the current DHCP servers for this client.</summary>
|
||||
/// <returns>A sequence of addresses of DHCP servers.</returns>
|
||||
public IEnumerable<IPAddress> GetDhcpServers() => NetworkInterface.GetAllNetworkInterfaces().Where(i => i.OperationalStatus == OperationalStatus.Up).SelectMany(i => i.GetIPProperties().DhcpServerAddresses).Distinct();
|
||||
|
||||
/// <summary>The <c>DhcpRemoveDNSRegistrations</c> function removes all DHCP-initiated DNS registrations for the client.</summary>
|
||||
public void RemoveDNSRegistrations() => DhcpRemoveDNSRegistrations().ThrowIfFailed();
|
||||
|
||||
private void ClearListeners()
|
||||
{
|
||||
foreach (KeyValuePair<HEVENT, DHCP_OPTION_ID> kv in paramChgEvents)
|
||||
{
|
||||
DhcpDeRegisterParamChange(0, default, kv.Key);
|
||||
}
|
||||
paramChgEvents.Clear();
|
||||
}
|
||||
|
||||
private T GetParam<T>(DHCP_OPTION_ID optionId)
|
||||
{
|
||||
using SafeCoTaskMemHandle pClassIdData = new(ClassId);
|
||||
using SafeCoTaskMemStruct<DHCPCAPI_CLASSID> pClass = (DHCPCAPI_CLASSID?)(ClassId is null ? null : new DHCPCAPI_CLASSID() { nBytesData = (uint)ClassId.Length, Data = pClassIdData });
|
||||
|
||||
DHCPCAPI_PARAMS_ARRAY sendParams = new();
|
||||
DHCPCAPI_PARAMS_ARRAY reqParams = DHCPCAPI_PARAMS_ARRAY.Make(out SafeNativeArray<DHCPAPI_PARAMS> pparam, optionId);
|
||||
|
||||
uint sz = 0;
|
||||
string adapter = Adapter;
|
||||
DhcpRequestParams(DHCPCAPI_REQUEST.DHCPCAPI_REQUEST_SYNCHRONOUS, default, adapter, pClass, sendParams, reqParams, IntPtr.Zero, ref sz, null).ThrowUnless(Win32Error.ERROR_MORE_DATA);
|
||||
|
||||
using SafeCoTaskMemHandle buffer = new(sz);
|
||||
Guid appId = Guid.NewGuid();
|
||||
DhcpRequestParams(DHCPCAPI_REQUEST.DHCPCAPI_REQUEST_SYNCHRONOUS, default, adapter, pClass, sendParams, reqParams, buffer, ref sz, appId.ToString("N")).ThrowIfFailed();
|
||||
if (sz == 0)
|
||||
return default;
|
||||
|
||||
DHCPAPI_PARAMS p = pparam[0];
|
||||
if (typeof(T).IsArray)
|
||||
{
|
||||
Type elemType = typeof(T).GetElementType();
|
||||
System.Diagnostics.Debug.WriteLine($"Array: type={elemType.Name}, elemSz={InteropExtensions.SizeOf(elemType)}, memSz={sz}");
|
||||
return (T)(object)p.Data.ToArray(elemType, sz / InteropExtensions.SizeOf(elemType), 0, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(typeof(T) == typeof(string) ? $"String: memSz={sz}" : $"Value: type={typeof(T).Name}, sz={InteropExtensions.SizeOf<T>()}, memSz={sz}");
|
||||
return p.Data.Convert<T>(p.nBytesData, CharSet.Ansi);
|
||||
}
|
||||
}
|
||||
|
||||
private static uint ThreadProc(IntPtr hgc)
|
||||
{
|
||||
var c = (DhcpClient)GCHandle.FromIntPtr(hgc).Target;
|
||||
HEVENT[] hevts;
|
||||
RebuildList();
|
||||
do
|
||||
{
|
||||
WAIT_STATUS state = WaitForMultipleObjects(hevts, false, INFINITE);
|
||||
if (state == (WAIT_STATUS)c.paramChgEvents.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (state == (WAIT_STATUS)c.paramChgEvents.Count + 1)
|
||||
{
|
||||
RebuildList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hevts.Length > (int)state && c.paramChgEvents.TryGetValue(hevts[(int)state], out var id))
|
||||
c.ParamChanged?.Invoke(id);
|
||||
}
|
||||
} while (true);
|
||||
return 0;
|
||||
|
||||
void RebuildList() => hevts = c.paramChgEvents.Keys.Concat(new HEVENT[] { c.closing, c.updateList }).ToArray();
|
||||
}
|
||||
|
||||
private IPAddress[] ToIP(uint[] ips) => ips is null ? new IPAddress[0] : Array.ConvertAll(ips, i => new IPAddress(i));
|
||||
|
||||
internal class DhcpInit
|
||||
{
|
||||
public readonly uint DhcpVersion, DhcpV6Version;
|
||||
|
||||
public DhcpInit()
|
||||
{
|
||||
DhcpCApiInitialize(out DhcpVersion).ThrowIfFailed();
|
||||
Dhcpv6CApiInitialize(out DhcpV6Version);
|
||||
}
|
||||
|
||||
~DhcpInit()
|
||||
{
|
||||
Dhcpv6CApiCleanup();
|
||||
DhcpCApiCleanup();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.Extensions;
|
||||
using Vanara.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using static Vanara.PInvoke.DnsApi;
|
||||
|
||||
namespace Vanara.Net;
|
||||
|
||||
/// <summary>Represents a DNS service.</summary>
|
||||
/// <seealso cref="System.IDisposable"/>
|
||||
public class DnsService : IDisposable
|
||||
{
|
||||
private readonly IntPtr ctx;
|
||||
private readonly AutoResetEvent evt = new(false);
|
||||
private readonly SafePDNS_SERVICE_INSTANCE pSvcInst;
|
||||
private bool disposed = false, registering = false;
|
||||
private Win32Error err = 0;
|
||||
private DNS_SERVICE_REGISTER_REQUEST req;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="DnsService"/> class.</summary>
|
||||
/// <param name="serviceName">The name of the service.</param>
|
||||
/// <param name="hostName">The name of the host of the service.</param>
|
||||
/// <param name="port">The port.</param>
|
||||
/// <param name="priority">The service priority.</param>
|
||||
/// <param name="weight">The service weight.</param>
|
||||
/// <param name="address">The service-associated address and port.</param>
|
||||
/// <param name="properties">A dictionary of property keys and values.</param>
|
||||
public DnsService(string serviceName, string hostName, ushort port, [Optional] ushort priority,
|
||||
[Optional] ushort weight, [Optional] IPAddress? address, [Optional] IDictionary<string, string>? properties)
|
||||
{
|
||||
using SafeCoTaskMemHandle v4 = address is null ? SafeCoTaskMemHandle.Null : new(address.MapToIPv4().GetAddressBytes());
|
||||
using SafeCoTaskMemHandle v6 = address is null ? SafeCoTaskMemHandle.Null : new(address.MapToIPv6().GetAddressBytes());
|
||||
pSvcInst = DnsServiceConstructInstance(serviceName, hostName, v4, v6, port,
|
||||
priority, weight, (uint)(properties?.Count ?? 0), properties?.Keys.ToArray(), properties?.Values.ToArray());
|
||||
ctx = (IntPtr)GCHandle.Alloc(this, GCHandleType.Normal);
|
||||
}
|
||||
|
||||
internal DnsService(IntPtr pInst)
|
||||
{
|
||||
pSvcInst = new SafePDNS_SERVICE_INSTANCE(pInst);
|
||||
ctx = (IntPtr)GCHandle.Alloc(this, GCHandleType.Normal);
|
||||
}
|
||||
|
||||
/// <summary>A string that represents the name of the host of the service.</summary>
|
||||
public string HostName => pSvcInst.pszHostName;
|
||||
|
||||
/// <summary>
|
||||
/// A string that represents the service name. This is a fully qualified domain name that begins with a service name, and ends with
|
||||
/// ".local". It takes the generalized form "<ServiceName>._<ServiceType>._<TransportProtocol>.local". For example, "MyMusicServer._http._tcp.local".
|
||||
/// </summary>
|
||||
public string InstanceName => pSvcInst.pszInstanceName;
|
||||
|
||||
/// <summary>A value that contains the interface index on which the service was discovered.</summary>
|
||||
public uint InterfaceIndex => pSvcInst.dwInterfaceIndex;
|
||||
|
||||
/// <summary>The service-associated IPv4 address, if defined.</summary>
|
||||
public IPAddress? Ip4Address => pSvcInst.ip4Address?.Address.MapToIPv4();
|
||||
|
||||
/// <summary>The service-associated IPv6 address, if defined.</summary>
|
||||
public IPAddress? Ip6Address => pSvcInst.ip6Address?.Address.MapToIPv6();
|
||||
|
||||
/// <summary>Gets a value indicating whether this instance is registered.</summary>
|
||||
/// <value><see langword="true"/> if this instance is registered; otherwise, <see langword="false"/>.</value>
|
||||
public bool IsRegistered => req.Version != 0;
|
||||
|
||||
/// <summary>A value that represents the port on which the service is running.</summary>
|
||||
public ushort Port => pSvcInst.wPort;
|
||||
|
||||
/// <summary>A value that represents the service priority.</summary>
|
||||
public ushort Priority => pSvcInst.wPriority;
|
||||
|
||||
/// <summary>The DNS service properties.</summary>
|
||||
public IReadOnlyDictionary<string, string> Properties => pSvcInst.properties;
|
||||
|
||||
/// <summary>A value that represents the service weight.</summary>
|
||||
public ushort Weight => pSvcInst.wWeight;
|
||||
|
||||
/// <summary>Used to obtain more information about a service advertised on the local network.</summary>
|
||||
/// <param name="serviceName">
|
||||
/// The service name. This is a fully qualified domain name that begins with a service name, and ends with ".local". It takes the
|
||||
/// generalized form "<ServiceName>._<ServiceType>._<TransportProtocol>.local". For example, "MyMusicServer._http._tcp.local".
|
||||
/// </param>
|
||||
/// <param name="adapter">The interface over which the query is sent. If <see langword="null"/>, then all interfaces will be considered.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel a pending asynchronous resolve operation.</param>
|
||||
/// <returns>If successful, returns a new <see cref="DnsService"/> instance; otherwise, throws the appropriate DNS-specific exception.</returns>
|
||||
/// <remarks>This function is asynchronous.</remarks>
|
||||
public static async Task<DnsService> ResolveAsync(string serviceName, NetworkInterface? adapter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using ManualResetEvent evt = new(false);
|
||||
Win32Error err = 0;
|
||||
IntPtr result = default;
|
||||
DNS_SERVICE_RESOLVE_REQUEST res = new()
|
||||
{
|
||||
Version = DNS_QUERY_REQUEST_VERSION1,
|
||||
InterfaceIndex = (uint)(adapter?.GetIPProperties().GetIPv4Properties().Index ?? 0),
|
||||
QueryName = serviceName,
|
||||
pResolveCompletionCallback = ResolveCallback,
|
||||
};
|
||||
DnsServiceResolve(res, out DNS_SERVICE_CANCEL c).ThrowUnless(Win32Error.DNS_REQUEST_PENDING);
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (WaitHandle.WaitAny(new[] { cancellationToken.WaitHandle, evt }) == 0)
|
||||
DnsServiceResolveCancel(c);
|
||||
});
|
||||
return result != IntPtr.Zero ? new DnsService(result) : throw err.GetException();
|
||||
|
||||
void ResolveCallback(Win32Error Status, IntPtr pQueryContext, IntPtr pInstance)
|
||||
{
|
||||
if ((err = Status).Succeeded)
|
||||
result = pInstance;
|
||||
evt.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Used to remove a registered service.</summary>
|
||||
/// <returns>If not successful, throws an appropriate DNS-specific exception.</returns>
|
||||
/// <remarks>This function is asynchronous.</remarks>
|
||||
public async Task DeRegisterAsync()
|
||||
{
|
||||
if (!IsRegistered)
|
||||
return;
|
||||
|
||||
if (registering)
|
||||
throw new InvalidOperationException("Service is already being deregistered.");
|
||||
|
||||
registering = true;
|
||||
DnsServiceDeRegister(req, IntPtr.Zero);
|
||||
await Task.Run(() => evt.WaitOne());
|
||||
registering = false;
|
||||
req = default;
|
||||
err.ThrowIfFailed();
|
||||
}
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
return;
|
||||
|
||||
disposed = true;
|
||||
pSvcInst?.Dispose();
|
||||
if (ctx != IntPtr.Zero)
|
||||
GCHandle.FromIntPtr(ctx).Free();
|
||||
}
|
||||
|
||||
/// <summary>Used to register a discoverable service on this device.</summary>
|
||||
/// <param name="unicastEnabled">
|
||||
/// <see langword="true"/> if the DNS protocol should be used to advertise the service; <see langword="false"/> if the mDNS protocol
|
||||
/// should be used.
|
||||
/// </param>
|
||||
/// <param name="adapter">
|
||||
/// An optional value that contains the network interface over which the service is to be advertised. If <see langword="null"/>, then all
|
||||
/// interfaces will be considered.
|
||||
/// </param>
|
||||
/// <param name="cancellationToken">A cancellation token that can be used to cancel the asynchonous operation.</param>
|
||||
/// <exception cref="System.InvalidOperationException">Service is already registered.</exception>
|
||||
/// <remarks>
|
||||
/// This function is asynchronous. To deregister the service, call DnsServiceDeRegister. The registration is tied to the lifetime of the
|
||||
/// calling process. If the process goes away, the service will be automatically deregistered.
|
||||
/// </remarks>
|
||||
public async Task RegisterAsync(bool unicastEnabled = false, NetworkInterface? adapter = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (IsRegistered)
|
||||
throw new InvalidOperationException("Service is already registered.");
|
||||
|
||||
if (registering)
|
||||
throw new InvalidOperationException("Service is already being registered.");
|
||||
|
||||
req = new DNS_SERVICE_REGISTER_REQUEST()
|
||||
{
|
||||
Version = DNS_QUERY_REQUEST_VERSION1,
|
||||
InterfaceIndex = (uint)(adapter?.GetIPProperties().GetIPv4Properties().Index ?? 0),
|
||||
pServiceInstance = pSvcInst,
|
||||
pQueryContext = ctx,
|
||||
pRegisterCompletionCallback = RegCallback,
|
||||
unicastEnabled = unicastEnabled
|
||||
};
|
||||
registering = true;
|
||||
DnsServiceRegister(req, out DNS_SERVICE_CANCEL svcCancel).ThrowUnless(Win32Error.DNS_REQUEST_PENDING);
|
||||
await Task.Run(() =>
|
||||
{
|
||||
if (WaitHandle.WaitAny(new[] { cancellationToken.WaitHandle, evt }) == 0)
|
||||
DnsServiceResolveCancel(svcCancel);
|
||||
});
|
||||
registering = false;
|
||||
err.ThrowIfFailed();
|
||||
}
|
||||
|
||||
private static void RegCallback(Win32Error Status, IntPtr pQueryContext, IntPtr pInstance)
|
||||
{
|
||||
DnsService svc = (DnsService)GCHandle.FromIntPtr(pQueryContext).Target;
|
||||
using SafePDNS_SERVICE_INSTANCE i = new(pInstance);
|
||||
svc.err = Status;
|
||||
svc.evt.Set();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>Abstracted classes around Win32 networking functions to provide simplified and object-oriented access to key networking capabilities like DNS, DHCP, filtering, access, and discovery.</Description>
|
||||
<AssemblyTitle>$(AssemblyName)</AssemblyTitle>
|
||||
<AssemblyName>Vanara.Net</AssemblyName>
|
||||
<PackageId>$(AssemblyName)</PackageId>
|
||||
<PackageTags>vanara;net-extensions;networking</PackageTags>
|
||||
<PackageReleaseNotes />
|
||||
<PackageReadmeFile>pkgreadme.md</PackageReadmeFile>
|
||||
<RootNamespace>Vanara.Net</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PInvoke\Dhcp\Vanara.PInvoke.Dhcp.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\DnsApi\Vanara.PInvoke.DnsApi.csproj" />
|
||||
<ProjectReference Include="..\PInvoke\Kernel32\Vanara.PInvoke.Kernel32.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,21 @@
|
|||
![Vanara](https://raw.githubusercontent.com/dahall/Vanara/master/docs/icons/VanaraHeading.png)
|
||||
### **Vanara.Management NuGet Package**
|
||||
[![Version](https://img.shields.io/nuget/v/Vanara.Management?label=NuGet&style=flat-square)](https://github.com/dahall/Vanara/releases)
|
||||
[![Build status](https://img.shields.io/appveyor/build/dahall/vanara?label=AppVeyor%20build&style=flat-square)](https://ci.appveyor.com/project/dahall/vanara)
|
||||
|
||||
Extensions and helper classes for System.Management.
|
||||
|
||||
### **What is Vanara?**
|
||||
|
||||
[Vanara](https://github.com/dahall/Vanara) is a community project that contains various .NET assemblies which have P/Invoke functions, interfaces, enums and structures from Windows libraries. Each assembly is associated with one or a few tightly related libraries.
|
||||
|
||||
### **Issues?**
|
||||
|
||||
First check if it's already fixed by trying the [AppVeyor build](https://ci.appveyor.com/nuget/vanara-prerelease).
|
||||
If you're still running into problems, file an [issue](https://github.com/dahall/Vanara/issues).
|
||||
|
||||
### **Included in Vanara.Management**
|
||||
|
||||
Classes
|
||||
---
|
||||
DynamicMgmtObject ManagementExtensions
|
|
@ -0,0 +1,7 @@
|
|||
## Assembly report for Vanara.Management.dll
|
||||
Extensions and helper classes for System.Management.
|
||||
### Classes
|
||||
Class | Description
|
||||
---- | ----
|
||||
[Vanara.Management.DynamicMgmtObject](https://github.com/dahall/Vanara/search?l=C%23&q=DynamicMgmtObject) | A dynamic object to handle WMI `System.Management.ManagementBaseObject` references.
|
||||
[Vanara.Management.ManagementExtensions](https://github.com/dahall/Vanara/search?l=C%23&q=ManagementExtensions) | Extension methods to work more easily with `System.Management`.
|
|
@ -279,6 +279,7 @@ public class Session : INamedEntity
|
|||
/// <param name="wait">
|
||||
/// Indicates whether the operation is synchronous. Specify <c>TRUE</c> to wait for the operation to complete, or <c>FALSE</c> to
|
||||
/// return immediately.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <para>If the function succeeds, the return value is a nonzero value.</para>
|
||||
/// <para>If the function fails, the return value is zero. To get extended error information, call GetLastError.</para>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>UnitTest.PInvoke.Net</AssemblyName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Net\Vanara.Net.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,74 @@
|
|||
using Microsoft.CodeAnalysis;
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.Internal;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Vanara.Net;
|
||||
using static Vanara.PInvoke.Dhcp;
|
||||
|
||||
namespace Vanara.PInvoke.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class NetTests
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public void _Setup()
|
||||
{
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void _TearDown()
|
||||
{
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestDnsCreateService()
|
||||
{
|
||||
const string svcName = "initial._windns-example._udp.local";
|
||||
using var dnsClient = new DnsService(svcName, "example.com", 1);
|
||||
await dnsClient.RegisterAsync();
|
||||
TestContext.WriteLine("Created\r\n=================");
|
||||
dnsClient.WriteValues();
|
||||
try
|
||||
{
|
||||
Assert.That(dnsClient.IsRegistered, Is.True);
|
||||
using var dnsLookup = DnsService.ResolveAsync(svcName).Result;
|
||||
Assert.That(dnsLookup.InstanceName, Is.EqualTo(svcName));
|
||||
TestContext.WriteLine("Found\r\n=================");
|
||||
dnsLookup.WriteValues();
|
||||
}
|
||||
finally
|
||||
{
|
||||
await dnsClient.DeRegisterAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDhcpProps()
|
||||
{
|
||||
using var client = new DhcpClient();
|
||||
client.WriteValues();
|
||||
client.GetDhcpServers().WriteValues();
|
||||
client.GetOriginalSubnetMask().WriteValues();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDhcpListeners()
|
||||
{
|
||||
var client = new DhcpClient();
|
||||
try
|
||||
{
|
||||
client.ChangeEventIds = new[] { DHCP_OPTION_ID.OPTION_DEFAULT_TTL, DHCP_OPTION_ID.OPTION_LEASE_TIME, DHCP_OPTION_ID.OPTION_MESSAGE };
|
||||
Thread.Sleep(1000);
|
||||
client.ChangeEventIds = null;
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
43
Vanara.sln
43
Vanara.sln
|
@ -399,7 +399,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DOSvc", "UnitTests\PInvoke\
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vanara.PInvoke.MsRdc", "PInvoke\MsRdc\Vanara.PInvoke.MsRdc.csproj", "{FE46691C-F788-471B-B538-B24F81C5579E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsRdc", "UnitTests\PInvoke\MsRdc\MsRdc.csproj", "{454FEEDE-D1C5-474F-BDE6-801CE6C78088}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsRdc", "UnitTests\PInvoke\MsRdc\MsRdc.csproj", "{454FEEDE-D1C5-474F-BDE6-801CE6C78088}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vanara.Net", "Net\Vanara.Net.csproj", "{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net", "UnitTests\Net\Net.csproj", "{F713978B-5EA5-459B-9D26-62DCC6305B78}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
@ -2634,6 +2638,42 @@ Global
|
|||
{454FEEDE-D1C5-474F-BDE6-801CE6C78088}.Release|x64.Build.0 = Release|Any CPU
|
||||
{454FEEDE-D1C5-474F-BDE6-801CE6C78088}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{454FEEDE-D1C5-474F-BDE6-801CE6C78088}.Release|x86.Build.0 = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|Any CPU.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|x64.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|x64.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|x86.ActiveCfg = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.DebugNoTests|x86.Build.0 = Debug|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{55D566F7-DF56-4C18-A8FF-5BBD6F672BC9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|x64.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|x64.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|x86.ActiveCfg = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.DebugNoTests|x86.Build.0 = Debug|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -2809,6 +2849,7 @@ Global
|
|||
{83DD9317-2B20-4AD3-A167-250D9AF2F1A5} = {385CAD2D-0A5E-4F80-927B-D5499D126B90}
|
||||
{FE46691C-F788-471B-B538-B24F81C5579E} = {212ABBD0-B724-4CFA-9D6D-E3891547FA90}
|
||||
{454FEEDE-D1C5-474F-BDE6-801CE6C78088} = {385CAD2D-0A5E-4F80-927B-D5499D126B90}
|
||||
{F713978B-5EA5-459B-9D26-62DCC6305B78} = {3EC6B40D-71D3-4E59-A0E0-544EC605FE11}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {543FAC75-2AF1-4EF1-9609-B242B63FEED4}
|
||||
|
|
Loading…
Reference in New Issue