aboutsummaryrefslogtreecommitdiffstats
path: root/glougloud/glougloud.c
diff options
context:
space:
mode:
authorlaurent <laurent@tigrou.my.domain>2012-03-27 01:21:50 +0200
committerlaurent <laurent@tigrou.my.domain>2012-03-27 01:21:50 +0200
commit0ab94d648289babfbeade80b0ce2d566e2518ed8 (patch)
tree7ce9850e57cac48078b5fb0469243a64996e67da /glougloud/glougloud.c
downloadglouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.tar.xz
glouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.zip
initial import from my hg repository
Diffstat (limited to 'glougloud/glougloud.c')
-rw-r--r--glougloud/glougloud.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/glougloud/glougloud.c b/glougloud/glougloud.c
new file mode 100644
index 0000000..7b86aa4
--- /dev/null
+++ b/glougloud/glougloud.c
@@ -0,0 +1,464 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <pcap.h>
+#include <pcap-int.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include "glougloud.h"
+#include "imsgev.h"
+
+struct server_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+struct user_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+};
+
+// XXX CONF
+#define PCAP_INTERFACE "lo0"
+#define PCAP_SNAPLEN 100
+#define PCAP_FILTER "not port 4430"
+
+static void imsgev_server(struct imsgev *, int, struct imsg *);
+static void imsgev_user(struct imsgev *, int, struct imsg *);
+
+struct server_proc *srv_proc;
+struct user_proc *usr_proc;
+LIST_HEAD(, user) usr_list;
+int net_socket;
+
+void __dead
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-vi]",
+ __progname);
+ exit(1);
+}
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+/*
+ * reimplement pcap_open_live with more restrictions on the bpf fd :
+ * - open device read only
+ * - lock the fd
+ * based on OpenBSD tcpdump, privsep_pcap.c v1.16
+ */
+
+static pcap_t *
+my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms,
+ char *ebuf, u_int dlt, u_int dirfilt)
+{
+ struct bpf_version bv;
+ u_int v;
+ pcap_t *p;
+ char bpf[sizeof "/dev/bpf0000000000"];
+ int fd, n = 0;
+ struct ifreq ifr;
+
+ p = xmalloc(sizeof(*p));
+ bzero(p, sizeof(*p));
+
+ /* priv part */
+
+ do {
+ snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++);
+ fd = open(bpf, O_RDONLY);
+ } while (fd < 0 && errno == EBUSY);
+ if (fd < 0)
+ return NULL;
+
+ v = 32768; /* XXX this should be a user-accessible hook */
+ ioctl(fd, BIOCSBLEN, &v);
+
+ strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) < 0)
+ return NULL;
+
+ if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
+ return NULL;
+
+ if (promisc)
+ /* this is allowed to fail */
+ ioctl(fd, BIOCPROMISC, NULL);
+ if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
+ return NULL;
+
+ /* lock the descriptor */
+ if (ioctl(fd, BIOCLOCK, NULL) < 0)
+ return NULL;
+
+ /* end of priv part */
+
+ /* fd is locked, can only use 'safe' ioctls */
+ if (ioctl(fd, BIOCVERSION, &bv) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+
+ if (bv.bv_major != BPF_MAJOR_VERSION ||
+ bv.bv_minor < BPF_MINOR_VERSION) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "kernel bpf filter out of date");
+ return NULL;
+ }
+
+ p->fd = fd;
+ p->snapshot = slen;
+
+ /* Get the data link layer type. */
+ if (ioctl(fd, BIOCGDLT, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+#if _BSDI_VERSION - 0 >= 199510
+ /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
+ switch (v) {
+
+ case DLT_SLIP:
+ v = DLT_SLIP_BSDOS;
+ break;
+
+ case DLT_PPP:
+ v = DLT_PPP_BSDOS;
+ break;
+ }
+#endif
+ p->linktype = v;
+
+ /* XXX hack from tcpdump */
+ if (p->linktype == DLT_PFLOG && p->snapshot < 160)
+ p->snapshot = 160;
+
+ /* set timeout */
+ if (to_ms != 0) {
+ struct timeval to;
+ to.tv_sec = to_ms / 1000;
+ to.tv_usec = (to_ms * 1000) % 1000000;
+ if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ }
+
+ if (ioctl(fd, BIOCGBLEN, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ p->bufsize = v;
+ p->buffer = (u_char *)malloc(p->bufsize);
+ if (p->buffer == NULL) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ return p;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_in sock_addr;
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct bpf_program bprog;
+ pcap_t *pcap;
+ int loglevel = 0, logpinvalid = 0;
+ int op;
+ int sock_on = 1;
+
+ if (geteuid() != 0)
+ errx(1, "need root privileges");
+
+ while ((op = getopt(argc, argv, "hvi")) != -1) {
+ switch (op) {
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'v':
+ loglevel++;
+ break;
+ case 'i':
+ logpinvalid = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ log_init(loglevel, logpinvalid);
+ event_init();
+
+ net_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (net_socket < 0)
+ fatal("server: socket failed");
+ setsockopt(net_socket, SOL_SOCKET, SO_REUSEADDR,
+ &sock_on, sizeof(sock_on));
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_addr.sin_port = htons(4430);
+ if (bind(net_socket, (struct sockaddr *)&sock_addr,
+ sizeof(sock_addr)) != 0)
+ fatal("server: socket bind failed, %s", strerror(errno));
+ fd_nonblock(net_socket);
+
+ srv_proc = xmalloc(sizeof(struct server_proc));
+ socketpair_prepare(srv_proc->fd);
+ srv_proc->pid = server_init(srv_proc->fd);
+ close(srv_proc->fd[1]);
+ imsgev_init(&srv_proc->iev, srv_proc->fd[0], NULL, imsgev_server);
+
+ pcap = my_pcap_open_live(PCAP_INTERFACE, PCAP_SNAPLEN, 1, PCAP_TO, errbuf, -1, 0);
+ if (pcap == NULL)
+ fatal("capture: pcap_open_live failed on interface %s\n"
+ "with snaplen %d : %s",
+ PCAP_INTERFACE, PCAP_SNAPLEN, errbuf);
+ if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0)
+ fatal("capture: pcap_compile failed with filter %s : %s",
+ PCAP_FILTER, pcap_geterr(pcap));
+ if (pcap_setfilter(pcap, &bprog) < 0)
+ fatal("capture: pcap_setfilter failed : %s",
+ pcap_geterr(pcap));
+
+ usr_proc = xmalloc(sizeof(struct user_proc));
+ socketpair_prepare(usr_proc->fd);
+ usr_proc->pid = user_init(usr_proc->fd, pcap);
+ close(usr_proc->fd[1]);
+ imsgev_init(&usr_proc->iev, usr_proc->fd[0], NULL, imsgev_user);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ log_info("entering event loop");
+ event_dispatch();
+
+ log_info("exiting");
+ exit(0);
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote) {
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+srvconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_srvconn *req;
+ struct imsg_srvconn res;
+ struct imsg_usrconn req2;
+ struct user *usr;
+
+ req = imsg->data;
+
+ if (req->deco) {
+ log_tmp("srvconn deco");
+ usr = finduser(&req->addr);
+ if (!usr)
+ fatal("trying to deco an inexistant user !");
+
+ addrcpy(&req2.addr, &usr->addr);
+ req2.deco = 1;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+
+ return;
+ }
+
+ if (req->addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+ log_tmp("srvconn accepted");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &req->addr);
+ usr->status = USER_REQUESTED;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req2.addr, &req->addr);
+ req2.deco = 0;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+ }
+ else
+ {
+ log_tmp("srvconn refused");
+ res.ok = 0;
+ addrcpy(&res.addr, &req->addr);
+ imsgev_compose(iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res, sizeof(res));
+ }
+
+}
+
+static void
+imsgev_server(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_REQ:
+ srvconn(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev server read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
+static void
+usrconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct user *usr;
+ struct imsg_usrconn *res;
+ struct imsg_srvconn res2;
+
+ res = imsg->data;
+
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("received usrconn result that wasn't initiated !");
+ usr->status = USER_ACCEPTED;
+
+ addrcpy(&res2.addr, &res->addr);
+ res2.ok = 1;
+ imsgev_compose(&srv_proc->iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res2, sizeof(res2));
+}
+
+/* XXX drop imsg when msgbuf->queued is too high ? */
+/* XXX review that func for correctness and security */
+static void
+usrdns(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrdns_req *req;
+ struct imsg_usrdns_res res;
+ struct hostent *ent;
+ int len;
+ char *name;
+ extern int h_errno;
+ struct in_addr addr;
+
+ req = imsg->data;
+
+ res.addr.s_addr = req->addr.s_addr;
+ addr.s_addr = htonl(req->addr.s_addr);
+ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
+ if (ent) {
+ len = strlen(ent->h_name);
+ if (len > DNSNAME_MAX)
+ name = ent->h_name - DNSNAME_MAX; /* keep right part */
+ else
+ name = ent->h_name;
+ strncpy(res.name, name, sizeof(res.name));
+ } else {
+ log_debug("usrdns failed for %x : %s",
+ addr, hstrerror(h_errno));
+ res.name[0] = '\0';
+ }
+
+ imsgev_compose(&usr_proc->iev, IMSG_USRDNS_RES, 0, 0, -1,
+ &res, sizeof(res));
+}
+
+static void
+imsgev_user(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_USRCONN_RES:
+ usrconn(iev, imsg);
+ break;
+ case IMSG_USRDNS_REQ:
+ usrdns(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev user read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+