340 lines
13 KiB
C++
340 lines
13 KiB
C++
/*=============================================================================|
|
|
| PROJECT SNAP7 1.3.0 |
|
|
|==============================================================================|
|
|
| Copyright (C) 2013, 2015 Davide Nardella |
|
|
| All rights reserved. |
|
|
|==============================================================================|
|
|
| SNAP7 is free software: you can redistribute it and/or modify |
|
|
| it under the terms of the Lesser GNU General Public License as published by |
|
|
| the Free Software Foundation, either version 3 of the License, or |
|
|
| (at your option) any later version. |
|
|
| |
|
|
| It means that you can distribute your commercial software linked with |
|
|
| SNAP7 without the requirement to distribute the source code of your |
|
|
| application and without the requirement that your application be itself |
|
|
| distributed under LGPL. |
|
|
| |
|
|
| SNAP7 is distributed in the hope that it will be useful, |
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
| Lesser GNU General Public License for more details. |
|
|
| |
|
|
| You should have received a copy of the GNU General Public License and a |
|
|
| copy of Lesser GNU General Public License along with Snap7. |
|
|
| If not, see http://www.gnu.org/licenses/ |
|
|
|=============================================================================*/
|
|
#ifndef snap_msgsock_h
|
|
#define snap_msgsock_h
|
|
//---------------------------------------------------------------------------
|
|
#include "snap_platform.h"
|
|
#include "snap_sysutils.h"
|
|
//----------------------------------------------------------------------------
|
|
#if defined(S7_OS_WINDOWS) || defined (OS_SOLARIS) || defined(OS_OSX)
|
|
# define MSG_NOSIGNAL 0
|
|
#endif
|
|
//----------------------------------------------------------------------------
|
|
// Non blocking connection to avoid root priviledges under UNIX
|
|
// i.e. raw socket pinger is not more used.
|
|
// Thanks to Rolf Stalder that made it ;)
|
|
//----------------------------------------------------------------------------
|
|
#ifdef PLATFORM_UNIX
|
|
#define NON_BLOCKING_CONNECT
|
|
#endif
|
|
#ifdef NON_BLOCKING_CONNECT
|
|
#include <fcntl.h>
|
|
#endif
|
|
//----------------------------------------------------------------------------
|
|
/*
|
|
In Windows sizeof socket varies depending of the platform :
|
|
win32 -> sizeof(SOCKET) = 4
|
|
win64 -> sizeof(SOCKET) = 8
|
|
|
|
Even though sizeof(SOCKET) is 8, should be safe to cast it to int, because
|
|
the value constitutes an index in per-process table of limited size
|
|
and not a real pointer.
|
|
|
|
Other Os define the socket as int regardless of the processor.
|
|
|
|
We want to sleep peacefully, so it's better to define a portable socket.
|
|
*/
|
|
|
|
#ifdef S7_OS_WINDOWS
|
|
typedef SOCKET socket_t;
|
|
#else
|
|
typedef int socket_t;
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
#define SD_RECEIVE 0x00
|
|
#define SD_SEND 0x01
|
|
#define SD_BOTH 0x02
|
|
#define MaxPacketSize 65536
|
|
|
|
//----------------------------------------------------------------------------
|
|
// For other platform we need to re-define next constants
|
|
#if defined(PLATFORM_UNIX) || defined(OS_OSX)
|
|
|
|
#define INVALID_SOCKET (socket_t)(~0)
|
|
#define SOCKET_ERROR (-1)
|
|
|
|
#define WSAEINTR EINTR
|
|
#define WSAEBADF EBADF
|
|
#define WSAEACCES EACCES
|
|
#define WSAEFAULT EFAULT
|
|
#define WSAEINVAL EINVAL
|
|
#define WSAEMFILE EMFILE
|
|
#define WSAEWOULDBLOCK EWOULDBLOCK
|
|
#define WSAEINPROGRESS EINPROGRESS
|
|
#define WSAEALREADY EALREADY
|
|
#define WSAENOTSOCK ENOTSOCK
|
|
#define WSAEDESTADDRREQ EDESTADDRREQ
|
|
#define WSAEMSGSIZE EMSGSIZE
|
|
#define WSAEPROTOTYPE EPROTOTYPE
|
|
#define WSAENOPROTOOPT ENOPROTOOPT
|
|
#define WSAEPROTONOSUPPORT EPROTONOSUPPORT
|
|
#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT
|
|
#define WSAEOPNOTSUPP EOPNOTSUPP
|
|
#define WSAEPFNOSUPPORT EPFNOSUPPORT
|
|
#define WSAEAFNOSUPPORT EAFNOSUPPORT
|
|
#define WSAEADDRINUSE EADDRINUSE
|
|
#define WSAEADDRNOTAVAIL EADDRNOTAVAIL
|
|
#define WSAENETDOWN ENETDOWN
|
|
#define WSAENETUNREACH ENETUNREACH
|
|
#define WSAENETRESET ENETRESET
|
|
#define WSAECONNABORTED ECONNABORTED
|
|
#define WSAECONNRESET ECONNRESET
|
|
#define WSAENOBUFS ENOBUFS
|
|
#define WSAEISCONN EISCONN
|
|
#define WSAENOTCONN ENOTCONN
|
|
#define WSAESHUTDOWN ESHUTDOWN
|
|
#define WSAETOOMANYREFS ETOOMANYREFS
|
|
#define WSAETIMEDOUT ETIMEDOUT
|
|
#define WSAECONNREFUSED ECONNREFUSED
|
|
#define WSAELOOP ELOOP
|
|
#define WSAENAMETOOLONG ENAMETOOLONG
|
|
#define WSAEHOSTDOWN EHOSTDOWN
|
|
#define WSAEHOSTUNREACH EHOSTUNREACH
|
|
#define WSAENOTEMPTY ENOTEMPTY
|
|
#define WSAEUSERS EUSERS
|
|
#define WSAEDQUOT EDQUOT
|
|
#define WSAESTALE ESTALE
|
|
#define WSAEREMOTE EREMOTE
|
|
#endif
|
|
|
|
#define WSAEINVALIDADDRESS 12001
|
|
|
|
#define ICmpBufferSize 4096
|
|
typedef byte TIcmpBuffer[ICmpBufferSize];
|
|
|
|
// Ping result
|
|
#define PR_CANNOT_PERFORM -1 // cannot ping :
|
|
// unix : no root rights or SUID flag set to
|
|
// open raw sockets
|
|
// windows : neither helper DLL found nor raw
|
|
// sockets can be opened (no administrator rights)
|
|
// In this case the execution continues whitout
|
|
// the benefit of the smart-connect.
|
|
|
|
#define PR_SUCCESS 0 // Host found
|
|
#define PR_ERROR 1 // Ping Error, Ping was performed but ...
|
|
// - host didn't replied (not found)
|
|
// - routing error
|
|
// - TTL expired
|
|
// - ... all other icmp error that we don't need
|
|
// to know.
|
|
|
|
// Ping Kind
|
|
#define pkCannotPing 1 // see PR_CANNOT_PERFORM comments
|
|
#define pkWinHelper 2 // use iphlpapi.dll (only windows)
|
|
#define pkRawSocket 3 // use raw sockets (unix/windows)
|
|
|
|
const byte ICMP_ECHORP = 0; // ECHO Reply
|
|
const byte ICMP_ECHORQ = 8; // ECHO Request
|
|
//---------------------------------------------------------------------------
|
|
// RAW SOCKET PING STRUCTS
|
|
//---------------------------------------------------------------------------
|
|
#pragma pack(1)
|
|
typedef struct{
|
|
byte ip_hl_v;
|
|
byte ip_tos;
|
|
word ip_len;
|
|
word ip_id ;
|
|
word ip_off;
|
|
byte ip_ttl;
|
|
byte ip_p;
|
|
word ip_sum;
|
|
longword ip_src;
|
|
longword ip_dst;
|
|
}TIPHeader;
|
|
|
|
typedef struct{
|
|
byte ic_type; // Type of message
|
|
byte ic_code; // Code
|
|
word ic_cksum; // 16 bit checksum
|
|
word ic_id; // ID (ic1 : ipv4)
|
|
word ic_seq; // Sequence
|
|
}TIcmpHeader;
|
|
|
|
typedef struct{
|
|
TIcmpHeader Header;
|
|
byte Data[32]; // use the well known default
|
|
}TIcmpPacket, *PIcmpPacket;
|
|
|
|
typedef struct{
|
|
TIPHeader IPH;
|
|
TIcmpPacket ICmpReply;
|
|
}TIcmpReply, *PIcmpReply;
|
|
#pragma pack()
|
|
|
|
//---------------------------------------------------------------------------
|
|
class TRawSocketPinger
|
|
{
|
|
private:
|
|
socket_t FSocket;
|
|
PIcmpPacket SendPacket;
|
|
TIcmpBuffer IcmpBuffer;
|
|
word FId, FSeq;
|
|
void InitPacket();
|
|
word PacketChecksum();
|
|
bool CanRead(int Timeout);
|
|
public:
|
|
bool Ping(longword ip_addr, int Timeout);
|
|
TRawSocketPinger();
|
|
~TRawSocketPinger();
|
|
};
|
|
typedef TRawSocketPinger *PRawSocketPinger;
|
|
//---------------------------------------------------------------------------
|
|
class TPinger
|
|
{
|
|
private:
|
|
PRawSocketPinger RawPinger;
|
|
bool RawAvail;
|
|
#ifdef S7_OS_WINDOWS
|
|
bool WinPing(longword ip_addr, int Timeout);
|
|
#endif
|
|
bool RawPing(longword ip_addr, int Timeout);
|
|
public:
|
|
TPinger();
|
|
~TPinger();
|
|
bool Ping(char *Host, int Timeout);
|
|
bool Ping(longword ip_addr, int Timeout);
|
|
};
|
|
typedef TPinger *PPinger;
|
|
//---------------------------------------------------------------------------
|
|
class TSnapBase // base class endian-aware
|
|
{
|
|
private:
|
|
bool LittleEndian;
|
|
protected:
|
|
longword SwapDWord(longword Value);
|
|
word SwapWord(word Value);
|
|
public:
|
|
TSnapBase();
|
|
};
|
|
//---------------------------------------------------------------------------
|
|
class TMsgSocket : public TSnapBase
|
|
{
|
|
private:
|
|
PPinger Pinger;
|
|
int GetLastSocketError();
|
|
int SockCheck(int SockResult);
|
|
void DestroySocket();
|
|
void SetSocketOptions();
|
|
bool CanWrite(int Timeout);
|
|
void GetLocal();
|
|
void GetRemote();
|
|
void SetSin(sockaddr_in &sin, char *Address, u_short Port);
|
|
void GetSin(sockaddr_in sin, char *Address, u_short &Port);
|
|
protected:
|
|
socket_t FSocket;
|
|
sockaddr_in LocalSin;
|
|
sockaddr_in RemoteSin;
|
|
//--------------------------------------------------------------------------
|
|
// low level socket
|
|
void CreateSocket();
|
|
// Called when a socket is assigned externally
|
|
void GotSocket();
|
|
// Returns how many bytes are ready to be read in the winsock buffer
|
|
int WaitingData();
|
|
// Waits until there at least "size" bytes ready to be read or until receive timeout occurs
|
|
int WaitForData(int Size, int Timeout);
|
|
// Clear socket input buffer
|
|
void Purge();
|
|
public:
|
|
longword ClientHandle;
|
|
longword LocalBind;
|
|
// Coordinates Address:Port
|
|
char LocalAddress[16];
|
|
char RemoteAddress[16];
|
|
word LocalPort;
|
|
word RemotePort;
|
|
// "speed" of the socket listener (used server-side)
|
|
int WorkInterval;
|
|
// Timeouts : 3 different values for fine tuning.
|
|
// Send timeout should be small since with work with small packets and TCP_NO_DELAY
|
|
// option, so we don't expect "time to wait".
|
|
// Recv timeout depends of equipment's processing time : we send a packet, the equipment
|
|
// processes the message, finally it sends the answer. In any case Recv timeout > Send Timeout.
|
|
// PingTimeout is the maximum time interval during which we expect that the PLC answers.
|
|
// By default is 750 ms, increase it if there are many switch/repeaters.
|
|
int PingTimeout;
|
|
int RecvTimeout;
|
|
int SendTimeout;
|
|
//int ConnTimeout;
|
|
// Output : Last operation error
|
|
int LastTcpError;
|
|
// Output : Connected to the remote Host/Peer/Client
|
|
bool Connected;
|
|
//--------------------------------------------------------------------------
|
|
TMsgSocket();
|
|
virtual ~TMsgSocket();
|
|
// Returns true if "something" can be read during the Timeout interval..
|
|
bool CanRead(int Timeout);
|
|
// Connects to a peer (using RemoteAddress and RemotePort)
|
|
int SckConnect(); // (client-side)
|
|
// Disconnects from a peer (gracefully)
|
|
void SckDisconnect();
|
|
// Disconnects RAW
|
|
void ForceClose();
|
|
// Binds to a local adapter (using LocalAddress and LocalPort) (server-side)
|
|
int SckBind();
|
|
// Listens for an incoming connection (server-side)
|
|
int SckListen();
|
|
// Set an external socket reference (tipically from a listener)
|
|
void SetSocket(socket_t s);
|
|
// Accepts an incoming connection returning a socket descriptor (server-side)
|
|
socket_t SckAccept();
|
|
// Pings the peer before connecting
|
|
bool Ping(char *Host);
|
|
bool Ping(sockaddr_in Addr);
|
|
// Sends a packet
|
|
int SendPacket(void *Data, int Size);
|
|
// Returns true if a Packet at least of "Size" bytes is ready to be read
|
|
bool PacketReady(int Size);
|
|
// Receives everything
|
|
int Receive(void *Data, int BufSize, int & SizeRecvd);
|
|
// Receives a packet of size specified.
|
|
int RecvPacket(void *Data, int Size);
|
|
// Peeks a packet of size specified without extract it from the socket queue
|
|
int PeekPacket(void *Data, int Size);
|
|
virtual bool Execute();
|
|
};
|
|
|
|
typedef TMsgSocket *PMsgSocket;
|
|
//---------------------------------------------------------------------------
|
|
void Msg_CloseSocket(socket_t FSocket);
|
|
longword Msg_GetSockAddr(socket_t FSocket);
|
|
//---------------------------------------------------------------------------
|
|
class SocketsLayer
|
|
{
|
|
private:
|
|
#ifdef S7_OS_WINDOWS
|
|
WSADATA wsaData;
|
|
#endif
|
|
public:
|
|
SocketsLayer();
|
|
~SocketsLayer();
|
|
};
|
|
|
|
#endif // snap_msgsock_h
|