Compare commits

...
Author SHA1 Message Date
xXJsonDeruloXx af2783cb03 research: probe Valve Bionic Steam client IPC 2026-09-28 15:59:04 -04:00
7 changed files with 712 additions and 0 deletions

No files matched your search

+153
View File
@@ -0,0 +1,153 @@
# Bionic Steam client probe
This is a research harness for Valve's Android/Bionic ARM64 Steam client
libraries. It does **not** vendor or redistribute Valve binaries. The build
script fetches the current package from Valve's public Steam client-update
endpoint into a temporary working directory.
The question under test is narrow:
> Can Valve's `androidarm64/libsteamclient.so` run directly under Android's
> Bionic linker, and if so, what does it need in order to become a usable
> Steam client?
## Result
Yes, the library itself works directly on stock Android. It is not a complete
Steam desktop client, however. It is a Steamworks IPC client for a separate
full Steam process.
On 2026-09-28, the public-beta ARM64 client manifest contained
`bins_androidarm64_linuxarm64.zip.35688b00e791987ab70b1b6c6ddd006d4c864470`
with SHA-256:
`811859a83c7c5ff220ac9731235e76fd0d5da603a5cdb56fe5f08e145a417cbd`
The archive contains Android ARM64 builds of `libsteamclient.so`,
`libsteamnetworkingsockets.so`, `libtier0_s.so`, `libvstdlib_s.so`, and
`steamservice.so`.
## ABI evidence
`libsteamclient.so` is a genuine Android/NDK build, not a glibc ARM64 binary
with an Android-flavored directory name:
- ELF64 AArch64 shared object.
- Depends on `libandroid.so`, `liblog.so`, `libm.so`, `libdl.so`, and Bionic
`libc.so`.
- Uses the NDK C++ namespace (`std::__ndk1`).
- Carries `.note.android.ident` and Android LLVM/Clang build strings.
- Contains no `GLIBC_*` symbol-version requirements.
Android's stock linker on an AYN Thor loaded both Valve libraries with no
compatibility shim. `CreateInterface("SteamClient023")` also succeeded.
## What `steamservice.so` proved
`steamservice.so` exports `SteamService_StartThread`,
`SteamService_GetIPCServer`, `SteamService_Stop`, and
`SteamService_Shutdown`. A non-null service name starts its worker thread
successfully on Android and shuts down normally.
That service is **not** the Steamworks host expected by `libsteamclient.so`.
Starting it does not create the TCP IPC listener used by `CreateSteamPipe`.
The service probe is retained because it establishes that the Bionic service
code itself is runnable and documents the calling convention.
## The actual dependency: Steam IPC on TCP 57343
With no Steam host available, both the legacy exported C entry point and the
`SteamClient023` virtual `CreateSteamPipe` method return `0`.
An `LD_PRELOAD` trace of `socket()` and `connect()` showed why:
```text
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) -> fd
connect(fd, 127.0.0.1:57343) -> ECONNREFUSED
connect(fd, 127.0.0.1:57343) -> ECONNREFUSED
```
A dummy listener captured the client's first 17 bytes:
```text
0d 00 00 00 09 01 00 00 00 00 00 00 00 00 00 00 00
```
Port 57343 is Steam's long-standing local IPC listener. Valve's Lepton
Android compatibility environment uses the same Steam gateway for its
Android `libsteamclient.so`.
## End-to-end proof
Desktop Steam on a Mac was listening on host `127.0.0.1:57343`. We mapped
Android's loopback port to it temporarily with:
```sh
adb reverse tcp:57343 tcp:57343
```
With no local `steamservice.so` involved, the unmodified Bionic client on the
Thor then returned:
```text
SteamClient023=<non-null> rc=0
Steam_CreateSteamPipe=1
Steam_ConnectToGlobalUser=1
Steam_BConnected=1
Steam_BLoggedOn=1
Steam_BReleaseSteamPipe=1
```
This proves the Bionic client is operational on normal Android and can speak
the real cross-process Steam IPC protocol to an ordinary desktop Steam
process.
## Implication for DroidDeck / PRoot
This discovery does **not** by itself remove PRoot. The Android archive does
not contain the full Steam executable or `steamwebhelper`; the Bionic client
expects that full Steam process to exist elsewhere and own port 57343.
The useful architectural split is now proven rather than hypothetical:
```text
Android/Bionic workload
-> Valve Bionic libsteamclient.so
-> TCP Steam IPC :57343
-> full Steam host process
```
For a PRoot-free DroidDeck, the remaining problem is therefore the **host
Steam process**, not Steamworks ABI compatibility. Plausible future backends
are a real Linux environment (rooted chroot/namespaces), a VM/AVF guest with
a host bridge, or a future Valve Bionic build of the full client. Reimplementing
Steam's private IPC server should be treated as a last resort.
## Reproducing
Build and fetch Valve's current Android package into `/tmp`:
```sh
tools/research/bionic-steam-probe/fetch_build.sh
```
Run without a Steam host. This should resolve the Bionic interfaces but
return pipe/user handle `0`:
```sh
tools/research/bionic-steam-probe/run_probe.sh
```
If desktop Steam is running on the ADB host and listening on loopback, test
the real bridge:
```sh
tools/research/bionic-steam-probe/run_probe.sh --reverse-host-steam
```
The runner removes the ADB reverse mapping on exit. No Valve binary is copied
into this repository.
## Security note
Steam binds 57343 to loopback on desktop systems. Keep any DroidDeck bridge
equally constrained; do not expose this private Steam IPC endpoint to the LAN.
On Android all regular applications share the device network namespace, so a
future local host design should also consider how to prevent unrelated apps
from reaching a loopback-only Steam IPC listener.
+98
View File
@@ -0,0 +1,98 @@
#!/usr/bin/env bash
set -euo pipefail
MANIFEST_URL="${MANIFEST_URL:-https://client-update.steamstatic.com/steam_client_publicbeta_linuxarm64}"
WORKDIR="${WORKDIR:-${TMPDIR:-/tmp}/droiddeck-bionic-steam-probe}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
mkdir -p "$WORKDIR/android" "$WORKDIR/bin"
find_ndk() {
if [[ -n "${ANDROID_NDK_HOME:-}" ]]; then
printf '%s\n' "$ANDROID_NDK_HOME"
return
fi
if [[ -n "${ANDROID_NDK_ROOT:-}" ]]; then
printf '%s\n' "$ANDROID_NDK_ROOT"
return
fi
local sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Library/Android/sdk}}"
local ndk
ndk="$(find "$sdk/ndk" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -1)"
[[ -n "$ndk" ]] || return 1
printf '%s\n' "$ndk"
}
NDK="$(find_ndk)" || {
echo "Android NDK not found; set ANDROID_NDK_HOME" >&2
exit 1
}
HOST_TAG="linux-x86_64"
[[ "$(uname -s)" == Darwin ]] && HOST_TAG="darwin-x86_64"
CC="$NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin/aarch64-linux-android26-clang"
curl -fsSL "$MANIFEST_URL" -o "$WORKDIR/manifest.vdf"
PACKAGE="$(python3 - "$WORKDIR/manifest.vdf" <<'PY'
import re
import sys
text = open(sys.argv[1], encoding="utf-8").read()
m = re.search(
r'"bins_androidarm64_linuxarm64"\s*\{.*?"file"\s*"([^"]+)"',
text,
re.S,
)
if not m:
raise SystemExit("bins_androidarm64_linuxarm64 not found")
print(m.group(1))
PY
)"
PACKAGE_SHA="$(python3 - "$WORKDIR/manifest.vdf" <<'PY'
import re
import sys
text = open(sys.argv[1], encoding="utf-8").read()
block = re.search(r'"bins_androidarm64_linuxarm64"\s*\{(.*?)\n\s*\}', text, re.S)
if not block:
raise SystemExit("bins_androidarm64_linuxarm64 block not found")
m = re.search(r'"sha2"\s*"([0-9a-fA-F]{64})"', block.group(1))
if not m:
raise SystemExit("sha2 missing from Android ARM64 package")
print(m.group(1).lower())
PY
)"
echo "Valve package: $PACKAGE"
curl -fsSL "https://client-update.steamstatic.com/$PACKAGE" -o "$WORKDIR/android.zip"
ACTUAL_SHA="$(python3 - "$WORKDIR/android.zip" <<'PY'
import hashlib
import sys
print(hashlib.sha256(open(sys.argv[1], "rb").read()).hexdigest())
PY
)"
[[ "$ACTUAL_SHA" == "$PACKAGE_SHA" ]] || {
echo "SHA-256 mismatch: manifest=$PACKAGE_SHA download=$ACTUAL_SHA" >&2
exit 1
}
python3 - "$WORKDIR/android.zip" "$WORKDIR/android" <<'PY'
import os
import sys
import zipfile
archive, out = sys.argv[1:]
wanted = {
"libsteamclient.so",
"steamservice.so",
"libtier0_s.so",
"libvstdlib_s.so",
}
with zipfile.ZipFile(archive) as zf:
for name in zf.namelist():
base = os.path.basename(name)
if base in wanted:
target = os.path.join(out, base)
with open(target, "wb") as f:
f.write(zf.read(name))
print(f"extracted {name} -> {target}")
PY
"$CC" -O0 -g -Wall -Wextra "$SCRIPT_DIR/steamclient_probe.c" -ldl -o "$WORKDIR/bin/steamclient-probe"
"$CC" -O0 -g -Wall -Wextra "$SCRIPT_DIR/steamservice_probe.c" -ldl -o "$WORKDIR/bin/steamservice-probe"
"$CC" -O0 -g -Wall -Wextra "$SCRIPT_DIR/steamclient_service_probe.c" -ldl -o "$WORKDIR/bin/steamclient-service-probe"
"$CC" -shared -fPIC -O0 -g -Wall -Wextra "$SCRIPT_DIR/trace_hooks.c" -o "$WORKDIR/bin/trace-hooks.so"
echo "Built probes in $WORKDIR/bin"
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail
WORKDIR="${WORKDIR:-${TMPDIR:-/tmp}/droiddeck-bionic-steam-probe}"
ADB="${ADB:-adb}"
REMOTE="/data/local/tmp/droiddeck-bionic"
REVERSE=0
if [[ "${1:-}" == "--reverse-host-steam" ]]; then
REVERSE=1
elif [[ -n "${1:-}" ]]; then
echo "usage: $0 [--reverse-host-steam]" >&2
exit 2
fi
[[ -f "$WORKDIR/bin/steamclient-probe" ]] || {
echo "Run fetch_build.sh first" >&2
exit 1
}
[[ -f "$WORKDIR/android/libsteamclient.so" ]] || {
echo "Valve libsteamclient.so is missing from $WORKDIR" >&2
exit 1
}
"$ADB" shell "mkdir -p $REMOTE"
"$ADB" push "$WORKDIR/bin/steamclient-probe" "$REMOTE/steamclient-probe" >/dev/null
"$ADB" push "$WORKDIR/android/libsteamclient.so" "$REMOTE/libsteamclient.so" >/dev/null
"$ADB" shell "chmod 755 $REMOTE/steamclient-probe"
cleanup() {
if (( REVERSE )); then
"$ADB" reverse --remove tcp:57343 >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
if (( REVERSE )); then
"$ADB" reverse --remove tcp:57343 >/dev/null 2>&1 || true
"$ADB" reverse tcp:57343 tcp:57343 >/dev/null
echo "Mapped Android 127.0.0.1:57343 to host 127.0.0.1:57343."
echo "A desktop Steam process must already be listening on the host."
else
echo "No bridge configured; CreateSteamPipe should be 0 unless Steam already"
echo "exists in the Android network namespace."
fi
set +e
"$ADB" shell "$REMOTE/steamclient-probe $REMOTE/libsteamclient.so"
rc=$?
set -e
if (( REVERSE )); then
if (( rc == 0 )); then
echo "PASS: Bionic libsteamclient connected to host Steam."
else
echo "FAIL: Bionic libsteamclient did not obtain a Steam pipe." >&2
fi
fi
exit "$rc"
@@ -0,0 +1,73 @@
#include <dlfcn.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
typedef void *(*create_interface_fn)(const char *, int *);
typedef int32_t (*create_pipe_fn)(void);
typedef int32_t (*connect_user_fn)(int32_t);
typedef bool (*status_fn)(int32_t, int32_t);
typedef bool (*release_pipe_fn)(int32_t);
static void *lookup(void *handle, const char *name) {
dlerror();
void *ptr = dlsym(handle, name);
const char *error = dlerror();
printf("dlsym %-28s = %p", name, ptr);
if (error != NULL) printf(" error=%s", error);
putchar('\n');
return ptr;
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s LIBSTEAMCLIENT.SO\n", argv[0]);
return 2;
}
printf("pid=%d uid=%d\n", getpid(), getuid());
void *client = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
if (client == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return 3;
}
create_interface_fn create_interface =
(create_interface_fn)lookup(client, "CreateInterface");
create_pipe_fn create_pipe =
(create_pipe_fn)lookup(client, "Steam_CreateSteamPipe");
connect_user_fn connect_user =
(connect_user_fn)lookup(client, "Steam_ConnectToGlobalUser");
status_fn connected =
(status_fn)lookup(client, "Steam_BConnected");
status_fn logged_on =
(status_fn)lookup(client, "Steam_BLoggedOn");
release_pipe_fn release_pipe =
(release_pipe_fn)lookup(client, "Steam_BReleaseSteamPipe");
int rc = -1;
void *iface = create_interface != NULL
? create_interface("SteamClient023", &rc) : NULL;
printf("SteamClient023=%p rc=%d\n", iface, rc);
int32_t pipe = create_pipe != NULL ? create_pipe() : 0;
printf("Steam_CreateSteamPipe=%d\n", pipe);
int32_t user = (pipe != 0 && connect_user != NULL)
? connect_user(pipe) : 0;
printf("Steam_ConnectToGlobalUser=%d\n", user);
if (pipe != 0 && user != 0 && connected != NULL) {
printf("Steam_BConnected=%d\n", connected(user, pipe));
}
if (pipe != 0 && user != 0 && logged_on != NULL) {
printf("Steam_BLoggedOn=%d\n", logged_on(user, pipe));
}
if (pipe != 0 && release_pipe != NULL) {
printf("Steam_BReleaseSteamPipe=%d\n", release_pipe(pipe));
}
dlclose(client);
return pipe != 0 && user != 0 ? 0 : 4;
}
@@ -0,0 +1,146 @@
#include <dlfcn.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
typedef void *(*create_interface_fn)(const char *, int *);
typedef void *(*service_start_fn)(const char *);
typedef void *(*service_get_ipc_fn)(void);
typedef void (*service_void_fn)(void);
typedef int32_t (*create_pipe_fn)(void);
typedef int32_t (*connect_user_fn)(int32_t);
typedef bool (*connected_fn)(int32_t, int32_t);
typedef bool (*release_pipe_fn)(int32_t);
static void *must_sym(void *handle, const char *name) {
dlerror();
void *ptr = dlsym(handle, name);
const char *error = dlerror();
printf("dlsym %-28s = %p", name, ptr);
if (error != NULL) printf(" error=%s", error);
putchar('\n');
fflush(stdout);
return ptr;
}
static void dump_unix_sockets(const char *label) {
FILE *file = fopen("/proc/self/net/unix", "r");
if (file == NULL) return;
printf("--- UNIX sockets: %s ---\n", label);
char line[1024];
while (fgets(line, sizeof(line), file) != NULL) {
if (strstr(line, "steam") != NULL ||
strstr(line, "Steam") != NULL ||
strstr(line, "@") != NULL) {
fputs(line, stdout);
}
}
fclose(file);
fflush(stdout);
}
static void *open_library(const char *path) {
printf("dlopen(%s)\n", path);
fflush(stdout);
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
if (handle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
} else {
printf("dlopen ok handle=%p\n", handle);
}
return handle;
}
int main(int argc, char **argv) {
if (argc < 3) {
fprintf(stderr, "usage: %s STEAMSERVICE.SO LIBSTEAMCLIENT.SO [service_arg]\n",
argv[0]);
return 2;
}
const char *service_arg = argc >= 4 ? argv[3] : ".";
printf("pid=%d uid=%d service_arg=%s\n",
getpid(), getuid(), service_arg);
void *service = open_library(argv[1]);
if (service == NULL) return 3;
service_start_fn service_start =
(service_start_fn)must_sym(service, "SteamService_StartThread");
service_get_ipc_fn get_ipc =
(service_get_ipc_fn)must_sym(service, "SteamService_GetIPCServer");
service_void_fn stop =
(service_void_fn)must_sym(service, "SteamService_Stop");
service_void_fn shutdown =
(service_void_fn)must_sym(service, "SteamService_Shutdown");
printf("IPC before start=%p\n", get_ipc != NULL ? get_ipc() : NULL);
void *started = service_start != NULL ? service_start(service_arg) : NULL;
printf("StartThread=%p IPC after start=%p\n",
started, get_ipc != NULL ? get_ipc() : NULL);
fflush(stdout);
usleep(200000);
dump_unix_sockets("after service start");
void *client = open_library(argv[2]);
if (client == NULL) return 4;
create_interface_fn create_interface =
(create_interface_fn)must_sym(client, "CreateInterface");
create_pipe_fn create_pipe =
(create_pipe_fn)must_sym(client, "Steam_CreateSteamPipe");
connect_user_fn connect_user =
(connect_user_fn)must_sym(client, "Steam_ConnectToGlobalUser");
connected_fn connected =
(connected_fn)must_sym(client, "Steam_BConnected");
connected_fn logged_on =
(connected_fn)must_sym(client, "Steam_BLoggedOn");
release_pipe_fn release_pipe =
(release_pipe_fn)must_sym(client, "Steam_BReleaseSteamPipe");
int rc = -999;
void *steam_client = create_interface != NULL
? create_interface("SteamClient023", &rc) : NULL;
printf("CreateInterface SteamClient023 -> %p rc=%d\n",
steam_client, rc);
int32_t adapter_pipe = 0;
if (steam_client != NULL) {
void **vtable = *(void ***)steam_client;
int32_t (*adapter_create_pipe)(void *) =
(int32_t (*)(void *))vtable[0];
printf("SteamClient023 vtable=%p CreateSteamPipe=%p\n",
vtable, (void *)adapter_create_pipe);
adapter_pipe = adapter_create_pipe(steam_client);
printf("SteamClient023::CreateSteamPipe -> %d\n", adapter_pipe);
}
int32_t pipe = create_pipe != NULL ? create_pipe() : 0;
printf("Steam_CreateSteamPipe -> %d\n", pipe);
dump_unix_sockets("after client CreateSteamPipe");
int32_t user = (connect_user != NULL && pipe != 0)
? connect_user(pipe) : 0;
printf("Steam_ConnectToGlobalUser(%d) -> %d\n", pipe, user);
if (connected != NULL && user != 0 && pipe != 0) {
printf("Steam_BConnected -> %d\n", connected(user, pipe));
}
if (logged_on != NULL && user != 0 && pipe != 0) {
printf("Steam_BLoggedOn -> %d\n", logged_on(user, pipe));
}
fflush(stdout);
sleep(2);
if (release_pipe != NULL && pipe != 0) {
printf("Steam_BReleaseSteamPipe -> %d\n", release_pipe(pipe));
}
if (stop != NULL) stop();
if (shutdown != NULL) shutdown();
dlclose(client);
dlclose(service);
puts("done");
return 0;
}
@@ -0,0 +1,83 @@
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef void *(*get_ipc_fn)(void);
typedef void *(*start_thread_fn)(const char *);
typedef void (*void_fn)(void);
static void *lookup(void *handle, const char *name) {
dlerror();
void *ptr = dlsym(handle, name);
const char *error = dlerror();
printf("dlsym %-28s = %p", name, ptr);
if (error != NULL) {
printf(" error=%s", error);
}
putchar('\n');
fflush(stdout);
return ptr;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr,
"usage: %s STEAMSERVICE.SO [symbols|getipc|start [arg]]\n",
argv[0]);
return 2;
}
printf("pid=%d uid=%d euid=%d\n", getpid(), getuid(), geteuid());
printf("dlopen(%s)\n", argv[1]);
fflush(stdout);
void *handle = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL);
if (handle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
return 3;
}
printf("dlopen ok handle=%p\n", handle);
get_ipc_fn get_ipc =
(get_ipc_fn)lookup(handle, "SteamService_GetIPCServer");
start_thread_fn start =
(start_thread_fn)lookup(handle, "SteamService_StartThread");
void_fn stop = (void_fn)lookup(handle, "SteamService_Stop");
void_fn shutdown = (void_fn)lookup(handle, "SteamService_Shutdown");
lookup(handle, "SteamService_RunMainLoop");
lookup(handle, "CreateInterface");
const char *mode = argc >= 3 ? argv[2] : "symbols";
if (!strcmp(mode, "getipc") || !strcmp(mode, "start")) {
printf("GetIPCServer before start -> %p\n",
get_ipc != NULL ? get_ipc() : NULL);
fflush(stdout);
}
if (!strcmp(mode, "start")) {
const char *arg = argc >= 4 ? argv[3] : NULL;
printf("StartThread arg=%s\n", arg != NULL ? arg : "<NULL>");
fflush(stdout);
void *result = start != NULL ? start(arg) : NULL;
printf("StartThread -> %p; GetIPCServer -> %p\n",
result, get_ipc != NULL ? get_ipc() : NULL);
fflush(stdout);
sleep(2);
if (stop != NULL) {
puts("Stop()");
fflush(stdout);
stop();
}
if (shutdown != NULL) {
puts("Shutdown()");
fflush(stdout);
shutdown();
}
}
dlclose(handle);
puts("done");
return 0;
}
@@ -0,0 +1,101 @@
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/un.h>
#include <unistd.h>
static void trace_line(const char *kind, int fd,
const struct sockaddr *addr, socklen_t len) {
char buffer[768];
const char *detail = "";
char endpoint[512] = {0};
if (addr != NULL && addr->sa_family == AF_UNIX &&
len > offsetof(struct sockaddr_un, sun_path)) {
const struct sockaddr_un *un = (const struct sockaddr_un *)addr;
size_t path_len = len - offsetof(struct sockaddr_un, sun_path);
if (path_len > sizeof(un->sun_path)) path_len = sizeof(un->sun_path);
if (path_len > 0 && un->sun_path[0] == '\0') {
snprintf(endpoint, sizeof(endpoint), "@%.*s",
(int)(path_len - 1), un->sun_path + 1);
} else {
snprintf(endpoint, sizeof(endpoint), "%.*s",
(int)path_len, un->sun_path);
}
detail = endpoint;
} else if (addr != NULL && addr->sa_family == AF_INET &&
len >= sizeof(struct sockaddr_in)) {
const struct sockaddr_in *in = (const struct sockaddr_in *)addr;
unsigned long ip = ntohl(in->sin_addr.s_addr);
snprintf(endpoint, sizeof(endpoint), "%lu.%lu.%lu.%lu:%u",
(ip >> 24) & 0xff, (ip >> 16) & 0xff,
(ip >> 8) & 0xff, ip & 0xff, ntohs(in->sin_port));
detail = endpoint;
}
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] %s fd=%d family=%d endpoint=%s\n",
kind, fd, addr != NULL ? addr->sa_family : -1, detail);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
}
int socket(int domain, int type, int protocol) {
int fd = (int)syscall(SYS_socket, domain, type, protocol);
char buffer[160];
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] socket domain=%d type=%d protocol=%d -> %d errno=%d\n",
domain, type, protocol, fd, fd < 0 ? errno : 0);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
return fd;
}
int socketpair(int domain, int type, int protocol, int sv[2]) {
int rc = (int)syscall(SYS_socketpair, domain, type, protocol, sv);
char buffer[180];
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] socketpair domain=%d type=%d -> %d [%d,%d] errno=%d\n",
domain, type, rc, rc == 0 ? sv[0] : -1,
rc == 0 ? sv[1] : -1, rc < 0 ? errno : 0);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
return rc;
}
int connect(int fd, const struct sockaddr *addr, socklen_t len) {
trace_line("connect", fd, addr, len);
int rc = (int)syscall(SYS_connect, fd, addr, len);
if (rc < 0) {
char buffer[120];
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] connect rc=%d errno=%d\n", rc, errno);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
}
return rc;
}
int bind(int fd, const struct sockaddr *addr, socklen_t len) {
trace_line("bind", fd, addr, len);
int rc = (int)syscall(SYS_bind, fd, addr, len);
if (rc < 0) {
char buffer[120];
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] bind rc=%d errno=%d\n", rc, errno);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
}
return rc;
}
int listen(int fd, int backlog) {
int rc = (int)syscall(SYS_listen, fd, backlog);
char buffer[120];
int n = snprintf(buffer, sizeof(buffer),
"[probe-trace] listen fd=%d backlog=%d -> %d errno=%d\n",
fd, backlog, rc, rc < 0 ? errno : 0);
if (n > 0) write(STDERR_FILENO, buffer, (size_t)n);
return rc;
}