/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: wsconfig.c Abstract: This module contains the Workstation service configuration routines. Author: Rita Wong (ritaw) 22-May-1991 Revision History: 08-May-1992 JohnRo Wksta transports are just an array of values for one key, not an entire section. Ditto for other domains for the browser. 13-May-1992 JohnRo Reworked to share code with registry watch code. --*/ #include "ws.h" #include // LsaQueryInformationPolicy #include "wsdevice.h" #include "wsconfig.h" #include "wsbind.h" #include "wsutil.h" #include "wsmain.h" #include // NT config file helpers in netlib #include // USE_WIN32_CONFIG (if defined), etc. #include // Section and keyword equates. #include // NetApiBufferFree(). #include // WORKSTATION_DISPLAY_NAME #include // PREFIX_ equates. #include // LPTSTR_ARRAY, etc. #include // wcscpy(). #include // Eventlog message IDs #include // Eventlog message IDs #define WS_LINKAGE_REGISTRY_PATH L"LanmanWorkstation\\Linkage" #define WS_BIND_VALUE_NAME L"Bind" //-------------------------------------------------------------------// // // // Local Function Prototypes // // // //-------------------------------------------------------------------// STATIC NTSTATUS WsBindATransport( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ); //-------------------------------------------------------------------// // // // Global variables // // // //-------------------------------------------------------------------// // // Workstation configuration information structure which holds the // computername, primary domain, wksta config buffer, and a resource // to serialize access to the whole thing. // WSCONFIGURATION_INFO WsInfo; STATIC WS_REDIR_FIELDS WsFields[] = { {WKSTA_KEYWORD_CHARWAIT, (LPDWORD) &WSBUF.wki502_char_wait, 3600, 0, 65535, DWordType, WKSTA_CHARWAIT_PARMNUM}, {WKSTA_KEYWORD_MAXCOLLECTIONCOUNT, (LPDWORD) &WSBUF.wki502_maximum_collection_count, 16, 0, 65535, DWordType, WKSTA_CHARCOUNT_PARMNUM}, {WKSTA_KEYWORD_COLLECTIONTIME, (LPDWORD) &WSBUF.wki502_collection_time, 250, 0, 65535000, DWordType, WKSTA_CHARTIME_PARMNUM}, {WKSTA_KEYWORD_KEEPCONN, (LPDWORD) &WSBUF.wki502_keep_conn, 600, 1, 65535, DWordType, WKSTA_KEEPCONN_PARMNUM}, {WKSTA_KEYWORD_MAXCMDS, (LPDWORD) &WSBUF.wki502_max_cmds, 50, 50, 65535, DWordType, PARMNUM_ALL}, // Not settable dynamically {WKSTA_KEYWORD_SESSTIMEOUT, (LPDWORD) &WSBUF.wki502_sess_timeout, 60, 60, 65535, DWordType, WKSTA_SESSTIMEOUT_PARMNUM}, {WKSTA_KEYWORD_SIZCHARBUF, (LPDWORD) &WSBUF.wki502_siz_char_buf, 512, 64, 4096, DWordType, WKSTA_SIZCHARBUF_PARMNUM}, {WKSTA_KEYWORD_MAXTHREADS, (LPDWORD) &WSBUF.wki502_max_threads, 17, 1, 256, DWordType, WKSTA_MAXTHREADS_PARMNUM}, {WKSTA_KEYWORD_LOCKQUOTA, (LPDWORD) &WSBUF.wki502_lock_quota, 6144, 0, MAXULONG, DWordType, WKSTA_LOCKQUOTA_PARMNUM}, {WKSTA_KEYWORD_LOCKINCREMENT, (LPDWORD) &WSBUF.wki502_lock_increment, 10, 0, MAXULONG, DWordType, WKSTA_LOCKINCREMENT_PARMNUM}, {WKSTA_KEYWORD_LOCKMAXIMUM, (LPDWORD) &WSBUF.wki502_lock_maximum, 500, 0, MAXULONG, DWordType, WKSTA_LOCKMAXIMUM_PARMNUM}, {WKSTA_KEYWORD_PIPEINCREMENT, (LPDWORD) &WSBUF.wki502_pipe_increment, 10, 0, MAXULONG, DWordType, WKSTA_PIPEINCREMENT_PARMNUM}, {WKSTA_KEYWORD_PIPEMAXIMUM, (LPDWORD) &WSBUF.wki502_pipe_maximum, 500, 0, MAXULONG, DWordType, WKSTA_PIPEMAXIMUM_PARMNUM}, {WKSTA_KEYWORD_CACHEFILETIMEOUT, (LPDWORD) &WSBUF.wki502_cache_file_timeout, 40, 0, MAXULONG, DWordType, WKSTA_CACHEFILETIMEOUT_PARMNUM}, {WKSTA_KEYWORD_DORMANTFILELIMIT, (LPDWORD) &WSBUF.wki502_dormant_file_limit, 45, 1, MAXULONG, DWordType, WKSTA_DORMANTFILELIMIT_PARMNUM}, {WKSTA_KEYWORD_READAHEADTHRUPUT, (LPDWORD) &WSBUF.wki502_read_ahead_throughput, MAXULONG,0, MAXULONG, DWordType, WKSTA_READAHEADTHRUPUT_PARMNUM}, {WKSTA_KEYWORD_MAILSLOTBUFFERS, (LPDWORD) &WSBUF.wki502_num_mailslot_buffers, 3, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable {WKSTA_KEYWORD_SERVERANNOUNCEBUFS, (LPDWORD) &WSBUF.wki502_num_srv_announce_buffers, 20, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable {WKSTA_KEYWORD_NUM_ILLEGAL_DG_EVENTS, (LPDWORD) &WSBUF.wki502_max_illegal_datagram_events, 5, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable {WKSTA_KEYWORD_ILLEGAL_DG_RESET_TIME, (LPDWORD) &WSBUF.wki502_illegal_datagram_event_reset_frequency, 3600, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable {WKSTA_KEYWORD_LOG_ELECTION_PACKETS, (LPDWORD) &WSBUF.wki502_log_election_packets, FALSE, 0, MAXULONG, BooleanType, PARMNUM_ALL}, // Not settable {WKSTA_KEYWORD_USEOPLOCKING, (LPDWORD) &WSBUF.wki502_use_opportunistic_locking, TRUE, 0, 0, BooleanType, WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM}, {WKSTA_KEYWORD_USEUNLOCKBEHIND, (LPDWORD) &WSBUF.wki502_use_unlock_behind, TRUE, 0, 0, BooleanType, WKSTA_USEUNLOCKBEHIND_PARMNUM}, {WKSTA_KEYWORD_USECLOSEBEHIND, (LPDWORD) &WSBUF.wki502_use_close_behind, TRUE, 0, 0, BooleanType, WKSTA_USECLOSEBEHIND_PARMNUM}, {WKSTA_KEYWORD_BUFNAMEDPIPES, (LPDWORD) &WSBUF.wki502_buf_named_pipes, TRUE, 0, 0, BooleanType, WKSTA_BUFFERNAMEDPIPES_PARMNUM}, {WKSTA_KEYWORD_USELOCKREADUNLOCK, (LPDWORD) &WSBUF.wki502_use_lock_read_unlock, TRUE, 0, 0, BooleanType, WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM}, {WKSTA_KEYWORD_UTILIZENTCACHING, (LPDWORD) &WSBUF.wki502_utilize_nt_caching, TRUE, 0, 0, BooleanType, WKSTA_UTILIZENTCACHING_PARMNUM}, {WKSTA_KEYWORD_USERAWREAD, (LPDWORD) &WSBUF.wki502_use_raw_read, TRUE, 0, 0, BooleanType, WKSTA_USERAWREAD_PARMNUM}, {WKSTA_KEYWORD_USERAWWRITE, (LPDWORD) &WSBUF.wki502_use_raw_write, TRUE, 0, 0, BooleanType, WKSTA_USERAWWRITE_PARMNUM}, {WKSTA_KEYWORD_USEWRITERAWDATA, (LPDWORD) &WSBUF.wki502_use_write_raw_data, TRUE, 0, 0, BooleanType, WKSTA_USEWRITERAWWITHDATA_PARMNUM}, {WKSTA_KEYWORD_USEENCRYPTION, (LPDWORD) &WSBUF.wki502_use_encryption, TRUE, 0, 0, BooleanType, WKSTA_USEENCRYPTION_PARMNUM}, {WKSTA_KEYWORD_BUFFILESDENYWRITE, (LPDWORD) &WSBUF.wki502_buf_files_deny_write, TRUE, 0, 0, BooleanType, WKSTA_BUFFILESWITHDENYWRITE_PARMNUM}, {WKSTA_KEYWORD_BUFREADONLYFILES, (LPDWORD) &WSBUF.wki502_buf_read_only_files, TRUE, 0, 0, BooleanType, WKSTA_BUFFERREADONLYFILES_PARMNUM}, {WKSTA_KEYWORD_FORCECORECREATE, (LPDWORD) &WSBUF.wki502_force_core_create_mode, TRUE, 0, 0, BooleanType, WKSTA_FORCECORECREATEMODE_PARMNUM}, {WKSTA_KEYWORD_USE512BYTEMAXTRANS, (LPDWORD) &WSBUF.wki502_use_512_byte_max_transfer, FALSE, 0, 0, BooleanType, WKSTA_USE512BYTESMAXTRANSFER_PARMNUM}, {NULL, NULL, 0, 0, BooleanType} }; // // For specifying the importance of a transport when binding to it. // The higher the number means that the transport will be searched // first. // STATIC DWORD QualityOfService = 65536; DWORD WsInAWorkgroup( VOID ) /*++ Routine Description: This function determines whether we are a member of a domain, or of a workgroup. First it checks to make sure we're running on a Windows NT system (otherwise we're obviously in a domain) and if so, queries LSA to get the Primary domain SID, if this is NULL, we're in a workgroup. If we fail for some random unexpected reason, we'll pretend we're in a domain (it's more restrictive). Arguments: None Return Value: TRUE - We're in a workgroup FALSE - We're in a domain --*/ { NT_PRODUCT_TYPE ProductType; OBJECT_ATTRIBUTES ObjectAttributes; LSA_HANDLE Handle; NTSTATUS Status; PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL; DWORD Result = FALSE; Status = RtlGetNtProductType(&ProductType); if (!NT_SUCCESS(Status)) { NetpKdPrint(( PREFIX_WKSTA "Could not get Product type\n")); return FALSE; } if (ProductType == NtProductLanManNt) { return(FALSE); } InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL); Status = LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_VIEW_LOCAL_INFORMATION, &Handle); if (!NT_SUCCESS(Status)) { NetpKdPrint(( PREFIX_WKSTA "Could not open LSA Policy Database\n")); return FALSE; } Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation, (PVOID *) &PolicyPrimaryDomainInfo); if (NT_SUCCESS(Status)) { if (PolicyPrimaryDomainInfo->Sid == NULL) { Result = TRUE; } else { Result = FALSE; } } if (PolicyPrimaryDomainInfo) { LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo); } LsaClose(Handle); return(Result); } NET_API_STATUS WsGetWorkstationConfiguration( VOID ) { NET_API_STATUS status; LPNET_CONFIG_HANDLE WorkstationSection; LPTSTR ComputerName; LPTSTR DomainNameT; DWORD version; NT_PRODUCT_TYPE NtProductType; BYTE Buffer[max(sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) + (DNLEN + 1) * sizeof(WCHAR), sizeof(LMDR_REQUEST_PACKET))]; PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer; PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer; // // Lock config information structure for write access since we are // initializing the data in the structure. // if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) { //IF_DEBUG(START) { DbgPrint("WKSSVC Acquire ConfigResource failed\n"); //} return NERR_InternalError; } // // Set pointer to configuration fields structure // WsInfo.WsConfigFields = WsFields; // // Get the version name. // version = GetVersion( ); WsInfo.MajorVersion = version & 0xff; WsInfo.MinorVersion = (version >> 8) & 0xff; WsInfo.RedirectorPlatform = PLATFORM_ID_NT; // // Get the configured computer name. NetpGetComputerName allocates // the memory to hold the computername string using NetApiBufferAllocate(). // if ((status = NetpGetComputerName( &ComputerName )) != NERR_Success) { RtlReleaseResource(&WsInfo.ConfigResource); //IF_DEBUG(START) { DbgPrint("WKSSVC Get computer name failed %lx\n", status); //} return status; } if ((status = I_NetNameCanonicalize( NULL, ComputerName, (LPTSTR) WsInfo.WsComputerName, sizeof( WsInfo.WsComputerName ), NAMETYPE_COMPUTER, 0 )) != NERR_Success) { LPWSTR SubString[1]; NetpKdPrint(( PREFIX_WKSTA FORMAT_LPTSTR " is an invalid computername.\n", ComputerName )); SubString[0] = ComputerName; WsLogEvent( APE_BAD_COMPNAME, EVENTLOG_ERROR_TYPE, 1, SubString, NERR_Success ); (void) NetApiBufferFree((PVOID) ComputerName); RtlReleaseResource(&WsInfo.ConfigResource); //IF_DEBUG(START) { DbgPrint("WKSSVC Invalid computer name failed %lx\n", status); //} return status; } // // Free memory allocated by NetpGetComputerName. // (void) NetApiBufferFree(ComputerName); WsInfo.WsComputerNameLength = STRLEN((LPWSTR) WsInfo.WsComputerName); // // Open config file and get handle to the [LanmanWorkstation] section // if ((status = NetpOpenConfigData( &WorkstationSection, NULL, // local (no server name) SECT_NT_WKSTA, TRUE // want read-only access )) != NERR_Success) { RtlReleaseResource(&WsInfo.ConfigResource); //IF_DEBUG(START) { DbgPrint("WKSSVC Open config file failed %lx\n", status); //} return status; } IF_DEBUG(CONFIG) { NetpKdPrint((PREFIX_WKSTA "ComputerName " FORMAT_LPTSTR ", length %lu\n", WsInfo.WsComputerName, WsInfo.WsComputerNameLength)); } // // Get the primary domain name from the configuration file // if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) { //IF_DEBUG(START) { DbgPrint("WKSSVC Get the primary domain name failed %lx\n", status); //} goto CloseConfigFile; } NetpAssert( DomainNameT != NULL ); if ( *DomainNameT != 0 ) { if ((status = I_NetNameCanonicalize( NULL, DomainNameT, (LPWSTR) WsInfo.WsPrimaryDomainName, (DNLEN + 1) * sizeof(TCHAR), WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN, 0 )) != NERR_Success) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR " is an invalid primary domain name.\n", DomainNameT)); SubString[0] = DomainNameT; WsLogEvent( APE_CS_InvalidDomain, EVENTLOG_ERROR_TYPE, 1, SubString, NERR_Success ); (void) NetApiBufferFree(DomainNameT); //IF_DEBUG(START) { DbgPrint("WKSSVC Invalid domain name failed %lx\n", status); //} goto CloseConfigFile; } } else { WsInfo.WsPrimaryDomainName[0] = 0; } // // Free memory allocated by NetpGetDomainName. // (void) NetApiBufferFree(DomainNameT); WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName); // // Read the redirector configuration fields // WsUpdateWkstaToMatchRegistry(WorkstationSection, TRUE); // // Initialize redirector configuration // Rrp->Type = ConfigInformation; Rrp->Version = REQUEST_PACKET_VERSION; STRCPY((LPWSTR) Rrp->Parameters.Start.RedirectorName, (LPWSTR) WsInfo.WsComputerName); Rrp->Parameters.Start.RedirectorNameLength = WsInfo.WsComputerNameLength*sizeof(TCHAR); Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR); STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName+WsInfo.WsComputerNameLength), (LPWSTR) WsInfo.WsPrimaryDomainName ); status = WsRedirFsControl( WsRedirDeviceHandle, FSCTL_LMR_START, Rrp, sizeof(LMR_REQUEST_PACKET) + Rrp->Parameters.Start.RedirectorNameLength+ Rrp->Parameters.Start.DomainNameLength, (LPBYTE) &WSBUF, sizeof(WKSTA_INFO_502), NULL ); if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA "Start redirector failed %lu\n", status)); SubString[0] = L"redirector"; WsLogEvent( NELOG_Service_Fail, EVENTLOG_ERROR_TYPE, 1, SubString, status ); //IF_DEBUG(START) { DbgPrint("WKSSVC Start redirector failed %lx\n", status); //} goto CloseConfigFile; } // // If we still have the default value for number of mailslot buffers, // pick a "reasonable" value based on the amount of physical memory // available in the system. // if (WSBUF.wki502_num_mailslot_buffers == MAXULONG) { MEMORYSTATUS MemoryStatus; GlobalMemoryStatus(&MemoryStatus); // // Lets take up 1/40th of 1% of physical memory for mailslot buffers // WSBUF.wki502_num_mailslot_buffers = (DWORD)(MemoryStatus.dwTotalPhys / (100 * 40 * 512)); } // // Initialize datagram receiver configuration // Drrp->Version = LMDR_REQUEST_PACKET_VERSION; Drrp->Parameters.Start.NumberOfMailslotBuffers = WSBUF.wki502_num_mailslot_buffers; Drrp->Parameters.Start.NumberOfServerAnnounceBuffers = WSBUF.wki502_num_srv_announce_buffers; Drrp->Parameters.Start.IllegalDatagramThreshold = WSBUF.wki502_max_illegal_datagram_events; Drrp->Parameters.Start.EventLogResetFrequency = WSBUF.wki502_illegal_datagram_event_reset_frequency; Drrp->Parameters.Start.LogElectionPackets = (WSBUF.wki502_log_election_packets != FALSE); RtlGetNtProductType(&NtProductType); Drrp->Parameters.Start.IsLanManNt = (NtProductType == NtProductLanManNt); status = WsDgReceiverIoControl( WsDgReceiverDeviceHandle, IOCTL_LMDR_START, Drrp, sizeof(LMDR_REQUEST_PACKET), NULL, 0, NULL ); if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA "Start datagram receiver failed %lu\n", status)); SubString[0] = L"datagram receiver"; WsLogEvent( NELOG_Service_Fail, EVENTLOG_ERROR_TYPE, 1, SubString, status ); //IF_DEBUG(START) { DbgPrint("WKSSVC Start Datagram recevier failed %lx\n", status); //} goto CloseConfigFile; } // do all error reporting in the routine // don't check any errors here WsCSCReportStartRedir(); status = NERR_Success; CloseConfigFile: (void) NetpCloseConfigData(WorkstationSection); RtlReleaseResource(&WsInfo.ConfigResource); return status; } VOID WsUpdateWkstaToMatchRegistry( IN LPNET_CONFIG_HANDLE WorkstationSection, IN BOOL IsWkstaInit ) /*++ Routine Description: This function reads each redirector configuration field into the WsInfo.WsConfigBuf (WSBUF) buffer so that the values are ready to be set in the redirector. If a field is not found or is invalid, the default value is set. Arguments: WorkstationSection - Supplies a handle to read the Workstation configuration parameters. IsWkstaInit - Supplies a flag which if TRUE indicates that this routine is called at workstation init time so the non-settable fields are acceptable and set in WSBUF. If FALSE, the non-settable fields are ignored. Return Value: None. --*/ { DWORD i; NET_API_STATUS status; DWORD TempDwordValue; // // NTRAID-70687-2/6/2000 davey Invalid keyword value. How to report error? // #define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \ { \ NetpKdPrint(( \ PREFIX_WKSTA "*ERROR* Tried to set keyword '" FORMAT_LPTSTR \ "' with invalid value.\n" \ "This error is ignored.\n", \ lptstrKeyword )); \ } for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) { // // This is to handle fields that are not settable via // NetWkstaSetInfo. However, these non-settable fields, // designated by Parmnum == PARMNUM_ALL, can be assigned when // the workstation is starting up. // if ((WsInfo.WsConfigFields[i].Parmnum != PARMNUM_ALL) || IsWkstaInit) { // // Depending on data type, get the appropriate kind of value. // switch (WsInfo.WsConfigFields[i].DataType) { case BooleanType: status = NetpGetConfigBool( WorkstationSection, WsInfo.WsConfigFields[i].Keyword, WsInfo.WsConfigFields[i].Default, (LPBOOL) (WsInfo.WsConfigFields[i].FieldPtr) ); if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) { REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword ); } break; case DWordType: status = NetpGetConfigDword( WorkstationSection, WsInfo.WsConfigFields[i].Keyword, WsInfo.WsConfigFields[i].Default, &TempDwordValue ); if ((status == NO_ERROR) || (status == NERR_CfgParamNotFound)) { // // Make sure keyword is in range. // if (TempDwordValue < WsInfo.WsConfigFields[i].Minimum || TempDwordValue > WsInfo.WsConfigFields[i].Maximum) { // // NTRAID-70689-2/6/2000 davey Better way to report error? // NetpKdPrint(( PREFIX_WKSTA FORMAT_LPTSTR " value out of range %lu (%lu-%lu)\n", WsInfo.WsConfigFields[i].Keyword, TempDwordValue, WsInfo.WsConfigFields[i].Minimum, WsInfo.WsConfigFields[i].Maximum )); // // Set back to default. // *(WsInfo.WsConfigFields[i].FieldPtr) = WsInfo.WsConfigFields[i].Default; } else { *(WsInfo.WsConfigFields[i].FieldPtr) = TempDwordValue; } } else { REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword ); } break; default: NetpAssert(FALSE); } // switch } } } NET_API_STATUS WsBindToTransports( VOID ) /*++ Routine Description: This function binds the transports specified in the registry to the redirector. The order of priority for the transports follows the order they are listed by the "Bind=" valuename. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. --*/ { NET_API_STATUS status; NET_API_STATUS tempStatus; NTSTATUS ntstatus; DWORD transportsBound; PRTL_QUERY_REGISTRY_TABLE queryTable; LIST_ENTRY header; PLIST_ENTRY pListEntry; PWS_BIND pBind; // // Ask the RTL to call us back for each subvalue in the MULTI_SZ // value \LanmanWorkstation\Linkage\Bind. // queryTable = (PVOID)LocalAlloc( 0, sizeof(RTL_QUERY_REGISTRY_TABLE) * 2 ); if (queryTable == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } InitializeListHead( &header); queryTable[0].QueryRoutine = WsBindATransport; queryTable[0].Flags = 0; queryTable[0].Name = WS_BIND_VALUE_NAME; queryTable[0].EntryContext = NULL; queryTable[0].DefaultType = REG_NONE; queryTable[0].DefaultData = NULL; queryTable[0].DefaultLength = 0; queryTable[1].QueryRoutine = NULL; queryTable[1].Flags = 0; queryTable[1].Name = NULL; ntstatus = RtlQueryRegistryValues( RTL_REGISTRY_SERVICES, // path relative to ... WS_LINKAGE_REGISTRY_PATH, queryTable, (PVOID) &header, // context NULL ); if ( !NT_SUCCESS( ntstatus)) { NetpKdPrint(( PREFIX_WKSTA "WsBindToTransports: RtlQueryRegistryValues Failed:" FORMAT_NTSTATUS "\n", ntstatus )); status = NetpNtStatusToApiStatus( ntstatus); } else { status = NO_ERROR; } // // First process all the data, then clean up. // for ( pListEntry = header.Flink; pListEntry != &header; pListEntry = pListEntry->Flink) { pBind = CONTAINING_RECORD( pListEntry, WS_BIND, ListEntry ); tempStatus = NO_ERROR; if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) { WaitForSingleObject( pBind->Redir->EventHandle, INFINITE ); tempStatus = WsMapStatus( pBind->Redir->IoStatusBlock.Status); pBind->Redir->Bound = (tempStatus == NO_ERROR); if (tempStatus == ERROR_DUP_NAME) { status = tempStatus; } } if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) { WaitForSingleObject( pBind->Dgrec->EventHandle, INFINITE ); tempStatus = WsMapStatus( pBind->Dgrec->IoStatusBlock.Status); pBind->Dgrec->Bound = (tempStatus == NO_ERROR); if (tempStatus == ERROR_DUP_NAME) { status = tempStatus; } } if ( tempStatus == ERROR_DUP_NAME) { NetpKdPrint(( PREFIX_WKSTA "Computername " FORMAT_LPTSTR " already exists on network " FORMAT_LPTSTR "\n", WsInfo.WsComputerName, pBind->TransportName )); } // // If one is installed but the other is not, clean up the other. // if ( pBind->Dgrec->Bound != pBind->Redir->Bound) { WsUnbindTransport2( pBind); } } if ( status != NO_ERROR) { if (status == ERROR_DUP_NAME) { WsLogEvent( NERR_DupNameReboot, EVENTLOG_ERROR_TYPE, 0, NULL, NERR_Success ); } for ( pListEntry = header.Flink; pListEntry != &header; pListEntry = pListEntry->Flink) { pBind = CONTAINING_RECORD( pListEntry, WS_BIND, ListEntry ); WsUnbindTransport2( pBind); } } for ( transportsBound = 0; IsListEmpty( &header) == FALSE; LocalFree((HLOCAL) pBind)) { pListEntry = RemoveHeadList( &header); pBind = CONTAINING_RECORD( pListEntry, WS_BIND, ListEntry ); if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) { CloseHandle( pBind->Redir->EventHandle); } if ( pBind->Redir->Bound == TRUE) { transportsBound++; } if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) { CloseHandle( pBind->Dgrec->EventHandle); } } (void) LocalFree((HLOCAL) queryTable); if ( WsRedirAsyncDeviceHandle != NULL) { (VOID)NtClose( WsRedirAsyncDeviceHandle); WsRedirAsyncDeviceHandle = NULL; } if ( WsDgrecAsyncDeviceHandle != NULL) { (VOID)NtClose( WsDgrecAsyncDeviceHandle); WsDgrecAsyncDeviceHandle = NULL; } if (transportsBound == 0) { NetpKdPrint(( PREFIX_WKSTA "WsBindToTransports: Failed to bind to any" " transports" FORMAT_API_STATUS "\n", status )); if ( status != ERROR_DUP_NAME) { WsLogEvent( NELOG_NoTranportLoaded, EVENTLOG_ERROR_TYPE, 0, NULL, NERR_Success ); status = NO_ERROR; } } return status; } STATIC NTSTATUS WsBindATransport( IN PWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData, IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext ) /*++ This routine always returns SUCCESS because we want all transports to be processed fully. --*/ { NET_API_STATUS status; DBG_UNREFERENCED_PARAMETER( ValueName); DBG_UNREFERENCED_PARAMETER( ValueLength); DBG_UNREFERENCED_PARAMETER( EntryContext); // // The value type must be REG_SZ (translated from REG_MULTI_SZ by the RTL). // if (ValueType != REG_SZ) { NetpKdPrint(( PREFIX_WKSTA "WsBindATransport: ignored invalid value " FORMAT_LPWSTR "\n", ValueName )); return STATUS_SUCCESS; } // // Bind transport // status = WsAsyncBindTransport( ValueData, // name of transport device object --QualityOfService, (PLIST_ENTRY)Context ); if ( status != NERR_Success) { NetpKdPrint(( PREFIX_WKSTA "WsAsyncBindTransport " FORMAT_LPTSTR " returns " FORMAT_API_STATUS "\n", ValueData, status )); } return STATUS_SUCCESS; } NET_API_STATUS WsAddDomains( VOID ) /*++ Routine Description: This function tells the datagram receiver the names to listen on for datagrams. The names include the computer name, the primary domain, name and the other domains. The logon domain is not added here; it is made known to the datagram receiver whenever a user logs on. Arguments: None. Return Value: NET_API_STATUS - NERR_Success or reason for failure. Warning: This routine is UNICODE only. --*/ { NET_API_STATUS status; LPNET_CONFIG_HANDLE SectionHandle = NULL; LPTSTR OtherDomainName = NULL; LPTSTR_ARRAY ArrayStart = NULL; BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) + (max(MAX_PATH, DNLEN) + 1) * sizeof(WCHAR)]; PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer; // // Now loop through and add all the other domains. // // // Open registry section listing the other domains. Note that this // is workstation servive parameter, NOT the browser service parameter. // if ((status = NetpOpenConfigData( &SectionHandle, NULL, // no server name SECT_NT_WKSTA, TRUE // read-only )) != NERR_Success) { // // Ignore the error if the config section couldn't be found. // status = NERR_Success; goto DomainsCleanup; } // // Get value for OtherDomains keyword in the wksta section. // This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ). // status = NetpGetConfigTStrArray( SectionHandle, WKSTA_KEYWORD_OTHERDOMAINS, &ArrayStart // Must be freed by NetApiBufferFree(). ); if (status != NERR_Success) { status = NERR_Success; // other domain is optional goto DomainsCleanup; } NetpAssert(ArrayStart != NULL); if (NetpIsTStrArrayEmpty(ArrayStart)) { goto DomainsCleanup; } OtherDomainName = ArrayStart; while (! NetpIsTStrArrayEmpty(OtherDomainName)) { if ((status = I_NetNameCanonicalize( NULL, OtherDomainName, (LPWSTR) Drrp->Parameters.AddDelName.Name, (DNLEN + 1) * sizeof(TCHAR), NAMETYPE_DOMAIN, 0 )) != NERR_Success) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR " is an invalid other domain name.\n", OtherDomainName)); SubString[0] = OtherDomainName; WsLogEvent( APE_CS_InvalidDomain, EVENTLOG_ERROR_TYPE, 1, SubString, NERR_Success ); status = NERR_Success; // loading other domains is optional goto NextOtherDomain; } // // Tell the datagram receiver about an other domain name // Drrp->Version = LMDR_REQUEST_PACKET_VERSION; Drrp->Parameters.AddDelName.Type = OtherDomain; Drrp->Parameters.AddDelName.DgReceiverNameLength = STRLEN(OtherDomainName) * sizeof(TCHAR); status = WsDgReceiverIoControl( WsDgReceiverDeviceHandle, IOCTL_LMDR_ADD_NAME, Drrp, sizeof(LMDR_REQUEST_PACKET) + Drrp->Parameters.AddDelName.DgReceiverNameLength, NULL, 0, NULL ); // // Service install still pending. Update checkpoint counter and the // status with the Service Controller. // WsGlobalData.Status.dwCheckPoint++; WsUpdateStatus(); if (status != NERR_Success) { LPWSTR SubString[1]; NetpKdPrint(( PREFIX_WKSTA "Add Other domain name " FORMAT_LPTSTR " failed with error code %lu\n", OtherDomainName, status )); SubString[0] = OtherDomainName; WsLogEvent( APE_CS_InvalidDomain, EVENTLOG_ERROR_TYPE, 1, SubString, status ); status = NERR_Success; // loading other domains is optional } NextOtherDomain: OtherDomainName = NetpNextTStrArrayEntry(OtherDomainName); } DomainsCleanup: // // Done with reading from config file. Close file, free memory, etc. // if (ArrayStart != NULL) { (VOID) NetApiBufferFree(ArrayStart); } if (SectionHandle != NULL) { (VOID) NetpCloseConfigData(SectionHandle); } return status; } VOID WsLogEvent( DWORD MessageId, WORD EventType, DWORD NumberOfSubStrings, LPWSTR *SubStrings, DWORD ErrorCode ) { HANDLE LogHandle; PSID UserSid = NULL; LogHandle = RegisterEventSourceW ( NULL, WORKSTATION_DISPLAY_NAME ); if (LogHandle == NULL) { NetpKdPrint((PREFIX_WKSTA "RegisterEventSourceW failed %lu\n", GetLastError())); return; } if (ErrorCode == NERR_Success) { // // No error codes were specified // (void) ReportEventW( LogHandle, EventType, 0, // event category MessageId, UserSid, (WORD)NumberOfSubStrings, 0, SubStrings, (PVOID) NULL ); } else { // // Log the error code specified // (void) ReportEventW( LogHandle, EventType, 0, // event category MessageId, UserSid, (WORD)NumberOfSubStrings, sizeof(DWORD), SubStrings, (PVOID) &ErrorCode ); } DeregisterEventSource(LogHandle); } NET_API_STATUS WsSetWorkStationDomainName( VOID ) { NET_API_STATUS status; LPNET_CONFIG_HANDLE WorkstationSection; LPTSTR ComputerName; LPTSTR DomainNameT; DWORD version; NT_PRODUCT_TYPE NtProductType; BYTE Buffer[sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) + (DNLEN + 1) * sizeof(WCHAR)]; PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer; NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName start.\n")); // // Lock config information structure for write access since we are // modifying the data in the WsInfo. // if (!RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) { return NERR_InternalError; } // // Get the primary domain name from the configuration file // if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) { goto CloseConfigFile; } NetpAssert( DomainNameT != NULL ); if ( *DomainNameT != 0 ) { if ((status = I_NetNameCanonicalize( NULL, DomainNameT, (LPWSTR) WsInfo.WsPrimaryDomainName, (DNLEN + 1) * sizeof(TCHAR), WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN, 0 )) != NERR_Success) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR " is an invalid primary domain name.\n", DomainNameT)); SubString[0] = DomainNameT; WsLogEvent( APE_CS_InvalidDomain, EVENTLOG_ERROR_TYPE, 1, SubString, NERR_Success ); (void) NetApiBufferFree(DomainNameT); goto CloseConfigFile; } } else { WsInfo.WsPrimaryDomainName[0] = 0; } // // Free memory allocated by NetpGetDomainName. // (void) NetApiBufferFree(DomainNameT); WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName); // // Initialize redirector configuration // Rrp->Type = ConfigInformation; Rrp->Version = REQUEST_PACKET_VERSION; Rrp->Parameters.Start.RedirectorNameLength = 0; Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR); STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName), (LPWSTR) WsInfo.WsPrimaryDomainName); NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName call rdr.\n")); status = WsRedirFsControl( WsRedirDeviceHandle, FSCTL_LMR_SET_DOMAIN_NAME, Rrp, sizeof(LMR_REQUEST_PACKET) + Rrp->Parameters.Start.DomainNameLength, NULL, 0, NULL ); if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) { LPWSTR SubString[1]; NetpKdPrint((PREFIX_WKSTA "Set domain name failed %lu\n", status)); SubString[0] = L"redirector"; WsLogEvent( NELOG_Service_Fail, EVENTLOG_ERROR_TYPE, 1, SubString, status ); } CloseConfigFile: RtlReleaseResource(&WsInfo.ConfigResource); return status; }