diff --git a/findings/README.md b/findings/README.md new file mode 100644 index 0000000..3f13308 --- /dev/null +++ b/findings/README.md @@ -0,0 +1,14 @@ +# Findings archive + +Hardware-grounded research from bringing OrbisRPC up on a real PS4 +(9.00, GoldHEN 2.4). Written for humans and for AI assistants joining +later: every claim below was observed on-console unless marked THEORY. + +- [loader.md](loader.md) — GoldHEN payloader vs BinLoader server vs + elfldr: behaviors, crashes, one-shot rule, ports. +- [elf-linkage.md](elf-linkage.md) — why OpenOrbis-linked binaries die + instantly in spawned processes; NEEDED/UND analysis; SDK port status. +- [tls-ime.md](tls-ime.md) — mbedTLS 3.x PSA failure on-console (fixed), + IME dialog init failures (open), exact error codes seen. +- [capture.md](capture.md) — how to observe: klog, FTP paths, ports, + screenshot locations, what each log proves. diff --git a/findings/capture.md b/findings/capture.md new file mode 100644 index 0000000..6ef1473 --- /dev/null +++ b/findings/capture.md @@ -0,0 +1,37 @@ +# Observing the console (for assistants) + +## Channels + +| Channel | Address | Use | +|---|---|---| +| FTP | `192.168.1.136:2121` (anonymous) | files up/down | +| Kernel log | `192.168.1.136:3232` (TCP stream) | loader events, faults, crashes | +| BinLoader server | `192.168.1.136:9020` (one-shot per arm) | inject; NEVER probe first — an empty connect burns the arm, then fire blind | +| elfldr (if running) | `192.168.1.136:9021` | third-party ELF test harness | + +## Key paths (FTP) + +- `/data/orbisRPC/log.txt` — daemon log. Absent = payload never ran. +- `/data/orbisRPC/app.log` — setup-app + transport logs. +- `/data/orbisRPC/screen.log` — every dialog as text + answers. +- `/data/orbisRPC/diag.txt` — sanitized report (token structurally excluded). +- `/data/GoldHEN/payloads/` — autoloaded at jailbreak. +- `/data/payloads/` — Payload Guest scan dir. +- `/data/pkg/` — PKG staging. +- `/user/av_contents/photo/NPXS20001//*/*/*.jpg` — screenshots. +- `/user/data/sce_coredumps/` — crash dumps. + +## Proof markers + +- Daemon alive: `log.txt` exists with `daemon start` line. +- Network proven: `tls: established (TLSv1.2)` in app.log. +- September success marker: `gateway ready` + `presence:` lines. +- Loader segfault signature: `signal 11 (SIGSEGV), thread GoldHENLoader`, + fault address 0, right after `payload launched successfully`. + +## Rules learned the hard way + +1. Never port-check 9020/9090 before sending — fire blind on tap. +2. Klog is a live firehose: capture across the send window, not after. +3. `log.txt` absent always means "never executed", never "crashed later". +4. Rebuild artifacts go stale fast — verify hashes match before concluding. diff --git a/findings/elf-linkage.md b/findings/elf-linkage.md new file mode 100644 index 0000000..6e7ca93 --- /dev/null +++ b/findings/elf-linkage.md @@ -0,0 +1,39 @@ +# ELF linkage: why OpenOrbis binaries die in spawned processes + +## Observed + +- elfldr.elf (ps4-payload-sdk build): runs, serves, no faults. +- SDK hello_world + SDK getaddrinfo/file probe: run, notification shown, + `dns: PASS`. +- Every OpenOrbis-linked binary (hello 90KB, v0.4.0, 1.0 daemon): instant + silent death, no first log line, under both GoldHEN payloader and elfldr. + +## Structural comparison (llvm-readelf, verified locally) + +| | working (elfldr/sdk) | dying (ours) | +|---|---|---| +| NEEDED | libkernel_web.sprx, libSceLibcInternal.sprx, libSceNet.sprx | libkernel.so + app-world `.so` set | +| Segments | merged RWE LOADs, no RELRO | split R-X/RW, GNU_RELRO, GNU_STACK | +| Hash | GNU_HASH + SYSV HASH (both have both) | same | +| Relocations | GLOB_DAT / JUMP_SLOT / RELATIVE only | same families | + +elfldr spawns via `rfork_thread(RFPROC|RFCFDG|RFMEM)` then resolves each +import and SIGKILLs on the first unresolvable one — silent by design. +Our 89 undefined imports (vs ~26 working) include `libkernel.so` symbols +with no provider in a bare spawned process, so death occurs before `main`. + +## Consequence + +The daemon must be rebuilt against daemon-world linkage +(ps4-payload-sdk: `libkernel_web` + internal libc + `libSceNet`, BSD +sockets instead of Sony `orbis/Net.h`). Status: toolchain assembled on +macOS (llvm-shim + SDK bindist), SDK hello proven running, daemon port +in progress on the `WIP` branch (`ORBISRPC_SDK_PAYLOAD` build flavor; +`scripts/build_sdk.sh`). First SDK-linked daemon binary exhibits the same +quiet death — import-set bisect ongoing (getaddrinfo/DNS/file proven +working; remaining suspects: stdio-heavy and SceNet-derived imports). + +## Ruled out + +- PIE vs EXEC (both crash), SELF vs ELF (page wants ELF), size (90KB dies), + hash style, relocation families, segment permissions. diff --git a/findings/loader.md b/findings/loader.md new file mode 100644 index 0000000..40423a9 --- /dev/null +++ b/findings/loader.md @@ -0,0 +1,23 @@ +# Loader behavior (observed on PS4 9.00, GoldHEN 2.4) + +## The three loaders + +| Loader | Port | Behavior observed | +|---|---|---| +| GoldHEN settings-page payloader | 9090 (transient) | Accepts bytes, prints "payload launched successfully", then its own `GoldHENLoader` thread SIGSEGVs (null read). Payload never executes. Reproduced with 90KB hello-world, v0.4.0, 1.0 daemon. | +| GoldHEN BinLoader server | 9020 (one-shot per arm) | The September success path (`gateway ready` + live presence). Any port *check* (empty connect) burns the arm — fire blind, first connection carries the payload. | +| elfldr (ps4-payload-dev v0.7) | 9021 (serves until reboot) | Third-party ELF runs cleanly (bootstraps, serves, no faults). Our OpenOrbis-linked binaries die silently inside it. | + +## Proven facts + +- The 9090 segfault is in GoldHEN's loader thread, not the payload: + `signal 11 (SIGSEGV), thread GoldHENLoader, fault address 0`. +- ELF support in the settings-page loader needs GoldHEN ≥ v2.4b18.5; + older builds cannot load ELFs at all. +- 9020 takes bytes with zero kernel reaction when unarmed; refused when down. +- FTP (2121) and klog (3232) are the observation channels; see capture.md. + +## Open + +- Whether a newer GoldHEN (v2.4b18+) fixes the 9090 segfault. +- The exact GoldHEN build on the test console (About screen). diff --git a/findings/tls-ime.md b/findings/tls-ime.md new file mode 100644 index 0000000..0e9aeed --- /dev/null +++ b/findings/tls-ime.md @@ -0,0 +1,45 @@ +# TLS + IME console findings + +## TLS: mbedTLS 3.x PSA failure (FIXED, verified on hardware) + +Symptom: every handshake died instantly with return `-1` (not a real +mbedTLS code), after DNS + TCP succeeded. + +Root cause (from mbedTLS debug trace on-console): + +```text +psa_crypto_init() returned -148 (-0x0094, INSUFFICIENT_ENTROPY) +``` + +mbedTLS 3.x routes TLS 1.3 through the PSA crypto subsystem, whose init +fails on PS4 even though `/dev/urandom` reads work fine (ClientHello +random bytes prove it). + +Fix (in tree, verified: full handshake, `TLS-ECDHE-ECDSA-WITH-CHACHA20- +POLY1305-SHA256`, cert chain verifies, connection test PASSES): + +```c +mbedtls_ssl_conf_max_tls_version(&t->conf, MBEDTLS_SSL_VERSION_TLS1_2); +``` + +Lesson: wire mbedTLS's debug layer (`mbedtls_debug_set_threshold` + +`mbedtls_ssl_conf_dbg`) into the log before guessing at handshake bugs. + +## IME keyboard: init refuses (OPEN) + +`sceImeDialogInit` fails on-console; codes seen: + +- `-2135162872` (`0x80BC0008`) with `userId=0` +- `-2135162864` (`0x80BC0010`) with real foreground user ID + +Tried, none fixed it: real user ID via UserService, centered position +(960,540), default type/label, per-dialog init/terminate lifecycle, +stale-session teardown. Reference: Apollo PS4 dialog.c pattern mirrored. +Workaround in place: token pre-seeded via config; IME still unresolved. + +## App lifecycle (fixed, verified) + +- Missing `sceMsgDialogInitialize` → every open failed into a black loop. + Fixed per Apollo pattern (init + terminate per dialog). +- 30s dialog watchdog blanked idle screens — removed, blocking waits now. +- Exit path terminates dialogs, unloads modules, `exit(0)`. diff --git a/orbisrpc/compat.c b/orbisrpc/compat.c index 590e047..141a183 100644 --- a/orbisrpc/compat.c +++ b/orbisrpc/compat.c @@ -8,6 +8,35 @@ #include <stdint.h> #include "clock.h" +#ifdef ORBISRPC_SDK_PAYLOAD +/* Payload-SDK builds: the SDK libc already provides errno, gmtime_r, + * clock_gettime and friends. Only the entropy poll + monotonic clock + * live here, written against plain POSIX. */ +int mbedtls_platform_entropy_poll(void *data, unsigned char *out, + size_t len, size_t *olen){ + (void)data; + if(!out || len == 0) return -1; + int fd = open("/dev/urandom", O_RDONLY); + if(fd < 0) return -1; + size_t got = 0; + while(got < len){ + long r = read(fd, (char *)out + got, len - got); + if(r <= 0){ close(fd); return -1; } + got += (size_t)r; + } + close(fd); + *olen = len; + return 0; +} + +int64_t orbis_mono_s(void){ + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return (int64_t)time(NULL); + return (int64_t)ts.tv_sec; +} + +#else + int *__errno_location(void) __attribute__((weak)); int *__errno_location(void){ static int errno_slot; @@ -90,3 +119,5 @@ int64_t orbis_mono_s(void){ return (int64_t)ts.tv_sec; #endif } + +#endif /* ORBISRPC_SDK_PAYLOAD */ diff --git a/orbisrpc/detect.c b/orbisrpc/detect.c index 8b37cad..6a61b37 100644 --- a/orbisrpc/detect.c +++ b/orbisrpc/detect.c @@ -23,9 +23,16 @@ #include "sfo.h" #include "tmdb.h" #include "nametable.h" +#ifdef ORBISRPC_SDK_PAYLOAD +/* Payload-SDK build: dlopen/dlsym come from the SDK libc (dlfcn); + * there is no UserService here — user_init() below degrades. */ +#include <dlfcn.h> +#include <stdint.h> +#else #include <orbis/UserService.h> #include <orbis/libkernel.h> #include <orbis/Sysmodule.h> +#endif #include <unistd.h> #include <stdio.h> #include <string.h> @@ -40,6 +47,12 @@ static int s_user_ok = 0; static int user_init(void){ if(s_user_inited) return s_user_ok ? 0 : -1; s_user_inited = 1; +#ifdef ORBISRPC_SDK_PAYLOAD + /* No UserService in payload-SDK builds: ShellCoreUtil (dlopen) is the + * only foreground signal; without it we report inactive (fail closed). */ + log_msg("UserService unavailable in SDK build; ShellCoreUtil only"); + return -1; +#else /* UserService is an external module -> load via internal id */ uint32_t r = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_USER_SERVICE); if(r != 0){ int32_t ir=(int32_t)r; log_msg("load UserService fail %d", ir); return -1; } @@ -47,6 +60,7 @@ static int user_init(void){ if(rc != 0){ log_msg("UserService init fail %d", rc); return -1; } s_user_ok = 1; return 0; +#endif } /* --- ShellCoreUtil runtime resolution ------------------------------- */ @@ -76,10 +90,16 @@ int detect_foreground_active(void){ /* fallback: foreground user exists. If UserService itself failed, * report inactive instead of guessing "playing". */ if(user_init() != 0) return 0; +#ifdef ORBISRPC_SDK_PAYLOAD + /* No UserService API in SDK builds (user_init always fails there, + * so this is unreachable); fail closed regardless. */ + return 0; +#else int32_t fg = -1; int32_t rc = sceUserServiceGetForegroundUser(&fg); if(rc != 0){ log_msg("GetForegroundUser err %d", rc); return 0; } return (fg >= 0) ? 1 : 0; +#endif } /* --- title naming ---------------------------------------------------- */ diff --git a/orbisrpc/tls.c b/orbisrpc/tls.c index 9daaee6..0c9b033 100644 --- a/orbisrpc/tls.c +++ b/orbisrpc/tls.c @@ -18,7 +18,13 @@ #include <mbedtls/entropy.h> #include <mbedtls/ctr_drbg.h> #include <mbedtls/debug.h> +#include <mbedtls/net_sockets.h> /* error codes only; transport is ours */ +#ifdef ORBISRPC_SDK_PAYLOAD +#include <sys/socket.h> +#include <errno.h> +#else #include <orbis/Net.h> +#endif #include <string.h> #include <stdlib.h> #include <fcntl.h> @@ -72,17 +78,33 @@ static int orbis_poll(void *data, unsigned char *out, size_t len, size_t *olen){ static int net_send(void *ctx, const unsigned char *b, size_t n){ tls_ctx_t *t = (tls_ctx_t *)ctx; size_t cap = n > 32768 ? 32768 : n; +#ifdef ORBISRPC_SDK_PAYLOAD + int r = (int)send(t->fd, b, cap, 0); + if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return MBEDTLS_ERR_SSL_WANT_WRITE; + if(r < 0) return MBEDTLS_ERR_NET_SEND_FAILED; + return r; +#else int r = (int)sceNetSend(t->fd, b, (int)cap, 0); if(r < 0) return MBEDTLS_ERR_SSL_WANT_WRITE; /* NBIO: retry till deadline */ return r; +#endif } static int net_recv(void *ctx, unsigned char *b, size_t n){ tls_ctx_t *t = (tls_ctx_t *)ctx; size_t cap = n > 16384 ? 16384 : n; +#ifdef ORBISRPC_SDK_PAYLOAD + int r = (int)recv(t->fd, b, cap, 0); + if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return MBEDTLS_ERR_SSL_WANT_READ; + if(r < 0) return MBEDTLS_ERR_NET_RECV_FAILED; + return r; +#else int r = (int)sceNetRecv(t->fd, b, (int)cap, 0); if(r < 0) return MBEDTLS_ERR_SSL_WANT_READ; /* NBIO: retry till deadline */ return r; +#endif } tls_ctx_t *tls_start(int fd, const char *host){ diff --git a/orbisrpc/tmdb.c b/orbisrpc/tmdb.c index 3205696..a40c883 100644 --- a/orbisrpc/tmdb.c +++ b/orbisrpc/tmdb.c @@ -5,11 +5,21 @@ #include "clock.h" #include "tmdb_crypto.h" #include "log.h" +#ifdef ORBISRPC_SDK_PAYLOAD +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#else #include <orbis/Net.h> #include <orbis/Sysmodule.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> +#endif #include <string.h> #include <stdlib.h> #include <stdio.h> @@ -28,6 +38,11 @@ static int s_ready = 0; * init inside game processes. */ static int net_up(void){ if(s_ready) return 0; +#ifdef ORBISRPC_SDK_PAYLOAD + /* BSD sockets need no init. */ + s_ready = 1; + return 0; +#else uint32_t ur = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); if((int)ur < 0){ log_msg("tmdb: load NET fail %d", (int)ur); return -1; } if(sceNetInit() < 0){ log_msg("tmdb: sceNetInit fail"); return -1; } @@ -35,6 +50,7 @@ static int net_up(void){ if(s_pool < 0){ log_msg("tmdb: net pool fail %d", (int)s_pool); return -1; } s_ready = 1; return 0; +#endif } /* Minimal blocking HTTP GET with deadline. Returns body bytes, or -1. */ @@ -42,6 +58,29 @@ static int http_get(const char *host, const char *path, char *body, size_t cap, int *out_status){ if(out_status) *out_status = 0; if(net_up() < 0) return -1; +#ifdef ORBISRPC_SDK_PAYLOAD + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + { + char portbuf[16]; + snprintf(portbuf, sizeof portbuf, "%d", TMDB_PORT); + if(getaddrinfo(host, portbuf, &hints, &res) != 0 || !res){ + log_msg("tmdb: dns fail"); + return -1; + } + } + int fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd < 0){ log_msg("tmdb: socket fail"); freeaddrinfo(res); return -1; } + struct timeval tv = { .tv_sec = TMDB_DEADLINE_S, .tv_usec = 0 }; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + if(connect(fd, res->ai_addr, res->ai_addrlen) < 0){ + log_msg("tmdb: connect fail"); freeaddrinfo(res); close(fd); return -1; + } + freeaddrinfo(res); +#else OrbisNetInAddr in; memset(&in, 0, sizeof in); int ok = 0; @@ -73,17 +112,31 @@ static int http_get(const char *host, const char *path, if(sceNetConnect(fd, &sa, sizeof sa) < 0){ log_msg("tmdb: connect fail"); sceNetSocketClose(fd); return -1; } +#endif char req[512]; int rl = snprintf(req, sizeof req, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: Mozilla/5.0\r\nConnection: close\r\n\r\n", path, host); - if(rl <= 0 || rl >= (int)sizeof req){ sceNetSocketClose(fd); return -1; } + if(rl <= 0 || rl >= (int)sizeof req){ +#ifdef ORBISRPC_SDK_PAYLOAD + close(fd); +#else + sceNetSocketClose(fd); +#endif + return -1; + } int sent = 0; int64_t dl = orbis_mono_s() + TMDB_DEADLINE_S; while(sent < rl){ +#ifdef ORBISRPC_SDK_PAYLOAD + int r = (int)send(fd, req+sent, (size_t)(rl-sent), 0); + if(r > 0){ sent += r; continue; } + if(orbis_mono_s() > dl || r == 0){ close(fd); return -1; } +#else int r = sceNetSend(fd, req+sent, rl-sent, 0); if(r > 0){ sent += r; continue; } if(orbis_mono_s() > dl || r == 0){ sceNetSocketClose(fd); return -1; } +#endif } /* read headers then body; cap total */ char hb[2048]; @@ -92,7 +145,11 @@ static int http_get(const char *host, const char *path, size_t bl = 0; for(;;){ char tmp[1024]; +#ifdef ORBISRPC_SDK_PAYLOAD + int r = (int)recv(fd, tmp, sizeof tmp, 0); +#else int r = sceNetRecv(fd, tmp, sizeof tmp, 0); +#endif if(r <= 0) break; size_t off = 0; if(!hdr_done){ @@ -107,7 +164,11 @@ static int http_get(const char *host, const char *path, /* off = body bytes already in this chunk */ if(off > (size_t)r) off = (size_t)r; } else if(hl >= sizeof hb - 1){ +#ifdef ORBISRPC_SDK_PAYLOAD + close(fd); return -1; /* headers too big */ +#else sceNetSocketClose(fd); return -1; /* headers too big */ +#endif } else continue; } while(off < (size_t)r && bl < cap - 1){ @@ -116,7 +177,11 @@ static int http_get(const char *host, const char *path, if(bl >= cap - 1) break; if(orbis_mono_s() > dl) break; } +#ifdef ORBISRPC_SDK_PAYLOAD + close(fd); +#else sceNetSocketClose(fd); +#endif body[bl] = 0; if(out_status) *out_status = status; if(status != 200 || bl == 0) return -1; diff --git a/orbisrpc/updater.c b/orbisrpc/updater.c index f6eb927..2fb0fdd 100644 --- a/orbisrpc/updater.c +++ b/orbisrpc/updater.c @@ -20,11 +20,19 @@ /* ---- PS4 HTTPS transport (mirrors ws.c/tmdb.c bring-up) ---- */ #include "tls.h" +#ifdef ORBISRPC_SDK_PAYLOAD +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#else #include <orbis/Net.h> #include <orbis/Sysmodule.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> +#endif #include <sys/time.h> #include <time.h> #include <unistd.h> @@ -41,6 +49,10 @@ static int32_t s_upool = -1; static int upd_net(void){ static int ready = 0; if(ready) return 0; +#ifdef ORBISRPC_SDK_PAYLOAD + ready = 1; + return 0; +#else uint32_t ur = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); if((int)ur < 0) return -1; if(sceNetInit() < 0) return -1; @@ -48,10 +60,38 @@ static int upd_net(void){ if(s_upool < 0) return -1; ready = 1; return 0; +#endif } static int upd_connect(const char *host, int port){ if(upd_net() < 0) return -1; +#ifdef ORBISRPC_SDK_PAYLOAD + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + { + char portbuf[16]; + snprintf(portbuf, sizeof portbuf, "%d", port); + if(getaddrinfo(host, portbuf, &hints, &res) != 0 || !res){ + log_msg("updater: dns fail"); + return -1; + } + } + int fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd < 0){ freeaddrinfo(res); return -1; } + struct timeval tv = { .tv_sec = UPD_DEADLINE_S, .tv_usec = 0 }; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + if(connect(fd, res->ai_addr, res->ai_addrlen) < 0){ + close(fd); freeaddrinfo(res); return -1; + } + freeaddrinfo(res); + { + int fl = fcntl(fd, F_GETFL, 0); + if(fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK); + } + return fd; +#else OrbisNetInAddr in; memset(&in, 0, sizeof in); int32_t rid = sceNetResolverCreate("updR", (uint32_t)s_upool, 0); @@ -74,6 +114,7 @@ static int upd_connect(const char *host, int port){ int nb = 1; sceNetSetsockopt(fd, SOL_SOCKET, SO_NBIO, &nb, sizeof nb); return fd; +#endif } /* HTTPS GET, returns heap body (caller frees) with out_len/out_status. */ @@ -84,7 +125,14 @@ static char *https_get(const char *host, const char *path, int fd = upd_connect(host, 443); if(fd < 0) return NULL; tls_ctx_t *t = tls_start(fd, host); - if(!t){ sceNetSocketClose(fd); return NULL; } + if(!t){ +#ifdef ORBISRPC_SDK_PAYLOAD + close(fd); +#else + sceNetSocketClose(fd); +#endif + return NULL; + } char req[512]; int rl = snprintf(req, sizeof req, "GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: orbisRPC/%s\r\nConnection: close\r\n\r\n", diff --git a/orbisrpc/ws.c b/orbisrpc/ws.c index e2d2ca8..0a3ac34 100644 --- a/orbisrpc/ws.c +++ b/orbisrpc/ws.c @@ -8,11 +8,23 @@ #include "clock.h" #include "tls.h" #include "log.h" +#ifdef ORBISRPC_SDK_PAYLOAD +/* Payload-SDK build: plain BSD sockets, no Sony modules. getaddrinfo + * replaces the kernel resolver; nothing needs initializing. */ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#else #include <orbis/Net.h> #include <orbis/Sysmodule.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> +#endif #include <string.h> #include <stdlib.h> #include <unistd.h> @@ -31,6 +43,11 @@ static int s_net_mem = 0; static int net_ensure(void){ if(s_net_ready) return 0; +#ifdef ORBISRPC_SDK_PAYLOAD + /* BSD sockets need no init. */ + s_net_ready = 1; + return 0; +#else uint32_t ur = sceSysmoduleLoadModuleInternal(ORBIS_SYSMODULE_INTERNAL_NET); if((int)ur < 0){ log_msg("load NET fail %d", (int)ur); return -1; } if(sceNetInit() < 0){ log_msg("sceNetInit fail"); return -2; } @@ -41,6 +58,7 @@ static int net_ensure(void){ * a crash recipe. The kernel resolver works fine with just the pool. */ s_net_ready = 1; return 0; +#endif } /* Write exactly n bytes of plaintext through the TLS engine. */ @@ -62,6 +80,49 @@ int ws_connect(ws_t *w, const char *host, int port, const char *resource, const w->rbuf = (unsigned char*)malloc(w->rcap); if(!w->rbuf){ log_msg("ws: rbuf alloc fail"); return -1; } if(net_ensure()<0) goto fail; +#ifdef ORBISRPC_SDK_PAYLOAD + /* Resolve via libc (works wherever BSD sockets do). */ + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + { + char portbuf[16]; + snprintf(portbuf, sizeof portbuf, "%d", port); + if(getaddrinfo(host, portbuf, &hints, &res) != 0 || !res){ + log_msg("resolve fail: %s", host); + goto fail; + } + } + { + struct sockaddr_in *a = (struct sockaddr_in *)res->ai_addr; + unsigned char *q = (unsigned char *)&a->sin_addr.s_addr; + log_msg("dial %s -> %u.%u.%u.%u:%d", host, q[0], q[1], q[2], q[3], port); + } + int fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd < 0){ log_msg("socket fail"); freeaddrinfo(res); goto fail; } + w->fd = fd; w->sock = fd; + { + struct timeval tv = { .tv_sec = 10, .tv_usec = 0 }; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); + } + if(connect(fd, res->ai_addr, res->ai_addrlen) < 0){ + log_msg("connect fail (syscall) to %s:%d", host, port); + freeaddrinfo(res); + goto fail; + } + freeaddrinfo(res); + { + struct timeval tv0 = { .tv_sec = 0, .tv_usec = 0 }; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv0, sizeof tv0); + } + { + int fl = fcntl(fd, F_GETFL, 0); + if(fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK); + } + w->nb = 1; + log_msg("tcp established"); +#else /* memid = our net pool: passing 0 here fails with EBADF (0x80410109). * If pool creation failed, skip DNS and only allow IP literals. */ int32_t rid = -1; @@ -123,6 +184,7 @@ int ws_connect(ws_t *w, const char *host, int port, const char *resource, const sceNetSetsockopt(fd, SOL_SOCKET, SO_NBIO, &on, sizeof on); w->nb = 1; log_msg("tcp established"); +#endif /* TLS handshake over the established connection (mbedTLS, no external * module needed). Socket is NBIO; tls_start pumps it with a deadline. */ w->tls = tls_start(fd, host); @@ -177,7 +239,11 @@ int ws_connect(ws_t *w, const char *host, int port, const char *resource, const return 0; fail: tls_free((tls_ctx_t*)w->tls); w->tls=NULL; +#ifdef ORBISRPC_SDK_PAYLOAD + if(w->fd > 0) close(w->fd); +#else if(w->fd > 0) sceNetSocketClose(w->fd); +#endif free(w->rbuf); w->rbuf=NULL; w->rcap=0; w->connected=0; w->tls=NULL; w->fd=0; w->sock=0; return -9; @@ -331,7 +397,11 @@ int ws_close(ws_t *w){ } ws_send_control(w, 0x8); /* best-effort masked CLOSE */ tls_free((tls_ctx_t*)w->tls); +#ifdef ORBISRPC_SDK_PAYLOAD + close(w->fd); +#else sceNetSocketClose(w->fd); +#endif free(w->rbuf); w->rbuf=NULL; w->rcap=0; w->connected=0; w->sock=0; w->tls=NULL; w->fd=0; w->rlen=0; w->rpos=0; w->skip_left=0; diff --git a/scripts/build_sdk.sh b/scripts/build_sdk.sh new file mode 100755 index 0000000..9feb5b1 --- /dev/null +++ b/scripts/build_sdk.sh @@ -0,0 +1,30 @@ +#!/bin/sh +# build_sdk.sh - OrbisRPC daemon payload via ps4-payload-sdk (daemon-world +# linkage: no libkernel.so, BSD sockets, SDK libc). Test vehicle: elfldr. +# Usage: PS4_PAYLOAD_SDK=/path ./scripts/build_sdk.sh +set -e +SDK="${PS4_PAYLOAD_SDK:-$HOME/ps4-payload-sdk/ps4-payload-sdk}" +[ -x "$SDK/toolchain/../../ps4-payload-sdk/bin/orbis-clang" ] || { + # resolve relative to SDK root regardless of layout + true +} +CC="$SDK/bin/orbis-clang" +export PS4_PAYLOAD_SDK="$SDK" +export PATH="$HOME/llvmshim:$PATH" +OUT="build-sdk" +mkdir -p "$OUT" +CFLAGS="-O2 -Wall -DORBISRPC_SDK_PAYLOAD -Iorbisrpc -Ithird_party/mbedtls/include" +echo "=== daemon sources (SDK) ===" +for f in log cfg jsonlite b64 sfo tmdb_crypto tmdb updater updater_util tls ws detect discord daemon compat clock main; do + # clock has no .c (header-only helper lives in compat.c); skip if missing + [ -f "orbisrpc/$f.c" ] || continue + "$CC" $CFLAGS -c -o "$OUT/$f.o" "orbisrpc/$f.c" || { echo "FAIL: $f"; exit 1; } +done +echo "=== mbedtls (SDK, skip net_sockets/timing/entropy_poll) ===" +for f in $(find third_party/mbedtls/library -name '*.c' ! -name 'net_sockets.c' ! -name 'timing.c' ! -name 'entropy_poll.c' | sort); do + o="$OUT/mbed_$(echo "$f" | sed 's|third_party/mbedtls/library/||; s|\.c$||').o" + "$CC" $CFLAGS -Wno-unused-parameter -c -o "$o" "$f" || { echo "FAIL: mbedtls $f"; exit 1; } +done +echo "=== link ===" +"$CC" -o "$OUT/orbisrpc_sdk.elf" "$OUT"/*.o || { echo "FAIL: link"; exit 1; } +ls -la "$OUT/orbisrpc_sdk.elf"