/////////////////////////////////////////////////////////////////////////////// // 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 #include #include #include #include #include #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 #include #include #endif int Socket::nofSockets_= 0; vector* Socket::getAdapters() { TRACE("Socket::getAdapters - start") vector* ret=new vector; #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(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(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(s1)->s_,&fds_); if(s2) { FD_SET(const_cast(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; }