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