00001
00002
00003
00004
00005
00006
00007 #include "wvtcp.h"
00008 #include "wvistreamlist.h"
00009 #include "wvmoniker.h"
00010 #include "wvlinkerhack.h"
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 #undef EINVAL
00023 #define EINVAL WSAEINVAL
00024 #define SOL_TCP IPPROTO_TCP
00025 #define SOL_IP IPPROTO_IP
00026 #define FORCE_NONZERO 1
00027 #else
00028 # if HAVE_STDLIB_H
00029 # include <stdlib.h>
00030 # endif
00031 #endif
00032 #if HAVE_SYS_SOCKET_H
00033 # include <sys/socket.h>
00034 #endif
00035 #if HAVE_NETDB_H
00036 # include <netdb.h>
00037 #endif
00038 #if HAVE_NETINET_IN_H
00039 # include <netinet/in.h>
00040 #endif
00041 #if HAVE_NETINET_IP_H
00042 # if HAVE_NETINET_IN_SYSTM_H
00043 # include <netinet/in_systm.h>
00044 # endif
00045 # include <netinet/ip.h>
00046 #endif
00047 #if HAVE_NETINET_TCP_H
00048 # include <netinet/tcp.h>
00049 #endif
00050
00051 #ifndef FORCE_NONZERO
00052 #define FORCE_NONZERO 0
00053 #endif
00054
00055 WV_LINK(WvTCPConn);
00056
00057
00058 static IWvStream *creator(WvStringParm s)
00059 {
00060 return new WvTCPConn(s);
00061 }
00062
00063 static WvMoniker<IWvStream> reg("tcp", creator);
00064
00065
00066 WvTCPConn::WvTCPConn(const WvIPPortAddr &_remaddr)
00067 {
00068 remaddr = (_remaddr.is_zero() && FORCE_NONZERO)
00069 ? WvIPPortAddr("127.0.0.1", _remaddr.port) : _remaddr;
00070 resolved = true;
00071 connected = false;
00072 incoming = false;
00073
00074 do_connect();
00075 }
00076
00077
00078 WvTCPConn::WvTCPConn(int _fd, const WvIPPortAddr &_remaddr)
00079 : WvFDStream(_fd)
00080 {
00081 remaddr = (_remaddr.is_zero() && FORCE_NONZERO)
00082 ? WvIPPortAddr("127.0.0.1", _remaddr.port) : _remaddr;
00083 resolved = true;
00084 connected = true;
00085 incoming = true;
00086 nice_tcpopts();
00087 }
00088
00089
00090 WvTCPConn::WvTCPConn(WvStringParm _hostname, uint16_t _port)
00091 : hostname(_hostname)
00092 {
00093 struct servent* serv;
00094 char *hnstr = hostname.edit(), *cptr;
00095
00096 cptr = strchr(hnstr, ':');
00097 if (!cptr)
00098 cptr = strchr(hnstr, '\t');
00099 if (!cptr)
00100 cptr = strchr(hnstr, ' ');
00101 if (cptr)
00102 {
00103 *cptr++ = 0;
00104 serv = getservbyname(cptr, NULL);
00105 remaddr.port = serv ? ntohs(serv->s_port) : atoi(cptr);
00106 }
00107
00108 if (_port)
00109 remaddr.port = _port;
00110
00111 resolved = connected = false;
00112 incoming = false;
00113
00114 WvIPAddr x(hostname);
00115 if (x != WvIPAddr())
00116 {
00117 remaddr = WvIPPortAddr(x, remaddr.port);
00118 resolved = true;
00119 do_connect();
00120 }
00121 else
00122 dns.findaddr(0, hostname, NULL);
00123 }
00124
00125
00126 WvTCPConn::~WvTCPConn()
00127 {
00128
00129 }
00130
00131
00132
00133
00134 void WvTCPConn::nice_tcpopts()
00135 {
00136 set_close_on_exec(true);
00137 set_nonblock(true);
00138
00139 int value = 1;
00140 setsockopt(getfd(), SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value));
00141 }
00142
00143
00144 void WvTCPConn::low_delay()
00145 {
00146 int value;
00147
00148 value = 1;
00149 setsockopt(getfd(), SOL_TCP, TCP_NODELAY, &value, sizeof(value));
00150
00151 #ifndef _WIN32
00152 value = IPTOS_LOWDELAY;
00153 setsockopt(getfd(), SOL_IP, IP_TOS, &value, sizeof(value));
00154 #endif
00155 }
00156
00157
00158 void WvTCPConn::debug_mode()
00159 {
00160 int value = 0;
00161 setsockopt(getfd(), SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value));
00162 }
00163
00164 void WvTCPConn::do_connect()
00165 {
00166 if (getfd() < 0)
00167 {
00168 int rwfd = socket(PF_INET, SOCK_STREAM, 0);
00169 if (rwfd < 0)
00170 {
00171 seterr(errno);
00172 return;
00173 }
00174 setfd(rwfd);
00175
00176 nice_tcpopts();
00177 }
00178
00179 sockaddr *sa = remaddr.sockaddr();
00180 int ret = connect(getfd(), sa, remaddr.sockaddr_len()), err = errno;
00181 assert(ret <= 0);
00182
00183 if (ret == 0 || (ret < 0 && err == EISCONN))
00184 connected = true;
00185 else if (ret < 0
00186 && err != EINPROGRESS
00187 && err != EWOULDBLOCK
00188 && err != EAGAIN
00189 && err != EALREADY
00190 && err != EINVAL )
00191 {
00192 connected = true;
00193 seterr(err);
00194 }
00195 delete sa;
00196 }
00197
00198
00199 void WvTCPConn::check_resolver()
00200 {
00201 const WvIPAddr *ipr;
00202 int dnsres = dns.findaddr(0, hostname, &ipr);
00203
00204 if (dnsres == 0)
00205 {
00206
00207 resolved = true;
00208 seterr(WvString("Unknown host \"%s\"", hostname));
00209 }
00210 else if (dnsres > 0)
00211 {
00212 remaddr = WvIPPortAddr(*ipr, remaddr.port);
00213 resolved = true;
00214 do_connect();
00215 }
00216 }
00217
00218 #ifndef SO_ORIGINAL_DST
00219 # define SO_ORIGINAL_DST 80
00220 #endif
00221
00222 WvIPPortAddr WvTCPConn::localaddr()
00223 {
00224 sockaddr_in sin;
00225 socklen_t sl = sizeof(sin);
00226
00227 if (!isok())
00228 return WvIPPortAddr();
00229
00230 if (
00231 #ifndef _WIN32
00232
00233
00234
00235 (!incoming || getsockopt(getfd(), SOL_IP,
00236 SO_ORIGINAL_DST, (char*)&sin, &sl) < 0) &&
00237 #endif
00238 getsockname(getfd(), (sockaddr *)&sin, &sl))
00239 {
00240 return WvIPPortAddr();
00241 }
00242
00243 return WvIPPortAddr(&sin);
00244 }
00245
00246
00247 const WvIPPortAddr *WvTCPConn::src() const
00248 {
00249 return &remaddr;
00250 }
00251
00252
00253 void WvTCPConn::pre_select(SelectInfo &si)
00254 {
00255 if (!resolved)
00256 dns.pre_select(hostname, si);
00257
00258 if (resolved && isok())
00259 {
00260 bool oldw = si.wants.writable;
00261 if (!isconnected()) {
00262 si.wants.writable = true;
00263 #ifdef _WIN32
00264
00265
00266
00267
00268
00269
00270
00271
00272 si.wants.isexception = true;
00273 #endif
00274 }
00275 WvFDStream::pre_select(si);
00276 si.wants.writable = oldw;
00277 return;
00278 }
00279 }
00280
00281
00282 bool WvTCPConn::post_select(SelectInfo &si)
00283 {
00284 bool result = false;
00285
00286 if (!resolved)
00287 {
00288 if (dns.post_select(hostname, si))
00289 {
00290 check_resolver();
00291 if (!isok())
00292 return true;
00293 }
00294 }
00295 else
00296 {
00297 result = WvFDStream::post_select(si);
00298 if (result && !connected)
00299 {
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 int conn_res = -1;
00311 socklen_t res_size = sizeof(conn_res);
00312 if (getsockopt(getfd(), SOL_SOCKET, SO_ERROR,
00313 &conn_res, &res_size))
00314 {
00315
00316 seterr(errno);
00317 connected = true;
00318 }
00319 else if (conn_res != 0)
00320 {
00321
00322 seterr(conn_res);
00323 connected = true;
00324 }
00325 else
00326 {
00327
00328 do_connect();
00329 }
00330 }
00331 }
00332
00333 return result;
00334 }
00335
00336
00337 bool WvTCPConn::isok() const
00338 {
00339 return !resolved || WvFDStream::isok();
00340 }
00341
00342
00343 size_t WvTCPConn::uwrite(const void *buf, size_t count)
00344 {
00345 if (connected)
00346 return WvFDStream::uwrite(buf, count);
00347 else
00348 return 0;
00349 }
00350
00351
00352
00353
00354 WvTCPListener::WvTCPListener(const WvIPPortAddr &_listenport)
00355 : listenport(_listenport)
00356 {
00357 listenport = _listenport;
00358 auto_list = NULL;
00359 auto_userdata = NULL;
00360
00361 sockaddr *sa = listenport.sockaddr();
00362
00363 int x = 1;
00364
00365 setfd(socket(PF_INET, SOCK_STREAM, 0));
00366 set_close_on_exec(true);
00367 set_nonblock(true);
00368 if (getfd() < 0
00369 || setsockopt(getfd(), SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x))
00370 || bind(getfd(), sa, listenport.sockaddr_len())
00371 || listen(getfd(), 5))
00372 {
00373 seterr(errno);
00374 }
00375
00376 if (listenport.port == 0)
00377 {
00378 socklen_t namelen = listenport.sockaddr_len();
00379
00380 if (getsockname(getfd(), sa, &namelen) != 0)
00381 seterr(errno);
00382 else
00383 listenport = WvIPPortAddr((sockaddr_in *)sa);
00384 }
00385
00386 delete sa;
00387 }
00388
00389
00390 WvTCPListener::~WvTCPListener()
00391 {
00392 close();
00393 }
00394
00395
00396
00397 void WvTCPListener::close()
00398 {
00399 WvFDStream::close();
00400
00401
00402
00403
00404 }
00405
00406
00407 WvTCPConn *WvTCPListener::accept()
00408 {
00409 struct sockaddr_in sin;
00410 socklen_t len = sizeof(sin);
00411 int newfd;
00412 WvTCPConn *ret;
00413
00414 newfd = ::accept(getfd(), (struct sockaddr *)&sin, &len);
00415 ret = new WvTCPConn(newfd, WvIPPortAddr(&sin));
00416 return ret;
00417 }
00418
00419
00420 void WvTCPListener::auto_accept(WvIStreamList *list,
00421 WvStreamCallback callfunc, void *userdata)
00422 {
00423 auto_list = list;
00424 auto_callback = callfunc;
00425 auto_userdata = userdata;
00426 setcallback(accept_callback, this);
00427 }
00428
00429 void WvTCPListener::auto_accept(WvStreamCallback callfunc, void *userdata)
00430 {
00431 auto_callback = callfunc;
00432 auto_userdata = userdata;
00433 setcallback(accept_global_callback, this);
00434 }
00435
00436
00437 void WvTCPListener::accept_global_callback(WvStream &s, void *userdata)
00438 {
00439 WvTCPListener &l = *(WvTCPListener *)userdata;
00440 WvTCPConn *connection = l.accept();
00441 connection->setcallback(l.auto_callback, l.auto_userdata);
00442 WvIStreamList::globallist.append(connection, true, "WvTCPConn");
00443 }
00444
00445
00446 void WvTCPListener::accept_callback(WvStream &s, void *userdata)
00447 {
00448 WvTCPListener &l = *(WvTCPListener *)userdata;
00449
00450 WvTCPConn *connection = l.accept();
00451 connection->setcallback(l.auto_callback, l.auto_userdata);
00452 l.auto_list->append(connection, true, "WvTCPConn");
00453 }
00454
00455
00456 size_t WvTCPListener::uread(void *, size_t)
00457 {
00458 return 0;
00459 }
00460
00461
00462 size_t WvTCPListener::uwrite(const void *, size_t)
00463 {
00464 return 0;
00465 }
00466
00467
00468 const WvIPPortAddr *WvTCPListener::src() const
00469 {
00470 return &listenport;
00471 }
00472