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