///////////////////////////////////////////////////////////////////////////////
// MQ4CPP - Message queuing for C++
// Copyright (C) 2004-2007 Riccardo Pompeo (Italy)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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 GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// History:
// Original Win32 code by Vijay R. Nyffenegger
// Modified to support Linux socket by Riccardo Pompeo
// Linux accept()/pthread_cancel problem resolved by Duncan McDiarmid
// Linux info about network interfaces by Floyd Davidson
//
#define SILENT
#include "Socket.h"
#include "Trace.h"
#ifndef WIN32
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define TIMEVAL struct timeval
#define inaddrr(x) (*(struct in_addr *) &ifr->x[sizeof sa.sin_port])
#define IFRSIZE ((int)(size * sizeof (struct ifreq)))
#define MAC_ADDRESS_CHAR_LEN 6
#else
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <errno.h>
#endif
int Socket::nofSockets_= 0;
vector<NetAdapter>* Socket::getAdapters()
{
TRACE("Socket::getAdapters - start")
vector<NetAdapter>* ret=new vector<NetAdapter>;
#ifdef WIN32
PIP_ADAPTER_ADDRESSES AdapterAddresses = NULL;
ULONG OutBufferLength = 0;
ULONG RetVal = 0;
// Realloc buffer size until no overflow occurs
do
{
RetVal = GetAdaptersAddresses(AF_INET,0,NULL,AdapterAddresses,&OutBufferLength);
if (RetVal == ERROR_BUFFER_OVERFLOW)
{
if (AdapterAddresses != NULL)
delete(AdapterAddresses);
AdapterAddresses =(PIP_ADAPTER_ADDRESSES) new char[OutBufferLength];
}
} while (RetVal == ERROR_BUFFER_OVERFLOW);
if (RetVal == NO_ERROR)
{
// If successful, output some information from the data we received
PIP_ADAPTER_ADDRESSES AdapterList = AdapterAddresses;
while (AdapterList)
{
PIP_ADAPTER_UNICAST_ADDRESS pUnicastAddress;
char szAdapterName[64];
int len = WideCharToMultiByte(CP_ACP, 0, AdapterList->FriendlyName, wcslen(AdapterList->FriendlyName),
szAdapterName, sizeof(szAdapterName),NULL, NULL);
if (len == 0)
{
delete(AdapterAddresses);
throw SocketException("Cannot convert adapter name");
}
szAdapterName[len] = '\0';
for (pUnicastAddress = AdapterList->FirstUnicastAddress; pUnicastAddress; pUnicastAddress = pUnicastAddress->Next)
{
char szAddress[NI_MAXHOST];
if (getnameinfo(pUnicastAddress->Address.lpSockaddr, pUnicastAddress->Address.iSockaddrLength,
szAddress, sizeof(szAddress), NULL, 0, NI_NUMERICHOST))
{
delete(AdapterAddresses);
throw SocketException("Can't convert network format to presentation format");
}
TRACE("Adapter name=" << szAdapterName)
TRACE("Adapter address=" << szAddress)
DUMP("Adapter phisical address",(char*)AdapterList->PhysicalAddress,AdapterList->PhysicalAddressLength);
NetAdapter anAdapter(string(szAdapterName), string(szAddress),
string((char*)AdapterList->PhysicalAddress,AdapterList->PhysicalAddressLength));
ret->push_back(anAdapter);
}
AdapterList = AdapterList->Next;
}
}
else
{
LPVOID lpMsgBuf;
if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, RetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,0,NULL))
{
string msg=(char*)lpMsgBuf;
LocalFree(lpMsgBuf);
delete(AdapterAddresses);
throw SocketException(msg);
}
LocalFree( lpMsgBuf );
}
delete(AdapterAddresses);
#else
int sockfd;
int size = 1;
ifreq* ifr;
ifconf ifc;
sockaddr_in sa;
string cur_interface_name;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if(sockfd ==-1)
throw SocketException("Cannot open socket");
ifc.ifc_len = IFRSIZE;
ifc.ifc_req = NULL;
do
{
++size;
ifc.ifc_req = static_cast<ifreq*>(realloc(ifc.ifc_req, IFRSIZE));
if (ifc.ifc_req == NULL)
throw SocketException("Out of memory");
ifc.ifc_len = IFRSIZE;
if (ioctl(sockfd, SIOCGIFCONF, &ifc))
throw SocketException("Error ioctl SIOCFIFCONF");
} while (IFRSIZE <= ifc.ifc_len);
ifr = ifc.ifc_req;
for(; (ifr - ifc.ifc_req) * sizeof(ifreq) < static_cast<unsigned long>(ifc.ifc_len); ++ifr)
{
if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) continue; // duplicate, skip it
if (ioctl(sockfd, SIOCGIFFLAGS, ifr) != 0) continue; // failed to get flags, skip it
string name = ifr->ifr_name;
// ------ get IP_ADDRESS ------
string ip = inet_ntoa(inaddrr(ifr_addr.sa_data));
// ------ get HW_ADDRESS ------
#ifdef __FreeBSD__
if (ioctl(sockfd, SIOCGIFMAC, ifr) != 0) continue; // failed to get mac, skip it
#else
if (ioctl(sockfd, SIOCGIFHWADDR, ifr) != 0) continue; // failed to get mac, skip it
#endif
string mac=string((char*)ifr->ifr_addr.sa_data,MAC_ADDRESS_CHAR_LEN);
TRACE("Adapter name=" << name)
TRACE("Adapter address=" << ip)
DUMP("Adapter phisical address",(char*)ifr->ifr_addr.sa_data,MAC_ADDRESS_CHAR_LEN)
NetAdapter anAdapter(name, ip, mac);
ret->push_back(anAdapter);
}
close(sockfd);
free(ifc.ifc_req);
#endif
TRACE("Socket::getAdapters - end")
return ret;
}
void Socket::Start()
{
TRACE("Socket::Start - start")
if (!nofSockets_)
{
#ifdef WIN32
WSADATA info;
if (WSAStartup(MAKEWORD(2,0), &info))
throw SocketException("Socket: Could not start WSA");
#endif
}
++nofSockets_;
TRACE("Socket::Start - end")
}
void Socket::End()
{
TRACE("Socket::End - start")
#ifdef WIN32
WSACleanup();
#endif
TRACE("Socket::End - end")
}
Socket::Socket() : s_(0)
{
TRACE("Socket::Socket - start")
Start();
// UDP: use SOCK_DGRAM instead of SOCK_STREAM
s_ = socket(AF_INET,SOCK_STREAM,0);
#ifdef WIN32
if (s_ == INVALID_SOCKET)
throw SocketException("Socket: socket returns error");
#else
if (s_ < 0)
{
TRACE("socket return=" << s_)
throw SocketException("Socket: socket returns error");
}
#endif
refCounter_ = new int(1);
TRACE("Socket::Socket - end")
}
Socket::Socket(SOCKET s) : s_(s)
{
TRACE("Socket::Socket - start")
Start();
refCounter_ = new int(1);
TRACE("Socket::Socket - end")
};
Socket::~Socket()
{
TRACE("Socket::~Socket - start")
if (! --(*refCounter_))
{
Close();
delete refCounter_;
}
--nofSockets_;
if (!nofSockets_) End();
TRACE("Socket::~Socket - end")
}
Socket::Socket(const Socket& o)
{
TRACE("Socket::Socket - start")
refCounter_=o.refCounter_;
(*refCounter_)++;
s_ =o.s_;
nofSockets_++;
TRACE("Socket::Socket - end")
}
Socket& Socket::operator =(Socket& o)
{
TRACE("Socket::operator= - start")
(*o.refCounter_)++;
refCounter_=o.refCounter_;
s_ =o.s_;
nofSockets_++;
TRACE("Socket::operator= - end")
return *this;
}
void Socket::Close()
{
TRACE("Socket::Close - start")
if(s_>=0)
{
#ifdef WIN32
closesocket(s_);
#else
shutdown(s_,SHUT_RDWR);
#endif
}
s_=-1;
TRACE("Socket::Close - end")
}
bool Socket::ReceiveBuffer(void* theBuffer,int theLen)
{
TRACE("Socket::ReceiveBuffer - start")
TRACE("Buffer len=" << theLen)
int rxcnt=0;
while(rxcnt < theLen)
{
int len=theLen-rxcnt;
int rv = recv (s_, ((char*)theBuffer)+rxcnt, len, 0);
if (rv <= 0)
{
TRACE("recv returns error=" << rv)
return false;
}
rxcnt+=rv;
}
TRACE("Socket::ReceiveBuffer - end")
return true;
}
std::string Socket::ReceiveBytes()
{
TRACE("Socket::ReceiveBytes - start")
std::string ret;
char buf[1024];
for ( ; ; )
{
u_long arg = 1024;
#ifdef WIN32
if (ioctlsocket(s_, FIONREAD, &arg) != 0)
break;
if (arg == 0)
break;
if (arg > 1024)
arg = 1024;
#endif
int rv = recv (s_, buf, arg, 0);
if (rv <= 0)
break;
std::string t;
t.assign (buf, rv);
ret += t;
}
TRACE("Socket::ReceiveBytes - end")
return ret;
}
std::string Socket::ReceiveLine()
{
TRACE("Socket::ReceiveLine - start")
std::string ret;
while (1)
{
char r;
switch(recv(s_, &r, 1, 0))
{
case 0: // not connected anymore;
return "";
case -1:
#ifdef WIN32
if (errno == EAGAIN)
return ret;
else
return "";
#else
return "";
#endif
}
ret += r;
if (r == '\n') return ret;
}
TRACE("Socket::ReceiveLine - end")
}
void Socket::SendLine(std::string s)
{
TRACE("Socket::SendLine - start")
s += '\n';
send(s_,s.c_str(),s.length(),0);
TRACE("Socket::SendLine - end")
}
void Socket::SendBytes(const std::string& s)
{
TRACE("Socket::SendBytes - start")
send(s_,s.data(),s.length(),0);
TRACE("Socket::SendBytes - end")
}
void Socket::SendBuffer(void* theBuffer,int theLen)
{
TRACE("Socket::SendBuffer - start")
send(s_,(char*)theBuffer,theLen,0);
TRACE("Socket::SendBuffer - end")
}
SocketServer::SocketServer(int port, int connections, TypeSocket type, const char* theIP)
{
TRACE("SocketServer::SocketServer - start")
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = PF_INET;
sa.sin_port = htons(port);
TRACE("sa.sin_port" << sa.sin_port)
if(theIP!=NULL)
{
#ifdef WIN32
sa.sin_addr.S_un.S_addr=inet_addr(theIP);
#else
inet_aton(theIP,&sa.sin_addr);
#endif
}
s_ = socket(AF_INET, SOCK_STREAM, 0);
#ifdef WIN32
if (s_ == INVALID_SOCKET)
throw SocketException("SocketServer: socket returns error");
if(type==NonBlockingSocket)
{
u_long arg = 1;
ioctlsocket(s_, FIONBIO, &arg);
}
#else
if (s_ < 0)
{
TRACE("socket error=" << errno)
throw SocketException("SocketServer: socket returns error");
}
#endif
TRACE("Socket=" << s_)
/* bind the socket to the internet address */
int retbind=bind(s_, (sockaddr *)&sa, sizeof(sockaddr_in));
#ifdef WIN32
if (retbind == SOCKET_ERROR)
{
closesocket(s_);
throw SocketException("SocketServer: bind returns error");
}
#else
if (retbind < 0)
{
TRACE("bind return error=" << errno)
shutdown(s_,SHUT_RDWR);
throw SocketException("SocketServer: bind returns error");
}
#endif
listen(s_, connections);
TRACE("SocketServer::SocketServer - end")
}
Socket* SocketServer::Accept()
{
TRACE("SocketServer::Accept - start")
SOCKET new_sock;
#ifdef WIN32
int len = sizeof(struct sockaddr);
new_sock = accept(s_, &itsSocketAddr, &len);
if (new_sock == INVALID_SOCKET)
{
int rc = WSAGetLastError();
if(rc==WSAEWOULDBLOCK)
return 0; // non-blocking call, no request pending
else
throw SocketException("SocketServer: accept returns error");
}
#else
socklen_t len = sizeof(struct sockaddr);
fd_set read_set;
FD_ZERO(&read_set);
TIMEVAL timeout;
for(;;)
{
pthread_testcancel();
timeout.tv_sec = 0;
timeout.tv_usec = 1000;
FD_SET(s_,&read_set);
if(s_<0)
throw SocketException("SocketServer: shutdown in progress");
int selret=select(s_+1, &read_set, NULL, NULL, &timeout);
if(selret<0)
throw SocketException("SocketServer: select returns error");
else if(selret==0) //detect time-out and check thread cancel
continue;
if(s_<0)
throw SocketException("SocketServer: shutdown in progress");
if(FD_ISSET(s_, &read_set))
{
new_sock = accept(s_, &itsSocketAddr, &len);
break;
}
}
if(new_sock < 0)
{
TRACE("accept error=" << new_sock)
throw SocketException("SocketServer: accept returns error");
}
#endif
Socket* r = new Socket(new_sock);
TRACE("SocketServer::Accept - end")
return r;
}
string SocketServer::address()
{
return inet_ntoa(((struct sockaddr_in*)&itsSocketAddr)->sin_addr);
}
unsigned short SocketServer::port()
{
TRACE("SocketServer::port - start")
unsigned short ret=(itsSocketAddr.sa_data[0]<<8)+itsSocketAddr.sa_data[1];
TRACE("SocketServer::port - end")
return ret;
}
SocketClient::SocketClient(const std::string& host, int port) : Socket()
{
TRACE("SocketClient::SocketClient - start")
std::string error;
hostent *he;
if ((he = gethostbyname(host.c_str())) == 0)
{
#ifdef WIN32
error = strerror(errno);
#else
error = "SocketClient: gethostbyname returns error";
#endif
TRACE("gethostbyname returns error")
throw SocketException(error);
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = *((in_addr *)he->h_addr);
memset(&(addr.sin_zero), 0, 8);
if (::connect(s_, (sockaddr *) &addr, sizeof(sockaddr)))
{
#ifdef WIN32
error = strerror(WSAGetLastError());
#else
error = "SocketClient: connect returns error";
#endif
TRACE("connect return error")
throw SocketException(error);
}
TRACE("SocketClient::SocketClient - end")
}
SocketSelect::SocketSelect(Socket const * const s1, Socket const * const s2, TypeSocket type)
{
FD_ZERO(&fds_);
FD_SET(const_cast<Socket*>(s1)->s_,&fds_);
if(s2)
{
FD_SET(const_cast<Socket*>(s2)->s_,&fds_);
}
TIMEVAL tval;
tval.tv_sec = 0;
tval.tv_usec = 1;
TIMEVAL *ptval;
if(type==NonBlockingSocket)
{
ptval = &tval;
}
else
{
ptval = 0;
}
#ifdef WIN32
if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) == SOCKET_ERROR)
throw SocketException("SocketSelect: select returns error");
#else
if (select (0, &fds_, (fd_set*) 0, (fd_set*) 0, ptval) < 0)
throw SocketException("SocketSelect: select returns error");
#endif
}
bool SocketSelect::Readable(Socket const * const s)
{
if (FD_ISSET(s->s_,&fds_)) return true;
return false;
}
SocketException::SocketException(const char* m)
{
msg = m;
}
SocketException::SocketException(string m)
{
msg = m;
}
string SocketException::getMessage() const
{
return msg;
}
syntax highlighted by Code2HTML, v. 0.9.1