mirror of
https://github.com/ps5-payload-dev/elfldr.git
synced 2026-10-19 02:19:52 -05:00
418 lines
7.5 KiB
C
418 lines
7.5 KiB
C
/* 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;
|
|
}
|