diff --git a/CMakeLists.txt b/CMakeLists.txt index 80764c5..4a974ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ endif() project(vitaImmich C) include("${VITASDK}/share/vita.cmake" REQUIRED) -set(VITA_APP_NAME "Vita Immich") +set(VITA_APP_NAME "Immich") set(VITA_TITLEID "VIMM00001") set(VITA_VERSION "01.00") @@ -47,4 +47,5 @@ vita_create_self(${PROJECT_NAME}.self ${PROJECT_NAME}) vita_create_vpk(${PROJECT_NAME}.vpk ${VITA_TITLEID} ${PROJECT_NAME}.self VERSION ${VITA_VERSION} NAME ${VITA_APP_NAME} + FILE ${CMAKE_SOURCE_DIR}/assets/sce_sys/icon0.png sce_sys/icon0.png ) diff --git a/README.md b/README.md index aac83d7..1d7a7b5 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,19 @@ This produces `build/vitaImmich.vpk`. apikey=your-immich-api-key ``` + If your server sits behind a public domain (reverse proxy) and your + router does not support NAT loopback, add the server's LAN IP so the + domain is pinned to it while on your home network: + + ``` + server=https://immich.example.com + apikey=your-immich-api-key + serverip=192.168.1.100 + ``` + + This keeps the Host header and TLS SNI on the domain (so the reverse + proxy still routes correctly) but connects to the LAN IP directly. + 4. Relaunch the app. ## Controls diff --git a/assets/sce_sys/icon0.png b/assets/sce_sys/icon0.png new file mode 100644 index 0000000..3bce5a1 Binary files /dev/null and b/assets/sce_sys/icon0.png differ diff --git a/src/main.c b/src/main.c index 7ecbfc2..b67f9a6 100644 --- a/src/main.c +++ b/src/main.c @@ -51,6 +51,9 @@ int _newlib_heap_size_user = 192 * 1024 * 1024; static char g_server[512]; static char g_apikey[256]; +static char g_serverip[64]; +/* optional DNS pin ("host:port:ip") for routers without NAT loopback */ +static struct curl_slist *g_resolve_list; static vita2d_pgf *g_font; @@ -167,7 +170,9 @@ static int load_config(void) f = fopen(CONFIG_PATH, "w"); if (f) { fputs("server=http://192.168.1.100:2283\n" - "apikey=PASTE_YOUR_IMMICH_API_KEY_HERE\n", f); + "apikey=PASTE_YOUR_IMMICH_API_KEY_HERE\n" + "# serverip=192.168.1.100 (optional: LAN IP of the\n" + "# server, for routers without NAT loopback)\n", f); fclose(f); } return -1; @@ -180,6 +185,8 @@ static int load_config(void) snprintf(g_server, sizeof(g_server), "%s", clean_line(s + 7)); else if (!strncmp(s, "apikey=", 7)) snprintf(g_apikey, sizeof(g_apikey), "%s", clean_line(s + 7)); + else if (!strncmp(s, "serverip=", 9)) + snprintf(g_serverip, sizeof(g_serverip), "%s", clean_line(s + 9)); } fclose(f); @@ -200,6 +207,30 @@ static int load_config(void) if (strncasecmp(g_server, "http://", 7) && strncasecmp(g_server, "https://", 8)) fatal_error(g_server, "Bad server URL in config (must start with http:// or https://):"); + + /* serverip= pins the hostname to a fixed IP (keeps Host header and + * TLS SNI intact, unlike putting the IP in the URL) */ + if (g_serverip[0]) { + int https = !strncasecmp(g_server, "https://", 8); + const char *host = strstr(g_server, "://") + 3; + char hostname[256]; + size_t i = 0; + while (host[i] && host[i] != '/' && host[i] != ':' && + i < sizeof(hostname) - 1) { + hostname[i] = host[i]; + i++; + } + hostname[i] = '\0'; + int port = https ? 443 : 80; + if (host[i] == ':') + port = atoi(host + i + 1); + + char resolve[400]; + snprintf(resolve, sizeof(resolve), "%s:%d:%s", + hostname, port, g_serverip); + g_resolve_list = curl_slist_append(NULL, resolve); + log_line("resolve pin: %s", resolve); + } return 0; } @@ -296,6 +327,8 @@ static CURLcode http_request(const char *url, const char *body, /* PoC: no CA bundle on the Vita, skip TLS verification */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if (g_resolve_list) + curl_easy_setopt(curl, CURLOPT_RESOLVE, g_resolve_list); if (body) curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); if (g_verbose_request) { @@ -725,6 +758,8 @@ static int worker_thread(SceSize args, void *argp) curl_easy_setopt(curl, CURLOPT_USERAGENT, "vitaImmich/0.1 (PS Vita)"); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if (g_resolve_list) + curl_easy_setopt(curl, CURLOPT_RESOLVE, g_resolve_list); for (;;) { if (g_req_state != REQ_PENDING) {