/build/buildd/libassa-3.4.1/assa/IPv4Socket.cpp

Go to the documentation of this file.
00001 // -*- c++ -*-
00002 //------------------------------------------------------------------------------
00003 //                          IPv4Socket.cpp
00004 //------------------------------------------------------------------------------
00005 //  Copyright (C) 1997-2002  Vladislav Grinchenko 
00006 //
00007 //  This library is free software; you can redistribute it and/or
00008 //  modify it under the terms of the GNU Library General Public
00009 //  License as published by the Free Software Foundation; either
00010 //  version 2 of the License, or (at your option) any later version.
00011 //------------------------------------------------------------------------------
00012 #include <sys/errno.h>
00013 
00014 #include "assa/MemDump.h"
00015 #include "assa/IPv4Socket.h"
00016 
00017 using namespace ASSA;
00018 
00019 Streambuf* 
00020 IPv4Socket::
00021 rdbuf (Streambuf* sb_) 
00022 { 
00023     trace_with_mask("IPv4Socket::rdbuf(sb_)",SOCKTRACE);
00024 
00025     if (sb_ == 0 || sb_ == m_rdbuf) {
00026         return (sb_);
00027     }
00028     Streambuf* old = m_rdbuf;
00029     m_rdbuf = sb_;
00030     return (old);
00031 }
00032 
00033 
00034 bool
00035 IPv4Socket::
00036 open (const int domain_)
00037 {
00038     trace_with_mask("IPv4Socket::open",SOCKTRACE);
00039     
00040     m_type = domain_;
00041     m_fd = ::socket(domain_, SOCK_STREAM, 0);
00042     if (m_fd < 0) {
00043         EL((ERROR,"OS::socket() error\n"));
00044         setstate (Socket::failbit);
00045         return (false);
00046     }
00047     clear ();
00048     turnOptionOn (Socket::nonblocking);
00049     return (true);
00050 }
00051 
00052 bool
00053 IPv4Socket::
00054 close()
00055 {
00056     trace_with_mask("IPv4Socket::close()",SOCKTRACE);
00057 
00058     if (m_fd >= 0) {
00059         DL((SOCK,"Closed FD: %d\n",m_fd));
00060 
00061         /*--- Flush data in output stream buffer ---*/
00062         flush ();
00063 		::close(m_fd);
00064         setstate (Socket::failbit);
00065         m_fd = -1;
00066 
00067         /*--- 
00068           Socket can be re-opened in the future.
00069           If there is some bytes left in it since last read(2),
00070           clean them up.
00071           ---*/
00072 
00073         if (m_rdbuf && m_rdbuf->in_avail ()) {
00074             for (int c; (c=m_rdbuf->sbumpc ()) != EOF;) { }
00075         }
00076     }
00077     return (true);
00078 }
00079 
00080 bool
00081 IPv4Socket::
00082 connect (const Address& his_address_)
00083 {
00084     trace_with_mask("IPv4Socket::connect()",SOCKTRACE);
00085 
00086     if (m_fd < 0 && open (getDomain()) == false) {
00087         return false;
00088     }
00089 
00090     int ret = ::connect (m_fd, 
00091                          (SA*) his_address_.getAddress(),
00092                          his_address_.getLength());
00093     if (ret < 0) {
00094         if (errno == EINPROGRESS) { // is it ASYNC connect in progress?
00095             DL((SOCK,"FD: %d OS::connect() error\n",m_fd));
00096         }
00097         else {
00098             EL((SOCK,"FD: %d OS::connect() error\n",m_fd));
00099         }
00100         return (false);
00101     }
00102     clear ();
00103     DL((SOCK,"Connection opened on FD: %d\n", m_fd));
00104     return (true);
00105 }
00106 
00107 bool 
00108 IPv4Socket::
00109 bind (const Address& addr_)
00110 {
00111     trace_with_mask("IPv4Socket::bind",SOCKTRACE);
00112 
00113     // if UNIX domain, save the path
00114     if ( getDomain() == AF_UNIX ) {
00115         char* p = ((SA_UN *) addr_.getAddress())->sun_path;
00116         m_path = new char[strlen(p)+1];
00117         strcpy(m_path, p);
00118         struct stat sb;
00119 
00120         if (stat (m_path, &sb) == 0) {
00121             if ( S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode) ) {
00122                 unlink(m_path);
00123             }
00124         }
00125     }
00126     /*---
00127       From Stevens, Ch 7.5 (p.196):
00128       "Set the SO_REUSEADDR socket option before calling bind(2)
00129       in all TCP servers."
00130       ---*/
00131     Assure_return ( turnOptionOn (reuseaddr) );
00132 
00133     int rt = ::bind(m_fd, addr_.getAddress(), addr_.getLength());
00134 
00135     if ( rt < 0) {
00136         EL((SOCK,"::bind() FD: %d failed\n",m_fd));
00137         setstate (Socket::failbit);
00138         return (false);
00139     }
00140     Assure_return ( (::listen(m_fd, 5) == 0) );
00141     return (true);
00142 }
00143 
00144 IPv4Socket*
00145 IPv4Socket::
00146 accept ()
00147 {
00148     /*
00149       Here's interesting spot - because accept() suppose to work
00150       both for INET and UNIX domain socket addresses, we have to
00151       allocate enough space and pass exact size of the address type 
00152       expected.
00153     
00154       Otherwise, if we use, for example, struct sockaddr_un as max.
00155       possible, and accept returns struct sockaddr_in, we can cast
00156       back to struct sockaddr_in, but internally address data members
00157       are not guaranteed to be aligned correctly!!!
00158     */
00159     trace_with_mask("IPv4Socket::accept",SOCKTRACE);
00160 
00161     socklen_t length = 0;
00162     int new_fd = -1;
00163     SA* remote_address = NULL;
00164 
00165     if ( getDomain() == AF_UNIX ) {
00166         length = sizeof(struct sockaddr_in);
00167         remote_address = (SA*) new SA_IN;
00168     }
00169     else { 
00170         remote_address = (SA*) new SA_UN;
00171         length = sizeof(struct sockaddr_un);
00172     }
00173     memset(remote_address, 0, length);
00174 
00175 #ifndef __CYGWIN32__
00176     new_fd = ::accept(m_fd, remote_address, &length);
00177 #else
00178     new_fd = ::accept(m_fd, remote_address, (int*)&length);
00179 #endif
00180     
00181     if ( new_fd < 0 ) {
00182         EL((ERROR,"OS::accept() failed\n"));
00183         close();
00184         return NULL;
00185     }
00186     if (length == sizeof(SA_IN)) {
00187         SA_IN* sa_in = (SA_IN*) remote_address;
00188 
00189         DL((SOCK,"Accepted new TCP connection from Addr %s, port %d\n", 
00190             inet_ntoa(sa_in->sin_addr), ntohs( sa_in->sin_port)));
00191     }
00192     else {
00193         SA_UN* sa_un = (SA_UN*) remote_address;
00194         DL((SOCK,"Accepted new UNIX connection from %s\n",
00195              sa_un->sun_path));
00196     }
00197     delete remote_address;
00198 
00199     IPv4Socket* s = new IPv4Socket (new_fd);
00200     s->clear ();
00201     s->turnOptionOn (Socket::nonblocking);
00202     return s;
00203 }
00204 
00205 int
00206 IPv4Socket::
00207 read (char* packet_, const unsigned int size_)
00208 {
00209     trace_with_mask("IPv4Socket::read",SOCKTRACE);
00210 
00211     register int len;
00212     register int sz = size_;
00213     char* tmp = packet_;
00214     
00215     if (m_fd < 0) return -1;
00216 
00217     len = 0;
00218     if (m_rdbuf->unbuffered ()) { 
00219         /*
00220           --- This needs to be redesigned ---
00221           I should read a character at a time in loop,
00222           until I get all characters, or EWOULDBLOCK or EOF.
00223           If ::read() returns 0 or -1, it will be converted
00224           by sbumpc() into EOF. Otherwise, sbumpc() returns
00225           character read. Is this the right thing here to do? 
00226         */
00227         if ((len = m_rdbuf->sbumpc ()) >= 0) {
00228             *tmp = len; 
00229             len = 1;
00230         }
00231     }
00232     else {
00233         len = m_rdbuf->sgetn (tmp, sz);
00234     }
00235     if (len == -1) {
00236         /*
00237           Non-blocking socket delivered partial packet.
00238         */
00239         if (errno != EWOULDBLOCK) {
00240             EL((ERROR,"::read(fd = %d) \n",m_fd));
00241             setstate (Socket::failbit);
00242         }
00243         return len;
00244     }
00245     tmp += len;
00246     sz -= len;
00247 
00248     if ((size_ - sz) == 0) 
00249     {
00250         DL((SOCK,"Peer has dropped connection FD: %d\n",m_fd));
00251         setstate (Socket::failbit | Socket::eofbit);
00252         return 0;
00253     }
00254 
00255     DL((SOCKTRACE,"==> FD: %d Received %d bytes\n", m_fd, size_ - sz));
00256     MemDump::dump_to_log (SOCKTRACE, "Data received:", packet_, size_ - sz);
00257 
00258     /*
00259       Return number of bytes read. If all requested bytes have been
00260       read, then sz is 0 and size_ is returned. If sz != 0, then
00261       writer has sent us a partial packet.
00262     */
00263     return (size_ - sz);        
00264 }
00265 
00266 int
00267 IPv4Socket::
00268 write(const char* packet_, const unsigned int size_)
00269 {
00270     trace_with_mask("IPv4Socket::write()",SOCKTRACE);
00271 
00272     register int ret = 0;
00273 
00274     if (m_fd < 0) return -1;
00275 
00276     if (m_rdbuf->unbuffered ()) {
00277         register int wlen = size_;
00278         register char* p = (char*) packet_;
00279 
00280         while (wlen-- > 0) {
00281             if (m_rdbuf->sputc (*p++) == EOF) {
00282                 return (EOF);
00283             }
00284         }
00285         ret = p - packet_;
00286     }
00287     else
00288         ret = m_rdbuf->sputn ((char*) packet_, size_);
00289 
00290     if (ret > 0) {
00291         DL((SOCK,"<= FD: %d Wrote %d bytes (requested %d bytes)\n",
00292             m_fd, ret, size_));
00293         MemDump::dump_to_log (SOCK, "Data written", (char*)packet_, ret);
00294     }
00295     return ret;
00296 }
00297 
00298 IPv4Socket*
00299 IPv4Socket::
00300 clone () const
00301 {
00302     const char self[] = "IPv4Socket::clone"; 
00303     trace_with_mask(self,SOCKTRACE);
00304 
00305     int nfd = dup (m_fd);
00306 
00307     IPv4Socket* s = new IPv4Socket (nfd);
00308 
00309     DL((SOCK,"Original socket has %d bytes in its get_area\n",
00310         m_rdbuf->in_avail ()));
00311 
00312     if (nfd < 0 || !good ()) {
00313         s->setstate (Socket::failbit);
00314     }
00315     else {
00316         s->clear ();
00317     }
00318     return s;
00319 }
00320 
00321 

Generated on Wed Jun 21 15:58:58 2006 for libassa by  doxygen 1.4.6