Blame


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