Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2012-2023 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : : * All rights reserved.
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
10 : : * 1. Redistributions of source code must retain the above copyright
11 : : * notice, this list of conditions and the following disclaimer
12 : : * in this position and unchanged.
13 : : * 2. Redistributions in binary form must reproduce the above copyright
14 : : * notice, this list of conditions and the following disclaimer in the
15 : : * documentation and/or other materials provided with the distribution.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : : */
28 : :
29 : : #include <sys/param.h>
30 : : #include <sys/wait.h>
31 : : #include <sys/socket.h>
32 : : #include <sys/time.h>
33 : :
34 : : #include <ctype.h>
35 : : #include <fcntl.h>
36 : : #include <errno.h>
37 : : #include <stdio.h>
38 : : #include <string.h>
39 : : #include <paths.h>
40 : : #include <poll.h>
41 : :
42 : : #include <bsd_compat.h>
43 : :
44 : : #include "pkg.h"
45 : : #include "private/event.h"
46 : : #include "private/pkg.h"
47 : : #include "private/utils.h"
48 : : #include "private/fetch.h"
49 : :
50 : : static struct fetcher fetchers [] = {
51 : : {
52 : : "tcp",
53 : : 0,
54 : : tcp_open,
55 : : NULL,
56 : : fh_close,
57 : : stdio_fetch,
58 : : },
59 : : {
60 : : "ssh",
61 : : 0,
62 : : ssh_open,
63 : : NULL,
64 : : fh_close,
65 : : stdio_fetch,
66 : : },
67 : : {
68 : : "pkg+https",
69 : : 0,
70 : : curl_open,
71 : : NULL,
72 : : curl_cleanup,
73 : : curl_fetch,
74 : : },
75 : : {
76 : : "pkg+http",
77 : : 0,
78 : : curl_open,
79 : : NULL,
80 : : curl_cleanup,
81 : : curl_fetch,
82 : : },
83 : : {
84 : : "https",
85 : : 0,
86 : : curl_open,
87 : : NULL,
88 : : curl_cleanup,
89 : : curl_fetch,
90 : : },
91 : : {
92 : : "http",
93 : : 0,
94 : : curl_open,
95 : : NULL,
96 : : curl_cleanup,
97 : : curl_fetch,
98 : : },
99 : : {
100 : : "file",
101 : : 0,
102 : : file_open,
103 : : fh_close,
104 : : NULL,
105 : : stdio_fetch,
106 : : },
107 : : };
108 : :
109 : : int
110 : 1 : pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
111 : : time_t t)
112 : : {
113 : 1 : int fd = -1;
114 : 1 : int retcode = EPKG_FATAL;
115 : : struct fetch_item fi;
116 : :
117 : 1 : memset(&fi, 0, sizeof(struct fetch_item));
118 : :
119 : 1 : fd = mkstemp(dest);
120 : :
121 [ + - ]: 1 : if (fd == -1) {
122 : 0 : pkg_emit_errno("mkstemp", dest);
123 : 0 : return(EPKG_FATAL);
124 : : }
125 : :
126 : 1 : fi.url = url;
127 : 1 : fi.mtime = t;
128 : 1 : retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
129 : :
130 [ - + ]: 1 : if (fi.mtime != 0) {
131 : 1 : struct timespec ts[2] = {
132 : 2 : {
133 : 1 : .tv_sec = fi.mtime,
134 : : .tv_nsec = 0
135 : : },
136 : 2 : {
137 : 1 : .tv_sec = fi.mtime,
138 : : .tv_nsec = 0
139 : : }
140 : : };
141 : 1 : futimens(fd, ts);
142 : 1 : }
143 : :
144 : 1 : close(fd);
145 : :
146 : : /* Remove local file if fetch failed */
147 [ + - ]: 1 : if (retcode != EPKG_OK)
148 : 0 : unlink(dest);
149 : :
150 : 1 : return (retcode);
151 : 1 : }
152 : :
153 : : int
154 : 2 : pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
155 : : ssize_t offset, int64_t size)
156 : : {
157 : 2 : int fd = -1;
158 : 2 : int retcode = EPKG_FATAL;
159 : : struct fetch_item fi;
160 : 2 : char *url_to_free = NULL;
161 : :
162 : 2 : fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
163 [ + - ]: 2 : if (fd == -1) {
164 : 0 : pkg_emit_errno("open", dest);
165 : 0 : return(EPKG_FATAL);
166 : : }
167 : :
168 [ + - ]: 2 : if (repo != NULL) {
169 : 2 : xasprintf(&url_to_free, "%s/%s", repo->url, url);
170 : 2 : fi.url = url_to_free;
171 : 2 : } else {
172 : 0 : fi.url = url;
173 : : }
174 : :
175 : 2 : fi.offset = offset;
176 : 2 : fi.size = size;
177 : 2 : fi.mtime = t;
178 : :
179 : 2 : retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
180 : 2 : free(url_to_free);
181 : :
182 [ + - ]: 2 : if (t != 0) {
183 : 0 : struct timespec ts[2] = {
184 : 0 : {
185 : 0 : .tv_sec = t,
186 : : .tv_nsec = 0
187 : : },
188 : 0 : {
189 : 0 : .tv_sec = t,
190 : : .tv_nsec = 0
191 : : }
192 : : };
193 : 0 : futimens(fd, ts);
194 : 0 : }
195 : 2 : close(fd);
196 : :
197 : : /* Remove local file if fetch failed */
198 [ + - ]: 2 : if (retcode != EPKG_OK)
199 : 0 : unlink(dest);
200 : :
201 : 2 : return (retcode);
202 : 2 : }
203 : :
204 : : #define URL_SCHEME_PREFIX "pkg+"
205 : :
206 : : static struct fetcher *
207 : 135 : select_fetcher(const char *url)
208 : : {
209 : : struct fetcher *f;
210 : : size_t nsz;
211 : :
212 [ + - ]: 940 : for (size_t i = 0; i < NELEM(fetchers); i++) {
213 : 940 : nsz = strlen(fetchers[i].scheme);
214 : :
215 [ + + + - ]: 940 : if ((strncasecmp(url, fetchers[i].scheme, nsz) == 0) &&
216 : 135 : url[nsz] == ':') {
217 : 135 : f = &fetchers[i];
218 : 135 : f->timeout =
219 : 135 : pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));
220 : 135 : return (f);
221 : : }
222 : 805 : }
223 : 0 : return (NULL);
224 : :
225 : 135 : }
226 : : int
227 : 300 : pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *fi,
228 : : bool silent)
229 : : {
230 : : struct pkg_kv *kv;
231 : 300 : kvlist_t envtorestore = tll_init();
232 : 300 : stringlist_t envtounset = tll_init();
233 : : char *tmp;
234 : 300 : int retcode = EPKG_OK;
235 : 300 : struct pkg_repo *fakerepo = NULL;
236 : : size_t nsz;
237 : :
238 : : /* A URL of the form http://host.example.com/ where
239 : : * host.example.com does not resolve as a simple A record is
240 : : * not valid according to RFC 2616 Section 3.2.2. Our usage
241 : : * with SRV records is incorrect. However it is encoded into
242 : : * /usr/sbin/pkg in various releases so we can't just drop it.
243 : : *
244 : : * Instead, introduce new pkg+http://, pkg+https://,
245 : : * pkg+ssh://, pkg+file:// to support the
246 : : * SRV-style server discovery, and also to allow eg. Firefox
247 : : * to run pkg-related stuff given a pkg+foo:// URL.
248 : : *
249 : : * Error if using plain http://, https:// etc with SRV
250 : : */
251 : :
252 : 300 : pkg_dbg(PKG_DBG_FETCH, 1, "Request to fetch %s", fi->url);
253 [ + + ]: 300 : if (repo == NULL) {
254 : 1 : fakerepo = xcalloc(1, sizeof(struct pkg_repo));
255 : 1 : fakerepo->url = xstrdup(fi->url);
256 : 1 : fakerepo->mirror_type = NOMIRROR;
257 : 1 : repo = fakerepo;
258 : 1 : }
259 : :
260 [ + + ]: 300 : if (repo->fetcher == NULL)
261 : 135 : repo->fetcher = select_fetcher(fi->url);
262 [ + - ]: 300 : if (repo->fetcher == NULL) {
263 : 0 : pkg_emit_error("Unknown scheme: %s", fi->url);
264 : 0 : return (EPKG_FATAL);
265 : : }
266 : :
267 : 300 : nsz = strlen(URL_SCHEME_PREFIX);
268 [ + - ]: 300 : if (strncasecmp(URL_SCHEME_PREFIX, fi->url, nsz) == 0) {
269 [ # # ]: 0 : if (repo->mirror_type != SRV) {
270 : 0 : pkg_emit_error("packagesite URL error for %s -- "
271 : : URL_SCHEME_PREFIX
272 : 0 : ":// implies SRV mirror type", fi->url);
273 : :
274 : : /* Too early for there to be anything to cleanup */
275 : 0 : return(EPKG_FATAL);
276 : : }
277 : 0 : fi->url += nsz;
278 : 0 : }
279 : :
280 : 300 : repo->silent = silent;
281 [ - + - + : 300 : tll_foreach(repo->env, k) {
# # ]
282 [ # # ]: 0 : if ((tmp = getenv(k->item->key)) != NULL) {
283 : 0 : kv = xcalloc(1, sizeof(*kv));
284 : 0 : kv->key = xstrdup(k->item->key);
285 : 0 : kv->value = xstrdup(tmp);
286 [ # # # # : 0 : tll_push_back(envtorestore, kv);
# # # # #
# ]
287 : 0 : } else {
288 [ # # # # : 0 : tll_push_back(envtounset, k->item->key);
# # # # #
# ]
289 : : }
290 : 0 : setenv(k->item->key, k->item->value, 1);
291 : 0 : }
292 : :
293 [ + + ]: 300 : if ((retcode = repo->fetcher->open(repo, fi)) != EPKG_OK)
294 : 146 : goto cleanup;
295 : 154 : pkg_dbg(PKG_DBG_FETCH, 1, "Fetch: fetcher used: %s", repo->fetcher->scheme);
296 : :
297 : 154 : retcode = repo->fetcher->fetch(repo, dest, fi);
298 [ + + ]: 304 : if (retcode == EPKG_OK)
299 : 150 : pkg_emit_fetch_finished(fi->url);
300 : :
301 : : cleanup:
302 [ - + - + : 300 : tll_foreach(envtorestore, k) {
# # ]
303 : 0 : setenv(k->item->key, k->item->value, 1);
304 [ # # # # : 0 : tll_remove_and_free(envtorestore, k, pkg_kv_free);
# # ]
305 : 0 : }
306 [ - + - + : 300 : tll_free(envtorestore);
# # ]
307 [ - + - + : 300 : tll_foreach(envtounset, k) {
# # ]
308 : 0 : unsetenv(k->item);
309 [ # # # # : 0 : tll_remove(envtounset, k);
# # ]
310 : 0 : }
311 [ - + - + : 300 : tll_free(envtounset);
# # ]
312 : :
313 [ + - + + ]: 300 : if (repo->fetcher != NULL && repo->fetcher->close != NULL)
314 : 289 : repo->fetcher->close(repo);
315 : 300 : free(fakerepo);
316 : :
317 [ + + ]: 300 : if (retcode == EPKG_OK) {
318 : 150 : struct timespec ts[2] = {
319 : 300 : {
320 : 150 : .tv_sec = fi->mtime,
321 : : .tv_nsec = 0
322 : : },
323 : 300 : {
324 : 150 : .tv_sec = fi->mtime,
325 : : .tv_nsec = 0
326 : : }
327 : : };
328 : 150 : futimens(dest, ts);
329 : 150 : }
330 : :
331 : 300 : return (retcode);
332 : 300 : }
|