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