aboutsummaryrefslogtreecommitdiffstats
path: root/broken/propagate
diff options
context:
space:
mode:
authorLaurent Ghigonis <laurent@p1sec.com>2013-03-26 02:00:36 +0100
committerLaurent Ghigonis <laurent@p1sec.com>2013-03-26 02:00:36 +0100
commit43648b5f488062fc7007e1161312dd36ba48f22f (patch)
tree802ea4b22714ff3181a27921bf6ef7b44db1dbc9 /broken/propagate
parentskylog: add runskype_skylog.sh explanation (diff)
downloadlaurent-tools-43648b5f488062fc7007e1161312dd36ba48f22f.tar.xz
laurent-tools-43648b5f488062fc7007e1161312dd36ba48f22f.zip
propagate: move to broken/
Diffstat (limited to 'broken/propagate')
-rw-r--r--broken/propagate/Makefile5
-rw-r--r--broken/propagate/README.txt101
-rw-r--r--broken/propagate/TODO.txt40
-rw-r--r--broken/propagate/src/Makefile15
-rw-r--r--broken/propagate/src/atomicio.c77
-rw-r--r--broken/propagate/src/atomicio.h40
-rw-r--r--broken/propagate/src/base64.c575
-rw-r--r--broken/propagate/src/base64.h63
-rw-r--r--broken/propagate/src/cli_fe_http.py34
-rw-r--r--broken/propagate/src/listener.c271
-rw-r--r--broken/propagate/src/log.c108
-rw-r--r--broken/propagate/src/msg.c225
-rw-r--r--broken/propagate/src/pg.c448
-rw-r--r--broken/propagate/src/pg.h174
-rw-r--r--broken/propagate/src/queue.h568
-rw-r--r--broken/propagate/src/route.c138
-rw-r--r--broken/propagate/src/tests/Makefile23
-rw-r--r--broken/propagate/src/tests/test_base64.c88
-rw-r--r--broken/propagate/src/tests/test_explode.c39
-rw-r--r--broken/propagate/src/util.c233
20 files changed, 3265 insertions, 0 deletions
diff --git a/broken/propagate/Makefile b/broken/propagate/Makefile
new file mode 100644
index 0000000..5722a0d
--- /dev/null
+++ b/broken/propagate/Makefile
@@ -0,0 +1,5 @@
+all:
+ cd src/; $(MAKE)
+
+clean:
+ cd src/; $(MAKE) clean
diff --git a/broken/propagate/README.txt b/broken/propagate/README.txt
new file mode 100644
index 0000000..97b601c
--- /dev/null
+++ b/broken/propagate/README.txt
@@ -0,0 +1,101 @@
+propagate - run commands and proxyfy connections on firewalled machines
+
+2012 Laurent 'laurent' Ghigonis <laurent@gouloum.fr>
+
+This tool is not fully functionnal yet
+- remote shell works (2012-03-05)
+
+Some code is from ihf tool i started with warren aka m101.
+
+
+Example of remote interactive shell
+===================================
+
+Client part addresses are still hardcoded in the software, as we
+do not have configuration file handling yet.
+
+==- server part -==
+
+./src/pg -l -vv -d
+
+mkfifo pipe; nc -k -vvv -l 127.0.0.1 3333 < pipe |nc -vvv -U
+/tmp/propagate_sock |tee pipe
+
+==- client part -==
+
+./src/pg -vv /bin/sh
+ls
+bak.sh
+Makefile
+pg.log
+pipe
+README
+src
+TODO
+
+
+IDEAS: Usage examples that i want to have in that tool
+======================================================
+
+Successive example steps that makes you have easy remote shell
+from your machine on hosts inside a remote network that you control.
+(A) Your machine
+(B) Application server running tomcat, where all other connections in/out are blocked
+(C) A server behind (B)
+
+==- I - Remote shell through jsp server running on 1.2.3.4 -==
+
+-- On the server (B) --
+cat > pg.conf
+listen on unix "/tmp/pg.sock"
+EOF
+pg -l
+# deploy fe_srv_http.war to /toto.jsp
+
+-- On the client (A) --
+cat > pg.conf
+route add B using "fe_cli_http.py 1.2.3.4 80 /toto.jsp" async
+EOF
+pg -t B /bin/sh
+
+==- II - Remote shell on another server 10.0.0.1 port 3000 living on (B) network -==
+
+-- On the server (B) --
+cat >> pg.conf
+route add C using inet 10.0.0.1 3000
+EOF
+pkill -x pg
+pg -l
+
+-- On the server (C) --
+cat > pg.conf
+listen on inet 10.0.0.1 3000
+EOF
+pg -l
+
+-- On the client (A) --
+cat >> pg.conf
+route add C gw B
+EOF
+pg -t C /bin/sh
+
+==- III - Use (B) and (C) from (A) for more than remote shell -==
+
+-- Connect via ssh to (B) --
+ssh -o ProxyCommand='pg -t B nc 127.0.0.1 22' 127.0.0.1
+
+-- Transfer a directory from (C) --
+scp -r -o ProxyCommand='pg -t C nc 127.0.0.1 22' 127.0.0.1:/backups/ .
+
+-- Use nmap from your machine (A) to scan (B) internal network -==
+ssh -D 3333 -o ProxyCommand='pg -t B nc 127.0.0.1 22' 127.0.0.1
+cat > /etc/tsocks.conf
+ local = 192.168.0.0/255.255.255.0
+ server = 127.0.0.1
+ server_type = 5
+ server_port = 3333
+EOF
+tsocks nmap -n 10.0.0.0-255
+
+-- Forward a local port on (A) to a port on (C) --
+nc -l 127.0.0.1 4025 |pg -t C nc 127.0.0.1 25
diff --git a/broken/propagate/TODO.txt b/broken/propagate/TODO.txt
new file mode 100644
index 0000000..873f9f4
--- /dev/null
+++ b/broken/propagate/TODO.txt
@@ -0,0 +1,40 @@
+---- FIX ----
+
+client does not exit when cmd exits
+
+second connection makes crash
+1330901344 W A is already executing a command
+1330901344 exec failed
+*** glibc detected *** ./src/pg: double free or corruption (out): 0x00007fff1637b6e0 ***
+==> need to cleanup connections / execs
+
+---- TODO ----
+
+cleanup dead connection
+cleanup dead execs
+cleanup dead route execs
+
+need to emulate a real terminal
+ ssh 127.0.0.1 => 'Pseudo-terminal will not be allocated because stdin is not a terminal.'
+
+async sigalarm()
+read all remainingargv as client command
+
+use base64
+
+conf file handling
+
+---- LONGTERM FIX ----
+
+maybe use libevent ... check reality of the following arguments
+ + less code, cleaner code ... maybe
+ + maybe more portable because it can use select() if poll() isn't there
+ - dependency if linked dynamicaly, bigger size if static
+
+---- LONGTERM FUNCTIONNALITIES ----
+
+port forwarding
+ but we could do it with nc
+
+socks proxy (write a separate lib in C)
+ but we could do it with ssh
diff --git a/broken/propagate/src/Makefile b/broken/propagate/src/Makefile
new file mode 100644
index 0000000..a59af5d
--- /dev/null
+++ b/broken/propagate/src/Makefile
@@ -0,0 +1,15 @@
+TARGET = pg
+CFLAGS = -g -Wall
+LDFLAGS =
+
+all: $(TARGET)
+
+%.o: %.c
+ $(CC) -o $@ -c $< $(CFLAGS) $(LDFLAGS)
+
+$(TARGET): pg.o msg.o route.o listener.o log.o util.o atomicio.o
+ $(CC) pg.o msg.o route.o listener.o log.o util.o atomicio.o -o $(TARGET) $(LDFLAGS) $(CFLAGS)
+
+clean:
+ rm -f $(TARGET) *~ *.o
+
diff --git a/broken/propagate/src/atomicio.c b/broken/propagate/src/atomicio.c
new file mode 100644
index 0000000..4ac3a08
--- /dev/null
+++ b/broken/propagate/src/atomicio.c
@@ -0,0 +1,77 @@
+/* propagate v0.1 laurent */
+/* $OpenBSD: atomicio.c,v 1.10 2011/01/08 00:47:19 jeremy Exp $ */
+/*
+ * Copyright (c) 2006 Damien Miller. All rights reserved.
+ * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved.
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "pg.h"
+#include "atomicio.h"
+
+/*
+ * ensure all of data on socket comes through. f==read || f==vwrite
+ */
+size_t
+atomicio(ssize_t (*f) (int, void *, size_t), int fd, void *_s, size_t n)
+{
+ char *s = _s;
+ size_t pos = 0;
+ ssize_t res;
+ struct pollfd pfd;
+ int try = 0;
+
+ pfd.fd = fd;
+ pfd.events = f == read ? POLLIN : POLLOUT;
+ while (n > pos && try < POLL_ATOMIC_RETRY) {
+ log_debug("atomicio: beg fd %d", fd);
+ res = (f) (fd, s + pos, n - pos);
+ log_debug("atomicio: end");
+ switch (res) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ if ((errno == EAGAIN) || (errno == ENOBUFS)) {
+ (void)poll(&pfd, 1, POLL_TIMEOUT_ATOMIC);
+ log_tmp("atomicio: try");
+ try++;
+ continue;
+ }
+ return 0;
+ case 0:
+ errno = EPIPE;
+ return pos;
+ default:
+ pos += (size_t)res;
+ }
+ }
+ return (pos);
+}
+
diff --git a/broken/propagate/src/atomicio.h b/broken/propagate/src/atomicio.h
new file mode 100644
index 0000000..c11e63f
--- /dev/null
+++ b/broken/propagate/src/atomicio.h
@@ -0,0 +1,40 @@
+/* $OpenBSD: atomicio.h,v 1.2 2007/09/07 14:50:44 tobias Exp $ */
+
+/*
+ * Copyright (c) 2006 Damien Miller. All rights reserved.
+ * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ATOMICIO_H
+#define _ATOMICIO_H
+
+/*
+ * Ensure all of data on socket comes through. f==read || f==vwrite
+ */
+size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t);
+
+#define vwrite (ssize_t (*)(int, void *, size_t))write
+
+#endif /* _ATOMICIO_H */
+
diff --git a/broken/propagate/src/base64.c b/broken/propagate/src/base64.c
new file mode 100644
index 0000000..acf49c8
--- /dev/null
+++ b/broken/propagate/src/base64.c
@@ -0,0 +1,575 @@
+/* base64.c -- Encode binary data using printable characters.
+ Copyright (C) 1999-2001, 2004-2006, 2009-2012 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Simon Josefsson. Partially adapted from GNU MailUtils
+ * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review
+ * from Paul Eggert, Bruno Haible, and Stepan Kasal.
+ *
+ * See also RFC 4648 <http://www.ietf.org/rfc/rfc4648.txt>.
+ *
+ * Be careful with error checking. Here is how you would typically
+ * use these functions:
+ *
+ * bool ok = base64_decode_alloc (in, inlen, &out, &outlen);
+ * if (!ok)
+ * FAIL: input was not valid base64
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN
+ *
+ * size_t outlen = base64_encode_alloc (in, inlen, &out);
+ * if (out == NULL && outlen == 0 && inlen != 0)
+ * FAIL: input too long
+ * if (out == NULL)
+ * FAIL: memory allocation error
+ * OK: data in OUT/OUTLEN.
+ *
+ */
+
+#include <config.h>
+
+/* Get prototype. */
+#include "base64.h"
+
+/* Get malloc. */
+#include <stdlib.h>
+
+/* Get UCHAR_MAX. */
+#include <limits.h>
+
+#include <string.h>
+
+/* C89 compliant way to cast 'char' to 'unsigned char'. */
+static inline unsigned char
+to_uchar (char ch)
+{
+ return ch;
+}
+
+/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
+ If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as
+ possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero
+ terminate the output buffer. */
+void
+base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen)
+{
+ static const char b64str[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ while (inlen && outlen)
+ {
+ *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ = b64str[((to_uchar (in[0]) << 4)
+ + (--inlen ? to_uchar (in[1]) >> 4 : 0))
+ & 0x3f];
+ if (!--outlen)
+ break;
+ *out++ =
+ (inlen
+ ? b64str[((to_uchar (in[1]) << 2)
+ + (--inlen ? to_uchar (in[2]) >> 6 : 0))
+ & 0x3f]
+ : '=');
+ if (!--outlen)
+ break;
+ *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '=';
+ if (!--outlen)
+ break;
+ if (inlen)
+ inlen--;
+ if (inlen)
+ in += 3;
+ }
+
+ if (outlen)
+ *out = '\0';
+}
+
+/* Allocate a buffer and store zero terminated base64 encoded data
+ from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e.,
+ the length of the encoded data, excluding the terminating zero. On
+ return, the OUT variable will hold a pointer to newly allocated
+ memory that must be deallocated by the caller. If output string
+ length would overflow, 0 is returned and OUT is set to NULL. If
+ memory allocation failed, OUT is set to NULL, and the return value
+ indicates length of the requested memory block, i.e.,
+ BASE64_LENGTH(inlen) + 1. */
+size_t
+base64_encode_alloc (const char *in, size_t inlen, char **out)
+{
+ size_t outlen = 1 + BASE64_LENGTH (inlen);
+
+ /* Check for overflow in outlen computation.
+ *
+ * If there is no overflow, outlen >= inlen.
+ *
+ * If the operation (inlen + 2) overflows then it yields at most +1, so
+ * outlen is 0.
+ *
+ * If the multiplication overflows, we lose at least half of the
+ * correct value, so the result is < ((inlen + 2) / 3) * 2, which is
+ * less than (inlen + 2) * 0.66667, which is less than inlen as soon as
+ * (inlen > 4).
+ */
+ if (inlen > outlen)
+ {
+ *out = NULL;
+ return 0;
+ }
+
+ *out = malloc (outlen);
+ if (!*out)
+ return outlen;
+
+ base64_encode (in, inlen, *out, outlen);
+
+ return outlen - 1;
+}
+
+/* With this approach this file works independent of the charset used
+ (think EBCDIC). However, it does assume that the characters in the
+ Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX
+ 1003.1-2001 require that char and unsigned char are 8-bit
+ quantities, though, taking care of that problem. But this may be a
+ potential problem on non-POSIX C99 platforms.
+
+ IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_"
+ as the formal parameter rather than "x". */
+#define B64(_) \
+ ((_) == 'A' ? 0 \
+ : (_) == 'B' ? 1 \
+ : (_) == 'C' ? 2 \
+ : (_) == 'D' ? 3 \
+ : (_) == 'E' ? 4 \
+ : (_) == 'F' ? 5 \
+ : (_) == 'G' ? 6 \
+ : (_) == 'H' ? 7 \
+ : (_) == 'I' ? 8 \
+ : (_) == 'J' ? 9 \
+ : (_) == 'K' ? 10 \
+ : (_) == 'L' ? 11 \
+ : (_) == 'M' ? 12 \
+ : (_) == 'N' ? 13 \
+ : (_) == 'O' ? 14 \
+ : (_) == 'P' ? 15 \
+ : (_) == 'Q' ? 16 \
+ : (_) == 'R' ? 17 \
+ : (_) == 'S' ? 18 \
+ : (_) == 'T' ? 19 \
+ : (_) == 'U' ? 20 \
+ : (_) == 'V' ? 21 \
+ : (_) == 'W' ? 22 \
+ : (_) == 'X' ? 23 \
+ : (_) == 'Y' ? 24 \
+ : (_) == 'Z' ? 25 \
+ : (_) == 'a' ? 26 \
+ : (_) == 'b' ? 27 \
+ : (_) == 'c' ? 28 \
+ : (_) == 'd' ? 29 \
+ : (_) == 'e' ? 30 \
+ : (_) == 'f' ? 31 \
+ : (_) == 'g' ? 32 \
+ : (_) == 'h' ? 33 \
+ : (_) == 'i' ? 34 \
+ : (_) == 'j' ? 35 \
+ : (_) == 'k' ? 36 \
+ : (_) == 'l' ? 37 \
+ : (_) == 'm' ? 38 \
+ : (_) == 'n' ? 39 \
+ : (_) == 'o' ? 40 \
+ : (_) == 'p' ? 41 \
+ : (_) == 'q' ? 42 \
+ : (_) == 'r' ? 43 \
+ : (_) == 's' ? 44 \
+ : (_) == 't' ? 45 \
+ : (_) == 'u' ? 46 \
+ : (_) == 'v' ? 47 \
+ : (_) == 'w' ? 48 \
+ : (_) == 'x' ? 49 \
+ : (_) == 'y' ? 50 \
+ : (_) == 'z' ? 51 \
+ : (_) == '0' ? 52 \
+ : (_) == '1' ? 53 \
+ : (_) == '2' ? 54 \
+ : (_) == '3' ? 55 \
+ : (_) == '4' ? 56 \
+ : (_) == '5' ? 57 \
+ : (_) == '6' ? 58 \
+ : (_) == '7' ? 59 \
+ : (_) == '8' ? 60 \
+ : (_) == '9' ? 61 \
+ : (_) == '+' ? 62 \
+ : (_) == '/' ? 63 \
+ : -1)
+
+static const signed char b64[0x100] = {
+ B64 (0), B64 (1), B64 (2), B64 (3),
+ B64 (4), B64 (5), B64 (6), B64 (7),
+ B64 (8), B64 (9), B64 (10), B64 (11),
+ B64 (12), B64 (13), B64 (14), B64 (15),
+ B64 (16), B64 (17), B64 (18), B64 (19),
+ B64 (20), B64 (21), B64 (22), B64 (23),
+ B64 (24), B64 (25), B64 (26), B64 (27),
+ B64 (28), B64 (29), B64 (30), B64 (31),
+ B64 (32), B64 (33), B64 (34), B64 (35),
+ B64 (36), B64 (37), B64 (38), B64 (39),
+ B64 (40), B64 (41), B64 (42), B64 (43),
+ B64 (44), B64 (45), B64 (46), B64 (47),
+ B64 (48), B64 (49), B64 (50), B64 (51),
+ B64 (52), B64 (53), B64 (54), B64 (55),
+ B64 (56), B64 (57), B64 (58), B64 (59),
+ B64 (60), B64 (61), B64 (62), B64 (63),
+ B64 (64), B64 (65), B64 (66), B64 (67),
+ B64 (68), B64 (69), B64 (70), B64 (71),
+ B64 (72), B64 (73), B64 (74), B64 (75),
+ B64 (76), B64 (77), B64 (78), B64 (79),
+ B64 (80), B64 (81), B64 (82), B64 (83),
+ B64 (84), B64 (85), B64 (86), B64 (87),
+ B64 (88), B64 (89), B64 (90), B64 (91),
+ B64 (92), B64 (93), B64 (94), B64 (95),
+ B64 (96), B64 (97), B64 (98), B64 (99),
+ B64 (100), B64 (101), B64 (102), B64 (103),
+ B64 (104), B64 (105), B64 (106), B64 (107),
+ B64 (108), B64 (109), B64 (110), B64 (111),
+ B64 (112), B64 (113), B64 (114), B64 (115),
+ B64 (116), B64 (117), B64 (118), B64 (119),
+ B64 (120), B64 (121), B64 (122), B64 (123),
+ B64 (124), B64 (125), B64 (126), B64 (127),
+ B64 (128), B64 (129), B64 (130), B64 (131),
+ B64 (132), B64 (133), B64 (134), B64 (135),
+ B64 (136), B64 (137), B64 (138), B64 (139),
+ B64 (140), B64 (141), B64 (142), B64 (143),
+ B64 (144), B64 (145), B64 (146), B64 (147),
+ B64 (148), B64 (149), B64 (150), B64 (151),
+ B64 (152), B64 (153), B64 (154), B64 (155),
+ B64 (156), B64 (157), B64 (158), B64 (159),
+ B64 (160), B64 (161), B64 (162), B64 (163),
+ B64 (164), B64 (165), B64 (166), B64 (167),
+ B64 (168), B64 (169), B64 (170), B64 (171),
+ B64 (172), B64 (173), B64 (174), B64 (175),
+ B64 (176), B64 (177), B64 (178), B64 (179),
+ B64 (180), B64 (181), B64 (182), B64 (183),
+ B64 (184), B64 (185), B64 (186), B64 (187),
+ B64 (188), B64 (189), B64 (190), B64 (191),
+ B64 (192), B64 (193), B64 (194), B64 (195),
+ B64 (196), B64 (197), B64 (198), B64 (199),
+ B64 (200), B64 (201), B64 (202), B64 (203),
+ B64 (204), B64 (205), B64 (206), B64 (207),
+ B64 (208), B64 (209), B64 (210), B64 (211),
+ B64 (212), B64 (213), B64 (214), B64 (215),
+ B64 (216), B64 (217), B64 (218), B64 (219),
+ B64 (220), B64 (221), B64 (222), B64 (223),
+ B64 (224), B64 (225), B64 (226), B64 (227),
+ B64 (228), B64 (229), B64 (230), B64 (231),
+ B64 (232), B64 (233), B64 (234), B64 (235),
+ B64 (236), B64 (237), B64 (238), B64 (239),
+ B64 (240), B64 (241), B64 (242), B64 (243),
+ B64 (244), B64 (245), B64 (246), B64 (247),
+ B64 (248), B64 (249), B64 (250), B64 (251),
+ B64 (252), B64 (253), B64 (254), B64 (255)
+};
+
+#if UCHAR_MAX == 255
+# define uchar_in_range(c) true
+#else
+# define uchar_in_range(c) ((c) <= 255)
+#endif
+
+/* Return true if CH is a character from the Base64 alphabet, and
+ false otherwise. Note that '=' is padding and not considered to be
+ part of the alphabet. */
+bool
+isbase64 (char ch)
+{
+ return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)];
+}
+
+/* Initialize decode-context buffer, CTX. */
+void
+base64_decode_ctx_init (struct base64_decode_context *ctx)
+{
+ ctx->i = 0;
+}
+
+/* If CTX->i is 0 or 4, there are four or more bytes in [*IN..IN_END), and
+ none of those four is a newline, then return *IN. Otherwise, copy up to
+ 4 - CTX->i non-newline bytes from that range into CTX->buf, starting at
+ index CTX->i and setting CTX->i to reflect the number of bytes copied,
+ and return CTX->buf. In either case, advance *IN to point to the byte
+ after the last one processed, and set *N_NON_NEWLINE to the number of
+ verified non-newline bytes accessible through the returned pointer. */
+static inline char *
+get_4 (struct base64_decode_context *ctx,
+ char const *restrict *in, char const *restrict in_end,
+ size_t *n_non_newline)
+{
+ if (ctx->i == 4)
+ ctx->i = 0;
+
+ if (ctx->i == 0)
+ {
+ char const *t = *in;
+ if (4 <= in_end - *in && memchr (t, '\n', 4) == NULL)
+ {
+ /* This is the common case: no newline. */
+ *in += 4;
+ *n_non_newline = 4;
+ return (char *) t;
+ }
+ }
+
+ {
+ /* Copy non-newline bytes into BUF. */
+ char const *p = *in;
+ while (p < in_end)
+ {
+ char c = *p++;
+ if (c != '\n')
+ {
+ ctx->buf[ctx->i++] = c;
+ if (ctx->i == 4)
+ break;
+ }
+ }
+
+ *in = p;
+ *n_non_newline = ctx->i;
+ return ctx->buf;
+ }
+}
+
+#define return_false \
+ do \
+ { \
+ *outp = out; \
+ return false; \
+ } \
+ while (false)
+
+/* Decode up to four bytes of base64-encoded data, IN, of length INLEN
+ into the output buffer, *OUT, of size *OUTLEN bytes. Return true if
+ decoding is successful, false otherwise. If *OUTLEN is too small,
+ as many bytes as possible are written to *OUT. On return, advance
+ *OUT to point to the byte after the last one written, and decrement
+ *OUTLEN to reflect the number of bytes remaining in *OUT. */
+static inline bool
+decode_4 (char const *restrict in, size_t inlen,
+ char *restrict *outp, size_t *outleft)
+{
+ char *out = *outp;
+ if (inlen < 2)
+ return false;
+
+ if (!isbase64 (in[0]) || !isbase64 (in[1]))
+ return false;
+
+ if (*outleft)
+ {
+ *out++ = ((b64[to_uchar (in[0])] << 2)
+ | (b64[to_uchar (in[1])] >> 4));
+ --*outleft;
+ }
+
+ if (inlen == 2)
+ return_false;
+
+ if (in[2] == '=')
+ {
+ if (inlen != 4)
+ return_false;
+
+ if (in[3] != '=')
+ return_false;
+ }
+ else
+ {
+ if (!isbase64 (in[2]))
+ return_false;
+
+ if (*outleft)
+ {
+ *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0)
+ | (b64[to_uchar (in[2])] >> 2));
+ --*outleft;
+ }
+
+ if (inlen == 3)
+ return_false;
+
+ if (in[3] == '=')
+ {
+ if (inlen != 4)
+ return_false;
+ }
+ else
+ {
+ if (!isbase64 (in[3]))
+ return_false;
+
+ if (*outleft)
+ {
+ *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0)
+ | b64[to_uchar (in[3])]);
+ --*outleft;
+ }
+ }
+ }
+
+ *outp = out;
+ return true;
+}
+
+/* Decode base64-encoded input array IN of length INLEN to output array
+ OUT that can hold *OUTLEN bytes. The input data may be interspersed
+ with newlines. Return true if decoding was successful, i.e. if the
+ input was valid base64 data, false otherwise. If *OUTLEN is too
+ small, as many bytes as possible will be written to OUT. On return,
+ *OUTLEN holds the length of decoded bytes in OUT. Note that as soon
+ as any non-alphabet, non-newline character is encountered, decoding
+ is stopped and false is returned. If INLEN is zero, then process
+ only whatever data is stored in CTX.
+
+ Initially, CTX must have been initialized via base64_decode_ctx_init.
+ Subsequent calls to this function must reuse whatever state is recorded
+ in that buffer. It is necessary for when a quadruple of base64 input
+ bytes spans two input buffers.
+
+ If CTX is NULL then newlines are treated as garbage and the input
+ buffer is processed as a unit. */
+
+bool
+base64_decode_ctx (struct base64_decode_context *ctx,
+ const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen)
+{
+ size_t outleft = *outlen;
+ bool ignore_newlines = ctx != NULL;
+ bool flush_ctx = false;
+ unsigned int ctx_i = 0;
+
+ if (ignore_newlines)
+ {
+ ctx_i = ctx->i;
+ flush_ctx = inlen == 0;
+ }
+
+
+ while (true)
+ {
+ size_t outleft_save = outleft;
+ if (ctx_i == 0 && !flush_ctx)
+ {
+ while (true)
+ {
+ /* Save a copy of outleft, in case we need to re-parse this
+ block of four bytes. */
+ outleft_save = outleft;
+ if (!decode_4 (in, inlen, &out, &outleft))
+ break;
+
+ in += 4;
+ inlen -= 4;
+ }
+ }
+
+ if (inlen == 0 && !flush_ctx)
+ break;
+
+ /* Handle the common case of 72-byte wrapped lines.
+ This also handles any other multiple-of-4-byte wrapping. */
+ if (inlen && *in == '\n' && ignore_newlines)
+ {
+ ++in;
+ --inlen;
+ continue;
+ }
+
+ /* Restore OUT and OUTLEFT. */
+ out -= outleft_save - outleft;
+ outleft = outleft_save;
+
+ {
+ char const *in_end = in + inlen;
+ char const *non_nl;
+
+ if (ignore_newlines)
+ non_nl = get_4 (ctx, &in, in_end, &inlen);
+ else
+ non_nl = in; /* Might have nl in this case. */
+
+ /* If the input is empty or consists solely of newlines (0 non-newlines),
+ then we're done. Likewise if there are fewer than 4 bytes when not
+ flushing context and not treating newlines as garbage. */
+ if (inlen == 0 || (inlen < 4 && !flush_ctx && ignore_newlines))
+ {
+ inlen = 0;
+ break;
+ }
+ if (!decode_4 (non_nl, inlen, &out, &outleft))
+ break;
+
+ inlen = in_end - in;
+ }
+ }
+
+ *outlen -= outleft;
+
+ return inlen == 0;
+}
+
+/* Allocate an output buffer in *OUT, and decode the base64 encoded
+ data stored in IN of size INLEN to the *OUT buffer. On return, the
+ size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL,
+ if the caller is not interested in the decoded length. *OUT may be
+ NULL to indicate an out of memory error, in which case *OUTLEN
+ contains the size of the memory block needed. The function returns
+ true on successful decoding and memory allocation errors. (Use the
+ *OUT and *OUTLEN parameters to differentiate between successful
+ decoding and memory error.) The function returns false if the
+ input was invalid, in which case *OUT is NULL and *OUTLEN is
+ undefined. */
+bool
+base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+ const char *in, size_t inlen, char **out,
+ size_t *outlen)
+{
+ /* This may allocate a few bytes too many, depending on input,
+ but it's not worth the extra CPU time to compute the exact size.
+ The exact size is 3 * (inlen + (ctx ? ctx->i : 0)) / 4, minus 1 if the
+ input ends with "=" and minus another 1 if the input ends with "==".
+ Dividing before multiplying avoids the possibility of overflow. */
+ size_t needlen = 3 * (inlen / 4) + 3;
+
+ *out = malloc (needlen);
+ if (!*out)
+ return true;
+
+ if (!base64_decode_ctx (ctx, in, inlen, *out, &needlen))
+ {
+ free (*out);
+ *out = NULL;
+ return false;
+ }
+
+ if (outlen)
+ *outlen = needlen;
+
+ return true;
+}
diff --git a/broken/propagate/src/base64.h b/broken/propagate/src/base64.h
new file mode 100644
index 0000000..8973755
--- /dev/null
+++ b/broken/propagate/src/base64.h
@@ -0,0 +1,63 @@
+/* base64.h -- Encode binary data using printable characters.
+ Copyright (C) 2004-2006, 2009-2012 Free Software Foundation, Inc.
+ Written by Simon Josefsson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef BASE64_H
+# define BASE64_H
+
+#include "config.h"
+
+/* Get size_t. */
+# include <stddef.h>
+
+/* Get bool. */
+# include <stdbool.h>
+
+/* This uses that the expression (n+(k-1))/k means the smallest
+ integer >= n/k, i.e., the ceiling of n/k. */
+# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4)
+
+struct base64_decode_context
+{
+ unsigned int i;
+ char buf[4];
+};
+
+extern bool isbase64 (char ch) _GL_ATTRIBUTE_CONST;
+
+extern void base64_encode (const char *restrict in, size_t inlen,
+ char *restrict out, size_t outlen);
+
+extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out);
+
+extern void base64_decode_ctx_init (struct base64_decode_context *ctx);
+
+extern bool base64_decode_ctx (struct base64_decode_context *ctx,
+ const char *restrict in, size_t inlen,
+ char *restrict out, size_t *outlen);
+
+extern bool base64_decode_alloc_ctx (struct base64_decode_context *ctx,
+ const char *in, size_t inlen,
+ char **out, size_t *outlen);
+
+#define base64_decode(in, inlen, out, outlen) \
+ base64_decode_ctx (NULL, in, inlen, out, outlen)
+
+#define base64_decode_alloc(in, inlen, out, outlen) \
+ base64_decode_alloc_ctx (NULL, in, inlen, out, outlen)
+
+#endif /* BASE64_H */
diff --git a/broken/propagate/src/cli_fe_http.py b/broken/propagate/src/cli_fe_http.py
new file mode 100644
index 0000000..40b7373
--- /dev/null
+++ b/broken/propagate/src/cli_fe_http.py
@@ -0,0 +1,34 @@
+import httplib
+import urllib
+import optparse
+import Cookie
+"""
+ Christian
+ Usage: The following program reads strings from the standard input
+ and sends them to the server ([host] [port]) in the POST
+ request on URL [url] in field 'cmd'.
+ The result is read from the COOKIE "result" and displayed.
+"""
+def send_cmd_http_post(host, port=80, base64command="base64command", url="/test.php"):
+ headers = { "Content-type": "application/x-www-form-urlencoded" }
+ params = urllib.urlencode({'cmd': base64command})
+ conn = httplib.HTTPConnection(host, port)
+ conn.request("POST", url, params, headers)
+ response = conn.getresponse()
+
+ response_headers = dict(response.getheaders())
+ if ('set-cookie' in response_headers):
+ cookie = Cookie.BaseCookie(response_headers['set-cookie'])
+ return cookie["result"].value
+ return "result-cookie-not-found"
+
+if __name__ == '__main__':
+ usage = "usage: %prog host port url"
+ parser = optparse.OptionParser(usage=usage)
+ (options, args) = parser.parse_args()
+ if len(args) != 3:
+ parser.error("incorrect number of arguments")
+ host, port, url = args
+ while (True):
+ base64command = raw_input()
+ print send_cmd_http_post(host, int(port), base64command, url)
diff --git a/broken/propagate/src/listener.c b/broken/propagate/src/listener.c
new file mode 100644
index 0000000..782385d
--- /dev/null
+++ b/broken/propagate/src/listener.c
@@ -0,0 +1,271 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <signal.h>
+
+#include <poll.h>
+
+#include "pg.h"
+
+#define SOCK_BASENAME "/tmp/propagate_sock"
+#define SOCK_NAME_MAXLEN 255
+
+static int listener_start(struct listener *);
+static int unix_start(struct listener *);
+static int inet_start(struct listener *);
+
+int
+listener_add(int type, char *path) {
+ struct listener *l;
+
+ if (listeners_count >= LISTENERS_MAX) {
+ log_warn("listeners max reached");
+ return -1;
+ }
+ l = xcalloc(1, sizeof(struct listener));
+ if (!l)
+ return -1;
+
+ l->type = type;
+ switch (type) {
+ case LISTENER_UNIX:
+ l->sock_unix.path = path;
+ break;
+ case LISTENER_INET:
+ log_warn("listener_add: XXX inet not implemented yet");
+ break;
+ }
+ LIST_INIT(&(l->conns));
+ l->conns_count = 0;
+
+ LIST_INSERT_HEAD(&listeners, l, entry);
+ listeners_count++;
+
+ listener_start(l);
+
+ return 0;
+}
+
+struct listener *
+listener_find(int fd) {
+ struct listener *l;
+
+ LIST_FOREACH(l, &listeners, entry) {
+ if (l->sock == fd)
+ return l;
+ }
+ return NULL;
+}
+
+int
+listener_conn_add(struct listener *l, int fd) {
+ struct conn *c;
+
+ if (l->conns_count >= LISTENER_CONN_MAX) {
+ log_warn("conn max reached");
+ return -1;
+ }
+ c = xcalloc(1, sizeof(struct conn));
+ if (!c)
+ return -1;
+
+ c->listener = l;
+ c->fd = fd;
+ c->state = CONN_OPEN;
+
+ LIST_INSERT_HEAD(&(l->conns), c, entry);
+ l->conns_count++;
+
+ setnonblock(fd);
+
+ return 0;
+}
+
+void
+listener_conn_del(struct conn *c) {
+ close(c->fd);
+ if (c->exec.cmd)
+ listener_conn_exec_kill(c);
+ LIST_REMOVE(c, entry);
+ c->listener->conns_count--;
+ free(c);
+}
+
+void
+listener_conn_move(struct conn *oldc, struct conn *newc) {
+ memcpy(newc, oldc, sizeof(struct conn));
+ LIST_REMOVE(oldc, entry);
+ newc->listener->conns_count--;
+ free(oldc);
+}
+
+struct conn *
+listener_conn_find(int fd) {
+ struct listener *l;
+ struct conn *c;
+
+ LIST_FOREACH(l, &listeners, entry) {
+ LIST_FOREACH(c, &l->conns, entry) {
+ if (c->fd == fd)
+ return c;
+ }
+ }
+ return NULL;
+}
+
+struct conn *
+listener_conn_find_orig(char orig) {
+ struct listener *l;
+ struct conn *c;
+
+ LIST_FOREACH(l, &listeners, entry) {
+ LIST_FOREACH(c, &l->conns, entry) {
+ if (c->orig == orig)
+ return c;
+ }
+ }
+ return NULL;
+
+}
+
+/* XXX indicate that we are only looking in input pipe */
+struct conn *
+listener_conn_find_exec(int fd) {
+ struct listener *l;
+ struct conn *c;
+
+ LIST_FOREACH(l, &listeners, entry) {
+ LIST_FOREACH(c, &l->conns, entry) {
+ if (c->exec.fd[0] == fd)
+ return c;
+ }
+ }
+ return NULL;
+}
+
+int
+listener_conn_exec(struct conn *c, char *cmd, char **argv) {
+ int pid;
+
+ log_debug("listener_conn_exec %s", cmd);
+
+ if (c->exec.cmd) {
+ log_warn("%c is already executing a command", c->orig);
+ return -1;
+ }
+ c->exec.cmd = cmd;
+ c->exec.argv = argv;
+
+ pid = execpipe(cmd, argv, c->exec.fd);
+ if (pid < 0)
+ return -1;
+ c->exec.pid = pid;
+
+ // XXX if async then writebuf ?
+ // XXX if err then MSG_ERR
+
+ return 0;
+}
+
+void
+listener_conn_exec_kill(struct conn *c) {
+ int i;
+
+ if (c->exec.cmd) {
+ free(c->exec.cmd);
+ c->exec.cmd = NULL;
+ }
+ if (c->exec.argv) {
+ for (i=0; c->exec.argv[i] != NULL; i++)
+ free(c->exec.argv[i]);
+ free(c->exec.argv);
+ c->exec.argv = NULL;
+ }
+ kill(c->exec.pid, SIGTERM);
+ close(c->exec.fd[0]);
+ close(c->exec.fd[1]);
+ if (c->exec.async_writebuf) {
+ free(c->exec.async_writebuf);
+ c->exec.async_writebuf = NULL;
+ }
+}
+
+
+int
+listener_conn_exec_bufferize(struct conn *c, int fd) {
+ uint8_t buf[BUFMAX];
+
+ log_warn("XXX conn exec bufferize not implemented");
+ read(fd, buf, sizeof(buf));
+ return -1;
+}
+
+static int
+listener_start(struct listener *l) {
+ switch (l->type) {
+ case LISTENER_UNIX:
+ if (unix_start(l) < 0)
+ log_warn("listener socket failed to start");
+ break;
+ case LISTENER_INET:
+ if (inet_start(l) < 0)
+ log_warn("listener inet failed to start");
+ break;
+ }
+ return 0;
+}
+
+static int
+unix_start(struct listener *l) {
+ int s, len;
+ struct sockaddr_un local;
+ char *path;
+
+ path = xmalloc(sizeof(SOCK_NAME_MAXLEN));
+ if (!path)
+ return -1;
+ snprintf(path, SOCK_NAME_MAXLEN, "%s", l->sock_unix.path);
+
+ /* XXX monitor death of the socket, to restart it */
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("unix_start: socket %s: %s", path, strerror(errno));
+ free(path);
+ return -1;
+ }
+
+ memset(&local, 0, sizeof(struct sockaddr_un));
+ local.sun_family = AF_UNIX;
+ strncpy(local.sun_path, path, sizeof(local.sun_path));
+ unlink(local.sun_path);
+ len = strlen(local.sun_path) + sizeof(local.sun_family);
+ if (bind(s, (struct sockaddr *)&local, len) == -1) {
+ log_warn("unix_start: bind %s: %s", path, strerror(errno));
+ free(path);
+ return -1;
+ }
+
+ if (listen(s, 5) == -1) {
+ log_warn("unix_start: listen %s: %s", path, strerror(errno));
+ free(path);
+ return -1;
+ }
+
+ // XXX setnonblock(s);
+
+ l->sock = s;
+ l->sock_unix.path = path;
+
+ return 0;
+}
+
+static int
+inet_start(struct listener *l) {
+ log_warn("inet_start: XXX not implemented yet");
+ return -1;
+}
diff --git a/broken/propagate/src/log.c b/broken/propagate/src/log.c
new file mode 100644
index 0000000..bf494fe
--- /dev/null
+++ b/broken/propagate/src/log.c
@@ -0,0 +1,108 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+
+FILE *logfile = NULL;
+int loglevel;
+
+#define LOG_FATAL 0
+#define LOG_WARN 1
+#define LOG_INFO 2
+#define LOG_DEBUG 3
+
+static void logit(int, const char *, va_list);
+
+void
+log_init(int level, int logstdout)
+{
+ char name[128];
+
+ if (logstdout == 1) {
+ logfile = stderr;
+ } else {
+ snprintf(name, sizeof(name), "pg.log");
+ logfile = fopen(name, "a+");
+ if (!logfile) {
+ printf("cannot open log file %s!\n", name);
+ exit(1);
+ }
+ }
+ loglevel = level;
+}
+
+void
+log_tmp(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vfprintf(logfile, msg, ap);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ 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
+fatal(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_FATAL, msg, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void
+logit(int level, const char *msg, va_list ap)
+{
+ time_t clock;
+ char type;
+
+ if (level <= loglevel) {
+ switch (level) {
+ case LOG_FATAL: type='X'; break;
+ case LOG_WARN: type='W'; break;
+ case LOG_INFO: type='.'; break;
+ case LOG_DEBUG: type=' '; break;
+ }
+ time(&clock);
+ fprintf(logfile, "%d %c ", (int)clock, type);
+ vfprintf(logfile, msg, ap);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+}
+
diff --git a/broken/propagate/src/msg.c b/broken/propagate/src/msg.c
new file mode 100644
index 0000000..568ff44
--- /dev/null
+++ b/broken/propagate/src/msg.c
@@ -0,0 +1,225 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/types.h>
+
+#include "pg.h"
+
+static int encode(uint8_t *, uint8_t **, int);
+static int decode(uint8_t *, uint8_t **, int);
+
+int
+msg_send(int fd, int type, char dest, uint8_t arg, int datalen, uint8_t *data) {
+ uint8_t *header = NULL, *data_encoded = NULL;
+ int len, data_encoded_len;
+
+ log_debug("msg_send");
+
+ len = writebuf((uint8_t *)MSG_MAGIC, fd, sizeof(MSG_MAGIC));
+ if (len < 0)
+ goto err;
+ header = msg_pack_header(type, conf.me, dest, arg, datalen);
+ if (!header)
+ goto err;
+ len = writebuf(header, fd, MSG_HEADER_SIZE_ENCODED);
+ if (len < 0)
+ goto err;
+
+ if (datalen > 0 && data) {
+ data_encoded_len = encode(data, &data_encoded, datalen);
+ if (!data_encoded || data_encoded_len <= 0)
+ goto err;
+ len = writebuf(data, fd, data_encoded_len);
+ if (len < 0)
+ goto err;
+ free(data_encoded);
+ }
+ free(header);
+
+ return len;
+
+err:
+ if (header)
+ free(header);
+ if (data_encoded)
+ free(data_encoded);
+ log_warn("msg_send: err");
+ return -1;
+}
+
+int
+msg_send_from_fd(int fd, int type, char dest, uint8_t arg, int datafd) {
+ uint8_t buf[BUFMAX];
+ uint8_t *encoded;
+ int buflen, encodedlen;
+
+ log_debug("msg_send_from_fd");
+
+ buflen = read(datafd, buf, sizeof(buf));
+ if (buflen < 0)
+ return -1;
+ encodedlen = encode(buf, &encoded, buflen);
+ if (encodedlen < 0)
+ return -1;
+ msg_send(fd, type, dest, arg, encodedlen, encoded);
+ free(encoded);
+
+ return 0;
+}
+
+uint8_t *
+msg_pack_header(int type, char orig, char dest, uint8_t arg, int datalen) {
+ struct msg_header h;
+ uint8_t *data;
+ int len;
+
+ if (datalen > MSG_DATALEN_MAX) {
+ log_warn("msg_pack_header: datalen too big %d", datalen);
+ return NULL;
+ }
+
+ h.version = MSG_VERSION;
+ h.type = type;
+ h.orig = orig;
+ h.dest = dest;
+ h.arg = arg;
+ h.datalen = datalen;
+
+ len = encode((uint8_t *)&h, &data, MSG_HEADER_SIZE);
+ if (len != MSG_HEADER_SIZE_ENCODED) {
+ log_warn("msg_pack_header: encoded header has invalid size %d !"
+ "This should NOT happend, as MSG_HEADER_SIZE_ENCODED should be fixed !",
+ len);
+ return NULL;
+ }
+
+ return data;
+}
+
+#define ERR(msg...) \
+{ \
+ log_warn(msg); \
+ return NULL; \
+}
+struct msg_header *
+msg_unpack_header(uint8_t *data) {
+ uint8_t *data_decoded;
+ struct msg_header *h;
+ int len;
+ /* keep in sync with pg.h MSG enum */
+ int msg_client[] = {MSG_DATA, MSG_OK, MSG_ERR};
+ int msg_server[] = {MSG_INIT, MSG_INIT_ASYNC, MSG_KILL, MSG_EXEC, MSG_READ, MSG_WRITE, MSG_DATA};
+#define MSG_CLIENT_COUNT 3
+#define MSG_SERVER_COUNT 7
+
+ len = decode(data, &data_decoded, MSG_HEADER_SIZE_ENCODED);
+ if (!data_decoded)
+ ERR("failed to decode message header")
+
+ h = (struct msg_header *)data_decoded;
+
+ if (h->version <= 0 || h->version > 255)
+ ERR("msg_unpack_header: invalid version %d", h->version)
+ if (h->version != MSG_VERSION)
+ ERR("msg_unpack_header: incompatible version %d", h->version)
+ if (conf.server && (intab(msg_server, h->type, MSG_SERVER_COUNT) < 0))
+ ERR("msg_unpack_header: type %d incorrect for server", h->type)
+ if (!conf.server && (intab(msg_client, h->type, MSG_CLIENT_COUNT) < 0))
+ ERR("msg_unpack_header: type %d incorrect for client", h->type)
+ if (!isalnum(h->orig))
+ ERR("msg_unpack_header: non alphanumeric originator %c", h->orig)
+ if (h->orig == conf.me)
+ ERR("msg_unpack_header: message pretends to come from me, ignoring")
+ if (!isalnum(h->dest))
+ ERR("msg_unpack_header: non alphanumeric destination %c", h->dest)
+ // XXX check dest on routes ?
+ if (h->datalen < 0)
+ ERR("msg_unpack_header: datalen < 0")
+ if (h->datalen > MSG_DATALEN_MAX)
+ ERR("msg_unpack_header: datalen too big %d", h->datalen)
+
+ return h;
+}
+
+int
+msg_read_data(int fd, uint8_t **out, int len) {
+ uint8_t *buf;
+ int buflen, decodedlen;
+
+ buflen = readbuf(fd, &buf, len);
+ if (buflen < 0)
+ return -1;
+ decodedlen = decode(buf, out, buflen);
+ if (decodedlen < 0)
+ log_warn("msg_read_data: decoding failed");
+
+ return decodedlen;
+}
+
+/* XXX rename to msg_fw_decode ? */
+int
+msg_read_data_to_fd(int ifd, int fd, int len) {
+ uint8_t *buf;
+ int buflen, writelen;
+
+ buflen = msg_read_data(ifd, &buf, len);
+ if (buflen < 0)
+ goto err;
+ writelen = writebuf(buf, fd, buflen);
+ if (writelen < 0)
+ goto err;
+ free(buf);
+
+ return writelen;
+
+err:
+ if (buf)
+ free(buf);
+ return -1;
+}
+
+static int
+encode(uint8_t *arg, uint8_t **out, int len) {
+ uint8_t *res;
+ int outlen;
+
+ outlen = len;
+ *out = NULL;
+ res = xmalloc(sizeof(uint8_t) * outlen);
+ if (!res)
+ goto err;
+ memcpy(res, arg, len);
+
+ *out = res;
+ return outlen;
+
+err:
+ log_warn("encode failed");
+ return -1;
+}
+
+static int
+decode(uint8_t *arg, uint8_t **out, int len) {
+ uint8_t *res;
+ int outlen;
+
+ outlen = len;
+ *out = NULL;
+ res = xmalloc(sizeof(uint8_t) * outlen);
+ if (!res)
+ goto err;
+ memcpy(res, arg, len);
+
+ *out = res;
+ return outlen;
+
+err:
+ log_warn("decode failed");
+ return -1;
+}
+
diff --git a/broken/propagate/src/pg.c b/broken/propagate/src/pg.c
new file mode 100644
index 0000000..f3c3c8d
--- /dev/null
+++ b/broken/propagate/src/pg.c
@@ -0,0 +1,448 @@
+/* XXX
+ * we should inspire from netcat
+ * http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/nc/
+ * see readwrite(), atomicio()
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <poll.h>
+
+#include "pg.h"
+
+extern char *__progname;
+
+static int usage();
+static void process(void);
+static int pfds_create(struct pollfd *);
+static int cmd_init(struct conn *, int, struct msg_header *);
+static int cmd_exec(struct conn *, int, struct msg_header *);
+static int cmd_kill(struct conn *, int, struct msg_header *);
+static int cmd_read(struct conn *, int, struct msg_header *);
+static int cmd_write(struct conn *, int, struct msg_header *);
+static int cmd_data_conn(struct conn *, int, struct msg_header *);
+static int cmd_data_route(struct route *, int, struct msg_header *);
+static int cmd_ok(struct route *, int, struct msg_header *);
+static int cmd_err(struct route *, int, struct msg_header *);
+
+int
+main(int argc, char *argv[]) {
+ int opt;
+ int loglevel = 1, logstdout = 0;
+ int deamonize = 0;
+ int server = 0;
+ char dest = 0;
+ char *cmd = NULL;
+
+ while ((opt = getopt(argc, argv, "dhltv")) != -1) {
+ switch (opt) {
+ case 'd':
+ logstdout = 1;
+ deamonize = 0;
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'l':
+ server = 1;
+ break;
+ case 't':
+ dest = optarg[0];
+ break;
+ case 'v':
+ loglevel++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ((server && argc != 0) ||
+ (!server && argc != 1) ||
+ (server && conf.client_dest))
+ usage();
+ if (!server) {
+ cmd = argv[0];
+ if (dest == 0)
+ dest = 'B';
+ conf.client_dest = dest;
+ }
+
+ log_init(loglevel, logstdout);
+ log_warn("** Starting propagate v%.1f", VERSION);
+ log_warn("** using message version %d", MSG_VERSION);
+ log_warn("** loglevel %d", loglevel);
+
+ conf.server = server;
+ LIST_INIT(&routes);
+ routes_count = 0;
+ LIST_INIT(&listeners);
+ listeners_count = 0;
+
+ /* XXX load conf */
+ if (server) {
+ conf.me = 'B';
+ listener_add(LISTENER_UNIX, "/tmp/propagate_sock");
+ } else {
+ conf.me = 'A';
+ route_add('B', ROUTE_PROC, "nc 127.0.0.1 3333", 0, NULL, 0);
+ }
+
+ if (server)
+ log_info("starting server %c", conf.me);
+ else
+ log_info("running client %c, dest %c, command %s", conf.me, conf.client_dest, cmd);
+
+ if (deamonize)
+ log_info("XXX deamonize not implemented yet");
+
+ if (!server)
+ send_cmd(conf.client_dest, MSG_EXEC, 0, (uint8_t *)cmd, strlen(cmd));
+
+ process();
+
+ return 0;
+}
+
+static int
+usage() {
+ printf("Usage: %s [-dhv] [-t destination] command\n", __progname);
+ printf(" %s -l [-dhv]\n", __progname);
+
+ exit(1);
+}
+
+static void
+process(void) {
+ struct listener *l;
+ struct conn *c;
+ struct route *r;
+ uint8_t *buf;
+ struct msg_header *hdr;
+ struct sockaddr_storage cliaddr;
+ socklen_t slen = sizeof(cliaddr);
+ int len, n, fd, cfd;
+ struct pollfd pfds[POLLER_MAX];
+ int pfds_count;
+ /* keep in sync with pg.h MSG enum */
+ int (*cmd_conn[MSG_MAX+1])
+ (struct conn *, int, struct msg_header *) = {
+ &cmd_init,
+ &cmd_init,
+ &cmd_kill,
+ &cmd_exec,
+ &cmd_read,
+ &cmd_write,
+ &cmd_data_conn,
+ NULL,
+ NULL
+ };
+ int (*cmd_route[MSG_MAX+1])
+ (struct route *, int, struct msg_header *) = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &cmd_data_route,
+ &cmd_ok,
+ &cmd_err
+ };
+
+
+ /* OLD TODO
+ * fork frontend and open pipe_fe
+ * poll()
+ * - pipe_fe input -> write stdout
+ * - stdin -> append send_buf
+ * sigalarm() every second
+ * - MSG_READ and write on stdout
+ * - if size(send_buf) > 0: MSG_WRITE send_buf
+ * handle network errors / retransmission in fe
+ * MSG_[READ|WRITE] do not expect MSG_OK, but warns on MSG_ERROR
+ * MSG_OK only for MSG_INIT and MSG_EXEC
+ */
+
+ for (;;) {
+ pfds_count = pfds_create(pfds);
+ n = poll(pfds, pfds_count, POLL_TIMEOUT);
+ log_tmp("end of poll, %d fds on %d", n, pfds_count);
+ if (n < 0) {
+ log_warn("polling error"); // XXX fatal ?
+ continue;
+ }
+
+ for (n=0; n<pfds_count; n++) {
+ if ((pfds[n].revents & POLLIN) == 0)
+ continue;
+ fd = pfds[n].fd;
+ log_tmp(" fd %d got something", fd);
+
+ if ((l = listener_find(fd))) {
+ log_debug("got listener new connection");
+ cfd = accept(fd, (struct sockaddr *)&cliaddr, &slen);
+ listener_conn_add(l, cfd);
+ } else if ((c = listener_conn_find_exec(fd))) {
+ log_debug("got exec data");
+ if (c->async)
+ listener_conn_exec_bufferize(c, fd);
+ else
+ msg_send_from_fd(c->fd, MSG_DATA, c->orig, 0, fd);
+ } else if (fd == fileno(stdin)) {
+ log_debug("got stdin data");
+ r = route_find(conf.client_dest);
+ if (r->proc.async)
+ route_bufferize(r, fd);
+ else
+ msg_send_from_fd(r->proc.fd[1], MSG_DATA, r->dest, 0, fd);
+ } else {
+ log_debug("got command");
+ len = readbuf(fd, &buf, sizeof(MSG_MAGIC));
+ if (len < sizeof(MSG_MAGIC)) {
+ log_info("Magic number too short");
+ continue;
+ }
+ if (memcmp(buf, MSG_MAGIC, sizeof(MSG_MAGIC))) {
+ log_info("Invalid magic number %4x", buf);
+ continue;
+ }
+ len = readbuf(fd, &buf, MSG_HEADER_SIZE_ENCODED);
+ if (len < MSG_HEADER_SIZE_ENCODED) {
+ log_warn("Message header too short");
+ continue;
+ }
+ hdr = msg_unpack_header(buf);
+ if (!hdr) {
+ log_warn("Invalid message header");
+ continue;
+ }
+
+ if (hdr->dest != conf.me) {
+ route_fw(fd, hdr, buf);
+ free(hdr);
+ continue;
+ }
+
+ if ((c = listener_conn_find(fd))) {
+ cmd_conn[hdr->type](c, fd, hdr);
+ break;
+ } else if ((r = route_find(hdr->orig))) {
+ cmd_route[hdr->type](r, fd, hdr);
+ break;
+ } else
+ log_warn("host %c has no reference ! ignoring command...",
+ hdr->orig);
+ free(hdr);
+ }
+ }
+ }
+}
+
+static int
+pfds_create(struct pollfd *pfds) {
+ struct route *r;
+ struct listener *l;
+ struct conn *c;
+ int count = 0;
+
+#define ADDFD(myfd) { \
+pfds[count].fd = myfd; \
+pfds[count].events = POLLIN; \
+count++; \
+}
+
+ if (!conf.server)
+ ADDFD(fileno(stdin))
+
+ LIST_FOREACH(r, &routes, entry) {
+ switch (r->type) {
+ case ROUTE_PROC:
+ ADDFD(r->proc.fd[0])
+ }
+ }
+
+ LIST_FOREACH(l, &listeners, entry) {
+ ADDFD(l->sock)
+ LIST_FOREACH(c, &l->conns, entry) {
+ ADDFD(c->fd)
+ if (c->exec.cmd)
+ ADDFD(c->exec.fd[0])
+ }
+ }
+
+ return count;
+}
+
+static int
+cmd_init(struct conn *c, int fd, struct msg_header *hdr) {
+ struct conn *oldc;
+
+ log_debug("received INIT from %c :)", hdr->orig);
+ if (c->state == CONN_READY) {
+ log_warn("received INIT on an already open connection !");
+ send_cmd(hdr->orig, MSG_ERR, 0, NULL, 0);
+ return -1;
+ }
+
+ /* checking if this client already has a connection opened on
+ * another fd */
+ oldc = listener_conn_find_orig(hdr->orig);
+ if (oldc)
+ listener_conn_move(oldc, c);
+
+ c->orig = hdr->orig;
+ c->state = CONN_READY;
+
+ switch (hdr->type) {
+ case MSG_INIT:
+ c->async = 0;
+ break;
+ case MSG_INIT_ASYNC:
+ c->async = 1;
+ break;
+ }
+
+ send_cmd(hdr->orig, MSG_OK, 0, NULL, 0);
+
+ return 0;
+}
+
+static int
+cmd_exec(struct conn *c, int fd, struct msg_header *hdr) {
+ char *cmd = NULL;
+ int cmdlen;
+ char **argv = NULL;
+ int argc;
+
+ cmdlen = readbuf(fd, (uint8_t **)&cmd, hdr->datalen);
+ if (cmdlen <= 0 || !cmd)
+ goto err;
+ argv = explode(cmd, cmdlen, " ", &argc);
+ if (!argv)
+ goto err;
+ if (listener_conn_exec(c, argv[0], argv) < 0)
+ goto err;
+
+ send_cmd(hdr->orig, MSG_OK, 0, NULL, 0);
+
+ return 0;
+
+err:
+ log_debug("exec failed");
+ if (cmd)
+ free(cmd);
+ if (argv)
+ free(argv);
+ send_cmd(hdr->orig, MSG_ERR, 0, NULL, 0);
+
+ return -1;
+}
+
+static int
+cmd_kill(struct conn *c, int fd, struct msg_header *hdr) {
+ listener_conn_exec_kill(c);
+ send_cmd(hdr->orig, MSG_OK, 0, NULL, 0);
+
+ return 0;
+}
+
+static int
+cmd_read(struct conn *c, int fd, struct msg_header *hdr) {
+ int len;
+
+ if (!c->exec.cmd) {
+ log_warn("cmd_read: no exec in progress !");
+ goto err;
+ }
+ if (c->async == 0) {
+ log_warn("cmd_read: not in async mode !");
+ goto err;
+ }
+
+ len = send_cmd(c->orig, MSG_DATA, 0,
+ c->exec.async_writebuf, c->exec.async_writebuf_size);
+ if (len < 0)
+ goto err;
+ c->exec.async_writebuf_size -= len;
+ c->exec.async_writebuf = realloc(c->exec.async_writebuf,
+ c->exec.async_writebuf_size);
+ return 0;
+
+err:
+ send_cmd(c->orig, MSG_ERR, 0, NULL, 0);
+ return -1;
+}
+
+static int
+cmd_write(struct conn *c, int fd, struct msg_header *hdr) {
+ if (!c->exec.cmd) {
+ log_warn("cmd_write: no exec in progress !");
+ goto err;
+ }
+ if (c->async == 0) {
+ log_warn("cmd_write: not in async mode !");
+ goto err;
+ }
+
+ if (msg_read_data_to_fd(fd, c->exec.fd[1], hdr->datalen) < 0) {
+ goto err;
+ }
+
+ return 0;
+
+err:
+ send_cmd(c->orig, MSG_ERR, 0, NULL, 0);
+ return -1;
+}
+
+static int
+cmd_data_conn(struct conn *c, int fd, struct msg_header *hdr) {
+ log_debug("received DATA from conn %c !", hdr->orig);
+
+ if (msg_read_data_to_fd(fd, c->exec.fd[1], hdr->datalen) < 0) {
+ log_warn("cmd_data: recvwrite failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+cmd_data_route(struct route *r, int fd, struct msg_header *hdr) {
+ log_debug("received DATA from route %c !", hdr->orig);
+
+ if (msg_read_data_to_fd(fd, fileno(stdout), hdr->datalen) < 0) {
+ log_warn("cmd_data: recvwrite failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+cmd_ok(struct route *r, int fd, struct msg_header *hdr) {
+ log_info("received OK from %c !", hdr->orig);
+
+ return 0;
+}
+
+static int
+cmd_err(struct route *r, int fd, struct msg_header *hdr) {
+ log_info("received ERROR from %c !", hdr->orig);
+
+ return 0;
+}
+
diff --git a/broken/propagate/src/pg.h b/broken/propagate/src/pg.h
new file mode 100644
index 0000000..b671883
--- /dev/null
+++ b/broken/propagate/src/pg.h
@@ -0,0 +1,174 @@
+#include <stdint.h>
+#include <stdarg.h>
+#include <poll.h>
+#include "queue.h"
+
+#define VERSION 0.1
+
+#define BUFMAX 1024 * 10
+#define READSIZE 1024
+#define WRITESIZE 1024
+
+#define POLL_TIMEOUT 1000
+#define POLL_TIMEOUT_ATOMIC 500
+#define POLL_ATOMIC_RETRY 3
+
+#define ROUTES_MAX 256
+#define LISTENERS_MAX 16
+#define LISTENER_CONN_MAX 10
+#define POLLER_MAX (ROUTES_MAX + (LISTENERS_MAX + LISTENERS_MAX * LISTENER_CONN_MAX))
+#define EXEC_MAX 10
+
+#define MSG_MAGIC "bXl4"
+struct msg_header {
+ uint8_t version;
+ uint8_t type;
+ uint8_t orig;
+ uint8_t dest;
+ uint8_t arg; /* UNUSED */
+ uint16_t datalen;
+};
+#define MSG_HEADER_SIZE 7
+// XXX change 8 to 13 when we'll use base64
+#define MSG_HEADER_SIZE_ENCODED 7
+#define MSG_VERSION 1
+#define MSG_DATALEN_MAX 1024 * 100
+
+/* keep in sync with pg.c process() cmd */
+/* keep in sync with msg.c msg_unpack_header msg_client and msg_server */
+/* XXX fix that mess, all in one file */
+enum msg_type {
+ MSG_INIT = 0,
+ MSG_INIT_ASYNC = 1,
+ MSG_KILL = 2,
+ MSG_EXEC = 3,
+ MSG_READ = 4,
+ MSG_WRITE = 5,
+ MSG_DATA = 6,
+ MSG_OK = 7,
+ MSG_ERR = 8
+};
+#define MSG_MAX 8
+
+struct route {
+ char dest;
+ int type;
+#define ROUTE_PROC 0
+#define ROUTE_GW 1
+ union {
+ struct {
+ char *cmd;
+ char **argv;
+ int pid;
+ int fd[2];
+ int async;
+ uint8_t *async_sndbuf;
+ int async_sndbuf_size;
+ } proc;
+ struct {
+ char dest;
+ } gw;
+ };
+ LIST_ENTRY(route) entry;
+};
+
+struct listener {
+ int sock;
+ int type;
+#define LISTENER_UNIX 0
+#define LISTENER_INET 1
+ union {
+ struct {
+ char *path;
+ } sock_unix;
+ struct {
+ // XXX TODO
+ //struct addrinfo *res;
+ } sock_inet;
+ };
+ LIST_HEAD(, conn) conns;
+ int conns_count;
+ LIST_ENTRY(listener) entry;
+};
+
+enum conn_state {
+ CONN_OPEN = 0,
+ CONN_READY = 1
+};
+
+struct conn {
+ struct listener *listener;
+ int orig;
+ int fd;
+ enum conn_state state;
+ int async;
+ struct {
+ char *cmd;
+ char **argv;
+ int pid;
+ int fd[2];
+ uint8_t *async_writebuf;
+ int async_writebuf_size;
+ } exec;
+ LIST_ENTRY(conn) entry;
+};
+
+struct conf {
+ char me;
+ int server;
+ char client_dest;
+};
+
+struct conf conf;
+LIST_HEAD(, route) routes;
+int routes_count;
+LIST_HEAD(, listener) listeners;
+int listeners_count;
+
+/* msg.c */
+int msg_send(int, int, char, uint8_t, int, uint8_t *);
+int msg_send_from_fd(int, int, char, uint8_t, int);
+uint8_t *msg_pack_header(int, char, char, uint8_t, int);
+struct msg_header *msg_unpack_header(uint8_t *);
+int msg_read_data(int, uint8_t **, int);
+int msg_read_data_to_fd(int, int, int);
+
+/* route.c */
+int route_add(char, int, char *, int, char *, char);
+int route_fw(int, struct msg_header *, uint8_t *);
+struct route *route_find(char);
+int route_bufferize(struct route *, int);
+
+/* listener.c */
+int listener_add(int, char *);
+struct listener *listener_find(int);
+struct listener *listener_find_orig(char);
+int listener_conn_add(struct listener *, int);
+void listener_conn_del(struct conn *);
+void listener_conn_move(struct conn *, struct conn *);
+struct conn *listener_conn_find(int);
+struct conn *listener_conn_find_orig(char);
+struct conn *listener_conn_find_exec(int);
+int listener_conn_exec(struct conn *, char *, char **);
+void listener_conn_exec_kill(struct conn *);
+int listener_conn_exec_bufferize(struct conn *, int);
+
+/* log.c */
+void log_init(int, int);
+void log_tmp(const char *, ...);
+void log_debug(const char *, ...);
+void log_info(const char *, ...);
+void log_warn(const char *, ...);
+void fatal(const char *, ...);
+
+/* util.c */
+int send_cmd(char, int, uint8_t, uint8_t *, int);
+int intab(int *, int, int);
+char **explode (char *, int, char *, int *);
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+int setnonblock(int);
+int execpipe(char *, char **, int *);
+int readbuf(int, uint8_t **, int);
+int writebuf(uint8_t *, int, int);
+int readwrite(int, int, int);
diff --git a/broken/propagate/src/queue.h b/broken/propagate/src/queue.h
new file mode 100644
index 0000000..fb23a72
--- /dev/null
+++ b/broken/propagate/src/queue.h
@@ -0,0 +1,568 @@
+/* $OpenBSD: queue.h,v 1.35 2012/01/11 00:06:48 bluhm Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_NEXT(head, elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST(head); \
+ (var) && ((tvar) = LIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_REMOVE_NEXT(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = CIRCLEQ_LAST(head, headname); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/broken/propagate/src/route.c b/broken/propagate/src/route.c
new file mode 100644
index 0000000..99f52ec
--- /dev/null
+++ b/broken/propagate/src/route.c
@@ -0,0 +1,138 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <poll.h>
+
+#include "pg.h"
+
+static int route_start(struct route *r);
+static int frontend_start(struct route *);
+
+int
+route_add(char dest, int type, char *cmd, int async, char *path, char gw) {
+ struct route *r;
+ char **argv;
+ int argc;
+
+ if (routes_count >= ROUTES_MAX) {
+ log_warn("routes max reached");
+ return -1;
+ }
+
+ r = xcalloc(1, sizeof(struct route));
+ if (!r)
+ return -1;
+
+ r->dest = dest;
+ r->type = type;
+ switch (type) {
+ case ROUTE_PROC:
+ argv = explode(cmd, strlen(cmd), " ", &argc);
+ log_tmp("route_add: explode argv 0 %s 1 %s 2 %s 3 %s argc %d",
+ argv[0], argv[1], argv[2], argv[3], argc);
+ if (!argv)
+ return -1;
+ r->proc.cmd = argv[0];
+ r->proc.argv = argv;
+ r->proc.async = async;
+ break;
+ case ROUTE_GW:
+ r->gw.dest = gw;
+ }
+
+ LIST_INSERT_HEAD(&routes, r, entry);
+
+ route_start(r);
+
+ return 0;
+}
+
+int
+route_fw(int fd, struct msg_header *hdr, uint8_t *hbuf) {
+ struct route *r;
+ int len;
+
+ log_debug("route %c to %c", hdr->orig, hdr->dest);
+
+ r = route_find(hdr->dest);
+ if (r) {
+ log_debug("route_fw: no route to %c", hdr->dest);
+ goto err;
+ }
+
+ // XXX HERE async bufferize
+
+ len = writebuf(hbuf, r->proc.fd[1], MSG_HEADER_SIZE_ENCODED);
+ if (len < 0)
+ goto err;
+ len = readwrite(fd, r->proc.fd[1], hdr->datalen);
+ if (len < 0)
+ goto err;
+
+ return 0;
+
+err:
+ log_warn("route_fw: err");
+ return -1;
+}
+
+int
+route_bufferize(struct route *r, int fd) {
+ uint8_t buf[BUFMAX];
+
+ log_warn("XXX route bufferize not implemented");
+ read(fd, buf, sizeof(buf));
+ return -1;
+}
+
+struct route *
+route_find(char dest) {
+ struct route *r;
+
+ LIST_FOREACH(r, &routes, entry) {
+ if (r->dest == dest) {
+ if (r->type == ROUTE_GW) {
+ dest = r->gw.dest;
+ continue;
+ }
+ return r;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+route_start(struct route *r) {
+ switch (r->type) {
+ case ROUTE_PROC:
+ if (frontend_start(r) < 0)
+ log_warn("frontend for dest %c failed to start", r->dest);
+ break;
+ }
+
+ send_cmd(r->dest, MSG_INIT, 0, NULL, 0);
+
+ return 0;
+}
+
+static int
+frontend_start(struct route *r) {
+ int pid;
+
+ /* XXX monitor death of fe / broken pipe, to restart it */
+ pid = execpipe(r->proc.cmd, r->proc.argv, r->proc.fd);
+ if (pid > 0)
+ r->proc.pid = pid;
+ log_debug("frontend_start: forked pid %d, fd %d %d", pid, r->proc.fd[0], r->proc.fd[1]);
+ setnonblock(r->proc.fd[0]);
+
+ return pid;
+}
+
diff --git a/broken/propagate/src/tests/Makefile b/broken/propagate/src/tests/Makefile
new file mode 100644
index 0000000..6f81535
--- /dev/null
+++ b/broken/propagate/src/tests/Makefile
@@ -0,0 +1,23 @@
+TEST_EXPLODE = test_explode
+TEST_B64 = test_base64
+CFLAGS = -g -Wall -std=c99
+LDFLAGS =
+
+all: $(TEST_EXPLODE) $(TEST_B64)
+
+%.o: %.c
+ $(CC) -o $@ -c $< $(CFLAGS) $(LDFLAGS) -I../
+
+$(TEST_EXPLODE): test_explode.o ../libihf.o
+ $(CC) test_explode.o ../libihf.o -o $(TEST_EXPLODE) $(LDFLAGS) $(CFLAGS) -I../
+
+$(TEST_B64): test_base64.o ../base64.o
+ $(CC) test_base64.o ../base64.o -o $(TEST_B64) $(LDFLAGS) $(CFLAGS) -I../
+
+.PHONY: clean mrproper
+
+mrproper: clean
+
+clean:
+ rm -f $(TEST_EXPLODE) $(TEST_B64) *~ *.o
+
diff --git a/broken/propagate/src/tests/test_base64.c b/broken/propagate/src/tests/test_base64.c
new file mode 100644
index 0000000..e32ff65
--- /dev/null
+++ b/broken/propagate/src/tests/test_base64.c
@@ -0,0 +1,88 @@
+/*
+ ihf - Tool for bypassing firewalls
+ Copyright (C) 2012 m_101, laurent
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "base64.h"
+
+#define TEST_STR "ag0 arg1 arg2 arg3 arg4\n"
+
+void print_array_uint8_t (uint8_t *array, int n_elements) {
+ int idx_array;
+
+ if (!array || n_elements <= 0)
+ return;
+
+ for (idx_array = 0; idx_array < n_elements; idx_array++)
+ printf("%02x ", array[idx_array]);
+ putchar('\n');
+}
+
+int main(int argc, char *argv[]) {
+ int idx_buf, sz_buf;
+ char *buf;
+ // b64
+ struct base64_decode_context dctx;
+ char *b64_encoded, *b64_decoded;
+ size_t len_decoded;
+
+ sz_buf = 256 + strlen(TEST_STR) + 1;
+
+ buf = calloc(sz_buf, sizeof(*buf));
+ if (!buf) {
+ fprintf(stderr, "error: Could not alloc buffer\n");
+ return -1;
+ }
+
+ for (idx_buf = 0; idx_buf < 256; idx_buf++)
+ buf[idx_buf] = idx_buf;
+ memcpy(buf + 256, TEST_STR, strlen(TEST_STR));
+
+ printf("printing array...\n");
+ print_array_uint8_t (buf, sz_buf - 1);
+
+ printf("\nEncoding buffer ...\n");
+ base64_encode_alloc(buf, sz_buf - 1, &b64_encoded);
+ if (!b64_encoded) {
+ fprintf(stderr, "error: Could not encode buffer\n");
+ return -1;
+ }
+
+ printf("encoded: %s\n\n", b64_encoded);
+
+ printf("Decoding buffer ...\n");
+ base64_decode_ctx_init (&dctx);
+ base64_decode_alloc_ctx(&dctx,
+ b64_encoded, strlen(b64_encoded),
+ &b64_decoded, &len_decoded);
+ printf("len_decoded: %lu\n\n", len_decoded);
+
+ printf("printing array...\n");
+ print_array_uint8_t (b64_decoded, len_decoded);
+
+ printf("comparing arrays...\n");
+ if (memcmp(buf, b64_decoded, len_decoded))
+ printf("Decoding failed\n");
+ else
+ printf("Decoding succeeded\n");
+
+ return 0;
+}
+
diff --git a/broken/propagate/src/tests/test_explode.c b/broken/propagate/src/tests/test_explode.c
new file mode 100644
index 0000000..0210397
--- /dev/null
+++ b/broken/propagate/src/tests/test_explode.c
@@ -0,0 +1,39 @@
+/*
+ ihf - Tool for bypassing firewalls
+ Copyright (C) 2012 m_101, laurent
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libihf.h"
+
+#define TEST_STR "arg0 arg1 arg2 arg3 arg4\n"
+
+int main(int argc, char *argv[]) {
+ char *toxplod = strdup(TEST_STR);
+ int n_exploded;
+ char **exploded = explode(toxplod, strlen(toxplod) + 1, " ", &n_exploded);
+ int idx_exploded;
+
+ printf("\nn_exploded: %d\n", n_exploded);
+ for (idx_exploded = 0; idx_exploded < n_exploded; idx_exploded++) {
+ printf("exploded %d: %s\n", idx_exploded, exploded[idx_exploded]);
+ }
+
+ return 0;
+}
+
diff --git a/broken/propagate/src/util.c b/broken/propagate/src/util.c
new file mode 100644
index 0000000..646492a
--- /dev/null
+++ b/broken/propagate/src/util.c
@@ -0,0 +1,233 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+
+#include "pg.h"
+#include "atomicio.h"
+
+int
+send_cmd(char dest, int type, uint8_t arg, uint8_t *data, int data_len) {
+ struct conn *c;
+ struct route *r;
+ int fd = -1, len;
+
+ r = route_find(dest);
+ if (r)
+ fd = r->proc.fd[1];
+ else {
+ c = listener_conn_find_orig(dest);
+ if (c)
+ fd = c->fd;
+ else {
+ log_warn("send_cmd %d: no route to go to dest %c", type, dest);
+ return -1;
+ }
+ }
+ len = msg_send(fd, type, dest, arg, data_len, data);
+
+ return len;
+}
+
+int
+intab(int *tab, int val, int len) {
+ int n;
+
+ for (n=0; n<len; n++) {
+ if (tab[n] == val)
+ return n;
+ }
+ return -1;
+}
+
+char **
+explode(char *str, int len_str, char *delim, int *n_tokens) {
+ char *token, *saveptr;
+ char *to_explode;
+ char **exploded;
+ int idx_str, idx_delim;
+ int len_delim;
+ int c_tokens;
+
+ if (!str || len_str <= 0 || !delim || !n_tokens)
+ return NULL;
+
+ to_explode = xcalloc(len_str, sizeof(*to_explode));
+ if (!to_explode)
+ return NULL;
+ memcpy(to_explode, str, len_str);
+
+ /* count n_tokens */
+ len_delim = strlen(delim);
+ c_tokens = 1;
+ for (idx_str = 0; idx_str < len_str; idx_str++) {
+ for (idx_delim = 0; idx_delim < len_delim; idx_delim++) {
+ if (str[idx_str] == delim[idx_delim])
+ c_tokens++;
+ }
+ }
+ c_tokens++;
+
+ /* copy str (strdup() does not seem to be reliable) */
+ exploded = xcalloc(c_tokens, sizeof(*exploded));
+ if (!exploded) {
+ free(to_explode);
+ return NULL;
+ }
+
+ /* sep tokens */
+ *n_tokens = 0;
+ token = strtok_r(to_explode, delim, &saveptr);
+ while (token) {
+ exploded[*n_tokens] = token;
+ (*n_tokens)++;
+ token = strtok_r(NULL, delim, &saveptr);
+ }
+ exploded[*n_tokens] = NULL;
+
+ return exploded;
+}
+
+void
+*xmalloc(size_t size) {
+ void *p;
+
+ if (size == 0) {
+ log_warn("attempted to malloc(0)");
+ return NULL;
+ }
+ p = malloc(size);
+ if (!p)
+ log_warn("malloc(%d) failed", size);
+
+ return p;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size) {
+ void *p;
+
+ if (nmemb == 0 || size == 0) {
+ log_warn("attempted to calloc(%d,%d)", nmemb, size);
+ return NULL;
+ }
+ p = calloc(nmemb, size);
+ if (!p)
+ log_warn("calloc(%d,%d) failed", nmemb, size);
+
+ return p;
+}
+
+int
+setnonblock(int fd) {
+ int flags;
+ int ret;
+
+ log_debug("nonblock %d", fd);
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ flags = 0;
+ ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret < 0 )
+ log_warn("fcntl setnonblock err : %s", strerror(errno));
+
+ return ret;
+}
+
+int
+execpipe(char *cmd, char **argv, int *pipes) {
+ int pipe_in[2], pipe_out[2];
+ pid_t pid;
+
+ pipe(pipe_in);
+ pipe(pipe_out);
+
+ pid = fork();
+ if (pid == -1) {
+ log_warn("failed to fork !");
+ return -1;
+ }
+
+ if (pid == 0) {
+ close(pipe_in[0]);
+ dup2(pipe_in[1], fileno(stdout));
+ close(pipe_in[1]); // XXX correct ?
+ close(pipe_out[1]);
+ dup2(pipe_out[0], fileno(stdin));
+ close(pipe_out[0]); // XXX correct ?
+ if (execvp(cmd, argv) == -1) {
+ log_warn("execpipe child: failed to execute %s %s : %d", cmd, argv, errno);
+ exit(1);
+ }
+
+ /* NOT REACHED */
+ }
+
+ pipes[0] = pipe_in[0];
+ pipes[1] = pipe_out[1];
+
+ return pid;
+}
+
+int
+readbuf(int fd, uint8_t **out, int len) {
+ uint8_t buf[BUFMAX];
+ int outlen;
+
+ if (len > sizeof(buf)) {
+ log_warn("readbuf: len too big %d", len);
+ return -1;
+ }
+ if ((outlen = atomicio(read, fd, buf, len)) < 0) {
+ log_warn("readbuf: atomicio read error");
+ return -1;
+ }
+ if (outlen != len)
+ log_warn("readbuf: invalid atomicio read size %d, should be %d",
+ outlen, len);
+
+ *out = buf;
+ return outlen;
+}
+
+int
+writebuf(uint8_t *buf, int fd, int len) {
+ int writelen;
+
+ if ((writelen = atomicio(vwrite, fd, buf, len)) < 0) {
+ log_warn("writebuf: atomicio write error");
+ return -1;
+ }
+ if (writelen < len)
+ log_warn("writebuf: incomplete write, %d on %d",
+ writelen, len);
+
+ return writelen;
+}
+
+int
+readwrite(int ifd, int ofd, int len) {
+ char buf[BUFMAX];
+ int red, wrote;
+
+ red = atomicio(read, ifd, buf, len);
+ if (red < 0) {
+ log_warn("readwrite: atomicio read error");
+ return -1;
+ }
+ if (red != len)
+ log_warn("readbuf: invalid atomicio read size %d, should be %d",
+ red, len);
+
+ if ((wrote = atomicio(vwrite, ofd, buf, red)) < red) {
+ log_warn("readwrite: atomicio write error");
+ return -1;
+ }
+
+ return wrote;
+}