aboutsummaryrefslogtreecommitdiffstats
path: root/glougloud/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'glougloud/server.c')
-rw-r--r--glougloud/server.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/glougloud/server.c b/glougloud/server.c
new file mode 100644
index 0000000..196c3a2
--- /dev/null
+++ b/glougloud/server.c
@@ -0,0 +1,230 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+
+#include "glougloud.h"
+#include "imsgev.h"
+
+#define USER_TIMEOUT 300
+#define USERTIMER 10
+
+struct server {
+ struct imsgev iev;
+ struct event ev;
+
+ struct event usrtimer_ev;
+ struct timeval usrtimer_tv;
+ time_t time;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED,
+ USER_REFUSED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+ time_t lastseen;
+};
+
+static void imsgev_main(struct imsgev *, int, struct imsg *);
+static void receive(int, short, void *);
+static void ev_usrtimer(int, short, void *);
+
+struct server *srv;
+LIST_HEAD(, user) usr_list;
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("server: got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+int
+server_init(int fd[2])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int pid;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("server fork");
+ if (pid > 0)
+ return pid;
+
+ setproctitle("server");
+ event_init();
+ close(fd[0]);
+
+ 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);
+
+ srv = xmalloc(sizeof(struct server));
+ imsgev_init(&srv->iev, fd[1], NULL, &imsgev_main);
+ srv->time = time(NULL);
+
+ event_set(&srv->ev, net_socket, EV_READ|EV_PERSIST, receive, NULL);
+ event_add(&srv->ev, NULL);
+
+ srv->usrtimer_tv.tv_sec = USERTIMER;
+ srv->usrtimer_tv.tv_usec = 0;
+ evtimer_set(&srv->usrtimer_ev, ev_usrtimer, NULL);
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimr failed: %s", strerror(errno));
+
+ droppriv();
+
+ log_info("server: entering event loop");
+ event_dispatch();
+
+ log_info("server: 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 *res;
+ struct user *usr;
+
+ res = imsg->data;
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("server: received srvconn response for inexistant connection");
+ if (res->ok) {
+ usr->status = USER_ACCEPTED;
+ } else {
+ usr->status = USER_REFUSED;
+ }
+}
+
+static void
+imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("server: %s got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_RES:
+ 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 read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+receive(int fd, short why, void *data)
+{
+ struct sockaddr_in remote;
+ struct imsg_srvconn req;
+ socklen_t len;
+ char buf[16384];
+ struct user *usr;
+ int rv;
+
+ len = sizeof(remote);
+ rv = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote, &len);
+ if (rv < 0) {
+ log_warn("server: recvfrom failed");
+ return;
+ }
+ usr = finduser(&remote);
+ if (usr) {
+ switch (usr->status) {
+ case USER_ACCEPTED:
+ log_tmp("server: data for existing user");
+ break;
+ case USER_REQUESTED:
+ log_warn("server: data for not yet accepted user");
+ break;
+ case USER_REFUSED:
+ log_warn("server: data for banned user !");
+ break;
+ }
+ } else {
+ log_debug("server: new user request");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &remote);
+ usr->status = USER_REQUESTED;
+ usr->lastseen = srv->time;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req.addr, &remote);
+ req.deco = 0;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+ }
+}
+
+static void
+ev_usrtimer(int fd, short why, void *data)
+{
+ struct user *usr;
+ struct imsg_srvconn req;
+
+ srv->time = time(NULL);
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ if (srv->time > usr->lastseen + USER_TIMEOUT) {
+ addrcpy(&req.addr, &usr->addr);
+ req.deco = 1;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+ }
+ }
+
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimer failed : %s", strerror(errno));
+}