text: /*++
  
  Copyright (c) 1989-2001  Microsoft Corporation
  
  Module Name:
  
      name.c
  
  Abstract:
  
      Local Address
  
  Author:
  
      Jiandong Ruan
  
  Revision History:
  
  --*/
  
  #include "precomp.h"
  #include "name.tmh"
  
  //#pragma alloc_text(PAGE, SmbCreateClient)
  //#pragma alloc_text(PAGE, SmbCloseClient)
  
  #define TA_ADDRESS_HEADER_SIZE      (FIELD_OFFSET(TA_ADDRESS,Address))
  
  VOID
  SmbDeleteClient(PSMB_CLIENT_ELEMENT ob)
  {
      KIRQL   Irql;
  
      SMB_ACQUIRE_SPINLOCK(ob->Device, Irql);
      ASSERT(EntryIsInList(&ob->Device->PendingDeleteClientList, &ob->Linkage));
      RemoveEntryList(&ob->Linkage);
      SMB_RELEASE_SPINLOCK(ob->Device, Irql);
  
      ExFreePool(ob);
  
      SmbPrint(SMB_TRACE_CALL, ("SmbDeleteClient: free Client %p\n", ob));
  }
  
  NTSTATUS
  SmbCreateClient(
      IN PSMB_DEVICE  Device,
      IN PIRP         Irp,
      PFILE_FULL_EA_INFORMATION   ea
      )
  {
      TRANSPORT_ADDRESS UNALIGNED     *pTransportAddr;
      PTA_ADDRESS                     pAddress;
      PTDI_ADDRESS_NETBIOS            pNetbiosAddr;
      PTDI_ADDRESS_NETBIOS_EX         pNetbiosAddrEx;
      PTDI_ADDRESS_NETBIOS_UNICODE_EX pUnicodeEx;
      PSMB_CLIENT_ELEMENT             ClientObject;
      PIO_STACK_LOCATION              IrpSp;
      UNICODE_STRING                  ucName;
  
      int                             i;
      DWORD                           RemainingBufferLength;
      CHAR                            Name[NETBIOS_NAME_SIZE + 1];
      USHORT                          NameType;
      OEM_STRING                      OemString;
      KIRQL                           Irql;
  
      PAGED_CODE();
  
      SmbPrint(SMB_TRACE_CALL, ("Enter SmbCreateClient\n"));
      pTransportAddr = (PTRANSPORT_ADDRESS)(((PUCHAR)ea->EaName) + ea->EaNameLength + 1);
      RemainingBufferLength = ea->EaValueLength;
      if (RemainingBufferLength < sizeof(TA_NETBIOS_ADDRESS)) {
          return STATUS_INVALID_ADDRESS_COMPONENT;
      }
  
      RemainingBufferLength -= sizeof(pTransportAddr->TAAddressCount);
      pAddress = (PTA_ADDRESS)pTransportAddr->Address;
  
      for (i = 0; i < pTransportAddr->TAAddressCount; i++) {
          //
          // First, make sure we can safely access pAddress->AddressLength
          //
          if (RemainingBufferLength < TA_ADDRESS_HEADER_SIZE) {
              return STATUS_INVALID_ADDRESS_COMPONENT;
          }
          RemainingBufferLength -= TA_ADDRESS_HEADER_SIZE;
          if (RemainingBufferLength < pAddress->AddressLength) {
              return STATUS_INVALID_ADDRESS_COMPONENT;
          }
  
          if (TDI_ADDRESS_TYPE_NETBIOS == pAddress->AddressType) {
  
              if (pAddress->AddressLength < sizeof(TDI_ADDRESS_NETBIOS)) {
                  return STATUS_INVALID_ADDRESS_COMPONENT;
              }
  
              pNetbiosAddr = (PTDI_ADDRESS_NETBIOS)pAddress->Address;
              RtlCopyMemory(Name, pNetbiosAddr->NetbiosName, NETBIOS_NAME_SIZE);
              NameType = pNetbiosAddr->NetbiosNameType;
              break;
  
          } else if (TDI_ADDRESS_TYPE_NETBIOS_EX == pAddress->AddressType) {
  
              if (pAddress->AddressLength < sizeof(TDI_ADDRESS_NETBIOS_EX)) {
                  return STATUS_INVALID_ADDRESS_COMPONENT;
              }
              pNetbiosAddrEx = (PTDI_ADDRESS_NETBIOS_EX)pAddress->Address;
              RtlCopyMemory(Name, pNetbiosAddrEx->EndpointName, NETBIOS_NAME_SIZE);
              NameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE;
              break;
  
          } else if (TDI_ADDRESS_TYPE_NETBIOS_UNICODE_EX == pAddress->AddressType) {
  
              if (pAddress->AddressLength < sizeof(TDI_ADDRESS_NETBIOS_UNICODE_EX)) {
                  return STATUS_INVALID_ADDRESS_COMPONENT;
              }
              pUnicodeEx = (PTDI_ADDRESS_NETBIOS_UNICODE_EX)pAddress->Address;
              OemString.Buffer = Name;
              OemString.Length = 0;
              OemString.MaximumLength = sizeof(Name);
  
              ucName = pUnicodeEx->EndpointName;
              RtlUpcaseUnicodeStringToOemString(&OemString, &ucName, FALSE);
              NameType = TDI_ADDRESS_NETBIOS_TYPE_QUICK_UNIQUE;
              break;
  
          }
  
          RemainingBufferLength -= pAddress->AddressLength;
          pAddress = (PTA_ADDRESS)(((PUCHAR)pAddress) + pAddress->AddressLength + TA_ADDRESS_HEADER_SIZE);
      }
      if (i >= pTransportAddr->TAAddressCount) {
          return STATUS_INVALID_ADDRESS_COMPONENT;
      }
  
      //
      // We have the name and name type
      //
      Name[NETBIOS_NAME_SIZE] = 0;
  
      ClientObject = ExAllocatePoolWithTag(NonPagedPool, sizeof(ClientObject[0]), CLIENT_OBJECT_POOL_TAG);
      if (NULL == ClientObject) {
          return STATUS_NO_MEMORY;
      }
  
      SmbInitializeObject((PSMB_OBJECT)ClientObject, TAG_CLIENT_OBJECT, (PSMB_OBJECT_CLEANUP)SmbDeleteClient);
  
      ClientObject->Device = Device;
  
      RtlCopyMemory(ClientObject->EndpointName, Name, NETBIOS_NAME_SIZE);
  
      KeInitializeSpinLock(&ClientObject->Lock);
      InitializeListHead(&ClientObject->ListenHead);
      InitializeListHead(&ClientObject->AssociatedConnection);
      InitializeListHead(&ClientObject->ActiveConnection);
      InitializeListHead(&ClientObject->PendingAcceptConnection);
      ClientObject->PendingAcceptNumber = 0;
  
      ClientObject->evConnect = NULL;
      ClientObject->ConEvContext = NULL;
  
      ClientObject->evDisconnect = NULL;
      ClientObject->DiscEvContext = NULL;
  
      ClientObject->evError = NULL;
      ClientObject->ErrorEvContext = NULL;
  
      ClientObject->evReceive = NULL;
      ClientObject->RcvEvContext = NULL;
  
      SMB_ACQUIRE_SPINLOCK(Device, Irql);
      if (RtlEqualMemory(Device->EndpointName, ClientObject->EndpointName, NETBIOS_NAME_SIZE)) {
          if (Device->SmbServer) {
              ExFreePool(ClientObject);
              SMB_RELEASE_SPINLOCK(Device, Irql);
              SmbPrint(SMB_TRACE_CALL, ("SmbCreateClient: Duplicate server\n"));
  
              return STATUS_ACCESS_DENIED;
          }
          SmbPrint(SMB_TRACE_CALL, ("SmbCreateClient: Server comes in\n"));
          Device->SmbServer = ClientObject;
      }
      InsertTailList(&Device->ClientList, &ClientObject->Linkage);
      SMB_RELEASE_SPINLOCK(Device, Irql);
  
      IrpSp = IoGetCurrentIrpStackLocation(Irp);
  
      IrpSp->FileObject->FsContext  = ClientObject;
      IrpSp->FileObject->FsContext2 = UlongToPtr(SMB_TDI_CLIENT);
  
      SmbPrint(SMB_TRACE_CALL, ("SmbCreateClient: new Client %p\n", ClientObject));
  
      return STATUS_SUCCESS;
  }
  
  NTSTATUS
  SmbCloseClient(
      IN PSMB_DEVICE  Device,
      IN PIRP         Irp
      )
  {
      PIO_STACK_LOCATION  IrpSp;
      KIRQL               Irql;
      PSMB_CLIENT_ELEMENT ClientObject;
  
      IrpSp = IoGetCurrentIrpStackLocation(Irp);
      if (IrpSp->FileObject->FsContext2 != UlongToPtr(SMB_TDI_CLIENT)) {
          ASSERT(0);
          return STATUS_INVALID_PARAMETER;
      }
  
      SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql);
      IrpSp->FileObject->FsContext2 = UlongToPtr(SMB_TDI_INVALID);
      SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
  
      if (NULL == IrpSp->FileObject->FsContext) {
          ASSERT(0);
          return STATUS_INVALID_PARAMETER;
      }
  
      ClientObject = (PSMB_CLIENT_ELEMENT)IrpSp->FileObject->FsContext;
  
      SMB_ACQUIRE_SPINLOCK(Device, Irql);
  
      ASSERT(EntryIsInList(&Device->ClientList, &ClientObject->Linkage));
  
      if (RtlEqualMemory(Device->EndpointName, ClientObject->EndpointName, NETBIOS_NAME_SIZE)) {
          ASSERT(Device->SmbServer);
          Device->SmbServer = NULL;
          SmbPrint(SMB_TRACE_CALL, ("SmbCloseClient: Server leaves\n"));
      }
      RemoveEntryList(&ClientObject->Linkage);
      InsertTailList(&Device->PendingDeleteClientList, &ClientObject->Linkage);
  
      SMB_RELEASE_SPINLOCK(Device, Irql);
  
      SmbDereferenceClient(ClientObject, SMB_REF_CREATE);
      IrpSp->FileObject->FsContext = NULL;
  
      SmbPrint(SMB_TRACE_CALL, ("SmbCloseClient: close Client %p\n", ClientObject));
      return STATUS_SUCCESS;
  }
  
  PSMB_CLIENT_ELEMENT
  SmbVerifyAndReferenceClient(
      PFILE_OBJECT    FileObject,
      SMB_REF_CONTEXT ctx
      )
  {
      PSMB_CLIENT_ELEMENT ClientObject;
      KIRQL               Irql;
  
      SMB_ACQUIRE_SPINLOCK(&SmbCfg, Irql);
      if (FileObject->FsContext2 != UlongToPtr(SMB_TDI_CLIENT)) {
          ClientObject = NULL;
      } else {
          ClientObject = (PSMB_CLIENT_ELEMENT)FileObject->FsContext;
          SmbReferenceClient(ClientObject, ctx);
      }
      SMB_RELEASE_SPINLOCK(&SmbCfg, Irql);
  
      return ClientObject;
  }
  