/*-*-c++-*- * $Id$ * * This file is part of plptools. * * Copyright (C) 1999 Philip Proudman * Copyright (C) 1999-2001 Fritz Elfert * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bufferstore.h" #include "ppsocket.h" #include "iowatch.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif using namespace std; ppsocket::ppsocket(const ppsocket & another) { m_Socket = another.m_Socket; m_HostAddr = another.m_HostAddr; m_PeerAddr = another.m_PeerAddr; m_Bound = another.m_Bound; m_LastError = another.m_LastError; myWatch = another.myWatch; } ppsocket::ppsocket() { m_Socket = INVALID_SOCKET; memset(&m_HostAddr, 0, sizeof(m_HostAddr)); memset(&m_PeerAddr, 0, sizeof(m_PeerAddr)); ((struct sockaddr_in *) &m_HostAddr)->sin_family = AF_INET; ((struct sockaddr_in *) &m_PeerAddr)->sin_family = AF_INET; m_Bound = false; m_LastError = 0; myWatch = 0L; } ppsocket::~ppsocket() { if (m_Socket != INVALID_SOCKET) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); ::close(m_Socket); } } void ppsocket:: setWatch(IOWatch *watch) { if (watch) { if (myWatch && (m_Socket != INVALID_SOCKET)) myWatch->remIO(m_Socket); myWatch = watch; } } bool ppsocket:: reconnect() { if (m_Socket != INVALID_SOCKET) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); ::close(m_Socket); } m_Socket = INVALID_SOCKET; if (!createSocket()) return (false); m_LastError = 0; m_Bound = false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) { m_LastError = errno; return (false); } if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) { m_LastError = errno; return (false); } if (myWatch) myWatch->addIO(m_Socket); return (true); } string ppsocket:: toString() { string ret = ""; char nbuf[10]; char *tmp = 0L; int port; tmp = inet_ntoa(((struct sockaddr_in *) &m_HostAddr)->sin_addr); ret += tmp ? tmp : "none:none"; if (tmp) { ret += ':'; sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_HostAddr)->sin_port)); ret += nbuf; } ret += " -> "; tmp = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr); ret += tmp ? tmp : "none:none"; if (tmp) { ret += ':'; sprintf(nbuf, "%d", ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port)); ret += nbuf; } return ret; } bool ppsocket:: connect(const char * const Peer, int PeerPort, const char * const Host, int HostPort) { //**************************************************** //* If we aren't already bound set the host and bind * //**************************************************** if (!bindSocket(Host, HostPort)) { if (m_LastError != 0) { return (false); } } //**************** //* Set the peer * //**************** if (!setPeer(Peer, PeerPort)) { return (false); } //*********** //* Connect * //*********** if (::connect(m_Socket, &m_PeerAddr, sizeof(m_PeerAddr)) != 0) { m_LastError = errno; return (false); } if (myWatch) myWatch->addIO(m_Socket); return (true); } bool ppsocket:: listen(const char * const Host, int Port) { //**************************************************** //* If we aren't already bound set the host and bind * //**************************************************** if (!bindSocket(Host, Port)) { if (m_LastError != 0) { return (false); } } //********************** //* Listen on the port * //********************** if (myWatch) myWatch->addIO(m_Socket); if (::listen(m_Socket, 5) != 0) { m_LastError = errno; return (false); } return (true); } ppsocket *ppsocket:: accept(string *Peer, IOWatch *iow) { #ifdef sun int len; #else # ifdef __FreeBSD__ # if __FreeBSD_version >= 400000 socklen_t len; # else unsigned len; # endif # endif socklen_t len; #endif ppsocket *accepted; char *peer; //***************************************************** //* Allocate a new object to hold the accepted socket * //***************************************************** accepted = new ppsocket; if (!iow) iow = myWatch; if (!accepted) { m_LastError = errno; return NULL; } //*********************** //* Accept a connection * //*********************** len = sizeof(struct sockaddr); accepted->m_Socket = ::accept(m_Socket, &accepted->m_PeerAddr, &len); if (accepted->m_Socket == INVALID_SOCKET) { m_LastError = errno; delete accepted; return NULL; } //**************************************************** //* Got a connection so fill in the other attributes * //**************************************************** // Make sure the new socket hasn't inherited O_NONBLOCK // from the accept socket int flags = fcntl(accepted->m_Socket, F_GETFL, 0); flags &= ~O_NONBLOCK; fcntl(accepted->m_Socket, F_SETFL, flags); accepted->m_HostAddr = m_HostAddr; accepted->m_Bound = true; //**************************************************** //* If required get the name of the connected client * //**************************************************** if (Peer) { peer = inet_ntoa(((struct sockaddr_in *) &accepted->m_PeerAddr)->sin_addr); if (peer) *Peer = peer; } if (accepted && iow) { accepted->setWatch(iow); iow->addIO(accepted->m_Socket); } return accepted; } bool ppsocket:: dataToGet(int sec, int usec) const { fd_set io; FD_ZERO(&io); FD_SET(m_Socket, &io); struct timeval t; t.tv_usec = usec; t.tv_sec = sec; return (select(m_Socket + 1, &io, NULL, NULL, &t) != 0) ? true : false; } int ppsocket:: getBufferStore(bufferStore & a, bool wait) { /* Returns a 0 for for no message, * 1 for message OK, and -1 for socket problem */ u_int32_t l; long count = 0; unsigned char *buff; unsigned char *bp; if (!wait && !dataToGet(0, 0)) return 0; a.init(); if (recv(&l, sizeof(l), MSG_NOSIGNAL) != sizeof(l)) { return -1; } l = ntohl(l); if (l > 16384) return -1; bp = buff = new unsigned char[l]; while (l > 0) { int j = recv(bp, l, MSG_NOSIGNAL); if (j == SOCKET_ERROR || j == 0) { delete[]buff; return -1; } count += j; l -= j; bp += j; }; a.init(buff, count); delete[]buff; return (a.getLen() == 0) ? 0 : 1; } bool ppsocket:: sendBufferStore(const bufferStore & a) { long l = a.getLen(); u_int32_t hl = htonl(l); long sent = 0; int retries = 0; int i; bufferStore b; b.addBytes(reinterpret_cast(&hl), sizeof(hl)); b.addBuff(a); l += 4; while (l > 0) { i = send((const char *)b.getString(sent), l, MSG_NOSIGNAL); if (i == SOCKET_ERROR || i == 0) return (false); sent += i; l -= i; if (++retries > 5) { m_LastError = 0; return (false); } } return true; } int ppsocket:: recv(void *buf, int len, int flags) { int i = ::recv(m_Socket, buf, len, flags); if (i < 0) m_LastError = errno; return (i); } int ppsocket:: send(const void * const buf, int len, int flags) { int i = ::send(m_Socket, buf, len, flags); if (i < 0) m_LastError = errno; return (i); } bool ppsocket:: closeSocket(void) { if (myWatch) myWatch->remIO(m_Socket); shutdown(m_Socket, SHUT_RDWR); if (::close(m_Socket) != 0) { m_LastError = errno; return false; } m_Socket = INVALID_SOCKET; return true; } bool ppsocket:: bindSocket(const char * const Host, int Port) { // If we are already bound return false but with no last error if (m_Bound) { m_LastError = 0; return false; } // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // Set SO_REUSEADDR int one = 1; if (setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, sizeof(int)) < 0) cerr << "Warning: Unable to set SO_REUSEADDR option\n"; // If a host name was supplied then use it if (!setHost(Host, Port)) return false; // Now bind the socket if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) != 0) { m_LastError = errno; return false; } m_Bound = true; return true; } bool ppsocket:: bindInRange(const char * const Host, int Low, int High, int Retries) { int port; int i; // If we are already bound return false but with no last error if (m_Bound) { m_LastError = 0; return (false); } // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // If the number of retries is greater than the range then work // through the range sequentially. if (Retries > High - Low) { for (port = Low; port <= High; port++) { if (!setHost(Host, port)) return false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0) break; } if (port > High) { m_LastError = errno; return false; } } else { for (i = 0; i < Retries; i++) { port = Low + (rand() % (High - Low)); if (!setHost(Host, port)) return false; if (::bind(m_Socket, &m_HostAddr, sizeof(m_HostAddr)) == 0) break; } if (i >= Retries) { m_LastError = errno; return false; } } m_Bound = true; return true; } bool ppsocket:: linger(bool LingerOn, int LingerTime) { int i; struct linger l; // If the socket hasn't been created create it now if (m_Socket == INVALID_SOCKET) { if (!createSocket()) return false; } // Set the lingering if (LingerOn) { l.l_onoff = 1; l.l_linger = LingerTime; } else { l.l_onoff = 0; l.l_linger = 0; } i = setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (const char *) &l, sizeof(l)); // Check for errors if (i != 0) { m_LastError = errno; return false; } return true; } bool ppsocket:: createSocket(void) { // If the socket has already been created just return true if (m_Socket != INVALID_SOCKET) return true; // Create the socket m_Socket = ::socket(PF_INET, SOCK_STREAM, 0); if (m_Socket == INVALID_SOCKET) { m_LastError = errno; return false; } // By default set no lingering linger(false); // Return indicating success return true; } bool ppsocket:: setPeer(const char * const Peer, int Port) { struct hostent *he = NULL; // If a peer name was supplied then use it if (Peer) { if (!isdigit(Peer[0])) // RFC1035 specifies that hostnames must not start // with a digit. So we can speed up things here. he = gethostbyname(Peer); if (!he) { struct in_addr ipaddr; if (!inet_aton(Peer, &ipaddr)) { m_LastError = errno; return false; } he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET); if (!he) { m_LastError = errno; return (false); } } memcpy(&((struct sockaddr_in *)&m_PeerAddr)->sin_addr, he->h_addr_list[0], sizeof(((struct sockaddr_in *)&m_PeerAddr)->sin_addr)); } // If a port name was supplied use it if (Port > 0) ((struct sockaddr_in *)&m_PeerAddr)->sin_port = htons(Port); return true; } bool ppsocket:: getPeer(string *Peer, int *Port) { char *peer; if (Peer) { peer = inet_ntoa(((struct sockaddr_in *) &m_PeerAddr)->sin_addr); if (!peer) { m_LastError = errno; return (false); } *Peer = peer; } if (Port) *Port = ntohs(((struct sockaddr_in *) &m_PeerAddr)->sin_port); return false; } bool ppsocket:: setHost(const char * const Host, int Port) { struct hostent *he; // If a host name was supplied then use it if (Host) { if (!isdigit(Host[0])) // RFC1035 specifies that hostnames must not start // with a digit. So we can speed up things here. he = gethostbyname(Host); he = gethostbyname(Host); if (!he) { struct in_addr ipaddr; if (!inet_aton(Host, &ipaddr)) { m_LastError = errno; return false; } he = gethostbyaddr((const char *)&ipaddr.s_addr, sizeof(ipaddr.s_addr), PF_INET); if (!he) { m_LastError = errno; return false; } } memcpy(&((struct sockaddr_in *)&m_HostAddr)->sin_addr, he->h_addr_list[0], sizeof(((struct sockaddr_in *)&m_HostAddr)->sin_addr)); } // If a port name was supplied use it if (Port > 0) ((struct sockaddr_in *)&m_HostAddr)->sin_port = htons(Port); return true; } bool ppsocket:: getHost(string *Host, int *Port) { char *host; if (Host) { host = inet_ntoa(((struct sockaddr_in *)&m_HostAddr)->sin_addr); if (!host) { m_LastError = errno; return false; } *Host = host; } if (Port) *Port = ntohs(((struct sockaddr_in *)&m_HostAddr)->sin_port); return true; } /* * Local variables: * c-basic-offset: 4 * End: */