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