From 497810ab4e439b8cf547991311a22621130be7d2 Mon Sep 17 00:00:00 2001 From: bol-van Date: Fri, 1 Aug 2025 12:48:16 +0300 Subject: [PATCH] tpws: special case for ip looking hostnames --- tpws/helpers.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ tpws/helpers.h | 2 ++ tpws/hostlist.c | 13 ++++++------ tpws/hostlist.h | 2 +- tpws/pools.c | 5 +++-- tpws/pools.h | 1 + tpws/tamper.c | 40 +++++++++++++++++++---------------- tpws/tamper.h | 3 ++- tpws/tpws_conn.c | 4 ++-- 9 files changed, 95 insertions(+), 30 deletions(-) diff --git a/tpws/helpers.c b/tpws/helpers.c index 6032841..8171c68 100644 --- a/tpws/helpers.c +++ b/tpws/helpers.c @@ -123,6 +123,61 @@ void expand_bits(void *target, const void *source, unsigned int source_bitlen, u if ((bitlen &= 7)) ((uint8_t*)target)[bytelen] = ((uint8_t*)source)[bytelen] & (~((1 << (8-bitlen)) - 1)); } +// " [fd00::1]" => "fd00::1" +// "[fd00::1]:8000" => "fd00::1" +// "127.0.0.1" => "127.0.0.1" +// " 127.0.0.1:8000" => "127.0.0.1" +// " vk.com:8000" => "vk.com" +// return value: true - host is ip addr +bool strip_host_to_ip(char *host) +{ + size_t l; + char *h,*p; + uint8_t addr[16]; + + for (h = host ; *h==' ' || *h=='\t' ; h++); + l = strlen(h); + if (l>=2) + { + if (*h=='[') + { + // ipv6 ? + for (p=++h ; *p && *p!=']' ; p++); + if (*p==']') + { + l = p-h; + memmove(host,h,l); + host[l]=0; + return inet_pton(AF_INET6, host, addr)>0; + } + } + else + { + if (inet_pton(AF_INET6, h, addr)>0) + { + // ipv6 ? + if (host!=h) + { + l = strlen(h); + memmove(host,h,l); + host[l]=0; + } + return true; + } + else + { + // ipv4 ? + for (p=h ; *p && *p!=':' ; p++); + l = p-h; + if (host!=h) memmove(host,h,l); + host[l]=0; + return inet_pton(AF_INET, host, addr)>0; + } + } + } + return false; +} + void ntop46(const struct sockaddr *sa, char *str, size_t len) { if (!len) return; diff --git a/tpws/helpers.h b/tpws/helpers.h index fd57de4..c61a974 100644 --- a/tpws/helpers.h +++ b/tpws/helpers.h @@ -31,6 +31,8 @@ bool append_to_list_file(const char *filename, const char *s); void expand_bits(void *target, const void *source, unsigned int source_bitlen, unsigned int target_bytelen); +bool strip_host_to_ip(char *host); + void ntop46(const struct sockaddr *sa, char *str, size_t len); void ntop46_port(const struct sockaddr *sa, char *str, size_t len); void print_sockaddr(const struct sockaddr *sa); diff --git a/tpws/hostlist.c b/tpws/hostlist.c index b1bc8fd..631f41f 100644 --- a/tpws/hostlist.c +++ b/tpws/hostlist.c @@ -170,7 +170,7 @@ bool LoadAllHostLists() -static bool SearchHostList(hostlist_pool *hostlist, const char *host) +static bool SearchHostList(hostlist_pool *hostlist, const char *host, bool no_match_subdomains) { if (hostlist) { @@ -195,6 +195,7 @@ static bool SearchHostList(hostlist_pool *hostlist, const char *host) } else VPRINT("negative\n"); + if (no_match_subdomains) break; p = strchr(p, '.'); if (p) p++; bHostFull = false; @@ -220,7 +221,7 @@ bool HostlistsReloadCheckForProfile(const struct desync_profile *dp) return HostlistsReloadCheck(&dp->hl_collection) && HostlistsReloadCheck(&dp->hl_collection_exclude); } // return : true = apply fooling, false = do not apply -static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, const struct hostlist_collection_head *hostlists_exclude, const char *host, bool *excluded, bool bSkipReloadCheck) +static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, const struct hostlist_collection_head *hostlists_exclude, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck) { struct hostlist_item *item; @@ -233,7 +234,7 @@ static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, con LIST_FOREACH(item, hostlists_exclude, next) { VPRINT("[%s] exclude ", item->hfile->filename ? item->hfile->filename : "fixed"); - if (SearchHostList(item->hfile->hostlist, host)) + if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains)) { if (excluded) *excluded = true; return false; @@ -245,7 +246,7 @@ static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, con LIST_FOREACH(item, hostlists, next) { VPRINT("[%s] include ", item->hfile->filename ? item->hfile->filename : "fixed"); - if (SearchHostList(item->hfile->hostlist, host)) + if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains)) return true; } return false; @@ -255,10 +256,10 @@ static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, con // return : true = apply fooling, false = do not apply -bool HostlistCheck(const struct desync_profile *dp, const char *host, bool *excluded, bool bSkipReloadCheck) +bool HostlistCheck(const struct desync_profile *dp, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck) { VPRINT("* hostlist check for profile %d\n",dp->n); - return HostlistCheck_(&dp->hl_collection, &dp->hl_collection_exclude, host, excluded, bSkipReloadCheck); + return HostlistCheck_(&dp->hl_collection, &dp->hl_collection_exclude, host, no_match_subdomains, excluded, bSkipReloadCheck); } diff --git a/tpws/hostlist.h b/tpws/hostlist.h index b11aa3f..954d0cb 100644 --- a/tpws/hostlist.h +++ b/tpws/hostlist.h @@ -9,7 +9,7 @@ bool AppendHostList(hostlist_pool **hostlist, const char *filename); bool LoadAllHostLists(); bool NonEmptyHostlist(hostlist_pool **hostlist); // return : true = apply fooling, false = do not apply -bool HostlistCheck(const struct desync_profile *dp,const char *host, bool *excluded, bool bSkipReloadCheck); +bool HostlistCheck(const struct desync_profile *dp,const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck); struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename); bool HostlistsReloadCheckForProfile(const struct desync_profile *dp); void HostlistsDebug(); diff --git a/tpws/pools.c b/tpws/pools.c index 3cd0334..b856903 100644 --- a/tpws/pools.c +++ b/tpws/pools.c @@ -616,6 +616,7 @@ static void ipcache_item_init(ip_cache_item *item) { ipcache_item_touch(item); item->hostname = NULL; + item->hostname_is_ip = false; } static void ipcache_item_destroy(ip_cache_item *item) { @@ -675,7 +676,7 @@ static void ipcache4Print(ip_cache4 *ipcache) { *s_ip=0; inet_ntop(AF_INET, &ipc->key.addr, s_ip, sizeof(s_ip)); - printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last)); + printf("%s : hostname=%s hostname_is_ip=%u now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", ipc->data.hostname_is_ip, (unsigned long long)(now-ipc->data.last)); } } @@ -732,7 +733,7 @@ static void ipcache6Print(ip_cache6 *ipcache) { *s_ip=0; inet_ntop(AF_INET6, &ipc->key.addr, s_ip, sizeof(s_ip)); - printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last)); + printf("%s : hostname=%s hostname_is_ip=%u now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", ipc->data.hostname_is_ip, (unsigned long long)(now-ipc->data.last)); } } diff --git a/tpws/pools.h b/tpws/pools.h index 67e1612..361a66c 100644 --- a/tpws/pools.h +++ b/tpws/pools.h @@ -179,6 +179,7 @@ typedef struct ip_cache_item { time_t last; char *hostname; + bool hostname_is_ip; } ip_cache_item; typedef struct ip_cache4 { diff --git a/tpws/tamper.c b/tpws/tamper.c index cd8f7ae..8e91756 100644 --- a/tpws/tamper.c +++ b/tpws/tamper.c @@ -91,7 +91,7 @@ static void TLSDebug(const uint8_t *tls,size_t sz) TLSDebugHandshake(tls+5,sz-5); } -bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname) +bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname, bool hostname_is_ip) { if (!params.cache_hostname) return true; @@ -109,11 +109,12 @@ bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, c DLOG_ERR("ipcache_put_hostname: out of memory\n"); return false; } - VPRINT("hostname cached: %s\n", hostname); + ipc->hostname_is_ip = hostname_is_ip; + VPRINT("hostname cached (is_ip=%u): %s\n", hostname_is_ip, hostname); } return true; } -static bool ipcache_get_hostname(const struct in_addr *a4, const struct in6_addr *a6, char *hostname, size_t hostname_buf_len) +static bool ipcache_get_hostname(const struct in_addr *a4, const struct in6_addr *a6, char *hostname, size_t hostname_buf_len, bool *hostname_is_ip) { if (!params.cache_hostname) { @@ -128,15 +129,16 @@ static bool ipcache_get_hostname(const struct in_addr *a4, const struct in6_addr } if (ipc->hostname) { - VPRINT("got cached hostname: %s\n", ipc->hostname); + VPRINT("got cached hostname (is_ip=%u): %s\n", ipc->hostname_is_ip, ipc->hostname); snprintf(hostname,hostname_buf_len,"%s",ipc->hostname); + if (hostname_is_ip) *hostname_is_ip = ipc->hostname_is_ip; } else *hostname = 0; return true; } -static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto) +static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, bool bNoSubdom, t_l7proto l7proto) { bool bHostlistsEmpty; @@ -167,11 +169,11 @@ static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, con return true; else if (hostname) // if hostlists are present profile matches only if hostname is known and satisfy profile hostlists - return HostlistCheck(dp, hostname, NULL, true); + return HostlistCheck(dp, hostname, bNoSubdom, NULL, true); return false; } -static struct desync_profile *dp_find(struct desync_profile_list_head *head, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto) +static struct desync_profile *dp_find(struct desync_profile_list_head *head, const struct sockaddr *dest, const char *hostname, bool bNoSubdom, t_l7proto l7proto) { struct desync_profile_list *dpl; if (params.debug) @@ -182,7 +184,7 @@ static struct desync_profile *dp_find(struct desync_profile_list_head *head, con } LIST_FOREACH(dpl, head, next) { - if (dp_match(&dpl->dp,dest,hostname,l7proto)) + if (dp_match(&dpl->dp,dest,hostname,bNoSubdom,l7proto)) { VPRINT("desync profile %d matches\n",dpl->dp.n); return &dpl->dp; @@ -198,11 +200,11 @@ void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest) if (!ctrack->hostname) { char host[256]; - if (ipcache_get_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , host, sizeof(host)) && *host) + if (ipcache_get_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , host, sizeof(host), &ctrack->hostname_is_ip) && *host) if (!(ctrack->hostname=strdup(host))) DLOG_ERR("hostname dup : out of memory"); } - ctrack->dp = dp_find(¶ms.desync_profiles, dest, ctrack->hostname, ctrack->l7proto); + ctrack->dp = dp_find(¶ms.desync_profiles, dest, ctrack->hostname, ctrack->hostname_is_ip, ctrack->l7proto); } @@ -213,7 +215,7 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, uint8_t *p, *pp, *pHost = NULL; size_t method_len = 0, pos, tpos, orig_size=*size; const char *method; - bool bHaveHost = false; + bool bHaveHost = false, bHostIsIp = false; char *pc, Host[256]; t_l7proto l7proto; @@ -271,8 +273,9 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, if (bHaveHost) { + bHostIsIp = strip_host_to_ip(Host); VPRINT("request hostname: %s\n", Host); - if (!ipcache_put_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , Host)) + if (!ipcache_put_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , Host, bHostIsIp)) DLOG_ERR("ipcache_put_hostname: out of memory"); } @@ -293,6 +296,7 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, DLOG_ERR("strdup hostname : out of memory\n"); return; } + ctrack->hostname_is_ip = bHostIsIp; ctrack->hostname_discovered = true; } @@ -312,7 +316,7 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment, if (bHaveHost && !ctrack->b_host_checked) { bool bHostExcluded; - ctrack->b_host_matches = HostlistCheck(ctrack->dp, Host, &bHostExcluded, false); + ctrack->b_host_matches = HostlistCheck(ctrack->dp, Host, bHostIsIp, &bHostExcluded, false); ctrack->b_host_checked = true; if (!ctrack->b_host_matches) ctrack->b_ah_check = !bHostExcluded; @@ -542,7 +546,7 @@ static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const ch } } -static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname, const char *client_ip_port, t_l7proto l7proto) +static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname, bool bNoSubdom, const char *client_ip_port, t_l7proto l7proto) { hostfail_pool *fail_counter; @@ -566,7 +570,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname VPRINT("auto hostlist (profile %d) : rechecking %s to avoid duplicates\n", dp->n, hostname); bool bExcluded=false; - if (!HostlistCheck(dp, hostname, &bExcluded, false) && !bExcluded) + if (!HostlistCheck(dp, hostname, bNoSubdom, &bExcluded, false) && !bExcluded) { VPRINT("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto->filename); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : adding to %s", hostname, dp->n, client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename); @@ -626,7 +630,7 @@ void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment // received not http reply. do not monitor this connection anymore VPRINT("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname); } - if (bFail) auto_hostlist_failed(ctrack->dp, ctrack->hostname, client_ip_port, ctrack->l7proto); + if (bFail) auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto); } if (!bFail) auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname, client_ip_port, ctrack->l7proto); } @@ -651,7 +655,7 @@ void rst_in(t_ctrack *ctrack, const struct sockaddr *client) { VPRINT("incoming RST detected for hostname %s\n", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : incoming RST", ctrack->hostname, ctrack->dp->n, client_ip_port, l7proto_str(ctrack->l7proto)); - auto_hostlist_failed(ctrack->dp, ctrack->hostname, client_ip_port, ctrack->l7proto); + auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto); } } } @@ -674,7 +678,7 @@ void hup_out(t_ctrack *ctrack, const struct sockaddr *client) // local leg dropped connection after first request. probably due to timeout. VPRINT("local leg closed connection after first request (timeout ?). hostname: %s\n", ctrack->hostname); HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : client closed connection without server reply", ctrack->hostname, ctrack->dp->n, client_ip_port, l7proto_str(ctrack->l7proto)); - auto_hostlist_failed(ctrack->dp, ctrack->hostname, client_ip_port, ctrack->l7proto); + auto_hostlist_failed(ctrack->dp, ctrack->hostname, ctrack->hostname_is_ip, client_ip_port, ctrack->l7proto); } } } diff --git a/tpws/tamper.h b/tpws/tamper.h index 73bd88e..5b5aabf 100644 --- a/tpws/tamper.h +++ b/tpws/tamper.h @@ -16,12 +16,13 @@ typedef struct bool bTamperInCutoff; bool b_host_checked,b_host_matches,b_ah_check; bool hostname_discovered; + bool hostname_is_ip; char *hostname; struct desync_profile *dp; // desync profile cache } t_ctrack; void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest); -bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname); +bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname, bool hostname_is_ip); void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags); void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment,size_t segment_buffer_size,size_t *size); diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index c1106de..1d999b8 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -495,7 +495,7 @@ static bool connect_remote_conn(tproxy_conn_t *conn) int mss=0; if (conn->track.hostname) - if (!ipcache_put_hostname(conn->dest.sa_family==AF_INET ? &((struct sockaddr_in*)&conn->dest)->sin_addr : NULL, conn->dest.sa_family==AF_INET6 ? &((struct sockaddr_in6*)&conn->dest)->sin6_addr : NULL , conn->track.hostname)) + if (!ipcache_put_hostname(conn->dest.sa_family==AF_INET ? &((struct sockaddr_in*)&conn->dest)->sin_addr : NULL, conn->dest.sa_family==AF_INET6 ? &((struct sockaddr_in6*)&conn->dest)->sin6_addr : NULL , conn->track.hostname, false)) DLOG_ERR("ipcache_put_hostname: out of memory"); apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest); @@ -507,7 +507,7 @@ static bool connect_remote_conn(tproxy_conn_t *conn) if (conn->track.hostname) { bool bHostExcluded; - conn->track.b_host_matches = HostlistCheck(conn->track.dp, conn->track.hostname, &bHostExcluded, false); + conn->track.b_host_matches = HostlistCheck(conn->track.dp, conn->track.hostname, conn->track.hostname_is_ip, &bHostExcluded, false); conn->track.b_host_checked = true; if (!conn->track.b_host_matches) {