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