Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

853 lines
24 KiB

  1. /*++
  2. Copyright (c) 1995-2000 Microsoft Corporation
  3. Module Name:
  4. rpcbind.c
  5. Abstract:
  6. Domain Name System (DNS) Server -- Admin Client API
  7. RPC binding routines for client.
  8. Author:
  9. Jim Gilroy (jamesg) September 1995
  10. Environment:
  11. User Mode Win32
  12. Revision History:
  13. --*/
  14. #include "dnsclip.h"
  15. #include "ws2tcpip.h"
  16. #include <rpcutil.h>
  17. #include <ntdsapi.h>
  18. #include <dnslibp.h>
  19. //
  20. // Allow 45 seconds for RPC connection. This should allow one 30 second
  21. // TCP attempt plus 15 seconds of the second TCP retry.
  22. //
  23. #define DNS_RPC_CONNECT_TIMEOUT ( 45 * 1000 ) // in milliseconds
  24. //
  25. // Local machine name
  26. //
  27. // Keep this as static data to check when attempt to access local
  28. // machine by name.
  29. // Buffer is large enough to hold unicode version of name.
  30. //
  31. static WCHAR wszLocalMachineName[ MAX_COMPUTERNAME_LENGTH + 1 ] = L"";
  32. LPWSTR pwszLocalMachineName = wszLocalMachineName;
  33. LPSTR pszLocalMachineName = ( LPSTR ) wszLocalMachineName;
  34. //
  35. // NT4 uses ANSI\UTF8 string for binding
  36. //
  37. DWORD
  38. FindProtocolToUseNt4(
  39. IN LPSTR pszServerName
  40. )
  41. /*++
  42. Routine Description:
  43. Determine which protocol to use.
  44. This is determined from server name:
  45. - noneexistent or local -> use LPC
  46. - valid IpAddress -> use TCP/IP
  47. - otherwise named pipes
  48. Arguments:
  49. pszServerName -- server name we want to bind to
  50. Return Value:
  51. DNS_RPC_USE_TCPIP
  52. DNS_RPC_USE_NP
  53. DNS_RPC_USE_LPC
  54. --*/
  55. {
  56. DWORD dwComputerNameLength;
  57. DWORD dwIpAddress;
  58. DWORD status;
  59. DNSDBG( RPC, (
  60. "FindProtocolToUseNt4(%s)\n",
  61. pszServerName ));
  62. //
  63. // no address given, use LPC
  64. //
  65. if ( pszServerName == NULL ||
  66. *pszServerName == 0 ||
  67. ( *pszServerName == '.' && *( pszServerName + 1 ) == 0 ) )
  68. {
  69. return DNS_RPC_USE_LPC;
  70. }
  71. //
  72. // if valid IP address, use TCP/IP
  73. // - except if loopback address, then use LPC
  74. //
  75. dwIpAddress = inet_addr( pszServerName );
  76. if ( dwIpAddress != INADDR_NONE )
  77. {
  78. if ( strcmp( "127.0.0.1", pszServerName ) == 0 )
  79. {
  80. return DNS_RPC_USE_LPC;
  81. }
  82. return DNS_RPC_USE_TCPIP;
  83. }
  84. //
  85. // DNS name -- use TCP/IP
  86. //
  87. if ( strchr( pszServerName, '.' ) )
  88. {
  89. status = Dns_ValidateName_UTF8(
  90. pszServerName,
  91. DnsNameHostnameFull );
  92. if ( status == ERROR_SUCCESS || status == DNS_ERROR_NON_RFC_NAME )
  93. {
  94. return DNS_RPC_USE_TCPIP;
  95. }
  96. }
  97. //
  98. // pszServerName is netBIOS computer name
  99. //
  100. // check if local machine name -- then use LPC
  101. // - save copy of local computer name if don't have it
  102. //
  103. if ( *pszLocalMachineName == '\0' )
  104. {
  105. dwComputerNameLength = MAX_COMPUTERNAME_LENGTH;
  106. if( !GetComputerName(
  107. pszLocalMachineName,
  108. &dwComputerNameLength ) )
  109. {
  110. *pszLocalMachineName = '\0';
  111. }
  112. }
  113. if ( ( *pszLocalMachineName != '\0' ) )
  114. {
  115. // if the machine has "\\" skip it for name compare.
  116. if ( *pszServerName == '\\' )
  117. {
  118. pszServerName += 2;
  119. }
  120. if ( _stricmp( pszLocalMachineName, pszServerName ) == 0 )
  121. {
  122. return DNS_RPC_USE_LPC;
  123. }
  124. if ( _stricmp( "localhost", pszServerName ) == 0 )
  125. {
  126. return DNS_RPC_USE_LPC;
  127. }
  128. }
  129. //
  130. // remote machine name -- use named pipes
  131. //
  132. return DNS_RPC_USE_NAMED_PIPE;
  133. }
  134. //
  135. // NT5 binding handle is unicode
  136. //
  137. DWORD
  138. FindProtocolToUse(
  139. IN LPWSTR pwszServerName
  140. )
  141. /*++
  142. Routine Description:
  143. Determine which protocol to use.
  144. This is determined from server name:
  145. - noneexistent or local -> use LPC
  146. - valid IpAddress -> use TCP/IP
  147. - otherwise named pipes
  148. Arguments:
  149. pwszServerName -- server name we want to bind to
  150. Return Value:
  151. DNS_RPC_USE_TCPIP
  152. DNS_RPC_USE_NP
  153. DNS_RPC_USE_LPC
  154. --*/
  155. {
  156. DWORD nameLength;
  157. DWORD status;
  158. CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
  159. DNSDBG( RPC, ( "FindProtocolToUse( %S )\n", pwszServerName ));
  160. //
  161. // If no server name was given, use LPC.
  162. // Special case "." as local machine for convenience in dnscmd.exe.
  163. //
  164. if ( pwszServerName == NULL ||
  165. *pwszServerName == 0 ||
  166. ( *pwszServerName == L'.' && * ( pwszServerName + 1 ) == 0 ) )
  167. {
  168. return DNS_RPC_USE_LPC;
  169. }
  170. //
  171. // If the name appears to be an address or a fully qualified
  172. // domain name, check for TCP versus LPC. We want to use LPC in
  173. // all cases where the target is the name of the local machine
  174. // or is a local address.
  175. //
  176. if ( ( wcschr( pwszServerName, L'.' ) ||
  177. wcschr( pwszServerName, L':' ) ) &&
  178. Dns_UnicodeToUtf8(
  179. pwszServerName,
  180. wcslen( pwszServerName ),
  181. nameBuffer,
  182. sizeof( nameBuffer ) ) )
  183. {
  184. struct addrinfo * paddrinfo = NULL;
  185. struct addrinfo hints = { 0 };
  186. //
  187. // Remove trailing dots from nameBuffer so that we can do string
  188. // compares later to see if it matches the local host name.
  189. //
  190. while ( nameBuffer[ 0 ] &&
  191. nameBuffer[ strlen( nameBuffer ) - 1 ] == '.' )
  192. {
  193. nameBuffer[ strlen( nameBuffer ) - 1 ] = '\0';
  194. }
  195. //
  196. // Attempt to convert the string into an address.
  197. //
  198. hints.ai_flags = AI_NUMERICHOST;
  199. if ( getaddrinfo(
  200. nameBuffer,
  201. NULL, // service name
  202. &hints,
  203. &paddrinfo ) == ERROR_SUCCESS &&
  204. paddrinfo )
  205. {
  206. if ( paddrinfo->ai_family == AF_INET )
  207. {
  208. PDNS_ADDR_ARRAY dnsapiArrayIpv4;
  209. BOOL addrIsLocal = FALSE;
  210. if ( strcmp( "127.0.0.1", nameBuffer ) == 0 )
  211. {
  212. return DNS_RPC_USE_LPC;
  213. }
  214. //
  215. // Check IP passed in against local IPv4 address list.
  216. // If we are unable to retrieve local IPv4 address list,
  217. // fail silently and use TCP/IP.
  218. //
  219. dnsapiArrayIpv4 = ( PDNS_ADDR_ARRAY )
  220. DnsQueryConfigAllocEx(
  221. DnsConfigLocalAddrsIp4,
  222. NULL, // adapter name
  223. FALSE ); // local alloc
  224. if ( dnsapiArrayIpv4 )
  225. {
  226. DWORD iaddr;
  227. struct sockaddr_in * psin4;
  228. psin4 = ( struct sockaddr_in * ) paddrinfo->ai_addr;
  229. for ( iaddr = 0; iaddr < dnsapiArrayIpv4->AddrCount; ++iaddr )
  230. {
  231. if ( DnsAddr_GetIp4( &dnsapiArrayIpv4->AddrArray[ iaddr ] ) ==
  232. psin4->sin_addr.s_addr )
  233. {
  234. addrIsLocal = TRUE;
  235. break;
  236. }
  237. }
  238. DnsFreeConfigStructure( dnsapiArrayIpv4, DnsConfigLocalAddrsIp4 );
  239. if ( addrIsLocal )
  240. {
  241. return DNS_RPC_USE_LPC;
  242. }
  243. }
  244. return DNS_RPC_USE_TCPIP;
  245. }
  246. else if ( paddrinfo->ai_family == AF_INET6 )
  247. {
  248. struct sockaddr_in6 * psin6;
  249. psin6 = ( struct sockaddr_in6 * ) paddrinfo->ai_addr;
  250. if ( IN6_IS_ADDR_LOOPBACK( &psin6->sin6_addr ) )
  251. {
  252. return DNS_RPC_USE_LPC;
  253. }
  254. return DNS_RPC_USE_TCPIP;
  255. }
  256. }
  257. status = Dns_ValidateName_UTF8(
  258. nameBuffer,
  259. DnsNameHostnameFull );
  260. if ( status == ERROR_SUCCESS || status == DNS_ERROR_NON_RFC_NAME )
  261. {
  262. //
  263. // Note: assume we will never need a larger buffer, and
  264. // if GetComputerName fails, return TCP/IP always.
  265. //
  266. CHAR szhost[ DNS_MAX_NAME_BUFFER_LENGTH ];
  267. DWORD dwhostsize = DNS_MAX_NAME_BUFFER_LENGTH;
  268. if ( GetComputerNameEx(
  269. ComputerNameDnsFullyQualified,
  270. szhost,
  271. &dwhostsize ) &&
  272. _stricmp( szhost, nameBuffer ) == 0 )
  273. {
  274. return DNS_RPC_USE_LPC;
  275. }
  276. return DNS_RPC_USE_TCPIP;
  277. }
  278. }
  279. //
  280. // pwszServerName is netBIOS computer name
  281. //
  282. // check if local machine name -- then use LPC
  283. // - save copy of local computer name if don't have it
  284. //
  285. if ( *pwszLocalMachineName == 0 )
  286. {
  287. nameLength = MAX_COMPUTERNAME_LENGTH;
  288. if( !GetComputerNameW(
  289. pwszLocalMachineName,
  290. &nameLength ) )
  291. {
  292. *pwszLocalMachineName = 0;
  293. }
  294. }
  295. if ( *pwszLocalMachineName != 0 )
  296. {
  297. // if the machine has "\\" skip it for name compare.
  298. if ( *pwszServerName == L'\\' )
  299. {
  300. pwszServerName += 2;
  301. }
  302. if ( _wcsicmp( pwszLocalMachineName, pwszServerName ) == 0 )
  303. {
  304. return DNS_RPC_USE_LPC;
  305. }
  306. if ( _wcsicmp( L"localhost", pwszServerName ) == 0 )
  307. {
  308. return DNS_RPC_USE_LPC;
  309. }
  310. }
  311. //
  312. // remote machine name -- use named pipes
  313. //
  314. return DNS_RPC_USE_NAMED_PIPE;
  315. }
  316. DNS_STATUS
  317. makeSpn(
  318. IN PWSTR ServiceClass,
  319. IN PWSTR ServiceName,
  320. IN OPTIONAL PWSTR InstanceName,
  321. IN OPTIONAL USHORT InstancePort,
  322. IN OPTIONAL PWSTR Referrer,
  323. OUT PWSTR *Spn
  324. )
  325. /*
  326. Routine Description:
  327. This routine is wrapper around DsMakeSpnWto avoid two calls to this function,
  328. one to find the size of the return value and second to get the actual value.
  329. jwesth: I stole this routine from ds\src\sam\client\wrappers.c.
  330. Arguments:
  331. ServiceClass -- Pointer to a constant null-terminated Unicode string specifying the
  332. class of the service. This parameter may be any string unique to that service;
  333. either the protocol name (for example, ldap) or the string form of a GUID will work.
  334. ServiceName -- Pointer to a constant null-terminated string specifying the DNS name,
  335. NetBIOS name, or distinguished name (DN). This parameter must be non-NULL.
  336. InstanceName -- Pointer to a constant null-terminated Unicode string specifying the DNS name
  337. or IP address of the host for an instance of the service. If ServiceName specifies
  338. the DNS or NetBIOS name of the service's host computer, the InstanceName parameter must be NULL.
  339. InstancePort -- Port number for an instance of the service. Use 0 for the default port.
  340. If this parameter is zero, the SPN does not include a port number.
  341. Referrer -- Pointer to a constant null-terminated Unicode string specifying the DNS name
  342. of the host that gave an IP address referral. This parameter is ignored unless the
  343. ServiceName parameter specifies an IP address.
  344. Spn -- Pointer to a Unicode string that receives the constructed SPN.
  345. The caller must free this value.
  346. Return Value:
  347. STATUS_SUCCESS
  348. Successful
  349. STATUS_NO_MEMORY
  350. not enough memory to complete the task
  351. STATUS_INVALID_PARAMETER
  352. one of the parameters is invalid
  353. STATUS_INTERNAL_ERROR
  354. opps something went wrong!
  355. */
  356. {
  357. DWORD DwStatus;
  358. NTSTATUS Status = STATUS_SUCCESS;
  359. ULONG SpnLength = 0;
  360. ADDRINFO * paddrinfo = NULL;
  361. ADDRINFO hints = { 0 };
  362. CHAR szname[ DNS_MAX_NAME_LENGTH + 1 ];
  363. PWSTR pwsznamecopy = NULL;
  364. PSTR psznamecopy = NULL;
  365. //
  366. // If ServiceName is an IP address, do DNS lookup on it.
  367. //
  368. hints.ai_flags = AI_NUMERICHOST;
  369. psznamecopy = Dns_StringCopyAllocate(
  370. ( PSTR ) ServiceName,
  371. 0,
  372. DnsCharSetUnicode,
  373. DnsCharSetUtf8 );
  374. if ( psznamecopy &&
  375. getaddrinfo(
  376. psznamecopy,
  377. NULL,
  378. &hints,
  379. &paddrinfo ) == ERROR_SUCCESS )
  380. {
  381. *szname = L'\0';
  382. if ( getnameinfo(
  383. paddrinfo->ai_addr,
  384. paddrinfo->ai_addrlen,
  385. szname,
  386. DNS_MAX_NAME_LENGTH,
  387. NULL,
  388. 0,
  389. 0 ) == ERROR_SUCCESS &&
  390. *szname )
  391. {
  392. pwsznamecopy = Dns_StringCopyAllocate(
  393. szname,
  394. 0,
  395. DnsCharSetUtf8,
  396. DnsCharSetUnicode );
  397. if ( pwsznamecopy )
  398. {
  399. ServiceName = pwsznamecopy;
  400. }
  401. }
  402. }
  403. freeaddrinfo( paddrinfo );
  404. //
  405. // Construct SPN.
  406. //
  407. *Spn = NULL;
  408. DwStatus = DsMakeSpnW(
  409. ServiceClass,
  410. ServiceName,
  411. NULL,
  412. 0,
  413. NULL,
  414. &SpnLength,
  415. NULL );
  416. if ( DwStatus != ERROR_BUFFER_OVERFLOW )
  417. {
  418. ASSERT( !"DwStatus must be INVALID_PARAMETER, so which parameter did we pass wrong?" );
  419. Status = STATUS_INVALID_PARAMETER;
  420. goto Error;
  421. }
  422. *Spn = MIDL_user_allocate( SpnLength * sizeof( WCHAR ) );
  423. if( *Spn == NULL ) {
  424. Status = STATUS_NO_MEMORY;
  425. goto Error;
  426. }
  427. DwStatus = DsMakeSpnW(
  428. ServiceClass,
  429. ServiceName,
  430. NULL,
  431. 0,
  432. NULL,
  433. &SpnLength,
  434. *Spn );
  435. if ( DwStatus != ERROR_SUCCESS )
  436. {
  437. ASSERT( !"DwStatus must be INVALID_PARAMETER or BUFFER_OVERFLOW, so what did we do wrong?" );
  438. Status = STATUS_INTERNAL_ERROR;
  439. goto Error;
  440. }
  441. goto Exit;
  442. Error:
  443. ASSERT( !NT_SUCCESS( Status ) );
  444. MIDL_user_free( *Spn );
  445. *Spn = NULL;
  446. Exit:
  447. FREE_HEAP( pwsznamecopy );
  448. FREE_HEAP( psznamecopy );
  449. return Status;
  450. }
  451. handle_t
  452. DNSSRV_RPC_HANDLE_bind(
  453. IN DNSSRV_RPC_HANDLE pszServerName
  454. )
  455. /*++
  456. Routine Description:
  457. Get binding handle to a DNS server.
  458. This routine is called from the DNS client stubs when
  459. it is necessary create an RPC binding to the DNS server.
  460. Arguments:
  461. pszServerName - String containing the name of the server to bind with.
  462. Return Value:
  463. The binding handle if successful.
  464. NULL if bind unsuccessful.
  465. --*/
  466. {
  467. RPC_STATUS status;
  468. LPWSTR binding;
  469. handle_t bindingHandle;
  470. DWORD RpcProtocol;
  471. PWSTR pwszspn = NULL;
  472. RPC_SECURITY_QOS rpcSecurityQOS;
  473. BOOL bW2KBind = dnsrpcGetW2KBindFlag();
  474. //
  475. // Clear thread local W2K bind retry flag for the next attempt.
  476. //
  477. dnsrpcSetW2KBindFlag( FALSE );
  478. //
  479. // Initialize RPC quality of service structure.
  480. //
  481. rpcSecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
  482. rpcSecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
  483. rpcSecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
  484. rpcSecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_DELEGATE;
  485. //
  486. // Determine protocol from target name (could be short name, long name, or IP).
  487. //
  488. RpcProtocol = FindProtocolToUse( (LPWSTR)pszServerName );
  489. IF_DNSDBG( RPC )
  490. {
  491. DNS_PRINT(( "RPC Protocol = %d\n", RpcProtocol ));
  492. }
  493. if( RpcProtocol == DNS_RPC_USE_LPC )
  494. {
  495. status = RpcStringBindingComposeW(
  496. 0,
  497. L"ncalrpc",
  498. NULL,
  499. DNS_RPC_LPC_EP_W,
  500. L"Security=Impersonation Static True",
  501. &binding );
  502. }
  503. else if( RpcProtocol == DNS_RPC_USE_NAMED_PIPE )
  504. {
  505. status = RpcStringBindingComposeW(
  506. 0,
  507. L"ncacn_np",
  508. ( LPWSTR ) pszServerName,
  509. DNS_RPC_NAMED_PIPE_W,
  510. L"Security=Impersonation Static True",
  511. &binding );
  512. }
  513. else
  514. {
  515. status = RpcStringBindingComposeW(
  516. 0,
  517. L"ncacn_ip_tcp",
  518. ( LPWSTR ) pszServerName,
  519. DNS_RPC_SERVER_PORT_W,
  520. NULL,
  521. &binding );
  522. }
  523. if ( status != RPC_S_OK )
  524. {
  525. DNS_PRINT((
  526. "ERROR: RpcStringBindingCompose failed for protocol %d\n"
  527. " Status = %d\n",
  528. RpcProtocol,
  529. status ));
  530. goto Cleanup;
  531. }
  532. status = RpcBindingFromStringBindingW(
  533. binding,
  534. &bindingHandle );
  535. if ( status != RPC_S_OK )
  536. {
  537. DNS_PRINT((
  538. "ERROR: RpcBindingFromStringBinding failed\n"
  539. " Status = %d\n",
  540. status ));
  541. goto Cleanup;
  542. }
  543. if ( RpcProtocol == DNS_RPC_USE_TCPIP )
  544. {
  545. //
  546. // Create SPN string
  547. //
  548. if ( !bW2KBind )
  549. {
  550. status = makeSpn(
  551. L"Rpcss",
  552. ( PWSTR ) pszServerName,
  553. NULL,
  554. 0,
  555. NULL,
  556. &pwszspn );
  557. }
  558. if ( !bW2KBind && status == RPC_S_OK )
  559. {
  560. //
  561. // Set up RPC security.
  562. //
  563. #if DBG
  564. printf( "rpcbind: SPN = %S\n", pwszspn );
  565. #endif
  566. status = RpcBindingSetAuthInfoExW(
  567. bindingHandle, // binding handle
  568. pwszspn, // app name to security provider
  569. RPC_C_AUTHN_LEVEL_CONNECT, // auth level
  570. RPC_C_AUTHN_GSS_NEGOTIATE, // auth package ID
  571. NULL, // client auth info, NULL specified logon info.
  572. 0, // auth service
  573. &rpcSecurityQOS ); // RPC security quality of service
  574. if ( status != RPC_S_OK )
  575. {
  576. DNS_PRINT((
  577. "ERROR: RpcBindingSetAuthInfo failed\n"
  578. " Status = %d\n",
  579. status ));
  580. goto Cleanup;
  581. }
  582. }
  583. else
  584. {
  585. #if DBG
  586. printf( "rpcbind: SPN = %s\n", DNS_RPC_SECURITY );
  587. #endif
  588. //
  589. // No SPN is available, so make the call that we used in W2K.
  590. // This seems to have a beneficial effect even though it is
  591. // not really correct. If the target is an IP address and there
  592. // is no reverse lookup zone, without the call below we do not
  593. // get a working RPC session.
  594. //
  595. if ( bW2KBind )
  596. {
  597. status = RpcBindingSetAuthInfoA(
  598. bindingHandle, // binding handle
  599. DNS_RPC_SECURITY, // app name to security provider
  600. RPC_C_AUTHN_LEVEL_CONNECT, // auth level
  601. RPC_C_AUTHN_WINNT, // auth package ID
  602. NULL, // client auth info, NULL specified logon info.
  603. 0 ); // auth service
  604. }
  605. else
  606. {
  607. status = RpcBindingSetAuthInfoExA(
  608. bindingHandle, // binding handle
  609. DNS_RPC_SECURITY, // app name to security provider
  610. RPC_C_AUTHN_LEVEL_CONNECT, // auth level
  611. RPC_C_AUTHN_GSS_NEGOTIATE, // auth package ID
  612. NULL, // client auth info, NULL specified logon info.
  613. 0, // auth service
  614. &rpcSecurityQOS ); // RPC security quality of service
  615. }
  616. if ( status != RPC_S_OK )
  617. {
  618. DNS_PRINT((
  619. "ERROR: RpcBindingSetAuthInfo failed\n"
  620. " Status = %d\n",
  621. status ));
  622. goto Cleanup;
  623. }
  624. }
  625. }
  626. #if 0
  627. //
  628. // Set RPC connection timeout. The default timeout is very long. If
  629. // the remote IP is unreachable we don't really need to wait that long.
  630. //
  631. // Can't do this. It's a nice idea but RPC uses this timeout for the
  632. // entire call, which means that long running RPC calls will return
  633. // RPC_S_CALL_CANCELLED to the client after 45 seconds. What I want
  634. // is a timeout option for connection only. RPC does not provide this.
  635. //
  636. RpcBindingSetOption(
  637. bindingHandle,
  638. RPC_C_OPT_CALL_TIMEOUT,
  639. DNS_RPC_CONNECT_TIMEOUT );
  640. #endif
  641. Cleanup:
  642. RpcStringFreeW( &binding );
  643. MIDL_user_free( pwszspn );
  644. if ( status != RPC_S_OK )
  645. {
  646. SetLastError( status );
  647. return NULL;
  648. }
  649. return bindingHandle;
  650. }
  651. void
  652. DNSSRV_RPC_HANDLE_unbind(
  653. IN DNSSRV_RPC_HANDLE pszServerName,
  654. IN handle_t BindHandle
  655. )
  656. /*++
  657. Routine Description:
  658. Unbind from DNS server.
  659. Called from the DNS client stubs when it is necessary to unbind
  660. from a server.
  661. Arguments:
  662. pszServerName - This is the name of the server from which to unbind.
  663. BindingHandle - This is the binding handle that is to be closed.
  664. Return Value:
  665. None.
  666. --*/
  667. {
  668. UNREFERENCED_PARAMETER(pszServerName);
  669. DNSDBG( RPC, ( "RpcBindingFree()\n" ));
  670. RpcBindingFree( &BindHandle );
  671. }
  672. //
  673. // End rpcbind.c
  674. //