diff options
author | Ori Bernstein <ori@eigenstate.org> | 2021-05-16 18:49:45 -0700 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2021-05-16 18:49:45 -0700 |
commit | 1ee1bfaa8c5644092e0c1ca3985ee74813bbfb1d (patch) | |
tree | 9be6163e8bcd0bd43b4016e2bfeb9571a548536e /sys/src/cmd/git/proto.c | |
parent | 013b2cad191eef50fd4e69c38f1544c5083b640d (diff) |
git: got git?
Add a snapshot of git9 to 9front.
Diffstat (limited to 'sys/src/cmd/git/proto.c')
-rw-r--r-- | sys/src/cmd/git/proto.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/sys/src/cmd/git/proto.c b/sys/src/cmd/git/proto.c new file mode 100644 index 000000000..19c0889af --- /dev/null +++ b/sys/src/cmd/git/proto.c @@ -0,0 +1,459 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> + +#include "git.h" + +#define Useragent "useragent git/2.24.1" +#define Contenthdr "headers Content-Type: application/x-git-%s-pack-request" +#define Accepthdr "headers Accept: application/x-git-%s-pack-result" + +enum { + Nproto = 16, + Nport = 16, + Nhost = 256, + Npath = 128, + Nrepo = 64, + Nbranch = 32, +}; + +void +tracepkt(int v, char *pfx, char *b, int n) +{ + char *f; + int o, i; + + if(chattygit < v) + return; + o = 0; + f = emalloc(n*4 + 1); + for(i = 0; i < n; i++){ + if(isprint(b[i])){ + f[o++] = b[i]; + continue; + } + f[o++] = '\\'; + switch(b[i]){ + case '\\': f[o++] = '\\'; break; + case '\n': f[o++] = 'n'; break; + case '\r': f[o++] = 'r'; break; + case '\v': f[o++] = 'v'; break; + case '\0': f[o++] = '0'; break; + default: + f[o++] = 'x'; + f[o++] = "0123456789abcdef"[(b[i]>>4)&0xf]; + f[o++] = "0123456789abcdef"[(b[i]>>0)&0xf]; + break; + } + } + f[o] = '\0'; + fprint(2, "%s %04x:\t%s\n", pfx, n, f); + free(f); +} + +int +readpkt(Conn *c, char *buf, int nbuf) +{ + char len[5]; + char *e; + int n; + + if(readn(c->rfd, len, 4) == -1) + return -1; + len[4] = 0; + n = strtol(len, &e, 16); + if(n == 0){ + dprint(1, "=r=> 0000\n"); + return 0; + } + if(e != len + 4 || n <= 4) + sysfatal("pktline: bad length '%s'", len); + n -= 4; + if(n >= nbuf) + sysfatal("pktline: undersize buffer"); + if(readn(c->rfd, buf, n) != n) + return -1; + buf[n] = 0; + tracepkt(1, "=r=>", buf, n); + return n; +} + +int +writepkt(Conn *c, char *buf, int nbuf) +{ + char len[5]; + + + snprint(len, sizeof(len), "%04x", nbuf + 4); + if(write(c->wfd, len, 4) != 4) + return -1; + if(write(c->wfd, buf, nbuf) != nbuf) + return -1; + tracepkt(1, "<=w=", buf, nbuf); + return 0; +} + +int +flushpkt(Conn *c) +{ + dprint(1, "<=w= 0000\n"); + return write(c->wfd, "0000", 4); +} + +static void +grab(char *dst, int n, char *p, char *e) +{ + int l; + + l = e - p; + if(l >= n) + sysfatal("overlong component"); + memcpy(dst, p, l); + dst[l] = 0; +} + +static int +parseuri(char *uri, char *proto, char *host, char *port, char *path, char *repo) +{ + char *s, *p, *q; + int n, hasport; + print("uri: \"%s\"\n", uri); + + p = strstr(uri, "://"); + if(p == nil) + snprint(proto, Nproto, "ssh"); + else if(strncmp(uri, "git+", 4) == 0) + grab(proto, Nproto, uri + 4, p); + else + grab(proto, Nproto, uri, p); + *port = 0; + hasport = 1; + if(strcmp(proto, "git") == 0) + snprint(port, Nport, "9418"); + else if(strncmp(proto, "https", 5) == 0) + snprint(port, Nport, "443"); + else if(strncmp(proto, "http", 4) == 0) + snprint(port, Nport, "80"); + else if(strncmp(proto, "hjgit", 5) == 0) + snprint(port, Nport, "17021"); + else if(strncmp(proto, "gits", 5) == 0) + snprint(port, Nport, "9419"); + else + hasport = 0; + s = (p != nil) ? p + 3 : uri; + p = nil; + if(!hasport){ + p = strstr(s, ":"); + if(p != nil) + p++; + } + if(p == nil) + p = strstr(s, "/"); + if(p == nil || strlen(p) == 1){ + werrstr("missing path"); + return -1; + } + + q = memchr(s, ':', p - s); + if(q){ + grab(host, Nhost, s, q); + grab(port, Nport, q + 1, p); + }else{ + grab(host, Nhost, s, p); + } + + snprint(path, Npath, "%s", p); + if((q = strrchr(p, '/')) != nil) + p = q + 1; + if(strlen(p) == 0){ + werrstr("missing repository in uri"); + return -1; + } + n = strlen(p); + if(hassuffix(p, ".git")) + n -= 4; + grab(repo, Nrepo, p, p + n); + return 0; +} + +static int +webclone(Conn *c, char *url) +{ + char buf[16]; + int n, conn; + + if((c->cfd = open("/mnt/web/clone", ORDWR)) < 0) + goto err; + if((n = read(c->cfd, buf, sizeof(buf)-1)) == -1) + goto err; + buf[n] = 0; + conn = atoi(buf); + + /* github will behave differently based on useragent */ + if(write(c->cfd, Useragent, sizeof(Useragent)) == -1) + return -1; + dprint(1, "open url %s\n", url); + if(fprint(c->cfd, "url %s", url) == -1) + goto err; + free(c->dir); + c->dir = smprint("/mnt/web/%d", conn); + return 0; +err: + if(c->cfd != -1) + close(c->cfd); + return -1; +} + +static int +webopen(Conn *c, char *file, int mode) +{ + char path[128]; + int fd; + + snprint(path, sizeof(path), "%s/%s", c->dir, file); + if((fd = open(path, mode)) == -1) + return -1; + return fd; +} + +static int +issmarthttp(Conn *c, char *direction) +{ + char buf[Pktmax+1], svc[128]; + int n; + + if((n = readpkt(c, buf, sizeof(buf))) == -1) + sysfatal("http read: %r"); + buf[n] = 0; + snprint(svc, sizeof(svc), "# service=git-%s-pack\n", direction); + if(strncmp(svc, buf, n) != 0){ + werrstr("dumb http protocol not supported"); + return -1; + } + if(readpkt(c, buf, sizeof(buf)) != 0){ + werrstr("protocol garble: expected flushpkt"); + return -1; + } + return 0; +} + +static int +dialhttp(Conn *c, char *host, char *port, char *path, char *direction) +{ + char *geturl, *suff, *hsep, *psep; + + suff = ""; + hsep = ""; + psep = ""; + if(port && strlen(port) != 0) + hsep = ":"; + if(path && path[0] != '/') + psep = "/"; + memset(c, 0, sizeof(*c)); + geturl = smprint("https://%s%s%s%s%s%s/info/refs?service=git-%s-pack", host, hsep, port, psep, path, suff, direction); + c->type = ConnHttp; + c->url = smprint("https://%s%s%s%s%s%s/git-%s-pack", host, hsep, port, psep, path, suff, direction); + c->cfd = webclone(c, geturl); + free(geturl); + if(c->cfd == -1) + return -1; + c->rfd = webopen(c, "body", OREAD); + c->wfd = -1; + if(c->rfd == -1) + return -1; + if(issmarthttp(c, direction) == -1) + return -1; + c->direction = estrdup(direction); + return 0; +} + +static int +dialssh(Conn *c, char *host, char *, char *path, char *direction) +{ + int pid, pfd[2]; + char cmd[64]; + + if(pipe(pfd) == -1) + sysfatal("unable to open pipe: %r"); + pid = fork(); + if(pid == -1) + sysfatal("unable to fork"); + if(pid == 0){ + close(pfd[1]); + dup(pfd[0], 0); + dup(pfd[0], 1); + snprint(cmd, sizeof(cmd), "git-%s-pack", direction); + dprint(1, "exec ssh '%s' '%s' %s\n", host, cmd, path); + execl("/bin/ssh", "ssh", host, cmd, path, nil); + }else{ + close(pfd[0]); + c->type = ConnSsh; + c->rfd = pfd[1]; + c->wfd = dup(pfd[1], -1); + } + return 0; +} + +static int +dialhjgit(Conn *c, char *host, char *port, char *path, char *direction, int auth) +{ + char *ds, *p, *e, cmd[512]; + int pid, pfd[2]; + + if((ds = netmkaddr(host, "tcp", port)) == nil) + return -1; + if(pipe(pfd) == -1) + sysfatal("unable to open pipe: %r"); + pid = fork(); + if(pid == -1) + sysfatal("unable to fork"); + if(pid == 0){ + close(pfd[1]); + dup(pfd[0], 0); + dup(pfd[0], 1); + dprint(1, "exec tlsclient -a %s\n", ds); + if(auth) + execl("/bin/tlsclient", "tlsclient", "-a", ds, nil); + else + execl("/bin/tlsclient", "tlsclient", ds, nil); + sysfatal("exec: %r"); + }else{ + close(pfd[0]); + p = cmd; + e = cmd + sizeof(cmd); + p = seprint(p, e - 1, "git-%s-pack %s", direction, path); + p = seprint(p + 1, e, "host=%s", host); + c->type = ConnGit9; + c->rfd = pfd[1]; + c->wfd = dup(pfd[1], -1); + if(writepkt(c, cmd, p - cmd + 1) == -1){ + fprint(2, "failed to write message\n"); + close(c->rfd); + close(c->wfd); + return -1; + } + } + return 0; +} + + +static int +dialgit(Conn *c, char *host, char *port, char *path, char *direction) +{ + char *ds, *p, *e, cmd[512]; + int fd; + + if((ds = netmkaddr(host, "tcp", port)) == nil) + return -1; + dprint(1, "dial %s git-%s-pack %s\n", ds, direction, path); + fd = dial(ds, nil, nil, nil); + if(fd == -1) + return -1; + p = cmd; + e = cmd + sizeof(cmd); + p = seprint(p, e - 1, "git-%s-pack %s", direction, path); + p = seprint(p + 1, e, "host=%s", host); + c->type = ConnGit; + c->rfd = fd; + c->wfd = dup(fd, -1); + if(writepkt(c, cmd, p - cmd + 1) == -1){ + fprint(2, "failed to write message\n"); + close(fd); + return -1; + } + return 0; +} + +void +initconn(Conn *c, int rd, int wr) +{ + c->type = ConnGit; + c->rfd = rd; + c->wfd = wr; +} + +int +gitconnect(Conn *c, char *uri, char *direction) +{ + char proto[Nproto], host[Nhost], port[Nport]; + char repo[Nrepo], path[Npath]; + + if(parseuri(uri, proto, host, port, path, repo) == -1){ + werrstr("bad uri %s", uri); + return -1; + } + + memset(c, 0, sizeof(Conn)); + if(strcmp(proto, "ssh") == 0) + return dialssh(c, host, port, path, direction); + else if(strcmp(proto, "git") == 0) + return dialgit(c, host, port, path, direction); + else if(strcmp(proto, "hjgit") == 0) + return dialhjgit(c, host, port, path, direction, 1); + else if(strcmp(proto, "gits") == 0) + return dialhjgit(c, host, port, path, direction, 0); + else if(strcmp(proto, "http") == 0 || strcmp(proto, "https") == 0) + return dialhttp(c, host, port, path, direction); + werrstr("unknown protocol %s", proto); + return -1; +} + +int +writephase(Conn *c) +{ + char hdr[128]; + int n; + + dprint(1, "start write phase\n"); + if(c->type != ConnHttp) + return 0; + + if(c->wfd != -1) + close(c->wfd); + if(c->cfd != -1) + close(c->cfd); + if((c->cfd = webclone(c, c->url)) == -1) + return -1; + n = snprint(hdr, sizeof(hdr), Contenthdr, c->direction); + if(write(c->cfd, hdr, n) == -1) + return -1; + n = snprint(hdr, sizeof(hdr), Accepthdr, c->direction); + if(write(c->cfd, hdr, n) == -1) + return -1; + if((c->wfd = webopen(c, "postbody", OWRITE)) == -1) + return -1; + c->rfd = -1; + return 0; +} + +int +readphase(Conn *c) +{ + dprint(1, "start read phase\n"); + if(c->type != ConnHttp) + return 0; + if(close(c->wfd) == -1) + return -1; + if((c->rfd = webopen(c, "body", OREAD)) == -1) + return -1; + c->wfd = -1; + return 0; +} + +void +closeconn(Conn *c) +{ + close(c->rfd); + close(c->wfd); + switch(c->type){ + case ConnGit: + break; + case ConnGit9: + case ConnSsh: + free(wait()); + break; + case ConnHttp: + close(c->cfd); + break; + } +} |