move network tools
[reactos.git] / reactos / apps / utils / net / 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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 DWORD 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 iRet = GetIpNetTable(pIpNetTable, &Size, TRUE);
174
175 if (iRet != NO_ERROR)
176 {
177 _tprintf(_T("failed to allocate memory for GetIpNetTable\n"));
178 DoFormatMessage();
179 goto cleanup;
180 }
181
182 /* check there are entries in the table */
183 if (pIpNetTable->dwNumEntries == 0)
184 {
185 _tprintf(_T("No ARP entires found\n"));
186 goto cleanup;
187 }
188
189
190
191 /* Retrieve the interface-to-ip address mapping
192 * table to get the IP address for adapter */
193
194 /* get table size */
195 Size = 0;
196 GetIpAddrTable(pIpAddrTable, &Size, 0);
197
198 pIpAddrTable = (MIB_IPADDRTABLE *) HeapAlloc(GetProcessHeap(), 0, Size);
199 if (pIpAddrTable == NULL)
200 goto cleanup;
201
202 ZeroMemory(pIpAddrTable, sizeof(*pIpAddrTable));
203
204 iRet = GetIpAddrTable(pIpAddrTable, &Size, TRUE);
205
206 if (iRet != NO_ERROR)
207 {
208 _tprintf(_T("GetIpAddrTable failed: %d\n"), iRet);
209 DoFormatMessage();
210 goto cleanup;
211 }
212
213
214 for (k=0; k < pIpAddrTable->dwNumEntries; k++)
215 {
216 if (pIpNetTable->table[0].dwIndex == pIpAddrTable->table[k].dwIndex)
217 {
218 //printf("debug print: pIpAddrTable->table[?].dwIndex = %lx\n", pIpNetTable->table[k].dwIndex);
219 inaddr2.s_addr = pIpAddrTable->table[k].dwAddr;
220 pszIpAddr = inet_ntoa(inaddr2);
221 strcpy(szIntIpAddr, pszIpAddr);
222 }
223 }
224
225
226 /* print header, including interface IP address and index number */
227 _tprintf(_T("\nInterface: %s --- 0x%lx \n"), szIntIpAddr, pIpNetTable->table[0].dwIndex);
228 _tprintf(_T(" Internet Address Physical Address Type\n"));
229
230 /* go through all ARP entries */
231 for (i=0; i < pIpNetTable->dwNumEntries; i++)
232 {
233
234 /* if the user has supplied their own internet addesss *
235 * only print the arp entry which matches that */
236 if (pszInetAddr)
237 {
238 inaddr.S_un.S_addr = pIpNetTable->table[i].dwAddr;
239 pszIpAddr = inet_ntoa(inaddr);
240
241 /* check if it matches, print it */
242 if (strcmp(pszIpAddr, pszInetAddr) == 0)
243 PrintEntries(&pIpNetTable->table[i]);
244 }
245 else
246 /* if an address is not supplied, print all entries */
247 PrintEntries(&pIpNetTable->table[i]);
248 }
249
250 return EXIT_SUCCESS;
251
252 cleanup:
253 if (pIpNetTable != NULL)
254 HeapFree(GetProcessHeap(), 0, pIpNetTable);
255 if (pIpAddrTable != NULL)
256 HeapFree(GetProcessHeap(), 0, pIpAddrTable);
257 return EXIT_FAILURE;
258 }
259
260
261 /*
262 *
263 * Takes an internet address, a MAC address and an optional interface
264 * address as arguments and checks their validity.
265 * Fill out an MIB_IPNETROW structure and insert the data into the
266 * ARP cache as a static entry.
267 *
268 */
269 INT Addhost(PTCHAR pszInetAddr, PTCHAR pszEthAddr, PTCHAR pszIfAddr)
270 {
271 PMIB_IPNETROW pAddHost = NULL;
272 PMIB_IPNETTABLE pIpNetTable = NULL;
273 DWORD dwIpAddr = 0;
274 ULONG Size = 0;
275 INT iRet, i, val, c;
276
277 /* error checking */
278
279 /* check IP address */
280 if (pszInetAddr != NULL)
281 {
282 if ((dwIpAddr = inet_addr(pszInetAddr)) == INADDR_NONE)
283 {
284 _tprintf(_T("ARP: bad IP address: %s\n"), pszInetAddr);
285 return EXIT_FAILURE;
286 }
287 }
288 else
289 {
290 Usage();
291 return EXIT_FAILURE;
292 }
293
294 /* check MAC address */
295 if (strlen(pszEthAddr) != 17)
296 {
297 _tprintf(_T("ARP: bad argument: %s\n"), pszEthAddr);
298 return EXIT_FAILURE;
299 }
300 for (i=0; i<17; i++)
301 {
302 if (pszEthAddr[i] == SEPERATOR)
303 continue;
304
305 if (!isxdigit(pszEthAddr[i]))
306 {
307 _tprintf(_T("ARP: bad argument: %s\n"), pszEthAddr);
308 return EXIT_FAILURE;
309 }
310 }
311
312 /* We need the IpNetTable to get the adapter index */
313 /* Return required buffer size */
314 GetIpNetTable(pIpNetTable, &Size, 0);
315
316 /* allocate memory for ARP address table */
317 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size);
318 if (pIpNetTable == NULL)
319 goto cleanup;
320
321 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable));
322
323 iRet = GetIpNetTable(pIpNetTable, &Size, TRUE);
324
325 if (iRet != NO_ERROR)
326 {
327 _tprintf(_T("failed to allocate memory for GetIpNetTable\n"));
328 DoFormatMessage();
329 goto cleanup;
330 }
331
332
333 /* reserve memory on heap and zero */
334 pAddHost = (MIB_IPNETROW *) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW));
335 if (pAddHost == NULL)
336 goto cleanup;
337
338 ZeroMemory(pAddHost, sizeof(MIB_IPNETROW));
339
340 /* set dwIndex field to the index of a local IP address to
341 * indicate the network on which the ARP entry applies */
342 if (pszIfAddr)
343 {
344 if (sscanf(pszIfAddr, "%lx", &pAddHost->dwIndex) == EOF)
345 {
346 goto cleanup;
347 }
348 }
349 else
350 {
351 //printf("debug print: pIpNetTable->table[0].dwIndex = %lx\n", pIpNetTable->table[0].dwIndex);
352 /* needs testing. I get the correct index on my machine, but need others
353 * to test their card index. Any problems and we can use GetAdaptersInfo instead */
354 pAddHost->dwIndex = pIpNetTable->table[0].dwIndex;
355 }
356
357 /* Set MAC address to 6 bytes (typical) */
358 pAddHost->dwPhysAddrLen = 6;
359
360
361 /* Encode bPhysAddr into correct byte array */
362 for (i=0; i<6; i++)
363 {
364 val =0;
365 c = toupper(pszEthAddr[i*3]);
366 c = c - (isdigit(c) ? '0' : ('A' - 10));
367 val += c;
368 val = (val << 4);
369 c = toupper(pszEthAddr[i*3 + 1]);
370 c = c - (isdigit(c) ? '0' : ('A' - 10));
371 val += c;
372 pAddHost->bPhysAddr[i] = (BYTE)val;
373 }
374
375
376 /* copy converted IP address */
377 pAddHost->dwAddr = dwIpAddr;
378
379
380 /* set type to static */
381 pAddHost->dwType = MIB_IPNET_TYPE_STATIC;
382
383
384 /* Add the ARP entry */
385 if ((iRet = SetIpNetEntry(pAddHost)) != NO_ERROR)
386 {
387 DoFormatMessage();
388 goto cleanup;
389 }
390
391 HeapFree(GetProcessHeap(), 0, pAddHost);
392
393 return EXIT_SUCCESS;
394
395 cleanup:
396 if (pIpNetTable != NULL)
397 HeapFree(GetProcessHeap(), 0, pIpNetTable);
398 if (pAddHost != NULL)
399 HeapFree(GetProcessHeap(), 0, pAddHost);
400 return EXIT_FAILURE;
401 }
402
403
404 /*
405 *
406 * Takes an internet address and an optional interface address as
407 * arguments and checks their validity.
408 * Add the interface number and IP to an MIB_IPNETROW structure
409 * and remove the entry from the ARP cache.
410 *
411 */
412 INT Deletehost(PTCHAR pszInetAddr, PTCHAR pszIfAddr)
413 {
414 PMIB_IPNETROW pDelHost = NULL;
415 PMIB_IPNETTABLE pIpNetTable = NULL;
416 DWORD Size = 0;
417 DWORD dwIpAddr = 0;
418 INT iRet;
419 BOOL bFlushTable = FALSE;
420
421 /* error checking */
422
423 /* check IP address */
424 if (pszInetAddr != NULL)
425 {
426 /* if wildcard is given, set flag to delete all hosts */
427 if (strncmp(pszInetAddr, "*", 1) == 0)
428 bFlushTable = TRUE;
429 else if ((dwIpAddr = inet_addr(pszInetAddr)) == INADDR_NONE)
430 {
431 _tprintf(_T("ARP: bad IP address: %s\n"), pszInetAddr);
432 exit(EXIT_FAILURE);
433 }
434 }
435 else
436 {
437 Usage();
438 exit(EXIT_FAILURE);
439 }
440
441 /* We need the IpNetTable to get the adapter index */
442 /* Return required buffer size */
443 GetIpNetTable(NULL, &Size, 0);
444
445 /* allocate memory for ARP address table */
446 pIpNetTable = (PMIB_IPNETTABLE) HeapAlloc(GetProcessHeap(), 0, Size);
447 if (pIpNetTable == NULL)
448 goto cleanup;
449
450 ZeroMemory(pIpNetTable, sizeof(*pIpNetTable));
451
452 iRet = GetIpNetTable(pIpNetTable, &Size, TRUE);
453
454 if (iRet != NO_ERROR)
455 {
456 _tprintf(_T("failed to allocate memory for GetIpNetTable\n"));
457 DoFormatMessage();
458 goto cleanup;
459 }
460
461 /* reserve memory on heap and zero */
462 pDelHost = (MIB_IPNETROW *) HeapAlloc(GetProcessHeap(), 0, sizeof(MIB_IPNETROW));
463 if (pDelHost == NULL)
464 goto cleanup;
465
466 ZeroMemory(pDelHost, sizeof(MIB_IPNETROW));
467
468
469 /* set dwIndex field to the index of a local IP address to
470 * indicate the network on which the ARP entry applies */
471 if (pszIfAddr)
472 {
473 if (sscanf(pszIfAddr, "%lx", &pDelHost->dwIndex) == EOF)
474 {
475 goto cleanup;
476 }
477 }
478 else
479 {
480 /* needs testing. I get the correct index on my machine, but need others
481 * to test their card index. Any problems and we can use GetAdaptersInfo instead */
482 pDelHost->dwIndex = pIpNetTable->table[0].dwIndex;
483 }
484
485 if (bFlushTable == TRUE)
486 {
487 /* delete arp cache */
488 if ((iRet = FlushIpNetTable(pDelHost->dwIndex)) != NO_ERROR)
489 {
490 DoFormatMessage();
491 goto cleanup;
492 }
493 else
494 {
495 HeapFree(GetProcessHeap(), 0, pDelHost);
496 return EXIT_SUCCESS;
497 }
498 }
499 else
500 /* copy converted IP address */
501 pDelHost->dwAddr = dwIpAddr;
502
503 /* Add the ARP entry */
504 if ((iRet = DeleteIpNetEntry(pDelHost)) != NO_ERROR)
505 {
506 DoFormatMessage();
507 goto cleanup;
508 }
509
510 HeapFree(GetProcessHeap(), 0, pDelHost);
511
512 return EXIT_SUCCESS;
513
514 cleanup:
515 if (pIpNetTable != NULL)
516 HeapFree(GetProcessHeap(), 0, pIpNetTable);
517 if (pDelHost != NULL)
518 HeapFree(GetProcessHeap(), 0, pDelHost);
519 return EXIT_FAILURE;
520 }
521
522
523
524 /*
525 *
526 * print program usage to screen
527 *
528 */
529 VOID Usage(VOID)
530 {
531 _tprintf(_T("\nDisplays and modifies the IP-to-Physical address translation tables used by\n"
532 "address resolution protocol (ARP).\n"
533 "\n"
534 "ARP -s inet_addr eth_addr [if_addr]\n"
535 "ARP -d inet_addr [if_addr]\n"
536 "ARP -a [inet_addr] [-N if_addr]\n"
537 "\n"
538 " -a Displays current ARP entries by interrogating the current\n"
539 " protocol data. If inet_addr is specified, the IP and Physical\n"
540 " addresses for only the specified computer are displayed. If\n"
541 " more than one network interface uses ARP, entries for each ARP\n"
542 " table are displayed.\n"
543 " -g Same as -a.\n"
544 " inet_addr Specifies an internet address.\n"
545 " -N if_addr Displays the ARP entries for the network interface specified\n"
546 " by if_addr.\n"
547 " -d Deletes the host specified by inet_addr. inet_addr may be\n"
548 " wildcarded with * to delete all hosts.\n"
549 " -s Adds the host and associates the Internet address inet_addr\n"
550 " with the Physical address eth_addr. The Physical address is\n"
551 " given as 6 hexadecimal bytes separated by hyphens. The entry\n"
552 " is permanent.\n"
553 " eth_addr Specifies a physical address.\n"
554 " if_addr If present, this specifies the Internet address of the\n"
555 " interface whose address translation table should be modified.\n"
556 " If not present, the first applicable interface will be used.\n"
557 "Example:\n"
558 " > arp -s 157.55.85.212 00-aa-00-62-c6-09 .... Adds a static entry.\n"
559 " > arp -a .... Displays the arp table.\n\n"));
560 }
561
562
563
564 /*
565 *
566 * Program entry.
567 * Parse command line and call the required function
568 *
569 */
570 INT main(int argc, char* argv[])
571 {
572 if ((argc < 2) || (argc > 5))
573 {
574 Usage();
575 return EXIT_FAILURE;
576 }
577
578 if (argv[1][0] == '-')
579 {
580 switch (argv[1][1])
581 {
582 case 'a': /* fall through */
583 case 'g':
584 if (argc == 2)
585 DisplayArpEntries(NULL, NULL);
586 else if (argc == 3)
587 DisplayArpEntries(argv[2], NULL);
588 else if ((argc == 4) && ((strcmp(argv[2], "-N")) == 0))
589 DisplayArpEntries(NULL, argv[3]);
590 else if ((argc == 5) && ((strcmp(argv[3], "-N")) == 0))
591 DisplayArpEntries(argv[2], argv[4]);
592 else
593 Usage();
594 return EXIT_FAILURE;
595 break;
596 case 'd': if (argc == 3)
597 Deletehost(argv[2], NULL);
598 else if (argc == 4)
599 Deletehost(argv[2], argv[3]);
600 else
601 Usage();
602 return EXIT_FAILURE;
603 break;
604 case 's': if (argc == 4)
605 Addhost(argv[2], argv[3], NULL);
606 else if (argc == 5)
607 Addhost(argv[2], argv[3], argv[4]);
608 else
609 Usage();
610 return EXIT_FAILURE;
611 break;
612 default:
613 Usage();
614 return EXIT_FAILURE;
615 }
616 }
617 else
618 Usage();
619
620 return EXIT_SUCCESS;
621 }