/* * PROJECT: ReactOS netstat utility * LICENSE: GPL - See COPYING in the top level directory * FILE: base/applications/network/netstat/netstat.c * PURPOSE: display IP stack statistics * COPYRIGHT: Copyright 2005 Ged Murphy */ /* * TODO: * sort function return values. * implement -b, -o and -v * clean up GetIpHostName * command line parser needs more work */ #define WIN32_NO_STATUS #include #include #include #define _INC_WINDOWS #include #include #include #include #include #include "netstat.h" enum ProtoType {IP, TCP, UDP, ICMP} Protocol; DWORD Interval; /* time to pause between printing output */ /* TCP endpoint states */ TCHAR TcpState[][32] = { _T("???"), _T("CLOSED"), _T("LISTENING"), _T("SYN_SENT"), _T("SYN_RCVD"), _T("ESTABLISHED"), _T("FIN_WAIT1"), _T("FIN_WAIT2"), _T("CLOSE_WAIT"), _T("CLOSING"), _T("LAST_ACK"), _T("TIME_WAIT"), _T("DELETE_TCB") }; /* * format message string and display output */ DWORD DoFormatMessage(DWORD ErrorCode) { LPVOID lpMsgBuf; DWORD RetVal; if ((RetVal = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &lpMsgBuf, 0, NULL ))) { _tprintf(_T("%s"), (LPTSTR)lpMsgBuf); LocalFree(lpMsgBuf); /* return number of TCHAR's stored in output buffer * excluding '\0' - as FormatMessage does*/ return RetVal; } else return 0; } /* * * Parse command line parameters and set any options * */ BOOL ParseCmdline(int argc, char* argv[]) { LPSTR Proto; CHAR c; INT i; if ((argc == 1) || (_istdigit(*argv[1]))) bNoOptions = TRUE; /* Parse command line for options we have been given. */ for (i = 1; i < argc; i++) { if ((argc > 1) && (argv[i][0] == '-' || argv[i][0] == '/')) { while ((c = *++argv[i]) != '\0') { switch (tolower(c)) { case 'a' : bDoShowAllCons = TRUE; break; case 'b' : bDoShowProcName = TRUE; break; case 'e' : bDoShowEthStats = TRUE; break; case 'n' : bDoShowNumbers = TRUE; break; case 'p' : bDoShowProtoCons = TRUE; Proto = argv[i+1]; if (!_stricmp("IP", Proto)) Protocol = IP; else if (!_stricmp("ICMP", Proto)) Protocol = ICMP; else if (!_stricmp("TCP", Proto)) Protocol = TCP; else if (!_stricmp("UDP", Proto)) Protocol = UDP; else { Usage(); return EXIT_FAILURE; } break; case 'r' : bDoShowRouteTable = TRUE; break; case 's' : bDoShowProtoStats = TRUE; break; case 'o' : bDoShowProcessId = TRUE; break; case 'v' : _tprintf(_T("got v\n")); bDoDispSeqComp = TRUE; break; default : Usage(); return EXIT_FAILURE; } } } else if (_istdigit(*argv[i])) { if (_stscanf(argv[i], "%lu", &Interval) != EOF) bLoopOutput = TRUE; else return EXIT_FAILURE; } // else // { // Usage(); // EXIT_FAILURE; // } } return EXIT_SUCCESS; } /* * Display table header */ VOID DisplayTableHeader() { _tprintf(_T("\n Proto Local Address Foreign Address State")); if (bDoShowProcessId) _tprintf(_T(" Process\n")); else _tprintf(_T("\n")); } /* * Simulate Microsofts netstat utility output */ BOOL DisplayOutput() { if (bNoOptions) { DisplayTableHeader(); ShowTcpTable(); return EXIT_SUCCESS; } if (bDoShowRouteTable) { /* mingw doesn't have lib for _tsystem */ if (system("route print") == -1) { _tprintf(_T("cannot find 'route.exe'\n")); return EXIT_FAILURE; } return EXIT_SUCCESS; } if (bDoShowEthStats) { ShowEthernetStatistics(); return EXIT_SUCCESS; } if (bDoShowProtoCons) { switch (Protocol) { case IP : if (bDoShowProtoStats) { ShowIpStatistics(); return EXIT_SUCCESS; } break; case ICMP : if (bDoShowProtoStats) { ShowIcmpStatistics(); return EXIT_SUCCESS; } break; case TCP : if (bDoShowProtoStats) ShowTcpStatistics(); _tprintf(_T("\nActive Connections\n")); DisplayTableHeader(); ShowTcpTable(); break; case UDP : if (bDoShowProtoStats) ShowUdpStatistics(); _tprintf(_T("\nActive Connections\n")); DisplayTableHeader(); ShowUdpTable(); break; default : break; } } else if (bDoShowProtoStats) { ShowIpStatistics(); ShowIcmpStatistics(); ShowTcpStatistics(); ShowUdpStatistics(); return EXIT_SUCCESS; } else { _tprintf(_T("\nActive Connections\n")); DisplayTableHeader(); ShowTcpTable(); if (bDoShowAllCons) ShowUdpTable(); } return EXIT_SUCCESS; } VOID ShowIpStatistics() { PMIB_IPSTATS pIpStats; DWORD dwRetVal; pIpStats = (MIB_IPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPSTATS)); if ((dwRetVal = GetIpStatistics(pIpStats)) == NO_ERROR) { _tprintf(_T("\nIPv4 Statistics\n\n")); _tprintf(_T(" %-34s = %lu\n"), _T("Packets Received"), pIpStats->dwInReceives); _tprintf(_T(" %-34s = %lu\n"), _T("Received Header Errors"), pIpStats->dwInHdrErrors); _tprintf(_T(" %-34s = %lu\n"), _T("Received Address Errors"), pIpStats->dwInAddrErrors); _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Forwarded"), pIpStats->dwForwDatagrams); _tprintf(_T(" %-34s = %lu\n"), _T("Unknown Protocols Received"), pIpStats->dwInUnknownProtos); _tprintf(_T(" %-34s = %lu\n"), _T("Received Packets Discarded"), pIpStats->dwInDiscards); _tprintf(_T(" %-34s = %lu\n"), _T("Received Packets Delivered"), pIpStats->dwInDelivers); _tprintf(_T(" %-34s = %lu\n"), _T("Output Requests"), pIpStats->dwOutRequests); _tprintf(_T(" %-34s = %lu\n"), _T("Routing Discards"), pIpStats->dwRoutingDiscards); _tprintf(_T(" %-34s = %lu\n"), _T("Discarded Output Packets"), pIpStats->dwOutDiscards); _tprintf(_T(" %-34s = %lu\n"), _T("Output Packets No Route"), pIpStats->dwOutNoRoutes); _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Required"), pIpStats->dwReasmReqds); _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Succesful"), pIpStats->dwReasmOks); _tprintf(_T(" %-34s = %lu\n"), _T("Reassembly Failures"), pIpStats->dwReasmFails); // _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams successfully fragmented"), NULL); /* FIXME: what is this one? */ _tprintf(_T(" %-34s = %lu\n"), _T("Datagrams Failing Fragmentation"), pIpStats->dwFragFails); _tprintf(_T(" %-34s = %lu\n"), _T("Fragments Created"), pIpStats->dwFragCreates); } else DoFormatMessage(dwRetVal); HeapFree(GetProcessHeap(), 0, pIpStats); } VOID ShowIcmpStatistics() { PMIB_ICMP pIcmpStats; DWORD dwRetVal; pIcmpStats = (MIB_ICMP*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_ICMP)); if ((dwRetVal = GetIcmpStatistics(pIcmpStats)) == NO_ERROR) { _tprintf(_T("\nICMPv4 Statistics\n\n")); _tprintf(_T(" Received Sent\n")); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Messages"), pIcmpStats->stats.icmpInStats.dwMsgs, pIcmpStats->stats.icmpOutStats.dwMsgs); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Errors"), pIcmpStats->stats.icmpInStats.dwErrors, pIcmpStats->stats.icmpOutStats.dwErrors); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Destination Unreachable"), pIcmpStats->stats.icmpInStats.dwDestUnreachs, pIcmpStats->stats.icmpOutStats.dwDestUnreachs); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Time Exceeded"), pIcmpStats->stats.icmpInStats.dwTimeExcds, pIcmpStats->stats.icmpOutStats.dwTimeExcds); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Parameter Problems"), pIcmpStats->stats.icmpInStats.dwParmProbs, pIcmpStats->stats.icmpOutStats.dwParmProbs); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Source Quenches"), pIcmpStats->stats.icmpInStats.dwSrcQuenchs, pIcmpStats->stats.icmpOutStats.dwSrcQuenchs); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Redirects"), pIcmpStats->stats.icmpInStats.dwRedirects, pIcmpStats->stats.icmpOutStats.dwRedirects); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echos"), pIcmpStats->stats.icmpInStats.dwEchos, pIcmpStats->stats.icmpOutStats.dwEchos); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Echo Replies"), pIcmpStats->stats.icmpInStats.dwEchoReps, pIcmpStats->stats.icmpOutStats.dwEchoReps); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamps"), pIcmpStats->stats.icmpInStats.dwTimestamps, pIcmpStats->stats.icmpOutStats.dwTimestamps); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Timestamp Replies"), pIcmpStats->stats.icmpInStats.dwTimestampReps, pIcmpStats->stats.icmpOutStats.dwTimestampReps); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Masks"), pIcmpStats->stats.icmpInStats.dwAddrMasks, pIcmpStats->stats.icmpOutStats.dwAddrMasks); _tprintf(_T(" %-25s %-11lu %lu\n"), _T("Address Mask Replies"), pIcmpStats->stats.icmpInStats.dwAddrMaskReps, pIcmpStats->stats.icmpOutStats.dwAddrMaskReps); } else DoFormatMessage(dwRetVal); HeapFree(GetProcessHeap(), 0, pIcmpStats); } VOID ShowTcpStatistics() { PMIB_TCPSTATS pTcpStats; DWORD dwRetVal; pTcpStats = (MIB_TCPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_TCPSTATS)); if ((dwRetVal = GetTcpStatistics(pTcpStats)) == NO_ERROR) { _tprintf(_T("\nTCP Statistics for IPv4\n\n")); _tprintf(_T(" %-35s = %lu\n"), _T("Active Opens"), pTcpStats->dwActiveOpens); _tprintf(_T(" %-35s = %lu\n"), _T("Passive Opens"), pTcpStats->dwPassiveOpens); _tprintf(_T(" %-35s = %lu\n"), _T("Failed Connection Attempts"), pTcpStats->dwAttemptFails); _tprintf(_T(" %-35s = %lu\n"), _T("Reset Connections"), pTcpStats->dwEstabResets); _tprintf(_T(" %-35s = %lu\n"), _T("Current Connections"), pTcpStats->dwCurrEstab); _tprintf(_T(" %-35s = %lu\n"), _T("Segments Received"), pTcpStats->dwInSegs); _tprintf(_T(" %-35s = %lu\n"), _T("Segments Sent"), pTcpStats->dwOutSegs); _tprintf(_T(" %-35s = %lu\n"), _T("Segments Retransmitted"), pTcpStats->dwRetransSegs); } else DoFormatMessage(dwRetVal); HeapFree(GetProcessHeap(), 0, pTcpStats); } VOID ShowUdpStatistics() { PMIB_UDPSTATS pUdpStats; DWORD dwRetVal; pUdpStats = (MIB_UDPSTATS*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_UDPSTATS)); if ((dwRetVal = GetUdpStatistics(pUdpStats)) == NO_ERROR) { _tprintf(_T("\nUDP Statistics for IPv4\n\n")); _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Received"), pUdpStats->dwInDatagrams); _tprintf(_T(" %-21s = %lu\n"), _T("No Ports"), pUdpStats->dwNoPorts); _tprintf(_T(" %-21s = %lu\n"), _T("Receive Errors"), pUdpStats->dwInErrors); _tprintf(_T(" %-21s = %lu\n"), _T("Datagrams Sent"), pUdpStats->dwOutDatagrams); } else DoFormatMessage(dwRetVal); HeapFree(GetProcessHeap(), 0, pUdpStats); } VOID ShowEthernetStatistics() { PMIB_IFTABLE pIfTable; DWORD dwSize = 0; DWORD dwRetVal = 0; pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IFTABLE)); if (GetIfTable(pIfTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { HeapFree(GetProcessHeap(), 0, pIfTable); pIfTable = (MIB_IFTABLE*) HeapAlloc(GetProcessHeap(), 0, dwSize); if ((dwRetVal = GetIfTable(pIfTable, &dwSize, 0)) == NO_ERROR) { _tprintf(_T("Interface Statistics\n\n")); _tprintf(_T(" Received Sent\n\n")); _tprintf(_T("%-20s %14lu %15lu\n"), _T("Bytes"), pIfTable->table[0].dwInOctets, pIfTable->table[0].dwOutOctets); _tprintf(_T("%-20s %14lu %15lu\n"), _T("Unicast packets"), pIfTable->table[0].dwInUcastPkts, pIfTable->table[0].dwOutUcastPkts); _tprintf(_T("%-20s %14lu %15lu\n"), _T("Non-unicast packets"), pIfTable->table[0].dwInNUcastPkts, pIfTable->table[0].dwOutNUcastPkts); _tprintf(_T("%-20s %14lu %15lu\n"), _T("Discards"), pIfTable->table[0].dwInDiscards, pIfTable->table[0].dwOutDiscards); _tprintf(_T("%-20s %14lu %15lu\n"), _T("Errors"), pIfTable->table[0].dwInErrors, pIfTable->table[0].dwOutErrors); _tprintf(_T("%-20s %14lu\n"), _T("Unknown Protocols"), pIfTable->table[0].dwInUnknownProtos); } else DoFormatMessage(dwRetVal); } HeapFree(GetProcessHeap(), 0, pIfTable); } VOID ShowTcpTable() { PMIB_TCPTABLE_OWNER_PID tcpTable; DWORD error, dwSize; DWORD i; CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN]; CHAR RemoteIp[HOSTNAMELEN], RemotePort[PORTNAMELEN]; CHAR Host[ADDRESSLEN]; CHAR Remote[ADDRESSLEN]; CHAR PID[64]; /* Get the table of TCP endpoints */ dwSize = sizeof (MIB_TCPTABLE_OWNER_PID); /* Should also work when we get new connections between 2 GetTcpTable() * calls: */ do { tcpTable = (PMIB_TCPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize); error = GetExtendedTcpTable(tcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0); if ( error != NO_ERROR ) HeapFree(GetProcessHeap(), 0, tcpTable); } while ( error == ERROR_INSUFFICIENT_BUFFER ); if (error != NO_ERROR) { printf("Failed to snapshot TCP endpoints.\n"); DoFormatMessage(error); exit(EXIT_FAILURE); } /* Dump the TCP table */ for (i = 0; i < tcpTable->dwNumEntries; i++) { /* If we aren't showing all connections, only display established, close wait * and time wait. This is the default output for netstat */ if (bDoShowAllCons || (tcpTable->table[i].dwState == MIB_TCP_STATE_ESTAB) || (tcpTable->table[i].dwState == MIB_TCP_STATE_CLOSE_WAIT) || (tcpTable->table[i].dwState == MIB_TCP_STATE_TIME_WAIT)) { /* I've split this up so it's easier to follow */ GetIpHostName(TRUE, tcpTable->table[i].dwLocalAddr, HostIp, HOSTNAMELEN); GetPortName(tcpTable->table[i].dwLocalPort, "tcp", HostPort, PORTNAMELEN); sprintf(Host, "%s:%s", HostIp, HostPort); if (tcpTable->table[i].dwState == MIB_TCP_STATE_LISTEN) { sprintf(Remote, "%s:0", HostIp); } else { GetIpHostName(FALSE, tcpTable->table[i].dwRemoteAddr, RemoteIp, HOSTNAMELEN); GetPortName(tcpTable->table[i].dwRemotePort, "tcp", RemotePort, PORTNAMELEN); sprintf(Remote, "%s:%s", RemoteIp, RemotePort); } if (bDoShowProcessId) { sprintf(PID, "%ld", tcpTable->table[i].dwOwningPid); } else { PID[0] = 0; } _tprintf(_T(" %-6s %-22s %-22s %-11s %s\n"), _T("TCP"), Host, Remote, TcpState[tcpTable->table[i].dwState], PID); } } HeapFree(GetProcessHeap(), 0, tcpTable); } VOID ShowUdpTable() { PMIB_UDPTABLE_OWNER_PID udpTable; DWORD error, dwSize; DWORD i; CHAR HostIp[HOSTNAMELEN], HostPort[PORTNAMELEN]; CHAR Host[ADDRESSLEN]; CHAR PID[64]; /* Get the table of UDP endpoints */ dwSize = 0; error = GetExtendedUdpTable(NULL, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0); if (error != ERROR_INSUFFICIENT_BUFFER) { printf("Failed to snapshot UDP endpoints.\n"); DoFormatMessage(error); exit(EXIT_FAILURE); } udpTable = (PMIB_UDPTABLE_OWNER_PID) HeapAlloc(GetProcessHeap(), 0, dwSize); error = GetExtendedUdpTable(udpTable, &dwSize, TRUE, AF_INET, UDP_TABLE_OWNER_PID, 0); if (error) { printf("Failed to snapshot UDP endpoints table.\n"); DoFormatMessage(error); HeapFree(GetProcessHeap(), 0, udpTable); exit(EXIT_FAILURE); } /* Dump the UDP table */ for (i = 0; i < udpTable->dwNumEntries; i++) { /* I've split this up so it's easier to follow */ GetIpHostName(TRUE, udpTable->table[i].dwLocalAddr, HostIp, HOSTNAMELEN); GetPortName(udpTable->table[i].dwLocalPort, "tcp", HostPort, PORTNAMELEN); sprintf(Host, "%s:%s", HostIp, HostPort); if (bDoShowProcessId) { sprintf(PID, "%ld", udpTable->table[i].dwOwningPid); } else { PID[0] = 0; } _tprintf(_T(" %-6s %-22s %-34s %s\n"), _T("UDP"), Host, _T("*:*"), PID); } HeapFree(GetProcessHeap(), 0, udpTable); } /* * Translate port numbers into their text equivalent if there is one */ PCHAR GetPortName(UINT Port, PCSTR Proto, CHAR Name[], INT NameLen) { struct servent *pServent; if (bDoShowNumbers) { sprintf(Name, "%d", htons((WORD)Port)); return Name; } /* Try to translate to a name */ if ((pServent = getservbyport(Port, Proto))) strcpy(Name, pServent->s_name ); else sprintf(Name, "%d", htons((WORD)Port)); return Name; } /* * convert addresses into dotted decimal or hostname */ PCHAR GetIpHostName(BOOL Local, UINT IpAddr, CHAR Name[], int NameLen) { // struct hostent *phostent; UINT nIpAddr; /* display dotted decimal */ nIpAddr = htonl(IpAddr); if (bDoShowNumbers) { sprintf(Name, "%d.%d.%d.%d", (nIpAddr >> 24) & 0xFF, (nIpAddr >> 16) & 0xFF, (nIpAddr >> 8) & 0xFF, (nIpAddr) & 0xFF); return Name; } Name[0] = _T('\0'); /* try to resolve the name */ if (!IpAddr) { if (!Local) { sprintf(Name, "%d.%d.%d.%d", (nIpAddr >> 24) & 0xFF, (nIpAddr >> 16) & 0xFF, (nIpAddr >> 8) & 0xFF, (nIpAddr) & 0xFF); } else { if (gethostname(Name, NameLen) != 0) DoFormatMessage(WSAGetLastError()); } } else if (IpAddr == 0x0100007f) { if (Local) { if (gethostname(Name, NameLen) != 0) DoFormatMessage(WSAGetLastError()); } else { _tcsncpy(Name, _T("localhost"), 10); } // } else if (phostent = gethostbyaddr((char*)&ipaddr, sizeof(nipaddr), PF_INET)) { // strcpy(name, phostent->h_name); } else { sprintf(Name, "%d.%d.%d.%d", ((nIpAddr >> 24) & 0x000000FF), ((nIpAddr >> 16) & 0x000000FF), ((nIpAddr >> 8) & 0x000000FF), ((nIpAddr) & 0x000000FF)); } return Name; } VOID Usage() { _tprintf(_T("\nDisplays current TCP/IP protocol statistics and network connections.\n\n" "NETSTAT [-a] [-e] [-n] [-p proto] [-r] [-s] [interval]\n\n" " -a Displays all connections and listening ports.\n" " -e Displays Ethernet statistics. May be combined with -s\n" " option\n" " -n Displays address and port numbers in numeric form.\n" " -p proto Shows connections for protocol 'proto' TCP or UDP.\n" " If used with the -s option to display\n" " per-protocol statistics, 'proto' may be TCP, UDP, or IP.\n" " -r Displays the current routing table.\n" " -s Displays per-protocol statistics. By default, Statistics are\n" " shown for IP, ICMP, TCP and UDP;\n" " the -p option may be used to specify a subset of the default.\n" " -o Displays the process ID for each connection.\n" " interval Redisplays selected statistics every 'interval' seconds.\n" " Press CTRL+C to stop redisplaying. By default netstat will\n" " print the current information only once.\n")); } /* * * Parse command line parameters and set any options * Run display output, looping over set intervals if a number is given * */ int main(int argc, char *argv[]) { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { _tprintf(_T("WSAStartup() failed : %d\n"), WSAGetLastError()); return -1; } if (ParseCmdline(argc, argv)) return -1; if (bLoopOutput) { while (1) { if (DisplayOutput()) return -1; Sleep(Interval*1000); } } if (DisplayOutput()) return -1; else return 0; }