LMikl
asked on
Windows sockets
How can I send multiply pings form different thread? I make it with Microsoft recommended example, but there I have some problem. Raw socket listen to the all ping replies, how can I listen to the different replies separately.
Did you check the IO Completion port option ?
CreateIOCompletionPort();
Create some threads and call the GetQueuedCompletionStatus( ) waiting for completion on Queued activity.
if you want sample for this I cam give it.
CreateIOCompletionPort();
Create some threads and call the GetQueuedCompletionStatus(
if you want sample for this I cam give it.
Note to 2. :
The source address of some reply will not necessarily be the address of the pinged host (host unreachable for instance). You have to use some other mechanism (sequence numbers, packet content, whatever) in order to univoquely identify the responses and associate them to the ICMP Echo requests you sent.
A pleasure to read your prose, Alf :)
The source address of some reply will not necessarily be the address of the pinged host (host unreachable for instance). You have to use some other mechanism (sequence numbers, packet content, whatever) in order to univoquely identify the responses and associate them to the ICMP Echo requests you sent.
A pleasure to read your prose, Alf :)
__nn__
If you get host unreachable you don't get it from the host that is unreachable of course, in that case you will of course have to use some other packet content as per the PING packets sent by the regular ping program. This program uses typically packet data to identify which host the packet was intended for and even if you don't get it from that host you can still grab out that ID from the packet itself.
A ping "server" doesn't really look very hard into the packet contents - it just return the packet it received with only slight modifications to the packet itself - so you can send it more or less whatever you want and when you get the packet in return you can inspect the packet and recognize the packet from that packet contents you provided when you sent the packet.
I assumed that was obvious ;)
Alf
If you get host unreachable you don't get it from the host that is unreachable of course, in that case you will of course have to use some other packet content as per the PING packets sent by the regular ping program. This program uses typically packet data to identify which host the packet was intended for and even if you don't get it from that host you can still grab out that ID from the packet itself.
A ping "server" doesn't really look very hard into the packet contents - it just return the packet it received with only slight modifications to the packet itself - so you can send it more or less whatever you want and when you get the packet in return you can inspect the packet and recognize the packet from that packet contents you provided when you sent the packet.
I assumed that was obvious ;)
Alf
ASKER
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Sample: IPv4 and IPv6 Ping Sample
//
// Files:
// iphdr.h - IPv4 and IPv6 packet header definitions
// ping.cpp - this file
// resolve.cpp - Common name resolution routine
// resolve.h - Header file for common name resolution routines
//
// Description:
// This sample illustrates how to use raw sockets to send ICMP
// echo requests and receive their response. This sample performs
// both IPv4 and IPv6 ICMP echo requests. When using raw sockets,
// the protocol value supplied to the socket API is used as the
// protocol field (or next header field) of the IP packet. Then
// as a part of the data submitted to sendto, we include both
// the ICMP request and data.
//
// For IPv4 the IP record route option is supported via the
// IP_OPTIONS socket option.
//
// Compile:
// cl -o ping.exe ping.cpp resolve.cpp ws2_32.lib
//
// Command Line Options/Parameters:
// ping.exe [-a 4|6] [-i ttl] [-l datasize] [-r] [host]
//
// -a Address family (IPv4 or IPv6)
// -i ttl TTL value to set on socket
// -l size Amount of data to send as part of the ICMP request
// -r Use IPv4 record route
// host Hostname or literal address
//
#ifdef _IA64_
#pragma warning (disable: 4267)
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
#include "iphdr.h"
#define DEFAULT_DATA_SIZE 32 // default data size
#define DEFAULT_SEND_COUNT 4 // number of ICMP requests to send
#define DEFAULT_RECV_TIMEOUT 6000 // six second
#define DEFAULT_TTL 128
#define MAX_RECV_BUF_LEN 0xFFFF // Max incoming packet size.
int gAddressFamily=AF_UNSPEC, // Address family to use
gProtocol=IPPROTO_ICMP, // Protocol value
gTtl=DEFAULT_TTL; // Default TTL value
int gDataSize=DEFAULT_DATA_SIZ E; // Amount of data to send
BOOL bRecordRoute=FALSE; // Use IPv4 record route?
char *gDestination=NULL, // Destination
recvbuf[MAX_RECV_BUF_LEN]; // For received packets
int recvbuflen = MAX_RECV_BUF_LEN; // Length of received packets.
//
// Function: usage
//
// Description:
// Print usage information.
//
void usage(char *progname)
{
printf("usage: %s [options] <host> \n", progname);
printf(" host Remote machine to ping\n");
printf(" options: \n");
printf(" -a 4|6 Address family (default: AF_UNSPEC)\n");
printf(" -i ttl Time to live (default: 128) \n");
printf(" -l bytes Amount of data to send (default: 32) \n");
printf(" -r Record route (IPv4 only)\n");
return;
}
//
// Function: InitIcmpHeader
//
// Description:
// Helper function to fill in various stuff in our ICMP request.
//
void InitIcmpHeader(char *buf, int datasize)
{
ICMP_HDR *icmp_hdr=NULL;
char *datapart=NULL;
icmp_hdr = (ICMP_HDR *)buf;
icmp_hdr->icmp_type = ICMPV4_ECHO_REQUEST_TYPE; // request an ICMP echo
icmp_hdr->icmp_code = ICMPV4_ECHO_REQUEST_CODE;
icmp_hdr->icmp_id = (USHORT)GetCurrentProcessI d();
icmp_hdr->icmp_checksum = 0;
icmp_hdr->icmp_sequence = 0;
datapart = buf + sizeof(ICMP_HDR);
//
// Place some data in the buffer.
//
memset(datapart, 'E', datasize);
}
//
// Function: InitIcmp6Header
//
// Description:
// Initialize the ICMP6 header as well as the echo request header.
//
int InitIcmp6Header(char *buf, int datasize)
{
ICMPV6_HDR *icmp6_hdr=NULL;
ICMPV6_ECHO_REQUEST *icmp6_req=NULL;
char *datapart=NULL;
// Initialize the ICMP6 headerf ields
icmp6_hdr = (ICMPV6_HDR *)buf;
icmp6_hdr->icmp6_type = ICMPV6_ECHO_REQUEST_TYPE;
icmp6_hdr->icmp6_code = ICMPV6_ECHO_REQUEST_CODE;
icmp6_hdr->icmp6_checksum = 0;
// Initialize the echo request fields
icmp6_req = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
icmp6_req->icmp6_echo_id = (USHORT)GetCurrentProcessI d();
icmp6_req->icmp6_echo_sequ ence = 0;
datapart = (char *)buf + sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST );
memset(datapart, '#', datasize);
return (sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST ));
}
//
// Function: checksum
//
// Description:
// This function calculates the 16-bit one's complement sum
// of the supplied buffer (ICMP) header.
//
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
//
// Function: ValidateArgs
//
// Description:
// Parse the command line arguments.
//
BOOL ValidateArgs(int argc, char **argv)
{
int i;
BOOL isValid = FALSE;
for(i=1; i < argc ;i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'a': // address family
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
if (argv[i+1][0] == '4')
gAddressFamily = AF_INET;
else if (argv[i+1][0] == '6')
gAddressFamily = AF_INET6;
else
{
usage(argv[0]);
goto CLEANUP;
}
i++;
break;
case 'i': // Set TTL value
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
gTtl = atoi(argv[++i]);
break;
case 'l': // buffer size tos end
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
gDataSize = atoi(argv[++i]);
break;
case 'r': // record route option
bRecordRoute = TRUE;
break;
default:
usage(argv[0]);
goto CLEANUP;
}
}
else
{
gDestination = argv[i];
}
}
isValid = TRUE;
CLEANUP:
return isValid;
}
//
// Function: SetIcmpSequence
//
// Description:
// This routine sets the sequence number of the ICMP request packet.
//
void SetIcmpSequence(char *buf)
{
ULONG sequence=0;
sequence = GetTickCount();
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_sequence = (USHORT)sequence;
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
ICMPV6_ECHO_REQUEST *req6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
req6 = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
req6->icmp6_echo_sequence = (USHORT)sequence;
}
}
//
// Function: ComputeIcmp6PseudoHeaderCh ecksum
//
// Description:
// This routine computes the ICMP6 checksum which includes the pseudo
// header of the IPv6 header (see RFC2460 and RFC2463). The one difficulty
// here is we have to know the source and destination IPv6 addresses which
// will be contained in the IPv6 header in order to compute the checksum.
// To do this we call the SIO_ROUTING_INTERFACE_QUER Y ioctl to find which
// local interface for the outgoing packet.
//
USHORT ComputeIcmp6PseudoHeaderCh ecksum(SOC KET s, char *icmppacket, int icmplen, struct addrinfo *dest)
{
SOCKADDR_STORAGE localif;
DWORD bytes;
char tmp[MAX_RECV_BUF_LEN] = {'\0'},
*ptr=NULL,
proto=0;
int rc, total, length, i;
// Find out which local interface for the destination
rc = WSAIoctl(
s,
SIO_ROUTING_INTERFACE_QUER Y,
dest->ai_addr,
(DWORD) dest->ai_addrlen,
(SOCKADDR *) &localif,
(DWORD) sizeof(localif),
&bytes,
NULL,
NULL
);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "WSAIoctl failed: %d\n", WSAGetLastError());
return 0xFFFF;
}
// We use a temporary buffer to calculate the pseudo header.
ptr = tmp;
total = 0;
// Copy source address
memcpy(ptr, &((SOCKADDR_IN6 *)&localif)->sin6_addr, sizeof(struct in6_addr));
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy destination address
memcpy(ptr, &((SOCKADDR_IN6 *)dest->ai_addr)->sin6_add r, sizeof(struct in6_addr));
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy ICMP packet length
length = htonl(icmplen);
memcpy(ptr, &length, sizeof(length));
ptr += sizeof(length);
total += sizeof(length);
// Zero the 3 bytes
memset(ptr, 0, 3);
ptr += 3;
total += 3;
// Copy next hop header
proto = IPPROTO_ICMP6;
memcpy(ptr, &proto, sizeof(proto));
ptr += sizeof(proto);
total += sizeof(proto);
// Copy the ICMP header and payload
memcpy(ptr, icmppacket, icmplen);
ptr += icmplen;
total += icmplen;
for(i=0; i < icmplen%2 ;i++)
{
*ptr = 0;
ptr++;
total++;
}
return checksum((USHORT *)tmp, total);
}
//
// Function: ComputeIcmpChecksum
//
// Description:
// This routine computes the checksum for the ICMP request. For IPv4 its
// easy, just compute the checksum for the ICMP packet and data. For IPv6,
// its more complicated. The pseudo checksum has to be computed for IPv6
// which includes the ICMP6 packet and data plus portions of the IPv6
// header which is difficult since we aren't building our own IPv6
// header.
//
void ComputeIcmpChecksum(SOCKET s, char *buf, int packetlen, struct addrinfo *dest)
{
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_checksum = 0;
icmpv4->icmp_checksum = checksum((USHORT *)buf, packetlen);
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
icmpv6->icmp6_checksum = 0;
icmpv6->icmp6_checksum = ComputeIcmp6PseudoHeaderCh ecksum(
s,
buf,
packetlen,
dest
);
}
}
//
// Function: PostRecvfrom
//
// Description:
// This routine posts an overlapped WSARecvFrom on the raw socket.
//
int PostRecvfrom(SOCKET s, char *buf, int buflen, SOCKADDR *from, int *fromlen, WSAOVERLAPPED *ol)
{
WSABUF wbuf;
DWORD flags,
bytes;
int rc;
wbuf.buf = buf;
wbuf.len = buflen;
flags = 0;
rc = WSARecvFrom(
s,
&wbuf,
1,
&bytes,
&flags,
from,
fromlen,
ol,
NULL
);
if (rc == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "WSARecvFrom failed: %d\n", WSAGetLastError());
return SOCKET_ERROR;
}
}
return NO_ERROR;
}
//
// Function: PrintPayload
//
// Description:
// This routine is for IPv4 only. It determines if there are any IP options
// present (by seeing if the IP header length is greater than 20 bytes) and
// if so it prints the IP record route options.
//
void PrintPayload(char *buf, int bytes)
{
int hdrlen=0,
routes=0,
i;
UNREFERENCED_PARAMETER(byt es);
if (gAddressFamily == AF_INET)
{
SOCKADDR_IN hop;
IPV4_OPTION_HDR *v4opt=NULL;
IPV4_HDR *v4hdr=NULL;
hop.sin_family = (USHORT)gAddressFamily;
hop.sin_port = 0;
v4hdr = (IPV4_HDR *)buf;
hdrlen = (v4hdr->ip_verlen & 0x0F) * 4;
// If the header length is greater than the size of the basic IPv4
// header then there are options present. Find them and print them.
if (hdrlen > sizeof(IPV4_HDR))
{
v4opt = (IPV4_OPTION_HDR *)(buf + sizeof(IPV4_HDR));
routes = (v4opt->opt_ptr / sizeof(ULONG)) - 1;
for(i=0; i < routes ;i++)
{
hop.sin_addr.s_addr = v4opt->opt_addr[i];
// Print the route
if (i == 0)
printf(" Route: ");
else
printf(" ");
PrintAddress((SOCKADDR *)&hop, sizeof(hop));
if (i < routes-1)
printf(" ->\n");
else
printf("\n");
}
}
}
return;
}
//
// Function: SetTtl
//
// Description:
// Sets the TTL on the socket.
//
int SetTtl(SOCKET s, int ttl)
{
int optlevel = 0,
option = 0,
rc;
rc = NO_ERROR;
if (gAddressFamily == AF_INET)
{
optlevel = IPPROTO_IP;
option = IP_TTL;
}
else if (gAddressFamily == AF_INET6)
{
optlevel = IPPROTO_IPV6;
option = IPV6_UNICAST_HOPS;
}
else
{
rc = SOCKET_ERROR;
}
if (rc == NO_ERROR)
{
rc = setsockopt(
s,
optlevel,
option,
(char *)&ttl,
sizeof(ttl)
);
}
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "SetTtl: setsockopt failed: %d\n", WSAGetLastError());
}
return rc;
}
//
// Function: main
//
// Description:
// Setup the ICMP raw socket and create the ICMP header. Add
// the appropriate IP option header and start sending ICMP
// echo requests to the endpoint. For each send and receive we
// set a timeout value so that we don't wait forever for a
// response in case the endpoint is not responding. When we
// receive a packet decode it.
//
int __cdecl main(int argc, char **argv)
{
WSADATA wsd;
WSAOVERLAPPED recvol;
SOCKET s=INVALID_SOCKET;
char *icmpbuf=NULL;
struct addrinfo *dest=NULL,
*local=NULL;
IPV4_OPTION_HDR ipopt;
SOCKADDR_STORAGE from;
DWORD bytes,
flags;
int packetlen=0,
fromlen,
time=0,
rc,
i,
status = 0;
recvol.hEvent = WSA_INVALID_EVENT;
// Parse the command line
if (ValidateArgs(argc, argv) == FALSE)
{
// invalid arguments supplied.
status = -1;
goto EXIT;
}
// Load Winsock
if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)
{
printf("WSAStartup() failed: %d\n", rc);
status = -1;
goto EXIT;
}
// Resolve the destination address
dest = ResolveAddress(
gDestination,
"0",
gAddressFamily,
0,
0
);
if (dest == NULL)
{
printf("bad name %s\n", gDestination);
status = -1;
goto CLEANUP;
}
gAddressFamily = dest->ai_family;
if (gAddressFamily == AF_INET)
gProtocol = IPPROTO_ICMP;
else if (gAddressFamily == AF_INET6)
gProtocol = IPPROTO_ICMP6;
// Get the bind address
local = ResolveAddress(
NULL,
"0",
gAddressFamily,
0,
0
);
if (local == NULL)
{
printf("Unable to obtain the bind address!\n");
status = -1;
goto CLEANUP;
}
// Create the raw socket
s = socket(gAddressFamily, SOCK_RAW, gProtocol);
if (s == INVALID_SOCKET)
{
printf("socket failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
SetTtl(s, gTtl);
// Figure out the size of the ICMP header and payload
if (gAddressFamily == AF_INET)
packetlen += sizeof(ICMP_HDR);
else if (gAddressFamily == AF_INET6)
packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST );
// Add in the data size
packetlen += gDataSize;
// Allocate the buffer that will contain the ICMP request
icmpbuf = (char *)HeapAlloc(GetProcessHeap (), HEAP_ZERO_MEMORY, packetlen);
if (icmpbuf == NULL)
{
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
// Initialize the ICMP headers
if (gAddressFamily == AF_INET)
{
if (bRecordRoute)
{
// Setup the IP option header to go out on every ICMP packet
ZeroMemory(&ipopt, sizeof(ipopt));
ipopt.opt_code = IP_RECORD_ROUTE; // record route option
ipopt.opt_ptr = 4; // point to the first addr offset
ipopt.opt_len = 39; // length of option header
rc = setsockopt(s, IPPROTO_IP, IP_OPTIONS,
(char *)&ipopt, sizeof(ipopt));
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "setsockopt(IP_OPTIONS) failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
}
InitIcmpHeader(icmpbuf, gDataSize);
}
else if (gAddressFamily == AF_INET6)
{
InitIcmp6Header(icmpbuf, gDataSize);
}
// Bind the socket -- need to do this since we post a receive first
rc = bind(s, local->ai_addr, (int)local->ai_addrlen);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Setup the receive operation
memset(&recvol, 0, sizeof(recvol));
recvol.hEvent = WSACreateEvent();
if (recvol.hEvent == WSA_INVALID_EVENT)
{
fprintf(stderr, "WSACreateEvent failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Post the first overlapped receive
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
printf("\nPinging ");
PrintAddress(dest->ai_addr , (int)dest->ai_addrlen);
printf(" with %d bytes of data\n\n", gDataSize);
// Start sending the ICMP requests
for(i=0; i < DEFAULT_SEND_COUNT ;i++)
{
// Set the sequence number and compute the checksum
SetIcmpSequence(icmpbuf);
ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);
time = GetTickCount();
rc = sendto(
s,
icmpbuf,
packetlen,
0,
dest->ai_addr,
(int)dest->ai_addrlen
);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "sendto failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Waite for a response
rc = WaitForSingleObject((HANDL E)recvol.h Event, DEFAULT_RECV_TIMEOUT);
if (rc == WAIT_FAILED)
{
fprintf(stderr, "WaitForSingleObject failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
else if (rc == WAIT_TIMEOUT)
{
printf("Request timed out.\n");
}
else
{
rc = WSAGetOverlappedResult(
s,
&recvol,
&bytes,
FALSE,
&flags
);
if (rc == FALSE)
{
fprintf(stderr, "WSAGetOverlappedResult failed: %d\n", WSAGetLastError());
}
time = GetTickCount() - time;
WSAResetEvent(recvol.hEven t);
printf("Reply from ");
PrintAddress((SOCKADDR *)&from, fromlen);
if (time == 0)
printf(": bytes=%d time<1ms TTL=%d\n", gDataSize, gTtl);
else
printf(": bytes=%d time=%dms TTL=%d\n", gDataSize, time, gTtl);
PrintPayload(recvbuf, bytes);
if (i < DEFAULT_SEND_COUNT - 1)
{
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
}
}
Sleep(1000);
}
CLEANUP:
//
// Cleanup
//
if (dest)
freeaddrinfo(dest);
if (local)
freeaddrinfo(local);
if (s != INVALID_SOCKET)
closesocket(s);
if (recvol.hEvent != WSA_INVALID_EVENT)
WSACloseEvent(recvol.hEven t);
if (icmpbuf)
HeapFree(GetProcessHeap(), 0, icmpbuf);
WSACleanup();
EXIT:
return status;
}
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Sample: Protocol header definitions used by ping (raw sockets)
//
// Files:
// iphdr.h - this file
//
// Description:
// This file contains various protocol header definitions used by
// the raw socket ping sample.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
// Align on a 1-byte boundary
#include <pshpack1.h>
// IPv4 header
typedef struct ip_hdr
{
unsigned char ip_verlen; // 4-bit IPv4 version
// 4-bit header length (in 32-bit words)
unsigned char ip_tos; // IP type of service
unsigned short ip_totallength; // Total length
unsigned short ip_id; // Unique identifier
unsigned short ip_offset; // Fragment offset field
unsigned char ip_ttl; // Time to live
unsigned char ip_protocol; // Protocol(TCP,UDP etc)
unsigned short ip_checksum; // IP checksum
unsigned int ip_srcaddr; // Source address
unsigned int ip_destaddr; // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
// IPv4 option header
typedef struct ipv4_option_hdr
{
unsigned char opt_code; // option type
unsigned char opt_len; // length of the option header
unsigned char opt_ptr; // offset into options
unsigned long opt_addr[9]; // list of IPv4 addresses
} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;
// ICMP header
typedef struct icmp_hdr
{
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short icmp_checksum;
unsigned short icmp_id;
unsigned short icmp_sequence;
} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;
// IPv6 protocol header
typedef struct ipv6_hdr
{
unsigned long ipv6_vertcflow; // 4-bit IPv6 version
// 8-bit traffic class
// 20-bit flow label
unsigned short ipv6_payloadlen; // payload length
unsigned char ipv6_nexthdr; // next header protocol value
unsigned char ipv6_hoplimit; // TTL
struct in6_addr ipv6_srcaddr; // Source address
struct in6_addr ipv6_destaddr; // Destination address
} IPV6_HDR, *PIPV6_HDR, FAR * LPIPV6_HDR;
// IPv6 fragment header
typedef struct ipv6_fragment_hdr
{
unsigned char ipv6_frag_nexthdr;
unsigned char ipv6_frag_reserved;
unsigned short ipv6_frag_offset;
unsigned long ipv6_frag_id;
} IPV6_FRAGMENT_HDR, *PIPV6_FRAGMENT_HDR, FAR * LPIPV6_FRAGMENT_HDR;
// ICMPv6 header
typedef struct icmpv6_hdr {
unsigned char icmp6_type;
unsigned char icmp6_code;
unsigned short icmp6_checksum;
} ICMPV6_HDR;
// ICMPv6 echo request body
typedef struct icmpv6_echo_request
{
unsigned short icmp6_echo_id;
unsigned short icmp6_echo_sequence;
} ICMPV6_ECHO_REQUEST;
// Define the UDP header
typedef struct udp_hdr
{
unsigned short src_portno; // Source port no.
unsigned short dst_portno; // Dest. port no.
unsigned short udp_length; // Udp packet length
unsigned short udp_checksum; // Udp checksum (optional)
} UDP_HDR, *PUDP_HDR;
// IPv4 option for record route
#define IP_RECORD_ROUTE 0x7
// ICMP6 protocol value (used in the socket call and IPv6 header)
#define IPPROTO_ICMP6 58
// ICMP types and codes
#define ICMPV4_ECHO_REQUEST_TYPE 8
#define ICMPV4_ECHO_REQUEST_CODE 0
#define ICMPV4_ECHO_REPLY_TYPE 0
#define ICMPV4_ECHO_REPLY_CODE 0
#define ICMPV4_MINIMUM_HEADER 8
// ICPM6 types and codes
#define ICMPV6_ECHO_REQUEST_TYPE 128
#define ICMPV6_ECHO_REQUEST_CODE 0
#define ICMPV6_ECHO_REPLY_TYPE 129
#define ICMPV6_ECHO_REPLY_CODE 0
// Restore byte alignment back to default
#include <poppack.h>
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Common routines for resolving addresses and hostnames
//
// Files:
// resolve.cpp - Common routines
// resolve.h - Header file for common routines
//
// Description:
// This file contains common name resolution and name printing
// routines and is used by many of the samples on this CD.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
#include <winsock2.h>
#include <ws2tcpip.h>
#include <strsafe.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
//
// Function: PrintAddress
//
// Description:
// This routine takes a SOCKADDR structure and its length and prints
// converts it to a string representation. This string is printed
// to the console via stdout.
//
int PrintAddress(SOCKADDR *sa, int salen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (rc != 0)
{
fprintf(stderr, "%s: getnameinfo failed: %d\n", __FILE__, rc);
return rc;
}
// If the port is zero then don't print it
if (strcmp(serv, "0") != 0)
{
if (sa->sa_family == AF_INET)
printf("[%s]:%s", host, serv);
else
printf("%s:%s", host, serv);
}
else
printf("%s", host);
return NO_ERROR;
}
//
// Function: FormatAddress
//
// Description:
// This is similar to the PrintAddress function except that instead of
// printing the string address to the console, it is formatted into
// the supplied string buffer.
//
int FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
HRESULT hRet;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (rc != 0)
{
fprintf(stderr, "%s: getnameinfo failed: %d\n", __FILE__, rc);
return rc;
}
if ( (strlen(host) + strlen(serv) + 1) > (unsigned)addrbuflen)
return WSAEFAULT;
addrbuf[0] = '\0';
if (sa->sa_family == AF_INET)
{
if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "%s:%s", host, serv)))
{
fprintf(stderr,"%s StringCchPrintf failed: 0x%x\n",__FILE__,hRet);
return (int)hRet;
}
}
else if (sa->sa_family == AF_INET6)
{
if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "[%s]:%s", host, serv)))
{
fprintf(stderr,"%s StringCchPrintf failed: 0x%x\n",__FILE__,hRet);
return (int)hRet;
}
}
return NO_ERROR;
}
//
// Function: ResolveAddress
//
// Description:
// This routine resolves the specified address and returns a list of addrinfo
// structure containing SOCKADDR structures representing the resolved addresses.
// Note that if 'addr' is non-NULL, then getaddrinfo will resolve it whether
// it is a string listeral address or a hostname.
//
struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto)
{
struct addrinfo hints,
*res = NULL;
int rc;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = ((addr) ? 0 : AI_PASSIVE);
hints.ai_family = af;
hints.ai_socktype = type;
hints.ai_protocol = proto;
rc = getaddrinfo(
addr,
port,
&hints,
&res
);
if (rc != 0)
{
fprintf(stderr, "Invalid address %s, getaddrinfo failed: %d\n", addr, rc);
return NULL;
}
return res;
}
//
// Function: ReverseLookup
//
// Description:
// This routine takes a SOCKADDR and does a reverse lookup for the name
// corresponding to that address.
//
int ReverseLookup(SOCKADDR *sa, int salen, char *buf, int buflen)
{
char host[NI_MAXHOST];
int hostlen=NI_MAXHOST,
rc;
HRESULT hRet;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
NULL,
0,
0
);
if (rc != 0)
{
fprintf(stderr, "getnameinfo failed: %d\n", rc);
return rc;
}
buf[0] = '\0';
if(FAILED(hRet = StringCchCopy(buf, buflen, host)))
{
fprintf(stderr,"StringCchC opy failed: 0x%x\n",hRet);
return (int)hRet;
}
return NO_ERROR;
}
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Common routines for resolving addresses and hostnames
//
// Files:
// resolve.h - Header file for common routines
//
// Description:
// This file contains common name resolution and name printing
// routines and is used by many of the samples on this CD.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
#ifndef _RESOLVE_H_
#define _RESOLVE_H_
#ifdef _cplusplus
extern "C" {
#endif
int PrintAddress(SOCKADDR *sa, int salen);
int FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen);
int ReverseLookup(SOCKADDR *sa, int salen, char *namebuf, int namebuflen);
struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto);
#ifdef _cplusplus
}
#endif
#endif
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Sample: IPv4 and IPv6 Ping Sample
//
// Files:
// iphdr.h - IPv4 and IPv6 packet header definitions
// ping.cpp - this file
// resolve.cpp - Common name resolution routine
// resolve.h - Header file for common name resolution routines
//
// Description:
// This sample illustrates how to use raw sockets to send ICMP
// echo requests and receive their response. This sample performs
// both IPv4 and IPv6 ICMP echo requests. When using raw sockets,
// the protocol value supplied to the socket API is used as the
// protocol field (or next header field) of the IP packet. Then
// as a part of the data submitted to sendto, we include both
// the ICMP request and data.
//
// For IPv4 the IP record route option is supported via the
// IP_OPTIONS socket option.
//
// Compile:
// cl -o ping.exe ping.cpp resolve.cpp ws2_32.lib
//
// Command Line Options/Parameters:
// ping.exe [-a 4|6] [-i ttl] [-l datasize] [-r] [host]
//
// -a Address family (IPv4 or IPv6)
// -i ttl TTL value to set on socket
// -l size Amount of data to send as part of the ICMP request
// -r Use IPv4 record route
// host Hostname or literal address
//
#ifdef _IA64_
#pragma warning (disable: 4267)
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
#include "iphdr.h"
#define DEFAULT_DATA_SIZE 32 // default data size
#define DEFAULT_SEND_COUNT 4 // number of ICMP requests to send
#define DEFAULT_RECV_TIMEOUT 6000 // six second
#define DEFAULT_TTL 128
#define MAX_RECV_BUF_LEN 0xFFFF // Max incoming packet size.
int gAddressFamily=AF_UNSPEC, // Address family to use
gProtocol=IPPROTO_ICMP, // Protocol value
gTtl=DEFAULT_TTL; // Default TTL value
int gDataSize=DEFAULT_DATA_SIZ
BOOL bRecordRoute=FALSE; // Use IPv4 record route?
char *gDestination=NULL, // Destination
recvbuf[MAX_RECV_BUF_LEN];
int recvbuflen = MAX_RECV_BUF_LEN; // Length of received packets.
//
// Function: usage
//
// Description:
// Print usage information.
//
void usage(char *progname)
{
printf("usage: %s [options] <host> \n", progname);
printf(" host Remote machine to ping\n");
printf(" options: \n");
printf(" -a 4|6 Address family (default: AF_UNSPEC)\n");
printf(" -i ttl Time to live (default: 128) \n");
printf(" -l bytes Amount of data to send (default: 32) \n");
printf(" -r Record route (IPv4 only)\n");
return;
}
//
// Function: InitIcmpHeader
//
// Description:
// Helper function to fill in various stuff in our ICMP request.
//
void InitIcmpHeader(char *buf, int datasize)
{
ICMP_HDR *icmp_hdr=NULL;
char *datapart=NULL;
icmp_hdr = (ICMP_HDR *)buf;
icmp_hdr->icmp_type = ICMPV4_ECHO_REQUEST_TYPE; // request an ICMP echo
icmp_hdr->icmp_code = ICMPV4_ECHO_REQUEST_CODE;
icmp_hdr->icmp_id = (USHORT)GetCurrentProcessI
icmp_hdr->icmp_checksum = 0;
icmp_hdr->icmp_sequence = 0;
datapart = buf + sizeof(ICMP_HDR);
//
// Place some data in the buffer.
//
memset(datapart, 'E', datasize);
}
//
// Function: InitIcmp6Header
//
// Description:
// Initialize the ICMP6 header as well as the echo request header.
//
int InitIcmp6Header(char *buf, int datasize)
{
ICMPV6_HDR *icmp6_hdr=NULL;
ICMPV6_ECHO_REQUEST *icmp6_req=NULL;
char *datapart=NULL;
// Initialize the ICMP6 headerf ields
icmp6_hdr = (ICMPV6_HDR *)buf;
icmp6_hdr->icmp6_type = ICMPV6_ECHO_REQUEST_TYPE;
icmp6_hdr->icmp6_code = ICMPV6_ECHO_REQUEST_CODE;
icmp6_hdr->icmp6_checksum = 0;
// Initialize the echo request fields
icmp6_req = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
icmp6_req->icmp6_echo_id = (USHORT)GetCurrentProcessI
icmp6_req->icmp6_echo_sequ
datapart = (char *)buf + sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST
memset(datapart, '#', datasize);
return (sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST
}
//
// Function: checksum
//
// Description:
// This function calculates the 16-bit one's complement sum
// of the supplied buffer (ICMP) header.
//
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
//
// Function: ValidateArgs
//
// Description:
// Parse the command line arguments.
//
BOOL ValidateArgs(int argc, char **argv)
{
int i;
BOOL isValid = FALSE;
for(i=1; i < argc ;i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'a': // address family
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
if (argv[i+1][0] == '4')
gAddressFamily = AF_INET;
else if (argv[i+1][0] == '6')
gAddressFamily = AF_INET6;
else
{
usage(argv[0]);
goto CLEANUP;
}
i++;
break;
case 'i': // Set TTL value
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
gTtl = atoi(argv[++i]);
break;
case 'l': // buffer size tos end
if (i+1 >= argc)
{
usage(argv[0]);
goto CLEANUP;
}
gDataSize = atoi(argv[++i]);
break;
case 'r': // record route option
bRecordRoute = TRUE;
break;
default:
usage(argv[0]);
goto CLEANUP;
}
}
else
{
gDestination = argv[i];
}
}
isValid = TRUE;
CLEANUP:
return isValid;
}
//
// Function: SetIcmpSequence
//
// Description:
// This routine sets the sequence number of the ICMP request packet.
//
void SetIcmpSequence(char *buf)
{
ULONG sequence=0;
sequence = GetTickCount();
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_sequence = (USHORT)sequence;
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
ICMPV6_ECHO_REQUEST *req6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
req6 = (ICMPV6_ECHO_REQUEST *)(buf + sizeof(ICMPV6_HDR));
req6->icmp6_echo_sequence = (USHORT)sequence;
}
}
//
// Function: ComputeIcmp6PseudoHeaderCh
//
// Description:
// This routine computes the ICMP6 checksum which includes the pseudo
// header of the IPv6 header (see RFC2460 and RFC2463). The one difficulty
// here is we have to know the source and destination IPv6 addresses which
// will be contained in the IPv6 header in order to compute the checksum.
// To do this we call the SIO_ROUTING_INTERFACE_QUER
// local interface for the outgoing packet.
//
USHORT ComputeIcmp6PseudoHeaderCh
{
SOCKADDR_STORAGE localif;
DWORD bytes;
char tmp[MAX_RECV_BUF_LEN] = {'\0'},
*ptr=NULL,
proto=0;
int rc, total, length, i;
// Find out which local interface for the destination
rc = WSAIoctl(
s,
SIO_ROUTING_INTERFACE_QUER
dest->ai_addr,
(DWORD) dest->ai_addrlen,
(SOCKADDR *) &localif,
(DWORD) sizeof(localif),
&bytes,
NULL,
NULL
);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "WSAIoctl failed: %d\n", WSAGetLastError());
return 0xFFFF;
}
// We use a temporary buffer to calculate the pseudo header.
ptr = tmp;
total = 0;
// Copy source address
memcpy(ptr, &((SOCKADDR_IN6 *)&localif)->sin6_addr, sizeof(struct in6_addr));
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy destination address
memcpy(ptr, &((SOCKADDR_IN6 *)dest->ai_addr)->sin6_add
ptr += sizeof(struct in6_addr);
total += sizeof(struct in6_addr);
// Copy ICMP packet length
length = htonl(icmplen);
memcpy(ptr, &length, sizeof(length));
ptr += sizeof(length);
total += sizeof(length);
// Zero the 3 bytes
memset(ptr, 0, 3);
ptr += 3;
total += 3;
// Copy next hop header
proto = IPPROTO_ICMP6;
memcpy(ptr, &proto, sizeof(proto));
ptr += sizeof(proto);
total += sizeof(proto);
// Copy the ICMP header and payload
memcpy(ptr, icmppacket, icmplen);
ptr += icmplen;
total += icmplen;
for(i=0; i < icmplen%2 ;i++)
{
*ptr = 0;
ptr++;
total++;
}
return checksum((USHORT *)tmp, total);
}
//
// Function: ComputeIcmpChecksum
//
// Description:
// This routine computes the checksum for the ICMP request. For IPv4 its
// easy, just compute the checksum for the ICMP packet and data. For IPv6,
// its more complicated. The pseudo checksum has to be computed for IPv6
// which includes the ICMP6 packet and data plus portions of the IPv6
// header which is difficult since we aren't building our own IPv6
// header.
//
void ComputeIcmpChecksum(SOCKET
{
if (gAddressFamily == AF_INET)
{
ICMP_HDR *icmpv4=NULL;
icmpv4 = (ICMP_HDR *)buf;
icmpv4->icmp_checksum = 0;
icmpv4->icmp_checksum = checksum((USHORT *)buf, packetlen);
}
else if (gAddressFamily == AF_INET6)
{
ICMPV6_HDR *icmpv6=NULL;
icmpv6 = (ICMPV6_HDR *)buf;
icmpv6->icmp6_checksum = 0;
icmpv6->icmp6_checksum = ComputeIcmp6PseudoHeaderCh
s,
buf,
packetlen,
dest
);
}
}
//
// Function: PostRecvfrom
//
// Description:
// This routine posts an overlapped WSARecvFrom on the raw socket.
//
int PostRecvfrom(SOCKET s, char *buf, int buflen, SOCKADDR *from, int *fromlen, WSAOVERLAPPED *ol)
{
WSABUF wbuf;
DWORD flags,
bytes;
int rc;
wbuf.buf = buf;
wbuf.len = buflen;
flags = 0;
rc = WSARecvFrom(
s,
&wbuf,
1,
&bytes,
&flags,
from,
fromlen,
ol,
NULL
);
if (rc == SOCKET_ERROR)
{
if (WSAGetLastError() != WSA_IO_PENDING)
{
fprintf(stderr, "WSARecvFrom failed: %d\n", WSAGetLastError());
return SOCKET_ERROR;
}
}
return NO_ERROR;
}
//
// Function: PrintPayload
//
// Description:
// This routine is for IPv4 only. It determines if there are any IP options
// present (by seeing if the IP header length is greater than 20 bytes) and
// if so it prints the IP record route options.
//
void PrintPayload(char *buf, int bytes)
{
int hdrlen=0,
routes=0,
i;
UNREFERENCED_PARAMETER(byt
if (gAddressFamily == AF_INET)
{
SOCKADDR_IN hop;
IPV4_OPTION_HDR *v4opt=NULL;
IPV4_HDR *v4hdr=NULL;
hop.sin_family = (USHORT)gAddressFamily;
hop.sin_port = 0;
v4hdr = (IPV4_HDR *)buf;
hdrlen = (v4hdr->ip_verlen & 0x0F) * 4;
// If the header length is greater than the size of the basic IPv4
// header then there are options present. Find them and print them.
if (hdrlen > sizeof(IPV4_HDR))
{
v4opt = (IPV4_OPTION_HDR *)(buf + sizeof(IPV4_HDR));
routes = (v4opt->opt_ptr / sizeof(ULONG)) - 1;
for(i=0; i < routes ;i++)
{
hop.sin_addr.s_addr = v4opt->opt_addr[i];
// Print the route
if (i == 0)
printf(" Route: ");
else
printf(" ");
PrintAddress((SOCKADDR *)&hop, sizeof(hop));
if (i < routes-1)
printf(" ->\n");
else
printf("\n");
}
}
}
return;
}
//
// Function: SetTtl
//
// Description:
// Sets the TTL on the socket.
//
int SetTtl(SOCKET s, int ttl)
{
int optlevel = 0,
option = 0,
rc;
rc = NO_ERROR;
if (gAddressFamily == AF_INET)
{
optlevel = IPPROTO_IP;
option = IP_TTL;
}
else if (gAddressFamily == AF_INET6)
{
optlevel = IPPROTO_IPV6;
option = IPV6_UNICAST_HOPS;
}
else
{
rc = SOCKET_ERROR;
}
if (rc == NO_ERROR)
{
rc = setsockopt(
s,
optlevel,
option,
(char *)&ttl,
sizeof(ttl)
);
}
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "SetTtl: setsockopt failed: %d\n", WSAGetLastError());
}
return rc;
}
//
// Function: main
//
// Description:
// Setup the ICMP raw socket and create the ICMP header. Add
// the appropriate IP option header and start sending ICMP
// echo requests to the endpoint. For each send and receive we
// set a timeout value so that we don't wait forever for a
// response in case the endpoint is not responding. When we
// receive a packet decode it.
//
int __cdecl main(int argc, char **argv)
{
WSADATA wsd;
WSAOVERLAPPED recvol;
SOCKET s=INVALID_SOCKET;
char *icmpbuf=NULL;
struct addrinfo *dest=NULL,
*local=NULL;
IPV4_OPTION_HDR ipopt;
SOCKADDR_STORAGE from;
DWORD bytes,
flags;
int packetlen=0,
fromlen,
time=0,
rc,
i,
status = 0;
recvol.hEvent = WSA_INVALID_EVENT;
// Parse the command line
if (ValidateArgs(argc, argv) == FALSE)
{
// invalid arguments supplied.
status = -1;
goto EXIT;
}
// Load Winsock
if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)
{
printf("WSAStartup() failed: %d\n", rc);
status = -1;
goto EXIT;
}
// Resolve the destination address
dest = ResolveAddress(
gDestination,
"0",
gAddressFamily,
0,
0
);
if (dest == NULL)
{
printf("bad name %s\n", gDestination);
status = -1;
goto CLEANUP;
}
gAddressFamily = dest->ai_family;
if (gAddressFamily == AF_INET)
gProtocol = IPPROTO_ICMP;
else if (gAddressFamily == AF_INET6)
gProtocol = IPPROTO_ICMP6;
// Get the bind address
local = ResolveAddress(
NULL,
"0",
gAddressFamily,
0,
0
);
if (local == NULL)
{
printf("Unable to obtain the bind address!\n");
status = -1;
goto CLEANUP;
}
// Create the raw socket
s = socket(gAddressFamily, SOCK_RAW, gProtocol);
if (s == INVALID_SOCKET)
{
printf("socket failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
SetTtl(s, gTtl);
// Figure out the size of the ICMP header and payload
if (gAddressFamily == AF_INET)
packetlen += sizeof(ICMP_HDR);
else if (gAddressFamily == AF_INET6)
packetlen += sizeof(ICMPV6_HDR) + sizeof(ICMPV6_ECHO_REQUEST
// Add in the data size
packetlen += gDataSize;
// Allocate the buffer that will contain the ICMP request
icmpbuf = (char *)HeapAlloc(GetProcessHeap
if (icmpbuf == NULL)
{
fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
// Initialize the ICMP headers
if (gAddressFamily == AF_INET)
{
if (bRecordRoute)
{
// Setup the IP option header to go out on every ICMP packet
ZeroMemory(&ipopt, sizeof(ipopt));
ipopt.opt_code = IP_RECORD_ROUTE; // record route option
ipopt.opt_ptr = 4; // point to the first addr offset
ipopt.opt_len = 39; // length of option header
rc = setsockopt(s, IPPROTO_IP, IP_OPTIONS,
(char *)&ipopt, sizeof(ipopt));
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "setsockopt(IP_OPTIONS) failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
}
InitIcmpHeader(icmpbuf, gDataSize);
}
else if (gAddressFamily == AF_INET6)
{
InitIcmp6Header(icmpbuf, gDataSize);
}
// Bind the socket -- need to do this since we post a receive first
rc = bind(s, local->ai_addr, (int)local->ai_addrlen);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "bind failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Setup the receive operation
memset(&recvol, 0, sizeof(recvol));
recvol.hEvent = WSACreateEvent();
if (recvol.hEvent == WSA_INVALID_EVENT)
{
fprintf(stderr, "WSACreateEvent failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Post the first overlapped receive
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
printf("\nPinging ");
PrintAddress(dest->ai_addr
printf(" with %d bytes of data\n\n", gDataSize);
// Start sending the ICMP requests
for(i=0; i < DEFAULT_SEND_COUNT ;i++)
{
// Set the sequence number and compute the checksum
SetIcmpSequence(icmpbuf);
ComputeIcmpChecksum(s, icmpbuf, packetlen, dest);
time = GetTickCount();
rc = sendto(
s,
icmpbuf,
packetlen,
0,
dest->ai_addr,
(int)dest->ai_addrlen
);
if (rc == SOCKET_ERROR)
{
fprintf(stderr, "sendto failed: %d\n", WSAGetLastError());
status = -1;
goto CLEANUP;
}
// Waite for a response
rc = WaitForSingleObject((HANDL
if (rc == WAIT_FAILED)
{
fprintf(stderr, "WaitForSingleObject failed: %d\n", GetLastError());
status = -1;
goto CLEANUP;
}
else if (rc == WAIT_TIMEOUT)
{
printf("Request timed out.\n");
}
else
{
rc = WSAGetOverlappedResult(
s,
&recvol,
&bytes,
FALSE,
&flags
);
if (rc == FALSE)
{
fprintf(stderr, "WSAGetOverlappedResult failed: %d\n", WSAGetLastError());
}
time = GetTickCount() - time;
WSAResetEvent(recvol.hEven
printf("Reply from ");
PrintAddress((SOCKADDR *)&from, fromlen);
if (time == 0)
printf(": bytes=%d time<1ms TTL=%d\n", gDataSize, gTtl);
else
printf(": bytes=%d time=%dms TTL=%d\n", gDataSize, time, gTtl);
PrintPayload(recvbuf, bytes);
if (i < DEFAULT_SEND_COUNT - 1)
{
fromlen = sizeof(from);
PostRecvfrom(s, recvbuf, recvbuflen, (SOCKADDR *)&from, &fromlen, &recvol);
}
}
Sleep(1000);
}
CLEANUP:
//
// Cleanup
//
if (dest)
freeaddrinfo(dest);
if (local)
freeaddrinfo(local);
if (s != INVALID_SOCKET)
closesocket(s);
if (recvol.hEvent != WSA_INVALID_EVENT)
WSACloseEvent(recvol.hEven
if (icmpbuf)
HeapFree(GetProcessHeap(),
WSACleanup();
EXIT:
return status;
}
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Sample: Protocol header definitions used by ping (raw sockets)
//
// Files:
// iphdr.h - this file
//
// Description:
// This file contains various protocol header definitions used by
// the raw socket ping sample.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
// Align on a 1-byte boundary
#include <pshpack1.h>
// IPv4 header
typedef struct ip_hdr
{
unsigned char ip_verlen; // 4-bit IPv4 version
// 4-bit header length (in 32-bit words)
unsigned char ip_tos; // IP type of service
unsigned short ip_totallength; // Total length
unsigned short ip_id; // Unique identifier
unsigned short ip_offset; // Fragment offset field
unsigned char ip_ttl; // Time to live
unsigned char ip_protocol; // Protocol(TCP,UDP etc)
unsigned short ip_checksum; // IP checksum
unsigned int ip_srcaddr; // Source address
unsigned int ip_destaddr; // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
// IPv4 option header
typedef struct ipv4_option_hdr
{
unsigned char opt_code; // option type
unsigned char opt_len; // length of the option header
unsigned char opt_ptr; // offset into options
unsigned long opt_addr[9]; // list of IPv4 addresses
} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;
// ICMP header
typedef struct icmp_hdr
{
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short icmp_checksum;
unsigned short icmp_id;
unsigned short icmp_sequence;
} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;
// IPv6 protocol header
typedef struct ipv6_hdr
{
unsigned long ipv6_vertcflow; // 4-bit IPv6 version
// 8-bit traffic class
// 20-bit flow label
unsigned short ipv6_payloadlen; // payload length
unsigned char ipv6_nexthdr; // next header protocol value
unsigned char ipv6_hoplimit; // TTL
struct in6_addr ipv6_srcaddr; // Source address
struct in6_addr ipv6_destaddr; // Destination address
} IPV6_HDR, *PIPV6_HDR, FAR * LPIPV6_HDR;
// IPv6 fragment header
typedef struct ipv6_fragment_hdr
{
unsigned char ipv6_frag_nexthdr;
unsigned char ipv6_frag_reserved;
unsigned short ipv6_frag_offset;
unsigned long ipv6_frag_id;
} IPV6_FRAGMENT_HDR, *PIPV6_FRAGMENT_HDR, FAR * LPIPV6_FRAGMENT_HDR;
// ICMPv6 header
typedef struct icmpv6_hdr {
unsigned char icmp6_type;
unsigned char icmp6_code;
unsigned short icmp6_checksum;
} ICMPV6_HDR;
// ICMPv6 echo request body
typedef struct icmpv6_echo_request
{
unsigned short icmp6_echo_id;
unsigned short icmp6_echo_sequence;
} ICMPV6_ECHO_REQUEST;
// Define the UDP header
typedef struct udp_hdr
{
unsigned short src_portno; // Source port no.
unsigned short dst_portno; // Dest. port no.
unsigned short udp_length; // Udp packet length
unsigned short udp_checksum; // Udp checksum (optional)
} UDP_HDR, *PUDP_HDR;
// IPv4 option for record route
#define IP_RECORD_ROUTE 0x7
// ICMP6 protocol value (used in the socket call and IPv6 header)
#define IPPROTO_ICMP6 58
// ICMP types and codes
#define ICMPV4_ECHO_REQUEST_TYPE 8
#define ICMPV4_ECHO_REQUEST_CODE 0
#define ICMPV4_ECHO_REPLY_TYPE 0
#define ICMPV4_ECHO_REPLY_CODE 0
#define ICMPV4_MINIMUM_HEADER 8
// ICPM6 types and codes
#define ICMPV6_ECHO_REQUEST_TYPE 128
#define ICMPV6_ECHO_REQUEST_CODE 0
#define ICMPV6_ECHO_REPLY_TYPE 129
#define ICMPV6_ECHO_REPLY_CODE 0
// Restore byte alignment back to default
#include <poppack.h>
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Common routines for resolving addresses and hostnames
//
// Files:
// resolve.cpp - Common routines
// resolve.h - Header file for common routines
//
// Description:
// This file contains common name resolution and name printing
// routines and is used by many of the samples on this CD.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
#include <winsock2.h>
#include <ws2tcpip.h>
#include <strsafe.h>
#include <stdio.h>
#include <stdlib.h>
#include "resolve.h"
//
// Function: PrintAddress
//
// Description:
// This routine takes a SOCKADDR structure and its length and prints
// converts it to a string representation. This string is printed
// to the console via stdout.
//
int PrintAddress(SOCKADDR *sa, int salen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (rc != 0)
{
fprintf(stderr, "%s: getnameinfo failed: %d\n", __FILE__, rc);
return rc;
}
// If the port is zero then don't print it
if (strcmp(serv, "0") != 0)
{
if (sa->sa_family == AF_INET)
printf("[%s]:%s", host, serv);
else
printf("%s:%s", host, serv);
}
else
printf("%s", host);
return NO_ERROR;
}
//
// Function: FormatAddress
//
// Description:
// This is similar to the PrintAddress function except that instead of
// printing the string address to the console, it is formatted into
// the supplied string buffer.
//
int FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
HRESULT hRet;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (rc != 0)
{
fprintf(stderr, "%s: getnameinfo failed: %d\n", __FILE__, rc);
return rc;
}
if ( (strlen(host) + strlen(serv) + 1) > (unsigned)addrbuflen)
return WSAEFAULT;
addrbuf[0] = '\0';
if (sa->sa_family == AF_INET)
{
if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "%s:%s", host, serv)))
{
fprintf(stderr,"%s StringCchPrintf failed: 0x%x\n",__FILE__,hRet);
return (int)hRet;
}
}
else if (sa->sa_family == AF_INET6)
{
if(FAILED(hRet = StringCchPrintf(addrbuf, addrbuflen, "[%s]:%s", host, serv)))
{
fprintf(stderr,"%s StringCchPrintf failed: 0x%x\n",__FILE__,hRet);
return (int)hRet;
}
}
return NO_ERROR;
}
//
// Function: ResolveAddress
//
// Description:
// This routine resolves the specified address and returns a list of addrinfo
// structure containing SOCKADDR structures representing the resolved addresses.
// Note that if 'addr' is non-NULL, then getaddrinfo will resolve it whether
// it is a string listeral address or a hostname.
//
struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto)
{
struct addrinfo hints,
*res = NULL;
int rc;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = ((addr) ? 0 : AI_PASSIVE);
hints.ai_family = af;
hints.ai_socktype = type;
hints.ai_protocol = proto;
rc = getaddrinfo(
addr,
port,
&hints,
&res
);
if (rc != 0)
{
fprintf(stderr, "Invalid address %s, getaddrinfo failed: %d\n", addr, rc);
return NULL;
}
return res;
}
//
// Function: ReverseLookup
//
// Description:
// This routine takes a SOCKADDR and does a reverse lookup for the name
// corresponding to that address.
//
int ReverseLookup(SOCKADDR *sa, int salen, char *buf, int buflen)
{
char host[NI_MAXHOST];
int hostlen=NI_MAXHOST,
rc;
HRESULT hRet;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
NULL,
0,
0
);
if (rc != 0)
{
fprintf(stderr, "getnameinfo failed: %d\n", rc);
return rc;
}
buf[0] = '\0';
if(FAILED(hRet = StringCchCopy(buf, buflen, host)))
{
fprintf(stderr,"StringCchC
return (int)hRet;
}
return NO_ERROR;
}
// From Network Programming for Microsoft Windows, Second Edition by
// Anthony Jones and James Ohlund.
// Copyright 2002. Reproduced by permission of Microsoft Press.
// All rights reserved.
//
//
// Common routines for resolving addresses and hostnames
//
// Files:
// resolve.h - Header file for common routines
//
// Description:
// This file contains common name resolution and name printing
// routines and is used by many of the samples on this CD.
//
// Compile:
// See ping.cpp
//
// Usage:
// See ping.cpp
//
#ifndef _RESOLVE_H_
#define _RESOLVE_H_
#ifdef _cplusplus
extern "C" {
#endif
int PrintAddress(SOCKADDR *sa, int salen);
int FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen);
int ReverseLookup(SOCKADDR *sa, int salen, char *namebuf, int namebuflen);
struct addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto);
#ifdef _cplusplus
}
#endif
#endif
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thank you.
My problem some more complicated. I make and run my threads from the java. And I don't know when our system will make a ping request. But I will try.
My problem some more complicated. I make and run my threads from the java. And I don't know when our system will make a ping request. But I will try.
Well, you refer to a "microsoft recommended example" but you fail to give an URL or anything that indicates where that example can be found so that leaves us to second guess what that example looks like.
I suggest that if you want us to bother to help you, you could either bother yourself to provide some of the code you've made or at least bother yourself to give us an URL or some such where this "Microsoft recomended example" can be found. msdn provide most of its documentation on-line so it probably has an URL somewhere.
In any event, I think using one thread per host type would be inefficient and a waste. How much code are you supposed to do per ping response? Most likely "not much" and if so it is best done by having one thread that loops receiving all the ping responses anyway. Using separate threads for this can only be inefficient.
If you still insist on having one thread per host you're checking and you check several simultaneously and you want each thread to handle the ping responses then do it like this:
1. Have a receiver thread that receives the ping responses. This is the ONLY thread that do recvfrom() or whatever you use to receive the packets. This thread do NOTHING except receiving ping responses and placing them in proper queues and possibly wake up the thread associated with that queue.
2. Inspect the packet and figure out which IP it comes from (the host you pinged) and post it in a queue for the thread that handles that host possibly wake up the thread associated with that queue.
3. The other threads are then doing a WaitForSingleObject() on a semaphore or some such for that queue and will wake up when you insert an element in that queue.
Alf