proxycheck-0.49a/0040775000175000017500000000000010056124714012142 5ustar mjtmjtproxycheck-0.49a/proxycheck.c0100644000175000017500000006706410055456511014477 0ustar mjtmjt/* $Id: proxycheck.c,v 1.23 2004/05/27 21:27:37 mjt Exp $ * open proxy checker, main program. * Michael Tokarev . * This code may be freely used and distributed according to * the terms of General Public License (GPL) version 2 or later. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "event.h" #include "pxy.h" typedef struct { const pxyproto_t *proto; /* proxy protocol to use */ ipport_t port; /* port to connect to */ } pxyprotoport_t; typedef struct pxyhost { struct in_addr addr; /* IP address of proxy */ pxyprotoport_t *pps; /* array of proto:ports to test */ pxyprotoport_t *cpp; /* next proto:port to test */ int nopen; /* numbef of open ports found */ int nactive; /* number of active connections */ struct pxyhost *next; /* next host to test */ } pxyhost_t; static char *progname; static int verbose; /* verbosity level */ static int maxconn; /* max number of connections */ static int maxhconn; /* max number of connection to one host */ static int stopfound; /* stop searching on first open port */ struct sockaddr_in baddr; /* bind address */ static pxyhost_t *hosts; /* host list */ static int nhosts; /* number of hosts to check */ static pxyhost_t *chost; /* current host */ static pxyprotoport_t *pps; /* default array of protos:ports */ static int ntodo; /* number of hosts and total ports */ static int nactive; /* number of active connections */ static int nhopen, npopen; /* number of open proxies found (hosts/ports) */ static long nread; /* number of bytes read */ static char *dstspec; /* destination specification */ static struct in_addr dstaddr; /* destination address */ static ipport_t dstport; /* destination port number */ static int printclosed; /* print a line about closed proxies */ static int extinfo; /* print extended info */ static int advanced; /* use advanced protos as well */ typedef struct { char *name; void (*connh)(pxyconn_t *c, int e); int (*getdata)(pxyconn_t *c); void (*exph)(pxyconn_t *c, int e); int (*init)(char *arg); char *arg; char *descr; } pxycheck_t; extern const pxycheck_t checks[]; static const pxycheck_t *check; static void PRINTFLIKE(2,3) err(int errnum, const char *fmt, ...) { char buf[1024]; int l = sprintf(buf, "%s: ", progname); va_list ap; va_start(ap, fmt); l += vsprintf(buf + l, fmt, ap); va_end(ap); if (errnum) l += sprintf(buf + l, ": %s\n", strerror(errno)); else buf[l++] = '\n'; write(2, buf, l); exit(1); } void PRINTFLIKE(3,4) pxyinfo(const pxyconn_t *c, int level, const char *fmt, ...) { if (level <= verbose) { char buf[512]; int len; va_list ap; if (c) len = sprintf(buf, "%s:%s:%d: ", inet_ntoa(c->pxyaddr), c->proto->aname, c->pxyport); else len = 0; va_start(ap, fmt); len += vsprintf(buf + len, fmt, ap); va_end(ap); buf[len++] = '\n'; write(2, buf, len); } } void pxyvio(pxyconn_t *c, int level, int direction, const char *s, int len) { if (level <= verbose) { char buf[90]; char *bs, *bp; const char *e = s + len; char *const be = buf + sizeof(buf) - 1 - 4; bs = buf + sprintf(buf, "%s:%s:%d: %s ", inet_ntoa(c->pxyaddr), c->proto->aname, c->pxyport, direction > 0 ? ">>" : "<<"); bp = bs; #define flushb() \ do { *bp++ = '\n'; write(2, buf, bp - buf); bp = bs; } while(0) while(s < e) { if (bp >= be) flushb(); switch(*s) { case '\n': *bp++ = '\\'; *bp++ = 'n'; break; case '\r': *bp++ = '\\'; *bp++ = 'r'; break; case '\t': *bp++ = '\\'; *bp++ = 't'; break; case '\\': *bp++ = '\\'; *bp++ = '\\'; break; default: if (*s < ' ' || *s >= 0177) bp += sprintf(bp, "\\%o", (unsigned char)*s); else *bp++ = *s; } if (*s == '\n') flushb(); ++s; } if (bp > bs) flushb(); } #undef flushb } void *emalloc(unsigned size) { void *ptr = malloc(size); if (!ptr) err(0, "out of memory (%d bytes)", size); return ptr; } void *erealloc(void *ptr, unsigned size) { ptr = realloc(ptr, size); if (!ptr) err(0, "out of memory (%d bytes)", size); return ptr; } static int satoi(const char *s) { int c = 0; if (*s < '0' || *s > '9') return -1; do c = c * 10 + (*s++ - '0'); while (*s >= '0' && *s <= '9'); return *s ? -1 : c; } static int hostaddr(const char *s, struct in_addr *a) { if (!inet_aton(s, a)) { struct hostent *he = gethostbyname(s); if (!he) return 0; if (he->h_addrtype != AF_INET || he->h_length != 4) return 0; memcpy(&a->s_addr, he->h_addr_list[0], 4); } return 1; } static ipport_t portnum(const char *s) { int p = satoi(s); return p < 1 || p > 0xffff ? 0 : (ipport_t)p; } static pxyhost_t * findhost(register struct in_addr addr) { register pxyhost_t *h; for (h = hosts; h; h = h->next) if (h->addr.s_addr == addr.s_addr) return h; h = (pxyhost_t*) emalloc(sizeof(*h)); memset(h, 0, sizeof(*h)); h->addr = addr; h->next = hosts; hosts = h; return h; } static void donehost(pxyhost_t *h) { pxyhost_t *hp; pxyinfo(NULL, 4, "%s: done, numopen=%d", inet_ntoa(h->addr), h->nopen); if (h->pps != pps) free(h->pps); if (h == hosts) hosts = h->next; else { hp = hosts; while(hp->next != h) hp = hp->next; hp->next = h->next; } hp = h->next; if (chost == h) chost = hp ? hp : hosts; free(h); --nhosts; } static void PRINTFLIKE(1,2) usage(const char *fmt, ...) { char buf[256]; int l = sprintf(buf, "%s: ", progname); va_list ap; va_start(ap, fmt); l += vsprintf(buf + l, fmt, ap); va_end(ap); l += sprintf(buf + l, "\n%s: `%s -h' for help\n", progname, progname); write(2, buf, l); exit(1); } /* parse list of proto/ports */ static pxyprotoport_t * addprotoport(pxyprotoport_t *pp, ipport_t port, const pxyproto_t *proto) { pxyprotoport_t *p; if (!pp) { pp = (pxyprotoport_t*)emalloc(sizeof(*pp) * 5); p = pp; } else { int n; for (n = 0; pp[n].port; ++n) if (pp[n].port == port && pp[n].proto == proto) return pp; if (!(n & 3)) pp = (pxyprotoport_t*)erealloc(pp, sizeof(*pp) * (n + 5)); p = pp + n; } p->port = port; p->proto = proto; p[1].port = 0; p[1].proto = NULL; return pp; } static pxyprotoport_t * parseprotoport(pxyprotoport_t *pp, char *s) { char *p; const pxyprobe_t *probe; const ipport_t *portp; ipport_t port; int n, found; static const char *delims = ",|/; \t"; if ((p = strchr(s, ':')) != NULL) { const pxyproto_t *proto; *p = '\0'; proto = pxyprotos; while(strcmp(proto->name, s) != 0 && strcmp(proto->aname, s) != 0) if (!(++proto)->name) usage("invalid protocol `%s'", s); *p++ = ':'; found = 0; while(*p) { if (strchr(delims, *p)) { ++p; continue; } s = p; n = 0; while(*p >= '0' && *p <= '9' && p < s + 5) n = n * 10 + (*p++ - '0'); if (!n || n > 0xffff) usage("invalid port specification near `%s'", s); ++found; pp = addprotoport(pp, n, proto); } if (!found) { probe = pxyprobes; while(probe->proto) { if (probe->proto == proto) { for(portp = probe->ports; *portp; ++portp) pp = addprotoport(pp, *portp, proto); if (probe->advanced >= advanced) break; } ++probe; } } } else { /* no protocol specified */ p = s; while(*p) { if (strchr(delims, *p)) { ++p; continue; } s = p; n = 0; while(*p >= '0' && *p <= '9' && p < s + 5) n = n * 10 + (*p++ - '0'); if (!n || n > 0xffff) usage("invalid port specification near `%s'", s); port = n; found = 0; probe = pxyprobes; while(probe->proto && (!found || probe->advanced <= advanced)) { for(portp = probe->ports; *portp; ++portp) if (*portp == port) { pp = addprotoport(pp, port, probe->proto); found = 1; break; } ++probe; } if (!found) for (probe = pxyprobes; probe->proto; ++probe) if (probe->advanced <= advanced) pp = addprotoport(pp, port, probe->proto); } } return pp; } static int numprotoports(const pxyprotoport_t *pp) { const pxyprotoport_t *ppp = pp; while(ppp->port) ++ppp; return ppp - pp; } static void addent(char *p) { char *n = p; struct in_addr addr; pxyhost_t *h; if ((p = strchr(n, ':'))) *p = '\0'; if (!hostaddr(n, &addr)) usage("invalid IP address `%s'", n); h = findhost(addr); if (p) { *p++ = ':'; if (*p) h->pps = parseprotoport(h->pps, p); else h->nopen = 1; /* remember to add std pps */ } else h->nopen = 1; /* remember to add std pps */ } static void init(int argc, char **argv) { int c; int timeout = 0; pxyhost_t *h; const pxyproto_t *proto; const pxyprobe_t *probe; const ipport_t *portp; pxyprotoport_t *pp; char *p; int npps; const char *readin = NULL; int add_defaults = 0; char *check_arg = NULL; if ((progname = strrchr(argv[0], '/')) != NULL) argv[0] = ++progname; else progname = argv[0]; while((c = getopt(argc, argv, "vd:c:p:Db:t:m:M:i:nasxh")) != EOF) switch(c) { case 'v': ++verbose; break; case 'p': pps = parseprotoport(pps, optarg); break; case 'd': dstspec = optarg; break; case 'D': ++add_defaults; break; case 'c': if ((p = strchr(optarg, ':')) != NULL) *p = '\0'; for(check = checks; ; ++check) if (!check->name) usage("unknown check `%s'", optarg); else if (strcmp(optarg, check->name) == 0) break; if (p) check_arg = p + 1, *p = ':'; else check_arg = NULL; break; case 'b': if (!hostaddr(optarg, &baddr.sin_addr)) usage("unknown host `%s'", optarg); baddr.sin_family = AF_INET; break; case 't': if ((timeout = satoi(optarg)) < 1 || timeout > 1000) usage("invalid timeout `%s'", optarg); break; case 'm': if ((maxconn = satoi(optarg)) < 1) usage("invalid maximum number of connections `%s'", optarg); break; case 'M': if ((maxhconn = satoi(optarg)) < 0) usage("invalid maximum number of connections to one host `%s'", optarg); break; case 'i': readin = optarg; break; case 'n': printclosed = 1; break; case 'a': ++advanced; break; case 's': ++stopfound; break; case 'x': extinfo = 1; break; case 'h': printf( "%s: Open proxy checker version " VERSION_STR "\n" "Usage is: `%s options host[:proto_port_spec]...'\n" "where options are:\n" " -h - print this help and exit\n" " -d dsthost:dstport - destination to connect to (required)\n" " -c check[:params] - method to check proxy (required, see below)\n" " -p proto_port_spec - proxy port/protocol specification\n" " (may be used more than once) - see below\n" " -D - do not reset default portlist in case -p option specified\n" " -a - use \"advanced\" protocols too (more -a's means more advanced)\n" " -t timeout - general timeout in secounds, default %d\n" " -m maxconn - maximum number of parallel connections\n" " -M maxhconn - maximum number of parallel connections to one host\n" " -s - stop probing a host after first found open proxy\n" " -b bindaddr - bind to specified address\n" " -x - print extended info (proxy software etc) if known\n" " -n - also print a line about definitely closed proxies\n" "\n" "proto_port_spec is in the form [proto:][port,port,...].\n" "If portlist is omitted, default ports for given protocols\n" "will be tried; if proto is omitted, either all protocols will\n" "be tried (if port is not known), or the protocols which are\n" "assotiated with this port.\n" "\n" "The following protocols are recognized:\n" , progname, progname, pxytimeout / 1000); for(proto = pxyprotos; proto->name; ++proto) printf(" %s (%s, %s, %s)\n", proto->aname, proto->name, proto->transport, proto->fullname); printf("\nThe following probes are made (level cf. -a):\n"); for(probe = pxyprobes; probe->proto; ++probe) { printf(" %s (level %d): ", probe->proto->aname, probe->advanced); for(portp = probe->ports;; ) { printf("%d", *portp++); putchar(*portp ? ',' : '\n'); if (!*portp) break; } } printf("\nThe following checks are available:\n"); for(check = checks; check->name; ++check) printf(" %s%s - %s", check->name, check->arg ? check->arg : "", check->descr); exit(0); default: err(0, "`%s -h' for help", progname); } argc -= optind; argv += optind; if (!*argv && !readin) usage("no host(s) to check specified"); if (!check) usage("no action (-c) specified"); while(*argv) addent(*argv++); if (readin) { FILE *f; char buf[8192]; if (readin[0] == '-' && readin[1] == '\0') f = stdin; else if ((f = fopen(readin, "r")) == NULL) err(errno, "unable to open %s", readin); while(fgets(buf, sizeof(buf), f)) { char *p = buf; char *e; while(*p == ' ' || *p == '\t') ++p; if (*p == '#' || *p == '\n' || !*p) continue; if ((e = strchr(p, '\n')) != NULL) *e = '\0'; addent(p); } if (f != stdin) fclose(f); } if (getuid() == 0 || geteuid() == 0) pxyinfo(NULL, 0, "warning: do not run this program as root"); check->init(check_arg); if (!dstspec) /* dstspec may be set in check->init() */ usage("no destination (-d) specified"); if (!(p = strchr(dstspec, ':'))) usage("destination port missing in `%s'", dstspec); *p = '\0'; if (!hostaddr(dstspec, &dstaddr)) usage("unknown destination host `%s'", dstspec); if (!(dstport = portnum(p+1))) usage("invalid destination port `%s'", p+1); *p = ':'; if (!pps || add_defaults) { /* no explicit ports given, pick up defaults */ for (probe = pxyprobes; probe->proto; ++probe) if (probe->advanced <= advanced) for (portp = probe->ports; *portp; ++portp) pps = addprotoport(pps, *portp, probe->proto); } npps = numprotoports(pps); for (h = hosts; h; h = h->next) { ++nhosts; if (!h->pps) h->pps = pps, ntodo += npps; else { if (h->nopen) { for (pp = pps; pp->port; ++pp) h->pps = addprotoport(h->pps, pp->port, pp->proto); } ntodo += numprotoports(h->pps); } h->nopen = 0; h->cpp = h->pps; #if 0 if (verbose) { pxyprotoport_t *pp; printf("%s:", inet_ntoa(h->addr)); for(pp = h->pps; pp->port; ++pp) printf(" %s:%d", pp->proto->name, pp->port); putchar('\n'); } #endif } pxyinfo(NULL, 1, "To check: hosts=%d, proto:ports=%d, host:proto:ports=%d", nhosts, npps, ntodo); if (timeout) pxytimeout = timeout * 1000; chost = hosts; signal(SIGPIPE, SIG_IGN); } static int nextconn(void) { pxyconn_t *c; pxyprotoport_t *pp; pxyhost_t *h; int fd; struct sockaddr_in sin; if (!ntodo) return 0; if (maxconn && nactive >= maxconn) return 0; /* find next host to connect to */ h = chost; fd = 0; do { if (h->cpp->port && (!maxhconn || h->nactive < maxhconn)) { fd = 1; break; } if (!(h = h->next)) h = hosts; } while (h != chost); if (!fd) return 0; fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { if (errno != EMFILE) err(errno, "unable to create socket"); if (!maxconn || maxconn > nactive) { pxyinfo(NULL, 1, "limiting max number of connections to %d", nactive); maxconn = nactive; } return 0; } pp = h->cpp++; if (!(chost = h->next)) /* move to next host to distribute load */ chost = hosts; --ntodo; if (baddr.sin_addr.s_addr && bind(fd, (struct sockaddr*)&baddr, sizeof(baddr)) < 0) err(errno, "unable to bind to %s", inet_ntoa(baddr.sin_addr)); if (fcntl(fd, F_SETFL, O_NONBLOCK|O_RDWR) < 0) /*fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)*/ err(errno, "unable to set socket flags"); c = pxynew(); if (!c) abort(); c->pxyaddr = h->addr; c->pxyport = pp->port; c->proto = pp->proto; c->dstaddr = dstaddr; c->dstport = dstport; c->data = h; ++h->nactive; ++nactive; /* from now on, we should expect to be called recursively: * do not touch chost and pp anymore */ if (!pxystart(c, fd)) err(errno, "unable to enqueue testing request"); sin.sin_family = AF_INET; sin.sin_addr = h->addr; sin.sin_port = htons(pp->port); if (connect(fd, (struct sockaddr*)&sin, sizeof(sin)) != 0 && errno != EINPROGRESS) pxyaction(c, -1); return 1; } static void freeconn(pxyconn_t *c) { int fd = c->fd; pxyhost_t *h = (pxyhost_t *)c->data; nread += c->nread; --h->nactive; --nactive; pxyinfo(c, 5, "done, nactive=%d ntodo=%d", nactive, ntodo); pxyfree(c); close(fd); if (stopfound && h->nopen && h->cpp->port) { /*XXX abort active connections: need a list of * all connections for this host, and a state of * each: in case of e.g. dsbl, connection may be * in final stage and it isn't a good idea to simple * abort connection in this case. * Here, we just will not do any more *new* connections. */ int skip = 0; do { ++h->cpp; ++skip; } while(h->cpp->port); ntodo -= skip; pxyinfo(NULL, 3, "%s: skipping %d other probes for this host (%d active)", inet_ntoa(h->addr), skip, h->nactive); } if (!h->nactive && !h->cpp->port) donehost(h); while(nextconn()) ; } static void isopen(pxyconn_t *c, int is_open, const char *info) { pxyhost_t *h = (pxyhost_t *)c->data; if (is_open || printclosed) { char buf[8192]; /* do not use pxybuf due to info pointing to it */ int l = sprintf(buf, "%s %s:%d ", inet_ntoa(c->pxyaddr), c->proto->aname, c->pxyport); if (is_open) { l += sprintf(buf + l, "open"); if (info) l += sprintf(buf + l, " %s", info); } else l += sprintf(buf + l, "closed"); if (extinfo && c->detail) l += sprintf(buf + l, " [%s]", c->detail); buf[l++] = '\n'; write(1, buf, l); } if (is_open) { if (!(h->nopen++)) ++nhopen; ++npopen; } freeconn(c); } void pxyaction(pxyconn_t *c, int result) { if (!result) { if (pxyreqiot(c, EV_IN, check->connh, 0, NULL)) check->connh(c, EV_IN); } else { if (result < 0) pxyinfo(c, c->pxystate ? 3 : 4, c->pxystate ? "%s" : "connect: %s", errno ? strerror(errno) : "EOF"); else if (result > 1) { isopen(c, 0, NULL); return; } /*else pxyinfo(c, 3, "seems not to be open");*/ freeconn(c); } } void pxycheckdata(pxyconn_t *c) { if (pxyreqio(c, EV_IN, check->exph)) check->exph(c, EV_IN); } int pxygetdata(pxyconn_t *c) { return check->getdata(c); } int main(int argc, char **argv) { time_t start; init(argc, argv); if (ev_init(0, EV_ADVANCED|EV_SELECT) != 0) err(errno, "ev_init"); start = time(NULL); while(nextconn()) ; while(nactive) if (ev_wait(0, -1) < 0 && errno != EINTR) err(errno, "ev_wait"); pxyinfo(NULL, 1, "NumOpen=%d(%d) NRead=%ld Time=%d", nhopen, npopen, nread, (int)(time(NULL) - start)); return nhopen ? 100 : 0; } static char *findip(pxyconn_t *c, char *buf) { /* look at possible IP address in a form [ip.add.re.ss] */ unsigned o1,o2,o3,o4; int l; while(*buf >= ' ' && *buf != '[') ++buf; if (*buf == '[' && sscanf(++buf, "%3u.%3u.%3u.%3u%n]", &o1,&o2,&o3,&o4, &l) == 4 && o1 && o1 < 256 && o2 < 256 && o3 < 256 && o4 < 256 && ((o1<<24)|(o2<<16)|(o3<<8)|o4) != ntohl(c->pxyaddr.s_addr)) { buf[l] = '\0'; return buf; } return NULL; } static char *sendstr; /* send this string... */ static char *expstr; /* and expect this */ static unsigned explen; /* length of expstr */ static int expectd(pxyconn_t *c) { return sendstr ? sprintf(pxybuf, "%s\r\n", sendstr) : sprintf(pxybuf, "%s:%s:%d\r\n", c->proto->aname, inet_ntoa(c->pxyaddr), c->pxyport); } static void expectw(pxyconn_t *c, int UNUSED e) { int l; char *p; if (pxyreadnext(c, 1, &l, 3) <= 0) ; else if (c->proto->check && c->proto->check(c, pxybuf, l)) return; else if ((p = (char*)memmem(pxybuf, l, expstr, explen)) != NULL) /* look at possible IP address after the expect string */ isopen(c, 1, findip(c, p + explen)); else /* if (!c->proto->check || !c->proto->check(c, pxybuf, l)) */ pxysave(c, pxybuf, l, explen); } static void expecth(pxyconn_t *c, int UNUSED e) { if (pxywrite(c, pxybuf, expectd(c), 3)) pxyreqio(c, EV_IN, expectw); } static int expecti(char *arg) { char *p = strchr(arg, ':'); if (!p || !p[1]) usage("send:expect strings expected"); if (p != arg) { sendstr = arg; *p++ = '\0'; arg = p; } else ++arg; if (!(expstr = arg) || !(explen = strlen(arg))) usage("specify a string to expect"); else if (explen >= sizeof(((pxybuf_t*)0)->buf)) usage("expect string is too long"); return 0; } #define DSBL_COOKIE_PORT 200 #define DSBL_COOKIE_LEN 32 static char dsblcookie[DSBL_COOKIE_LEN+1]; static char *dsbluser, *dsblpass, *dsblrcpt, *dsblfrom; static int dsblmsg(char *buf, pxyconn_t *c) { char pxyaddr[sizeof("255.255.255.255")]; int l; strcpy(pxyaddr, inet_ntoa(c->pxyaddr)); l = sprintf(buf, "Message-ID: <%s@%s>\r\n" "To: <%s>\r\n" "Subject: Open %s Proxy test message\r\n" "\r\n" "DSBL LISTME: %s [%s]:%d\r\n" "%s\r\n" "Connect to %s:%d\r\n", dsblcookie, dsblfrom, /* Message-ID */ dsblrcpt, /* To: */ c->proto->fullname, /* Subj */ c->proto->name, pxyaddr, c->pxyport, /* dsbl listme */ dsblcookie, /* dsbl cookie */ inet_ntoa(c->dstaddr), c->dstport); if (c->detail) l += sprintf(buf + l, "Proxy info: %s\r\n", c->detail); l += sprintf(buf + l, "DSBL END\r\n\r\n.\r\n"); return l; } static int dsbld(pxyconn_t *c) { int l = sprintf(pxybuf, "HELO [%s]\r\n" "MAIL FROM:<%s>\r\n" "RCPT TO:<%s>\r\n" "DATA\r\n", inet_ntoa(c->pxyaddr), dsblfrom, dsblrcpt); l += dsblmsg(pxybuf + l, c); l += sprintf(pxybuf + l, "QUIT\r\n"); return l; } static void dsblo(pxyconn_t *c, char *line) { isopen(c, 1, memcmp(line, "250 listed [", 12) ? NULL : findip(c, line + 11)); } static void dsble(pxyconn_t *c, int UNUSED e) { int l; char *s; /* state: * 0 - connected, 2xx helo * 1 - 2xx mail from * 2 - 2xx rcpt to * 3 - 3xx data * 4 - 2xx ok */ if (pxyreadnext(c, 1, &l, 3) <= 0) return; if (c->proto->check && c->proto->check(c, pxybuf, l)) return; s = pxybuf; for(;;) { char *n = (char*)memmem(s, l, c->appstate == 3 ? "\r\n3" : "\r\n2", 3); if (!n) break; n += 2; l -= n - s; s = n; if (c->appstate++ == 4) { dsblo(c, n); return; } } pxysave(c, s, l, 0); } static void dsblh(pxyconn_t *c, int UNUSED e) { /* state: * 0 - connected, send helo * 1 - wait for initial 2xx reply, send MAIL FROM * 2 - wait for 2xx to MAIL FROM, send RCPT TO * 3 - wait for 2xx to RCPT TO, send DATA * 4 - wait for 3xx to DATA, send message * 5 - wait for final 2xx reply */ int l; char *p; switch(c->appstate) { case 0: /* connected, send helo */ if (pxyprintf(c, 3, "HELO [%s]\r\n", inet_ntoa(c->pxyaddr))) c->appstate = 1; break; case 1: /* wait reply to HELO, send MAIL FROM */ case 2: /* wait reply to MAIL, send RCPT TO */ case 3: /* wait reply to RCPT, send DATA */ if (pxyreadnext(c, 1, &l, 3) <= 0) ; else if ((p = (char*)memmem(pxybuf, l, "\r\n2", 3)) /*&& p[5] == ' '*/) { l = c->appstate == 1 ? sprintf(pxybuf, "MAIL FROM:<%s>\r\n", dsblfrom) : c->appstate == 2 ? sprintf(pxybuf, "RCPT TO:<%s>\r\n", dsblrcpt) : sprintf(pxybuf, "DATA\r\n"); if (pxysave(c, pxybuf, l, 10) && pxywrite(c, pxybuf, l, 3) && pxyrenew(c, pxytimeout/2, NULL)) c->appstate += 1; } else if (c->appstate != 1 || !c->proto->check || !c->proto->check(c, pxybuf, l)) pxysave(c, pxybuf, l, 10); break; case 4: /* wait reply to DATA, send message */ if (pxyreadnext(c, 1, &l, 3) <= 0) ; else if ((p = (char*)memmem(pxybuf, l, "\r\n3", 3)) /*&& p[3] == ' '*/) { if (verbose < 6) pxyinfo(c, 2, "sending message"); if (pxywrite(c, pxybuf, dsblmsg(pxybuf, c), 6) && pxywrite(c, "QUIT\r\n", 6, 3) && pxysave(c, NULL, 0, 0) && pxyrenew(c, 0, NULL)) c->appstate = 5; } else pxysave(c, pxybuf, l, 10); break; default: /* wait final data ack */ if (pxyreadnext(c, 1, &l, 3) <= 0) ; else if (pxybuf[0] == '2' && pxybuf[3] == ' ') dsblo(c, pxybuf); else pxysave(c, pxybuf, l, 10); } } static char *egetenv(const char *name, char *def) { char *v = getenv(name); return v ? v : def; } static void dsbl_cookie_alarm(int UNUSED sig) { err(0, "unable to obtain cookie: timeout"); } static int dsbli(char *arg) { if (!dstspec) dstspec = (arg && *arg) ? arg : egetenv("DSBL_SMTP", "mx.listme.dsbl.org"); if (!strchr(dstspec, ':')) { sprintf(pxybuf, "%s:25", dstspec); dstspec = strdup(pxybuf); } dsbluser = egetenv("DSBL_USER", "anonimous"); dsblpass = egetenv("DSBL_PASS", ""); dsblfrom = egetenv("DSBL_FROM", dsbluser); dsblrcpt = egetenv("DSBL_RCPT", "listme@listme.dsbl.org"); if (!(arg = getenv("DSBL_COOKIE"))) { char *chost = egetenv("DSBL_COOKIE_HOST", "cookie.dsbl.org"); struct sockaddr_in sin; int fd; int l; sin.sin_family = AF_INET; sin.sin_port = htons(DSBL_COOKIE_PORT); if (!hostaddr(chost, &sin.sin_addr)) err(0, "unknown DSBL cookie host %s", chost); signal(SIGALRM, dsbl_cookie_alarm); alarm(3*60); if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 || connect(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) err(errno, "unable to connect to cookie server"); l = sprintf(pxybuf, "%s\n%s\n", dsbluser, dsblpass); if (send(fd, pxybuf, l, 0) < 0 || recv(fd, dsblcookie, DSBL_COOKIE_LEN, 0) != DSBL_COOKIE_LEN) err(errno, "unable to obtain cookie"); alarm(0); close(fd); dsblcookie[DSBL_COOKIE_LEN] = 0; } else if (strlen(arg) != DSBL_COOKIE_LEN) usage("invalid dsbl cookie `%s'", arg); else strcpy(dsblcookie, arg); pxyinfo(NULL, 4, "dsbl cookie: %s", dsblcookie); return 0; } const pxycheck_t checks[] = { { "chat", expecth, expectd, expectw, expecti, ":sendstr:expectstr", "perform a little chat: send `sendstr'\n" " to the remote system and assume proxy is open is `expectstr'\n" " is returned. `sendstr' defaults to proto:ip:port\n" }, { "dsbl", dsblh, dsbld, dsble, dsbli, "[:smtpserver[:port]]", "attempt to submit proxy to DSBL-like system\n" " DSBL settings are expected to be in environment:\n" "\t$DSBL_USER - username (anonimous)\n" "\t$DSBL_PASS - password (default is empty)\n" "\t$DSBL_COOKIE_HOST - cookie server (cookie.dsbl.org)\n" "\t$DSBL_COOKIE - already obtained DSBL cookie\n" "\t$DSBL_RCPT - recipient (listme@listme.dsbl.org)\n" "\t$DSBL_FROM - sender address (nobody)\n" "\t$DSBL_SMTP - smtp server if -d not given (mx.listme.dsbl.org)\n" }, {0,0,0,0,0,0,0} }; proxycheck-0.49a/pxy.c0100644000175000017500000004304510055456013013126 0ustar mjtmjt/* $Id: pxy.c,v 1.19 2004/05/27 14:18:43 mjt Exp $ * open proxy checker, proxy protocol routines. * Michael Tokarev . * This code may be freely used and distributed according to * the terms of General Public License (GPL) version 2 or later. */ #include #include #include #include #include #include #include #include #include #include "event.h" #include "pxy.h" int pxytimeout = 30000; /* 30 sec */ char pxybuf[8193]; static void timedout(pxyconn_t *c) { errno = ETIMEDOUT; pxyaction(c, -1); } int pxyrenew(pxyconn_t *c, int tmo, void (*tmfn)(pxyconn_t *)) { if (!tmfn) tmfn = timedout; ev_tm_del(0, &c->timer); if (ev_tm_add(0, &c->timer, tmo ? tmo : pxytimeout, (ev_tm_cbck_f*)tmfn, c) != NULL) return 1; else { pxyaction(c, -1); return 0; } } pxyconn_t *pxynew() { pxyconn_t *c = (pxyconn_t *)malloc(sizeof(pxyconn_t)); if (c) memset(c, 0, sizeof(*c)); return c; } void pxyfree(pxyconn_t *c) { ev_tm_del(0, &c->timer); if (c->fd >= 0) ev_io_del(0, c->fd); if (c->detail) free(c->detail); if (c->buf) { free(c->buf); c->buf = NULL; } free(c); } int pxyreqio(pxyconn_t *c, int e, void(*iofn)(pxyconn_t*,int)) { if (ev_io_mod(0, c->fd, e, (ev_io_cbck_f*)iofn, c) == 0) return 1; else { pxyaction(c, -1); return 0; } } int pxyreqiot(pxyconn_t *c, int e, void(*iofn)(pxyconn_t*,int), int tmo, void(*tmfn)(pxyconn_t*)) { return pxyreqio(c, e, iofn) && pxyrenew(c, tmo, tmfn); } int pxywrite(pxyconn_t *c, const char *buf, int len, int level) { int r; if ((r = write(c->fd, buf, len)) <= 0) { pxyaction(c, r < 0 ? -1 : 1); return 0; } else { pxyvio(c, level, 1, buf, len); return 1; } } int pxyprintf(pxyconn_t *c, int loglevel, const char *fmt, ...) { va_list ap; int l; va_start(ap, fmt); l = vsprintf(pxybuf, fmt, ap); va_end(ap); return pxywrite(c, pxybuf, l, loglevel); } int pxystart(pxyconn_t *c, int fd) { c->fd = fd; if (ev_io_add(0, fd, EV_OUT, (ev_io_cbck_f*)c->proto->handler, c) != 0) { pxyaction(c, -1); return 0; } else return pxyrenew(c, pxytimeout/2, NULL); } int pxyread(pxyconn_t *c, char *buf, int l, int minlen, int level) { l = read(c->fd, buf, l); if (l > 0) { pxyvio(c, level, 0, buf, l); c->nread += l; } else if (l < 0) { if (minlen || errno != EAGAIN) { pxyaction(c, -1); return -1; } else l = 0; } else { errno = 0; pxyaction(c, -1); return -1; } buf[l] = '\0'; return l; } int pxyreadnext(pxyconn_t *c, int minlen, int *tlen, int loglevel) { int l, r; if (c->buf && (l = c->buf->len) != 0) memcpy(pxybuf, c->buf->buf, l); else l = 0; r = pxyread(c, pxybuf + l, sizeof(pxybuf) - l - 1, minlen, loglevel); if (r >= 0 && tlen) *tlen = r + l; return r; } int pxysave(pxyconn_t *c, char *buf, unsigned len, unsigned max) { if (c->nread > 2048) { pxyinfo(c, 2, "too much input"); pxyaction(c, 2); return 0; } if (buf && len > 0) { if (!c->buf && !(c->buf = (pxybuf_t*)malloc(sizeof(pxybuf_t)))) { errno = ENOMEM; pxyaction(c, -1); return 0; } if (len > sizeof(c->buf->buf)) { buf += len - sizeof(c->buf->buf); len = sizeof(c->buf->buf); } if (max && len > max) { buf += len - max; len = max; } memcpy(c->buf->buf, buf, len); c->buf->len = len; } else if (c->buf) c->buf->len = 0; return 1; } /* find the string `str' in memory block, case-insensitive * (`str' should be lowercased) */ char *memcfind(const char *buf, int l, const char *str) { const char *s, *b, *e; for (e = buf + l - strlen(str); buf <= e; ++buf) { #define lc(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c)) if (lc(*buf) != *str) continue; s = str + 1; b = buf + 1; for(;;) { if (!*s) return (char*)buf; if (*s != lc(*b)) break; ++s; ++b; } } return NULL; } /* parse HTTP headers */ static int httpheaders(pxyconn_t *c, char *buf, int l) { char *a; int r; if (memcmp(buf, "HTTP/", 5) != 0 || !(a = strchr(buf, ' ')) || a > buf + 8 || a[1] < '1' || a[1] > '9' || a[2] < '0' || a[2] > '9' || a[3] < '0' || a[3] > '9' || a[4] != ' ') return 0; r = (a[1] - '0') * 100 + (a[2] - '0') * 10 + (a[3] - '0'); /* find Proxy-agent:, Via: headers */ if ((a = memcfind(buf, l, "\nproxy-agent: ")) || (a = memcfind(buf, l, "\nvia: "))) { int via; char *e; ++a; a += (via = *a == 'V' || *a == 'v') ? 5 : 13; while(*a == ' ') ++a; if (via) { while(*a > ' ') ++a; while(*a == ' ') ++a; } e = a; while((unsigned char)*e >= ' ' && *e != 0x7f) ++e; if (e != a) { if (e - a > 60) a[65] = '\0'; else *e = '\0'; c->detail = strdup(a); pxyinfo(c, 2, "Proxy-agent: %s", a); } } if (r / 100 != 2) { /*XX determine whenever this is a permanent or temp error */ pxyinfo(c, 2, "HTTP request refused or failed (%d)", r); /* too bad some proxies return 403 or 404 errors on temp errors */ if (r == 407 || /* r == 404 || */ /* r == 403 || */ r == 400 || r == 302) pxyaction(c, 2); else pxyaction(c, 1); return -1; } return r; } static int hcc(pxyconn_t *c, char *buf, int l) { int r; /* todo: * - headers may come in separate packets; * - detect e.g. "Out of buffers" errors seen from wingate-3.0 (1182) * - detect redirects (status=3xx) */ if (c->pxystate > 1) return 0; c->pxystate = 2; if (memcfind(buf, l, "hwarang/")) pxyinfo(c, 2, "probably HwaRang \"lazy\" proxy"); r = httpheaders(c, buf, l); if (!r) return 0; if (r < 0) return 1; /* 2xx result */ /*if (r / 100 == 2)*/ { if (memcfind(buf, l, "content-length:") /* some STUPID software... || memcfind(buf, l, "content-type:") */) { pxyinfo(c, 2, "HTTP CONNECT answered with a page (code %d)", r); pxyaction(c, 1); /* maybe temp. error message */ return 1; } else { pxyinfo(c, 2, "HTTP request successeful (%d)", r); return 0; } } } static void hch(pxyconn_t *c, int UNUSED e) { /* HTTP CONNECT. * state: * 1 - request sent, want reply */ if (c->pxystate == 0) { /* some proxies requires \r\n in separate packet - doh */ if (pxyprintf(c, 3, "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(c->dstaddr), c->dstport) && pxywrite(c, "\r\n", 2, 3) && pxyreqiot(c, EV_IN, hch, 0, NULL)) c->pxystate = 1; } else /* todo: read headers if any here. * requires some logic change: upper-level protocol * routines should deal with already read data */ pxyaction(c, 0); } /* HTTP POST/PUT methods */ static int hxc(pxyconn_t *c, char *buf, int l) { int r; if (c->pxystate > 2) return 0; c->pxystate = 2; r = httpheaders(c, buf, l); if (!r) return 0; if (r < 0) return 1; /*if (r / 100 == 2)*/ { pxyinfo(c, 2, "HTTP request successeful (%d)", r); return 0; } } static void hxh(pxyconn_t *c, int UNUSED e) { if (!c->pxystate) { int dl = pxygetdata(c); char *b = pxybuf + dl + 1; int l = sprintf(b, "%s http://%s:%d/ HTTP/1.0\r\n" "Content-length: %d\r\n" "Connection: close\r\n" "\r\n", strchr(c->proto->fullname, ' ') + 1, inet_ntoa(c->dstaddr), c->dstport, dl); if (pxywrite(c, b, l, 3)) { c->pxystate = 1; pxyinfo(c, 3, "sending data"); if (pxywrite(c, pxybuf, dl, 6)) pxyreqiot(c, EV_IN, hxh, 0, NULL); } } else pxycheckdata(c); } static void s5h(pxyconn_t *c, int UNUSED e) { /* SOCKS5 * state: * 1 - auth request sent, want reply * 2 - connect request sent, want reply */ int l; if (!c->pxystate) { pxybuf[0] = 5; /* socks version */ pxybuf[1] = 1; /* nmethods */ pxybuf[2] = 0; /* no auth reqd */ if (pxywrite(c, pxybuf, 3, 4) && pxyreqiot(c, EV_IN, s5h, pxytimeout / 2, NULL)) c->pxystate = 1; } else if (c->pxystate == 1) { if ((l = pxyread(c, pxybuf, 4, 2, 4)) <= 0) ; else if (pxybuf[1] != 0) { pxyinfo(c, 2, "auth rejected (v=%d c=%d%s)", (unsigned char)pxybuf[0], (unsigned char)pxybuf[1], pxybuf[0] == 0 && pxybuf[1] == 91 ? ", socks4-only server" : ""); pxyaction(c, 2); } else if (l > 2) { pxyinfo(c, 2, "too much socks5 data v=%d c=%d %d %d", (unsigned char)pxybuf[0], (unsigned char)pxybuf[1], (unsigned char)pxybuf[2], (unsigned char)pxybuf[3]); pxyaction(c, 2); } else { ipport_t p = htons(c->dstport); pxyinfo(c, 3, "auth accepted"); pxybuf[0] = 5; /* version */ pxybuf[1] = 1; /* connect cmd */ pxybuf[2] = 0; /* reserved */ pxybuf[3] = 1; /* ipv4 addrtype */ memcpy(pxybuf+4, &c->dstaddr.s_addr, 4); memcpy(pxybuf+8, &p, 2); if (pxywrite(c, pxybuf, 10, 4) && pxyrenew(c, 0, NULL)) c->pxystate = 2; } } else if ((l = pxyread(c, pxybuf, 10, 2, 4)) <= 0) ; else if (pxybuf[1] == 0) { pxyinfo(c, 2, "request granted (v=%d c=%d l=%d)", (unsigned char)pxybuf[0], (unsigned char)pxybuf[1], l); pxyaction(c, 0); } else { char *s; switch(pxybuf[1]) { case 1: s = "general server failure"; l = 1; break; case 2: s = "connection not allowed by ruleset"; l = 2; break; case 3: s = "network unreachable"; l = 1; break; case 4: s = "host unreachable"; l = 1; break; case 5: s = "connection refused"; l = 1; break; case 6: s = "TTL expired"; l = 1; break; case 7: s = "command not supported"; l = 2; break; case 8: s = "address type not supported"; l = 2; break; default: s = "unexpected response"; l = 1; break; } pxyinfo(c, 2, "%s (v=%d c=%d)", s, (unsigned char)pxybuf[0], (unsigned char)pxybuf[1]); pxyaction(c, l); } } static void s4h(pxyconn_t *c, int UNUSED e) { /* SOCKS4. * state: * 1 - connect request sent, want reply */ int l; if (!c->pxystate) { ipport_t p = htons(c->dstport); pxybuf[0] = 4; /* socks version */ pxybuf[1] = 1; /* request: connect */ memcpy(pxybuf+2, &p, 2); memcpy(pxybuf+4, &c->dstaddr.s_addr, 4); pxybuf[8] = 0; /* empty/null username */ if (pxywrite(c, pxybuf, 9, 4) && pxyreqiot(c, EV_IN, s4h, 0, NULL)) c->pxystate = 1; } else if ((l = pxyread(c, pxybuf, 8, 8, 4)) <= 0) ; else if (pxybuf[1] == 90) { pxyinfo(c, 2, "request granted (v=%d c=%d)", (unsigned char)pxybuf[0], (unsigned char)pxybuf[1]); pxyaction(c, 0); } else { char *s; switch(pxybuf[1]) { case 91: s = "request rejected or failed"; break; case 92: s = "identd required"; break; case 93: s = "identd info mismatch"; break; case 1: if (pxybuf[0] == 5) { pxyinfo(c, 2, "request rejected (v=5 c=1, socks5-only server)"); pxyaction(c, 2); return; } default: s = "unexpected response"; break; } pxyinfo(c, 2, "%s (v=%d c=%d)", s, (unsigned char)pxybuf[0], (unsigned char)pxybuf[1]); pxyaction(c, 1); } } static int checklist(const char *buf, int l, const char *const lp[]) { const char *const *sp = lp; while(*sp) if (memmem(buf, l, *sp, strlen(*sp))) return sp - lp; else ++sp; return -1; } static int wgc(pxyconn_t *c, char *buf, int l) { /* look at Password:/etc strings */ static const char *const wgabort[] = { "sername", "ogin:", "assword:", NULL }; if (checklist(buf, l, wgabort) < 0) return 0; pxyaction(c, 2); return 1; } static void wgh(pxyconn_t *c, int e); static void wgh_tmo(pxyconn_t *c) { wgh(c, -1); } static void wgh(pxyconn_t *c, int e) { /* WINGATE/telnet. * state: * 1 - wait for a prompt, interpret Username:/Login: and similar; * we're waiting for a small timeout here, if not found - go to * next state * 2 - connection request sent, wait for reply */ int l; if (!c->pxystate) { if (pxyreadnext(c, 0, &l, 3) < 0) return; c->pxystate = 1; if (!pxyreqiot(c, EV_IN, wgh, pxytimeout / 8, wgh_tmo) || !l || !pxysave(c, pxybuf, l, 0)) return; } if (c->pxystate == 1) { if (e < 0) { /*XX try to recognize whenever to use telnet or "plain" wingate */ l = sprintf(pxybuf, "%s:%d\r\n", inet_ntoa(c->dstaddr), c->dstport); if (c->pxyport == 23) l += sprintf(pxybuf + l, "telnet %s %d\r\n", inet_ntoa(c->dstaddr), c->dstport); if (pxywrite(c, pxybuf, l, 3) && pxyrenew(c, 0, NULL)) c->pxystate = 2; } else if (pxyreadnext(c, 0, &l, 3) < 0 || wgc(c, pxybuf, l)) return; else { static const char *const wggo[] = { "cisco>", /*0 wingate-3.0 1181 */ "MNGTR>", /*1 wingate-3.0 1181 */ "WinGate>", /*2 wingate 23 */ "host[:port]:", /*3 telnet 23 */ "host_name:port", /*4 TelNet Gateway 23*/ "SpoonProxy>", /*5 host port 23 */ "tn-gw", /*6 telnet gateways 23 */ "telnet>", /*7*/ "CCProxy Telnet>", /*8 open host port 23 */ NULL }; const char *d; switch(checklist(pxybuf, l, wggo)) { case 0: case 1: case 2: if (!pxyprintf(c, 3, "%s:%d\r\n", inet_ntoa(c->dstaddr), c->dstport)) return; d = "host:port (wingate)"; break; case 3: case 4: if (!pxyprintf(c, 3, "%s:%d\r\n", inet_ntoa(c->dstaddr), c->dstport)) return; d = "host:port (telnet gateway)"; break; case 5: if (!pxyprintf(c, 3, "%s %d\r\n", inet_ntoa(c->dstaddr), c->dstport)) return; d = "host port (SpoonProxy)"; break; case 6: case 7: if (!pxyprintf(c, 3, "telnet %s %d\r\n", inet_ntoa(c->dstaddr), c->dstport)) return; d = "telnet host port (telnet gateway)"; break; case 8: if (!pxyprintf(c, 3, "open %s %d\r\n", inet_ntoa(c->dstaddr), c->dstport)) return; d = "open host port (CCProxy)"; break; default: return; } if (pxysave(c, pxybuf, l, 0) && pxyrenew(c, 0, NULL)) c->pxystate = 2; c->detail = strdup(d); } } else pxyaction(c, 0); } static void fuh(pxyconn_t *c, int UNUSED e) { /* FTP proxy. Wait for initial FTP greething and * issue a command: * USER dummy@targethost:targetport\r\n * After this, target receives * USER dummy * and transparent connection begun. * There is no way to use such proxy with protocols * dependant on first line from client (e.g. http). */ int l; if (!c->pxystate) { if (pxyreadnext(c, 0, &l, 3) < 0) /* check if established */ return; c->pxystate = 1; if (!pxyreqiot(c, EV_IN, fuh, pxytimeout / 2, NULL) || !l || !pxysave(c, pxybuf, l, 0)) return; } if (c->pxystate == 1) { if (pxyreadnext(c, 1, &l, 3) > 0 && l > 4) { if (memcmp(pxybuf, "220 ", 4) == 0) { char *a, *e; pxybuf[l] = '\0'; a = pxybuf + 4; if (!(*a >= 'A' && *a <= 'Z') && !(*a >= 'a' && *a <= 'z')) { while(*a && *a != ' ' && *a != '\n' && *a != '\r') ++a; while(*a == ' ') ++a; } e = a; while(*e && *e >= ' ' && *e != 0x7f) ++e; if (a != e) { if (e - a > 6 && memcmp(e - 6, " ready", 6) == 0) e -= 6; if (e - a > 70) a[65] = '\0'; else *e = '\0'; pxyinfo(c, 2, "Proxy-agent: %s", a); c->detail = strdup(a); } pxysave(c, NULL, 0, 0); if (pxyprintf(c, 3, "USER dummy@%s:%d\r\n", inet_ntoa(c->dstaddr), c->dstport)) pxyaction(c, 0); } else { pxyinfo(c, 2, "seems not to be FTP proxy"); /*XXX handle temp errors? */ pxyaction(c, 2); } } } } enum pxyfamily { pfSOCKS, pfHTTP, pfWG, pfFTP }; const pxyproto_t pxyprotos[] = { /* name aname transport fullname family hdl chk */ { "socks5", "s5", "socks5", "SOCKS5", pfSOCKS, s5h, NULL }, #define S5P (pxyprotos+0) { "socks4", "s4", "socks4", "SOCKS4", pfSOCKS, s4h, NULL }, #define S4P (pxyprotos+1) { "wingate", "wg", "wingate", "WINGATE", pfWG, wgh, wgc }, #define WGP (pxyprotos+2) { "http-connect","hc", "http", "HTTP CONNECT", pfHTTP, hch, hcc }, #define HCP (pxyprotos+3) { "http-post", "ho", "http", "HTTP POST", pfHTTP, hxh, hxc }, #define HOP (pxyprotos+4) { "http-put", "hu", "http", "HTTP PUT", pfHTTP, hxh, hxc }, #define HUP (pxyprotos+5) { "ftp-user", "fu", "ftp", "FTP USER", pfFTP, fuh, NULL }, #define FUP (pxyprotos+6) {0,0,0,0,0,0,0} }; static const ipport_t htc[] = {80,81,1075,3128,4480,6588,8000,8080,8081,8090,0}, hta[] = {7033,8085,8095,8100,8105,8110,0}, spp[] = {1080,1075,0}, wgp[] = {23,0}, fup[] = {21,0}, p1813[] = {1813,0}, /* skk proxy, socks5 only */ p5490[] = {5490,0} /* NONAME/1.4, probably trojan - http connect only */ ; const pxyprobe_t pxyprobes[] = { /* keep this list sorted by 3rd column (level)! */ /* pro ports adv */ { S5P, spp, 0 }, /* socks5 basic */ { S4P, spp, 0 }, /* socks4 basic */ { HCP, htc, 0 }, /* HTTP CONNECT basic */ { HCP, p5490, 0 }, /* NONAME/1.4, http connect only */ { HCP, hta, 1 }, /* HTTP CONNECT extended ports */ { HOP, htc, 1 }, /* HTTP POST basic */ { WGP, wgp, 2 }, /* wingate/telnet */ { HOP, hta, 2 }, /* HTTP POST extended ports */ { S5P, p1813, 2 }, /* SKK proxy (socks5) */ { HUP, htc, 3 }, /* HTTP PUT basic */ { FUP, fup, 3 }, /* FTP */ { HUP, hta, 4 }, /* HTTP PUT extended ports */ {0,0,0} }; proxycheck-0.49a/event.c0100644000175000017500000005661710056117400013433 0ustar mjtmjt/* $Id: event.c,v 1.17 2004/05/29 14:32:32 mjt Exp $ * Timer and I/O Event core * Author: Michael Tokarev, * License: LGPL. */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include "event.h" #if HAVE_SYS_SELECT_H # include #endif #if HAVE_EPOLL # include # ifdef HAVE_POLL # undef HAVE_POLL # endif # define HAVE_POLL 1 #endif #if HAVE_DEVPOLL # include # include # include # include #endif #if HAVE_KQUEUE # include #endif #if HAVE_POLL # include #endif #define ARR_ROUND(x) ((x) < 64 ? ((x) & ~3) + 4 : ((x) & ~31) + 32) struct ev_fd { /* information about a filedescriptor */ short events; /* bitmask: events of interest */ short revents; /* bitmask: events ready */ #if HAVE_POLL int pfdi; /* index in ct.pfd[] array */ #endif ev_io_cbck_f *cbck; /* application callback routine */ void *data; /* application data */ struct ev_fd *next; /* next in ready list */ }; struct ev_method; #if 0 struct ev_sig { struct ev_ct *ct; ev_sig_cbck_f *cbck; void *data; int raised; struct ev_sig *next; }; #endif struct ev_ct { /* the event context */ struct ev_tm *tmhead, *tmtail; /* list of timers */ int tmcnt; /* current number of timers */ ev_time_t tmsum; /* sum of `when' values of all timers */ int loop; /* reenterancy protection */ /* common fields */ struct ev_fd *efd; /* array of FD structures (dynalloc), index by fd */ int aefd; /* number of entries allocated in efd */ int maxfd; /* max FD number so far */ int nfd; /* total number of FDs monitored */ const struct ev_method *method; /* current I/O method */ int sigpipe[2]; int sigcnt; /* method-specific data */ int qfd; /* fd for epoll, kqueue, devpoll */ #if HAVE_POLL struct pollfd *pfd; /* array of pollfd structures (dynalloc) */ int apfd; /* number of entries in pfd (allocated so far) */ #endif /* HAVE_POLL */ fd_set rfdset, wfdset, xfdset; }; #if HAVE_EPOLL /***************************************/ #if IMPLEMENT_LINUX_EPOLL #include #ifndef __NR_epoll_create #define __NR_epoll_create 254 #define __NR_epoll_ctl 255 #define __NR_epoll_wait 256 #endif _syscall1(int, epoll_create, int, size) _syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event) _syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents, int, maxevents, int, timeout) #endif static int evio_epoll_init(struct ev_ct *ct, int maxfd) { int epfd = epoll_create(maxfd); assert(EPOLLIN == EV_IN && EPOLLOUT == EV_OUT && EPOLLPRI == EV_PRI); if (epfd < 0) return -1; ct->qfd = epfd; return 0; } static int evio_epoll_ctl(struct ev_ct *ct, int func, int fd, int events) { struct epoll_event ev; ev.events = events; ev.data.fd = fd; return epoll_ctl(ct->qfd, func, fd, &ev); } static int evio_epoll_add(struct ev_ct *ct, int fd, int events) { return evio_epoll_ctl(ct, EPOLL_CTL_ADD, fd, events); } static int evio_epoll_mod(struct ev_ct *ct, int fd, int events) { return evio_epoll_ctl(ct, EPOLL_CTL_MOD, fd, events); } static int evio_epoll_del(struct ev_ct *ct, int fd) { return evio_epoll_ctl(ct, EPOLL_CTL_DEL, fd, 0); } static int evio_epoll_wait(struct ev_ct *ct, int timeout, struct ev_fd **efdp) { #define EPOLL_CHUNK 200 struct epoll_event epev[EPOLL_CHUNK]; int ready; /* wait for events */ ready = epoll_wait(ct->qfd, epev, EPOLL_CHUNK, timeout < 0 ? -1 : timeout); if (ready > 0) { /* initialize list of ready fds */ struct epoll_event *ep = epev, *epe = epev + ready; do { struct ev_fd *efd = ct->efd + ep->data.fd; assert(ep->data.fd >= 0 && ep->data.fd <= ct->maxfd); assert(efd->cbck != NULL); assert(efd->next == NULL); assert(efd->revents == 0); efd->revents = ep->events; *efdp = efd; efdp = &efd->next; } while (++ep < epe); } *efdp = NULL; return ready; } #endif /* HAVE_EPOLL */ #if HAVE_DEVPOLL /***************************************/ static int evio_devpoll_init(struct ev_ct *ct, int maxfd) { int dpfd = open("/dev/poll", O_RDWR); if (dpfd < 0) return -1; ct->qfd = dpfd; maxfd = maxfd; return 0; } static int evio_devpoll_mod(struct ev_ct *ct, int fd, int events) { struct pollfd pfd; pfd.fd = fd; pfd.events = events; pfd.revents = 0; return write(ct->qfd, &pfd, sizeof(pfd)) < 0 ? -1 : 0; } static int evio_devpoll_add(struct ev_ct *ct, int fd, int events) { assert(POLLIN == EV_IN && POLLOUT == EV_OUT && POLLPRI == EV_PRI); return evio_devpoll_mod(ct, fd, events); } static int evio_devpoll_del(struct ev_ct *ct, int fd) { return evio_devpoll_mod(ct, fd, POLLREMOVE); } static int evio_devpoll_wait(struct ev_ct *ct, int timeout, struct ev_fd **efdp) { #define DEVPOLL_CHUNK 100 struct pollfd pfda[DEVPOLL_CHUNK]; struct pollfd *pfd, *pfde; dvpoll_t dvp; int ready; /* wait for events */ dvp.dp_timeout = timeout; dvp.dp_nfds = DEVPOLL_CHUNK; dvp.dp_fds = pfd = pfda; ready = ioctl(ct->qfd, DP_POLL, &dvp); if (ready > 0) { /* initialize list of ready fds */ for (pfde = pfd + ready; pfd < pfde; ++pfd) { struct ev_fd *efd = ct->efd + pfd->fd; assert(pfd->fd >= 0 && pfd->fd <= ct->maxfd); assert(efd->cbck != NULL); assert(efd->revents == 0); efd->revents = pfd->revents; *efdp = efd; efdp = &efd->next; } } *efdp = NULL; return ready; } #endif /* HAVE_DEVPOLL */ #if HAVE_KQUEUE /***************************************/ static int evio_kqueue_init(struct ev_ct *ct, int maxfd) { int kqfd = kqueue(); if (kqfd < 0) return -1; ct->qfd = kqfd; maxfd = maxfd; return 0; } static int evio_kqueue_mod(struct ev_ct *ct, int fd, int events) { struct kevent kev; struct ev_fd *efd = ct->efd + fd; static struct timespec zero_ts; if ((efd->events & EV_IN) && !(events & EV_IN)) { EV_SET(&kev, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (kevent(ct->qfd, &kev, 1, 0, 0, &zero_ts)) return -1; efd->events &= ~EV_IN; } else if (!(efd->events & EV_IN) && (events & EV_IN)) { EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); if (kevent(ct->qfd, &kev, 1, 0, 0, &zero_ts) < 0) return -1; efd->events |= EV_IN; } if ((efd->events & EV_OUT) && !(events & EV_OUT)) { EV_SET(&kev, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (kevent(ct->qfd, &kev, 1, 0, 0, &zero_ts)) return -1; efd->events &= ~EV_OUT; } else if (!(efd->events & EV_OUT) && (events & EV_OUT)) { EV_SET(&kev, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); if (kevent(ct->qfd, &kev, 1, 0, 0, &zero_ts) < 0) return -1; efd->events |= EV_OUT; } return 0; } static int evio_kqueue_add(struct ev_ct *ct, int fd, int events) { return evio_kqueue_mod(ct, fd, events); } static int evio_kqueue_del(struct ev_ct *ct, int fd) { return evio_kqueue_mod(ct, fd, 0); } static int evio_kqueue_wait(struct ev_ct *ct, int timeout, struct ev_fd **efdp) { #define KQUEUE_CHUNK 200 struct kevent keva[KQUEUE_CHUNK]; struct kevent *kev, *keve; int ready; struct timespec ts, *tsp; /* wait for events */ if (timeout < 0) tsp = NULL; else { ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout % 1000) * 1000000; tsp = &ts; } kev = keva; ready = kevent(ct->qfd, 0, 0, kev, KQUEUE_CHUNK, tsp); if (ready > 0) { /* initialize list of ready fds */ for (keve = kev + ready; kev < keve; ++kev) { short ev; struct ev_fd *efd = ct->efd + kev->ident; assert(kev->ident <= (unsigned)ct->maxfd); assert(efd->cbck != NULL); if (kev->filter == EVFILT_READ) ev = EV_IN; else if (kev->filter == EVFILT_WRITE) ev = EV_OUT; else continue; if (!efd->revents) { *efdp = efd; efdp = &efd->next; } efd->revents |= ev; } } *efdp = NULL; return ready; } #endif /* HAVE_KQUEUE */ #if HAVE_POLL /***************************************/ static int evio_poll_init(struct ev_ct *ct, int maxfd) { struct pollfd *pfd; assert(POLLIN == EV_IN && POLLOUT == EV_OUT && POLLPRI == EV_PRI); maxfd = ARR_ROUND(maxfd); pfd = (struct pollfd *)calloc(maxfd, sizeof(struct pollfd)); if (!pfd) return errno = ENOMEM, -1; ct->pfd = pfd; ct->apfd = maxfd; return 0; } static int evio_poll_add(struct ev_ct *ct, int fd, int events) { struct pollfd *pfd; int pfdi = ct->nfd; if (pfdi >= ct->apfd) { int apfd = ARR_ROUND(pfdi+1); struct pollfd *pfdp, *pfde; pfd = (struct pollfd *)realloc(ct->pfd, sizeof(struct pollfd) * apfd); if (!pfd) return errno = ENOMEM, -1; for(pfdp = pfd + pfdi, pfde = pfd + apfd; pfdp < pfde; ++pfdp) pfdp->fd = -1; ct->pfd = pfd; ct->apfd = apfd; } pfd = ct->pfd + pfdi; assert(pfd->events == 0); pfd->fd = fd; pfd->events = events; pfd->revents = 0; assert(ct->efd[fd].pfdi < 0); ct->efd[fd].pfdi = pfdi; return 0; } static int evio_poll_mod(struct ev_ct *ct, int fd, int events) { struct ev_fd *efd = ct->efd + fd; int pfdi = efd->pfdi; assert(pfdi >= 0 && pfdi <= ct->nfd); assert(ct->pfd[pfdi].fd == fd); ct->pfd[pfdi].events = events; return 0; } static int evio_poll_del(struct ev_ct *ct, int fd) { struct ev_fd *efd = ct->efd + fd; int pfdi = efd->pfdi; int lastfd; assert(pfdi >= 0 && pfdi <= ct->nfd); assert(ct->pfd[pfdi].fd == fd); ct->pfd[pfdi].fd = -1; efd->pfdi = -1; lastfd = ct->nfd - 1; if (lastfd != pfdi) { /* move last pfd to pfdi'th position */ ct->pfd[pfdi] = ct->pfd[lastfd]; ct->efd[ct->pfd[pfdi].fd].pfdi = pfdi; } ct->pfd[lastfd].fd = -1; efd->pfdi = -1; return 0; } static int evio_poll_wait(struct ev_ct *ct, int timeout, struct ev_fd **efdp) { struct pollfd *pfd, *pfde; struct ev_fd *efd; int ready, cnt; /* wait for events */ pfd = ct->pfd; ready = poll(pfd, ct->nfd, timeout); if (ready > 0) { /* initialize list of ready fds */ for (pfde = pfd + ct->nfd, cnt = ready; pfd < pfde; ++pfd) { assert(pfd->fd >= 0 && pfd->fd <= ct->maxfd); efd = ct->efd + pfd->fd; assert(efd->pfdi == pfd - ct->pfd); assert(efd->cbck != NULL); if (pfd->revents) { efd->revents = (pfd->revents & POLLERR) ? EV_IN|EV_OUT|EV_PRI : pfd->revents; *efdp = efd; efdp = &efd->next; if (--cnt) break; } } } *efdp = NULL; return ready; } #endif /* HAVE_POLL */ /* SELECT is always present */ /***************************************/ static int evio_select_init(struct ev_ct *ct, int maxfd) { FD_ZERO(&ct->rfdset); FD_ZERO(&ct->wfdset); FD_ZERO(&ct->xfdset); maxfd = maxfd; return 0; } static int evio_select_add(struct ev_ct *ct, int fd, int events) { assert(fd < FD_SETSIZE); if (events & EV_IN) FD_SET(fd, &ct->rfdset); if (events & EV_OUT) FD_SET(fd, &ct->wfdset); if (events & EV_PRI) FD_SET(fd, &ct->xfdset); return 0; } static int evio_select_mod(struct ev_ct *ct, int fd, int events) { if (events & EV_IN) FD_SET(fd, &ct->rfdset); else FD_CLR(fd, &ct->rfdset); if (events & EV_OUT) FD_SET(fd, &ct->wfdset); else FD_CLR(fd, &ct->wfdset); if (events & EV_PRI) FD_SET(fd, &ct->xfdset); else FD_CLR(fd, &ct->xfdset); return 0; } static int evio_select_del(struct ev_ct *ct, int fd) { FD_CLR(fd, &ct->rfdset); FD_CLR(fd, &ct->wfdset); FD_CLR(fd, &ct->xfdset); return 0; } static int evio_select_wait(struct ev_ct *ct, int timeout, struct ev_fd **efdp) { int ready, cur, fd; fd_set fdr = ct->rfdset; fd_set fdw = ct->wfdset; fd_set fdx = ct->xfdset; struct timeval tv, *tvp; if (timeout < 0) tvp = NULL; else { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; tvp = &tv; } /* wait for events */ ready = select(ct->maxfd + 1, &fdr, &fdw, &fdx, tvp); if (ready > 0) { /* initialize list of ready fds */ for(fd = 0, cur = ready; fd <= ct->maxfd; ++fd) { int revents = 0; struct ev_fd *efd = ct->efd + fd; if (!efd->cbck) continue; if (FD_ISSET(fd, &fdr)) revents |= EV_IN; if (FD_ISSET(fd, &fdw)) revents |= EV_OUT; if (FD_ISSET(fd, &fdx)) revents |= EV_PRI; if (!revents) continue; assert(!efd->next); assert(!efd->revents); efd->revents = revents; *efdp = efd; efdp = &efd->next; if (!--cur) break; } } *efdp = NULL; return ready; } static struct ev_ct *ev_defct; #define GETCTX(ct) if (!ct) ct = ev_defct ev_time_t ev_now; time_t ev_time; ev_time_t ev_gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); ev_time = tv.tv_sec; ev_now = (ev_time_t)tv.tv_sec * 1000 + tv.tv_usec / 1000; return ev_now; } int ev_fdlimit(void) { struct rlimit rlim; getrlimit(RLIMIT_NOFILE, &rlim); return rlim.rlim_cur; } struct ev_method { const char *name; int type; int (*init)(struct ev_ct *ct, int maxfd); int (*wait)(struct ev_ct *ct, int tmo, struct ev_fd **pefd); int (*add)(struct ev_ct *ct, int fd, int events); int (*mod)(struct ev_ct *ct, int fd, int events); int (*del)(struct ev_ct *ct, int fd); int maxfd; }; static const struct ev_method methods[] = { #define EVIO_METHOD(name,type,maxfd) \ { #name, type, evio_##name##_init, evio_##name##_wait, \ evio_##name##_add, evio_##name##_mod, evio_##name##_del, maxfd } #if HAVE_EPOLL EVIO_METHOD(epoll,EV_EPOLL,0), #endif #if HAVE_DEVPOLL EVIO_METHOD(devpoll,EV_DEVPOLL,0), #endif #if HAVE_KQUEUE EVIO_METHOD(kqueue,EV_KQUEUE,0), #endif #if HAVE_POLL EVIO_METHOD(poll,EV_POLL,0), #endif EVIO_METHOD(select,EV_SELECT,FD_SETSIZE-1) }; struct ev_ct *ev_ct_new(int maxfdhint, int type) { unsigned i; struct ev_ct *ct; const struct ev_method *em; int maxfd; const char *method; ev_gettime(); ct = (struct ev_ct *)calloc(1, sizeof(struct ev_ct)); if (!ct) return errno = ENOMEM, (struct ev_ct*)0; ct->qfd = -1; i = 0; if (maxfdhint <= 0) maxfdhint = ev_fdlimit(); maxfdhint = ARR_ROUND(maxfdhint); method = getenv("EV_METHOD"); if (method || !type) type = 0xffff; #ifdef ENOTSUP errno = ENOTSUP; #else errno = ENOENT; #endif for(;;) { em = &methods[i]; if ((em->type & type) && (!method || strcmp(method, em->name) == 0)) { maxfd = em->maxfd && em->maxfd < maxfdhint ? em->maxfd : maxfdhint; if (em->init(ct, maxfd) == 0) break; } if (++i < sizeof(methods)/sizeof(methods[0])) continue; free(ct); return NULL; } ct->efd = (struct ev_fd *)calloc(maxfd, sizeof(struct ev_fd)); if (!ct->efd) { if (ct->qfd >= 0) close(ct->qfd); #if HAVE_POLL if (ct->pfd) free(ct->pfd); #endif free(ct); errno = ENOMEM; return NULL; } #if HAVE_POLL { struct ev_fd *efd, *efde; for(efd = ct->efd, efde = efd + maxfd; efd < efde; ++efd) efd->pfdi = -1; } #endif ct->aefd = maxfd; ct->method = em; ct->maxfd = -1; return ct; } void ev_ct_free(struct ev_ct *ct) { GETCTX(ct); if (ct->qfd >= 0) close(ct->qfd); #if HAVE_POLL if (ct->pfd) free(ct->pfd); #endif free(ct->efd); free(ct); if (ct == ev_defct) ev_defct = NULL; } const char *ev_method_name(const struct ev_ct *ct) { GETCTX(ct); return ct->method->name; } int ev_method(const struct ev_ct *ct) { GETCTX(ct); return ct->method->type; } int ev_init(int maxfdhint, int type) { if (!ev_defct && !(ev_defct = ev_ct_new(maxfdhint, type))) return -1; return 0; } void ev_free(void) { if (ev_defct) ev_ct_free(ev_defct); } #define CHKFD(ct,fd,err) \ if (fd < 0) return errno = EINVAL, err #define GETFD(ct,fd,efd,err) \ CHKFD(ct,fd,err); \ if (fd > ct->maxfd) return errno = ENOENT, err; \ efd = ct->efd + fd; \ if (!efd->cbck) return errno = ENOENT, err int ev_io_add(struct ev_ct *ct, int fd, int events, ev_io_cbck_f *cb, void *data) { int r; struct ev_fd *efd; GETCTX(ct); CHKFD(ct, fd, -1); if (!cb) return errno = EFAULT, -1; if (ct->method->maxfd && fd > ct->method->maxfd) return errno = EMFILE, -1; if (fd >= ct->aefd) { r = ARR_ROUND(fd); efd = (struct ev_fd *)realloc(ct->efd, sizeof(struct ev_fd) * r); if (!efd) return errno = ENOMEM, -1; memset(efd, 0, sizeof(struct ev_fd) * (r - ct->aefd)); #if HAVE_POLL { struct ev_fd *efdp, *efde; for(efdp = efd + ct->aefd, efde = efd + r; efdp < efde; ++efdp) efdp->pfdi = -1; } #endif ct->efd = efd; ct->aefd = r; } efd = ct->efd + fd; if (efd->cbck) return errno = EEXIST, -1; r = ct->method->add(ct, fd, events); if (r != 0) return r; efd->cbck = cb; efd->data = data; if (ct->maxfd < fd) ct->maxfd = fd; ++ct->nfd; return 0; } /* modify parameters for existing FD */ int ev_io_mod(struct ev_ct *ct, int fd, int events, ev_io_cbck_f *cb, void *data) { int r; struct ev_fd *efd; GETCTX(ct); GETFD(ct, fd, efd, -1); if (!cb) return errno = EFAULT, -1; r = ct->method->mod(ct, fd, events); if (r != 0) return r; efd->cbck = cb; efd->data = data; efd->events = events; efd->revents = 0; return 0; } /* deregister an FD */ int ev_io_del(struct ev_ct *ct, int fd) { struct ev_fd *efd; GETCTX(ct); GETFD(ct, fd, efd, -1); ct->method->del(ct, fd); /* ignore errors */ efd->cbck = NULL; efd->data = NULL; efd->events = efd->revents = 0; if (ct->maxfd == fd) { do --fd; while(fd >= 0 && ct->efd[fd].cbck == NULL); ct->maxfd = fd; } --ct->nfd; return 0; } int ev_io_count(const struct ev_ct *ct) { GETCTX(ct); return ct->nfd; } /* create new timer to be fired at the time specified by `when', * and insert it into the list appropriately. */ static struct ev_tm * ev_tm_new(struct ev_ct *ct, struct ev_tm *tmr, ev_time_t when, ev_tm_cbck_f *cbck, void *data) { GETCTX(ct); if (!cbck) return errno = EFAULT, (struct ev_tm*)0; if (tmr) assert(!tmr->evtm_prev && !tmr->evtm_next && !tmr->evtm_when); else if ((tmr = (struct ev_tm *)malloc(sizeof(struct ev_tm))) == NULL) return errno = ENOMEM, (struct ev_tm*)0; tmr->evtm_when = when; tmr->evtm_cbck = cbck; tmr->evtm_data = data; if (!ct->tmhead) { /* no other timers are registered, just create empty list */ assert(!ct->tmtail && !ct->tmcnt && !ct->tmsum); ct->tmhead = ct->tmtail = tmr; tmr->evtm_next = tmr->evtm_prev = NULL; } else if (when >= ct->tmtail->evtm_when) { /* add after the tail */ ct->tmtail->evtm_next = tmr; tmr->evtm_prev = ct->tmtail; ct->tmtail = tmr; tmr->evtm_next = NULL; } else if (when < ct->tmhead->evtm_when) { /* add before the head */ ct->tmhead->evtm_prev = tmr; tmr->evtm_next = ct->tmhead; ct->tmhead = tmr; tmr->evtm_prev = NULL; } else if (ct->tmsum <= when * ct->tmcnt) { /* add into the middle, going from the tail */ struct ev_tm *prev = ct->tmtail; while(prev->evtm_when > when) { assert(prev->evtm_prev && prev->evtm_when >= prev->evtm_prev->evtm_when); prev = prev->evtm_prev; } tmr->evtm_prev = prev; tmr->evtm_next = prev->evtm_next; prev->evtm_next->evtm_prev = tmr; prev->evtm_next = tmr; } else { /* add into the middle, going from the head */ struct ev_tm *next = ct->tmhead; while(next->evtm_when <= when) { assert(next->evtm_next && next->evtm_when <= next->evtm_next->evtm_when); next = next->evtm_next; } tmr->evtm_next = next; tmr->evtm_prev = next->evtm_prev; next->evtm_prev->evtm_next = tmr; next->evtm_prev = tmr; } ct->tmcnt += 1; ct->tmsum += when; return tmr; } struct ev_tm * ev_tm_add(struct ev_ct *ct, struct ev_tm *tmr, int mstimeout, ev_tm_cbck_f *cbck, void *data) { if (mstimeout < 0) return errno = EINVAL, (struct ev_tm*)0; else return ev_tm_new(ct, tmr, ev_now + mstimeout, cbck, data); } struct ev_tm * ev_ts_add(struct ev_ct *ct, struct ev_tm *tmr, int stimeout, ev_tm_cbck_f *cbck, void *data) { if (stimeout < 0) return errno = EINVAL, (struct ev_tm*)0; else return ev_tm_new(ct, tmr, ((ev_now + 500) / 1000 + stimeout) * 1000, cbck, data); } int ev_tm_del(struct ev_ct *ct, struct ev_tm *tmr) { int mstimeout; GETCTX(ct); if (!tmr->evtm_when) { assert(tmr->evtm_prev == NULL && tmr->evtm_next == NULL); return errno = ENOENT, -1; } assert(tmr->evtm_prev != NULL || ct->tmhead == tmr); assert(tmr->evtm_next != NULL || ct->tmtail == tmr); if (tmr->evtm_prev) tmr->evtm_prev->evtm_next = tmr->evtm_next; else ct->tmhead = tmr->evtm_next; if (tmr->evtm_next) tmr->evtm_next->evtm_prev = tmr->evtm_prev; else ct->tmtail = tmr->evtm_prev; ct->tmcnt -= 1; ct->tmsum -= tmr->evtm_when; mstimeout = tmr->evtm_when < ev_now ? 0 : tmr->evtm_when - ev_now; tmr->evtm_prev = tmr->evtm_next = NULL; tmr->evtm_when = 0; return mstimeout; } /* return the time when first timer will be fired */ ev_time_t ev_tm_first(const struct ev_ct *ct) { GETCTX(ct); return ct->tmhead ? ct->tmhead->evtm_when : 0; } /* return the time from now to the first timer to be fired * or -1 if no timer is set */ int ev_tm_timeout(const struct ev_ct *ct) { GETCTX(ct); if (!ct->tmhead) return -1; if (ct->tmhead->evtm_when > ev_now) return 0; return ct->tmhead->evtm_when - ev_now; } int ev_tm_count(const struct ev_ct *ct) { GETCTX(ct); return ct->tmcnt; } /* wait and dispatch any events, single */ int ev_wait(struct ev_ct *ct, int timeout) { struct ev_tm *tmr; struct ev_fd *efdl; int r; int saved_errno = 0; GETCTX(ct); if (ct->loop) return errno = EAGAIN, -1; ct->loop = 1; if (timeout && ct->tmhead) { r = ct->tmhead->evtm_when - ev_now; if (r < 0) timeout = 0; else if (timeout < 0 || r < timeout) timeout = r; } r = ct->method->wait(ct, timeout, &efdl); if (r < 0) saved_errno = errno; ev_gettime(); while((tmr = ct->tmhead) != NULL && tmr->evtm_when <= ev_now) { if ((ct->tmhead = tmr->evtm_next) != NULL) tmr->evtm_next->evtm_prev = NULL; else ct->tmtail = NULL; ct->tmcnt -= 1; ct->tmsum -= tmr->evtm_when; tmr->evtm_prev = tmr->evtm_next = NULL; tmr->evtm_when = 0; tmr->evtm_cbck(tmr->evtm_data, tmr, ct); } while(efdl) { struct ev_fd *efd = efdl; int revents = efd->revents; efdl = efd->next; efd->revents = 0; efd->next = NULL; if (revents) efd->cbck(efd->data, revents, efd - ct->efd, ct); } ct->loop = 0; if (r < 0) errno = saved_errno; return r; } #if TEST #include #define delay 3000 static void pc(struct ev_ct *ct, struct ev_tm *tmr, void *data) { char *t = (char*)data; write(1, t, 1); if (*t >= 'a' && *t <= 'z') *t = *t - 'a' + 'A'; else if (*t >= 'A' && *t <= 'Z') *t = *t - 'A' + 'a'; ev_tm_add(ct, tmr, delay, pc, data); } static void rt(struct ev_ct *ct, int fd, int events, void *data) { char buf[10]; int l = read(fd, buf, sizeof(buf)); if (l <= 0) { ev_ct_free(ct); exit(l < 0 ? 1 : 0); } write(1, "input: ", 7); write(1, buf, l); ev_io_del(0, 0); ev_io_add(0, 0, EV_IN, rt, 0); } int main() { static char text[] = "\rdingDONG"; char *p; if (ev_init(1, 0) != 0) { perror("ev_init"); return 1; } printf("evio method: %s\n", ev_method_name(0)); for(p = text; *p; ++p) ev_tm_add(0, 0, delay, pc, p); ev_io_add(0, 0, EV_IN, rt, 0); while(1) ev_wait(0, -1); return 1; } #endif /* TEST */ proxycheck-0.49a/memmem.c0100644000175000017500000000357510055456013013567 0ustar mjtmjt/* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #ifdef HAVE_CONFIG_H # include #endif #ifndef HAVE_MEMMEM #undef memmem /* Return the first occurrence of NEEDLE in HAYSTACK. */ void * memmem (haystack, haystack_len, needle, needle_len) const void *haystack; size_t haystack_len; const void *needle; size_t needle_len; { const char *begin; const char *const last_possible = (const char *) haystack + haystack_len - needle_len; if (needle_len == 0) /* The first occurrence of the empty string is deemed to occur at the beginning of the string. */ return (void *) haystack; /* Sanity check, otherwise the loop might search through the whole memory. */ if (haystack_len < needle_len) return NULL; for (begin = (const char *) haystack; begin <= last_possible; ++begin) if (begin[0] == ((const char *) needle)[0] && !memcmp ((const void *) &begin[1], (const void *) ((const char *) needle + 1), needle_len - 1)) return (void *) begin; return NULL; } #endif proxycheck-0.49a/proxylogger.c0100644000175000017500000001065010055456511014666 0ustar mjtmjt/* $Id: proxylogger.c,v 1.4 2004/05/27 21:27:37 mjt Exp $ * A trivial program (should be invoked by inetd) that * writes out a string "550 ESMTP_unwelcome [peer.ip.add.ress]" * to the network and optionally waits for a string in form * [junk]protocol:ip.add.re.ss:port\n * from the remote system. May be used as a destination for * proxycheck program. All connections (together with the * information in the above form, if given) are optionally * logged to a specified file. * Options: * -n - do not wait for a proxy connection info * -t timeout - timeout in secs * -l logfile - where to log proxy/connection info * -s say - what to say * -S saylast - what to say when correct proxy parameters are seen */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef INADDR_NONE # define INADDR_NONE ((in_addr_t)-1) #endif struct sockaddr_in peer; struct in_addr pxyaddr; unsigned short pxyport; char *logfile; char *say = "550 open proxy cheking service"; char *saylast = "550 ESMTP_unwelcome"; char buf[2048]; static struct proto { const char *see; int len; const char *say; } protos[] = { { "hc", 2, "hc" }, { "http", 4, "hc" }, { "http-connect", 12, "hc" }, /* { "hg", 2, "hg" }, { "http-get", 8, "hg" },*/ { "ho", 2, "ho" }, { "http-post", 9, "ho" }, { "hu", 2, "hu" }, { "http-put", 8, "hu" }, { "wg", 2, "wg" }, { "wingate", 7, "wg" }, { "s4", 2, "s4" }, { "socks4", 6, "s4" }, { "s5", 2, "s5" }, { "socks5", 6, "s5" }, { "fu", 2, "fu" }, { "ftp", 3, "fu" }, { NULL, 0, NULL } }; void info(const char *proto) { int logfd; if (logfile && (logfd = open(logfile, O_CREAT|O_WRONLY|O_APPEND, 0644)) > 0) { time_t t = time(NULL); struct tm *tm = localtime(&t); int l = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d %s", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, inet_ntoa(peer.sin_addr)); if (proto) l += sprintf(buf + l, " %s:%s:%d", proto, inet_ntoa(pxyaddr), pxyport); buf[l++] = '\n'; write(logfd, buf, l); close(logfd); } if (proto) { int l = sprintf(buf, "%s [%s]\r\n", saylast, inet_ntoa(peer.sin_addr)); write(1, buf, l); } exit(0); } void timedout(int sig) { sig = sig; info(NULL); } void findproxyinfo(char *b, char *be) { const struct proto *pr; char *p; *be = '\0'; while(b < be) { for(pr = protos; pr->see; ++pr) if (*b == pr->see[0] && b + pr->len < be && b[pr->len] == ':' && memcmp(pr->see, b, pr->len) == 0) break; if (!pr->see) { ++b; continue; } b += pr->len + 1; if ((p = strchr(b, ':')) == NULL) continue; *p = '\0'; pxyaddr.s_addr = inet_addr(b); *p++ = ':'; if (pxyaddr.s_addr == INADDR_NONE) continue; b = p; pxyport = 0; while(*b >= '0' && *b <= '9' && b - p < 6) pxyport = pxyport * 10 + (*b++ - '0'); if (!(*b >= 'a' && *b <= 'z') && !(*b >= 'A' && *b <= 'Z') && !(*b >= '0' && *b <= '9')) info(pr->say); } } int main(int argc, char **argv) { int timeout = 30; int nowait = 0; int l, r, wl; char *w; while((l = getopt(argc, argv, "t:l:s:S:n")) != EOF) switch(l) { case 't': timeout = atoi(optarg); break; case 'l': logfile = optarg; break; case 's': say = optarg; break; case 'S': saylast = optarg; break; case 'n': nowait = 1; break; default: return 1; } l = sizeof(peer); if (getpeername(0, (struct sockaddr*)&peer, &l) < 0 || l != sizeof(peer) || peer.sin_family != AF_INET) return 1; signal(SIGALRM, timedout); signal(SIGPIPE, SIG_IGN); alarm(timeout); /* print some easily recognizeable string */ wl = sprintf(buf, "%s [%s]\r\n", say, inet_ntoa(peer.sin_addr)); w = (char*)malloc(wl); memcpy(w, buf, wl); write(0, w, wl); if (nowait || !logfile) info(NULL); /* read data from the network. * try to recognize: * junk space proto:ipaddr:port space */ l = 0; while((r = read(0, buf + l, sizeof(buf) - l - 1)) > 0) { char *e; char *s = buf + l - 30; if (s < buf) s = buf; r += l; e = buf + r; findproxyinfo(s, e); for(s = buf + l; s < e; ++s) if (*s == '\n') if (write(1, w, wl) < 0) info(NULL); } info(NULL); return 0; } proxycheck-0.49a/pxy.h0100644000175000017500000001070510055456013013130 0ustar mjtmjt/* $Id: pxy.h,v 1.8 2004/05/27 14:18:43 mjt Exp $ * open proxy checker, common definitions. * Michael Tokarev . * This code may be freely used and distributed according to * the terms of General Public License (GPL) version 2 or later. */ #ifndef _PXY_H #define _PXY_H #include "event.h" #if !defined(__GNUC__) && !defined(__attribute__) # define __attribute__(c) #endif #ifndef UNUSED # define UNUSED __attribute__((unused)) #endif #ifndef PRINTFLIKE # define PRINTFLIKE(f,v) __attribute__((format(printf,f,v))) #endif typedef struct pxyconn pxyconn_t; typedef unsigned short ipport_t; typedef struct { unsigned len; /* current length in buffer */ char buf[128-3*sizeof(unsigned)]; /* the buffer itself */ } pxybuf_t; extern char pxybuf[8193]; /* Proxy protocol definition. */ typedef struct { const char *name; /* "canonical" name of a protocol */ const char *aname; /* alternative name */ const char *transport;/* http */ const char *fullname; /* HTTP CONNECT */ int family; /* internal code: "family" of proto, HTTP, FTP, etc */ void (*handler)(pxyconn_t *, int); /* protocol handler */ int (*check)(pxyconn_t *c, char *buf, int l); } pxyproto_t; extern const pxyproto_t pxyprotos[]; /* array of all supported protocols */ typedef struct { const pxyproto_t *proto; const ipport_t *ports; int advanced; } pxyprobe_t; extern const pxyprobe_t pxyprobes[]; /* Active proxy connection structure. * There aren't many of those: upper limit is the * number of open files. * The structure supposed to be "embedded" into another * structure with additional data for higher-level protocol * handled by hlcbck routine. */ struct pxyconn { int fd; /* filedescriptor */ struct in_addr pxyaddr; /* address of a proxy */ struct in_addr dstaddr; /* address of destination */ ipport_t pxyport; /* port number of a proxy */ ipport_t dstport; /* port number of destination */ const pxyproto_t *proto; /* proxy protocol description */ pxybuf_t *buf; /* buffer holding data read from net */ int pxystate; /* current proxy protocol state */ int appstate; /* current application protocol state */ int nread; /* number of bytes read so far */ void *data; /* app-supplied data */ char *detail; /* strdup'ed additional info if any */ struct ev_tm timer; /* I/O timer */ pxyconn_t *next; /* for linked lists, app usage */ }; extern int pxytimeout; /* almost all routines returns > 0 on ok and 0 on error; * in error case, pxyaction will be called automatically. */ /* allocate new connection structure */ pxyconn_t *pxynew(); /* free connection resources, timers etc */ void pxyfree(pxyconn_t *c); /* start new connection attempt */ int pxystart(pxyconn_t *c, int fd); /* renew a timer; if tmo==0, use pxytimeout */ int pxyrenew(pxyconn_t *c, int tmo, void (*tmfn)(pxyconn_t*)); int pxyreqio(pxyconn_t *c, int e, void(*fn)(pxyconn_t*,int)); int pxyreqiot(pxyconn_t *c, int e, void(*iofn)(pxyconn_t*,int), int tmo, void(*tmfn)(pxyconn_t*)); /* read bytes from connection, return number of bytes read (>=0) or call pxyaction and return -1. In case of EAGAIN, returns 0 if minlen == 0, or -1 as error */ int pxyread(pxyconn_t *c, char *buf, int l, int minlen, int loglevel); /* read next chunk of bytes into pxybuf[] together with saved data, return number of bytes available (>0) or call pxyaction and return 0 */ int pxyreadnext(pxyconn_t *c, int minlen, int *tlen, int loglevel); /* save data from buf of len `len', at most max bytes, within connection structure. Calls pxyaction on error */ int pxysave(pxyconn_t *c, char *buf, unsigned len, unsigned max); /* write something from buf of length len to the remote, with logging */ int pxywrite(pxyconn_t *c, const char *buf, int len, int loglevel); int pxyprintf(pxyconn_t *c, int loglevel, const char *fmt, ...) PRINTFLIKE(3,4); /* app-supplied: */ void pxyinfo(const pxyconn_t *c, int level, const char *fmt, ...) PRINTFLIKE(3,4); void pxyaction(pxyconn_t *c, int result); /* 0 - connected, 1 - done, 2 - definitely not open, -1 - err. * Should free connection in case result != 0, or change event * callback if == 0. */ int pxygetdata(pxyconn_t *c); /* fills in pxybuf[] with app-data for one-shot proxies and * returns length of it */ void pxycheckdata(pxyconn_t *c); /* all-in-once result check */ /* log the raw io: direction is 0 for read and 1 for write */ void pxyvio(pxyconn_t *c, int loglevel, int direction, const char *buf, int bufl); #endif proxycheck-0.49a/event.h0100644000175000017500000000427310056116620013432 0ustar mjtmjt/* $Id: event.h,v 1.7 2004/05/29 14:26:24 mjt Exp $ * Timer and I/O Event header * Author: Michael Tokarev * Licence: LGPL. */ #ifndef _EVENT_H #define _EVENT_H #include struct ev_ct; struct ev_tm; typedef long long ev_time_t; #define EV_SELECT 0x01 #define EV_POLL 0x02 #define EV_EPOLL 0x04 #define EV_KQUEUE 0x08 #define EV_DEVPOLL 0x10 #define EV_ADVANCED (EV_EPOLL|EV_KQUEUE|EV_DEVPOLL) int ev_init(int maxfdhint, int type); void ev_free(void); struct ev_ct *ev_ct_new(int maxfdhint, int type); void ev_ct_free(struct ev_ct *ct); const char *ev_method_name(const struct ev_ct *ct); int ev_method(const struct ev_ct *ct); int ev_wait(struct ev_ct *ct, int timeout); ev_time_t ev_gettime(void); extern ev_time_t ev_now; extern time_t ev_time; int ev_fdlimit(void); /* waiting for I/O */ #define EV_IN 0x01 #define EV_PRI 0x02 #define EV_OUT 0x04 typedef void ev_io_cbck_f(void *data, int revents, int fd, struct ev_ct *ct); int ev_io_add(struct ev_ct *ct, int fd, int events, ev_io_cbck_f *cb, void *data); int ev_io_mod(struct ev_ct *ct, int fd, int events, ev_io_cbck_f *cb, void *data); int ev_io_del(struct ev_ct *ct, int fd); int ev_io_count(const struct ev_ct *ct); /* timers */ typedef void ev_tm_cbck_f(void *data, struct ev_tm *tmr, struct ev_ct *ct); struct ev_tm { struct ev_tm *evtm_prev, *evtm_next; ev_time_t evtm_when; ev_tm_cbck_f *evtm_cbck; void *evtm_data; }; struct ev_tm * ev_tm_add(struct ev_ct *ct, struct ev_tm *tmr, int mstimeout, ev_tm_cbck_f *cb, void *data); struct ev_tm * ev_ts_add(struct ev_ct *ct, struct ev_tm *tmr, int stimeout, ev_tm_cbck_f *cb, void *data); int ev_tm_del(struct ev_ct *ct, struct ev_tm *tmr); int ev_tm_count(const struct ev_ct *ct); ev_time_t ev_tm_first(const struct ev_ct *ct); int ev_tm_timeout(const struct ev_ct *ct); #if 0 typedef void ev_sig_cbck_f(void *data, int sig, struct ev_ct *ct); int ev_sig_add(struct ev_ct *ct, int sig, ev_sig_cbck_f *cbck, void *data); int ev_sig_mod(struct ev_ct *ct, int sig, ev_sig_cbck_f *cbck, void *data); int ev_sig_del(struct ev_ct *ct, int sig); int ev_sig_count(const struct ev_ct *ct); #endif #endif /* include guard */ proxycheck-0.49a/Makefile.in0100644000175000017500000000355410056124515014210 0ustar mjtmjt# $Id: Makefile.in,v 1.19 2004/05/29 15:16:29 mjt Exp $ # Makefile for proxycheck. GPL CC = @CC@ CFLAGS = @CFLAGS@ DEFS = -D_GNU_SOURCE -D_BSD_SOURCE -DHAVE_CONFIG_H LD = $(CC) LDFLAGS = @LDFLAGS@ LIBRESOLV = @LIBRESOLV@ LIBSOCKET = @LIBSOCKET@ USE_CFLAGS = -I. $(CFLAGS) $(DEFS) SRCS = proxycheck.c pxy.c event.c memmem.c proxylogger.c HDRS = pxy.h event.h DIST = $(SRCS) $(HDRS) Makefile.in event.3 proxycheck.1 CHANGES configure VERSION = @VERSION@ VERSION_DATE = @VERSION_DATE@ all: proxycheck proxycheck_OBJS = proxycheck.o pxy.o event.o memmem.o proxycheck: $(proxycheck_OBJS) $(LD) $(LDFLAGS) -o $@ $(proxycheck_OBJS) $(LIBRESOLV) $(LIBSOCKET) $(proxycheck_OBJS): config.h proxylogger_OBJS = proxylogger.o proxylogger: $(proxylogger_OBJS) $(LD) $(LDFLAGS) -o $@ $(proxylogger_OBJS) $(LIBSOCKET) $(proxylogger_OBJS): config.h .c.o: $(CC) $(USE_CFLAGS) -c $< proxycheck.o: proxycheck.c $(CC) $(USE_CFLAGS) -c -DVERSION_STR='"$(VERSION) $(VERSION_DATE)"' $< Makefile config.h: Makefile.in configure CHANGES ./configure @echo @echo Please rerun make. @false event.3.html: event.3 groff -Thtml -mandoc event.3 > $@.tmp mv $@.tmp $@ b = proxycheck-$(VERSION) dist: $(b).tar.gz $(b).tar.gz: $(DIST) rm -rf $(b) mkdir $(b) ln $(DIST) $(b)/ tar cfz $@ $(b) rm -rf $(b) clean: rm -f *~ *.o core config.log config.h conftest* depend dep deps: @echo Generating deps for: @echo \ $(SRCS) @sed '/^# depend/q' Makefile.in > Makefile.tmp @$(CC) $(CFLAGS) -MM $(SRCS) >> Makefile.tmp @if cmp Makefile.tmp Makefile.in ; then \ echo Makefile.in unchanged; \ rm -f Makefile.tmp; \ else \ echo Updating Makfile.in; \ mv -f Makefile.tmp Makefile.in ; \ fi # depend: anything after this line will be replaced by make depend proxycheck.o: proxycheck.c event.h pxy.h pxy.o: pxy.c event.h pxy.h event.o: event.c event.h memmem.o: memmem.c proxylogger.o: proxylogger.c proxycheck-0.49a/event.30100644000175000017500000004463110056122445013351 0ustar mjtmjt.\" $Id: event.3,v 1.7 2004/05/29 14:58:45 mjt Exp $ .\" manpage for event library .TH event 3 .SH NAME event \- timer and I/O event manager .SH SYNOPSYS .nf #include struct \fBev_ct\fR; struct \fBev_tm\fR; struct ev_ct *\fBev_ct_new\fR(int \fImaxfdhint\fR, int \fItypeflags\fR) int \fBev_init\fR(int \fImaxfdhint\fR, int \fItypeflags\fR) void \fBev_ct_free\fR(struct ev_ct *\fIct\fR) void \fBev_free\fR(void) int \fBev_method\fR(const struct ev_ct *\fIct\fR) const char *\fBev_method_name\fR(const struct ev_ct *\fIct\fR) int \fBev_fdlimit\fR(void) typedef void \fBev_io_cbck_f\fR(void *\fIdata\fR, int \fIrevents\fR, int \fIfd\fR, struct ev_ct *\fIct\fR) int \fBev_io_add\fR(struct ev_ct *\fIct\fR, int \fIfd\fR, int \fIevents\fR, ev_io_cbck_f *\fRcbck\fR, void *\fIdata\fR) int \fBev_io_mod\fR(struct ev_ct *\fIct\fR, int \fIfd\fR, int \fIevents\fR, ev_io_cbck_f *\fRcbck\fR, void *\fIdata\fR) int \fBev_io_del\fR(struct ev_ct *\fIct\fR, int \fIfd\fR) int \fBev_io_count\fR(const struct ev_ct *\fIct\fR) typedef void \fBev_tm_cbck_f\fR(void *\fIdata\fR, struct ev_tm *\fItmr\fR, struct ev_ct *\fIct\fR) extern ev_time_t \fBev_now\fR extern time_t \fBev_time\fR ev_time_t \fBev_gettime\fR(void) struct ev_tm *\fBev_tm_add\fR(struct ev_ct *\fIct\fR, int \fImstimeout\fR, struct ev_tm *\fItmr\fR, ev_tm_cbck_f *\fIcbck\fR, void *\fIdata\fR) struct ev_tm *\fBev_ts_add\fR(struct ev_ct *\fIct\fR, int \fIstimeout\fR, struct ev_tm *\fItmr\fR, ev_tm_cbck_f *\fIcbck\fR, void *\fIdata\fR) int \fBev_tm_del\fR(struct ev_ct *\fIct\fR, struct ev_tm *\fItmr\fR) int \fBev_tm_count\fR(const struct ev_ct *\fIct\fR) ev_time_t \fBev_tm_first\fR(const struct ev_ct *\fIct\fR) int \fBev_tm_timeout\fR(const struct ev_ct *\fIct\fR) int \fBev_wait\fR(struct ev_ct *\fIct\fR, int \fImstimeout\fR) .fi .SH DESCRIPTION .PP The event module implements a simple threading core that allows a process to wait for multiple I/O and/or timer events. Multiple I/O streams and timers can be monitored simultaneously. Events are delivered via callback routines provided by the application. When requesting an event, application provides private context that is passed back to the callback routine when the routine is executed, together with some more information about the event. .PP There are two main types of events recognized by the module, and two types of callback routines are defined: I/O events, i.e. when a filedescriptor becomes readable or writable, and timer events, which gets executed once at a given time (specified as an offset from current time). Timers have millisecond resolution. .PP The module uses one of several different underlying mechanisms for event monitoring, including traditional \fBselect\fR(2) and \fBpoll\fR(2), and more advanced methods available for a given operating system, such as \fBepoll\fR(4), \fBkqueue\fR(2) and devpoll (\fBpoll\fR(7d)). .PP There are several sets of routines provided by the module, for the following tasks: initialisation, adding/modifying/removing I/O watchers (keyed by a filedescriptor), adding/removing timers, and performing actual event waiting and dispatching. The following subsections describes each group of routines in turn. .SS Initialisation .PP Every routine in this module requires an \fIevent context\fR of type \fIstruct ev_ct\fR (opaquie to the application), that holds internal state of the module, to be passed as a first argument \fIet\fR. This argument may be NULL pointer, in which case a default event context is used (which should be initialized too). This way, it is possible to use several "contexts", or instances, of the module (for example when an application itself is multi-threaded, with it's own event context in every thread). To initialize and free event context, the following routines are provided: .PP .nf struct ev_ct *\fBev_ct_new\fR(int \fImaxfdhint\fR, int \fItypeflags\fR) int \fBev_init\fR(int \fImaxfdhint\fR, int \fItypeflags\fR) .fi .RS creates and initializes new event context \- either specific, or global default that will be used when no context is provided (when \fIct\fR argument for other routines described below is NULL). One of this routines (or both, to initialize either as many specific or single global context) should be called before any other routine in this module. .PP \fBev_ct_new\fR() return either pointer to newly allocated and initialized event context, or NULL in case of error; \fBev_init\fR() return 0 on success or negative value on error. In case of error, global variable \fIerrno\fR will hold specific error information. .PP Parameter \fImaxfdhint\fR specifies maximum filedescriptor that application expects to monitor (only as a hint, to allocate enouth resources in one go instead of reallocating new memory on demand). If \fImaxfdhint\fR is <= 0, the module will determine maximum number of files a process may open, using \fBev_fdlimit\fR() routine (see below). .PP The \fItypeflags\fR parameter (a bitmask) specifies which underlying method the module should use for event monitoring. Module will choose the "best" method that matches \fItypeflags\fR from ones which are available on a given system. \fItypeflags\fR may be 0, in which case any method may be used. Currently, the following methods are recognized (some are platform-specific): .IP \fBEV_EPOLL\fR epoll, available on Linux 2.6 and above, or with patch on 2.4. .IP \fBEV_KQUEUE\fR kqueue, available on FreeBSD, OpenBSD and some others. .IP \fBEV_DEVPOLL\fR /dev/poll interface with \fBioctl\fR(), available on Solaris. .IP \fBEV_POLL\fR traditional \fBpoll\fR(2) interface, problematic to use when number of filedescriptors being monitored is large. .IP \fBEV_SELECT\fR traditional \fRselect\fR(2) interface, most portable, also problematic when number of filedescriptors is large, and usually have upper limit of 1024 filedescriptors (which may be avoided on some systems). .IP \fBEV_ADVANCED\fR a shortcut for all the more "advanced" methods, the module will choose one available on a given platform if any. .PP Another way to specify which method to use is to set \fB$EV_METHOD\fR environment variable to one of the following values: epoll, kqueue, devpoll, poll, select. .PP The following error conditions are possible: .IP "ENOTSUP or ENOSYS" method(s) requested by \fItypeflags\fR (or by \fB$EV_METHOD\fR variable) isn't supported .IP ENOMEM there is no memory available to initialize structure .RE .PP .nf void \fBev_ct_free\fR(struct ev_ct *\fIct\fR) void \fBev_free\fR(void) .fi .RS deallocates resources assotiated with the given specific event context \fIct\fR or global default context. Calling \fBev_ct_free\fR() with NULL \fIct\fR argument is the same as calling \fBev_free\fR(). Note that these routines does \fInot\fR close opened files or frees timer structures which may be assotiated with the event context, this is the responsibility of an application. Usually, freeing an event context that still have some timers or I/O watchers assotiated with it is an error. .RE .PP .nf int \fBev_method\fR(const struct ev_ct *\fIct\fR) const char *\fBev_method_name\fR(const struct ev_ct *\fIct\fR) .fi .RS return the code (described near \fBev_init\fR() above) or name of underlying operating system mechanism used for monitoring. .RE .PP .nf int \fBev_fdlimit\fR(void) .fi .RS return number of filedescriptors a process may open, according to \fBgetrlimit\fR(2) system call. .RE .SS "Monitoring I/O availability" .PP The module may monitor a set of filedescriptors and call application\-supplied callback routine (of type \fBev_io_cbck_f\fR) when a filedescriptor becomes readable, writable or have an exceptional condition. An application registers a filedescriptor to be monitored, together with a set of conditions of interest, a pointer to callback routine and a pointer to application-specific data. When any of conditions becomes available, module will execute callback routine, passing it the data pointer, a bitmask indicating which conditions become true, and a filedescriptor in question, together with a pointer to assotiated event context. Available conditions are: .IP \fBEV_IN\fR filedescriptor is readable (there's some input data to be read), or for end of file. .IP \fBEV_OUT\fR filedescriptor is writable, usually for sockets when underlying network stack sent buffered data to a peer and more buffer space become available. .IP \fBEV_PRI\fR there's urgent data to be read. .PP The following types and routines are defined: .PP .nf typedef void \fBev_io_cbck_f\fR(void *\fIdata\fR, int \fIrevents\fR, int \fIfd\fR, struct ev_ct *\fIct\fR) .fi .RS The type of application-supplied callback routine which will be called by the module when any of the conditions of interest becomes true. \fIct\fR is the event context assotiated with the event, either specific or default but is never NULL. \fIfd\fR is the file descriptor in question. \fIrevents\fR is a bitmask indicating which conditions (EV_IN, EV_OUT or EV_PRI) are true for the filedescriptor. \fIdata\fR is the application-specific data that was passed to \fBev_io_add\fR() or \fBev_io_mod\fR() (below) when the \fIfd\fR where registered. .PP It is ok to add/remove events from within the callback routine. .PP Note that C language calling rules allows one to use a routine that accepts only a subset of arguments. Namely, a routine that expects only one argument, a pointer to application data (e.g. a structure describing server connection), will act as a callback just fine. .RE .PP .nf int \fBev_io_add\fR(struct ev_ct *\fIct\fR, int \fIfd\fR, int \fIevents\fR, ev_io_cbck_f *\fRcbck\fR, void *\fIdata\fR) .fi .RS Registers the new filedescriptor \fIfd\fR to be monitored for conditions specified by \fIevents\fR parameter, to call a callback routine \fIcbck\fR with data \fIdata\fR when any of the conditions becomes true. Routine return 0 if registration was successeful, or negative value on error (and sets \fIerrno\fR variable appropriately). It is an error to register a filedescriptor twice. The following error conditions are possible: .IP EEXIST \fIfd\fR is already registered with a given event context (for \fBev_io_add\fR() only) .IP ENOENT \fIfd\fR is not registered with a given event context (for \fBev_io_mod\fR() only, see below) .IP EINVAL \fIfd\fR is negative .IP EMFILE \fIfd\fR is too large for underlying monitoring mechanism to handle (e.g. \fBselect\fR(2) usually unable to work when \fIfd\fR >= 1024) .IP ENOMEM there is no memory available to initialize internal structure .IP EFAULT \fIcbck\fR is NULL .RE .PP .nf int \fBev_io_mod\fR(struct ev_ct *\fIct\fR, int \fIfd\fR, int \fIevents\fR, ev_io_cbck_f *\fRcbck\fR, void *\fIdata\fR) .fi .RS Modifies monitoring parameters for already registered filedescriptor \fIfd\fR. Parameters and return value are the same of \fBev_io_add\fR(). .RE .PP .nf int \fBev_io_del\fR(struct ev_ct *\fIct\fR, int \fIfd\fR) .fi .RS Deregisters filedescriptor \fIfd\fR and return 0 on success or negative value (and sets \fBerrno\fR) on failure. It is ok to deregister already closed filedescriptor \fIfd\fR. The following error conditions are possible: .IP ENOENT \fIfd\fR is not registered with this event context. .RE .PP .nf int \fBev_io_count\fR(const struct ev_ct *\fIct\fR) .fi .RS return number of filedescriptors currently registered with the given event context \fIct\fR. The number may be used to watch when there's no filedescriptors to be monitored, as a condition to exit event loop for example. There's no error return. .SS Timers .PP In addition to I/O events, the module also implements concept of a \fItimer\fR, which is once-triggering event based on time. Timer events are delivered by callbacks in a way similar to I/O events. Unlike I/O events, each timer is assotiated with a structure which is owned by application and have to be allocated and freed appropriately (or it may be a part of some larger application structure). When timer event is triggered (i.e. when the module calls the application-supplied callback routine), the timer is already removed from the list of active timers, and pointer to timer structure is passed to the routine. An application may free the storage if it was dynamically allocated, or reuse the timer structure (to implement repeating timers). .PP Module caches current time to reduce system call overhead, updating it during initialisation and at each call to \fBev_wait\fR() dispatching routine (below). In most cases this is sufficient, but an application may update the cached time by calling \fBev_gettime\fR() routine (below). Cached current time is stored in \fBev_time\fR (of type \fBtime_t\fR, with secound resolution), and in \fBev_now\fR (of type \fBlong long\fR) global variables. .PP The following types, routines and variables are provided: .PP .nf typedef void \fBev_tm_cbck_f\fR(void *\fIdata\fR, struct ev_tm *\fItmr\fR, struct ev_ct *\fIct\fR) .fi .RS The type of timer callback routine. When the module calls the timer callback routine, it passes application\-registered data pointer \fIdata\fR, pointer to the timer structure \fItmr\fR and assotiated event context \fIct\fR to it. When the callback routine is executed, the timer in question \fItmr\fR was already removed from set of active timers and was disassotiated from the event context, and may be reused or freed by an application as appropriate. .PP It is ok to add/remove events from within the callback routine, and \fItmr\fR structure may be reused to (re\-)add a timer as appropriate. .PP The same note as given for \fBev_io_cbck_f\fR callback applies here as well: actual callback may expect and handle less parameters than the module passes to it (e.g. usually, only \fIdata\fR pointer is sufficient for an application). .RE .PP .nf extern ev_time_t \fBev_now\fR extern time_t \fBev_time\fR ev_time_t \fBev_gettime\fR(void) .fi .RS cached current time in secounds (\fBev_time\fR) or millisecounds (\fBev_now\fR), and routine that updates the cache and return the same value as it has just stored in \fBev_now\fR. Type \fBev_time_t\fR is a 64bit integer (long long). There is no error return. .RE .nf .PP .nf struct ev_tm *\fBev_tm_add\fR(struct ev_ct *\fIct\fR, int \fImstimeout\fR, struct ev_tm *\fItmr\fR, ev_tm_cbck_f *\fIcbck\fR, void *\fIdata\fR) .fi .RS Registers new timer event to be triggered after \fImstimeout\fR millisecounds from now (since \fBev_now\fR). When the timer will be triggered, the module will call the callback \fIcbck\fR with the value \fIdata\fR. Argument \fItmr\fR may be either NULL, in which case the routine will allocate new timer structure dynamically and return it upon successeful completion, or timer structure already allocated by application, in which case it shold be initialized to zero, and \fImust not\fR already be registered. In either case the application is responsible for freeing memory hold by \fItmr\fR when it will be disassotiated from the event context (either when the module will execute callback routine or after \fBev_tm_del\fR). Routine return pointer to the timer structure, or NULL in case of error (and sets \fBerrno\fR appropriately). .PP Timer structure is opaque for an application, and should be zero-initialized on allocation. The only two fields assessible by the applications are: .nf ev_tm_cbck_f *\fIevtm_cbck\fR void *\fIevtm_data\fR .fi which holds the pointer to the callback routine and the application-supplied data. Both fields may be modified by application while the timer is assotiated with an event context, with the exception that \fIevtm_cbck\fR can not be NULL. .PP Possible \fBerrno\fR values after call to \fBev_tm_add\fR() are: .IP EFAULT \fIcbck\fR parameter is NULL .IP EINVAL \fImstimeout\fR value is negative .IP ENOMEM \fItmr\fR is NULL and there is no memory to allocate new structure. .RE .PP .nf struct ev_tm *\fBev_ts_add\fR(struct ev_ct *\fIct\fR, int \fIstimeout\fR, struct ev_tm *\fItmr\fR, ev_tm_cbck_f *\fIcbck\fR, void *\fIdata\fR) .fi .RS similar to \fBev_tm_add\fR(), with the difference that this one expects timeout in secounds instead of millisecounds, and tries to fire all timers sheduled for the same secound at once (even if they where registered at different millisecounds). .RE .PP .nf int \fBev_tm_del\fR(struct ev_ct *\fIct\fR, struct ev_tm *\fItmr\fR) .fi .RS Removes the given timer \fItmr\fR, which should be registered with the event context \fIct\fR by \fBev_tm_add\fR() routine above. As usual, the application owns the \fItmr\fR structure after the call to this routine. Routine return amount of millisecounds left to the time when the timer should be triggered (which may be 0), or negative value on error. In an attempt to remove a timer which isn't registered (or has been triggered already), routine will indicate error and set \fBerrno\fR to ENOENT. .RE .PP .nf int \fBev_tm_count\fR(const struct ev_ct *\fIct\fR) .fi .RS return number of timers registered with a given event context \fIct\fR. .RE .PP .nf ev_time_t \fBev_tm_first\fR(const struct ev_ct *\fIct\fR) int \fBev_tm_timeout\fR(const struct ev_ct *\fIct\fR) .fi .RS return a time when first timer will be triggered (or 0 if no timers are registered), and amount of millisecounds left to that time (or -1). .RE .SS "Event Loop" .PP The main event loop handling routines are as follows: .PP .nf int \fBev_wait\fR(struct ev_ct *\fIct\fR, int \fImstimeout\fR) .fi .RS one\-shot wait\-and\-dispatch routine. It waits up to \fImstimeout\fR millisecounds (specify negative value for to wait forever) for registered events to happen and executes all necessary callback routines, and when returns. Return value indicates how many I/O events where handled (i.e. how many filedescriptors where ready), which may be 0 in case of timeout or a timer expired, or -1 in case of error. If error occured (typical case is interrupt, in which case \fBerrno\fR will be set to EINTR), \fBev_wait\fR() still executes any pending timers and updates current time cache. For real event loop, an application should call this routine repeatedly until some condition (e.g. number of filedescriptors registered is non-zero) is true. .SH "RETURN VALUES" .PP Most routines in the module return zero or positive integer value in case of error .SH "SEE ALSO" select(2), poll(2), epoll(4), kqueue(2), poll(7d), gettimeofday(2), time(2). .SH BUGS The module is using non-standard time representation (\fBev_time_t\fR type which is currently defined as long long). This may be not portable. But using any standard types (struct timeval, struct timespec and the like) complicates code significantly. .SH AUTHOR This software was written by Michael Tokarev, , with help of ideas from work by Wietse Venema. proxycheck-0.49a/proxycheck.10100644000175000017500000002336010055456013014401 0ustar mjtmjt.\" $Id: proxycheck.1,v 1.7 2004/05/27 17:21:34 mjt Exp $ .\" manpage for proxycheck .\" Michael Tokarev .TH proxycheck 1 .SH NAME proxycheck \- open proxy server checker .SH SYNOPSYS \fBproxycheck\fR \fIoptions\fR \fIhost\fR[:\fIproto_port_spec\fR]... .SH DESCRIPTION \fBproxycheck\fR is a simple open proxy checking tool which is capable to quickly discovery open proxy servers on many hosts. It's primary goal is to detect an open proxy server in order to prevent it's abuse by various "bad guys", mostly spammers. Having a wide-open proxy service running on a publicaly accessible network is a very bad idea nowadays, and \fBproxycheck\fR may be used to find such system in order to be able to either secure a system, or to refuse servicing it until it will be secured properly. In order to determine if a given host is running an open proxy service, \fBproxycheck\fR tries to connect to a given destination system via a host and perform some actions, trying to talk with the destination system. If a talk is successeful, \fBproxycheck\fR assumes the proxy service is running and wide-open. \fBproxycheck\fR supports all commonly used proxy protocols, namely, HTTP CONNECT method, SOCKS versions 4 and 5, and Wingate "telnet"-style proxies. In future, support for more protocols may be added. Please note that with current number of various trojan horses cicrulating around, each opening a proxy on a random port, it is not really enouth to probe for standard (in whatever reason) ports built into the \fBproxycheck\fR. Instead, it is highly recommended to use a list of currently active ports maintained by several people on the 'net. .SH OPTIONS The following command-line options are recognized: .IP \fB\-h\fR print a short help and exit. .IP \fB\-v\fR increase the verbosity level. All debugging messages will go to standard error stream. .IP "\fB\-d\fR \fIdeshost\fR:\fIdestport\fR (required)" try to establish a proxied connection to the given \fIdsthost\fR, port \fIdstport\fR. This option is required. .IP "\fB\-c\fR \fIcheck\fR[:\fIparams\fR] (required)" the "method" \fBproxycheck\fR will use when talking to a destination system to determine if a proxy is open or not. Interpretation of \fIparams\fR is \fIcheck\fR\-dependant. This option is required. Several methods are available: .RS .IP "\fBchat\fR:\fIsendstr\fR:\fIexpectstr\fR" Try to perform simple "chat" with the destination system: send the string given as \fIsendstr\fR and wait for \fIexpectstr\fR on output. If \fIsendstr\fR is empty, \fBproxycheck\fR will send the proxy parameters in the form .br .nf \fIprotocol\fR:\fIip-address\fR:\fIportnumber\fR .br .fi to the remote system. Proxy assumed to be open if \fIexpectstr\fR is found. .IP "\fBdsbl\fR (no parameters accepted)" try to submit all found proxies to the DSBL.org\-like system, see http://dsbl.org/ for more details. All the parameters required (username, password, recipient address, cookie server, ...) are expected to be found in environment variables. Run \fBproxycheck\fR with \fB\-h\fR option to see a list of recognized variables and their default values. By default, \fBproxycheck\fR will anonimously submit all found proxies to unconfirmed.dsbl.org (which isn't very useful). For trusted DSBL user, at least DSBL_USER and DSBL_PASS variables should be set properly. .RE .IP "\fB\-p\fR \fIproto_port_spec\fR" specifies protocol and ports to connect to. If not given, \fBproxycheck\fR will try it's built-in default list. This option may be specified more than once. See below for \fIproto_port_spec\fR. If \fIproto_port_spec\fR is specified for a single host to check, it applies to that host only, and no protocols/ports in default list will be checked for that host. .IP \fB\-D\fR do not reset default port list when using \fB\-p\fR option, but prepend new ports to it instead. .IP \fB\-a\fR use more "advanced" ports/protocols. The more \fB\-a\fR's given, the more ports/protocols will be probed. For a complete list of all ports and protocols and their level, execute \fBproxycheck\fR with \fB\-h\fR option. .IP "\fB\-t\fR \fItimeout\fR" a timeout, in secounds, for every operation. Default value is 30 secounds. The timer starts at the connection attempt to the proxy itself, after sending the "connect" command to the proxy and so on. .IP "\fB\-m\fR \fImaxconn\fR" Do not attempt to make more than \fImaxconn\fR parallel connections. By default, maximum number of parallel connections limited by the operating system and on most systems it is around 1000. .IP "\fB\-M\fR \fImaxhconn\fR" Do not make more than \fImaxhconn\fR parallel connections to the same host (default is unlimited). This may be useful for overloaded proxies which can't handle many parallel connections using different ports/protocols, but may significantly slow down the whole process. .IP \fB\-s\fR when an open proxy is found on a given IP, stop probing for other ports/protocols for this IP. Best used when many IPs are tested, and/or with \fB\-M\fR option. This is because currently, \fBproxycheck\fR will not make any \fInew\fR connections to such host, but will wait for already active connections to complete. .IP "\fB\-b\fR \fIbindaddr\fR" use \fIbindaddr\fR as a source address for all outgoing connections. .IP \fB\-n\fR write a line about definitely closed proxies to stdout in additional to writing about open proxies, in a form .br .nf 127.0.0.1 http:8080 closed .fi .IP \fB\-x\fR print extended proxy information (proxy-agent and the like) if available. This will be on the same "open" (or "closed" with -n) line, last, enclosed in square brackets []. .IP "\fB\-i\fR \fIfilename\fR" read list of hosts to check from a given file \fIfilename\fR (in addition to command line), or from stdin if \fIfilename\fR if `\-'. .RE .SS "Protocol and Port specification" Proxy protocols and ports to try (\fIproto_port_spec\fR) specified using the following syntax: .br .nf [proto:][port,port,port] .br like: .br hc:3128,8080 (http protocol on ports 3128 and 8080) hc: (default list of ports for http protocol) 3128 (try http protocol on standard http port 3128) 1234 (try all protocols on non-standard port 1234) .br .fi Run \fBproxycheck -h\fR to see a list of supported protocols and default ports. .SH USAGE Simplest usage of \fBproxycheck\fR is to try to connect to e.g. your own mailserver with \fBchat\fR check method. First, connect to your mailserver on port 25 to see which line it outputs upon connection (SMTP greething line), and use it with \fBchat\fR: .nf proxycheck -d yourmailserver.example.org:25 \\ -c chat::\fIgreething\fR ip.add.re.ss... .fi \fBproxycheck\fR will write a single line for every proto:port it finds to be open on stdout, in the form: .br .nf 127.0.0.3 hc:80 open .br .fi where \fI127.0.0.3\fR is an IP address of a host being tested, \fIhc\fR is the protocol name (HTTP CONNECT, consult \fBproxycheck \-h\fR for a full list of protocols) and \fI80\fR is a port number where the proxy service is running. In addition, if \fBproxycheck\fR is able to guess \fIoutgoing\fR IP address of a proxy as seen by a destination system, and if that address is different from input \fBproxycheck\fR is connecting to, it will print this information too on the same line, like: .br .nf 127.0.0.2 hc:80 open 127.0.0.3 .br .fi where \fI127.0.0.3\fR is \fIoutgoing\fR IP addres of a multihomed/cascaded proxy as reported by the destination system. This IP address is hint only, there is no simple and reliable way currently exists for \fBproxycheck\fR to determine that information. \fBProxycheck\fR is able to parse a line sent by remote system in \fB\-c chat\fR mode \- in this mode, \fBproxycheck\fR skips all printable characters after \fIexpstr\fR it found and searches for opening `[', when tries to find closing ']' and interpret digits and dots in between as an IP address which gets printed like above. If your mailserver's initial reply contains remote system's IP, or if your mailserver replies with remote system's IP address to HELO/EHLO command, this feature may be useful (in the last case, HELO command should be specified in chat). When \fB\-n\fR option is specified, for proto:ports which aren't running open proxy service, and for which \fBproxycheck\fR is able to strongly determine this, a line in the following format will be written: .br .nf 127.0.0.4 hc:80 closed .br .fi Note however that in most cases there is no way to reliable determine whenever a given service is \fInot\fR open: for example, an open proxy server may be overloaded and refusing connections. In most cases, \fBproxycheck\fR assumes proxy is in unknown state, only a few codes are recognized as real indication of "closed" state. When \fB\-x\fR option is specified, there will be additional proxy info written on the same line (if available), like: .br .nf 127.0.0.2 hc:80 open 127.0.0.3 [AnalogX 3.1415926] 127.0.0.3 hc:80 open [AnalogX 3.1415926] 127.0.0.4 hc:80 closed [AnalogX 3.1415926] .br .fi One may see some detail of \fBproxycheck\fR's operations giving sufficient number of \fB\-v\fR options in the command line. Verbosity level of 5 (\fB\-vvvvv\fR) will show almost everything. All the debugging output will go to the standard error stream and thus will not affect normal operations (when you process \fBproxycheck\fR's output using some script). .SH "EXIT CODE" \fBproxycheck\fR will exit with code 100 if at least one open proxy server was found. In case of incorrect usage, it will exit with code 1. If no open proxies where found, \fBproxycheck\fR will return 0. .SH LICENSE This program is free software. It may be used and distributed in the terms of General Public License (GPL) version 2 or later. .SH AUTHOR \fBproxycheck\fR written by Michael Tokarev . Latest version of this utlilty may be found at http://www.corpit.ru/mjt/proxycheck.html. proxycheck-0.49a/CHANGES0100644000175000017500000001616210056120334013130 0ustar mjtmjt2004-05-29 0.49a - numerous portability fixes here and there - real event.3 manpage 2004-05-27 0.49 - autoconf'ified, sort of - use advanced event mechanisms (epoll, kqueue, devpoll) when available - new option: -i file (or -i -) to read hosts to check from file - add timeout for dsbl cookie 2004-02-17 0.46 - adopted for new DSBL format - changed protocol names: http=>http-connect, ftp=>ftp-user - do not stop on Content-Type: header seen in HTTP-CONNECT responses (what an idiotic software does this?!) - removed obsolete 118[0-4] ports (old mimail variants) 2003-08-07 0.45a - fixed multihomed proxy detection with new DSBL (DSBL now correctly replies with "250 listed [ip.add.re.ss]" instead of "220 listed [ip.add.ress]" to the final end-of-message terminator) 2003-05-11 0.45 - new option -D to not reset default portlist if -p is given - new port - 5490 - NONAME/1.4 HTTP CONNECT-only proxy (trojan?) 2003-05-05 0.44 - added ports 1075 to list of ports for socks and http (MSP proxy) 2003-05-01 0.43 - determine DSBL-listed IP for DSBL submissions (new DSBL listme@ server now allows this by printing an IP that was listed on final \r\n.\r\n reply line) - added some proxy info recognision (proxy-agent - yay!), activate with -x. - removed broken HTTP GET support altogether - moved HTTP PUT and wingate/telnet to be more "advanced" - added port 21 to the list of FTP ports - changed connect timeout to be half of -t - yet another wingate/telnet prompt ("telnet>") - CCProxy Telnet handler - write "open" string w/o stdio/buffering - fixed error (proxycheck always prints "closed" lines ignoring -n flag) 2003-02-20 0.42 - fixed a long-standing bug in dsbl handler - data received from the remote was not collected correctly. Well, this one wasn't happened too frequently - I only seen it once, when a proxy delivered data from the target mailserver one byte at a time. - added yet another port, 1813 (socks5), as advanced (level2) port. SKK proxy listens here, but it is seen unfrequently. 2003-02-14 0.42b2 - added proxylogger - receiving part of proxycheck, to be used from inetd and with -c chat. 2003-02-12 0.42b1 - fixed a small bug in last wingate/telnet code changes (mostly cosmetic: "Resource temporarily unavailable" vs "Connection timed out"). - added another telnet/wingate proxy variant, with a prompt "SpoonProxy>", which expects a command in form "host port" (instead of tn-wg/wingate which is of the form "host:port"). 2003-02-04 0.42b - some code cleanups/changes: may break things, testing... - FTP proxy detection added, port 1183 only for now. Interesting to experiment with M$ ftp servers ;) - fixed not finding advanced protocol if no ports are specified (e.g. -pwg: tried default protos:ports instead of wg:25,1181) - modified telnet/wingate proxy code to be a bit faster and to know which command to sent to a particular proxy. 2003-02-02 0.41 - moved wingate tests to be advanced (do not check by default) - allow to use -cdsbl w/o -d: -c dsbl[:smtpserver[:port]], smtpserver defaults to ${DSBL_SMTP:-mx.listme.dsbl.org} - print info about total number of open ports found in final stats line - new option: -s, to stop trying other ports if one open is found 2003-01-12 0.40 - removed usage of alloca() - pxybuf[] is now signed, to shut up SunPRO C compiler with it's *huge* amount of warnings generated - little cleanups in debug output 2003-01-09 0.40b3 (BETA) - in -c chat, try to find [ip.add.re.ss] after the expect string from the remote system, and if found, write it after "1.2.3.4 hc:80 open" info (Note format change). Destination system may write an address of the system it's talking with this way - it will be output address of a proxy. Take it as a guess only, nothing more - value, if found, cannot be trusted. This is printed only if remote says different address from what we're connecting to. 2003-01-08 0.40b2 (BETA) - moved some ports and protocols to be more "advanced". Check with -h option. - (implementation) reorganized protocol/port specifications Some compilers may choke at the end of pxy.c - will see... 2003-01-02 0.40b1 (BETA) - changed protocol names to be two-char by default (please review your scripts: change is incompatible) - added "advanced" protocol support: currently, this includes HTTP PUT (hu), HTTP GET (hg), HTTP POST (ho). Activated by either -a option or explicitly. 2002-12-31 0.34 - fixed timeradd() macro (missing \) - thanks to Kai Schlichting - send SMTP commands one-at-a-time, 'cause some AV engines intercepting port 25 traffic from a proxy loose commands if sent in one TCP packet This made SMTP code somewhat funny at best. - new option: -M, to limit number of concurrent connections to one host. NOTE: experimental, and has at least one bug which I'll fix soon - added (hackish) check for Content-{length,type}: header in HTTP response - terminate connection if such header present 2002-12-28 0.33 - portability: FreeBSD <5.0 sys/event.h clashes with my event module. No good workaround except of maybe prefixing all names by mjt_ which is ugly. For now, renamed EV_ERROR to EV_ERRNO. - fixed warnings in event.c produced on FreeBSD (casting NULL to pointer) - some infrastructure for handling GET/POST/PUT/etc proxies. Not finished and commented out for now. - changed in_addr_t/in_addr again. Leandro Santi. Now using inet_aton(), which requires -lresolv on solaris. - added a workaround for http proxies which requires \r\n in the separate packet. - little cleanups. 2002-12-24 0.32 - renamed EVENT_xxx to EV_xxx to be consistent across names (no code change) - removed ipaddrport_t altogether (no real need for it) - print octal numbers w/o leading zero; print \t as \t, not as \11 in verbose debug mode - new option -n to print info about closed (definitely) proxies. Before, lines like "127.0.0.2 http:8080 closed" was printed automatically. I.e. restore pre-0.2 behaviour and turn new behaviour on with -n. - warn about being run as root - portability: some systems lacks in_addr_t data type. Heh. Changed usages of in_addr_t to unsigned. Thanks to Leandro Santi lesanti{at}uolsinectis{dot}com{dot}ar. 2002-12-24 0.31 - portability fixes, thanks to Andy Igoshin ai{at}vsu{dot}ru: - AIX has no timeradd, conditionally added timeradd and timercmp to event.c - Solaris has ipaddr_t type - renamed to ipaddrport_t. - fixed a typo: tenlet -> telnet 2002-12-23 0.30 - added a manpage - default values for -c (checkproxy.corpit.ru:25) and -d (chat::ESMTP_unwelcome) - understand trailing colon after a hostname - many cleanups in the code - show protocol exchange for wingate and socks in verbose mode - print "ip.add.re.ss proto:port closed" for proxies which are proved to be closed (note that this may break existing usage of proxycheck - now something may go to stdout even if no open proxy was found - always check return value and/or search for "open") - recognize HTTP reply codes if any - try also "telnet host port" on port 23 (somewhat hackish) proxycheck-0.49a/configure0100755000175000017500000001366510056121061014047 0ustar mjtmjt#! /bin/sh # $Id: configure,v 1.4 2004/05/29 14:46:09 mjt Exp $ # autoconf-style configuration script # Author: Michael Tokarev # License: GPL name=proxycheck case "$1" in --help | --hel | --he | --h | -help | -hel | -he | -h ) cat <&2; exit 1 ;; esac if [ -f proxycheck.c -a -f event.h -a -f CHANGES -a -f Makefile.in ] ; then : else echo "configure: error: sources not found at `pwd`" >&2 exit 1 fi set -e rm -f conftest* confdef* config.log exec 5>config.log cat <&5 This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. EOF cat >confdef.h <>confdef.h } ac_subst() { subst="$subst $*" } ac_error() { if [ -n "$*" ]; then echo "configure: $*" >&2 fi echo "configure: see config.log and conftest.* for possible explanation" exit 1 } ac_checking() { echo $en "checking $1... $ec" >&2 echo >&5 echo "configure: *** checking for $1 ***" >&5 } read VERSION_DATE VERSION < CHANGES ac_subst VERSION VERSION_DATE echo "Configuring $name $VERSION ($VERSION_DATE)" echo ### check for C compiler. Set $CC ### ac_checking "for C compiler" rm -f conftest*; cat >conftest.c <&5 && ./conftest 2>&5 ; then echo "\$CC ($CC)" >&2 else echo no >&2 ac_error "\$CC ($CC) is not a working compiler" fi else for cc in gcc cc ; do if $cc -o conftest conftest.c 2>&5 && ./conftest 2>&5 ; then echo $cc CC=$cc break fi done if [ -z "$CC" ]; then echo no ac_error "no working C compiler found in \$PATH" fi fi ac_subst CC if [ -z "$CFLAGS" ]; then ac_checking "whenever C compiler ($CC) is GNU CC" rm -f conftest*; cat >conftest.c <&5 | grep yes_it_is_gcc >/dev/null ; then echo yes CFLAGS="-Wall -W -O2" else echo no CFLAGS=-O fi fi cc="$CC $CFLAGS" if [ -z "$LDFLAGS" ]; then LDFLAGS='$(CFLAGS)'; ldflags= else ldflags="$LDFLAGS" fi ccld="$cc $ldflags -o conftest conftest.c" cpp="$cc -E conftest.c" cc="$cc -c conftest.c" ac_subst CFLAGS LDFLAGS ac_run() { ac_checking "$1" rm -f conftest*; cat >conftest.c if $ccld $2 2>&5 && ./conftest 2>&5 ; then echo ${3:-yes} >&2 return 0 else echo ${4:-no} >&2 return 1 fi } ac_link() { ac_checking "$1" rm -f conftest*; cat >conftest.c if $ccld $2 2>&5 ; then echo ${3:-yes} >&2 return 0 else echo ${4:-no} >&2 return 1 fi } ac_cpp() { ac_checking "$1" rm -f conftest*; cat >conftest.c if $cpp $2 2>&5 ; then echo ${3:-yes} >&2 return 0 else echo ${4:-no} >&2 return 1 fi } ac_run "whenever C compiler ($CC) works" </dev/null || exit 1 #include int main(int argc, char **argv) { puts("hello, world!"); return 0; } EOF if ac_link "for socket routines" <" </dev/null && ac_define HAVE_SYS_SELECT_H #include #include #include #include int foo() { return 0; } EOF ac_link "for poll()" < #include int main() { struct pollfd pfd[2]; return poll(pfd, 2, 10); } EOF ac_link "for epoll" < #include #include int main() { struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = 0; epoll_create(10); epoll_ctl(10, EPOLL_CTL_ADD, 0, &ev); return 0; } EOF ac_link "for kqueue" < #include #include int main() { struct kevent ke; EV_SET(&ke, 1, EVFILT_READ, EV_ADD, 0, 0, 0); kqueue(); kevent(10, &ke, 1, 0, 0, 0); return 0; } EOF ac_link "for devpoll" < #include #include #include #include int main() { struct pollfd pfd; dvpoll_t dp; pfd.fd = 10; pfd.events = POLLIN|POLLREMOVE; dp.dp_timeout = 10; dp.dp_nfds = 1; dp.dp_fds = &pfd; ioctl(10, DP_POLL, &dp); return 0; } EOF echo "Using the following I/O multiplexing methods: $ev_methods" >&2 ac_link "for memmem()" < int main(int argc, char **argv) { memmem(argv[0], 'a', 10); return 0; } EOF echo $en "creating Makefile... $ec" for var in $subst LIBS DEFS ; do eval echo "\"s|@$var@|\$$var|\"" done >>confdef.sed rm -f Makefile.tmp echo "# Automatically generated from Makefile.in by configure" >Makefile.tmp echo "#" >>Makefile.tmp sed -f confdef.sed Makefile.in >>Makefile.tmp chmod +x Makefile.tmp mv -f Makefile.tmp Makefile echo ok echo $en "creating config.h... $ec" mv -f confdef.h config.h echo ok echo "all done." rm -f conftest* confdef* exit 0