windows-nt/Source/XPSP1/NT/ds/dns/dnsup/dnsup.c
2020-09-26 16:20:57 +08:00

5058 lines
104 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996-2001 Microsoft Corporation
Module Name:
dnsup.c
Abstract:
Domain Name System (DNS) Update Client
Main program.
Author:
Jim Gilroy (jamesg) October, 1996
Environment:
User Mode - Win32
Revision History:
--*/
#include "..\dnsapi\local.h"
#if 0
#include <windows.h>
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h> // printf()
#include <string.h> // strtoul()
#include <windns.h>
#include <dnsapi.h>
#include "dnslib.h"
#include "..\resolver\idl\resrpc.h"
#include "dnsapip.h"
#include "dnslibp.h"
#endif
#include "svcguid.h" // RnR guids
// Use dnslib memory routines
#if 0
#define ALLOCATE_HEAP(iSize) Dns_Alloc(iSize)
#define ALLOCATE_HEAP_ZERO(iSize) Dns_AllocZero(iSize)
#define REALLOCATE_HEAP(pMem,iSize) Dns_Realloc((pMem),(iSize))
#define FREE_HEAP(pMem) Dns_Free(pMem)
#endif
// Debug flag
DWORD LocalDebugFlag;
//
// Printing
//
#define dnsup_PrintRoutine ((PRINT_ROUTINE) fprintf)
#define dnsup_PrintContext ((PPRINT_CONTEXT) stdout)
//
// Special names and buffers
//
PCHAR SingleLongName = "longname";
CHAR SeLongName[] = "longname";
PCHAR LongName = ( "longname"
".label22222222222222222222222222222222222222222222222222"
".label33333333333333333333333333333333333333333333333333"
".label44444444444444444444444444444444444444444444444444"
".label55555555555555555555555555555555555555555555555555"
".label66666666666666666666666666666666666666666666666666.");
#define LONG_LABEL_NAME ("longlabel" \
".longlabel2222222222222222222222222222222222222222222222222222222222")
CHAR NameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR AddressBuffer[ sizeof(DNS_IP6_ADDRESS) ];
//
// Quit or Exit status
//
#define ERROR_DNSUP_QUIT ((DNS_STATUS)(0x87654321))
//
// Command table setup
//
typedef DNS_STATUS (* COMMAND_FUNCTION)( DWORD Argc, CHAR** Argv);
typedef struct _COMMAND_INFO
{
PSTR pszCommandName;
PSTR pszDescription;
COMMAND_FUNCTION pCommandFunction;
}
COMMAND_INFO, *LPCOMMAND_INFO;
//
// Note, command table is at bottom of file to
// avoid need for prototyping all the functions
//
extern COMMAND_INFO GlobalCommandInfo[];
COMMAND_FUNCTION
GetCommandFunction(
IN PSTR pszCommandName
)
{
DWORD i;
//
// find command in list matching string
//
i = 0;
while( GlobalCommandInfo[i].pszCommandName )
{
if( _stricmp(
pszCommandName,
GlobalCommandInfo[i].pszCommandName ) == 0 )
{
return( GlobalCommandInfo[i].pCommandFunction );
}
i++;
}
return( NULL );
}
//
// Print utils
//
VOID
PrintCommands(
VOID
)
{
DWORD i;
//
// print all commands in list
//
i = 0;
while( GlobalCommandInfo[i].pszCommandName )
{
printf(
" %10s (%s)\n",
GlobalCommandInfo[i].pszCommandName,
GlobalCommandInfo[i].pszDescription );
i++;
}
}
VOID
PrintDnsQueryFlags(
VOID
)
{
DWORD i;
//
// print query flags
//
printf(
"DNS Query flags:\n"
"\tDNS_QUERY_STANDARD = 0x%x\n"
"\tDNS_QUERY_ACCEPT_PARTIAL_UDP = 0x%x\n"
"\tDNS_QUERY_USE_TCP_ONLY = 0x%x\n"
"\tDNS_QUERY_NO_RECURSION = 0x%x\n"
"\tDNS_QUERY_BYPASS_CACHE = 0x%x\n"
"\tDNS_QUERY_NO_WIRE_QUERY = 0x%x\n"
"\tDNS_QUERY_NO_HOSTS_FILE = 0x%x\n"
"\tDNS_QUERY_NO_LOCAL_NAME = 0x%x\n",
DNS_QUERY_STANDARD ,
DNS_QUERY_ACCEPT_PARTIAL_UDP ,
DNS_QUERY_USE_TCP_ONLY ,
DNS_QUERY_NO_RECURSION ,
DNS_QUERY_BYPASS_CACHE ,
DNS_QUERY_NO_WIRE_QUERY ,
DNS_QUERY_NO_HOSTS_FILE ,
DNS_QUERY_NO_LOCAL_NAME
);
}
//
// Optional DNS server list to use
//
// Overides default list on this client for some commands
//
PIP_ARRAY pDnsServerArray = NULL;
DNS_STATUS
readNameServers(
IN PIP_ARRAY * ppIpServers,
IN DWORD * pArgc,
IN PSTR ** pArgv
)
{
DWORD argc = *pArgc;
PCHAR * argv = *pArgv;
PCHAR * startArgv;
PCHAR arg;
CHAR ch;
IP_ADDRESS ipserver;
PIP_ARRAY aipservers = NULL;
DWORD countServers = 0;
DWORD i;
//
// -n / -N denotes DNS server
// server IP immediate follows (in same arg)
//
startArgv = argv;
while ( argc )
{
arg = argv[0];
if ( '-' == *arg++ )
{
ch = *arg++;
if ( ch == 'n' || ch == 'N' )
{
countServers++;
argc--;
argv++;
continue;
}
}
break;
}
//
// found servers
// - allocate IP array
// - parse servers into it
// - reset callers Argc, Argv
//
if ( countServers )
{
*pArgc = argc;
*pArgv = argv;
argv = startArgv;
aipservers = DnsCreateIpArray( countServers );
if ( ! aipservers )
{
return( ERROR_OUTOFMEMORY );
}
for (i=0; i<(INT)countServers; i++)
{
arg = (*argv++) + 2;
if ( *arg == '.' )
{
arg = "127.0.0.1";
}
ipserver = inet_addr( arg );
if ( ipserver == INADDR_NONE )
{
printf( "ERROR: name server IP (in arg %s) is bogus\n", arg );
return( ERROR_INVALID_PARAMETER );
}
IF_DNSDBG( INIT )
{
DNS_PRINT((
"Read name server address from arg %s.\n",
arg ));
}
aipservers->AddrArray[i] = ipserver;
}
DnsDbg_IpArray(
"Name servers to register with\n",
NULL,
aipservers );
}
*ppIpServers = aipservers;
return( ERROR_SUCCESS );
}
DNS_STATUS
ProcessCommandLine(
IN INT Argc,
IN CHAR ** Argv
)
/*++
Routine Description:
Process command in Argc\Argv form.
Arguments:
Argc -- arg count
Argv -- argument list
Argv[0] -- dnsup
Argv[1] -- Command to execute
Argv[2...] -- arguments to command
Return Value:
Return from the desired command.
Usually a pass through of the return code from DNS Update API call.
--*/
{
DNS_STATUS status;
COMMAND_FUNCTION pcommandFunction;
PCHAR pcommand;
if ( Argc < 1 )
{
goto Usage;
}
DNS_PRINT(( "Argc = %d\n", Argc ));
//
// check for server list
//
status = readNameServers(
& pDnsServerArray,
& Argc,
& Argv );
if ( status != ERROR_SUCCESS )
{
goto Usage;
}
//
// next param is command
// - optionally decorated with leading "-"
//
if ( Argc < 1 )
{
goto Usage;
}
pcommand = Argv[0];
if ( *pcommand == '-' )
{
pcommand++;
}
pcommandFunction = GetCommandFunction( pcommand );
if ( ! pcommandFunction )
{
status = ERROR_INVALID_PARAMETER;
printf( "Unknown Command Specified -- type dnsup -?.\n" );
goto Usage;
}
//
// dispatch to processor for this command
// - skip over command argv
Argc--;
Argv++;
status = pcommandFunction( Argc, Argv );
if ( status == ERROR_SUCCESS ||
status == ERROR_DNSUP_QUIT )
{
printf( "Command successfully completed.\n" );
}
else
{
printf( "Command failed, %d (%ul)\n", status, status );
}
return( status );
Usage:
printf(
"DnsUp command line:\n"
"\t[-n<Server IP List>] -<Command> [Command Parameters].\n"
"\t<Server IP List> is a list of one or more DNS server IP addresses\n"
"\t\toverriding the default list on this client\n"
"Commands:\n"
);
PrintCommands();
return( ERROR_INVALID_PARAMETER );
}
VOID
InteractiveLoop(
VOID
)
/*++
Routine Description:
Interactive loop.
Arguments:
None
Return Value:
None
--*/
{
#define MAX_ARG_COUNT 50
DNS_STATUS status;
INT argc;
CHAR * argv[ MAX_ARG_COUNT ];
CHAR lineBuffer[500];
//
// loop taking command params
//
while ( 1 )
{
printf( "\n> " );
// read next command line
gets( lineBuffer );
argc = Dns_TokenizeString(
lineBuffer,
argv,
MAX_ARG_COUNT );
DNS_PRINT(( "argc = %d\n", argc ));
IF_DNSDBG( INIT )
{
DnsDbg_Argv(
NULL,
argv,
argc,
FALSE // not unicode
);
}
// process next command line
status = ProcessCommandLine(
argc,
argv );
if ( status == ERROR_DNSUP_QUIT )
{
break;
}
}
}
LONG
__cdecl
main(
IN INT Argc,
IN CHAR ** Argv
)
/*++
Routine Description:
DnsUp program entry point.
Executes specified command corresponding to a DNS update API operation.
Arguments:
Argc -- arg count
Argv -- argument list
Argv[0] -- dnsup
Argv[1] -- Command to execute
Argv[2...] -- arguments to command
Return Value:
Zero if successful.
1 on error.
--*/
{
DNS_STATUS status;
//
// initialize debug
//
Dns_StartDebugEx(
0, // no flag value
"dnsup.flag", // read flag from file
NULL, //&LocalDebugFlag,
"dnsup.log", // log to file
0, // no wrap limit
FALSE, // don't use existing global
FALSE,
TRUE // make this file global
);
DNS_PRINT(( "*pDnsDebugFlag = %08x\n", *pDnsDebugFlag ));
//DNS_PRINT(( "LocalDebugFlag = %08x\n", LocalDebugFlag ));
if ( Argc < 1 )
{
goto Usage;
}
DNS_PRINT(( "Argc = %d\n", Argc ));
// skip "dnsup" argument
Argc--;
Argv++;
//
// interactive mode?
// - if no command, just open interactively
//
if ( Argc == 0 )
{
InteractiveLoop();
status = ERROR_SUCCESS;
}
else
{
status = ProcessCommandLine(
Argc,
Argv );
}
Dns_EndDebug();
return( status != ERROR_SUCCESS );
Usage:
Dns_EndDebug();
printf(
"usage: DnsUp [-n<Server IP List>] -<Command> [Command Parameters].\n"
"\t<Server IP List> is a list of one or more DNS server IP addresses\n"
"\t\toverriding the default list on this client\n"
"Commands:\n"
);
PrintCommands();
return(1);
}
//
// Command processing
//
PDNS_RECORD
buildRecordList(
IN DWORD Argc,
IN PSTR * Argv
)
/*++
Routine Description:
Build record list from Argc \ Argv list.
Arguments:
Argc -- arg count
Argv -- argument list
Return Value:
Ptr to list of records built.
NULL on parsing error.
--*/
{
PCHAR arg;
PCHAR pszname;
WORD type;
BOOLEAN fadd;
BOOLEAN section;
CHAR ch;
PDNS_RECORD prr;
DNS_RRSET rrset;
INT recordArgc;
PCHAR * recordArgv;
//
// loop through remaining arguments building appropriate record types
//
DNS_RRSET_INIT( rrset );
recordArgc = (-3);
pszname = NULL;
while ( Argc-- && (arg = *Argv++) )
{
//
// if previous arg started new record, first parameter is name
//
if ( recordArgc == (-2) )
{
pszname = arg;
type = 0;
recordArgc++;
continue;
}
//
// second parameter is type
//
else if ( recordArgc == (-1) )
{
type = DnsRecordTypeForName( arg, 0 );
if ( type == 0 )
{
printf( "ERROR: unknown type %s.\n", arg );
goto Failed;
}
recordArgc++;
continue;
}
//
// check for end of existing record \ start of new record
//
ch = *arg;
if ( ch == '+' || ch == '-' )
{
// build old record (if any)
if ( pszname != NULL )
{
prr = Dns_RecordBuild_A(
& rrset,
pszname,
type,
fadd,
section,
recordArgc,
recordArgv );
if ( !prr )
{
printf( "ERROR: building record.\n" );
goto Failed;
}
if ( fadd && section == DNSREC_UPDATE )
{
prr->dwTtl = 3600;
}
}
fadd = ( ch == '+' );
recordArgc = (-2);
//
// second character in new arg, indicates section
// - for update ADD records use 3600 TTL instead of zero
//
ch = *++arg;
if ( ch == 0 )
{
section = DNSREC_QUESTION;
}
else if ( ch == 'p' )
{
section = DNSREC_PREREQ;
}
else if ( ch == 'u' )
{
section = DNSREC_UPDATE;
}
else if ( ch == 'a' )
{
section = DNSREC_ADDITIONAL;
}
else
{
printf( "ERROR: unknown section id %d.\n", ch );
goto Failed;
}
continue;
}
// catch bad starting record (no +/-)
else if ( recordArgc < 0 )
{
printf( "ERROR: bad start of record arg %s\n", arg );
goto Failed;
}
//
// anything else is data, save in argv format
// - save starting point
// - count records
else if ( recordArgc == 0 )
{
recordArgv = --Argv;
Argv++;
}
recordArgc++;
continue;
}
//
// build any final record
//
if ( pszname != NULL )
{
prr = Dns_RecordBuild_A(
& rrset,
pszname,
type,
fadd,
section,
recordArgc,
recordArgv );
if ( !prr )
{
printf( "ERROR: building record.\n" );
goto Failed;
}
if ( fadd && section == DNSREC_UPDATE )
{
prr->dwTtl = 3600;
}
}
IF_DNSDBG( INIT )
{
DnsDbg_RecordSet(
"Record set:\n",
rrset.pFirstRR );
}
return( rrset.pFirstRR );
Failed:
printf( "ERROR: building records from arguments.\n" );
SetLastError( ERROR_INVALID_PARAMETER );
return( NULL );
}
PCHAR
getNamePointer(
IN PSTR pszName
)
/*++
Routine Description:
Get name pointer.
This may be legit pointer or tag
Tags:
- null
- blank
- badptr
- badname
- longname
- longlabel
- buffer
Arguments:
Argc -- arg count
Argv -- argument list
Return Value:
Ptr to list of records built.
NULL on parsing error.
--*/
{
PCHAR pname = pszName;
//
// check special case names
//
if ( _stricmp( pname, "null" ) == 0 )
{
pname = NULL;
}
else if ( _stricmp( pname, "blank" ) == 0 )
{
pname = "";
}
else if ( _stricmp( pname, "badname" ) == 0 )
{
pname = "..badname..";
}
else if ( _stricmp( pname, "longname" ) == 0 )
{
//pname = LONG_NAME;
pname = LongName;
}
else if ( _stricmp( pname, "longlabel" ) == 0 )
{
pname = LONG_LABEL_NAME;
}
else if ( _stricmp( pname, "badptr" ) == 0 )
{
pname = (PCHAR) (-1);
}
else if ( _stricmp( pname, "buffer" ) == 0 )
{
pname = (PCHAR) NameBuffer;
}
return( pname );
}
BOOL
getAddressPointer(
IN PSTR pAddrString,
OUT PCHAR * ppAddr,
OUT PDWORD pLength,
OUT PDWORD pFamily
)
/*++
Routine Description:
Get address from address string argument.
Wraps up special arguments (NULL, badptr), with string
to address conversion.
Arguments:
pAddrString -- address as string
pAddr -- ptr to recv address
pLength -- ptr to recv length of returned address
pFamily -- ptr to recv family (socket family) of returned address
Return Value:
TRUE if valid address converted.
FALSE if special ptr or pass through.
--*/
{
PCHAR paddr;
INT length = sizeof(IP4_ADDRESS);
INT family = AF_INET;
BOOL result = FALSE;
//
// first check for special cases
// - default to AF_INET
//
if ( _stricmp( pAddrString, "null" ) == 0 )
{
paddr = NULL;
}
else if ( _stricmp( pAddrString, "blank" ) == 0 )
{
paddr = "";
length = 0;
}
else if ( _stricmp( pAddrString, "badptr" ) == 0 )
{
paddr = (PCHAR) (-1);
}
//
// not-special -- treat as address string
// - use type and length on successful conversion
// - just return pAddrString and it's length if not successful
//
else
{
family = 0; // any family
length = sizeof(AddressBuffer);
result = Dns_StringToAddress_A(
AddressBuffer,
& length,
pAddrString,
& family );
if ( result )
{
paddr = AddressBuffer;
}
else
{
paddr = pAddrString;
length = strlen( pAddrString );
family = AF_INET;
}
}
*ppAddr = paddr;
*pLength = length;
*pFamily = family;
return( result );
}
BOOL
getSockaddrFromString(
IN OUT PSOCKADDR * ppSockaddr,
IN OUT PDWORD pSockaddrLength,
IN PSTR pAddrString
)
/*++
Routine Description:
Get sockaddr from address string argument.
Wraps up special arguments (NULL, badptr), with string
to address conversion.
Arguments:
ppSockaddr -- addr of ptr to sockaddr buffer
on return may be set to point at dummy sockaddr
pSockaddrLength -- addr with sockaddr buffer length
receives sockaddr length
pAddrString -- address as string
Return Value:
TRUE if parsed sockaddr string
Note this does not mean it is a valid sockaddr.
FALSE if special ptr or pass through.
--*/
{
PCHAR paddr = NULL;
DWORD addrLength;
DWORD family;
DWORD flags = 0;
DNS_STATUS status;
//
// convert address string
//
// if valid string => write into given sockaddr
// if invalid => set sockaddr ptr to dummy sockaddr
//
if ( getAddressPointer(
pAddrString,
& paddr,
& addrLength,
& family ) )
{
status = Dns_AddressToSockaddr(
*ppSockaddr,
pSockaddrLength,
TRUE, // clear sockaddr
paddr,
addrLength,
family );
if ( status != NO_ERROR )
{
DNS_ASSERT( FALSE );
return FALSE;
}
}
else // special bogus address, use it directly
{
*ppSockaddr = (PSOCKADDR) paddr;
}
return TRUE;
}
//
// Command functions
//
DNS_STATUS
ProcessQuit(
IN DWORD Argc,
IN PSTR * Argv
)
{
//
// alert command processor to break iteractive loop
//
return( ERROR_DNSUP_QUIT );
}
//
// Update routines
//
// DCR: could add context handle interface to update routines
//
DNS_STATUS
ProcessUpdate(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR arg;
PDNS_RECORD prr = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
HANDLE hCreds=NULL;
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// build update packet RRs
//
prr = buildRecordList(
Argc,
Argv );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build \ send update
//
status = DnsUpdate(
prr,
flags,
NULL, // no UPDATE adapter list specified
hCreds,
&pmsgRecv
);
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Update response:\n",
pmsgRecv );
}
Dns_RecordListFree( prr );
FREE_HEAP( pmsgRecv );
if ( status != ERROR_SUCCESS )
{
printf(
"DnsUpdate failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "Update successfully completed.\n" );
}
return( status );
Usage:
Dns_RecordListFree( prr );
printf(
"DnsUpdate\n"
"usage:\n"
" DnsUp -u [-f<Flags>] [<Record> | ...]\n"
" <Record> record for update packet\n"
" <Record> == (-,+)(p,u,a) <Name> <Type> [Data | ...]\n"
" (-,+) - add or delete, exist or no-exist flag\n"
" (p,u,a) - prereq, update or additional section\n"
" <Name> - RR owner name\n"
" <Type> - record type (ex A, SRV, PTR, TXT, CNAME, etc.)\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessUpdateTest(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR pszzone = NULL;
PCHAR arg;
PDNS_RECORD prr = NULL;
PDNS_MSG_BUF pmsg = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
IP_ADDRESS ipserver;
PIP_ARRAY aipservers = NULL;
PSTR pszserverName = NULL;
HANDLE hCreds = NULL;
//
// optional name server to query?
//
aipservers = pDnsServerArray;
if ( !aipservers )
{
aipservers = DnsQueryConfigAlloc(
DnsConfigDnsServerList,
NULL );
}
if ( !aipservers )
{
printf( "Update failed: no DNS server list available.\n" );
return( GetLastError() );
}
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// security -- then need target server name
// presence of server name automatically turns on security
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-s", 2 ) == 0 )
{
pszserverName = arg+2;
flags |= DNS_UPDATE_SECURITY_ONLY;
Argc--;
Argv++;
}
//
// name of zone to update
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( *arg != '-' && *arg != '+' )
{
pszzone = arg;
DNSDBG( INIT, (
"Read update zone name %s\n",
pszzone ));
Argc--;
Argv++;
}
//
// build update packet RRs
//
prr = buildRecordList(
Argc,
Argv );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build \ send update
//
status = Dns_UpdateLibEx(
prr,
flags,
pszzone,
pszserverName,
aipservers,
NULL,
&pmsgRecv
);
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Update response:\n",
pmsgRecv );
}
DnsApiFree( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"DnsUpdateEx failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "Update successfully completed.\n" );
}
return( status );
Usage:
FREE_HEAP( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
printf(
"UpdateTest -- Dns_UpdateLibEx\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -ut [-f<Flags>] [-s<ServerName>] [<ZoneName>] [<Record> | ...]\n"
" <DNS IP> optional IP address of server to send update to.\n"
" <ServerName> name of DNS server we are sending update to;\n"
" presence of server name automatically turns on update security\n"
" <ZoneName> name of zone to update\n"
" <Record> record for update packet\n"
" <Record> == (-,+)(p,u,a) <Name> <Type> [Data | ...]\n"
" (-,+) - add or delete, exist or no-exist flag\n"
" (p,u,a) - prereq, update or additional section\n"
" <Name> - RR owner name\n"
" <Type> - record type (ex A, SRV, PTR, TXT, CNAME, etc.)\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessDhcpTest(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
REGISTER_HOST_ENTRY hostEntry;
REGISTER_HOST_STATUS hostStatus;
HANDLE doneEvent;
//
// create completion event
//
doneEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
//
// build dumpy DHCP update
//
hostEntry.Addr.ipAddr = 0x01010101;
hostEntry.dwOptions = REGISTER_HOST_A;
hostStatus.hDoneEvent = doneEvent;
//
// send to DHCP DynDNS update routine
//
status = DnsAsyncRegisterHostAddrs(
L"El59x1",
L"testdhcpname",
&hostEntry,
1, // address count
NULL, // no regkey with previous
0,
L"nttest.microsoft.com",
& hostStatus,
60, // TTL
0
);
if( status != ERROR_SUCCESS )
{
printf(
"DnsAsynRegisterHostAddrs_W() %s.\n",
status,
DnsStatusString(status) );
return( status );
}
printf( "DHCP DynDNS registration successfully completed.\n" );
WaitForSingleObject(
doneEvent,
INFINITE );
return( status );
}
//
// Query routines
//
DNS_STATUS
ProcessQueryEx(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname;
WORD type;
DWORD flags = 0;
PDNS_MSG_BUF pmsgRecv = NULL;
HANDLE hevent = NULL;
DNS_QUERY_INFO queryInfo;
//
// name to query
//
if ( Argc < 2 || Argc > 4 )
{
goto Usage;
}
pszname = Argv[0];
Argc--;
Argv++;
//
// type
//
type = DnsRecordTypeForName( Argv[0], 0 );
if ( type == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// flags and options
//
while ( Argc )
{
if ( _stricmp( Argv[0], "-a" ) == 0 )
{
hevent = CreateEvent( NULL, FALSE, FALSE, NULL );
}
else
{
flags = strtoul( Argv[0], NULL, 16 );
}
Argc--;
Argv++;
}
//
// query
//
RtlZeroMemory(
& queryInfo,
sizeof(queryInfo) );
queryInfo.pName = pszname;
queryInfo.Type = type;
queryInfo.Flags = flags;
queryInfo.pDnsServers = pDnsServerArray;
queryInfo.hEvent = hevent;
status = DnsQueryEx( &queryInfo );
if ( status == ERROR_IO_PENDING )
{
printf(
"DnsQueryEx() running asynchronously.\n"
"\tEvent = %p\n",
hevent );
WaitForSingleObject( hevent, INFINITE );
printf( "DnsQueryEx() async completion.\n" );
status = queryInfo.Status;
}
if ( status == ERROR_SUCCESS ||
status == DNS_ERROR_RCODE_NAME_ERROR ||
status == DNS_INFO_NO_RECORDS )
{
printf(
"DnsQueryEx() completed => %s.\n"
"\tstatus = %d\n",
DnsStatusString( status ),
status );
DnsPrint_QueryInfo(
dnsup_PrintRoutine,
dnsup_PrintContext,
"DnsQueryEx() result",
&queryInfo );
}
else
{
printf(
"DnsQueryEx() failed, %x %s.\n",
status,
DnsStatusString(status) );
DNS_ASSERT( !queryInfo.pAnswerRecords );
DNS_ASSERT( !queryInfo.pAliasRecords );
DNS_ASSERT( !queryInfo.pAdditionalRecords );
DNS_ASSERT( !queryInfo.pAuthorityRecords );
}
return( status );
Usage:
printf(
"DnsQueryEx\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -qex <Name> <Type> <Flags> [-a]\n"
" <DNS IP> optional IP address of DNS server to query.\n"
" <Name> DNS name to query\n"
" <Type> type of query\n"
" <Flags> flags to use in query (in hex)\n"
" -a to indicate asynchronous operation\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessQueryCompare(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname;
WORD type;
PIP_ARRAY aipservers;
PDNS_MSG_BUF pmsgRecv = NULL;
PDNS_RECORD prrQuery;
PDNS_RECORD prrParsed;
PDNS_RECORD prrQueryDiff;
PDNS_RECORD prrParsedDiff;
//
// optional name server to query?
//
aipservers = pDnsServerArray;
//
// name to query
//
if ( Argc < 2 )
{
goto Usage;
}
pszname = Argv[0];
Argc--;
Argv++;
//
// type
//
type = DnsRecordTypeForName( Argv[0], 0 );
if ( type == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// build expected RR set
//
prrParsed = buildRecordList(
Argc,
Argv );
if ( !prrParsed )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Parsed comparison record list:\n",
prrParsed );
//
// query
//
// DCR: waiting for new DnsQueryEx()
//
return( ERROR_CALL_NOT_IMPLEMENTED );
#if 0
status = DnsQueryEx(
& pmsgRecv,
& prrQuery,
pszname,
type,
0, // currently no flag support
aipservers,
NULL
);
if( status != ERROR_SUCCESS )
{
printf(
"DnsQueryEx() failed, %x %s.\n",
status,
DnsStatusString(status) );
if ( DnsIsStatusRcode(status) )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
return( status );
}
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response records:\n",
prrQuery );
//
// compare with records received
//
DnsRecordSetCompare(
prrQuery,
prrParsed,
& prrQueryDiff,
& prrParsedDiff );
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Unmatch records in query response:\n",
prrQueryDiff );
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Unmatch records in parsed list:\n",
prrParsedDiff );
return( ERROR_SUCCESS );
#endif
Usage:
printf(
"Query compare\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -qc <Name> <Type> [<Record> | ...]\n"
" <DNS IP> optional IP address of DNS server to update.\n"
" <Name> DNS name to query\n"
" <Type> type of query\n"
" <Record> record in list to compare\n"
" <Record> == (-,+) <Name> <Type> [Data | ...]\n"
" (-,+) - add or delete, exist or no-exist flag\n"
" <Name> - RR owner name\n"
" <Type> - record type\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessQuery(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname;
WORD type;
DWORD flags = 0;
PDNS_RECORD precord = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
PDNS_MSG_BUF * ppmsgRecv = NULL;
PIP_ARRAY aipservers = NULL;
//
// name to query
//
if ( Argc < 2 || Argc > 3 )
{
goto Usage;
}
pszname = getNamePointer( Argv[0] );
Argc--;
Argv++;
//
// type
//
type = DnsRecordTypeForName( Argv[0], 0 );
if ( type == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// flags
//
if ( Argc )
{
flags = strtoul( Argv[0], NULL, 16 );
Argc--;
Argv++;
}
if ( Argc )
{
goto Usage;
}
//
// only if flag has BYPASS_CACHE set can we
// 1) send to specific servers
// 2) receive message buffer itself
//
aipservers = pDnsServerArray;
if ( flags & DNS_QUERY_BYPASS_CACHE )
{
ppmsgRecv = & pmsgRecv;
}
//
// query
//
status = DnsQuery(
pszname,
type,
flags,
aipservers,
& precord,
ppmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"DnsQuery() failed, %x %s.\n",
status,
DnsStatusString(status) );
if ( DnsIsStatusRcode(status) && pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
}
else
{
printf( "DnsQuery() successfully completed.\n" );
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response records:\n",
precord );
}
DnsRecordListFree( precord, DnsFreeRecordListDeep );
return( status );
Usage:
printf(
"DnsQuery\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -q <Name> <Type> [<Flags>]\n"
" <DNS IP> optional IP address of DNS server to query.\n"
" <Name> DNS name to query\n"
" <Type> type of query\n"
" <Flags> flags to use in query (in hex)\n"
"\n"
" Note for DnsQuery() to use specific server or to return\n"
" a DNS message buffer, BYPASS_CACHE flag MUST be set.\n"
);
PrintDnsQueryFlags();
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessQueryMultiple(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname1;
WORD type1;
PCHAR pszname2;
WORD type2;
DWORD flags = 0;
PDNS_RECORD precord = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
PDNS_MSG_BUF * ppmsgRecv = NULL;
PIP_ARRAY aipservers = NULL;
//
// optional name server to query?
//
if ( Argc < 4 || Argc > 5 )
{
goto Usage;
}
//
// first name to query
//
pszname1 = Argv[0];
Argc--;
Argv++;
//
// first type
//
type1 = DnsRecordTypeForName( Argv[0], 0 );
if ( type1 == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// second name to query
//
pszname2 = Argv[0];
Argc--;
Argv++;
//
// second type
//
type2 = DnsRecordTypeForName( Argv[0], 0 );
if ( type2 == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// flags
//
if ( Argc )
{
flags = strtoul( Argv[0], NULL, 16 );
Argc--;
Argv++;
}
if ( Argc )
{
goto Usage;
}
//
// only if flag has BYPASS_CACHE set can we
// 1) send to specific servers
// 2) receive message buffer itself
//
if ( flags & DNS_QUERY_BYPASS_CACHE )
{
aipservers = pDnsServerArray;
ppmsgRecv = & pmsgRecv;
}
//
// make first query
//
status = DnsQuery(
pszname1,
type1,
flags,
aipservers,
& precord,
ppmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"DnsQuery() failed, %x %s.\n",
status,
DnsStatusString(status) );
if ( DnsIsStatusRcode(status) && pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
}
else
{
printf( "DnsQuery() successfully completed.\n" );
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response records:\n",
precord );
}
DnsRecordListFree( precord, DnsFreeRecordListDeep );
//
// make second query
//
status = DnsQuery(
pszname2,
type2,
flags,
aipservers,
& precord,
ppmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"DnsQuery() failed, %x %s.\n",
status,
DnsStatusString(status) );
if ( DnsIsStatusRcode(status) && pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
}
else
{
printf( "DnsQuery() successfully completed.\n" );
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response records:\n",
precord );
}
DnsRecordListFree( precord, DnsFreeRecordListDeep );
return( status );
Usage:
printf(
"Multiple DnsQuery()s\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -qm <Name1> <Type1> <Name2> <Type2> [<Flags>]\n"
" <DNS IP> optional IP address of DNS server to query.\n"
" <Name1> DNS name to query\n"
" <Type1> type of query\n"
" <Name2> DNS name to query\n"
" <Type2> type of query\n"
" <Flags> flags to use in query (in hex)\n"
"\n"
" Note for DnsQuery() to use specific server or to return\n"
" a DNS message buffer, BYPASS_CACHE flag MUST be set.\n"
);
PrintDnsQueryFlags();
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessQueryTest(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname;
WORD type;
DWORD flags = 0;
PDNS_RECORD precord = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
PIP_ARRAY aipservers = NULL;
//
// optional name server to query?
//
aipservers = pDnsServerArray;
//
// name to query
//
if ( Argc < 2 || Argc > 3 )
{
goto Usage;
}
pszname = Argv[0];
Argc--;
Argv++;
//
// type
//
type = DnsRecordTypeForName( Argv[0], 0 );
if ( type == 0 )
{
goto Usage;
}
Argc--;
Argv++;
//
// flags
//
if ( Argc )
{
flags = strtoul( Argv[0], NULL, 16 );
Argc--;
Argv++;
}
if ( Argc )
{
goto Usage;
}
//
// query
//
status = QueryDirectEx(
& pmsgRecv,
& precord,
NULL, // no header
0, // no header counts
pszname,
type,
NULL, // no records
flags,
aipservers,
NULL
);
if( status != ERROR_SUCCESS )
{
printf(
"QueryDirectEx() failed, %x %s.\n",
status,
DnsStatusString(status) );
if ( DnsIsStatusRcode(status) )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
}
}
else
{
printf( "QueryDirectEx() successfully completed.\n" );
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response:\n",
pmsgRecv );
DnsPrint_RecordSet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Query response records:\n",
precord );
}
DnsRecordListFree( precord, DnsFreeRecordListDeep );
return( status );
Usage:
printf(
"QueryDirectEx()\n"
"usage:\n"
" DnsUp [-n<DNS IP>|...] -qt <Name> <Type> [<Flags>]\n"
" QueryTest -- uses dnslib.lib QueryDirectEx()\n"
" <DNS IP> optional IP address of DNS server to query.\n"
" <Name> DNS name to query\n"
" <Type> type of query\n"
" <Flags> flags to use in query (in hex)\n"
);
return( ERROR_INVALID_PARAMETER );
}
#if 0
DNS_STATUS
ProcessUpdateMultiple(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR pszzone = NULL;
PCHAR pszname = NULL;
IP_ADDRESS ip;
IP_ADDRESS hostIp;
DWORD countIp;
DWORD i;
PCHAR arg;
PDNS_RECORD prr = NULL;
PDNS_MSG_BUF pmsg = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
IP_ADDRESS ipserver;
PIP_ARRAY aipservers = NULL;
PIP_ARRAY ipArray = NULL;
PSTR pszserverName = NULL;
HANDLE hCreds=NULL;
//
// optional name server to query?
//
aipservers = pDnsServerArray;
if ( !aipservers )
{
aipservers = Dns_GetDnsServerList( FALSE );
}
if ( !aipservers )
{
printf( "Update failed: no DNS server list available.\n" );
return( GetLastError() );
}
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// security -- then need target server name
// presence of server name automatically turns on security
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-s", 2 ) == 0 )
{
pszserverName = arg+2;
flags |= DNS_UPDATE_SECURITY_ON;
Argc--;
Argv++;
}
//
// name of zone to update
//
if ( Argc < 1 )
{
goto Usage;
}
pszzone = Argv[0];
DNSDBG( INIT, (
"Read update zone name %s\n",
pszzone ));
Argc--;
Argv++;
//
// name to update
//
if ( Argc < 1 )
{
goto Usage;
}
pszname = Argv[0];
DNSDBG( INIT, (
"Read update host name %s\n",
pszname ));
Argc--;
Argv++;
//
// start IP
//
if ( Argc < 1 )
{
goto Usage;
}
ip = inet_addr( Argv[0] );
if ( ip == (-1) )
{
goto Usage;
}
Argc--;
Argv++;
//
// IP count
//
if ( Argc < 1 )
{
goto Usage;
}
countIp = strtoul( Argv[0], NULL, 10 );
Argc--;
Argv++;
//
// build update record list
// - allocate and generate IP array
// - then create DNS records
//
ipArray = Dns_CreateIpArray( countIp );
if ( !ipArray )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: creating IP array\n" );
goto Usage;
}
goto Usage;
}
hostIp = ntohl( ip );
for ( i=0; i<countIp; i++ )
{
ipArray->AddrArray[i] = htonl( hostIp );
hostIp++;
}
prr = Dns_HostUpdateRRSet(
pszname,
ipArray,
3600 );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build \ send update
//
status = Dns_UpdateLibEx(
prr,
flags,
pszzone,
pszserverName,
aipservers,
hCreds,
&pmsgRecv
);
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Update response:\n",
pmsgRecv );
}
FREE_HEAP( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"Dns_UpdateLibEx failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "Update successfully completed.\n" );
}
return( status );
Usage:
FREE_HEAP( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
printf(
"usage:\n"
" DnsUp [-n<DNS IP>|...] -um [-f<Flags>] [-s<ServerName>] <ZoneName>\n"
" <HostName> <Starting IP> <IP Count>\n"
" <DNS IP> optional IP address of server to send update to.\n"
" <ServerName> name of DNS server we are sending update to;\n"
" presence of server name automatically turns on update security\n"
" <ZoneName> name of zone to update\n"
" <HostName> name of host to update\n"
" <Starting IP> first IP (in dotted decimal) in range to update\n"
" <IP Count> count of IPs to send in update; IP address in update will\n"
" be the next <IP count> after <Starting IP>\n"
);
return( ERROR_INVALID_PARAMETER );
}
#endif
DNS_STATUS
ProcessIQuery(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR arg;
PDNS_RECORD prr = NULL;
PDNS_MSG_BUF pmsg = NULL;
PDNS_MSG_BUF pmsgRecv = NULL;
IP_ADDRESS ipserver;
PIP_ARRAY aipservers = NULL;
DNS_HEADER header;
//
// setup header
// - zero flags
*(PDWORD) &header = 0;
header.Xid = (WORD) GetCurrentTimeInSeconds();
header.Opcode = DNS_OPCODE_IQUERY;
//
// optional name server to query?
//
aipservers = pDnsServerArray;
if ( !aipservers )
{
aipservers = DnsQueryConfigAlloc(
DnsConfigDnsServerList,
NULL );
}
if ( !aipservers )
{
printf( "Update failed: no DNS server list available.\n" );
return( GetLastError() );
}
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// build update packet RRs
//
prr = buildRecordList(
Argc,
Argv );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build \ send update
//
status = QueryDirectEx(
& pmsgRecv,
NULL, // no response records
& header, // header with IQUERY set
TRUE, // no copy header count fields
NULL, // no question
0, // no question type
prr, // record list built
0, // flags
aipservers, // server IP list
NULL // no adapter list
);
if ( pmsgRecv )
{
DnsPrint_Message(
dnsup_PrintRoutine,
dnsup_PrintContext,
"IQUERY response:\n",
pmsgRecv );
}
FREE_HEAP( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
if( status != ERROR_SUCCESS )
{
printf(
"QueryDirectExEx failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "IQUERY successfully completed.\n" );
}
return( status );
Usage:
FREE_HEAP( aipservers );
Dns_RecordListFree( prr );
FREE_HEAP( pmsg );
FREE_HEAP( pmsgRecv );
printf(
"usage:\n"
" DnsUp [-n<DNS IP>|...] -iq [-f<Flags>] [<Record> | ...]\n"
" <DNS IP> optional IP address of server to send update to.\n"
" <ServerName> name of DNS server we are sending update to;\n"
" presence of server name automatically turns on update security\n"
" <ZoneName> name of zone to update\n"
" <Record> record for update packet\n"
" <Record> == (-,+)(p,u,a) <Name> <Type> [Data | ...]\n"
" (-,+) - add or delete, exist or no-exist flag\n"
" (p,u,a) - prereq, update or additional section\n"
" <Name> - RR owner name\n"
" <Type> - record type\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessValidateName(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname;
DWORD format;
//
// name to validate
//
if ( Argc != 2 )
{
goto Usage;
}
pszname = Argv[0];
Argc--;
Argv++;
//
// name format
//
if ( Argc )
{
format = strtoul( Argv[0], NULL, 10 );
}
//
// validate
//
status = DnsValidateName_A(
pszname,
format );
printf(
"DnsValidateName( %s, %d ) status = %d %s.\n",
pszname,
format,
status,
DnsStatusString(status) );
return( status );
Usage:
printf(
"DnsValidateName\n"
"usage:\n"
" DnsUp -vn <Name> <Format>\n"
" <Name> DNS name to validate\n"
" <Format> format of name\n"
"\tDomainName -- %d\n"
"\tDomainLabel -- %d\n"
"\tHostnameFull -- %d\n"
"\tHostnameLabel -- %d\n"
"\tWildcard -- %d\n"
"\tSrvRecord -- %d\n",
DnsNameDomain,
DnsNameDomainLabel,
DnsNameHostnameFull,
DnsNameHostnameLabel,
DnsNameWildcard,
DnsNameSrvRecord
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessNameCompare(
IN DWORD Argc,
IN PSTR * Argv
)
{
BOOL result;
PCHAR pszname1;
PCHAR pszname2;
//
// names to compare
//
if ( Argc != 2 )
{
goto Usage;
}
pszname1 = Argv[0];
pszname2 = Argv[1];
//
// compare
//
result = DnsNameCompare_A(
pszname1,
pszname2 );
printf(
"DnsNameCompare( %s, %s ) result = %s.\n",
pszname1,
pszname2,
result ? "TRUE" : "FALSE"
);
return( ERROR_SUCCESS );
Usage:
printf(
"DnsNameCompare\n"
"usage:\n"
" DnsUp -nc <Name1> <Name2>\n"
" <Name1> <Name2> DNS names to compare.\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessNameCompareEx(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pszname1;
PCHAR pszname2;
//
// names to compare
//
if ( Argc != 2 )
{
goto Usage;
}
pszname1 = Argv[0];
pszname2 = Argv[1];
//
// compare
//
status = DnsNameCompareEx_A(
pszname1,
pszname2,
0 );
printf(
"DnsNameCompareEx( %s, %s ) result = %d.\n",
pszname1,
pszname2,
status
);
return( ERROR_SUCCESS );
Usage:
printf(
"DnsNameCompareEx\n"
"usage:\n"
" DnsUp -cnx <Name1> <Name2>\n"
" <Name1> <Name2> DNS names to compare.\n"
"Compare Result:\n"
"\tNot Equal -- %d\n"
"\tEqual -- %d\n"
"\tLeft is Ancestor -- %d\n"
"\tRight is Ancestor -- %d\n"
"\tInvalid Name -- %d\n",
DnsNameCompareNotEqual,
DnsNameCompareEqual,
DnsNameCompareLeftParent,
DnsNameCompareRightParent,
DnsNameCompareInvalid
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessStringTranslate(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
WCHAR unicodeString[ DNS_MAX_NAME_LENGTH ];
WCHAR unicodeStandard[ DNS_MAX_NAME_LENGTH ];
BYTE utf8Standard[ DNS_MAX_NAME_LENGTH ];
PWCHAR punicode;
PCHAR putf8;
DWORD unicodeLength;
DWORD utf8Length;
DWORD unicodeLengthStandard;
//
// string translate
//
if ( Argc < 1 )
{
goto Usage;
}
//
// read in unicode characters
//
unicodeLength = 0;
while ( Argc )
{
unicodeString[ unicodeLength++] = (WORD) strtoul( Argv[0], NULL, 16 );
Argc--;
Argv++;
}
unicodeString[ unicodeLength ] = 0;
DnsPrint_UnicodeStringBytes(
dnsup_PrintRoutine,
dnsup_PrintContext,
"DnsStringTranslate().\n"
"Input string",
unicodeString,
unicodeLength );
//
// convert to UTF8 -- my way and their way
//
putf8 = Dns_StringCopyAllocate(
(PCHAR) unicodeString,
0,
DnsCharSetUnicode,
DnsCharSetUtf8 );
if ( !putf8 )
{
return( DNS_ERROR_NO_MEMORY );
}
utf8Length = WideCharToMultiByte(
CP_UTF8,
0, // no flags
(PWCHAR) unicodeString,
(-1), // null terminated
utf8Standard,
MAXWORD, // assuming adequate length
NULL,
NULL );
DnsPrint_Utf8StringBytes(
dnsup_PrintRoutine,
dnsup_PrintContext,
"My UTF8",
putf8,
strlen(putf8) );
DnsPrint_Utf8StringBytes(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Standard UTF8",
utf8Standard,
strlen(utf8Standard) );
//
// convert back to unicode
//
punicode = Dns_StringCopyAllocate(
(PCHAR) utf8Standard,
0,
DnsCharSetUtf8,
DnsCharSetUnicode );
unicodeLengthStandard = MultiByteToWideChar(
CP_UTF8,
0, // no flags
(PCHAR) utf8Standard,
(-1), // null terminated
(PWCHAR) unicodeStandard,
MAXWORD // assuming adequate length
);
DnsPrint_UnicodeStringBytes(
dnsup_PrintRoutine,
dnsup_PrintContext,
"My unicode",
punicode,
wcslen(punicode) );
DnsPrint_UnicodeStringBytes(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Standard unicode",
unicodeStandard,
wcslen(unicodeStandard) );
status = ERROR_SUCCESS;
return( status );
Usage:
printf(
"usage:\n"
" DnsUp -s [unicode chars in hex]\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessQueryConfig(
IN DWORD Argc,
IN PSTR * Argv
)
{
#define CONFIG_BUF_SIZE (100)
DNS_STATUS status;
DNS_CONFIG_TYPE config;
PCHAR psznext;
PCHAR pendString;
PWSTR pwsadapter = NULL;
DWORD flag = 0;
BOOL ballocated = FALSE;
BOOL bsleep = FALSE;
DWORD bufLength = 0;
DWORD sentBufLength;
PBYTE pbuffer = NULL;
PBYTE pallocResult;
BYTE buffer[ CONFIG_BUF_SIZE ];
//
// query config
//
if ( Argc < 1 )
{
goto Usage;
}
// config value
config = strtoul( Argv[0], &pendString, 10 );
Argc--;
Argv++;
// flag?
while ( Argc )
{
psznext = Argv[0];
// flag
if ( !_stricmp( psznext, "-f" ) ||
!_stricmp( psznext, "/f" ) )
{
Argc--;
Argv++;
flag = strtoul( Argv[0], &pendString, 16 );
Argc--;
Argv++;
if ( flag & DNS_CONFIG_FLAG_ALLOC )
{
bufLength = sizeof(PVOID);
pbuffer = buffer;
}
continue;
}
// length input
if ( !_stricmp( psznext, "-l" ) ||
!_stricmp( psznext, "/l" ) )
{
Argc--;
Argv++;
bufLength = strtoul( Argv[0], &pendString, 16 );
Argc--;
Argv++;
continue;
}
// sleep?
if ( !_stricmp( psznext, "-s" ) ||
!_stricmp( psznext, "/s" ) )
{
Argc--;
Argv++;
bsleep = TRUE;
continue;
}
// buffer
if ( !_stricmp( psznext, "-b" ) ||
!_stricmp( psznext, "/b" ) )
{
Argc--;
Argv++;
pbuffer = buffer;
bufLength = CONFIG_BUF_SIZE;
continue;
}
// adapter name
else
{
pwsadapter = DnsStringCopyAllocateEx(
Argv[0],
0,
DnsCharSetAnsi, // ANSI in
DnsCharSetUnicode // unicode out
);
continue;
}
}
//
// query DNS config
//
sentBufLength = bufLength;
status = DnsQueryConfig(
config,
flag,
pwsadapter,
NULL,
pbuffer,
& bufLength );
if ( status == ERROR_SUCCESS )
{
printf(
"DnsQueryConfig() successful.\n"
"\tflag = 0x%.8X\n"
"\tadapter = %s\n"
"\tsent length = %d\n"
"\tlength required = %d\n",
flag,
pwsadapter,
sentBufLength,
bufLength );
if ( !pbuffer )
{
return( status );
}
printf(
"\tbuffer = %02x %02x %02x %02x ...\n",
pbuffer[0],
pbuffer[1],
pbuffer[2],
pbuffer[3] );
if ( flag & DNS_CONFIG_FLAG_ALLOC )
{
pbuffer = * (PVOID *) pbuffer;
}
switch( config )
{
case DnsConfigPrimaryDomainName_W:
printf(
"Primary domain name = %S\n",
pbuffer );
break;
case DnsConfigPrimaryDomainName_A:
case DnsConfigPrimaryDomainName_UTF8:
printf(
"Primary domain name = %s\n",
pbuffer );
break;
case DnsConfigDnsServerList:
DnsPrint_IpArray(
dnsup_PrintRoutine,
dnsup_PrintContext,
"DNS server list",
NULL,
(PIP_ARRAY) pbuffer );
break;
case DnsConfigPrimaryHostNameRegistrationEnabled:
printf(
"Is Primary registration enabled = %d\n",
*(PBOOL)pbuffer );
break;
case DnsConfigAdapterHostNameRegistrationEnabled:
printf(
"Is Adapter registration enabled = %d\n",
*(PBOOL)pbuffer );
break;
case DnsConfigAddressRegistrationMaxCount:
printf(
"Max IP registration count = %d\n",
*(PDWORD)pbuffer );
break;
default:
printf( "Unknown config %d", config );
}
}
else if ( status == ERROR_MORE_DATA )
{
printf(
"DnsQueryConfig() ERROR_MORE_DATA.\n"
"\tsent length = %d\n"
"\tlength required = %d\n",
sentBufLength,
bufLength );
}
else
{
printf(
"DnsQueryConfig() failed status = %d (0x%.8X).\n",
status, status );
}
if ( bsleep )
{
printf( "Sleeping" );
Sleep( MAXDWORD );
}
return( status );
Usage:
printf(
"usage:\n"
" DnsUp -config <Config> [-f <flag>] [-b] [-l <length>] [<adapter name>]\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessReplaceRecordSet(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR arg;
PDNS_RECORD prr = NULL;
HANDLE hCreds=NULL;
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// build update packet RRs
//
prr = buildRecordList(
Argc,
Argv );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build \ send update
//
status = DnsReplaceRecordSetA(
prr,
flags,
hCreds,
NULL, // no adapter list specified
NULL // reserved
);
Dns_RecordListFree( prr );
if ( status != ERROR_SUCCESS )
{
printf(
"DnsReplaceRecordSet failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "ReplaceRecordSet successfully completed.\n" );
}
return( status );
Usage:
Dns_RecordListFree( prr );
printf(
"usage:\n"
" DnsUp -rs [-f<Flags>] [<Record> | ...]\n"
" <Record> record for update packet\n"
" <Record> == (+)(u) <Name> <Type> [Data | ...]\n"
" <Name> - RR owner name\n"
" <Type> - record type (ex A, SRV, PTR, TXT, CNAME, etc.)\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessModifyRecordsInSet(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
DWORD flags = 0;
PCHAR arg;
PDNS_RECORD prr;
PDNS_RECORD prrAdd = NULL;
PDNS_RECORD prrDelete = NULL;
HANDLE hCreds=NULL;
INT addCount;
DNS_RRSET rrsetDelete;
DNS_RRSET rrsetAdd;
DNS_RRSET_INIT( rrsetAdd );
DNS_RRSET_INIT( rrsetDelete );
//
// flags?
//
if ( Argc < 1 )
{
goto Usage;
}
// flag?
arg = Argv[0];
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
//
// build update add RRs
//
prr = buildRecordList(
Argc,
Argv );
if ( !prr )
{
status = GetLastError();
if ( status != ERROR_SUCCESS )
{
printf( "ERROR: building records from arguments\n" );
goto Usage;
}
}
//
// build separate Add and Delete lists
//
while ( prr )
{
if ( prr->Flags.S.Delete )
{
DNS_RRSET_ADD( rrsetDelete, prr );
}
else
{
DNS_RRSET_ADD( rrsetAdd, prr );
}
prr = prr->pNext;
}
DNS_RRSET_TERMINATE( rrsetAdd );
DNS_RRSET_TERMINATE( rrsetDelete );
//
// build \ send update
//
status = DnsModifyRecordsInSet_A(
rrsetAdd.pFirstRR,
rrsetDelete.pFirstRR,
flags,
hCreds,
NULL, // no adapter list specified
NULL // reserved
);
Dns_RecordListFree( rrsetAdd.pFirstRR );
Dns_RecordListFree( rrsetDelete.pFirstRR );
if ( status != ERROR_SUCCESS )
{
printf(
"DnsModifyRecordsInSet failed, %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "ModifyRecordsInSet successfully completed.\n" );
}
return( status );
Usage:
Dns_RecordListFree( rrsetAdd.pFirstRR );
Dns_RecordListFree( rrsetDelete.pFirstRR );
printf(
"usage:\n"
" DnsUp -mr [-f<Flags>] [<Record> | ...]\n"
" <Record> == (-,+)(u) <Name> <Type> [Data | ...]\n"
" (-,+) - add or delete, exist or no-exist flag\n"
" <Name> - RR owner name\n"
" <Type> - record type (ex A, SRV, PTR, TXT, CNAME, etc.)\n"
" <Data> - data strings (type specific), if any\n"
);
return( ERROR_INVALID_PARAMETER );
}
//
// Winsock \ RnR
//
//
// Protocol array for WSAQUERYSET
//
// DCR: may want to default to NULL instead?
//
AFPROTOCOLS g_ProtocolArray[2] =
{
{ AF_INET, IPPROTO_UDP },
{ AF_INET, IPPROTO_TCP }
};
//
// Service GUIDs
//
GUID HostNameGuid = SVCID_HOSTNAME;
GUID HostAddrByNameGuid = SVCID_INET_HOSTADDRBYNAME;
GUID AddressGuid = SVCID_INET_HOSTADDRBYINETSTRING;
GUID IANAGuid = SVCID_INET_SERVICEBYNAME;
typedef struct _GuidStringMap
{
LPGUID pGuid;
LPSTR pString;
};
struct _GuidStringMap GuidMapTable[] =
{
& HostNameGuid , "HostName" ,
& HostAddrByNameGuid , "AddrByName" ,
& HostAddrByNameGuid , "HostAddrByName" ,
& AddressGuid , "HostAddrByInetString" ,
& IANAGuid , "ServiceByName" ,
& IANAGuid , "Iana" ,
NULL , "NULL" ,
NULL , NULL ,
};
PGUID
guidForString(
IN PSTR pszGuidName
)
/*++
Routine Description:
Get GUID for string.
This is to allow simple specification of command line for GUIDs.
Arguments:
pszGuidName -- name of desired GUID
(see table above for current names of common guids)
Return Value:
Ptr to desired GUID if found.
Errors default to hostname (host lookup) GUID.
--*/
{
DWORD i;
PCHAR pname;
//
// if no guid name, use host name guid
//
if ( !pszGuidName )
{
return &HostNameGuid;
}
//
// find guid matching name in table
//
i = 0;
while ( pname = GuidMapTable[i].pString )
{
if ( _stricmp( pname, pszGuidName ) == 0 )
{
return GuidMapTable[i].pGuid;
}
i++;
}
// default to HostName guid
return &HostNameGuid;
}
//
// Parsing utility
//
typedef struct _RnRParseBlob
{
HANDLE Handle;
PCHAR pBuffer;
PWSAQUERYSET pQuerySet;
PGUID pGuid;
PSTR pName;
DWORD Flags;
DWORD NameSpace;
DWORD BufferLength;
}
RNR_PARSE_BLOB, *PRNR_PARSE_BLOB;
VOID
printRnRArgHelp(
VOID
)
/*++
Routine Description:
Print RnR argument usage.
Arguments:
None
Return Value:
None
--*/
{
//
// describe standard RnR lookup arguements
//
// DCR: add flag, ptr validity check
// DCR: add "already set" check for names and guids
// DCR: fix zero flag case below
//
printf(
"\tRnR Arguments:\n"
"\t\t-h<handle> -- ptr in hex to lookup handle\n"
"\t\t-b<buffer> -- ptr in hex to result buffer to use\n"
"\t\t-l<buf length> -- buffer length to use\n"
"\t\t-f<flag> -- RnR control flag in hex or LUP_X mneumonic\n"
"\t\t may be multiple flags specified\n"
"\t\t-q<query set> -- ptr in hex to existing WSAQUERYSET struct\n"
"\t\t-i<name space> -- name space id NS_X mneumonic\n"
"\t\t-a<name> -- ANSI lookup name\n"
"\t\t-g<guid> -- NS GUID mneumonic:\n"
"\t\t HostName\n"
"\t\t HostAddrByName\n"
"\t\t HostAddrByInetString\n"
"\t\t ServiceByName\n"
);
}
DNS_STATUS
parseForRnr(
IN DWORD Argc,
IN PSTR * Argv,
OUT PRNR_PARSE_BLOB pBlob
)
/*++
Routine Description:
Parse RnR routine command line params.
This is to simplify avoid duplicate parsing for various RnR
functions.
Arguments:
Argc -- argc
Argv -- argument array
pBlob -- ptr to RnR parsing blob to be filled by function
Return Value:
ERROR_SUCCESS if successful parsing.
ErrorCode on parsing error.
(Note, currently errors are ignored and whatever parsing is possible
is completed.)
--*/
{
PCHAR parg;
//
// init parse blob
//
RtlZeroMemory(
pBlob,
sizeof(RNR_PARSE_BLOB) );
//
// read arguments
//
// DCR: add flag, ptr validity check
// DCR: add "already set" check for names and guids
// DCR: fix zero flag case below
//
while ( Argc )
{
parg = Argv[0];
if ( strncmp( parg, "-h", 2 ) == 0 )
{
pBlob->Handle = (PVOID) (ULONG_PTR) strtoul( parg+2, NULL, 16 );
}
else if ( strncmp( parg, "-f", 2 ) == 0 )
{
DWORD thisFlag;
thisFlag = strtoul( parg+2, NULL, 16 );
if ( thisFlag == 0 )
{
thisFlag = Dns_RnrLupFlagForString( parg+2, 0 );
}
pBlob->Flags |= thisFlag;
}
else if ( strncmp( parg, "-b", 2 ) == 0 )
{
pBlob->pBuffer = (PBYTE) (ULONG_PTR) strtoul( parg+2, NULL, 16 );
}
else if ( strncmp( parg, "-l", 2 ) == 0 )
{
pBlob->BufferLength = strtoul( parg+2, NULL, 10 );
}
else if ( strncmp( parg, "-a", 2 ) == 0 )
{
pBlob->pName = parg+2;
}
else if ( strncmp( parg, "-g", 2 ) == 0 )
{
pBlob->pGuid = guidForString( parg+2 );
}
else if ( strncmp( parg, "-i", 2 ) == 0 )
{
pBlob->NameSpace = Dns_RnrNameSpaceIdForString(
parg+2,
0 // length unknown
);
}
else if ( strncmp( parg, "-q", 2 ) == 0 )
{
pBlob->pQuerySet = (PWSAQUERYSET) (ULONG_PTR) strtoul( parg+2, NULL, 16 );
}
else if ( *parg=='?' || strncmp( parg, "-?", 2 ) == 0 )
{
goto Usage;
}
else
{
printf( "Unknown Arg = %s\n", parg );
goto Usage;
}
Argc--;
Argv++;
}
return( ERROR_SUCCESS );
Usage:
printRnRArgHelp();
return( ERROR_INVALID_PARAMETER );
}
PWSAQUERYSET
buildWsaQuerySet(
IN PRNR_PARSE_BLOB pBlob
)
/*++
Routine Description:
Create (allocate) a query set.
Arguments:
pBlob -- ptr to blob of parsing info
Return Value:
Ptr to new WSAQUERYSET struct.
NULL on allocation failure.
--*/
{
PWSAQUERYSET pwsaq;
//
// allocate
//
pwsaq = ALLOCATE_HEAP_ZERO( sizeof(WSAQUERYSET) );
if ( !pwsaq )
{
return( NULL );
}
//
// basic
// - default GUID to hostname lookup
//
pwsaq->dwSize = sizeof(WSAQUERYSET);
pwsaq->dwNumberOfProtocols = 2;
pwsaq->lpafpProtocols = g_ProtocolArray;
pwsaq->lpServiceClassId = & HostNameGuid;
//
// tack on info from blob
// - keep parsing generic by casting name to TSTR
//
if ( pBlob )
{
pwsaq->dwNameSpace = pBlob->NameSpace;
pwsaq->lpszServiceInstanceName = (LPTSTR) pBlob->pName;
pwsaq->lpServiceClassId = pBlob->pGuid;
}
return (PWSAQUERYSET) pwsaq;
}
DNS_STATUS
ProcessLookupServiceBegin(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
HANDLE handle = NULL;
PWSAQUERYSET pquerySet = NULL;
PWSAQUERYSET pquerySetLocal = NULL;
RNR_PARSE_BLOB blob;
//
// WSALookupServiceBegin
//
//
// parse args
//
status = parseForRnr( Argc, Argv, &blob );
if ( status != ERROR_SUCCESS )
{
goto Usage;
}
//
// setup query set
//
pquerySet = blob.pQuerySet;
if ( !pquerySet )
{
pquerySet = buildWsaQuerySet( &blob );
pquerySetLocal = pquerySet;
}
IF_DNSDBG( INIT )
{
DnsDbg_WsaQuerySet(
"Query set to WLSBegin",
pquerySet,
FALSE // currently ANSI
);
}
DnsPrint_WsaQuerySet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"QuerySet to LookupServiceBegin",
(PWSAQUERYSET) pquerySet,
FALSE // ANSI call
);
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
status = WSALookupServiceBeginA(
pquerySet,
blob.Flags,
& handle );
if ( status != ERROR_SUCCESS )
{
status = GetLastError();
printf(
"WSALookupServiceBegin( %p, %08x, %p ) failed, %d %s.\n",
pquerySet,
blob.Flags,
& handle,
status,
DnsStatusString(status) );
if ( pquerySetLocal )
{
FREE_HEAP( pquerySetLocal );
}
}
else
{
printf(
"WSALookupServiceBegin() succeeded.\n"
"\thandle = %p\n"
"\tqueryset = %p\n",
handle,
pquerySet );
}
return( status );
Usage:
printf(
"WSALookupServiceBegin\n"
"usage:\n"
" DnsUp -lsb [RnR args]\n"
" note handle arg does not apply\n"
);
printRnRArgHelp();
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessLookupServiceNext(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
HANDLE handle = NULL;
PCHAR pbuf = NULL;
PCHAR pbufLocal = NULL;
DWORD bufLength;
RNR_PARSE_BLOB blob;
//
// WSALookupServiceNext
//
//
// parse args
//
status = parseForRnr( Argc, Argv, &blob );
if ( status != ERROR_SUCCESS )
{
goto Usage;
}
//
// if no handle need to do WLSBegin
//
handle = blob.Handle;
if ( !handle )
{
goto Usage;
}
//
// setup buffer
//
bufLength = blob.BufferLength;
pbuf = blob.pBuffer;
if ( !pbuf && bufLength )
{
pbuf = ALLOCATE_HEAP( blob.BufferLength );
if ( !pbuf )
{
status = DNS_ERROR_NO_MEMORY;
}
pbufLocal = pbuf;
}
//
// call
//
printf(
"Enter WSALookupServiceNext()\n"
"\thandle = %p\n"
"\tflag = %08x\n"
"\tlength = %p (%d)\n"
"\tbuffer = %p\n",
handle,
blob.Flags,
&bufLength, bufLength,
pbuf );
status = WSALookupServiceNextA(
handle,
blob.Flags,
& bufLength,
(PWSAQUERYSETA) pbuf );
if ( status != ERROR_SUCCESS )
{
status = GetLastError();
printf(
"WSALookupServiceNext( h=%p, f=%08x, len=%d, buf=%p ) failed, %d %s.\n",
handle,
blob.Flags,
bufLength,
pbuf,
status,
DnsStatusString(status) );
if ( pbufLocal )
{
FREE_HEAP( pbufLocal );
}
}
else
{
printf(
"WSALookupServiceNext() succeeded.\n"
"\thandle = %p\n"
"\tbuf length = %d\n"
"\tpbuffer = %p\n",
handle,
bufLength,
pbuf );
if ( pbuf )
{
DnsPrint_WsaQuerySet(
dnsup_PrintRoutine,
dnsup_PrintContext,
"Result from LookupServiceNext",
(PWSAQUERYSET) pbuf,
FALSE // ANSI call
);
}
}
return( status );
Usage:
printf(
"WSALookupServiceNext\n"
"usage:\n"
" DnsUp -lsn [RnR args]\n"
" valid WSALookupServiceNext args:\n"
" <handle> <flags> <buffer> <buf length>\n"
" <handle> is required, call WSALookupServiceBegin\n"
" to get handle to use with this function\n"
);
printRnRArgHelp();
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessLookupServiceEnd(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
HANDLE handle;
//
// WSALookupServiceEnd <handle>
//
if ( Argc < 1 )
{
goto Usage;
}
// read handle
//
// DCR: need QWORD read to handle win64 case
handle = (HANDLE) (UINT_PTR) strtoul( Argv[0], NULL, 16 );
//
// call
//
status = WSALookupServiceEnd( handle );
if ( status != ERROR_SUCCESS )
{
printf(
"WSALookupServiceEnd( %p ) failed, %d %s.\n",
handle,
status,
DnsStatusString(status) );
}
else
{
printf(
"WSALookupServiceEnd( %p ) succeeded.\n",
handle );
}
Dns_CleanupWinsock();
return( status );
Usage:
printf(
"WSALookupServiceEnd\n"
"usage:\n"
" DnsUp -lse -h<handle>\n"
" <handle> - lookup handle to close in hex.\n"
" handle is from prior WSALookupServiceBegin call.\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessGetHostName(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
CHAR nameBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
DWORD length = DNS_MAX_NAME_BUFFER_LENGTH;
PCHAR pname = nameBuffer;
INT result;
//
// gethostname [-n<name>] [-l<length>]
//
if ( Argc > 2 )
{
goto Usage;
}
//
// get optional name or length
// - defaults to full size buffer above
//
while ( Argc )
{
PCHAR arg = Argv[0];
if ( strncmp( arg, "-n", 2 ) == 0 )
{
pname = getNamePointer( arg+2 );
Argc--;
Argv++;
}
else if ( strncmp( arg, "-l", 2 ) == 0 )
{
length = strtoul( arg+2, NULL, 10 );
Argc--;
Argv++;
}
else
{
goto Usage;
}
}
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
result = gethostname( (PSTR)pname, length );
if ( result != 0 )
{
status = GetLastError();
printf(
"gethostname(), %d (%x) %s.\n",
status, status,
DnsStatusString(status) );
}
else
{
printf(
"gethostname() successfully completed.\n"
"\tname = %s\n",
pname );
}
return( status );
Usage:
printf(
"gethostname\n"
"usage:\n"
" DnsUp -ghn [-n<Name>] [-l<length>]\n"
" <Name> - one of special names.\n"
" <Length> - buffer length to pass in\n"
" Defaults to valid name buffer of full DNS name length\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessGetHostByName(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pname;
PHOSTENT phost;
//
// gethostbyname <name>
//
if ( Argc != 1 )
{
goto Usage;
}
// name
pname = getNamePointer( Argv[0] );
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
phost = gethostbyname( (PCSTR)pname );
status = GetLastError();
if ( !phost )
{
printf(
"gethostbyname(), %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "gethostbyname successfully completed.\n" );
DnsPrint_Hostent(
dnsup_PrintRoutine,
dnsup_PrintContext,
NULL, // default header
phost,
FALSE // ANSI
);
}
return( status );
Usage:
printf(
"gethostbyname\n"
"usage:\n"
" DnsUp -ghbn <Name>\n"
" <Name> - DNS name to query.\n"
" \"NULL\" for null input.\n"
" \"blank\" for blank (empty string)\n"
);
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessGetHostByAddr(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pname;
PCHAR paddr;
PHOSTENT phost;
IP6_ADDRESS ip6;
INT length;
INT family;
//
// gethostbyaddr <ip> [len] [family]
//
if ( Argc < 1 || Argc > 3 )
{
goto Usage;
}
//
// address
//
pname = getNamePointer( Argv[0] );
Argc--;
Argv++;
length = sizeof(IP6_ADDRESS);
family = 0;
if ( pname
&&
Dns_StringToAddress_A(
(PCHAR) & ip6,
& length,
pname,
& family ) )
{
paddr = (PCHAR) &ip6;
}
else
{
paddr = pname;
length = sizeof(IP4_ADDRESS);
family = AF_INET;
}
// length -- optional
if ( Argc )
{
length = strtoul( Argv[0], NULL, 10 );
Argc--;
Argv++;
}
// family -- optional
if ( Argc )
{
family = strtoul( Argv[0], NULL, 10 );
Argc--;
Argv++;
}
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
printf(
"calling gethostbyaddr( %p, %d, %d )\n",
paddr,
length,
family );
phost = gethostbyaddr(
(PSTR)paddr,
length,
family );
status = GetLastError();
if ( !phost )
{
printf(
"gethostbyaddr(), %d %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "gethostbyaddr successfully completed.\n" );
DnsPrint_Hostent(
dnsup_PrintRoutine,
dnsup_PrintContext,
NULL, // default header
phost,
FALSE // ANSI
);
}
return( status );
Usage:
printf(
"gethostbyaddr\n"
"usage:\n"
" DnsUp -ghba <Address> [<length>] [<family>]\n"
" <Name> - DNS name to query.\n"
" \"null\" for null input.\n"
" \"blank\" for blank (empty string)\n"
" [length] - length of address\n"
" defaults to sizeof(IP_ADDRESS)\n"
" [family] - address family\n"
" AF_INET = %d\n"
" AF_INET6 = %d\n"
" AF_ATM = %d\n"
" defaults to AF_INET\n",
AF_INET,
AF_INET6,
AF_ATM );
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessGetAddrInfo(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
PCHAR pname;
PSTR pservice = NULL;
PADDRINFO phint = NULL;
PADDRINFO paddrInfo = NULL;
ADDRINFO hintAddrInfo;
BOOL fhint = FALSE;
INT result;
//
// getaddrinfo <name> [service] [hints]
//
if ( Argc < 1 )
{
goto Usage;
}
//
// name
//
pname = getNamePointer( Argv[0] );
Argc--;
Argv++;
//
// setup hints
// - default flags to include canonnical name
//
RtlZeroMemory(
& hintAddrInfo,
sizeof(hintAddrInfo) );
hintAddrInfo.ai_flags = AI_CANONNAME;
//
// get optional service name or hints
//
while ( Argc )
{
PCHAR arg = Argv[0];
if ( strncmp( arg, "-s", 2 ) == 0 )
{
pservice = getNamePointer( arg+2 );
Argc--;
Argv++;
}
if ( strncmp( arg, "-h", 2 ) == 0 )
{
fhint = TRUE;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-f", 2 ) == 0 )
{
hintAddrInfo.ai_flags = strtoul( arg+2, NULL, 16 );
fhint = TRUE;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-m", 2 ) == 0 )
{
hintAddrInfo.ai_family = strtoul( arg+2, NULL, 10 );
fhint = TRUE;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-t", 2 ) == 0 )
{
hintAddrInfo.ai_socktype = strtoul( arg+2, NULL, 10 );
fhint = TRUE;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-p", 2 ) == 0 )
{
hintAddrInfo.ai_protocol = strtoul( arg+2, NULL, 10 );
fhint = TRUE;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-l", 2 ) == 0 )
{
hintAddrInfo.ai_addrlen = strtoul( arg+2, NULL, 10 );
fhint = TRUE;
Argc--;
Argv++;
}
else
{
goto Usage;
}
}
//
// setup hints
//
if ( fhint )
{
phint = &hintAddrInfo;
}
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
printf(
"calling getaddrinfo()\n"
"\tpname = %s\n"
"\tpservice = %s\n"
"\tphint = %p\n"
"\tpaddrinfo buf = %p\n",
pname,
pservice,
phint,
paddrInfo );
if ( phint )
{
DnsPrint_AddrInfo(
dnsup_PrintRoutine,
dnsup_PrintContext,
"hint addrinfo",
1, // indent
phint
);
}
result = getaddrinfo(
pname,
pservice,
phint,
& paddrInfo );
status = GetLastError();
if ( result != NO_ERROR )
{
printf(
"getaddrinfo(), %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf( "getaddrinfo successfully completed.\n" );
DnsPrint_AddrInfo(
dnsup_PrintRoutine,
dnsup_PrintContext,
NULL, // default header
0, // no indent
paddrInfo
);
freeaddrinfo( paddrInfo );
}
return( status );
Usage:
printf(
"getaddrinfo\n"
"usage:\n"
" DnsUp -gai <Name> [-s<Service>] [-h] [-f<flags>] [-m<family>]\n"
" [-p<Protocol>] [-t<SockType>] [-l<length>]\n"
" <Name> - DNS name to query.\n"
" \"null\" for null input.\n"
" \"blank\" for blank (empty string)\n"
" <Service> - service name to query for.\n"
" [-h] - use hints (this allows empty hint buffer\n"
" <flags> - hint flags\n"
" AI_PASSIVE = %0x\n"
" AI_CANONNAME = %0x\n"
" AI_NUMERICHOST = %0x\n"
" <family> - hint family\n"
" AF_INET = %d\n"
" AF_INET6 = %d\n"
" AF_ATM = %d\n"
" <socktype> - hint socket type\n"
" <protocol> - hint protocol\n"
" <length> - hint address length\n",
AI_PASSIVE,
AI_CANONNAME,
AI_NUMERICHOST,
AF_INET,
AF_INET6,
AF_ATM );
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessGetNameInfo(
IN DWORD Argc,
IN PSTR * Argv
)
{
DNS_STATUS status;
BOOL result;
PCHAR paddr = NULL;
DWORD addrLength;
DWORD family;
DWORD flags = 0;
SOCKADDR_IN6 sockaddr;
CHAR hostBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
CHAR serviceBuffer[ DNS_MAX_NAME_BUFFER_LENGTH ];
PSOCKADDR_IN6 psockaddr = &sockaddr;
PCHAR pservice = serviceBuffer;
PCHAR phost = hostBuffer;
DWORD sockaddrLength = sizeof(sockaddr);
DWORD serviceLength = sizeof(hostBuffer);
DWORD hostLength = DNS_MAX_NAME_BUFFER_LENGTH;
//
// getnameinfo <address> [sockaddr fields | args]
//
if ( Argc < 1 )
{
goto Usage;
}
//
// address
//
RtlZeroMemory(
psockaddr,
sizeof(*psockaddr) );
#if 0
if ( getAddressPointer(
Argv[0],
& paddr,
& addrLength,
& family ) )
{
sockaddr.sin6_family = (SHORT) family;
if ( family == AF_INET )
{
RtlCopyMemory(
& ((PSOCKADDR_IN)psockaddr)->sin_addr,
paddr,
addrLength );
sockaddrLength = sizeof(SOCKADDR_IN);
}
else // IP6
{
RtlCopyMemory(
& psockaddr->sin6_addr,
paddr,
addrLength );
sockaddrLength = sizeof(SOCKADDR_IN6);
}
}
else // special bogus address, use it directly
{
psockaddr = (PSOCKADDR_IN6) paddr;
}
#endif
if ( getAddressPointer(
Argv[0],
& paddr,
& addrLength,
& family ) )
{
status = Dns_AddressToSockaddr(
(PSOCKADDR) &sockaddr,
& sockaddrLength,
TRUE, // clear
paddr,
addrLength,
family );
if ( status != NO_ERROR )
{
goto Usage;
}
}
else // special bogus address, use it directly
{
psockaddr = (PSOCKADDR_IN6) paddr;
}
Argc--;
Argv++;
//
// get sockaddr fields or optional arguments
// - note, if using bogus sockaddr, the sockaddr tweaks are lost
//
while ( Argc )
{
PCHAR arg = Argv[0];
// flags
if ( strncmp( arg, "-f", 2 ) == 0 )
{
flags = strtoul( arg+2, NULL, 16 );
Argc--;
Argv++;
}
// sockaddr subfields
else if ( strncmp( arg, "-m", 2 ) == 0 )
{
sockaddr.sin6_family = (SHORT) strtoul( arg+2, NULL, 10 );
Argc--;
Argv++;
}
else if ( strncmp( arg, "-p", 2 ) == 0 )
{
sockaddr.sin6_port = (SHORT) strtoul( arg+2, NULL, 10 );
Argc--;
Argv++;
}
// tweak out params
else if ( strncmp( arg, "-sl", 3 ) == 0 )
{
serviceLength = strtoul( arg+3, NULL, 10 );
Argc--;
Argv++;
}
else if ( _strnicmp( arg, "-snull", 5 ) == 0 )
{
pservice = NULL;
Argc--;
Argv++;
}
else if ( strncmp( arg, "-hl", 3 ) == 0 )
{
hostLength = strtoul( arg+3, NULL, 10 );
Argc--;
Argv++;
}
else if ( _strnicmp( arg, "-hnull", 5 ) == 0 )
{
phost = NULL;
Argc--;
Argv++;
}
else
{
goto Usage;
}
}
//
// force winsock start
//
Dns_InitializeWinsock();
//
// call
//
printf(
"Calling getnameinfo()\n"
"\tsockaddr %p\n"
"\tsockaddr len %d\n"
"\thost buffer %p\n"
"\thost buflen %d\n"
"\tservice buffer %p\n"
"\tservice buflen %d\n"
"\tflags %08x\n",
psockaddr,
sockaddrLength,
phost,
hostLength,
pservice,
serviceLength,
flags );
DnsPrint_Sockaddr(
dnsup_PrintRoutine,
dnsup_PrintContext,
"\n\tsockaddr",
1, // indent
(PSOCKADDR) psockaddr,
sockaddrLength );
result = getnameinfo(
(PSOCKADDR) psockaddr,
sockaddrLength,
phost,
hostLength,
pservice,
serviceLength,
flags );
status = GetLastError();
if ( result != NO_ERROR )
{
printf(
"getnameinfo(), %x %s.\n",
status,
DnsStatusString(status) );
}
else
{
printf(
"getnameinfo successfully completed.\n"
"\thost = %s\n"
"\tservice = %s\n",
phost,
pservice );
}
return( status );
Usage:
printf(
"getnameinfo\n"
"usage:\n"
" DnsUp -gni <Address> [-s<Service>] [-f<flags>] [-m<family>]\n"
" [-p<port>] [-sl<length>] [-snull] [-hl<length>] [-hnull]\n"
" <Address> - Address string to query name for.\n"
" \"null\" for null input.\n"
" \"blank\" for blank (empty string)\n"
" <flags> - hint flags\n"
" AI_PASSIVE = %0x\n"
" AI_CANONNAME = %0x\n"
" AI_NUMERICHOST = %0x\n"
" <family> - sockaddr family, overrides default for address;\n"
" AF_INET = %d\n"
" AF_INET6 = %d\n"
" AF_ATM = %d\n"
" <port> - port to set in sockaddr; defaults to zero\n"
" <length> - length of out buffer\n"
" -sl<length> - length of service name buffer\n"
" -hl<length> - length of host name buffer\n"
" [-snull] - server buffer ptr NULL\n"
" [-hnull] - host buffer ptr NULL\n",
AI_PASSIVE,
AI_CANONNAME,
AI_NUMERICHOST,
AF_INET,
AF_INET6,
AF_ATM );
return( ERROR_INVALID_PARAMETER );
}
DNS_STATUS
ProcessClusterIp(
IN DWORD Argc,
IN PSTR * Argv
)
{
SOCKADDR_IN6 sockaddr;
PSOCKADDR psockaddr = (PSOCKADDR) &sockaddr;
DWORD sockaddrLength = sizeof(sockaddr);
IP_ADDRESS ip;
BOOL fadd = TRUE;
PWSTR pname = NULL;
DNS_STATUS status;
//
// -ci <Name> <IP> [-d]
//
if ( Argc < 1 )
{
goto Usage;
}
//
// name
//
pname = (PWSTR) Dns_StringCopyAllocate(
Argv[0],
0, // unknown length
DnsCharSetAnsi,
DnsCharSetUnicode );
Argc--;
Argv++;
//
// cluster IP
//
#if 0
ip = inet_addr( Argv[0] );
if ( ip == (-1) )
{
goto Usage;
}
Argc--;
Argv++;
#endif
if ( !getSockaddrFromString(
& psockaddr,
& sockaddrLength,
Argv[0] ) )
{
goto Usage;
}
Argc--;
Argv++;
//
// optional delete flag
//
if ( Argc > 0 )
{
if ( strncmp( Argv[0], "-d", 2 ) == 0 )
{
fadd = FALSE;
Argc--;
Argv++;
}
if ( Argc != 0 )
{
goto Usage;
}
}
//
// notify resolver
//
status = DnsRegisterClusterAddress(
0xd734453d,
pname,
psockaddr,
fadd );
FREE_HEAP( pname );
return( status );
Usage:
printf(
"Set Cluster IP\n"
"usage:\n"
" DnsUp [-ci] <Name> <IP> [-d]\n"
" <Name> is cluster name\n"
" <IP> is cluster IP\n"
" [-d] to indicate delete of cluster IP\n"
);
FREE_HEAP( pname );
return( ERROR_INVALID_PARAMETER );
}
#if 0
DNS_STATUS
ProcessAddIp(
IN DWORD Argc,
IN PSTR * Argv
)
{
IP_ADDRESS ip;
BOOL fadd = TRUE;
//
// -addip <IP>
//
if ( Argc < 1 )
{
goto Usage;
}
//
// IP to add
//
ip = inet_addr( Argv[0] );
if ( ip == (-1) )
{
goto Usage;
}
Argc--;
Argv++;
//
// optional delete flag
//
if ( Argc > 0 )
{
if ( strncmp( Argv[0], "-d", 2 ) == 0 )
{
fadd = FALSE;
Argc--;
Argv++;
}
if ( Argc != 0 )
{
goto Usage;
}
}
//
// notify resolver
//
DnsNotifyResolverClusterIp(
ip,
fadd );
return( ERROR_SUCCESS );
Usage:
printf(
"usage:\n"
" DnsUp [-ci] <IP> [-d]\n"
" <IP> is cluster IP\n"
" [-d] to indicate delete of cluster IP\n"
);
return( ERROR_INVALID_PARAMETER );
}
#endif
DNS_STATUS
ProcessSetEnvironmentVariable(
IN DWORD Argc,
IN PSTR * Argv
)
{
//
// setenv <name> <value>
//
if ( Argc < 1 || Argc > 2 )
{
goto Usage;
}
SetEnvironmentVariableA(
Argv[0],
(Argc == 2) ? Argv[1] : NULL );
return( ERROR_SUCCESS );
Usage:
printf(
"Set environment variable\n"
"usage:\n"
" DnsUp [-setenv] <name> [<value>]\n"
" <name> name of environment variable\n"
" <value> value of environment variable, if missing\n"
" then environment variable is deleted\n"
);
return( ERROR_INVALID_PARAMETER );
}
//
// Command Table
//
// Keep this down here to avoid having prototypes for
// all these functions.
//
COMMAND_INFO GlobalCommandInfo[] =
{
// query
{ "qex" , "QueryEx" , ProcessQueryEx },
{ "qc" , "QueryCompare" , ProcessQueryCompare },
{ "q" , "Query" , ProcessQuery },
{ "qm" , "QueryMulitple" , ProcessQueryMultiple },
{ "qt" , "QueryTest" , ProcessQueryTest },
{ "iq" , "IQuery" , ProcessIQuery },
// update
{ "u" , "Update" , ProcessUpdate },
{ "ut" , "UpdateTest" , ProcessUpdateTest },
//{ "um" , "UpdateMultiple" , ProcessUpdateMultiple },
{ "dt" , "DhcpTest" , ProcessDhcpTest },
{ "mr" , "ModifyRecordsInSet" , ProcessModifyRecordsInSet },
{ "rs" , "ReplaceRecordSet" , ProcessReplaceRecordSet },
// utility
{ "vn" , "ValidateName" , ProcessValidateName },
{ "s" , "StringTranslate" , ProcessStringTranslate },
{ "cn" , "NameCompare" , ProcessNameCompare },
{ "cnx" , "NameCompareEx" , ProcessNameCompareEx },
{ "config" , "QueryConfig" , ProcessQueryConfig },
// config
{ "setenv" , "SetEnvironmentVariable", ProcessSetEnvironmentVariable },
// resolver
{ "ci" , "ClusterIp" , ProcessClusterIp },
// RnR
{ "ghn" , "gethostname" , ProcessGetHostName },
{ "ghbn" , "gethostbyname" , ProcessGetHostByName },
{ "ghba" , "gethostbyaddr" , ProcessGetHostByAddr },
{ "gai" , "getaddrinfo" , ProcessGetAddrInfo },
{ "gni" , "getnameinfo" , ProcessGetNameInfo },
{ "lsb" , "LookupServiceBegin" , ProcessLookupServiceBegin },
{ "lsn" , "LookupServiceNext" , ProcessLookupServiceNext },
{ "lse" , "LookupServiceEnd" , ProcessLookupServiceEnd },
// quit
{ "quit" , "Quit" , ProcessQuit },
{ "exit" , "Quit" , ProcessQuit },
{ NULL, NULL, NULL },
};
//
// End dnsup.c
//