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