1 /* 2 * ReactOS Win32 Applications 3 * Copyright (C) 2005 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 */ 19 20 /* 21 * COPYRIGHT: See COPYING in the top level directory 22 * PROJECT: ReactOS arp utility 23 * FILE: base/applications/network/arp/arp.c 24 * PURPOSE: view and manipulate the ARP cache 25 * PROGRAMMERS: Ged Murphy (gedmurphy@gmail.com) 26 * REVISIONS: 27 * GM 27/06/05 Created 28 * 29 */ 30 31 #define WIN32_NO_STATUS 32 #include <stdarg.h> 33 #include <windef.h> 34 #include <winbase.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <tchar.h> 38 #define _INC_WINDOWS 39 #include <winsock2.h> 40 #include <iphlpapi.h> 41 42 #include <arp_msg.h> 43 44 /* 45 * Globals 46 */ 47 const char SEPARATOR = '-'; 48 int _CRT_glob = 0; // stop * from listing dir files in arp -d * 49 50 /* 51 * function declarations 52 */ 53 DWORD DoFormatMessage(VOID); 54 DWORD PrintEntries(PMIB_IPNETROW pIpAddRow); 55 DWORD DisplayArpEntries(PTCHAR pszInetAddr, PTCHAR pszIfAddr); 56 DWORD Addhost(PTCHAR pszInetAddr, PTCHAR pszEthAddr, PTCHAR pszIfAddr); 57 DWORD Deletehost(PTCHAR pszInetAddr, PTCHAR pszIfAddr); 58 VOID Usage(VOID); 59 60 /* 61 * convert error code into meaningful message 62 */ 63 DWORD DoFormatMessage(VOID) 64 { 65 LPTSTR lpMsgBuf; 66 DWORD RetVal; 67 68 DWORD ErrorCode = GetLastError(); 69 70 if (ErrorCode != ERROR_SUCCESS) 71 { 72 RetVal = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 73 FORMAT_MESSAGE_FROM_SYSTEM | 74 FORMAT_MESSAGE_IGNORE_INSERTS, 75 NULL, 76 ErrorCode, 77 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ 78 (LPTSTR) &lpMsgBuf, 79 0, 80 NULL ); 81 82 if (RetVal != 0) 83 { 84 _tprintf(_T("%s"), lpMsgBuf); 85 LocalFree(lpMsgBuf); 86 /* return number of TCHAR's stored in output buffer 87 * excluding '\0' - as FormatMessage does*/ 88 return RetVal; 89 } 90 } 91 return 0; 92 } 93 94 VOID 95 PrintMessage( 96 DWORD dwMessage) 97 { 98 LPTSTR lpMsgBuf; 99 DWORD RetVal; 100 101 RetVal = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 102 FORMAT_MESSAGE_FROM_HMODULE | 103 FORMAT_MESSAGE_IGNORE_INSERTS, 104 GetModuleHandleW(NULL), 105 dwMessage, 106 LANG_USER_DEFAULT, 107 (LPTSTR)&lpMsgBuf, 108 0, 109 NULL); 110 if (RetVal != 0) 111 { 112 _tprintf(_T("%s"), lpMsgBuf); 113 LocalFree(lpMsgBuf); 114 } 115 } 116 117 VOID 118 PrintMessageV( 119 DWORD dwMessage, 120 ...) 121 { 122 LPTSTR lpMsgBuf; 123 va_list args = NULL; 124 DWORD RetVal; 125 126 va_start(args, dwMessage); 127 128 RetVal = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE, 129 GetModuleHandleW(NULL), 130 dwMessage, 131 LANG_USER_DEFAULT, 132 (LPTSTR)&lpMsgBuf, 133 0, 134 &args); 135 va_end(args); 136 137 if (RetVal != 0) 138 { 139 _tprintf(_T("%s"), lpMsgBuf); 140 LocalFree(lpMsgBuf); 141 } 142 } 143 144 /* 145 * 146 * Takes an ARP entry and prints the IP address, 147 * the MAC address and the entry type to screen 148 * 149 */ 150 DWORD PrintEntries(PMIB_IPNETROW pIpAddRow) 151 { 152 IN_ADDR inaddr; 153 TCHAR cMacAddr[20]; 154 155 /* print IP addresses */ 156 inaddr.S_un.S_addr = pIpAddRow->dwAddr; 157 _tprintf(_T(" %-22s"), inet_ntoa(inaddr)); 158 159 /* print MAC address */ 160 _stprintf(cMacAddr, _T("%02x-%02x-%02x-%02x-%02x-%02x"), 161 pIpAddRow->bPhysAddr[0], 162 pIpAddRow->bPhysAddr[1], 163 pIpAddRow->bPhysAddr[2], 164 pIpAddRow->bPhysAddr[3], 165 pIpAddRow->bPhysAddr[4], 166 pIpAddRow->bPhysAddr[5]); 167 _tprintf(_T("%-22s"), cMacAddr); 168 169 /* print cache type */ 170 switch (pIpAddRow->dwType) 171 { 172 case MIB_IPNET_TYPE_DYNAMIC: 173 PrintMessage(MSG_ARP_DYNAMIC); 174 break; 175 176 case MIB_IPNET_TYPE_STATIC: 177 PrintMessage(MSG_ARP_STATIC); 178 break; 179 180 case MIB_IPNET_TYPE_INVALID: 181 PrintMessage(MSG_ARP_INVALID); 182 break; 183 184 case MIB_IPNET_TYPE_OTHER: 185 PrintMessage(MSG_ARP_OTHER); 186 break; 187 } 188 _putts(_T("")); 189 return NO_ERROR; 190 } 191 192 /* 193 * 194 * Takes optional parameters of an internet address and interface address. 195 * Retrieve all entries in the ARP cache. If an internet address is 196 * specified, display the ARP entry relating to that address. If an 197 * interface address is specified, display all entries relating to 198 * that interface. 199 * 200 */ 201 /* FIXME: allow user to specify an interface address, via pszIfAddr */ 202 DWORD DisplayArpEntries(PTCHAR pszInetAddr, PTCHAR pszIfAddr) 203 { 204 DWORD i, k, dwCount; 205 PMIB_IPNETTABLE pIpNetTable = NULL; 206 PMIB_IPADDRTABLE pIpAddrTable = NULL; 207 ULONG Size = 0; 208 struct in_addr inaddr, inaddr2; 209 PTCHAR pszIpAddr; 210 TCHAR szIntIpAddr[20]; 211 DWORD dwError = NO_ERROR; 212 213 /* retrieve the IP-to-physical address mapping table */ 214 215 /* get table size */ 216 GetIpNetTable(pIpNetTable, &Size, 0); 217 218 /* allocate memory for ARP address table */ 219 pIpNetTable = (PMIB_IPNETTABLE)HeapAlloc(GetProcessHeap(), 0, Size); 220 if (pIpNetTable == NULL) 221 { 222 PrintMessage(MSG_ARP_NO_MEMORY); 223 dwError = ERROR_NOT_ENOUGH_MEMORY; 224 goto cleanup; 225 } 226 227 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 228 229 dwError = GetIpNetTable(pIpNetTable, &Size, TRUE); 230 if (dwError != NO_ERROR) 231 { 232 _tprintf(_T("GetIpNetTable failed: %lu\n"), dwError); 233 DoFormatMessage(); 234 goto cleanup; 235 } 236 237 /* check there are entries in the table */ 238 if (pIpNetTable->dwNumEntries == 0) 239 { 240 PrintMessage(MSG_ARP_NO_ENTRIES); 241 goto cleanup; 242 } 243 244 /* Retrieve the interface-to-ip address mapping 245 * table to get the IP address for adapter */ 246 247 /* get table size */ 248 Size = 0; 249 GetIpAddrTable(pIpAddrTable, &Size, 0); 250 251 pIpAddrTable = (PMIB_IPADDRTABLE)HeapAlloc(GetProcessHeap(), 0, Size); 252 if (pIpAddrTable == NULL) 253 { 254 PrintMessage(MSG_ARP_NO_MEMORY); 255 dwError = ERROR_NOT_ENOUGH_MEMORY; 256 goto cleanup; 257 } 258 259 ZeroMemory(pIpAddrTable, sizeof(*pIpAddrTable)); 260 261 dwError = GetIpAddrTable(pIpAddrTable, &Size, TRUE); 262 if (dwError != NO_ERROR) 263 { 264 _tprintf(_T("GetIpAddrTable failed: %lu\n"), dwError); 265 DoFormatMessage(); 266 goto cleanup; 267 } 268 269 for (k = 0; k < pIpAddrTable->dwNumEntries; k++) 270 { 271 if (pIpNetTable->table[0].dwIndex == pIpAddrTable->table[k].dwIndex) 272 { 273 //printf("debug print: pIpAddrTable->table[?].dwIndex = %lx\n", pIpNetTable->table[k].dwIndex); 274 inaddr2.s_addr = pIpAddrTable->table[k].dwAddr; 275 pszIpAddr = inet_ntoa(inaddr2); 276 strcpy(szIntIpAddr, pszIpAddr); 277 } 278 } 279 280 /* Count relevant ARP entries */ 281 dwCount = 0; 282 for (i = 0; i < pIpNetTable->dwNumEntries; i++) 283 { 284 /* if the user has supplied their own internet address * 285 * only count the arp entry which matches that */ 286 if (pszInetAddr) 287 { 288 inaddr.S_un.S_addr = pIpNetTable->table[i].dwAddr; 289 pszIpAddr = inet_ntoa(inaddr); 290 291 /* check if it matches, count it */ 292 if (strcmp(pszIpAddr, pszInetAddr) == 0) 293 dwCount++; 294 } 295 else 296 { 297 /* if an address is not supplied, count all entries */ 298 dwCount++; 299 } 300 } 301 302 /* Print message and leave if there are no relevant ARP entries */ 303 if (dwCount == 0) 304 { 305 PrintMessage(MSG_ARP_NO_ENTRIES); 306 goto cleanup; 307 } 308 309 /* print header, including interface IP address and index number */ 310 PrintMessageV(MSG_ARP_INTERFACE, szIntIpAddr, pIpNetTable->table[0].dwIndex); 311 312 /* go through all ARP entries */ 313 for (i = 0; i < pIpNetTable->dwNumEntries; i++) 314 { 315 316 /* if the user has supplied their own internet address * 317 * only print the arp entry which matches that */ 318 if (pszInetAddr) 319 { 320 inaddr.S_un.S_addr = pIpNetTable->table[i].dwAddr; 321 pszIpAddr = inet_ntoa(inaddr); 322 323 /* check if it matches, print it */ 324 if (strcmp(pszIpAddr, pszInetAddr) == 0) 325 PrintEntries(&pIpNetTable->table[i]); 326 } 327 else 328 /* if an address is not supplied, print all entries */ 329 PrintEntries(&pIpNetTable->table[i]); 330 } 331 332 cleanup: 333 if (pIpNetTable != NULL) 334 HeapFree(GetProcessHeap(), 0, pIpNetTable); 335 if (pIpAddrTable != NULL) 336 HeapFree(GetProcessHeap(), 0, pIpAddrTable); 337 338 return dwError; 339 } 340 341 /* 342 * 343 * Takes an internet address, a MAC address and an optional interface 344 * address as arguments and checks their validity. 345 * Fill out an MIB_IPNETROW structure and insert the data into the 346 * ARP cache as a static entry. 347 * 348 */ 349 DWORD Addhost(PTCHAR pszInetAddr, PTCHAR pszEthAddr, PTCHAR pszIfAddr) 350 { 351 PMIB_IPNETROW pAddHost = NULL; 352 PMIB_IPNETTABLE pIpNetTable = NULL; 353 DWORD dwIpAddr = 0; 354 ULONG Size = 0; 355 INT i, val, c; 356 DWORD dwError = NO_ERROR; 357 358 /* error checking */ 359 360 /* check IP address */ 361 if (pszInetAddr == NULL) 362 { 363 Usage(); 364 return ERROR_INVALID_PARAMETER; 365 } 366 367 dwIpAddr = inet_addr(pszInetAddr); 368 if (dwIpAddr == INADDR_NONE) 369 { 370 PrintMessageV(MSG_ARP_BAD_IP_ADDRESS, pszInetAddr); 371 return ERROR_INVALID_PARAMETER; 372 } 373 374 /* check MAC address */ 375 if (strlen(pszEthAddr) != 17) 376 { 377 PrintMessageV(MSG_ARP_BAD_ARGUMENT, pszEthAddr); 378 return ERROR_INVALID_PARAMETER; 379 } 380 381 for (i = 0; i < 17; i++) 382 { 383 if (pszEthAddr[i] == SEPARATOR) 384 continue; 385 386 if (!isxdigit(pszEthAddr[i])) 387 { 388 PrintMessageV(MSG_ARP_BAD_ARGUMENT, pszEthAddr); 389 return ERROR_INVALID_PARAMETER; 390 } 391 } 392 393 /* We need the IpNetTable to get the adapter index */ 394 /* Return required buffer size */ 395 GetIpNetTable(pIpNetTable, &Size, 0); 396 397 /* allocate memory for ARP address table */ 398 pIpNetTable = (PMIB_IPNETTABLE)HeapAlloc(GetProcessHeap(), 0, Size); 399 if (pIpNetTable == NULL) 400 { 401 PrintMessage(MSG_ARP_NO_MEMORY); 402 dwError = ERROR_NOT_ENOUGH_MEMORY; 403 goto cleanup; 404 } 405 406 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 407 408 dwError = GetIpNetTable(pIpNetTable, &Size, TRUE); 409 if (dwError != NO_ERROR) 410 { 411 _tprintf(_T("GetIpNetTable failed: %lu\n"), dwError); 412 DoFormatMessage(); 413 goto cleanup; 414 } 415 416 /* reserve memory on heap and zero */ 417 pAddHost = (PMIB_IPNETROW)HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW)); 418 if (pAddHost == NULL) 419 { 420 PrintMessage(MSG_ARP_NO_MEMORY); 421 dwError = ERROR_NOT_ENOUGH_MEMORY; 422 goto cleanup; 423 } 424 425 ZeroMemory(pAddHost, sizeof(MIB_IPNETROW)); 426 427 /* set dwIndex field to the index of a local IP address to 428 * indicate the network on which the ARP entry applies */ 429 if (pszIfAddr) 430 { 431 if (sscanf(pszIfAddr, "%lx", &pAddHost->dwIndex) == EOF) 432 { 433 goto cleanup; 434 } 435 } 436 else 437 { 438 //printf("debug print: pIpNetTable->table[0].dwIndex = %lx\n", pIpNetTable->table[0].dwIndex); 439 /* needs testing. I get the correct index on my machine, but need others 440 * to test their card index. Any problems and we can use GetAdaptersInfo instead */ 441 pAddHost->dwIndex = pIpNetTable->table[0].dwIndex; 442 } 443 444 /* Set MAC address to 6 bytes (typical) */ 445 pAddHost->dwPhysAddrLen = 6; 446 447 448 /* Encode bPhysAddr into correct byte array */ 449 for (i = 0; i < 6; i++) 450 { 451 val = 0; 452 c = toupper(pszEthAddr[i * 3]); 453 c = c - (isdigit(c) ? '0' : ('A' - 10)); 454 val += c; 455 val = (val << 4); 456 c = toupper(pszEthAddr[i * 3 + 1]); 457 c = c - (isdigit(c) ? '0' : ('A' - 10)); 458 val += c; 459 pAddHost->bPhysAddr[i] = (BYTE)val; 460 } 461 462 /* copy converted IP address */ 463 pAddHost->dwAddr = dwIpAddr; 464 465 466 /* set type to static */ 467 pAddHost->dwType = MIB_IPNET_TYPE_STATIC; 468 469 470 /* Add the ARP entry */ 471 dwError = SetIpNetEntry(pAddHost); 472 if (dwError != NO_ERROR) 473 { 474 DoFormatMessage(); 475 goto cleanup; 476 } 477 478 cleanup: 479 if (pIpNetTable != NULL) 480 HeapFree(GetProcessHeap(), 0, pIpNetTable); 481 if (pAddHost != NULL) 482 HeapFree(GetProcessHeap(), 0, pAddHost); 483 484 return dwError; 485 } 486 487 /* 488 * 489 * Takes an internet address and an optional interface address as 490 * arguments and checks their validity. 491 * Add the interface number and IP to an MIB_IPNETROW structure 492 * and remove the entry from the ARP cache. 493 * 494 */ 495 DWORD Deletehost(PTCHAR pszInetAddr, PTCHAR pszIfAddr) 496 { 497 PMIB_IPNETROW pDelHost = NULL; 498 PMIB_IPNETTABLE pIpNetTable = NULL; 499 ULONG Size = 0; 500 DWORD dwIpAddr = 0; 501 BOOL bFlushTable = FALSE; 502 DWORD dwError = NO_ERROR; 503 504 /* error checking */ 505 506 /* check IP address */ 507 if (pszInetAddr == NULL) 508 { 509 Usage(); 510 return ERROR_INVALID_PARAMETER; 511 } 512 513 /* if wildcard is given, set flag to delete all hosts */ 514 if (strncmp(pszInetAddr, "*", 1) == 0) 515 { 516 bFlushTable = TRUE; 517 } 518 else 519 { 520 dwIpAddr = inet_addr(pszInetAddr); 521 if (dwIpAddr == INADDR_NONE) 522 { 523 PrintMessageV(MSG_ARP_BAD_IP_ADDRESS, pszInetAddr); 524 return ERROR_INVALID_PARAMETER; 525 } 526 } 527 528 /* We need the IpNetTable to get the adapter index */ 529 /* Return required buffer size */ 530 GetIpNetTable(NULL, &Size, 0); 531 532 /* allocate memory for ARP address table */ 533 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size); 534 if (pIpNetTable == NULL) 535 { 536 PrintMessage(MSG_ARP_NO_MEMORY); 537 dwError = ERROR_NOT_ENOUGH_MEMORY; 538 goto cleanup; 539 } 540 541 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 542 543 dwError = GetIpNetTable(pIpNetTable, &Size, TRUE); 544 if (dwError != NO_ERROR) 545 { 546 _tprintf(_T("GetIpNetTable failed: %lu\n"), dwError); 547 DoFormatMessage(); 548 goto cleanup; 549 } 550 551 /* reserve memory on heap and zero */ 552 pDelHost = (MIB_IPNETROW *)HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW)); 553 if (pDelHost == NULL) 554 { 555 PrintMessage(MSG_ARP_NO_MEMORY); 556 dwError = ERROR_NOT_ENOUGH_MEMORY; 557 goto cleanup; 558 } 559 560 ZeroMemory(pDelHost, sizeof(MIB_IPNETROW)); 561 562 /* set dwIndex field to the index of a local IP address to 563 * indicate the network on which the ARP entry applies */ 564 if (pszIfAddr) 565 { 566 if (sscanf(pszIfAddr, "%lx", &pDelHost->dwIndex) == EOF) 567 { 568 goto cleanup; 569 } 570 } 571 else 572 { 573 /* needs testing. I get the correct index on my machine, but need others 574 * to test their card index. Any problems and we can use GetAdaptersInfo instead */ 575 pDelHost->dwIndex = pIpNetTable->table[0].dwIndex; 576 } 577 578 if (bFlushTable != FALSE) 579 { 580 /* delete arp cache */ 581 dwError = FlushIpNetTable(pDelHost->dwIndex); 582 if (dwError != NO_ERROR) 583 { 584 DoFormatMessage(); 585 goto cleanup; 586 } 587 } 588 else 589 { 590 /* copy converted IP address */ 591 pDelHost->dwAddr = dwIpAddr; 592 593 /* Delete the ARP entry */ 594 dwError = DeleteIpNetEntry(pDelHost); 595 if (dwError != NO_ERROR) 596 { 597 DoFormatMessage(); 598 goto cleanup; 599 } 600 } 601 602 cleanup: 603 if (pIpNetTable != NULL) 604 HeapFree(GetProcessHeap(), 0, pIpNetTable); 605 if (pDelHost != NULL) 606 HeapFree(GetProcessHeap(), 0, pDelHost); 607 608 return dwError; 609 } 610 611 /* 612 * 613 * print program usage to screen 614 * 615 */ 616 VOID Usage(VOID) 617 { 618 PrintMessage(MSG_ARP_SYNTAX); 619 } 620 621 /* 622 * 623 * Program entry. 624 * Parse command line and call the required function 625 * 626 */ 627 INT main(int argc, char* argv[]) 628 { 629 DWORD dwError = NO_ERROR; 630 631 if ((argc < 2) || (argc > 5)) 632 { 633 Usage(); 634 return EXIT_FAILURE; 635 } 636 637 if (argv[1][0] != '-') 638 { 639 Usage(); 640 return EXIT_SUCCESS; 641 } 642 643 switch (argv[1][1]) 644 { 645 case 'a': /* fall through */ 646 case 'g': 647 if (argc == 2) 648 dwError = DisplayArpEntries(NULL, NULL); 649 else if (argc == 3) 650 dwError = DisplayArpEntries(argv[2], NULL); 651 else if ((argc == 4) && ((strcmp(argv[2], "-N")) == 0)) 652 dwError = DisplayArpEntries(NULL, argv[3]); 653 else if ((argc == 5) && ((strcmp(argv[3], "-N")) == 0)) 654 dwError = DisplayArpEntries(argv[2], argv[4]); 655 else 656 { 657 Usage(); 658 dwError = ERROR_INVALID_PARAMETER; 659 } 660 break; 661 662 case 'd': 663 if (argc == 3) 664 dwError = Deletehost(argv[2], NULL); 665 else if (argc == 4) 666 dwError = Deletehost(argv[2], argv[3]); 667 else 668 { 669 Usage(); 670 dwError = ERROR_INVALID_PARAMETER; 671 } 672 break; 673 674 case 's': 675 if (argc == 4) 676 dwError = Addhost(argv[2], argv[3], NULL); 677 else if (argc == 5) 678 dwError = Addhost(argv[2], argv[3], argv[4]); 679 else 680 { 681 Usage(); 682 dwError = ERROR_INVALID_PARAMETER; 683 } 684 break; 685 686 default: 687 Usage(); 688 dwError = ERROR_INVALID_PARAMETER; 689 break; 690 } 691 692 return (dwError == NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE; 693 } 694