feat: changes to image proxy, proxy favicons
This commit is contained in:
parent
86a9ebb90a
commit
1b9187b153
7 changed files with 212 additions and 16 deletions
|
|
@ -11,15 +11,20 @@
|
||||||
static char cache_dir[BUFFER_SIZE_MEDIUM] = {0};
|
static char cache_dir[BUFFER_SIZE_MEDIUM] = {0};
|
||||||
static int cache_ttl_search_val = DEFAULT_CACHE_TTL_SEARCH;
|
static int cache_ttl_search_val = DEFAULT_CACHE_TTL_SEARCH;
|
||||||
static int cache_ttl_infobox_val = DEFAULT_CACHE_TTL_INFOBOX;
|
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_search(int ttl) { cache_ttl_search_val = ttl; }
|
||||||
|
|
||||||
void set_cache_ttl_infobox(int ttl) { cache_ttl_infobox_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_search(void) { return cache_ttl_search_val; }
|
||||||
|
|
||||||
int get_cache_ttl_infobox(void) { return cache_ttl_infobox_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) {
|
static void md5_hash(const char *str, char *output) {
|
||||||
unsigned char hash[EVP_MAX_MD_SIZE];
|
unsigned char hash[EVP_MAX_MD_SIZE];
|
||||||
unsigned int hash_len;
|
unsigned int hash_len;
|
||||||
|
|
|
||||||
|
|
@ -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_search(int ttl);
|
||||||
void set_cache_ttl_infobox(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_search(void);
|
||||||
int get_cache_ttl_infobox(void);
|
int get_cache_ttl_infobox(void);
|
||||||
|
int get_cache_ttl_image(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,8 @@ int load_config(const char *filename, Config *config) {
|
||||||
config->cache_ttl_search = atoi(value);
|
config->cache_ttl_search = atoi(value);
|
||||||
} else if (strcmp(key, "ttl_infobox") == 0) {
|
} else if (strcmp(key, "ttl_infobox") == 0) {
|
||||||
config->cache_ttl_infobox = atoi(value);
|
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) {
|
} else if (strcmp(section, "engines") == 0) {
|
||||||
if (strcmp(key, "engines") == 0) {
|
if (strcmp(key, "engines") == 0) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#define DEFAULT_CACHE_DIR "/tmp/omnisearch_cache"
|
#define DEFAULT_CACHE_DIR "/tmp/omnisearch_cache"
|
||||||
#define DEFAULT_CACHE_TTL_SEARCH 3600
|
#define DEFAULT_CACHE_TTL_SEARCH 3600
|
||||||
#define DEFAULT_CACHE_TTL_INFOBOX 86400
|
#define DEFAULT_CACHE_TTL_INFOBOX 86400
|
||||||
|
#define DEFAULT_CACHE_TTL_IMAGE 604800
|
||||||
#define DEFAULT_MAX_PROXY_RETRIES 3
|
#define DEFAULT_MAX_PROXY_RETRIES 3
|
||||||
|
|
||||||
#define BUFFER_SIZE_SMALL 256
|
#define BUFFER_SIZE_SMALL 256
|
||||||
|
|
@ -42,6 +43,7 @@ typedef struct {
|
||||||
char cache_dir[512];
|
char cache_dir[512];
|
||||||
int cache_ttl_search;
|
int cache_ttl_search;
|
||||||
int cache_ttl_infobox;
|
int cache_ttl_infobox;
|
||||||
|
int cache_ttl_image;
|
||||||
char engines[512];
|
char engines[512];
|
||||||
} Config;
|
} Config;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ int main() {
|
||||||
.cache_dir = DEFAULT_CACHE_DIR,
|
.cache_dir = DEFAULT_CACHE_DIR,
|
||||||
.cache_ttl_search = DEFAULT_CACHE_TTL_SEARCH,
|
.cache_ttl_search = DEFAULT_CACHE_TTL_SEARCH,
|
||||||
.cache_ttl_infobox = DEFAULT_CACHE_TTL_INFOBOX,
|
.cache_ttl_infobox = DEFAULT_CACHE_TTL_INFOBOX,
|
||||||
|
.cache_ttl_image = DEFAULT_CACHE_TTL_IMAGE,
|
||||||
.engines = ""};
|
.engines = ""};
|
||||||
|
|
||||||
if (load_config("config.ini", &cfg) != 0) {
|
if (load_config("config.ini", &cfg) != 0) {
|
||||||
|
|
@ -72,6 +73,7 @@ int main() {
|
||||||
|
|
||||||
set_cache_ttl_search(cfg.cache_ttl_search);
|
set_cache_ttl_search(cfg.cache_ttl_search);
|
||||||
set_cache_ttl_infobox(cfg.cache_ttl_infobox);
|
set_cache_ttl_infobox(cfg.cache_ttl_infobox);
|
||||||
|
set_cache_ttl_image(cfg.cache_ttl_image);
|
||||||
|
|
||||||
if (cfg.proxy_list_file[0] != '\0') {
|
if (cfg.proxy_list_file[0] != '\0') {
|
||||||
if (load_proxy_list(cfg.proxy_list_file) < 0) {
|
if (load_proxy_list(cfg.proxy_list_file) < 0) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
#include "ImageProxy.h"
|
#include "ImageProxy.h"
|
||||||
|
#include "../Cache/Cache.h"
|
||||||
#include "../Proxy/Proxy.h"
|
#include "../Proxy/Proxy.h"
|
||||||
|
#include <arpa/inet.h>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
#include <curl/urlapi.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#define MAX_IMAGE_SIZE (10 * 1024 * 1024)
|
#define MAX_IMAGE_SIZE (10 * 1024 * 1024)
|
||||||
|
|
||||||
|
|
@ -13,7 +20,97 @@ typedef struct {
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
} MemoryBuffer;
|
} 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) {
|
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, "://");
|
const char *protocol = strstr(url, "://");
|
||||||
if (!protocol) {
|
if (!protocol) {
|
||||||
protocol = url;
|
protocol = url;
|
||||||
|
|
@ -30,23 +127,20 @@ static int is_allowed_domain(const char *url) {
|
||||||
}
|
}
|
||||||
strncpy(host, protocol, host_len);
|
strncpy(host, protocol, host_len);
|
||||||
|
|
||||||
const char *allowed_domains[] = {"mm.bing.net", "th.bing.com", NULL};
|
char *colon = strchr(host, ':');
|
||||||
|
if (colon) {
|
||||||
for (int i = 0; allowed_domains[i] != NULL; i++) {
|
*colon = '\0';
|
||||||
size_t domain_len = strlen(allowed_domains[i]);
|
|
||||||
size_t host_str_len = strlen(host);
|
|
||||||
|
|
||||||
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,
|
static size_t write_callback(void *contents, size_t size, size_t nmemb,
|
||||||
void *userp) {
|
void *userp) {
|
||||||
size_t realsize = size * nmemb;
|
size_t realsize = size * nmemb;
|
||||||
|
|
@ -73,6 +167,31 @@ static size_t write_callback(void *contents, size_t size, size_t nmemb,
|
||||||
return realsize;
|
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) {
|
int image_proxy_handler(UrlParams *params) {
|
||||||
const char *url = NULL;
|
const char *url = NULL;
|
||||||
for (int i = 0; i < params->count; i++) {
|
for (int i = 0; i < params->count; i++) {
|
||||||
|
|
@ -87,13 +206,59 @@ int image_proxy_handler(UrlParams *params) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_allowed_domain(url)) {
|
int domain_check = is_allowed_domain(url);
|
||||||
send_response("Domain not allowed");
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CURL *curl = curl_easy_init();
|
CURL *curl = curl_easy_init();
|
||||||
if (!curl) {
|
if (!curl) {
|
||||||
|
free(cache_key);
|
||||||
send_response("Failed to initialize curl");
|
send_response("Failed to initialize curl");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -102,6 +267,7 @@ int image_proxy_handler(UrlParams *params) {
|
||||||
|
|
||||||
if (!buf.data) {
|
if (!buf.data) {
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
free(cache_key);
|
||||||
send_response("Memory allocation failed");
|
send_response("Memory allocation failed");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +277,10 @@ int image_proxy_handler(UrlParams *params) {
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf);
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
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);
|
apply_proxy_settings(curl);
|
||||||
|
|
||||||
CURLcode res = curl_easy_perform(curl);
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
|
@ -130,14 +300,27 @@ int image_proxy_handler(UrlParams *params) {
|
||||||
|
|
||||||
if (res != CURLE_OK || response_code != 200) {
|
if (res != CURLE_OK || response_code != 200) {
|
||||||
free(buf.data);
|
free(buf.data);
|
||||||
|
free(cache_key);
|
||||||
send_response("Failed to fetch image");
|
send_response("Failed to fetch image");
|
||||||
return 0;
|
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 =
|
const char *mime_type =
|
||||||
strlen(content_type) > 0 ? content_type : "image/jpeg";
|
strlen(content_type) > 0 ? content_type : "image/jpeg";
|
||||||
|
|
||||||
|
cache_set(cache_key, buf.data, buf.size);
|
||||||
|
|
||||||
serve_data(buf.data, buf.size, mime_type);
|
serve_data(buf.data, buf.size, mime_type);
|
||||||
|
|
||||||
free(buf.data);
|
free(buf.data);
|
||||||
|
free(cache_key);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
<div class="result">
|
<div class="result">
|
||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<div class="result-favicon"
|
<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>
|
</div>
|
||||||
<span class="url">
|
<span class="url">
|
||||||
{{result[1]}}
|
{{result[1]}}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue