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	return BlockingDequeue((flags & MSG_PEEK) != 0, _SocketTimeout(flags),
217		_buffer);
218}
219
220
221DECL_DATAGRAM_SOCKET(inline net_buffer*)::Dequeue(bool peek)
222{
223	AutoLocker _(fLock);
224	return _Dequeue(peek);
225}
226
227
228DECL_DATAGRAM_SOCKET(inline status_t)::BlockingDequeue(bool peek,
229	bigtime_t timeout, net_buffer** _buffer)
230{
231	AutoLocker _(fLock);
232
233	bool waited = false;
234	while (fBuffers.IsEmpty()) {
235		status_t status = SocketStatus(peek);
236		if (status != B_OK) {
237			if (peek)
238				_NotifyOneReader(false);
239			return status;
240		}
241
242		status = _Wait(timeout);
243		if (status != B_OK)
244			return status;
245
246		waited = true;
247	}
248
249	*_buffer = _Dequeue(peek);
250	if (peek && waited) {
251		// There is a new buffer in the list; but since we are only peeking,
252		// notify the next waiting reader.
253		_NotifyOneReader(false);
254	}
255
256	if (*_buffer == NULL)
257		return B_NO_MEMORY;
258
259	return B_OK;
260}
261
262
263DECL_DATAGRAM_SOCKET(inline void)::Clear()
264{
265	AutoLocker _(fLock);
266	_Clear();
267}
268
269
270DECL_DATAGRAM_SOCKET(inline ssize_t)::AvailableData() const
271{
272	AutoLocker _(fLock);
273	status_t status = SocketStatus(true);
274	if (status < B_OK)
275		return status;
276
277	return fCurrentBytes;
278}
279
280
281DECL_DATAGRAM_SOCKET(inline void)::WakeAll()
282{
283	release_sem_etc(fNotify, 0, B_RELEASE_ALL);
284}
285
286
287DECL_DATAGRAM_SOCKET(inline void)::NotifyOne()
288{
289	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
290		| B_DO_NOT_RESCHEDULE);
291}
292
293
294DECL_DATAGRAM_SOCKET(inline status_t)::SocketStatus(bool peek) const
295{
296	if (peek)
297		return fSocket->error;
298
299	status_t status = fSocket->error;
300	fSocket->error = B_OK;
301
302	return status;
303}
304
305
306DECL_DATAGRAM_SOCKET(inline status_t)::_Enqueue(net_buffer* buffer)
307{
308	if (fSocket->receive.buffer_size > 0
309		&& (fCurrentBytes + buffer->size) > fSocket->receive.buffer_size)
310		return ENOBUFS;
311
312	fBuffers.Add(buffer);
313	fCurrentBytes += buffer->size;
314
315	_NotifyOneReader(true);
316
317	return B_OK;
318}
319
320
321DECL_DATAGRAM_SOCKET(inline net_buffer*)::_Dequeue(bool peek)
322{
323	if (fBuffers.IsEmpty())
324		return NULL;
325
326	if (peek)
327		return ModuleBundle::Buffer()->clone(fBuffers.Head(), false);
328
329	net_buffer* buffer = fBuffers.RemoveHead();
330	fCurrentBytes -= buffer->size;
331
332	return buffer;
333}
334
335
336DECL_DATAGRAM_SOCKET(inline void)::_Clear()
337{
338	BufferList::Iterator it = fBuffers.GetIterator();
339	while (it.HasNext())
340		ModuleBundle::Buffer()->free(it.Next());
341	fCurrentBytes = 0;
342}
343
344
345DECL_DATAGRAM_SOCKET(inline status_t)::_Wait(bigtime_t timeout)
346{
347	LockingBase::Unlock(&fLock);
348	status_t status = acquire_sem_etc(fNotify, 1, B_CAN_INTERRUPT
349		| (timeout != 0 ? B_ABSOLUTE_TIMEOUT : B_RELATIVE_TIMEOUT), timeout);
350	LockingBase::Lock(&fLock);
351
352	return status;
353}
354
355
356DECL_DATAGRAM_SOCKET(inline void)::_NotifyOneReader(bool notifySocket)
357{
358	release_sem_etc(fNotify, 1, B_RELEASE_IF_WAITING_ONLY
359		| B_DO_NOT_RESCHEDULE);
360
361	if (notifySocket) {
362		ModuleBundle::Stack()->notify_socket(fSocket, B_SELECT_READ,
363			fCurrentBytes);
364	}
365}
366
367
368DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
369{
370	if (ModuleBundle::Stack()->is_restarted_syscall())
371		return ModuleBundle::Stack()->restore_syscall_restart_timeout();
372
373	bigtime_t timeout = fSocket->receive.timeout;
374	if ((flags & MSG_DONTWAIT) != 0)
375		timeout = 0;
376	else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
377		timeout += system_time();
378
379	ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
380	return timeout;
381}
382
383
384#endif	// PROTOCOL_UTILITIES_H
385