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 INT PrintEntries(PMIB_IPNETROW pIpAddRow); 53 INT DisplayArpEntries(PTCHAR pszInetAddr, PTCHAR pszIfAddr); 54 INT Addhost(PTCHAR pszInetAddr, PTCHAR pszEthAddr, PTCHAR pszIfAddr); 55 INT 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 LPVOID 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"), (LPTSTR)lpMsgBuf); 83 84 LocalFree(lpMsgBuf); 85 /* return number of TCHAR's stored in output buffer 86 * excluding '\0' - as FormatMessage does*/ 87 return RetVal; 88 } 89 } 90 return 0; 91 } 92 93 /* 94 * 95 * Takes an ARP entry and prints the IP address, 96 * the MAC address and the entry type to screen 97 * 98 */ 99 INT PrintEntries(PMIB_IPNETROW pIpAddRow) 100 { 101 IN_ADDR inaddr; 102 TCHAR cMacAddr[20]; 103 104 /* print IP addresses */ 105 inaddr.S_un.S_addr = pIpAddRow->dwAddr; 106 _tprintf(_T(" %-22s"), inet_ntoa(inaddr)); 107 108 /* print MAC address */ 109 _stprintf(cMacAddr, _T("%02x-%02x-%02x-%02x-%02x-%02x"), 110 pIpAddRow->bPhysAddr[0], 111 pIpAddRow->bPhysAddr[1], 112 pIpAddRow->bPhysAddr[2], 113 pIpAddRow->bPhysAddr[3], 114 pIpAddRow->bPhysAddr[4], 115 pIpAddRow->bPhysAddr[5]); 116 _tprintf(_T("%-22s"), cMacAddr); 117 118 /* print cache type */ 119 switch (pIpAddRow->dwType) 120 { 121 case MIB_IPNET_TYPE_DYNAMIC : _tprintf(_T("dynamic\n")); 122 break; 123 case MIB_IPNET_TYPE_STATIC : _tprintf(_T("static\n")); 124 break; 125 case MIB_IPNET_TYPE_INVALID : _tprintf(_T("invalid\n")); 126 break; 127 case MIB_IPNET_TYPE_OTHER : _tprintf(_T("other\n")); 128 break; 129 } 130 return EXIT_SUCCESS; 131 } 132 133 /* 134 * 135 * Takes optional parameters of an internet address and interface address. 136 * Retrieve all entries in the ARP cache. If an internet address is 137 * specified, display the ARP entry relating to that address. If an 138 * interface address is specified, display all entries relating to 139 * that interface. 140 * 141 */ 142 /* FIXME: allow user to specify an interface address, via pszIfAddr */ 143 INT DisplayArpEntries(PTCHAR pszInetAddr, PTCHAR pszIfAddr) 144 { 145 INT iRet; 146 UINT i, k; 147 PMIB_IPNETTABLE pIpNetTable = NULL; 148 PMIB_IPADDRTABLE pIpAddrTable = NULL; 149 ULONG Size = 0; 150 struct in_addr inaddr, inaddr2; 151 PTCHAR pszIpAddr; 152 TCHAR szIntIpAddr[20]; 153 154 /* retrieve the IP-to-physical address mapping table */ 155 156 /* get table size */ 157 GetIpNetTable(pIpNetTable, &Size, 0); 158 159 /* allocate memory for ARP address table */ 160 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size); 161 if (pIpNetTable == NULL) 162 goto cleanup; 163 164 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 165 166 if (GetIpNetTable(pIpNetTable, &Size, TRUE) != NO_ERROR) 167 { 168 _tprintf(_T("failed to allocate memory for GetIpNetTable\n")); 169 DoFormatMessage(); 170 goto cleanup; 171 } 172 173 /* check there are entries in the table */ 174 if (pIpNetTable->dwNumEntries == 0) 175 { 176 _tprintf(_T("No ARP entires found\n")); 177 goto cleanup; 178 } 179 180 181 182 /* Retrieve the interface-to-ip address mapping 183 * table to get the IP address for adapter */ 184 185 /* get table size */ 186 Size = 0; 187 GetIpAddrTable(pIpAddrTable, &Size, 0); 188 189 pIpAddrTable = (MIB_IPADDRTABLE *) HeapAlloc(GetProcessHeap(), 0, Size); 190 if (pIpAddrTable == NULL) 191 goto cleanup; 192 193 ZeroMemory(pIpAddrTable, sizeof(*pIpAddrTable)); 194 195 if ((iRet = GetIpAddrTable(pIpAddrTable, &Size, TRUE)) != NO_ERROR) 196 { 197 _tprintf(_T("GetIpAddrTable failed: %d\n"), iRet); 198 DoFormatMessage(); 199 goto cleanup; 200 } 201 202 203 for (k=0; k < pIpAddrTable->dwNumEntries; k++) 204 { 205 if (pIpNetTable->table[0].dwIndex == pIpAddrTable->table[k].dwIndex) 206 { 207 //printf("debug print: pIpAddrTable->table[?].dwIndex = %lx\n", pIpNetTable->table[k].dwIndex); 208 inaddr2.s_addr = pIpAddrTable->table[k].dwAddr; 209 pszIpAddr = inet_ntoa(inaddr2); 210 strcpy(szIntIpAddr, pszIpAddr); 211 } 212 } 213 214 215 /* print header, including interface IP address and index number */ 216 _tprintf(_T("\nInterface: %s --- 0x%lx \n"), szIntIpAddr, pIpNetTable->table[0].dwIndex); 217 _tprintf(_T(" Internet Address Physical Address Type\n")); 218 219 /* go through all ARP entries */ 220 for (i=0; i < pIpNetTable->dwNumEntries; i++) 221 { 222 223 /* if the user has supplied their own internet address * 224 * only print the arp entry which matches that */ 225 if (pszInetAddr) 226 { 227 inaddr.S_un.S_addr = pIpNetTable->table[i].dwAddr; 228 pszIpAddr = inet_ntoa(inaddr); 229 230 /* check if it matches, print it */ 231 if (strcmp(pszIpAddr, pszInetAddr) == 0) 232 PrintEntries(&pIpNetTable->table[i]); 233 } 234 else 235 /* if an address is not supplied, print all entries */ 236 PrintEntries(&pIpNetTable->table[i]); 237 } 238 239 return EXIT_SUCCESS; 240 241 cleanup: 242 if (pIpNetTable != NULL) 243 HeapFree(GetProcessHeap(), 0, pIpNetTable); 244 if (pIpAddrTable != NULL) 245 HeapFree(GetProcessHeap(), 0, pIpAddrTable); 246 return EXIT_FAILURE; 247 } 248 249 /* 250 * 251 * Takes an internet address, a MAC address and an optional interface 252 * address as arguments and checks their validity. 253 * Fill out an MIB_IPNETROW structure and insert the data into the 254 * ARP cache as a static entry. 255 * 256 */ 257 INT Addhost(PTCHAR pszInetAddr, PTCHAR pszEthAddr, PTCHAR pszIfAddr) 258 { 259 PMIB_IPNETROW pAddHost = NULL; 260 PMIB_IPNETTABLE pIpNetTable = NULL; 261 DWORD dwIpAddr = 0; 262 ULONG Size = 0; 263 INT i, val, c; 264 265 /* error checking */ 266 267 /* check IP address */ 268 if (pszInetAddr != NULL) 269 { 270 if ((dwIpAddr = inet_addr(pszInetAddr)) == INADDR_NONE) 271 { 272 _tprintf(_T("ARP: bad IP address: %s\n"), pszInetAddr); 273 return EXIT_FAILURE; 274 } 275 } 276 else 277 { 278 Usage(); 279 return EXIT_FAILURE; 280 } 281 282 /* check MAC address */ 283 if (strlen(pszEthAddr) != 17) 284 { 285 _tprintf(_T("ARP: bad argument: %s\n"), pszEthAddr); 286 return EXIT_FAILURE; 287 } 288 for (i=0; i<17; i++) 289 { 290 if (pszEthAddr[i] == SEPARATOR) 291 continue; 292 293 if (!isxdigit(pszEthAddr[i])) 294 { 295 _tprintf(_T("ARP: bad argument: %s\n"), pszEthAddr); 296 return EXIT_FAILURE; 297 } 298 } 299 300 /* We need the IpNetTable to get the adapter index */ 301 /* Return required buffer size */ 302 GetIpNetTable(pIpNetTable, &Size, 0); 303 304 /* allocate memory for ARP address table */ 305 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size); 306 if (pIpNetTable == NULL) 307 goto cleanup; 308 309 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 310 311 if (GetIpNetTable(pIpNetTable, &Size, TRUE) != NO_ERROR) 312 { 313 _tprintf(_T("failed to allocate memory for GetIpNetTable\n")); 314 DoFormatMessage(); 315 goto cleanup; 316 } 317 318 319 /* reserve memory on heap and zero */ 320 pAddHost = (MIB_IPNETROW *) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW)); 321 if (pAddHost == NULL) 322 goto cleanup; 323 324 ZeroMemory(pAddHost, sizeof(MIB_IPNETROW)); 325 326 /* set dwIndex field to the index of a local IP address to 327 * indicate the network on which the ARP entry applies */ 328 if (pszIfAddr) 329 { 330 if (sscanf(pszIfAddr, "%lx", &pAddHost->dwIndex) == EOF) 331 { 332 goto cleanup; 333 } 334 } 335 else 336 { 337 //printf("debug print: pIpNetTable->table[0].dwIndex = %lx\n", pIpNetTable->table[0].dwIndex); 338 /* needs testing. I get the correct index on my machine, but need others 339 * to test their card index. Any problems and we can use GetAdaptersInfo instead */ 340 pAddHost->dwIndex = pIpNetTable->table[0].dwIndex; 341 } 342 343 /* Set MAC address to 6 bytes (typical) */ 344 pAddHost->dwPhysAddrLen = 6; 345 346 347 /* Encode bPhysAddr into correct byte array */ 348 for (i=0; i<6; i++) 349 { 350 val =0; 351 c = toupper(pszEthAddr[i*3]); 352 c = c - (isdigit(c) ? '0' : ('A' - 10)); 353 val += c; 354 val = (val << 4); 355 c = toupper(pszEthAddr[i*3 + 1]); 356 c = c - (isdigit(c) ? '0' : ('A' - 10)); 357 val += c; 358 pAddHost->bPhysAddr[i] = (BYTE)val; 359 } 360 361 362 /* copy converted IP address */ 363 pAddHost->dwAddr = dwIpAddr; 364 365 366 /* set type to static */ 367 pAddHost->dwType = MIB_IPNET_TYPE_STATIC; 368 369 370 /* Add the ARP entry */ 371 if (SetIpNetEntry(pAddHost) != NO_ERROR) 372 { 373 DoFormatMessage(); 374 goto cleanup; 375 } 376 377 HeapFree(GetProcessHeap(), 0, pAddHost); 378 379 return EXIT_SUCCESS; 380 381 cleanup: 382 if (pIpNetTable != NULL) 383 HeapFree(GetProcessHeap(), 0, pIpNetTable); 384 if (pAddHost != NULL) 385 HeapFree(GetProcessHeap(), 0, pAddHost); 386 return EXIT_FAILURE; 387 } 388 389 /* 390 * 391 * Takes an internet address and an optional interface address as 392 * arguments and checks their validity. 393 * Add the interface number and IP to an MIB_IPNETROW structure 394 * and remove the entry from the ARP cache. 395 * 396 */ 397 INT Deletehost(PTCHAR pszInetAddr, PTCHAR pszIfAddr) 398 { 399 PMIB_IPNETROW pDelHost = NULL; 400 PMIB_IPNETTABLE pIpNetTable = NULL; 401 ULONG Size = 0; 402 DWORD dwIpAddr = 0; 403 BOOL bFlushTable = FALSE; 404 405 /* error checking */ 406 407 /* check IP address */ 408 if (pszInetAddr != NULL) 409 { 410 /* if wildcard is given, set flag to delete all hosts */ 411 if (strncmp(pszInetAddr, "*", 1) == 0) 412 bFlushTable = TRUE; 413 else if ((dwIpAddr = inet_addr(pszInetAddr)) == INADDR_NONE) 414 { 415 _tprintf(_T("ARP: bad IP address: %s\n"), pszInetAddr); 416 exit(EXIT_FAILURE); 417 } 418 } 419 else 420 { 421 Usage(); 422 exit(EXIT_FAILURE); 423 } 424 425 /* We need the IpNetTable to get the adapter index */ 426 /* Return required buffer size */ 427 GetIpNetTable(NULL, &Size, 0); 428 429 /* allocate memory for ARP address table */ 430 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size); 431 if (pIpNetTable == NULL) 432 goto cleanup; 433 434 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable)); 435 436 if (GetIpNetTable(pIpNetTable, &Size, TRUE) != NO_ERROR) 437 { 438 _tprintf(_T("failed to allocate memory for GetIpNetTable\n")); 439 DoFormatMessage(); 440 goto cleanup; 441 } 442 443 /* reserve memory on heap and zero */ 444 pDelHost = (MIB_IPNETROW *) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW)); 445 if (pDelHost == NULL) 446 goto cleanup; 447 448 ZeroMemory(pDelHost, sizeof(MIB_IPNETROW)); 449 450 451 /* set dwIndex field to the index of a local IP address to 452 * indicate the network on which the ARP entry applies */ 453 if (pszIfAddr) 454 { 455 if (sscanf(pszIfAddr, "%lx", &pDelHost->dwIndex) == EOF) 456 { 457 goto cleanup; 458 } 459 } 460 else 461 { 462 /* needs testing. I get the correct index on my machine, but need others 463 * to test their card index. Any problems and we can use GetAdaptersInfo instead */ 464 pDelHost->dwIndex = pIpNetTable->table[0].dwIndex; 465 } 466 467 if (bFlushTable != FALSE) 468 { 469 /* delete arp cache */ 470 if (FlushIpNetTable(pDelHost->dwIndex) != NO_ERROR) 471 { 472 DoFormatMessage(); 473 goto cleanup; 474 } 475 else 476 { 477 HeapFree(GetProcessHeap(), 0, pDelHost); 478 return EXIT_SUCCESS; 479 } 480 } 481 else 482 /* copy converted IP address */ 483 pDelHost->dwAddr = dwIpAddr; 484 485 /* Add the ARP entry */ 486 if (DeleteIpNetEntry(pDelHost) != NO_ERROR) 487 { 488 DoFormatMessage(); 489 goto cleanup; 490 } 491 492 HeapFree(GetProcessHeap(), 0, pDelHost); 493 494 return EXIT_SUCCESS; 495 496 cleanup: 497 if (pIpNetTable != NULL) 498 HeapFree(GetProcessHeap(), 0, pIpNetTable); 499 if (pDelHost != NULL) 500 HeapFree(GetProcessHeap(), 0, pDelHost); 501 return EXIT_FAILURE; 502 } 503 504 /* 505 * 506 * print program usage to screen 507 * 508 */ 509 VOID Usage(VOID) 510 { 511 _tprintf(_T("\nDisplays and modifies the IP-to-Physical address translation tables used by\n" 512 "address resolution protocol (ARP).\n" 513 "\n" 514 "ARP -s inet_addr eth_addr [if_addr]\n" 515 "ARP -d inet_addr [if_addr]\n" 516 "ARP -a [inet_addr] [-N if_addr]\n" 517 "\n" 518 " -a Displays current ARP entries by interrogating the current\n" 519 " protocol data. If inet_addr is specified, the IP and Physical\n" 520 " addresses for only the specified computer are displayed. If\n" 521 " more than one network interface uses ARP, entries for each ARP\n" 522 " table are displayed.\n" 523 " -g Same as -a.\n" 524 " inet_addr Specifies an internet address.\n" 525 " -N if_addr Displays the ARP entries for the network interface specified\n" 526 " by if_addr.\n" 527 " -d Deletes the host specified by inet_addr. inet_addr may be\n" 528 " wildcarded with * to delete all hosts.\n" 529 " -s Adds the host and associates the Internet address inet_addr\n" 530 " with the Physical address eth_addr. The Physical address is\n" 531 " given as 6 hexadecimal bytes separated by hyphens. The entry\n" 532 " is permanent.\n" 533 " eth_addr Specifies a physical address.\n" 534 " if_addr If present, this specifies the Internet address of the\n" 535 " interface whose address translation table should be modified.\n" 536 " If not present, the first applicable interface will be used.\n" 537 "Example:\n" 538 " > arp -s 157.55.85.212 00-aa-00-62-c6-09 .... Adds a static entry.\n" 539 " > arp -a .... Displays the arp table.\n\n")); 540 } 541 542 /* 543 * 544 * Program entry. 545 * Parse command line and call the required function 546 * 547 */ 548 INT main(int argc, char* argv[]) 549 { 550 if ((argc < 2) || (argc > 5)) 551 { 552 Usage(); 553 return EXIT_FAILURE; 554 } 555 556 if (argv[1][0] == '-') 557 { 558 switch (argv[1][1]) 559 { 560 case 'a': /* fall through */ 561 case 'g': 562 if (argc == 2) 563 DisplayArpEntries(NULL, NULL); 564 else if (argc == 3) 565 DisplayArpEntries(argv[2], NULL); 566 else if ((argc == 4) && ((strcmp(argv[2], "-N")) == 0)) 567 DisplayArpEntries(NULL, argv[3]); 568 else if ((argc == 5) && ((strcmp(argv[3], "-N")) == 0)) 569 DisplayArpEntries(argv[2], argv[4]); 570 else 571 { 572 Usage(); 573 return EXIT_FAILURE; 574 } 575 break; 576 case 'd': if (argc == 3) 577 Deletehost(argv[2], NULL); 578 else if (argc == 4) 579 Deletehost(argv[2], argv[3]); 580 else 581 { 582 Usage(); 583 return EXIT_FAILURE; 584 } 585 break; 586 case 's': if (argc == 4) 587 Addhost(argv[2], argv[3], NULL); 588 else if (argc == 5) 589 Addhost(argv[2], argv[3], argv[4]); 590 else 591 { 592 Usage(); 593 return EXIT_FAILURE; 594 } 595 break; 596 default: 597 Usage(); 598 return EXIT_FAILURE; 599 } 600 } 601 else 602 Usage(); 603 604 return EXIT_SUCCESS; 605 } 606