/** * \file info_slave.c * * \brief The information slave process. * * When running PennMUSH under Unix, a second process (info_slave) is * started and the server farms out DNS and ident lookups to the * info_slave, and reads responses from the info_slave asynchronously. * Communication between server and slave is by means of a local socket. * */ #include "copyrite.h" #include "config.h" #ifdef WIN32 #error "info_slave is not currently supported on Windows" #endif #include <stdio.h> #include <stdlib.h> #ifdef I_SYS_TYPES #include <sys/types.h> #endif #ifdef I_SYS_SOCKET #include <sys/socket.h> #endif #ifdef I_NETINET_IN #include <netinet/in.h> #endif #include <netdb.h> #include <ctype.h> #include <string.h> #ifdef I_UNISTD #include <unistd.h> #endif #include <sys/uio.h> #include "conf.h" #include "externs.h" #include "ident.h" #include "mysocket.h" #include "confmagic.h" /* Duplicate these, rather than trying to include strutil.o... */ /** Arguments for functions that call APPEND_TO_BUF */ #define APPEND_ARGS int len, blen, clen /** Add string c to buffer buff of max length mlen */ #define APPEND_TO_BUF(mlen) \ /* Trivial cases */ \ if (c[0] == '\0') \ return 0; \ /* The array is at least two characters long here */ \ if (c[1] == '\0') \ return safe_chr(c[0], buff, bp); \ len = strlen(c); \ blen = *bp - buff; \ if (blen > (mlen)) \ return len; \ if ((len + blen) <= (mlen)) \ clen = len; \ else \ clen = (mlen) - blen; \ memcpy(*bp, c, clen); \ *bp += clen; \ return len - clen #ifdef SAFE_CHR_FUNCTION int safe_chr(char c, char *buf, char **bufp) { /* adds a character to a string, being careful not to overflow buffer */ if ((*bufp - buf >= BUFFER_LEN - 1)) return 1; *(*bufp)++ = c; return 0; } #endif int safe_str(const char *c, char *buff, char **bp) { /* copies a string into a buffer, making sure there's no overflow. */ APPEND_ARGS; if (!c || !*c) return 0; APPEND_TO_BUF(BUFFER_LEN); } #undef APPEND_ARGS #undef APPEND_TO_BUF int main(int argc, char *argv[]) { int mush; int port; int fd; union sockaddr_u local, remote; static char buf[BUFFER_LEN]; /* overkill */ char *bp; int len, size; IDENT *ident_result; char host[NI_MAXHOST]; char lport[NI_MAXSERV]; int use_ident, use_dns, timeout; socklen_t llen, rlen; struct iovec dat[3]; if (argc < 2) { fprintf(stderr, "info_slave needs a port number!\n"); return EXIT_FAILURE; } port = atoi(argv[1]); use_ident = 1; if (argc >= 3) { /* The second argument is -1 if we don't want ident used. * Anything else is the timeout. Default is 5 seconds. */ use_ident = atoi(argv[2]); } else use_ident = 5; if (argc >= 4) { /* The third argument is 1 to do DNS lookups, 0 to not. */ use_dns = atoi(argv[3]); } else use_dns = 1; #ifdef HAS_SOCKETPAIR mush = port; /* We inherit open file descriptions and sockets from parent */ #else mush = make_socket_conn("127.0.0.1", NULL, 0, port, NULL); if (mush == -1) { /* Couldn't connect */ fprintf(stderr, "Couldn't connect to mush!\n"); return EXIT_FAILURE; } #endif /* yes, we are _blocking_ */ for (;;) { /* grab a request */ /* First, the address size. */ len = read(mush, &rlen, sizeof rlen); if (len < (int) sizeof rlen) { perror("info_slave reading remote size (Did the mush crash?)"); return EXIT_FAILURE; } /* Now the first address and len of the second. */ dat[0].iov_base = (char *) &remote.data; dat[0].iov_len = rlen; dat[1].iov_base = (char *) &llen; dat[1].iov_len = sizeof llen; size = rlen + sizeof llen; len = readv(mush, dat, 2); if (len < size) { perror("info_slave reading remote sockaddr and local size"); return EXIT_FAILURE; } /* Now the second address and fd. */ dat[0].iov_base = (char *) &local.data; dat[0].iov_len = llen; dat[1].iov_base = (char *) &fd; dat[1].iov_len = sizeof fd; size = llen + sizeof fd; len = readv(mush, dat, 2); if (len < size) { perror("info_slave reading local sockaddr and fd"); return EXIT_FAILURE; } if (!fd) /* MUSH aborted query part way through or only wrote a partial * packet */ continue; bp = buf; if (getnameinfo(&remote.addr, rlen, host, sizeof host, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0) safe_str("An error occured", buf, &bp); else safe_str(host, buf, &bp); safe_chr('^', buf, &bp); if (getnameinfo(&local.addr, llen, NULL, 0, lport, sizeof lport, NI_NUMERICHOST | NI_NUMERICSERV) != 0) safe_str("An error occured", buf, &bp); else safe_str(lport, buf, &bp); safe_chr('^', buf, &bp); if (use_ident > 0) { timeout = use_ident; ident_result = ident_query(&local.addr, llen, &remote.addr, rlen, &timeout); if (ident_result && ident_result->identifier) { safe_str(ident_result->identifier, buf, &bp); safe_chr('@', buf, &bp); } if (ident_result) ident_free(ident_result); } if (use_dns) { if (getnameinfo(&remote.addr, rlen, host, sizeof host, NULL, 0, NI_NUMERICSERV) != 0) { safe_str("An error occured", buf, &bp); } else { safe_str(host, buf, &bp); } } else safe_str(host, buf, &bp); *bp = '\0'; size = strlen(buf); dat[0].iov_base = (char *) &fd; dat[0].iov_len = sizeof fd; dat[1].iov_base = (char *) &size; dat[1].iov_len = sizeof size; dat[2].iov_base = buf; dat[2].iov_len = size; len = writev(mush, dat, 3); size = dat[0].iov_len + dat[1].iov_len + dat[2].iov_len; if (len < size) { perror("info_slave write packet"); return EXIT_FAILURE; } } return EXIT_SUCCESS; }