X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=daemon.c;h=b129b83e4026490c1e6e77861cd6f03a5007d01e;hb=fffe694d607ea683b5d08ee99a46d9b06cb74006;hp=eb4f3f1e9f08cd3a95f429a477495cf4151ddd86;hpb=1ad7a06adb6fbe42357daa58738008cbadbb8650;p=git.git diff --git a/daemon.c b/daemon.c index eb4f3f1e9..b129b83e4 100644 --- a/daemon.c +++ b/daemon.c @@ -1,31 +1,28 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pkt-line.h" #include "cache.h" +#include "pkt-line.h" #include "exec_cmd.h" #include "interpolate.h" +#include + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 256 +#endif + static int log_syslog; static int verbose; static int reuseaddr; static const char daemon_usage[] = -"git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n" +"git-daemon [--verbose] [--syslog] [--export-all]\n" " [--timeout=n] [--init-timeout=n] [--strict-paths]\n" " [--base-path=path] [--user-path | --user-path=path]\n" " [--interpolated-path=path]\n" " [--reuseaddr] [--detach] [--pid-file=file]\n" " [--[enable|disable|allow-override|forbid-override]=service]\n" -" [--user=user [[--group=group]] [directory...]"; +" [--inetd | [--listen=host_or_ipaddr] [--port=n]\n" +" [--user=user [--group=group]]\n" +" [directory...]"; /* List of acceptable pathname prefixes */ static char **ok_paths; @@ -56,13 +53,19 @@ static unsigned int init_timeout; * Feel free to make dynamic as needed. */ #define INTERP_SLOT_HOST (0) -#define INTERP_SLOT_DIR (1) -#define INTERP_SLOT_PERCENT (2) +#define INTERP_SLOT_CANON_HOST (1) +#define INTERP_SLOT_IP (2) +#define INTERP_SLOT_PORT (3) +#define INTERP_SLOT_DIR (4) +#define INTERP_SLOT_PERCENT (5) static struct interp interp_table[] = { { "%H", 0}, + { "%CH", 0}, + { "%IP", 0}, + { "%P", 0}, { "%D", 0}, - { "%%", "%"}, + { "%%", 0}, }; @@ -396,7 +399,11 @@ static void make_service_overridable(const char *name, int ena) { die("No such service %s", name); } -static void parse_extra_args(char *extra_args, int buflen) +/* + * Separate the "extra args" information as supplied by the client connection. + * Any resulting data is squirrelled away in the given interpolation table. + */ +static void parse_extra_args(struct interp *table, char *extra_args, int buflen) { char *val; int vallen; @@ -408,16 +415,90 @@ static void parse_extra_args(char *extra_args, int buflen) val = extra_args + 5; vallen = strlen(val) + 1; if (*val) { - char *save = xmalloc(vallen); - interp_table[INTERP_SLOT_HOST].value = save; - strlcpy(save, val, vallen); + /* Split : at colon. */ + char *host = val; + char *port = strrchr(host, ':'); + if (port) { + *port = 0; + port++; + interp_set_entry(table, INTERP_SLOT_PORT, port); + } + interp_set_entry(table, INTERP_SLOT_HOST, host); } + /* On to the next one */ extra_args = val + vallen; } } } +void fill_in_extra_table_entries(struct interp *itable) +{ + char *hp; + + /* + * Replace literal host with lowercase-ized hostname. + */ + hp = interp_table[INTERP_SLOT_HOST].value; + if (!hp) + return; + for ( ; *hp; hp++) + *hp = tolower(*hp); + + /* + * Locate canonical hostname and its IP address. + */ +#ifndef NO_IPV6 + { + struct addrinfo hints; + struct addrinfo *ai, *ai0; + int gai; + static char addrbuf[HOST_NAME_MAX + 1]; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + gai = getaddrinfo(interp_table[INTERP_SLOT_HOST].value, 0, &hints, &ai0); + if (!gai) { + for (ai = ai0; ai; ai = ai->ai_next) { + struct sockaddr_in *sin_addr = (void *)ai->ai_addr; + + inet_ntop(AF_INET, &sin_addr->sin_addr, + addrbuf, sizeof(addrbuf)); + interp_set_entry(interp_table, + INTERP_SLOT_CANON_HOST, ai->ai_canonname); + interp_set_entry(interp_table, + INTERP_SLOT_IP, addrbuf); + break; + } + freeaddrinfo(ai0); + } + } +#else + { + struct hostent *hent; + struct sockaddr_in sa; + char **ap; + static char addrbuf[HOST_NAME_MAX + 1]; + + hent = gethostbyname(interp_table[INTERP_SLOT_HOST].value); + + ap = hent->h_addr_list; + memset(&sa, 0, sizeof sa); + sa.sin_family = hent->h_addrtype; + sa.sin_port = htons(0); + memcpy(&sa.sin_addr, *ap, hent->h_length); + + inet_ntop(hent->h_addrtype, &sa.sin_addr, + addrbuf, sizeof(addrbuf)); + + interp_set_entry(interp_table, INTERP_SLOT_CANON_HOST, hent->h_name); + interp_set_entry(interp_table, INTERP_SLOT_IP, addrbuf); + } +#endif +} + + static int execute(struct sockaddr *addr) { static char line[1000]; @@ -455,11 +536,21 @@ static int execute(struct sockaddr *addr) loginfo("Extended attributes (%d bytes) exist <%.*s>", (int) pktlen - len, (int) pktlen - len, line + len + 1); - if (len && line[len-1] == '\n') + if (len && line[len-1] == '\n') { line[--len] = 0; + pktlen--; + } - if (len != pktlen) - parse_extra_args(line + len + 1, pktlen - len - 1); + /* + * Initialize the path interpolation table for this connection. + */ + interp_clear_table(interp_table, ARRAY_SIZE(interp_table)); + interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%"); + + if (len != pktlen) { + parse_extra_args(interp_table, line + len + 1, pktlen - len - 1); + fill_in_extra_table_entries(interp_table); + } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); @@ -467,7 +558,12 @@ static int execute(struct sockaddr *addr) if (!strncmp("git-", line, 4) && !strncmp(s->name, line + 4, namelen) && line[namelen + 4] == ' ') { - interp_table[INTERP_SLOT_DIR].value = line+namelen+5; + /* + * Note: The directory here is probably context sensitive, + * and might depend on the actual service being performed. + */ + interp_set_entry(interp_table, + INTERP_SLOT_DIR, line + namelen + 5); return run_service(interp_table, s); } } @@ -663,23 +759,22 @@ static int set_reuse_addr(int sockfd) #ifndef NO_IPV6 -static int socksetup(int port, int **socklist_p) +static int socksetup(char *listen_addr, int listen_port, int **socklist_p) { int socknum = 0, *socklist = NULL; int maxfd = -1; char pbuf[NI_MAXSERV]; - struct addrinfo hints, *ai0, *ai; int gai; - sprintf(pbuf, "%d", port); + sprintf(pbuf, "%d", listen_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; - gai = getaddrinfo(NULL, pbuf, &hints, &ai0); + gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0); if (gai) die("getaddrinfo() failed: %s\n", gai_strerror(gai)); @@ -733,20 +828,27 @@ static int socksetup(int port, int **socklist_p) #else /* NO_IPV6 */ -static int socksetup(int port, int **socklist_p) +static int socksetup(char *listen_addr, int listen_port, int **socklist_p) { struct sockaddr_in sin; int sockfd; + memset(&sin, 0, sizeof sin); + sin.sin_family = AF_INET; + sin.sin_port = htons(listen_port); + + if (listen_addr) { + /* Well, host better be an IP address here. */ + if (inet_pton(AF_INET, listen_addr, &sin.sin_addr.s_addr) <= 0) + return 0; + } else { + sin.sin_addr.s_addr = htonl(INADDR_ANY); + } + sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) return 0; - memset(&sin, 0, sizeof sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - if (set_reuse_addr(sockfd)) { close(sockfd); return 0; @@ -855,13 +957,14 @@ static void store_pid(const char *path) fclose(f); } -static int serve(int port, struct passwd *pass, gid_t gid) +static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid) { int socknum, *socklist; - socknum = socksetup(port, &socklist); + socknum = socksetup(listen_addr, listen_port, &socklist); if (socknum == 0) - die("unable to allocate any listen sockets on port %u", port); + die("unable to allocate any listen sockets on host %s port %u", + listen_addr, listen_port); if (pass && gid && (initgroups(pass->pw_name, gid) || setgid (gid) || @@ -873,7 +976,8 @@ static int serve(int port, struct passwd *pass, gid_t gid) int main(int argc, char **argv) { - int port = DEFAULT_GIT_PORT; + int listen_port = 0; + char *listen_addr = NULL; int inetd_mode = 0; const char *pid_file = NULL, *user_name = NULL, *group_name = NULL; int detach = 0; @@ -890,12 +994,20 @@ int main(int argc, char **argv) for (i = 1; i < argc; i++) { char *arg = argv[i]; + if (!strncmp(arg, "--listen=", 9)) { + char *p = arg + 9; + char *ph = listen_addr = xmalloc(strlen(arg + 9) + 1); + while (*p) + *ph++ = tolower(*p++); + *ph = 0; + continue; + } if (!strncmp(arg, "--port=", 7)) { char *end; unsigned long n; n = strtoul(arg+7, &end, 0); if (arg[7] && !*end) { - port = n; + listen_port = n; continue; } } @@ -995,6 +1107,11 @@ int main(int argc, char **argv) if (inetd_mode && (group_name || user_name)) die("--user and --group are incompatible with --inetd"); + if (inetd_mode && (listen_port || listen_addr)) + die("--listen= and --port= are incompatible with --inetd"); + else if (listen_port == 0) + listen_port = DEFAULT_GIT_PORT; + if (group_name && !user_name) die("--group supplied without --user"); @@ -1043,5 +1160,5 @@ int main(int argc, char **argv) if (pid_file) store_pid(pid_file); - return serve(port, pass, gid); + return serve(listen_addr, listen_port, pass, gid); }