diff options
author | laurent <laurent@tigrou.my.domain> | 2012-03-27 01:21:50 +0200 |
---|---|---|
committer | laurent <laurent@tigrou.my.domain> | 2012-03-27 01:21:50 +0200 |
commit | 0ab94d648289babfbeade80b0ce2d566e2518ed8 (patch) | |
tree | 7ce9850e57cac48078b5fb0469243a64996e67da | |
download | glouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.tar.xz glouglou-0ab94d648289babfbeade80b0ce2d566e2518ed8.zip |
initial import from my hg repository
-rwxr-xr-x | glouglou_ruby/aa_test.rb | 304 | ||||
-rwxr-xr-x | glouglou_ruby/glouglou_ruby.rb | 304 | ||||
-rw-r--r-- | glouglou_ruby/vars | 2 | ||||
-rw-r--r-- | glougloud/Makefile | 12 | ||||
-rw-r--r-- | glougloud/README | 1 | ||||
-rwxr-xr-x | glougloud/glougloud | bin | 0 -> 74978 bytes | |||
-rw-r--r-- | glougloud/glougloud.c | 464 | ||||
-rw-r--r-- | glougloud/glougloud.h | 72 | ||||
-rw-r--r-- | glougloud/glougloud.o | bin | 0 -> 24004 bytes | |||
-rw-r--r-- | glougloud/imsgev.c | 160 | ||||
-rw-r--r-- | glougloud/imsgev.h | 48 | ||||
-rw-r--r-- | glougloud/imsgev.o | bin | 0 -> 9212 bytes | |||
-rw-r--r-- | glougloud/server.c | 230 | ||||
-rw-r--r-- | glougloud/server.o | bin | 0 -> 14224 bytes | |||
-rw-r--r-- | glougloud/user.c | 838 | ||||
-rw-r--r-- | glougloud/user.o | bin | 0 -> 35556 bytes | |||
-rw-r--r-- | glougloud/util.c | 188 | ||||
-rw-r--r-- | glougloud/util.o | bin | 0 -> 11692 bytes |
18 files changed, 2623 insertions, 0 deletions
diff --git a/glouglou_ruby/aa_test.rb b/glouglou_ruby/aa_test.rb new file mode 100755 index 0000000..c5d2af8 --- /dev/null +++ b/glouglou_ruby/aa_test.rb @@ -0,0 +1,304 @@ +#!/usr/bin/env ruby + +# ported from examples/aa_test.cpp in AGG source tree. + +top = File.expand_path(File.join(File.dirname(__FILE__), "..", "..")) +src = File.join(top, "src") +$LOAD_PATH.unshift src +$LOAD_PATH.unshift File.join(src, "lib") + +require 'gtk2' + +window = Gtk::Window.new +window.set_default_size(480, 350) + +area = Gtk::DrawingArea.new + +def stroke_line(context, x1, y1, x2, y2, line_width, dash_length) + context.save do + yield if block_given? + context.move_to(x1, y1) + context.line_to(x2, y2) + context.set_dash(dash_length) if dash_length > 0 + context.line_width = line_width + context.line_cap = :round + context.stroke + end +end + +def fill_triangle(context, x1, y1, x2, y2, x3, y3) + context.save do + yield if block_given? + context.triangle(x1, y1, x2, y2, x3, y3) + context.fill + end +end + +def set_gradient(context, x1, y1, x2, y2, color_stops) + pattern = Cairo::LinearPattern.new(x1, y1, x2, y2) + color_stops.each do |offset, color| + pattern.add_color_stop(offset, color) + end + context.set_source(pattern) +end + +def stroke_gradient_line(context, x1, y1, x2, y2, + line_width, dash_length, color_stops) + stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do + set_gradient(context, x1, y1, x2, y2, color_stops) + end +end + +def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops) + fill_triangle(context, x1, y1, x2, y2, x3, y3) do + set_gradient(context, + x1, y1, + x3 - (x3 - x2) / 2.0, + y3 - (y3 - y2) / 2.0, + color_stops) + end +end + +def draw_background_circle(context, w, h) + context.set_source_color([1.0, 1.0, 1.0, 0.2]) + + cx = w / 2.0 + cy = h / 2.0 + radius = [cx, cy].min + 1.upto(180) do |i| + n = 2 * Math::PI * i / 180.0 + stroke_line(context, + cx + radius * Math.sin(n), + cy + radius * Math.cos(n), + cx, cy, + 1.0, i < 90 ? i : 0) + end +end + +def draw_upper_small_circles(context, i) + context.circle(18 + i * 4 + 0.5, + 33 + 0.5, + i / 20.0) + context.fill + + context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5, + 27 + (i - 1) / 10.0 + 0.5, + 0.5) + context.fill +end + +def draw_upper_circles(context, i) + context.set_source_color(:white) + context.circle(20 + i * (i + 1) + 0.5, + 20.5, + i / 2.0) + context.fill + + draw_upper_small_circles(context, i) +end + +def draw_upper_gradient_lines(context, i) + stroke_gradient_line(context, + 20 + i * (i + 1), + 40.5, + 20 + i * (i + 1) + (i - 1) * 4, + 100.5, + i, + 0, + [[0, :white], + [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]]) +end + +def draw_middle_small_circles(context, i) + stroke_gradient_line(context, + 17.5 + i * 4, + 107, + 17.5 + i * 4 + i / 6.66666667, + 107, + 1, + 0, + [[0, :red], + [1, :blue]]) + + stroke_gradient_line(context, + 18 + i * 4, + 112.5, + 18 + i * 4, + 112.5 + i / 6.66666667, + 1, + 0, + [[0, :red], + [1, :blue]]) +end + +def draw_middle_gradient_lines(context, i) + stroke_gradient_line(context, + 21.5, + 120 + (i - 1) * 3.1, + 52.5, + 120 + (i - 1) * 3.1, + 1, + 0, + [[0, :red], + [1, :white]]) + + stroke_gradient_line(context, + 52.5, + 118 + i * 3, + 83.5, + 118 + i * 3, + 2 - (i - 1) / 10.0, + 0, + [[0, :green], + [1, :white]]) + + stroke_gradient_line(context, + 83.5, + 119 + i * 3, + 114.5, + 119 + i * 3, + 2 - (i - 1) / 10.0, + 3.0, + [[0, :blue], + [1, :white]]) +end + +def draw_middle_white_lines(context, i) + context.set_source_color(:white) + stroke_line(context, + 125.5, 119.5 + (i + 2) * (i / 2.0), + 135.5, 119.5 + (i + 2) * (i / 2.0), + i, 0) +end + +def draw_lower_short_lines(context, i) + context.set_source_color(:white) + + stroke_line(context, + 17.5 + i * 4, 192, + 18.5 + i * 4, 192, + i / 10.0, 0) + + stroke_line(context, + 17.5 + i * 4 + (i - 1) / 10.0, 186, + 18.5 + i * 4 + (i - 1) / 10.0, 186, + 1.0, 0) +end + +def draw_right_triangles(context, i, w, h) + x1 = w - 150 + x2 = w - 20 + y_upper = h - 20 - i * (i + 2) + y_middle = h - 20 - i * (i + 1.5) + y_lower = h - 20 - i * (i + 1) + context.save do + pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle) + pattern.add_color_stop(0, :white) + pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]) + context.set_source(pattern) + context.move_to(x1, y_middle) + context.line_to(x2, y_lower) + context.line_to(x2, y_upper) + context.fill + end +end + +def draw_sample_shapes(context, w, h) + context.set_source_color(:black) + context.paint + + draw_background_circle(context, w, h) + + 1.upto(20) do |i| + draw_upper_circles(context, i) + draw_upper_gradient_lines(context, i) + draw_middle_small_circles(context, i) + draw_middle_gradient_lines(context, i) + draw_middle_white_lines(context, i) if i <= 10 + draw_lower_short_lines(context, i) + end + + 1.upto(13) do |i| + draw_right_triangles(context, i, w, h) + end +end + +def draw_random_shapes(context, w, h) + srand(123) + + context.set_source_color(:black) + context.paint + + n_circles = 20000 + start = Time.now + n_circles.times do |i| + context.circle(rand(w), rand(h), rand(20.0) + 1.0) + context.set_source_color([rand, rand, rand, 0.5 + rand / 2]) + context.fill + end + circle_draw_time = Time.now - start + + n_lines = 2000 + start = Time.now + n_lines.times do |i| + x1 = rand(w) + y1 = rand(h) + stroke_gradient_line(context, + x1, y1, + x1 + rand(w * 0.5) - w * 0.25, + y1 + rand(h * 0.5) - h * 0.25, + 10.0, 0, + [[0, [rand, rand, rand, 0.5 + rand / 2]], + [1, [rand, rand, rand, rand]]]) + end + line_draw_time = Time.now - start + + n_triangles = 2000 + start = Time.now + n_triangles.times do |i| + x1 = rand(w) + y1 = rand(h) + fill_gradient_triangle(context, + x1, y1, + x1 + rand(w * 0.4) - w * 0.2, + y1 + rand(h * 0.4) - h * 0.2, + x1 + rand(w * 0.4) - w * 0.2, + y1 + rand(h * 0.4) - h * 0.2, + [[0, [rand, rand, rand, 0.5 + rand / 2]], + [0.5, [rand, rand, rand, rand]], + [1, [rand, rand, rand, rand]]]) + end + triangle_draw_time = Time.now - start + + puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" % + [n_circles.to_f / circle_draw_time, + n_lines.to_f / line_draw_time, + n_triangles.to_f / triangle_draw_time] +end + +random_mode = false + +area.add_events(Gdk::Event::BUTTON_PRESS_MASK) +area.signal_connect("button-press-event") do |widget, event| + random_mode = !random_mode + widget.queue_draw + false +end + +area.signal_connect("expose-event") do |widget, event| + context = widget.window.create_cairo_context + x, y, w, h = widget.allocation.to_a + if random_mode + draw_random_shapes(context, w, h) + else + draw_sample_shapes(context, w, h) + end + true +end + +window.add(area) + +window.show_all + +Gtk.main + diff --git a/glouglou_ruby/glouglou_ruby.rb b/glouglou_ruby/glouglou_ruby.rb new file mode 100755 index 0000000..c5d2af8 --- /dev/null +++ b/glouglou_ruby/glouglou_ruby.rb @@ -0,0 +1,304 @@ +#!/usr/bin/env ruby + +# ported from examples/aa_test.cpp in AGG source tree. + +top = File.expand_path(File.join(File.dirname(__FILE__), "..", "..")) +src = File.join(top, "src") +$LOAD_PATH.unshift src +$LOAD_PATH.unshift File.join(src, "lib") + +require 'gtk2' + +window = Gtk::Window.new +window.set_default_size(480, 350) + +area = Gtk::DrawingArea.new + +def stroke_line(context, x1, y1, x2, y2, line_width, dash_length) + context.save do + yield if block_given? + context.move_to(x1, y1) + context.line_to(x2, y2) + context.set_dash(dash_length) if dash_length > 0 + context.line_width = line_width + context.line_cap = :round + context.stroke + end +end + +def fill_triangle(context, x1, y1, x2, y2, x3, y3) + context.save do + yield if block_given? + context.triangle(x1, y1, x2, y2, x3, y3) + context.fill + end +end + +def set_gradient(context, x1, y1, x2, y2, color_stops) + pattern = Cairo::LinearPattern.new(x1, y1, x2, y2) + color_stops.each do |offset, color| + pattern.add_color_stop(offset, color) + end + context.set_source(pattern) +end + +def stroke_gradient_line(context, x1, y1, x2, y2, + line_width, dash_length, color_stops) + stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do + set_gradient(context, x1, y1, x2, y2, color_stops) + end +end + +def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops) + fill_triangle(context, x1, y1, x2, y2, x3, y3) do + set_gradient(context, + x1, y1, + x3 - (x3 - x2) / 2.0, + y3 - (y3 - y2) / 2.0, + color_stops) + end +end + +def draw_background_circle(context, w, h) + context.set_source_color([1.0, 1.0, 1.0, 0.2]) + + cx = w / 2.0 + cy = h / 2.0 + radius = [cx, cy].min + 1.upto(180) do |i| + n = 2 * Math::PI * i / 180.0 + stroke_line(context, + cx + radius * Math.sin(n), + cy + radius * Math.cos(n), + cx, cy, + 1.0, i < 90 ? i : 0) + end +end + +def draw_upper_small_circles(context, i) + context.circle(18 + i * 4 + 0.5, + 33 + 0.5, + i / 20.0) + context.fill + + context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5, + 27 + (i - 1) / 10.0 + 0.5, + 0.5) + context.fill +end + +def draw_upper_circles(context, i) + context.set_source_color(:white) + context.circle(20 + i * (i + 1) + 0.5, + 20.5, + i / 2.0) + context.fill + + draw_upper_small_circles(context, i) +end + +def draw_upper_gradient_lines(context, i) + stroke_gradient_line(context, + 20 + i * (i + 1), + 40.5, + 20 + i * (i + 1) + (i - 1) * 4, + 100.5, + i, + 0, + [[0, :white], + [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]]) +end + +def draw_middle_small_circles(context, i) + stroke_gradient_line(context, + 17.5 + i * 4, + 107, + 17.5 + i * 4 + i / 6.66666667, + 107, + 1, + 0, + [[0, :red], + [1, :blue]]) + + stroke_gradient_line(context, + 18 + i * 4, + 112.5, + 18 + i * 4, + 112.5 + i / 6.66666667, + 1, + 0, + [[0, :red], + [1, :blue]]) +end + +def draw_middle_gradient_lines(context, i) + stroke_gradient_line(context, + 21.5, + 120 + (i - 1) * 3.1, + 52.5, + 120 + (i - 1) * 3.1, + 1, + 0, + [[0, :red], + [1, :white]]) + + stroke_gradient_line(context, + 52.5, + 118 + i * 3, + 83.5, + 118 + i * 3, + 2 - (i - 1) / 10.0, + 0, + [[0, :green], + [1, :white]]) + + stroke_gradient_line(context, + 83.5, + 119 + i * 3, + 114.5, + 119 + i * 3, + 2 - (i - 1) / 10.0, + 3.0, + [[0, :blue], + [1, :white]]) +end + +def draw_middle_white_lines(context, i) + context.set_source_color(:white) + stroke_line(context, + 125.5, 119.5 + (i + 2) * (i / 2.0), + 135.5, 119.5 + (i + 2) * (i / 2.0), + i, 0) +end + +def draw_lower_short_lines(context, i) + context.set_source_color(:white) + + stroke_line(context, + 17.5 + i * 4, 192, + 18.5 + i * 4, 192, + i / 10.0, 0) + + stroke_line(context, + 17.5 + i * 4 + (i - 1) / 10.0, 186, + 18.5 + i * 4 + (i - 1) / 10.0, 186, + 1.0, 0) +end + +def draw_right_triangles(context, i, w, h) + x1 = w - 150 + x2 = w - 20 + y_upper = h - 20 - i * (i + 2) + y_middle = h - 20 - i * (i + 1.5) + y_lower = h - 20 - i * (i + 1) + context.save do + pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle) + pattern.add_color_stop(0, :white) + pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]) + context.set_source(pattern) + context.move_to(x1, y_middle) + context.line_to(x2, y_lower) + context.line_to(x2, y_upper) + context.fill + end +end + +def draw_sample_shapes(context, w, h) + context.set_source_color(:black) + context.paint + + draw_background_circle(context, w, h) + + 1.upto(20) do |i| + draw_upper_circles(context, i) + draw_upper_gradient_lines(context, i) + draw_middle_small_circles(context, i) + draw_middle_gradient_lines(context, i) + draw_middle_white_lines(context, i) if i <= 10 + draw_lower_short_lines(context, i) + end + + 1.upto(13) do |i| + draw_right_triangles(context, i, w, h) + end +end + +def draw_random_shapes(context, w, h) + srand(123) + + context.set_source_color(:black) + context.paint + + n_circles = 20000 + start = Time.now + n_circles.times do |i| + context.circle(rand(w), rand(h), rand(20.0) + 1.0) + context.set_source_color([rand, rand, rand, 0.5 + rand / 2]) + context.fill + end + circle_draw_time = Time.now - start + + n_lines = 2000 + start = Time.now + n_lines.times do |i| + x1 = rand(w) + y1 = rand(h) + stroke_gradient_line(context, + x1, y1, + x1 + rand(w * 0.5) - w * 0.25, + y1 + rand(h * 0.5) - h * 0.25, + 10.0, 0, + [[0, [rand, rand, rand, 0.5 + rand / 2]], + [1, [rand, rand, rand, rand]]]) + end + line_draw_time = Time.now - start + + n_triangles = 2000 + start = Time.now + n_triangles.times do |i| + x1 = rand(w) + y1 = rand(h) + fill_gradient_triangle(context, + x1, y1, + x1 + rand(w * 0.4) - w * 0.2, + y1 + rand(h * 0.4) - h * 0.2, + x1 + rand(w * 0.4) - w * 0.2, + y1 + rand(h * 0.4) - h * 0.2, + [[0, [rand, rand, rand, 0.5 + rand / 2]], + [0.5, [rand, rand, rand, rand]], + [1, [rand, rand, rand, rand]]]) + end + triangle_draw_time = Time.now - start + + puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" % + [n_circles.to_f / circle_draw_time, + n_lines.to_f / line_draw_time, + n_triangles.to_f / triangle_draw_time] +end + +random_mode = false + +area.add_events(Gdk::Event::BUTTON_PRESS_MASK) +area.signal_connect("button-press-event") do |widget, event| + random_mode = !random_mode + widget.queue_draw + false +end + +area.signal_connect("expose-event") do |widget, event| + context = widget.window.create_cairo_context + x, y, w, h = widget.allocation.to_a + if random_mode + draw_random_shapes(context, w, h) + else + draw_sample_shapes(context, w, h) + end + true +end + +window.add(area) + +window.show_all + +Gtk.main + diff --git a/glouglou_ruby/vars b/glouglou_ruby/vars new file mode 100644 index 0000000..e7d6c90 --- /dev/null +++ b/glouglou_ruby/vars @@ -0,0 +1,2 @@ +export RUBYLIB=`find /usr/local/lib/ruby/gems/1.8/gems/ -type d -path "*/lib" |xargs |tr ' ' ':'` +export LD_PRELOAD="/usr/lib/libpthread.so.13.1" diff --git a/glougloud/Makefile b/glougloud/Makefile new file mode 100644 index 0000000..6def8e7 --- /dev/null +++ b/glougloud/Makefile @@ -0,0 +1,12 @@ +PROG = glougloud +OBJS = glougloud.o server.o user.o util.o imsgev.o +CFLAGS+=-Wall -g +LDFLAGS=-lpcap -levent -lutil + +all: + make $(OBJS) + $(CC) $(OBJS) -o $(PROG) $(LDFLAGS) + +clean: + rm -f $(PROG) *~ *.o + diff --git a/glougloud/README b/glougloud/README new file mode 100644 index 0000000..07edeb6 --- /dev/null +++ b/glougloud/README @@ -0,0 +1 @@ +glougloud - glouglou server, for network traffic visualisation in real time diff --git a/glougloud/glougloud b/glougloud/glougloud Binary files differnew file mode 100755 index 0000000..bf4f24a --- /dev/null +++ b/glougloud/glougloud 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; + } +} + diff --git a/glougloud/glougloud.h b/glougloud/glougloud.h new file mode 100644 index 0000000..6d1359b --- /dev/null +++ b/glougloud/glougloud.h @@ -0,0 +1,72 @@ +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <stdio.h> +#include <pcap.h> + +#define GLOUGLOUD_USER "_glougloud" +#define PCAP_COUNT 20 +#define PCAP_TO 300 +#define DNSNAME_MAX 20 + +/* ipc */ + +enum imsg_type { + IMSG_SRVCONN_REQ, + IMSG_SRVCONN_RES, + IMSG_USRCONN_REQ, + IMSG_USRCONN_RES, + IMSG_USRDNS_REQ, + IMSG_USRDNS_RES +}; + +/* XXX restore the _req _res structs, so that we have less bytes going on + * the pipe */ +struct imsg_srvconn { + struct sockaddr_in addr; + u_short ok; + u_short deco; +}; +struct imsg_usrconn { + struct sockaddr_in addr; + u_short ok; + u_short deco; +}; +struct imsg_usrdns_req { + struct in_addr addr; +}; +struct imsg_usrdns_res { + struct in_addr addr; + char name[DNSNAME_MAX]; +}; + +/* glougloud.c */ + +extern int net_socket; + +/* server.c */ + +int server_init(int *); + +/* user.c */ + +int user_init(int *, pcap_t *pcap); + +/* util.c */ + +void log_init(int, int); +void log_pinvalid(const char *, ...); +void log_tmp(const char *, ...); +void log_debug(const char *, ...); +void log_info(const char *, ...); +void log_warn(const char *, ...); +void fatal(const char *, ...); + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void fd_nonblock(int); +void droppriv(); +void addrcpy(struct sockaddr_in *, struct sockaddr_in *); +void socketpair_prepare(int *); + diff --git a/glougloud/glougloud.o b/glougloud/glougloud.o Binary files differnew file mode 100644 index 0000000..29614eb --- /dev/null +++ b/glougloud/glougloud.o diff --git a/glougloud/imsgev.c b/glougloud/imsgev.c new file mode 100644 index 0000000..ba577aa --- /dev/null +++ b/glougloud/imsgev.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2009 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "imsgev.h" + +void imsgev_add(struct imsgev *); +void imsgev_dispatch(int, short, void *); +void imsgev_disconnect(struct imsgev *, int); + +void +imsgev_init(struct imsgev *iev, int fd, void *data, + void (*callback)(struct imsgev *, int, struct imsg *)) +{ + imsg_init(&iev->ibuf, fd); + iev->terminate = 0; + + iev->data = data; + iev->handler = imsgev_dispatch; + iev->callback = callback; + + iev->events = EV_READ; + event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); + event_add(&iev->ev, NULL); +} + +int +imsgev_compose(struct imsgev *iev, u_int16_t type, u_int32_t peerid, + uint32_t pid, int fd, void *data, u_int16_t datalen) +{ + int r; + + r = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen); + if (r != -1) + imsgev_add(iev); + + return (r); +} + +void +imsgev_close(struct imsgev *iev) +{ + iev->terminate = 1; + imsgev_add(iev); +} + +void +imsgev_clear(struct imsgev *iev) +{ + event_del(&iev->ev); + msgbuf_clear(&iev->ibuf.w); + close(iev->ibuf.fd); +} + +void +imsgev_add(struct imsgev *iev) +{ + short events = 0; + + if (!iev->terminate) + events = EV_READ; + if (iev->ibuf.w.queued || iev->terminate) + events |= EV_WRITE; + + /* optimization: skip event_{del/set/add} if already set */ + if (events == iev->events) + return; + + iev->events = events; + event_del(&iev->ev); + event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); + event_add(&iev->ev, NULL); +} + +void +imsgev_dispatch(int fd, short ev, void *humppa) +{ + struct imsgev *iev = humppa; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + ssize_t n; + + iev->events = 0; + + if (ev & EV_READ) { + if ((n = imsg_read(ibuf)) == -1) { + imsgev_disconnect(iev, IMSGEV_EREAD); + return; + } + if (n == 0) { + /* + * Connection is closed for reading, and we assume + * it is also closed for writing, so we error out + * if write data is pending. + */ + imsgev_disconnect(iev, + (iev->ibuf.w.queued) ? IMSGEV_EWRITE : IMSGEV_DONE); + return; + } + } + + if (ev & EV_WRITE) { + /* + * We wanted to write data out but the connection is either + * closed, or some error occured. Both case are not recoverable + * from the imsg perspective, so we treat it as a WRITE error. + */ + if ((n = msgbuf_write(&ibuf->w)) != 0) { + imsgev_disconnect(iev, IMSGEV_EWRITE); + return; + } + } + + while (iev->terminate == 0) { + if ((n = imsg_get(ibuf, &imsg)) == -1) { + imsgev_disconnect(iev, IMSGEV_EIMSG); + return; + } + if (n == 0) + break; + iev->callback(iev, IMSGEV_IMSG, &imsg); + imsg_free(&imsg); + } + + if (iev->terminate && iev->ibuf.w.queued == 0) { + imsgev_disconnect(iev, IMSGEV_DONE); + return; + } + + imsgev_add(iev); +} + +void +imsgev_disconnect(struct imsgev *iev, int code) +{ + iev->callback(iev, code, NULL); +} diff --git a/glougloud/imsgev.h b/glougloud/imsgev.h new file mode 100644 index 0000000..5e7419e --- /dev/null +++ b/glougloud/imsgev.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __IMSGEV_H__ +#define __IMSGEV_H__ + +#include <event.h> +#include <imsg.h> + +#define IMSG_LEN(m) ((m)->hdr.len - IMSG_HEADER_SIZE) + +struct imsgev { + struct imsgbuf ibuf; + void (*handler)(int, short, void *); + struct event ev; + void *data; + short events; + int terminate; + void (*callback)(struct imsgev *, int, struct imsg *); +}; + +#define IMSGEV_IMSG 0 +#define IMSGEV_DONE 1 +#define IMSGEV_EREAD 2 +#define IMSGEV_EWRITE 3 +#define IMSGEV_EIMSG 4 + +void imsgev_init(struct imsgev *, int, void *, void (*)(struct imsgev *, + int, struct imsg *)); +int imsgev_compose(struct imsgev *, u_int16_t, u_int32_t, u_int32_t, int, + void *, u_int16_t); +void imsgev_close(struct imsgev *); +void imsgev_clear(struct imsgev *); + +#endif /* __IMSGEV_H__ */ diff --git a/glougloud/imsgev.o b/glougloud/imsgev.o Binary files differnew file mode 100644 index 0000000..821cd63 --- /dev/null +++ b/glougloud/imsgev.o 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)); +} diff --git a/glougloud/server.o b/glougloud/server.o Binary files differnew file mode 100644 index 0000000..27fccd3 --- /dev/null +++ b/glougloud/server.o diff --git a/glougloud/user.c b/glougloud/user.c new file mode 100644 index 0000000..ac024c6 --- /dev/null +++ b/glougloud/user.c @@ -0,0 +1,838 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/queue.h> + +#include <netinet/in.h> +#include <arpa/inet.h> +#include <net/if.h> +#include <netinet/in_systm.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <netinet/ip_icmp.h> + +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <pcap.h> +#include <pcap-int.h> + +#include "glougloud.h" +#include "imsgev.h" + +/* XXX a user process should be able to have multiple clients, so that we + * can send the same data to multiple machines with almost overhead */ + +#define NULL_HDRLEN 4 // XXX portable ? +#define NODE_MAX_WITHOUT_TIMEOUT 1000 +#define NODE_TIMEOUT 300 // XXX conf ? +#define CONN_TIMEOUT 300 // XXX conf ? +#define CONN_TIMEOUT_UDP 20 // XXX conf ? +#define CONN_TIMEOUT_ICMP 10 // XXX conf ? +#define CONN_FREEIDS_COUNT 65536 /* 2^16 as long as freeids are u_int16_t */ +#define CONNTIMER 5 // XXX conf ? +#define PACKET_VERSION 1 + +struct node { + LIST_ENTRY(node) entry; + struct in_addr addr; + time_t lastseen; + int used; + short namelen; +#define NODENAME_WAITING -1 +#define NODENAME_FAILED -2 + char *name; +}; + +enum connstate { + CONNSTATE_ESTABLISHED, + CONNSTATE_TCPFIN, + CONNSTATE_TCPFIN2 +}; + +struct conn { + LIST_ENTRY(conn) entry; + u_int id; + enum connstate state; + struct node *src; + u_int src_port; + struct node *dst; + u_int dst_port; + u_int proto; + u_int size; + time_t lastseen; +}; + +struct user { + LIST_ENTRY(user) entry; + struct sockaddr_in addr; +}; + +struct capture { + int fd; + struct imsgev iev; + + pcap_t *pcap; + struct event pcap_ev; + struct timeval pcap_tv; + pcap_handler pcap_handler; + + LIST_HEAD(, conn) conn_list; + LIST_HEAD(, node) node_list; + int node_count; + u_int16_t conn_freeids[CONN_FREEIDS_COUNT]; + int conn_freeids_ptr; + struct event conntimer_ev; + struct timeval conntimer_tv; + time_t time; + + int pinvalid; + int ptruncated; +}; + +struct packet { + u_int8_t ver; + u_int8_t type; +/* XXX nicer way for _SIZE ... ? */ +#define PACKET_NEWCONN 0 +#define PACKET_NEWCONN_SIZE (2 + sizeof((struct packet *)0)->pdat.newconn) +#define PACKET_DELCONN 1 +#define PACKET_DELCONN_SIZE (2 + sizeof((struct packet *)0)->pdat.delconn) +#define PACKET_DATA 2 +#define PACKET_DATA_SIZE (2 + sizeof((struct packet *)0)->pdat.data) +#define PACKET_NAME 3 +#define PACKET_NAME_SIZE (2 + sizeof((struct packet *)0)->pdat.name) +#define PACKET_EXTRA_SIZEMAX 256 + + union { + struct newconn { + u_int16_t id; + u_int32_t src; + u_int32_t dst; + u_int8_t proto; + u_int8_t size; + } newconn; + struct delconn { + u_int16_t id; + } delconn; + struct data { + u_int16_t connid; + u_int8_t size; + } data; + struct name { + u_int32_t addr; + u_int8_t len; + } name; + } pdat; +#define newconn_id pdat.newconn.id +#define newconn_src pdat.newconn.src +#define newconn_dst pdat.newconn.dst +#define newconn_proto pdat.newconn.proto +#define newconn_size pdat.newconn.size +#define delconn_id pdat.delconn.id +#define data_connid pdat.data.connid +#define data_size pdat.data.size +#define name_addr pdat.name.addr +#define name_len pdat.name.len + + u_char extra[PACKET_EXTRA_SIZEMAX]; +}; + +struct phandler { + pcap_handler f; + int type; +}; + +static void ip_handle(struct ip *, const u_char *, u_int); +static void sendto_all(struct packet *, int); +static struct node *node_add(struct in_addr *); +static void node_del(struct node *); +static struct node *node_find(struct in_addr *); +static void conn_add(struct in_addr *, int, struct in_addr *, int, int, int); +static void conn_data(struct conn *, int, int); +static void conn_del(struct conn *); +static pcap_handler lookup_phandler(int); +static struct user *finduser(struct sockaddr_in *); + +static void phandler_ether(u_char *, + const struct pcap_pkthdr *, const u_char *); +static void phandler_loop(u_char *, + const struct pcap_pkthdr *, const u_char *); +static void ev_pcap(int, short, void *); +static void ev_timer(int, short, void *); + +static void usrconn(struct imsgev *, struct imsg *); +static void imsgev_main(struct imsgev *, int, struct imsg *); + +static struct phandler phandlers[] = { + { phandler_ether, DLT_EN10MB }, + { phandler_ether, DLT_IEEE802 }, + { phandler_loop, DLT_LOOP }, + { NULL, 0 }, +}; +struct capture *cap; +LIST_HEAD(, user) usr_list; +int usr_count = 0; + +static void +sig_handler(int sig, short why, void *data) +{ + log_info("user: got signal %d", sig); + if (sig == SIGINT || sig == SIGTERM) + event_loopexit(NULL); +} + +int +user_init(int fd[2], pcap_t *pcap) +{ + struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup; + int pid, i; + + pid = fork(); + if (pid < 0) + fatal("user fork"); + if (pid > 0) + return pid; + + setproctitle("user"); + 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); + + cap = xcalloc(1, sizeof(struct capture)); + cap->fd = fd[1]; + imsgev_init(&cap->iev, cap->fd, NULL, &imsgev_main); + for (i=0; i<CONN_FREEIDS_COUNT-1; i++) + cap->conn_freeids[i] = i; + cap->time = time(NULL); + + cap->pcap = pcap; + cap->pcap_handler = lookup_phandler(pcap_datalink(pcap)); + cap->pcap_tv.tv_sec = 0; + cap->pcap_tv.tv_usec = PCAP_TO; + event_set(&cap->pcap_ev, pcap_fileno(cap->pcap), + EV_READ, ev_pcap, NULL); + + cap->conntimer_tv.tv_sec = CONNTIMER; + cap->conntimer_tv.tv_usec = 0; + evtimer_set(&cap->conntimer_ev, ev_timer, NULL); + if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1) + fatal("user: event_add conntimer failed: %s", strerror(errno)); + + droppriv(); + + log_info("user: entering event loop"); + event_dispatch(); + + log_info("user: exiting"); + exit(0); +} + +/* + * Parse an IP packet and descide what to do with it. + * 'ip' is a pointer the the captured IP packet + * 'pend' is a pointer to the end of the captured IP packet + * 'wirelen' is the size of the IP packet off the wire + */ +#define NOTCAPTURED(v) ((u_char *)v > pend - sizeof(*v)) +#define NOTRECEIVED(v) (wirelen < sizeof(v)) +static void +ip_handle(struct ip *ip, const u_char *pend, u_int wirelen) +{ + u_int len, ip_hlen, off; + const u_char *cp; + struct tcphdr *tcph; + struct udphdr *udph; + struct icmp *icmp; + struct in_addr src, dst; + u_int src_port, dst_port; + u_int size, proto, close, response; + struct conn *c, *conn; + + if (NOTCAPTURED(ip)) { + log_pinvalid("user: ip truncated"); + cap->ptruncated++; + return; + } + + if (ip->ip_v != IPVERSION) { + log_pinvalid("user: invalid ip version"); + cap->pinvalid++; + return; + } + + len = ntohs(ip->ip_len); + if (wirelen < len) { + log_pinvalid("user: ip too small"); + cap->pinvalid++; + len = wirelen; + } + + ip_hlen = ip->ip_hl * 4; + if (ip_hlen < sizeof(struct ip) || ip_hlen > len) { + log_pinvalid("user: ip_hlen invalid, %d", ip_hlen); + cap->pinvalid++; + return; + } + len -= ip_hlen; + + src.s_addr = ntohl(ip->ip_src.s_addr); + dst.s_addr = ntohl(ip->ip_dst.s_addr); + src_port = 0; + dst_port = 0; + proto = IPPROTO_IP; + size = len; + close = 0; + + off = ntohs(ip->ip_off); + if ((off & IP_OFFMASK) == 0) { + cp = (const u_char *)ip + ip_hlen; + switch (ip->ip_p) { + + case IPPROTO_TCP: + tcph = (struct tcphdr *)cp; + if (NOTCAPTURED(&tcph->th_flags)) { + log_pinvalid("user: tcp truncated"); + cap->ptruncated++; + return; + } + if (NOTRECEIVED(*tcph)) { + log_pinvalid("user: tcp too small"); + cap->pinvalid++; + return; + } + src_port = ntohs(tcph->th_sport); + dst_port = ntohs(tcph->th_dport); + proto = IPPROTO_TCP; + size = len - sizeof(*tcph); + if ((tcph->th_flags & TH_FIN) && + (tcph->th_flags & TH_ACK)) + close = 1; + break; + + case IPPROTO_UDP: + udph = (struct udphdr *)cp; + if (NOTCAPTURED(&udph->uh_dport)) { + log_pinvalid("user: udp truncated, " + "ip %x, udph %x, uh_port %x, pend %x, ip_hlen %d", + ip, udph, &udph->uh_dport, pend, ip_hlen); + cap->ptruncated++; + return; + } + if (NOTRECEIVED(*udph)) { + log_pinvalid("user: udp too small"); + cap->pinvalid++; + return; + } + src_port = ntohs(udph->uh_sport); + dst_port = ntohs(udph->uh_dport); + proto = IPPROTO_UDP; + size = len - sizeof(*udph); + break; + + case IPPROTO_ICMP: + icmp = (struct icmp *)cp; + if (NOTRECEIVED(*icmp)) { + log_pinvalid("user: icmp too small"); + cap->pinvalid++; + return; + } + proto = IPPROTO_ICMP; + size = len - sizeof(*icmp); + break; + + default: + log_warn("user: unknown ip protocol !"); + break; + } + } else { + /* + * if this isn't the first frag, we're missing the + * next level protocol header. + */ + log_tmp("user: got a fragmented ip packet !"); + } + + conn = NULL; + LIST_FOREACH(c, &cap->conn_list, entry) { + if (((c->src->addr.s_addr == src.s_addr && + c->src_port == src_port && + c->dst->addr.s_addr == dst.s_addr && + c->dst_port == dst_port) || + (c->src->addr.s_addr == dst.s_addr && + c->src_port == dst_port && + c->dst->addr.s_addr == src.s_addr && + c->dst_port == src_port)) && + c->proto == proto) { + conn = c; + if (c->src->addr.s_addr == src.s_addr) + response = 0; + else + response = 1; + break; + } + } + + if (conn) { + if (!close) { + conn_data(conn, size, response); + } else { + conn_del(conn); + } + } else { + if (!close) { + conn_add(&src, src_port, &dst, dst_port, proto, size); + } else { + log_warn("user: captured connection close w/o open !"); + } + } +} + +/* XXX all the packets must have data htos */ +static void +sendto_all(struct packet *p, int size) +{ + struct user *usr; + + LIST_FOREACH(usr, &usr_list, entry) { + if (sendto(net_socket, &p, size, 0, + (struct sockaddr *)&usr->addr, sizeof(usr->addr)) == -1) + log_warn("send_to failed: %s", strerror(errno)); + } +} + +static struct node * +node_add(struct in_addr *addr) +{ + struct node *n; + struct imsg_usrdns_req req; + + log_debug("user: node_add"); + + n = xcalloc(1, sizeof(struct node)); + n->addr.s_addr = addr->s_addr; + n->namelen = NODENAME_WAITING; + n->lastseen = cap->time; + LIST_INSERT_HEAD(&cap->node_list, n, entry); + cap->node_count++; + + req.addr.s_addr = addr->s_addr; + imsgev_compose(&cap->iev, IMSG_USRDNS_REQ, 0, 0, -1, + &req, sizeof(req)); + + return n; +} + +static void +node_del(struct node *n) +{ + if (n->used) + fatal("user: trying to remove a used node !"); + log_debug("user: node_del"); + + LIST_REMOVE(n, entry); + free(n->name); + free(n); + cap->node_count--; +} + +static struct node * +node_find(struct in_addr *remote) +{ + struct node *n; + + LIST_FOREACH(n, &cap->node_list, entry) + if (n->addr.s_addr == remote->s_addr) + return n; + return NULL; +} + +static void +conn_add(struct in_addr *src, int src_port, struct in_addr *dst, int dst_port, int proto, int size) +{ + struct packet p; + struct conn *c; + struct node *srcnode; + struct node *dstnode; + int id; + + log_debug("user: conn_add, %x:%d->%x:%d %d [%d]", + src->s_addr, src_port, dst->s_addr, dst_port, proto, size); + if (cap->conn_freeids_ptr == CONN_FREEIDS_COUNT) { + log_warn("user: out of connection identifiers !"); + return; + } + + id = cap->conn_freeids[cap->conn_freeids_ptr]; + cap->conn_freeids_ptr++; + + srcnode = node_find(src); + if (!srcnode) + srcnode = node_add(src); + srcnode->used++; + dstnode = node_find(dst); + if (!dstnode) + dstnode = node_add(dst); + dstnode->used++; + + c = xmalloc(sizeof(struct conn)); + c->id = id; + c->state = CONNSTATE_ESTABLISHED; + c->src = srcnode; + c->src_port = src_port; + c->dst = dstnode; + c->dst_port = dst_port; + c->proto = proto; + c->size = size; + c->lastseen = cap->time; + LIST_INSERT_HEAD(&cap->conn_list, c, entry); + + bzero(&p, sizeof(p)); + p.ver = PACKET_VERSION; + p.type = PACKET_NEWCONN; + p.newconn_id = id; + p.newconn_src = htonl(src->s_addr); + p.newconn_dst = htonl(dst->s_addr); + p.newconn_proto = htons(proto); + p.newconn_size = htons(size << 16); + sendto_all(&p, PACKET_NEWCONN_SIZE); +} + +static void +conn_data(struct conn *c, int size, int response) +{ + struct packet p; + + log_debug("user: conn_data"); + + c->lastseen = cap->time; + + bzero(&p, sizeof(p)); + p.ver = PACKET_VERSION; + p.type = PACKET_DATA; + p.data_connid = c->id; + p.data_size = htons(size << 16 | response); //XXX + sendto_all(&p, PACKET_DATA_SIZE); +} + +static void +conn_del(struct conn *c) +{ + struct packet p; + + log_debug("user: conn_del"); + + if (c->proto == IPPROTO_TCP) { + switch (c->state) { + case CONNSTATE_ESTABLISHED: + c->state = CONNSTATE_TCPFIN; + return; + case CONNSTATE_TCPFIN: + c->state = CONNSTATE_TCPFIN2; + return; + case CONNSTATE_TCPFIN2: + break; + } + } + + bzero(&p, sizeof(p)); + p.ver = PACKET_VERSION; + p.type = PACKET_DELCONN; + p.delconn_id = c->id; + sendto_all(&p, PACKET_DELCONN_SIZE); + + cap->conn_freeids_ptr--; + cap->conn_freeids[cap->conn_freeids_ptr] = c->id; + + c->src->used--; + c->dst->used--; + + LIST_REMOVE(c, entry); + free(c); +} + +static pcap_handler +lookup_phandler(int type) +{ + struct phandler *p; + + for (p = phandlers; p->f; ++p) { + if (type == p->type) + return p->f; + } + fatal("user: unknown data link type 0x%x", type); + /* NOTREACHED */ + return NULL; +} + +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 +phandler_ether(u_char *user, const struct pcap_pkthdr *h, const u_char *p) +{ + struct ether_header *ep; + struct ip *ip; + u_short ether_type; + const u_char *pend; + u_int len; + + log_debug("user: pcap handler ethernet !"); + + /* XXX here i assume that packets are alligned, which might not + * be the case when using dump files, says tcpdump sources */ + + ep = (struct ether_header *)p; + pend = p + h->caplen; + len = h->len - sizeof(struct ether_header); + + ether_type = ntohs(ep->ether_type); + if (ether_type <= ETHERMTU) + log_tmp("llc packet !"); + else { + switch (ether_type) { + case ETHERTYPE_IP: + log_tmp("ether IP"); + ip = (struct ip *)(ep + sizeof(struct ether_header)); + ip_handle(ip, pend, len); + break; + default: + log_tmp("non ip packet !"); + break; + } + } +} + +static void +phandler_loop(u_char *user, const struct pcap_pkthdr *h, const u_char *p) +{ + struct ip *ip; + struct ether_header *ep; + u_short ether_type; + u_int family; + const u_char *pend; + u_int len; + + log_debug("user: pcap handler loop !"); + + /* XXX here i assume that packets are alligned, which might not + * be the case when using dump files, says tcpdump sources */ + + pend = p + h->caplen; + len = h->len; + + memcpy((char *)&family, (char *)p, sizeof(family)); + family = ntohl(family); + switch (family) { + case AF_INET: + log_tmp("loop family AF_INET"); + ip = (struct ip *)(p + NULL_HDRLEN); + len -= NULL_HDRLEN; + ip_handle(ip, pend, len); + break; + case AF_LINK: + ep = (struct ether_header *)(p + NULL_HDRLEN); + ether_type = ntohs(ep->ether_type); + if (ether_type <= ETHERMTU) + log_tmp("llc packet !"); + else { + switch (ether_type) { + case ETHERTYPE_IP: + log_tmp("loop family AF_LINK IP"); + ip = (struct ip *)(ep + sizeof(*ep)); + len -= NULL_HDRLEN + sizeof (*ep); + ip_handle(ip, pend, len); + break; + default: + log_tmp("loop non ip packet !"); + break; + } + } + default: + log_tmp("unknown family %x !", family); + break; + } +} + +static void +ev_pcap(int fd, short why, void *data) +{ + log_tmp("ev_pcap"); + pcap_dispatch(cap->pcap, PCAP_COUNT, cap->pcap_handler, NULL); + + /* reschedule */ + if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1) + fatal("user: event_add pcap failed : %s", strerror(errno)); +} + +static void +ev_timer(int fd, short why, void *data) +{ + struct conn *c; + struct node *n; + int i, to; + + log_tmp("ev_timer"); + cap->time = time(NULL); + + i = 0; + LIST_FOREACH(c, &cap->conn_list, entry) { + switch (c->proto) { + case IPPROTO_UDP: + to = CONN_TIMEOUT_UDP; + break; + case IPPROTO_ICMP: + to = CONN_TIMEOUT_ICMP; + break; + default: + to = CONN_TIMEOUT; + break; + } + if (cap->time > c->lastseen + to) + conn_del(c); + else + i++; + } + + if (cap->node_count > NODE_MAX_WITHOUT_TIMEOUT) { + LIST_FOREACH(n, &cap->node_list, entry) { + if (n->used == 0 && + cap->time > n->lastseen + NODE_TIMEOUT) + node_del(n); + } + } + + log_tmp("user: ev_timer leaving with %d active connections and %d active nodes", i, cap->node_count); + if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1) + fatal("user: event_add conntimer failed : %s", strerror(errno)); +} + +static void +usrconn(struct imsgev *iev, struct imsg *imsg) +{ + struct imsg_usrconn *req; + struct imsg_usrconn res; + struct user *usr; + + req = imsg->data; + + if (req->deco) { + usr = finduser(&req->addr); + if (!usr) + fatal("user: trying to deco an inexistant user !"); + LIST_REMOVE(usr, entry); + free(usr); + usr_count--; + if (usr_count == 0) + event_del(&cap->pcap_ev); + + return; + } + + usr = xmalloc(sizeof(struct user)); + addrcpy(&usr->addr, &req->addr); + LIST_INSERT_HEAD(&usr_list, usr, entry); + if (usr_count == 0) { + if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1) + fatal("user: event_add failed : %s", strerror(errno)); + } + usr_count++; + + addrcpy(&res.addr, &usr->addr); + res.ok = 1; + imsgev_compose(&cap->iev, IMSG_USRCONN_RES, 0, 0, -1, + &res, sizeof(res)); +} + +static void +usrdns(struct imsgev *iev, struct imsg *imsg) +{ + struct imsg_usrdns_res *res; + struct node *n; + struct packet p; + + res = imsg->data; + + n = node_find(&res->addr); + if (!n) + fatal("user: received usrdns response for inexistant node !"); + if (n->namelen != NODENAME_WAITING) + fatal("user: received usrdns response for a nonwaiting node!"); + + if (res->name[0] == '\0') { + log_debug("user: resolv for %x failed", res->addr.s_addr); + n->namelen = NODENAME_FAILED; + return; + } + n->namelen = strnlen(res->name, DNSNAME_MAX); + n->name = strndup(res->name, DNSNAME_MAX); + + log_debug("user: sending node name of %x is %s", + n->addr.s_addr, n->name); + bzero(&p, sizeof(p)); + p.ver = PACKET_VERSION; + p.type = PACKET_NAME; + p.name_addr = htonl(n->addr.s_addr); + p.name_len = n->namelen; + strncpy(p.extra, n->name, sizeof(p.extra)); + sendto_all(&p, PACKET_NAME_SIZE + p.name_len); +} + +static void +imsgev_main(struct imsgev *iev, int code, struct imsg *imsg) +{ + switch (code) { + case IMSGEV_IMSG: + log_debug("user: %s got imsg %i on fd %i", + __func__, imsg->hdr.type, iev->ibuf.fd); + switch (imsg->hdr.type) { + case IMSG_USRCONN_REQ: + usrconn(iev, imsg); + break; + case IMSG_USRDNS_RES: + usrdns(iev, imsg); + break; + default: + fatal("user: %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; + } +} + diff --git a/glougloud/user.o b/glougloud/user.o Binary files differnew file mode 100644 index 0000000..89be7c2 --- /dev/null +++ b/glougloud/user.o diff --git a/glougloud/util.c b/glougloud/util.c new file mode 100644 index 0000000..83d55a0 --- /dev/null +++ b/glougloud/util.c @@ -0,0 +1,188 @@ +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <pwd.h> +#include <string.h> + +#include "glougloud.h" + +FILE *logfile; +int loglevel; +int logpinvalid; + +#define LOGFILE "/var/log/glougloud" +#define LOG_FORCED -2 +#define LOG_FATAL -1 +#define LOG_WARN 0 +#define LOG_INFO 1 +#define LOG_DEBUG 2 + +static void logit(int, const char *, const char *, va_list); + +void +log_init(int level, int pinvalid) +{ + logfile = fopen(LOGFILE, "a+"); + if (!logfile) { + printf("cannot open log file %s!\n", LOGFILE); + exit(1); + } + loglevel = level; + logpinvalid = pinvalid; +} + +void +log_tmp(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + logit(LOG_FORCED, "XXX ", msg, ap); + va_end(ap); +} +void +log_pinvalid(const char *msg, ...) +{ + va_list ap; + + if (!logpinvalid) + return; + + va_start(ap, msg); + logit(LOG_FORCED, "pinvalid: ", msg, ap); + va_end(ap); +} +void +log_debug(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + logit(LOG_DEBUG, "", msg, ap); + va_end(ap); +} +void +log_info(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + logit(LOG_INFO, "", msg, ap); + va_end(ap); +} +void +log_warn(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + logit(LOG_WARN, "", msg, ap); + va_end(ap); +} +void __dead +fatal(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + logit(LOG_FATAL, "", msg, ap); + va_end(ap); + + exit(1); +} + +/* XXX mpsafe */ +static void +logit(int level, const char *prefix, const char *msg, va_list ap) +{ + time_t clock; + + if (level <= loglevel) { + time(&clock); + fprintf(logfile, "%d ", (int)clock); + vfprintf(logfile, prefix, ap); + vfprintf(logfile, msg, ap); + fprintf(logfile, "\n"); + fflush(logfile); + } +} + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + fatal("xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + fatal("xmalloc: out of memory (allocating %lu bytes)", (u_long) size); + return ptr; +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0) + fatal("xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + fatal("xcalloc: out of memory (allocating %lu bytes)", (u_long) size); + return ptr; +} + +void +fd_nonblock(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK); + if (rc == -1) + log_warn("failed to set fd %i non-blocking", fd); +} + +void +droppriv() +{ + struct passwd *pw; + + pw = getpwnam(GLOUGLOUD_USER); + if (!pw) + fatal("unknown user %s", GLOUGLOUD_USER); + if (chroot(pw->pw_dir) != 0) + fatal("unable to chroot"); + if (chdir("/") != 0) + fatal("unable to chdir"); + if (setgroups(1, &pw->pw_gid) == -1) + fatal("setgroups() failed"); + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) + fatal("setresgid failed"); + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) + fatal("setresuid() failed"); + endpwent(); +} + +void +addrcpy(struct sockaddr_in *dst, struct sockaddr_in *src) +{ + dst->sin_addr.s_addr = src->sin_addr.s_addr; + dst->sin_port = src->sin_port; + dst->sin_family = src->sin_family; +} + +void +socketpair_prepare(int fd[2]) +{ + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1) + fatal("socketpair_prepare"); + fd_nonblock(fd[0]); + fd_nonblock(fd[1]); +} + diff --git a/glougloud/util.o b/glougloud/util.o Binary files differnew file mode 100644 index 0000000..403f795 --- /dev/null +++ b/glougloud/util.o |