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 bool WvTCPConn::pre_select(SelectInfo &si)
00254 {
00255 if (!resolved)
00256 {
00257 if (dns.pre_select(hostname, si))
00258 {
00259 check_resolver();
00260 if (!isok())
00261 return true;
00262 }
00263 }
00264
00265 if (resolved && isok())
00266 {
00267 bool oldw = si.wants.writable, retval;
00268 if (!isconnected()) {
00269 si.wants.writable = true;
00270 #ifdef _WIN32
00271
00272
00273
00274
00275
00276
00277
00278
00279 si.wants.isexception = true;
00280 #endif
00281 }
00282 retval = WvFDStream::pre_select(si);
00283 si.wants.writable = oldw;
00284 return retval;
00285 }
00286 else
00287 return false;
00288 }
00289
00290
00291 bool WvTCPConn::post_select(SelectInfo &si)
00292 {
00293 bool result = false;
00294
00295 if (!resolved)
00296 check_resolver();
00297 else
00298 {
00299 result = WvFDStream::post_select(si);
00300 if (result && !connected)
00301 {
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312 int conn_res = -1;
00313 socklen_t res_size = sizeof(conn_res);
00314 if (getsockopt(getfd(), SOL_SOCKET, SO_ERROR,
00315 &conn_res, &res_size))
00316 {
00317
00318 seterr(errno);
00319 connected = true;
00320 }
00321 else if (conn_res != 0)
00322 {
00323
00324 seterr(conn_res);
00325 connected = true;
00326 }
00327 else
00328 {
00329
00330 do_connect();
00331 }
00332 }
00333 }
00334
00335 return result;
00336 }
00337
00338
00339 bool WvTCPConn::isok() const
00340 {
00341 return !resolved || WvFDStream::isok();
00342 }
00343
00344
00345 size_t WvTCPConn::uwrite(const void *buf, size_t count)
00346 {
00347 if (connected)
00348 return WvFDStream::uwrite(buf, count);
00349 else
00350 return 0;
00351 }
00352
00353
00354
00355
00356 WvTCPListener::WvTCPListener(const WvIPPortAddr &_listenport)
00357 : listenport(_listenport)
00358 {
00359 listenport = _listenport;
00360 auto_list = NULL;
00361 auto_userdata = NULL;
00362
00363 sockaddr *sa = listenport.sockaddr();
00364
00365 int x = 1;
00366
00367 setfd(socket(PF_INET, SOCK_STREAM, 0));
00368 set_close_on_exec(true);
00369 set_nonblock(true);
00370 if (getfd() < 0
00371 || setsockopt(getfd(), SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x))
00372 || bind(getfd(), sa, listenport.sockaddr_len())
00373 || listen(getfd(), 5))
00374 {
00375 seterr(errno);
00376 }
00377
00378 if (listenport.port == 0)
00379 {
00380 socklen_t namelen = listenport.sockaddr_len();
00381
00382 if (getsockname(getfd(), sa, &namelen) != 0)
00383 seterr(errno);
00384 else
00385 listenport = WvIPPortAddr((sockaddr_in *)sa);
00386 }
00387
00388 delete sa;
00389 }
00390
00391
00392 WvTCPListener::~WvTCPListener()
00393 {
00394 close();
00395 }
00396
00397
00398
00399 void WvTCPListener::close()
00400 {
00401 WvFDStream::close();
00402
00403
00404
00405
00406 }
00407
00408
00409 WvTCPConn *WvTCPListener::accept()
00410 {
00411 struct sockaddr_in sin;
00412 socklen_t len = sizeof(sin);
00413 int newfd;
00414 WvTCPConn *ret;
00415
00416 newfd = ::accept(getfd(), (struct sockaddr *)&sin, &len);
00417 ret = new WvTCPConn(newfd, WvIPPortAddr(&sin));
00418 return ret;
00419 }
00420
00421
00422 void WvTCPListener::auto_accept(WvIStreamList *list,
00423 WvStreamCallback callfunc, void *userdata)
00424 {
00425 auto_list = list;
00426 auto_callback = callfunc;
00427 auto_userdata = userdata;
00428 setcallback(accept_callback, this);
00429 }
00430
00431 void WvTCPListener::auto_accept(WvStreamCallback callfunc, void *userdata)
00432 {
00433 auto_callback = callfunc;
00434 auto_userdata = userdata;
00435 setcallback(accept_global_callback, this);
00436 }
00437
00438
00439 void WvTCPListener::accept_global_callback(WvStream &s, void *userdata)
00440 {
00441 WvTCPListener &l = *(WvTCPListener *)userdata;
00442 WvTCPConn *connection = l.accept();
00443 connection->setcallback(l.auto_callback, l.auto_userdata);
00444 WvIStreamList::globallist.append(connection, true);
00445 }
00446
00447
00448 void WvTCPListener::accept_callback(WvStream &s, void *userdata)
00449 {
00450 WvTCPListener &l = *(WvTCPListener *)userdata;
00451
00452 WvTCPConn *connection = l.accept();
00453 connection->setcallback(l.auto_callback, l.auto_userdata);
00454 l.auto_list->append(connection, true);
00455 }
00456
00457
00458 size_t WvTCPListener::uread(void *, size_t)
00459 {
00460 return 0;
00461 }
00462
00463
00464 size_t WvTCPListener::uwrite(const void *, size_t)
00465 {
00466 return 0;
00467 }
00468
00469
00470 const WvIPPortAddr *WvTCPListener::src() const
00471 {
00472 return &listenport;
00473 }
00474