aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Cache/Cache.c5
-rw-r--r--src/Cache/Cache.h2
-rw-r--r--src/Config.c2
-rw-r--r--src/Config.h2
-rw-r--r--src/Main.c2
-rw-r--r--src/Routes/ImageProxy.c211
-rw-r--r--templates/results.html2
7 files changed, 211 insertions, 15 deletions
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 <arpa/inet.h>
#include <curl/curl.h>
+#include <curl/urlapi.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <openssl/evp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/socket.h>
#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;
}
diff --git a/templates/results.html b/templates/results.html
index 5207057..57c2265 100644
--- a/templates/results.html
+++ b/templates/results.html
@@ -68,7 +68,7 @@
<div class="result">
<div class="result-header">
<div class="result-favicon"
- style="background-image: url('https://{{result[4]}}/favicon.ico'), url('https://{{result[4]}}/favicon.png');">
+ style="background-image: url('/proxy?url=https://{{result[4]}}/favicon.ico'), url('/proxy?url=https://{{result[4]}}/favicon.png');">
</div>
<span class="url">
{{result[1]}}