diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fefcd75..8222214 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@v4 - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y upx-ucl lib32z1 gcc-multilib + run: sudo apt-get update && sudo apt-get install -y upx-ucl lib32z1 gcc-multilib qemu-user - name: Download toolchain env: @@ -66,6 +66,35 @@ jobs: export PATH="$PWD/${{ matrix.toolchain }}/bin:$PATH" make uget CROSS_COMPILE=${{ matrix.cross_compile }} + - name: Test uget (qemu-arm) + run: | + SYSROOT=$(find ${{ matrix.toolchain }} -type d -name target | head -1) + echo "Using sysroot: $SYSROOT" + QEMU="qemu-arm -L $SYSROOT" + fail=0 + + echo -n "Test: no args (expect rc=6)... " + rc=0; $QEMU ./uget 2>/dev/null || rc=$? + [ "$rc" -eq 6 ] && echo "PASS" || { echo "FAIL (rc=$rc)"; fail=1; } + + echo -n "Test: bad args (expect rc=6)... " + rc=0; $QEMU ./uget foo bar 2>/dev/null || rc=$? + [ "$rc" -eq 6 ] && echo "PASS" || { echo "FAIL (rc=$rc)"; fail=1; } + + echo -n "Test: HTTP GET httpbin.org/get... " + rc=0; out=$($QEMU ./uget httpbin.org/get 2>/dev/null) || rc=$? + if [ "$rc" -eq 0 ] && echo "$out" | grep -q '"Host"'; then + echo "PASS" + else + echo "FAIL (rc=$rc)"; fail=1 + fi + + echo -n "Test: HTTP 404 (expect rc=44)... " + rc=0; $QEMU ./uget httpbin.org/status/404 2>/dev/null || rc=$? + [ "$rc" -eq 44 ] && echo "PASS" || { echo "FAIL (rc=$rc)"; fail=1; } + + exit $fail + - name: Generate uget.sh run: ./bin2sh uget > uget.sh diff --git a/Makefile b/Makefile index 2d6e841..118d7ad 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,17 @@ -CFLAGS=-Os +CFLAGS=-Os -ffunction-sections -fdata-sections +LDFLAGS=-Wl,--gc-sections CROSS_COMPILE?=arm-hisiv510-linux- CC=$(CROSS_COMPILE)gcc STRIP=$(CROSS_COMPILE)strip +STRIP_FLAGS=-R .comment -R .note -R .note.ABI-tag -R .eh_frame -R .eh_frame_hdr -R .ARM.attributes -R .jcr BINARIES=uget bin2sh all: $(BINARIES) uget: uget.o - $(CC) $(CFLAGS) -o $@ $^ - $(STRIP) -R .comment -R .note -R .note.ABI-tag $@ + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + $(STRIP) $(STRIP_FLAGS) $@ -upx $@ bin2sh: bin2sh.c diff --git a/uget.c b/uget.c index 6384ba3..ed0eaf5 100644 --- a/uget.c +++ b/uget.c @@ -1,5 +1,3 @@ -#include -#include #include #include @@ -18,72 +16,48 @@ #define ERR_SEND 5 #define ERR_USAGE 6 -#define NDEBUG - -int get_http_respcode(const char *inpbuf) { - char proto[4096], descr[4096]; - int code; +int get_http_respcode(const char *buf) { + int i, code = 0; + while (*buf && *buf != ' ') buf++; + while (*buf == ' ') buf++; + for (i = 0; i < 3 && *buf >= '0' && *buf <= '9'; i++) + code = code * 10 + (*buf++ - '0'); + return code ? code : -1; +} - if (sscanf(inpbuf, "%s %d %s", proto, &code, descr) < 2) - return -1; - return code; +static char *bufcat(char *dst, const char *src) { + while (*src) *dst++ = *src++; + return dst; } int download(int writefd, char *hostname, char *uri) { - int ret = ERR_GENERAL; - - struct addrinfo hints, *res, *res0; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - int err = getaddrinfo(hostname, "80", &hints, &res0); - if (err) { -#ifndef NDEBUG - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(err)); -#endif + struct hostent *he = gethostbyname(hostname); + if (!he) return ERR_GETADDRINFO; - } - int s = -1; - for (res = res0; res; res = res->ai_next) { - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s < 0) { - ret = ERR_SOCKET; - continue; - } + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return ERR_SOCKET; -#ifndef NDEBUG - char buf[256]; - inet_ntop(res->ai_family, &((struct sockaddr_in *)res->ai_addr)->sin_addr, - buf, sizeof(buf)); - fprintf(stderr, "Connecting to %s...\n", buf); -#endif - - if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { - ret = ERR_CONNECT; - close(s); - s = -1; - continue; - } - break; /* okay we got one */ - } - freeaddrinfo(res0); + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(80); + memcpy(&addr.sin_addr, he->h_addr, he->h_length); - if (s < 0) { - return ret; + if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(s); + return ERR_CONNECT; } char buf[4096]; - // use the hack to save some space in .rodata - strcpy(buf, "GET /"); - if (uri) { - strncat(buf, uri, sizeof(buf) - strlen(buf) - 1); - } - strncat(buf, " HTTP/1.0\r\nHost: ", sizeof(buf) - strlen(buf) - 1); - strncat(buf, hostname, sizeof(buf) - strlen(buf) - 1); - strncat(buf, "\r\n\r\n", sizeof(buf) - strlen(buf) - 1); - int tosent = strlen(buf); + char *p = bufcat(buf, "GET /"); + if (uri) + p = bufcat(p, uri); + p = bufcat(p, " HTTP/1.0\r\nHost: "); + p = bufcat(p, hostname); + p = bufcat(p, "\r\n\r\n"); + int tosent = p - buf; int nsent = send(s, buf, tosent, 0); if (nsent != tosent) return ERR_SEND; @@ -157,7 +131,7 @@ int main(int argc, char **argv) { wait(&wstatus); ret = WEXITSTATUS(wstatus); } else { - execlp(temfname, temfname, (char *)NULL); + execl(temfname, temfname, (char *)NULL); return EXIT_FAILURE; } }