Blame


1 09876a9d 2024-04-25 thomas.ad /*
2 452122e2 2024-04-25 thomas.ad * Copyright (c) 2024 Tobias Heider <me@tobhe.de>
3 09876a9d 2024-04-25 thomas.ad * Copyright (c) 2022 Omar Polo <op@openbsd.org>
4 09876a9d 2024-04-25 thomas.ad *
5 09876a9d 2024-04-25 thomas.ad * Permission to use, copy, modify, and distribute this software for any
6 09876a9d 2024-04-25 thomas.ad * purpose with or without fee is hereby granted, provided that the above
7 09876a9d 2024-04-25 thomas.ad * copyright notice and this permission notice appear in all copies.
8 09876a9d 2024-04-25 thomas.ad *
9 09876a9d 2024-04-25 thomas.ad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 09876a9d 2024-04-25 thomas.ad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 09876a9d 2024-04-25 thomas.ad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 09876a9d 2024-04-25 thomas.ad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 09876a9d 2024-04-25 thomas.ad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 09876a9d 2024-04-25 thomas.ad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 09876a9d 2024-04-25 thomas.ad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 09876a9d 2024-04-25 thomas.ad */
17 09876a9d 2024-04-25 thomas.ad
18 09876a9d 2024-04-25 thomas.ad #include <sys/types.h>
19 09876a9d 2024-04-25 thomas.ad #include <sys/socket.h>
20 09876a9d 2024-04-25 thomas.ad
21 09876a9d 2024-04-25 thomas.ad #include <err.h>
22 09876a9d 2024-04-25 thomas.ad #include <errno.h>
23 09876a9d 2024-04-25 thomas.ad #include <limits.h>
24 09876a9d 2024-04-25 thomas.ad #include <netdb.h>
25 09876a9d 2024-04-25 thomas.ad #include <poll.h>
26 09876a9d 2024-04-25 thomas.ad #include <stdio.h>
27 09876a9d 2024-04-25 thomas.ad #include <stdlib.h>
28 09876a9d 2024-04-25 thomas.ad #include <string.h>
29 09876a9d 2024-04-25 thomas.ad #include <tls.h>
30 09876a9d 2024-04-25 thomas.ad #include <unistd.h>
31 09876a9d 2024-04-25 thomas.ad
32 09876a9d 2024-04-25 thomas.ad #include "got_version.h"
33 09876a9d 2024-04-25 thomas.ad
34 a36700cf 2024-04-25 thomas.ad #include "got_lib_pkt.h"
35 a36700cf 2024-04-25 thomas.ad
36 452122e2 2024-04-25 thomas.ad #include "bufio.h"
37 452122e2 2024-04-25 thomas.ad
38 09876a9d 2024-04-25 thomas.ad #define UPLOAD_PACK_ADV "application/x-git-upload-pack-advertisement"
39 09876a9d 2024-04-25 thomas.ad #define UPLOAD_PACK_REQ "application/x-git-upload-pack-request"
40 09876a9d 2024-04-25 thomas.ad #define UPLOAD_PACK_RES "application/x-git-upload-pack-result"
41 09876a9d 2024-04-25 thomas.ad
42 09876a9d 2024-04-25 thomas.ad #define GOT_USERAGENT "got/" GOT_VERSION_STR
43 09876a9d 2024-04-25 thomas.ad #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
44 09876a9d 2024-04-25 thomas.ad #define hasprfx(str, p) (strncasecmp(str, p, strlen(p)) == 0)
45 09876a9d 2024-04-25 thomas.ad
46 09876a9d 2024-04-25 thomas.ad FILE *tmp;
47 09876a9d 2024-04-25 thomas.ad
48 09876a9d 2024-04-25 thomas.ad static int verbose;
49 09876a9d 2024-04-25 thomas.ad
50 452122e2 2024-04-25 thomas.ad static char *
51 452122e2 2024-04-25 thomas.ad bufio_getdelim_sync(struct bufio *bio, const char *nl, size_t *len)
52 452122e2 2024-04-25 thomas.ad {
53 452122e2 2024-04-25 thomas.ad int r;
54 452122e2 2024-04-25 thomas.ad
55 452122e2 2024-04-25 thomas.ad do {
56 452122e2 2024-04-25 thomas.ad r = bufio_read(bio);
57 452122e2 2024-04-25 thomas.ad if (r == -1 && errno != EAGAIN)
58 452122e2 2024-04-25 thomas.ad errx(1, "bufio_read: %s", bufio_io_err(bio));
59 452122e2 2024-04-25 thomas.ad } while (r == -1 && errno == EAGAIN);
60 452122e2 2024-04-25 thomas.ad return buf_getdelim(&bio->rbuf, nl, len);
61 452122e2 2024-04-25 thomas.ad }
62 452122e2 2024-04-25 thomas.ad
63 452122e2 2024-04-25 thomas.ad static size_t
64 452122e2 2024-04-25 thomas.ad bufio_drain_sync(struct bufio *bio, void *d, size_t len)
65 452122e2 2024-04-25 thomas.ad {
66 452122e2 2024-04-25 thomas.ad int r;
67 452122e2 2024-04-25 thomas.ad
68 452122e2 2024-04-25 thomas.ad do {
69 452122e2 2024-04-25 thomas.ad r = bufio_read(bio);
70 452122e2 2024-04-25 thomas.ad if (r == -1 && errno != EAGAIN)
71 452122e2 2024-04-25 thomas.ad errx(1, "bufio_read: %s", bufio_io_err(bio));
72 452122e2 2024-04-25 thomas.ad } while (r == -1 && errno == EAGAIN);
73 452122e2 2024-04-25 thomas.ad return bufio_drain(bio, d, len);
74 452122e2 2024-04-25 thomas.ad }
75 452122e2 2024-04-25 thomas.ad
76 452122e2 2024-04-25 thomas.ad static void
77 452122e2 2024-04-25 thomas.ad bufio_close_sync(struct bufio *bio)
78 452122e2 2024-04-25 thomas.ad {
79 452122e2 2024-04-25 thomas.ad int r;
80 452122e2 2024-04-25 thomas.ad
81 452122e2 2024-04-25 thomas.ad do {
82 452122e2 2024-04-25 thomas.ad r = bufio_close(bio);
83 452122e2 2024-04-25 thomas.ad if (r == -1 && errno == EAGAIN)
84 452122e2 2024-04-25 thomas.ad errx(1, "bufio_read: %s", bufio_io_err(bio));
85 452122e2 2024-04-25 thomas.ad } while (r == -1 && errno == EAGAIN);
86 452122e2 2024-04-25 thomas.ad }
87 452122e2 2024-04-25 thomas.ad
88 09876a9d 2024-04-25 thomas.ad static long long
89 09876a9d 2024-04-25 thomas.ad hexstrtonum(const char *str, long long min, long long max, const char **errstr)
90 09876a9d 2024-04-25 thomas.ad {
91 09876a9d 2024-04-25 thomas.ad long long lval;
92 09876a9d 2024-04-25 thomas.ad char *cp;
93 09876a9d 2024-04-25 thomas.ad
94 09876a9d 2024-04-25 thomas.ad errno = 0;
95 09876a9d 2024-04-25 thomas.ad lval = strtoll(str, &cp, 16);
96 09876a9d 2024-04-25 thomas.ad if (*str == '\0' || *cp != '\0') {
97 09876a9d 2024-04-25 thomas.ad *errstr = "not a number";
98 09876a9d 2024-04-25 thomas.ad return 0;
99 09876a9d 2024-04-25 thomas.ad }
100 09876a9d 2024-04-25 thomas.ad if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
101 09876a9d 2024-04-25 thomas.ad lval < min || lval > max) {
102 09876a9d 2024-04-25 thomas.ad *errstr = "out of range";
103 09876a9d 2024-04-25 thomas.ad return 0;
104 09876a9d 2024-04-25 thomas.ad }
105 09876a9d 2024-04-25 thomas.ad
106 09876a9d 2024-04-25 thomas.ad *errstr = NULL;
107 09876a9d 2024-04-25 thomas.ad return lval;
108 09876a9d 2024-04-25 thomas.ad }
109 09876a9d 2024-04-25 thomas.ad
110 09876a9d 2024-04-25 thomas.ad static int
111 09876a9d 2024-04-25 thomas.ad dial(int https, const char *host, const char *port)
112 09876a9d 2024-04-25 thomas.ad {
113 09876a9d 2024-04-25 thomas.ad struct addrinfo hints, *res, *res0;
114 452122e2 2024-04-25 thomas.ad int error, saved_errno, fd = -1;
115 09876a9d 2024-04-25 thomas.ad const char *cause = NULL;
116 09876a9d 2024-04-25 thomas.ad
117 09876a9d 2024-04-25 thomas.ad memset(&hints, 0, sizeof(hints));
118 09876a9d 2024-04-25 thomas.ad hints.ai_family = AF_UNSPEC;
119 09876a9d 2024-04-25 thomas.ad hints.ai_socktype = SOCK_STREAM;
120 09876a9d 2024-04-25 thomas.ad error = getaddrinfo(host, port, &hints, &res0);
121 09876a9d 2024-04-25 thomas.ad if (error) {
122 09876a9d 2024-04-25 thomas.ad warnx("%s", gai_strerror(error));
123 452122e2 2024-04-25 thomas.ad return -1;
124 09876a9d 2024-04-25 thomas.ad }
125 09876a9d 2024-04-25 thomas.ad
126 09876a9d 2024-04-25 thomas.ad for (res = res0; res; res = res->ai_next) {
127 09876a9d 2024-04-25 thomas.ad fd = socket(res->ai_family, res->ai_socktype,
128 09876a9d 2024-04-25 thomas.ad res->ai_protocol);
129 09876a9d 2024-04-25 thomas.ad if (fd == -1) {
130 09876a9d 2024-04-25 thomas.ad cause = "socket";
131 09876a9d 2024-04-25 thomas.ad continue;
132 09876a9d 2024-04-25 thomas.ad }
133 09876a9d 2024-04-25 thomas.ad
134 09876a9d 2024-04-25 thomas.ad if (connect(fd, res->ai_addr, res->ai_addrlen) == 0)
135 09876a9d 2024-04-25 thomas.ad break;
136 09876a9d 2024-04-25 thomas.ad
137 09876a9d 2024-04-25 thomas.ad cause = "connect";
138 09876a9d 2024-04-25 thomas.ad saved_errno = errno;
139 09876a9d 2024-04-25 thomas.ad close(fd);
140 09876a9d 2024-04-25 thomas.ad fd = -1;
141 09876a9d 2024-04-25 thomas.ad errno = saved_errno;
142 09876a9d 2024-04-25 thomas.ad }
143 09876a9d 2024-04-25 thomas.ad freeaddrinfo(res0);
144 09876a9d 2024-04-25 thomas.ad
145 09876a9d 2024-04-25 thomas.ad if (fd == -1) {
146 09876a9d 2024-04-25 thomas.ad warn("%s", cause);
147 452122e2 2024-04-25 thomas.ad return -1;
148 09876a9d 2024-04-25 thomas.ad }
149 09876a9d 2024-04-25 thomas.ad
150 452122e2 2024-04-25 thomas.ad return fd;
151 09876a9d 2024-04-25 thomas.ad }
152 09876a9d 2024-04-25 thomas.ad
153 452122e2 2024-04-25 thomas.ad static int
154 452122e2 2024-04-25 thomas.ad http_open(struct bufio *bio, int https, const char *method, const char *host, const char *port,
155 452122e2 2024-04-25 thomas.ad const char *path, const char *path_sufx, const char *query, const char *ctype)
156 09876a9d 2024-04-25 thomas.ad {
157 09876a9d 2024-04-25 thomas.ad const char *chdr = NULL, *te = "";
158 09876a9d 2024-04-25 thomas.ad char *p, *req;
159 09876a9d 2024-04-25 thomas.ad int r;
160 452122e2 2024-04-25 thomas.ad
161 09876a9d 2024-04-25 thomas.ad if (path_sufx != NULL && *path && path[strlen(path) - 1] == '/')
162 09876a9d 2024-04-25 thomas.ad path_sufx++; /* skip the slash */
163 09876a9d 2024-04-25 thomas.ad
164 09876a9d 2024-04-25 thomas.ad if (strcmp(method, "POST") == 0)
165 09876a9d 2024-04-25 thomas.ad te = "\r\nTransfer-Encoding: chunked\r\n";
166 09876a9d 2024-04-25 thomas.ad
167 09876a9d 2024-04-25 thomas.ad if (ctype)
168 09876a9d 2024-04-25 thomas.ad chdr = "Content-Type: ";
169 09876a9d 2024-04-25 thomas.ad
170 09876a9d 2024-04-25 thomas.ad r = asprintf(&p, "%s/%s%s%s", path, path_sufx,
171 09876a9d 2024-04-25 thomas.ad query ? "?" : "", query ? query : "");
172 09876a9d 2024-04-25 thomas.ad if (r == -1)
173 09876a9d 2024-04-25 thomas.ad err(1, "asprintf");
174 09876a9d 2024-04-25 thomas.ad
175 09876a9d 2024-04-25 thomas.ad r = asprintf(&req, "%s %s HTTP/1.1\r\n"
176 09876a9d 2024-04-25 thomas.ad "Host: %s\r\n"
177 09876a9d 2024-04-25 thomas.ad "Connection: close\r\n"
178 09876a9d 2024-04-25 thomas.ad "User-agent: %s\r\n"
179 09876a9d 2024-04-25 thomas.ad "%s%s%s\r\n",
180 09876a9d 2024-04-25 thomas.ad method, p, host, GOT_USERAGENT,
181 09876a9d 2024-04-25 thomas.ad chdr ? chdr : "", ctype ? ctype : "", te);
182 09876a9d 2024-04-25 thomas.ad if (r == -1)
183 09876a9d 2024-04-25 thomas.ad err(1, "asprintf");
184 452122e2 2024-04-25 thomas.ad free(p);
185 09876a9d 2024-04-25 thomas.ad
186 09876a9d 2024-04-25 thomas.ad if (verbose > 0)
187 b39e70da 2024-04-25 thomas.ad fprintf(stderr, "%s: request: %s\n", getprogname(), req);
188 452122e2 2024-04-25 thomas.ad
189 09876a9d 2024-04-25 thomas.ad
190 452122e2 2024-04-25 thomas.ad r = bufio_compose(bio, req, r);
191 452122e2 2024-04-25 thomas.ad if (r == -1)
192 452122e2 2024-04-25 thomas.ad err(1, "bufio_compose_fmt");
193 09876a9d 2024-04-25 thomas.ad free(req);
194 09876a9d 2024-04-25 thomas.ad
195 452122e2 2024-04-25 thomas.ad do {
196 452122e2 2024-04-25 thomas.ad r = bufio_write(bio);
197 452122e2 2024-04-25 thomas.ad if (r == -1 && errno != EAGAIN)
198 452122e2 2024-04-25 thomas.ad errx(1, "bufio_read: %s", bufio_io_err(bio));
199 452122e2 2024-04-25 thomas.ad } while (bio->wbuf.len != 0);
200 452122e2 2024-04-25 thomas.ad
201 452122e2 2024-04-25 thomas.ad return 0;
202 09876a9d 2024-04-25 thomas.ad }
203 09876a9d 2024-04-25 thomas.ad
204 09876a9d 2024-04-25 thomas.ad static int
205 452122e2 2024-04-25 thomas.ad http_parse_reply(struct bufio *bio, int *chunked, const char *expected_ctype)
206 09876a9d 2024-04-25 thomas.ad {
207 452122e2 2024-04-25 thomas.ad char *cp, *line;
208 452122e2 2024-04-25 thomas.ad size_t linelen;
209 09876a9d 2024-04-25 thomas.ad
210 09876a9d 2024-04-25 thomas.ad *chunked = 0;
211 09876a9d 2024-04-25 thomas.ad
212 452122e2 2024-04-25 thomas.ad line = bufio_getdelim_sync(bio, "\r\n", &linelen);
213 452122e2 2024-04-25 thomas.ad if (line == NULL) {
214 452122e2 2024-04-25 thomas.ad warnx("%s: bufio_getdelim_sync()", __func__);
215 09876a9d 2024-04-25 thomas.ad return -1;
216 09876a9d 2024-04-25 thomas.ad }
217 09876a9d 2024-04-25 thomas.ad
218 29460ff0 2024-04-25 thomas.ad if (verbose > 0)
219 b39e70da 2024-04-25 thomas.ad fprintf(stderr, "%s: response: %s\n", getprogname(), line);
220 29460ff0 2024-04-25 thomas.ad
221 09876a9d 2024-04-25 thomas.ad if ((cp = strchr(line, ' ')) == NULL) {
222 09876a9d 2024-04-25 thomas.ad warnx("malformed HTTP response");
223 452122e2 2024-04-25 thomas.ad return -1;
224 09876a9d 2024-04-25 thomas.ad }
225 09876a9d 2024-04-25 thomas.ad cp++;
226 09876a9d 2024-04-25 thomas.ad
227 09876a9d 2024-04-25 thomas.ad if (strncmp(cp, "200 ", 4) != 0) {
228 09876a9d 2024-04-25 thomas.ad warnx("malformed HTTP response");
229 452122e2 2024-04-25 thomas.ad return -1;
230 09876a9d 2024-04-25 thomas.ad }
231 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
232 09876a9d 2024-04-25 thomas.ad
233 452122e2 2024-04-25 thomas.ad while(1) {
234 452122e2 2024-04-25 thomas.ad line = bufio_getdelim_sync(bio, "\r\n", &linelen);
235 452122e2 2024-04-25 thomas.ad if (line == NULL) {
236 452122e2 2024-04-25 thomas.ad warnx("%s: bufio_getdelim_sync()", __func__);
237 452122e2 2024-04-25 thomas.ad return -1;
238 452122e2 2024-04-25 thomas.ad }
239 452122e2 2024-04-25 thomas.ad if (*line == '\0') {
240 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
241 09876a9d 2024-04-25 thomas.ad break;
242 452122e2 2024-04-25 thomas.ad }
243 09876a9d 2024-04-25 thomas.ad
244 09876a9d 2024-04-25 thomas.ad if (hasprfx(line, "content-type:")) {
245 09876a9d 2024-04-25 thomas.ad cp = strchr(line, ':') + 1;
246 09876a9d 2024-04-25 thomas.ad cp += strspn(cp, " \t");
247 09876a9d 2024-04-25 thomas.ad cp[strcspn(cp, " \t")] = '\0';
248 09876a9d 2024-04-25 thomas.ad if (strcmp(cp, expected_ctype) != 0) {
249 09876a9d 2024-04-25 thomas.ad warnx("server not using the \"smart\" "
250 09876a9d 2024-04-25 thomas.ad "HTTP protocol.");
251 452122e2 2024-04-25 thomas.ad return -1;
252 09876a9d 2024-04-25 thomas.ad }
253 09876a9d 2024-04-25 thomas.ad }
254 09876a9d 2024-04-25 thomas.ad if (hasprfx(line, "transfer-encoding:")) {
255 09876a9d 2024-04-25 thomas.ad cp = strchr(line, ':') + 1;
256 09876a9d 2024-04-25 thomas.ad cp += strspn(cp, " \t");
257 09876a9d 2024-04-25 thomas.ad cp[strcspn(cp, " \t")] = '\0';
258 09876a9d 2024-04-25 thomas.ad if (strcmp(cp, "chunked") != 0) {
259 09876a9d 2024-04-25 thomas.ad warnx("unknown transfer-encoding");
260 452122e2 2024-04-25 thomas.ad return -1;
261 09876a9d 2024-04-25 thomas.ad }
262 09876a9d 2024-04-25 thomas.ad *chunked = 1;
263 09876a9d 2024-04-25 thomas.ad }
264 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
265 09876a9d 2024-04-25 thomas.ad }
266 09876a9d 2024-04-25 thomas.ad
267 09876a9d 2024-04-25 thomas.ad return 0;
268 09876a9d 2024-04-25 thomas.ad }
269 09876a9d 2024-04-25 thomas.ad
270 09876a9d 2024-04-25 thomas.ad static ssize_t
271 452122e2 2024-04-25 thomas.ad http_read(struct bufio *bio, int chunked, size_t *chunksz, char *buf, size_t bufsz)
272 09876a9d 2024-04-25 thomas.ad {
273 09876a9d 2024-04-25 thomas.ad const char *errstr;
274 452122e2 2024-04-25 thomas.ad char *line = NULL;
275 452122e2 2024-04-25 thomas.ad size_t r;
276 09876a9d 2024-04-25 thomas.ad ssize_t ret = 0, linelen;
277 09876a9d 2024-04-25 thomas.ad
278 09876a9d 2024-04-25 thomas.ad if (!chunked) {
279 452122e2 2024-04-25 thomas.ad r = bufio_drain_sync(bio, buf, bufsz);
280 452122e2 2024-04-25 thomas.ad if (r == 0)
281 09876a9d 2024-04-25 thomas.ad return -1;
282 09876a9d 2024-04-25 thomas.ad return r;
283 09876a9d 2024-04-25 thomas.ad }
284 09876a9d 2024-04-25 thomas.ad
285 09876a9d 2024-04-25 thomas.ad while (bufsz > 0) {
286 09876a9d 2024-04-25 thomas.ad if (*chunksz == 0) {
287 09876a9d 2024-04-25 thomas.ad again:
288 452122e2 2024-04-25 thomas.ad line = bufio_getdelim_sync(bio, "\r\n", &linelen);
289 452122e2 2024-04-25 thomas.ad if (line == NULL) {
290 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
291 09876a9d 2024-04-25 thomas.ad break;
292 09876a9d 2024-04-25 thomas.ad }
293 452122e2 2024-04-25 thomas.ad if (*line == '\0') {
294 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
295 452122e2 2024-04-25 thomas.ad goto again; /* was the CRLF after the chunk */
296 09876a9d 2024-04-25 thomas.ad }
297 09876a9d 2024-04-25 thomas.ad
298 09876a9d 2024-04-25 thomas.ad *chunksz = hexstrtonum(line, 0, INT_MAX, &errstr);
299 09876a9d 2024-04-25 thomas.ad if (errstr != NULL) {
300 09876a9d 2024-04-25 thomas.ad warnx("invalid HTTP chunk: size is %s (%s)",
301 09876a9d 2024-04-25 thomas.ad errstr, line);
302 09876a9d 2024-04-25 thomas.ad ret = -1;
303 09876a9d 2024-04-25 thomas.ad break;
304 09876a9d 2024-04-25 thomas.ad }
305 09876a9d 2024-04-25 thomas.ad
306 452122e2 2024-04-25 thomas.ad if (*chunksz == 0) {
307 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
308 09876a9d 2024-04-25 thomas.ad break;
309 452122e2 2024-04-25 thomas.ad }
310 452122e2 2024-04-25 thomas.ad buf_drain(&bio->rbuf, linelen);
311 09876a9d 2024-04-25 thomas.ad }
312 09876a9d 2024-04-25 thomas.ad
313 452122e2 2024-04-25 thomas.ad r = bufio_drain_sync(bio, buf, MINIMUM(*chunksz, bufsz));
314 09876a9d 2024-04-25 thomas.ad if (r == 0) {
315 09876a9d 2024-04-25 thomas.ad break;
316 09876a9d 2024-04-25 thomas.ad }
317 09876a9d 2024-04-25 thomas.ad
318 09876a9d 2024-04-25 thomas.ad ret += r;
319 09876a9d 2024-04-25 thomas.ad buf += r;
320 09876a9d 2024-04-25 thomas.ad bufsz -= r;
321 09876a9d 2024-04-25 thomas.ad *chunksz -= r;
322 09876a9d 2024-04-25 thomas.ad }
323 09876a9d 2024-04-25 thomas.ad
324 09876a9d 2024-04-25 thomas.ad return ret;
325 09876a9d 2024-04-25 thomas.ad }
326 09876a9d 2024-04-25 thomas.ad
327 452122e2 2024-04-25 thomas.ad static int
328 452122e2 2024-04-25 thomas.ad http_chunk(struct bufio *bio, const void *buf, size_t len)
329 09876a9d 2024-04-25 thomas.ad {
330 452122e2 2024-04-25 thomas.ad int r;
331 09876a9d 2024-04-25 thomas.ad
332 452122e2 2024-04-25 thomas.ad if (bufio_compose_fmt(bio, "%zx\r\n", len) ||
333 452122e2 2024-04-25 thomas.ad bufio_compose(bio, buf, len) ||
334 452122e2 2024-04-25 thomas.ad bufio_compose(bio, "\r\n", 2))
335 452122e2 2024-04-25 thomas.ad return 1;
336 452122e2 2024-04-25 thomas.ad
337 452122e2 2024-04-25 thomas.ad do {
338 452122e2 2024-04-25 thomas.ad r = bufio_write(bio);
339 452122e2 2024-04-25 thomas.ad if (r == -1 && errno != EAGAIN)
340 452122e2 2024-04-25 thomas.ad errx(1, "bufio_read: %s", bufio_io_err(bio));
341 452122e2 2024-04-25 thomas.ad } while (bio->wbuf.len != 0);
342 452122e2 2024-04-25 thomas.ad
343 452122e2 2024-04-25 thomas.ad return 0;
344 09876a9d 2024-04-25 thomas.ad }
345 09876a9d 2024-04-25 thomas.ad
346 09876a9d 2024-04-25 thomas.ad static int
347 09876a9d 2024-04-25 thomas.ad get_refs(int https, const char *host, const char *port, const char *path)
348 09876a9d 2024-04-25 thomas.ad {
349 452122e2 2024-04-25 thomas.ad struct bufio bio;
350 a36700cf 2024-04-25 thomas.ad char buf[GOT_PKT_MAX];
351 09876a9d 2024-04-25 thomas.ad const char *errstr, *sufx = "/info/refs";
352 09876a9d 2024-04-25 thomas.ad size_t skip, chunksz = 0;
353 09876a9d 2024-04-25 thomas.ad ssize_t r;
354 09876a9d 2024-04-25 thomas.ad int chunked;
355 452122e2 2024-04-25 thomas.ad int sock;
356 452122e2 2024-04-25 thomas.ad int ret = -1;
357 09876a9d 2024-04-25 thomas.ad
358 452122e2 2024-04-25 thomas.ad if ((sock = dial(https, host, port)) == -1)
359 09876a9d 2024-04-25 thomas.ad return -1;
360 09876a9d 2024-04-25 thomas.ad
361 452122e2 2024-04-25 thomas.ad if (bufio_init(&bio)) {
362 452122e2 2024-04-25 thomas.ad warnx("bufio_init");
363 452122e2 2024-04-25 thomas.ad goto err;
364 452122e2 2024-04-25 thomas.ad }
365 452122e2 2024-04-25 thomas.ad bufio_set_fd(&bio, sock);
366 452122e2 2024-04-25 thomas.ad if (https && bufio_starttls(&bio, host, 0, NULL, 0, NULL, 0) == -1) {
367 452122e2 2024-04-25 thomas.ad warnx("bufio_starttls");
368 452122e2 2024-04-25 thomas.ad goto err;
369 09876a9d 2024-04-25 thomas.ad }
370 09876a9d 2024-04-25 thomas.ad
371 452122e2 2024-04-25 thomas.ad if (http_open(&bio, https, "GET", host, port, path, sufx,
372 452122e2 2024-04-25 thomas.ad "service=git-upload-pack", NULL) == -1)
373 452122e2 2024-04-25 thomas.ad goto err;
374 452122e2 2024-04-25 thomas.ad
375 ff4d9c2e 2024-04-25 thomas.ad /* Fetch the initial reference announcement from the server. */
376 452122e2 2024-04-25 thomas.ad if (http_parse_reply(&bio, &chunked, UPLOAD_PACK_ADV) == -1)
377 452122e2 2024-04-25 thomas.ad goto err;
378 452122e2 2024-04-25 thomas.ad
379 09876a9d 2024-04-25 thomas.ad /* skip first pack; why git over http is like this? */
380 452122e2 2024-04-25 thomas.ad r = http_read(&bio, chunked, &chunksz, buf, 4);
381 452122e2 2024-04-25 thomas.ad if (r <= 0)
382 452122e2 2024-04-25 thomas.ad goto err;
383 09876a9d 2024-04-25 thomas.ad buf[4] = '\0';
384 09876a9d 2024-04-25 thomas.ad skip = hexstrtonum(buf, 0, INT_MAX, &errstr);
385 09876a9d 2024-04-25 thomas.ad if (errstr != NULL) {
386 09876a9d 2024-04-25 thomas.ad warnx("pktlen is %s", errstr);
387 452122e2 2024-04-25 thomas.ad goto err;
388 09876a9d 2024-04-25 thomas.ad }
389 09876a9d 2024-04-25 thomas.ad
390 09876a9d 2024-04-25 thomas.ad /* TODO: validate it's # service=git-upload-pack\n */
391 09876a9d 2024-04-25 thomas.ad while (skip > 0) {
392 452122e2 2024-04-25 thomas.ad r = http_read(&bio, chunked, &chunksz, buf,
393 09876a9d 2024-04-25 thomas.ad MINIMUM(skip, sizeof(buf)));
394 452122e2 2024-04-25 thomas.ad if (r <= 0)
395 452122e2 2024-04-25 thomas.ad goto err;
396 09876a9d 2024-04-25 thomas.ad skip -= r;
397 09876a9d 2024-04-25 thomas.ad }
398 09876a9d 2024-04-25 thomas.ad
399 09876a9d 2024-04-25 thomas.ad for (;;) {
400 452122e2 2024-04-25 thomas.ad r = http_read(&bio, chunked, &chunksz, buf, sizeof(buf));
401 452122e2 2024-04-25 thomas.ad if (r == -1)
402 452122e2 2024-04-25 thomas.ad goto err;
403 09876a9d 2024-04-25 thomas.ad
404 09876a9d 2024-04-25 thomas.ad if (r == 0)
405 09876a9d 2024-04-25 thomas.ad break;
406 09876a9d 2024-04-25 thomas.ad
407 09876a9d 2024-04-25 thomas.ad fwrite(buf, 1, r, stdout);
408 09876a9d 2024-04-25 thomas.ad }
409 09876a9d 2024-04-25 thomas.ad
410 09876a9d 2024-04-25 thomas.ad fflush(stdout);
411 452122e2 2024-04-25 thomas.ad ret = 0;
412 452122e2 2024-04-25 thomas.ad err:
413 452122e2 2024-04-25 thomas.ad bufio_close_sync(&bio);
414 452122e2 2024-04-25 thomas.ad bufio_free(&bio);
415 452122e2 2024-04-25 thomas.ad return ret;
416 09876a9d 2024-04-25 thomas.ad }
417 09876a9d 2024-04-25 thomas.ad
418 09876a9d 2024-04-25 thomas.ad static int
419 09876a9d 2024-04-25 thomas.ad upload_request(int https, const char *host, const char *port, const char *path,
420 09876a9d 2024-04-25 thomas.ad FILE *in)
421 09876a9d 2024-04-25 thomas.ad {
422 452122e2 2024-04-25 thomas.ad struct bufio bio;
423 09876a9d 2024-04-25 thomas.ad const char *errstr;
424 a36700cf 2024-04-25 thomas.ad char buf[GOT_PKT_MAX];
425 09876a9d 2024-04-25 thomas.ad ssize_t r;
426 09876a9d 2024-04-25 thomas.ad size_t chunksz = 0;
427 09876a9d 2024-04-25 thomas.ad long long t;
428 09876a9d 2024-04-25 thomas.ad int chunked;
429 452122e2 2024-04-25 thomas.ad int sock;
430 452122e2 2024-04-25 thomas.ad int ret = -1;
431 09876a9d 2024-04-25 thomas.ad
432 452122e2 2024-04-25 thomas.ad if ((sock = dial(https, host, port)) == -1)
433 09876a9d 2024-04-25 thomas.ad return -1;
434 ed77be68 2024-04-25 thomas.ad
435 452122e2 2024-04-25 thomas.ad if (bufio_init(&bio)) {
436 452122e2 2024-04-25 thomas.ad warnx("bufio_init");
437 452122e2 2024-04-25 thomas.ad goto err;
438 452122e2 2024-04-25 thomas.ad }
439 452122e2 2024-04-25 thomas.ad bufio_set_fd(&bio, sock);
440 452122e2 2024-04-25 thomas.ad if (https && bufio_starttls(&bio, host, 0, NULL, 0, NULL, 0) == -1) {
441 452122e2 2024-04-25 thomas.ad warnx("bufio_starttls");
442 452122e2 2024-04-25 thomas.ad goto err;
443 452122e2 2024-04-25 thomas.ad }
444 ed77be68 2024-04-25 thomas.ad #ifndef PROFILE
445 ed77be68 2024-04-25 thomas.ad /* TODO: can we push this upwards such that get_refs() is covered? */
446 ed77be68 2024-04-25 thomas.ad if (pledge("stdio", NULL) == -1)
447 ed77be68 2024-04-25 thomas.ad err(1, "pledge");
448 ed77be68 2024-04-25 thomas.ad #endif
449 452122e2 2024-04-25 thomas.ad if (http_open(&bio, https, "POST", host, port, path, "/git-upload-pack",
450 452122e2 2024-04-25 thomas.ad NULL, UPLOAD_PACK_REQ) == -1)
451 452122e2 2024-04-25 thomas.ad goto err;
452 452122e2 2024-04-25 thomas.ad
453 ff4d9c2e 2024-04-25 thomas.ad /*
454 ff4d9c2e 2024-04-25 thomas.ad * Read have/want lines generated by got-fetch-pack and forward
455 ff4d9c2e 2024-04-25 thomas.ad * them to the server in the POST request body.
456 ff4d9c2e 2024-04-25 thomas.ad */
457 09876a9d 2024-04-25 thomas.ad for (;;) {
458 09876a9d 2024-04-25 thomas.ad r = fread(buf, 1, 4, in);
459 09876a9d 2024-04-25 thomas.ad if (r != 4)
460 09876a9d 2024-04-25 thomas.ad goto err;
461 09876a9d 2024-04-25 thomas.ad
462 09876a9d 2024-04-25 thomas.ad buf[4] = '\0';
463 09876a9d 2024-04-25 thomas.ad t = hexstrtonum(buf, 0, sizeof(buf), &errstr);
464 09876a9d 2024-04-25 thomas.ad if (errstr != NULL) {
465 09876a9d 2024-04-25 thomas.ad warnx("pktline len is %s", errstr);
466 09876a9d 2024-04-25 thomas.ad goto err;
467 09876a9d 2024-04-25 thomas.ad }
468 09876a9d 2024-04-25 thomas.ad
469 09876a9d 2024-04-25 thomas.ad if (t == 0) {
470 06dc3607 2024-04-25 thomas.ad const char *flushpkt = "0000";
471 06dc3607 2024-04-25 thomas.ad if (http_chunk(&bio, flushpkt, strlen(flushpkt)))
472 452122e2 2024-04-25 thomas.ad goto err;
473 06dc3607 2024-04-25 thomas.ad continue; /* got-fetch-pack will send "done" */
474 09876a9d 2024-04-25 thomas.ad }
475 09876a9d 2024-04-25 thomas.ad
476 09876a9d 2024-04-25 thomas.ad if (t < 6) {
477 09876a9d 2024-04-25 thomas.ad warnx("pktline len is too small");
478 09876a9d 2024-04-25 thomas.ad goto err;
479 09876a9d 2024-04-25 thomas.ad }
480 09876a9d 2024-04-25 thomas.ad
481 09876a9d 2024-04-25 thomas.ad r = fread(buf + 4, 1, t - 4, in);
482 09876a9d 2024-04-25 thomas.ad if (r != t - 4)
483 09876a9d 2024-04-25 thomas.ad goto err;
484 09876a9d 2024-04-25 thomas.ad
485 452122e2 2024-04-25 thomas.ad if (http_chunk(&bio, buf, t))
486 452122e2 2024-04-25 thomas.ad goto err;
487 06dc3607 2024-04-25 thomas.ad
488 06dc3607 2024-04-25 thomas.ad /*
489 06dc3607 2024-04-25 thomas.ad * Once got-fetch-pack is done the server will
490 06dc3607 2024-04-25 thomas.ad * send pack file data.
491 06dc3607 2024-04-25 thomas.ad */
492 06dc3607 2024-04-25 thomas.ad if (t == 9 && strncmp(buf + 4, "done\n", 5) == 0) {
493 06dc3607 2024-04-25 thomas.ad if (http_chunk(&bio, NULL, 0))
494 06dc3607 2024-04-25 thomas.ad goto err;
495 06dc3607 2024-04-25 thomas.ad break;
496 06dc3607 2024-04-25 thomas.ad }
497 09876a9d 2024-04-25 thomas.ad }
498 09876a9d 2024-04-25 thomas.ad
499 452122e2 2024-04-25 thomas.ad if (http_parse_reply(&bio, &chunked, UPLOAD_PACK_RES) == -1)
500 09876a9d 2024-04-25 thomas.ad goto err;
501 09876a9d 2024-04-25 thomas.ad
502 ff4d9c2e 2024-04-25 thomas.ad /* Fetch pack file data from server. */
503 09876a9d 2024-04-25 thomas.ad for (;;) {
504 452122e2 2024-04-25 thomas.ad r = http_read(&bio, chunked, &chunksz, buf, sizeof(buf));
505 452122e2 2024-04-25 thomas.ad if (r == -1)
506 452122e2 2024-04-25 thomas.ad goto err;
507 09876a9d 2024-04-25 thomas.ad
508 09876a9d 2024-04-25 thomas.ad if (r == 0)
509 09876a9d 2024-04-25 thomas.ad break;
510 09876a9d 2024-04-25 thomas.ad
511 09876a9d 2024-04-25 thomas.ad fwrite(buf, 1, r, stdout);
512 09876a9d 2024-04-25 thomas.ad }
513 09876a9d 2024-04-25 thomas.ad
514 452122e2 2024-04-25 thomas.ad ret = 0;
515 09876a9d 2024-04-25 thomas.ad err:
516 452122e2 2024-04-25 thomas.ad bufio_close_sync(&bio);
517 452122e2 2024-04-25 thomas.ad bufio_free(&bio);
518 452122e2 2024-04-25 thomas.ad return ret;
519 09876a9d 2024-04-25 thomas.ad }
520 09876a9d 2024-04-25 thomas.ad
521 09876a9d 2024-04-25 thomas.ad static __dead void
522 09876a9d 2024-04-25 thomas.ad usage(void)
523 09876a9d 2024-04-25 thomas.ad {
524 09876a9d 2024-04-25 thomas.ad fprintf(stderr, "usage: %s [-qv] proto host port path\n",
525 09876a9d 2024-04-25 thomas.ad getprogname());
526 09876a9d 2024-04-25 thomas.ad exit(1);
527 09876a9d 2024-04-25 thomas.ad }
528 09876a9d 2024-04-25 thomas.ad
529 09876a9d 2024-04-25 thomas.ad int
530 09876a9d 2024-04-25 thomas.ad main(int argc, char **argv)
531 09876a9d 2024-04-25 thomas.ad {
532 09876a9d 2024-04-25 thomas.ad struct pollfd pfd;
533 09876a9d 2024-04-25 thomas.ad const char *host, *port, *path;
534 09876a9d 2024-04-25 thomas.ad int https = 0;
535 09876a9d 2024-04-25 thomas.ad int ch;
536 09876a9d 2024-04-25 thomas.ad
537 cf87b1d1 2024-04-25 thomas.ad #ifndef PROFILE
538 a9a48d93 2024-04-25 thomas.ad if (pledge("stdio rpath inet dns unveil", NULL) == -1)
539 09876a9d 2024-04-25 thomas.ad err(1, "pledge");
540 09876a9d 2024-04-25 thomas.ad #endif
541 09876a9d 2024-04-25 thomas.ad
542 09876a9d 2024-04-25 thomas.ad while ((ch = getopt(argc, argv, "qv")) != -1) {
543 09876a9d 2024-04-25 thomas.ad switch (ch) {
544 09876a9d 2024-04-25 thomas.ad case 'q':
545 09876a9d 2024-04-25 thomas.ad verbose = -1;
546 09876a9d 2024-04-25 thomas.ad break;
547 09876a9d 2024-04-25 thomas.ad case 'v':
548 09876a9d 2024-04-25 thomas.ad verbose++;
549 09876a9d 2024-04-25 thomas.ad break;
550 09876a9d 2024-04-25 thomas.ad default:
551 09876a9d 2024-04-25 thomas.ad usage();
552 09876a9d 2024-04-25 thomas.ad }
553 09876a9d 2024-04-25 thomas.ad }
554 09876a9d 2024-04-25 thomas.ad argc -= optind;
555 09876a9d 2024-04-25 thomas.ad argv += optind;
556 09876a9d 2024-04-25 thomas.ad
557 09876a9d 2024-04-25 thomas.ad if (argc != 4)
558 09876a9d 2024-04-25 thomas.ad usage();
559 09876a9d 2024-04-25 thomas.ad
560 09876a9d 2024-04-25 thomas.ad https = strcmp(argv[0], "https") == 0;
561 e9495ffd 2024-04-25 thomas.ad #ifndef PROFILE
562 a9a48d93 2024-04-25 thomas.ad if (https) {
563 a9a48d93 2024-04-25 thomas.ad if (unveil("/etc/ssl/cert.pem", "r") == -1)
564 a9a48d93 2024-04-25 thomas.ad err(1, "unveil /etc/ssl/cert.pem");
565 a9a48d93 2024-04-25 thomas.ad } else {
566 e9495ffd 2024-04-25 thomas.ad /* drop "rpath" */
567 a9a48d93 2024-04-25 thomas.ad if (pledge("stdio inet dns unveil", NULL) == -1)
568 e9495ffd 2024-04-25 thomas.ad err(1, "pledge");
569 e9495ffd 2024-04-25 thomas.ad }
570 a9a48d93 2024-04-25 thomas.ad #else
571 a9a48d93 2024-04-25 thomas.ad if (unveil("gmon.out", "rwc") != 0)
572 a9a48d93 2024-04-25 thomas.ad err(1, "unveil gmon.out");
573 e9495ffd 2024-04-25 thomas.ad #endif
574 a9a48d93 2024-04-25 thomas.ad if (unveil(NULL, NULL) == -1)
575 a9a48d93 2024-04-25 thomas.ad err(1, "unveil NULL");
576 a9a48d93 2024-04-25 thomas.ad
577 09876a9d 2024-04-25 thomas.ad host = argv[1];
578 09876a9d 2024-04-25 thomas.ad port = argv[2];
579 09876a9d 2024-04-25 thomas.ad path = argv[3];
580 09876a9d 2024-04-25 thomas.ad
581 09876a9d 2024-04-25 thomas.ad if (get_refs(https, host, port, path) == -1)
582 09876a9d 2024-04-25 thomas.ad errx(1, "failed to get refs");
583 09876a9d 2024-04-25 thomas.ad
584 09876a9d 2024-04-25 thomas.ad pfd.fd = 0;
585 09876a9d 2024-04-25 thomas.ad pfd.events = POLLIN;
586 09876a9d 2024-04-25 thomas.ad if (poll(&pfd, 1, INFTIM) == -1)
587 09876a9d 2024-04-25 thomas.ad err(1, "poll");
588 09876a9d 2024-04-25 thomas.ad
589 09876a9d 2024-04-25 thomas.ad if ((ch = fgetc(stdin)) == EOF)
590 09876a9d 2024-04-25 thomas.ad return 0;
591 09876a9d 2024-04-25 thomas.ad
592 09876a9d 2024-04-25 thomas.ad ungetc(ch, stdin);
593 09876a9d 2024-04-25 thomas.ad if (upload_request(https, host, port, path, stdin) == -1) {
594 09876a9d 2024-04-25 thomas.ad fflush(tmp);
595 09876a9d 2024-04-25 thomas.ad errx(1, "failed to upload request");
596 09876a9d 2024-04-25 thomas.ad }
597 09876a9d 2024-04-25 thomas.ad
598 09876a9d 2024-04-25 thomas.ad return 0;
599 09876a9d 2024-04-25 thomas.ad }