[SNDVOL32] Add the small line dialog
[reactos.git] / base / applications / network / tracert / tracert.cpp
1 /*
2 * PROJECT: ReactOS trace route utility
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: base/applications/network/tracert/tracert.cpp
5 * PURPOSE: Trace network paths through networks
6 * COPYRIGHT: Copyright 2018 Ged Murphy <gedmurphy@reactos.org>
7 *
8 */
9
10 #ifdef __REACTOS__
11 #define WIN32_NO_STATUS
12 #include <stdarg.h>
13 #include <windef.h>
14 #include <winbase.h>
15 #include <winuser.h>
16 #define _INC_WINDOWS
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <winsock2.h>
20 #else
21 #include <winsock2.h>
22 #include <Windows.h>
23 #endif
24 #include <ws2tcpip.h>
25 #include <iphlpapi.h>
26 #include <icmpapi.h>
27 #include <strsafe.h>
28 #include "resource.h"
29
30 #define SIZEOF_ICMP_ERROR 8
31 #define SIZEOF_IO_STATUS_BLOCK 8
32 #define PACKET_SIZE 32
33 #define MAX_IPADDRESS 32
34 #define NUM_OF_PINGS 3
35
36 struct TraceInfo
37 {
38 bool ResolveAddresses;
39 ULONG MaxHops;
40 ULONG Timeout;
41 WCHAR HostName[NI_MAXHOST];
42 WCHAR TargetIP[MAX_IPADDRESS];
43 int Family;
44
45 HANDLE hIcmpFile;
46 PADDRINFOW Target;
47
48 } Info = { 0 };
49
50
51
52 #ifndef USE_CONUTILS
53 static
54 INT
55 LengthOfStrResource(
56 _In_ HINSTANCE hInst,
57 _In_ UINT uID
58 )
59 {
60 HRSRC hrSrc;
61 HGLOBAL hRes;
62 LPWSTR lpName, lpStr;
63
64 if (hInst == NULL) return -1;
65
66 lpName = (LPWSTR)MAKEINTRESOURCE((uID >> 4) + 1);
67
68 if ((hrSrc = FindResourceW(hInst, lpName, (LPWSTR)RT_STRING)) &&
69 (hRes = LoadResource(hInst, hrSrc)) &&
70 (lpStr = (WCHAR*)LockResource(hRes)))
71 {
72 UINT x;
73 uID &= 0xF;
74 for (x = 0; x < uID; x++)
75 {
76 lpStr += (*lpStr) + 1;
77 }
78 return (int)(*lpStr);
79 }
80 return -1;
81 }
82
83 static
84 INT
85 AllocAndLoadString(
86 _In_ UINT uID,
87 _Out_ LPWSTR *lpTarget
88 )
89 {
90 HMODULE hInst;
91 INT Length;
92
93 hInst = GetModuleHandleW(NULL);
94 Length = LengthOfStrResource(hInst, uID);
95 if (Length++ > 0)
96 {
97 (*lpTarget) = (LPWSTR)LocalAlloc(LMEM_FIXED,
98 Length * sizeof(WCHAR));
99 if ((*lpTarget) != NULL)
100 {
101 INT Ret;
102 if (!(Ret = LoadStringW(hInst, uID, *lpTarget, Length)))
103 {
104 LocalFree((HLOCAL)(*lpTarget));
105 }
106 return Ret;
107 }
108 }
109 return 0;
110 }
111
112 static
113 INT
114 OutputText(
115 _In_ UINT uID,
116 ...)
117 {
118 LPWSTR Format;
119 DWORD Ret = 0;
120 va_list lArgs;
121
122 if (AllocAndLoadString(uID, &Format) > 0)
123 {
124 va_start(lArgs, uID);
125
126 LPWSTR Buffer;
127 Ret = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
128 Format,
129 0,
130 0,
131 (LPWSTR)&Buffer,
132 0,
133 &lArgs);
134 va_end(lArgs);
135
136 if (Ret)
137 {
138 wprintf(Buffer);
139 LocalFree(Buffer);
140 }
141 LocalFree((HLOCAL)Format);
142 }
143
144 return Ret;
145 }
146 #else
147 #define OutputText(Id, ...) ConResPrintf(StdOut, Id, __VA_ARGS__)
148 #endif //USE_CONUTILS
149
150 static
151 VOID
152 Usage()
153 {
154 OutputText(IDS_USAGE);
155 }
156
157 static ULONG
158 GetULONG(
159 _In_z_ LPWSTR String
160 )
161 {
162 ULONG Length;
163 Length = wcslen(String);
164
165 ULONG i = 0;
166 while ((i < Length) && ((String[i] < L'0') || (String[i] > L'9'))) i++;
167 if ((i >= Length) || ((String[i] < L'0') || (String[i] > L'9')))
168 {
169 return (ULONG)-1;
170 }
171
172 LPWSTR StopString;
173 return wcstoul(&String[i], &StopString, 10);
174 }
175
176 static bool
177 ResolveTarget()
178 {
179 ADDRINFOW Hints;
180 ZeroMemory(&Hints, sizeof(Hints));
181 Hints.ai_family = Info.Family;
182 Hints.ai_flags = AI_CANONNAME;
183
184 int Status;
185 Status = GetAddrInfoW(Info.HostName,
186 NULL,
187 &Hints,
188 &Info.Target);
189 if (Status != 0)
190 {
191 return false;
192 }
193
194 Status = GetNameInfoW(Info.Target->ai_addr,
195 Info.Target->ai_addrlen,
196 Info.TargetIP,
197 MAX_IPADDRESS,
198 NULL,
199 0,
200 NI_NUMERICHOST);
201 if (Status != 0)
202 {
203 return false;
204 }
205
206 return true;
207 }
208
209 static bool
210 PrintHopInfo(_In_ PVOID Buffer)
211 {
212 SOCKADDR_IN6 SockAddrIn6 = { 0 };
213 SOCKADDR_IN SockAddrIn = { 0 };
214 PSOCKADDR SockAddr;
215 socklen_t Size;
216
217 if (Info.Family == AF_INET6)
218 {
219 PIPV6_ADDRESS_EX Ipv6Addr = (PIPV6_ADDRESS_EX)Buffer;
220 SockAddrIn6.sin6_family = AF_INET6;
221 CopyMemory(SockAddrIn6.sin6_addr.u.Word, Ipv6Addr->sin6_addr, sizeof(SockAddrIn6.sin6_addr));
222 //SockAddrIn6.sin6_addr = Ipv6Addr->sin6_addr;
223 SockAddr = (PSOCKADDR)&SockAddrIn6;
224 Size = sizeof(SOCKADDR_IN6);
225
226 }
227 else
228 {
229 IPAddr *Address = (IPAddr *)Buffer;
230 SockAddrIn.sin_family = AF_INET;
231 SockAddrIn.sin_addr.S_un.S_addr = *Address;
232 SockAddr = (PSOCKADDR)&SockAddrIn;
233 Size = sizeof(SOCKADDR_IN);
234 }
235
236 INT Status;
237 bool Resolved = false;
238 WCHAR HostName[NI_MAXHOST];
239 if (Info.ResolveAddresses)
240 {
241 Status = GetNameInfoW(SockAddr,
242 Size,
243 HostName,
244 NI_MAXHOST,
245 NULL,
246 0,
247 NI_NAMEREQD);
248 if (Status == 0)
249 {
250 Resolved = true;
251 }
252 }
253
254 WCHAR IpAddress[MAX_IPADDRESS];
255 Status = GetNameInfoW(SockAddr,
256 Size,
257 IpAddress,
258 NI_MAXHOST,
259 NULL,
260 0,
261 NI_NUMERICHOST);
262 if (Status == 0)
263 {
264 if (Resolved)
265 {
266 OutputText(IDS_HOP_RES_INFO, HostName, IpAddress);
267 }
268 else
269 {
270 OutputText(IDS_HOP_IP_INFO, IpAddress);
271 }
272 }
273
274 return (Status == 0);
275 }
276
277 static bool
278 DecodeResponse(
279 _In_ PVOID ReplyBuffer,
280 _In_ bool OutputHopAddress,
281 _Out_ bool& FoundTarget
282 )
283 {
284 ULONG RoundTripTime;
285 PVOID AddressInfo;
286 ULONG Status;
287
288 if (Info.Family == AF_INET6)
289 {
290 PICMPV6_ECHO_REPLY EchoReplyV6;
291 EchoReplyV6 = (PICMPV6_ECHO_REPLY)ReplyBuffer;
292 Status = EchoReplyV6->Status;
293 RoundTripTime = EchoReplyV6->RoundTripTime;
294 AddressInfo = &EchoReplyV6->Address;
295 }
296 else
297 {
298 PICMP_ECHO_REPLY EchoReplyV4;
299 EchoReplyV4 = (PICMP_ECHO_REPLY)ReplyBuffer;
300 Status = EchoReplyV4->Status;
301 RoundTripTime = EchoReplyV4->RoundTripTime;
302 AddressInfo = &EchoReplyV4->Address;
303 }
304
305 switch (Status)
306 {
307 case IP_SUCCESS:
308 case IP_TTL_EXPIRED_TRANSIT:
309 if (RoundTripTime)
310 {
311 OutputText(IDS_HOP_TIME, RoundTripTime);
312 }
313 else
314 {
315 OutputText(IDS_HOP_ZERO);
316 }
317 break;
318
319 case IP_DEST_HOST_UNREACHABLE:
320 case IP_DEST_NET_UNREACHABLE:
321 FoundTarget = true;
322 PrintHopInfo(AddressInfo);
323 OutputText(IDS_HOP_RESPONSE);
324 if (Status == IP_DEST_HOST_UNREACHABLE)
325 {
326 OutputText(IDS_DEST_HOST_UNREACHABLE);
327 }
328 else if (Status == IP_DEST_NET_UNREACHABLE)
329 {
330 OutputText(IDS_DEST_NET_UNREACHABLE);
331 }
332 return true;
333
334 case IP_REQ_TIMED_OUT:
335 OutputText(IDS_TIMEOUT);
336 break;
337
338 case IP_GENERAL_FAILURE:
339 OutputText(IDS_GEN_FAILURE);
340 return false;
341
342 default:
343 OutputText(IDS_TRANSMIT_FAILED, Status);
344 return false;
345 }
346
347 if (OutputHopAddress)
348 {
349 if (Status == IP_SUCCESS)
350 {
351 FoundTarget = true;
352 }
353 if (Status == IP_TTL_EXPIRED_TRANSIT || Status == IP_SUCCESS)
354 {
355 PrintHopInfo(AddressInfo);
356 OutputText(IDS_LINEBREAK);
357 }
358 else if (Status == IP_REQ_TIMED_OUT)
359 {
360 OutputText(IDS_REQ_TIMED_OUT);
361 }
362 }
363
364 return true;
365 }
366
367 static bool
368 RunTraceRoute()
369 {
370 bool Success = false;
371 Success = ResolveTarget();
372 if (!Success)
373 {
374 OutputText(IDS_UNABLE_RESOLVE, Info.HostName);
375 return false;
376 }
377
378 BYTE SendBuffer[PACKET_SIZE];
379 ICMPV6_ECHO_REPLY ReplyBufferv6;
380 #ifdef _WIN64
381 ICMP_ECHO_REPLY32 ReplyBufferv432;
382 #else
383 ICMP_ECHO_REPLY ReplyBufferv4;
384 #endif
385 PVOID ReplyBuffer;
386
387 DWORD ReplySize = PACKET_SIZE + SIZEOF_ICMP_ERROR + SIZEOF_IO_STATUS_BLOCK;
388 if (Info.Family == AF_INET6)
389 {
390 ReplyBuffer = &ReplyBufferv6;
391 ReplySize += sizeof(ICMPV6_ECHO_REPLY);
392 }
393 else
394 {
395 #ifdef _WIN64
396 ReplyBuffer = &ReplyBufferv432;
397 ReplySize += sizeof(ICMP_ECHO_REPLY32);
398 #else
399 ReplyBuffer = &ReplyBufferv4;
400 ReplySize += sizeof(ICMP_ECHO_REPLY);
401 #endif
402 }
403
404 if (Info.Family == AF_INET6)
405 {
406 Info.hIcmpFile = Icmp6CreateFile();
407 }
408 else
409 {
410 Info.hIcmpFile = IcmpCreateFile();
411 }
412 if (Info.hIcmpFile == INVALID_HANDLE_VALUE)
413 {
414 FreeAddrInfoW(Info.Target);
415 return false;
416 }
417
418 OutputText(IDS_TRACE_INFO, Info.HostName, Info.TargetIP, Info.MaxHops);
419
420 IP_OPTION_INFORMATION IpOptionInfo;
421 ZeroMemory(&IpOptionInfo, sizeof(IpOptionInfo));
422
423 bool Quit = false;
424 ULONG HopCount = 1;
425 bool FoundTarget = false;
426 while ((HopCount <= Info.MaxHops) && (FoundTarget == false) && (Quit == false))
427 {
428 OutputText(IDS_HOP_COUNT, HopCount);
429
430 for (int Ping = 1; Ping <= NUM_OF_PINGS; Ping++)
431 {
432 IpOptionInfo.Ttl = static_cast<UCHAR>(HopCount);
433
434 if (Info.Family == AF_INET6)
435 {
436 struct sockaddr_in6 Source;
437
438 ZeroMemory(&Source, sizeof(Source));
439 Source.sin6_family = AF_INET6;
440
441 (void)Icmp6SendEcho2(Info.hIcmpFile,
442 NULL,
443 NULL,
444 NULL,
445 &Source,
446 (struct sockaddr_in6 *)Info.Target->ai_addr,
447 SendBuffer,
448 (USHORT)PACKET_SIZE,
449 &IpOptionInfo,
450 ReplyBuffer,
451 ReplySize,
452 Info.Timeout);
453 }
454 else
455 {
456 (void)IcmpSendEcho2(Info.hIcmpFile,
457 NULL,
458 NULL,
459 NULL,
460 ((PSOCKADDR_IN)Info.Target->ai_addr)->sin_addr.s_addr,
461 SendBuffer,
462 (USHORT)PACKET_SIZE,
463 &IpOptionInfo,
464 ReplyBuffer,
465 ReplySize,
466 Info.Timeout);
467 }
468
469 if (DecodeResponse(ReplyBuffer, (Ping == NUM_OF_PINGS), FoundTarget) == false)
470 {
471 Quit = true;
472 break;
473 }
474
475 if (FoundTarget)
476 {
477 Success = true;
478 break;
479 }
480 }
481
482 HopCount++;
483 Sleep(100);
484 }
485
486 OutputText(IDS_TRACE_COMPLETE);
487
488 FreeAddrInfoW(Info.Target);
489 if (Info.hIcmpFile)
490 {
491 IcmpCloseHandle(Info.hIcmpFile);
492 }
493
494 return Success;
495 }
496
497 static bool
498 ParseCmdline(int argc, wchar_t *argv[])
499 {
500 if (argc < 2)
501 {
502 Usage();
503 return false;
504 }
505
506 for (int i = 1; i < argc; i++)
507 {
508 if (argv[i][0] == '-')
509 {
510 switch (argv[i][1])
511 {
512 case 'd':
513 Info.ResolveAddresses = FALSE;
514 break;
515
516 case 'h':
517 Info.MaxHops = GetULONG(argv[++i]);
518 break;
519
520 case 'j':
521 printf("-j is not yet implemented.\n");
522 return false;
523
524 case 'w':
525 Info.Timeout = GetULONG(argv[++i]);
526 break;
527
528 case '4':
529 Info.Family = AF_INET;
530 break;
531
532 case '6':
533 Info.Family = AF_INET6;
534 break;
535
536 default:
537 {
538 OutputText(IDS_INVALID_OPTION, argv[i]);
539 Usage();
540 return false;
541 }
542 }
543 }
544 else
545 {
546 StringCchCopyW(Info.HostName, NI_MAXHOST, argv[i]);
547 break;
548 }
549 }
550
551 return true;
552 }
553
554 EXTERN_C
555 int wmain(int argc, wchar_t *argv[])
556 {
557 Info.ResolveAddresses = true;
558 Info.MaxHops = 30;
559 Info.Timeout = 4000;
560 Info.Family = AF_UNSPEC;
561
562 if (!ParseCmdline(argc, argv))
563 {
564 return 1;
565 }
566
567 WSADATA WsaData;
568 if (WSAStartup(MAKEWORD(2, 2), &WsaData))
569 {
570 return 1;
571 }
572
573 bool Success;
574 Success = RunTraceRoute();
575
576 WSACleanup();
577
578 return Success ? 0 : 1;
579 }