Compare commits
10 Commits
load_ramdi
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
a300f01d08 | ||
|
24db85c5ab | ||
|
43a9d78fdd | ||
|
36b688d17a | ||
|
736c89620f | ||
|
e34da5300d | ||
|
77181411cd | ||
|
08ec5c3a78 | ||
|
f6a430fd36 | ||
|
0a972b5b86 |
9
Makefile
9
Makefile
@ -126,6 +126,15 @@ clean:
|
||||
.depend: version.h
|
||||
$(CPP) $(CFLAGS) -M $(ALLSRC) > .depend
|
||||
|
||||
ALL_TESTS := test_parse_content_length
|
||||
|
||||
test: $(ALL_TESTS)
|
||||
@set -e; \
|
||||
for tst in $(ALL_TESTS); do echo "$$tst"; ./$$tst; done
|
||||
|
||||
test_parse_content_length: test_parse_content_length.c url.c
|
||||
$(CC) $(CFLAGS) -flto $(INIT_DEFS) -o $@ $<
|
||||
|
||||
ifeq (.depend,$(wildcard .depend))
|
||||
include .depend
|
||||
endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
#define IMAGE_LOCATION "/image"
|
||||
#define STAGE2_LOCATION "/root"
|
||||
#define LIVE_DEVICE "/dev/loop0"
|
||||
#define LOMOUNT_DEVICE "/dev/loop3"
|
||||
#define STAGE2FS "squashfs"
|
||||
#define LIVEFS "squashfs"
|
||||
#ifndef STAGE2_BINNAME
|
||||
|
@ -133,7 +133,7 @@ set_loop (const char *device, const char *file)
|
||||
}
|
||||
|
||||
|
||||
char * loopdev = "/dev/loop3"; /* Ugly. But do I care? */
|
||||
char * loopdev = LOMOUNT_DEVICE; /* Ugly. But do I care? */
|
||||
|
||||
void del_loop(char *device)
|
||||
{
|
||||
|
46
network.c
46
network.c
@ -890,16 +890,8 @@ enum return_type ftp_prepare(void)
|
||||
char * questions_auto[] = { "server", "directory", "user", "pass", NULL };
|
||||
static char ** answers = NULL;
|
||||
enum return_type results;
|
||||
unsigned long ramdisk_size;
|
||||
|
||||
update_splash("prepare");
|
||||
ramdisk_size = get_ramdisk_size(NULL);
|
||||
if (!ramdisk_possible(ramdisk_size)) {
|
||||
stg1_error_message("FTP install needs more than %d Mbytes of memory (detected %u Mbytes).",
|
||||
BYTES2MB(ramdisk_size), BYTES2MB(total_memory()));
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
results = intf_select_and_up();
|
||||
|
||||
if (results != RETURN_OK)
|
||||
@ -957,6 +949,13 @@ enum return_type ftp_prepare(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ramdisk_possible(size)) {
|
||||
close(fd);
|
||||
close(ftp_serv_response);
|
||||
stg1_error_message("FTP install needs more than %u Mbytes of memory (detected %u Mbytes).",
|
||||
BYTES2MB(size)*2, BYTES2MB(total_memory()));
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
log_message("FTP: size of download %d bytes", size);
|
||||
|
||||
results = load_ramdisk_fd(fd, size);
|
||||
@ -986,17 +985,9 @@ enum return_type http_prepare(void)
|
||||
char * questions_auto[] = { "server", "directory", NULL };
|
||||
static char ** answers = NULL;
|
||||
enum return_type results;
|
||||
unsigned long ramdisk_size;
|
||||
|
||||
update_splash("prepare");
|
||||
|
||||
ramdisk_size = get_ramdisk_size(NULL);
|
||||
if (!ramdisk_possible(ramdisk_size)) {
|
||||
stg1_error_message("HTTP install needs more than %d Mbytes of memory (detected %u Mbytes).",
|
||||
BYTES2MB(ramdisk_size), BYTES2MB(total_memory()));
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
results = intf_select_and_up();
|
||||
|
||||
if (results != RETURN_OK)
|
||||
@ -1005,7 +996,9 @@ enum return_type http_prepare(void)
|
||||
do {
|
||||
char location_full[500];
|
||||
char *tmp;
|
||||
int fd, size;
|
||||
int fd;
|
||||
unsigned long size;
|
||||
int is_iso = 0;
|
||||
|
||||
snprintf(location_full, sizeof(location_full),
|
||||
"Please enter the name or IP address of the HTTP server, "
|
||||
@ -1017,6 +1010,13 @@ enum return_type http_prepare(void)
|
||||
}
|
||||
|
||||
strcpy(location_full, answers[1]);
|
||||
log_message("HTTP: trying to retrieve %s", location_full);
|
||||
fd = http_download_file(answers[0], location_full, &size);
|
||||
if (fd >= 0) {
|
||||
is_iso = 1;
|
||||
goto download;
|
||||
}
|
||||
|
||||
tmp = get_ramdisk_realname();
|
||||
strcat(location_full, tmp);
|
||||
free(tmp);
|
||||
@ -1034,9 +1034,17 @@ enum return_type http_prepare(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
log_message("HTTP: size of download %d bytes", size);
|
||||
download:
|
||||
if (!ramdisk_possible(size)) {
|
||||
close(fd);
|
||||
stg1_error_message("HTTP install needs more than %u Mbytes of memory (detected %u Mbytes).",
|
||||
BYTES2MB(size)*2, BYTES2MB(total_memory()));
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
results = load_ramdisk_fd(fd, size);
|
||||
log_message("HTTP: size of download %lu bytes", size);
|
||||
|
||||
results = load_ramdisk_or_iso(fd, size, is_iso);
|
||||
close(fd);
|
||||
if (results != RETURN_OK)
|
||||
return RETURN_ERROR;
|
||||
|
@ -3,7 +3,7 @@
|
||||
%def_with splash
|
||||
|
||||
Name: propagator
|
||||
Version: 20210907
|
||||
Version: 20211007
|
||||
Release: alt1
|
||||
|
||||
Summary: 'Early userspace' set of binaries
|
||||
@ -33,12 +33,32 @@ including init and various helpers for hw probing and bootstrapping.
|
||||
%install
|
||||
%makeinstall_std libdir=%_libdir
|
||||
|
||||
%check
|
||||
%make_build \
|
||||
%{?_with_cifs:WITH_CIFS=t} \
|
||||
%{?_with_shell:WITH_SHELL=t} \
|
||||
%{?_with_splash:WITH_SPLASH=t} \
|
||||
version=%version-%release \
|
||||
libdir=%_libdir \
|
||||
test
|
||||
|
||||
%files
|
||||
%_bindir/gencpio
|
||||
%_bindir/mkmodpack
|
||||
%_sbindir/propagator
|
||||
|
||||
%changelog
|
||||
* Thu Oct 07 2021 Alexey Sheplyakov <asheplyakov@altlinux.org> 20211007-alt1
|
||||
- Fixed HTTP boot when server sent more headers after Content-Length (closes: #41072)
|
||||
|
||||
* Thu Sep 22 2021 Alexey Sheplyakov <asheplyakov@altlinux.org> 20210922-alt1
|
||||
- Support booting complete ISOs via HTTP (closes: #40710)
|
||||
|
||||
* Wed Sep 08 2021 Alexey Sheplyakov <asheplyakov@altlinux.org> 20210908-alt1
|
||||
- Figure out stage2 size at the run time instead of relying on
|
||||
ramdisk_size kernel command line parameter. As a result `ramdisk_size`
|
||||
parameter is not required any more (closes: #40629)
|
||||
|
||||
* Tue Sep 07 2021 Alexey Sheplyakov <asheplyakov@altlinux.org> 20210907-alt1
|
||||
- Improved IO errors handling when loading stage2 (closes: #40803)
|
||||
- Support loading stage2 images >= 2GB (related: #40710)
|
||||
|
75
test_parse_content_length.c
Normal file
75
test_parse_content_length.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
void log_message(const char *msg, ...) {
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
#include "url.c" /* yes, include the source file */
|
||||
|
||||
static int test_parse_content_length(const char *headers, unsigned long expected, int shouldfail) {
|
||||
int err;
|
||||
unsigned long val;
|
||||
err = parse_content_length(headers, &val);
|
||||
if (err < 0 && !shouldfail) {
|
||||
log_message("%s: unexpectedly failed on input '%s'", __func__, headers);
|
||||
return 2;
|
||||
}
|
||||
if (err == 0 && shouldfail) {
|
||||
log_message("%s: unexpectedly passed on invalid input '%s'", __func__, headers);
|
||||
return 3;
|
||||
}
|
||||
if (!shouldfail && val != expected) {
|
||||
log_message("%s: expected %lu, got %lu on input '%s'",
|
||||
__func__, expected, val, headers);
|
||||
return 5;
|
||||
}
|
||||
if (!shouldfail && val == expected) {
|
||||
log_message("%s: OK: '%s'", __func__, headers);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int i, err = 0;
|
||||
const char *invalid_inputs[] = {
|
||||
"", /* empty */
|
||||
"Abyrvalg: foobar", /* no Content-Length: */
|
||||
"Content-Length: xyz", /* no digits */
|
||||
"Content-Length: 12xyz", /* invalid characters */
|
||||
"Content-Length: 12 34", /* stray symbols */
|
||||
"Content-Length: 123456789012345678901234567890", /* too big */
|
||||
};
|
||||
const char *valid_inputs[] = {
|
||||
"Content-Length: 1234",
|
||||
"Content-Length: 1234",
|
||||
"Content-Length: 1234 ",
|
||||
"Content-Length: 1234 ",
|
||||
"Content-Length: 1234 \r\n\r\n",
|
||||
"Content-Length: 1234\r\n"
|
||||
"Last-Modified: Sun, 12 Sep 2021 22:31:46 GMT\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Etag: \"613e7fd2-3849a800\"\r\n"
|
||||
"Accept-Ranges: bytes\r\n\r\n"
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(invalid_inputs); i++) {
|
||||
err += test_parse_content_length(invalid_inputs[i], 0,
|
||||
/* shouldfail = */ 1);
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(valid_inputs); i++) {
|
||||
err += test_parse_content_length(valid_inputs[i], 1234,
|
||||
/* shouldfail = */ 0);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* compile command:
|
||||
gcc -O2 -g -flto -o test_parse_content_length.c test_parse_content_length.c
|
||||
*/
|
166
tools.c
166
tools.c
@ -19,6 +19,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
@ -33,6 +36,7 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/mman.h> /* memfd_create */
|
||||
|
||||
#include "stage1.h"
|
||||
#include "log.h"
|
||||
@ -41,6 +45,7 @@
|
||||
#include "automatic.h"
|
||||
#include <errno.h>
|
||||
#include <linux/loop.h>
|
||||
#include "lomount.h"
|
||||
#include "tools.h"
|
||||
#include "modules.h"
|
||||
#include "sha256.h"
|
||||
@ -293,6 +298,45 @@ static void save_stuff_for_rescue(void)
|
||||
log_message("saved file %s for rescue (%d bytes)", file, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop mount ISO loaded to RAM, and open stage2
|
||||
*
|
||||
* @param ram_fd file descriptor of ISO image
|
||||
* @return file descriptor of stage2 or -1 on error
|
||||
*/
|
||||
static int open_stage2_from_iso(int iso_fd)
|
||||
{
|
||||
int stage2_fd = -1;
|
||||
const char *stage2 = get_ramdisk_path(IMAGE_LOCATION); /* /image/live */
|
||||
if (!stage2) {
|
||||
log_message("%s: get_ramdisk_size: got NULL", __func__);
|
||||
goto out;
|
||||
}
|
||||
if (do_losetup_fd(LOMOUNT_DEVICE, iso_fd, NULL) < 0) {
|
||||
log_message("%s: could not setup loopback for iso_fd", __func__);
|
||||
goto out_free;
|
||||
}
|
||||
if (my_mount(LOMOUNT_DEVICE, IMAGE_LOCATION, "iso9660", 0)) {
|
||||
log_message("%s: failed to mount ISO loopback", __func__);
|
||||
goto out_del_loop;
|
||||
}
|
||||
stage2_fd = open(stage2, O_RDONLY);
|
||||
if (stage2_fd < 0) {
|
||||
log_message("%s: failed to open %s", __func__, stage2);
|
||||
goto out_umount;
|
||||
}
|
||||
free((char *)stage2);
|
||||
return stage2_fd;
|
||||
|
||||
out_umount:
|
||||
umount(IMAGE_LOCATION);
|
||||
out_del_loop:
|
||||
del_loop(LOMOUNT_DEVICE);
|
||||
out_free:
|
||||
free((char *)stage2);
|
||||
out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief copy exactly len bytes from buffer to file descriptor
|
||||
@ -346,16 +390,46 @@ static int copy_loop(int dst_fd, int src_fd, unsigned long size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
|
||||
int make_ramfd(unsigned long size)
|
||||
{
|
||||
int ramfd;
|
||||
ramfd = memfd_create("image", MFD_ALLOW_SEALING);
|
||||
if (ramfd < 0) {
|
||||
log_perror("memfd_create");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* By default memfd has no size limit. Set it to avoid OOM */
|
||||
if (!size) {
|
||||
/* XXX: when downloading an image via FTP size can
|
||||
* be 0/unknown (the protocol makes it extremely
|
||||
* difficult to figure out the file size).
|
||||
* Set the limit to of 1/2 RAM in this case.
|
||||
*/
|
||||
size = total_memory()/2;
|
||||
}
|
||||
if (ftruncate(ramfd, (off_t)size) < 0) {
|
||||
log_perror("ftruncate ramfd");
|
||||
goto out;
|
||||
}
|
||||
if (fcntl(ramfd, F_ADD_SEALS, F_SEAL_GROW) < 0) {
|
||||
log_perror("fcntl ramfd F_SEAL_GROW");
|
||||
goto out;
|
||||
}
|
||||
return ramfd;
|
||||
out:
|
||||
close(ramfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum return_type load_ramdisk_or_iso(int source_fd, unsigned long size, int is_iso)
|
||||
{
|
||||
char * ramdisk = "/dev/ram3"; /* warning, verify that this file exists in the initrd, and that root=/dev/ram3 is actually passed to the kernel at boot time */
|
||||
int ram_fd;
|
||||
char * wait_msg = "Loading program into memory...";
|
||||
|
||||
ram_fd = open(ramdisk, O_WRONLY);
|
||||
if (ram_fd == -1) {
|
||||
log_perror(ramdisk);
|
||||
stg1_error_message("Could not open ramdisk device file.");
|
||||
ram_fd = make_ramfd(size);
|
||||
if (ram_fd < 0) {
|
||||
stg1_error_message("Couldn't allocate memory for image.");
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
@ -371,28 +445,62 @@ enum return_type load_ramdisk_fd(int source_fd, unsigned long size)
|
||||
}
|
||||
end_progression();
|
||||
|
||||
if (is_iso) {
|
||||
int stage2_fd = open_stage2_from_iso(ram_fd);
|
||||
if (stage2_fd < 0) {
|
||||
close(ram_fd);
|
||||
stg1_error_message("Could not open stage2 from ISO in RAM");
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
/* got a (mounted) loopback device backed by ram_fd,
|
||||
* ram_fd itself is not necessary any more */
|
||||
close(ram_fd);
|
||||
/* to setup loopback device from stage2_fd */
|
||||
ram_fd = stage2_fd;
|
||||
}
|
||||
|
||||
if (do_losetup_fd(LIVE_DEVICE, ram_fd, NULL) < 0) {
|
||||
close(ram_fd);
|
||||
if (is_iso) {
|
||||
umount(IMAGE_LOCATION);
|
||||
del_loop(LOMOUNT_DEVICE);
|
||||
}
|
||||
stg1_error_message("Could not setup loopback for 2nd stage");
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
/* got a loopback device, don't need ram_fd any more */
|
||||
close(ram_fd);
|
||||
|
||||
if (do_losetup(LIVE_DEVICE, ramdisk) == -1)
|
||||
fatal_error("could not setup loopback for loaded second stage");
|
||||
|
||||
if (my_mount(LIVE_DEVICE, STAGE2_LOCATION, STAGE2FS, 0))
|
||||
if (my_mount(LIVE_DEVICE, STAGE2_LOCATION, STAGE2FS, 0)) {
|
||||
stg1_error_message("Failed to mount stage2");
|
||||
if (is_iso) {
|
||||
umount(IMAGE_LOCATION);
|
||||
del_loop(LOMOUNT_DEVICE);
|
||||
}
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
|
||||
set_param(MODE_RAMDISK);
|
||||
|
||||
if (IS_RESCUE) {
|
||||
save_stuff_for_rescue();
|
||||
if (umount(STAGE2_LOCATION)) {
|
||||
log_perror(ramdisk);
|
||||
log_message("rescue: failed to umount " STAGE2_LOCATION);
|
||||
return RETURN_ERROR;
|
||||
}
|
||||
if (is_iso) {
|
||||
/* XXX: should I do anything special here? */
|
||||
}
|
||||
return RETURN_OK; /* fucksike, I lost several hours wondering why the kernel won't see the rescue if it is alreay mounted */
|
||||
}
|
||||
|
||||
return RETURN_OK;
|
||||
}
|
||||
|
||||
enum return_type load_ramdisk_fd(int source_fd, unsigned long size) {
|
||||
return load_ramdisk_or_iso(source_fd, size, 0);
|
||||
}
|
||||
|
||||
int splash_verbose()
|
||||
{
|
||||
@ -656,10 +764,12 @@ int string_array_length(char ** a)
|
||||
return i;
|
||||
}
|
||||
|
||||
int do_losetup(char * device, char * target)
|
||||
/* setup loop device from file descriptor targfd */
|
||||
int do_losetup_fd(char *device, int targfd, const char *name)
|
||||
{
|
||||
int i, loopfd, targfd;
|
||||
int i, loopfd;
|
||||
struct loop_info loopInfo;
|
||||
memset(&loopInfo, 0, sizeof(loopInfo));
|
||||
|
||||
my_insmod("loop", NULL);
|
||||
/* wait for udev's dust settles down */
|
||||
@ -670,19 +780,10 @@ int do_losetup(char * device, char * target)
|
||||
return -1;
|
||||
}
|
||||
|
||||
targfd = open( target, O_RDONLY );
|
||||
if ( targfd < 0 )
|
||||
{
|
||||
log_message( "losetup: error opening %s: %s", target, strerror(errno) );
|
||||
close( loopfd );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( ioctl(loopfd, LOOP_SET_FD, targfd) < 0 )
|
||||
{
|
||||
log_message( "losetup: error setting up loopback device: %s", strerror(errno) );
|
||||
close( loopfd );
|
||||
close( targfd );
|
||||
return -1;
|
||||
}
|
||||
/* Note: LOOP_SET_FD triggers change event. While processing it
|
||||
@ -693,8 +794,8 @@ int do_losetup(char * device, char * target)
|
||||
*/
|
||||
udev_settle();
|
||||
|
||||
memset(&loopInfo, 0, sizeof(loopInfo));
|
||||
strcpy(loopInfo.lo_name, target);
|
||||
if (name) {
|
||||
strcpy(loopInfo.lo_name, name);
|
||||
|
||||
if ( ioctl(loopfd, LOOP_SET_STATUS, &loopInfo) < 0 )
|
||||
log_message( "losetup: error setting up loopback device: %s", strerror(errno) );
|
||||
@ -705,6 +806,7 @@ int do_losetup(char * device, char * target)
|
||||
* from mounting the device while udev still running blkid on it
|
||||
*/
|
||||
udev_settle();
|
||||
}
|
||||
|
||||
close( loopfd );
|
||||
/* udev might be monitoring loopback device (with fanotify) and
|
||||
@ -713,6 +815,20 @@ int do_losetup(char * device, char * target)
|
||||
* (which is going to mount the newly configured loopback device)
|
||||
*/
|
||||
udev_settle();
|
||||
close( targfd );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_losetup(char * device, char * target)
|
||||
{
|
||||
int ret = 0, targfd;
|
||||
targfd = open( target, O_RDONLY );
|
||||
if ( targfd < 0 )
|
||||
{
|
||||
log_message( "losetup: error opening %s: %s", target, strerror(errno) );
|
||||
return -1;
|
||||
}
|
||||
ret = do_losetup_fd(device, targfd, target);
|
||||
close(targfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
16
tools.h
16
tools.h
@ -36,6 +36,7 @@ int ramdisk_possible(unsigned long ramdisk_size);
|
||||
char * get_ramdisk_realname(void);
|
||||
enum return_type load_ramdisk(char *, unsigned long size);
|
||||
enum return_type load_ramdisk_fd(int ramdisk_fd, unsigned long size);
|
||||
enum return_type load_ramdisk_or_iso(int ramdisk_fd, unsigned long size, int is_iso);
|
||||
void * memdup(void *src, size_t size);
|
||||
void add_to_env(char * name, char * value);
|
||||
void handle_env(char ** env);
|
||||
@ -44,6 +45,19 @@ int pass_env(int);
|
||||
char ** list_directory(char * direct);
|
||||
int string_array_length(char ** a);
|
||||
int do_losetup(char * device, char * target);
|
||||
/**
|
||||
* Setup a loopback device backed by fd.
|
||||
*
|
||||
* @param device path to loopback device
|
||||
* @param fd backing file descriptor
|
||||
* @param name for pretty output from losetup, typically name of the backing file.
|
||||
* @return 0 on success, otherwise -1
|
||||
*
|
||||
* @note: should be used instead of do_losetup for anonymous files,
|
||||
* i.e. ones created by memfd_create, open(O_TMPFILE), etc
|
||||
*/
|
||||
int do_losetup_fd(char *device, int fd, const char *name);
|
||||
|
||||
char * get_ramdisk_path(const char *);
|
||||
char * get_uid_file_path(const char *, const char *);
|
||||
enum return_type verify_ramdisk_digest(const char *filename, const char *sha256_hash);
|
||||
@ -63,6 +77,6 @@ struct param_elem
|
||||
#define ptr_begins_static_str(pointer,static_str) (!strncmp(pointer,static_str,sizeof(static_str)-1))
|
||||
#define streq !strcmp
|
||||
#define MKDEV(ma,mi) ((ma)<<8 | (mi))
|
||||
#define BYTES2MB(b) ((unsigned int)(b)/1024/1024)
|
||||
#define BYTES2MB(b) ((unsigned int)((b)/1024/1024))
|
||||
|
||||
#endif
|
||||
|
81
url.c
81
url.c
@ -24,6 +24,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in_systm.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@ -394,11 +395,72 @@ int ftp_end_data_command(int sock)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_content_length(const char *headers, unsigned long *size) {
|
||||
const char *header_content_length = "Content-Length: ";
|
||||
const char *hdr = NULL, *ptr = NULL, *start = NULL, *end = NULL;
|
||||
const char *nexthdr = NULL;
|
||||
|
||||
int http_download_file(char * hostname, char * remotename, int * size)
|
||||
hdr = strstr(headers, header_content_length);
|
||||
if (!hdr) {
|
||||
log_message("%s: error: Content-Length header not found", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
start = hdr + strlen(header_content_length);
|
||||
nexthdr = strstr(start, "\r\n");
|
||||
|
||||
errno = 0;
|
||||
*size = strtoul(start, (char **)&end, 10);
|
||||
|
||||
/* HTTP headers are pretty standard, hence reject a bogus header
|
||||
* instead of trying to download file of unknown length.
|
||||
* Also bail out if the file is too long (i.e. size can't be
|
||||
* represented by unsigned long).
|
||||
* As a side effect this prevents 32-bit platforms from
|
||||
* loading large (>= 4GB) files into RAM
|
||||
*/
|
||||
if (errno != 0 && errno != ERANGE) {
|
||||
log_message("%s: error: invalid Content-Length header '%s', parse error: %s", __func__, hdr, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ERANGE == errno || *size == ULONG_MAX) {
|
||||
log_message("%s: error: length '%s' is too big (>= %lu)", __func__, start, ULONG_MAX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* XXX: on non-numerical inputs strtoul returns 0 *without*
|
||||
* setting errno. Therefore validate the header manually: only
|
||||
* a sequence of decimal digits surrounded by whitspace is OK.
|
||||
* Note: endptr points first non-digit/space character or
|
||||
* end of the string
|
||||
*/
|
||||
for (ptr = end; nexthdr ? ptr < nexthdr : *ptr != '\0'; ptr++) {
|
||||
if (!isspace(*ptr) && !isdigit(*ptr)) {
|
||||
log_message("%s: error: invalid character %c in Content-Length header '%s'", __func__, *ptr, hdr);
|
||||
return -1;
|
||||
}
|
||||
if (isdigit(*ptr)) {
|
||||
/* Content-Length: 123 456
|
||||
* *end = '4'
|
||||
*/
|
||||
log_message("%s: error: stray data '%s' in Content-Length header: '%s'", __func__, ptr, hdr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size == 0) {
|
||||
log_message("%s: length is zero", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_download_file(char * hostname, char * remotename, unsigned long * size)
|
||||
{
|
||||
char * buf;
|
||||
char headers[4096];
|
||||
char *headers_end = NULL; /* points to 1st \r in \r\n\r\n */
|
||||
char * nextChar = headers;
|
||||
int checkedCode;
|
||||
struct in_addr serverAddress;
|
||||
@ -406,7 +468,6 @@ int http_download_file(char * hostname, char * remotename, int * size)
|
||||
int sock;
|
||||
int rc;
|
||||
struct sockaddr_in destPort;
|
||||
char * header_content_length = "Content-Length: ";
|
||||
|
||||
if ((rc = get_host_address(hostname, &serverAddress))) return rc;
|
||||
|
||||
@ -435,7 +496,7 @@ int http_download_file(char * hostname, char * remotename, int * size)
|
||||
|
||||
*nextChar = '\0';
|
||||
checkedCode = 0;
|
||||
while (!strstr(headers, "\r\n\r\n")) {
|
||||
while (!(headers_end = strstr(headers, "\r\n\r\n"))) {
|
||||
polls.fd = sock;
|
||||
polls.events = POLLIN;
|
||||
rc = poll(&polls, 1, TIMEOUT_SECS*1000);
|
||||
@ -493,10 +554,14 @@ int http_download_file(char * hostname, char * remotename, int * size)
|
||||
}
|
||||
}
|
||||
|
||||
if ((buf = strstr(headers, header_content_length)))
|
||||
*size = charstar_to_int(buf + strlen(header_content_length));
|
||||
else
|
||||
*size = 0;
|
||||
|
||||
*headers_end = '\0'; /* skip \r\n\r\n */
|
||||
if (parse_content_length(headers, size) < 0) {
|
||||
goto out_err;
|
||||
}
|
||||
return sock;
|
||||
|
||||
out_err:
|
||||
*size = 0; /* unknown/error */
|
||||
close(sock);
|
||||
return FTPERR_SERVER_IO_ERROR;
|
||||
}
|
||||
|
2
url.h
2
url.h
@ -26,7 +26,7 @@ int ftp_open_connection(char * host, char * name, char * password, char * proxy)
|
||||
int ftp_start_download(int sock, char * remotename, int * size);
|
||||
int ftp_end_data_command(int sock);
|
||||
|
||||
int http_download_file(char * hostname, char * remotename, int * size);
|
||||
int http_download_file(char * hostname, char * remotename, unsigned long * size);
|
||||
|
||||
|
||||
#define FTPERR_BAD_SERVER_RESPONSE -1
|
||||
|
Loading…
Reference in New Issue
Block a user