/* * 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; } }