Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

wvtcp.cc

Go to the documentation of this file.
00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2002 Net Integration Technologies, Inc.
00004  * 
00005  * WvStream-based TCP connection class.
00006  */
00007 #include "wvtcp.h"
00008 #include "wvstreamlist.h"
00009 #include "wvmoniker.h"
00010 
00011 #include <fcntl.h>
00012 
00013 #ifdef _WIN32
00014 #define setsockopt(a,b,c,d,e) setsockopt(a,b,c, (const char*) d,e)
00015 #define getsockopt(a,b,c,d,e) getsockopt(a,b,c,(char *)d, e) 
00016 #undef errno
00017 #define errno GetLastError()
00018 #define EWOULDBLOCK WSAEWOULDBLOCK
00019 #define EINPROGRESS WSAEINPROGRESS
00020 #define EISCONN WSAEISCONN
00021 #define EALREADY WSAEALREADY
00022 #define SOL_TCP IPPROTO_TCP
00023 #define SOL_IP IPPROTO_IP
00024 #else
00025 #include <errno.h>
00026 #include <netdb.h>
00027 #include <sys/socket.h>
00028 #include <netinet/in.h>
00029 #include <netinet/ip.h>
00030 #include <netinet/tcp.h>
00031 #endif
00032 
00033 
00034 static IWvStream *creator(WvStringParm s, IObject *, void *)
00035 {
00036     return new WvTCPConn(s);
00037 }
00038 
00039 static WvMoniker<IWvStream> reg("tcp", creator);
00040 
00041 
00042 WvTCPConn::WvTCPConn(const WvIPPortAddr &_remaddr)
00043 {
00044     remaddr = _remaddr;
00045     resolved = true;
00046     connected = false;
00047     
00048     do_connect();
00049 }
00050 
00051 
00052 WvTCPConn::WvTCPConn(int _fd, const WvIPPortAddr &_remaddr) :
00053     WvFDStream(_fd)
00054 {
00055     remaddr = _remaddr;
00056     resolved = true;
00057     connected = true;
00058     nice_tcpopts();
00059 }
00060 
00061 
00062 WvTCPConn::WvTCPConn(WvStringParm _hostname, __u16 _port) :
00063     hostname(_hostname)
00064 {
00065     struct servent* serv;
00066     char *hnstr = hostname.edit(), *cptr;
00067     
00068     cptr = strchr(hnstr, ':');
00069     if (!cptr)
00070         cptr = strchr(hnstr, '\t');
00071     if (!cptr)
00072         cptr = strchr(hnstr, ' ');
00073     if (cptr)
00074     {
00075         *cptr++ = 0;
00076         serv = getservbyname(cptr, NULL);
00077         remaddr.port = serv ? ntohs(serv->s_port) : atoi(cptr);
00078     }
00079     
00080     if (_port)
00081         remaddr.port = _port;
00082     
00083     resolved = connected = false;
00084     
00085     WvIPAddr x(hostname);
00086     if (x != WvIPAddr())
00087     {
00088         remaddr = WvIPPortAddr(x, remaddr.port);
00089         resolved = true;
00090         do_connect();
00091     }
00092     else
00093         dns.findaddr(0, hostname, NULL);
00094 }
00095 
00096 
00097 WvTCPConn::~WvTCPConn()
00098 {
00099     // nothing to do
00100 }
00101 
00102 
00103 // Set a few "nice" options on our socket... (read/write, non-blocking, 
00104 // keepalive)
00105 void WvTCPConn::nice_tcpopts()
00106 {
00107 #ifndef _WIN32
00108     fcntl(getfd(), F_SETFD, FD_CLOEXEC);
00109     fcntl(getfd(), F_SETFL, O_RDWR|O_NONBLOCK);
00110 #else
00111     u_long arg = 1;
00112     ioctlsocket(getfd(), FIONBIO, &arg); // non-blocking
00113 #endif
00114     int value = 1;
00115     setsockopt(getfd(), SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value));
00116 }
00117 
00118 
00119 void WvTCPConn::low_delay()
00120 {
00121     int value;
00122     
00123     value = 1;
00124     setsockopt(getfd(), SOL_TCP, TCP_NODELAY, &value, sizeof(value));
00125     
00126 #ifndef _WIN32
00127     value = IPTOS_LOWDELAY;
00128     setsockopt(getfd(), SOL_IP, IP_TOS, &value, sizeof(value));
00129 #endif
00130 }
00131 
00132 
00133 void WvTCPConn::debug_mode()
00134 {
00135     int value = 0;
00136     setsockopt(getfd(), SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value));
00137 }
00138 
00139 void WvTCPConn::do_connect()
00140 {
00141     int rwfd = socket(PF_INET, SOCK_STREAM, 0);
00142     if (rwfd < 0)
00143     {
00144         seterr(errno);
00145         return;
00146     }
00147     setfd(rwfd);
00148     
00149     nice_tcpopts();
00150     
00151     sockaddr *sa = remaddr.sockaddr();
00152     if (connect(getfd(), sa, remaddr.sockaddr_len()) < 0
00153         && errno != EINPROGRESS
00154 #ifdef _WIN32
00155         && errno != WSAEWOULDBLOCK
00156 #endif
00157         )
00158     {
00159         seterr(errno);
00160         delete sa;
00161         return;
00162     }
00163     
00164     delete sa;
00165 }
00166 
00167 
00168 void WvTCPConn::check_resolver()
00169 {
00170     const WvIPAddr *ipr;
00171     int dnsres = dns.findaddr(0, hostname, &ipr);
00172     
00173     if (dnsres == 0)
00174     {
00175         // error resolving!
00176         resolved = true;
00177         seterr(WvString("Unknown host \"%s\"", hostname));
00178     }
00179     else if (dnsres > 0)
00180     {
00181         remaddr = WvIPPortAddr(*ipr, remaddr.port);
00182         resolved = true;
00183         do_connect();
00184     }
00185 }
00186 
00187 #ifndef SO_ORIGINAL_DST
00188 # define SO_ORIGINAL_DST 80
00189 #endif
00190 
00191 WvIPPortAddr WvTCPConn::localaddr()
00192 {
00193     sockaddr_in sin;
00194     socklen_t sl = sizeof(sin);
00195     
00196     if (!isok())
00197         return WvIPPortAddr();
00198     
00199     if (
00200 #ifndef _WIN32
00201         getsockopt(getfd(), SOL_IP, SO_ORIGINAL_DST, (char*)&sin, &sl) < 0 &&
00202 #endif
00203         getsockname(getfd(), (sockaddr *)&sin, &sl))
00204     {
00205         return WvIPPortAddr();
00206     }
00207     
00208     return WvIPPortAddr(&sin);
00209 }
00210 
00211 
00212 const WvIPPortAddr *WvTCPConn::src() const
00213 {
00214     return &remaddr;
00215 }
00216 
00217 
00218 bool WvTCPConn::pre_select(SelectInfo &si)
00219 {
00220     if (!resolved)
00221     {
00222         if (dns.pre_select(hostname, si))
00223         {
00224             check_resolver();
00225             if (!isok())
00226                 return true; // oops, failed to resolve the name!
00227         }
00228     }
00229 
00230     if (resolved && isok()) // name might be resolved now.
00231     {
00232         bool oldw = si.wants.writable, retval;
00233         if (!isconnected()) {
00234             si.wants.writable = true; 
00235 #ifdef _WIN32
00236             // WINSOCK INSANITY ALERT!
00237             // In Unix, you detect the success OR failure of a non-blocking 
00238             // connect() by select()ing with the socket in the write set.
00239             // HOWEVER, in Windows, you detect the success of connect() 
00240             // by select()ing with the socket in the write set, and the failure
00241             // of connect() by select()ing with the socket in the exception set!
00242             si.wants.isexception = true;
00243 #endif
00244         }
00245         retval = WvFDStream::pre_select(si);
00246         si.wants.writable = oldw;
00247         return retval;
00248     }
00249     else
00250         return false;
00251 }
00252                           
00253 
00254 bool WvTCPConn::post_select(SelectInfo &si)
00255 {
00256     bool result = false;
00257 
00258     if (!resolved)
00259         check_resolver();
00260     else
00261     {
00262         result = WvFDStream::post_select(si);
00263 
00264         if (result && !connected)
00265         {
00266             int conn_res;
00267             socklen_t res_size = sizeof(conn_res);
00268             if (getsockopt(getfd(), SOL_SOCKET, SO_ERROR, &conn_res, &res_size))
00269             {
00270                 // getsockopt failed
00271                 seterr(errno);
00272             }
00273             else if (conn_res != 0)
00274             {
00275                 // connect failed
00276                 seterr(conn_res);
00277             }
00278             else
00279             {
00280                 // connect succeeded!
00281                 connected = true;
00282             }
00283         }
00284     }
00285     
00286     return result;
00287 }
00288 
00289 
00290 bool WvTCPConn::isok() const
00291 {
00292     return !resolved || WvFDStream::isok();
00293 }
00294 
00295 
00296 size_t WvTCPConn::uwrite(const void *buf, size_t count)
00297 {
00298     if (connected)
00299         return WvFDStream::uwrite(buf, count);
00300     else
00301         return 0; // can't write yet; let them enqueue it instead
00302 }
00303 
00304 
00305 
00306 
00307 WvTCPListener::WvTCPListener(const WvIPPortAddr &_listenport)
00308         : listenport(_listenport)
00309 {
00310     listenport = _listenport;
00311     auto_list = NULL;
00312     auto_userdata = NULL;
00313     
00314     sockaddr *sa = listenport.sockaddr();
00315     
00316     int x = 1;
00317 
00318     setfd(socket(PF_INET, SOCK_STREAM, 0));
00319     if (getfd() < 0
00320         || setsockopt(getfd(), SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x))
00321 #ifndef _WIN32
00322         || fcntl(getfd(), F_SETFD, 1)
00323 #endif
00324         || bind(getfd(), sa, listenport.sockaddr_len())
00325         || listen(getfd(), 5))
00326     {
00327         seterr(errno);
00328     }
00329     
00330     if (listenport.port == 0) // auto-select a port number
00331     {
00332         socklen_t namelen = listenport.sockaddr_len();
00333         
00334         if (getsockname(getfd(), sa, &namelen) != 0)
00335             seterr(errno);
00336         else
00337             listenport = WvIPPortAddr((sockaddr_in *)sa);
00338     }
00339     
00340     delete sa;
00341 }
00342 
00343 
00344 WvTCPListener::~WvTCPListener()
00345 {
00346     close();
00347 }
00348 
00349 
00350 //#include <wvlog.h>
00351 void WvTCPListener::close()
00352 {
00353     WvFDStream::close();
00354 /*    WvLog log("ZAP!");
00355     
00356     log("Closing TCP LISTENER at %s!!\n", listenport);
00357     abort();*/
00358 }
00359 
00360 
00361 WvTCPConn *WvTCPListener::accept()
00362 {
00363     struct sockaddr_in sin;
00364     socklen_t len = sizeof(sin);
00365     int newfd;
00366     WvTCPConn *ret;
00367 
00368     newfd = ::accept(getfd(), (struct sockaddr *)&sin, &len);
00369     ret = new WvTCPConn(newfd, WvIPPortAddr(&sin));
00370     return ret;
00371 }
00372 
00373 
00374 void WvTCPListener::auto_accept(WvStreamList *list,
00375                                 WvStreamCallback callfunc, void *userdata)
00376 {
00377     auto_list = list;
00378     auto_callback = callfunc;
00379     auto_userdata = userdata;
00380     setcallback(accept_callback, this);
00381 }
00382 
00383 
00384 void WvTCPListener::accept_callback(WvStream &, void *userdata)
00385 {
00386     WvTCPListener &l = *(WvTCPListener *)userdata;
00387 
00388     WvTCPConn *connection = l.accept();
00389     connection->setcallback(l.auto_callback, l.auto_userdata);
00390     l.auto_list->append(connection, true);
00391 }
00392 
00393 
00394 size_t WvTCPListener::uread(void *, size_t)
00395 {
00396     return 0;
00397 }
00398 
00399 
00400 size_t WvTCPListener::uwrite(const void *, size_t)
00401 {
00402     return 0;
00403 }
00404 
00405 
00406 const WvIPPortAddr *WvTCPListener::src() const
00407 {
00408     return &listenport;
00409 }
00410 

Generated on Sat Feb 21 21:05:34 2004 for WvStreams by doxygen 1.3.5