* [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
@ 2025-08-29 3:36 Ahmad Gani
2025-08-29 3:36 ` [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code Ahmad Gani
` (2 more replies)
0 siblings, 3 replies; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 3:36 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Ahmad Gani, Alviro Iskandar Setiawan, GNU/Weeb Mailing List
Hi Chief,
This is revision v7 of the initial work on the integration of the DNS
parser lib in gwproxy. This is an RFC draft; the patches themselves
aren't final.
There are 2 patches in this series:
- refactor gwproxy codebase (specifically, DNS system)
- add dns parser code
Thanks for taking a look.
# Changelog
v1 -> v2:
- use existing convert_str_to_ssaddr instead of init_addr
- fix memory leak when init_addr (now it's replaced) failed
- modify convert_str_to_ssaddr to support default port
- bring back __cold attribute on convert_ssaddr_to_str
- don't fill a dangling pointer as Sir Alviro said
- for now it's blocking, attempt_retry label is not needed
v2 -> v3:
- remove TODO(reyuki): hints->ai_family is used to filter results
- make gw_ares_getaddrinfo asynchronous by change UDP socket to non-blocking
- move socket creation to library initialization
- make UDP socket unconnected
- restructure internal struct and program execution flow to support async
- update unit test of dns parser
- transaction id creation is delegated to caller
- fix logic bug in serialize_answ that can lead to memory error: invalid read
- rename parameter prt and split commit message
- move variable declaration in for loop according to the Sir Alviro coding-style
v3 -> v4:
- add else block in the convert_str_to_ssaddr function
- use format specifier %hu for uint16_t in printf
- update base commit to branch master from upstream remote repository
v4 -> v5:
- squash commits to eliminate changes unrelated to the commit subject
- change commit subject: use imperative form for the subject
- update base commit to branch master from upstream remote repository
- changing the email subject
- drop the c-ares style thing
- restructure dnsparser.c and dnsparser.h
v5 -> v6:
- mark this feature as experimental and disabled by default
- fix minor issues from master branch
- fix dns parser
- add dns server as gwproxy' cmdline option
- add fallback mechanism for raw dns backend
v6 -> v7:
- squash commits
- refactor gwproxy codebase (specifically, DNS system)
- add dns parser code
Signed-off-by: Ahmad Gani <reyuki@gnuweeb.org>
---
Ahmad Gani (2):
dnsparser: Add dns parser code
gwproxy: refactor code base to add experimental raw DNS backend
Makefile | 2 +-
configure | 8 +
src/gwproxy/dns.c | 240 +++++++++++++++--
src/gwproxy/dns.h | 29 +-
src/gwproxy/dnsparser.c | 581 ++++++++++++++++++++++++++++++++++++++++
src/gwproxy/dnsparser.h | 192 +++++++++++++
src/gwproxy/ev/epoll.c | 69 ++++-
src/gwproxy/gwproxy.c | 54 +++-
src/gwproxy/gwproxy.h | 5 +-
9 files changed, 1131 insertions(+), 49 deletions(-)
create mode 100644 src/gwproxy/dnsparser.c
create mode 100644 src/gwproxy/dnsparser.h
base-commit: b2a7a2d33ba8676d917fd26fc3a86bdde8961d76
--
Ahmad Gani
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code
2025-08-29 3:36 [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ahmad Gani
@ 2025-08-29 3:36 ` Ahmad Gani
2025-08-29 4:39 ` Ammar Faizi
2025-08-29 3:36 ` [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend Ahmad Gani
2025-08-29 4:36 ` [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ammar Faizi
2 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 3:36 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Ahmad Gani, Alviro Iskandar Setiawan, GNU/Weeb Mailing List
Introducing dns parser for better flexibility over the program flow, the
DNS protocol implementation is limited to standard query (OPCODE_QUERY).
Also, Delegate creation of transaction id to the caller, It gives the
caller more flexibility in handling DNS requests.
A single domain name lookup can have two responses: one for IPv4 and one
for IPv6. With this change, the caller may use the same txid for both
queries, making it easier to group related DNS responses.
Signed-off-by: Ahmad Gani <reyuki@gnuweeb.org>
---
src/gwproxy/dnsparser.c | 581 ++++++++++++++++++++++++++++++++++++++++
src/gwproxy/dnsparser.h | 192 +++++++++++++
2 files changed, 773 insertions(+)
create mode 100644 src/gwproxy/dnsparser.c
create mode 100644 src/gwproxy/dnsparser.h
diff --git a/src/gwproxy/dnsparser.c b/src/gwproxy/dnsparser.c
new file mode 100644
index 000000000000..cb2a090dfbb0
--- /dev/null
+++ b/src/gwproxy/dnsparser.c
@@ -0,0 +1,581 @@
+#define _DEFAULT_SOURCE
+#include <endian.h>
+#include <stdbool.h>
+#include <gwproxy/dnsparser.h>
+#include <gwproxy/net.h>
+
+static ssize_t construct_qname(uint8_t *dst, size_t dst_len, const char *qname)
+{
+ const uint8_t *p = (const uint8_t *)qname;
+ uint8_t *lp = dst; /* Length position. */
+ uint8_t *sp = lp + 1; /* String position. */
+ size_t total = 0;
+ uint16_t l = 0;
+
+ while (1) {
+ uint8_t c = *p++;
+
+ total++;
+ if (total >= dst_len)
+ return -ENAMETOOLONG;
+
+ if (c == '.' || c == '\0') {
+ if (l < 1 || l > DOMAIN_LABEL_LIMIT)
+ return -EINVAL;
+
+ *lp = (uint8_t)l;
+ lp = sp++;
+ l = 0;
+ if (!c)
+ break;
+ } else {
+ l++;
+ *sp = c;
+ sp++;
+ }
+ }
+
+ return total;
+}
+
+static int calculate_question_len(uint8_t *in, size_t in_len)
+{
+ const uint8_t *p = in;
+ int tot_len, advance_len;
+
+ tot_len = 0;
+ while (true) {
+ if (*p == 0x0) {
+ tot_len++;
+ break;
+ }
+
+ if (tot_len >= (int)in_len)
+ return -ENOBUFS;
+
+ advance_len = *p + 1;
+ tot_len += advance_len;
+ p += advance_len;
+ }
+
+ if (tot_len > DOMAIN_NAME_LIMIT)
+ return -ENAMETOOLONG;
+
+ tot_len += 4;
+ if (tot_len >= (int)in_len)
+ return -ENOBUFS;
+
+ return tot_len;
+}
+
+static int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *out)
+{
+ gwdns_header_pkt *hdr;
+ uint16_t raw_flags;
+ size_t idx, i;
+ void *ptr;
+ int ret;
+
+ idx = sizeof(*hdr);
+ if (idx >= in_len)
+ return -EAGAIN;
+
+ hdr = (void *)in;
+ if (memcmp(&txid, &hdr->id, sizeof(txid)))
+ return -EINVAL;
+
+ memcpy(&raw_flags, &hdr->flags, sizeof(raw_flags));
+ raw_flags = ntohs(raw_flags);
+ /* QR MUST 1 = response from dns server */
+ if (!DNS_QR(raw_flags))
+ return -EINVAL;
+
+ /* OPCODE MUST 0 = standard query */
+ if (DNS_OPCODE(raw_flags))
+ return -EINVAL;
+
+ /* RCODE MUST 0 = No error */
+ if (DNS_RCODE(raw_flags))
+ return -EPROTO;
+
+ // is it safe or recommended to alter the in buffer directly?
+ hdr->ancount = ntohs(hdr->ancount);
+ if (!hdr->ancount)
+ return -ENODATA;
+
+ /*
+ * Check the sizes upfront.
+ *
+ * 1 bytes for variable-length
+ * in[idx] for the length of first name
+ * 1 bytes for null terminator
+ * 2 bytes for qtype
+ * 2 bytes for qclass
+ */
+ if ((size_t)(1 + in[idx] + 1 + 2 + 2) >= in_len)
+ return -EINVAL;
+
+ ret = calculate_question_len(&in[idx], in_len - idx);
+ if (ret <= 0)
+ return -EINVAL;
+
+ idx += ret;
+ if (idx >= in_len)
+ return -EAGAIN;
+
+ out->hdr.ancount = 0;
+ ptr = malloc(hdr->ancount * sizeof(uint8_t *));
+ if (!ptr)
+ return -ENOMEM;
+
+ out->rr_answ = ptr;
+ for (i = 0; i < hdr->ancount; i++) {
+ uint16_t is_compressed, rdlength;
+ gwdns_serialized_answ *item = malloc(sizeof(gwdns_serialized_answ));
+ if (!item) {
+ ret = -ENOMEM;
+ goto exit_free;
+ }
+
+ memcpy(&is_compressed, &in[idx], sizeof(is_compressed));
+ is_compressed = ntohs(is_compressed);
+ is_compressed = DNS_IS_COMPRESSED(is_compressed);
+ assert(is_compressed);
+ idx += 2; // NAME
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ free(item);
+ goto exit_free;
+ }
+
+ memcpy(&item->rr_type, &in[idx], 2);
+ item->rr_type = ntohs(item->rr_type);
+ idx += 2; // TYPE
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ free(item);
+ goto exit_free;
+ }
+ memcpy(&item->rr_class, &in[idx], 2);
+ item->rr_class = ntohs(item->rr_class);
+ idx += 2; // CLASS
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ free(item);
+ goto exit_free;
+ }
+ memcpy(&item->ttl, &in[idx], 4);
+ item->ttl = be32toh(item->ttl);
+ idx += 4; // TTL
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ free(item);
+ goto exit_free;
+ }
+
+ memcpy(&rdlength, &in[idx], sizeof(rdlength));
+ rdlength = ntohs(rdlength);
+ switch (item->rr_type) {
+ case TYPE_AAAA:
+ if (rdlength != sizeof(struct in6_addr)) {
+ ret = -EINVAL;
+ free(item);
+ goto exit_free;
+ }
+ break;
+ case TYPE_A:
+ if (rdlength != sizeof(struct in_addr)) {
+ ret = -EINVAL;
+ free(item);
+ goto exit_free;
+ }
+ break;
+ case TYPE_CNAME:
+ idx += 2 + rdlength;
+ free(item);
+ continue;
+
+ default:
+ ret = -EINVAL;
+ free(item);
+ goto exit_free;
+ break;
+ }
+
+ item->rdlength = rdlength;
+ idx += 2;
+ if (idx >= in_len) {
+ ret = -EAGAIN;
+ free(item);
+ goto exit_free;
+ }
+
+ ptr = malloc(rdlength);
+ if (!ptr) {
+ ret = -ENOMEM;
+ free(item);
+ goto exit_free;
+ }
+
+ memcpy(ptr, &in[idx], rdlength);
+ idx += rdlength;
+ if (idx > in_len) {
+ ret = -EINVAL;
+ free(item);
+ free(ptr);
+ goto exit_free;
+ }
+
+ item->rdata = ptr;
+ out->rr_answ[out->hdr.ancount] = item;
+ out->hdr.ancount++;
+ }
+
+ if (!out->hdr.ancount) {
+ free(out->rr_answ);
+ return -ENODATA;
+ }
+
+ return 0;
+exit_free:
+ for (i = 0; i < out->hdr.ancount; i++) {
+ free(out->rr_answ[i]->rdata);
+ free(out->rr_answ[i]);
+ }
+ free(out->rr_answ);
+ return ret;
+}
+
+static void free_serialize_answ(gwdns_answ_data *answ)
+{
+ size_t i;
+ for (i = 0; i < answ->hdr.ancount; i++) {
+ free(answ->rr_answ[i]->rdata);
+ free(answ->rr_answ[i]);
+ }
+ free(answ->rr_answ);
+}
+
+int gwdns_parse_query(uint16_t txid, const char *service,
+ uint8_t *in, size_t in_len,
+ struct gwdns_addrinfo_node **ai)
+{
+ struct gwdns_addrinfo_node *results, *tail;
+ gwdns_answ_data raw_answ;
+ int r, port;
+ size_t i;
+
+ port = atoi(service);
+ if (port < 0)
+ return -EINVAL;
+ port = htons(port);
+
+ r = serialize_answ(txid, in, in_len, &raw_answ);
+ if (r)
+ return r;
+
+ results = tail = NULL;
+ for (i = 0; i < raw_answ.hdr.ancount; i++) {
+ struct gwdns_addrinfo_node *new_node;
+ gwdns_serialized_answ *answ;
+ struct sockaddr_in6 *i6;
+ struct sockaddr_in *i4;
+
+ answ = raw_answ.rr_answ[i];
+ new_node = malloc(sizeof(*new_node));
+ if (!new_node) {
+ r = -ENOMEM;
+ goto exit_free;
+ }
+ new_node->ai_next = NULL;
+
+ if (answ->rr_type == TYPE_AAAA) {
+ i6 = &new_node->ai_addr.i6;
+ new_node->ai_family = AF_INET6;
+ new_node->ai_addrlen = sizeof(i6);
+ i6->sin6_port = port;
+ i6->sin6_family = AF_INET6;
+ assert(sizeof(i6->sin6_addr) == answ->rdlength);
+ memcpy(&i6->sin6_addr, answ->rdata, answ->rdlength);
+ } else {
+ i4 = &new_node->ai_addr.i4;
+ new_node->ai_family = AF_INET;
+ new_node->ai_addrlen = sizeof(i4);
+ i4->sin_port = port;
+ i4->sin_family = AF_INET;
+ assert(sizeof(i4->sin_addr) == answ->rdlength);
+ memcpy(&i4->sin_addr, answ->rdata, answ->rdlength);
+ new_node->ai_ttl = answ->ttl;
+ }
+
+ if (!tail)
+ results = new_node;
+ else
+ tail->ai_next = new_node;
+ tail = new_node;
+ }
+
+ *ai = results;
+ r = 0;
+exit_free:
+ free_serialize_answ(&raw_answ);
+ return r;
+}
+
+void gwdns_free_parsed_query(struct gwdns_addrinfo_node *ai)
+{
+ struct gwdns_addrinfo_node *tmp, *node = ai;
+ while (node) {
+ tmp = node->ai_next;
+ free(node);
+ node = tmp;
+ }
+}
+
+static ssize_t construct_question(gwdns_question_part *question)
+{
+ gwdns_header_pkt *hdr;
+ gwdns_query_pkt pkt;
+ uint16_t qtype, qclass;
+ size_t required_len;
+ ssize_t bw;
+
+ switch (question->type) {
+ case AF_INET6:
+ question->type = TYPE_AAAA;
+ break;
+ case AF_INET:
+ question->type = TYPE_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ hdr = &pkt.hdr;
+ /*
+ * the memset implicitly set opcode to OPCODE_QUERY
+ */
+ memset(hdr, 0, sizeof(*hdr));
+ /*
+ * no need to htons, so no ntohs for comparison in serialize_answ.
+ */
+ hdr->id = question->txid;
+ DNS_SET_RD(hdr->flags, true);
+ hdr->flags = htons(hdr->flags);
+ hdr->qdcount = htons(1);
+
+ /*
+ * pkt.body is interpreted as question section
+ * for layout and format, see RFC 1035 4.1.2. Question section format
+ */
+ bw = construct_qname(pkt.body, sizeof(pkt.body) - 3, question->domain);
+ if (bw < 0)
+ return bw;
+
+ pkt.body[bw++] = 0x0;
+ qtype = htons(question->type);
+ qclass = htons(CLASS_IN);
+ memcpy(&pkt.body[bw], &qtype, 2);
+ bw += 2;
+ memcpy(&pkt.body[bw], &qclass, 2);
+ bw += 2;
+
+ required_len = sizeof(pkt.hdr) + bw;
+ if (question->dst_len < required_len)
+ return -ENOBUFS;
+
+ memcpy(question->dst_buffer, &pkt, required_len);
+
+ return required_len;
+}
+
+ssize_t gwdns_build_query(uint16_t txid, const char *name, int family, uint8_t *out, size_t out_len)
+{
+ gwdns_question_part q;
+
+ q.domain = name;
+ q.type = family;
+ q.txid = txid;
+ q.dst_buffer = out;
+ q.dst_len = out_len;
+ return construct_question(&q);
+}
+
+#ifdef RUNTEST
+
+void test_parse_ipv4(void)
+{
+ struct gwdns_addrinfo_node *d, *node;
+ uint16_t txid;
+
+ uint8_t recv_pkt[] = {
+ 0x23, 0xc6, 0x81, 0x80, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00,
+ 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x35, 0x00, 0x04, 0x4a, 0x7d, 0x18, 0x8a, 0xc0, 0x0c, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x04, 0x4a, 0x7d, 0x18, 0x66,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x04,
+ 0x4a, 0x7d, 0x18, 0x64, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x35, 0x00, 0x04, 0x4a, 0x7d, 0x18, 0x8b, 0xc0, 0x0c, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x04, 0x4a, 0x7d, 0x18, 0x65,
+ 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x35, 0x00, 0x04,
+ 0x4a, 0x7d, 0x18, 0x71
+ };
+
+ memcpy(&txid, recv_pkt, 2);
+ d = NULL;
+ assert(!gwdns_parse_query(txid, "80", recv_pkt, sizeof(recv_pkt), &d));
+ assert(d);
+ node = d;
+ while (node) {
+ struct gwdns_addrinfo_node *tmp;
+ char buff[FULL_ADDRSTRLEN];
+
+ tmp = node->ai_next;
+ assert(node->ai_family == AF_INET);
+ convert_ssaddr_to_str(buff, &node->ai_addr);
+ printf("IPv4: %s\n", buff);
+ node = tmp;
+ }
+
+ gwdns_free_parsed_query(d);
+}
+
+void test_parse_ipv6(void)
+{
+ struct gwdns_addrinfo_node *d, *node;
+ uint16_t txid;
+
+ uint8_t recv_pkt[] = {
+ 0x45, 0x67,
+ 0x81, 0x80,
+ 0x00, 0x01,
+ 0x00, 0x04,
+ 0x00, 0x00,
+ 0x00, 0x00,
+
+ 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d,
+ 0x00,
+ 0x00, 0x1c,
+ 0x00, 0x01,
+
+ 0xc0, 0x0c,
+ 0x00, 0x1c,
+ 0x00, 0x01,
+ 0x00, 0x00, 0x09, 0x06,
+ 0x00, 0x10,
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x03, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71,
+
+ 0xc0, 0x0c,
+ 0x00, 0x1c, 0x00, 0x01, 0x00, 0x00, 0x09, 0x06,
+ 0x00, 0x10,
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x03, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a,
+
+ 0xc0, 0x0c,
+ 0x00, 0x1c,
+ 0x00, 0x01,
+ 0x00, 0x00, 0x09, 0x06,
+ 0x00, 0x10,
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x03, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+
+ 0xc0, 0x0c,
+ 0x00, 0x1c,
+ 0x00, 0x01,
+ 0x00, 0x00, 0x0c, 0x16,
+ 0x00, 0x10,
+ 0x24, 0x04, 0x68, 0x00, 0x40, 0x03, 0x0c, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71
+ };
+
+ memcpy(&txid, recv_pkt, sizeof(txid));
+
+ node = NULL;
+ assert(!gwdns_parse_query(txid, "80", recv_pkt, sizeof(recv_pkt), &d));
+ assert(d);
+ node = d;
+ while (node) {
+ struct gwdns_addrinfo_node *tmp;
+ char buff[FULL_ADDRSTRLEN];
+
+ tmp = node->ai_next;
+ assert(node->ai_family == AF_INET6);
+ convert_ssaddr_to_str(buff, &node->ai_addr);
+ printf("IPv6: %s\n", buff);
+ node = tmp;
+ }
+
+ gwdns_free_parsed_query(d);
+}
+
+void test_build_ipv4(void)
+{
+ uint8_t buff[UDP_MSG_LIMIT];
+ gwdns_header_pkt *hdr;
+ uint16_t c;
+ ssize_t r;
+
+ c = 0xFFFF;
+ r = gwdns_build_query(c, "google.com", AF_INET, buff, sizeof(buff));
+ assert(r > 0);
+
+ hdr = (void *)buff;
+ assert(ntohs(hdr->qdcount) == 1);
+ assert(!hdr->nscount);
+ assert(!DNS_QR(hdr->flags));
+ assert(DNS_OPCODE(hdr->flags) == OPCODE_QUERY);
+ c = htons(TYPE_A);
+ assert(!memcmp(buff + 12 + 12, &c, 2));
+}
+
+void test_build_ipv6(void)
+{
+ uint8_t buff[UDP_MSG_LIMIT];
+ gwdns_header_pkt *hdr;
+ uint16_t c;
+ ssize_t r;
+
+ c = 0xFFFF;
+ r = gwdns_build_query(c, "google.com", AF_INET6, buff, sizeof(buff));
+ assert(r > 0);
+
+ hdr = (void *)buff;
+ assert(ntohs(hdr->qdcount) == 1);
+ assert(!hdr->nscount);
+ assert(!DNS_QR(hdr->flags));
+ assert(DNS_OPCODE(hdr->flags) == OPCODE_QUERY);
+ c = htons(TYPE_AAAA);
+ assert(!memcmp(buff + 12 + 12, &c, 2));
+}
+
+/*
+ * test mock data of recv in both IPv4 and IPv6
+ *
+ * the mock data are produced by this script:
+ * https://gist.github.com/realyukii/d7b450b4ddc305c66a2d8cd5600f23c4
+ */
+void run_all_tests(void)
+{
+ /*
+ * We can't use serialize_answ to parse multiple response at once.
+ * The caller MUST call serialize_answ more than one time if there's
+ * more than one response, because txid is passed to only verify one
+ * response.
+ */
+ fprintf(stderr, "test constructing DNS standard query packet for TYPE_A!\n");
+ test_build_ipv4();
+ fprintf(stderr, "test constructing DNS standard query packet for TYPE_AAAA!\n");
+ test_build_ipv6();
+ fprintf(stderr, "test parsing DNS standard query packet for TYPE_A!\n");
+ test_parse_ipv4();
+ fprintf(stderr, "test parsing DNS standard query packet for TYPE_AAAA!\n");
+ test_parse_ipv6();
+ fprintf(stderr, "all tests passed!\n");
+}
+
+int main(void)
+{
+ run_all_tests();
+ return 0;
+}
+
+#endif
diff --git a/src/gwproxy/dnsparser.h b/src/gwproxy/dnsparser.h
new file mode 100644
index 000000000000..b4c4b88b1c21
--- /dev/null
+++ b/src/gwproxy/dnsparser.h
@@ -0,0 +1,192 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <gwproxy/net.h>
+
+#ifndef GWP_DNS_PARSER_H
+#define GWP_DNS_PARSER_H
+
+#ifndef __packed
+#define __packed __attribute__((__packed__))
+#endif
+
+/*
+ * 4. MESSAGES
+ * 4.1. Format
+ *
+ * All communications inside of the domain protocol are carried in a single
+ * format called a message. The top-level format of a message is divided
+ * into 5 sections (some of which may be empty in certain cases), shown below:
+ *
+ * +---------------------+
+ * | Header |
+ * +---------------------+
+ * | Question | the question for the name server
+ * +---------------------+
+ * | Answer | RRs answering the question
+ * +---------------------+
+ * | Authority | RRs pointing toward an authority
+ * +---------------------+
+ * | Additional | RRs holding additional information
+ * +---------------------+
+ *
+ * These sections are defined in RFC 1035 §4.1. The Header section is always
+ * present and includes fields that specify which of the other sections follow,
+ * as well as metadata such as whether the message is a query or response,
+ * the opcode, etc.
+ */
+
+/* Flag bit position in little-endian machine */
+#define DNS_QR_BIT 0xF
+#define DNS_OPCODE_BIT 0xB // 4-bit field
+#define DNS_AA_BIT 0xA
+#define DNS_TC_BIT 0x9
+#define DNS_RD_BIT 0x8
+#define DNS_RA_BIT 0x7
+#define DNS_Z_BIT 0x4 // 3-bit field
+#define DNS_RCODE_BIT 0x0 // 4-bit field
+#define DNS_COMPRESSION_BIT (0x3 << 0xE)
+
+/* Flag extraction macros for listtle-endian machine */
+#define DNS_QR(flags) (((flags) >> DNS_QR_BIT) & 0x1)
+#define DNS_OPCODE(flags) (((flags) >> DNS_OPCODE_BIT) & 0xF)
+#define DNS_RCODE(flags) ((flags) & 0xF)
+#define DNS_IS_COMPRESSED(mask) ((mask) & DNS_COMPRESSION_BIT)
+
+/* Flag construction macros for little-endian machine */
+#define DNS_SET_RD(flags, val) (flags) = ((flags) & ~(1 << DNS_RD_BIT)) | ((!!(val)) << DNS_RD_BIT)
+
+/* as per RFC 1035 §2.3.4. Size limits */
+#define DOMAIN_LABEL_LIMIT 63
+#define DOMAIN_NAME_LIMIT 255
+#define UDP_MSG_LIMIT 512
+
+typedef enum {
+ OPCODE_QUERY = 0, // Standard query (QUERY)
+} gwdns_op;
+
+typedef enum {
+ TYPE_A = 1, // IPv4 host address
+ TYPE_CNAME = 5, // the canonical name for an alias
+ TYPE_AAAA = 28, // IPv6 host address
+} gwdns_type;
+
+typedef enum {
+ CLASS_IN = 1, // Internet
+} gwdns_class;
+
+typedef struct {
+ uint16_t id;
+ uint16_t flags;
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+} __packed gwdns_header_pkt;
+
+typedef struct {
+ uint8_t question[UDP_MSG_LIMIT];
+ char answr[UDP_MSG_LIMIT];
+} gwdns_question_buffer;
+
+typedef struct {
+ uint8_t *dst_buffer;
+ uint16_t txid;
+ uint16_t type;
+ size_t dst_len;
+ const char *domain;
+} gwdns_question_part;
+
+/*
+ * 4.1.3. Resource record format
+ *
+ * The answer, authority, and additional sections all share the same
+ * format: a variable number of resource records, where the number of
+ * records is specified in the corresponding count field in the header.
+ */
+typedef struct {
+ uint8_t *name; // DOMAIN NAME: variable‑length sequence of labels (length-byte followed by label, ending in 0), possibly compressed
+ uint16_t rr_type; // TYPE: two-octet code identifying the RR type (see gwdns_type)
+ uint16_t rr_class; // CLASS: two-octet code identifying the RR class (see gwdns_class)
+ uint32_t ttl; // TTL: 32-bit unsigned, time to live in seconds
+ uint16_t rdlength; // RDLENGTH: length in octets of RDATA
+ uint8_t *rdata; // RDATA: variable-length data, format depends on TYPE and CLASS
+} gwdns_serialized_rr;
+
+typedef struct {
+ char qname[DOMAIN_NAME_LIMIT];
+ uint16_t qtype;
+ uint16_t qclass;
+} gwdns_serialized_question;
+
+typedef gwdns_serialized_rr gwdns_serialized_answ;
+
+typedef struct {
+ gwdns_header_pkt hdr;
+ uint8_t body[UDP_MSG_LIMIT];
+} gwdns_query_pkt;
+
+typedef struct {
+ gwdns_header_pkt hdr;
+ gwdns_serialized_question question;
+ gwdns_serialized_answ **rr_answ;
+} gwdns_answ_data;
+
+struct gwdns_addrinfo_node {
+ int ai_family;
+ int ai_ttl;
+ socklen_t ai_addrlen;
+ struct gwp_sockaddr ai_addr;
+ struct gwdns_addrinfo_node *ai_next;
+};
+
+/*
+ * Build standard query for domain name lookup.
+ *
+ * The caller may need to check for potential transaction ID collisions.
+ *
+ * Possible errors are:
+ * - ENAMETOOLONG name is too long.
+ * - ENOBUFS length specified by out_len is not sufficient.
+ * - EINVAL malformed name or unsupported value of family.
+ *
+ * @param txid transaction id
+ * @param name domain name
+ * @param family choose request for IPv4 or IPv6
+ * @param out destination buffer for constructed packet
+ * @param out_len available capacity of destination buffer
+ * @return length of bytes written into dst_buffer on success,
+ * or a negative integer on failure.
+ */
+ssize_t gwdns_build_query(uint16_t txid, const char *name, int family, uint8_t *out, size_t out_len);
+
+/*
+ * Parse name server's answer
+ *
+ * Possible errors are:
+ * -EAGAIN in buffer is not sufficient, no bytes are processed, need more data.
+ * -EINVAL the content of in buffer is not valid.
+ * -ENOMEM failed to allocate dynamic memory.
+ * -ENODATA the packet didn't contain any answers.
+ * -EPROTO the DNS server can't understand your question
+ *
+ * @param txid transaction id of question
+ * @param service port number in ascii
+ * @param in a pointer to buffer that need to be parsed
+ * @param in_len a pointer to buffer that need to be parsed
+ * @param ai a pointer to address info
+ * @return zero on success or a negative number on failure
+ */
+int gwdns_parse_query(uint16_t txid, const char *service,
+ uint8_t *in, size_t in_len,
+ struct gwdns_addrinfo_node **ai);
+void gwdns_free_parsed_query(struct gwdns_addrinfo_node *addrinfo);
+
+#endif /* #ifndef GWP_DNS_PARSER_H */
\ No newline at end of file
--
Ahmad Gani
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend
2025-08-29 3:36 [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ahmad Gani
2025-08-29 3:36 ` [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code Ahmad Gani
@ 2025-08-29 3:36 ` Ahmad Gani
2025-08-29 4:36 ` Ammar Faizi
2025-08-29 4:36 ` [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ammar Faizi
2 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 3:36 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Ahmad Gani, Alviro Iskandar Setiawan, GNU/Weeb Mailing List
The raw DNS backend is now marked as experimental and disabled by default.
It can be enabled at build time with the configure option --use-raw-dns,
and at runtime with the gwproxy option -r 1.
Add fallback mechanism for raw DNS
If *_PREFER_* is used in the restyp, the raw DNS backend will attempt to
retry DNS query with different address family.
Add DNS server option (remove hard-coded DNS server)
Introduce --dns-server gwproxy' cmdline option instead of hard-coded
value.
Signed-off-by: Ahmad Gani <reyuki@gnuweeb.org>
---
Makefile | 2 +-
configure | 8 ++
src/gwproxy/dns.c | 240 ++++++++++++++++++++++++++++++++++++-----
src/gwproxy/dns.h | 29 ++++-
src/gwproxy/ev/epoll.c | 69 ++++++++++--
src/gwproxy/gwproxy.c | 54 ++++++++--
src/gwproxy/gwproxy.h | 5 +-
7 files changed, 358 insertions(+), 49 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/configure b/configure
index cef07b1cb652..539610916149 100755
--- a/configure
+++ b/configure
@@ -34,6 +34,9 @@ for opt do
--cxx=*)
cxx="$optarg";
;;
+ --use-raw-dns)
+ use_raw_dns="yes";
+ ;;
--use-io-uring)
use_io_uring="yes";
;;
@@ -87,6 +90,7 @@ Options: [defaults in brackets after descriptions]
--cxx=CMD Use CMD as the C++ compiler
--debug Build with debug enabled
--use-io-uring Enable io_uring support (default: no)
+ --use-raw-dns Enable experimental raw DNS backend (default: no)
--sanitize Enable sanitizers (default: no)
EOF
exit 0;
@@ -333,6 +337,10 @@ if test "${use_io_uring}" = "yes"; then
CXXFLAGS="${CXXFLAGS} -I./src/liburing/src/include";
fi;
+if test "${use_raw_dns}" = "yes"; then
+ add_config "CONFIG_RAW_DNS";
+fi;
+
if test "${use_sanitize}" = "yes"; then
add_config "CONFIG_SANITIZE";
if ! add_c_and_cxx_flag "-fsanitize=address"; then
diff --git a/src/gwproxy/dns.c b/src/gwproxy/dns.c
index cdc62a5bcf78..9f563e083fd2 100644
--- a/src/gwproxy/dns.c
+++ b/src/gwproxy/dns.c
@@ -23,6 +23,7 @@
#include <pthread.h>
#include <sys/eventfd.h>
+#include <gwproxy/dnsparser.h>
struct gwp_dns_ctx;
@@ -33,6 +34,14 @@ struct gwp_dns_wrk {
};
struct gwp_dns_ctx {
+#ifdef CONFIG_RAW_DNS
+ uint32_t entry_cap;
+ struct gwp_dns_entry **entries;
+ int sockfd;
+ int ns_family;
+ struct gwp_sockaddr ns_addr;
+ uint8_t ns_addrlen;
+#endif
volatile bool should_stop;
pthread_mutex_t lock;
pthread_cond_t cond;
@@ -182,6 +191,116 @@ int gwp_dns_resolve(struct gwp_dns_ctx *ctx, const char *name,
return found ? 0 : -EHOSTUNREACH;
}
+#ifdef CONFIG_RAW_DNS
+
+void cp_nsaddr(struct gwp_dns_ctx *ctx, struct gwp_sockaddr *addr, uint8_t *addrlen)
+{
+ *addr = ctx->ns_addr;
+ *addrlen = ctx->ns_addrlen;
+}
+
+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);
+}
+
+void gwp_dns_raw_entry_free(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e)
+{
+ struct gwp_dns_entry *new_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);
+}
+
+static void free_all_queued_entries(struct gwp_dns_ctx *ctx)
+{
+ uint32_t 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);
+}
+
+static bool realloc_entries(struct gwp_dns_ctx *ctx)
+{
+ struct gwp_dns_entry **tmp;
+ int new_cap;
+
+ 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;
+}
+
+int gwp_dns_process(struct gwp_dns_ctx *ctx, struct gwp_dns_entry *e)
+{
+ 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) {
+ if (r == -ENODATA) {
+ uint16_t txid;
+ int af;
+
+ /* Fallback to other family if this one yield no results */
+ switch (ctx->cfg.restyp) {
+ case GWP_DNS_RESTYP_PREFER_IPV4:
+ af = AF_INET6;
+ break;
+ case GWP_DNS_RESTYP_PREFER_IPV6:
+ af = AF_INET;
+ break;
+ default:
+ goto exit_free_ai;
+ break;
+ }
+
+ txid = (uint16_t)rand();
+ r = gwdns_build_query(txid, e->name, af, e->payload, sizeof(e->payload));
+ if (r > 0) {
+ e->txid = txid;
+ e->payloadlen = (int)r;
+ r = -EAGAIN;
+ }
+ }
+ goto exit_free_ai;
+ }
+
+ e->addr = ai->ai_addr;
+
+exit_free_ai:
+ gwdns_free_parsed_query(ai);
+ return (int)r;
+}
+#endif /* #ifndef CONFIG_RAW_DNS */
+
static void gwp_dns_entry_free(struct gwp_dns_entry *e)
{
if (!e)
@@ -719,33 +838,48 @@ 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;
+ if (cfg->use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ 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->entry_cap = DEFAULT_ENTRIES_CAP;
+ ctx->entries = malloc(ctx->entry_cap * sizeof(*ctx->entries));
+ if (!ctx->entries)
+ goto out_destroy_mutex;
+#endif
+ } else {
+ r = pthread_cond_init(&ctx->cond, NULL);
+ if (r) {
+ r = -r;
+ goto out_destroy_mutex;
+ }
+
+ ctx->nr_sleeping = 0;
+ ctx->workers = NULL;
+ ctx->head = NULL;
+ ctx->tail = NULL;
+ r = init_workers(ctx);
+ if (r)
+ goto out_destroy_cond;
}
+ ctx->nr_entries = 0;
r = init_cache(ctx);
if (r)
goto out_destroy_cond;
- ctx->nr_sleeping = 0;
- ctx->nr_entries = 0;
- ctx->workers = NULL;
- ctx->head = NULL;
- ctx->tail = NULL;
ctx->should_stop = false;
ctx->last_scan = time(NULL);
- r = init_workers(ctx);
- if (r)
- goto out_free_cache;
*ctx_p = ctx;
return 0;
-out_free_cache:
- free_cache(ctx->cache);
out_destroy_cond:
- pthread_cond_destroy(&ctx->cond);
+ if (!cfg->use_raw_dns)
+ pthread_cond_destroy(&ctx->cond);
out_destroy_mutex:
pthread_mutex_destroy(&ctx->lock);
out_free_ctx:
@@ -762,10 +896,16 @@ static void put_all_queued_entries(struct gwp_dns_ctx *ctx)
void gwp_dns_ctx_free(struct gwp_dns_ctx *ctx)
{
- free_workers(ctx);
+ if (ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ free_all_queued_entries(ctx);
+#endif
+ } else {
+ free_workers(ctx);
+ pthread_cond_destroy(&ctx->cond);
+ put_all_queued_entries(ctx);
+ }
pthread_mutex_destroy(&ctx->lock);
- pthread_cond_destroy(&ctx->cond);
- put_all_queued_entries(ctx);
free_cache(ctx->cache);
free(ctx);
}
@@ -791,14 +931,51 @@ struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx,
{
struct gwp_dns_entry *e;
size_t nl, sl;
+#ifdef CONFIG_RAW_DNS
+ uint16_t txid;
+ ssize_t r;
+ int af;
+#endif
e = malloc(sizeof(*e));
if (!e)
return NULL;
- e->ev_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
- if (e->ev_fd < 0)
- goto out_free_e;
+ if (ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ if (ctx->nr_entries == ctx->entry_cap && realloc_entries(ctx))
+ return NULL;
+
+ 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;
+
+ switch (ctx->cfg.restyp) {
+ case GWP_DNS_RESTYP_PREFER_IPV4:
+ case GWP_DNS_RESTYP_IPV4_ONLY:
+ af = AF_INET;
+ break;
+ case GWP_DNS_RESTYP_PREFER_IPV6:
+ case GWP_DNS_RESTYP_IPV6_ONLY:
+ af = AF_INET6;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ txid = (uint16_t)rand();
+ r = gwdns_build_query(txid, name, af, e->payload, sizeof(e->payload));
+ if (r < 0)
+ goto out_free_e;
+ e->payloadlen = (int)r;
+#endif
+ } else {
+ e->ev_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+ if (e->ev_fd < 0)
+ goto out_free_e;
+ }
/*
* Merge name and service into a single allocated string to
@@ -811,7 +988,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_close_fd;
e->service = e->name + nl + 1;
memcpy(e->name, name, nl + 1);
@@ -820,13 +997,26 @@ 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);
+ if (ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ e->idx = ctx->nr_entries++;
+ ctx->entries[e->idx] = e;
+#endif
+ } else {
+ atomic_init(&e->refcnt, 2);
+ push_queue(ctx, e);
+ }
return e;
-out_close_ev_fd:
- close(e->ev_fd);
+out_close_fd:
+ if (ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ close(e->udp_fd);
+#endif
+ } else {
+ close(e->ev_fd);
+ }
out_free_e:
free(e);
return NULL;
diff --git a/src/gwproxy/dns.h b/src/gwproxy/dns.h
index 10c7cea2ebe4..410989f6414c 100644
--- a/src/gwproxy/dns.h
+++ b/src/gwproxy/dns.h
@@ -10,10 +10,19 @@
#include <stdbool.h>
#include <netinet/in.h>
#include <gwproxy/net.h>
-
-struct gwp_dns_wrk;
+#include <gwproxy/dnsparser.h>
+#include <gwproxy/syscall.h>
struct gwp_dns_entry {
+#ifdef CONFIG_RAW_DNS
+ uint32_t idx;
+ int udp_fd;
+ int payloadlen;
+ union {
+ uint16_t txid;
+ uint8_t payload[UDP_MSG_LIMIT];
+ };
+#endif
char *name;
char *service;
_Atomic(int) refcnt;
@@ -31,10 +40,16 @@ 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;
uint32_t restyp;
+ bool use_raw_dns;
+#ifdef CONFIG_RAW_DNS
+ const char *ns_addr_str;
+#endif
};
struct gwp_dns_ctx;
@@ -76,7 +91,6 @@ 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.
@@ -87,6 +101,15 @@ struct gwp_dns_entry *gwp_dns_queue(struct gwp_dns_ctx *ctx,
*/
bool gwp_dns_entry_put(struct gwp_dns_entry *entry);
+#ifdef CONFIG_RAW_DNS
+
+void cp_nsaddr(struct gwp_dns_ctx *ctx, struct gwp_sockaddr *addr, uint8_t *addrlen);
+
+void gwp_dns_raw_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);
+#endif
+
/**
* Lookup a DNS entry in the cache. If the entry is found, it fills the
* `addr` structure with the resolved address and returns 0. If the entry is
diff --git a/src/gwproxy/ev/epoll.c b/src/gwproxy/ev/epoll.c
index d46568a6a2b1..d824aeec2958 100644
--- a/src/gwproxy/ev/epoll.c
+++ b/src/gwproxy/ev/epoll.c
@@ -778,24 +778,55 @@ static int handle_connect(struct gwp_wrk *w, struct gwp_conn_pair *gcp)
return 0;
}
+#ifdef CONFIG_RAW_DNS
+static int arm_poll_for_raw_dns_query(struct gwp_wrk *w,
+ struct gwp_conn_pair *gcp)
+{
+ struct gwp_dns_entry *gde = gcp->gde;
+ struct gwp_dns_ctx *dctx;
+ struct gwp_sockaddr addr;
+ uint8_t addrlen;
+ ssize_t r;
+
+ dctx = w->ctx->dns;
+ cp_nsaddr(dctx, &addr, &addrlen);
+ r = __sys_sendto(
+ gde->udp_fd, gde->payload, gde->payloadlen, MSG_NOSIGNAL,
+ &addr.sa, addrlen
+ );
+
+ return (int)r;
+}
+#endif
+
static int arm_poll_for_dns_query(struct gwp_wrk *w,
struct gwp_conn_pair *gcp)
{
struct gwp_dns_entry *gde = gcp->gde;
struct epoll_event ev;
- int r;
+ ssize_t r;
assert(gde);
- assert(gde->ev_fd >= 0);
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);
+ if (w->ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ arm_poll_for_raw_dns_query(w, gcp);
+
+ r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_ADD, gde->udp_fd, &ev);
if (unlikely(r))
- return r;
+ return (int)r;
+#endif
+ } else {
+ assert(gde->ev_fd >= 0);
+ r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_ADD, gde->ev_fd, &ev);
+ if (unlikely(r))
+ return (int)r;
+ }
return 0;
}
@@ -826,13 +857,25 @@ static int handle_ev_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp)
int r, ct = gcp->conn_state;
assert(gde);
- assert(gde->ev_fd >= 0);
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;
+ if (w->ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ r = gwp_dns_process(w->ctx->dns, gde);
+ if (r == -EAGAIN) {
+ pr_dbg(&w->ctx->lh, "DNS Fallback");
+ arm_poll_for_raw_dns_query(w, gcp);
+ return 0;
+ } else if (r)
+ gde->res = r;
+#endif
+ } else {
+ assert(gde->ev_fd >= 0);
+ r = __sys_epoll_ctl(w->ep_fd, EPOLL_CTL_DEL, gde->ev_fd, NULL);
+ if (unlikely(r))
+ return r;
+ }
log_dns_query(w, gcp, gde);
if (likely(!gde->res)) {
@@ -844,8 +887,14 @@ static int handle_ev_dns_query(struct gwp_wrk *w, struct gwp_conn_pair *gcp)
else
r = -EIO;
}
-
- gwp_dns_entry_put(gde);
+
+ if (w->ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ gwp_dns_raw_entry_free(w->ctx->dns, gde);
+#endif
+ } else {
+ gwp_dns_entry_put(gde);
+ }
gcp->gde = NULL;
return r;
}
diff --git a/src/gwproxy/gwproxy.c b/src/gwproxy/gwproxy.c
index 7a90609dd5f1..36305e65c4cb 100644
--- a/src/gwproxy/gwproxy.c
+++ b/src/gwproxy/gwproxy.c
@@ -44,6 +44,10 @@
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
+ { "raw-dns", required_argument, NULL, 'r' },
+#ifdef CONFIG_RAW_DNS
+ { "dns-server", required_argument, NULL, 'j' },
+#endif
{ "event-loop", required_argument, NULL, 'e' },
{ "bind", required_argument, NULL, 'b' },
{ "target", required_argument, NULL, 't' },
@@ -54,7 +58,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' },
@@ -74,6 +77,7 @@ static const struct gwp_cfg default_opts = {
.event_loop = "epoll",
.bind = "[::]:1080",
.target = NULL,
+ .use_raw_dns = false,
.as_socks5 = false,
.as_http = false,
.socks5_prefer_ipv6 = false,
@@ -81,7 +85,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,
@@ -94,6 +97,9 @@ static const struct gwp_cfg default_opts = {
.log_level = 3,
.log_file = "/dev/stdout",
.pid_file = NULL,
+#ifdef CONFIG_RAW_DNS
+ .ns_addr_str = "1.1.1.1"
+#endif
};
__cold
@@ -104,6 +110,10 @@ static void show_help(const char *app)
printf(" -h, --help Show this help message and exit\n");
printf(" -e, --event-loop=name Specify the event loop to use (default: %s)\n", default_opts.event_loop);
printf(" Available values: epoll, io_uring\n");
+ printf(" -r, --raw-dns=0|1 Use experimental raw DNS as the backend (default: %d)\n", default_opts.use_raw_dns);
+#ifdef CONFIG_RAW_DNS
+ printf(" -j, --dns-server=addr DNS server address (default: %s)\n", default_opts.ns_addr_str);
+#endif
printf(" -b, --bind=addr:port Bind to the specified address (default: %s)\n", default_opts.bind);
printf(" -t, --target=addr_port Target address to connect to\n");
printf(" -S, --as-socks5=0|1 Run as a SOCKS5 proxy (default: %d)\n", default_opts.as_socks5);
@@ -114,7 +124,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);
@@ -159,6 +168,9 @@ static int parse_options(int argc, char *argv[], struct gwp_cfg *cfg)
case 'h':
show_help(argv[0]);
exit(0);
+ case 'r':
+ cfg->use_raw_dns = !!atoi(optarg);
+ break;
case 'e':
cfg->event_loop = optarg;
break;
@@ -189,9 +201,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;
@@ -228,6 +237,11 @@ static int parse_options(int argc, char *argv[], struct gwp_cfg *cfg)
case 'p':
cfg->pid_file = optarg;
break;
+#ifdef CONFIG_RAW_DNS
+ case 'j':
+ cfg->ns_addr_str = optarg;
+ break;
+#endif
default:
fprintf(stderr, "Unknown option: %c\n", c);
show_help(argv[0]);
@@ -689,12 +703,23 @@ static int gwp_ctx_init_dns(struct gwp_ctx *ctx)
{
struct gwp_cfg *cfg = &ctx->cfg;
const struct gwp_dns_cfg dns_cfg = {
+ .use_raw_dns = cfg->use_raw_dns,
.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,
+#ifdef CONFIG_RAW_DNS
+ .ns_addr_str = cfg->ns_addr_str
+#endif
};
int r;
+ if (cfg->use_raw_dns) {
+#ifndef CONFIG_RAW_DNS
+ pr_err(&ctx->lh, "raw DNS backend is not enabled in this build");
+ return -ENOSYS;
+#endif
+ }
+
if (!cfg->as_socks5 && !cfg->as_http) {
ctx->dns = NULL;
return 0;
@@ -733,6 +758,10 @@ static int gwp_ctx_parse_ev(struct gwp_ctx *ctx)
ctx->ev_used = GWP_EV_EPOLL;
pr_dbg(&ctx->lh, "Using event loop: epoll");
} else if (!strcmp(ev, "io_uring") || !strcmp(ev, "iou")) {
+ if (ctx->cfg.use_raw_dns) {
+ pr_err(&ctx->lh, "raw DNS backend is not supported for io_uring yet");
+ return -ENOSYS;
+ }
ctx->ev_used = GWP_EV_IO_URING;
pr_dbg(&ctx->lh, "Using event loop: io_uring");
} else {
@@ -990,8 +1019,15 @@ int gwp_free_conn_pair(struct gwp_wrk *w, struct gwp_conn_pair *gcp)
if (gcp->timer_fd >= 0)
__sys_close(gcp->timer_fd);
- if (gcp->gde)
- gwp_dns_entry_put(gcp->gde);
+ if (gcp->gde) {
+ if (w->ctx->cfg.use_raw_dns) {
+#ifdef CONFIG_RAW_DNS
+ gwp_dns_raw_entry_free(w->ctx->dns, gcp->gde);
+#endif
+ } else {
+ gwp_dns_entry_put(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..cdc5adf88ad6 100644
--- a/src/gwproxy/gwproxy.h
+++ b/src/gwproxy/gwproxy.h
@@ -24,6 +24,7 @@ struct gwp_cfg {
const char *event_loop;
const char *bind;
const char *target;
+ bool use_raw_dns;
bool as_socks5;
bool as_http;
bool socks5_prefer_ipv6;
@@ -31,7 +32,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;
@@ -44,6 +44,9 @@ struct gwp_cfg {
int log_level;
const char *log_file;
const char *pid_file;
+#ifdef CONFIG_RAW_DNS
+ const char *ns_addr_str;
+#endif
};
struct gwp_ctx;
--
Ahmad Gani
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 3:36 [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ahmad Gani
2025-08-29 3:36 ` [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code Ahmad Gani
2025-08-29 3:36 ` [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend Ahmad Gani
@ 2025-08-29 4:36 ` Ammar Faizi
2025-08-29 6:57 ` Ahmad Gani
2 siblings, 1 reply; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 4:36 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 10:36:23AM +0700, Ahmad Gani wrote:
> This is revision v7 of the initial work on the integration of the DNS
> parser lib in gwproxy. This is an RFC draft; the patches themselves
> aren't final.
This series looks better. But it still needs further revision.
I detest you keep repeating something like this:
Applying: gwproxy: refactor code base to add experimental raw DNS backend
.git/rebase-apply/patch:546: trailing whitespace.
warning: 1 line adds whitespace errors.
It's trivial for me to fix. But please don't send that again.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend
2025-08-29 3:36 ` [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend Ahmad Gani
@ 2025-08-29 4:36 ` Ammar Faizi
2025-08-29 7:35 ` Ahmad Gani
0 siblings, 1 reply; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 4:36 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 10:36:25AM +0700, Ahmad Gani wrote:
> + close(e->udp_fd);
Again, please use __sys_close() instead of close() for all code in
src/gwproxy/. This rule applies to future patches too.
> + default:
> + assert(0);
> + break;
> + }
> +
> + txid = (uint16_t)rand();
> + r = gwdns_build_query(txid, name, af, e->payload, sizeof(e->payload));
This part is problematic when compiled without debug mode because
'assert(0)' is actually a macro that does nothing when compiled
with -DNDEBUG. The compiler says:
gcc -ggdb3 -rdynamic -O2 -fpic -fPIC -shared -o libgwpsocks5.so src/gwproxy/socks5.c.o -lpthread -lanl
src/gwproxy/dns.c: In function ‘gwp_dns_queue’:
src/gwproxy/dns.c:969:21: warning: ‘af’ may be used uninitialized [-Wmaybe-uninitialized]
969 | r = gwdns_build_query(txid, name, af, e->payload, sizeof(e->payload));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/gwproxy/dns.c:937:13: note: ‘af’ was declared here
937 | int af;
| ^~
I suggest to return -EINVAL instead of break. Don't forget to free
associated resources if needed. You can keep assert(0) for the debug
mode. Keep in mind that assert() is a macro that disappears entirely
when compiled with -DNDEBUG.
I have added a GitHub CI build using './configure --debug' to cover
breakage for both non-debug and debug build cases. You can push your
changes to your GitHub fork and make sure everything is green before
sending the patches to the list.
You need to rebase your work on top of b2a7a2d33ba867 ("github: Test
build with --debug flag"), obviously.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code
2025-08-29 3:36 ` [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code Ahmad Gani
@ 2025-08-29 4:39 ` Ammar Faizi
0 siblings, 0 replies; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 4:39 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 10:36:24AM +0700, Ahmad Gani wrote:
> +
> +#endif /* #ifndef GWP_DNS_PARSER_H */
> \ No newline at end of file
Please end all text files with a blank line.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 4:36 ` [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ammar Faizi
@ 2025-08-29 6:57 ` Ahmad Gani
2025-08-29 7:22 ` Ammar Faizi
0 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 6:57 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 11:36 AM Ammar Faizi wrote:
> I detest you keep repeating something like this:
>
> Applying: gwproxy: refactor code base to add experimental raw DNS backend
> .git/rebase-apply/patch:546: trailing whitespace.
>
> warning: 1 line adds whitespace errors.
>
> It's trivial for me to fix. But please don't send that again.
Perhaps I should use a script to check whether the files adhere to the
required rules and patterns. Do you know of any existing tools or scripts
that perform such checks?
--
Ahmad Gani
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 6:57 ` Ahmad Gani
@ 2025-08-29 7:22 ` Ammar Faizi
2025-08-29 7:30 ` Ammar Faizi
2025-08-29 7:36 ` Ammar Faizi
0 siblings, 2 replies; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 7:22 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 1:57 PM Ahmad Gani wrote:
> Perhaps I should use a script to check whether the files adhere to the
> required rules and patterns. Do you know of any existing tools or scripts
> that perform such checks?
There are several ways to check for it:
1) The easiest way is to turn on the regex search. You can find this
pattern in your epoll.c:
(^|.)\s+$
It will match trailing whitespaces.
2) Try to apply your own patches with 'git am'. It will yell at you if
there are whitespaces.
3) In the Linux kernel, there is a script called checkpatch.pl. It can
detect trailing whitespaces and other trivial formatting errors. I
don't usually use it, though.
https://github.com/torvalds/linux/blob/master/scripts/checkpatch.pl
Unfortunately, it must be run from the root directory of the kernel
tree. So you need to git clone the entire kernel tree to use it. Maybe
it can be modified to run outside of the kernel tree, but I don't
think it's worth it. Not that urgent anyway.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:22 ` Ammar Faizi
@ 2025-08-29 7:30 ` Ammar Faizi
2025-08-29 7:39 ` Ahmad Gani
2025-08-29 7:36 ` Ammar Faizi
1 sibling, 1 reply; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 7:30 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:22 PM Ammar Faizi wrote:
> Unfortunately, it must be run from the root directory of the kernel
> tree. So you need to git clone the entire kernel tree to use it. Maybe
> it can be modified to run outside of the kernel tree, but I don't
> think it's worth it. Not that urgent anyway.
FWIW, I tried to run the checkpatch.pl script on your patch #2. Here
is the result:
https://gist.github.com/ammarfaizi2/589dfc9cdef9963cfa854273d8702ca7
The most bothersome part is the error, obviously. That whitespace. If
you can be clean, that would be great.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend
2025-08-29 4:36 ` Ammar Faizi
@ 2025-08-29 7:35 ` Ahmad Gani
2025-08-29 7:39 ` Ammar Faizi
0 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 7:35 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 11:36 AM Ammar Faizi wrote:
> On Fri, Aug 29, 2025 at 10:36:25AM +0700, Ahmad Gani wrote:
> > + close(e->udp_fd);
>
> Again, please use __sys_close() instead of close() for all code in
> src/gwproxy/. This rule applies to future patches too.
Oops, sorry for the oversight. I will replace it.
> > + default:
> > + assert(0);
> > + break;
> > + }
> > +
> > + txid = (uint16_t)rand();
> > + r = gwdns_build_query(txid, name, af, e->payload, sizeof(e->payload));
>
> This part is problematic when compiled without debug mode because
> 'assert(0)' is actually a macro that does nothing when compiled
> with -DNDEBUG. The compiler says:
>
> gcc -ggdb3 -rdynamic -O2 -fpic -fPIC -shared -o libgwpsocks5.so src/gwproxy/socks5.c.o -lpthread -lanl
> src/gwproxy/dns.c: In function ‘gwp_dns_queue’:
> src/gwproxy/dns.c:969:21: warning: ‘af’ may be used uninitialized [-Wmaybe-uninitialized]
> 969 | r = gwdns_build_query(txid, name, af, e->payload, sizeof(e->payload));
> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> src/gwproxy/dns.c:937:13: note: ‘af’ was declared here
> 937 | int af;
> | ^~
>
> I suggest to return -EINVAL instead of break.
The function returns NULL in case of error.
> Don't forget to free associated resources if needed. You can keep assert(0)
> for the debug mode. Keep in mind that assert() is a macro that disappears
> entirely when compiled with -DNDEBUG.
I was not aware of the warning because I compiled it in debug mode.
Thanks for the notice. I will fix it.
> I have added a GitHub CI build using './configure --debug' to cover
> breakage for both non-debug and debug build cases. You can push your
> changes to your GitHub fork and make sure everything is green before
> sending the patches to the list.
Okay, I will make sure it's all green first.
> You need to rebase your work on top of b2a7a2d33ba867 ("github: Test
> build with --debug flag"), obviously.
FYI: This patchset is already rebased on top of b2a7a2d33ba867.
--
Ahmad Gani
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:22 ` Ammar Faizi
2025-08-29 7:30 ` Ammar Faizi
@ 2025-08-29 7:36 ` Ammar Faizi
2025-08-29 7:51 ` Ahmad Gani
1 sibling, 1 reply; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 7:36 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:22 PM Ammar Faizi wrote:
> 2) Try to apply your own patches with 'git am'. It will yell at you if
> there are whitespaces.
s/whitespaces/trailing whitespaces/
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend
2025-08-29 7:35 ` Ahmad Gani
@ 2025-08-29 7:39 ` Ammar Faizi
0 siblings, 0 replies; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 7:39 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:36 PM Ahmad Gani wrote:
> The function returns NULL in case of error.
Oh, right, I missed the return type. Yes, 'return NULL' instead of
'break' should be the expected code.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:30 ` Ammar Faizi
@ 2025-08-29 7:39 ` Ahmad Gani
2025-08-29 7:45 ` Ammar Faizi
0 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 7:39 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:31 PM Ammar Faizi wrote:
> FWIW, I tried to run the checkpatch.pl script on your patch #2. Here
> is the result:
>
> https://gist.github.com/ammarfaizi2/589dfc9cdef9963cfa854273d8702ca7
>
> The most bothersome part is the error, obviously. That whitespace. If
> you can be clean, that would be great.
Cool. it can detect something that compiler didn't catch:
```
WARNING: break is not useful after a goto
#229: FILE: src/gwproxy/dns.c:282:
+ goto exit_free_ai;
+ break;
```
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:39 ` Ahmad Gani
@ 2025-08-29 7:45 ` Ammar Faizi
0 siblings, 0 replies; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 7:45 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:40 PM Ahmad Gani wrote:
> Cool. it can detect something that compiler didn't catch:
Yeah, that's great. But note that you don't need to address all
warnings. That long printf() call is totally fine here. And it's
better to be written that long.
I am not sure why it is yelling about ENOSYS. It does not make much
sense to warn that for gwproxy. So you can ignore some warnings. My
primary complaint was the whitespace error.
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:36 ` Ammar Faizi
@ 2025-08-29 7:51 ` Ahmad Gani
2025-08-29 8:00 ` Ammar Faizi
0 siblings, 1 reply; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 7:51 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:36 PM Ammar Faizi wrote:
> On Fri, Aug 29, 2025 at 2:22 PM Ammar Faizi wrote:
> > 2) Try to apply your own patches with 'git am'. It will yell at you if
> > there are whitespaces.
>
> s/whitespaces/trailing whitespaces/
I guess I will use VSCode approach, it allow search with regex to find
trailing space, tried this:
```
[ \t]+$
```
In the search bar and enable the regex icon.
It detects trailing space from other files too, but I guess it's fine as
it's not part of the patchset.
--
Ahmad Gani
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 7:51 ` Ahmad Gani
@ 2025-08-29 8:00 ` Ammar Faizi
2025-08-29 8:06 ` Ahmad Gani
0 siblings, 1 reply; 17+ messages in thread
From: Ammar Faizi @ 2025-08-29 8:00 UTC (permalink / raw)
To: Ahmad Gani; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 2:52 PM Ahmad Gani wrote:
> It detects trailing space from other files too, but I guess it's fine as
> it's not part of the patchset.
It would be great if you could send one patch to clean all trailing
whitespaces for all files in src/gwproxy/. Commits introducing those
trailing whitespaces did not go via git-format-patch and git-am, so
they were not caught and did not cause errors
--
Ammar Faizi
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy
2025-08-29 8:00 ` Ammar Faizi
@ 2025-08-29 8:06 ` Ahmad Gani
0 siblings, 0 replies; 17+ messages in thread
From: Ahmad Gani @ 2025-08-29 8:06 UTC (permalink / raw)
To: Ammar Faizi; +Cc: Alviro Iskandar Setiawan, GNU/Weeb Mailing List
On Fri, Aug 29, 2025 at 3:00 PM Ammar Faizi wrote:
> On Fri, Aug 29, 2025 at 2:52 PM Ahmad Gani wrote:
> > It detects trailing space from other files too, but I guess it's fine as
> > it's not part of the patchset.
>
> It would be great if you could send one patch to clean all trailing
> whitespaces for all files in src/gwproxy/. Commits introducing those
> trailing whitespaces did not go via git-format-patch and git-am, so
> they were not caught and did not cause errors
Well, I don't mind cleaning it up, I will send the patch shortly.
--
Ahmad Gani
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2025-08-29 8:06 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-29 3:36 [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ahmad Gani
2025-08-29 3:36 ` [PATCH gwproxy v7 1/2] dnsparser: Add dns parser code Ahmad Gani
2025-08-29 4:39 ` Ammar Faizi
2025-08-29 3:36 ` [PATCH gwproxy v7 2/2] gwproxy: refactor code base to add experimental raw DNS backend Ahmad Gani
2025-08-29 4:36 ` Ammar Faizi
2025-08-29 7:35 ` Ahmad Gani
2025-08-29 7:39 ` Ammar Faizi
2025-08-29 4:36 ` [PATCH gwproxy v7 0/2] Initial work on integration of DNS parser lib in gwproxy Ammar Faizi
2025-08-29 6:57 ` Ahmad Gani
2025-08-29 7:22 ` Ammar Faizi
2025-08-29 7:30 ` Ammar Faizi
2025-08-29 7:39 ` Ahmad Gani
2025-08-29 7:45 ` Ammar Faizi
2025-08-29 7:36 ` Ammar Faizi
2025-08-29 7:51 ` Ahmad Gani
2025-08-29 8:00 ` Ammar Faizi
2025-08-29 8:06 ` Ahmad Gani
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox