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 */
DoFormatMessage(VOID)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
PrintMessage(DWORD dwMessage)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
PrintMessageV(DWORD dwMessage,...)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 */
PrintEntries(PMIB_IPNETROW pIpAddRow)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 */
DisplayArpEntries(PTCHAR pszInetAddr,PTCHAR 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 */
Addhost(PTCHAR pszInetAddr,PTCHAR pszEthAddr,PTCHAR pszIfAddr)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 */
Deletehost(PTCHAR pszInetAddr,PTCHAR pszIfAddr)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 */
Usage(VOID)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 */
main(int argc,char * argv[])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