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