/*++ Copyright (c) 1997 Microsoft Corporation Module Name: winntsif.c Abstract: winntsif.c is responsible for filling in the nt setup answer file with data from the win9xupg system being upgraded. This data is then used during unattended setup to control the installation of various portions of the winnt system. This is a rewrite and rationalization of the old unattend.c file. Author: Marc R. Whitten (marcw) 16-Feb-1998 Revision History: ovidiut 14-Mar-2000 Added random admin password for GUI setup marcw 29-Aug-1999 ID mapping for NICs. marcw 08-Mar-1999 Migration dlls can handle section\keys. marcw 23-Sep-1998 DLC fix --*/ #include "pch.h" #define DBG_WINNTSIF "WinntSif" /*++ Macro Expansion List Description: WINNTSIF_SECTIONS lists all of the sections in winnt.sif that will be populated by the winntsif processing code. The engine will enumerate each section in this list and then process the settings associated with that section. Line Syntax: STRSECTION(SectionName,SettingsList) FUNSECTION(SectionFunction,SettingsList) Arguments: SectionName - This is a string containing the name of the section that is processed. SectionFunction - This is an enumeration function that is called for sections that will have multiple instances. This function returns a new section name each time it is called, until there are no more instances to process. At that time, this function returns NULL, ending the processing if the associated section settings. SettingsList - A list of settings associated with each function. Each of these settings will be processed for the given section (or multiple sections in the case of FUNSECTION lines..) See the description of the _SECTION_SETTINGS macro for more details on these settings. Variables Generated From List: g_SectionList --*/ /*++ Macro Expansion List Description: _SECTION_SETTINGS lists the settings to be processed for the section X. Each section in the WINNTSIF_SECTIONS macro above contains a list of these settings.. Line Syntax: FUNDATA(CreationFunction,SettingName,DataFunction) STRDATA(CreationFunction,SettingName,DataString) REGDATA(CreationFunction,SettingName,RegKey,RegValue) Arguments: CreationFunction - This is an optional boolean function which returns TRUE if the setting should be written, FALSE if it should be skipped. SettingName - A string containing the name of the setting being processed. DataFunction - A Function which returns the string data to be written for the specified setting, or NULL if nothing is to be written. DataString - The actual string to write for the data of the setting. RegKey/RegValue - The Key and value to use to retrieve the data to be written for the setting from the registry. Variables Generated From List: Used with WINNTSIF_SECTIONS to generate g_SectionList. --*/ #define WINNTSIF_SECTIONS \ STRSECTION(WINNT_DATA,DATA_SECTION_SETTINGS) \ STRSECTION(S_UNATTENDED,UNATTENDED_SECTION_SETTINGS) \ STRSECTION(S_GUIUNATTENDED,GUIUNATTENDED_SECTION_SETTINGS) \ STRSECTION(S_USERDATA,USERDATA_SECTION_SETTINGS) \ STRSECTION(S_DISPLAY,DISPLAY_SECTION_SETTINGS) \ FUNSECTION(pNetAdapterSections,FUNDATA(NULL,S_INFID,pGetNetAdapterPnpId)) \ STRSECTION(S_NETWORKING,NETWORKING_SECTION_SETTINGS) \ STRSECTION(S_PAGE_IDENTIFICATION,IDENTIFICATION_SECTION_SETTINGS) \ STRSECTION(S_PAGE_NETPROTOCOLS,NETPROTOCOLS_SECTION_SETTINGS) \ STRSECTION(S_MS_NWIPX,FUNDATA(NULL,S_ADAPTERSECTIONS,pGetAdaptersWithIpxBindings)) \ FUNSECTION(pIpxAdapterSections,IPX_ADAPTER_SECTION_SETTINGS) \ FUNSECTION(pHomenetSection, HOMENET_SECTION_SETTINGS) \ STRSECTION(S_MS_TCPIP,TCPIP_SECTION_SETTINGS) \ FUNSECTION(pTcpAdapterSections,TCP_ADAPTER_SECTION_SETTINGS) \ STRSECTION(S_PAGE_NETCLIENTS,NETCLIENTS_SECTION_SETTINGS) \ STRSECTION(S_SERVICESTARTTYPES,SERVICESTARTTYPES_SECTION_SETTINGS) \ STRSECTION(S_PAGE_NETSERVICES,NETSERVICES_SECTION_SETTINGS) \ STRSECTION(S_NETOPTIONALCOMPONENTS,NETOPTIONALCOMPONENTS_SECTION_SETTINGS) \ STRSECTION(S_MSRASCLI,STRDATA(pIsRasInstalled,S_PARAMSSECTION,S_PARAMSRASCLI)) \ STRSECTION(S_PARAMSRASCLI,PARAMSRASCLI_SECTION_SETTINGS) \ FUNSECTION(pRasPortSections,RASPORT_SECTION_SETTINGS) \ STRSECTION(S_MS_NWCLIENT,NWCLIENT_SECTION_SETTINGS) \ FUNSECTION(pBindingSections,FUNDATA(NULL,NULL,NULL)) \ STRSECTION(S_REGIONALSETTINGS,REGIONALSETTINGS_SECTION_SETTINGS) \ #if 0 // this was moved to winnt32 dll as part of Setup components update STRSECTION(WINNT_WIN95UPG_95_DIR_A,FUNDATA(NULL,WINNT_WIN95UPG_NTKEY_A,pGetReplacementDll)) \ #endif // // [Data] // #define DATA_SECTION_SETTINGS \ FUNDATA(NULL,WINNT_D_MIGTEMPDIR,pGetTempDir) \ FUNDATA(NULL,WINNT_D_WIN9XSIF,pGetWin9xSifDir) \ STRDATA(NULL,WINNT_U_UNATTENDMODE,WINNT_A_DEFAULTHIDE) \ FUNDATA(NULL,WINNT_D_INSTALLDIR,pGetWinDir) \ FUNDATA(NULL,WINNT_D_WIN9XBOOTDRIVE,pGetWin9xBootDrive) \ FUNDATA(NULL,WINNT_D_GUICODEPAGEOVERRIDE,pGetGuiCodePage) \ FUNDATA(NULL,WINNT_D_BACKUP_LIST,pBackupFileList) \ FUNDATA(NULL,WINNT_D_ROLLBACK_MOVE,pUninstallMoveFileList) \ FUNDATA(NULL,WINNT_D_ROLLBACK_DELETE,pUninstallDelFileList) \ FUNDATA(NULL,WINNT_D_ROLLBACK_DELETE_DIR,pUninstallDelDirList) \ FUNDATA(NULL,S_ROLLBACK_MK_DIRS,pUninstallMkDirList) \ // // [Unattended] // #define UNATTENDED_SECTION_SETTINGS \ STRDATA(NULL,S_NOWAITAFTERTEXTMODE,S_ONE) \ STRDATA(NULL,S_NOWAITAFTERGUIMODE,S_ONE) \ STRDATA(NULL,S_CONFIRMHARDWARE,S_NO) \ FUNDATA(NULL,S_KEYBOARDLAYOUT,pGetKeyboardLayout) \ FUNDATA(NULL,S_KEYBOARDHARDWARE,pGetKeyboardHardware) // // [GuiUnattended] // #define GUIUNATTENDED_SECTION_SETTINGS \ FUNDATA(NULL,S_TIMEZONE,pGetTimeZone) \ STRDATA(NULL,S_SERVERTYPE,S_STANDALONE) \ FUNDATA(NULL,WINNT_US_ADMINPASS,pSetAdminPassword) // // [UserData] // #define USERDATA_SECTION_SETTINGS \ FUNDATA(NULL,S_FULLNAME,pGetFullName) \ REGDATA(NULL,S_ORGNAME,S_WINDOWS_CURRENTVERSION,S_REGISTEREDORGANIZATION) \ FUNDATA(NULL,S_COMPUTERNAME,pGetComputerName) \ // // [Display] // #define DISPLAY_SECTION_SETTINGS \ FUNDATA(NULL,S_BITSPERPEL,pGetBitsPerPixel) \ FUNDATA(NULL,S_XRESOLUTION,pGetXResolution) \ FUNDATA(NULL,S_YRESOLUTION,pGetYResolution) \ FUNDATA(NULL,S_VREFRESH,pGetVerticalRefreshRate) \ // // [Networking] // #define NETWORKING_SECTION_SETTINGS \ STRDATA(pIsNetworkingInstalled,S_PROCESSPAGESECTIONS,S_YES) \ STRDATA(pIsNetworkingInstalled,S_UPGRADEFROMPRODUCT,S_WINDOWS95) \ FUNDATA(pIsNetworkingInstalled,S_BUILDNUMBER,pGetBuildNumber) // // [Identification] // #define IDENTIFICATION_SECTION_SETTINGS \ FUNDATA(pIsNetworkingInstalled,S_JOINDOMAIN,pGetUpgradeDomainName) \ FUNDATA(pIsNetworkingInstalled,S_JOINWORKGROUP,pGetUpgradeWorkgroupName) // // [NetProtocols] // #define NETPROTOCOLS_SECTION_SETTINGS \ STRDATA(pIsNwIpxInstalled,S_MS_NWIPX,S_MS_NWIPX) \ STRDATA(pIsTcpIpInstalled,S_MS_TCPIP,S_MS_TCPIP) \ // // These protocols were removed from Whistler, don't migrate settings for them // // STRDATA(pIsNetBeuiInstalled,S_MS_NETBEUI,S_MS_NETBEUI) \ // STRDATA(pIsMsDlcInstalled,S_MS_DLC,S_MS_DLC) \ // // [Adapter.ipx] // #define IPX_ADAPTER_SECTION_SETTINGS \ FUNDATA(NULL,S_SPECIFICTO,pSpecificTo) \ FUNDATA(NULL,S_PKTTYPE,pGetIpxPacketType) \ FUNDATA(NULL,S_NETWORKNUMBER,pGetIpxNetworkNumber) \ // // [MS_TCPIP] // #define TCPIP_SECTION_SETTINGS \ FUNDATA(pIsTcpIpInstalled,S_ADAPTERSECTIONS,pGetAdaptersWithTcpBindings) \ FUNDATA(pIsTcpIpInstalled,S_DNS,pGetDnsStatus) \ REGDATA(pIsDnsEnabled,S_DNSHOST,S_MSTCP_KEY,S_HOSTNAMEVAL) \ FUNDATA(pIsDnsEnabled,S_DNSSUFFIXSEARCHORDER,pGetDnsSuffixSearchOrder) \ REGDATA(pIsTcpIpInstalled,S_SCOPEID,S_MSTCP_KEY,S_SCOPEID) \ REGDATA(pIsTcpIpInstalled,S_IMPORTLMHOSTSFILE,S_MSTCP_KEY,S_LMHOSTS) \ // // [Adapter.tcpip] // #define TCP_ADAPTER_SECTION_SETTINGS \ FUNDATA(NULL,S_SPECIFICTO,pSpecificTo) \ FUNDATA(NULL,S_DHCP,pGetDhcpStatus) \ STRDATA(NULL,S_NETBIOSOPTION,S_ONE) \ FUNDATA(pHasStaticIpAddress,S_IPADDRESS,pGetIpAddress) \ FUNDATA(pHasStaticIpAddress,S_SUBNETMASK,pGetSubnetMask) \ FUNDATA(NULL,S_DEFAULTGATEWAY,pGetGateway) \ FUNDATA(pIsDnsEnabled,S_DNSSERVERSEARCHORDER,pGetDnsServerSearchOrder) \ REGDATA(pIsDnsEnabled,S_DNSDOMAIN,S_MSTCP_KEY,S_DOMAINVAL) \ FUNDATA(NULL,S_WINS,pGetWinsStatus) \ FUNDATA(NULL,S_WINSSERVERLIST,pGetWinsServers) #define HOMENET_SECTION_SETTINGS \ FUNDATA(pExternalIsAdapter,S_EXTERNAL_ADAPTER, pIcsExternalAdapter) \ FUNDATA(pExternalIsRasConn,S_EXTERNAL_CONNECTION_NAME, pIcsExternalConnectionName) \ FUNDATA(NULL, S_INTERNAL_IS_BRIDGE, pInternalIsBridge) \ FUNDATA(pHasInternalAdapter, S_INTERNAL_ADAPTER, pInternalAdapter) \ FUNDATA(pHasBridge, S_BRIDGE, pBridge) \ FUNDATA(NULL, S_DIAL_ON_DEMAND, pDialOnDemand) \ REGDATA(NULL, S_ENABLEICS, S_ICS_KEY, S_ENABLED) \ REGDATA(NULL, S_SHOW_TRAY_ICON, S_ICS_KEY, S_SHOW_TRAY_ICON) \ STRDATA(NULL, S_ISW9XUPGRADE, S_YES) // // [NetClients] // #define NETCLIENTS_SECTION_SETTINGS \ STRDATA(pIsWkstaInstalled,S_MS_NETCLIENT,S_MS_NETCLIENT) \ STRDATA(pIsNwClientInstalled,S_MS_NWCLIENT,S_MS_NWCLIENT) \ // // [ServiceStartTypes] // #define SERVICESTARTTYPES_SECTION_SETTINGS \ STRDATA(pDisableBrowserService,S_BROWSER,TEXT("3")) \ // // [NetServices] // #define NETSERVICES_SECTION_SETTINGS \ STRDATA(pInstallMsServer,S_MS_SERVER,S_MS_SERVER) \ STRDATA(pIsRasInstalled,S_MSRASCLI,S_MSRASCLI) // // [NetOptionalComponents] // #define NETOPTIONALCOMPONENTS_SECTION_SETTINGS \ STRDATA(pIsSnmpInstalled,S_SNMP,S_ONE) \ STRDATA(pIsUpnpInstalled,S_UPNP,S_ONE) // // [params.rascli] // #define PARAMSRASCLI_SECTION_SETTINGS \ STRDATA(pIsRasInstalled,S_DIALOUTPROTOCOLS,S_ALL) \ FUNDATA(pIsRasInstalled,S_PORTSECTIONS,pGetRasPorts) // // [com] // #define RASPORT_SECTION_SETTINGS \ FUNDATA(NULL,S_PORTNAME,pRasPortName) \ STRDATA(NULL,S_PORTUSAGE,S_CLIENT) // // [NwClient] // #define NWCLIENT_SECTION_SETTINGS \ REGDATA(pIsNwClientInstalled,S_PREFERREDSERVER,S_AUTHAGENTREG,S_AUTHENTICATINGAGENT) \ REGDATA(pIsNwClientInstalled,S_DEFAULTCONTEXT,S_NWREDIRREG,S_DEFAULTNAMECONTEXT) \ REGDATA(pIsNwClientInstalled,S_DEFAULTTREE,S_NWREDIRREG,S_PREFERREDNDSTREE) \ FUNDATA(pIsNwClientInstalled,S_LOGONSCRIPT,pGetScriptProcessingStatus) \ #define REGIONALSETTINGS_SECTION_SETTINGS \ FUNDATA(NULL,S_LANGUAGEGROUP,pGetLanguageGroups) \ REGDATA(NULL,S_LANGUAGE,S_SYSTEMLOCALEREG,TEXT("")) \ // // typedefs for the various functions prototypes used by the winntsif code. // typedef BOOL (* CREATION_FUNCTION) (VOID); typedef PCTSTR (* DATA_FUNCTION) (VOID); typedef PCTSTR (* SECTION_FUNCTION) (VOID); // // The SETTING_TYPE enum contains all of the possible Types of settings which // may occur in the macro expansion list above. // typedef enum { FUNCTION_SETTING = 1, STRING_SETTING, REGISTRY_SETTING, LAST_SETTING } SETTING_TYPE; // // This structure wraps a key and a value inside a single structure. It is accessed // within the union below. // typedef struct { PCTSTR Key; PCTSTR Value; } REGKEYANDVALUE, *PREGKEYANDVALUE; // // SETTING contains the information to create a single setting within // a winntsif file. // typedef struct { SETTING_TYPE SettingType; CREATION_FUNCTION CreationFunction; PCTSTR KeyName; // // The data depends on the SETTING_TYPE above. // union { REGKEYANDVALUE Registry; DATA_FUNCTION Function; PCTSTR String; } Data; } SETTING, *PSETTING; // // Section is the toplevel hierarchy used for the winntsif file. // Each section contains a list of settings that will be processed and // possibly written for that section. // #define MAX_SETTINGS 16 typedef struct { PTSTR SectionString; SECTION_FUNCTION SectionFunction; SETTING SettingList[MAX_SETTINGS]; } SECTION, *PSECTION; #define FUNDATA(create,key,datafunction) {FUNCTION_SETTING, (create), (key), {(PTSTR) (datafunction), NULL}}, #define STRDATA(create,key,datastring) {STRING_SETTING, (create), (key), {(PTSTR) (datastring), NULL}}, #define REGDATA(create,key,regkey,regvalue) {REGISTRY_SETTING, (create), (key), {(regkey), (regvalue)}}, #define STRSECTION(section,list) {(section),NULL,{list /*,*/ {LAST_SETTING,NULL,NULL,{NULL,NULL}}}}, #define FUNSECTION(function,list) {NULL,(function),{list /*,*/ {LAST_SETTING,NULL,NULL,{NULL,NULL}}}}, typedef struct { PCTSTR Text; BOOL Installed; } BINDINGPART, *PBINDINGPART; #define NUM_PROTOCOLS 4 #define NUM_CLIENTS 2 typedef struct { BINDINGPART Clients[NUM_CLIENTS]; BINDINGPART Protocols[NUM_PROTOCOLS]; } BINDINGINFO, *PBINDINGINFO; BINDINGINFO g_BindingInfo = {{{S_MS_NETCLIENT, FALSE},{S_MS_NWCLIENT, FALSE}},{{S_MS_TCPIP, FALSE},{S_MS_NWIPX, FALSE},{S_MS_NETBEUI, FALSE}, {S_MS_DLC, FALSE}}}; TCHAR g_CurrentAdapter[MEMDB_MAX]; // During adapter/section enumeration, contains current adapter name. TCHAR g_CurrentSection[MEMDB_MAX]; // During some section enums, contains current section name. TCHAR g_TempBuffer[MEMDB_MAX]; // Contains the current value returned from pGetRegistryValue MEMDB_ENUM g_TempEnum; // A global enumerator that may be used by various section functions. HINF g_IntlInf; INFSTRUCT g_InfStruct = INITINFSTRUCT_POOLHANDLE; POOLHANDLE g_LocalePool; HASHTABLE g_LocaleTable; BOOL g_fIcsAdapterInPlace = FALSE; BOOL g_fHasIcsExternalAdapter = FALSE; TCHAR g_IcsAdapter[MEMDB_MAX] = {0}; TCHAR g_IcsExternalAdapter[MEMDB_MAX] = {0}; BOOL g_fIcsInternalIsBridge = FALSE; #define S_REGIONALSETTINGS TEXT("RegionalSettings") #define CLEARBUFFER() ZeroMemory(g_TempBuffer,MEMDB_MAX * sizeof (TCHAR)); // // Helper and Miscellaneous functions.. // /*++ Routine Description: pGetRegistryValue is a utility wrapper used for getting data out of the registry. Because winntsif processing requires frequent and very similar reads from the registry, this wrapper is modified to be a little friendlier to this type of processing than the normal functions in reg.h. This function reads the data from the registry, and packs it into a multisz. It is capable of handling REG_DWORD, REG_MULTI_SZ, and REG_SZ style registry data. The data is stored in g_TempBuffer as well as being passed back through the return value. If the specified Key/Value does not exist in the registry, or the function is unable to retrieve a value, NULL is returned. Arguments: KeyString - Contains the Key to be read from the registry. ValueString - Contains the Value to be read from the registry. Return Value: A pointer to a multisz containing the data if it could be read from the registry, NULL otherwise. --*/ PCTSTR pGetRegistryValue ( IN PCTSTR KeyString, IN PCTSTR ValueString ) { PCTSTR rString = NULL; HKEY key = NULL; PBYTE data = NULL; DWORD type = REG_NONE; LONG rc = ERROR_SUCCESS; PTSTR end; MYASSERT(KeyString && ValueString); // // Open registry key. // key = OpenRegKeyStr(KeyString); if (!key) { DEBUGMSG((DBG_WINNTSIF, "Key %s does not exist.",KeyString)); return NULL; } __try { // // Get type of data // rc = RegQueryValueExA (key, ValueString, NULL, &type, NULL, NULL); if (rc != ERROR_SUCCESS) { DEBUGMSG((DBG_WINNTSIF,"RegQueryValueEx failed for %s[%s]. Value may not exist.",KeyString,ValueString)); SetLastError (rc); return NULL; } MYASSERT(type == REG_DWORD || type == REG_MULTI_SZ || type == REG_SZ); // // Get data and move it to a multistring // data = GetRegValueData (key, ValueString); if (!data) { DEBUGMSG((DBG_WHOOPS,"pGetRegistryValue: RegQueryValueEx succeeded, but GetRegValueData failed...Could be a problem.")); return NULL; } CLEARBUFFER(); switch (type) { case REG_DWORD: wsprintf(g_TempBuffer,"%u", *(DWORD*) data); break; case REG_SZ: StringCopy(g_TempBuffer,(PCTSTR) data); // // some data is stored as REG_SZ, but is actually a comma separated multisz // append one more NULL to the end // end = GetEndOfString (g_TempBuffer) + 1; *end = 0; break; case REG_MULTI_SZ: end = (PTSTR) data; while (*end) { end = GetEndOfString (end) + 1; } memcpy(g_TempBuffer,data,(LONG)end - (LONG)data); break; default: LOG ((LOG_ERROR,"Unexpected registry type found while creating Setup answer file.")); break; }; } __finally { // // Clean up resources. // CloseRegKey(key); if (data) { MemFree(g_hHeap, 0, data); } } return g_TempBuffer; } BOOL CALLBACK pEnumLocalesFunc ( IN PTSTR Locale ) { PTSTR group = NULL; PTSTR directory = NULL; // // Get the language group. // if (InfFindFirstLine (g_IntlInf, S_LOCALES, Locale, &g_InfStruct)) { group = InfGetStringField (&g_InfStruct, 3); if (group) { group = PoolMemDuplicateString (g_LocalePool, group); } ELSE_DEBUGMSG ((DBG_WARNING, "Unable to retrieve group data for locale %s.", Locale)); } // // Get the language directory. // if (group && InfFindFirstLine (g_IntlInf, S_LANGUAGEGROUPS, group, &g_InfStruct)) { directory = InfGetStringField (&g_InfStruct, 2); if (directory) { directory = PoolMemDuplicateString (g_LocalePool, directory); } } // // Save the information into the locale hash table. // if (group) { HtAddStringAndData (g_LocaleTable, group, &directory); } return TRUE; } VOID pBuildLanguageData ( VOID ) { // // Allocate needed resources. // g_LocaleTable = HtAllocWithData (sizeof (PTSTR)); g_LocalePool = PoolMemInitNamedPool (TEXT("Locale Pool")); // // Read data in from intl.inf. This is used to gather // the necessary information we need for each installed // locale. // g_IntlInf = InfOpenInfInAllSources (S_INTLINF); if (g_IntlInf != INVALID_HANDLE_VALUE) { EnumSystemLocales (pEnumLocalesFunc, LCID_INSTALLED); InfCloseInfFile (g_IntlInf); } InfCleanUpInfStruct (&g_InfStruct); } PTSTR GetNeededLangDirs ( VOID ) { UINT bytes = 0; PTSTR rDirs = NULL; HASHTABLE_ENUM e; GROWBUFFER buf = GROWBUF_INIT; PTSTR dir; // // Gather language data. // pBuildLanguageData (); if (EnumFirstHashTableString (&e, g_LocaleTable)) { do { if (e.ExtraData) { dir = *((PTSTR *) e.ExtraData); } else { dir = NULL; } // // Some language groups do not require an optional dir. // if (dir && *dir) { MultiSzAppend (&buf, dir); } } while (EnumNextHashTableString (&e)); } if (buf.Buf) { bytes = SizeOfMultiSz (buf.Buf); } if (bytes) { rDirs = PoolMemGetMemory (g_LocalePool, bytes); CopyMemory (rDirs, buf.Buf, bytes); } FreeGrowBuffer (&buf); return rDirs; } /*++ Routine Description: This simple helper function determines wether a specific net component has bindings or not. Arguments: NetComponent - Contains the Networking component to enumerate. Return Value: TRUE if the specified Networking Component has bindings, FALSE otherwise. --*/ BOOL pDoesNetComponentHaveBindings ( IN PCTSTR NetComponent ) { PTSTR keyString = NULL; REGKEY_ENUM e; keyString = JoinPaths(S_ENUM_NETWORK_KEY,NetComponent); if (!EnumFirstRegKeyStr (&e, keyString)) { FreePathString(keyString); return FALSE; } FreePathString(keyString); AbortRegKeyEnum (&e); return TRUE; } /*++ Routine Description: pGatherNetAdapterInfo is responsible for preprocessing the NetAdapter information in the registry in order to build up a tree of information about these adapters in memdb. This tree contains information about each adapter, including its pnpid, its network bindings, and the nettrans keys for each of those bindings. Arguments: None. Return Value: None. --*/ VOID pGatherNetAdapterInfo ( VOID ) { NETCARD_ENUM eNetCard; REGVALUE_ENUM eRegVal; UINT curAdapter = 1; UINT offset = 0; TCHAR adapterString[30]; PCTSTR bindingsKey = NULL; PCTSTR networkKey = NULL; PCTSTR netTransKey = NULL; PTSTR p = NULL; HKEY hKey; BOOL fBoundToTCP = FALSE; ZeroMemory(g_IcsAdapter, sizeof(g_IcsAdapter)); // // Enumerate all net cards, we'll create // entries for all of the non-dialup PNP adapters // with the following proviso: If more than one net card is specified, // if (EnumFirstNetCard(&eNetCard)) { __try { do { fBoundToTCP = FALSE; // // Skip the Dial-Up Adapter. // if (StringIMatch(eNetCard.Description,S_DIALUP_ADAPTER_DESC)) { continue; } // // Create the adapter section name for this adapter. // wsprintf(adapterString,TEXT("Adapter%u"),curAdapter); // // Next, we need to enumerate all of the bindings for this adapter // and create nettrans keys for each. // bindingsKey = JoinPaths(eNetCard.CurrentKey,S_BINDINGS); // // Open this key and enumerate the bindings underneath it. // if ((hKey = OpenRegKeyStr(bindingsKey)) != NULL) { if (EnumFirstRegValue(&eRegVal,hKey)) { do { // // For each protocol entry, build up the nettrans key. // networkKey = JoinPaths(S_NETWORK_BRANCH,eRegVal.ValueName); netTransKey = JoinPaths(S_SERVICECLASS,pGetRegistryValue(networkKey,S_DRIVERVAL)); // // Save this key into memdb. // MemDbSetValueEx( MEMDB_CATEGORY_NETTRANSKEYS, adapterString, netTransKey, NULL, 0, &offset ); // // link it into the adapters section. // p = _tcschr(eRegVal.ValueName,TEXT('\\')); if (p) { *p = 0; } MemDbSetValueEx( MEMDB_CATEGORY_NETADAPTERS, adapterString, S_BINDINGS, eRegVal.ValueName, offset, NULL ); if ((!fBoundToTCP) && 0 == lstrcmpi(eRegVal.ValueName, S_MSTCP)) { fBoundToTCP = TRUE; } FreePathString(networkKey); FreePathString(netTransKey); } while (EnumNextRegValue(&eRegVal)); } CloseRegKey(hKey); } // // Finally, store the pnpid for this card into memdb. // MemDbSetValueEx( MEMDB_CATEGORY_NETADAPTERS, adapterString, MEMDB_FIELD_PNPID, eNetCard.HardwareId && *eNetCard.HardwareId ? eNetCard.HardwareId : eNetCard.CompatibleIDs, 0, NULL ); //store the driver key for this card into memdb MemDbSetValueEx( MEMDB_CATEGORY_NETADAPTERS, adapterString, MEMDB_FIELD_DRIVER, eNetCard.HardwareEnum.Driver, 0, NULL ); if (fBoundToTCP && 0 == lstrcmp(eNetCard.CompatibleIDs, S_ICSHARE)) { //Save the ICSHARE adapter name for further use lstrcpyn(g_IcsAdapter, adapterString, sizeof(g_IcsAdapter)/sizeof(g_IcsAdapter[0])); } FreePathString(bindingsKey); curAdapter++; } while (EnumNextNetCard(&eNetCard)); } __except (EXCEPTION_EXECUTE_HANDLER) { EnumNetCardAbort(&eNetCard); LOG ((LOG_ERROR,"Caught exception while gathering data about network adapters.")); return; } } } /*++ Routine Description: pEnumFirstAdapterByBinding and pEnumNextAdapterByBinding are memdb enumeration wrapperz that are used by several functions to enumerate based upon a certain protocol. This is necessary in order to build the per-adapter portions of the winnt.sif file. Arguments: Enum - A pointer to a valid MEMDB_ENUM structure. Binding - Contains the binding to enumerate upon. Return Value: TRUE if an adapter was found with the specified binding, FALSE otherwise. --*/ BOOL pEnumFirstAdapterByBinding ( IN PMEMDB_ENUM Enum, IN PCTSTR Binding ) { BOOL rBindingFound = FALSE; TCHAR key[MEMDB_MAX]; UINT unused; if (MemDbEnumItems(Enum,MEMDB_CATEGORY_NETADAPTERS)) { do { MemDbBuildKey(key,MEMDB_CATEGORY_NETADAPTERS,Enum -> szName,S_BINDINGS,Binding); rBindingFound = MemDbGetValue(key,&unused); } while(!rBindingFound && MemDbEnumNextValue(Enum)); } return rBindingFound; } BOOL pEnumNextAdapterByBinding ( IN OUT PMEMDB_ENUM Enum, IN PCTSTR Binding ) { BOOL rBindingFound = FALSE; TCHAR key[MEMDB_MAX]; UINT unused; while(!rBindingFound && MemDbEnumNextValue(Enum)) { MemDbBuildKey(key,MEMDB_CATEGORY_NETADAPTERS,Enum -> szName,S_BINDINGS,Binding); rBindingFound = MemDbGetValue(key,&unused); } return rBindingFound; } /*++ Routine Description: pGetNetTransBinding returns the registry key to the Network Transport settings for a specified Adapter and Protocol. Arguments: Adapter - Contains the adapter to use to retrieve nettrans key information. Protocol - Contains the protocol to use to retrieve NetTrans Key information. Return Value: The NetTrans Key if successful, NULL otherwise. --*/ PCTSTR pGetNetTransBinding( IN PCTSTR Adapter, IN PCTSTR Protocol ) { TCHAR key[MEMDB_MAX]; UINT netTransOffset; MemDbBuildKey(key,MEMDB_CATEGORY_NETADAPTERS,Adapter,S_BINDINGS,Protocol); if (!MemDbGetValue(key,&netTransOffset)) { LOG ((LOG_ERROR,"Adapter %s does not have a binding to %s.",Adapter,Protocol)); return NULL; } if (!MemDbBuildKeyFromOffset(netTransOffset,g_TempBuffer,2,NULL)) { DEBUGMSG((DBG_ERROR,"Error building net trans key..Adapter: %s Protocol: %s",Adapter,Protocol)); return NULL; } return g_TempBuffer; } /*++ Routine Description: pListAdaptersWithBinding is used to create a multisz of adapter section names for adapters that have bindings to a specific networking component. Several winntsif sections require data in this form. Arguments: Binding - The Binding to use as a filter in listing the adapter sections. Suffix - an optional suffix which is attached to each adapter section name. This is useful for building sections such as: adapter1.ipx adapter2.ipx, etc etc. Return Value: The list of adapters with the specified network component binding if any, NULL otherwise. --*/ PCTSTR pListAdaptersWithBinding ( IN PCTSTR Binding, IN PCTSTR Suffix OPTIONAL ) { PCTSTR rAdapterSections = NULL; PTSTR string = g_TempBuffer; MEMDB_ENUM e; *string = 0; // // Enumerate all adapters, and create an entry for each adapter that has // IPX bindings. // if (pEnumFirstAdapterByBinding(&e,Binding)) { rAdapterSections = g_TempBuffer; do { // // Add this adapter into the multistring. // StringCopy(string,e.szName); if (Suffix) { StringCat(string,Suffix); } string = GetEndOfString(string) + 1; } while (pEnumNextAdapterByBinding(&e,Binding)); ++string; *string = 0; } return rAdapterSections; } // // Section Functions // /*++ Routine Description: Each Section Function is used to build n number of sections within the winntsif file. This is necessary for information which has multiple sections based upon some criteria. As an example, pNetAdapterSections returns a valid section for each adapter found on the system. When there are no more sections needed, these functions return NULL. Arguments: None. Return Value: A valid section name if one is necessary or NULL otherwise. --*/ PCTSTR pNetAdapterSections ( VOID ) { static BOOL firstTime = TRUE; PCTSTR rSection = NULL; BOOL moreNetAdapterInfo = FALSE; // // The first time this function is called it causes all net adapter information // to be prescanned into memdb and starts an enumeration. Afterwards, // the function simply continues the enumeration. // if (firstTime) { firstTime = FALSE; // // The first thing we need to do is gather all of the necessary net card // information that will be needed during winntsif processing and store // it in a reasonable manner. // pGatherNetAdapterInfo(); // // After pre-scanning all of the network adapter information into // memdb, we simply enumerate the adapters and return the section // names. // moreNetAdapterInfo = MemDbEnumItems(&g_TempEnum,MEMDB_CATEGORY_NETADAPTERS); } else { moreNetAdapterInfo = MemDbEnumNextValue(&g_TempEnum); } if (moreNetAdapterInfo) { StringCopy(g_CurrentAdapter,g_TempEnum.szName); rSection = g_CurrentAdapter; // // We have a minor hack here. The [NetAdapters] Section is a little unique // and doesn't fit our overall scheme. We secretly fill it in inside this // function. // WriteInfKey(S_PAGE_NETADAPTERS,g_TempEnum.szName,g_TempEnum.szName); } return rSection; } PCTSTR pIpxAdapterSections ( VOID ) { static firstTime = TRUE; PCTSTR rSectionName = NULL; BOOL moreIpxAdapterSections; if (firstTime) { firstTime = FALSE; moreIpxAdapterSections = pEnumFirstAdapterByBinding(&g_TempEnum,S_NWLINK); } else { moreIpxAdapterSections = pEnumNextAdapterByBinding(&g_TempEnum,S_NWLINK); } if (moreIpxAdapterSections) { StringCopy(g_CurrentAdapter,g_TempEnum.szName); StringCopy(g_CurrentSection,g_TempEnum.szName); StringCat(g_CurrentSection,S_IPX_SUFFIX); rSectionName = g_CurrentSection; } return rSectionName; } PCTSTR pTcpAdapterSections ( VOID ) { static firstTime = TRUE; PCTSTR rSectionName = NULL; BOOL moreTcpAdapterSections; g_fIcsAdapterInPlace = FALSE; if (firstTime) { firstTime = FALSE; moreTcpAdapterSections = pEnumFirstAdapterByBinding(&g_TempEnum,S_MSTCP); } else { moreTcpAdapterSections = pEnumNextAdapterByBinding(&g_TempEnum,S_MSTCP); } if (moreTcpAdapterSections) { StringCopy(g_CurrentAdapter,g_TempEnum.szName); StringCopy(g_CurrentSection,g_TempEnum.szName); StringCat(g_CurrentSection,S_TCPIP_SUFFIX); rSectionName = g_CurrentSection; //if ICS is installed, in Win9x, the external LAN adapter uses the TCP settings of //the virtual adapter -- ICSHARE. So when we save the TCP settings of this LAN //adapter, we should save the settings of ICSHARE adapter. That's why //we replace the g_CurrentAdapter with g_IcsAdapter if (g_fHasIcsExternalAdapter && lstrcmp(g_CurrentAdapter, g_IcsExternalAdapter) == 0) { StringCopy(g_CurrentAdapter, g_IcsAdapter); g_fIcsAdapterInPlace = TRUE; } } return rSectionName; } PCTSTR pRasPortSections ( VOID ) { static BOOL firstTime = TRUE; static REGKEY_ENUM e; static UINT modemNum = 1; BOOL moreRasPortSections; PCTSTR rSectionName = NULL; if (firstTime) { firstTime = FALSE; moreRasPortSections = HwComp_DialUpAdapterFound() && EnumFirstRegKeyStr(&e,S_MODEMS); } else { moreRasPortSections = EnumNextRegKey(&e); } if (moreRasPortSections) { wsprintf(g_CurrentSection,TEXT("COM%u"),modemNum); modemNum++; rSectionName = g_CurrentSection; } return rSectionName; } VOID pInitializeBindingInfo ( HASHTABLE Table ) { MEMDB_ENUM e; UINT i; UINT j; TCHAR buffer[MEMDB_MAX]; BOOL enabled=FALSE; // // This function will enumerate all possible binding paths on the machine and add them to // the provided string table. Later on, specifically enabled bindings will be removed. // if (MemDbEnumItems (&e, MEMDB_CATEGORY_NETADAPTERS)) { do { for (i = 0; i < NUM_CLIENTS; i++) { if (!g_BindingInfo.Clients[i].Installed) { continue; } for (j = 0; j < NUM_PROTOCOLS; j++) { if (g_BindingInfo.Protocols[j].Installed) { // // Add this client/protocl/adapter possibility to // the hash table. // wsprintf ( buffer, TEXT("%s,%s,%s"), g_BindingInfo.Clients[i].Text, g_BindingInfo.Protocols[j].Text, e.szName ); HtAddStringAndData (Table, buffer, &enabled); DEBUGMSG ((DBG_VERBOSE, "DISABLED BINDING: %s", buffer)); } } } for (j = 0; j < NUM_PROTOCOLS; j++) { // // Add the protocol adapter mapping into the table. // if (g_BindingInfo.Protocols[j].Installed) { wsprintf (buffer, TEXT("%s,%s"), g_BindingInfo.Protocols[j].Text, e.szName); HtAddStringAndData (Table, buffer, &enabled); DEBUGMSG ((DBG_VERBOSE, "DISABLED BINDING: %s", buffer)); } } } while(MemDbEnumNextValue (&e)); } } VOID pResolveEnabledBindings ( HASHTABLE Table ) { // // This function removes enabled binding paths from the hash table provided // (previously initialized with all of the binding possibilities that could // exist on the machine..) // NETCARD_ENUM eCard; REGVALUE_ENUM eValues; REGVALUE_ENUM eProtocolValues; UINT curAdapter = 1; TCHAR adapterString[30]; PTSTR bindingsPath = NULL; PTSTR protocolBindingsPath = NULL; PTSTR protocol = NULL; PTSTR client = NULL; HKEY key; HKEY protocolsKey; TCHAR buffer[MEMDB_MAX]; HASHITEM index; BOOL enabled=TRUE; if (EnumFirstNetCard (&eCard)) { __try { do { // // Skip the Dial-Up Adapter. // if (StringIMatch (eCard.Description, S_DIALUP_ADAPTER_DESC)) { continue; } // // Create the adapter section name for this adapter. // wsprintf (adapterString, TEXT("Adapter%u"), curAdapter); curAdapter++; bindingsPath = JoinPaths(eCard.CurrentKey,S_BINDINGS); key = OpenRegKeyStr (bindingsPath); FreePathString (bindingsPath); if (!key) { continue; } if (EnumFirstRegValue (&eValues, key)) { do { protocol = NULL; /* if (IsPatternMatch (TEXT("NETBEUI*"), eValues.ValueName)) { protocol = S_MS_NETBEUI; } else if (IsPatternMatch (TEXT("MSDLC*"), eValues.ValueName)) { protocol = S_MS_DLC; } */ if (IsPatternMatch (TEXT("NWLINK*"), eValues.ValueName)) { protocol = S_MS_NWIPX; } else if (IsPatternMatch (TEXT("MSTCP*"), eValues.ValueName)) { protocol = S_MS_TCPIP; } if (!protocol) { continue; } // // Enable protocol <-> adapter binding. // wsprintf (buffer, TEXT("%s,%s"), protocol, adapterString); index = HtFindString (Table, buffer); if (index) { HtSetStringData (Table, index, (PBYTE) &enabled); } DEBUGMSG ((DBG_VERBOSE, "ENABLED BINDING: %s", buffer)); // // Search the bindings and enable protocol <-> client bindings // protocolBindingsPath = JoinPaths (S_NETWORK_BRANCH, eValues.ValueName); bindingsPath = JoinPaths (protocolBindingsPath, S_BINDINGS); FreePathString(protocolBindingsPath); protocolsKey = OpenRegKeyStr (bindingsPath); FreePathString (bindingsPath); if (!protocolsKey) { continue; } if (EnumFirstRegValue (&eProtocolValues, protocolsKey)) { do { client = NULL; if (IsPatternMatch (TEXT("NWREDIR*"), eProtocolValues.ValueName) || IsPatternMatch (TEXT("NWLINK*"), eProtocolValues.ValueName) || IsPatternMatch (TEXT("NOVELLIPX32*"), eProtocolValues.ValueName) || IsPatternMatch (TEXT("IPXODI*"), eProtocolValues.ValueName) || IsPatternMatch (TEXT("NOVELL32*"), eProtocolValues.ValueName)) { client = S_MS_NWCLIENT; } else if (IsPatternMatch (TEXT("VREDIR*"), eProtocolValues.ValueName)) { client = S_MS_NETCLIENT; } if (client) { // // We can now remove a path from the bindings table -- we've got an // enabled one. // wsprintf (buffer, TEXT("%s,%s,%s"), client, protocol, adapterString); index = HtFindString (Table, buffer); if (index) { HtSetStringData (Table, index, (PBYTE) &enabled); } DEBUGMSG ((DBG_VERBOSE, "ENABLED BINDING: %s", buffer)); } } while (EnumNextRegValue (&eProtocolValues)); } CloseRegKey (protocolsKey); } while (EnumNextRegValue (&eValues)); } CloseRegKey (key); } while (EnumNextNetCard (&eCard)); } __except (EXCEPTION_EXECUTE_HANDLER) { EnumNetCardAbort(&eCard); LOG ((LOG_ERROR,"Caught exception while gathering data about network adapters.")); return; } } } BOOL pSplitBindingPathIntoComponents ( IN OUT PTSTR BindingPath, OUT PCTSTR * Client, OUT PCTSTR * Protocol, OUT PCTSTR * Adapter ) { PTSTR p; p = BindingPath; *Client = p; p = _tcschr (p, TEXT(',')); MYASSERT (p); if (!p) { return FALSE; } *p = 0; p = _tcsinc (p); *Protocol = p; p = _tcschr (p, TEXT(',')); if (!p) { // // Only Adapter and Protocol specified. // *Adapter = *Protocol; *Protocol = *Client; *Client = NULL; } else { // // Adapter/Protocol/Client specified. // *p = 0; p = _tcsinc (p); *Adapter = p; } return TRUE; } PCTSTR pBindingSections ( VOID ) { HASHTABLE disabledBindings; HASHTABLE_ENUM e; TCHAR bindingString[MEMDB_MAX]; PTSTR client = NULL; PTSTR protocol = NULL; PTSTR adapter = NULL; DWORD keyVal = 0; disabledBindings = HtAllocWithData (sizeof(BOOL)); if (!disabledBindings) { return NULL; } pInitializeBindingInfo (disabledBindings); pResolveEnabledBindings (disabledBindings); // // Simply enumerate the table and blast each disabled setting // to winnt.sif. // if (EnumFirstHashTableString (&e, disabledBindings)) { do { if (!*((PBOOL) e.ExtraData)) { StringCopy (bindingString, e.String); if (!pSplitBindingPathIntoComponents (bindingString, &client, &protocol, &adapter)) { continue; } MYASSERT (protocol && adapter); // // Write full path.. // keyVal = 0; if (client) { keyVal = WriteInfKeyEx (S_NETBINDINGS, S_DISABLED, client, keyVal, TRUE); } keyVal = WriteInfKeyEx (S_NETBINDINGS, S_DISABLED, protocol, keyVal, TRUE); keyVal = WriteInfKeyEx (S_NETBINDINGS, S_DISABLED, adapter, keyVal, TRUE); DEBUGMSG ((DBG_VERBOSE, "DISABLED BINDING: %s", e.String)); } } while (EnumNextHashTableString (&e)); } HtFree (disabledBindings); // // All of the binding information is handled outside the normal winntsif proceessing. Therefore, we always return // NULL here (winntsif processing continues on...) // return NULL; } // // Creation Functions // /*++ Routine Description: Each Creation function is responsible for determining wether a specific winntsif setting should be processed or not. The purpose of most of these functions is obvious. pIsNetworkingInstalled - returns TRUE if the machine has networking installed. pIsNetBeuiInstalled - TRUE if NETBEUI is installed on the machine. pIsMsDlcInstalled - TRUE if MSDLC or MSDLC32 is installed on the machine. pIsNwIpxInstalled - TRUE if the machine is running the IPX protocol. pIsTcpIpInstalled - TRUE if the machine is running the TCPIP protocol. pIsDnsEnabled - TRUE DNS support is enabled. pIsRasInstalled TRUE if the machine uses Remote Access. pIsWkstaInstalled TRUE if the Workstation Service is installed. pIsNwClientInstalled TRUE if the NWLINK is installed. pHasStaticIpAddress TRUE if the machine has a static IP address. Arguments: None. Return Value: TRUE if the winntsif engine should process the setting, FALSE otherwise. --*/ BOOL pIsNetworkingInstalled ( VOID ) { static BOOL firstTime = TRUE; static BOOL rCreate = FALSE; if (firstTime) { firstTime = FALSE; rCreate = HwComp_DialUpAdapterFound() || MemDbGetEndpointValueEx( MEMDB_CATEGORY_NETADAPTERS, TEXT("Adapter1"), MEMDB_FIELD_PNPID, g_TempBuffer ); } return rCreate; } BOOL pInstallMsServer ( VOID ) { if (MemDbGetEndpointValueEx( MEMDB_CATEGORY_NETADAPTERS, TEXT("Adapter1"), MEMDB_FIELD_PNPID, g_TempBuffer )) { return TRUE; } return FALSE; } BOOL pIsNetBeuiInstalled ( VOID ) { static BOOL rCreate = FALSE; static BOOL firstTime = TRUE; UINT i; if (firstTime) { rCreate = pIsNetworkingInstalled() && pDoesNetComponentHaveBindings(S_NETBEUI); if (rCreate) { // // Need to make sure tcp/ip is taken care of when processing bindings. // for (i=0;idwFileVersionMS < FileVerMS || (VsInfo->dwFileVersionMS == FileVerMS && VsInfo->dwFileVersionLS <= FileVerLS); } } PoolMemReleaseMemory (g_GlobalPool, lpData); } return b; } BOOL pNewerW95upgntOnCD ( IN PCTSTR ReplacementDll ) { DWORD dwLength, dwTemp; PVOID lpData = NULL; VS_FIXEDFILEINFO *VsInfo; UINT DataLength; DWORD result; UINT u; PCTSTR pathCDdllSource = NULL; PCTSTR pathCDdllTarget = NULL; DWORD FileVerMS; DWORD FileVerLS; BOOL b = TRUE; __try { for (u = 0; u < SOURCEDIRECTORYCOUNT(); u++) { pathCDdllSource = JoinPaths (SOURCEDIRECTORY(u), WINNT_WIN95UPG_NTKEY); if (DoesFileExist (pathCDdllSource)) { break; } FreePathString (pathCDdllSource); pathCDdllSource = NULL; } if (!pathCDdllSource) { __leave; } pathCDdllTarget = JoinPaths (g_TempDir, WINNT_WIN95UPG_NTKEY); SetFileAttributes (pathCDdllTarget, FILE_ATTRIBUTE_NORMAL); DeleteFile (pathCDdllTarget); result = SetupDecompressOrCopyFile (pathCDdllSource, pathCDdllTarget, 0); if (result != ERROR_SUCCESS) { LOG (( LOG_ERROR, "pGetW95UpgNTCDFileVersion: Unable to decompress %s", pathCDdllSource )); __leave; } if (dwLength = GetFileVersionInfoSize ((PTSTR)pathCDdllTarget, &dwTemp)) { lpData = PoolMemGetMemory (g_GlobalPool, dwLength); if (GetFileVersionInfo ((PTSTR)pathCDdllTarget, 0, dwLength, lpData)) { if (VerQueryValue (lpData, TEXT("\\"), &VsInfo, &DataLength)) { b = pFileVersionLesser ( ReplacementDll, VsInfo->dwFileVersionMS, VsInfo->dwFileVersionLS ); } } } } __finally { if (lpData) { PoolMemReleaseMemory (g_GlobalPool, lpData); } if (pathCDdllSource) { FreePathString (pathCDdllSource); } if (pathCDdllTarget) { SetFileAttributes (pathCDdllTarget, FILE_ATTRIBUTE_NORMAL); DeleteFile (pathCDdllTarget); FreePathString (pathCDdllTarget); } } return b; } /*++ Routine Description: pGetReplacementDll is responsible for scanning the registry for an NT side win9x replacement dll and putting it into the answer file if found. Arguments: None. Return Value: a multisz containing the required data, or NULL if it could not be found. --*/ PCTSTR pGetReplacementDll ( VOID ) { HKEY key = NULL; PTSTR val = NULL; BOOL b = FALSE; key = OpenRegKey (HKEY_LOCAL_MACHINE, WINNT_WIN95UPG_REPLACEMENT); if (!key) { return NULL; } __try { val = GetRegValueString (key, WINNT_WIN95UPG_NTKEY); if (!val) { __leave; } if (pNewerW95upgntOnCD (val)) { __leave; } CLEARBUFFER(); StringCopy (g_TempBuffer, val); b = TRUE; } __finally { CloseRegKey (key); if (val) { MemFree (g_hHeap, 0, val); } } return b ? g_TempBuffer : NULL; } #endif VOID pGenerateRandomPassword ( OUT PTSTR Password ) /*++ Routine Description: pGenerateRandomPassword creates a password of upper-case, lower-case and numeric letters. The password has a length between 8 and 14 characters. Arguments: Password - Receives the generated password Return Value: none --*/ { INT Length; TCHAR Offset; INT Limit; PTSTR p; // // Generate a random length based on the tick count // srand (GetTickCount()); Length = (rand() % 6) + 8; p = Password; while (Length) { Limit = rand() % 3; Offset = TEXT(' '); if (Limit == 0) { Limit = 10; Offset = TEXT('0'); } else if (Limit == 1) { Limit = 26; Offset = TEXT('a'); } else if (Limit == 2) { Limit = 26; Offset = TEXT('A'); } *p = Offset + (rand() % Limit); p++; Length--; } *p = 0; DEBUGMSG ((DBG_WINNTSIF, "Generated password: %s", Password)); } PCTSTR pSetAdminPassword ( VOID ) /*++ Routine Description: pSetAdminPassword retrieves the AdminPassword as specified in the unattend file, if present. Otherwise it generates a random one. This information is stored in memdb to be available later. Arguments: None. Return Value: A pointer to the admin password. --*/ { BOOL attribs = 0; TCHAR node[MEMDB_MAX]; BOOL blank = FALSE; if (!g_UnattendScriptFile || !*g_UnattendScriptFile || !GetPrivateProfileString ( S_GUIUNATTENDED, WINNT_US_ADMINPASS, TEXT(""), g_TempBuffer, MEMDB_MAX, *g_UnattendScriptFile ) ) { if (g_PersonalSKU) { // // for Personal set an empty admin password // StringCopy (g_TempBuffer, TEXT("*")); } else { pGenerateRandomPassword (g_TempBuffer); attribs = PASSWORD_ATTR_RANDOM; } } else { if (GetPrivateProfileString ( S_GUIUNATTENDED, WINNT_US_ENCRYPTEDADMINPASS, TEXT(""), node, MEMDB_MAX, *g_UnattendScriptFile )) { if (StringIMatch (node, S_YES) || StringIMatch (node, TEXT("1"))) { attribs = PASSWORD_ATTR_ENCRYPTED; } } } if (StringMatch (g_TempBuffer, TEXT("*"))) { blank = TRUE; *g_TempBuffer = 0; } MemDbBuildKey (node, MEMDB_CATEGORY_STATE, MEMDB_ITEM_ADMIN_PASSWORD, g_TempBuffer, NULL); MemDbSetValue (node, attribs); if (blank) { wsprintf (g_TempBuffer, TEXT("*%c"), 0); } return g_TempBuffer; } //ICS upgrade functions /*++ Routine Description: pGetNetAdapterSectionNameBasedOnDriverID retrieves the net adapter name (for example, "Adapter1") based on the ID of the driver of this adapter. For example, "0001" is the ID for the driver under HKLM\System\CurrentControlSet\Services\Class\Net\0001. Arguments: String of the driver ID Return Value: A pointer to the adapter name --*/ PCTSTR pGetNetAdapterSectionNameBasedOnDriverID( PCTSTR pszDriverID ) { TCHAR szID[256]; PCTSTR rAdapterSections = NULL; PTSTR string = g_TempBuffer; PCSTR pReturn = NULL; MEMDB_ENUM e; //first cache the string for the driver ID lstrcpyn(szID, pszDriverID, sizeof(szID)/sizeof(szID[0])); *string = 0; // // Enumerate all adapters, and create an entry for each adapter that has // TCP bindings. // if (pEnumFirstAdapterByBinding(&e, S_MSTCP)) { do { // // check whether the driver ID of the card // MemDbGetEndpointValueEx( MEMDB_CATEGORY_NETADAPTERS, e.szName, MEMDB_FIELD_DRIVER, g_TempBuffer ); string = _tcsstr(g_TempBuffer, S_NET_PREFIX); if (string) { string += TcharCount(S_NET_PREFIX); if (0 == StringICompare(string, szID)) { pReturn = e.szName; break; } } } while (pEnumNextAdapterByBinding(&e,S_MSTCP)); } return pReturn; } /*++ Routine Description: pHomenetSection is used to control whether the "ICSharing" section should show up in the answer file or not. If the ICS (Internet Connection Sharing) is installed, then we return "ICSharing" string for the first time of this call, otherwise always return NULL. In such way, we can ensure the "ICSharing" section will show up in the answer file ONLY when ICS is installed. Arguments: None Return Value: A pointer to "ICSharing" string if ICS is installed. Otherwise, return NULL. --*/ PCTSTR pHomenetSection( VOID ) { static BOOL firstTime = TRUE; PCTSTR pReturn = NULL; if (firstTime && NULL != pGetRegistryValue (S_ICS_KEY, S_EXTERNAL_ADAPTER)) { firstTime = FALSE; StringCopy(g_CurrentSection, S_HOMENET); pReturn = g_CurrentSection; } return pReturn; } /*++ Routine Description: pExternalIsAdapter detects whether the External connection of ICS is a LAN connection --*/ BOOL pExternalIsAdapter( void ) { BOOL fRet = FALSE; if (NULL == pGetRegistryValue (S_ICS_KEY, S_EXTERNAL_ADAPTER)) { return FALSE; } return (NULL != pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer)); return fRet; } /*++ Routine Description: pExternalIsRasConn detects whether the External connection of ICS is a RAS connection --*/ BOOL pExternalIsRasConn( void ) { if (NULL == pGetRegistryValue (S_ICS_KEY, S_EXTERNAL_ADAPTER)) { return FALSE; } return NULL == pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer); } /*++ Routine Description: pHasInternalAdapter detects whether the first Internal Adapter is specified for ICS --*/ BOOL pHasInternalAdapter( void ) { //To have the "InternalAdapter" key, two conditions have to be statisfy: //(1) there is "InternalAdapter" AND //(2) this is the only internal adapter, i.e. internal is NOT bridge return (NULL != pGetRegistryValue (S_ICS_KEY, S_INTERNAL_ADAPTER) && (!g_fIcsInternalIsBridge)); } /*++ Routine Description: pHasBridge detects whether there are two ICS Internal Adapters. If so, we need to bridge those two adapter together --*/ pHasBridge( void ) { return g_fIcsInternalIsBridge; } /*++ Routine Description: pIcsExternalAdapter retrieves the adapter name (for example, "Adapter1") for external ICS connection --*/ PCTSTR pIcsExternalAdapter ( VOID ) { PCSTR pszAdapter; if (NULL == pGetRegistryValue(S_ICS_KEY, S_EXTERNAL_ADAPTER)) return NULL; pszAdapter = pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer); if (NULL == pszAdapter) { return NULL; } //ICSHARE adapter's TCP setting overwrite the TCP setting of the external adapter //We remember all the info here and will use it when saving the TCP setting of this adapter if (0 != lstrlen(g_IcsAdapter)) { //this flag will be used to upgrade the TCP/IP settings of the ICSHARE adapter g_fHasIcsExternalAdapter = TRUE; lstrcpyn(g_IcsExternalAdapter, pszAdapter, sizeof(g_IcsExternalAdapter)/sizeof(g_IcsExternalAdapter[0])); } CLEARBUFFER(); StringCopy(g_TempBuffer, pszAdapter); return g_TempBuffer; } /*++ Routine Description: pIcsExternalConnectionName retrieves the name (for example, "My Dial-up Connection") for external ICS RAS connection --*/ PCSTR pIcsExternalConnectionName ( VOID ) { TCHAR szKey[MEMDB_MAX * 2]; // Contains the current value returned from pGetRegistryValue PCSTR pReturn = NULL; if (NULL == pGetRegistryValue(S_ICS_KEY, S_EXTERNAL_ADAPTER)) { return NULL; } StringCopy(szKey, S_NET_DRIVER_KEY); StringCat(szKey, _T("\\")); StringCat(szKey, g_TempBuffer); //The "DriverDesc" value has to be "Dial-Up Adapter" if (NULL == pGetRegistryValue(szKey, S_DRIVERDESC) && 0 != StringCompare(g_TempBuffer, S_DIALUP_ADAPTER_DESC)) { return NULL; } //the default ras connection should be the external one. The reg location of the connection name is at //HKCU\RemoteAccess\Default return pGetRegistryValue(S_REMOTEACCESS_KEY, S_RAS_DEFAULT); } /*++ Routine Description: pInternalIsBridge detects whether there are two ICS Internal Adapters. If so, we need to bridge those two adapter together --*/ PCSTR pInternalIsBridge( VOID ) { g_fIcsInternalIsBridge = (NULL != pGetRegistryValue (S_ICS_KEY, S_INTERNAL_ADAPTER) && NULL != pGetRegistryValue (S_ICS_KEY, S_INTERNAL_ADAPTER2)); CLEARBUFFER(); StringCopy(g_TempBuffer, (g_fIcsInternalIsBridge) ? S_YES : S_NO); return g_TempBuffer; } /*++ Routine Description: pInternalAdapter retrieves the adapter name (for example, "Adapter1") for first internal ICS connection NOTE: internal connections has to be LAN conncections --*/ PCSTR pInternalAdapter( VOID ) { PCSTR pszAdapter; if (NULL == pGetRegistryValue(S_ICS_KEY, S_INTERNAL_ADAPTER)) { return NULL; } pszAdapter = pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer); if (NULL == pszAdapter) { return NULL; } CLEARBUFFER(); StringCopy(g_TempBuffer, pszAdapter); return g_TempBuffer; } /*++ Routine Description: pBridge retrieves name the two internal adapter names (for example, "Adapter2") --*/ PCSTR pBridge( VOID ) { TCHAR szBuff[MEMDB_MAX] = {0}; TCHAR * psz = szBuff; PCSTR pszAdapter; if (NULL == pGetRegistryValue(S_ICS_KEY, S_INTERNAL_ADAPTER)) { return NULL; } pszAdapter = pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer); if (NULL == pszAdapter) { return NULL; } StringCopy(psz, pszAdapter); psz = GetEndOfString(szBuff) + 1; if (NULL == pGetRegistryValue(S_ICS_KEY, S_INTERNAL_ADAPTER2)) { return NULL; } pszAdapter = pGetNetAdapterSectionNameBasedOnDriverID(g_TempBuffer); if (NULL == pszAdapter) { return NULL; } StringCopy(psz, pszAdapter); psz = GetEndOfString(psz) + 1; *psz = 0; CLEARBUFFER(); memcpy(g_TempBuffer, szBuff, sizeof(szBuff) < sizeof(g_TempBuffer) ? sizeof(szBuff) : sizeof(g_TempBuffer)); return g_TempBuffer; } /*++ Routine Description: pDialOnDemand retrieves the dial-on-demand feature for ICS connection --*/ PCSTR pDialOnDemand( VOID ) { PCTSTR KeyString = S_INET_SETTINGS; PCTSTR ValueString = S_ENABLE_AUTODIAL; PCTSTR rString = NULL; HKEY key = NULL; PBYTE data = NULL; DWORD type = REG_NONE; DWORD BufferSize = 0; LONG rc = ERROR_SUCCESS; PCTSTR end; // // // Open registry key. // key = OpenRegKeyStr(KeyString); if (!key) { DEBUGMSG((DBG_WINNTSIF, "Key %s does not exist.",KeyString)); return NULL; } // // Get type of data // rc = RegQueryValueExA (key, ValueString, NULL, &type, NULL, &BufferSize); if (rc != ERROR_SUCCESS) { DEBUGMSG((DBG_WINNTSIF,"RegQueryValueEx failed for %s[%s]. Value may not exist.",KeyString,ValueString)); CloseRegKey(key); SetLastError (rc); return NULL; } if (0 == BufferSize || (REG_DWORD != type && REG_BINARY != type)) { DEBUGMSG((DBG_WINNTSIF,"EnableAutoDial is not a DWORD, nor a Binary.")); CloseRegKey(key); return NULL; } if (REG_BINARY == type && sizeof(DWORD) != BufferSize) { DEBUGMSG((DBG_WINNTSIF,"EnableAutoDial is a binary, but the buffer size is not 4.")); CloseRegKey(key); return NULL; } data = (PBYTE) MemAlloc (g_hHeap, 0, BufferSize); if (NULL == data) { DEBUGMSG((DBG_WINNTSIF,"Alloc failed. Out of memory.")); CloseRegKey(key); return NULL; } rc = RegQueryValueExA (key, ValueString, NULL, NULL, data, &BufferSize); if (rc != ERROR_SUCCESS) { DEBUGMSG((DBG_WINNTSIF,"RegQueryValueEx failed for %s[%s]. Value may not exist.",KeyString,ValueString)); MemFree(g_hHeap, 0, data); data = NULL; CloseRegKey(key); SetLastError (rc); return NULL; } CLEARBUFFER(); wsprintf(g_TempBuffer,"%u",*((DWORD*) data)); // // Clean up resources. // CloseRegKey(key); if (data) { MemFree(g_hHeap, 0, data); data = NULL; } return g_TempBuffer; } // // Processing functions // // // g_SectionList contains the list of all winntsif sections that will be enumerated and processed by BuildWinntSifFile. // SECTION g_SectionList[] = {WINNTSIF_SECTIONS /*,*/ {NULL,NULL,{{LAST_SETTING,NULL,NULL,{NULL,NULL}}}}}; /*++ Routine Description: pProcessSectionSettings is responsible for processing a single section worth of data. For this section, it will process all of the settings in the settinglist passed in. Arguments: SectionName - The name of the section being processed. SettingsList - a list of settings to be processed for this sections. Return Value: --*/ BOOL pProcessSectionSettings ( IN PCTSTR SectionName, IN PSETTING SettingsList ) { PSETTING curSetting = SettingsList; PTSTR data = NULL; PTSTR p; MULTISZ_ENUM e; UINT index = 0; TCHAR key[MEMDB_MAX]; MYASSERT(curSetting); MYASSERT(SectionName); DEBUGMSG((DBG_WINNTSIF,"pProcessSectionSettings: Processing [%s] Section...",SectionName)); // // The last setting in the list is a null setting with the type LAST_SETTING. We use this as the // break condition of our loop. // while (curSetting -> SettingType != LAST_SETTING) { if (!curSetting -> CreationFunction || curSetting -> CreationFunction()) { // // Any setting that gets to this point MUST have a key name. // Since this data is static, we just assert this. // MYASSERT(curSetting -> KeyName); // // We must still get the data for this particular setting. How we get that data is determined by // the SettingType of the current setting. If at the end, data is NULL, we will write nothing. // switch (curSetting -> SettingType) { case FUNCTION_SETTING: data = (PTSTR) curSetting -> Data.Function(); break; case STRING_SETTING: StringCopy(g_TempBuffer,curSetting -> Data.String); p = GetEndOfString (g_TempBuffer) + 1; *p = 0; data = g_TempBuffer; break; case REGISTRY_SETTING: data = (PTSTR) pGetRegistryValue(curSetting -> Data.Registry.Key, curSetting -> Data.Registry.Value); break; default: DEBUGMSG(( DBG_WHOOPS, "pProcessSectionSettings: Unexpected Setting Type for Section %s, Key %s. (Type: %u)", SectionName, curSetting -> KeyName, curSetting -> SettingType )); break; } // // If we found data, go ahead and create the setting. All data is stored in multi strings, typically only one string long. // if (data) { // // Make sure this isn't suppressed. // MemDbBuildKey ( key, MEMDB_CATEGORY_SUPPRESS_ANSWER_FILE_SETTINGS, SectionName, curSetting->KeyName, NULL ); if (MemDbGetPatternValue (key, NULL)) { DEBUGMSG ((DBG_WINNTSIF, "Answer File Section is suppressed: [%s] %s", SectionName, curSetting->KeyName)); } else { DEBUGMSG((DBG_WINNTSIF,"Creating WinntSif Entry: Section: %s, Key: %s.",SectionName, curSetting -> KeyName)); if (EnumFirstMultiSz(&e,data)) { index = 0; do { index = WriteInfKeyEx(SectionName, curSetting -> KeyName, e.CurrentString, index, FALSE); DEBUGMSG_IF(( !index, DBG_ERROR, "pProcessSectionSettings: WriteInfKeyEx Failed. Section: %s Key: %s Value: %s", SectionName, curSetting -> KeyName, e.CurrentString )); DEBUGMSG_IF((index,DBG_WINNTSIF,"Value: %s",e.CurrentString)); } while (EnumNextMultiSz(&e)); } } } ELSE_DEBUGMSG((DBG_WARNING,"pProcessSectionSettings: No data for Section %s, Key %s.",SectionName, curSetting -> KeyName)); } curSetting++; } return TRUE; } /*++ Routine Description: BuildWinntSifFile is responsible for writing all of the necessary unattend settings to the winnt.sif file. The Win9xUpg code uses these unattended settings to control the behavior of text mode and GUI mode setup so that the settings gathered from win9x are incorporated into the new NT system. The settings to be written are kept in the global list g_SettingL ist which is itself built from macro expansion lists. This function cycles through these settings, calculating wether each setting should be written and if so, with what data. Arguments: None. Return Value: TRUE if the function returned successfully, FALSE otherwise. --*/ BOOL pBuildWinntSifFile ( VOID ) { BOOL rSuccess = TRUE; PSECTION curSection = g_SectionList; PCTSTR sectionName = NULL; while (curSection -> SectionString || curSection -> SectionFunction) { sectionName = curSection -> SectionString ? curSection -> SectionString : curSection -> SectionFunction(); while (sectionName) { if (!pProcessSectionSettings (sectionName,curSection -> SettingList)) { LOG ((LOG_ERROR,"Unable to process answer file settings for %s Section.",sectionName)); rSuccess = FALSE; } // // If the section name was from a static string, we set the sectionName to NULL, exiting this loop. // If the section name was from a function, we call the function again. If there is another // section to build, it will return a new name, otherwise, it will return NULL. // sectionName = curSection -> SectionString ? NULL : curSection -> SectionFunction(); } // // Go to the next setting. // curSection++; } return rSuccess; } DWORD BuildWinntSifFile ( DWORD Request ) { switch (Request) { case REQUEST_QUERYTICKS: return TICKS_BUILD_UNATTEND; case REQUEST_RUN: if (!pBuildWinntSifFile ()) { return GetLastError (); } else { return ERROR_SUCCESS; } default: DEBUGMSG ((DBG_ERROR, "Bad parameter in BuildWinntSif")); } return 0; } VOID TerminateWinntSifBuilder ( VOID ) { if (g_LocalePool) { HtFree (g_LocaleTable); PoolMemDestroyPool (g_LocalePool); g_LocaleTable = NULL; g_LocalePool = NULL; } }