// -*- c++ -*- //------------------------------------------------------------------------------ // $Id: Socket.cpp,v 1.13 2006/09/24 17:35:33 vlg Exp $ //------------------------------------------------------------------------------ // Socket.C //------------------------------------------------------------------------------ // Copyright (c) 1999,2005 by Vladislav Grinchenko // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. //------------------------------------------------------------------------ // Created: 03/22/99 //------------------------------------------------------------------------ #include #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) || defined (__CYGWIN32__) # include #endif #if defined (__NetBSD__) # include # include #endif #if defined (WIN32) # define O_NONBLOCK 04000 typedef unsigned int socklen_t; #endif #include "assa/Socket.h" #include "assa/XDRHack.h" using namespace ASSA; const int ASSA::Socket::PGSIZE = 4096; /** There can be bytes already stored in the Streambuf buffer, in addition to bytes that arrived and are ready for reading in the socket buffer (kernel address space). Sum of these two numbers is the true number of bytes available for immediate reading. */ int Socket:: getBytesAvail (void) const { trace_with_mask("Socket::getBytesAvail",SOCKTRACE); Socket* This = (Socket*) this; u_long ba = 0; int ret = 0; #if defined(WIN32) ret = ioctlsocket (m_fd, FIONREAD, &ba); #else ret = ioctl (m_fd, FIONREAD, &ba); #endif if (ret == -1) { EL((ASSAERR,"ioctl(2) failed with ret: %d\n", ret)); return ret; } ba += This->rdbuf ()->in_avail (); DL((SOCKTRACE,"%ld bytes available for reading\n", ba)); return (int(ba)); } Socket& Socket:: flush () { if (good () && rdbuf ()) { if (rdbuf ()->pubsync () == EOF) { setstate (badbit); } } return (*this); } int Socket:: set_option (int level_, int optname_, int val_) { int ret = setsockopt (m_fd, level_, optname_, (const char*) &val_, sizeof (val_)); if (ret < 0) { setstate (Socket::failbit); } return ret; } /** This is a private function used only by class Socket. The only value for flags_ is O_NONBLOCK. */ int Socket:: set_fd_options (long flags_) { trace_with_mask("Socket::set_fd_options",SOCKTRACE); int val; int ret; #if defined (WIN32) u_long set_nonblock = 1; if ((val = ioctlsocket (m_fd, FIONBIO, &set_nonblock)) == 0) { m_nonblocking = true; return 0; } return -1; #else // POSIX/UNIX if ((val = ::fcntl (m_fd, F_GETFL, 0)) < 0) { return -1; } val |= flags_; // turn flags on DL ((SOCKTRACE,"Set flags fcntl(%d, %s)\n", m_fd, decode_fcntl_flags (val).c_str ())); ret = ::fcntl (m_fd, F_SETFL, val); val = ::fcntl (m_fd, F_GETFL, 0); DL ((SOCKTRACE,"Flags are set to %s via fcntl(25)\n", decode_fcntl_flags (val).c_str ())); return (ret); #endif } /** This is a private function used only by class Socket. The only value for flags_ is O_NONBLOCK - set the socket back to the blocking mode. */ int Socket:: clear_fd_options (long flags_) { trace_with_mask("Socket::clear_fd_options",SOCKTRACE); long oldflags; long newflags; int ret; #if defined (WIN32) u_long set_block = 0; if ((ret = ioctlsocket (m_fd, FIONBIO, &set_block)) == 0) { m_nonblocking = false; return 0; } return -1; #else if ((oldflags = ::fcntl (m_fd, F_GETFL, 0)) < 0) { return -1; } newflags = oldflags & ~flags_; // clear flags DL ((SOCKTRACE,"Set flags fcntl(%d, %s)\n", m_fd, decode_fcntl_flags (newflags).c_str ())); ret = ::fcntl (m_fd, F_SETFL, newflags); newflags = ::fcntl (m_fd, F_GETFL, 0); DL ((SOCKTRACE,"Flags are set to %s via fcntl(%d)\n", decode_fcntl_flags (newflags).c_str ())); return (ret); #endif } bool Socket:: turnOptionOn (opt_t opt_) { trace_with_mask("Socket::turnOptionOn",SOCKTRACE); if (nonblocking == opt_) return set_fd_options (O_NONBLOCK); int optname; if (reuseaddr == opt_) optname = SO_REUSEADDR; else { EL((ASSAERR,"Invalid socket option\n")); return false; } return set_option (SOL_SOCKET, optname, 1) == 0; } bool Socket:: turnOptionOff (opt_t opt_) { trace_with_mask("Socket::turnOptionOff",SOCKTRACE); if (nonblocking == opt_) return clear_fd_options (O_NONBLOCK); int optname; if (reuseaddr == opt_) optname = SO_REUSEADDR; else { EL((ASSAERR,"Invalid socket option\n")); return false; } return set_option (SOL_SOCKET, optname, 0) == 0; } bool Socket:: setOption (opt_t opt_, int arg_) { trace_with_mask("Socket::setOption(,)",SOCKTRACE); int optname; if (nonblocking == opt_) { return (arg_ == 1) ? set_fd_options (O_NONBLOCK) : clear_fd_options (O_NONBLOCK); } if (rcvlowat == opt_) { optname = SO_RCVLOWAT; } else if (sndlowat == opt_) { optname = SO_SNDLOWAT; } else { EL((ASSAERR,"Invalid socket option\n")); return false; } return set_option (SOL_SOCKET, optname, arg_) == 0; } int Socket:: getOption (opt_t opt_) const { trace_with_mask("Socket::getOption",SOCKTRACE); int optval = 0; if (nonblocking == opt_) { #if defined (WIN32) return (m_nonblocking ? 1 : 0); #else if ((optval = ::fcntl (m_fd, F_GETFL, 0)) < 0) { return -1; } return ((optval & O_NONBLOCK) == O_NONBLOCK ? 1 : 0); #endif } int optname; int level = SOL_SOCKET; bool bin = false; socklen_t len = sizeof (optval); int ret; if (rcvlowat == opt_) { optname = SO_RCVLOWAT; } else if (sndlowat == opt_) { optname = SO_SNDLOWAT; } else if (reuseaddr == opt_) { optname = SO_REUSEADDR; bin = true; } else { EL((ASSAERR,"Invalid socket option\n")); return (-1); } #if defined (__CYGWIN32__) || defined (WIN32) ret = getsockopt (m_fd, level, optname, (char*) &optval, (int*)&len); #else // posix/unix ret = getsockopt (m_fd, level, optname, (char*) &optval, &len); #endif if (ret < 0) { return (-1); } if (bin) { return (ret == 0 ? 0 : 1); } return (ret); } int Socket:: ignore(int n_, int delim_) { trace_with_mask("Socket::ignore",SOCKTRACE); register int b; register int count = 0; register char c; if (n_ == INT_MAX && delim_ == EOF) { char buf[PGSIZE]; while ((b = read (buf, PGSIZE))) { count += b; } setstate (Socket::eofbit|Socket::failbit); return count; } for (; n_; n_--, count++) { if ( (b = read (&c, 1)) == 0 ) { setstate (Socket::eofbit|Socket::failbit); break; } if ( c == delim_ ) break; } return count; } //----------------------------------------------------------------------------- // Input operators //----------------------------------------------------------------------------- /** From xdr_simple(3N) man page: "xdr_char() translates between C characters and their external representations. Note: encoded characters are not packed, and occupy 4 bytes each." From SunOS 5.3 Network Interfaces Programmer's Guide, "XDR Protocol Specification": "The representation of all items requires a multipe of four bytes (or 32 bits) of data. ... The n bytes are followed by enough (0 to 3) residual zero bytes, r, to make the total byte count a multiple of four." The implication if this is that even though we transerfer 1 byte, we still have to allocate and transfer 4 bytes to hold it. */ Socket& Socket:: operator>>(char& n_) { int c = 0; int len = sizeof (int); XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &c, len, XDR_DECODE); if (read ((char* ) &c, len) == len) { xdr_char (&xdrs, &n_); } else { setstate (Socket::eofbit|Socket::failbit); } xdr_destroy(&xdrs); return *this; } /** For format explanation, see comment to operator<<(string&) */ Socket& Socket:: operator>> (std::string& s_) { char c = 0; size_t n = 0; s_ = ""; (*this) >> n; if (n == 0) { return *this; } size_t len = n; while (len-- && read (&c, 1) == 1) { s_ += c; } ignore (4 - n % 4); return *this; } Socket& Socket:: operator>> (short& n_) { short val; if (read ((char*) &val, sizeof(short)) == sizeof(short)) { n_ = (short) ntohs ((short)val); } else { setstate (Socket::eofbit|Socket::failbit); } return *this; } Socket& Socket:: operator>> (unsigned short& n_) { u_short val; if (read ((char*) &val, sizeof(u_short)) == sizeof(u_short)) { n_ = (u_short) ntohs ((u_short)val); } else { setstate (Socket::eofbit|Socket::failbit); } return *this; } #define LONGEST long /* On 64-bit platforms, sizeof (long) = 8 bytes. * ntohl()/htonh() operats only on int32_t types which is 4 bytes long * everywhere. So, for 64-bit longs we need to process twice as much * and swapt data accordingly. */ #define READ_INT(TYPE) \ Socket& Socket::operator>>(TYPE& n_) \ {\ LONGEST val;\ int typesz = sizeof(TYPE);\ if (read ( (char* ) &val, typesz) == typesz) {\ if (sizeof(int32_t) <= typesz) {\ n_ = (TYPE) ntohl (val); \ }\ else {\ if (Socket::is_little_endian ()) {\ *((int32_t*)(&n_)+1) = ntohl (*((int32_t*)(&val)) );\ *((int32_t*)(&n_) ) = ntohl (*((int32_t*)(&val))+1);\ }\ else {\ *((int32_t*)(&n_) ) = ntohl (*((int32_t*)(&val)) );\ *((int32_t*)(&n_)+1) = ntohl (*((int32_t*)(&val))+1);\ }\ }\ }\ else {\ setstate (Socket::eofbit|Socket::failbit);\ }\ return *this;\ } READ_INT(int); READ_INT(unsigned int); READ_INT(long); READ_INT(unsigned long); Socket& Socket:: operator>> (float& n_) { float val; XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &val, sizeof(float), XDR_DECODE); if (read ((char*) &val, sizeof(float)) == sizeof(float)) { xdr_float (&xdrs, &n_); } else { setstate (Socket::eofbit|Socket::failbit); } xdr_destroy (&xdrs); return *this; } Socket& Socket:: operator>> (double& n_) { double val = 0; XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &val, sizeof(double), XDR_DECODE); if (read ((char*) &val, sizeof(double)) == sizeof(double)) { xdr_double (&xdrs, &n_); } else { setstate (Socket::eofbit|Socket::failbit); } xdr_destroy (&xdrs); return *this; } //----------------------------------------------------------------------------- // Output operators //----------------------------------------------------------------------------- Socket& Socket:: operator<< (char n_) { /* See comment to operator>>(char n_) */ int buf = 0; int len = sizeof (int); XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &buf, len, XDR_ENCODE); xdr_char (&xdrs, &n_); if (write ((const char*) &buf, len) != len) { (Socket::eofbit|Socket::failbit); } xdr_destroy (&xdrs); return *this; } /** XDR STRING representation: (from RFC 1832 - http://www.faqs.org/rfcs/rfc1832.html) 0 1 2 3 4 5 ... +-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ | length n |byte0|byte1|...| n-1 | 0 |...| 0 | +-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+ |<-------4 bytes------->|<------n bytes------>|<---r bytes--->| |<----n+r (where (n+r) mod 4 = 0)---->| If n is not a multiple of four, then the n bytes are followed by enough (0 to 3) residual zero bytes, r, to make the total byte count a multiple of four. In other words, (4 - n % 4) == r. We follow the format, but send data bytes as-is - no conversion is required. We also pad data with r bytes. */ Socket& Socket:: operator<< (const std::string& s_) { static const char pad [4] = { 0, 0, 0, 0 }; (*this) << s_.length (); int ret = write (s_.c_str (), s_.length ()); if ( ret != s_.length () ) { setstate (Socket::eofbit|Socket::failbit); } size_t r = 4 - s_.length() % 4; if (r) { if (write (pad, r) != r) { setstate (Socket::eofbit|Socket::failbit); } } return *this; } Socket& Socket:: operator<< (short n_) { short val = (short) htons((short)n_); if (write ((const char*) &val, sizeof(short)) != sizeof(short)) { setstate (Socket::eofbit|Socket::failbit); } return *this; } Socket& Socket:: operator<< (unsigned short n_) { u_short val = (u_short) htons((u_short)n_); if (write ((const char*) &val, sizeof(u_short)) != sizeof(u_short)) { setstate (Socket::eofbit|Socket::failbit); } return *this; } #define WRITE_INT(TYPE) \ Socket& Socket::operator<< (TYPE n_) \ { \ LONGEST val;\ int typesz = sizeof(TYPE);\ if (sizeof(int32_t) <= typesz) {\ val = (TYPE) ntohl (n_); \ }\ else {\ if (Socket::is_little_endian ()) {\ *((int32_t*)(&val)+1) = htonl (*((int32_t*)(&n_)) );\ *((int32_t*)(&val) ) = htonl (*((int32_t*)(&n_))+1);\ }\ else {\ *((int32_t*)(&val) ) = htonl (*((int32_t*)(&n_)) );\ *((int32_t*)(&val)+1) = htonl (*((int32_t*)(&n_))+1);\ }\ }\ if (write ((const char*) &val, typesz) != typesz) {\ setstate (Socket::eofbit|Socket::failbit);\ }\ return *this;\ } WRITE_INT(int); WRITE_INT(unsigned int); WRITE_INT(long); WRITE_INT(unsigned long); Socket& Socket:: operator<< (float n_) { float buf, f = n_; XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &buf, sizeof(float), XDR_ENCODE); xdr_float (&xdrs, &f); int ret = write ((const char*) &buf, sizeof(float)); xdr_destroy (&xdrs); if ( ret != sizeof(float) ) { setstate (Socket::eofbit|Socket::failbit); } return *this; } Socket& Socket:: operator<< (double n_) { double buf, f = n_; XDR xdrs; xdrmem_create (&xdrs, (caddr_t) &buf, sizeof(double), XDR_ENCODE); xdr_double (&xdrs, &f); int ret = write ((const char*) &buf, sizeof(double)); xdr_destroy (&xdrs); if ( ret != sizeof(double) ) { setstate (Socket::eofbit|Socket::failbit); } return *this; } void Socket:: dumpState (void) const { trace_with_mask("Socket::dumpState",SOCKTRACE); char state_set[] = "[ set]\n"; char state_not_set[] = "[not set]\n"; std::ostringstream msg; msg << "\n"; msg << "\tTesting good() ....... "; if (this->good ()) msg << state_set; else msg << state_not_set; msg << "\tTesting eof() ........ "; if (this->eof ()) msg << state_set; else msg << state_not_set; msg << "\tTesting fail() ....... "; if (this->fail ()) msg << state_set; else msg << state_not_set; msg << "\tTesting bad() ........ "; if (this->bad ()) msg << state_set; else msg << state_not_set; msg << "\tTesting !() .......... "; if ( !(*this) ) msg << state_set; else msg << state_not_set; msg << "\tTesting void *() ..... "; if ( *this ) msg << state_set; else msg << state_not_set; msg << "\tTesting nonblocking... "; if (getOption (nonblocking) == 1) msg << state_set; else msg << state_not_set; /*--- Terminate stream buffer ---*/ msg << std::ends; DL((SOCKTRACE,"%s\n", msg.str ().c_str ())); } bool Socket:: is_little_endian () { union { char c [sizeof (short)]; short v; } endian_u; endian_u.v = 256; return (endian_u.c [0] == 0); } string Socket:: decode_fcntl_flags (long mask_) { string answer; #if !defined (WIN32) if (mask_ & O_RDONLY) { answer = "O_RDONLY|"; // 000000 } if (mask_ & O_WRONLY) { answer += "O_WRONLY|"; // 000001 } if (mask_ & O_RDWR) { answer += "O_RDWR|"; // 000002 } if (mask_ & O_APPEND) { answer += "O_APPEND|"; // 001000 } if (mask_ & O_NONBLOCK) { answer += "O_NONBLOCK|";// 004000 } if (mask_ & O_SYNC) { answer += "O_SYNC|"; // 010000 } if (mask_ & O_ASYNC) { // 020000 answer += "O_ASYNC|"; } answer.erase (answer.end () - 1); #endif return answer; }