[PATCH 04/12] iphlpapi: Move the ICMP reply retrieval to a helper function.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Wed Sep 23 09:28:25 CDT 2020
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
dlls/iphlpapi/icmp.c | 351 ++++++++++++++++++++++---------------------
1 file changed, 178 insertions(+), 173 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c
index 8e425ea..1aeba1c 100644
--- a/dlls/iphlpapi/icmp.c
+++ b/dlls/iphlpapi/icmp.c
@@ -144,6 +144,177 @@ static int in_cksum(u_short *addr, int len)
return(answer);
}
+/* Receive a reply (IPv4); this function uses, takes ownership of and will always free `buffer` */
+static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, void *reply_buf, DWORD reply_size, DWORD timeout)
+{
+ int repsize = MAXIPLEN + MAXICMPLEN + min(65535, reply_size);
+ struct icmp *icmp_header = (struct icmp*)buffer;
+ char *endbuf = (char*)reply_buf + reply_size;
+ struct ip *ip_header = (struct ip*)buffer;
+ struct icmp_echo_reply *ier = reply_buf;
+ unsigned short id, seq, cksum;
+ struct sockaddr_in addr;
+ int ip_header_len = 0;
+ socklen_t addrlen;
+ struct pollfd fdr;
+ DWORD recv_time;
+ int res;
+
+ id = icmp_header->icmp_id;
+ seq = icmp_header->icmp_seq;
+ cksum = icmp_header->icmp_cksum;
+ fdr.fd = sid;
+ fdr.events = POLLIN;
+ addrlen = sizeof(addr);
+
+ while (poll(&fdr,1,timeout)>0) {
+ recv_time = GetTickCount();
+ res=recvfrom(sid, buffer, repsize, 0, (struct sockaddr*)&addr, &addrlen);
+ TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
+ ier->Status=IP_REQ_TIMED_OUT;
+
+ /* Check whether we should ignore this packet */
+ if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) {
+ ip_header_len=ip_header->ip_hl << 2;
+ icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
+ TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code);
+ if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
+ if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
+ {
+ ier->Status=IP_SUCCESS;
+ SetLastError(NO_ERROR);
+ }
+ } else {
+ switch (icmp_header->icmp_type) {
+ case ICMP_UNREACH:
+ switch (icmp_header->icmp_code) {
+ case ICMP_UNREACH_HOST:
+#ifdef ICMP_UNREACH_HOST_UNKNOWN
+ case ICMP_UNREACH_HOST_UNKNOWN:
+#endif
+#ifdef ICMP_UNREACH_ISOLATED
+ case ICMP_UNREACH_ISOLATED:
+#endif
+#ifdef ICMP_UNREACH_HOST_PROHIB
+ case ICMP_UNREACH_HOST_PROHIB:
+#endif
+#ifdef ICMP_UNREACH_TOSHOST
+ case ICMP_UNREACH_TOSHOST:
+#endif
+ ier->Status=IP_DEST_HOST_UNREACHABLE;
+ break;
+ case ICMP_UNREACH_PORT:
+ ier->Status=IP_DEST_PORT_UNREACHABLE;
+ break;
+ case ICMP_UNREACH_PROTOCOL:
+ ier->Status=IP_DEST_PROT_UNREACHABLE;
+ break;
+ case ICMP_UNREACH_SRCFAIL:
+ ier->Status=IP_BAD_ROUTE;
+ break;
+ default:
+ ier->Status=IP_DEST_NET_UNREACHABLE;
+ }
+ break;
+ case ICMP_TIMXCEED:
+ if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
+ ier->Status=IP_TTL_EXPIRED_REASSEM;
+ else
+ ier->Status=IP_TTL_EXPIRED_TRANSIT;
+ break;
+ case ICMP_PARAMPROB:
+ ier->Status=IP_PARAM_PROBLEM;
+ break;
+ case ICMP_SOURCEQUENCH:
+ ier->Status=IP_SOURCE_QUENCH;
+ break;
+ }
+ if (ier->Status!=IP_REQ_TIMED_OUT) {
+ struct ip* rep_ip_header;
+ struct icmp* rep_icmp_header;
+ /* The ICMP header size of all the packets we accept is the same */
+ rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN);
+ rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
+
+ /* Make sure that this is really a reply to our packet */
+ if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) {
+ ier->Status=IP_REQ_TIMED_OUT;
+ } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
+ (rep_icmp_header->icmp_code!=0) ||
+ (rep_icmp_header->icmp_id!=id) ||
+ /* windows doesn't check this checksum, else tracert */
+ /* behind a Linux 2.2 masquerading firewall would fail*/
+ /* (rep_icmp_header->icmp_cksum!=cksum) || */
+ (rep_icmp_header->icmp_seq!=seq)) {
+ /* This was not a reply to one of our packets after all */
+ TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n",
+ rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
+ rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
+ rep_icmp_header->icmp_cksum);
+ TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
+ id,seq,
+ cksum);
+ ier->Status=IP_REQ_TIMED_OUT;
+ }
+ }
+ }
+ }
+
+ if (ier->Status==IP_REQ_TIMED_OUT) {
+ /* This packet was not for us.
+ * Decrease the timeout so that we don't enter an endless loop even
+ * if we get flooded with ICMP packets that are not for us.
+ */
+ DWORD t = (recv_time - send_time);
+ if (timeout > t) timeout -= t;
+ else timeout = 0;
+ continue;
+ } else {
+ /* Check free space, should be large enough for an ICMP_ECHO_REPLY and remainning icmp data */
+ if (endbuf-(char *)ier < sizeof(struct icmp_echo_reply)+(res-ip_header_len-ICMP_MINLEN)) {
+ res=ier-(ICMP_ECHO_REPLY *)reply_buf;
+ SetLastError(IP_GENERAL_FAILURE);
+ goto done;
+ }
+ /* This is a reply to our packet */
+ memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
+ /* Status is already set */
+ ier->RoundTripTime= recv_time - send_time;
+ ier->DataSize=res-ip_header_len-ICMP_MINLEN;
+ ier->Reserved=0;
+ ier->Data=endbuf-ier->DataSize;
+ memcpy(ier->Data, ((char *)ip_header)+ip_header_len+ICMP_MINLEN, ier->DataSize);
+ ier->Options.Ttl=ip_header->ip_ttl;
+ ier->Options.Tos=ip_header->ip_tos;
+ ier->Options.Flags=ip_header->ip_off >> 13;
+ ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
+ if (ier->Options.OptionsSize!=0) {
+ ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize;
+ /* FIXME: We are supposed to rearrange the option's 'source route' data */
+ memcpy(ier->Options.OptionsData, ((char *)ip_header)+ip_header_len, ier->Options.OptionsSize);
+ endbuf=(char*)ier->Options.OptionsData;
+ } else {
+ ier->Options.OptionsData=NULL;
+ endbuf=ier->Data;
+ }
+
+ /* Prepare for the next packet */
+ endbuf-=ier->DataSize;
+ ier++;
+
+ /* Check out whether there is more but don't wait this time */
+ timeout=0;
+ }
+ }
+ res=ier-(ICMP_ECHO_REPLY*)reply_buf;
+ if (res==0)
+ SetLastError(IP_REQ_TIMED_OUT);
+done:
+ HeapFree(GetProcessHeap(), 0, buffer);
+ TRACE("received %d replies\n",res);
+ return res;
+}
+
/*
@@ -273,20 +444,12 @@ DWORD WINAPI IcmpSendEcho(
)
{
icmp_t* icp=(icmp_t*)IcmpHandle;
- unsigned char *buffer;
- int reqsize, repsize;
-
- struct icmp_echo_reply* ier;
- struct ip* ip_header;
struct icmp* icmp_header;
- char* endbuf;
- int ip_header_len;
- struct pollfd fdr;
- DWORD send_time,recv_time;
struct sockaddr_in addr;
- socklen_t addrlen;
- unsigned short id,seq,cksum;
- int res;
+ unsigned short id, seq;
+ unsigned char *buffer;
+ int reqsize, repsize;
+ DWORD send_time;
if (IcmpHandle==INVALID_HANDLE_VALUE) {
/* FIXME: in fact win98 seems to ignore the handle value !!! */
@@ -331,7 +494,7 @@ DWORD WINAPI IcmpSendEcho(
icmp_header->icmp_id=id;
icmp_header->icmp_seq=seq;
memcpy(buffer+ICMP_MINLEN, RequestData, RequestSize);
- icmp_header->icmp_cksum=cksum=in_cksum((u_short*)buffer,reqsize);
+ icmp_header->icmp_cksum=in_cksum((u_short*)buffer,reqsize);
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=DestinationAddress;
@@ -372,15 +535,6 @@ DWORD WINAPI IcmpSendEcho(
icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
}
- /* Get ready for receiving the reply
- * Do it before we send the request to minimize the risk of introducing delays
- */
- fdr.fd = icp->sid;
- fdr.events = POLLIN;
- addrlen=sizeof(addr);
- ier=ReplyBuffer;
- endbuf=(char *) ReplyBuffer+ReplySize;
-
/* Send the packet */
TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, inet_ntoa(addr.sin_addr));
#if 0
@@ -394,8 +548,7 @@ DWORD WINAPI IcmpSendEcho(
#endif
send_time = GetTickCount();
- res=sendto(icp->sid, buffer, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr));
- if (res<0) {
+ if (sendto(icp->sid, buffer, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
if (errno==EMSGSIZE)
SetLastError(IP_PACKET_TOO_BIG);
else {
@@ -415,155 +568,7 @@ DWORD WINAPI IcmpSendEcho(
return 0;
}
- /* Get the reply */
- ip_header=(struct ip*)buffer;
- ip_header_len=0; /* because gcc was complaining */
- while (poll(&fdr,1,Timeout)>0) {
- recv_time = GetTickCount();
- res=recvfrom(icp->sid, buffer, repsize, 0, (struct sockaddr*)&addr, &addrlen);
- TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
- ier->Status=IP_REQ_TIMED_OUT;
-
- /* Check whether we should ignore this packet */
- if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct ip)+ICMP_MINLEN)) {
- ip_header_len=ip_header->ip_hl << 2;
- icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
- TRACE("received an ICMP packet of type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code);
- if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
- if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
- {
- ier->Status=IP_SUCCESS;
- SetLastError(NO_ERROR);
- }
- } else {
- switch (icmp_header->icmp_type) {
- case ICMP_UNREACH:
- switch (icmp_header->icmp_code) {
- case ICMP_UNREACH_HOST:
-#ifdef ICMP_UNREACH_HOST_UNKNOWN
- case ICMP_UNREACH_HOST_UNKNOWN:
-#endif
-#ifdef ICMP_UNREACH_ISOLATED
- case ICMP_UNREACH_ISOLATED:
-#endif
-#ifdef ICMP_UNREACH_HOST_PROHIB
- case ICMP_UNREACH_HOST_PROHIB:
-#endif
-#ifdef ICMP_UNREACH_TOSHOST
- case ICMP_UNREACH_TOSHOST:
-#endif
- ier->Status=IP_DEST_HOST_UNREACHABLE;
- break;
- case ICMP_UNREACH_PORT:
- ier->Status=IP_DEST_PORT_UNREACHABLE;
- break;
- case ICMP_UNREACH_PROTOCOL:
- ier->Status=IP_DEST_PROT_UNREACHABLE;
- break;
- case ICMP_UNREACH_SRCFAIL:
- ier->Status=IP_BAD_ROUTE;
- break;
- default:
- ier->Status=IP_DEST_NET_UNREACHABLE;
- }
- break;
- case ICMP_TIMXCEED:
- if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
- ier->Status=IP_TTL_EXPIRED_REASSEM;
- else
- ier->Status=IP_TTL_EXPIRED_TRANSIT;
- break;
- case ICMP_PARAMPROB:
- ier->Status=IP_PARAM_PROBLEM;
- break;
- case ICMP_SOURCEQUENCH:
- ier->Status=IP_SOURCE_QUENCH;
- break;
- }
- if (ier->Status!=IP_REQ_TIMED_OUT) {
- struct ip* rep_ip_header;
- struct icmp* rep_icmp_header;
- /* The ICMP header size of all the packets we accept is the same */
- rep_ip_header=(struct ip*)(((char*)icmp_header)+ICMP_MINLEN);
- rep_icmp_header=(struct icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
-
- /* Make sure that this is really a reply to our packet */
- if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 2)+ICMP_MINLEN>ip_header->ip_len) {
- ier->Status=IP_REQ_TIMED_OUT;
- } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
- (rep_icmp_header->icmp_code!=0) ||
- (rep_icmp_header->icmp_id!=id) ||
- /* windows doesn't check this checksum, else tracert */
- /* behind a Linux 2.2 masquerading firewall would fail*/
- /* (rep_icmp_header->icmp_cksum!=cksum) || */
- (rep_icmp_header->icmp_seq!=seq)) {
- /* This was not a reply to one of our packets after all */
- TRACE("skipping type,code=%d,%d id,seq=%d,%d cksum=%d\n",
- rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
- rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
- rep_icmp_header->icmp_cksum);
- TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
- id,seq,
- cksum);
- ier->Status=IP_REQ_TIMED_OUT;
- }
- }
- }
- }
-
- if (ier->Status==IP_REQ_TIMED_OUT) {
- /* This packet was not for us.
- * Decrease the timeout so that we don't enter an endless loop even
- * if we get flooded with ICMP packets that are not for us.
- */
- DWORD t = (recv_time - send_time);
- if (Timeout > t) Timeout -= t;
- else Timeout = 0;
- continue;
- } else {
- /* Check free space, should be large enough for an ICMP_ECHO_REPLY and remainning icmp data */
- if (endbuf-(char *)ier < sizeof(struct icmp_echo_reply)+(res-ip_header_len-ICMP_MINLEN)) {
- res=ier-(ICMP_ECHO_REPLY *)ReplyBuffer;
- SetLastError(IP_GENERAL_FAILURE);
- goto done;
- }
- /* This is a reply to our packet */
- memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
- /* Status is already set */
- ier->RoundTripTime= recv_time - send_time;
- ier->DataSize=res-ip_header_len-ICMP_MINLEN;
- ier->Reserved=0;
- ier->Data=endbuf-ier->DataSize;
- memcpy(ier->Data, ((char *)ip_header)+ip_header_len+ICMP_MINLEN, ier->DataSize);
- ier->Options.Ttl=ip_header->ip_ttl;
- ier->Options.Tos=ip_header->ip_tos;
- ier->Options.Flags=ip_header->ip_off >> 13;
- ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
- if (ier->Options.OptionsSize!=0) {
- ier->Options.OptionsData=(unsigned char *) ier->Data-ier->Options.OptionsSize;
- /* FIXME: We are supposed to rearrange the option's 'source route' data */
- memcpy(ier->Options.OptionsData, ((char *)ip_header)+ip_header_len, ier->Options.OptionsSize);
- endbuf=(char*)ier->Options.OptionsData;
- } else {
- ier->Options.OptionsData=NULL;
- endbuf=ier->Data;
- }
-
- /* Prepare for the next packet */
- endbuf-=ier->DataSize;
- ier++;
-
- /* Check out whether there is more but don't wait this time */
- Timeout=0;
- }
- }
- res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer;
- if (res==0)
- SetLastError(IP_REQ_TIMED_OUT);
-done:
- HeapFree(GetProcessHeap(), 0, buffer);
- TRACE("received %d replies\n",res);
- return res;
+ return icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout);
}
/***********************************************************************
--
2.21.0
More information about the wine-devel
mailing list