From c01fc3fb1065f3871d40fec4c911b4ac8a00dae8 Mon Sep 17 00:00:00 2001
From: SirHumza <204067870+SirHumza@users.noreply.github.com>
Date: Wed, 23 Sep 2026 16:56:25 +0200
Subject: [PATCH] WIP: SDK-port net branches + findings archive
---
findings/README.md | 14 +++++++++
findings/capture.md | 37 ++++++++++++++++++++++
findings/elf-linkage.md | 39 +++++++++++++++++++++++
findings/loader.md | 23 ++++++++++++++
findings/tls-ime.md | 45 ++++++++++++++++++++++++++
orbisrpc/compat.c | 31 ++++++++++++++++++
orbisrpc/detect.c | 20 ++++++++++++
orbisrpc/tls.c | 22 +++++++++++++
orbisrpc/tmdb.c | 67 ++++++++++++++++++++++++++++++++++++++-
orbisrpc/updater.c | 50 ++++++++++++++++++++++++++++-
orbisrpc/ws.c | 70 +++++++++++++++++++++++++++++++++++++++++
scripts/build_sdk.sh | 30 ++++++++++++++++++
12 files changed, 446 insertions(+), 2 deletions(-)
create mode 100644 findings/README.md
create mode 100644 findings/capture.md
create mode 100644 findings/elf-linkage.md
create mode 100644 findings/loader.md
create mode 100644 findings/tls-ime.md
create mode 100755 scripts/build_sdk.sh
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
#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
+#include
+#else
#include
#include
#include
+#endif
#include
#include
#include
@@ -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
#include
#include
+#include /* error codes only; transport is ours */
+#ifdef ORBISRPC_SDK_PAYLOAD
+#include
+#include
+#else
#include
+#endif
#include
#include
#include
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#else
#include
#include
#include
#include
#include
+#endif
#include
#include
#include
@@ -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
+#include
+#include
+#include
+#include
+#else
#include
#include
#include
#include
#include
+#endif
#include
#include
#include
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#else
#include
#include
#include
#include
#include
+#endif
#include
#include
#include
@@ -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"