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 Tue Oct 5 01:09:21 2004 for WvStreams by doxygen 1.3.7