/* * Search the entire arp table, and do some action on matching entries. */ void search(in_addr_t addr, void (*action)(struct sockaddr_dl *sdl, struct sockaddr_inarp *sin, struct rt_msghdr *rtm)) { int mib[7]; size_t needed; char *lim, *buf = NULL, *next; struct rt_msghdr *rtm; struct sockaddr_inarp *sin; struct sockaddr_dl *sdl; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_FLAGS; mib[5] = RTF_LLINFO; mib[6] = getrtable(); while (1) { if (sysctl(mib, 7, NULL, &needed, NULL, 0) == -1) err(1, "route-sysctl-estimate"); if (needed == 0) return; if ((buf = realloc(buf, needed)) == NULL) err(1, "malloc"); if (sysctl(mib, 7, buf, &needed, NULL, 0) == -1) { if (errno == ENOMEM) continue; err(1, "actual retrieval of routing table"); } lim = buf + needed; break; } for (next = buf; next < lim; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; if (rtm->rtm_version != RTM_VERSION) continue; sin = (struct sockaddr_inarp *)(next + rtm->rtm_hdrlen); sdl = (struct sockaddr_dl *)(sin + 1); if (addr) { if (addr != sin->sin_addr.s_addr) continue; found_entry = 1; } (*action)(sdl, sin, rtm); } free(buf); }
int main(int argc, char *argv[]) { int ch, func = 0, rtn; const char *errstr; pid = getpid(); opterr = 0; rdomain = getrtable(); while ((ch = getopt(argc, argv, "andsFfV:W")) != -1) { switch (ch) { case 'a': aflag = 1; break; case 'n': nflag = 1; break; case 'd': if (func) usage(); func = F_DELETE; break; case 's': if (func) usage(); func = F_SET; break; case 'F': replace = 1; break; case 'f': if (func) usage(); func = F_FILESET; break; case 'V': rdomain = strtonum(optarg, 0, RT_TABLEID_MAX, &errstr); if (errstr != NULL) { warn("bad rdomain: %s", errstr); usage(); } break; case 'W': if (func) usage(); func = F_WAKE; break; default: usage(); break; } } argc -= optind; argv += optind; if (!func) func = F_GET; rtn = 0; switch (func) { case F_GET: if (aflag && argc == 0) dump(); else if (!aflag && argc == 1) rtn = get(argv[0]); else usage(); break; case F_SET: if (argc < 2 || argc > 5) usage(); if (replace) delete(argv[0], NULL); rtn = set(argc, argv) ? 1 : 0; break; case F_DELETE: if (aflag && argc == 0) search(0, nuke_entry); else if (!aflag && argc == 1) rtn = delete(argv[0], argv[1]); else usage(); break; case F_FILESET: if (argc != 1) usage(); rtn = file(argv[0]); break; case F_WAKE: if (aflag || nflag || replace || rdomain > 0) usage(); if (argc == 1) rtn = wake(argv[0], NULL); else if (argc == 2) rtn = wake(argv[0], argv[1]); else usage(); break; } return (rtn); }
int allow_data_connection(struct session *s) { struct sockaddr *client_sa, *orig_sa, *proxy_sa, *server_sa; int prepared = 0; /* * The pf rules below do quite some NAT rewriting, to keep up * appearances. Points to keep in mind: * 1) The client must think it's talking to the real server, * for both control and data connections. Transparently. * 2) The server must think that the proxy is the client. * 3) Source and destination ports are rewritten to minimize * port collisions, to aid security (some systems pick weak * ports) or to satisfy RFC requirements (source port 20). */ /* Cast this once, to make code below it more readable. */ client_sa = sstosa(&s->client_ss); server_sa = sstosa(&s->server_ss); proxy_sa = sstosa(&s->proxy_ss); if (fixed_server) /* Fixed server: data connections must appear to come from / go to the original server, not the fixed one. */ orig_sa = sstosa(&s->orig_server_ss); else /* Server not fixed: orig_server == server. */ orig_sa = sstosa(&s->server_ss); /* Passive modes. */ if (s->cmd == CMD_PASV || s->cmd == CMD_EPSV) { s->port = parse_port(s->cmd); if (s->port < MIN_PORT) { logmsg(LOG_CRIT, "#%d bad port in '%s'", s->id, linebuf); return (0); } s->proxy_port = pick_proxy_port(); logmsg(LOG_INFO, "#%d passive: client to server port %d" " via port %d", s->id, s->port, s->proxy_port); if (prepare_commit(s->id) == -1) goto fail; prepared = 1; proxy_reply(s->cmd, orig_sa, s->proxy_port); logmsg(LOG_DEBUG, "#%d proxy: %s", s->id, linebuf); /* pass in from $client to $orig_server port $proxy_port rdr-to $server port $port */ if (add_rdr(s->id, client_sa, s->client_rd, orig_sa, s->proxy_port, server_sa, s->port, getrtable()) == -1) goto fail; /* pass out from $client to $server port $port nat-to $proxy */ if (add_nat(s->id, client_sa, getrtable(), server_sa, s->port, proxy_sa, PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1) goto fail; } /* Active modes. */ if (s->cmd == CMD_PORT || s->cmd == CMD_EPRT) { logmsg(LOG_INFO, "#%d active: server to client port %d" " via port %d", s->id, s->port, s->proxy_port); if (prepare_commit(s->id) == -1) goto fail; prepared = 1; /* pass in from $server to $proxy port $proxy_port rdr-to $client port $port */ if (add_rdr(s->id, server_sa, getrtable(), proxy_sa, s->proxy_port, client_sa, s->port, s->client_rd) == -1) goto fail; /* pass out from $server to $client port $port nat-to $orig_server port $natport */ if (rfc_mode && s->cmd == CMD_PORT) { /* Rewrite sourceport to RFC mandated 20. */ if (add_nat(s->id, server_sa, s->client_rd, client_sa, s->port, orig_sa, 20, 20) == -1) goto fail; } else { /* Let pf pick a source port from the standard range. */ if (add_nat(s->id, server_sa, s->client_rd, client_sa, s->port, orig_sa, PF_NAT_PROXY_PORT_LOW, PF_NAT_PROXY_PORT_HIGH) == -1) goto fail; } } /* Commit rules if they were prepared. */ if (prepared && (do_commit() == -1)) { if (errno != EBUSY) goto fail; /* One more try if busy. */ usleep(5000); if (do_commit() == -1) goto fail; } s->cmd = CMD_NONE; s->port = 0; return (1); fail: logmsg(LOG_CRIT, "#%d pf operation failed: %s", s->id, strerror(errno)); if (prepared) do_rollback(); return (0); }