musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/* 
 * Multiproces, Pipe-Using, Asynchronous Ident Lookup Support
 * Copyright (c) 1998-2003 Abigail Brady
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * The Very Impressive Ident-Lookup Code.
 *
 * probably slightly buggy.
 * 
 * (also, should do an exec() I guess)
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/wait.h>

#include "config.h"
#include "log.h"

#include "ident/ident.h"

#include "pipe-ident.h"

#include "musicio.h"

#include <map>
#include <set>

void try_bans();

namespace ident {
  handler_t handler = 0;

int pfd[2] = { 0, 0};
int rfd[2] = { 0, 0};

  enum reqtype {
    r_Ident,
    r_Rdns,
  };

  //! a request packet for IDENT information
struct req {
  reqtype type;
  int id;
  sockaddr_storage addr;
  int lport;
};

  //! a reply packet containing IDENT information
struct ans {
  reqtype type;
  int id;
  union {
    struct {
      char uid[16];
      char os[16];
      char cset[16];
    } i;
    struct {
      sockaddr_storage addr;
      char host[256];
    } h;
  } u;
};

int ident::dead = 0;

void sendreq(InAddr *addr) {
    if (ident::dead) return;

    struct req msg;
    msg.type = r_Rdns;
    msg.addr = *addr;

    if (write(pfd[1], &msg, sizeof msg)==-1) {
	ident::dead = 1;
    }
}

void sendreq(InAddr *addr, InAddr *laddr, int ticket) {
    if (ident::dead) return;

    struct req msg;
    msg.type = r_Ident;
    msg.id = ticket;
    msg.addr = *addr;
    msg.lport = laddr->get_port();

    if (write(pfd[1], &msg, sizeof msg)==-1) {
	ident::dead = 1;
    }
}

void sstrncpy(char *dest, const char *src, int len) {
    if (!src) {
	dest[0] = 0;
	return;
    }
    strncpy(dest, src, len);
    dest[len-1] = 0;
}

int slave() {
    printf("ident lookup child process : %i\n", getpid());
    while (1) {
	struct req msg;
	struct timeval tv;

	if (read(pfd[0], &msg, sizeof msg)!=(sizeof msg)) {
	    return 0;
	}
	tv.tv_sec = 2;
	tv.tv_usec = 0;

	if (msg.type==r_Rdns) {
	  InAddr *c = InAddr::create(&msg.addr);
	  if (!c)
	    continue;
	  
	  hostent *he = c->gethost();
	  delete c;
	  if (he) {
	    struct ans rep;
	    rep.type = r_Rdns;
	    rep.u.h.addr = msg.addr;
	    strncpy(rep.u.h.host, he->h_name, 255);
	    rep.u.h.host[255] = 0;

	    write(rfd[1], &rep, sizeof rep);
	  }
	}

	if (msg.type==r_Ident) {
	  ident_t *i = id_open(NULL, (struct sockaddr*)&msg.addr, &tv);
	  if (i) {
	    char *id, *opsys, *charset;
	    struct ans rep;
	    rep.type = r_Ident;
	    InAddr *c = InAddr::create(&msg.addr);
	    if (!c)
	      continue;
	    int lport = ntohs(msg.lport);
	    int fport = ntohs(c->get_port());
	    delete c;
	    if (id_query(i, fport, lport, &tv)!=-1) {
	      id_parse(i, &tv, &lport, &fport, &id, &opsys, &charset);
	      rep.id = msg.id;
	      sstrncpy(rep.u.i.uid, id, 16);
	      sstrncpy(rep.u.i.os, opsys, 16); 
	      sstrncpy(rep.u.i.cset, charset, 16);
	      if (strcmp(rep.u.i.uid, "NO-USER") &&
		  strcmp(rep.u.i.uid, "UNKNOWN-ERROR"))
		write(rfd[1], &rep, sizeof rep);
	      id_close(i);
	    } else {
	      id_close(i);
	    }
	  }
	}
    }
    return 0;
}

pid_t id_child;

void init() {
    
    pipe(pfd);
    pipe(rfd);
    
    if (!(id_child=fork())) {
	int i;
	for (i=3;i<1024;i++) {
	    if (i!=pfd[0] && i!=pfd[1] && i!=rfd[0] && i!=rfd[1]) close(i);
	}

	exit(slave());
    }

    xreg(pfd[1], "ident outgoing");
    xreg(rfd[0], "ident incoming");
    
    close(pfd[0]);
    close(rfd[1]);

}

  std::map<string, string> cache;
  std::set<string> requests;
  
  string atop(const sockaddr_storage &addr) {
    InAddr *a = InAddr::create(&addr);
    if (!a)
      return "????";
    string s = a->tostring();
    delete a;
    return s;
  }

void poll() {
  while (1) {
    int bytes;
    ioctl(rfd[0], FIONREAD, &bytes);
    if (bytes && ((bytes%sizeof(struct ans))==0)) {
	struct ans msg;

	if (read(rfd[0], &msg, sizeof msg)!=(sizeof msg)) {
	    return;
	}
	
	if (msg.type == r_Ident && ident::handler)
	    ident::handler(msg.id, msg.u.i.uid);
	
	if (msg.type == r_Rdns) {
	  cache[atop(msg.u.h.addr)] = msg.u.h.host;

	  //	  log(PFL_HOSTS, 0, "net", "%s is %s", atop(msg.u.h.addr),
	  //	      msg.u.h.host);
	  try_bans();
	}
    } else
      return;
  }
}

void set_handler(ident::handler_t h) {
    ident::handler = h;
}

int ticket = 0;

int ident::lookup(InAddr *addr, InAddr *laddr) {
    ticket++;
    if (pfd[0])
        sendreq(addr, laddr, ticket);
    return ticket;
}

void done() {
    int status;
    kill(id_child, SIGKILL);
    wait(&status);
    close(pfd[1]);
    close(rfd[0]);
}

  string dnslookup(InAddr *addr) {
    string id = addr->tostring();
    if (cache.find(id)!=cache.end()) {
      return cache[id];
    }

    if (requests.find(id)!=requests.end())
      return "";

    if (pfd[0])
      sendreq(addr);
    
    requests.insert(id);

    return "";
  }

  string dnslookup(const string &host) {
    if (cache.find(host)!=cache.end()) {
      return cache[host];
    }

    in_addr i;
    if (inet_aton(host.c_str(), &i)) {
      sockaddr_in sa;
      sa.sin_family = AF_INET;
      sa.sin_addr = i;
      InAddr *a = InAddr::create((sockaddr*)&sa);
      dnslookup(a);
    }

    return host;
  }
}