Jakiś tydzień temu zabrałem się za kończenie nowych obrazów dla komputerów na salach laboratoryjnych. Poza drobnymi zmianami (dystrybucja lekkich aplikacji via SCCM) chciałem zminimalizować czas jaki poświęcałem na zmianę nazw komputerów po klonowaniu. Kilka lat temu przyjęliśmy, że komputery na salach będą się nazywać KompXXX-YY. Gdzie XXX to numer sali a YY to komputer na miejscu YY licząc od prawej strony sali. W systemach Linux rozwiązanie było proste :) Dorzuciliśmy opcję 12 do DHCP i systemy pobierały sobie odpowiednią nazwę z serwera DHCP. W przypadku Windows sprawa się komplikuje i dobrze by było nazwy zmienić na samym początku by nie robić sobie bałaganu. Nazwę można pobrać czy wygenerować na kilka sposobów:

  • pobranie nazwy z DNS – wszystko fajnie, tylko jak nazwy nie mam w DNS bo pojawi się po dodaniu Windowsa do domeny
  • wykorzystanie Sysprepa do generowania nazwy Komp400-XX – Muszę mieć kilka plików unattend do Sysprepa :/
  • przeparsowanie otrzymanego Adresu IP i wygenerowanie nazwy komputera.  - Czasem od naszej reguły nazewnictwa zdarzają się wyjątki:)
  • pobranie nazwy z DHCP – idealne rozwiązanie.

 

Skoro miałem wszystkie nazwy w DHCP to teraz wystarczyło pobrać odpowiednią opcje i zamienić nazwę stanowiska:) Jak sie okazało otrzymanie nazwy komputera z DHCP i wrzucenie jej do jakieś zmiennej nie jest takie proste. Jedyne dostępne API dla Windowsowego klienta DHCP jest napisane w C++:/  Opcje z DHCP pobieramy za pomocą funkcji DHCPRequestParams, która przyjmuje na wejściu kilka parametrów. Między innymi wymagane jest podanie nazwy interfejsu sieciowego. TUTAJ UWAGA nazwa interfejsu jest rozumiana jako identyfikator urządzenia a nie jako np. “Połączenie lokalne”. Identyfikator również możemy sobie wyciągnąć za pomocą API do interfejsów sieciowych.  Przyznam się, że stworzenie działającej aplikacji, która pobiera identyfikator pierwszego interfejsu a następnie wyciąga z DHCP odpowiednią nazwę zepsuł mi trochę krwi. Najpierw musiałem sobie przypomnieć pewne podstawy Visual C++, potem musiałem załapać, że w nazwie interfejsu mam podać identyfikator. Następnie musiałem rozgryźć jak dynamicznie wyszukać ten identyfikator. Koniec końców udało mi się napisać mały programik w VC++, który pobiera odpowiednią opcje z DHCP i za pomocą newsida zmienia nazwę i SID. Poniżej kod, który pobiera id pierwszego interfejsu i następnie wyciąga z DHCP nazwę stacji.  Przypominam iż z zawodu nie jestem programistą i na samym początku dążę do tego by kod działał wedle moich oczekiwaniami. Następnie jak mam czas to staram się go upiększać. W kodzie częściowo starałem się wykorzystać przykłady dostępne na stronach MSDN.

#include < winsock2.h>
#include < iphlpapi.h >
#include < stdio.h >
#include < stdlib.h >
#include < windows.h >
#include < dhcpcsdk.h >
#include < string >
#include < wchar.h >
#pragma comment( lib, "dhcpcsvc.lib" )
#pragma comment(lib, "IPHLPAPI.lib")
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

int __cdecl main()
{
CONST int iAdapterIDLenght= 260;
CONST int iHostnameLenght=60;
int dw;
CHAR pszHostNameBuf[iHostnameLenght];
DWORD dwError, dwSize;
CHAR TmpBuffer[iHostnameLenght];
WCHAR wcAdapterName[iAdapterIDLenght];
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADAPTER_INFO pAdapter = NULL;
DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);

pAdapterInfo = (IP_ADAPTER_INFO *) MALLOC(sizeof (IP_ADAPTER_INFO));
if (pAdapterInfo == NULL)
{
printf("Error allocating memory needed to call GetAdaptersinfo\n");
return 1;
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
{
FREE(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) MALLOC(ulOutBufLen);
if (pAdapterInfo == NULL)
{
printf("Error allocating memory needed to call GetAdaptersinfo\n");
return 1;
}
}

if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
pAdapter = pAdapterInfo;
dw = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pAdapter->AdapterName, -1, wcAdapterName, 0);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pAdapter->AdapterName, -1, wcAdapterName, dw);

DHCPCAPI_PARAMS DhcpApiHostNameParams = {
0, // Flags
OPTION_HOST_NAME, // OptionId
FALSE, // vendor specific?
NULL, // data filled in on return
0 // nBytes
};
DHCPCAPI_PARAMS_ARRAY RequestParams = {
1, // only one option to request
&DhcpApiHostNameParams
};

DHCPCAPI_PARAMS_ARRAY SendParams = {0,NULL};

dwSize = sizeof(TmpBuffer);
dwError = DhcpRequestParams(
DHCPCAPI_REQUEST_SYNCHRONOUS, // Flags
NULL, // Reserved
wcAdapterName, // Adapter Name
NULL, // not using class id
SendParams, // sent parameters
RequestParams, // requesting params
(PBYTE) TmpBuffer, // buffer
&dwSize, // buffer size
NULL // Request ID
);

if( ERROR_MORE_DATA == dwError )
{
printf("Error: %s",dwError);
}

if( NO_ERROR == dwError )
{

if( DhcpApiHostNameParams.nBytesData )
{

CopyMemory(pszHostNameBuf, DhcpApiHostNameParams.Data,DhcpApiHostNameParams.nBytesData);
pszHostNameBuf[DhcpApiHostNameParams.nBytesData] = '\0';
printf("(12) DHCP Host Name: %s\n",pszHostNameBuf);
return 0;
}
}
else
{
printf("Error: %u",dwError);
}

}
else
{
printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);

}
if (pAdapterInfo)
FREE(pAdapterInfo);
getchar();
return 0;
}


W wolnej chwili postaram się napisać małą bibliotekę do podpięcia pod C#, tak bym mógł pobrać dowolne opcje z DHCP.