// Socket.C -*- C++ -*- // Copyright (C) 2002 Clinton Ebadi // Copyright (c) 1997, 1998 Etienne BERNARD // 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 // 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. #include "Socket.H" #include "Bot.H" #include #include #include #include #include #include #include #include #include #include Socket::Socket() : remotePort(0), remoteAddress(0), localPort(0), localAddress(0) { fd = new s_fd; fd->fd = socket(AF_INET, SOCK_STREAM, 0); } Socket::Socket(int newfd, unsigned long newAddress, int newPort) : remotePort(newPort), remoteAddress(newAddress), localPort(0), localAddress(0) { fd = new s_fd; fd->fd = newfd; } Socket::Socket(const Socket &s) : remotePort(s.remotePort), remoteAddress(s.remoteAddress), localPort(s.localPort), localAddress(s.localAddress) { s.fd->n++; fd = s.fd; } Socket::~Socket() { if (--fd->n == 0) { close(); delete fd; } } int Socket::getRemotePort() const { return remotePort; } int Socket::getLocalPort() const { return localPort; } int Socket::getFileDescriptor() const { return fd->fd; } bool Socket::isConnected() { return fd->fd != -1; } bool Socket::setRemoteHostname(String hostname) { struct hostent *host; if ((host = gethostbyname((const char *)hostname)) == 0) return false; memcpy(&remoteAddress, host->h_addr, host->h_length); remoteAddress = ntohl(remoteAddress); return true; } bool Socket::setRemoteIP(unsigned long address) { remoteAddress = address; return true; } bool Socket::setRemotePort(int p) { remotePort = p; return true; } bool Socket::setLocalHostname(String hostname) { struct hostent *host; if ((host = gethostbyname((const char *)hostname)) == 0) return false; memcpy(&localAddress, host->h_addr, host->h_length); struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; local_addr.sin_port = htons(localPort); local_addr.sin_addr.s_addr = htonl(localAddress); if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) return false; return true; } bool Socket::setLocalIP(unsigned long address) { localAddress = address; struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; local_addr.sin_port = htons(localPort); local_addr.sin_addr.s_addr = htonl(localAddress); if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) return false; return true; } bool Socket::setLocalPort(int p) { localPort = p; struct sockaddr_in local_addr; local_addr.sin_family = AF_INET; local_addr.sin_port = htons(localPort); local_addr.sin_addr.s_addr = htonl(localAddress); if (bind(fd->fd, (struct sockaddr *)(&local_addr), sizeof(local_addr)) < 0) return false; return true; } bool Socket::setNonBlocking(bool nonblock) { long flags; if (fd->fd == -1) return false; // We first get the file descriptor's flags if (!fcntl(fd->fd, F_GETFL, &flags)) return false; if (nonblock) return fcntl(fd->fd, F_SETFL, flags | O_NONBLOCK); else return fcntl(fd->fd, F_SETFL, flags & ~O_NONBLOCK); } bool Socket::connect() { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(remoteAddress); addr.sin_port = htons(remotePort); if (::connect(fd->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { // Bot::logLine (String("Socket Error: ") // + strerror (errno) + String("\n")); return false; } return true; } bool Socket::listen(int backlog) { return ::listen(fd->fd, backlog) == 0; } Socket Socket::accept() { struct sockaddr_in addr; socklen_t addrlen=sizeof(addr); int newfd = ::accept(fd->fd, (sockaddr *)&addr, &addrlen); if (newfd == -1) return Socket(-1, 0, 0); unsigned long newRemoteAddress = ntohl(addr.sin_addr.s_addr); int newRemotePort = ntohs(addr.sin_port); return Socket(newfd, newRemoteAddress, newRemotePort); } void Socket::close() { ::close(fd->fd); fd->fd = -1; } bool Socket::write(String s, bool m) { if (fd->fd == -1) return false; if (m) { if (::write(fd->fd, (const char *)s, s.length()) + ::write(fd->fd, "\n", 1) != s.length() + 1) return false; } else if (::write(fd->fd, (const char *)s, s.length()) + ::write(fd->fd, "\r\n", 2) != s.length() + 2) return false; return true; } String Socket::readLine() { // fixme: this could probably be sped up (use some faster way of // reading from the socket than reading a char at a time) // We allocate the buffer statically to keep from having to overhead // of reallocating a new buffer every time we call this (which is // every time anything happens on IRC so the overhead of // re-allocating a buffer every time is potentially too large). // // Since it is static, and the length of lines will differ for each // read, an embedded \0 is inserted after the last character of the // line and then a copy of the the c_str is returned as a String so // that only the line that was just read is returned. static std::string buf (512, ' '); std::string::size_type pos = 0; // pos in buffer int nb; // number of bytes read by ::read char r; // temp var for storing output of read into std::string::size_type length = buf.length (); do { nb = ::read(fd->fd, &r, 1); switch (nb) { case 0: return String(""); case -1: if (errno != EINTR && errno != EAGAIN) return String(""); sleep(1); } if (nb != -1) if (pos < length) buf[pos++] = r; else { buf.resize (length * 2); length = buf.length (); buf[pos++] = r; } } while (r != '\n'); if (pos > 1 && buf[pos-2] == '\r') buf[pos-2] = '\0'; else buf[pos-1] = '\0'; // c_str () is used because the String constructor for std::string // will copy the entire std::string into it when we only want it to // copy up to the first null. return String (buf.c_str ()); } String Socket::readChar() { char r[2]; int nb; nb = ::read(fd->fd, &r, 1); switch (nb) { case 0: return String(""); case -1: if(errno != EINTR && errno != EAGAIN) return String(""); sleep(1); } r[1] = '\0'; return String(r); } // // Here we use a circular buffer to read from the socket // String // Socket::readLine() // { // // The result string // char result[512]; // // Position in the result string // int pos = 0; // // Number of chars read from the socket // int nb; // // First, we read from the socket // beginning: // if (end <= begin) { // nb = ::read(fd->fd, buf+end, begin - end); // } else { // nb = ::read(fd->fd, buf+end, 1024 - end); // } // if (nb == -1 && errno != EINTR && errno != EAGAIN) // return String(""); // end = (end + nb) % 1024; // // Fine, now we read our string from the buffer // while (buf[begin] != '\r' && buf[begin] != '\n') { // result[pos++] = buf[begin++]; // begin %= 1024; // if (begin == end) // goto beginning; // } // result[pos] = '\0'; // // Now we skip the final '\r' and '\n' // if (buf[begin] == '\r') // begin = (begin + 1) % 1024; // if (buf[begin] == '\n') // begin = (begin + 1) % 1024; // // And we return the result // return String(result); // } // bool // Socket::hasData() // { // cout << "DEBUG hasData = " << (begin != end) << endl; // return begin != end; // }