text: /*--
  
  
  Copyright (c) 1987-1996  Microsoft Corporation
  
  Module Name:
  
      mailslot.c
  
  Abstract:
  
      Routines for doing I/O on the netlogon service's mailslots.
  
  Author:
  
      03-Nov-1993 (cliffv)
  
  Environment:
  
      User mode only.
      Contains NT-specific code.
      Requires ANSI C extensions: slash-slash comments, long external names.
  
  Revision History:
  
  --*/
  
  //
  // Common include files.
  //
  
  #include "logonsrv.h"   // Include files common to entire service
  #pragma hdrstop
  
  //
  // Include files specific to this .c file
  //
  
  #include <lmbrowsr.h>   // I_BrowserSetNetlogonState
  #include <srvann.h>     // Service announcement
  #include <nbtioctl.h>   // IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE
  
  
  //
  // Define maximum buffer size returned from the browser.
  //
  // Header returned by browser + actual mailslot message size + name of
  // mailslot + name of transport.
  //
  
  #define MAILSLOT_MESSAGE_SIZE \
             (sizeof(NETLOGON_MAILSLOT)+ \
                    NETLOGON_MAX_MS_SIZE + \
                    (NETLOGON_LM_MAILSLOT_LEN+1) * sizeof(WCHAR) + \
                    (MAXIMUM_FILENAME_LENGTH+1) * sizeof(WCHAR))
  
  /////////////////////////////////////////////////////////////////////////////
  //
  // Structure describing one of the primary mailslots the netlogon service
  // will read messages from.
  //
  // This structure is used only by netlogon's main thread and therefore needs
  // no synchronization.
  //
  /////////////////////////////////////////////////////////////////////////////
  
  typedef struct _NETLOGON_MAILSLOT_DESC {
  
      HANDLE BrowserHandle;   // Handle to the browser device driver
  
      HANDLE BrowserReadEvent;// Handle to wait on for overlapped I/O
  
      OVERLAPPED Overlapped;  // Governs overlapped I/O
  
      BOOL ReadPending;       // True if a read operation is pending
  
      LPBYTE CurrentMessage;  // Pointer to Message1 or Message2 below
  
      LPBYTE PreviousMessage; // Previous value of CurrentMessage
  
  
      //
      // Buffer containing message from browser
      //
      // The buffers are alternated allowing us to compare if an incoming
      // message is identical to the previous message.
      //
      // Leave room so the actual used portion of each buffer is properly aligned.
      // The NETLOGON_MAILSLOT struct begins with a LARGE_INTEGER which must be
      // aligned.
  
      BYTE Message1[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
      BYTE Message2[ MAILSLOT_MESSAGE_SIZE + ALIGN_WORST ];
  
  } NETLOGON_MAILSLOT_DESC, *PNETLOGON_MAILSLOT_DESC;
  
  PNETLOGON_MAILSLOT_DESC NlGlobalMailslotDesc;
  
  
  
  
  HANDLE
  NlBrowserCreateEvent(
      VOID
      )
  /*++
  
  Routine Description:
  
      Creates an event to be used in a DeviceIoControl to the browser.
  
      ??: Consider caching one or two events to reduce the number of create
      events
  
  Arguments:
  
      None
  
  Return Value:
  
      Handle to an event or NULL if the event couldn't be allocated.
  
  --*/
  {
      HANDLE EventHandle;
      //
      // Create a completion event
      //
  
      EventHandle = CreateEvent(
                                    NULL,     // No security ettibutes
                                    TRUE,     // Manual reset
                                    FALSE,    // Initially not signaled
                                    NULL);    // No Name
  
      if ( EventHandle == NULL ) {
          NlPrint((NL_CRITICAL, "Cannot create Browser read event %ld\n", GetLastError() ));
      }
  
      return EventHandle;
  }
  
  
  VOID
  NlBrowserCloseEvent(
      IN HANDLE EventHandle
      )
  /*++
  
  Routine Description:
  
      Closes an event used in a DeviceIoControl to the browser.
  
  Arguments:
  
      EventHandle - Handle of the event to close
  
  Return Value:
  
      None.
  
  --*/
  {
      (VOID) CloseHandle( EventHandle );
  }
  
  
  
  VOID
  NlBrowserClose(
      VOID
      );
  
  
  NTSTATUS
  NlBrowserDeviceIoControl(
      IN HANDLE BrowserHandle,
      IN DWORD FunctionCode,
      IN PLMDR_REQUEST_PACKET RequestPacket,
      IN DWORD RequestPacketSize,
      IN LPBYTE Buffer,
      IN DWORD BufferSize
      )
  /*++
  
  Routine Description:
  
      Send a DeviceIoControl syncrhonously to the browser.
  
  Arguments:
  
      FunctionCode - DeviceIoControl function code
  
      RequestPacket - The request packet to send.
  
      RequestPacketSize - Size (in bytes) of the request packet.
  
      Buffer - Additional buffer to pass to the browser
  
      BufferSize - Size (in bytes) of Buffer
  
  Return Value:
  
      Status of the operation.
  
      STATUS_NETWORK_UNREACHABLE: Cannot write to network.
  
      STATUS_BAD_NETWORK_PATH: The name the datagram is destined for isn't
          registered
  
  
  --*/
  {
      NTSTATUS Status;
      DWORD WinStatus;
      OVERLAPPED Overlapped;
      DWORD BytesReturned;
  
      //
      // Initialization
      //
  
      RequestPacket->Version = LMDR_REQUEST_PACKET_VERSION_DOM;
  
      //
      // Get a completion event
      //
  
      Overlapped.hEvent = NlBrowserCreateEvent();
  
      if ( Overlapped.hEvent == NULL ) {
          return NetpApiStatusToNtStatus( GetLastError() );
      }
  
      //
      // Send the request to the Datagram Receiver device driver.
      //
  
      if ( !DeviceIoControl(
                     BrowserHandle,
                     FunctionCode,
                     RequestPacket,
                     RequestPacketSize,
                     Buffer,
                     BufferSize,
                     &BytesReturned,
                     &Overlapped )) {
  
          WinStatus = GetLastError();
  
          if ( WinStatus == ERROR_IO_PENDING ) {
              if ( !GetOverlappedResult( BrowserHandle,
                                         &Overlapped,
                                         &BytesReturned,
                                         TRUE )) {
                  WinStatus = GetLastError();
              } else {
                  WinStatus = NO_ERROR;
              }
          }
      } else {
          WinStatus = NO_ERROR;
      }
  
      //
      // Delete the completion event
      //
  
      NlBrowserCloseEvent( Overlapped.hEvent );
  
  
      if ( WinStatus ) {
          //
          // Some transports return an error if the name cannot be resolved:
          //  Nbf returns ERROR_NOT_READY
          //  NetBt returns ERROR_BAD_NETPATH
          //
          if ( WinStatus == ERROR_BAD_NETPATH || WinStatus == ERROR_NOT_READY ) {
              Status = STATUS_BAD_NETWORK_PATH;
          } else {
              NlPrint((NL_CRITICAL,"Ioctl %lx to Browser returns %ld\n", FunctionCode, WinStatus));
              Status = NetpApiStatusToNtStatus( WinStatus );
          }
      } else {
          Status = STATUS_SUCCESS;
      }
  
  
      return Status;
  }
  
  
  
  NTSTATUS
  NlBrowserOpenDriver(
      PHANDLE BrowserHandle
      )
  /*++
  
  Routine Description:
  
      This routine opens the NT LAN Man Datagram Receiver driver.
  
  Arguments:
  
      BrowserHandle - Upon success, returns a handle to the browser driver
          Close it using NtClose
  
  Return Value:
  
      Status of the operation
  
  --*/
  {
      NTSTATUS Status;
      BOOL ReturnValue;
  
      UNICODE_STRING DeviceName;
  
      IO_STATUS_BLOCK IoStatusBlock;
      OBJECT_ATTRIBUTES ObjectAttributes;
  
  
      //
      // Open the browser device.
      //
      RtlInitUnicodeString(&DeviceName, DD_BROWSER_DEVICE_NAME_U);
  
      InitializeObjectAttributes(
          &ObjectAttributes,
          &DeviceName,
          OBJ_CASE_INSENSITIVE,
          NULL,
          NULL
          );
  
      Status = NtOpenFile(
                     BrowserHandle,
                     SYNCHRONIZE,
                     &ObjectAttributes,
                     &IoStatusBlock,
                     0,
                     0
                     );
  
      if (NT_SUCCESS(Status)) {
          Status = IoStatusBlock.Status;
      }
  
      return Status;
  }
  
  
  NTSTATUS
  NlBrowserRenameDomain(
      IN LPWSTR OldDomainName OPTIONAL,
      IN LPWSTR NewDomainName
      )
  /*++
  
  Routine Description:
  
      Tell the browser to rename the domain.
  
  Arguments:
  
      OldDomainName - previous name of the domain.
          If not specified, the primary domain is implied.
  
      NewDomainName - new name of the domain.
  
  Return Value:
  
      Status of the operation.
  
  --*/
  {
      NTSTATUS Status;
      HANDLE BrowserHandle = NULL;
      LPBYTE Where;
  
      UCHAR PacketBuffer[sizeof(LMDR_REQUEST_PACKET)+2*(DNLEN+1)*sizeof(WCHAR)];
      PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET)PacketBuffer;
  
  
      //
      // Open the browser driver.
      //
  
      Status = NlBrowserOpenDriver( &BrowserHandle );
  
      if (Status != NERR_Success) {
          return(Status);
      }
  
      //
      // Build the request packet.
      //
      RtlInitUnicodeString( &RequestPacket->TransportName, NULL );
      RequestPacket->Parameters.DomainRename.ValidateOnly = FALSE;
  
  
      //
      // Copy the new domain name into the packet.
      //
  
      Where = (LPBYTE) RequestPacket->Parameters.DomainRename.DomainName;
      RequestPacket->Parameters.DomainRename.DomainNameLength = wcslen( NewDomainName ) * sizeof(WCHAR);
      wcscpy( (LPWSTR)Where, NewDomainName );
      Where += RequestPacket->Parameters.DomainRename.DomainNameLength + sizeof(WCHAR);
  
  
      //
      // Copy the old domain name to the request packet.
      //
  
      if ( OldDomainName == NULL ) {
          RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
      } else {
          wcscpy( (LPWSTR)Where, OldDomainName );
          RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
                                (LPWSTR)Where );
          Where += RequestPacket->EmulatedDomainName.Length + sizeof(WCHAR);
      }
  
  
      //
      // Pass the reeqest to the browser.
      //
  
      Status = NlBrowserDeviceIoControl(
                     BrowserHandle,
                     IOCTL_LMDR_RENAME_DOMAIN,
                     RequestPacket,
                     (ULONG)(Where - (LPBYTE)RequestPacket),
                     NULL,
                     0 );
  
      if (Status != NERR_Success) {
          NlPrint((NL_CRITICAL,
                   "NlBrowserRenameDomain: Unable rename domain from %ws to %ws: %lx\n",
                  OldDomainName,
                  NewDomainName,
                  Status ));
      }
  
      if ( BrowserHandle != NULL ) {
          NtClose( BrowserHandle );
      }
      return Status;
  
  }
  
  
  NET_API_STATUS
  NlBrowserDeviceControlGetInfo(
      IN DWORD FunctionCode,
      IN PLMDR_REQUEST_PACKET RequestPacket,
      IN DWORD RequestPacketSize,
      OUT LPVOID *OutputBuffer,
      IN  ULONG PreferedMaximumLength,
      IN  ULONG BufferHintSize
      )
  /*++
  
  Routine Description:
  
      This function allocates the buffer and fill it with the information
      that is retrieved from the datagram receiver.
  
  Arguments:
  
      FunctionCode - DeviceIoControl function code
  
      RequestPacket - The request packet to send.
  
      RequestPacketSize - Size (in bytes) of the request packet.
  
      OutputBuffer - Returns a pointer to the buffer allocated by this routine
          which contains the use information requested.  This buffer should
          be freed using MIDL_user_free.
  
      PreferedMaximumLength - Supplies the number of bytes of information to
          return in the buffer.  If this value is MAXULONG, we will try to
          return all available information if there is enough memory resource.
  
      BufferHintSize - Supplies the hint size of the output buffer so that the
          memory allocated for the initial buffer will most likely be large
          enough to hold all requested data.
  
  Return Value:
  
      NET_API_STATUS - NERR_Success or reason for failure.
  
  --*/
  {
  
  //
  // Buffer allocation size for enumeration output buffer.
  //
  #define INITIAL_ALLOCATION_SIZE  48*1024  // First attempt size (48K)
  #define FUDGE_FACTOR_SIZE        1024  // Second try TotalBytesNeeded
                                         //     plus this amount
  
      NET_API_STATUS NetStatus;
      NTSTATUS Status;
      DWORD OutputBufferLength;
      DWORD TotalBytesNeeded = 1;
      ULONG OriginalResumeKey;
  
      //
      // Initialization
      //
  
      if ( NlGlobalMailslotDesc == NULL ||
           NlGlobalMailslotDesc->BrowserHandle == NULL ) {
          return ERROR_NOT_SUPPORTED;
      }
  
      OriginalResumeKey = RequestPacket->Parameters.EnumerateNames.ResumeHandle;
  
      //
      // If PreferedMaximumLength is MAXULONG, then we are supposed to get all
      // the information, regardless of size.  Allocate the output buffer of a
      // reasonable size and try to use it.  If this fails, the Redirector FSD
      // will say how much we need to allocate.
      //
  
      if (PreferedMaximumLength == MAXULONG) {
          OutputBufferLength = (BufferHintSize) ?
                               BufferHintSize :
                               INITIAL_ALLOCATION_SIZE;
      } else {
          OutputBufferLength = PreferedMaximumLength;
      }
  
      OutputBufferLength = ROUND_UP_COUNT(OutputBufferLength, ALIGN_WCHAR);
  
      if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
          return ERROR_NOT_ENOUGH_MEMORY;
      }
  
      RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
  
      //
      // Make the request of the Datagram Receiver
      //
  
      RequestPacket->Parameters.EnumerateServers.EntriesRead = 0;
  
      Status = NlBrowserDeviceIoControl(
                      NlGlobalMailslotDesc->BrowserHandle,
                      FunctionCode,
                      RequestPacket,
                      RequestPacketSize,
                      *OutputBuffer,
                      OutputBufferLength );
  
      NetStatus = NetpNtStatusToApiStatus(Status);
  
  
      //
      // If we couldn't get all the data on the first call,
      //  the datagram receiver returned the needed size of the buffer.
      //
  
      if ( RequestPacket->Parameters.EnumerateNames.EntriesRead !=
           RequestPacket->Parameters.EnumerateNames.TotalEntries ) {
  
          NetpAssert(
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateNames.TotalBytesNeeded
                      ) ==
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateServers.TotalBytesNeeded
                      )
                  );
  
          NetpAssert(
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.GetBrowserServerList.TotalBytesNeeded
                      ) ==
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateServers.TotalBytesNeeded
                      )
                  );
  
          TotalBytesNeeded = RequestPacket->Parameters.EnumerateNames.TotalBytesNeeded;
      }
  
      if ((TotalBytesNeeded > OutputBufferLength) &&
          (PreferedMaximumLength == MAXULONG)) {
  
          //
          // Initial output buffer allocated was too small and we need to return
          // all data.  First free the output buffer before allocating the
          // required size plus a fudge factor just in case the amount of data
          // grew.
          //
  
          MIDL_user_free(*OutputBuffer);
  
          OutputBufferLength =
              ROUND_UP_COUNT((TotalBytesNeeded + FUDGE_FACTOR_SIZE),
                             ALIGN_WCHAR);
  
          if ((*OutputBuffer = MIDL_user_allocate(OutputBufferLength)) == NULL) {
              return ERROR_NOT_ENOUGH_MEMORY;
          }
  
          RtlZeroMemory((PVOID) *OutputBuffer, OutputBufferLength);
  
  
          NetpAssert(
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateNames.ResumeHandle
                      ) ==
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateServers.ResumeHandle
                      )
                  );
  
          NetpAssert(
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.EnumerateNames.ResumeHandle
                      ) ==
                  FIELD_OFFSET(
                      LMDR_REQUEST_PACKET,
                      Parameters.GetBrowserServerList.ResumeHandle
                      )
                  );
  
          RequestPacket->Parameters.EnumerateNames.ResumeHandle = OriginalResumeKey;
          RequestPacket->Parameters.EnumerateServers.EntriesRead = 0;
  
          //
          //  Make the request of the Datagram Receiver
          //
  
          Status = NlBrowserDeviceIoControl(
                          NlGlobalMailslotDesc->BrowserHandle,
                          FunctionCode,
                          RequestPacket,
                          RequestPacketSize,
                          *OutputBuffer,
                          OutputBufferLength );
  
          NetStatus = NetpNtStatusToApiStatus(Status);
  
      }
  
  
      //
      // If not successful in getting any data, or if the caller asked for
      // all available data with PreferedMaximumLength == MAXULONG and
      // our buffer overflowed, free the output buffer and set its pointer
      // to NULL.
      //
      if ((NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA) ||
          (TotalBytesNeeded == 0) ||
          (PreferedMaximumLength == MAXULONG && NetStatus == ERROR_MORE_DATA) ||
          (RequestPacket->Parameters.EnumerateServers.EntriesRead == 0)) {
  
          MIDL_user_free(*OutputBuffer);
          *OutputBuffer = NULL;
  
          //
          // PreferedMaximumLength == MAXULONG and buffer overflowed means
          // we do not have enough memory to satisfy the request.
          //
          if (NetStatus == ERROR_MORE_DATA) {
              NetStatus = ERROR_NOT_ENOUGH_MEMORY;
          }
      }
  
      return NetStatus;
  }
  
  NET_API_STATUS
  NlBrowserGetTransportList(
      OUT PLMDR_TRANSPORT_LIST *TransportList
      )
  
  /*++
  
  Routine Description:
  
      This routine returns the list of transports bound into the browser.
  
  Arguments:
  
      TransportList - Transport list to return.
          This buffer should be freed using MIDL_user_free.
  
  Return Value:
  
      NERR_Success or reason for failure.
  
  --*/
  
  {
      NET_API_STATUS NetStatus;
      LMDR_REQUEST_PACKET RequestPacket;
  
      RequestPacket.Type = EnumerateXports;
  
      RtlInitUnicodeString(&RequestPacket.TransportName, NULL);
      RtlInitUnicodeString(&RequestPacket.EmulatedDomainName, NULL);
  
      NetStatus = NlBrowserDeviceControlGetInfo(
                      IOCTL_LMDR_ENUMERATE_TRANSPORTS,
                      &RequestPacket,
                      sizeof(RequestPacket),
                      TransportList,
                      0xffffffff,
                      4096 );
  
      return NetStatus;
  }
  
  
  
  NTSTATUS
  NlBrowserAddDelName(
      IN PDOMAIN_INFO DomainInfo,
      IN BOOLEAN AddName,
      IN DGRECEIVER_NAME_TYPE NameType,
      IN LPWSTR TransportName OPTIONAL,
      IN PUNICODE_STRING Name OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Add or delete a name in the browser.
  
  Arguments:
  
      DomainInfo - Hosted domain the name is to be added/deleted for.
  
      AddName - TRUE to add the name.  FALSE to delete the name.
  
      NameType - Type of name to be added/deleted
  
      TransportName -- Name of the transport to send on.
          Use NULL to send on all transports.
  
      Name - Name to be added
          If not specified, all names of NameType are deleted for this hosted domain.
  
  Return Value:
  
      Status of the operation.
  
  --*/
  {
      NTSTATUS Status;
      LPBYTE Where;
      PLMDR_REQUEST_PACKET RequestPacket = NULL;
      ULONG TransportNameSize;
  
      //
      // Build the request packet.
      //
  
      if ( TransportName != NULL ) {
          TransportNameSize = (wcslen(TransportName) + 1) * sizeof(WCHAR);
      } else {
          TransportNameSize = 0;
      }
  
      RequestPacket = NetpMemoryAllocate( sizeof(LMDR_REQUEST_PACKET) +
                                          (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR) +
                                          (DNLEN + 1) * sizeof(WCHAR) +
                                          TransportNameSize );
  
      if (RequestPacket == NULL) {
          return STATUS_NO_MEMORY;
      }
  
      RequestPacket->Parameters.AddDelName.Type = NameType;
  
      //
      // Copy the name to be added to request packet.
      //
  
      Where = (LPBYTE) RequestPacket->Parameters.AddDelName.Name;
      if ( Name == NULL ) {
          NlAssert( !AddName );
          RequestPacket->Parameters.AddDelName.DgReceiverNameLength = 0;
      } else {
          RequestPacket->Parameters.AddDelName.DgReceiverNameLength =
              Name->Length;
          RtlCopyMemory( Where, Name->Buffer, Name->Length );
          Where += Name->Length;
      }
  
      //
      // Copy the hosted domain name to the request packet.
      //
  
      wcscpy( (LPWSTR)Where,
              DomainInfo->DomUnicodeDomainNameString.Buffer );
      RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
                            (LPWSTR)Where );
      Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
  
      //
      // Fill in the TransportName
      //
  
      if ( TransportName != NULL ) {
          wcscpy( (LPWSTR) Where, TransportName);
          RtlInitUnicodeString( &RequestPacket->TransportName, (LPWSTR) Where );
          Where += TransportNameSize;
      } else {
          RequestPacket->TransportName.Length = 0;
          RequestPacket->TransportName.Buffer = NULL;
      }
  
  
      //
      // Do the actual work
      //
  
      Status = NlBrowserDeviceIoControl(
                     NlGlobalMailslotDesc->BrowserHandle,
                     AddName ? IOCTL_LMDR_ADD_NAME_DOM : IOCTL_LMDR_DELETE_NAME_DOM,
                     RequestPacket,
                     (ULONG)(Where - (LPBYTE)RequestPacket),
                     NULL,
                     0 );
  
      NetpMemoryFree( RequestPacket );
      return Status;
  }
  
  
  VOID
  NlBrowserAddName(
      IN PDOMAIN_INFO DomainInfo
      )
  /*++
  
  Routine Description:
  
      Add the Domain<1B> name.  This is the name NetGetDcName uses to identify
      the PDC.
  
  Arguments:
  
      DomainInfo - Hosted domain the name is to be added for.
  
  Return Value:
  
      None.
  
  --*/
  {
      LPWSTR MsgStrings[3] = { NULL };
      BOOL AtLeastOneTransportEnabled = FALSE;
      BOOL NameAdded = FALSE;
  
      if ( NlGlobalMailslotDesc == NULL) {
          NlPrintDom((NL_CRITICAL, DomainInfo,
                   "NlBrowserAddName: before browser initialized.\n" ));
          goto Cleanup;
      }
  
      //
      // If the domain has been renamed,
      //  delete any old names lying around.
      //
  
      if ( DomainInfo->DomFlags & DOM_RENAMED_1B_NAME ) {
          NlBrowserDelName( DomainInfo );
      }
  
      //
      // Add the <domain>0x1B name.
      //
      // This is the name NetGetDcName uses to identify the PDC.
      //
      // Do this for each transport separately and log any error
      //  indicating which transport failed.
      //
  
      if ( DomainInfo->DomRole == RolePrimary ) {
          PLIST_ENTRY ListEntry;
          PNL_TRANSPORT TransportEntry;
          NTSTATUS Status = STATUS_SUCCESS;
  
          //
          // Capture the domain name for event log output.
          //  If we can't capture (i.e. no memory), we simply
          //  won't output below.
          //
  
          EnterCriticalSection( &NlGlobalDomainCritSect );
          MsgStrings[0] = NetpAllocWStrFromWStr( DomainInfo->DomUnicodeDomainName );
          LeaveCriticalSection( &NlGlobalDomainCritSect );
  
  
          EnterCriticalSection( &NlGlobalTransportCritSect );
          for ( ListEntry = NlGlobalTransportList.Flink ;
                ListEntry != &NlGlobalTransportList ;
                ListEntry = ListEntry->Flink) {
  
              TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  
              //
              // Skip deleted transports.
              //
              if ( !TransportEntry->TransportEnabled ) {
                  continue;
              }
              AtLeastOneTransportEnabled = TRUE;
  
              Status = NlBrowserAddDelName( DomainInfo,
                                            TRUE,
                                            PrimaryDomainBrowser,
                                            TransportEntry->TransportName,
                                            &DomainInfo->DomUnicodeDomainNameString );
  
              if ( NT_SUCCESS(Status) ) {
                  NameAdded = TRUE;
                  NlPrintDom(( NL_MISC, DomainInfo,
                               "Added the 0x1B name on transport %ws\n",
                               TransportEntry->TransportName ));
  
              //
              // Output the event log indicating the name of the failed transport
              //
              } else if ( MsgStrings[0] != NULL ) {
                  NlPrintDom(( NL_CRITICAL, DomainInfo,
                               "Failed to add the 0x1B name on transport %ws\n",
                               TransportEntry->TransportName ));
  
                  MsgStrings[1] = TransportEntry->TransportName;
                  MsgStrings[2] = (LPWSTR) LongToPtr( Status );
  
                  NlpWriteEventlog(
                      NELOG_NetlogonAddNameFailure,
                      EVENTLOG_ERROR_TYPE,
                      (LPBYTE)&Status,
                      sizeof(Status),
                      MsgStrings,
                      3 | NETP_LAST_MESSAGE_IS_NTSTATUS );
              }
          }
          LeaveCriticalSection( &NlGlobalTransportCritSect );
  
          //
          // Indicate that the name was added (at least on one transport)
          //
  
          if ( NameAdded ) {
              EnterCriticalSection( &NlGlobalDomainCritSect );
              DomainInfo->DomFlags |= DOM_ADDED_1B_NAME;
              LeaveCriticalSection( &NlGlobalDomainCritSect );
          }
          if ( !AtLeastOneTransportEnabled ) {
              NlPrintDom(( NL_CRITICAL, DomainInfo,
                           "Can't add the 0x1B name because all transports are disabled\n" ));
          }
      }
  
  Cleanup:
  
      if ( MsgStrings[0] != NULL ) {
          NetApiBufferFree( MsgStrings[0] );
      }
  }
  
  
  VOID
  NlBrowserDelName(
      IN PDOMAIN_INFO DomainInfo
      )
  /*++
  
  Routine Description:
  
      Delete the Domain<1B> name.  This is the name NetGetDcName uses to identify
      the PDC.
  
  Arguments:
  
      DomainInfo - Hosted domain the name is to be deleted for.
  
  Return Value:
  
      Success (Not used)
  
  --*/
  {
      NTSTATUS Status;
  
      if ( NlGlobalMailslotDesc == NULL) {
          NlPrintDom((NL_CRITICAL, DomainInfo,
                   "NlBrowserDelName: before browser initialized.\n" ));
          return;
      }
  
      //
      // Delete the <domain>0x1B name.
      //
  
      EnterCriticalSection(&NlGlobalDomainCritSect);
      if ( NlGlobalMailslotDesc->BrowserHandle != NULL &&
           (DomainInfo->DomFlags & (DOM_ADDED_1B_NAME|DOM_RENAMED_1B_NAME)) != 0 ) {
          LeaveCriticalSection(&NlGlobalDomainCritSect);
  
          Status = NlBrowserAddDelName( DomainInfo,
                                        FALSE,
                                        PrimaryDomainBrowser,
                                        NULL,     // Delete on all transports
                                        NULL );   // Delete all such names to handle the rename case
  
          if (! NT_SUCCESS(Status)) {
              NlPrintDom((NL_CRITICAL, DomainInfo,
                       "Can't remove the 0x1B name: 0x%lx\n",
                       Status));
          }
  
          EnterCriticalSection(&NlGlobalDomainCritSect);
          DomainInfo->DomFlags &= ~(DOM_ADDED_1B_NAME|DOM_RENAMED_1B_NAME);
      }
      LeaveCriticalSection(&NlGlobalDomainCritSect);
  
      return;
  }
  
  
  
  NET_API_STATUS
  NlBrowserFixAllNames(
      IN PDOMAIN_INFO DomainInfo,
      IN PVOID Context
  )
  /*++
  
  Routine Description:
  
      Scavenge the DomainName<1B> name.
  
  Arguments:
  
      DomainInfo - The domain being scavenged.
  
      Context - Not Used
  
  Return Value:
  
      Success (not used).
  
  --*/
  {
  
  
      //
      // Ensure our Domain<1B> name is registered.
      //
  
      if ( NlGlobalTerminate ) {
          return NERR_Success;
      }
  
      if ( DomainInfo->DomRole == RolePrimary ) {
          NlBrowserAddName( DomainInfo );
      } else {
          NlBrowserDelName( DomainInfo );
      }
  
      return NERR_Success;
      UNREFERENCED_PARAMETER( Context );
  }
  
  
  ULONG
  NlServerType(
      IN DWORD Role
      )
  
  /*++
  
  Routine Description:
  
      Determines server type, that is used to set in service table.
  
  Arguments:
  
      Role - Role to be translated
  
  Return Value:
  
      SV_TYPE_DOMAIN_CTRL     if role is primary domain controller
      SV_TYPE_DOMAIN_BAKCTRL  if backup
      0                       if none of the above
  
  
  --*/
  {
      switch (Role) {
      case RolePrimary:
          return SV_TYPE_DOMAIN_CTRL;
      case RoleBackup:
          return SV_TYPE_DOMAIN_BAKCTRL;
      default:
          return 0;
      }
  }
  
  
  
  VOID
  NlBrowserSyncHostedDomains(
      VOID
      )
  /*++
  
  Routine Description:
  
      Tell the browser and SMB server to delete any hosted domains it has
      that we don't have.
  
  Arguments:
  
      None.
  
  Return Value:
  
      None.
  
  --*/
  {
      NET_API_STATUS NetStatus;
      LPWSTR HostedDomainName;
      LPWSTR HostedComputerName;
      DWORD RoleBits;
  
      PBROWSER_EMULATED_DOMAIN Domains;
      DWORD EntriesRead;
      DWORD TotalEntries;
      DWORD i;
  
      PSERVER_TRANSPORT_INFO_1 TransportInfo1;
  
  #ifdef notdef
      //
      // Enumerate the Hosted domains.
      //  ?? Don't call this function.  This function requires the browser be
      //  running.  Rather, invent an Ioctl to the bowser to enumerate domains
      //  and use that ioctl here.
      //
      // ?? Note: Role no longer comes back from this API.  Role in the browser
      //  is now maintained on a per "network" basis.
  
      NetStatus = I_BrowserQueryEmulatedDomains(
                      NULL,
                      &Domains,
                      &EntriesRead );
  
      if ( NetStatus != NERR_Success ) {
  
          NlPrint((NL_CRITICAL,"NlBrowserSyncHostedDomains: Couldn't I_BrowserQueryEmulatedDomains %ld 0x%lx.\n",
                  NetStatus, NetStatus ));
  
      //
      // Handle each enumerated domain
      //
  
      } else if ( EntriesRead != 0 ) {
  
          for ( i=0 ; i<EntriesRead; i++ ) {
              PDOMAIN_INFO DomainInfo;
  
              //
              // If we know the specified domain,
              //  all is well.
              //
  
              DomainInfo = NlFindNetbiosDomain( Domains[i].DomainName, FALSE );
  
              if ( DomainInfo != NULL ) {
  
                  //
                  // Ensure the hosted server name is identical.
                  //
  
                  if ( NlNameCompare( Domains[i].EmulatedServerName,
                                      DomainInfo->DomUnicodeComputerNameString.Buffer,
                                      NAMETYPE_COMPUTER) != 0 ) {
  
                      NlPrintDom((NL_CRITICAL, DomainInfo,
                               "NlBrowserSyncHostedDomains: hosted computer name missmatch: %ws %ws.\n",
                               Domains[i].EmulatedServerName,
                               DomainInfo->DomUnicodeComputerNameString.Buffer ));
  
                      // Tell the browser the name we have by deleting and re-adding
                      NlBrowserUpdate( DomainInfo, RoleInvalid );
                      NlBrowserUpdate( DomainInfo, DomainInfo->DomRole );
                  }
                  NlDereferenceDomain( DomainInfo );
  
              //
              // If we don't have the specified domain,
              //  delete it from the browser.
              //
              } else {
                  NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: Browser had an hosted domain we didn't (deleting)\n",
                          Domains[i].DomainName ));
  
                  //  ?? Don't call this function.  This function requires the browser be
                  //  running.  Rather, invent an Ioctl to the bowser to enumerate domains
                  //  and use that ioctl here.
                  //
  
                  NetStatus = I_BrowserSetNetlogonState(
                                  NULL,
                                  Domains[i].DomainName,
                                  NULL,
                                  0 );
  
                  if ( NetStatus != NERR_Success ) {
                          NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: Couldn't I_BrowserSetNetlogonState %ld 0x%lx.\n",
                                  Domains[i].DomainName,
                                  NetStatus, NetStatus ));
                      // This isn't fatal
                  }
              }
          }
  
          NetApiBufferFree( Domains );
      }
  #endif // notdef
  
  
      //
      // Enumerate the transports supported by the server.
      //
  
      NetStatus = NetServerTransportEnum(
                      NULL,       // local
                      1,          // level 1
                      (LPBYTE *) &TransportInfo1,
                      0xFFFFFFFF, // PrefMaxLength
                      &EntriesRead,
                      &TotalEntries,
                      NULL );     // No resume handle
  
      if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
          NlPrint(( NL_CRITICAL,
                    "NlBrowserSyncEnulatedDomains: Cannot NetServerTransportEnum %ld\n",
                    NetStatus ));
  
  
      //
      // Handle each enumerated transport.
  
      } else if ( EntriesRead != 0 ) {
  
          //
          // Weed out duplicates.
          //
          //  It'd be really inefficient to process duplicate entries.  Especially,
          //  in the cases where corrective action needed to be taken.
          //
  
          for ( i=0; i<EntriesRead; i++ ) {
              DWORD j;
  
              for ( j=i+1; j<EntriesRead; j++ ) {
                  if ( TransportInfo1[i].svti1_domain != NULL &&
                       TransportInfo1[i].svti1_transportaddresslength ==
                       TransportInfo1[j].svti1_transportaddresslength &&
                       RtlEqualMemory( TransportInfo1[i].svti1_transportaddress,
                                         TransportInfo1[j].svti1_transportaddress,
                                         TransportInfo1[i].svti1_transportaddresslength ) &&
                      NlNameCompare( TransportInfo1[i].svti1_domain,
                                     TransportInfo1[j].svti1_domain,
                                     NAMETYPE_DOMAIN ) == 0 ) {
  #ifdef notdef
                      NlPrint((NL_CRITICAL,
                               "%ws: NlBrowserSyncHostedDomains: Duplicate SMB server entry ignored.\n",
                               TransportInfo1[i].svti1_domain ));
  #endif // notdef
                      TransportInfo1[j].svti1_domain = NULL;
  
                  }
              }
          }
  
          //
          // Process each enumerated domain.
          //
  
          for ( i=0 ; i<EntriesRead; i++ ) {
              PDOMAIN_INFO DomainInfo;
  
              WCHAR UnicodeComputerName[CNLEN+1];
              ULONG UnicodeComputerNameSize;
              NTSTATUS TempStatus;
  
              //
              // ignore duplicates
              //
  
              if ( TransportInfo1[i].svti1_domain == NULL ) {
                  continue;
              }
  
  #ifdef notdef
              NlPrint((NL_CRITICAL,
                       "%ws: NlBrowserSyncHostedDomains: processing SMB entry.\n",
                       TransportInfo1[i].svti1_domain ));
  #endif // notdef
  
  
              //
              // If we know the specified domain,
              //  all is well.
              //
  
              DomainInfo = NlFindNetbiosDomain( TransportInfo1[i].svti1_domain, FALSE );
  
              if ( DomainInfo != NULL ) {
  
                  //
                  // Ensure the Hosted server name is identical.
                  //
  
                  if ( TransportInfo1[i].svti1_transportaddresslength !=
                          DomainInfo->DomOemComputerNameLength ||
                       !RtlEqualMemory( TransportInfo1[i].svti1_transportaddress,
                                         DomainInfo->DomOemComputerName,
                                         DomainInfo->DomOemComputerNameLength ) ) {
  
                      TempStatus = RtlOemToUnicodeN(
                                        UnicodeComputerName,
                                        CNLEN*sizeof(WCHAR),
                                        &UnicodeComputerNameSize,
                                        TransportInfo1[i].svti1_transportaddress,
                                        TransportInfo1[i].svti1_transportaddresslength );
  
                      if ( NT_SUCCESS(TempStatus) ) {
                          UnicodeComputerName[UnicodeComputerNameSize/sizeof(WCHAR)] = L'\0';
  
                          NlPrintDom((NL_CRITICAL, DomainInfo,
                                   "NlBrowserSyncHostedDomains: hosted computer name mismatch (SMB server): %ws %ws.\n",
                                   UnicodeComputerName,
                                   DomainInfo->DomUnicodeComputerNameString.Buffer ));
  
                          //
                          // Tell the SMB server the name we have by deleting and re-adding
                          //
  
                          NetStatus = NetServerComputerNameDel(
                                          NULL,
                                          UnicodeComputerName );
  
                          if ( NetStatus != NERR_Success ) {
                              NlPrintDom((NL_CRITICAL, DomainInfo,
                                       "NlBrowserSyncHostedDomains: can't NetServerComputerNameDel: %ws.\n",
                                       UnicodeComputerName ));
                              // This isn't fatal
                          }
  
                          NetStatus = NlServerComputerNameAdd(
                                          DomainInfo->DomUnicodeDomainName,
                                          DomainInfo->DomUnicodeComputerNameString.Buffer );
  
                          if ( NetStatus != NERR_Success ) {
                              NlPrintDom((NL_CRITICAL, DomainInfo,
                                       "NlBrowserSyncHostedDomains: can't NetServerComputerNameAdd: %ws.\n",
                                       DomainInfo->DomUnicodeComputerNameString.Buffer ));
                              // This isn't fatal
                          }
                      }
  
                  }
                  NlDereferenceDomain( DomainInfo );
  
  
              //
              // If we don't have the specified domain,
              //  delete it from the SMB server.
              //
              } else {
                  NlPrint((NL_CRITICAL,"%ws: NlBrowserSyncHostedDomains: SMB server had a hosted domain we didn't (deleting)\n",
                          TransportInfo1[i].svti1_domain ));
  
                  TempStatus = RtlOemToUnicodeN(
                                    UnicodeComputerName,
                                    CNLEN*sizeof(WCHAR),
                                    &UnicodeComputerNameSize,
                                    TransportInfo1[i].svti1_transportaddress,
                                    TransportInfo1[i].svti1_transportaddresslength );
  
                  if ( !NT_SUCCESS(TempStatus) ) {
                      NlPrint((NL_CRITICAL,
                               "%ws: NlBrowserSyncHostedDomains: can't RtlOemToUnicode: %lx.\n",
                               TransportInfo1[i].svti1_domain,
                               TempStatus ));
                      // This isn't fatal
  
                  } else {
                      UnicodeComputerName[UnicodeComputerNameSize/sizeof(WCHAR)] = L'\0';
  
                      // When we really do hosted domains,
                      //  we have to work out a mechanism where the SMB server and
                      //  Netlogon has the same set of hosted domains.
                      //
                      // I ran into a case where netlogon had processed a rename
                      // of the domain and the SMB server hadn't.  In that case,
                      // the code below would delete the primary domain of the SMB
                      // server.
                      //
  
  #ifdef notdef
                      NetStatus = NetServerComputerNameDel(
                                      NULL,
                                      UnicodeComputerName );
  
                      if ( NetStatus != NERR_Success ) {
                          NlPrint((NL_CRITICAL,
                                   "%ws: NlBrowserSyncHostedDomains: can't NetServerComputerNameDel: %ws.\n",
                                   TransportInfo1[i].svti1_domain,
                                   UnicodeComputerName ));
                          // This isn't fatal
                      }
  #endif // notdef
                  }
  
              }
          }
  
  
          (VOID) NetApiBufferFree( TransportInfo1 );
      }
      return;
  }
  
  
  VOID
  NlBrowserUpdate(
      IN PDOMAIN_INFO DomainInfo,
      IN DWORD Role
      )
  /*++
  
  Routine Description:
  
      Tell the browser and SMB server about our new role.
  
  Arguments:
  
      DomainInfo - Hosted domain the name is to be deleted for.
  
      Role - Our new Role.
          RoleInvalid implies the domain is being deleted.
  
  Return Value:
  
      None.
  
  --*/
  {
      NET_API_STATUS NetStatus;
  
      DWORD BrowserRole;
  
      //
      // Initialization.
      //
      switch (Role) {
      case RolePrimary:
          BrowserRole = BROWSER_ROLE_PDC ; break;
      case RoleBackup:
          BrowserRole = BROWSER_ROLE_BDC ; break;
      default:
          // Default to telling the browser to delete the Hosted domain.
          BrowserRole = 0 ; break;
      }
  
  
      //
      // Tell the server what role to announce
      //
  
      if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
          BOOL Ok;
  
          //
          // Since the service controller doesn't have a mechanism to set some
          // bits and turn others off, turn all of the bits off then set the right
          // ones.
          //
          Ok = I_ScSetServiceBits( NlGlobalServiceHandle,
                                   SV_TYPE_DOMAIN_CTRL |
                                       SV_TYPE_DOMAIN_BAKCTRL, // Bits of interest
                                   FALSE,      // Set bits off
                                   FALSE,      // Don't force immediate announcement
                                   NULL);   // All transports
          if ( !Ok ) {
  
              NetStatus = GetLastError();
  
              NlPrint((NL_CRITICAL,"Couldn't I_ScSetServiceBits off %ld 0x%lx.\n",
                      NetStatus, NetStatus ));
              // This isn't fatal
          }
  
          //
          // For the primary domain,
          //  Tell the service controller and let it tell the server service after it
          //  merges the bits from the other services.
          //
          if ( BrowserRole != 0 ) {
              Ok = I_ScSetServiceBits( NlGlobalServiceHandle,
                                       NlServerType(Role),
                                       TRUE,      // Set bits on
                                       TRUE,      // Force immediate announcement
                                       NULL);   // All transports
  
          }
  
          if ( !Ok ) {
  
              NetStatus = GetLastError();
  
              NlPrint((NL_CRITICAL,"Couldn't I_ScSetServiceBits %ld 0x%lx.\n",
                      NetStatus, NetStatus ));
              // This isn't fatal
          }
      } else {
  
          //
          // For domains that aren't the primary domain,
          //  tell the Lanman server directly
          //  (since the service controller doesn't care about those doamins).
          //
          NetStatus = I_NetServerSetServiceBitsEx(
                                  NULL,                       // Local server service
                                  DomainInfo->DomUnicodeComputerNameString.Buffer,
                                  NULL,                       // All transports
                                  SV_TYPE_DOMAIN_CTRL |
                                      SV_TYPE_DOMAIN_BAKCTRL, // Bits of interest
                                  NlServerType(Role),         // New Role
                                  TRUE );                     // Update immediately
  
          if ( NetStatus != NERR_Success ) {
  
              NlPrintDom(( NL_CRITICAL, DomainInfo,
                        "NlBrowserUpdate: Couldn't I_NetServerSetServiceBitsEx %ld 0x%lx.\n",
                        NetStatus, NetStatus ));
              // This isn't fatal
          }
      }
  
  
  #ifdef notdef
      //
      // Tell the browser our role has changed.
      //
  
      // Avoid deleting the primary domain
      if ( BrowserRole != 0 || !IsPrimaryDomain(DomainInfo) ) {
          //  ?? Don't call this function.  This function requires the browser be
          //  running.  Rather, invent an Ioctl to the bowser to enumerate domains
          //  and use that ioctl here.
          //
          // This function serves two purposes: Adding/deleting the hosted domain
          //  in the browser and setting the role.  The first might very well be
          //  a function of the bowser.  The later is naturally a function of
          // the browser service (but should be indirected through the bowser to
          // avoid domain rename issues).
          //
          // When we really do multiple hosted domains, create an emulated domain
          // via one IOCTL to the bowser.  Change it's role via another ioctl
          // to the bowser.  Both calls will result in notifications to the browser
          // service via normal PNP notifications.
          //
          // One might even think that the ioctl to change its role is the one
          // below that adds the 1B name.  That is, if the 1B name is added, then
          // this machine is the PDC.  If not, then it is not the PDC.
          //
          // In the mean time, hack the interface to the browser service indicating
          //  that it should NEVER create an emulated domain based on this call.
          //  Otherwise, on a domain rename, we may end up creating an emulated domain
          //  because our notification and the browser's are asynchronous.
          //
          // Actually, I've updated the bowser to do the 1B trick mentioned above.
          //  So this code merely has to do the right thing for the hosted domain.
          //
  
          NetStatus = I_BrowserSetNetlogonState(
                          NULL,
                          DomainInfo->DomUnicodeDomainName,
                          DomainInfo->DomUnicodeComputerNameString.Buffer,
                          BrowserRole | BROWSER_ROLE_AVOID_CREATING_DOMAIN );
  
          if ( NetStatus != NERR_Success ) {
              if ( BrowserRole != 0 || NetStatus != ERROR_NO_SUCH_DOMAIN ) {
                  NlPrintDom((NL_CRITICAL, DomainInfo,
                          "NlBrowserUpdate: Couldn't I_BrowserSetNetlogonState %ld 0x%lx.\n",
                          NetStatus, NetStatus ));
              }
              // This isn't fatal
          }
      }
  #endif // notdef
  
      //
      // Register or deregister the Domain<1B> name depending on the new role
      //
  
      if ( Role == RolePrimary ) {
          NlBrowserAddName( DomainInfo );
      } else {
          NlBrowserDelName( DomainInfo );
      }
  
      //
      // Tell the SMB server to delete a removed Hosted domain.
      //
  
      if ( Role == RoleInvalid && !IsPrimaryDomain(DomainInfo) ) {
  
          NetStatus = NetServerComputerNameDel(
                          NULL,
                          DomainInfo->DomUnicodeComputerNameString.Buffer );
  
          if ( NetStatus != NERR_Success ) {
              NlPrintDom(( NL_CRITICAL, DomainInfo,
                        "NlBrowserUpdate: Couldn't NetServerComputerNameDel %ld 0x%lx.\n",
                        NetStatus, NetStatus ));
              // This isn't fatal
          }
      }
  
      return;
  
  }
  
  
  
  BOOL
  NlBrowserOpen(
      VOID
      )
  /*++
  
  Routine Description:
  
      This routine opens the NT LAN Man Datagram Receiver driver and prepares
      for reading mailslot messages from it.
  
  Arguments:
  
      None.
  
  Return Value:
  
      TRUE -- iff initialization is successful.
  
      if false, NlExit will already have been called.
  
  --*/
  {
      NTSTATUS Status;
      BOOL ReturnValue;
  
      BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
                  (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
      PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
  
  
      //
      // Allocate the mailslot descriptor for this mailslot
      //
  
      NlGlobalMailslotDesc = NetpMemoryAllocate( sizeof(NETLOGON_MAILSLOT_DESC) );
  
      if ( NlGlobalMailslotDesc == NULL ) {
          NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
          return FALSE;
      }
  
      RtlZeroMemory( NlGlobalMailslotDesc, sizeof(NETLOGON_MAILSLOT_DESC) );
  
      NlGlobalMailslotDesc->CurrentMessage =
          ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
  
  
      //
      // Open the browser device.
      //
  
      Status = NlBrowserOpenDriver( &NlGlobalMailslotDesc->BrowserHandle );
  
      if (! NT_SUCCESS(Status)) {
          NlPrint((NL_CRITICAL,
                   "NtOpenFile browser driver failed: 0x%lx\n",
                   Status));
          ReturnValue = FALSE;
          goto Cleanup;
      }
  
  
      //
      // Create a completion event
      //
  
      NlGlobalMailslotHandle =
          NlGlobalMailslotDesc->BrowserReadEvent = NlBrowserCreateEvent();
  
      if ( NlGlobalMailslotDesc->BrowserReadEvent == NULL ) {
          Status = NetpApiStatusToNtStatus( GetLastError() );
          ReturnValue = FALSE;
          goto Cleanup;
      }
  
  
      //
      // Set the maximum number of messages to be queued
      //
  
      RequestPacket->TransportName.Length = 0;
      RequestPacket->TransportName.Buffer = NULL;
      RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
      RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount =
          NlGlobalMemberWorkstation ?
              1 :
              NlGlobalParameters.MaximumMailslotMessages;
  
      Status = NlBrowserDeviceIoControl(
                     NlGlobalMailslotDesc->BrowserHandle,
                     IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
                     RequestPacket,
                     sizeof(LMDR_REQUEST_PACKET),
                     NULL,
                     0 );
  
      if (! NT_SUCCESS(Status)) {
          NlPrint((NL_CRITICAL,"Can't set browser max message count: 0x%lx\n",
                           Status));
          ReturnValue = FALSE;
          goto Cleanup;
      }
  
  
      ReturnValue = TRUE;
  
  Cleanup:
      if ( !ReturnValue ) {
          NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
  
          NlExit( NELOG_NetlogonBrowserDriver, NetStatus, LogErrorAndNtStatus, NULL);
          NlBrowserClose();
      }
  
      return ReturnValue;
  }
  
  
  VOID
  NlBrowserClose(
      VOID
      )
  /*++
  
  Routine Description:
  
      This routine cleans up after a NlBrowserInitialize()
  
  Arguments:
  
      None.
  
  Return Value:
  
      None.
  
  --*/
  {
      IO_STATUS_BLOCK IoSb;
      NTSTATUS Status;
  
      BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
                  (max(CNLEN, DNLEN) + 1) * sizeof(WCHAR)];
      PLMDR_REQUEST_PACKET RequestPacket = (PLMDR_REQUEST_PACKET) Buffer;
  
      if ( NlGlobalMailslotDesc == NULL) {
          return;
      }
  
  
      //
      //  If we've opened the browser, clean up.
      //
  
      if ( NlGlobalMailslotDesc->BrowserHandle != NULL ) {
  
          //
          // Tell the browser to stop queueing messages
          //
  
          RequestPacket->TransportName.Length = 0;
          RequestPacket->TransportName.Buffer = NULL;
          RtlInitUnicodeString( &RequestPacket->EmulatedDomainName, NULL );
          RequestPacket->Parameters.NetlogonMailslotEnable.MaxMessageCount = 0;
  
          Status = NlBrowserDeviceIoControl(
                         NlGlobalMailslotDesc->BrowserHandle,
                         IOCTL_LMDR_NETLOGON_MAILSLOT_ENABLE,
                         RequestPacket,
                         sizeof(LMDR_REQUEST_PACKET),
                         NULL,
                         0 );
  
          if (! NT_SUCCESS(Status)) {
              NlPrint((NL_CRITICAL,"Can't reset browser max message count: 0x%lx\n",
                               Status));
          }
  
  
          //
          //  Cancel the I/O operations outstanding on the browser.
          //
  
          NtCancelIoFile(NlGlobalMailslotDesc->BrowserHandle, &IoSb);
  
          //
          // Close the handle to the browser
          //
  
          NtClose(NlGlobalMailslotDesc->BrowserHandle);
          NlGlobalMailslotDesc->BrowserHandle = NULL;
      }
  
      //
      // Close the global browser read event
      //
  
      if ( NlGlobalMailslotDesc->BrowserReadEvent != NULL ) {
          NlBrowserCloseEvent(NlGlobalMailslotDesc->BrowserReadEvent);
      }
      NlGlobalMailslotHandle = NULL;
  
      //
      // Free the descriptor describing the browser
      //
  
      NetpMemoryFree( NlGlobalMailslotDesc );
      NlGlobalMailslotDesc = NULL;
  
  }
  
  NTSTATUS
  NlpWriteMailslot(
      IN LPWSTR MailslotName,
      IN LPVOID Buffer,
      IN DWORD BufferSize
      )
  
  /*++
  
  Routine Description:
  
      Write a message to a named mailslot
  
  Arguments:
  
      MailslotName - Unicode name of the mailslot to write to.
  
      Buffer - Data to write to the mailslot.
  
      BufferSize - Number of bytes to write to the mailslot.
  
  Return Value:
  
      NT status code for the operation
  
  --*/
  
  {
      NTSTATUS Status;
      NET_API_STATUS NetStatus;
  
      //
      //  Write the mailslot message.
      //
  
      NetStatus = NetpLogonWriteMailslot( MailslotName, Buffer, BufferSize );
      if ( NetStatus != NERR_Success ) {
          Status = NetpApiStatusToNtStatus( NetStatus );
          NlPrint((NL_CRITICAL, "NetpLogonWriteMailslot failed %lx\n", Status));
          return Status;
      }
  
  #if NETLOGONDBG
      NlPrint(( NL_MAILSLOT,
                "Sent '%s' message to %ws on all transports.\n",
                NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)Buffer)->Opcode),
                MailslotName));
  
      NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
  #endif // NETLOGONDBG
  
      return STATUS_SUCCESS;
  }
  
  NTSTATUS
  NlFlushNetbiosCacheName(
      IN LPCWSTR NetbiosDomainName,
      IN CHAR Extention,
      IN PNL_TRANSPORT Transport
      )
  /*++
  
  Routine Description:
  
      This routine flushes the specified name from the Netbios
      remote cache table.
  
  Arguments:
  
      NetbiosDomainName - The name to be flushed.
  
      Extention - the type of the name (extention added as the
          16th character of the name to flush): 0x00, 0x1C, 0x1B, etc.
  
      Transport - The transport (device) on which the name is to
          be flushed.
  
  Return Value:
  
      STATUS_SUCCESS: The name has been successfully flushed
  
      STATUS_RESOURCE_NAME_NOT_FOUND: The name was not found in the cache
  
      Otherwise, an error returned by NtCreateFile or NtDeviceIoControlFile
  
  --*/
  {
      NTSTATUS        NtStatus = STATUS_SUCCESS;
      IO_STATUS_BLOCK IoStatusBlock;
      CHAR            NameToBeFlushed[NETBIOS_NAMESIZE];
  
      //
      // First open the Netbios device if it hasn't been done already
      //
  
      EnterCriticalSection( &NlGlobalTransportCritSect );
      if ( Transport->DeviceHandle == INVALID_HANDLE_VALUE ) {
          OBJECT_ATTRIBUTES Attributes;
          UNICODE_STRING UnicodeString;
          HANDLE LocalHandle;
  
          RtlInitUnicodeString( &UnicodeString, Transport->TransportName );
  
          InitializeObjectAttributes( &Attributes,
                                      &UnicodeString,
                                      OBJ_CASE_INSENSITIVE,
                                      NULL,
                                      NULL );
  
          NtStatus = NtCreateFile( &LocalHandle,
                                   MAXIMUM_ALLOWED,
                                   &Attributes,
                                   &IoStatusBlock,
                                   NULL,            // allocation size
                                   FILE_ATTRIBUTE_NORMAL,
                                   FILE_SHARE_READ | FILE_SHARE_WRITE,
                                   FILE_OPEN_IF,
                                   0,
                                   NULL,            // no EAs
                                   0 );
  
          if( !NT_SUCCESS(NtStatus) ) {
              LeaveCriticalSection( &NlGlobalTransportCritSect );
              NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: NtCreateFile failed 0x%lx\n",
                        NtStatus ));
              return NtStatus;
          }
  
          Transport->DeviceHandle = LocalHandle;
      }
      LeaveCriticalSection( &NlGlobalTransportCritSect );
  
      //
      // Now form the name to flush
      //
      // Convert to upper case, blank pad to the right
      // and put the appropriate Extension at the end
      //
  
      RtlFillMemory( &NameToBeFlushed, NETBIOS_NAMESIZE, ' ' );
  
      NtStatus = RtlUpcaseUnicodeToOemN( NameToBeFlushed,
                                         NETBIOS_NAMESIZE - 1,  // Maximum for resulting string size
                                         NULL,         // Don't care about the resulting string size
                                         (LPWSTR)NetbiosDomainName,
                                         wcslen(NetbiosDomainName)*sizeof(WCHAR) );
  
      if ( !NT_SUCCESS(NtStatus) ) {
          NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: RtlUpcaseUnicodeToOemN failed 0x%lx\n",
                    NtStatus ));
          return NtStatus;
      }
  
      //
      // Set the appropriate extention
      //
  
      NameToBeFlushed[NETBIOS_NAMESIZE-1] = Extention;
  
      //
      // Finally flush the name from the cache
      //
  
      NtStatus = NtDeviceIoControlFile(
                        Transport->DeviceHandle, // Handle
                        NULL,                    // Event
                        NULL,                    // ApcRoutine
                        NULL,                    // ApcContext
                        &IoStatusBlock,          // IoStatusBlock
                        IOCTL_NETBT_REMOVE_FROM_REMOTE_TABLE,  // IoControlCode
                        NameToBeFlushed,         // InputBuffer
                        sizeof(NameToBeFlushed), // InputBufferSize
                        NULL,                    // OutputBuffer
                        0 );                     // OutputBufferSize
  
      //
      // STATUS_RESOURCE_NAME_NOT_FOUND just means that the name was not in the cache
      //
  
      if ( !NT_SUCCESS(NtStatus) && NtStatus != STATUS_RESOURCE_NAME_NOT_FOUND ) {
          NlPrint(( NL_CRITICAL, "NlFlushNetbiosCacheName: NtDeviceIoControlFile failed 0x%lx\n",
                    NtStatus ));
      }
  
      return NtStatus;
  }
  
  
  NTSTATUS
  NlBrowserSendDatagram(
      IN PVOID ContextDomainInfo,
      IN ULONG IpAddress,
      IN LPWSTR UnicodeDestinationName,
      IN DGRECEIVER_NAME_TYPE NameType,
      IN LPWSTR TransportName,
      IN LPSTR OemMailslotName,
      IN PVOID Buffer,
      IN ULONG BufferSize,
      IN BOOL SendSynchronously,
      IN OUT PBOOL FlushNameOnOneIpTransport OPTIONAL
      )
  /*++
  
  Routine Description:
  
      Send the specified mailslot message to the specified mailslot on the
      specified server on the specified transport..
  
  Arguments:
  
      DomainInfo - Hosted domain sending the datagram
  
      IpAddress - IpAddress of the machine to send the message to.
          If zero, UnicodeDestinationName must be specified.
          If ALL_IP_TRANSPORTS, UnicodeDestination must be specified but the datagram
              will only be sent on IP transports.
  
      UnicodeDestinationName -- Name of the server to send to.
  
      NameType -- Type of name represented by UnicodeDestinationName.
  
      TransportName -- Name of the transport to send on.
          Use NULL to send on all transports.
  
      OemMailslotName -- Name of the mailslot to send to.
  
      Buffer -- Specifies a pointer to the mailslot message to send.
  
      BufferSize -- Size in bytes of the mailslot message
  
      SendSynchronously -- If TRUE, the send will happen syncronously (i.e. the
          send will not return until the network I/O completes). Otherwise, the
          send will happen asynchronously (i.e. it will be queued for processing).
  
      FlushNameOnOneIpTransport -- Used only if we send on all transports (i.e.
          TransportName is NULL), otherwise ignored.  If TRUE, the name specified
          by UnicodeDestinationName will be flushed on one of the available IP
          enabled transports prior to sending the datagram. On return, set to
          FALSE if the name has been successfully flushed or the name was not
          found in the cache.
  
  Return Value:
  
      Status of the operation.
  
      STATUS_NETWORK_UNREACHABLE: Cannot write to network.
  
  --*/
  {
      PLMDR_REQUEST_PACKET RequestPacket = NULL;
      PDOMAIN_INFO DomainInfo = (PDOMAIN_INFO) ContextDomainInfo;
  
      DWORD OemMailslotNameSize;
      DWORD TransportNameSize;
      DWORD DestinationNameSize;
  
      NTSTATUS Status;
      LPBYTE Where;
  
      //
      // If the transport isn't specified,
      //  send on all transports.
      //
  
      if ( TransportName == NULL ) {
          ULONG i;
          PLIST_ENTRY ListEntry;
          NTSTATUS SavedStatus = STATUS_NETWORK_UNREACHABLE;
          ULONG TransportCount = 0;
          ULONG BadNetPathCount = 0;
  
          //
          // Send on all transports.
          //
  
          EnterCriticalSection( &NlGlobalTransportCritSect );
          for ( ListEntry = NlGlobalTransportList.Flink ;
                ListEntry != &NlGlobalTransportList ;
                ListEntry = ListEntry->Flink) {
  
              PNL_TRANSPORT TransportEntry;
  
              TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  
              //
              // Skip deleted transports.
              //
              if ( !TransportEntry->TransportEnabled ) {
                  continue;
              }
  
              //
              // Skip direct host IPX transport unless sending to a particular
              // machine.
              //
  
              if ( TransportEntry->DirectHostIpx &&
                   NameType != ComputerName ) {
                  continue;
              }
  
              //
              // Skip non-IP transports if sending to an IP address.
              //
  
              if ( IpAddress != 0  &&
                   TransportEntry->IpAddress == 0 ) {
                  continue;
              }
  
              //
              // Leave the critical section before sending the datagram
              // because NetBt now doesn't return from the datagram send
              // until after the name lookup completes.  So, it can take
              // a considerable amount of time for the datagram send to
              // return to us.
              //
  
              LeaveCriticalSection( &NlGlobalTransportCritSect );
  
              //
              // If this is IP transport, flush the name if requested
              //
  
              if ( FlushNameOnOneIpTransport != NULL &&
                   *FlushNameOnOneIpTransport &&
                   TransportEntry->IsIpTransport ) {
  
                  NTSTATUS TmpStatus;
                  CHAR Extention;
  
                  if ( NameType == ComputerName ) {
                      Extention = 0x00;
                  } else if ( NameType == DomainName ) {
                      Extention = 0x1C;
                  } else if ( NameType == PrimaryDomainBrowser ) {
                      Extention = 0x1B;
                  } else {
                      NlAssert( !"[NETLOGON] Unexpected name type passed to NlBrowserSendDatagram" );
                  }
  
                  TmpStatus = NlFlushNetbiosCacheName( UnicodeDestinationName,
                                                       Extention,
                                                       TransportEntry );
  
                  //
                  // If we successfully flushed the name or the name is not in the cache,
                  //  indicate that the name has been flushed
                  //
                  if ( NT_SUCCESS(TmpStatus) || TmpStatus == STATUS_RESOURCE_NAME_NOT_FOUND ) {
                      *FlushNameOnOneIpTransport = FALSE;
                  }
              }
  
              Status = NlBrowserSendDatagram(
                                DomainInfo,
                                IpAddress,
                                UnicodeDestinationName,
                                NameType,
                                TransportEntry->TransportName,
                                OemMailslotName,
                                Buffer,
                                BufferSize,
                                SendSynchronously,
                                FlushNameOnOneIpTransport );
  
              EnterCriticalSection( &NlGlobalTransportCritSect );
  
              //
              // Since a TransportEntry is never removed from the global
              // transport list (it can only become marked as disabled
              // during the time when we had the crit sect released),
              // we should be able to follow its link to the next entry in
              // the global list on the next iteration of the loop.  The
              // only problem can occur when the service was said to terminate
              // and NlTransportClose was called to free up the global list.
              // In this case NlGlobalTerminate is set to TRUE so we can
              // successfully return from this routine.
              //
  
              if ( NlGlobalTerminate ) {
                  LeaveCriticalSection( &NlGlobalTransportCritSect );
                  Status = STATUS_SUCCESS;
                  goto Cleanup;
              }
  
              TransportCount ++;
              if ( NT_SUCCESS(Status) ) {
                  // If any transport works, we've been successful
                  SavedStatus = STATUS_SUCCESS;
              } else if ( Status == STATUS_BAD_NETWORK_PATH ) {
                  // Count the number of transports that couldn't resolve the name
                  BadNetPathCount ++;
              } else {
                  // Remember the real reason for the failure instead of the default failure status
                  // Remember only the first failure.
                  if ( SavedStatus == STATUS_NETWORK_UNREACHABLE ) {
                      SavedStatus = Status;
                  }
              }
  
          }
          LeaveCriticalSection( &NlGlobalTransportCritSect );
  
          //
          // If we're returning the default status,
          //  and at least one transport couldn't resolved the name,
          //  and all transports couldn't resolve the name,
          //  tell the caller we couldn't resolve the name.
          //
  
          if (  SavedStatus == STATUS_NETWORK_UNREACHABLE &&
                BadNetPathCount > 0 &&
                TransportCount == BadNetPathCount ) {
              SavedStatus = STATUS_BAD_NETWORK_PATH;
          }
  
          //
          // If we have no transports available,
          //  tell the caller we couldn't resolve the name
          //
  
          if ( TransportCount == 0 ) {
              NlPrint(( NL_CRITICAL, "NlBrowserSendDatagram: No transports available\n" ));
              SavedStatus = STATUS_BAD_NETWORK_PATH;
          }
  
          return SavedStatus;
      }
  
      //
      // Allocate a request packet.
      //
  
      OemMailslotNameSize = strlen(OemMailslotName) + 1;
      TransportNameSize = (wcslen(TransportName) + 1) * sizeof(WCHAR);
  
      if ( UnicodeDestinationName == NULL ) {
          return STATUS_INTERNAL_ERROR;
      }
  
      DestinationNameSize = wcslen(UnicodeDestinationName) * sizeof(WCHAR);
  
      RequestPacket = NetpMemoryAllocate(
                                    sizeof(LMDR_REQUEST_PACKET) +
                                    TransportNameSize +
                                    OemMailslotNameSize +
                                    DestinationNameSize + sizeof(WCHAR) +
                                    DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR) +
                                    sizeof(WCHAR)) ; // For alignment
  
      if (RequestPacket == NULL) {
          Status = STATUS_NO_MEMORY;
          goto Cleanup;
      }
  
  
  
      //
      // Fill in the Request Packet.
      //
  
      RequestPacket->Type = Datagram;
      RequestPacket->Parameters.SendDatagram.DestinationNameType = NameType;
  
  
      //
      // Fill in the name of the machine to send the mailslot message to.
      //
  
      RequestPacket->Parameters.SendDatagram.NameLength = DestinationNameSize;
  
      Where = (LPBYTE) RequestPacket->Parameters.SendDatagram.Name;
      RtlCopyMemory( Where, UnicodeDestinationName, DestinationNameSize );
      Where += DestinationNameSize;
  
  
      //
      // Fill in the name of the mailslot to send to.
      //
  
      RequestPacket->Parameters.SendDatagram.MailslotNameLength =
          OemMailslotNameSize;
      strcpy( Where, OemMailslotName);
      Where += OemMailslotNameSize;
      Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
  
  
      //
      // Fill in the TransportName
      //
  
      wcscpy( (LPWSTR) Where, TransportName);
      RtlInitUnicodeString( &RequestPacket->TransportName, (LPWSTR) Where );
      Where += TransportNameSize;
  
  
      //
      // Copy the hosted domain name to the request packet.
      //
      wcscpy( (LPWSTR)Where,
              DomainInfo->DomUnicodeDomainNameString.Buffer );
      RtlInitUnicodeString( &RequestPacket->EmulatedDomainName,
                            (LPWSTR)Where );
      Where += DomainInfo->DomUnicodeDomainNameString.Length + sizeof(WCHAR);
  
  
  
      //
      // Send the request to the browser.
      //
  
  
      Status = NlBrowserDeviceIoControl(
                     NlGlobalMailslotDesc->BrowserHandle,
                     SendSynchronously ?
                       IOCTL_LMDR_WRITE_MAILSLOT :
                       IOCTL_LMDR_WRITE_MAILSLOT_ASYNC,
                     RequestPacket,
                     (ULONG)(Where - (LPBYTE)RequestPacket),
                     Buffer,
                     BufferSize );
  
  
      //
      // Free locally used resources.
      //
  Cleanup:
  
      if ( RequestPacket != NULL ) {
          NetpMemoryFree( RequestPacket );
      }
  
  
      NlpDumpBuffer( NL_MAILSLOT_TEXT, Buffer, BufferSize );
  
      // NlPrint(( NL_MAILSLOT, "Transport %ws 0x%lx\n", TransportName, Status ));
  
      return Status;
  }
  
  
  NTSTATUS
  NlBrowserSendDatagramA(
      IN PDOMAIN_INFO DomainInfo,
      IN ULONG IpAddress,
      IN LPSTR OemServerName,
      IN DGRECEIVER_NAME_TYPE NameType,
      IN LPWSTR TransportName,
      IN LPSTR OemMailslotName,
      IN PVOID Buffer,
      IN ULONG BufferSize
      )
  /*++
  
  Routine Description:
  
      Send asynchronously the specified mailslot message to the specified
      mailslot on the specified server on the specified transport.
  
  Arguments:
  
      DomainInfo - Hosted domain sending the datagram
  
      IpAddress - IpAddress of the machine to send the message to.
          If zero, OemServerName must be specified.
  
      OemServerName -- Name of the server to send to.
  
      NameType -- Type of name represented by OemServerName.
  
      TransportName -- Name of the transport to send on.
          Use NULL to send on all transports.
  
      OemMailslotName -- Name of the mailslot to send to.
  
      Buffer -- Specifies a pointer to the mailslot message to send.
  
      BufferSize -- Size in bytes of the mailslot message
  
  Return Value:
  
      Status of the operation.
  
  --*/
  {
      NET_API_STATUS NetStatus;
      WCHAR UnicodeDestinationName[CNLEN+1];
  
  
      //
      // Convert DestinationName to unicode
      //
  
      NetStatus = NetpNCopyStrToWStr(
                      UnicodeDestinationName,
                      OemServerName,
                      CNLEN );
  
      if ( NetStatus != NERR_Success ) {
          return NetpApiStatusToNtStatus( NetStatus );
      }
  
      UnicodeDestinationName[CNLEN] = L'\0';
  
      //
      // Pass the request to the function taking unicode destination name.
      //
  
      return NlBrowserSendDatagram(
                      DomainInfo,
                      IpAddress,
                      UnicodeDestinationName,
                      NameType,
                      TransportName,
                      OemMailslotName,
                      Buffer,
                      BufferSize,
                      FALSE,   // send synchronously ?
                      NULL );  // Don't flush Netbios cache
  
  }
  
  
  
  
  VOID
  NlMailslotPostRead(
      IN BOOLEAN IgnoreDuplicatesOfPreviousMessage
      )
  
  /*++
  
  Routine Description:
  
      Post a read on the mailslot if one isn't already posted.
  
  Arguments:
  
      IgnoreDuplicatesOfPreviousMessage - TRUE to indicate that the next
          message read should be ignored if it is a duplicate of the previous
          message.
  
  Return Value:
  
      TRUE -- iff successful.
  
  --*/
  {
      NET_API_STATUS WinStatus;
      ULONG LocalBytesRead;
  
      //
      // If a read is already pending,
      //  immediately return to caller.
      //
  
      if ( NlGlobalMailslotDesc->ReadPending ) {
          return;
      }
  
      //
      // Decide which buffer to read into.
      //
      // Switch back and forth so we always have the current buffer and the
      // previous buffer.
      //
  
      if ( IgnoreDuplicatesOfPreviousMessage ) {
          NlGlobalMailslotDesc->PreviousMessage = NlGlobalMailslotDesc->CurrentMessage;
          if ( NlGlobalMailslotDesc->CurrentMessage >= NlGlobalMailslotDesc->Message2 ) {
              NlGlobalMailslotDesc->CurrentMessage =
                  ROUND_UP_POINTER( NlGlobalMailslotDesc->Message1, ALIGN_WORST);
          } else {
              NlGlobalMailslotDesc->CurrentMessage =
                  ROUND_UP_POINTER( NlGlobalMailslotDesc->Message2, ALIGN_WORST);
          }
  
      //
      // If duplicates of the previous message need not be ignored,
      //  indicate so.
      //  Don't bother switching the buffer pointers.
      //
  
      } else {
          NlGlobalMailslotDesc->PreviousMessage = NULL;
      }
  
  
      //
      // Post an overlapped read to the mailslot.
      //
  
      RtlZeroMemory( &NlGlobalMailslotDesc->Overlapped,
                     sizeof(NlGlobalMailslotDesc->Overlapped) );
  
      NlGlobalMailslotDesc->Overlapped.hEvent = NlGlobalMailslotDesc->BrowserReadEvent;
  
      if ( !DeviceIoControl(
                     NlGlobalMailslotDesc->BrowserHandle,
                     IOCTL_LMDR_NETLOGON_MAILSLOT_READ,
                     NULL,
                     0,
                     NlGlobalMailslotDesc->CurrentMessage,
                     MAILSLOT_MESSAGE_SIZE,
                     &LocalBytesRead,
                     &NlGlobalMailslotDesc->Overlapped )) {
  
          WinStatus = GetLastError();
  
          //
          // On error, wait a second before returning.  This ensures we don't
          //  consume the system in an infinite loop.  We don't shutdown netlogon
          //  because the error might be a temporary low memory condition.
          //
  
          if(  WinStatus != ERROR_IO_PENDING ) {
              LPWSTR MsgStrings[1];
  
              NlPrint((NL_CRITICAL,
                      "Error in reading mailslot message from browser"
                      ". WinStatus = %ld\n",
                      WinStatus ));
  
              MsgStrings[0] = (LPWSTR) ULongToPtr( WinStatus );
  
              NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
                                EVENTLOG_WARNING_TYPE,
                                (LPBYTE)&WinStatus,
                                sizeof(WinStatus),
                                MsgStrings,
                                1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  
              Sleep( 1000 );
  
          } else {
              NlGlobalMailslotDesc->ReadPending = TRUE;
          }
  
      } else {
          NlGlobalMailslotDesc->ReadPending = TRUE;
      }
  
      return;
  
  }
  
  
  BOOL
  NlMailslotOverlappedResult(
      OUT LPBYTE *Message,
      OUT PULONG BytesRead,
      OUT LPWSTR *TransportName,
      OUT PNL_TRANSPORT *Transport,
      OUT PSOCKADDR *ClientSockAddr,
      OUT LPWSTR *DestinationName,
      OUT PBOOLEAN IgnoreDuplicatesOfPreviousMessage,
      OUT PNETLOGON_PNP_OPCODE NlPnpOpcode
      )
  
  /*++
  
  Routine Description:
  
      Get the overlapped result of a previous mailslot read.
  
  Arguments:
  
      Message - Returns a pointer to the buffer containing the message
  
      BytesRead - Returns the number of bytes read into the buffer
  
      TransportName - Returns a pointer to the name of the transport the message
          was received on.
  
      Transport - Returns a pointer to the Transport structure if this is a
          mailslot message.
  
      ClientSockAddr - Returns a pointer to the SockAddr of the client that
          sent the message.
          Returns NULL if transport isn't running IP.
  
      DestinationName - Returns a pointer to the name of the server or domain
          the message was sent to.
  
      IgnoreDuplicatesOfPreviousMessage - Indicates that duplicates of the
          previous message are to be ignored.
  
      NpPnpOpcode - Returns the PNP opcode if this is a PNP operation.
          Returns NlPnpMailslotMessage if this is a mailslot message.
  
  Return Value:
  
      TRUE -- iff successful.
  
  --*/
  {
      NET_API_STATUS WinStatus;
      ULONG LocalBytesRead;
      PNETLOGON_MAILSLOT NetlogonMailslot;
  
      //
      // Default to not ignoring duplicate messages.
      //  (Only ignore duplicates if a message has been properly processed.)
  
      *IgnoreDuplicatesOfPreviousMessage = FALSE;
  
      //
      // By default, assume a mailslot message is available.
      *NlPnpOpcode = NlPnpMailslotMessage;
  
      //
      // Always post another read regardless of the success or failure of
      //  GetOverlappedResult.
      // We don't know the failure mode of GetOverlappedResult, so we don't
      // know in the failure case if we're discarding a mailslot message.
      // But we do know that there is no read pending, so make sure we
      // issue another one.
      //
  
      NlGlobalMailslotDesc->ReadPending = FALSE; // no read pending anymore
  
  
      //
      // Get the result of the last read
      //
  
      if( !GetOverlappedResult( NlGlobalMailslotDesc->BrowserHandle,
                                &NlGlobalMailslotDesc->Overlapped,
                                &LocalBytesRead,
                                TRUE) ) {    // wait for the read to complete.
  
          LPWSTR MsgStrings[1];
  
          // On error, wait a second before returning.  This ensures we don't
          //  consume the system in an infinite loop.  We don't shutdown netlogon
          //  because the error might be a temporary low memory condition.
          //
  
          WinStatus = GetLastError();
  
          NlPrint((NL_CRITICAL,
                  "Error in GetOverlappedResult on mailslot read"
                  ". WinStatus = %ld\n",
                  WinStatus ));
  
          MsgStrings[0] = (LPWSTR) ULongToPtr( WinStatus );
  
          NlpWriteEventlog( NELOG_NetlogonFailedToReadMailslot,
                            EVENTLOG_WARNING_TYPE,
                            (LPBYTE)&WinStatus,
                            sizeof(WinStatus),
                            MsgStrings,
                            1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  
          Sleep( 1000 );
  
          return FALSE;
  
      }
  
      //
      // On success,
      //  Return the mailslot message to the caller.
  
  
      NetlogonMailslot = (PNETLOGON_MAILSLOT) NlGlobalMailslotDesc->CurrentMessage;
  
  
      //
      // Return pointers into the buffer returned by the browser
      //
  
      *Message = &NlGlobalMailslotDesc->CurrentMessage[
                      NetlogonMailslot->MailslotMessageOffset];
      *TransportName = (LPWSTR) &NlGlobalMailslotDesc->CurrentMessage[
                      NetlogonMailslot->TransportNameOffset];
      if ( NetlogonMailslot->ClientSockAddrSize == 0 ) {
          *ClientSockAddr = NULL;
      } else {
          *ClientSockAddr = (PSOCKADDR) &NlGlobalMailslotDesc->CurrentMessage[
                          NetlogonMailslot->ClientSockAddrOffset];
      }
  
      //
      // If this is a PNP notification,
      //  simply return the opcode and the transport name.
      //
  
      if ( NetlogonMailslot->MailslotNameSize == 0 ) {
          *NlPnpOpcode = NetlogonMailslot->MailslotNameOffset;
          *Message = NULL;
          *BytesRead = 0;
          *DestinationName = NULL;
          *Transport = NULL;
  
          NlPrint(( NL_MAILSLOT_TEXT,
                    "Received PNP opcode 0x%x on transport: %ws\n",
                    *NlPnpOpcode,
                    *TransportName ));
  
      //
      // If this is a mailslot message,
      //  return the message to the caller.
      //
  
      } else {
  
          *BytesRead = NetlogonMailslot->MailslotMessageSize;
          *DestinationName = (LPWSTR) &NlGlobalMailslotDesc->CurrentMessage[
                          NetlogonMailslot->DestinationNameOffset];
  
          //
          // Determine the transport the request came in on.
          //
  
          *Transport = NlTransportLookupTransportName( *TransportName );
  
          if ( *Transport == NULL ) {
              NlPrint((NL_CRITICAL,
                      "%ws: Received message for this unsupported transport\n",
                      *TransportName ));
              return FALSE;
          }
  
          //
          // Determine if we can discard an ancient or duplicate message
          //
          // Only discard messages that are either expensive to process on this
          // machine or generate excessive traffic to respond to.  Don't discard
          // messages that we've worked hard to get (e.g., discovery responses).
          //
  
          switch ( ((PNETLOGON_LOGON_QUERY)*Message)->Opcode) {
          case LOGON_REQUEST:
          case LOGON_SAM_LOGON_REQUEST:
          case LOGON_PRIMARY_QUERY:
  
              //
              // If the message is too old,
              //  discard it.
              //
  
              if ( NlTimeHasElapsedEx( &NetlogonMailslot->TimeReceived,
                                       &NlGlobalParameters.MailslotMessageTimeout_100ns,
                                       NULL )) {
  
  #if NETLOGONDBG
                  NlPrint(( NL_MAILSLOT,
                            "%ws: Received '%s' message on %ws:"
                                  " (Discarded as too old.)\n",
                            *DestinationName,
                            NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
                            *TransportName ));
  #endif // NETLOGONDBG
                  return FALSE;
              }
  
              //
              // If the previous message was recent,
              //  and this message is identical to it,
              //  discard the current message.
              //
  
  #ifdef notdef
              NlPrint(( NL_MAILSLOT, "%ws: test prev\n", *DestinationName ));
  #endif // notdef
  
              if ( NlGlobalMailslotDesc->PreviousMessage != NULL ) {
                  PNETLOGON_MAILSLOT PreviousNetlogonMailslot;
  
                  PreviousNetlogonMailslot = (PNETLOGON_MAILSLOT)
                      NlGlobalMailslotDesc->PreviousMessage;
  
  #ifdef notdef
                  NlPrint(( NL_MAILSLOT, "%ws: test time\n", *DestinationName ));
  #endif // notdef
  
                  // ??: Compare source netbios name?
                  if ( (PreviousNetlogonMailslot->TimeReceived.QuadPart +
                       NlGlobalParameters.MailslotDuplicateTimeout_100ns.QuadPart >
                       NetlogonMailslot->TimeReceived.QuadPart) ) {
  
  #ifdef notdef
                      NlPrint(( NL_MAILSLOT, "%ws: test message\n", *DestinationName ));
  #endif // notdef
  
                      if ( (PreviousNetlogonMailslot->MailslotMessageSize ==
                           NetlogonMailslot->MailslotMessageSize) &&
  
                           RtlEqualMemory(
                              &NlGlobalMailslotDesc->CurrentMessage[
                                  NetlogonMailslot->MailslotMessageOffset],
                              &NlGlobalMailslotDesc->PreviousMessage[
                                  PreviousNetlogonMailslot->MailslotMessageOffset],
                              NetlogonMailslot->MailslotMessageSize ) ) {
  
  
                          //
                          // Ensure the next comparison is to the timestamp of the
                          // message we actually responded to.
                          //
  
                          NetlogonMailslot->TimeReceived =
                              PreviousNetlogonMailslot->TimeReceived;
  
  
                          NlPrint(( NL_MAILSLOT,
                                    "%ws: Received '%s' message on %ws:"
                                          " (Discarded as duplicate of previous.)\n",
                                    *DestinationName,
                                    NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
                                    *TransportName ));
  
                          *IgnoreDuplicatesOfPreviousMessage = TRUE;
                          return FALSE;
  
                      }
                  }
              }
  
              //
              // If this isn't an IP transport,
              //  and if the caller explicitly wanted one,
              //  discard the message.
              //
              // NT 5 only sends the query on IP when netlogon is running.
              // When Netlogon isn't running, the query is sent on all transports
              //  bound to the redir.  Since this DC ignores duplicate messages,
              //  we want to avoid responding to the non-IP requests or we'll
              //  ignore the IP query as being a duplicate of this one.
              //
              // WIN 98 with the Active Directory service pack also sets this bit
              // and sends on all transports.
              //
  
              if ( !(*Transport)->IsIpTransport ) {
                  DWORD Version;
                  DWORD VersionFlags;
                  DWORD LocalBytesRead;
  
                  LocalBytesRead = *BytesRead;
  
                  Version = NetpLogonGetMessageVersion( *Message,
                                                        &LocalBytesRead,
                                                        &VersionFlags );
  
                  if ( VersionFlags & NETLOGON_NT_VERSION_IP ) {
  
                      NlPrint(( NL_MAILSLOT,
                                "%ws: Received '%s' message on %ws:"
                                      " (Caller wants response on IP transport.)\n",
                                *DestinationName,
                                NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
                                *TransportName ));
  
                      return FALSE;
                  }
              }
          }
  
          NlPrint(( NL_MAILSLOT,
                    "%ws: Received '%s' message on %ws\n",
                    *DestinationName,
                    NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)*Message)->Opcode),
                    *TransportName ));
  
          NlpDumpBuffer(NL_MAILSLOT_TEXT, *Message, *BytesRead);
      }
  
      return TRUE;
  
  }
  
  NET_API_STATUS
  NlServerComputerNameAdd(
      IN LPWSTR HostedDomainName,
      IN LPWSTR HostedServerName
  )
  /*++
  
  Routine Description:
  
      This routine causes the SMB server to respond to requests on HostedServerName
      and to announce this servername as being a member of HostedDomainName.
  
      This code was stolen from NetServerComputerNameAdd.  It is different from that
      API in the following ways:
  
      1) It only works locally.
      2) HostedDomainName is not optional.
      3) Failure to add the name on any transport fails the routine
  
  Arguments:
  
      HostedServerName --A pointer to the ASCIIZ string containing the
          name which the server should stop supporting
  
      HostedDomainName --A pointer to the ASCIIZ string containing the
          domain name the server should use when announcing the presence of
          'HostedServerName'
  
  Return Value:
  
      NERR_Success, or reason for failure
  
  --*/
  {
      DWORD resumehandle = 0;
      NET_API_STATUS retval;
      DWORD entriesread, totalentries;
      DWORD i, j;
      UCHAR NetBiosName[ MAX_PATH ];
      OEM_STRING NetBiosNameString;
      UNICODE_STRING UniName;
      PSERVER_TRANSPORT_INFO_1 psti1;
  
      //
      // Ensure a valid HostedServerName was passed in
      //
      if( HostedServerName == NULL ) {
          return ERROR_INVALID_PARAMETER;
      }
  
      //
      // Convert the HostedServerName to an OEM string
      //
      RtlInitUnicodeString( &UniName, HostedServerName );
      NetBiosNameString.Buffer = (PCHAR)NetBiosName;
      NetBiosNameString.MaximumLength = sizeof( NetBiosName );
      (VOID) RtlUpcaseUnicodeStringToOemString(
                                  &NetBiosNameString,
                                  &UniName,
                                  FALSE
                                  );
  
  
      //
      // Enumerate all the transports so we can add the name and domain
      //  to each one.
      //
      retval = NetServerTransportEnum ( NULL,
                                        1,
                                        (LPBYTE *)&psti1,
                                        (DWORD)-1,
                                        &entriesread,
                                        &totalentries,
                                        &resumehandle );
      if( retval == NERR_Success ) {
          //
          // Add the new name and domain to all of the transports
          //
          for( i=0; i < entriesread; i++ ) {
  
              //
              // Make sure we haven't already added to this transport
              //
              for( j = 0; j < i; j++ ) {
                  if( wcscmp( psti1[j].svti1_transportname, psti1[i].svti1_transportname ) == 0 ) {
                      break;
                  }
              }
  
              if( i != j ) {
                  psti1[i].svti1_transportname[0] = '\0';
                  continue;
              }
  
              psti1[i].svti1_transportaddress = NetBiosName;
              psti1[i].svti1_transportaddresslength = strlen( NetBiosName );
              psti1[i].svti1_domain = HostedDomainName;
  
              retval = NetServerTransportAddEx( NULL, 1, (LPBYTE)&psti1[ i ]  );
  
  #ifndef NWLNKIPX_WORKS
              //
              // ??: The SMB server doesn't allow multiple names on NWLNK IPX.
              //
  
              if ( retval == ERROR_TOO_MANY_NAMES &&
                   _wcsicmp( psti1[i].svti1_transportname, L"\\Device\\NwlnkIpx" ) == 0 ) {
                  retval = NERR_Success;
              }
  #endif // NWLNKIPX_WORKS
  
              if( retval != NERR_Success ) {
  
                  NlPrint((NL_CRITICAL,
                           "%ws: NlServerComputerNameAdd: Cannot add %ws to SMB server on transport %ws %ld\n",
                           HostedDomainName,
                           HostedServerName,
                           psti1[i].svti1_transportname,
                           retval ));
  
                  //
                  // Remove any names already added.
                  //
  
                  for( j=0; j < i; j++ ) {
                      NET_API_STATUS TempStatus;
  
                      if ( psti1[j].svti1_transportname[0] == '\0' ) {
                          continue;
                      }
  
                      TempStatus = NetServerTransportDel( NULL, 1, (LPBYTE)&psti1[ j ]  );
  
                      NlPrint((NL_CRITICAL,
                               "%ws: NlServerComputerNameAdd: Cannot remove %ws to SMB server on transport %ws %ld\n",
                               HostedDomainName,
                               HostedServerName,
                               psti1[i].svti1_transportname,
                               TempStatus ));
                  }
                  break;
              }
          }
  
          MIDL_user_free( psti1 );
      }
  
  
      return retval;
  }
  