/*++ Copyright (c) 1994 Microsoft Corporation Module Name: dhcploc.c Abstract: This apps is used to detect the rogue DHCP server on a subnet. To build, 'nmake UMTEST=dhcpcli' Author: Madan Appiah (madana) 21-Oct-1993 Environment: User Mode - Win32 Revision History: Oct 1996, (a-martih) Martin Holladay Corrected AV bug when passed an unknown cmd-line parameter. --*/ #include #include #include #include #include #include #define RECEIVE_TIMEOUT 5 // in secs. 5 secs #define THREAD_TERMINATION_TIMEOUT 10000 // in msecs. 10 secs #define SOCKET_RECEIVE_BUFFER_SIZE 1024 * 4 // 4K max. #define AUTH_SERVERS_MAX 64 #define SMALL_BUFFER_SIZE 32 #define ALERT_INTERVAL 5 * 60 // 5 mins #define ALERT_MESSAGE_LENGTH 256 #define MAX_ALERT_NAMES 256 DWORD GlobalAuthServers[AUTH_SERVERS_MAX]; BOOL GlobalNoAuthPrint = FALSE; DWORD GlobalAuthServersCount = 0; HANDLE GlobalRecvThreadHandle = NULL; BOOL GlobalTerminate = FALSE; DWORD GlobalIpAddress = 0; time_t GlobalLastAlertTime = 0; DWORD GlobalAlertInterval = ALERT_INTERVAL; LPWSTR GlobalAlertNames[MAX_ALERT_NAMES]; DWORD GlobalAlertNamesCount = 0; #if DBG VOID DhcpPrintRoutine( IN DWORD DebugFlag, IN LPSTR Format, ... ) { #define MAX_PRINTF_LEN 1024 // Arbitrary. va_list arglist; char OutputBuffer[MAX_PRINTF_LEN]; ULONG length = 0; // // Put a the information requested by the caller onto the line // va_start(arglist, Format); length += (ULONG) vsprintf(&OutputBuffer[length], Format, arglist); va_end(arglist); DhcpAssert(length <= MAX_PRINTF_LEN); // // Output to the debug terminal, // printf( "%s", OutputBuffer); } #endif // DBG DWORD OpenSocket( SOCKET *Socket, DWORD IpAddress, DWORD Port ) { DWORD Error; SOCKET Sock; DWORD OptValue; struct sockaddr_in SocketName; // // Create a socket // Sock = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ); if ( Sock == INVALID_SOCKET ) { Error = WSAGetLastError(); goto Cleanup; } // // Make the socket share-able // OptValue = TRUE; Error = setsockopt( Sock, SOL_SOCKET, SO_REUSEADDR, (LPBYTE)&OptValue, sizeof(OptValue) ); if ( Error != ERROR_SUCCESS ) { Error = WSAGetLastError(); goto Cleanup; } OptValue = TRUE; Error = setsockopt( Sock, SOL_SOCKET, SO_BROADCAST, (LPBYTE)&OptValue, sizeof(OptValue) ); if ( Error != ERROR_SUCCESS ) { Error = WSAGetLastError(); goto Cleanup; } OptValue = SOCKET_RECEIVE_BUFFER_SIZE; Error = setsockopt( Sock, SOL_SOCKET, SO_RCVBUF, (LPBYTE)&OptValue, sizeof(OptValue) ); if ( Error != ERROR_SUCCESS ) { Error = WSAGetLastError(); goto Cleanup; } SocketName.sin_family = PF_INET; SocketName.sin_port = htons( (unsigned short)Port ); SocketName.sin_addr.s_addr = IpAddress; RtlZeroMemory( SocketName.sin_zero, 8); // // Bind this socket to the DHCP server port // Error = bind( Sock, (struct sockaddr FAR *)&SocketName, sizeof( SocketName ) ); if ( Error != ERROR_SUCCESS ) { Error = WSAGetLastError(); goto Cleanup; } *Socket = Sock; Error = ERROR_SUCCESS; Cleanup: if( Error != ERROR_SUCCESS ) { // // if we aren't successful, close the socket if it is opened. // if( Sock != INVALID_SOCKET ) { closesocket( Sock ); } } return( Error ); } BOOL IsAuthServer( DWORD IpAddress ) { DWORD i; for( i = 0; i < GlobalAuthServersCount; i++ ) { if( IpAddress == GlobalAuthServers[i] ){ return( TRUE ); } } return( FALSE ); } VOID ExtractOptions1( POPTION Option, PDHCP_OPTIONS DhcpOptions, DWORD MessageSize ) { POPTION start = Option; POPTION nextOption; LPBYTE MagicCookie; // // initialize option data. // RtlZeroMemory( DhcpOptions, sizeof( DHCP_OPTIONS ) ); if ( MessageSize == 0 ) { return; } // // check magic cookie. // MagicCookie = (LPBYTE) Option; if( (*MagicCookie != (BYTE)DHCP_MAGIC_COOKIE_BYTE1) || (*(MagicCookie+1) != (BYTE)DHCP_MAGIC_COOKIE_BYTE2) || (*(MagicCookie+2) != (BYTE)DHCP_MAGIC_COOKIE_BYTE3) || (*(MagicCookie+3) != (BYTE)DHCP_MAGIC_COOKIE_BYTE4)) { return; } Option = (LPOPTION) (MagicCookie + 4); while ( Option->OptionType != OPTION_END ) { if ( Option->OptionType == OPTION_PAD || Option->OptionType == OPTION_END ) { nextOption = (LPOPTION)( (LPBYTE)(Option) + 1); } else { nextOption = (LPOPTION)( (LPBYTE)(Option) + Option->OptionLength + 2); } // // Make sure that we don't walk off the edge of the message, due // to a forgotten OPTION_END option. // if ((PCHAR)nextOption - (PCHAR)start > (long)MessageSize ) { return; } switch ( Option->OptionType ) { case OPTION_MESSAGE_TYPE: DhcpAssert( Option->OptionLength == sizeof(BYTE) ); DhcpOptions->MessageType = (BYTE UNALIGNED *)&Option->OptionValue; break; case OPTION_SUBNET_MASK: DhcpAssert( Option->OptionLength == sizeof(DWORD) ); DhcpOptions->SubnetMask = (DHCP_IP_ADDRESS UNALIGNED *)&Option->OptionValue; break; case OPTION_LEASE_TIME: DhcpAssert( Option->OptionLength == sizeof(DWORD) ); DhcpOptions->LeaseTime = (DWORD UNALIGNED *)&Option->OptionValue; break; case OPTION_SERVER_IDENTIFIER: DhcpAssert( Option->OptionLength == sizeof(DWORD) ); DhcpOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)&Option->OptionValue; break; case OPTION_RENEWAL_TIME: DhcpAssert( Option->OptionLength == sizeof(DWORD) ); DhcpOptions->T1Time = (DWORD UNALIGNED *)&Option->OptionValue; break; case OPTION_REBIND_TIME: DhcpAssert( Option->OptionLength == sizeof(DWORD) ); DhcpOptions->T2Time = (DWORD UNALIGNED *)&Option->OptionValue; break; default: break; } Option = nextOption; } return; } DWORD SendDiscovery( VOID ) { DWORD Error; BYTE MessageBuffer[DHCP_SEND_MESSAGE_SIZE]; PDHCP_MESSAGE dhcpMessage = (PDHCP_MESSAGE)MessageBuffer; LPOPTION option; LPBYTE OptionEnd; BYTE value; BYTE *HardwareAddress = "123456"; BYTE HardwareAddressLength = 6; LPSTR HostName = "ROGUE"; SOCKET Sock; struct sockaddr_in socketName; DWORD i; // // prepare message. // RtlZeroMemory( dhcpMessage, DHCP_SEND_MESSAGE_SIZE ); dhcpMessage->Operation = BOOT_REQUEST; dhcpMessage->HardwareAddressType = 1; // // Transaction ID is filled in during send // dhcpMessage->SecondsSinceBoot = 60; // random value ?? dhcpMessage->Reserved = htons(DHCP_BROADCAST); memcpy( dhcpMessage->HardwareAddress, HardwareAddress, HardwareAddressLength ); dhcpMessage->HardwareAddressLength = (BYTE)HardwareAddressLength; option = &dhcpMessage->Option; OptionEnd = (LPBYTE)dhcpMessage + DHCP_SEND_MESSAGE_SIZE; // // always add magic cookie first // option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) option, OptionEnd ); value = DHCP_DISCOVER_MESSAGE; option = DhcpAppendOption( option, OPTION_MESSAGE_TYPE, &value, 1, OptionEnd ); // // Add client ID Option. // option = DhcpAppendClientIDOption( option, 1, HardwareAddress, HardwareAddressLength, OptionEnd ); // // add Host name and comment options. // option = DhcpAppendOption( option, OPTION_HOST_NAME, (LPBYTE)HostName, (BYTE)((strlen(HostName) + 1) * sizeof(CHAR)), OptionEnd ); // // Add END option. // option = DhcpAppendOption( option, OPTION_END, NULL, 0, OptionEnd ); // // Send the message // // // open socket. // Error = OpenSocket( &Sock, GlobalIpAddress, DHCP_SERVR_PORT ); if( Error != ERROR_SUCCESS ) { printf("OpenReceiveSocket failed %ld.", Error ); return( Error ); } // // Initialize the outgoing address. // socketName.sin_family = PF_INET; socketName.sin_port = htons( DHCP_SERVR_PORT ); socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST); for ( i = 0; i < 8 ; i++ ) { socketName.sin_zero[i] = 0; } Error = sendto( Sock, (PCHAR)MessageBuffer, DHCP_SEND_MESSAGE_SIZE, 0, (struct sockaddr *)&socketName, sizeof( struct sockaddr ) ); if ( Error == SOCKET_ERROR ) { Error = WSAGetLastError(); printf("sendto failed %ld\n", Error ); return( Error ); } return( ERROR_SUCCESS ); } VOID LogEvent( LPSTR MsgTypeString, LPSTR IpAddressString, LPSTR ServerAddressString ) { HANDLE EventlogHandle; LPSTR Strings[3]; // // open event registry. // EventlogHandle = RegisterEventSourceA( NULL, "DhcpTools" ); if (EventlogHandle == NULL) { printf("RegisterEventSourceA failed %ld.", GetLastError() ); return; } Strings[0] = MsgTypeString; Strings[1] = ServerAddressString; Strings[2] = IpAddressString; if( !ReportEventA( EventlogHandle, (WORD)EVENTLOG_INFORMATION_TYPE, 0, // event category DHCP_ROGUE_SERVER_MESSAGE, NULL, (WORD)3, 0, Strings, NULL ) ) { printf("ReportEventA failed %ld.", GetLastError() ); } DeregisterEventSource(EventlogHandle); return; } VOID RaiseAlert( LPSTR MsgTypeString, LPSTR IpAddressString, LPSTR ServerAddressString ) { time_t TimeNow; DWORD Error; TimeNow = time( NULL ); if( TimeNow > (time_t)(GlobalLastAlertTime + GlobalAlertInterval) ) { WCHAR uIpAddressString[SMALL_BUFFER_SIZE]; WCHAR uServerAddressString[SMALL_BUFFER_SIZE]; WCHAR uMsgTypeString[SMALL_BUFFER_SIZE]; DWORD i; LPWSTR MessageParams[3]; WCHAR AlertMessage[ ALERT_MESSAGE_LENGTH ]; DWORD MsgLength; MessageParams[0] = DhcpOemToUnicode( MsgTypeString, uMsgTypeString ); MessageParams[1] = DhcpOemToUnicode( ServerAddressString, uServerAddressString ); MessageParams[2] = DhcpOemToUnicode( IpAddressString, uIpAddressString ); MsgLength = FormatMessage( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, DHCP_ROGUE_SERVER_MESSAGE, 0, // language id. AlertMessage, // return buffer place holder. ALERT_MESSAGE_LENGTH, // minimum buffer size (in characters) to allocate. (va_list *)MessageParams // insert strings. ); if( MsgLength == 0 ) { printf("FormatMessage failed %ld.", GetLastError() ); } else { // // send alert message. // for( i = 0; i < GlobalAlertNamesCount; i++) { Error = NetMessageBufferSend( NULL, GlobalAlertNames[i], NULL, (LPBYTE)AlertMessage, MsgLength * sizeof(WCHAR) ); if( Error != ERROR_SUCCESS ) { printf("NetMessageBufferSend failed %ld.", Error ); break; } } } GlobalLastAlertTime = TimeNow; } } VOID DisplayMessage( LPSTR MessageBuffer, DWORD BufferLength, struct sockaddr_in *source ) { DHCP_OPTIONS DhcpOptions; PDHCP_MESSAGE DhcpMessage; SYSTEMTIME SystemTime; DWORD MessageType; LPSTR MessageTypeString; BOOL AuthServer = FALSE; CHAR IpAddressString[SMALL_BUFFER_SIZE]; CHAR ServerAddressString[SMALL_BUFFER_SIZE]; // // check to see this is valid DHCP packet. // if( BufferLength < DHCP_MESSAGE_FIXED_PART_SIZE ) { return; } DhcpMessage = (LPDHCP_MESSAGE) MessageBuffer; if( (DhcpMessage->Operation != BOOT_REQUEST) && (DhcpMessage->Operation != BOOT_REPLY) ) { return; } // // extract options. // ExtractOptions1( &DhcpMessage->Option, &DhcpOptions, BufferLength - DHCP_MESSAGE_FIXED_PART_SIZE ); if( DhcpOptions.MessageType == NULL ) { return; } MessageType = *DhcpOptions.MessageType; if( (MessageType < DHCP_DISCOVER_MESSAGE ) || (MessageType > DHCP_RELEASE_MESSAGE ) ) { return; } // // packet is valid dhcp packet, print info. // // // if this packet is from one of the auth server and we are asked // not to print auth servers packet, so so. // if( DhcpOptions.ServerIdentifier != NULL ) { AuthServer = IsAuthServer(*DhcpOptions.ServerIdentifier); } if( GlobalNoAuthPrint && AuthServer ) { return; } GetLocalTime( &SystemTime ); printf("%02u:%02u:%02u ", SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond ); switch ( MessageType ) { case DHCP_DISCOVER_MESSAGE: MessageTypeString = "DISCOVER"; case DHCP_OFFER_MESSAGE: MessageTypeString = "OFFER"; break; case DHCP_REQUEST_MESSAGE: MessageTypeString = "REQUEST"; break; case DHCP_DECLINE_MESSAGE: MessageTypeString = "DECLINE"; break; case DHCP_ACK_MESSAGE: MessageTypeString = "ACK"; break; case DHCP_NACK_MESSAGE: MessageTypeString = "NACK"; break; case DHCP_RELEASE_MESSAGE: MessageTypeString = "RELEASE"; break; default: MessageTypeString = "UNKNOWN"; break; } printf("%8s ", MessageTypeString); strcpy( IpAddressString, inet_ntoa(*(struct in_addr *)&DhcpMessage->YourIpAddress) ); printf("(IP)%-15s ", IpAddressString ); if(DhcpOptions.ServerIdentifier != NULL ) { DWORD ServerId; ServerId = *DhcpOptions.ServerIdentifier; strcpy( ServerAddressString, inet_ntoa(*(struct in_addr *)&ServerId) ); printf("(S)%-15s ", ServerAddressString ); if( source->sin_addr.s_addr != ServerId ) { printf("(S1)%-15s ", inet_ntoa(*(struct in_addr *)&source->sin_addr.s_addr) ); } } // // beep if this it is a non-auth server. // if( AuthServer == FALSE ) { printf("***"); MessageBeep( MB_ICONASTERISK ); // // log an event. // LogEvent( MessageTypeString, IpAddressString, ServerAddressString ); RaiseAlert( MessageTypeString, IpAddressString, ServerAddressString ); } printf("\n"); } DWORD ReceiveDatagram( VOID ) { DWORD Error; SOCKET Sock; BOOL SocketOpened = FALSE; fd_set readSocketSet; struct timeval timeout; struct sockaddr socketName; int socketNameSize = sizeof( socketName ); CHAR MessageBuffer[DHCP_MESSAGE_SIZE]; Error = OpenSocket( &Sock, GlobalIpAddress, DHCP_CLIENT_PORT ); if( Error != ERROR_SUCCESS ) { printf("OpenReceiveSocket failed %ld.", Error ); goto Cleanup; } SocketOpened = TRUE; // // receive message. // while( GlobalTerminate != TRUE ) { FD_ZERO( &readSocketSet ); FD_SET( Sock, &readSocketSet ); timeout.tv_sec = RECEIVE_TIMEOUT; timeout.tv_usec = 0; Error = select( 0, &readSocketSet, NULL, NULL, &timeout); if ( Error == 0 ) { // // Timeout before read data is available. // // printf("Receive timeout.\n"); } else { // // receive available message. // Error = recvfrom( Sock, MessageBuffer, sizeof(MessageBuffer), 0, &socketName, &socketNameSize ); if ( Error == SOCKET_ERROR ) { Error = WSAGetLastError(); printf("recvfrom failed %ld\n", Error ); goto Cleanup; } if( GlobalTerminate == TRUE ) { break; } DisplayMessage( MessageBuffer, Error, // buffer length returned. (struct sockaddr_in *)&socketName ); } } Cleanup: if( SocketOpened == TRUE ) { // // close socket. // closesocket( Sock ); } GlobalTerminate = TRUE; return( Error ); } DWORD __cdecl main( int argc, char **argv ) { DWORD Error; LPSTR AppName = NULL; WSADATA wsaData; DWORD ThreadId; // // parse input parameters. // if( argc < 1 ) { goto Usage; } AppName = argv[0]; argv++; argc--; if( argc < 1 ) { goto Usage; } // // parse flag parameter. // while( (argv[0][0] == '-') || (argv[0][0] == '/') ) { switch (argv[0][1] ) { case 'p': GlobalNoAuthPrint = TRUE; break; case 'i': GlobalAlertInterval = atoi( &argv[0][3] ) * 60; break; case 'a': { LPSTR NextName; LPSTR Ptr; Ptr = &argv[0][3]; // // skip blanks. // while( *Ptr == ' ' ) { Ptr++; } NextName = Ptr; while( *Ptr != '\0' ) { if( *Ptr == ' ' ) { // // found another name. // *Ptr++ = '\0'; GlobalAlertNames[GlobalAlertNamesCount] = DhcpOemToUnicode( NextName, NULL ); GlobalAlertNamesCount++; if( GlobalAlertNamesCount >= MAX_ALERT_NAMES ) { break; } // // skip blanks. // while( *Ptr == ' ' ) { Ptr++; } NextName = Ptr; } else { Ptr++; } } if( GlobalAlertNamesCount < MAX_ALERT_NAMES ) { if( NextName != Ptr ) { GlobalAlertNames[GlobalAlertNamesCount] = DhcpOemToUnicode( NextName, NULL ); GlobalAlertNamesCount++; } } break; } // // (a-martih) - Bug Fix // default: if ((_stricmp(argv[0], "/?")) && (_stricmp(argv[0], "-?")) && (_stricmp(argv[0], "/h")) && (_stricmp(argv[0], "-h")) ) { printf( "\nunknown flag, %s \n", argv[0] ); } goto Usage; break; } argv++; argc--; } if( argc < 1 ) { goto Usage; } // // read ipaddress parameter. // GlobalIpAddress = inet_addr( argv[0] ); argv++; argc--; // // now read auth dhcp servers ipaddresses. // while( (argc > 0) && (GlobalAuthServersCount < AUTH_SERVERS_MAX) ) { GlobalAuthServers[GlobalAuthServersCount++] = inet_addr( argv[0] ); argv++; argc--; } // // init socket. // Error = WSAStartup( WS_VERSION_REQUIRED, &wsaData); if( Error != ERROR_SUCCESS ) { printf( "WSAStartup failed %ld.\n", Error ); return(1); } // // create receive datagrams thread. // GlobalRecvThreadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ReceiveDatagram, NULL, 0, &ThreadId ); if( GlobalRecvThreadHandle == NULL ) { printf("CreateThread failed %ld.\n", GetLastError() ); return(1); } // // read input. // while ( GlobalTerminate != TRUE ) { CHAR ch; ch = (CHAR)_getch(); switch( ch ) { case 'q': case 'Q': // case '\c': GlobalTerminate = TRUE; break; case 'd': case 'D': // // send out discover message. // Error = SendDiscovery(); if(Error != ERROR_SUCCESS ) { printf("SendDiscover failed %ld.\n", Error ); } break; case 'h': case 'H': default: printf("Type d - to discover; q - to quit; h - for help.\n"); // // print out help message. // break; } } // // terminate receive thread. // WaitForSingleObject( GlobalRecvThreadHandle, THREAD_TERMINATION_TIMEOUT ); CloseHandle( GlobalRecvThreadHandle ); // Cleanup: return(0); Usage: printf("\nUSAGE:\n\n"); printf("%s [-p] [-a:\"list-of-alertnames\"] [-i:alertinterval] " "machine-ip-address " "[list of valid dhcp servers ip addresses]", AppName ); return(1); }