405 lines
12 KiB
C
405 lines
12 KiB
C
/*
|
|
* IP configuration utility
|
|
*
|
|
* Copyright 2008 Andrew Riedi
|
|
* Copyright 2010 Andrew Nguyen
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <iphlpapi.h>
|
|
#include <wine/unicode.h>
|
|
|
|
#include "ipconfig.h"
|
|
|
|
static int ipconfig_vprintfW(const WCHAR *msg, va_list va_args)
|
|
{
|
|
int wlen;
|
|
DWORD count, ret;
|
|
WCHAR msg_buffer[8192];
|
|
|
|
wlen = vsprintfW(msg_buffer, msg, va_args);
|
|
|
|
ret = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg_buffer, wlen, &count, NULL);
|
|
if (!ret)
|
|
{
|
|
DWORD len;
|
|
char *msgA;
|
|
|
|
len = WideCharToMultiByte(GetConsoleOutputCP(), 0, msg_buffer, wlen,
|
|
NULL, 0, NULL, NULL);
|
|
msgA = HeapAlloc(GetProcessHeap(), 0, len);
|
|
if (!msgA)
|
|
return 0;
|
|
|
|
WideCharToMultiByte(GetConsoleOutputCP(), 0, msg_buffer, wlen, msgA, len,
|
|
NULL, NULL);
|
|
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
|
|
HeapFree(GetProcessHeap(), 0, msgA);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static int ipconfig_printfW(const WCHAR *msg, ...)
|
|
{
|
|
va_list va_args;
|
|
int len;
|
|
|
|
va_start(va_args, msg);
|
|
len = ipconfig_vprintfW(msg, va_args);
|
|
va_end(va_args);
|
|
|
|
return len;
|
|
}
|
|
|
|
static int ipconfig_message_printfW(int msg, ...)
|
|
{
|
|
va_list va_args;
|
|
WCHAR msg_buffer[8192];
|
|
int len;
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
|
|
sizeof(msg_buffer)/sizeof(WCHAR));
|
|
|
|
va_start(va_args, msg);
|
|
len = ipconfig_vprintfW(msg_buffer, va_args);
|
|
va_end(va_args);
|
|
|
|
return len;
|
|
}
|
|
|
|
static int ipconfig_message(int msg)
|
|
{
|
|
static const WCHAR formatW[] = {'%','s',0};
|
|
WCHAR msg_buffer[8192];
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
|
|
sizeof(msg_buffer)/sizeof(WCHAR));
|
|
|
|
return ipconfig_printfW(formatW, msg_buffer);
|
|
}
|
|
|
|
static const WCHAR *iftype_to_string(DWORD type)
|
|
{
|
|
static WCHAR msg_buffer[50];
|
|
|
|
int msg;
|
|
|
|
switch (type)
|
|
{
|
|
case IF_TYPE_ETHERNET_CSMACD:
|
|
/* The loopback adapter appears as an Ethernet device. */
|
|
case IF_TYPE_SOFTWARE_LOOPBACK:
|
|
msg = STRING_ETHERNET;
|
|
break;
|
|
default:
|
|
msg = STRING_UNKNOWN;
|
|
}
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
|
|
sizeof(msg_buffer)/sizeof(WCHAR));
|
|
|
|
return msg_buffer;
|
|
}
|
|
|
|
static void print_field(int msg, const WCHAR *value)
|
|
{
|
|
static const WCHAR formatW[] = {' ',' ',' ',' ','%','s',':',' ','%','s','\n',0};
|
|
|
|
WCHAR field[] = {'.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',
|
|
' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ','.',' ',0};
|
|
WCHAR name_buffer[sizeof(field)/sizeof(WCHAR)];
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), msg, name_buffer, sizeof(name_buffer)/sizeof(WCHAR));
|
|
memcpy(field, name_buffer, sizeof(WCHAR) * min(strlenW(name_buffer), sizeof(field)/sizeof(WCHAR) - 1));
|
|
|
|
ipconfig_printfW(formatW, field, value);
|
|
}
|
|
|
|
static void print_value(const WCHAR *value)
|
|
{
|
|
static const WCHAR formatW[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
|
|
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
|
|
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
|
|
' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
|
|
'%','s','\n',0};
|
|
|
|
ipconfig_printfW(formatW, value);
|
|
}
|
|
|
|
static BOOL socket_address_to_string(WCHAR *buf, DWORD len, SOCKET_ADDRESS *addr)
|
|
{
|
|
return WSAAddressToStringW(addr->lpSockaddr,
|
|
addr->iSockaddrLength, NULL,
|
|
buf, &len) == 0;
|
|
}
|
|
|
|
static void print_basic_information(void)
|
|
{
|
|
IP_ADAPTER_ADDRESSES *adapters;
|
|
ULONG out = 0;
|
|
|
|
if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &out) == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
adapters = HeapAlloc(GetProcessHeap(), 0, out);
|
|
if (!adapters)
|
|
exit(1);
|
|
|
|
if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &out) == ERROR_SUCCESS)
|
|
{
|
|
IP_ADAPTER_ADDRESSES *p;
|
|
|
|
for (p = adapters; p; p = p->Next)
|
|
{
|
|
static const WCHAR newlineW[] = {'\n',0};
|
|
static const WCHAR emptyW[] = {0};
|
|
|
|
IP_ADAPTER_UNICAST_ADDRESS *addr;
|
|
IP_ADAPTER_GATEWAY_ADDRESS_LH *gateway;
|
|
WCHAR addr_buf[54];
|
|
|
|
ipconfig_message_printfW(STRING_ADAPTER_FRIENDLY, iftype_to_string(p->IfType), p->FriendlyName);
|
|
ipconfig_printfW(newlineW);
|
|
print_field(STRING_CONN_DNS_SUFFIX, p->DnsSuffix);
|
|
|
|
for (addr = p->FirstUnicastAddress; addr; addr = addr->Next)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &addr->Address))
|
|
print_field(STRING_IP_ADDRESS, addr_buf);
|
|
/* FIXME: Output corresponding subnet mask. */
|
|
}
|
|
|
|
if (p->FirstGatewayAddress)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &p->FirstGatewayAddress->Address))
|
|
print_field(STRING_DEFAULT_GATEWAY, addr_buf);
|
|
|
|
for (gateway = p->FirstGatewayAddress->Next; gateway; gateway = gateway->Next)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &gateway->Address))
|
|
print_value(addr_buf);
|
|
}
|
|
}
|
|
else
|
|
print_field(STRING_DEFAULT_GATEWAY, emptyW);
|
|
|
|
ipconfig_printfW(newlineW);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, adapters);
|
|
}
|
|
}
|
|
|
|
static const WCHAR *nodetype_to_string(DWORD type)
|
|
{
|
|
static WCHAR msg_buffer[50];
|
|
|
|
int msg;
|
|
|
|
switch (type)
|
|
{
|
|
case BROADCAST_NODETYPE:
|
|
msg = STRING_BROADCAST;
|
|
break;
|
|
case PEER_TO_PEER_NODETYPE:
|
|
msg = STRING_PEER_TO_PEER;
|
|
break;
|
|
case MIXED_NODETYPE:
|
|
msg = STRING_MIXED;
|
|
break;
|
|
case HYBRID_NODETYPE:
|
|
msg = STRING_HYBRID;
|
|
break;
|
|
default:
|
|
msg = STRING_UNKNOWN;
|
|
}
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer,
|
|
sizeof(msg_buffer)/sizeof(WCHAR));
|
|
|
|
return msg_buffer;
|
|
}
|
|
|
|
static WCHAR *physaddr_to_string(WCHAR *buf, BYTE *addr, DWORD len)
|
|
{
|
|
static const WCHAR fmtW[] = {'%','0','2','X','-',0};
|
|
static const WCHAR fmt2W[] = {'%','0','2','X',0};
|
|
|
|
if (!len)
|
|
*buf = '\0';
|
|
else
|
|
{
|
|
WCHAR *p = buf;
|
|
DWORD i;
|
|
|
|
for (i = 0; i < len - 1; i++)
|
|
{
|
|
sprintfW(p, fmtW, addr[i]);
|
|
p += 3;
|
|
}
|
|
sprintfW(p, fmt2W, addr[i]);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static const WCHAR *boolean_to_string(int value)
|
|
{
|
|
static WCHAR msg_buffer[15];
|
|
|
|
LoadStringW(GetModuleHandleW(NULL), value ? STRING_YES : STRING_NO,
|
|
msg_buffer, sizeof(msg_buffer)/sizeof(WCHAR));
|
|
|
|
return msg_buffer;
|
|
}
|
|
|
|
static void print_full_information(void)
|
|
{
|
|
static const WCHAR newlineW[] = {'\n',0};
|
|
static const WCHAR emptyW[] = {0};
|
|
|
|
FIXED_INFO *info;
|
|
IP_ADAPTER_ADDRESSES *adapters;
|
|
ULONG out = 0;
|
|
|
|
if (GetNetworkParams(NULL, &out) == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
info = HeapAlloc(GetProcessHeap(), 0, out);
|
|
if (!info)
|
|
exit(1);
|
|
|
|
if (GetNetworkParams(info, &out) == ERROR_SUCCESS)
|
|
{
|
|
WCHAR hostnameW[MAX_HOSTNAME_LEN + 4];
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, info->HostName, -1, hostnameW, sizeof(hostnameW));
|
|
print_field(STRING_HOSTNAME, hostnameW);
|
|
|
|
/* FIXME: Output primary DNS suffix. */
|
|
|
|
print_field(STRING_NODE_TYPE, nodetype_to_string(info->NodeType));
|
|
print_field(STRING_IP_ROUTING, boolean_to_string(info->EnableRouting));
|
|
|
|
/* FIXME: Output WINS proxy status and DNS suffix search list. */
|
|
|
|
ipconfig_printfW(newlineW);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, info);
|
|
}
|
|
|
|
if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &out) == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
adapters = HeapAlloc(GetProcessHeap(), 0, out);
|
|
if (!adapters)
|
|
exit(1);
|
|
|
|
if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &out) == ERROR_SUCCESS)
|
|
{
|
|
IP_ADAPTER_ADDRESSES *p;
|
|
|
|
for (p = adapters; p; p = p->Next)
|
|
{
|
|
IP_ADAPTER_UNICAST_ADDRESS *addr;
|
|
WCHAR physaddr_buf[3 * MAX_ADAPTER_ADDRESS_LENGTH];
|
|
IP_ADAPTER_GATEWAY_ADDRESS_LH *gateway;
|
|
WCHAR addr_buf[54];
|
|
|
|
ipconfig_message_printfW(STRING_ADAPTER_FRIENDLY, iftype_to_string(p->IfType), p->FriendlyName);
|
|
ipconfig_printfW(newlineW);
|
|
print_field(STRING_CONN_DNS_SUFFIX, p->DnsSuffix);
|
|
print_field(STRING_DESCRIPTION, p->Description);
|
|
print_field(STRING_PHYS_ADDR, physaddr_to_string(physaddr_buf, p->PhysicalAddress, p->PhysicalAddressLength));
|
|
print_field(STRING_DHCP_ENABLED, boolean_to_string(p->Flags & IP_ADAPTER_DHCP_ENABLED));
|
|
|
|
/* FIXME: Output autoconfiguration status. */
|
|
|
|
for (addr = p->FirstUnicastAddress; addr; addr = addr->Next)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &addr->Address))
|
|
print_field(STRING_IP_ADDRESS, addr_buf);
|
|
/* FIXME: Output corresponding subnet mask. */
|
|
}
|
|
|
|
if (p->FirstGatewayAddress)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &p->FirstGatewayAddress->Address))
|
|
print_field(STRING_DEFAULT_GATEWAY, addr_buf);
|
|
|
|
for (gateway = p->FirstGatewayAddress->Next; gateway; gateway = gateway->Next)
|
|
{
|
|
if (socket_address_to_string(addr_buf, sizeof(addr_buf)/sizeof(WCHAR), &gateway->Address))
|
|
print_value(addr_buf);
|
|
}
|
|
}
|
|
else
|
|
print_field(STRING_DEFAULT_GATEWAY, emptyW);
|
|
|
|
ipconfig_printfW(newlineW);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, adapters);
|
|
}
|
|
}
|
|
|
|
int wmain(int argc, WCHAR *argv[])
|
|
{
|
|
static const WCHAR slashHelp[] = {'/','?',0};
|
|
static const WCHAR slashAll[] = {'/','a','l','l',0};
|
|
|
|
WSADATA data;
|
|
|
|
if (WSAStartup(MAKEWORD(2, 2), &data))
|
|
return 1;
|
|
|
|
if (argc > 1)
|
|
{
|
|
if (!strcmpW(slashHelp, argv[1]))
|
|
{
|
|
ipconfig_message(STRING_USAGE);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
else if (!strcmpiW(slashAll, argv[1]))
|
|
{
|
|
if (argv[2])
|
|
{
|
|
ipconfig_message(STRING_INVALID_CMDLINE);
|
|
ipconfig_message(STRING_USAGE);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
|
|
print_full_information();
|
|
}
|
|
else
|
|
{
|
|
ipconfig_message(STRING_INVALID_CMDLINE);
|
|
ipconfig_message(STRING_USAGE);
|
|
WSACleanup();
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
print_basic_information();
|
|
|
|
WSACleanup();
|
|
return 0;
|
|
}
|