1/*
2 * Copyright 2007-2010, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *      Hugo Santos, hugosantos@gmail.com
7 */
8#ifndef PROTOCOL_UTILITIES_H
9#define PROTOCOL_UTILITIES_H
10
11
12#include <lock.h>
13#include <Select.h>
14#include <util/AutoLock.h>
15#include <util/DoublyLinkedList.h>
16
17#include <AddressUtilities.h>
18#include <net_buffer.h>
19#include <net_protocol.h>
20#include <net_socket.h>
21#include <net_stack.h>
22
23
24class MutexLocking {
25public:
26	typedef mutex Type;
27	typedef MutexLocker AutoLocker;
28
29	static status_t Init(mutex* lock, const char* name)
30		{ mutex_init_etc(lock, name, MUTEX_FLAG_CLONE_NAME); return B_OK; }
31	static void Destroy(mutex* lock) { mutex_destroy(lock); }
32	static status_t Lock(mutex* lock) { return mutex_lock(lock); }
33	static status_t Unlock(mutex* lock) { mutex_unlock(lock); return B_OK; }
34};
35
36
37extern net_buffer_module_info* gBufferModule;
38extern net_stack_module_info* gStackModule;
39
40class NetModuleBundleGetter {
41public:
42	static net_stack_module_info* Stack() { return gStackModule; }
43	static net_buffer_module_info* Buffer() { return gBufferModule; }
44};
45
46
47class ProtocolSocket {
48public:
49								ProtocolSocket(net_socket* socket);
50
51			status_t			Open();
52
53			SocketAddress		LocalAddress()
54									{ return SocketAddress(
55										fDomain->address_module,
56										&fSocket->address); }
57			ConstSocketAddress	LocalAddress() const
58									{ return ConstSocketAddress(
59										fDomain->address_module,
60										&fSocket->address); }
61
62			SocketAddress		PeerAddress()
63									{ return SocketAddress(
64										fDomain->address_module,
65										&fSocket->peer); }
66			ConstSocketAddress	PeerAddress() const
67									{ return ConstSocketAddress(
68										fDomain->address_module,
69										&fSocket->peer); }
70
71			net_domain*			Domain() const { return fDomain; }
72			net_address_module_info* AddressModule() const
73									{ return fDomain->address_module; }
74
75			net_socket*			Socket() const { return fSocket; }
76
77protected:
78			net_socket*			fSocket;
79			net_domain*			fDomain;
80};
81
82
83inline
84ProtocolSocket::ProtocolSocket(net_socket* socket)
85	:
86	fSocket(socket),
87	fDomain(NULL)
88{
89}
90
91
92inline status_t
93ProtocolSocket::Open()
94{
95	fDomain = fSocket->first_protocol->module->get_domain(
96		fSocket->first_protocol);
97
98	if (fDomain == NULL || fDomain->address_module == NULL)
99		return EAFNOSUPPORT;
100
101	return B_OK;
102}
103
104
105template<typename LockingBase = MutexLocking,
106	typename ModuleBundle = NetModuleBundleGetter>
107class DatagramSocket : public ProtocolSocket {
108public:
109								DatagramSocket(const char* name,
110									net_socket* socket);
111	virtual						~DatagramSocket();
112
113			status_t			InitCheck() const;
114
115			status_t			Enqueue(net_buffer* buffer);
116			status_t			EnqueueClone(net_buffer* buffer);
117
118			status_t			Dequeue(uint32 flags, net_buffer** _buffer);
119			net_buffer*			Dequeue(bool clone);
120			status_t			BlockingDequeue(bool peek, bigtime_t timeout,
121									net_buffer** _buffer);
122
123			void				Clear();
124
125			bool				IsEmpty() const { return fBuffers.IsEmpty(); }
126			ssize_t				AvailableData() const;
127
128			void				WakeAll();
129			void				NotifyOne();
130
131protected:
132	virtual	status_t			SocketStatus(bool peek) const;
133
134private:
135			status_t			_Enqueue(net_buffer* buffer);
136			net_buffer*			_Dequeue(bool peek);
137			void				_Clear();
138
139			status_t			_Wait(bigtime_t timeout);
140			void				_NotifyOneReader(bool notifySocket);
141
142			bigtime_t			_SocketTimeout(uint32 flags) const;
143
144protected:
145	typedef typename LockingBase::Type LockType;
146	typedef typename LockingBase::AutoLocker AutoLocker;
147	typedef DoublyLinkedListCLink<net_buffer> NetBufferLink;
148	typedef DoublyLinkedList<net_buffer, NetBufferLink> BufferList;
149
150			sem_id				fNotify;
151			BufferList			fBuffers;
152			size_t				fCurrentBytes;
153	mutable	LockType			fLock;
154};
155
156
157#define DECL_DATAGRAM_SOCKET(args) \
158	template<typename LockingBase, typename ModuleBundle> args \
159	DatagramSocket<LockingBase, ModuleBundle>
160
161
162DECL_DATAGRAM_SOCKET(inline)::DatagramSocket(const char* name,
163	net_socket* socket)
164	:
165	ProtocolSocket(socket), fCurrentBytes(0)
166{
167	status_t status = LockingBase::Init(&fLock, name);
168	if (status != B_OK)
169		fNotify = status;
170	else
171		fNotify = create_sem(0, name);
172}
173
174
175DECL_DATAGRAM_SOCKET(inline)::~DatagramSocket()
176{
177	_Clear();
178	delete_sem(fNotify);
179	LockingBase::Destroy(&fLock);
180}
181
182
183DECL_DATAGRAM_SOCKET(inline status_t)::InitCheck() const
184{
185	return fNotify >= 0 ? B_OK : fNotify;
186}
187
188
189DECL_DATAGRAM_SOCKET(inline status_t)::Enqueue(net_buffer* buffer)
190{
191	AutoLocker _(fLock);
192	return _Enqueue(buffer);
193}
194
195
196DECL_DATAGRAM_SOCKET(inline status_t)::EnqueueClone(net_buffer* _buffer)
197{
198	AutoLocker _(fLock);
199
200	net_buffer* buffer = ModuleBundle::Buffer()->clone(_buffer, false);
201	if (buffer == NULL)
202		return B_NO_MEMORY;
203
204	status_t status = _Enqueue(buffer);
205	if (status != B_OK)
206		ModuleBundle::Buffer()->free(buffer);
207
208	return status;
209}
210
211
212DECL_DATAGRAM_SOCKET(inline status_t)::Dequeue(uint32 flags,
213	net_buffer** _buffer)
214{
215	return BlockingDequeue((flags & MSG_PEEK) != 0, _SocketTimeout(flags),
216		_buffer);
217}
218
219
220DECL_DATAGRAM_SOCKET(inline net_buffer*)::Dequeue(bool peek)
221{
222	AutoLocker _(fLock);
223	return _Dequeue(peek);
224}
225
226
227DECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool peek,
228	bigtime_t timeout, net_buffer** _buffer)
229{
230	AutoLocker _(fLock);
231
232	bool waited = false;
233	while (fBuffers.IsEmpty()) {
234		status_t status = SocketStatus(peek);
235		if (status != B_OK) {
236			if (peek)
237				_NotifyOneReader(false);
238			return status;
239		}
240
241		status = _Wait(timeout);
242		if (status != B_OK)
243			return status;
244
245		waited = true;
246	}
247
248	*_buffer = _Dequeue(peek);
249	if (peek && waited) {
250		// There is a new buffer in the list; but since we are only peeking,
251		// notify the next waiting reader.
252		_NotifyOneReader(false);
253	}
254
255	if (*_buffer == NULL)
256		return B_NO_MEMORY;
257
258	return B_OK;
259}
260
261
262DECL_DATAGRAM_SOCKET(inline void)::Clear()
263{
264	AutoLocker _(fLock);
265	_Clear();
266}
267
268
269DECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
270{
271	AutoLocker _(fLock);
272	status_t status = SocketStatus(true);
273	if (status < B_OK)
274		return status;
275
276	return fCurrentBytes;
277}
278
279
280DECL_DATAGRAM_SOCKET(inline void)::WakeAll()
281{
282	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
283}
284
285
286DECL_DATAGRAM_SOCKET(inline void)::NotifyOne()
287{
288	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
289		| B_DO_NOT_RESCHEDULE);
290}
291
292
293DECL_DATAGRAM_SOCKET(inline status_t)::SocketStatus(bool peek) const
294{
295	if (peek)
296		return fSocket->error;
297
298	status_t status = fSocket->error;
299	fSocket->error = B_OK;
300
301	return status;
302}
303
304
305DECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer* buffer)
306{
307	if (fSocket->receive.buffer_size > 0
308		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
309		return ENOBUFS;
310
311	fBuffers.Add(buffer);
312	fCurrentBytes += buffer->size;
313
314	_NotifyOneReader(true);
315
316	return B_OK;
317}
318
319
320DECL_DATAGRAM_SOCKET(inline net_buffer*)::_Dequeue(bool peek)
321{
322	if (fBuffers.IsEmpty())
323		return NULL;
324
325	if (peek)
326		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
327
328	net_buffer* buffer = fBuffers.RemoveHead();
329	fCurrentBytes -= buffer->size;
330
331	return buffer;
332}
333
334
335DECL_DATAGRAM_SOCKET(inline void)::_Clear()
336{
337	BufferList::Iterator it = fBuffers.GetIterator();
338	while (it.HasNext())
339		ModuleBundle::Buffer()->free(it.Next());
340	fCurrentBytes = 0;
341}
342
343
344DECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
345{
346	LockingBase::Unlock(&fLock);
347	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
348		| B_ABSOLUTE_TIMEOUT, timeout);
349	LockingBase::Lock(&fLock);
350
351	return status;
352}
353
354
355DECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
356{
357	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
358		| B_DO_NOT_RESCHEDULE);
359
360	if (notifySocket) {
361		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
362			fCurrentBytes);
363	}
364}
365
366
367DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
368{
369	bigtime_t timeout = fSocket->receive.timeout;
370
371	if ((flags & MSG_DONTWAIT) != 0)
372		timeout = 0;
373	else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
374		timeout += system_time();
375
376	if (ModuleBundle::Stack()->is_restarted_syscall())
377		timeout = ModuleBundle::Stack()->restore_syscall_restart_timeout();
378	else
379		ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
380
381	return timeout;
382}
383
384
385#endif	// PROTOCOL_UTILITIES_H
386