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