1bfb45f71SHugo Santos/*
24e78098eSAxel Dörfler * Copyright 2007-2013, Haiku, Inc. All Rights Reserved.
3bfb45f71SHugo Santos * Distributed under the terms of the MIT License.
4bfb45f71SHugo Santos *
5bfb45f71SHugo Santos * Authors:
6bfb45f71SHugo Santos *      Hugo Santos, hugosantos@gmail.com
7bfb45f71SHugo Santos */
8bfb45f71SHugo Santos#ifndef PROTOCOL_UTILITIES_H
9bfb45f71SHugo Santos#define PROTOCOL_UTILITIES_H
10bfb45f71SHugo Santos
1115ab0bcfSAxel Dörfler
12bfb45f71SHugo Santos#include <lock.h>
1315ab0bcfSAxel Dörfler#include <Select.h>
14bfb45f71SHugo Santos#include <util/AutoLock.h>
15bfb45f71SHugo Santos#include <util/DoublyLinkedList.h>
16bfb45f71SHugo Santos
1715ab0bcfSAxel Dörfler#include <AddressUtilities.h>
18bfb45f71SHugo Santos#include <net_buffer.h>
194b55736dSHugo Santos#include <net_protocol.h>
20bfb45f71SHugo Santos#include <net_socket.h>
21bfb45f71SHugo Santos#include <net_stack.h>
22bfb45f71SHugo Santos
234b55736dSHugo Santos
242b07b8e0SIngo Weinholdclass MutexLocking {
25bfb45f71SHugo Santospublic:
262b07b8e0SIngo Weinhold	typedef mutex Type;
272b07b8e0SIngo Weinhold	typedef MutexLocker AutoLocker;
282b07b8e0SIngo Weinhold
2965b5dd50SAxel Dörfler	static status_t Init(mutex* lock, const char* name)
302b07b8e0SIngo Weinhold		{ mutex_init_etc(lock, name, MUTEX_FLAG_CLONE_NAME); return B_OK; }
3165b5dd50SAxel Dörfler	static void Destroy(mutex* lock) { mutex_destroy(lock); }
3265b5dd50SAxel Dörfler	static status_t Lock(mutex* lock) { return mutex_lock(lock); }
3365b5dd50SAxel Dörfler	static status_t Unlock(mutex* lock) { mutex_unlock(lock); return B_OK; }
34bfb45f71SHugo Santos};
35bfb45f71SHugo Santos
36bfb45f71SHugo Santos
3765b5dd50SAxel Dörflerextern net_buffer_module_info* gBufferModule;
3865b5dd50SAxel Dörflerextern net_stack_module_info* gStackModule;
39bfb45f71SHugo Santos
404e78098eSAxel Dörfler
41d438fa4cSHugo Santosclass NetModuleBundleGetter {
42bfb45f71SHugo Santospublic:
4365b5dd50SAxel Dörfler	static net_stack_module_info* Stack() { return gStackModule; }
4465b5dd50SAxel Dörfler	static net_buffer_module_info* Buffer() { return gBufferModule; }
45bfb45f71SHugo Santos};
46bfb45f71SHugo Santos
47bfb45f71SHugo Santos
484b55736dSHugo Santosclass ProtocolSocket {
494b55736dSHugo Santospublic:
50866e21d3SAxel Dörfler								ProtocolSocket(net_socket* socket);
51866e21d3SAxel Dörfler
52866e21d3SAxel Dörfler			status_t			Open();
53866e21d3SAxel Dörfler
54866e21d3SAxel Dörfler			SocketAddress		LocalAddress()
55866e21d3SAxel Dörfler									{ return SocketAddress(
56866e21d3SAxel Dörfler										fDomain->address_module,
57866e21d3SAxel Dörfler										&fSocket->address); }
58866e21d3SAxel Dörfler			ConstSocketAddress	LocalAddress() const
59866e21d3SAxel Dörfler									{ return ConstSocketAddress(
60866e21d3SAxel Dörfler										fDomain->address_module,
61866e21d3SAxel Dörfler										&fSocket->address); }
62866e21d3SAxel Dörfler
63866e21d3SAxel Dörfler			SocketAddress		PeerAddress()
64866e21d3SAxel Dörfler									{ return SocketAddress(
65866e21d3SAxel Dörfler										fDomain->address_module,
66866e21d3SAxel Dörfler										&fSocket->peer); }
67866e21d3SAxel Dörfler			ConstSocketAddress	PeerAddress() const
68866e21d3SAxel Dörfler									{ return ConstSocketAddress(
69866e21d3SAxel Dörfler										fDomain->address_module,
70866e21d3SAxel Dörfler										&fSocket->peer); }
71866e21d3SAxel Dörfler
72866e21d3SAxel Dörfler			net_domain*			Domain() const { return fDomain; }
73866e21d3SAxel Dörfler			net_address_module_info* AddressModule() const
74866e21d3SAxel Dörfler									{ return fDomain->address_module; }
75866e21d3SAxel Dörfler
76866e21d3SAxel Dörfler			net_socket*			Socket() const { return fSocket; }
774b55736dSHugo Santos
784b55736dSHugo Santosprotected:
79866e21d3SAxel Dörfler			net_socket*			fSocket;
80866e21d3SAxel Dörfler			net_domain*			fDomain;
814b55736dSHugo Santos};
824b55736dSHugo Santos
834b55736dSHugo Santos
8465b5dd50SAxel Dörflerinline
8565b5dd50SAxel DörflerProtocolSocket::ProtocolSocket(net_socket* socket)
8665b5dd50SAxel Dörfler	:
8765b5dd50SAxel Dörfler	fSocket(socket),
8865b5dd50SAxel Dörfler	fDomain(NULL)
8965b5dd50SAxel Dörfler{
9065b5dd50SAxel Dörfler}
914b55736dSHugo Santos
924b55736dSHugo Santos
934b55736dSHugo Santosinline status_t
944b55736dSHugo SantosProtocolSocket::Open()
954b55736dSHugo Santos{
964b55736dSHugo Santos	fDomain = fSocket->first_protocol->module->get_domain(
974b55736dSHugo Santos		fSocket->first_protocol);
984b55736dSHugo Santos
994b55736dSHugo Santos	if (fDomain == NULL || fDomain->address_module == NULL)
1004b55736dSHugo Santos		return EAFNOSUPPORT;
1014b55736dSHugo Santos
1024b55736dSHugo Santos	return B_OK;
1034b55736dSHugo Santos}
1044b55736dSHugo Santos
1054b55736dSHugo Santos
1062b07b8e0SIngo Weinholdtemplate<typename LockingBase = MutexLocking,
107d438fa4cSHugo Santos	typename ModuleBundle = NetModuleBundleGetter>
1084b55736dSHugo Santosclass DatagramSocket : public ProtocolSocket {
109bfb45f71SHugo Santospublic:
110866e21d3SAxel Dörfler								DatagramSocket(const char* name,
111866e21d3SAxel Dörfler									net_socket* socket);
112866e21d3SAxel Dörfler	virtual						~DatagramSocket();
113bfb45f71SHugo Santos
114866e21d3SAxel Dörfler			status_t			InitCheck() const;
115bfb45f71SHugo Santos
116866e21d3SAxel Dörfler			status_t			Enqueue(net_buffer* buffer);
1172651e51dSAxel Dörfler			status_t			EnqueueClone(net_buffer* buffer);
1182651e51dSAxel Dörfler
1192651e51dSAxel Dörfler			status_t			Dequeue(uint32 flags, net_buffer** _buffer);
120866e21d3SAxel Dörfler			net_buffer*			Dequeue(bool clone);
121d62ee168SAxel Dörfler			status_t			BlockingDequeue(bool peek, bigtime_t timeout,
122866e21d3SAxel Dörfler									net_buffer** _buffer);
123bfb45f71SHugo Santos
1242651e51dSAxel Dörfler			void				Clear();
12549f3c71eSHugo Santos
126d62ee168SAxel Dörfler			bool				IsEmpty() const { return fBuffers.IsEmpty(); }
127866e21d3SAxel Dörfler			ssize_t				AvailableData() const;
128bfb45f71SHugo Santos
129866e21d3SAxel Dörfler			void				WakeAll();
130d62ee168SAxel Dörfler			void				NotifyOne();
13157e2a3d5SHugo Santos
132bfb45f71SHugo Santosprotected:
133d62ee168SAxel Dörfler	virtual	status_t			SocketStatus(bool peek) const;
13474b0e858SHugo Santos
135d62ee168SAxel Dörflerprivate:
136866e21d3SAxel Dörfler			status_t			_Enqueue(net_buffer* buffer);
137d62ee168SAxel Dörfler			net_buffer*			_Dequeue(bool peek);
138866e21d3SAxel Dörfler			void				_Clear();
139bfb45f71SHugo Santos
140866e21d3SAxel Dörfler			status_t			_Wait(bigtime_t timeout);
141866e21d3SAxel Dörfler			void				_NotifyOneReader(bool notifySocket);
142bfb45f71SHugo Santos
143866e21d3SAxel Dörfler			bigtime_t			_SocketTimeout(uint32 flags) const;
144bfb45f71SHugo Santos
145d62ee168SAxel Dörflerprotected:
146bfb45f71SHugo Santos	typedef typename LockingBase::Type LockType;
147bfb45f71SHugo Santos	typedef typename LockingBase::AutoLocker AutoLocker;
148bfb45f71SHugo Santos	typedef DoublyLinkedListCLink<net_buffer> NetBufferLink;
149bfb45f71SHugo Santos	typedef DoublyLinkedList<net_buffer, NetBufferLink> BufferList;
150bfb45f71SHugo Santos
151866e21d3SAxel Dörfler			sem_id				fNotify;
152866e21d3SAxel Dörfler			BufferList			fBuffers;
153866e21d3SAxel Dörfler			size_t				fCurrentBytes;
154866e21d3SAxel Dörfler	mutable	LockType			fLock;
155bfb45f71SHugo Santos};
156bfb45f71SHugo Santos
157bfb45f71SHugo Santos
158bfb45f71SHugo Santos#define DECL_DATAGRAM_SOCKET(args) \
159bfb45f71SHugo Santos	template<typename LockingBase, typename ModuleBundle> args \
160bfb45f71SHugo Santos	DatagramSocket<LockingBase, ModuleBundle>
161bfb45f71SHugo Santos
162bfb45f71SHugo Santos
16365b5dd50SAxel DörflerDECL_DATAGRAM_SOCKET(inline)::DatagramSocket(const char* name,
16465b5dd50SAxel Dörfler	net_socket* socket)
165866e21d3SAxel Dörfler	:
166866e21d3SAxel Dörfler	ProtocolSocket(socket), fCurrentBytes(0)
167bfb45f71SHugo Santos{
168d438fa4cSHugo Santos	status_t status = LockingBase::Init(&fLock, name);
16965b5dd50SAxel Dörfler	if (status != B_OK)
170d438fa4cSHugo Santos		fNotify = status;
171d438fa4cSHugo Santos	else
172bfb45f71SHugo Santos		fNotify = create_sem(0, name);
173bfb45f71SHugo Santos}
174bfb45f71SHugo Santos
175bfb45f71SHugo Santos
176bfb45f71SHugo SantosDECL_DATAGRAM_SOCKET(inline)::~DatagramSocket()
177bfb45f71SHugo Santos{
178bfb45f71SHugo Santos	_Clear();
179d438fa4cSHugo Santos	delete_sem(fNotify);
180d438fa4cSHugo Santos	LockingBase::Destroy(&fLock);
181bfb45f71SHugo Santos}
182bfb45f71SHugo Santos
183bfb45f71SHugo Santos
184bfb45f71SHugo SantosDECL_DATAGRAM_SOCKET(inline status_t)::InitCheck() const
185bfb45f71SHugo Santos{
18665b5dd50SAxel Dörfler	return fNotify >= 0 ? B_OK : fNotify;
187bfb45f71SHugo Santos}
188bfb45f71SHugo Santos
189bfb45f71SHugo Santos
19065b5dd50SAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::Enqueue(net_buffer* buffer)
191bfb45f71SHugo Santos{
192bfb45f71SHugo Santos	AutoLocker _(fLock);
193bfb45f71SHugo Santos	return _Enqueue(buffer);
194bfb45f71SHugo Santos}
195bfb45f71SHugo Santos
196bfb45f71SHugo Santos
1972651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::EnqueueClone(net_buffer* _buffer)
198bfb45f71SHugo Santos{
199bfb45f71SHugo Santos	AutoLocker _(fLock);
200bfb45f71SHugo Santos
20165b5dd50SAxel Dörfler	net_buffer* buffer = ModuleBundle::Buffer()->clone(_buffer, false);
202bfb45f71SHugo Santos	if (buffer == NULL)
203bfb45f71SHugo Santos		return B_NO_MEMORY;
204bfb45f71SHugo Santos
205bfb45f71SHugo Santos	status_t status = _Enqueue(buffer);
206690acf8bSAxel Dörfler	if (status != B_OK)
207bfb45f71SHugo Santos		ModuleBundle::Buffer()->free(buffer);
208bfb45f71SHugo Santos
209bfb45f71SHugo Santos	return status;
210bfb45f71SHugo Santos}
211bfb45f71SHugo Santos
212bfb45f71SHugo Santos
2132651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::Dequeue(uint32 flags,
2142651e51dSAxel Dörfler	net_buffer** _buffer)
215bfb45f71SHugo Santos{
2162651e51dSAxel Dörfler	return BlockingDequeue((flags & MSG_PEEK) != 0, _SocketTimeout(flags),
2172651e51dSAxel Dörfler		_buffer);
218bfb45f71SHugo Santos}
219bfb45f71SHugo Santos
220bfb45f71SHugo Santos
2212651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline net_buffer*)::Dequeue(bool peek)
222bfb45f71SHugo Santos{
2232651e51dSAxel Dörfler	AutoLocker _(fLock);
2242651e51dSAxel Dörfler	return _Dequeue(peek);
225bfb45f71SHugo Santos}
226bfb45f71SHugo Santos
227bfb45f71SHugo Santos
228d62ee168SAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool peek,
22965b5dd50SAxel Dörfler	bigtime_t timeout, net_buffer** _buffer)
230bfb45f71SHugo Santos{
231bfb45f71SHugo Santos	AutoLocker _(fLock);
232bfb45f71SHugo Santos
233bfb45f71SHugo Santos	bool waited = false;
234bfb45f71SHugo Santos	while (fBuffers.IsEmpty()) {
235d62ee168SAxel Dörfler		status_t status = SocketStatus(peek);
236d62ee168SAxel Dörfler		if (status != B_OK) {
237d62ee168SAxel Dörfler			if (peek)
238d62ee168SAxel Dörfler				_NotifyOneReader(false);
239bfb45f71SHugo Santos			return status;
240d62ee168SAxel Dörfler		}
24174b0e858SHugo Santos
242d62ee168SAxel Dörfler		status = _Wait(timeout);
243d62ee168SAxel Dörfler		if (status != B_OK)
24474b0e858SHugo Santos			return status;
245d62ee168SAxel Dörfler
246bfb45f71SHugo Santos		waited = true;
247bfb45f71SHugo Santos	}
248bfb45f71SHugo Santos
249d62ee168SAxel Dörfler	*_buffer = _Dequeue(peek);
250d62ee168SAxel Dörfler	if (peek && waited) {
251d62ee168SAxel Dörfler		// There is a new buffer in the list; but since we are only peeking,
252d62ee168SAxel Dörfler		// notify the next waiting reader.
253bfb45f71SHugo Santos		_NotifyOneReader(false);
254bfb45f71SHugo Santos	}
255bfb45f71SHugo Santos
256bfb45f71SHugo Santos	if (*_buffer == NULL)
257bfb45f71SHugo Santos		return B_NO_MEMORY;
258bfb45f71SHugo Santos
259bfb45f71SHugo Santos	return B_OK;
260bfb45f71SHugo Santos}
261bfb45f71SHugo Santos
262bfb45f71SHugo Santos
263bfb45f71SHugo SantosDECL_DATAGRAM_SOCKET(inline void)::Clear()
264bfb45f71SHugo Santos{
265bfb45f71SHugo Santos	AutoLocker _(fLock);
266bfb45f71SHugo Santos	_Clear();
267bfb45f71SHugo Santos}
268bfb45f71SHugo Santos
269bfb45f71SHugo Santos
27074b0e858SHugo SantosDECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
271bfb45f71SHugo Santos{
272bfb45f71SHugo Santos	AutoLocker _(fLock);
273d62ee168SAxel Dörfler	status_t status = SocketStatus(true);
27474b0e858SHugo Santos	if (status < B_OK)
27574b0e858SHugo Santos		return status;
27674b0e858SHugo Santos
277bfb45f71SHugo Santos	return fCurrentBytes;
278bfb45f71SHugo Santos}
279bfb45f71SHugo Santos
280bfb45f71SHugo Santos
2812651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline void)::WakeAll()
2822651e51dSAxel Dörfler{
2832651e51dSAxel Dörfler	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
2842651e51dSAxel Dörfler}
2852651e51dSAxel Dörfler
2862651e51dSAxel Dörfler
2872651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline void)::NotifyOne()
2882651e51dSAxel Dörfler{
2892651e51dSAxel Dörfler	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
2902651e51dSAxel Dörfler		| B_DO_NOT_RESCHEDULE);
2912651e51dSAxel Dörfler}
2922651e51dSAxel Dörfler
2932651e51dSAxel Dörfler
294d62ee168SAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::SocketStatus(bool peek) const
29574b0e858SHugo Santos{
296d62ee168SAxel Dörfler	if (peek)
297d62ee168SAxel Dörfler		return fSocket->error;
2984e78098eSAxel Dörfler
299d62ee168SAxel Dörfler	status_t status = fSocket->error;
300d62ee168SAxel Dörfler	fSocket->error = B_OK;
301d62ee168SAxel Dörfler
302d62ee168SAxel Dörfler	return status;
30374b0e858SHugo Santos}
30474b0e858SHugo Santos
30574b0e858SHugo Santos
3062651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer* buffer)
307bfb45f71SHugo Santos{
3082651e51dSAxel Dörfler	if (fSocket->receive.buffer_size > 0
3092651e51dSAxel Dörfler		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
3102651e51dSAxel Dörfler		return ENOBUFS;
311bfb45f71SHugo Santos
3122651e51dSAxel Dörfler	fBuffers.Add(buffer);
3132651e51dSAxel Dörfler	fCurrentBytes += buffer->size;
3142651e51dSAxel Dörfler
3152651e51dSAxel Dörfler	_NotifyOneReader(true);
3162651e51dSAxel Dörfler
3172651e51dSAxel Dörfler	return B_OK;
318bfb45f71SHugo Santos}
319bfb45f71SHugo Santos
320bfb45f71SHugo Santos
3212651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline net_buffer*)::_Dequeue(bool peek)
322bfb45f71SHugo Santos{
3232651e51dSAxel Dörfler	if (fBuffers.IsEmpty())
3242651e51dSAxel Dörfler		return NULL;
3252651e51dSAxel Dörfler
3262651e51dSAxel Dörfler	if (peek)
3272651e51dSAxel Dörfler		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
3282651e51dSAxel Dörfler
3292651e51dSAxel Dörfler	net_buffer* buffer = fBuffers.RemoveHead();
3302651e51dSAxel Dörfler	fCurrentBytes -= buffer->size;
3312651e51dSAxel Dörfler
3322651e51dSAxel Dörfler	return buffer;
333bfb45f71SHugo Santos}
334bfb45f71SHugo Santos
335bfb45f71SHugo Santos
3362651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline void)::_Clear()
337d62ee168SAxel Dörfler{
3382651e51dSAxel Dörfler	BufferList::Iterator it = fBuffers.GetIterator();
3392651e51dSAxel Dörfler	while (it.HasNext())
3402651e51dSAxel Dörfler		ModuleBundle::Buffer()->free(it.Next());
3412651e51dSAxel Dörfler	fCurrentBytes = 0;
3422651e51dSAxel Dörfler}
3432651e51dSAxel Dörfler
3442651e51dSAxel Dörfler
3452651e51dSAxel DörflerDECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
3462651e51dSAxel Dörfler{
3472651e51dSAxel Dörfler	LockingBase::Unlock(&fLock);
3482651e51dSAxel Dörfler	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
3494e78098eSAxel Dörfler		| (timeout != 0 ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT), timeout);
3502651e51dSAxel Dörfler	LockingBase::Lock(&fLock);
3512651e51dSAxel Dörfler
3522651e51dSAxel Dörfler	return status;
353d62ee168SAxel Dörfler}
354d62ee168SAxel Dörfler
355d62ee168SAxel Dörfler
356bfb45f71SHugo SantosDECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
357bfb45f71SHugo Santos{
358bfb45f71SHugo Santos	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
359bfb45f71SHugo Santos		| B_DO_NOT_RESCHEDULE);
360bfb45f71SHugo Santos
361d62ee168SAxel Dörfler	if (notifySocket) {
362bfb45f71SHugo Santos		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
363bfb45f71SHugo Santos			fCurrentBytes);
364d62ee168SAxel Dörfler	}
365bfb45f71SHugo Santos}
366bfb45f71SHugo Santos
367bfb45f71SHugo Santos
368bfb45f71SHugo SantosDECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
369bfb45f71SHugo Santos{
3701a84d6b3SPhilippe Houdoin	if (ModuleBundle::Stack()->is_restarted_syscall())
3711a84d6b3SPhilippe Houdoin		return ModuleBundle::Stack()->restore_syscall_restart_timeout();
372bfb45f71SHugo Santos
3731a84d6b3SPhilippe Houdoin	bigtime_t timeout = fSocket->receive.timeout;
37465b5dd50SAxel Dörfler	if ((flags & MSG_DONTWAIT) != 0)
375bfb45f71SHugo Santos		timeout = 0;
376bfb45f71SHugo Santos	else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
377bfb45f71SHugo Santos		timeout += system_time();
378bfb45f71SHugo Santos
3791a84d6b3SPhilippe Houdoin	ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
380bfb45f71SHugo Santos	return timeout;
381bfb45f71SHugo Santos}
382bfb45f71SHugo Santos
383866e21d3SAxel Dörfler
38465b5dd50SAxel Dörfler#endif	// PROTOCOL_UTILITIES_H
385