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 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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
if (host_str_len >= domain_len) {
|
||||
const char *suffix = host + host_str_len - domain_len;
|
||||
if (strcmp(suffix, allowed_domains[i]) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
char *colon = strchr(host, ':');
|
||||
if (colon) {
|
||||
*colon = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (is_private_hostname(host)) {
|
||||
curl_url_cleanup(h);
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]}}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue