xref: /reactos/base/applications/network/arp/arp.c (revision 40462c92)
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