aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2016-02-25 05:22:47 +0100
committerJason A. Donenfeld <Jason@zx2c4.com>2016-02-25 05:22:47 +0100
commit70afda655d56960218898d130113eeca51b38455 (patch)
treea9d04671128fd8435cb762bb93e1daad1909d88d
parentWork on musl (diff)
downloadgit-daemon-dummy-70afda655d56960218898d130113eeca51b38455.tar.xz
git-daemon-dummy-70afda655d56960218898d130113eeca51b38455.zip
Automatically determine host name
-rw-r--r--README.md1
-rw-r--r--git-daemon-dummy.c274
-rw-r--r--openrc/git-daemon-dummy.confd1
-rwxr-xr-xopenrc/git-daemon-dummy.initd2
4 files changed, 141 insertions, 137 deletions
diff --git a/README.md b/README.md
index d788cf2..bc9e428 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,6 @@ use of `epoll`.
### Usage
Usage: ./git-daemon-dummy [OPTION]...
- -u URI, --uri-prefix=URI use URI as prefix to redirect uri (default=https://git.example.com)
-d, --daemonize run as a background daemon
-f, --foreground run in the foreground (default)
-P FILE, --pid-file=FILE write pid of listener process to FILE
diff --git a/git-daemon-dummy.c b/git-daemon-dummy.c
index 12975f2..b515323 100644
--- a/git-daemon-dummy.c
+++ b/git-daemon-dummy.c
@@ -29,8 +29,6 @@
#include <netinet/in.h>
#include "seccomp-bpf.h"
-static const char MESSAGE_TEMPLATE[] = "\n******************************************************\n\n This git repository has moved! Please clone with:\n\n $ git clone %s%s\n\n******************************************************";
-
enum {
LISTEN_BACKLOG = 16,
EPOLL_EVENTS = 128,
@@ -125,10 +123,9 @@ void seccomp_enable_filter(void)
}
}
-static void parse_options(int argc, char *argv[], bool *daemonize, int *port, char **pid_file, char **message)
+static void parse_options(int argc, char *argv[], bool *daemonize, int *port, char **pid_file)
{
static const struct option long_options[] = {
- {"uri-prefix", required_argument, NULL, 'u'},
{"daemonize", no_argument, NULL, 'd'},
{"foreground", no_argument, NULL, 'f'},
{"port", required_argument, NULL, 'p'},
@@ -138,23 +135,12 @@ static void parse_options(int argc, char *argv[], bool *daemonize, int *port, ch
};
int option_index = 0, option;
- *message = NULL;
*pid_file = NULL;
*daemonize = false;
*port = 9418;
- while ((option = getopt_long(argc, argv, "u:dfP:p:h", long_options, &option_index)) != -1) {
+ while ((option = getopt_long(argc, argv, "dfP:p:h", long_options, &option_index)) != -1) {
switch (option) {
- case 'u': {
- size_t len = strlen(optarg);
- if (len && optarg[len - 1] == '/')
- optarg[len - 1] = '\0';
- if (asprintf(message, MESSAGE_TEMPLATE, optarg, "%s") < 0) {
- perror("asprintf");
- exit(EXIT_FAILURE);
- }
- break;
- }
case 'd':
*daemonize = true;
break;
@@ -171,7 +157,6 @@ static void parse_options(int argc, char *argv[], bool *daemonize, int *port, ch
case '?':
default:
fprintf(stderr, "Usage: %s [OPTION]...\n", argv[0]);
- fprintf(stderr, " -u URI, --uri-prefix=URI use URI as prefix to redirect uri (default=https://git.example.com)\n");
fprintf(stderr, " -d, --daemonize run as a background daemon\n");
fprintf(stderr, " -f, --foreground run in the foreground (default)\n");
fprintf(stderr, " -P FILE, --pid-file=FILE write pid of listener process to FILE\n");
@@ -180,14 +165,6 @@ static void parse_options(int argc, char *argv[], bool *daemonize, int *port, ch
exit(option == 'h' ? EXIT_SUCCESS : EXIT_FAILURE);
}
}
-
- if (!*message) {
- if (asprintf(message, MESSAGE_TEMPLATE, "https://git.example.com", "%s") < 0) {
- perror("asprintf");
- exit(EXIT_FAILURE);
- }
- fprintf(stderr, "Warning: please specify -u/--uri-prefix to avoid returning the example prefix.\n");
- }
}
static int get_listen_socket(int port)
@@ -270,130 +247,55 @@ static int setup_epoll(int listen_fd)
return epoll_fd;
}
-static void handle_new_connection(int epoll_fd, struct epoll_event *event)
+static void process_request(int epoll_fd, int fd, bool direct);
+struct out_data {
+ int fd;
+ unsigned int len;
+ char data[];
+};
+
+static void event_new_connection(int epoll_fd, struct epoll_event *event)
{
int connection_fd;
- struct epoll_event new_event = {
- .events = EPOLLIN | EPOLLET
- };
if (event->events & EPOLLERR || event->events & EPOLLHUP)
exit(EXIT_FAILURE);
for (;;) {
connection_fd = accept4(event->data.fd, NULL, NULL, SOCK_NONBLOCK);
- if (connection_fd < 0) {
- if (errno == EAGAIN || errno == EWOULDBLOCK)
- break;
- perror("accept4");
+ if (connection_fd < 0)
break;
- }
- new_event.data.fd = connection_fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, connection_fd, &new_event) < 0) {
- perror("epoll_ctl");
- close(connection_fd);
- }
+ process_request(epoll_fd, connection_fd, true);
}
}
-struct out_data {
- int fd;
- char repo[];
-};
-
-static void handle_read_data(int epoll_fd, struct epoll_event *event)
+static void event_can_read(int epoll_fd, struct epoll_event *event)
{
- struct out_data *out;
- char buf[MAX_MSG_SIZE];
- char *repo;
- ssize_t len;
- unsigned long total_size;
- struct epoll_event new_event = {
- .events = EPOLLOUT
- };
-
- if (read(event->data.fd, buf, 4) != 4) {
- if (errno != EAGAIN && errno != EWOULDBLOCK)
- close(event->data.fd);
- return;
- }
- buf[4] = '\0';
- total_size = strtoul(buf, NULL, 16);
- if (total_size <= 4 || total_size > MAX_MSG_SIZE) {
- close(event->data.fd);
- return;
- }
-
- len = read(event->data.fd, buf, total_size - 4);
- if (len != total_size - 4) {
- close(event->data.fd);
- return;
- }
- if (buf[len - 1]) {
- close(event->data.fd);
- return;
- }
- if (strncmp(buf, "git-upload-pack ", strlen("git-upload-pack "))) {
- close(event->data.fd);
- return;
- }
-
- repo = buf + strlen("git-upload-pack ");
- if (repo[0] != '/') {
- --repo;
- repo[0] = '/';
- }
- out = malloc(sizeof(struct out_data) + strlen(repo) + 1);
- if (!out) {
- close(event->data.fd);
- return;
- }
- out->fd = event->data.fd;
- strcpy(out->repo, repo);
- new_event.data.ptr = out;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, out->fd, &new_event) < 0) {
- free(out);
+ if (event->events & EPOLLERR || event->events & EPOLLHUP)
close(event->data.fd);
- }
+ else
+ process_request(epoll_fd, event->data.fd, false);
}
-static void handle_write_data(int epoll_fd, const char *message, struct epoll_event *event)
+static void event_can_write(int epoll_fd, struct epoll_event *event)
{
struct out_data *out = event->data.ptr;
- char hexlen[128];
- size_t msg_len = strlen(out->repo) + strlen(message) - 2 + 1;
- unsigned int len = 8 + msg_len;
- struct {
- char len[4];
- char err[4];
- char message[msg_len];
- } __attribute__((packed)) to_send;
-
- sprintf(hexlen, "%04x", len);
- memcpy(to_send.len, hexlen, 4);
- memcpy(to_send.err, "ERR ", 4);
- sprintf(to_send.message, message, out->repo);
-
- if (write(out->fd, (void *)&to_send, len) < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ if (event->events & EPOLLERR || event->events & EPOLLHUP) {
+ close(out->fd);
+ free(out);
return;
-
- close(out->fd);
- free(out);
-}
-
-static void handle_new_data(int epoll_fd, const char *message, struct epoll_event *event)
-{
- if (event->events & EPOLLERR || event->events & EPOLLHUP || !(event->events & EPOLLIN || event->events & EPOLLOUT)) {
- close(event->data.fd);
+ }
+ if (write(out->fd, out->data, out->len) != out->len) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ close(out->fd);
+ free(out);
+ }
return;
}
- if (event->events & EPOLLIN)
- handle_read_data(epoll_fd, event);
- if (event->events & EPOLLOUT)
- handle_write_data(epoll_fd, message, event);
+ close(out->fd);
}
-static void event_loop(int epoll_fd, int listen_fd, const char *message)
+static void event_loop(int epoll_fd, int listen_fd)
{
struct epoll_event events[EPOLL_EVENTS] = { 0 };
int num_events;
@@ -401,30 +303,134 @@ static void event_loop(int epoll_fd, int listen_fd, const char *message)
while ((num_events = epoll_wait(epoll_fd, events, EPOLL_EVENTS, -1)) >= 0) {
for (int i = 0; i < num_events; ++i) {
if (events[i].data.fd == listen_fd)
- handle_new_connection(epoll_fd, &events[i]);
- else
- handle_new_data(epoll_fd, message, &events[i]);
+ event_new_connection(epoll_fd, &events[i]);
+ else if (events[i].events & EPOLLIN)
+ event_can_read(epoll_fd, &events[i]);
+ else if (events[i].events & EPOLLOUT)
+ event_can_write(epoll_fd, &events[i]);
}
}
+}
+
- perror("epoll_wait");
- exit(EXIT_FAILURE);
+static int parse_data(int fd, char *buf, char **repo, char **host)
+{
+ size_t line_len, parsed_len;
+ ssize_t len;
+
+ if (read(fd, buf, 4) != 4)
+ return (errno == EAGAIN || errno == EWOULDBLOCK) ? -2 : -1;
+
+ buf[4] = '\0';
+ parsed_len = strtoul(buf, NULL, 16);
+ if (parsed_len <= 4)
+ return -1;
+ parsed_len -= 4;
+ if (parsed_len >= MAX_MSG_SIZE)
+ return -1;
+
+ len = read(fd, buf, parsed_len);
+ if (len != parsed_len)
+ return -1;
+ buf[len] = '\0';
+
+ line_len = strlen(buf);
+ if (line_len && buf[line_len - 1] == '\n')
+ buf[--line_len] = '\0';
+
+ if (len <= line_len)
+ return -1;
+
+ *repo = buf;
+ if (strncasecmp(*repo, "git-upload-pack ", strlen("git-upload-pack ")))
+ return -1;
+ *repo += strlen("git-upload-pack ");
+ if (*repo[0] != '/') {
+ --*repo;
+ *repo[0] = '/';
+ }
+
+ *host = buf + line_len + 1;
+ if (strncasecmp(*host, "host=", strlen("host=")))
+ return -1;
+ *host += strlen("host=");
+ if (!*host[0])
+ return -1;
+
+ return 0;
+}
+
+static void process_request(int epoll_fd, int fd, bool direct)
+{
+ static const char message_template[] = "\n******************************************************\n\n This git repository has moved! Please clone with:\n\n $ git clone https://%s%s\n\n******************************************************";
+ char buf[MAX_MSG_SIZE];
+ struct out_data *out, *copy = NULL;
+ size_t msg_len, buffer_len;
+ char *repo, *host;
+ int ret;
+
+ ret = parse_data(fd, buf, &repo, &host);
+ if (ret == -1)
+ goto err;
+ else if (ret == -2) {
+ struct epoll_event new_event = {
+ .events = EPOLLIN | EPOLLET,
+ .data = {
+ .fd = fd
+ }
+ };
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &new_event) < 0)
+ goto err;
+ return;
+ }
+
+ msg_len = strlen(repo) + strlen(host) + strlen(message_template) - 2 + 1;
+ buffer_len = msg_len + sizeof(struct out_data);
+ if (buffer_len > MAX_MSG_SIZE * 2)
+ goto err;
+ out = alloca(buffer_len);
+ out->fd = fd;
+ out->len = 8 + msg_len;
+ if (out->len > 0xffff)
+ goto err;
+ sprintf(out->data, "%04x", out->len);
+ memcpy(out->data + 4, "ERR ", 4);
+ sprintf(out->data + 8, message_template, host, repo);
+
+ if (write(fd, out->data, out->len) != out->len) {
+ struct epoll_event new_event = {
+ .events = EPOLLOUT
+ };
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ goto err;
+ copy = malloc(buffer_len);
+ if (!copy)
+ goto err;
+ memcpy(copy, out, buffer_len);
+ new_event.data.ptr = copy;
+ if (epoll_ctl(epoll_fd, direct ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &new_event) < 0)
+ goto err;
+ return;
+ }
+err:
+ close(fd);
+ free(copy);
}
int main(int argc, char *argv[])
{
int listen_fd, epoll_fd, port;
bool daemonize;
- char *message, *pid_file;
+ char *pid_file;
close(STDIN_FILENO);
- parse_options(argc, argv, &daemonize, &port, &pid_file, &message);
+ parse_options(argc, argv, &daemonize, &port, &pid_file);
listen_fd = get_listen_socket(port);
epoll_fd = setup_epoll(listen_fd);
daemonize_and_pidfile(daemonize, pid_file);
prctl(PR_SET_NAME, "git-daemon-dummy");
drop_privileges();
seccomp_enable_filter();
- event_loop(epoll_fd, listen_fd, message);
+ event_loop(epoll_fd, listen_fd);
return EXIT_FAILURE;
}
diff --git a/openrc/git-daemon-dummy.confd b/openrc/git-daemon-dummy.confd
deleted file mode 100644
index 02db5fb..0000000
--- a/openrc/git-daemon-dummy.confd
+++ /dev/null
@@ -1 +0,0 @@
-URI_PREFIX="https://git.gentoo.org"
diff --git a/openrc/git-daemon-dummy.initd b/openrc/git-daemon-dummy.initd
index 071fe30..1a85051 100755
--- a/openrc/git-daemon-dummy.initd
+++ b/openrc/git-daemon-dummy.initd
@@ -9,7 +9,7 @@ depend() {
start() {
ebegin "Starting git-daemon-dummy"
- start-stop-daemon --start --exec /usr/bin/git-daemon-dummy --pidfile $PID_FILE -- --daemonize --pid-file=$PID_FILE --uri-prefix="$URI_PREFIX"
+ start-stop-daemon --start --exec /usr/bin/git-daemon-dummy --pidfile $PID_FILE -- --daemonize --pid-file=$PID_FILE
eend $?
}