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