/*
* uicmp.cxx
*
* ICMP socket class implementation.
*
* Portable Windows Library
*
* Copyright (c) 1993-1998 Equivalence Pty. Ltd.
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): ______________________________________.
*
* $Log: uicmp.cxx,v $
* Revision 1.16 2005/11/30 12:47:42 csoutheren
* Removed tabs, reformatted some code, and changed tags for Doxygen
*
* Revision 1.15 2002/10/10 04:43:44 robertj
* VxWorks port, thanks Martijn Roest
*
* Revision 1.14 2001/09/10 03:03:36 robertj
* Major change to fix problem with error codes being corrupted in a
* PChannel when have simultaneous reads and writes in threads.
*
* Revision 1.13 2001/06/30 06:59:07 yurik
* Jac Goudsmit from Be submit these changes 6/28. Implemented by Yuri Kiryanov
*
* Revision 1.12 2001/03/07 07:00:17 yurik
* #ifdef'd setsockopt IPPROTO_IP for BeOS
*
* Revision 1.11 2001/03/06 22:20:21 craigs
* Fixed TTL and other stuff so that traceroute is almost possible!
*
* Revision 1.10 1999/08/09 04:06:39 robertj
* Change to avoid name space problem with X windows library
*
* Revision 1.9 1999/02/22 13:26:54 robertj
* BeOS port changes.
*
* Revision 1.8 1998/11/30 21:52:06 robertj
* New directory structure.
*
* Revision 1.7 1998/09/24 04:12:29 robertj
* Added open software license.
*
* Revision 1.6 1998/08/26 01:45:56 craigs
* Fixed error in IPHdr
*
* Revision 1.5 1998/01/26 07:27:09 robertj
* Added part support for extra ping info. Still needs TTL for traceroute.
*
* Revision 1.4 1996/11/16 11:12:56 craigs
* Fixed problem with work misaligns under SOlaris
*
* Revision 1.3 1996/10/31 10:20:07 craigs
* Moved ICMP implementation into here, as it is now platform dependent
*
* Revision 1.6 1996/09/14 13:09:34 robertj
* Major upgrade:
* rearranged sockets to help support IPX.
* added indirect channel class and moved all protocols to descend from it,
* separating the protocol from the low level byte transport.
*
* Revision 1.5 1996/08/11 06:52:14 robertj
* Oops
*
* Revision 1.4 1996/08/07 13:40:57 robertj
* Fixed sparc memory alignment problem from int 64
*
* Revision 1.3 1996/06/03 10:03:10 robertj
* Changed ping to return more parameters.
*
* Revision 1.2 1996/05/30 10:08:51 robertj
* Fixed bug in ping (checksum incorrect).
*
* Revision 1.1 1996/05/15 21:11:35 robertj
* Initial revision
*
*/
#pragma implementation "icmpsock.h"
#include <ptlib.h>
#include <ptlib/sockets.h>
#define MAX_IP_LEN 60
#define MAX_ICMP_LEN 76
#define ICMP_DATA_LEN (64-8)
#define RX_BUFFER_SIZE (MAX_IP_LEN+MAX_ICMP_LEN+ICMP_DATA_LEN)
#define ICMP_ECHO_REPLY 0
#define ICMP_ECHO 8
#define ICMP_TIMXCEED 11
typedef struct {
BYTE type;
BYTE code;
WORD checksum;
WORD id;
WORD sequence;
PInt64 sendtime;
BYTE data[ICMP_DATA_LEN-sizeof(PInt64)];
} ICMPPacket;
typedef struct {
BYTE verIhl;
BYTE typeOfService;
WORD totalLength;
WORD identification;
WORD fragOff;
BYTE timeToLive;
BYTE protocol;
WORD checksum;
BYTE sourceAddr[4];
BYTE destAddr[4];
} IPHdr;
static WORD CalcChecksum(void * p, PINDEX len)
{
WORD * ptr = (WORD *)p;
DWORD sum = 0;
while (len > 1) {
sum += *ptr++;
len-=2;
}
if (len > 0) {
WORD t = *(BYTE *)ptr;
sum += t;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (WORD)~sum;
}
PICMPSocket::PICMPSocket()
{
OpenSocket();
}
BOOL PICMPSocket::Ping(const PString & host)
{
PingInfo info;
return Ping(host, info);
}
BOOL PICMPSocket::Ping(const PString & host, PingInfo & info)
{
if (!WritePing(host, info))
return FALSE;
return ReadPing(info);
}
BOOL PICMPSocket::WritePing(const PString & host, PingInfo & info)
{
// find address of the host
PIPSocket::Address addr;
if (!GetHostAddress(host, addr))
return SetErrorValues(BadParameter, EINVAL);
// create the ICMP packet
ICMPPacket packet;
// clear the packet including data area
memset(&packet, 0, sizeof(packet));
packet.type = ICMP_ECHO;
packet.sequence = info.sequenceNum;
packet.id = info.identifier;
#ifndef BE_BONELESS
if (info.ttl != 0) {
char ttl = (char)info.ttl;
if (::setsockopt(os_handle, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) != 0)
return FALSE;
}
#endif
// set the send time
packet.sendtime = PTimer::Tick().GetMilliSeconds();
// calculate the checksum
packet.checksum = CalcChecksum(&packet, sizeof(packet));
// send the packet
return WriteTo(&packet, sizeof(packet), addr, 0);
}
BOOL PICMPSocket::ReadPing(PingInfo & info)
{
// receive a packet
BYTE packet[RX_BUFFER_SIZE];
IPHdr * ipHdr;
ICMPPacket * icmpPacket;
WORD port;
PInt64 now;
PTimer timeout(GetReadTimeout());
for (;;) {
memset(&packet, 0, sizeof(packet));
if (!ReadFrom(packet, sizeof(packet), info.remoteAddr, port))
return FALSE;
now = PTimer::Tick().GetMilliSeconds();
ipHdr = (IPHdr *)packet;
icmpPacket = (ICMPPacket *)(packet + ((ipHdr->verIhl & 0xf) << 2));
if (( icmpPacket->type == ICMP_ECHO_REPLY) &&
((WORD)icmpPacket->id == info.identifier)) {
info.status = PingSuccess;
break;
}
if (icmpPacket->type == ICMP_TIMXCEED) {
info.status = TtlExpiredTransmit;
break;
}
if (!timeout.IsRunning())
return FALSE;
}
info.remoteAddr = Address(ipHdr->sourceAddr[0], ipHdr->sourceAddr[1],
ipHdr->sourceAddr[2], ipHdr->sourceAddr[3]);
info.localAddr = Address(ipHdr->destAddr[0], ipHdr->destAddr[1],
ipHdr->destAddr[2], ipHdr->destAddr[3]);
// calc round trip time. Be careful, as unaligned "long long" ints
// can cause problems on some platforms
#if defined(P_SUN4) || defined(P_SOLARIS)
PInt64 then;
BYTE * pthen = (BYTE *)&then;
BYTE * psendtime = (BYTE *)&icmpPacket->sendtime;
memcpy(pthen, psendtime, sizeof(PInt64));
info.delay.SetInterval(now - then);
#else
info.delay.SetInterval(now - icmpPacket->sendtime);
#endif
info.sequenceNum = icmpPacket->sequence;
return TRUE;
}
BOOL PICMPSocket::OpenSocket()
{
#if !defined BE_BONELESS && !defined(P_VXWORKS)
struct protoent * p = ::getprotobyname(GetProtocolName());
if (p == NULL)
return ConvertOSError(-1);
return ConvertOSError(os_handle = os_socket(AF_INET, SOCK_RAW, p->p_proto));
#else // Raw sockets not supported in BeOS R4 or VxWorks.
return ConvertOSError(os_handle = os_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
#endif //!defined BE_BONELESS && !defined(P_VXWORKS)
}
const char * PICMPSocket::GetProtocolName() const
{
return "icmp";
}
PICMPSocket::PingInfo::PingInfo(WORD id)
{
identifier = id;
sequenceNum = 0;
ttl = 255;
buffer = NULL;
status = PingSuccess;
}
// End Of File ///////////////////////////////////////////////////////////////
syntax highlighted by Code2HTML, v. 0.9.1