From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server-vie001.gnuweeb.org X-Spam-Level: X-Spam-Status: No, score=-1.2 required=5.0 tests=ALL_TRUSTED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,URIBL_DBL_BLOCKED_OPENDNS, URIBL_ZEN_BLOCKED_OPENDNS autolearn=ham autolearn_force=no version=3.4.6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gnuweeb.org; s=new2025; t=1756391838; bh=VsfQBB/CV8krcYrta9SNLfmN1Rg9+okhYtNN+gTTkb8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Transfer-Encoding:Message-ID:Date:From: Reply-To:Subject:To:Cc:In-Reply-To:References:Resent-Date: Resent-From:Resent-To:Resent-Cc:User-Agent:Content-Type: Content-Transfer-Encoding; b=IDFiWCjtTCs/OPfTYtW1z8vMkFmLTpqWuiMEOGgkrHe/R4mNllP3w02ZjyTgOO582 gEQpR1qBGlwbZYKGq5YIEvsVYDalUcHzEbjrFVN4jBf3V8aLrbx5LN2QCWUqqnfSP+ qrsaU/FDYQolbrH3QvYPt8ilokfRExTfpDBl/qroYmsCmkst3RsXyNKveF2TByMQkk fvEaPwKfRzI8AEv0znLssdTp5jaF2SDdFFPTpxyLVJ8OfaqdivCkTAI1umjXwmewtm prB8y/Dr53jnsPAwMWTW2o06ePAGrX9biU/KsGKvpqApM9m5SwOQ5HgrHH7SOYcOeB JDkUj+siuxLaw== Received: from zero (unknown [182.253.228.107]) by server-vie001.gnuweeb.org (Postfix) with ESMTPSA id 1274A3127F72; Thu, 28 Aug 2025 14:37:12 +0000 (UTC) From: Ahmad Gani To: Ammar Faizi Cc: Ahmad Gani , Alviro Iskandar Setiawan , GNU/Weeb Mailing List Subject: [PATCH gwproxy v6 07/11] dns: refactor dns.c to integrate the dns parser Date: Thu, 28 Aug 2025 21:34:29 +0700 Message-ID: <20250828143444.540247-8-reyuki@gnuweeb.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250828143444.540247-1-reyuki@gnuweeb.org> References: <20250828143444.540247-1-reyuki@gnuweeb.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: remove threads support and eventfd in dns.c Signed-off-by: Ahmad Gani --- Makefile | 2 +- src/gwproxy/dns.c | 394 ++++++++++++-------------------------- src/gwproxy/dns.h | 33 ++-- src/gwproxy/ev/epoll.c | 32 +++- src/gwproxy/ev/io_uring.c | 6 +- src/gwproxy/gwproxy.c | 11 +- src/gwproxy/gwproxy.h | 1 - src/gwproxy/tests/dns.c | 110 +++++------ 8 files changed, 222 insertions(+), 367 deletions(-) diff --git a/Makefile b/Makefile index 3ac35f052793..eb750e0fc31c 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ LIBGWPSOCKS5_TEST_CC_SOURCES = $(GWPROXY_DIR)/tests/socks5.c LIBGWPSOCKS5_TEST_OBJECTS = $(LIBGWPSOCKS5_TEST_CC_SOURCES:%.c=%.c.o) LIBGWDNS_TARGET = libgwdns.so -LIBGWDNS_CC_SOURCES = $(GWPROXY_DIR)/dns.c $(GWPROXY_DIR)/dns_cache.c +LIBGWDNS_CC_SOURCES = $(GWPROXY_DIR)/dns.c $(GWPROXY_DIR)/dnsparser.c $(GWPROXY_DIR)/dns_cache.c $(GWPROXY_DIR)/net.c LIBGWDNS_OBJECTS = $(LIBGWDNS_CC_SOURCES:%.c=%.c.o) LIBGWDNS_TEST_TARGET = $(GWPROXY_DIR)/tests/dns.t LIBGWDNS_TEST_CC_SOURCES = $(GWPROXY_DIR)/tests/dns.c diff --git a/src/gwproxy/dns.c b/src/gwproxy/dns.c index 1e271a388493..fea81109ca6d 100644 --- a/src/gwproxy/dns.c +++ b/src/gwproxy/dns.c @@ -23,46 +23,38 @@ #include #include +#include struct gwp_dns_ctx; -struct gwp_dns_wrk { - struct gwp_dns_ctx *ctx; - uint32_t id; - pthread_t thread; -}; - struct gwp_dns_ctx { + int nr_entries; + int entry_cap; + struct gwp_dns_entry **entries; + int sockfd; + int ns_family; + struct gwp_sockaddr ns_addr; + uint8_t ns_addrlen; volatile bool should_stop; pthread_mutex_t lock; - pthread_cond_t cond; - uint32_t nr_sleeping; - uint32_t nr_entries; - struct gwp_dns_entry *head; - struct gwp_dns_entry *tail; - struct gwp_dns_wrk *workers; struct gwp_dns_cache *cache; time_t last_scan; struct gwp_dns_cfg cfg; }; -static void put_all_entries(struct gwp_dns_entry *head) +void cp_nsaddr(struct gwp_dns_ctx *ctx, struct gwp_sockaddr *addr, uint8_t *addrlen) { - struct gwp_dns_entry *e, *next; - - for (e = head; e; e = next) { - next = e->next; - gwp_dns_entry_put(e); - } + *addr = ctx->ns_addr; + *addrlen = ctx->ns_addrlen; } -static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, +__attribute__((unused)) +static bool iterate_addr_list(struct gwdns_addrinfo_node *res, struct gwp_sockaddr *gs, uint32_t rt) { - struct addrinfo *ai; + struct gwdns_addrinfo_node *ai; - if (!res) - return false; + assert(res); /* * Handle IPV4_ONLY and IPV6_ONLY cases together. @@ -75,9 +67,9 @@ static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, if (ai->ai_family != fm) continue; if (fm == AF_INET) - gs->i4 = *(struct sockaddr_in *)ai->ai_addr; + gs->i4 = ai->ai_addr.i4; else - gs->i6 = *(struct sockaddr_in6 *)ai->ai_addr; + gs->i6 = ai->ai_addr.i6; return true; } return false; @@ -91,19 +83,19 @@ static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, int prm = (rt == GWP_DNS_RESTYP_PREFER_IPV6) ? AF_INET6 : AF_INET; int sec = (prm == AF_INET6) ? AF_INET : AF_INET6; - struct sockaddr *fallback = NULL; + struct gwp_sockaddr *fallback = NULL; for (ai = res; ai; ai = ai->ai_next) { if (ai->ai_family != prm) { if (ai->ai_family == sec && !fallback) - fallback = ai->ai_addr; + fallback = &ai->ai_addr; continue; } if (prm == AF_INET) - gs->i4 = *(struct sockaddr_in *)ai->ai_addr; + gs->i4 = ai->ai_addr.i4; else - gs->i6 = *(struct sockaddr_in6 *)ai->ai_addr; + gs->i6 = ai->ai_addr.i6; return true; } @@ -111,9 +103,9 @@ static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, return false; if (sec == AF_INET) - gs->i4 = *(struct sockaddr_in *)fallback; + gs->i4 = fallback->i4; else - gs->i6 = *(struct sockaddr_in6 *)fallback; + gs->i6 = fallback->i6; return true; } @@ -123,10 +115,10 @@ static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, */ for (ai = res; ai; ai = ai->ai_next) { if (ai->ai_family == AF_INET) { - gs->i4 = *(struct sockaddr_in *)ai->ai_addr; + gs->i4 = ai->ai_addr.i4; return true; } else if (ai->ai_family == AF_INET6) { - gs->i6 = *(struct sockaddr_in6 *)ai->ai_addr; + gs->i6 = ai->ai_addr.i6; return true; } } @@ -134,19 +126,7 @@ static bool iterate_addr_list(struct addrinfo *res, struct gwp_sockaddr *gs, return false; } -static void prep_hints(struct addrinfo *hints, uint32_t restyp) -{ - memset(hints, 0, sizeof(*hints)); - hints->ai_family = AF_UNSPEC; - hints->ai_socktype = SOCK_STREAM; - hints->ai_flags = AI_ADDRCONFIG; - - if (restyp == GWP_DNS_RESTYP_IPV4_ONLY) - hints->ai_family = AF_INET; - else if (restyp == GWP_DNS_RESTYP_IPV6_ONLY) - hints->ai_family = AF_INET6; -} - +__attribute__((unused)) static void try_pass_result_to_cache(struct gwp_dns_ctx *ctx, const char *name, const struct addrinfo *ai) { @@ -159,38 +139,40 @@ static void try_pass_result_to_cache(struct gwp_dns_ctx *ctx, const char *name, gwp_dns_cache_insert(ctx->cache, name, ai, time(NULL) + x); } -int gwp_dns_resolve(struct gwp_dns_ctx *ctx, const char *name, - const char *service, struct gwp_sockaddr *addr, - uint32_t restyp) -{ - struct addrinfo *res = NULL, hints; - bool found; - int r; - - prep_hints(&hints, restyp); - r = getaddrinfo(name, service, &hints, &res); - if (r) - return -r; +// static int gwp_dns_find_preferred_addr(struct gwp_dns_ctx *ctx, struct gwdns_addrinfo_node *ai, const char *name, +// struct gwp_sockaddr *addr, uint32_t restyp) +// { +// bool found; - found = iterate_addr_list(res, addr, restyp); - if (found) - try_pass_result_to_cache(ctx, name, res); +// found = iterate_addr_list(ai, addr, restyp); +// if (found) +// try_pass_result_to_cache(ctx, name, ai); - if (res) - freeaddrinfo(res); +// return found ? 0 : -EHOSTUNREACH; +// } - return found ? 0 : -EHOSTUNREACH; +static void _gwp_dns_entry_free(struct gwp_dns_entry *e) +{ + assert(e); + assert(e->udp_fd >= 0); + close(e->udp_fd); + free(e->name); + free(e); } -static void gwp_dns_entry_free(struct gwp_dns_entry *e) +void gwp_dns_entry_free(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e) { - if (!e) - return; + struct gwp_dns_entry *new_e; - assert(e->ev_fd >= 0); - close(e->ev_fd); - free(e->name); - free(e); + assert(e); + + new_e = ctx->entries[--ctx->nr_entries]; + assert(ctx->nr_entries == new_e->idx); + new_e->idx = e->idx; + ctx->entries[e->idx] = new_e; + ctx->entries[ctx->nr_entries] = NULL; + + _gwp_dns_entry_free(e); } /* @@ -203,6 +185,7 @@ static void gwp_dns_entry_free(struct gwp_dns_entry *e) * cache to find expired entries and delete them. * - Save the current time as the last scan time. */ +__attribute__((unused)) static void cond_scan_cache(struct gwp_dns_ctx *ctx) { if (!ctx->cache) @@ -223,146 +206,6 @@ static void cond_scan_cache(struct gwp_dns_ctx *ctx) ctx->last_scan = time(NULL); } -/* - * Must be called with ctx->lock held. - */ -static void wait_for_queue_entry(struct gwp_dns_ctx *ctx) -{ - ctx->nr_sleeping++; - pthread_cond_wait(&ctx->cond, &ctx->lock); - ctx->nr_sleeping--; - cond_scan_cache(ctx); -} - -/* - * Must be called with ctx->lock held. May release the lock, but - * it will reacquire it before returning. - */ -static void process_queue_entry_single(struct gwp_dns_ctx *ctx) -{ - struct gwp_dns_entry *e = ctx->head; - - if (!e) - return; - - e = ctx->head; - ctx->head = e->next; - if (!ctx->head) { - ctx->tail = NULL; - assert(ctx->nr_entries == 1); - } - - ctx->nr_entries--; - pthread_mutex_unlock(&ctx->lock); - - if (atomic_load(&e->refcnt) == 1) { - /* - * If the refcnt is 1, it means we are the last reference - * to this entry. The client no longer cares about the - * result. We can free it immediately. No need to resolve - * the query nor to signal the eventfd. - */ - gwp_dns_entry_free(e); - goto out; - } - - e->res = gwp_dns_resolve(ctx, e->name, e->service, &e->addr, ctx->cfg.restyp); - eventfd_write(e->ev_fd, 1); - gwp_dns_entry_put(e); -out: - pthread_mutex_lock(&ctx->lock); -} - -/* - * Must be called with ctx->lock held. May release the lock, but - * it will reacquire it before returning. - */ -static void process_queue_entry(struct gwp_dns_ctx *ctx) -{ - process_queue_entry_single(ctx); -} - -static void *gwp_dns_thread_entry(void *arg) -{ - struct gwp_dns_wrk *w = arg; - struct gwp_dns_ctx *ctx = w->ctx; - - pthread_mutex_lock(&ctx->lock); - while (!ctx->should_stop) { - if (ctx->head) - process_queue_entry(ctx); - else - wait_for_queue_entry(ctx); - } - pthread_mutex_unlock(&ctx->lock); - - return NULL; -} - -static void free_worker(struct gwp_dns_wrk *w) -{ - struct gwp_dns_ctx *ctx; - - if (!w) - return; - - ctx = w->ctx; - pthread_mutex_lock(&ctx->lock); - ctx->should_stop = true; - pthread_cond_broadcast(&ctx->cond); - pthread_mutex_unlock(&ctx->lock); - pthread_join(w->thread, NULL); -} - -static void free_workers(struct gwp_dns_ctx *ctx) -{ - uint32_t i; - - if (!ctx->workers) - return; - - for (i = 0; i < ctx->cfg.nr_workers; i++) - free_worker(&ctx->workers[i]); - - free(ctx->workers); - ctx->workers = NULL; -} - -static int init_workers(struct gwp_dns_ctx *ctx) -{ - struct gwp_dns_wrk *workers, *w; - uint32_t i; - int r; - - if (ctx->cfg.nr_workers == 0) - return -EINVAL; - - workers = calloc(ctx->cfg.nr_workers, sizeof(*workers)); - if (!workers) - return -ENOMEM; - - ctx->workers = workers; - for (i = 0; i < ctx->cfg.nr_workers; i++) { - w = &workers[i]; - w->ctx = ctx; - w->id = i; - r = pthread_create(&w->thread, NULL, gwp_dns_thread_entry, w); - if (r) { - r = -r; - goto out_err; - } - } - - return 0; - -out_err: - while (i--) - free_worker(&workers[i]); - free(workers); - ctx->workers = NULL; - return r; -} - static bool fetch_i4(struct gwp_dns_cache_entry *e, struct gwp_sockaddr *addr, uint16_t port) { @@ -499,33 +342,26 @@ int gwp_dns_ctx_init(struct gwp_dns_ctx **ctx_p, const struct gwp_dns_cfg *cfg) goto out_free_ctx; } - r = pthread_cond_init(&ctx->cond, NULL); - if (r) { - r = -r; - goto out_destroy_mutex; - } - r = init_cache(ctx); if (r) - goto out_destroy_cond; + goto out_destroy_mutex; - ctx->nr_sleeping = 0; - ctx->nr_entries = 0; - ctx->workers = NULL; - ctx->head = NULL; - ctx->tail = NULL; + r = convert_str_to_ssaddr(cfg->ns_addr_str, &ctx->ns_addr, 53); + if (r) + goto out_destroy_mutex; + ctx->ns_addrlen = ctx->ns_addr.sa.sa_family == AF_INET + ? sizeof(ctx->ns_addr.i4) + : sizeof(ctx->ns_addr.i6); ctx->should_stop = false; ctx->last_scan = time(NULL); - r = init_workers(ctx); - if (r) - goto out_free_cache; + ctx->nr_entries = 0; + ctx->entry_cap = DEFAULT_ENTRIES_CAP; + ctx->entries = malloc(ctx->entry_cap * sizeof(*ctx->entries)); + if (!ctx->entries) + goto out_destroy_mutex; *ctx_p = ctx; return 0; -out_free_cache: - free_cache(ctx->cache); -out_destroy_cond: - pthread_cond_destroy(&ctx->cond); out_destroy_mutex: pthread_mutex_destroy(&ctx->lock); out_free_ctx: @@ -534,51 +370,67 @@ out_free_ctx: return r; } -static void put_all_queued_entries(struct gwp_dns_ctx *ctx) +static void free_all_queued_entries(struct gwp_dns_ctx *ctx) { - put_all_entries(ctx->head); - ctx->head = ctx->tail = NULL; + int i; + for (i = 0; i < ctx->nr_entries; i++) { + struct gwp_dns_entry *e = ctx->entries[i]; + _gwp_dns_entry_free(e); + } + + free(ctx->entries); } void gwp_dns_ctx_free(struct gwp_dns_ctx *ctx) { - free_workers(ctx); pthread_mutex_destroy(&ctx->lock); - pthread_cond_destroy(&ctx->cond); - put_all_queued_entries(ctx); + free_all_queued_entries(ctx); free_cache(ctx->cache); free(ctx); } -static void push_queue(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e) +static bool realloc_entries(struct gwp_dns_ctx *ctx) { - pthread_mutex_lock(&ctx->lock); - if (ctx->tail) - ctx->tail->next = e; - else - ctx->head = e; - ctx->tail = e; - e->next = NULL; + struct gwp_dns_entry **tmp; + int new_cap; - ctx->nr_entries++; - if (ctx->nr_sleeping) - pthread_cond_signal(&ctx->cond); - pthread_mutex_unlock(&ctx->lock); + new_cap = ctx->entry_cap * 2; + tmp = realloc(ctx->entries, new_cap * sizeof(*tmp)); + if (!tmp) + return 1; + + ctx->entries = tmp; + ctx->entry_cap = new_cap; + + return 0; } struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx, const char *name, const char *service) { struct gwp_dns_entry *e; + uint16_t txid; size_t nl, sl; + ssize_t r; + + if (ctx->nr_entries == ctx->entry_cap && realloc_entries(ctx)) + return NULL; e = malloc(sizeof(*e)); if (!e) return NULL; - e->ev_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); - if (e->ev_fd < 0) + r = __sys_socket(ctx->ns_addr.sa.sa_family, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (r < 0) + goto out_free_e; + e->udp_fd = (int)r; + + txid = (uint16_t)rand(); + // TODO(reyuki): avoid hard-coded AF_INET and use restyp instead + r = gwdns_build_query(txid, name, AF_INET, e->payload, sizeof(e->payload)); + if (r < 0) goto out_free_e; + e->payloadlen = (int)r; /* * Merge name and service into a single allocated string to @@ -591,7 +443,7 @@ struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx, sl = service ? strlen(service) : 0; e->name = malloc(nl + 1 + sl + 1); if (!e->name) - goto out_close_ev_fd; + goto out_free_e; e->service = e->name + nl + 1; memcpy(e->name, name, nl + 1); @@ -600,31 +452,39 @@ struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx, else e->service[0] = '\0'; - atomic_init(&e->refcnt, 2); e->res = 0; - push_queue(ctx, e); + e->idx = ctx->nr_entries++; + ctx->entries[e->idx] = e; + return e; -out_close_ev_fd: - close(e->ev_fd); out_free_e: free(e); return NULL; } -bool gwp_dns_entry_put(struct gwp_dns_entry *e) +int gwp_dns_process(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e) { - int x; - - if (!e) - return false; + struct gwdns_addrinfo_node *ai; + uint8_t buff[UDP_MSG_LIMIT]; + ssize_t r; + + r = __sys_recvfrom( + e->udp_fd, buff, sizeof(buff), 0, + &ctx->ns_addr.sa, (socklen_t *)&ctx->ns_addrlen + ); + if (r <= 0) + return (int)r; + + r = gwdns_parse_query(e->txid, e->service, buff, r, &ai); + if (r) + goto exit_free_ai; - x = atomic_fetch_sub(&e->refcnt, 1); - assert(x > 0); - if (x == 1) { - gwp_dns_entry_free(e); - return true; - } + e->addr = ai->ai_addr; + // gwp_dns_find_preferred_addr(ctx, ai, e->name, &e->addr, ctx->cfg.restyp); - return false; +exit_free_ai: + gwdns_free_parsed_query(ai); + return (int)r; } + diff --git a/src/gwproxy/dns.h b/src/gwproxy/dns.h index 10c7cea2ebe4..12d21552c92e 100644 --- a/src/gwproxy/dns.h +++ b/src/gwproxy/dns.h @@ -10,17 +10,21 @@ #include #include #include - -struct gwp_dns_wrk; +#include +#include struct gwp_dns_entry { + int idx; char *name; char *service; - _Atomic(int) refcnt; int res; - int ev_fd; + int udp_fd; struct gwp_sockaddr addr; - struct gwp_dns_entry *next; + int payloadlen; + union { + uint16_t txid; + uint8_t payload[UDP_MSG_LIMIT]; + }; }; enum { @@ -31,9 +35,12 @@ enum { GWP_DNS_RESTYP_PREFER_IPV6 = 4, }; +#define DEFAULT_ENTRIES_CAP 255 + struct gwp_dns_cfg { int cache_expiry; /* In seconds. <= 0 to disable cache. */ uint32_t nr_workers; + const char *ns_addr_str; uint32_t restyp; }; @@ -64,7 +71,7 @@ void gwp_dns_ctx_free(struct gwp_dns_ctx *ctx); /** * Queue a DNS resolution request. It returns a pointer to a gwp_dns_entry * with eventfd set to a valid file descriptor that can be used to wait for - * the resolution result. The caller's responsible to call gwp_dns_entry_put() + * the resolution result. The caller's responsible to call gwp_dns_entry_free() * to release the entry when it is no longer needed. * * The returned eventfd file descriptor is non-blocking. @@ -77,15 +84,11 @@ void gwp_dns_ctx_free(struct gwp_dns_ctx *ctx); struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx, const char *name, const char *service); -/** - * Release a DNS entry. This function decrements the reference count of the - * entry. If the reference count reaches zero, the entry is freed. - * - * @param entry Pointer to the DNS entry to release. If the entry is - * NULL, this function does nothing. - * @return True if the entry was freed, false otherwise. - */ -bool gwp_dns_entry_put(struct gwp_dns_entry *entry); +void cp_nsaddr(struct gwp_dns_ctx *ctx, struct gwp_sockaddr *addr, uint8_t *addrlen); + +void gwp_dns_entry_free(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e); + +int gwp_dns_process(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e); /** * Lookup a DNS entry in the cache. If the entry is found, it fills the diff --git a/src/gwproxy/ev/epoll.c b/src/gwproxy/ev/epoll.c index d46568a6a2b1..5e117be2ae8f 100644 --- a/src/gwproxy/ev/epoll.c +++ b/src/gwproxy/ev/epoll.c @@ -155,7 +155,7 @@ static int free_conn_pair(struct gwp_wrk *w, struct gwp_conn_pair *gcp) int r; if (gde) { - r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_DEL, gde->ev_fd, NULL); + r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_DEL, gde->udp_fd, NULL); if (unlikely(r)) return r; } @@ -782,22 +782,36 @@ static int arm_poll_for_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp) { struct gwp_dns_entry *gde = gcp->gde; + struct gwp_sockaddr addr; + struct gwp_dns_ctx *dctx; struct epoll_event ev; - int r; + uint8_t addrlen; + ssize_t r; assert(gde); - assert(gde->ev_fd >= 0); + dctx = w->ctx->dns; + + cp_nsaddr(dctx, &addr, &addrlen); + r = __sys_sendto( + gde->udp_fd, gde->payload, gde->payloadlen, MSG_NOSIGNAL, + &addr.sa, addrlen + ); + if (unlikely(r < 0)) + goto exit_close; ev.events = EPOLLIN; ev.data.u64 = 0; ev.data.ptr = gcp; ev.data.u64 |= EV_BIT_DNS_QUERY; - r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_ADD, gde->ev_fd, &ev); + r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_ADD, gde->udp_fd, &ev); if (unlikely(r)) - return r; + goto exit_close; return 0; +exit_close: + close(gde->udp_fd); + return (int)r; } static void log_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp, @@ -830,9 +844,9 @@ static int handle_ev_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp) assert(ct == CONN_STATE_SOCKS5_DNS_QUERY || ct == CONN_STATE_HTTP_DNS_QUERY); - r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_DEL, gde->ev_fd, NULL); - if (unlikely(r)) - return r; + r = gwp_dns_process(w->ctx->dns, gde); + if (r) + gde->res = r; log_dns_query(w, gcp, gde); if (likely(!gde->res)) { @@ -845,7 +859,7 @@ static int handle_ev_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp) r = -EIO; } - gwp_dns_entry_put(gde); + gwp_dns_entry_free(w->ctx->dns, gde); gcp->gde = NULL; return r; } diff --git a/src/gwproxy/ev/io_uring.c b/src/gwproxy/ev/io_uring.c index 78440935383b..9cb06b1214ca 100644 --- a/src/gwproxy/ev/io_uring.c +++ b/src/gwproxy/ev/io_uring.c @@ -675,13 +675,13 @@ static int prep_domain_resolution(struct gwp_wrk *w, struct gwp_conn_pair *gcp) assert(gde); s = get_sqe_nofail(w); - io_uring_prep_poll_add(s, gde->ev_fd, POLLIN); + io_uring_prep_poll_add(s, gde->udp_fd, POLLIN); io_uring_sqe_set_data(s, gcp); s->user_data |= EV_BIT_IOU_DNS_QUERY; get_gcp(gcp); pr_dbg(&ctx->lh, "Prepared DNS query for domain '%s' (fd=%d, idx=%u, ref_cnt=%d)", - gde->name, gde->ev_fd, gcp->idx, gcp->ref_cnt); + gde->name, gde->udp_fd, gcp->idx, gcp->ref_cnt); return 0; } @@ -741,7 +741,7 @@ static int handle_ev_dns_query(struct gwp_wrk *w, void *udata) gde->name, ip_to_str(&gcp->target_addr), gcp->target.fd, gcp->idx); - gwp_dns_entry_put(gde); + gwp_dns_entry_free(ctx->dns, gde); gcp->gde = NULL; return handle_socks5_connect_target(w, gcp); } diff --git a/src/gwproxy/gwproxy.c b/src/gwproxy/gwproxy.c index 7a90609dd5f1..d0a010ab4b27 100644 --- a/src/gwproxy/gwproxy.c +++ b/src/gwproxy/gwproxy.c @@ -54,7 +54,6 @@ static const struct option long_opts[] = { { "socks5-auth-file", required_argument, NULL, 'A' }, { "socks5-dns-cache-secs", required_argument, NULL, 'L' }, { "nr-workers", required_argument, NULL, 'w' }, - { "nr-dns-workers", required_argument, NULL, 'W' }, { "connect-timeout", required_argument, NULL, 'c' }, { "target-buf-size", required_argument, NULL, 'T' }, { "client-buf-size", required_argument, NULL, 'C' }, @@ -81,7 +80,6 @@ static const struct gwp_cfg default_opts = { .socks5_auth_file = NULL, .socks5_dns_cache_secs = 0, .nr_workers = 4, - .nr_dns_workers = 4, .connect_timeout = 5, .target_buf_size = 2048, .client_buf_size = 2048, @@ -114,7 +112,6 @@ static void show_help(const char *app) printf(" -L, --socks5-dns-cache-secs=sec SOCKS5 DNS cache duration in seconds (default: %d)\n", default_opts.socks5_dns_cache_secs); printf(" Set to 0 or a negative number to disable DNS caching.\n"); printf(" -w, --nr-workers=nr Number of worker threads (default: %d)\n", default_opts.nr_workers); - printf(" -W, --nr-dns-workers=nr Number of DNS worker threads for SOCKS5 (default: %d)\n", default_opts.nr_dns_workers); printf(" -c, --connect-timeout=sec Connection to target timeout in seconds (default: %d)\n", default_opts.connect_timeout); printf(" -T, --target-buf-size=nr Target buffer size in bytes (default: %d)\n", default_opts.target_buf_size); printf(" -C, --client-buf-size=nr Client buffer size in bytes (default: %d)\n", default_opts.client_buf_size); @@ -189,9 +186,6 @@ static int parse_options(int argc, char *argv[], struct gwp_cfg *cfg) case 'w': cfg->nr_workers = atoi(optarg); break; - case 'W': - cfg->nr_dns_workers = atoi(optarg); - break; case 'c': cfg->connect_timeout = atoi(optarg); break; @@ -691,7 +685,8 @@ static int gwp_ctx_init_dns(struct gwp_ctx *ctx) const struct gwp_dns_cfg dns_cfg = { .cache_expiry = cfg->socks5_dns_cache_secs, .restyp = cfg->socks5_prefer_ipv6 ? GWP_DNS_RESTYP_PREFER_IPV6 : 0, - .nr_workers = cfg->nr_dns_workers + .nr_workers = 1, + .ns_addr_str = "1.1.1.1" }; int r; @@ -991,7 +986,7 @@ int gwp_free_conn_pair(struct gwp_wrk *w, struct gwp_conn_pair *gcp) __sys_close(gcp->timer_fd); if (gcp->gde) - gwp_dns_entry_put(gcp->gde); + gwp_dns_entry_free(w->ctx->dns, gcp->gde); switch (gcp->prot_type) { case GWP_PROT_TYPE_SOCKS5: diff --git a/src/gwproxy/gwproxy.h b/src/gwproxy/gwproxy.h index 095c0ce700c3..0206e68e7481 100644 --- a/src/gwproxy/gwproxy.h +++ b/src/gwproxy/gwproxy.h @@ -31,7 +31,6 @@ struct gwp_cfg { const char *socks5_auth_file; int socks5_dns_cache_secs; int nr_workers; - int nr_dns_workers; int connect_timeout; int target_buf_size; int client_buf_size; diff --git a/src/gwproxy/tests/dns.c b/src/gwproxy/tests/dns.c index 7735d24afdd2..130ccfec59b6 100644 --- a/src/gwproxy/tests/dns.c +++ b/src/gwproxy/tests/dns.c @@ -20,25 +20,30 @@ struct req_template { const char *domain, *service; }; +struct poll_map { + struct gwp_dns_entry *e; + int fd; +}; + static const struct req_template req_template[] = { - { "localhost", "80" }, { "facebook.com", "80" }, - { "google.com", "443" }, - { "github.com", "443" }, - { "example.com", "80" }, - { "twitter.com", "443" }, - { "reddit.com", "80" }, - { "youtube.com", "443" }, - { "wikipedia.org", "80" }, - { "stackoverflow.com", "443" }, - { "amazon.com", "80" }, - { "microsoft.com", "443" }, - { "apple.com", "80" }, - { "linkedin.com", "443" }, - { "bing.com", "80" }, }; -static int poll_all_in(struct pollfd *pfd, int n, int timeout) +static struct gwp_dns_entry *find_item(struct poll_map *map, int n, int fd) +{ + struct gwp_dns_entry *e; + int i; + + e = NULL; + for (i = 0; i < n; i++) { + if (map[i].fd == fd) + e = map[i].e; + } + + return e; +} + +static int poll_all_in(struct gwp_dns_ctx *ctx, struct poll_map *map, struct pollfd *pfd, int n, int timeout) { int ret, i, t = 0; @@ -54,7 +59,12 @@ static int poll_all_in(struct pollfd *pfd, int n, int timeout) } for (i = 0; i < n; i++) { - if (pfd[i].revents & (POLLIN | POLLERR | POLLHUP)) { + struct gwp_dns_entry *e; + if (pfd[i].revents & POLLIN) { + e = find_item(map, n, pfd[i].fd); + assert(e); + ret = gwp_dns_process(ctx, e); + assert(!ret); pfd[i].events = 0; t++; } @@ -67,12 +77,14 @@ static int poll_all_in(struct pollfd *pfd, int n, int timeout) static void test_basic_dns_multiple_requests(void) { - struct gwp_dns_cfg cfg = { .nr_workers = 1 }; - struct gwp_dns_entry *earr[ARRAY_SIZE(req_template)]; + struct gwp_dns_cfg cfg = { .nr_workers = 1, .ns_addr_str = "1.1.1.1" }; + struct poll_map pollfd_map[ARRAY_SIZE(req_template)]; struct pollfd pfd[ARRAY_SIZE(req_template)]; + struct gwp_sockaddr addr; struct gwp_dns_ctx *ctx; + uint8_t addrlen; + ssize_t r; int i, n; - int r; r = gwp_dns_ctx_init(&ctx, &cfg); assert(!r); @@ -81,65 +93,37 @@ static void test_basic_dns_multiple_requests(void) n = (int)ARRAY_SIZE(req_template); for (i = 0; i < n; i++) { const struct req_template *rt = &req_template[i]; - earr[i] = gwp_dns_queue(ctx, rt->domain, rt->service); - assert(earr[i]); - assert(earr[i]->ev_fd >= 0); - pfd[i].fd = earr[i]->ev_fd; + struct gwp_dns_entry *e; + e = gwp_dns_queue(ctx, rt->domain, rt->service); + assert(e); + assert(e->udp_fd >= 0); + pfd[i].fd = e->udp_fd; pfd[i].events = POLLIN; + cp_nsaddr(ctx, &addr, &addrlen); + r = __sys_sendto( + e->udp_fd, e->payload, e->payloadlen, MSG_NOSIGNAL, + &addr.sa, addrlen + ); + assert(r > 0); + pollfd_map[i].fd = e->udp_fd; + pollfd_map[i].e = e; } - r = poll_all_in(pfd, n, 5000); + r = poll_all_in(ctx, pollfd_map, pfd, n, 5000); assert(!r); for (i = 0; i < n; i++) { - assert(earr[i]->res == 0); - r = earr[i]->addr.sa.sa_family; + assert(pollfd_map[i].e->res == 0); + r = pollfd_map[i].e->addr.sa.sa_family; assert(r == AF_INET || r == AF_INET6); } - for (i = 0; i < n; i++) - gwp_dns_entry_put(earr[i]); - gwp_dns_ctx_free(ctx); -} - -static void test_dns_cache(void) -{ - struct gwp_dns_cfg cfg = { .nr_workers = 1, .cache_expiry = 10 }; - struct gwp_sockaddr addr; - struct gwp_dns_ctx *ctx; - struct gwp_dns_entry *e; - struct pollfd pfd; - int r; - - r = gwp_dns_ctx_init(&ctx, &cfg); - assert(!r); - assert(ctx != NULL); - - e = gwp_dns_queue(ctx, "localhost", "80"); - assert(e != NULL); - assert(e->ev_fd >= 0); - pfd.fd = e->ev_fd; - pfd.events = POLLIN; - r = poll_all_in(&pfd, 1, 5000); - assert(r == 0); - assert(e->res == 0); - r = e->addr.sa.sa_family; - assert(r == AF_INET || r == AF_INET6); - gwp_dns_entry_put(e); - - r = gwp_dns_cache_lookup(ctx, "localhost", "80", &addr); - assert(!r); - r = addr.sa.sa_family; - assert(r == AF_INET || r == AF_INET6); - r = gwp_dns_cache_lookup(ctx, "aaaa.com", "80", &addr); - assert(r == -ENOENT); gwp_dns_ctx_free(ctx); } int main(void) { test_basic_dns_multiple_requests(); - test_dns_cache(); printf("All tests passed.\n"); return 0; } -- Ahmad Gani