4c9322119a6810221debb75a97dc1e72e1bef887
[reactos.git] / base / applications / network / arp / arp.c
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 }