• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
41 static void
spdyf_panic_std(void * cls,const char * file,unsigned int line,const char * reason)42 spdyf_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  */
58 SPDY_PanicCallback spdyf_panic = &spdyf_panic_std;
59 
60 
61 /**
62  * Global closure argument for "spdyf_panic".
63  */
64 void *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  */
73 static void
spdyf_cleanup_sessions(struct SPDY_Daemon * daemon)74 spdyf_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  */
94 static void
spdyf_close_all_sessions(struct SPDY_Daemon * daemon)95 spdyf_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  */
119 static int
spdyf_parse_options_va(struct SPDY_Daemon * daemon,va_list valist)120 spdyf_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 
160 void
SPDY_set_panic_func(SPDY_PanicCallback cb,void * cls)161 SPDY_set_panic_func (SPDY_PanicCallback cb,
162 					void *cls)
163 {
164 	spdyf_panic = cb;
165 	spdyf_panic_cls = cls;
166 }
167 
168 
169 struct SPDY_Daemon *
SPDYF_start_daemon_va(uint16_t port,const char * certfile,const char * keyfile,SPDY_NewSessionCallback nscb,SPDY_SessionClosedCallback sccb,SPDY_NewRequestCallback nrcb,SPDY_NewDataCallback npdcb,SPDYF_NewStreamCallback fnscb,SPDYF_NewDataCallback fndcb,void * cls,void * fcls,va_list valist)170 SPDYF_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 
372 void
SPDYF_stop_daemon(struct SPDY_Daemon * daemon)373 SPDYF_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 
391 int
SPDYF_get_timeout(struct SPDY_Daemon * daemon,unsigned long long * timeout)392 SPDYF_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 
431 int
SPDYF_get_fdset(struct SPDY_Daemon * daemon,fd_set * read_fd_set,fd_set * write_fd_set,fd_set * except_fd_set,bool all)432 SPDYF_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 
473 void
SPDYF_run(struct SPDY_Daemon * daemon)474 SPDYF_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