mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
An opt-in second Steam client (Steam settings, Steam client: x86-64): Valve's x86-64 Linux client, unchanged, runs under FEX in its own home with its own game library and the same sign-in. Linux builds run directly, so titles such as Team Fortress 2 and Half-Life use their Linux versions; Windows games use DroidDeck-Proton as on arm64. The arm64 client stays the default. - droiddeck-steam-x64: FEX and the x86-64 rootfs from DroidDeck-Components (sha256-pinned), client install and sync from Valve's CDN, sign-in shared with the arm64 client, its web helper on the GPU (ANGLE GL over FEX's GL thunk, zink, Turnip), Proton wrappers for the arm64 client's tools, DroidDeck direct for Linux builds, SD-card library (x86-64/ beside the arm64 one), repair. - FEX 2610 built by build-fex.yml with thunks and three patches: an openat2 fallback without openat2 (proot), newer-clang thunkgen, MAP_32BIT exhaustion returning ENOMEM. libblsession resolves RESOLVE_IN_ROOT paths inside the root. - Session: x86-64 gating, shortcuts and compatibility tools per client, logs from the client that ran. - App: client setting with the FEX version, an x86-64 chip on the Games header, the active client's library, art, storage, downloads, repair, Offline Mode, Decky, desktop mode, optional VAC diagnostics; per-client state for art, the content log, repair and leftovers. - Strings in every locale. - Review fixes: the library cache is saved only by the newest scan; an abandoned session's Steam logs come from the client it recorded; distinct VAC console names; FEXInterpreter in CPU pinning; clearer VAC hint in pl, ru, uk, cs and bg.
571 lines
21 KiB
C
571 lines
21 KiB
C
/*
|
|
* Ported from WinNative (maxjivi05, feature/wayland-gamescope, 2026-09-18/19), GPL-3.0.
|
|
*/
|
|
|
|
#define _GNU_SOURCE 1
|
|
#include <arpa/inet.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ifaddrs.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netpacket/packet.h>
|
|
#include <netinet/in.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define LINK_FILE "/etc/droiddeck-net"
|
|
#define LO_NAME "lo"
|
|
#define LO_INDEX 1
|
|
#define LO_MTU 65536
|
|
#define MAX_ADDRS 16
|
|
#define FRESH_NS 1000000000LL
|
|
|
|
struct link_addr {
|
|
int family;
|
|
unsigned char bytes[16];
|
|
unsigned prefix;
|
|
};
|
|
|
|
struct link {
|
|
char name[IFNAMSIZ];
|
|
unsigned index;
|
|
int mtu;
|
|
unsigned char mac[6];
|
|
struct link_addr addrs[MAX_ADDRS];
|
|
int addr_count;
|
|
struct link_addr gateways[2];
|
|
int gateway_count;
|
|
};
|
|
|
|
static int parse_addr(const char *text, struct link_addr *out) {
|
|
memset(out, 0, sizeof(*out));
|
|
if (inet_pton(AF_INET, text, out->bytes) == 1) out->family = AF_INET;
|
|
else if (inet_pton(AF_INET6, text, out->bytes) == 1) out->family = AF_INET6;
|
|
else return 0;
|
|
return 1;
|
|
}
|
|
|
|
typedef FILE *(*fopen_fn)(const char *, const char *);
|
|
|
|
static int read_link(struct link *link) {
|
|
static fopen_fn real_fopen;
|
|
if (real_fopen == NULL) real_fopen = (fopen_fn) dlsym(RTLD_NEXT, "fopen");
|
|
FILE *f = real_fopen ? real_fopen(LINK_FILE, "re") : NULL;
|
|
if (f == NULL) return 0;
|
|
|
|
memset(link, 0, sizeof(*link));
|
|
char line[160], text[64];
|
|
unsigned m[6], prefix;
|
|
while (fgets(line, sizeof(line), f)) {
|
|
if (sscanf(line, "if %15s %u %d", link->name, &link->index, &link->mtu) == 3) continue;
|
|
if (sscanf(line, "mac %2x:%2x:%2x:%2x:%2x:%2x", m, m + 1, m + 2, m + 3, m + 4, m + 5) == 6) {
|
|
for (int i = 0; i < 6; i++) link->mac[i] = (unsigned char) m[i];
|
|
} else if (sscanf(line, "addr %63s %u", text, &prefix) == 2) {
|
|
struct link_addr addr;
|
|
if (link->addr_count < MAX_ADDRS && parse_addr(text, &addr)
|
|
&& prefix <= (addr.family == AF_INET ? 32u : 128u)) {
|
|
addr.prefix = prefix;
|
|
link->addrs[link->addr_count++] = addr;
|
|
}
|
|
} else if (sscanf(line, "gw %63s", text) == 1) {
|
|
struct link_addr addr;
|
|
if (link->gateway_count < 2 && parse_addr(text, &addr)) {
|
|
link->gateways[link->gateway_count++] = addr;
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
return link->name[0] != '\0' && link->index > LO_INDEX && link->addr_count > 0;
|
|
}
|
|
|
|
static long long now_ns(void) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
|
|
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
|
|
}
|
|
|
|
static int fresh(long long *at) {
|
|
long long then = __atomic_load_n(at, __ATOMIC_RELAXED);
|
|
return then != 0 && now_ns() - then < FRESH_NS;
|
|
}
|
|
|
|
static pthread_mutex_t link_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
static struct link link_read;
|
|
static int link_ok;
|
|
static long long link_at;
|
|
|
|
static int load_link(struct link *link) {
|
|
pthread_mutex_lock(&link_lock);
|
|
if (!fresh(&link_at)) {
|
|
link_ok = read_link(&link_read);
|
|
__atomic_store_n(&link_at, now_ns(), __ATOMIC_RELAXED);
|
|
}
|
|
int ok = link_ok;
|
|
if (ok) *link = link_read;
|
|
pthread_mutex_unlock(&link_lock);
|
|
return ok;
|
|
}
|
|
|
|
static void link_lock_before_fork(void) { pthread_mutex_lock(&link_lock); }
|
|
static void link_unlock_after_fork(void) { pthread_mutex_unlock(&link_lock); }
|
|
static void link_reset_after_fork(void) { pthread_mutex_init(&link_lock, NULL); }
|
|
|
|
__attribute__((constructor)) static void install_link_fork_handlers(void) {
|
|
pthread_atfork(link_lock_before_fork, link_unlock_after_fork, link_reset_after_fork);
|
|
}
|
|
|
|
static long long ifaddrs_unusable_at, nameindex_unusable_at;
|
|
|
|
static void prefix_mask(int family, unsigned prefix, unsigned char *out) {
|
|
int len = family == AF_INET ? 4 : 16;
|
|
for (int i = 0; i < len; i++, prefix -= prefix < 8 ? prefix : 8) {
|
|
out[i] = prefix >= 8 ? 0xff : (unsigned char) (0xff00 >> prefix);
|
|
}
|
|
}
|
|
|
|
static int is_link_local(const struct link_addr *addr) {
|
|
return addr->family == AF_INET6 && addr->bytes[0] == 0xfe && (addr->bytes[1] & 0xc0) == 0x80;
|
|
}
|
|
|
|
struct entry {
|
|
struct ifaddrs ifa;
|
|
struct sockaddr_storage addr, mask, extra;
|
|
char name[IFNAMSIZ];
|
|
};
|
|
|
|
static struct entry *fill_entry(struct entry *e, const char *name, unsigned flags) {
|
|
snprintf(e->name, sizeof(e->name), "%s", name);
|
|
e->ifa.ifa_name = e->name;
|
|
e->ifa.ifa_flags = flags;
|
|
e->ifa.ifa_addr = (struct sockaddr *) &e->addr;
|
|
e[-1].ifa.ifa_next = &e->ifa;
|
|
return e;
|
|
}
|
|
|
|
static void fill_packet(struct entry *e, unsigned index, const unsigned char *mac) {
|
|
struct sockaddr_ll *ll = (struct sockaddr_ll *) &e->addr;
|
|
ll->sll_family = AF_PACKET;
|
|
ll->sll_ifindex = (int) index;
|
|
ll->sll_hatype = mac ? ARPHRD_ETHER : ARPHRD_LOOPBACK;
|
|
ll->sll_halen = 6;
|
|
if (mac) memcpy(ll->sll_addr, mac, 6);
|
|
}
|
|
|
|
static void fill_inet(struct entry *e, const struct link_addr *addr, unsigned index) {
|
|
unsigned char mask[16];
|
|
prefix_mask(addr->family, addr->prefix, mask);
|
|
e->ifa.ifa_netmask = (struct sockaddr *) &e->mask;
|
|
if (addr->family == AF_INET) {
|
|
struct sockaddr_in *sin = (struct sockaddr_in *) &e->addr;
|
|
struct sockaddr_in *smask = (struct sockaddr_in *) &e->mask;
|
|
sin->sin_family = smask->sin_family = AF_INET;
|
|
memcpy(&sin->sin_addr, addr->bytes, 4);
|
|
memcpy(&smask->sin_addr, mask, 4);
|
|
if (e->ifa.ifa_flags & IFF_BROADCAST) {
|
|
struct sockaddr_in *bcast = (struct sockaddr_in *) &e->extra;
|
|
bcast->sin_family = AF_INET;
|
|
bcast->sin_addr.s_addr = sin->sin_addr.s_addr | ~smask->sin_addr.s_addr;
|
|
e->ifa.ifa_broadaddr = (struct sockaddr *) bcast;
|
|
}
|
|
} else {
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &e->addr;
|
|
struct sockaddr_in6 *smask = (struct sockaddr_in6 *) &e->mask;
|
|
sin6->sin6_family = smask->sin6_family = AF_INET6;
|
|
memcpy(&sin6->sin6_addr, addr->bytes, 16);
|
|
memcpy(&smask->sin6_addr, mask, 16);
|
|
if (is_link_local(addr)) sin6->sin6_scope_id = index;
|
|
}
|
|
}
|
|
|
|
static const struct link_addr LO_V4 = { AF_INET, { 127, 0, 0, 1 }, 8 };
|
|
static const struct link_addr LO_V6 = { AF_INET6, { [15] = 1 }, 128 };
|
|
|
|
static int real_list_usable(const struct ifaddrs *list) {
|
|
for (const struct ifaddrs *e = list; e; e = e->ifa_next) {
|
|
if (e->ifa_addr && e->ifa_addr->sa_family == AF_INET && !(e->ifa_flags & IFF_LOOPBACK)) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
typedef void (*freeifaddrs_fn)(struct ifaddrs *);
|
|
static freeifaddrs_fn real_freeifaddrs;
|
|
|
|
int getifaddrs(struct ifaddrs **out) {
|
|
typedef int (*getifaddrs_fn)(struct ifaddrs **);
|
|
static getifaddrs_fn real;
|
|
if (real == NULL) real = (getifaddrs_fn) dlsym(RTLD_NEXT, "getifaddrs");
|
|
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) dlsym(RTLD_NEXT, "freeifaddrs");
|
|
struct ifaddrs *list = NULL;
|
|
int asked = !fresh(&ifaddrs_unusable_at);
|
|
int rc = -1, saved = errno;
|
|
if (asked) {
|
|
rc = real(&list);
|
|
saved = errno;
|
|
if (rc == 0 && real_list_usable(list)) {
|
|
*out = list;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct link link;
|
|
if (!load_link(&link)) {
|
|
if (!asked) return real(out);
|
|
if (rc == 0) *out = list;
|
|
errno = saved;
|
|
return rc;
|
|
}
|
|
if (asked) __atomic_store_n(&ifaddrs_unusable_at, now_ns(), __ATOMIC_RELAXED);
|
|
if (rc == 0 && list) real_freeifaddrs(list);
|
|
|
|
struct entry *block = calloc((size_t) link.addr_count + 5, sizeof(*block));
|
|
if (block == NULL) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
unsigned lo_flags = IFF_UP | IFF_RUNNING | IFF_LOOPBACK;
|
|
unsigned up_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST;
|
|
struct entry *e = block;
|
|
fill_packet(fill_entry(++e, LO_NAME, lo_flags), LO_INDEX, NULL);
|
|
fill_inet(fill_entry(++e, LO_NAME, lo_flags), &LO_V4, LO_INDEX);
|
|
fill_inet(fill_entry(++e, LO_NAME, lo_flags), &LO_V6, LO_INDEX);
|
|
fill_packet(fill_entry(++e, link.name, up_flags), link.index, link.mac);
|
|
for (int i = 0; i < link.addr_count; i++) {
|
|
fill_inet(fill_entry(++e, link.name, up_flags), &link.addrs[i], link.index);
|
|
}
|
|
block[1].ifa.ifa_data = &block[1].ifa;
|
|
*out = &block[1].ifa;
|
|
return 0;
|
|
}
|
|
|
|
void freeifaddrs(struct ifaddrs *list) {
|
|
if (list == NULL) return;
|
|
if (list->ifa_data == list) {
|
|
free((struct entry *) list - 1);
|
|
return;
|
|
}
|
|
if (real_freeifaddrs == NULL) real_freeifaddrs = (freeifaddrs_fn) dlsym(RTLD_NEXT, "freeifaddrs");
|
|
real_freeifaddrs(list);
|
|
}
|
|
|
|
struct name_block {
|
|
struct if_nameindex index[3];
|
|
char names[2][IFNAMSIZ];
|
|
};
|
|
|
|
struct if_nameindex *if_nameindex(void) {
|
|
typedef struct if_nameindex *(*if_nameindex_fn)(void);
|
|
static if_nameindex_fn real;
|
|
if (real == NULL) real = (if_nameindex_fn) dlsym(RTLD_NEXT, "if_nameindex");
|
|
struct if_nameindex *list = NULL;
|
|
int asked = !fresh(&nameindex_unusable_at);
|
|
int saved = errno;
|
|
if (asked) {
|
|
list = real();
|
|
saved = errno;
|
|
if (list && list[0].if_index != 0) return list;
|
|
}
|
|
|
|
struct link link;
|
|
struct name_block *block;
|
|
if (!load_link(&link)) {
|
|
if (!asked) return real();
|
|
errno = saved;
|
|
return list;
|
|
}
|
|
if (asked) __atomic_store_n(&nameindex_unusable_at, now_ns(), __ATOMIC_RELAXED);
|
|
if ((block = calloc(1, sizeof(*block))) == NULL) {
|
|
if (asked) errno = saved;
|
|
return list;
|
|
}
|
|
if (list) if_freenameindex(list);
|
|
snprintf(block->names[0], IFNAMSIZ, "%s", LO_NAME);
|
|
snprintf(block->names[1], IFNAMSIZ, "%s", link.name);
|
|
block->index[0] = (struct if_nameindex) { LO_INDEX, block->names[0] };
|
|
block->index[1] = (struct if_nameindex) { link.index, block->names[1] };
|
|
return block->index;
|
|
}
|
|
|
|
void if_freenameindex(struct if_nameindex *list) {
|
|
typedef void (*if_freenameindex_fn)(struct if_nameindex *);
|
|
if (list == NULL) return;
|
|
if (list[0].if_name == ((struct name_block *) list)->names[0]) {
|
|
free(list);
|
|
return;
|
|
}
|
|
((if_freenameindex_fn) dlsym(RTLD_NEXT, "if_freenameindex"))(list);
|
|
}
|
|
|
|
unsigned int if_nametoindex(const char *name) {
|
|
typedef unsigned int (*if_nametoindex_fn)(const char *);
|
|
unsigned int index = ((if_nametoindex_fn) dlsym(RTLD_NEXT, "if_nametoindex"))(name);
|
|
if (index != 0 || name == NULL) return index;
|
|
int saved = errno;
|
|
struct link link;
|
|
if (strcmp(name, LO_NAME) == 0) return LO_INDEX;
|
|
if (load_link(&link) && strcmp(name, link.name) == 0) return link.index;
|
|
errno = saved;
|
|
return 0;
|
|
}
|
|
|
|
char *if_indextoname(unsigned int index, char name[IF_NAMESIZE]) {
|
|
typedef char *(*if_indextoname_fn)(unsigned int, char *);
|
|
char *found = ((if_indextoname_fn) dlsym(RTLD_NEXT, "if_indextoname"))(index, name);
|
|
if (found || name == NULL) return found;
|
|
int saved = errno;
|
|
struct link link;
|
|
if (index == LO_INDEX) return strcpy(name, LO_NAME);
|
|
if (load_link(&link) && index == link.index) return strcpy(name, link.name);
|
|
errno = saved;
|
|
return NULL;
|
|
}
|
|
|
|
static int mac_unusable(const struct ifreq *ifr) {
|
|
const unsigned char *a = (const unsigned char *) ifr->ifr_hwaddr.sa_data;
|
|
if (ifr->ifr_hwaddr.sa_family != ARPHRD_ETHER) return 1;
|
|
int rest = a[1] | a[2] | a[3] | a[4] | a[5];
|
|
return (a[0] & 0x03) || (rest == 0 && (a[0] == 0 || a[0] == 0x02));
|
|
}
|
|
|
|
static const struct link_addr *first_v4(const struct link *link) {
|
|
for (int i = 0; i < link->addr_count; i++) {
|
|
if (link->addrs[i].family == AF_INET) return &link->addrs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int answer_ifreq(unsigned long request, struct ifreq *ifr, int real_rc) {
|
|
char name[IFNAMSIZ + 1] = { 0 };
|
|
memcpy(name, ifr->ifr_name, IFNAMSIZ);
|
|
int loopback = strcmp(name, LO_NAME) == 0;
|
|
struct link link;
|
|
if (!load_link(&link) || (!loopback && strcmp(name, link.name) != 0)) return 0;
|
|
|
|
if (request == SIOCGIFHWADDR) {
|
|
if (real_rc == 0 && (loopback || !mac_unusable(ifr))) return 0;
|
|
memset(&ifr->ifr_hwaddr, 0, sizeof(ifr->ifr_hwaddr));
|
|
ifr->ifr_hwaddr.sa_family = loopback ? ARPHRD_LOOPBACK : ARPHRD_ETHER;
|
|
if (!loopback) memcpy(ifr->ifr_hwaddr.sa_data, link.mac, 6);
|
|
return 1;
|
|
}
|
|
if (real_rc == 0) return 0;
|
|
|
|
const struct link_addr *v4 = loopback ? &LO_V4 : first_v4(&link);
|
|
struct sockaddr_in *sin = (struct sockaddr_in *) &ifr->ifr_addr;
|
|
unsigned char mask[16];
|
|
switch (request) {
|
|
case SIOCGIFFLAGS:
|
|
ifr->ifr_flags = loopback ? IFF_UP | IFF_RUNNING | IFF_LOOPBACK
|
|
: IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST;
|
|
return 1;
|
|
case SIOCGIFMTU:
|
|
ifr->ifr_mtu = loopback ? LO_MTU : link.mtu;
|
|
return 1;
|
|
case SIOCGIFINDEX:
|
|
ifr->ifr_ifindex = loopback ? LO_INDEX : (int) link.index;
|
|
return 1;
|
|
case SIOCGIFADDR:
|
|
case SIOCGIFNETMASK:
|
|
case SIOCGIFBRDADDR:
|
|
if (v4 == NULL) return 0;
|
|
prefix_mask(AF_INET, v4->prefix, mask);
|
|
memset(sin, 0, sizeof(*sin));
|
|
sin->sin_family = AF_INET;
|
|
for (int i = 0; i < 4; i++) {
|
|
unsigned char *out = (unsigned char *) &sin->sin_addr + i;
|
|
*out = request == SIOCGIFNETMASK ? mask[i]
|
|
: request == SIOCGIFADDR ? v4->bytes[i]
|
|
: (unsigned char) (v4->bytes[i] | ~mask[i]);
|
|
}
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int is_link_query(unsigned long request) {
|
|
return request == SIOCGIFHWADDR || request == SIOCGIFFLAGS || request == SIOCGIFMTU
|
|
|| request == SIOCGIFINDEX || request == SIOCGIFADDR || request == SIOCGIFNETMASK
|
|
|| request == SIOCGIFBRDADDR;
|
|
}
|
|
|
|
void bl_kgsl_chip_id_fixup(unsigned long request, void *arg, int rc);
|
|
#include "../../quest/kgsl_compat.h"
|
|
int bl_kgsl_poll(int (*real)(int, unsigned long, void *), int fd, unsigned long request,
|
|
void *arg, int *rc) __attribute__((visibility("hidden")));
|
|
int bl_ntsync_ioctl(int fd, unsigned long request, void *arg, int *rc) __attribute__((visibility("hidden")));
|
|
int bl_drm_gem_close(int fd, unsigned long request, void *arg, int *rc) __attribute__((visibility("hidden")));
|
|
int bl_winebus_rumble_ioctl(int (*real)(int, unsigned long, void *), int fd, unsigned long request,
|
|
void *arg, void *caller, int *rc) __attribute__((visibility("hidden")));
|
|
|
|
int ioctl(int fd, unsigned long request, ...) {
|
|
typedef int (*ioctl_fn)(int, unsigned long, void *);
|
|
static ioctl_fn real;
|
|
va_list ap;
|
|
va_start(ap, request);
|
|
void *arg = va_arg(ap, void *);
|
|
va_end(ap);
|
|
|
|
int rc;
|
|
if (bl_ntsync_ioctl(fd, request, arg, &rc)) return rc;
|
|
if (bl_drm_gem_close(fd, request, arg, &rc)) return rc;
|
|
if (real == NULL) real = (ioctl_fn) dlsym(RTLD_NEXT, "ioctl");
|
|
if (bl_winebus_rumble_ioctl(real, fd, request, arg, __builtin_return_address(0), &rc)) return rc;
|
|
if (!bl_kgsl_poll(real, fd, request, arg, &rc)) rc = quest_kgsl_ioctl(real, fd, request, arg);
|
|
bl_kgsl_chip_id_fixup(request, arg, rc);
|
|
if (arg == NULL || !is_link_query(request)) return rc;
|
|
int saved = errno;
|
|
if (answer_ifreq(request, (struct ifreq *) arg, rc)) return 0;
|
|
errno = saved;
|
|
return rc;
|
|
}
|
|
|
|
static void print_v6(FILE *out, const unsigned char *bytes) {
|
|
for (int i = 0; i < 16; i++) fprintf(out, "%02x", bytes[i]);
|
|
}
|
|
|
|
static unsigned v6_scope(const struct link_addr *addr) {
|
|
if (memcmp(addr, &LO_V6, sizeof(*addr)) == 0) return 0x10;
|
|
return is_link_local(addr) ? 0x20 : 0x00;
|
|
}
|
|
|
|
static void write_table(FILE *out, const char *table, const struct link *link) {
|
|
static const unsigned char zero[16];
|
|
unsigned char mask[16];
|
|
if (strcmp(table, "route") == 0) {
|
|
fprintf(out, "Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n");
|
|
for (int i = 0; i < link->gateway_count; i++) {
|
|
if (link->gateways[i].family != AF_INET) continue;
|
|
unsigned gw;
|
|
memcpy(&gw, link->gateways[i].bytes, 4);
|
|
fprintf(out, "%s\t00000000\t%08X\t0003\t0\t0\t0\t00000000\t0\t0\t0\n", link->name, gw);
|
|
}
|
|
for (int i = 0; i < link->addr_count; i++) {
|
|
if (link->addrs[i].family != AF_INET) continue;
|
|
unsigned addr, net;
|
|
prefix_mask(AF_INET, link->addrs[i].prefix, mask);
|
|
memcpy(&addr, link->addrs[i].bytes, 4);
|
|
memcpy(&net, mask, 4);
|
|
fprintf(out, "%s\t%08X\t00000000\t0001\t0\t0\t0\t%08X\t0\t0\t0\n",
|
|
link->name, addr & net, net);
|
|
}
|
|
} else if (strcmp(table, "ipv6_route") == 0) {
|
|
for (int i = 0; i < link->gateway_count; i++) {
|
|
if (link->gateways[i].family != AF_INET6) continue;
|
|
print_v6(out, zero);
|
|
fprintf(out, " 00 ");
|
|
print_v6(out, zero);
|
|
fprintf(out, " 00 ");
|
|
print_v6(out, link->gateways[i].bytes);
|
|
fprintf(out, " 00000400 00000001 00000000 00000003 %8s\n", link->name);
|
|
}
|
|
for (int i = 0; i < link->addr_count; i++) {
|
|
if (link->addrs[i].family != AF_INET6) continue;
|
|
unsigned char net[16];
|
|
prefix_mask(AF_INET6, link->addrs[i].prefix, mask);
|
|
for (int b = 0; b < 16; b++) net[b] = link->addrs[i].bytes[b] & mask[b];
|
|
print_v6(out, net);
|
|
fprintf(out, " %02x ", link->addrs[i].prefix);
|
|
print_v6(out, zero);
|
|
fprintf(out, " 00 ");
|
|
print_v6(out, zero);
|
|
fprintf(out, " 00000100 00000001 00000000 00000001 %8s\n", link->name);
|
|
}
|
|
} else if (strcmp(table, "if_inet6") == 0) {
|
|
print_v6(out, LO_V6.bytes);
|
|
fprintf(out, " %02x %02x %02x 80 %8s\n", LO_INDEX, LO_V6.prefix, v6_scope(&LO_V6), LO_NAME);
|
|
for (int i = 0; i < link->addr_count; i++) {
|
|
if (link->addrs[i].family != AF_INET6) continue;
|
|
print_v6(out, link->addrs[i].bytes);
|
|
fprintf(out, " %02x %02x %02x 80 %8s\n", link->index, link->addrs[i].prefix,
|
|
v6_scope(&link->addrs[i]), link->name);
|
|
}
|
|
} else if (strcmp(table, "dev") == 0) {
|
|
fprintf(out, "Inter-| Receive | Transmit\n"
|
|
" face |bytes packets errs drop fifo frame compressed multicast|"
|
|
"bytes packets errs drop fifo colls carrier compressed\n");
|
|
fprintf(out, "%6s: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n", LO_NAME);
|
|
fprintf(out, "%6s: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n", link->name);
|
|
} else if (strcmp(table, "arp") == 0) {
|
|
fprintf(out, "IP address HW type Flags HW address Mask Device\n");
|
|
} else {
|
|
fprintf(out, "1\n");
|
|
}
|
|
}
|
|
|
|
static const char *denied_table(const char *path) {
|
|
static const char *const tables[] = { "route", "ipv6_route", "if_inet6", "dev", "arp" };
|
|
if (strncmp(path, "/proc/net/", 10) == 0) {
|
|
for (size_t i = 0; i < sizeof(tables) / sizeof(tables[0]); i++) {
|
|
if (strcmp(path + 10, tables[i]) == 0) return tables[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
size_t len = strlen(path);
|
|
if (strncmp(path, "/sys/class/net/", 15) == 0 && len > 8 && strcmp(path + len - 8, "/carrier") == 0) {
|
|
return "carrier";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static FILE *synthesize(const char *table) {
|
|
struct link link;
|
|
if (!load_link(&link)) return NULL;
|
|
int fd = memfd_create("droiddeck-net", MFD_CLOEXEC);
|
|
if (fd < 0) return NULL;
|
|
FILE *out = fdopen(fd, "w+");
|
|
if (out == NULL) {
|
|
close(fd);
|
|
return NULL;
|
|
}
|
|
write_table(out, table, &link);
|
|
if (fflush(out) != 0) {
|
|
fclose(out);
|
|
return NULL;
|
|
}
|
|
rewind(out);
|
|
return out;
|
|
}
|
|
|
|
int bl_status_without_tracer(const char *path, int flags, const void *caller) __attribute__((visibility("hidden")));
|
|
|
|
static FILE *open_stream(fopen_fn real, const char *path, const char *mode, const void *caller) {
|
|
int status = mode && mode[0] == 'r' && !strchr(mode, '+')
|
|
? bl_status_without_tracer(path, O_RDONLY | O_CLOEXEC, caller) : -1;
|
|
if (status >= 0) {
|
|
FILE *copy = fdopen(status, "r");
|
|
if (copy == NULL) close(status);
|
|
return copy;
|
|
}
|
|
FILE *f = real(path, mode);
|
|
if (f || path == NULL || mode == NULL || mode[0] != 'r') return f;
|
|
int saved = errno;
|
|
const char *table = denied_table(path);
|
|
if (table && (f = synthesize(table)) != NULL) return f;
|
|
errno = saved;
|
|
return NULL;
|
|
}
|
|
|
|
FILE *fopen(const char *path, const char *mode) {
|
|
static fopen_fn real;
|
|
if (real == NULL) real = (fopen_fn) dlsym(RTLD_NEXT, "fopen");
|
|
return open_stream(real, path, mode, __builtin_return_address(0));
|
|
}
|
|
|
|
FILE *fopen64(const char *path, const char *mode) {
|
|
static fopen_fn real;
|
|
if (real == NULL) real = (fopen_fn) dlsym(RTLD_NEXT, "fopen64");
|
|
return open_stream(real, path, mode, __builtin_return_address(0));
|
|
}
|