From 1b9187b1534992c0235dad064238b2d836489ca8 Mon Sep 17 00:00:00 2001 From: frosty Date: Sat, 28 Mar 2026 15:01:13 -0400 Subject: feat: changes to image proxy, proxy favicons --- src/Cache/Cache.c | 5 ++ src/Cache/Cache.h | 2 + src/Config.c | 2 + src/Config.h | 2 + src/Main.c | 2 + src/Routes/ImageProxy.c | 211 ++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 210 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/Cache/Cache.c b/src/Cache/Cache.c index d739277..22f2553 100644 --- a/src/Cache/Cache.c +++ b/src/Cache/Cache.c @@ -11,15 +11,20 @@ static char cache_dir[BUFFER_SIZE_MEDIUM] = {0}; static int cache_ttl_search_val = DEFAULT_CACHE_TTL_SEARCH; static int cache_ttl_infobox_val = DEFAULT_CACHE_TTL_INFOBOX; +static int cache_ttl_image_val = DEFAULT_CACHE_TTL_IMAGE; void set_cache_ttl_search(int ttl) { cache_ttl_search_val = ttl; } void set_cache_ttl_infobox(int ttl) { cache_ttl_infobox_val = ttl; } +void set_cache_ttl_image(int ttl) { cache_ttl_image_val = ttl; } + int get_cache_ttl_search(void) { return cache_ttl_search_val; } int get_cache_ttl_infobox(void) { return cache_ttl_infobox_val; } +int get_cache_ttl_image(void) { return cache_ttl_image_val; } + static void md5_hash(const char *str, char *output) { unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int hash_len; diff --git a/src/Cache/Cache.h b/src/Cache/Cache.h index 0a84406..9989b43 100644 --- a/src/Cache/Cache.h +++ b/src/Cache/Cache.h @@ -17,7 +17,9 @@ char *cache_compute_key(const char *query, int page, const char *engine_name); void set_cache_ttl_search(int ttl); void set_cache_ttl_infobox(int ttl); +void set_cache_ttl_image(int ttl); int get_cache_ttl_search(void); int get_cache_ttl_infobox(void); +int get_cache_ttl_image(void); #endif diff --git a/src/Config.c b/src/Config.c index 0c243bd..0c3fc1c 100644 --- a/src/Config.c +++ b/src/Config.c @@ -92,6 +92,8 @@ int load_config(const char *filename, Config *config) { config->cache_ttl_search = atoi(value); } else if (strcmp(key, "ttl_infobox") == 0) { config->cache_ttl_infobox = atoi(value); + } else if (strcmp(key, "ttl_image") == 0) { + config->cache_ttl_image = atoi(value); } } else if (strcmp(section, "engines") == 0) { if (strcmp(key, "engines") == 0) { diff --git a/src/Config.h b/src/Config.h index 67882d1..ce316f6 100644 --- a/src/Config.h +++ b/src/Config.h @@ -6,6 +6,7 @@ #define DEFAULT_CACHE_DIR "/tmp/omnisearch_cache" #define DEFAULT_CACHE_TTL_SEARCH 3600 #define DEFAULT_CACHE_TTL_INFOBOX 86400 +#define DEFAULT_CACHE_TTL_IMAGE 604800 #define DEFAULT_MAX_PROXY_RETRIES 3 #define BUFFER_SIZE_SMALL 256 @@ -42,6 +43,7 @@ typedef struct { char cache_dir[512]; int cache_ttl_search; int cache_ttl_infobox; + int cache_ttl_image; char engines[512]; } Config; diff --git a/src/Main.c b/src/Main.c index 8aa161d..137cab5 100644 --- a/src/Main.c +++ b/src/Main.c @@ -52,6 +52,7 @@ int main() { .cache_dir = DEFAULT_CACHE_DIR, .cache_ttl_search = DEFAULT_CACHE_TTL_SEARCH, .cache_ttl_infobox = DEFAULT_CACHE_TTL_INFOBOX, + .cache_ttl_image = DEFAULT_CACHE_TTL_IMAGE, .engines = ""}; if (load_config("config.ini", &cfg) != 0) { @@ -72,6 +73,7 @@ int main() { set_cache_ttl_search(cfg.cache_ttl_search); set_cache_ttl_infobox(cfg.cache_ttl_infobox); + set_cache_ttl_image(cfg.cache_ttl_image); if (cfg.proxy_list_file[0] != '\0') { if (load_proxy_list(cfg.proxy_list_file) < 0) { diff --git a/src/Routes/ImageProxy.c b/src/Routes/ImageProxy.c index c2d1a9a..670da68 100644 --- a/src/Routes/ImageProxy.c +++ b/src/Routes/ImageProxy.c @@ -1,9 +1,16 @@ #include "ImageProxy.h" +#include "../Cache/Cache.h" #include "../Proxy/Proxy.h" +#include #include +#include +#include +#include +#include #include #include #include +#include #define MAX_IMAGE_SIZE (10 * 1024 * 1024) @@ -13,7 +20,97 @@ typedef struct { size_t capacity; } MemoryBuffer; +static int is_private_ip(const char *ip_str) { + struct in_addr addr; + if (inet_pton(AF_INET, ip_str, &addr) != 1) { + return 0; + } + + uint32_t ip = ntohl(addr.s_addr); + + // 10.0.0.0/8 + if ((ip >> 24) == 10) { + return 1; + } + + // 172.16.0.0/12 + if ((ip >> 20) == 0xAC) { + uint8_t second = (ip >> 16) & 0xFF; + if (second >= 16 && second <= 31) { + return 1; + } + } + + // 192.168.0.0/16 + if ((ip >> 16) == 0xC0A8) { + return 1; + } + + // 127.0.0.0/8 + if ((ip >> 24) == 127) { + return 1; + } + + // 169.254.0.0/16 + if ((ip >> 16) == 0xA9FE) { + return 1; + } + + return 0; +} + +static int is_private_hostname(const char *hostname) { + struct addrinfo hints, *res, *p; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + int err = getaddrinfo(hostname, NULL, &hints, &res); + if (err != 0) { + return 0; + } + + for (p = res; p != NULL; p = p->ai_next) { + if (p->ai_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; + char ip_str[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &(ipv4->sin_addr), ip_str, INET_ADDRSTRLEN); + + if (is_private_ip(ip_str)) { + freeaddrinfo(res); + return 1; + } + } + } + + freeaddrinfo(res); + return 0; +} + static int is_allowed_domain(const char *url) { + CURLU *h = curl_url(); + if (!h) { + return -1; + } + + curl_url_set(h, CURLUPART_URL, url, 0); + + char *scheme = NULL; + curl_url_get(h, CURLUPART_SCHEME, &scheme, 0); + + int valid_scheme = 0; + if (scheme && (strcasecmp(scheme, "http") == 0 || strcasecmp(scheme, "https") == 0)) { + valid_scheme = 1; + } + + if (scheme) + curl_free(scheme); + + if (!valid_scheme) { + curl_url_cleanup(h); + return -1; + } + const char *protocol = strstr(url, "://"); if (!protocol) { protocol = url; @@ -30,21 +127,18 @@ static int is_allowed_domain(const char *url) { } strncpy(host, protocol, host_len); - const char *allowed_domains[] = {"mm.bing.net", "th.bing.com", NULL}; - - for (int i = 0; allowed_domains[i] != NULL; i++) { - size_t domain_len = strlen(allowed_domains[i]); - size_t host_str_len = strlen(host); + char *colon = strchr(host, ':'); + if (colon) { + *colon = '\0'; + } - if (host_str_len >= domain_len) { - const char *suffix = host + host_str_len - domain_len; - if (strcmp(suffix, allowed_domains[i]) == 0) { - return 1; - } - } + if (is_private_hostname(host)) { + curl_url_cleanup(h); + return 0; } - return 0; + curl_url_cleanup(h); + return 1; } static size_t write_callback(void *contents, size_t size, size_t nmemb, @@ -73,6 +167,31 @@ static size_t write_callback(void *contents, size_t size, size_t nmemb, return realsize; } +static char *url_encode_key(const char *url) { + char *hash = malloc(33); + if (!hash) + return NULL; + + unsigned char md5hash[16]; + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if (!ctx) { + free(hash); + return NULL; + } + + EVP_DigestInit_ex(ctx, EVP_md5(), NULL); + EVP_DigestUpdate(ctx, url, strlen(url)); + EVP_DigestFinal_ex(ctx, md5hash, NULL); + EVP_MD_CTX_free(ctx); + + for (int i = 0; i < 16; i++) { + sprintf(hash + (i * 2), "%02x", md5hash[i]); + } + hash[32] = '\0'; + + return hash; +} + int image_proxy_handler(UrlParams *params) { const char *url = NULL; for (int i = 0; i < params->count; i++) { @@ -87,13 +206,59 @@ int image_proxy_handler(UrlParams *params) { return 0; } - if (!is_allowed_domain(url)) { - send_response("Domain not allowed"); + int domain_check = is_allowed_domain(url); + if (domain_check == -1) { + send_response("Invalid URL scheme"); + return 0; + } + if (domain_check == 0) { + send_response("Private addresses are not allowed"); + return 0; + } + + char *cache_key = url_encode_key(url); + if (!cache_key) { + send_response("Failed to generate cache key"); + return 0; + } + + char *cached_data = NULL; + size_t cached_size = 0; + int cache_ttl = get_cache_ttl_image(); + + if (cache_get(cache_key, cache_ttl, &cached_data, &cached_size) == 0) { + char content_type[64] = {0}; + + const char *ext = strrchr(url, '.'); + if (ext) { + if (strcasecmp(ext, ".png") == 0) { + strncpy(content_type, "image/png", sizeof(content_type) - 1); + } else if (strcasecmp(ext, ".gif") == 0) { + strncpy(content_type, "image/gif", sizeof(content_type) - 1); + } else if (strcasecmp(ext, ".webp") == 0) { + strncpy(content_type, "image/webp", sizeof(content_type) - 1); + } else if (strcasecmp(ext, ".svg") == 0) { + strncpy(content_type, "image/svg+xml", sizeof(content_type) - 1); + } else if (strcasecmp(ext, ".ico") == 0) { + strncpy(content_type, "image/x-icon", sizeof(content_type) - 1); + } else if (strcasecmp(ext, ".bmp") == 0) { + strncpy(content_type, "image/bmp", sizeof(content_type) - 1); + } + } + + if (strlen(content_type) == 0) { + strncpy(content_type, "image/jpeg", sizeof(content_type) - 1); + } + + serve_data(cached_data, cached_size, content_type); + free(cached_data); + free(cache_key); return 0; } CURL *curl = curl_easy_init(); if (!curl) { + free(cache_key); send_response("Failed to initialize curl"); return 0; } @@ -102,6 +267,7 @@ int image_proxy_handler(UrlParams *params) { if (!buf.data) { curl_easy_cleanup(curl); + free(cache_key); send_response("Memory allocation failed"); return 0; } @@ -111,6 +277,10 @@ int image_proxy_handler(UrlParams *params) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + curl_easy_setopt(curl, CURLOPT_USERAGENT, + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/120.0.0.0 Safari/537.36"); apply_proxy_settings(curl); CURLcode res = curl_easy_perform(curl); @@ -130,14 +300,27 @@ int image_proxy_handler(UrlParams *params) { if (res != CURLE_OK || response_code != 200) { free(buf.data); + free(cache_key); send_response("Failed to fetch image"); return 0; } + if (strlen(content_type) == 0 || + strncmp(content_type, "image/", 6) != 0) { + free(buf.data); + free(cache_key); + send_response("Invalid content type"); + return 0; + } + const char *mime_type = strlen(content_type) > 0 ? content_type : "image/jpeg"; + + cache_set(cache_key, buf.data, buf.size); + serve_data(buf.data, buf.size, mime_type); free(buf.data); + free(cache_key); return 0; } -- cgit v1.2.3