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