int status_client_ncurses(struct conf **confs) { int ret=-1; int csin=-1; int csout=-1; pid_t childpid=-1; struct async *as=NULL; const char *monitor_logfile=get_string(confs[OPT_MONITOR_LOGFILE]); struct asfd *so_asfd=NULL; struct sel *sel=NULL; if(!(sel=sel_alloc())) goto end; setup_signals(); // Fork a burp child process that will contact the server over SSL. // We will read and write from and to its stdout and stdin. if((childpid=fork_monitor(&csin, &csout, confs))<0) goto end; //printf("childpid: %d\n", childpid); if(!(as=async_alloc()) || as->init(as, 0) || !setup_asfd_linebuf_write(as, "monitor stdin", &csin) || !setup_asfd_linebuf_read(as, "monitor stdout", &csout)) goto end; //printf("ml: %s\n", monitor_logfile); #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) { if(!setup_asfd_ncurses_stdin(as)) goto end; ncurses_init(); } #endif if(!(so_asfd=setup_asfd_stdout(as))) goto end; if(monitor_logfile && !(lfzp=fzp_open(monitor_logfile, "wb"))) goto end; log_fzp_set_direct(lfzp); ret=status_client_ncurses_main_loop(as, so_asfd, sel, get_string(confs[OPT_ORIG_CLIENT])); end: #ifdef HAVE_NCURSES if(actg==ACTION_STATUS) ncurses_free(); #endif if(ret) logp("%s exiting with error: %d\n", __func__, ret); fzp_close(&lfzp); async_asfd_free_all(&as); close_fd(&csin); close_fd(&csout); sel_free(&sel); return ret; }
static int run_server(struct conf **confs, const char *conffile, int *rfds, int *sfds) { int i=0; int ret=-1; SSL_CTX *ctx=NULL; int found_normal_child=0; struct asfd *asfd=NULL; struct asfd *scfd=NULL; struct async *mainas=NULL; const char *port=get_string(confs[OPT_PORT]); const char *address=get_string(confs[OPT_ADDRESS]); const char *status_port=get_string(confs[OPT_STATUS_PORT]); const char *status_address=get_string(confs[OPT_STATUS_ADDRESS]); if(!(ctx=ssl_initialise_ctx(confs))) { logp("error initialising ssl ctx\n"); goto end; } if((ssl_load_dh_params(ctx, confs))) { logp("error loading dh params\n"); goto end; } if(init_listen_socket(address, port, rfds) || init_listen_socket(status_address, status_port, sfds)) goto end; if(!(mainas=async_alloc()) || mainas->init(mainas, 0)) goto end; for(i=0; i<LISTEN_SOCKETS && rfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server socket", &rfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_MAIN; } for(i=0; i<LISTEN_SOCKETS && sfds[i]!=-1; i++) { struct asfd *newfd; if(!(newfd=setup_asfd(mainas, "main server status socket", &sfds[i]))) goto end; newfd->fdtype=ASFD_FD_SERVER_LISTEN_STATUS; } while(!hupreload) { switch(mainas->read_write(mainas)) { case 0: for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->new_client) { // Incoming client. asfd->new_client=0; if(process_incoming_client(asfd, ctx, conffile, confs)) goto end; if(!get_int(confs[OPT_FORK])) { gentleshutdown++; ret=1; goto end; } continue; } } break; default: int removed=0; // Maybe one of the fds had a problem. // Find and remove it and carry on if possible. for(asfd=mainas->asfd; asfd; ) { struct asfd *a; if(!asfd->want_to_remove) { asfd=asfd->next; continue; } mainas->asfd_remove(mainas, asfd); logp("%s: disconnected fd %d\n", asfd->desc, asfd->fd); a=asfd->next; asfd_free(&asfd); asfd=a; removed++; } if(removed) break; // If we got here, there was no fd to remove. // It is a fatal error. goto end; } for(asfd=mainas->asfd; asfd; asfd=asfd->next) { if(asfd->fdtype!=ASFD_FD_SERVER_PIPE_READ || !asfd->rbuf->buf) continue; // One of the child processes is giving us information. // Try to append it to any of the status child pipes. for(scfd=mainas->asfd; scfd; scfd=scfd->next) { if(scfd->fdtype!=ASFD_FD_SERVER_PIPE_WRITE) continue; switch(scfd->append_all_to_write_buffer(scfd, asfd->rbuf)) { case APPEND_OK: case APPEND_BLOCKED: break; default: goto end; } } // Free the information, even if we did not manage // to append it. That should be OK, more will be along // soon. iobuf_free_content(asfd->rbuf); } chld_check_for_exiting(mainas); // Leave if we had a SIGUSR1 and there are no children running. if(gentleshutdown) { if(!gentleshutdown_logged) { logp("got SIGUSR2 gentle reload signal\n"); logp("will shut down once children have exited\n"); gentleshutdown_logged++; } // FIX THIS: // found_normal_child=chld_add_fd_to_normal_sets(confs, &fsr, &fse, &mfd); else if(!found_normal_child) { logp("all children have exited - shutting down\n"); break; } } } if(hupreload) logp("got SIGHUP reload signal\n"); ret=0; end: async_asfd_free_all(&mainas); if(ctx) ssl_destroy_ctx(ctx); return ret; }
static int run_child(int *cfd, SSL_CTX *ctx, struct sockaddr_storage *addr, int status_wfd, int status_rfd, const char *conffile, int forking) { int ret=-1; int ca_ret=0; SSL *ssl=NULL; BIO *sbio=NULL; struct conf **confs=NULL; struct conf **cconfs=NULL; struct cntr *cntr=NULL; struct async *as=NULL; const char *cname=NULL; struct asfd *asfd=NULL; int is_status_server=0; if(!(confs=confs_alloc()) || !(cconfs=confs_alloc())) goto end; set_peer_env_vars(addr); // Reload global config, in case things have changed. This means that // the server does not need to be restarted for most conf changes. confs_init(confs); confs_init(cconfs); if(conf_load_global_only(conffile, confs)) goto end; // Hack to keep forking turned off if it was specified as off on the // command line. if(!forking) set_int(confs[OPT_FORK], 0); if(!(sbio=BIO_new_socket(*cfd, BIO_NOCLOSE)) || !(ssl=SSL_new(ctx))) { logp("There was a problem joining ssl to the socket\n"); goto end; } SSL_set_bio(ssl, sbio, sbio); /* Do not try to check peer certificate straight away. Clients can send a certificate signing request when they have no certificate. */ SSL_set_verify(ssl, SSL_VERIFY_PEER /* | SSL_VERIFY_FAIL_IF_NO_PEER_CERT */, 0); if(ssl_do_accept(ssl)) goto end; if(!(as=async_alloc()) || as->init(as, 0) || !(asfd=setup_asfd_ssl(as, "main socket", cfd, ssl))) goto end; asfd->set_timeout(asfd, get_int(confs[OPT_NETWORK_TIMEOUT])); asfd->ratelimit=get_float(confs[OPT_RATELIMIT]); if(authorise_server(as->asfd, confs, cconfs) || !(cname=get_string(cconfs[OPT_CNAME])) || !*cname) { // Add an annoying delay in case they are tempted to // try repeatedly. log_and_send(as->asfd, "unable to authorise on server"); sleep(1); goto end; } if(!get_int(cconfs[OPT_ENABLED])) { log_and_send(as->asfd, "client not enabled on server"); sleep(1); goto end; } // Set up counters. Have to wait until here to get cname. if(!(cntr=cntr_alloc()) || cntr_init(cntr, cname)) goto end; set_cntr(confs[OPT_CNTR], cntr); set_cntr(cconfs[OPT_CNTR], cntr); /* At this point, the client might want to get a new certificate signed. Clients on 1.3.2 or newer can do this. */ if((ca_ret=ca_server_maybe_sign_client_cert(as->asfd, confs, cconfs))<0) { logp("Error signing client certificate request for %s\n", cname); goto end; } else if(ca_ret>0) { // Certificate signed and sent back. // Everything is OK, but we will close this instance // so that the client can start again with a new // connection and its new certificates. logp("Signed and returned client certificate request for %s\n", cname); ret=0; goto end; } /* Now it is time to check the certificate. */ if(ssl_check_cert(ssl, confs, cconfs)) { log_and_send(as->asfd, "check cert failed on server"); goto end; } if(status_rfd>=0) { is_status_server=1; if(!setup_asfd(as, "status server parent socket", &status_rfd)) goto end; } ret=child(as, is_status_server, status_wfd, confs, cconfs); end: *cfd=-1; if(as && asfd_flush_asio(as->asfd)) ret=-1; async_asfd_free_all(&as); // This closes cfd for us. logp("exit child\n"); if(cntr) cntr_free(&cntr); if(confs) { set_cntr(confs[OPT_CNTR], NULL); confs_free(&confs); } if(cconfs) { set_cntr(cconfs[OPT_CNTR], NULL); confs_free(&cconfs); } return ret; }
static int process_incoming_client(struct asfd *asfd, SSL_CTX *ctx, const char *conffile, struct conf **confs) { int cfd=-1; pid_t childpid; int pipe_rfd[2]; int pipe_wfd[2]; socklen_t client_length=0; struct sockaddr_storage client_name; enum asfd_fdtype fdtype=asfd->fdtype; int forking=get_int(confs[OPT_FORK]); client_length=sizeof(client_name); if((cfd=accept(asfd->fd, (struct sockaddr *)&client_name, &client_length))==-1) { // Look out, accept will get interrupted by SIGCHLDs. if(errno==EINTR) return 0; logp("accept failed on %s (%d) in %s: %s\n", asfd->desc, asfd->fd, __func__, strerror(errno)); return -1; } reuseaddr(cfd); if(log_peer_address(&client_name)) return -1; if(!forking) return run_child(&cfd, ctx, &client_name, -1, -1, conffile, forking); if(chld_check_counts(confs, asfd)) { logp("Closing new connection.\n"); close_fd(&cfd); return 0; } if(pipe(pipe_rfd)<0 || pipe(pipe_wfd)<0) { logp("pipe failed: %s", strerror(errno)); close_fd(&cfd); return -1; } switch((childpid=fork())) { case -1: logp("fork failed: %s\n", strerror(errno)); return -1; case 0: { // Child. int p; int ret; struct sigaction sa; struct async *as=asfd->as; async_asfd_free_all(&as); // Close unnecessary file descriptors. // Go up to FD_SETSIZE and hope for the best. // FIX THIS: Now that async_asfd_free_all() is doing // everything, double check whether this is needed. for(p=3; p<(int)FD_SETSIZE; p++) { if(p!=pipe_rfd[1] && p!=pipe_wfd[0] && p!=cfd) close(p); } // Set SIGCHLD back to default, so that I // can get sensible returns from waitpid. memset(&sa, 0, sizeof(sa)); sa.sa_handler=SIG_DFL; sigaction(SIGCHLD, &sa, NULL); close(pipe_rfd[0]); // close read end close(pipe_wfd[1]); // close write end confs_free_content(confs); confs_init(confs); ret=run_child(&cfd, ctx, &client_name, pipe_rfd[1], fdtype==ASFD_FD_SERVER_LISTEN_STATUS?pipe_wfd[0]:-1, conffile, forking); close(pipe_rfd[1]); close(pipe_wfd[0]); close_fd(&cfd); exit(ret); } default: // Parent. close(pipe_rfd[1]); // close write end close(pipe_wfd[0]); // close read end close_fd(&cfd); return setup_parent_child_pipes(asfd, childpid, &pipe_rfd[0], &pipe_wfd[1]); } }
int status_client_ncurses(enum action act, struct conf **confs) { int csin=-1; int csout=-1; int ret=-1; pid_t childpid=-1; struct async *as=NULL; const char *monitor_logfile=get_string(confs[OPT_MONITOR_LOGFILE]); #ifdef HAVE_NCURSES_H actg=act; // So that the sighandler can call endwin(). #else if(act==ACTION_STATUS) { printf("To use the live status monitor, you need to recompile with ncurses support.\n"); goto end; } #endif setup_signals(); // Fork a burp child process that will contact the server over SSL. // We will read and write from and to its stdout and stdin. if((childpid=fork_monitor(&csin, &csout, confs))<0) goto end; //printf("childpid: %d\n", childpid); set_non_blocking(csin); set_non_blocking(csout); if(!(as=async_alloc()) || as->init(as, 0) || !setup_asfd(as, "monitor stdin", &csin, NULL, ASFD_STREAM_LINEBUF, ASFD_FD_CLIENT_MONITOR_WRITE, -1, confs) || !setup_asfd(as, "monitor stdout", &csout, NULL, ASFD_STREAM_LINEBUF, ASFD_FD_CLIENT_MONITOR_READ, -1, confs)) goto end; //printf("ml: %s\n", monitor_logfile); #ifdef HAVE_NCURSES_H if(actg==ACTION_STATUS) { int stdinfd=fileno(stdin); if(!setup_asfd(as, "stdin", &stdinfd, NULL, ASFD_STREAM_NCURSES_STDIN, ASFD_FD_CLIENT_NCURSES_READ, -1, confs)) goto end; ncurses_init(); } #endif if(monitor_logfile && !(lfp=open_file(monitor_logfile, "wb"))) goto end; set_logfp_direct(lfp); ret=main_loop(as, act, confs); end: #ifdef HAVE_NCURSES_H if(actg==ACTION_STATUS) endwin(); #endif if(ret) logp("%s exiting with error: %d\n", __func__, ret); close_fp(&lfp); async_asfd_free_all(&as); close_fd(&csin); close_fd(&csout); return ret; }