mirror of
https://github.com/ps5-payload-dev/elfldr.git
synced 2026-10-19 02:19:52 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02cfe91eb3 | ||
|
|
54966675d3 | ||
|
|
1dc9e6813b | ||
|
|
5de9433fc9 | ||
|
|
da71d1e85b | ||
|
|
81ed676301 | ||
|
|
699e8bcff0 | ||
|
|
9a72acabde | ||
|
|
8d6caf7862 | ||
|
|
78902735dc | ||
|
|
66fb5cd4ef | ||
|
|
b0bdb2ad3d | ||
|
|
96b225f7c6 | ||
|
|
8e575ad9d4 | ||
|
|
417e73b43e | ||
|
|
acf67e8b3d | ||
|
|
19f5dc8ac8 |
No files matched your search
@@ -24,37 +24,29 @@ else
|
||||
endif
|
||||
|
||||
CFLAGS := -Wall -Werror -O1
|
||||
LDADD := -lSceSsl -lSceHttp
|
||||
|
||||
all: elfldr-ps5.elf
|
||||
|
||||
socksrv_elf.c: socksrv.elf
|
||||
bootstrap_elf.c: bootstrap.elf
|
||||
|
||||
bootstrap.o: socksrv_elf.c
|
||||
main.o: bootstrap_elf.c
|
||||
bootstrap.o: socksrv.elf
|
||||
main.o: bootstrap.elf
|
||||
|
||||
bootstrap.elf: bootstrap.o elfldr.o pt.o notify.o
|
||||
$(CC) -o $@ $^
|
||||
$(STRIP) $@
|
||||
|
||||
bootstrap_elf.c: bootstrap.elf
|
||||
xxd -i $^ > $@
|
||||
|
||||
socksrv.elf: socksrv.o elfldr.o pt.o notify.o
|
||||
$(CC) -o $@ $^
|
||||
socksrv.elf: socksrv.o selfldr.o elfldr.o pt.o notify.o uri.o
|
||||
$(CC) -o $@ $^ $(LDADD)
|
||||
$(STRIP) $@
|
||||
|
||||
socksrv_elf.c: socksrv.elf
|
||||
xxd -i $^ > $@
|
||||
|
||||
elfldr-ps5.elf: main.o elfldr.o pt.o notify.o
|
||||
$(CC) -o $@ $^
|
||||
$(STRIP) $@
|
||||
|
||||
clean:
|
||||
rm -f bootstrap_elf.c socksrv_elf.c *.o *.elf
|
||||
rm -f *.o *.elf
|
||||
|
||||
test: elfldr-ps5.elf
|
||||
$(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $^
|
||||
|
||||
.INTERMEDIATE: socksrv_elf.c socksrv.elf bootstrap_elf.c bootstrap.elf
|
||||
.INTERMEDIATE: socksrv.elf bootstrap.elf
|
||||
@@ -14,7 +14,25 @@ a rudimentary ELF loader on port 9020, e.g., [BD-J ps5-payload-loader][bdj],
|
||||
or the [webkit approached from Specter][webkit]. Then, run the following:
|
||||
```console
|
||||
john@localhost:~$ export PS5_HOST=ps5
|
||||
john@localhost:~$ wget -q -O - https://github.com/ps5-payload-dev/elfldr/releases/latest/download/Payload.zip | gunzip -c -d | nc -q0 $PS5_HOST 9020
|
||||
john@localhost:~$ export PS5_PORT=9020
|
||||
john@localhost:~$ wget -q -O - https://github.com/ps5-payload-dev/elfldr/releases/latest/download/elfldr-ps5.elf | nc -q0 $PS5_HOST $PS5_PORT
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
```console
|
||||
john@localhost:~$ export PS5_HOST=ps5
|
||||
john@localhost:~$ export PS5_PORT=9021
|
||||
john@localhost:~$ export PS5_PAYLOAD=payload.elf # from local storage
|
||||
john@localhost:~$ nc -q0 $PS5_HOST $PS5_PORT < $PS5_PAYLOAD
|
||||
john@localhost:~$
|
||||
john@localhost:~$ export PS5_PAYLOAD=file:/data/payload.elf # from console storage
|
||||
john@localhost:~$ echo "$PS5_PAYLOAD" | nc -q0 $PS5_HOST $PS5_PORT
|
||||
john@localhost:~$
|
||||
john@localhost:~$ export PS5_PAYLOAD=https://github.com/ps5-payload-dev/ftpsrv/releases/latest/download/ftpsrv-ps5.elf
|
||||
john@localhost:~$ echo "$PS5_PAYLOAD" | nc -q0 $PS5_HOST $PS5_PORT
|
||||
john@localhost:~$
|
||||
john@localhost:~$ export PS5_PAYLOAD=https://github.com/ps5-payload-dev/ftpsrv/releases/latest/download/ftpsrv-ps5.elf
|
||||
john@localhost:~$ curl "http://$PS5_HOST:$PS5_PORT?uri=$PS5_PAYLOAD"
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2026 John Törnblom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#define INCASSET(name, file) \
|
||||
__asm__(".section .rodata\n" \
|
||||
".global " #name "\n" \
|
||||
".global " #name "_end\n" \
|
||||
".global " #name "_size\n" \
|
||||
".align 16\n" \
|
||||
#name ":\n" \
|
||||
".incbin \"" file "\"\n" \
|
||||
#name "_end:\n" \
|
||||
#name "_size:\n" \
|
||||
".quad " #name "_end - " #name "\n" \
|
||||
".previous\n"); \
|
||||
extern const unsigned char name[]; \
|
||||
extern const unsigned long name##_size;
|
||||
+9
-3
@@ -19,13 +19,19 @@ along with this program; see the file COPYING. If not, see
|
||||
|
||||
#include <ps5/kernel.h>
|
||||
|
||||
#include "asset.h"
|
||||
#include "elfldr.h"
|
||||
#include "log.h"
|
||||
#include "notify.h"
|
||||
#include "pt.h"
|
||||
|
||||
INCASSET(socksrv_elf, "socksrv.elf");
|
||||
|
||||
#include "socksrv_elf.c"
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static char* const argv[] = {"elfldr.elf", 0};
|
||||
|
||||
|
||||
/**
|
||||
@@ -42,7 +48,7 @@ main() {
|
||||
notify("Spawning elfldr.elf...");
|
||||
LOG_PUTS("Spawning elfldr.elf...");
|
||||
|
||||
if(elfldr_sanity_check(socksrv_elf, socksrv_elf_len)) {
|
||||
if(elfldr_sanity_check(socksrv_elf, socksrv_elf_size)) {
|
||||
LOG_PUTS("socksrv.elf is corrupted");
|
||||
return -1;
|
||||
}
|
||||
@@ -67,7 +73,7 @@ main() {
|
||||
ret = -1;
|
||||
} else {
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
ret = elfldr_spawn("elfldr.elf", -1, socksrv_elf);
|
||||
ret = elfldr_spawn(-1, argv, socksrv_elf, socksrv_elf_size);
|
||||
}
|
||||
|
||||
// restore my privileges
|
||||
|
||||
@@ -59,8 +59,8 @@ along with this program; see the file COPYING. If not, see
|
||||
* Context structure for the ELF loader.
|
||||
**/
|
||||
typedef struct elfldr_ctx {
|
||||
uint8_t* elf;
|
||||
pid_t pid;
|
||||
const uint8_t* elf;
|
||||
pid_t pid;
|
||||
|
||||
intptr_t base_addr;
|
||||
size_t base_size;
|
||||
@@ -78,7 +78,7 @@ static const char* SceSpZeroConf = "/system/vsh/app/NPXS40112/eboot.bin";
|
||||
* Parse a R_X86_64_RELATIVE relocatable.
|
||||
**/
|
||||
static int
|
||||
r_relative(elfldr_ctx_t *ctx, Elf64_Rela* rela) {
|
||||
r_relative(elfldr_ctx_t *ctx, const Elf64_Rela* rela) {
|
||||
intptr_t* loc = ctx->base_mirror + rela->r_offset;
|
||||
intptr_t val = ctx->base_addr + rela->r_addend;
|
||||
|
||||
@@ -92,7 +92,7 @@ r_relative(elfldr_ctx_t *ctx, Elf64_Rela* rela) {
|
||||
* Parse a PT_LOAD program header.
|
||||
**/
|
||||
static int
|
||||
data_load(elfldr_ctx_t *ctx, Elf64_Phdr *phdr) {
|
||||
data_load(elfldr_ctx_t *ctx, const Elf64_Phdr *phdr) {
|
||||
void* data = ctx->base_mirror + phdr->p_vaddr;
|
||||
|
||||
if(!phdr->p_memsz) {
|
||||
@@ -110,9 +110,9 @@ data_load(elfldr_ctx_t *ctx, Elf64_Phdr *phdr) {
|
||||
|
||||
|
||||
int
|
||||
elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
|
||||
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf;
|
||||
Elf64_Phdr *phdr;
|
||||
elfldr_sanity_check(const uint8_t *elf, size_t elf_size) {
|
||||
const Elf64_Ehdr *ehdr = (const Elf64_Ehdr*)elf;
|
||||
const Elf64_Phdr *phdr;
|
||||
|
||||
if(elf_size < sizeof(Elf64_Ehdr) ||
|
||||
elf_size < sizeof(Elf64_Phdr) + ehdr->e_phoff ||
|
||||
@@ -125,7 +125,7 @@ elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
phdr = (Elf64_Phdr*)(elf + ehdr->e_phoff);
|
||||
phdr = (const Elf64_Phdr*)(elf + ehdr->e_phoff);
|
||||
for(int i=0; i<ehdr->e_phnum; i++) {
|
||||
if(phdr[i].p_offset + phdr[i].p_filesz > elf_size) {
|
||||
return -1;
|
||||
@@ -140,10 +140,10 @@ elfldr_sanity_check(uint8_t *elf, size_t elf_size) {
|
||||
* Load an ELF into the address space of a process with the given pid.
|
||||
**/
|
||||
static intptr_t
|
||||
elfldr_load(pid_t pid, uint8_t *elf) {
|
||||
Elf64_Ehdr *ehdr = (Elf64_Ehdr*)elf;
|
||||
Elf64_Phdr *phdr = (Elf64_Phdr*)(elf + ehdr->e_phoff);
|
||||
Elf64_Shdr *shdr = (Elf64_Shdr*)(elf + ehdr->e_shoff);
|
||||
elfldr_load(pid_t pid, const uint8_t *elf) {
|
||||
const Elf64_Ehdr *ehdr = (const Elf64_Ehdr*)elf;
|
||||
const Elf64_Phdr *phdr = (const Elf64_Phdr*)(elf + ehdr->e_phoff);
|
||||
const Elf64_Shdr *shdr = (const Elf64_Shdr*)(elf + ehdr->e_shoff);
|
||||
|
||||
elfldr_ctx_t ctx = {.elf = elf, .pid=pid};
|
||||
|
||||
@@ -207,7 +207,7 @@ elfldr_load(pid_t pid, uint8_t *elf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Elf64_Rela* rela = (Elf64_Rela*)(elf + shdr[i].sh_offset);
|
||||
const Elf64_Rela* rela = (const Elf64_Rela*)(elf + shdr[i].sh_offset);
|
||||
for(int j=0; j<shdr[i].sh_size/sizeof(Elf64_Rela); j++) {
|
||||
switch(rela[j].r_info & 0xffffffffl) {
|
||||
case R_X86_64_RELATIVE:
|
||||
@@ -348,7 +348,7 @@ elfldr_payload_args(pid_t pid) {
|
||||
* Prepare registers of a process for execution of an ELF.
|
||||
**/
|
||||
static int
|
||||
elfldr_prepare_exec(pid_t pid, uint8_t *elf) {
|
||||
elfldr_prepare_exec(pid_t pid, const uint8_t *elf) {
|
||||
intptr_t entry;
|
||||
intptr_t args;
|
||||
struct reg r;
|
||||
@@ -437,7 +437,7 @@ elfldr_raise_privileges(pid_t pid) {
|
||||
* Execute an ELF inside the process with the given pid.
|
||||
**/
|
||||
int
|
||||
elfldr_exec(pid_t pid, int stdio, uint8_t* elf) {
|
||||
elfldr_exec(pid_t pid, int stdio, const uint8_t* elf) {
|
||||
uint8_t caps[16];
|
||||
intptr_t jaildir;
|
||||
intptr_t rootdir;
|
||||
@@ -567,9 +567,7 @@ sys_budget_set(long budget) {
|
||||
|
||||
|
||||
static int
|
||||
elfldr_rfork_entry(void* progname) {
|
||||
char* const argv[] = {(char*)progname, 0};
|
||||
|
||||
elfldr_rfork_entry(void* argv) {
|
||||
if(sys_budget_set(0)) {
|
||||
klog_perror("sys_budget_set");
|
||||
return -1;
|
||||
@@ -602,8 +600,7 @@ elfldr_rfork_entry(void* progname) {
|
||||
* Execute an ELF inside a new process.
|
||||
**/
|
||||
pid_t
|
||||
elfldr_spawn(const char* progname, int stdio, uint8_t* elf) {
|
||||
|
||||
elfldr_spawn(int stdio, char* const argv[], const uint8_t* elf, size_t payload_size) {
|
||||
uint8_t int3instr = 0xcc;
|
||||
struct kevent evt;
|
||||
intptr_t brkpoint;
|
||||
@@ -624,7 +621,7 @@ elfldr_spawn(const char* progname, int stdio, uint8_t* elf) {
|
||||
}
|
||||
|
||||
if((pid=rfork_thread(RFPROC | RFCFDG | RFMEM, stack+PAGE_SIZE-8,
|
||||
elfldr_rfork_entry, (void*)progname)) < 0) {
|
||||
elfldr_rfork_entry, (void*)argv)) < 0) {
|
||||
LOG_PERROR("rfork_thread");
|
||||
free(stack);
|
||||
close(kq);
|
||||
@@ -704,7 +701,7 @@ elfldr_spawn(const char* progname, int stdio, uint8_t* elf) {
|
||||
}
|
||||
|
||||
// Execute the ELF
|
||||
elfldr_set_procname(pid, progname);
|
||||
elfldr_set_procname(pid, argv[0]);
|
||||
if(elfldr_exec(pid, stdio, elf)) {
|
||||
kill(pid, SIGKILL);
|
||||
return -1;
|
||||
|
||||
@@ -18,12 +18,33 @@ along with this program; see the file COPYING. If not, see
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
/**
|
||||
* Find the id of a process with the given name.
|
||||
**/
|
||||
pid_t elfldr_find_pid(const char* name);
|
||||
|
||||
pid_t elfldr_spawn(const char* progname, int stdio, uint8_t *elf);
|
||||
int elfldr_exec(pid_t pid, int stdio, uint8_t* elf);
|
||||
|
||||
/**
|
||||
* Spawn a new process that executes the given ELF file.
|
||||
**/
|
||||
pid_t elfldr_spawn(int stdio, char* const argv[], const uint8_t *elf,
|
||||
size_t elf_size);
|
||||
|
||||
|
||||
/**
|
||||
* Execute an ELF file in a given process.
|
||||
**/
|
||||
int elfldr_exec(pid_t pid, int stdio, const uint8_t* elf);
|
||||
|
||||
|
||||
/**
|
||||
* Read an ELF from the given socket.
|
||||
**/
|
||||
int elfldr_read(int fd, uint8_t** elf, size_t* elf_size);
|
||||
int elfldr_sanity_check(uint8_t *elf, size_t elf_size);
|
||||
|
||||
|
||||
int elfldr_sanity_check(const uint8_t *elf, size_t elf_size);
|
||||
|
||||
|
||||
int elfldr_raise_privileges(pid_t pid);
|
||||
@@ -16,12 +16,13 @@ along with this program; see the file COPYING. If not, see
|
||||
|
||||
#include <ps5/kernel.h>
|
||||
|
||||
#include "asset.h"
|
||||
#include "elfldr.h"
|
||||
#include "log.h"
|
||||
#include "notify.h"
|
||||
#include "pt.h"
|
||||
|
||||
#include "bootstrap_elf.c"
|
||||
INCASSET(bootstrap_elf, "bootstrap.elf");
|
||||
|
||||
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ main() {
|
||||
notify("Bootstrapping elfldr.elf...");
|
||||
LOG_PUTS("Bootstrapping elfldr.elf...");
|
||||
|
||||
if(elfldr_sanity_check(bootstrap_elf, bootstrap_elf_len)) {
|
||||
if(elfldr_sanity_check(bootstrap_elf, bootstrap_elf_size)) {
|
||||
LOG_PUTS("bootstrap.elf is corrupted");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/* Copyright (C) 2025 John Törnblom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <ps5/klog.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "selfldr.h"
|
||||
|
||||
|
||||
/**
|
||||
* Data structure for SELF headers.
|
||||
**/
|
||||
typedef struct self_head {
|
||||
uint32_t magic;
|
||||
uint8_t version;
|
||||
uint8_t mode;
|
||||
uint8_t endian;
|
||||
uint8_t attrs;
|
||||
uint32_t key_type;
|
||||
uint16_t header_size;
|
||||
uint16_t meta_size;
|
||||
uint64_t file_size;
|
||||
uint16_t num_entries;
|
||||
uint16_t flags;
|
||||
} self_head_t;
|
||||
|
||||
|
||||
/**
|
||||
* Prototype for arguments passed to selfldr_rfork_entry().
|
||||
**/
|
||||
typedef struct self_spawn_args {
|
||||
int stdio;
|
||||
uint8_t *self;
|
||||
size_t self_size;
|
||||
char* const* argv;
|
||||
} self_spawn_args_t;
|
||||
|
||||
|
||||
/**
|
||||
* Duplicate the file descriptor from the given process.
|
||||
**/
|
||||
static int
|
||||
rdup(pid_t pid, int fd) {
|
||||
int err;
|
||||
|
||||
if((err = syscall(0x25b, pid, fd)) < 0) {
|
||||
errno = -err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Entry point for the forked process.
|
||||
**/
|
||||
static int
|
||||
selfldr_rfork_entry(void *ctx) {
|
||||
self_spawn_args_t *args = (self_spawn_args_t *)ctx;
|
||||
char *const envp[] = {0};
|
||||
pid_t ppid = getppid();
|
||||
char path[PATH_MAX];
|
||||
int fd;
|
||||
|
||||
if(syscall(0x23b, 0)) {
|
||||
klog_perror("sys_budget_set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(rdup(ppid, args->stdio) < 0) {
|
||||
klog_perror("rdup");
|
||||
return 0;
|
||||
}
|
||||
if(rdup(ppid, args->stdio) < 0) {
|
||||
klog_perror("rdup");
|
||||
return 0;
|
||||
}
|
||||
if(rdup(ppid, args->stdio) < 0) {
|
||||
klog_perror("rdup");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(path, "/user/temp/payload_%d.self", getpid());
|
||||
if((fd=open(path, O_WRONLY | O_CREAT | O_TRUNC, 0755)) < 0) {
|
||||
klog_perror("open");
|
||||
return 0;
|
||||
}
|
||||
if(write(fd, args->self, args->self_size) != args->self_size) {
|
||||
klog_perror("write");
|
||||
return 0;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
execve(path, args->argv, envp);
|
||||
perror("execve");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
selfldr_spawn(int stdio, char* const argv[], uint8_t *self, size_t self_size) {
|
||||
self_spawn_args_t args = {stdio, self, self_size, argv};
|
||||
struct kevent evt;
|
||||
void *stack;
|
||||
pid_t pid;
|
||||
int kq;
|
||||
|
||||
if((kq=kqueue()) < 0) {
|
||||
LOG_PERROR("kqueue");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(stack=malloc(PAGE_SIZE))) {
|
||||
LOG_PERROR("malloc");
|
||||
close(kq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((pid=rfork_thread(RFPROC | RFCFDG | RFMEM, stack+PAGE_SIZE-8,
|
||||
selfldr_rfork_entry, &args)) < 0) {
|
||||
LOG_PERROR("rfork_thread");
|
||||
free(stack);
|
||||
close(kq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
EV_SET(&evt, pid, EVFILT_PROC, EV_ADD, NOTE_EXEC | NOTE_EXIT, 0, 0);
|
||||
if(kevent(kq, &evt, 1, &evt, 1, 0) < 0) {
|
||||
LOG_PERROR("kevent");
|
||||
free(stack);
|
||||
close(kq);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(stack);
|
||||
close(kq);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
selfldr_read(int fd, uint8_t **self, size_t *self_size) {
|
||||
self_head_t head;
|
||||
uint8_t *buf;
|
||||
|
||||
if(recv(fd, &head, sizeof(head), MSG_PEEK | MSG_WAITALL) != sizeof(head)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(buf=malloc(head.file_size))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(recv(fd, buf, head.file_size, MSG_WAITALL) != head.file_size) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*self = buf;
|
||||
*self_size = head.file_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2025 John Törnblom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Read a SELF from the given socket.
|
||||
**/
|
||||
int selfldr_read(int fd, uint8_t **self, size_t *self_size);
|
||||
|
||||
|
||||
/**
|
||||
* Spawn a new process that executes the given SELF file.
|
||||
**/
|
||||
pid_t selfldr_spawn(int stdio, char* const argv[],
|
||||
uint8_t *self, size_t self_size);
|
||||
@@ -16,6 +16,7 @@ along with this program; see the file COPYING. If not, see
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <limits.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
@@ -30,29 +31,274 @@ along with this program; see the file COPYING. If not, see
|
||||
#include "elfldr.h"
|
||||
#include "log.h"
|
||||
#include "notify.h"
|
||||
#include "selfldr.h"
|
||||
#include "uri.h"
|
||||
|
||||
|
||||
/**
|
||||
* Process connections in induvidual threads.
|
||||
* Magic number that socket input starts with (little endian).
|
||||
**/
|
||||
#define PAYLOAD_MAGIC_ELF 0x464C457F // ELF payload
|
||||
#define PAYLOAD_MAGIC_URI_FILE 0x656C6966 // file:// URI
|
||||
#define PAYLOAD_MAGIC_URI_HTTP 0x70747468 // http:// or https:// URI
|
||||
#define PAYLOAD_MAGIC_PS4_SELF 0x1D3D154F // PS4 SELF payload
|
||||
#define PAYLOAD_MAGIC_PS5_SELF 0xEEF51454 // PS5 SELF payload
|
||||
#define PAYLOAD_MAGIC_HTTP_GET 0x20544547 // HTTP GET request
|
||||
|
||||
|
||||
/**
|
||||
* Decode an escaped argument.
|
||||
**/
|
||||
static char*
|
||||
args_decode(const char* s) {
|
||||
size_t length = strlen(s);
|
||||
char *arg = malloc(length+1);
|
||||
size_t off = 0;
|
||||
int escape = 0;
|
||||
|
||||
for(size_t i=0; i<length; i++) {
|
||||
if(s[i] == '\\' && !escape) {
|
||||
escape = 1;
|
||||
} else {
|
||||
arg[off++] = s[i];
|
||||
escape = 0;
|
||||
}
|
||||
}
|
||||
|
||||
arg[off] = 0;
|
||||
return arg;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static int
|
||||
args_split(const char* args, char** argv, size_t size) {
|
||||
char* buf = strdup(args);
|
||||
size_t len = strlen(buf);
|
||||
int escape = 0;
|
||||
int argc = 0;
|
||||
|
||||
memset(argv, 0, size*sizeof(char*));
|
||||
for(int i=0; i<len && argc<size; i++) {
|
||||
if(escape) {
|
||||
escape = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buf[i] == '\\') {
|
||||
escape = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buf[i] == ' ') {
|
||||
buf[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buf[i] && !i) {
|
||||
argv[argc++] = buf+i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(buf[i] && !buf[i-1]) {
|
||||
argv[argc++] = buf+i;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<argc; i++) {
|
||||
argv[i] = args_decode(argv[i]);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
return argc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Spawn an ELF or a SELF payload.
|
||||
**/
|
||||
static pid_t
|
||||
payload_spawn(char* filename, char* args, int fd,
|
||||
uint8_t* payload, size_t payload_size) {
|
||||
int magic = *((int*)payload);
|
||||
char* argv[255+2] = {0};
|
||||
pid_t pid = -1;
|
||||
|
||||
argv[0] = filename;
|
||||
args_split(args, argv+1, 255);
|
||||
|
||||
if(magic == PAYLOAD_MAGIC_ELF) {
|
||||
pid = elfldr_spawn(fd, argv, payload, payload_size);
|
||||
|
||||
} else if(magic == PAYLOAD_MAGIC_PS4_SELF || magic == PAYLOAD_MAGIC_PS5_SELF) {
|
||||
pid = selfldr_spawn(fd, argv, payload, payload_size);
|
||||
}
|
||||
|
||||
for(int i=1; argv[i]; i++) {
|
||||
free(argv[i]);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static int
|
||||
payload_readuri(int fd, char* uri, size_t size) {
|
||||
char c;
|
||||
int n;
|
||||
|
||||
for(int i=0; i<size; i++) {
|
||||
if((n=read(fd, &c, 1)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(n == 0) {
|
||||
uri[i] = 0;
|
||||
return 0;
|
||||
}
|
||||
if(c == '\r') {
|
||||
continue;
|
||||
}
|
||||
if(c == '\n') {
|
||||
uri[i] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uri[i] = c;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Read a HTTP (GET) request.
|
||||
**/
|
||||
static int
|
||||
payload_readhttp(int fd, char* uri, size_t size) {
|
||||
const char *headers = ("HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain; charset=utf-8\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n");
|
||||
char buf[PATH_MAX+255] = {0};
|
||||
char* param_uri;
|
||||
ssize_t n;
|
||||
char* p;
|
||||
|
||||
if((n=read(fd, buf, sizeof(buf)-1)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(memcmp(buf, "GET ", 4)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!(p=strchr(buf+4, ' '))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(strstr(buf, "user-agent: rnps-action-cards")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
|
||||
if((param_uri=uri_get_param(buf+4, "uri"))) {
|
||||
strncpy(uri, param_uri, size);
|
||||
free(param_uri);
|
||||
} else {
|
||||
snprintf(uri, size, "file:/%s", buf+4);
|
||||
}
|
||||
|
||||
write(fd, headers, strlen(headers));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process connection input.
|
||||
**/
|
||||
static void
|
||||
on_connection(int fd) {
|
||||
char* filename = "payload.elf";
|
||||
char uri[PATH_MAX+1] = {0};
|
||||
uint8_t* buf = 0;
|
||||
size_t len = 0;
|
||||
int optval = 1;
|
||||
uint8_t* elf;
|
||||
size_t len;
|
||||
char* args = "";
|
||||
char* pipe = "";
|
||||
int magic = 0;
|
||||
|
||||
if(setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0) {
|
||||
LOG_PERROR("setsockopt");
|
||||
return;
|
||||
}
|
||||
|
||||
if(elfldr_read(fd, &elf, &len)) {
|
||||
LOG_PERROR("elfldr_read");
|
||||
write(fd, "[elfldr.elf] Error reading ELF file\n\r\0", 38);
|
||||
} else {
|
||||
if(elfldr_spawn("payload.elf", fd, elf) < 0) {
|
||||
write(fd, "[elfldr.elf] Error running ELF file\n\r\0", 38);
|
||||
if(recv(fd, &magic, sizeof(magic), MSG_PEEK | MSG_WAITALL) != sizeof(magic)) {
|
||||
LOG_PERROR("recv");
|
||||
write(fd, "[elfldr.elf] Unknown payload format\n\r\0", 38);
|
||||
return;
|
||||
}
|
||||
|
||||
if(magic == PAYLOAD_MAGIC_URI_FILE || magic == PAYLOAD_MAGIC_URI_HTTP) {
|
||||
if(payload_readuri(fd, uri, PATH_MAX) || uri_get_content(uri, &buf, &len)) {
|
||||
LOG_PERROR("payload_readuri");
|
||||
write(fd, "[elfldr.elf] Error reading URI payload\n\r\0", 41);
|
||||
}
|
||||
free(elf);
|
||||
|
||||
} else if(magic == PAYLOAD_MAGIC_HTTP_GET) {
|
||||
if(payload_readhttp(fd, uri, PATH_MAX) || uri_get_content(uri, &buf, &len)) {
|
||||
LOG_PERROR("payload_readhttp");
|
||||
write(fd, "[elfldr.elf] Error reading HTTP payload\n\r\0", 42);
|
||||
}
|
||||
|
||||
} else if(magic == PAYLOAD_MAGIC_ELF) {
|
||||
if(elfldr_read(fd, &buf, &len)) {
|
||||
LOG_PERROR("elfldr_read");
|
||||
write(fd, "[elfldr.elf] Error reading ELF payload\n\r\0", 41);
|
||||
}
|
||||
|
||||
} else if(magic == PAYLOAD_MAGIC_PS4_SELF || magic == PAYLOAD_MAGIC_PS5_SELF) {
|
||||
if(selfldr_read(fd, &buf, &len)) {
|
||||
LOG_PERROR("selfldr_read");
|
||||
write(fd, "[elfldr.elf] Error reading SELF payload\n\r\0", 42);
|
||||
}
|
||||
} else {
|
||||
write(fd, "[elfldr.elf] Unknown payload format\n\r\0", 38);
|
||||
}
|
||||
|
||||
if(buf) {
|
||||
if(!(filename=uri_get_filename(uri))) {
|
||||
filename = strdup("payload.elf");
|
||||
}
|
||||
if(!(args=uri_get_param(uri, "args"))) {
|
||||
args = strdup("");
|
||||
}
|
||||
if(!(pipe=uri_get_param(uri, "pipe"))) {
|
||||
pipe = strdup("1");
|
||||
}
|
||||
|
||||
if(!strcmp(pipe, "0") ||
|
||||
!strcasecmp(pipe, "false") ||
|
||||
!strcasecmp(pipe, "no")) {
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
if(payload_spawn(filename, args, fd, buf, len) < 0) {
|
||||
write(fd, "[elfldr.elf] Error spawning payload\n\r\0", 38);
|
||||
}
|
||||
free(filename);
|
||||
free(pipe);
|
||||
free(args);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
/* Copyright (C) 2026 John Törnblom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
typedef struct http_ctx {
|
||||
int libnetMemId;
|
||||
int libsslCtxId;
|
||||
int libhttpCtxId;
|
||||
int tmplId;
|
||||
int connId;
|
||||
int reqId;
|
||||
} http_ctx_t;
|
||||
|
||||
|
||||
int sceNetInit();
|
||||
int sceNetPoolCreate(const char*, int, int);
|
||||
int sceNetPoolDestroy(int);
|
||||
|
||||
int sceSslInit(size_t);
|
||||
int sceSslTerm(int);
|
||||
|
||||
int sceHttpInit(int, int, size_t);
|
||||
int sceHttpTerm(int);
|
||||
|
||||
int sceHttpCreateTemplate(int, const char*, int, int);
|
||||
int sceHttpsSetSslCallback(int, void*, void*);
|
||||
int sceHttpSetResponseHeaderMaxSize(int, size_t);
|
||||
int sceHttpDeleteTemplate(int);
|
||||
|
||||
int sceHttpCreateConnectionWithURL(int, const char*, int);
|
||||
int sceHttpDeleteConnection(int);
|
||||
|
||||
int sceHttpCreateRequestWithURL(int, int, const char*, uint64_t);
|
||||
int sceHttpSendRequest(int, const void*, size_t);
|
||||
int sceHttpGetResponseContentLength(int, int*, uint64_t*);
|
||||
int sceHttpGetStatusCode(int, int*);
|
||||
int sceHttpReadData(int, void *, size_t);
|
||||
int sceHttpDeleteRequest(int);
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static int
|
||||
http_ssl_cb(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static int
|
||||
http_init(http_ctx_t* ctx, const char* agent, const char* url) {
|
||||
int err;
|
||||
|
||||
ctx->libnetMemId = -1;
|
||||
ctx->libsslCtxId = -1;
|
||||
ctx->libhttpCtxId = -1;
|
||||
ctx->tmplId = -1;
|
||||
ctx->reqId = -1;
|
||||
|
||||
if((err=sceNetInit()) < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if((ctx->libnetMemId=sceNetPoolCreate("http_get", 16*1024, 0)) < 0) {
|
||||
return ctx->libnetMemId;
|
||||
}
|
||||
|
||||
if((ctx->libsslCtxId=sceSslInit(128*1024)) < 0) {
|
||||
return ctx->libsslCtxId;
|
||||
}
|
||||
|
||||
if((ctx->libhttpCtxId=sceHttpInit(ctx->libnetMemId, ctx->libsslCtxId,
|
||||
32*1024)) < 0) {
|
||||
return ctx->libhttpCtxId;
|
||||
}
|
||||
|
||||
if((ctx->tmplId=sceHttpCreateTemplate(ctx->libhttpCtxId, agent, 2, 1)) < 0) {
|
||||
return ctx->tmplId;
|
||||
}
|
||||
|
||||
if((err=sceHttpSetResponseHeaderMaxSize(ctx->tmplId, 0x2000)) < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if((err=sceHttpsSetSslCallback(ctx->tmplId, http_ssl_cb, 0))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if((ctx->connId=sceHttpCreateConnectionWithURL(ctx->tmplId, url, 0)) < 0) {
|
||||
return ctx->connId;
|
||||
}
|
||||
|
||||
if((ctx->reqId=sceHttpCreateRequestWithURL(ctx->connId, 0, url, 0)) < 0) {
|
||||
return ctx->reqId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static int
|
||||
http_request(http_ctx_t* ctx, uint8_t** data, size_t* len) {
|
||||
int status = -1;
|
||||
int err;
|
||||
int n;
|
||||
|
||||
if((err=sceHttpSendRequest(ctx->reqId, 0, 0))) {
|
||||
return err;
|
||||
}
|
||||
if((err=sceHttpGetStatusCode(ctx->reqId, &status))) {
|
||||
return err;
|
||||
}
|
||||
if((err=sceHttpGetResponseContentLength(ctx->reqId, &err, len))) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if(!len || status != 200) {
|
||||
// TODO: support resources without a content length
|
||||
return status;
|
||||
}
|
||||
|
||||
if(!(*data=malloc(*len))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if((n=sceHttpReadData(ctx->reqId, *data, *len) < 0)) {
|
||||
return n;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static void
|
||||
http_fini(http_ctx_t* ctx) {
|
||||
if(ctx->reqId >= 0) {
|
||||
sceHttpDeleteRequest(ctx->reqId);
|
||||
}
|
||||
if(ctx->connId >= 0) {
|
||||
sceHttpDeleteConnection(ctx->connId);
|
||||
}
|
||||
if(ctx->tmplId >= 0) {
|
||||
sceHttpDeleteTemplate(ctx->tmplId);
|
||||
}
|
||||
if(ctx->libhttpCtxId >= 0) {
|
||||
sceHttpTerm(ctx->libhttpCtxId);
|
||||
}
|
||||
if(ctx->libsslCtxId >= 0) {
|
||||
sceSslTerm(ctx->libsslCtxId);
|
||||
}
|
||||
if(ctx->libnetMemId >= 0) {
|
||||
sceNetPoolDestroy(ctx->libnetMemId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
**/
|
||||
static uint8_t*
|
||||
http_get(const char* url, size_t* len) {
|
||||
http_ctx_t ctx;
|
||||
size_t size = 0;
|
||||
uint8_t* data;
|
||||
|
||||
if((http_init(&ctx, "elfldr.elf", url))) {
|
||||
http_fini(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(http_request(&ctx, &data, &size) != 200) {
|
||||
http_fini(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
http_fini(&ctx);;
|
||||
if(len) {
|
||||
*len = size;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a payload from he given URL.
|
||||
**/
|
||||
static int
|
||||
url_read(const char* url, uint8_t** content, size_t* content_size) {
|
||||
if((*content=http_get(url, content_size))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read a payload from given path.
|
||||
**/
|
||||
static int
|
||||
path_read(const char* path, uint8_t** content, size_t* content_size) {
|
||||
uint8_t* buf;
|
||||
ssize_t size;
|
||||
FILE* f;
|
||||
|
||||
if(!(f=fopen(path, "rb"))) {
|
||||
return -1;
|
||||
}
|
||||
if(fseek(f, 0, SEEK_END)) {
|
||||
return -1;
|
||||
}
|
||||
if((size=ftell(f)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if(fseek(f, 0, SEEK_SET)) {
|
||||
return -1;
|
||||
}
|
||||
if(!(buf=malloc(size))) {
|
||||
return -1;
|
||||
}
|
||||
if(fread(buf, 1, size, f) != size) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
if(fclose(f)) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*content = buf;
|
||||
*content_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
hexval(char c) {
|
||||
if(c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if(c >= 'a' && c <= 'f') {
|
||||
return c - 'a' + 10;
|
||||
}
|
||||
if(c >= 'A' && c <= 'F') {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
uri_decode(const char* src, size_t len) {
|
||||
char* out;
|
||||
char* dst;
|
||||
int lo;
|
||||
int hi;
|
||||
|
||||
if(!(out=malloc(len+1))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dst = out;
|
||||
for(size_t i=0; i<len; i++) {
|
||||
if(src[i] == '%' && i+2 < len) {
|
||||
hi = hexval(src[i+1]);
|
||||
lo = hexval(src[i+2]);
|
||||
if(hi >= 0 && lo >= 0) {
|
||||
*dst++ = (char)((hi << 4) | lo);
|
||||
i += 2;
|
||||
} else {
|
||||
*dst++ = src[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
*dst++ = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
*dst = 0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
uri_get_path(const char* uri) {
|
||||
size_t len = strlen(uri);
|
||||
const char* begin = 0;
|
||||
const char* end = 0;
|
||||
|
||||
end = uri + len;
|
||||
for(int i=0; i<len; i++) {
|
||||
if(!begin && uri[i] == ':' && uri[i+1] == '/') {
|
||||
begin = uri + i + 1;
|
||||
}
|
||||
else if(uri[i] == '?' || uri[i] == '#' || uri[i] == '\0') {
|
||||
end = uri + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return uri_decode(begin, end-begin);
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
uri_get_filename(const char* uri) {
|
||||
const char* begin = 0;
|
||||
const char* end = 0;
|
||||
size_t len;
|
||||
|
||||
if(!(len=strlen(uri))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
end = uri + len;
|
||||
for(int i=0; i<len; i++) {
|
||||
if(uri[i] == '/') {
|
||||
begin = uri + i + 1;
|
||||
}
|
||||
else if(uri[i] == '?' || uri[i] == '#' || uri[i] == '\0') {
|
||||
end = uri + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return uri_decode(begin, end-begin);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
uri_get_content(const char* uri, uint8_t** content, size_t* content_size) {
|
||||
int err = -1;
|
||||
char *path;
|
||||
|
||||
if(!strncmp(uri, "file:/", 6)) {
|
||||
if((path=uri_get_path(uri))) {
|
||||
err = path_read(path, content, content_size);
|
||||
free(path);
|
||||
}
|
||||
} else if(!strncmp(uri, "http:/", 6) || !strncmp(uri, "https:/", 7)) {
|
||||
err = url_read(uri, content, content_size);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
uri_get_param(const char* uri, const char* name) {
|
||||
const char* begin;
|
||||
const char* end;
|
||||
const char* p;
|
||||
size_t len;
|
||||
|
||||
if(!(p=strchr(uri, '?'))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
p++;
|
||||
len = strlen(name);
|
||||
|
||||
while(*p && *p != '#') {
|
||||
if(!strncmp(p, name, len) && p[len] == '=') {
|
||||
begin = p + len + 1;
|
||||
|
||||
if(!(end=strchr(begin, '&'))) {
|
||||
if(!(end=strchr(begin, '#'))) {
|
||||
end = begin + strlen(begin);
|
||||
}
|
||||
}
|
||||
|
||||
return uri_decode(begin, end-begin);
|
||||
}
|
||||
|
||||
while(*p && *p != '&' && *p != '#') {
|
||||
p++;
|
||||
}
|
||||
|
||||
if(*p == '&') {
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright (C) 2026 John Törnblom
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
/**
|
||||
* Read the content from the given URI. Supported protocols are:
|
||||
* - file://
|
||||
* - http://
|
||||
* - https://
|
||||
**/
|
||||
int uri_get_content(const char* uri, uint8_t** content, size_t* content_size);
|
||||
|
||||
|
||||
/**
|
||||
* Get the value of a named parameter associated with the given URI.
|
||||
**/
|
||||
char* uri_get_param(const char* uri, const char* name);
|
||||
|
||||
|
||||
/**
|
||||
* Get the filename associated with the given URI.
|
||||
**/
|
||||
char* uri_get_filename(const char* uri);
|
||||
Reference in new issue
Block a user