aboutsummaryrefslogtreecommitdiff
path: root/rcynic/rcynic.c
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2006-09-21 14:11:31 +0000
committerRob Austein <sra@hactrn.net>2006-09-21 14:11:31 +0000
commitee3b1724e7f5076dacaa6a2b82daa8092360e53d (patch)
treec64d555f95d5e02ee89b3475e1175803b70b9458 /rcynic/rcynic.c
parent212f806a8f77b4d7831072d2ae50305573bae766 (diff)
Checkpoint
svn path=/rcynic/rcynic.c; revision=298
Diffstat (limited to 'rcynic/rcynic.c')
-rw-r--r--rcynic/rcynic.c148
1 files changed, 112 insertions, 36 deletions
diff --git a/rcynic/rcynic.c b/rcynic/rcynic.c
index 33d6b252..352907f1 100644
--- a/rcynic/rcynic.c
+++ b/rcynic/rcynic.c
@@ -19,6 +19,9 @@
/*
* "Cynical rsync": Recursively walk RPKI tree using rsync to pull
* data from remote sites, validating certificates and CRLs as we go.
+ *
+ * I'll probably end up breaking this up into multiple smaller files,
+ * but it's easiest to put everything in a single mongo file initially.
*/
#include <assert.h>
@@ -35,6 +38,18 @@
#include <time.h>
#include <string.h>
#include <sys/stat.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/safestack.h>
+
+DECLARE_STACK_OF(char)
+
+static STACK_OF(char) *rsync_cache;
+
+
/*
* Logging functions.
@@ -87,27 +102,78 @@ static void fatal(int retval, char *fmt, ...)
exit(retval);
}
+
+
+/*
+ * Make a directory if it doesn't already exist.
+ */
+
+static int mkdir_maybe(char *name)
+{
+ char *b, buffer[FILENAME_MAX];
+
+ if (!name || strlen(name) >= sizeof(buffer) - 2)
+ return 0;
+ strcpy(buffer, name);
+ strcat(buffer, "/.");
+ if (access(buffer, F_OK) == 0)
+ return 1;
+ if ((b = strrchr(strrchr(buffer, '/'), '/')) != 0) {
+ *b = '\0';
+ if (!mkdir_maybe(buffer))
+ return 0;
+ }
+ return mkdir(name, 0777) == 0;
+}
+
+/*
+ * Convert an rsync URI to a filename, checking for evil character
+ * sequences.
+ */
+
+static char *uri_to_filename(char *name)
+{
+ int n;
+
+ if (!name || strncmp(name, "rsync://", sizeof("rsync://") - 1))
+ return 0;
+ name += sizeof("rsync://") - 1;
+
+ if (name[0] == '/' || name[0] == '.' || !strcmp(name, "../") ||
+ strstr(name, "//") || strstr(name, "/../") ||
+ ((n = strlen(name)) >= 3 && !strcmp(name + n - 3, "/..")))
+ return 0;
+
+ return strdup(name);
+}
+
+
+
/*
* Run rsync.
*
* This probably isn't paranoid enough. Should use select() to do
* some kind of timeout when rsync is taking too long. Breaking the
* log stream into lines without fgets() is a pain, maybe setting
- * nonblocking I/O before calling fdopen() would suffice to let us
- * use select()? If we can time out, we need to be able to kill()
- * the rsync process. Might need to use waitpid() instead of wait()
- * so we can specify the pid we want and WNOHANG.
+ * nonblocking I/O before calling fdopen() would suffice to let us use
+ * select()? If we time out, we need to kill() the rsync process.
*/
static char *rsync_cmd[] = {
"rsync", "--update", "--times", "--copy-links", "--itemize-changes"
};
+static int rsync_cmp(const char * const *a, const char * const *b)
+{
+ return strcmp(*a, *b);
+}
+
static int rsync(char *args, ...)
{
- char *uri = 0, *argv[100], buffer[2000];
+ char *uri = 0, *path, *s, *argv[100], buffer[2000];
int argc, pipe_fds[2], n, pid_status = -1;
va_list ap;
+ pid_t pid;
FILE *f;
for (argc = 0; argc < sizeof(rsync_cmd)/sizeof(*rsync_cmd); argc++)
@@ -122,17 +188,43 @@ static int rsync(char *args, ...)
}
va_end(ap);
- /*
- * This is where we'd check to see if we've already pulled this URI.
- */
+ if (!uri) {
+ logmsg("Couldn't extract URI from rsync command");
+ return 0;
+ }
- if (pipe(pipe_fds) < 0)
- fatal(1, "pipe() failed");
+ if ((path = uri_to_filename(uri)) == NULL) {
+ logmsg("Couldn't extract filename from URI for rsync cache");
+ return 0;
+ }
+
+ assert(rsync_cache != NULL);
+ if ((s = sk_value(rsync_cache, sk_find(rsync_cache, path))) != NULL &&
+ !strncmp(s, path, strlen(s))) {
+ logmsg("Cache hit %s for URI %s, skipping rsync", s, uri);
+ free(path);
+ return 1;
+ }
- switch (vfork()) {
- case -1:
- fatal(1, "vfork() failed");
- case 0:
+ if (pipe(pipe_fds) < 0) {
+ logmsg("pipe() failed");
+ return 0;
+ }
+
+ if ((f = fdopen(pipe_fds[0], "r")) == NULL) {
+ logmsg("Couldn't fdopen() rsync's output stream");
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ return 0;
+ }
+
+ switch ((pid = vfork())) {
+ case -1:
+ logmsg("vfork() failed");
+ fclose(f);
+ close(pipe_fds[1]);
+ return 0;
+ case 0:
close(pipe_fds[0]);
if (dup2(pipe_fds[1], 1) < 0)
fatal(-2, "dup2(1) failed");
@@ -144,9 +236,6 @@ static int rsync(char *args, ...)
close(pipe_fds[1]);
- if ((f = fdopen(pipe_fds[0], "r")) == NULL)
- fatal(1, "Couldn't open rsync's output stream");
-
while (fgets(buffer, sizeof(buffer), f)) {
char *s = strchr(buffer, '\n');
if (s)
@@ -154,7 +243,9 @@ static int rsync(char *args, ...)
logmsg("%s", buffer);
}
- wait(&pid_status);
+ sk_push(rsync_cache, path);
+
+ waitpid(pid, &pid_status, 0);
if (WEXITSTATUS(pid_status)) {
logmsg("rsync exited with status %d", pid_status);
@@ -164,24 +255,9 @@ static int rsync(char *args, ...)
}
}
+
+
/*
- * Make a directory if it doesn't already exist.
+ * Dunno yet whether Perl parse_cert() has a C equivalent.
*/
-static int mkdir_maybe(char *name)
-{
- char *b, buffer[FILENAME_MAX];
-
- if (!name || strlen(name) >= sizeof(buffer) - 2)
- return 0;
- strcpy(buffer, name);
- strcat(buffer, "/.");
- if (access(buffer, F_OK) == 0)
- return 1;
- if ((b = strrchr(strrchr(buffer, '/'), '/')) != 0) {
- *b = '\0';
- if (!mkdir_maybe(buffer))
- return 0;
- }
- return mkdir(name, 0777) == 0;
-}