PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llmessage/lliosocket.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 706 lines | 512 code | 62 blank | 132 comment | 67 complexity | a03731c727e54f0afb4cd0c791209767 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lliosocket.cpp
  3. * @author Phoenix
  4. * @date 2005-07-31
  5. * @brief Sockets declarations for use with the io pipes
  6. *
  7. * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "lliosocket.h"
  30. #include "llapr.h"
  31. #include "llbuffer.h"
  32. #include "llhost.h"
  33. #include "llmemtype.h"
  34. #include "llpumpio.h"
  35. //
  36. // constants
  37. //
  38. static const S32 LL_DEFAULT_LISTEN_BACKLOG = 10;
  39. static const S32 LL_SEND_BUFFER_SIZE = 40000;
  40. static const S32 LL_RECV_BUFFER_SIZE = 40000;
  41. //static const U16 LL_PORT_DISCOVERY_RANGE_MIN = 13000;
  42. //static const U16 LL_PORT_DISCOVERY_RANGE_MAX = 13050;
  43. //
  44. // local methods
  45. //
  46. bool is_addr_in_use(apr_status_t status)
  47. {
  48. #if LL_WINDOWS
  49. return (WSAEADDRINUSE == APR_TO_OS_ERROR(status));
  50. #else
  51. return (EADDRINUSE == APR_TO_OS_ERROR(status));
  52. #endif
  53. }
  54. #if LL_LINUX
  55. // Define this to see the actual file descriptors being tossed around.
  56. //#define LL_DEBUG_SOCKET_FILE_DESCRIPTORS 1
  57. #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS
  58. #include "apr_portable.h"
  59. #endif
  60. #endif
  61. // Quick function
  62. void ll_debug_socket(const char* msg, apr_socket_t* apr_sock)
  63. {
  64. #if LL_DEBUG_SOCKET_FILE_DESCRIPTORS
  65. if(!apr_sock)
  66. {
  67. lldebugs << "Socket -- " << (msg?msg:"") << ": no socket." << llendl;
  68. return;
  69. }
  70. // *TODO: Why doesn't this work?
  71. //apr_os_sock_t os_sock;
  72. int os_sock;
  73. if(APR_SUCCESS == apr_os_sock_get(&os_sock, apr_sock))
  74. {
  75. lldebugs << "Socket -- " << (msg?msg:"") << " on fd " << os_sock
  76. << " at " << apr_sock << llendl;
  77. }
  78. else
  79. {
  80. lldebugs << "Socket -- " << (msg?msg:"") << " no fd "
  81. << " at " << apr_sock << llendl;
  82. }
  83. #endif
  84. }
  85. ///
  86. /// LLSocket
  87. ///
  88. // static
  89. LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
  90. {
  91. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  92. LLSocket::ptr_t rv;
  93. apr_socket_t* socket = NULL;
  94. apr_pool_t* new_pool = NULL;
  95. apr_status_t status = APR_EGENERAL;
  96. // create a pool for the socket
  97. status = apr_pool_create(&new_pool, pool);
  98. if(ll_apr_warn_status(status))
  99. {
  100. if(new_pool) apr_pool_destroy(new_pool);
  101. return rv;
  102. }
  103. if(STREAM_TCP == type)
  104. {
  105. status = apr_socket_create(
  106. &socket,
  107. APR_INET,
  108. SOCK_STREAM,
  109. APR_PROTO_TCP,
  110. new_pool);
  111. }
  112. else if(DATAGRAM_UDP == type)
  113. {
  114. status = apr_socket_create(
  115. &socket,
  116. APR_INET,
  117. SOCK_DGRAM,
  118. APR_PROTO_UDP,
  119. new_pool);
  120. }
  121. else
  122. {
  123. if(new_pool) apr_pool_destroy(new_pool);
  124. return rv;
  125. }
  126. if(ll_apr_warn_status(status))
  127. {
  128. if(new_pool) apr_pool_destroy(new_pool);
  129. return rv;
  130. }
  131. rv = ptr_t(new LLSocket(socket, new_pool));
  132. if(port > 0)
  133. {
  134. apr_sockaddr_t* sa = NULL;
  135. status = apr_sockaddr_info_get(
  136. &sa,
  137. APR_ANYADDR,
  138. APR_UNSPEC,
  139. port,
  140. 0,
  141. new_pool);
  142. if(ll_apr_warn_status(status))
  143. {
  144. rv.reset();
  145. return rv;
  146. }
  147. // This allows us to reuse the address on quick down/up. This
  148. // is unlikely to create problems.
  149. ll_apr_warn_status(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1));
  150. status = apr_socket_bind(socket, sa);
  151. if(ll_apr_warn_status(status))
  152. {
  153. rv.reset();
  154. return rv;
  155. }
  156. lldebugs << "Bound " << ((DATAGRAM_UDP == type) ? "udp" : "tcp")
  157. << " socket to port: " << sa->port << llendl;
  158. if(STREAM_TCP == type)
  159. {
  160. // If it's a stream based socket, we need to tell the OS
  161. // to keep a queue of incoming connections for ACCEPT.
  162. lldebugs << "Setting listen state for socket." << llendl;
  163. status = apr_socket_listen(
  164. socket,
  165. LL_DEFAULT_LISTEN_BACKLOG);
  166. if(ll_apr_warn_status(status))
  167. {
  168. rv.reset();
  169. return rv;
  170. }
  171. }
  172. }
  173. else
  174. {
  175. // we need to indicate that we have an ephemeral port if the
  176. // previous calls were successful. It will
  177. port = PORT_EPHEMERAL;
  178. }
  179. rv->mPort = port;
  180. rv->setNonBlocking();
  181. return rv;
  182. }
  183. // static
  184. LLSocket::ptr_t LLSocket::create(apr_socket_t* socket, apr_pool_t* pool)
  185. {
  186. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  187. LLSocket::ptr_t rv;
  188. if(!socket)
  189. {
  190. return rv;
  191. }
  192. rv = ptr_t(new LLSocket(socket, pool));
  193. rv->mPort = PORT_EPHEMERAL;
  194. rv->setNonBlocking();
  195. return rv;
  196. }
  197. bool LLSocket::blockingConnect(const LLHost& host)
  198. {
  199. if(!mSocket) return false;
  200. apr_sockaddr_t* sa = NULL;
  201. std::string ip_address;
  202. ip_address = host.getIPString();
  203. if(ll_apr_warn_status(apr_sockaddr_info_get(
  204. &sa,
  205. ip_address.c_str(),
  206. APR_UNSPEC,
  207. host.getPort(),
  208. 0,
  209. mPool)))
  210. {
  211. return false;
  212. }
  213. setBlocking(1000);
  214. ll_debug_socket("Blocking connect", mSocket);
  215. if(ll_apr_warn_status(apr_socket_connect(mSocket, sa))) return false;
  216. setNonBlocking();
  217. return true;
  218. }
  219. LLSocket::LLSocket(apr_socket_t* socket, apr_pool_t* pool) :
  220. mSocket(socket),
  221. mPool(pool),
  222. mPort(PORT_INVALID)
  223. {
  224. ll_debug_socket("Constructing wholely formed socket", mSocket);
  225. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  226. }
  227. LLSocket::~LLSocket()
  228. {
  229. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  230. // *FIX: clean up memory we are holding.
  231. if(mSocket)
  232. {
  233. ll_debug_socket("Destroying socket", mSocket);
  234. apr_socket_close(mSocket);
  235. mSocket = NULL;
  236. }
  237. if(mPool)
  238. {
  239. apr_pool_destroy(mPool);
  240. }
  241. }
  242. // See http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html#ss13.4
  243. // for an explanation of how to get non-blocking sockets and timeouts with
  244. // consistent behavior across platforms.
  245. void LLSocket::setBlocking(S32 timeout)
  246. {
  247. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  248. // set up the socket options
  249. ll_apr_warn_status(apr_socket_timeout_set(mSocket, timeout));
  250. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_NONBLOCK, 0));
  251. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
  252. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
  253. }
  254. void LLSocket::setNonBlocking()
  255. {
  256. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  257. // set up the socket options
  258. ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
  259. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_NONBLOCK, 1));
  260. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
  261. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
  262. }
  263. ///
  264. /// LLIOSocketReader
  265. ///
  266. LLIOSocketReader::LLIOSocketReader(LLSocket::ptr_t socket) :
  267. mSource(socket),
  268. mInitialized(false)
  269. {
  270. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  271. }
  272. LLIOSocketReader::~LLIOSocketReader()
  273. {
  274. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  275. //lldebugs << "Destroying LLIOSocketReader" << llendl;
  276. }
  277. static LLFastTimer::DeclareTimer FTM_PROCESS_SOCKET_READER("Socket Reader");
  278. // virtual
  279. LLIOPipe::EStatus LLIOSocketReader::process_impl(
  280. const LLChannelDescriptors& channels,
  281. buffer_ptr_t& buffer,
  282. bool& eos,
  283. LLSD& context,
  284. LLPumpIO* pump)
  285. {
  286. LLFastTimer t(FTM_PROCESS_SOCKET_READER);
  287. PUMP_DEBUG;
  288. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  289. if(!mSource) return STATUS_PRECONDITION_NOT_MET;
  290. if(!mInitialized)
  291. {
  292. PUMP_DEBUG;
  293. // Since the read will not block, it's ok to initialize and
  294. // attempt to read off the descriptor immediately.
  295. mInitialized = true;
  296. if(pump)
  297. {
  298. PUMP_DEBUG;
  299. lldebugs << "Initializing poll descriptor for LLIOSocketReader."
  300. << llendl;
  301. apr_pollfd_t poll_fd;
  302. poll_fd.p = NULL;
  303. poll_fd.desc_type = APR_POLL_SOCKET;
  304. poll_fd.reqevents = APR_POLLIN;
  305. poll_fd.rtnevents = 0x0;
  306. poll_fd.desc.s = mSource->getSocket();
  307. poll_fd.client_data = NULL;
  308. pump->setConditional(this, &poll_fd);
  309. }
  310. }
  311. //if(!buffer)
  312. //{
  313. // buffer = new LLBufferArray;
  314. //}
  315. PUMP_DEBUG;
  316. const apr_size_t READ_BUFFER_SIZE = 1024;
  317. char read_buf[READ_BUFFER_SIZE]; /*Flawfinder: ignore*/
  318. apr_size_t len;
  319. apr_status_t status = APR_SUCCESS;
  320. do
  321. {
  322. PUMP_DEBUG;
  323. len = READ_BUFFER_SIZE;
  324. status = apr_socket_recv(mSource->getSocket(), read_buf, &len);
  325. buffer->append(channels.out(), (U8*)read_buf, len);
  326. } while((APR_SUCCESS == status) && (READ_BUFFER_SIZE == len));
  327. lldebugs << "socket read status: " << status << llendl;
  328. LLIOPipe::EStatus rv = STATUS_OK;
  329. PUMP_DEBUG;
  330. // *FIX: Also need to check for broken pipe
  331. if(APR_STATUS_IS_EOF(status))
  332. {
  333. // *FIX: Should we shut down the socket read?
  334. if(pump)
  335. {
  336. pump->setConditional(this, NULL);
  337. }
  338. rv = STATUS_DONE;
  339. eos = true;
  340. }
  341. else if(APR_STATUS_IS_EAGAIN(status))
  342. {
  343. /*Commented out by Aura 9-9-8 for DEV-19961.
  344. // everything is fine, but we can terminate this process pump.
  345. rv = STATUS_BREAK;
  346. */
  347. }
  348. else
  349. {
  350. if(ll_apr_warn_status(status))
  351. {
  352. rv = STATUS_ERROR;
  353. }
  354. }
  355. PUMP_DEBUG;
  356. return rv;
  357. }
  358. ///
  359. /// LLIOSocketWriter
  360. ///
  361. LLIOSocketWriter::LLIOSocketWriter(LLSocket::ptr_t socket) :
  362. mDestination(socket),
  363. mLastWritten(NULL),
  364. mInitialized(false)
  365. {
  366. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  367. }
  368. LLIOSocketWriter::~LLIOSocketWriter()
  369. {
  370. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  371. //lldebugs << "Destroying LLIOSocketWriter" << llendl;
  372. }
  373. static LLFastTimer::DeclareTimer FTM_PROCESS_SOCKET_WRITER("Socket Writer");
  374. // virtual
  375. LLIOPipe::EStatus LLIOSocketWriter::process_impl(
  376. const LLChannelDescriptors& channels,
  377. buffer_ptr_t& buffer,
  378. bool& eos,
  379. LLSD& context,
  380. LLPumpIO* pump)
  381. {
  382. LLFastTimer t(FTM_PROCESS_SOCKET_WRITER);
  383. PUMP_DEBUG;
  384. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  385. if(!mDestination) return STATUS_PRECONDITION_NOT_MET;
  386. if(!mInitialized)
  387. {
  388. PUMP_DEBUG;
  389. // Since the write will not block, it's ok to initialize and
  390. // attempt to write immediately.
  391. mInitialized = true;
  392. if(pump)
  393. {
  394. PUMP_DEBUG;
  395. lldebugs << "Initializing poll descriptor for LLIOSocketWriter."
  396. << llendl;
  397. apr_pollfd_t poll_fd;
  398. poll_fd.p = NULL;
  399. poll_fd.desc_type = APR_POLL_SOCKET;
  400. poll_fd.reqevents = APR_POLLOUT;
  401. poll_fd.rtnevents = 0x0;
  402. poll_fd.desc.s = mDestination->getSocket();
  403. poll_fd.client_data = NULL;
  404. pump->setConditional(this, &poll_fd);
  405. }
  406. }
  407. PUMP_DEBUG;
  408. // *FIX: Some sort of writev implementation would be much more
  409. // efficient - not only because writev() is better, but also
  410. // because we won't have to do as much work to find the start
  411. // address.
  412. buffer->lock();
  413. LLBufferArray::segment_iterator_t it;
  414. LLBufferArray::segment_iterator_t end = buffer->endSegment();
  415. LLSegment segment;
  416. it = buffer->constructSegmentAfter(mLastWritten, segment);
  417. /*
  418. if(NULL == mLastWritten)
  419. {
  420. it = buffer->beginSegment();
  421. segment = (*it);
  422. }
  423. else
  424. {
  425. it = buffer->getSegment(mLastWritten);
  426. segment = (*it);
  427. S32 size = segment.size();
  428. U8* data = segment.data();
  429. if((data + size) == mLastWritten)
  430. {
  431. ++it;
  432. segment = (*it);
  433. }
  434. else
  435. {
  436. // *FIX: check the math on this one
  437. segment = LLSegment(
  438. (*it).getChannelMask(),
  439. mLastWritten + 1,
  440. size - (mLastWritten - data));
  441. }
  442. }
  443. */
  444. PUMP_DEBUG;
  445. apr_size_t len;
  446. bool done = false;
  447. apr_status_t status = APR_SUCCESS;
  448. while(it != end)
  449. {
  450. PUMP_DEBUG;
  451. if((*it).isOnChannel(channels.in()))
  452. {
  453. PUMP_DEBUG;
  454. len = (apr_size_t)segment.size();
  455. status = apr_socket_send(
  456. mDestination->getSocket(),
  457. (const char*)segment.data(),
  458. &len);
  459. // We sometimes get a 'non-blocking socket operation could not be
  460. // completed immediately' error from apr_socket_send. In this
  461. // case we break and the data will be sent the next time the chain
  462. // is pumped.
  463. if(APR_STATUS_IS_EAGAIN(status))
  464. {
  465. ll_apr_warn_status(status);
  466. break;
  467. }
  468. mLastWritten = segment.data() + len - 1;
  469. PUMP_DEBUG;
  470. if((S32)len < segment.size())
  471. {
  472. break;
  473. }
  474. }
  475. ++it;
  476. if(it != end)
  477. {
  478. segment = (*it);
  479. }
  480. else
  481. {
  482. done = true;
  483. }
  484. }
  485. buffer->unlock();
  486. PUMP_DEBUG;
  487. if(done && eos)
  488. {
  489. return STATUS_DONE;
  490. }
  491. return STATUS_OK;
  492. }
  493. ///
  494. /// LLIOServerSocket
  495. ///
  496. LLIOServerSocket::LLIOServerSocket(
  497. apr_pool_t* pool,
  498. LLIOServerSocket::socket_t listener,
  499. factory_t factory) :
  500. mPool(pool),
  501. mListenSocket(listener),
  502. mReactor(factory),
  503. mInitialized(false),
  504. mResponseTimeout(DEFAULT_CHAIN_EXPIRY_SECS)
  505. {
  506. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  507. }
  508. LLIOServerSocket::~LLIOServerSocket()
  509. {
  510. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  511. //lldebugs << "Destroying LLIOServerSocket" << llendl;
  512. }
  513. void LLIOServerSocket::setResponseTimeout(F32 timeout_secs)
  514. {
  515. mResponseTimeout = timeout_secs;
  516. }
  517. static LLFastTimer::DeclareTimer FTM_PROCESS_SERVER_SOCKET("Server Socket");
  518. // virtual
  519. LLIOPipe::EStatus LLIOServerSocket::process_impl(
  520. const LLChannelDescriptors& channels,
  521. buffer_ptr_t& buffer,
  522. bool& eos,
  523. LLSD& context,
  524. LLPumpIO* pump)
  525. {
  526. LLFastTimer t(FTM_PROCESS_SERVER_SOCKET);
  527. PUMP_DEBUG;
  528. LLMemType m1(LLMemType::MTYPE_IO_TCP);
  529. if(!pump)
  530. {
  531. llwarns << "Need a pump for server socket." << llendl;
  532. return STATUS_ERROR;
  533. }
  534. if(!mInitialized)
  535. {
  536. PUMP_DEBUG;
  537. // This segment sets up the pump so that we do not call
  538. // process again until we have an incoming read, aka connect()
  539. // from a remote host.
  540. lldebugs << "Initializing poll descriptor for LLIOServerSocket."
  541. << llendl;
  542. apr_pollfd_t poll_fd;
  543. poll_fd.p = NULL;
  544. poll_fd.desc_type = APR_POLL_SOCKET;
  545. poll_fd.reqevents = APR_POLLIN;
  546. poll_fd.rtnevents = 0x0;
  547. poll_fd.desc.s = mListenSocket->getSocket();
  548. poll_fd.client_data = NULL;
  549. pump->setConditional(this, &poll_fd);
  550. mInitialized = true;
  551. return STATUS_OK;
  552. }
  553. // we are initialized, and told to process, so we must have a
  554. // socket waiting for a connection.
  555. lldebugs << "accepting socket" << llendl;
  556. PUMP_DEBUG;
  557. apr_pool_t* new_pool = NULL;
  558. apr_status_t status = apr_pool_create(&new_pool, mPool);
  559. apr_socket_t* socket = NULL;
  560. status = apr_socket_accept(
  561. &socket,
  562. mListenSocket->getSocket(),
  563. new_pool);
  564. LLSocket::ptr_t llsocket(LLSocket::create(socket, new_pool));
  565. //EStatus rv = STATUS_ERROR;
  566. if(llsocket)
  567. {
  568. PUMP_DEBUG;
  569. apr_sockaddr_t* remote_addr;
  570. apr_socket_addr_get(&remote_addr, APR_REMOTE, socket);
  571. char* remote_host_string;
  572. apr_sockaddr_ip_get(&remote_host_string, remote_addr);
  573. LLSD context;
  574. context["remote-host"] = remote_host_string;
  575. context["remote-port"] = remote_addr->port;
  576. LLPumpIO::chain_t chain;
  577. chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(llsocket)));
  578. if(mReactor->build(chain, context))
  579. {
  580. chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(llsocket)));
  581. pump->addChain(chain, mResponseTimeout);
  582. status = STATUS_OK;
  583. }
  584. else
  585. {
  586. llwarns << "Unable to build reactor to socket." << llendl;
  587. }
  588. }
  589. else
  590. {
  591. llwarns << "Unable to create linden socket." << llendl;
  592. }
  593. PUMP_DEBUG;
  594. // This needs to always return success, lest it get removed from
  595. // the pump.
  596. return STATUS_OK;
  597. }
  598. #if 0
  599. LLIODataSocket::LLIODataSocket(
  600. U16 suggested_port,
  601. U16 start_discovery_port,
  602. apr_pool_t* pool) :
  603. mSocket(NULL)
  604. {
  605. if(!pool || (PORT_INVALID == suggested_port)) return;
  606. if(ll_apr_warn_status(apr_socket_create(&mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, pool))) return;
  607. apr_sockaddr_t* sa = NULL;
  608. if(ll_apr_warn_status(apr_sockaddr_info_get(&sa, APR_ANYADDR, APR_UNSPEC, suggested_port, 0, pool))) return;
  609. apr_status_t status = apr_socket_bind(mSocket, sa);
  610. if((start_discovery_port > 0) && is_addr_in_use(status))
  611. {
  612. const U16 MAX_ATTEMPT_PORTS = 50;
  613. for(U16 attempt_port = start_discovery_port;
  614. attempt_port < (start_discovery_port + MAX_ATTEMPT_PORTS);
  615. ++attempt_port)
  616. {
  617. sa->port = attempt_port;
  618. sa->sa.sin.sin_port = htons(attempt_port);
  619. status = apr_socket_bind(mSocket, sa);
  620. if(APR_SUCCESS == status) break;
  621. if(is_addr_in_use(status)) continue;
  622. (void)ll_apr_warn_status(status);
  623. }
  624. }
  625. if(ll_apr_warn_status(status)) return;
  626. if(sa->port)
  627. {
  628. lldebugs << "Bound datagram socket to port: " << sa->port << llendl;
  629. mPort = sa->port;
  630. }
  631. else
  632. {
  633. mPort = LLIOSocket::PORT_EPHEMERAL;
  634. }
  635. // set up the socket options options
  636. ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
  637. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
  638. ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
  639. }
  640. LLIODataSocket::~LLIODataSocket()
  641. {
  642. }
  643. #endif