/** Called after the local connection to the mush has established */ void local_connected(struct conn *c) { char *hostid; int len; #if SSL_DEBUG_LEVEL > 0 errputs(stdout, "Local connection attempt completed. Setting up pipe."); #endif bufferevent_setcb(c->local_bev, pipe_cb, NULL, ssl_event_cb, c); bufferevent_enable(c->local_bev, EV_READ | EV_WRITE); bufferevent_setcb(c->remote_bev, pipe_cb, NULL, ssl_event_cb, c); bufferevent_enable(c->remote_bev, EV_READ | EV_WRITE); c->state = C_ESTABLISHED; /* Now pass the remote host and IP to the mush as the very first line it gets */ len = strlen(c->remote_host) + strlen(c->remote_ip) + 3; hostid = malloc(len + 1); sprintf(hostid, "%s^%s\r\n", c->remote_ip, c->remote_host); if (send_with_creds(bufferevent_getfd(c->local_bev), hostid, len) < 0) { penn_perror("send_with_creds"); delete_conn(c); } free(hostid); }
/** Dump a representation of the memcheck skip list into a file, using the dot language. * Use from a debugger: * \verbatim * (gdb) print memcheck_dump_struct("memcheck.dot") * \endverbatim * and then turn into an image: * \verbatim * # dot -Tsvg -o memcheck.svg memcheck.dot * \endverbatim * (dot is part of the graphviz package) * \param filename The output file name. */ void memcheck_dump_struct(const char *filename) { FILE *fp; MEM *chk; int n; fp = fopen(filename, "w"); if (!fp) { penn_perror("fopen"); return; } fputs("digraph memcheck_skiplist {\n", fp); fputs("rankdir=LR;\n", fp); fputs("node [shape=record];\n", fp); fputs("head [label=\"<l0>HEAD", fp); for (n = 1; n < MAX_LINKS; n += 1) { fprintf(fp, "|<l%d>%d", n, n); if (!memcheck_head->links[n]) fputs("\\n(NULL)", fp); } fputs("\"];\n", fp); for (n = 0; n < MAX_LINKS; n += 1) { if (memcheck_head->links[n]) fprintf(fp, "head:l%d -> mc%p:l%d;\n", n, (void *) memcheck_head->links[n], n); } for (chk = memcheck_head->links[0]; chk; chk = chk->links[0]) { fprintf(fp, "mc%p [label=\"{<l0>%s|<s0>%d}", (void *) chk, chk->ref_name, chk->ref_count); for (n = 1; n < chk->link_count; n += 1) { fprintf(fp, "|<l%d>", n); if (!chk->links[n]) fputs(" (NULL)", fp); } fputs("\"];\n", fp); if (chk->links[0]) fprintf(fp, "mc%p:s0 -> mc%p:l0\n", (void *) chk, (void *) chk->links[0]); for (n = 1; n < chk->link_count; n += 1) { if (chk->links[n]) fprintf(fp, "mc%p:l%d -> mc%p:l%d;\n", (void *) chk, n, (void *) chk->links[n], n); } } fputs("}\n", fp); fclose(fp); }
static void send_resp(evutil_socket_t fd, short what __attribute__ ((__unused__)), void *arg) { struct is_data *data = arg; ssize_t len; len = send(fd, &data->resp, sizeof data->resp, 0); if (len != (int) sizeof data->resp) { penn_perror("error writing packet"); exit(EXIT_FAILURE); } event_free(data->ev); free(data); }
/** Dump a representation of an intmap into a file, using the dot language. * Use from a debugger: * \verbatim * (gdb)print im_dump_graph(queue_map, "queue.dot") * \endverbatim * and then turn into an image: * \verbatim * # dot -Tpng -o queue.png queue.dot * \endverbatim * (dot is part of the graphviz package) * \param im the map to print. * \param filename The output file name. */ void im_dump_graph(intmap *im, const char *filename) { FILE *fp; if (!im || !filename) return; fp = fopen(filename, "w"); if (!fp) { penn_perror("fopen"); return; } fputs("digraph patricia { \n", fp); fputs("node [shape=Mrecord, colorscheme=blues3, style=filled];\n", fp); pat_list_nodes(im->root, fp); pat_list_links(im->root, fp); fputs("}\n", fp); fclose(fp); }
bool make_info_slave(void) { int socks[2]; pid_t child; int n; if (info_slave_state != INFO_SLAVE_DOWN) { if (info_slave_pid > 0) kill_info_slave(); info_slave_state = INFO_SLAVE_DOWN; } if (startup_attempts == 0) time(&startup_window); startup_attempts += 1; if (startup_attempts > MAX_ATTEMPTS) { time_t now; time(&now); if (difftime(now, startup_window) <= 60.0) { /* Too many failed attempts to start info_slave in 1 minute */ do_rawlog(LT_ERR, "Disabling info_slave due to too many errors."); info_slave_halted = true; return false; } else { /* Reset counter */ startup_window = now; startup_attempts = 0; } } #ifndef AF_LOCAL /* Use Posix.1g names. */ #define AF_LOCAL AF_UNIX #endif #ifdef HAVE_SOCKETPAIR if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, socks) < 0) { penn_perror("creating slave datagram socketpair"); return false; } if (socks[0] >= maxd) maxd = socks[0] + 1; if (socks[1] >= maxd) maxd = socks[1] + 1; #endif child = fork(); if (child < 0) { penn_perror("forking info slave"); #ifdef HAVE_SOCKETPAIR closesocket(socks[0]); closesocket(socks[1]); #endif return false; } else if (child > 0) { info_slave_state = INFO_SLAVE_READY; info_slave_pid = child; #ifdef HAVE_SOCKETPAIR info_slave = socks[0]; closesocket(socks[1]); do_rawlog(LT_ERR, "Spawning info slave, communicating using socketpair, pid %d.", child); #endif make_nonblocking(info_slave); } else { int errfd = fileno(stderr); int dupfd; /* Close unneeded fds and sockets: Everything but stderr and the socket used to talk to the mush */ for (n = 0; n < maxd; n++) { if (n == errfd) continue; #ifdef HAVE_SOCKETPAIR if (n == socks[1]) continue; #endif close(n); } /* Reuse stdin and stdout for talking to the slave */ dupfd = dup2(socks[1], 0); if (dupfd < 0) { penn_perror("dup2() of stdin in info_slave"); exit(1); } dupfd = dup2(socks[1], 1); if (dupfd < 0) { penn_perror("dup2() of stdout in info_slave"); exit(1); } close(socks[1]); execl("./info_slave", "info_slave", "for", MUDNAME, (char *) NULL); penn_perror("execing info slave"); exit(1); } if (info_slave >= maxd) maxd = info_slave + 1; lower_priority_by(info_slave_pid, 4); for (n = 0; n < maxd; n++) if (FD_ISSET(n, &info_pending)) query_info_slave(n); return true; }
void reap_info_slave(void) { struct response_dgram resp; ssize_t len; char hostname[BUFFER_LEN], *hp; int n, count; conn_source source; if (info_slave_state != INFO_SLAVE_PENDING) { if (info_slave_state == INFO_SLAVE_DOWN) make_info_slave(); return; } len = recv(info_slave, &resp, sizeof resp, 0); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return; else if (len < 0 || len != (int) sizeof resp) { penn_perror("reading info_slave response"); return; } /* okay, now we have some info! */ if (!FD_ISSET(resp.fd, &info_pending)) { /* Duplicate or spoof. Ignore. */ return; } FD_CLR(resp.fd, &info_pending); /* See if we have any other pending queries and change state if not. */ for (n = 0, count = 0; n < pending_max; n++) if (FD_ISSET(n, &info_pending)) count++; if (count == 0) { info_slave_state = INFO_SLAVE_READY; pending_max = 0; } hp = hostname; if (resp.hostname[0]) safe_str(resp.hostname, hostname, &hp); else safe_str(resp.ipaddr, hostname, &hp); *hp = '\0'; if (Forbidden_Site(resp.ipaddr) || Forbidden_Site(hostname)) { if (!Deny_Silent_Site(resp.ipaddr, AMBIGUOUS) || !Deny_Silent_Site(hostname, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "[%d/%s/%s] Refused connection.", resp.fd, hostname, resp.ipaddr); } shutdown(resp.fd, 2); closesocket(resp.fd); return; } if (resp.connected_to == TINYPORT) source = CS_IP_SOCKET; else if (resp.connected_to == SSLPORT) source = CS_OPENSSL_SOCKET; else source = CS_UNKNOWN; do_log(LT_CONN, 0, 0, "[%d/%s/%s] Connection opened from %s.", resp.fd, hostname, resp.ipaddr, source_to_s(source)); set_keepalive(resp.fd, options.keepalive_timeout); initializesock(resp.fd, hostname, resp.ipaddr, source); }
void query_info_slave(int fd) { struct request_dgram req; struct hostname_info *hi; char buf[BUFFER_LEN], *bp; ssize_t slen; FD_SET(fd, &info_pending); if (fd > pending_max) pending_max = fd + 1; info_queue_time = time(NULL); if (info_slave_state == INFO_SLAVE_DOWN) { if (!make_info_slave()) { FD_CLR(fd, &info_pending); closesocket(fd); /* Just drop the connection if the slave gets halted. A subsequent reconnect will work. */ } return; } memset(&req, 0, sizeof req); req.rlen = MAXSOCKADDR; if (getpeername(fd, (struct sockaddr *) req.remote.data, &req.rlen) < 0) { penn_perror("socket peer vanished"); shutdown(fd, 2); closesocket(fd); FD_CLR(fd, &info_pending); return; } /* Check for forbidden sites before bothering with ident */ bp = buf; hi = ip_convert(&req.remote.addr, req.rlen); safe_str(hi ? hi->hostname : "Not found", buf, &bp); *bp = '\0'; if (Forbidden_Site(buf)) { char port[NI_MAXSERV]; if (getnameinfo(&req.remote.addr, req.rlen, NULL, 0, port, sizeof port, NI_NUMERICHOST | NI_NUMERICSERV) != 0) penn_perror("getting remote port number"); else { if (!Deny_Silent_Site(buf, AMBIGUOUS)) { do_log(LT_CONN, 0, 0, "[%d/%s] Refused connection (remote port %s)", fd, buf, port); } } closesocket(fd); FD_CLR(fd, &info_pending); return; } req.llen = MAXSOCKADDR; if (getsockname(fd, (struct sockaddr *) req.local.data, &req.llen) < 0) { penn_perror("socket self vanished"); closesocket(fd); FD_CLR(fd, &info_pending); return; } req.fd = fd; req.use_dns = USE_DNS; slen = send(info_slave, &req, sizeof req, 0); if (slen < 0) { penn_perror("info slave query: write error"); make_info_slave(); return; } else if (slen != (int) sizeof req) { /* Shouldn't happen! */ penn_perror("info slave query: partial packet"); make_info_slave(); return; } info_slave_state = INFO_SLAVE_PENDING; }
/** Create a new SSL slave. * \param port The port to listen on for SSL connections. * \return 0 on success, -1 on failure */ int make_ssl_slave(void) { int fds[2]; if (ssl_slave_state != SSL_SLAVE_DOWN) { do_rawlog(LT_ERR, "Attempt to start ssl slave when a copy is already running."); return -1; } if (ssl_slave_halted) { do_rawlog(LT_ERR, "Attempt to start disabled ssl slave."); return -1; } if (startup_attempts == 0) time(&startup_window); startup_attempts += 1; if (startup_attempts > MAX_ATTEMPTS) { time_t now; time(&now); if (difftime(now, startup_window) <= 60.0) { do_rawlog(LT_ERR, "Disabling ssl_slave due to too many errors."); ssl_slave_halted = true; return -1; } else { /* Reset counter */ startup_window = now; startup_attempts = 0; } } if (pipe(fds) < 0) { do_rawlog(LT_ERR, "Unable to create pipe to speak to ssl_slave: %s", strerror(errno)); return -1; } if (fds[0] >= maxd) maxd = fds[0] + 1; if (fds[1] >= maxd) maxd = fds[1] + 1; if ((ssl_slave_pid = fork()) == 0) { /* Set up and exec ssl_slave */ int n, errfd = -1, connfd = -1; struct log_stream *lg; /* Close all open files but LT_CONN and LT_ERR, and assign them as stdout and stderr, respectively. */ /* If called on startup, maxd is 0 but log files and such have been opened. Use a reasonable max descriptor. If called because ssl_slave went down, maxd will be set properly already. */ if (!maxd) maxd = 20; lg = lookup_log(LT_ERR); if (lg) errfd = fileno(lg->fp); lg = lookup_log(LT_CONN); if (lg) connfd = fileno(lg->fp); dup2(fds[0], 0); /* stdin */ dup2(connfd, 1); /* stdout */ dup2(errfd, 2); /* stderr */ for (n = 3; n < maxd; n++) close(n); execl("./ssl_slave", "ssl_slave", "for", MUDNAME, NULL); penn_perror("execing ssl slave"); return EXIT_FAILURE; } else if (ssl_slave_pid < 0) { do_rawlog(LT_ERR, "Failure to fork ssl_slave: %s", strerror(errno)); return -1; } else { struct ssl_slave_config cf; ssl_slave_ctl_fd = fds[1]; close(fds[0]); /* Set up arguments to the slave */ memset(&cf, 0, sizeof cf); strcpy(cf.socket_file, options.socket_file); strcpy(cf.ssl_ip_addr, SSL_IP_ADDR); cf.normal_port = options.port; cf.ssl_port = options.ssl_port; strcpy(cf.private_key_file, options.ssl_private_key_file); strcpy(cf.ca_file, options.ssl_ca_file); cf.require_client_cert = options.ssl_require_client_cert; cf.keepalive_timeout = options.keepalive_timeout; if (write(ssl_slave_ctl_fd, &cf, sizeof cf) < 0) { do_rawlog(LT_ERR, "Unable to send ssl_slave config options: %s", strerror(errno)); return -1; } ssl_slave_state = SSL_SLAVE_RUNNING; do_rawlog(LT_ERR, "Spawning ssl_slave, communicating over %s, pid %d.", options.socket_file, ssl_slave_pid); return 0; } return -1; }