1/*
2    This file is part of libmicrospdy
3    Copyright Copyright (C) 2012 Andrey Uzunov
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file microspdy/daemon.c
21 * @brief  daemon functionality
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "session.h"
29#include "io.h"
30
31
32/**
33 * Default implementation of the panic function,
34 * prints an error message and aborts.
35 *
36 * @param cls unused
37 * @param file name of the file with the problem
38 * @param line line number with the problem
39 * @param reason error message with details
40 */
41static void
42spdyf_panic_std (void *cls,
43	       const char *file,
44	       unsigned int line,
45	       const char *reason)
46{
47	(void)cls;
48	fprintf (stdout, "Fatal error in libmicrospdy %s:%u: %s\n",
49		file, line, reason);
50	//raise(SIGINT); //used for gdb
51	abort ();
52}
53
54
55/**
56 * Global handler for fatal errors.
57 */
58SPDY_PanicCallback spdyf_panic = &spdyf_panic_std;
59
60
61/**
62 * Global closure argument for "spdyf_panic".
63 */
64void *spdyf_panic_cls;
65
66
67/**
68 * Free resources associated with all closed connections.
69 * (destroy responses, free buffers, etc.).
70 *
71 * @param daemon daemon to clean up
72 */
73static void
74spdyf_cleanup_sessions (struct SPDY_Daemon *daemon)
75{
76	struct SPDY_Session *session;
77
78	while (NULL != (session = daemon->cleanup_head))
79	{
80		DLL_remove (daemon->cleanup_head,
81			daemon->cleanup_tail,
82			session);
83
84		SPDYF_session_destroy(session);
85	}
86}
87
88
89/**
90 * Closing of all connections handled by the daemon.
91 *
92 * @param daemon SPDY daemon
93 */
94static void
95spdyf_close_all_sessions (struct SPDY_Daemon *daemon)
96{
97	struct SPDY_Session *session;
98
99	while (NULL != (session = daemon->sessions_head))
100	{
101		//prepare GOAWAY frame
102		SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true);
103		//try to send the frame (it is best effort, so it will maybe sent)
104		SPDYF_session_write(session,true);
105		SPDYF_session_close(session);
106	}
107
108	spdyf_cleanup_sessions(daemon);
109}
110
111
112/**
113 * Parse a list of options given as varargs.
114 *
115 * @param daemon the daemon to initialize
116 * @param valist the options
117 * @return SPDY_YES on success, SPDY_NO on error
118 */
119static int
120spdyf_parse_options_va (struct SPDY_Daemon *daemon,
121		  va_list valist)
122{
123	enum SPDY_DAEMON_OPTION opt;
124
125	while (SPDY_DAEMON_OPTION_END != (opt = (enum SPDY_DAEMON_OPTION) va_arg (valist, int)))
126	{
127		if(opt & daemon->options)
128		{
129			SPDYF_DEBUG("Daemon option %i used twice",opt);
130			return SPDY_NO;
131		}
132		daemon->options |= opt;
133
134		switch (opt)
135		{
136			case SPDY_DAEMON_OPTION_SESSION_TIMEOUT:
137				daemon->session_timeout = va_arg (valist, unsigned int) * 1000;
138				break;
139			case SPDY_DAEMON_OPTION_SOCK_ADDR:
140				daemon->address = va_arg (valist, struct sockaddr *);
141				break;
142			case SPDY_DAEMON_OPTION_FLAGS:
143				daemon->flags = va_arg (valist, enum SPDY_DAEMON_FLAG);
144				break;
145			case SPDY_DAEMON_OPTION_IO_SUBSYSTEM:
146				daemon->io_subsystem = va_arg (valist, enum SPDY_IO_SUBSYSTEM);
147				break;
148			case SPDY_DAEMON_OPTION_MAX_NUM_FRAMES:
149				daemon->max_num_frames = va_arg (valist, uint32_t);
150				break;
151			default:
152				SPDYF_DEBUG("Wrong option for the daemon %i",opt);
153				return SPDY_NO;
154		}
155	}
156	return SPDY_YES;
157}
158
159
160void
161SPDY_set_panic_func (SPDY_PanicCallback cb,
162					void *cls)
163{
164	spdyf_panic = cb;
165	spdyf_panic_cls = cls;
166}
167
168
169struct SPDY_Daemon *
170SPDYF_start_daemon_va (uint16_t port,
171					const char *certfile,
172					const char *keyfile,
173					SPDY_NewSessionCallback nscb,
174					SPDY_SessionClosedCallback sccb,
175					SPDY_NewRequestCallback nrcb,
176					SPDY_NewDataCallback npdcb,
177					SPDYF_NewStreamCallback fnscb,
178					SPDYF_NewDataCallback fndcb,
179					void * cls,
180					void * fcls,
181					va_list valist)
182{
183	struct SPDY_Daemon *daemon = NULL;
184	int afamily;
185	int option_on = 1;
186	int ret;
187	struct sockaddr_in* servaddr4 = NULL;
188#if HAVE_INET6
189	struct sockaddr_in6* servaddr6 = NULL;
190#endif
191	socklen_t addrlen;
192
193	if (NULL == (daemon = malloc (sizeof (struct SPDY_Daemon))))
194	{
195		SPDYF_DEBUG("malloc");
196		return NULL;
197	}
198	memset (daemon, 0, sizeof (struct SPDY_Daemon));
199	daemon->socket_fd = -1;
200	daemon->port = port;
201
202	if(SPDY_YES != spdyf_parse_options_va (daemon, valist))
203	{
204		SPDYF_DEBUG("parse");
205		goto free_and_fail;
206	}
207
208  if(0 == daemon->max_num_frames)
209    daemon->max_num_frames = SPDYF_NUM_SENT_FRAMES_AT_ONCE;
210
211	if(!port && NULL == daemon->address)
212	{
213		SPDYF_DEBUG("Port is 0");
214		goto free_and_fail;
215	}
216  if(0 == daemon->io_subsystem)
217    daemon->io_subsystem = SPDY_IO_SUBSYSTEM_OPENSSL;
218
219  if(SPDY_YES != SPDYF_io_set_daemon(daemon, daemon->io_subsystem))
220		goto free_and_fail;
221
222  if(SPDY_IO_SUBSYSTEM_RAW != daemon->io_subsystem)
223  {
224    if (NULL == certfile
225      || NULL == (daemon->certfile = strdup (certfile)))
226    {
227      SPDYF_DEBUG("strdup (certfile)");
228      goto free_and_fail;
229    }
230    if (NULL == keyfile
231      || NULL == (daemon->keyfile = strdup (keyfile)))
232    {
233      SPDYF_DEBUG("strdup (keyfile)");
234      goto free_and_fail;
235    }
236  }
237
238	daemon->new_session_cb = nscb;
239	daemon->session_closed_cb = sccb;
240	daemon->new_request_cb = nrcb;
241	daemon->received_data_cb = npdcb;
242	daemon->cls = cls;
243	daemon->fcls = fcls;
244	daemon->fnew_stream_cb = fnscb;
245	daemon->freceived_data_cb = fndcb;
246
247#if HAVE_INET6
248	//handling IPv6
249	if((daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
250		&& NULL != daemon->address && AF_INET6 != daemon->address->sa_family)
251	{
252		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but IPv4 address provided");
253		goto free_and_fail;
254	}
255
256  addrlen = sizeof (struct sockaddr_in6);
257
258	if(NULL == daemon->address)
259	{
260		if (NULL == (servaddr6 = malloc (addrlen)))
261		{
262			SPDYF_DEBUG("malloc");
263			goto free_and_fail;
264		}
265		memset (servaddr6, 0, addrlen);
266		servaddr6->sin6_family = AF_INET6;
267		servaddr6->sin6_addr = in6addr_any;
268		servaddr6->sin6_port = htons (port);
269		daemon->address = (struct sockaddr *) servaddr6;
270	}
271
272  if(AF_INET6 == daemon->address->sa_family)
273  {
274    afamily = PF_INET6;
275  }
276  else
277  {
278    afamily = PF_INET;
279  }
280#else
281	//handling IPv4
282	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
283	{
284		SPDYF_DEBUG("SPDY_DAEMON_FLAG_ONLY_IPV6 set but no support");
285		goto free_and_fail;
286	}
287
288  addrlen = sizeof (struct sockaddr_in);
289
290	if(NULL == daemon->address)
291	{
292		if (NULL == (servaddr4 = malloc (addrlen)))
293		{
294			SPDYF_DEBUG("malloc");
295			goto free_and_fail;
296		}
297		memset (servaddr4, 0, addrlen);
298		servaddr4->sin_family = AF_INET;
299		servaddr4->sin_addr = INADDR_ANY;
300		servaddr4->sin_port = htons (port);
301		daemon->address = (struct sockaddr *) servaddr4;
302	}
303
304	afamily = PF_INET;
305#endif
306
307	daemon->socket_fd = socket (afamily, SOCK_STREAM, 0);
308	if (-1 == daemon->socket_fd)
309	{
310		SPDYF_DEBUG("sock");
311		goto free_and_fail;
312	}
313
314	//setting option for the socket to reuse address
315	ret = setsockopt(daemon->socket_fd, SOL_SOCKET, SO_REUSEADDR, &option_on, sizeof(option_on));
316	if(ret)
317	{
318		SPDYF_DEBUG("WARNING: SO_REUSEADDR was not set for the server");
319	}
320
321#if HAVE_INET6
322	if(daemon->flags & SPDY_DAEMON_FLAG_ONLY_IPV6)
323	{
324		ret = setsockopt(daemon->socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &option_on, sizeof(option_on));
325		if(ret)
326		{
327			SPDYF_DEBUG("setsockopt with IPPROTO_IPV6 failed");
328			goto free_and_fail;
329		}
330	}
331#endif
332
333	if (-1 == bind (daemon->socket_fd, daemon->address, addrlen))
334	{
335		SPDYF_DEBUG("bind %i",errno);
336		goto free_and_fail;
337	}
338
339	if (listen (daemon->socket_fd, 20) < 0)
340	{
341		SPDYF_DEBUG("listen %i",errno);
342		goto free_and_fail;
343	}
344
345	if(SPDY_YES != daemon->fio_init(daemon))
346	{
347		SPDYF_DEBUG("tls");
348		goto free_and_fail;
349	}
350
351	return daemon;
352
353	//for GOTO
354	free_and_fail:
355	if(daemon->socket_fd > 0)
356		(void)close (daemon->socket_fd);
357
358	free(servaddr4);
359#if HAVE_INET6
360	free(servaddr6);
361#endif
362	if(NULL != daemon->certfile)
363		free(daemon->certfile);
364	if(NULL != daemon->keyfile)
365		free(daemon->keyfile);
366	free (daemon);
367
368	return NULL;
369}
370
371
372void
373SPDYF_stop_daemon (struct SPDY_Daemon *daemon)
374{
375	daemon->fio_deinit(daemon);
376
377	shutdown (daemon->socket_fd, SHUT_RDWR);
378	spdyf_close_all_sessions (daemon);
379	(void)close (daemon->socket_fd);
380
381	if(!(SPDY_DAEMON_OPTION_SOCK_ADDR & daemon->options))
382		free(daemon->address);
383
384	free(daemon->certfile);
385	free(daemon->keyfile);
386
387	free(daemon);
388}
389
390
391int
392SPDYF_get_timeout (struct SPDY_Daemon *daemon,
393		     unsigned long long *timeout)
394{
395	unsigned long long earliest_deadline = 0;
396	unsigned long long now;
397	struct SPDY_Session *pos;
398	bool have_timeout;
399
400	if(0 == daemon->session_timeout)
401		return SPDY_NO;
402
403	now = SPDYF_monotonic_time();
404	have_timeout = false;
405	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
406	{
407		if ( (! have_timeout) ||
408			(earliest_deadline > pos->last_activity + daemon->session_timeout) )
409			earliest_deadline = pos->last_activity + daemon->session_timeout;
410
411		have_timeout = true;
412
413		if (SPDY_YES == pos->fio_is_pending(pos))
414		{
415			earliest_deadline = 0;
416			break;
417		}
418	}
419
420	if (!have_timeout)
421		return SPDY_NO;
422	if (earliest_deadline <= now)
423		*timeout = 0;
424	else
425		*timeout = earliest_deadline - now;
426
427	return SPDY_YES;
428}
429
430
431int
432SPDYF_get_fdset (struct SPDY_Daemon *daemon,
433				fd_set *read_fd_set,
434				fd_set *write_fd_set,
435				fd_set *except_fd_set,
436				bool all)
437{
438	(void)except_fd_set;
439	struct SPDY_Session *pos;
440	int fd;
441	int max_fd = -1;
442
443	fd = daemon->socket_fd;
444	if (-1 != fd)
445	{
446		FD_SET (fd, read_fd_set);
447		/* update max file descriptor */
448		max_fd = fd;
449	}
450
451	for (pos = daemon->sessions_head; NULL != pos; pos = pos->next)
452	{
453		fd = pos->socket_fd;
454		FD_SET(fd, read_fd_set);
455		if (all
456		    || (NULL != pos->response_queue_head) //frames pending
457		    || (NULL != pos->write_buffer) //part of last frame pending
458		    || (SPDY_SESSION_STATUS_CLOSING == pos->status) //the session is about to be closed
459		    || (daemon->session_timeout //timeout passed for the session
460			&& (pos->last_activity + daemon->session_timeout < SPDYF_monotonic_time()))
461		    || (SPDY_YES == pos->fio_is_pending(pos)) //data in TLS' read buffer pending
462		    || ((pos->read_buffer_offset - pos->read_buffer_beginning) > 0) // data in lib's read buffer pending
463		    )
464			FD_SET(fd, write_fd_set);
465		if(fd > max_fd)
466			max_fd = fd;
467	}
468
469	return max_fd;
470}
471
472
473void
474SPDYF_run (struct SPDY_Daemon *daemon)
475{
476	struct SPDY_Session *pos;
477	struct SPDY_Session *next;
478	int num_ready;
479	fd_set rs;
480	fd_set ws;
481	fd_set es;
482	int max;
483	struct timeval timeout;
484	int ds;
485
486	timeout.tv_sec = 0;
487	timeout.tv_usec = 0;
488	FD_ZERO (&rs);
489	FD_ZERO (&ws);
490	FD_ZERO (&es);
491	//here we need really all descriptors to see later which are ready
492	max = SPDYF_get_fdset(daemon,&rs,&ws,&es, true);
493
494	num_ready = select (max + 1, &rs, &ws, &es, &timeout);
495
496	if(num_ready < 1)
497		return;
498
499	if ( (-1 != (ds = daemon->socket_fd)) &&
500		(FD_ISSET (ds, &rs)) ){
501		SPDYF_session_accept(daemon);
502	}
503
504	next = daemon->sessions_head;
505	while (NULL != (pos = next))
506	{
507		next = pos->next;
508		ds = pos->socket_fd;
509		if (ds != -1)
510		{
511			//fill the read buffer
512			if (FD_ISSET (ds, &rs) || pos->fio_is_pending(pos)){
513				SPDYF_session_read(pos);
514			}
515
516			//do something with the data in read buffer
517			if(SPDY_NO == SPDYF_session_idle(pos))
518			{
519				//the session was closed, cannot write anymore
520				//continue;
521			}
522
523			//write whatever has been put to the response queue
524			//during read or idle operation, something might be put
525			//on the response queue, thus call write operation
526			if (FD_ISSET (ds, &ws)){
527				if(SPDY_NO == SPDYF_session_write(pos, false))
528				{
529					//SPDYF_session_close(pos);
530					//continue;
531				}
532			}
533
534			/* the response queue has been flushed for half closed
535			 * connections, so let close them */
536			/*if(pos->read_closed)
537			{
538				SPDYF_session_close(pos);
539			}*/
540		}
541	}
542
543	spdyf_cleanup_sessions(daemon);
544}
545