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>
12 #define WIN32_NO_STATUS
31 #define SIZEOF_ICMP_ERROR 8
32 #define SIZEOF_IO_STATUS_BLOCK 8
33 #define PACKET_SIZE 32
34 #define MAX_IPADDRESS 32
35 #define NUM_OF_PINGS 3
39 bool ResolveAddresses
;
42 WCHAR HostName
[NI_MAXHOST
];
43 WCHAR TargetIP
[MAX_IPADDRESS
];
65 if (hInst
== NULL
) return -1;
67 lpName
= (LPWSTR
)MAKEINTRESOURCE((uID
>> 4) + 1);
69 if ((hrSrc
= FindResourceW(hInst
, lpName
, (LPWSTR
)RT_STRING
)) &&
70 (hRes
= LoadResource(hInst
, hrSrc
)) &&
71 (lpStr
= (WCHAR
*)LockResource(hRes
)))
75 for (x
= 0; x
< uID
; x
++)
77 lpStr
+= (*lpStr
) + 1;
88 _Out_ LPWSTR
*lpTarget
94 hInst
= GetModuleHandleW(NULL
);
95 Length
= LengthOfStrResource(hInst
, uID
);
98 (*lpTarget
) = (LPWSTR
)LocalAlloc(LMEM_FIXED
,
99 Length
* sizeof(WCHAR
));
100 if ((*lpTarget
) != NULL
)
103 if (!(Ret
= LoadStringW(hInst
, uID
, *lpTarget
, Length
)))
105 LocalFree((HLOCAL
)(*lpTarget
));
123 if (AllocAndLoadString(uID
, &Format
) > 0)
125 va_start(lArgs
, uID
);
128 Ret
= FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_STRING
,
142 LocalFree((HLOCAL
)Format
);
148 #define OutputText(Id, ...) ConResMsgPrintfEx(StdOut, NULL, 0, Id, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), ##__VA_ARGS__)
149 #endif //USE_CONUTILS
155 OutputText(IDS_USAGE
);
164 Length
= wcslen(String
);
167 while ((i
< Length
) && ((String
[i
] < L
'0') || (String
[i
] > L
'9'))) i
++;
168 if ((i
>= Length
) || ((String
[i
] < L
'0') || (String
[i
] > L
'9')))
174 return wcstoul(&String
[i
], &StopString
, 10);
181 ZeroMemory(&Hints
, sizeof(Hints
));
182 Hints
.ai_family
= Info
.Family
;
183 Hints
.ai_flags
= AI_CANONNAME
;
186 Status
= GetAddrInfoW(Info
.HostName
,
195 Status
= GetNameInfoW(Info
.Target
->ai_addr
,
196 Info
.Target
->ai_addrlen
,
211 PrintHopInfo(_In_ PVOID Buffer
)
213 SOCKADDR_IN6 SockAddrIn6
= { 0 };
214 SOCKADDR_IN SockAddrIn
= { 0 };
218 if (Info
.Family
== AF_INET6
)
220 PIPV6_ADDRESS_EX Ipv6Addr
= (PIPV6_ADDRESS_EX
)Buffer
;
221 SockAddrIn6
.sin6_family
= AF_INET6
;
222 CopyMemory(SockAddrIn6
.sin6_addr
.u
.Word
, Ipv6Addr
->sin6_addr
, sizeof(SockAddrIn6
.sin6_addr
));
223 //SockAddrIn6.sin6_addr = Ipv6Addr->sin6_addr;
224 SockAddr
= (PSOCKADDR
)&SockAddrIn6
;
225 Size
= sizeof(SOCKADDR_IN6
);
230 IPAddr
*Address
= (IPAddr
*)Buffer
;
231 SockAddrIn
.sin_family
= AF_INET
;
232 SockAddrIn
.sin_addr
.S_un
.S_addr
= *Address
;
233 SockAddr
= (PSOCKADDR
)&SockAddrIn
;
234 Size
= sizeof(SOCKADDR_IN
);
238 bool Resolved
= false;
239 WCHAR HostName
[NI_MAXHOST
];
240 if (Info
.ResolveAddresses
)
242 Status
= GetNameInfoW(SockAddr
,
255 WCHAR IpAddress
[MAX_IPADDRESS
];
256 Status
= GetNameInfoW(SockAddr
,
267 OutputText(IDS_HOP_RES_INFO
, HostName
, IpAddress
);
271 OutputText(IDS_HOP_IP_INFO
, IpAddress
);
275 return (Status
== 0);
280 _In_ PVOID ReplyBuffer
,
281 _In_
bool OutputHopAddress
,
282 _Out_
bool& FoundTarget
289 if (Info
.Family
== AF_INET6
)
291 PICMPV6_ECHO_REPLY EchoReplyV6
;
292 EchoReplyV6
= (PICMPV6_ECHO_REPLY
)ReplyBuffer
;
293 Status
= EchoReplyV6
->Status
;
294 RoundTripTime
= EchoReplyV6
->RoundTripTime
;
295 AddressInfo
= &EchoReplyV6
->Address
;
299 PICMP_ECHO_REPLY EchoReplyV4
;
300 EchoReplyV4
= (PICMP_ECHO_REPLY
)ReplyBuffer
;
301 Status
= EchoReplyV4
->Status
;
302 RoundTripTime
= EchoReplyV4
->RoundTripTime
;
303 AddressInfo
= &EchoReplyV4
->Address
;
309 case IP_TTL_EXPIRED_TRANSIT
:
312 OutputText(IDS_HOP_TIME
, RoundTripTime
);
316 OutputText(IDS_HOP_ZERO
);
320 case IP_DEST_HOST_UNREACHABLE
:
321 case IP_DEST_NET_UNREACHABLE
:
323 PrintHopInfo(AddressInfo
);
324 OutputText(IDS_HOP_RESPONSE
);
325 if (Status
== IP_DEST_HOST_UNREACHABLE
)
327 OutputText(IDS_DEST_HOST_UNREACHABLE
);
329 else if (Status
== IP_DEST_NET_UNREACHABLE
)
331 OutputText(IDS_DEST_NET_UNREACHABLE
);
335 case IP_REQ_TIMED_OUT
:
336 OutputText(IDS_TIMEOUT
);
339 case IP_GENERAL_FAILURE
:
340 OutputText(IDS_GEN_FAILURE
);
344 OutputText(IDS_TRANSMIT_FAILED
, Status
);
348 if (OutputHopAddress
)
350 if (Status
== IP_SUCCESS
)
354 if (Status
== IP_TTL_EXPIRED_TRANSIT
|| Status
== IP_SUCCESS
)
356 PrintHopInfo(AddressInfo
);
357 OutputText(IDS_LINEBREAK
);
359 else if (Status
== IP_REQ_TIMED_OUT
)
361 OutputText(IDS_REQ_TIMED_OUT
);
371 bool Success
= false;
372 Success
= ResolveTarget();
375 OutputText(IDS_UNABLE_RESOLVE
, Info
.HostName
);
379 BYTE SendBuffer
[PACKET_SIZE
];
383 DWORD ReplySize
= PACKET_SIZE
+ SIZEOF_ICMP_ERROR
+ SIZEOF_IO_STATUS_BLOCK
;
384 if (Info
.Family
== AF_INET6
)
386 ReplySize
+= sizeof(ICMPV6_ECHO_REPLY
);
391 ReplySize
+= sizeof(ICMP_ECHO_REPLY32
);
393 ReplySize
+= sizeof(ICMP_ECHO_REPLY
);
397 HANDLE heap
= GetProcessHeap();
398 ReplyBuffer
= HeapAlloc(heap
, HEAP_ZERO_MEMORY
, ReplySize
);
399 if (ReplyBuffer
== NULL
)
401 FreeAddrInfoW(Info
.Target
);
405 if (Info
.Family
== AF_INET6
)
407 Info
.hIcmpFile
= Icmp6CreateFile();
411 Info
.hIcmpFile
= IcmpCreateFile();
413 if (Info
.hIcmpFile
== INVALID_HANDLE_VALUE
)
415 HeapFree(heap
, 0, ReplyBuffer
);
416 FreeAddrInfoW(Info
.Target
);
420 OutputText(IDS_TRACE_INFO
, Info
.HostName
, Info
.TargetIP
, Info
.MaxHops
);
422 IP_OPTION_INFORMATION IpOptionInfo
;
423 ZeroMemory(&IpOptionInfo
, sizeof(IpOptionInfo
));
427 bool FoundTarget
= false;
428 while ((HopCount
<= Info
.MaxHops
) && (FoundTarget
== false) && (Quit
== false))
430 OutputText(IDS_HOP_COUNT
, HopCount
);
432 for (int Ping
= 1; Ping
<= NUM_OF_PINGS
; Ping
++)
434 IpOptionInfo
.Ttl
= static_cast<UCHAR
>(HopCount
);
436 if (Info
.Family
== AF_INET6
)
438 struct sockaddr_in6 Source
;
440 ZeroMemory(&Source
, sizeof(Source
));
441 Source
.sin6_family
= AF_INET6
;
443 (void)Icmp6SendEcho2(Info
.hIcmpFile
,
448 (struct sockaddr_in6
*)Info
.Target
->ai_addr
,
458 (void)IcmpSendEcho2(Info
.hIcmpFile
,
462 ((PSOCKADDR_IN
)Info
.Target
->ai_addr
)->sin_addr
.s_addr
,
471 if (DecodeResponse(ReplyBuffer
, (Ping
== NUM_OF_PINGS
), FoundTarget
) == false)
488 OutputText(IDS_TRACE_COMPLETE
);
490 HeapFree(heap
, 0, ReplyBuffer
);
491 FreeAddrInfoW(Info
.Target
);
494 IcmpCloseHandle(Info
.hIcmpFile
);
501 ParseCmdline(int argc
, wchar_t *argv
[])
509 for (int i
= 1; i
< argc
; i
++)
511 if (argv
[i
][0] == '-')
516 Info
.ResolveAddresses
= FALSE
;
520 Info
.MaxHops
= GetULONG(argv
[++i
]);
524 printf("-j is not yet implemented.\n");
528 Info
.Timeout
= GetULONG(argv
[++i
]);
532 Info
.Family
= AF_INET
;
536 Info
.Family
= AF_INET6
;
541 OutputText(IDS_INVALID_OPTION
, argv
[i
]);
549 StringCchCopyW(Info
.HostName
, NI_MAXHOST
, argv
[i
]);
558 int wmain(int argc
, wchar_t *argv
[])
561 /* Initialize the Console Standard Streams */
565 Info
.ResolveAddresses
= true;
568 Info
.Family
= AF_UNSPEC
;
570 if (!ParseCmdline(argc
, argv
))
576 if (WSAStartup(MAKEWORD(2, 2), &WsaData
))
582 Success
= RunTraceRoute();
586 return Success
? 0 : 1;