Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- include/icmpapi.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+)
diff --git a/include/icmpapi.h b/include/icmpapi.h index 102851a..fd86e84 100644 --- a/include/icmpapi.h +++ b/include/icmpapi.h @@ -21,6 +21,8 @@ #ifndef __WINE_ICMPAPI_H #define __WINE_ICMPAPI_H
+#include "winternl.h" + #ifdef __cplusplus extern "C" { #endif @@ -30,10 +32,24 @@ HANDLE WINAPI IcmpCreateFile( VOID );
+HANDLE WINAPI Icmp6CreateFile( + VOID + ); + BOOL WINAPI IcmpCloseHandle( HANDLE IcmpHandle );
+DWORD WINAPI IcmpParseReplies( + LPVOID ReplyBuffer, + DWORD ReplySize + ); + +DWORD WINAPI Icmp6ParseReplies( + LPVOID ReplyBuffer, + DWORD ReplySize + ); + DWORD WINAPI IcmpSendEcho( HANDLE IcmpHandle, IPAddr DestinationAddress, @@ -45,6 +61,50 @@ DWORD WINAPI IcmpSendEcho( DWORD Timeout );
+DWORD WINAPI IcmpSendEcho2( + HANDLE IcmpHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + IPAddr DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout + ); + +DWORD WINAPI IcmpSendEcho2Ex( + HANDLE IcmpHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + IPAddr SourceAddress, + IPAddr DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout + ); + +DWORD WINAPI Icmp6SendEcho2( + HANDLE IcmpHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + struct sockaddr_in6* SourceAddress, + struct sockaddr_in6* DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout + ); +
#ifdef __cplusplus }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 6965ae8..1465e26 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -1027,6 +1027,8 @@ static void testIcmpSendEcho(void) ok(reply->Status == IP_SUCCESS, "Expect status:0x%08x, got:0x%08x\n", IP_SUCCESS, reply->Status); ok(reply->DataSize == sizeof(senddata), "Got size:%d\n", reply->DataSize); ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + IcmpCloseHandle(icmp); }
/*
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 1465e26..6cbe725 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -996,7 +996,11 @@ static void testIcmpSendEcho(void) address = htonl(INADDR_LOOPBACK); ret = IcmpSendEcho(icmp, address, senddata, sizeof(senddata), NULL, replydata, replysz, 1000); error = GetLastError(); - if (ret) + if (!ret) + { + skip ("Failed to ping with error %d, is lo interface down?.\n", error); + } + else if (winetest_debug > 1) { PICMP_ECHO_REPLY pong = (PICMP_ECHO_REPLY) replydata; trace ("send addr : %s\n", ntoa(address)); @@ -1008,10 +1012,6 @@ static void testIcmpSendEcho(void) trace ("ttl : %u\n", pong->Options.Ttl); trace ("flags : 0x%x\n", pong->Options.Flags); } - else - { - skip ("Failed to ping with error %d, is lo interface down?.\n", error); - }
/* check reply data */ SetLastError(0xdeadbeef);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@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); }
/***********************************************************************
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 141 ++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 77 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index 1aeba1c..b6d2ffd 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -442,6 +442,50 @@ DWORD WINAPI IcmpSendEcho( DWORD ReplySize, DWORD Timeout ) +{ + return IcmpSendEcho2Ex(IcmpHandle, NULL, NULL, NULL, 0, DestinationAddress, + RequestData, RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); +} + +/*********************************************************************** + * IcmpSendEcho2 (IPHLPAPI.@) + */ +DWORD WINAPI IcmpSendEcho2( + HANDLE IcmpHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + IPAddr DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout + ) +{ + return IcmpSendEcho2Ex(IcmpHandle, Event, ApcRoutine, ApcContext, 0, + DestinationAddress, RequestData, RequestSize, RequestOptions, + ReplyBuffer, ReplySize, Timeout); +} + +/*********************************************************************** + * IcmpSendEcho2Ex (IPHLPAPI.@) + */ +DWORD WINAPI IcmpSendEcho2Ex( + HANDLE IcmpHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + IPAddr SourceAddress, + IPAddr DestinationAddress, + LPVOID RequestData, + WORD RequestSize, + PIP_OPTION_INFORMATION RequestOptions, + LPVOID ReplyBuffer, + DWORD ReplySize, + DWORD Timeout + ) { icmp_t* icp=(icmp_t*)IcmpHandle; struct icmp* icmp_header; @@ -451,6 +495,10 @@ DWORD WINAPI IcmpSendEcho( int reqsize, repsize; DWORD send_time;
+ TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d)\n", IcmpHandle, + Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, + RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); + if (IcmpHandle==INVALID_HANDLE_VALUE) { /* FIXME: in fact win98 seems to ignore the handle value !!! */ SetLastError(ERROR_INVALID_PARAMETER); @@ -473,6 +521,22 @@ DWORD WINAPI IcmpSendEcho( return 0; }
+ if (Event) + { + FIXME("unsupported for events\n"); + return 0; + } + if (ApcRoutine) + { + FIXME("unsupported for APCs\n"); + return 0; + } + if (SourceAddress) + { + FIXME("unsupported for source addresses\n"); + return 0; + } + /* Prepare the request */ id=getpid() & 0xFFFF; seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF; @@ -571,83 +635,6 @@ DWORD WINAPI IcmpSendEcho( return icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); }
-/*********************************************************************** - * IcmpSendEcho2 (IPHLPAPI.@) - */ -DWORD WINAPI IcmpSendEcho2( - HANDLE IcmpHandle, - HANDLE Event, - PIO_APC_ROUTINE ApcRoutine, - PVOID ApcContext, - IPAddr DestinationAddress, - LPVOID RequestData, - WORD RequestSize, - PIP_OPTION_INFORMATION RequestOptions, - LPVOID ReplyBuffer, - DWORD ReplySize, - DWORD Timeout - ) -{ - TRACE("(%p, %p, %p, %p, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, - Event, ApcRoutine, ApcContext, DestinationAddress, RequestData, - RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); - - if (Event) - { - FIXME("unsupported for events\n"); - return 0; - } - if (ApcRoutine) - { - FIXME("unsupported for APCs\n"); - return 0; - } - return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, - RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); -} - -/*********************************************************************** - * IcmpSendEcho2Ex (IPHLPAPI.@) - */ -DWORD WINAPI IcmpSendEcho2Ex( - HANDLE IcmpHandle, - HANDLE Event, - PIO_APC_ROUTINE ApcRoutine, - PVOID ApcContext, - IPAddr SourceAddress, - IPAddr DestinationAddress, - LPVOID RequestData, - WORD RequestSize, - PIP_OPTION_INFORMATION RequestOptions, - LPVOID ReplyBuffer, - DWORD ReplySize, - DWORD Timeout - ) -{ - TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle, - Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, - RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); - - if (Event) - { - FIXME("unsupported for events\n"); - return 0; - } - if (ApcRoutine) - { - FIXME("unsupported for APCs\n"); - return 0; - } - if (SourceAddress) - { - FIXME("unsupported for source addresses\n"); - return 0; - } - - return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData, - RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout); -} - /* * Copyright (c) 1989 The Regents of the University of California. * All rights reserved.
The code above it already sets endbuf to the beginning of the last data added. On a new reply, endbuf is decremented to make space for the new data. There's no reason to adjust it again for the old data after it has already been set.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 1 - 1 file changed, 1 deletion(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index b6d2ffd..79fd35f 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -299,7 +299,6 @@ static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, voi }
/* Prepare for the next packet */ - endbuf-=ier->DataSize; ier++;
/* Check out whether there is more but don't wait this time */
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index 79fd35f..d59bb4b 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -309,6 +309,25 @@ static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, voi if (res==0) SetLastError(IP_REQ_TIMED_OUT); done: + if (res) + { + /* Move the data so there's no gap between it and the ICMP_ECHO_REPLY array */ + DWORD gap_size = endbuf - (char*)ier; + + if (gap_size) + { + memmove(ier, endbuf, ((char*)reply_buf + reply_size) - endbuf); + + /* Fix the pointers */ + while (ier-- != reply_buf) + { + ier->Data = (char*)ier->Data - gap_size; + if (ier->Options.OptionsData) + ier->Options.OptionsData -= gap_size; + } + } + } + HeapFree(GetProcessHeap(), 0, buffer); TRACE("received %d replies\n",res); return res;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index d59bb4b..8ef0321 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -325,6 +325,20 @@ done: if (ier->Options.OptionsData) ier->Options.OptionsData -= gap_size; } + + /* According to MSDN, the reply buffer needs to hold space for a IO_STATUS_BLOCK, + found at the very end of the reply. This is confirmed on Windows XP, but Vista + and later do not store it anywhere and in fact don't even require it at all. + + However, in case old apps analyze this IO_STATUS_BLOCK and expect it, we mimic + it and write it out if there's enough space available in the buffer. */ + if (gap_size >= sizeof(IO_STATUS_BLOCK)) + { + IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)((char*)reply_buf + reply_size - sizeof(IO_STATUS_BLOCK)); + + io->Pointer = NULL; /* Always NULL or STATUS_SUCCESS */ + io->Information = reply_size - gap_size; + } } }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 117 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 8 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index 8ef0321..722620a 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -106,9 +106,51 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
typedef struct { int sid; + LONG lock_count; + HANDLE lock_thread; IP_OPTION_INFORMATION default_opts; } icmp_t;
+static void icmp_lock(icmp_t *icp) +{ + InterlockedIncrement(&icp->lock_count); +} + +static void icmp_unlock(icmp_t *icp) +{ + if (InterlockedDecrement(&icp->lock_count) == 0x80000000) + { + HANDLE thread = InterlockedExchangePointer(&icp->lock_thread, NULL); + + if (thread) + { + NtAlertThread(thread); + CloseHandle(thread); + } + } +} + +static void icmp_wait_for_released_locks(icmp_t *icp) +{ + DWORD wait = INFINITE; + + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &icp->lock_thread, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + icp->lock_thread = NULL; + wait = 1; + } + + if (InterlockedExchangeAdd(&icp->lock_count, 0x80000000) != 0) + { + do + SleepEx(wait, TRUE); + while (icp->lock_count != 0x80000000 || icp->lock_thread); + } + else + if (icp->lock_thread) CloseHandle(icp->lock_thread); +} + #define IP_OPTS_UNKNOWN 0 #define IP_OPTS_DEFAULT 1 #define IP_OPTS_CUSTOM 2 @@ -347,6 +389,30 @@ done: return res; }
+struct icmp_get_reply_async_ctx +{ + icmp_t *icp; + HANDLE event; + unsigned char *buffer; + void *reply_buf; + DWORD reply_size; + DWORD send_time; + DWORD timeout; +}; + +static DWORD WINAPI icmp_get_reply_async_func(VOID *parameter) +{ + struct icmp_get_reply_async_ctx *ctx = parameter; + + icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, ctx->reply_size, ctx->timeout); + + SetEvent(ctx->event); + + icmp_unlock(ctx->icp); + HeapFree(GetProcessHeap(), 0, ctx); + return 0; +} +
/* @@ -381,6 +447,7 @@ HANDLE WINAPI Icmp6CreateFile(VOID) } icp->sid=sid; icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; + icp->lock_count=0; return (HANDLE)icp; }
@@ -439,6 +506,7 @@ HANDLE WINAPI IcmpCreateFile(VOID) } icp->sid=sid; icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; + icp->lock_count=0; return (HANDLE)icp; }
@@ -455,6 +523,9 @@ BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle) return FALSE; }
+ /* Windows waits until all outstanding async requests are complete or timed out */ + icmp_wait_for_released_locks(icp); + close( icp->sid ); HeapFree(GetProcessHeap (), 0, icp); return TRUE; @@ -526,6 +597,7 @@ DWORD WINAPI IcmpSendEcho2Ex( unsigned char *buffer; int reqsize, repsize; DWORD send_time; + DWORD res = 0;
TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d)\n", IcmpHandle, Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, @@ -553,11 +625,6 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
- if (Event) - { - FIXME("unsupported for events\n"); - return 0; - } if (ApcRoutine) { FIXME("unsupported for APCs\n"); @@ -569,6 +636,8 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
+ icmp_lock(icp); + /* Prepare the request */ id=getpid() & 0xFFFF; seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF; @@ -580,7 +649,7 @@ DWORD WINAPI IcmpSendEcho2Ex( buffer = HeapAlloc(GetProcessHeap(), 0, max( repsize, reqsize )); if (buffer == NULL) { SetLastError(ERROR_OUTOFMEMORY); - return 0; + goto done; }
icmp_header=(struct icmp*)buffer; @@ -661,10 +730,42 @@ DWORD WINAPI IcmpSendEcho2Ex( } } HeapFree(GetProcessHeap(), 0, buffer); - return 0; + goto done; }
- return icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + if (Event) + { + struct icmp_get_reply_async_ctx *ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx)); + + if (ctx) + { + ctx->icp = icp; + ctx->event = Event; + ctx->buffer = buffer; + ctx->reply_buf = ReplyBuffer; + ctx->reply_size = ReplySize; + ctx->send_time = send_time; + ctx->timeout = Timeout; + if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + { + SetLastError(ERROR_IO_PENDING); + return 0; + } + + HeapFree(GetProcessHeap(), 0, ctx); + } + else + SetLastError(ERROR_OUTOFMEMORY); + + HeapFree(GetProcessHeap(), 0, buffer); + goto done; + } + + res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + +done: + icmp_unlock(icp); + return res; }
/*
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 202 +++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 6cbe725..63ce27e 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -865,9 +865,11 @@ static void testIcmpSendEcho(void) { HANDLE icmp; char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; + char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); IPAddr address; ICMP_ECHO_REPLY *reply; + HANDLE event; INT i;
memset(senddata, 0, sizeof(senddata)); @@ -881,6 +883,15 @@ static void testIcmpSendEcho(void) || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, "expected 87, got %d\n", error);
+ address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(INVALID_HANDLE_VALUE, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata, replysz, 1000); + error = GetLastError(); + ok (!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok (error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, + "expected 87, got %d\n", error); + icmp = IcmpCreateFile(); if (icmp == INVALID_HANDLE_VALUE) { @@ -1028,6 +1039,197 @@ static void testIcmpSendEcho(void) ok(reply->DataSize == sizeof(senddata), "Got size:%d\n", reply->DataSize); ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
+ + /* + * IcmpSendEcho2 + */ + address = 0; + replysz = sizeof(replydata2); + memset(senddata, 0, sizeof(senddata)); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == IP_BAD_DESTINATION) /* <= 2003 */, + "expected 1214, got %d\n", error); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "CreateEventW failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == ERROR_IO_PENDING) /* <= 2003 */, + "Got last error: 0x%08x\n", error); + if (error == ERROR_IO_PENDING) + { + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + } + + address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + /* synchronous tests */ + SetLastError(0xdeadbeef); + address = htonl(INADDR_LOOPBACK); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, NULL, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK) + ICMP_MINLEN; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + if (!ret) + { + error = GetLastError(); + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ret, "IcmpSendEcho2 failed unexpectedly\n"); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + /* asynchronous tests with event */ + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + address = htonl(INADDR_LOOPBACK); + memset(senddata, 0, sizeof(senddata)); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + if (!ret && error != ERROR_IO_PENDING) + { + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else + { + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */ + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + CloseHandle(event); IcmpCloseHandle(icmp); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 112 +++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 25 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index 722620a..cac1dc3 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -71,6 +71,7 @@ #include "winternl.h" #include "ipexport.h" #include "icmpapi.h" +#include "wine/asm.h" #include "wine/debug.h"
/* Set up endianness macros for the ip and ip_icmp BSD headers */ @@ -187,11 +188,11 @@ static int in_cksum(u_short *addr, int len) }
/* 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) +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); + int repsize = MAXIPLEN + MAXICMPLEN + min(65535, *reply_size); struct icmp *icmp_header = (struct icmp*)buffer; - char *endbuf = (char*)reply_buf + reply_size; + 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; @@ -354,11 +355,13 @@ done: if (res) { /* Move the data so there's no gap between it and the ICMP_ECHO_REPLY array */ + char *reply_end = (char*)reply_buf + *reply_size; DWORD gap_size = endbuf - (char*)ier;
if (gap_size) { - memmove(ier, endbuf, ((char*)reply_buf + reply_size) - endbuf); + *reply_size -= gap_size; + memmove(ier, endbuf, reply_end - endbuf);
/* Fix the pointers */ while (ier-- != reply_buf) @@ -376,13 +379,15 @@ done: it and write it out if there's enough space available in the buffer. */ if (gap_size >= sizeof(IO_STATUS_BLOCK)) { - IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)((char*)reply_buf + reply_size - sizeof(IO_STATUS_BLOCK)); + IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)(reply_end - sizeof(IO_STATUS_BLOCK));
io->Pointer = NULL; /* Always NULL or STATUS_SUCCESS */ - io->Information = reply_size - gap_size; + io->Information = *reply_size; } } } + else + *reply_size = 8; /* ICMP error message */
HeapFree(GetProcessHeap(), 0, buffer); TRACE("received %d replies\n",res); @@ -393,6 +398,9 @@ struct icmp_get_reply_async_ctx { icmp_t *icp; HANDLE event; + PIO_APC_ROUTINE apc; + void *apc_ctx; + HANDLE thread; unsigned char *buffer; void *reply_buf; DWORD reply_size; @@ -400,13 +408,52 @@ struct icmp_get_reply_async_ctx DWORD timeout; };
+#ifdef __i386__ +/* The stdcall calling convention has the callee clean the stack. Vista and later + * have different callback signatures, so we can't rely on it restoring the stack. + */ +extern void CALLBACK icmp_get_reply_async_call_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +__ASM_GLOBAL_FUNC(icmp_get_reply_async_call_apc, + "pushl %ebp\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") + __ASM_CFI(".cfi_rel_offset %ebp,0\n\t") + "movl %esp,%ebp\n\t" + __ASM_CFI(".cfi_def_cfa_register %ebp\n\t") + "pushl 16(%ebp)\n\t" /* io.Information */ + "pushl $0\n\t" /* io.Status */ + "movl %esp,%eax\n\t" + "pushl $0\n\t" + "pushl %eax\n\t" + "pushl 12(%ebp)\n\t" + "call *8(%ebp)\n\t" + "leave\n\t" + __ASM_CFI(".cfi_def_cfa %esp,4\n\t") + __ASM_CFI(".cfi_same_value %ebp\n\t") + "ret") +#else +static void CALLBACK icmp_get_reply_async_call_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3) +{ + IO_STATUS_BLOCK io; + + io.Pointer = NULL; /* Always NULL or STATUS_SUCCESS */ + io.Information = arg3; + ((PIO_APC_ROUTINE)arg1)(arg2, &io, 0); +} +#endif + static DWORD WINAPI icmp_get_reply_async_func(VOID *parameter) { struct icmp_get_reply_async_ctx *ctx = parameter;
- icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, ctx->reply_size, ctx->timeout); + icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, &ctx->reply_size, ctx->timeout);
- SetEvent(ctx->event); + if (ctx->apc) + { + NtQueueApcThread(ctx->thread, icmp_get_reply_async_call_apc, (ULONG_PTR)ctx->apc, (ULONG_PTR)ctx->apc_ctx, ctx->reply_size); + CloseHandle(ctx->thread); + } + if (ctx->event) + SetEvent(ctx->event);
icmp_unlock(ctx->icp); HeapFree(GetProcessHeap(), 0, ctx); @@ -625,11 +672,6 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
- if (ApcRoutine) - { - FIXME("unsupported for APCs\n"); - return 0; - } if (SourceAddress) { FIXME("unsupported for source addresses\n"); @@ -733,25 +775,45 @@ DWORD WINAPI IcmpSendEcho2Ex( goto done; }
- if (Event) + if (Event || ApcRoutine) { struct icmp_get_reply_async_ctx *ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx));
if (ctx) { - ctx->icp = icp; - ctx->event = Event; - ctx->buffer = buffer; - ctx->reply_buf = ReplyBuffer; - ctx->reply_size = ReplySize; - ctx->send_time = send_time; - ctx->timeout = Timeout; - if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + BOOL failed = FALSE; + + /* The APC is executed only if there's no event on Vista and later */ + ctx->apc = NULL; + if (ApcRoutine && !(Event && LOBYTE(LOWORD(GetVersion())) >= 6)) { - SetLastError(ERROR_IO_PENDING); - return 0; + if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &ctx->thread, 0, FALSE, DUPLICATE_SAME_ACCESS)) + ctx->apc = ApcRoutine; + else + failed = TRUE; }
+ if (!failed) + { + ctx->icp = icp; + ctx->event = Event; + ctx->apc_ctx = ApcContext; + ctx->buffer = buffer; + ctx->reply_buf = ReplyBuffer; + ctx->reply_size = ReplySize; + ctx->send_time = send_time; + ctx->timeout = Timeout; + + if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + { + SetLastError(ERROR_IO_PENDING); + return 0; + } + + if (ctx->apc) + CloseHandle(ctx->thread); + } HeapFree(GetProcessHeap(), 0, ctx); } else @@ -761,7 +823,7 @@ DWORD WINAPI IcmpSendEcho2Ex( goto done; }
- res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, &ReplySize, Timeout);
done: icmp_unlock(icp);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 63ce27e..da842e5 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -861,8 +861,28 @@ static void testSetTcpEntry(void) "got %u, expected %u\n", ret, ERROR_MR_MID_NOT_FOUND); }
+static BOOL icmp_send_echo_test_apc_expect; +static void WINAPI icmp_send_echo_test_apc_XP(void *context) +{ + ok(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n"); + ok(context == (void*)0xdeadc0de, "Wrong context: %p\n", context); + icmp_send_echo_test_apc_expect = FALSE; +} + +static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved) +{ + icmp_send_echo_test_apc_XP(context); + ok(io_status->Status == 0, "Got IO Status 0x%08x\n", io_status->Status); + ok(io_status->Information == sizeof(ICMP_ECHO_REPLY) + 32 /* sizeof(senddata) */, + "Got IO Information %lu\n", io_status->Information); +} + static void testIcmpSendEcho(void) { + /* The APC function's signature is different pre-Vista */ + const PIO_APC_ROUTINE apc = broken(LOBYTE(LOWORD(GetVersion())) < 6) + ? (PIO_APC_ROUTINE)icmp_send_echo_test_apc_XP + : icmp_send_echo_test_apc; HANDLE icmp; char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; @@ -1230,6 +1250,32 @@ static void testIcmpSendEcho(void) ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
CloseHandle(event); + + /* asynchronous tests with APC */ + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + address = htonl(INADDR_LOOPBACK); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff; + icmp_send_echo_test_apc_expect = TRUE; + /* + NOTE: On versions Vista and up, supplying both an event and apc results in only the event being used. + This is unreliable since older versions tend to either use both, or only the apc, so not tested. + */ + ret = IcmpSendEcho2(icmp, NULL, apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error); + SleepEx(200, TRUE); + SleepEx(0, TRUE); + ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n"); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK), + ntoa(reply->Address)); + ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status); + ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize); + /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */ + ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + IcmpCloseHandle(icmp); }