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