pennmush-1.8.3p3/game/data/
pennmush-1.8.3p3/game/log/
pennmush-1.8.3p3/game/save/
pennmush-1.8.3p3/game/txt/evt/
pennmush-1.8.3p3/game/txt/nws/
pennmush-1.8.3p3/po/
pennmush-1.8.3p3/win32/msvc.net/
pennmush-1.8.3p3/win32/msvc6/
/**
 * \file info_master.c
 *
 * \brief mush-end functions for talking to info_slave
 */

#include "copyrite.h"
#include "config.h"

#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#ifdef I_UNISTD
#include <unistd.h>
#endif
#ifdef I_SYS_TIME
#include <sys/time.h>
#endif
#if !defined(I_SYS_TIME) || defined(TIME_WITH_SYS_TIME)
#include <time.h>
#endif
#ifdef I_NETDB
#include <netdb.h>
#endif
#ifdef I_SYS_SOCKET
#include <sys/socket.h>
#endif

#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>

#include "conf.h"
#include "externs.h"
#include "access.h"
#include "mysocket.h"
#include "ident.h"
#include "lookup.h"
#include "log.h"
#include "wait.h"


#ifdef INFO_SLAVE

#ifdef WIN32
#error "info_slave will not work on Windows."
#endif

#ifndef HAVE_SOCKETPAIR
#error "no supported communication options for talking with info_slave are available."
#endif

static fd_set info_pending; /**< Keep track of fds pending a slave lookup */
static int pending_max = 0;
int info_slave = -1;
pid_t info_slave_pid = -1;      /**< Process id of the info_slave process */
enum is_state info_slave_state = INFO_SLAVE_DOWN;       /**< State of the info_slave process */
time_t info_queue_time; /**< Time of last write to slave */


 /* From bsd.c */
extern int maxd;
DESC *initializesock(int s, char *addr, char *ip, int use_ssl);

/** Re-query lookups that have timed out */
void
update_pending_info_slaves(void)
{
  time_t now;
  int newsock;

  now = time(NULL);

  if (info_slave_state == INFO_SLAVE_PENDING && now > info_queue_time + 30) {
    /* rerun any pending queries that got lost */
    info_queue_time = now;
    for (newsock = 0; newsock < pending_max; newsock++)
      if (FD_ISSET(newsock, &info_pending))
        query_info_slave(newsock);
  }
}

void
init_info_slave(void)
{
  FD_ZERO(&info_pending);
  make_info_slave();
}

void
make_info_slave(void)
{
  int socks[2];
  char num[NI_MAXSERV];
  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;
  }
#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) {
    perror("creating slave datagram socketpair");
    return;
  }
  if (socks[0] >= maxd)
    maxd = socks[0] + 1;
  if (socks[1] >= maxd)
    maxd = socks[1] + 1;
#endif

  child = fork();
  if (child < 0) {
    perror("forking info slave");
#ifdef HAVE_SOCKETPAIR
    closesocket(socks[0]);
    closesocket(socks[1]);
#endif
    return;
  } 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);
    /* Close unneeded fds and sockets: Everything but stdout 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);
    }
    snprintf(num, NI_MAXSERV, "%d", socks[1]);
    execl("./info_slave", "info_slave", num, (char *) NULL);
    perror("execing info slave");
    _exit(1);
  }

  if (info_slave >= maxd)
    maxd = info_slave + 1;

  lower_priority_by(info_slave, 4);

  for (n = 0; n < maxd; n++)
    if (FD_ISSET(n, &info_pending))
      query_info_slave(n);
}

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) {
    make_info_slave();
    return;
  }

  req.rlen = MAXSOCKADDR;
  if (getpeername(fd, (struct sockaddr *) req.remote.data, &req.rlen) < 0) {
    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)
      perror("getting remote port number");
    else {
      if (!Deny_Silent_Site(buf, AMBIGUOUS)) {
        do_log(LT_CONN, 0, 0, T("[%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) {
    perror("socket self vanished");
    closesocket(fd);
    FD_CLR(fd, &info_pending);
    return;
  }

  req.fd = fd;
  req.use_dns = USE_DNS;
  req.use_ident = USE_IDENT;
  req.timeout = IDENT_TIMEOUT;

  slen = send(info_slave, &req, sizeof req, 0);
  if (slen < 0) {
    perror("info slave query: write error");
    make_info_slave();
    return;
  } else if (slen != (int) sizeof req) {
    /* Shouldn't happen! */
    perror("info slave query: partial packet");
    make_info_slave();
    return;
  }
  info_slave_state = INFO_SLAVE_PENDING;
}

void
reap_info_slave(void)
{
  struct response_dgram resp;
  ssize_t len;
  char hostname[BUFFER_LEN], *hp;
  int n, count;

  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) {
    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.ident[0]) {
    safe_str(resp.ident, hostname, &hp);
    safe_chr('@', hostname, &hp);
  }
  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, T("[%d/%s/%s] Refused connection."), resp.fd,
             hostname, resp.ipaddr);
    }
    shutdown(resp.fd, 2);
    closesocket(resp.fd);
    return;
  }

  do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Connection opened."), resp.fd,
         hostname, resp.ipaddr);
  set_keepalive(resp.fd);
  initializesock(resp.fd, hostname, resp.ipaddr,
                 (resp.connected_to == SSLPORT));
}

/** Kill the info_slave process, typically at shutdown.
 */
void
kill_info_slave(void)
{
  WAIT_TYPE my_stat;
  pid_t pid;
  struct timeval pad;

  if (info_slave_state != INFO_SLAVE_DOWN) {
    if (info_slave_pid > 0) {
      do_rawlog(LT_ERR, "Terminating info_slave pid %d", info_slave_pid);

      block_a_signal(SIGCHLD);

      closesocket(info_slave);
      kill(info_slave_pid, 15);
      /* Have to wait long enough for the info_slave to actually
         die. This will hopefully be enough time. */
      pad.tv_sec = 0;
      pad.tv_usec = 100;
      select(0, NULL, NULL, NULL, &pad);

      pid = mush_wait(info_slave_pid, &my_stat, WNOHANG);
      info_slave_pid = -1;
      unblock_a_signal(SIGCHLD);
    }
    info_slave_state = INFO_SLAVE_DOWN;
  }
}


#endif                          /* INFO_SLAVE */