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=1754452662; bh=cc3hBInGAwSoaa6obAm6C9x78n6MzFvg5xuip7ksie8=; 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=wIVbfhtLWeKA1VXEigdvGn2MYQicEENJ4D+ePOAVno4/bq09pVZmre6m6uLVS5GBX MoexkGgyTXf2rx3r3QjVDfhmlELlZ1e/YiehpL++nHtkhdnvsTtGHyKaMe6agK7zNH xsn51MsunCYtDHAqlJDzWflBUErEAYRg8PITgtV80rqbPRxrFFrCRdT9SRZe91LN9f rc+yYvmSU7Q8cXXS9E6bWf3dxQpRkMONZu9lNM368kUCGELFuDjs4NENUWbFwqz5/L 7yWwdorjhdwW1L/fbbuV3DQejaECPFvSpl5W+6nWooPC8KnCoouBOy7GZwyTreJc6G Yr9U0gmvGIUGw== Received: from zero (unknown [182.253.228.106]) by server-vie001.gnuweeb.org (Postfix) with ESMTPSA id 41EF03127C39; Wed, 6 Aug 2025 03:57:41 +0000 (UTC) From: Ahmad Gani To: Ammar Faizi Cc: Ahmad Gani , Alviro Iskandar Setiawan , GNU/Weeb Mailing List Subject: [PATCH gwproxy v4 6/6] dnslookup: Make gw_ares_getaddrinfo asynchronous Date: Wed, 6 Aug 2025 10:57:24 +0700 Message-ID: <20250806035727.216702-7-reyuki@gnuweeb.org> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250806035727.216702-1-reyuki@gnuweeb.org> References: <20250806035727.216702-1-reyuki@gnuweeb.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: The previous gw_ares_getaddrinfo version is blocking and no different with glibc's getaddrinfo, this changes introduce asynchronous version of gw_ares_getaddrinfo function. Signed-off-by: Ahmad Gani --- src/gwproxy/dnslookup.c | 367 ++++++++++++++++++++++++++++------------ src/gwproxy/dnslookup.h | 99 ++++++++--- src/gwproxy/dnsparser.c | 14 +- 3 files changed, 337 insertions(+), 143 deletions(-) diff --git a/src/gwproxy/dnslookup.c b/src/gwproxy/dnslookup.c index de286fb5ab14..4832394adbed 100644 --- a/src/gwproxy/dnslookup.c +++ b/src/gwproxy/dnslookup.c @@ -6,48 +6,45 @@ #include #include #include -#include +#include #include -static int resolve_name(int sockfd, const char *name, uint16_t type, uint16_t nport, - struct gw_ares_addrinfo *result, struct gw_addrinfo_node **tail) +int gw_ares_process_event(gw_ares_channel_t *channel) { - uint8_t send_buff[UDP_MSG_LIMIT]; uint8_t recv_buff[UDP_MSG_LIMIT]; - gwdns_query_pkt *query_pkt; + struct gw_ares_request *req; gwdns_answ_data raw_answ; - gwdns_question_part q; - ssize_t buff_len; + socklen_t addrlen; int ret; - q.domain = name; - q.type = type; - q.dst_buffer = send_buff; - q.dst_len = UDP_MSG_LIMIT; - buff_len = construct_question(&q); - if (buff_len < 0) { - /* - * I'm confident that 512 bytes is more than sufficient - * to construct single dns query packet. - */ - assert(buff_len != -ENOBUFS); - ret = GW_ARES_EINVAL; - return ret; - } - query_pkt = (void *)send_buff; - - ret = __sys_send(sockfd, send_buff, buff_len, MSG_NOSIGNAL); + addrlen = sizeof(*channel->addr); + ret = __sys_recvfrom(channel->sockfd, recv_buff, UDP_MSG_LIMIT, + MSG_NOSIGNAL, &channel->addr->sa, &addrlen); if (ret < 0) { - ret = GW_ARES_INTERNAL_ERR; + /* GW_ARES_WAITING is unused for now. */ + if (ret == -EAGAIN) + ret = GW_ARES_WAITING; + else + ret = GW_ARES_INTERNAL_ERR; return ret; } - ret = __sys_recv(sockfd, recv_buff, UDP_MSG_LIMIT, MSG_NOSIGNAL); - if (ret < 0) { - ret = GW_ARES_INTERNAL_ERR; - return ret; + if (ret < 2) + return GW_ARES_EINVAL; + + printf("%d bytes received\n", ret); + req = NULL; + int idx; + for (idx = 0; idx < channel->req_nr; idx++) { + if (!memcmp(&channel->reqs[idx]->txid, recv_buff, 2)) { + req = channel->reqs[idx]; + break; + } } + if (!req) + return GW_ARES_NOENT; + /* * TODO(reyuki): even though it's unlikely, * but what todo when the connection is closed? @@ -55,7 +52,8 @@ static int resolve_name(int sockfd, const char *name, uint16_t type, uint16_t np */ assert(ret); - ret = serialize_answ(query_pkt->hdr.id, recv_buff, ret, &raw_answ); + memset(&raw_answ, 0, sizeof(raw_answ)); + ret = serialize_answ(req->txid, recv_buff, ret, &raw_answ); if (ret) { /* * TODO(reyuki): the reason of failure can vary, @@ -63,13 +61,20 @@ static int resolve_name(int sockfd, const char *name, uint16_t type, uint16_t np * short recv, but this is UDP packet, retry from send? * or just drop the request? */ + assert(ret == -ENODATA); } - assert(!ret); - /* TODO(reyuki): hints->ai_family is used to filter results */ - for (size_t i = 0; i < raw_answ.hdr.ancount; i++) { - gwdns_serialized_answ *answ = raw_answ.rr_answ[i]; - struct gw_addrinfo_node *new_node = malloc(sizeof(*new_node)); + printf("ancount=%hu\n", raw_answ.hdr.ancount); + + size_t i; + for (i = 0; i < raw_answ.hdr.ancount; i++) { + struct gw_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) { ret = GW_ARES_ENOMEM; goto exit_free; @@ -77,119 +82,193 @@ static int resolve_name(int sockfd, const char *name, uint16_t type, uint16_t np 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(new_node->ai_addr.i6); - new_node->ai_addr.i6.sin6_port = nport; - new_node->ai_addr.i6.sin6_family = AF_INET6; + new_node->ai_addrlen = sizeof(i6); + i6->sin6_port = req->dst_port; + i6->sin6_family = AF_INET6; /* * no overflow. * it's guaranteed to be true by serialize_answ function */ - assert(sizeof(new_node->ai_addr.i6.sin6_addr) == answ->rdlength); - memcpy(&new_node->ai_addr.i6.sin6_addr, answ->rdata, answ->rdlength); + 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(new_node->ai_addr.i4); - new_node->ai_addr.i4.sin_port = nport; - new_node->ai_addr.i4.sin_family = AF_INET; + new_node->ai_addrlen = sizeof(i4); + i4->sin_port = req->dst_port; + i4->sin_family = AF_INET; /* * no overflow. * it's guaranteed to be true by serialize_answ function */ - assert(sizeof(new_node->ai_addr.i4.sin_addr) == answ->rdlength); - memcpy(&new_node->ai_addr.i4.sin_addr, answ->rdata, answ->rdlength); + assert(sizeof(i4->sin_addr) == answ->rdlength); + memcpy(&i4->sin_addr, answ->rdata, answ->rdlength); new_node->ai_ttl = answ->ttl; } - if (!*tail) - result->nodes = new_node; + if (!req->tail) + req->results->nodes = new_node; else - (*tail)->ai_next = new_node; - *tail = new_node; + req->tail->ai_next = new_node; + req->tail = new_node; } ret = GW_ARES_SUCCESS; exit_free: free_serialize_answ(&raw_answ); + req->refcnt--; + assert(req->refcnt >= 0); + if (!req->refcnt) { + channel->getaddrinfo_cb( + req->callback_args, GW_ARES_SUCCESS, req->results + ); + free(req); + channel->req_nr--; + req = channel->reqs[channel->req_nr]; + channel->reqs[idx] = req; + channel->reqs[channel->req_nr] = NULL; + } return ret; } +static int resolve_name(gw_ares_channel_t *channel, const char *name, + uint16_t type, uint16_t txid) +{ + uint8_t send_buff[UDP_MSG_LIMIT]; + gwdns_question_part q; + ssize_t buff_len; + int ret; + + q.domain = name; + q.type = type; + q.txid = txid; + q.dst_buffer = send_buff; + q.dst_len = UDP_MSG_LIMIT; + buff_len = construct_question(&q); + if (buff_len < 0) { + /* + * I'm confident that 512 bytes is more than sufficient + * to construct single dns query packet. + */ + assert(buff_len != -ENOBUFS); + ret = GW_ARES_EINVAL; + return ret; + } + + ret = __sys_sendto( + channel->sockfd, send_buff, buff_len, MSG_NOSIGNAL, + &channel->addr->sa, channel->addrlen + ); + if (ret < 0) + return ret; + + return 0; +} + +static int resize_reqs(gw_ares_channel_t *channel) +{ + struct gw_ares_request **ptr; + int new_cap; + + new_cap = channel->req_cap * 2; + ptr = realloc(channel->reqs, new_cap); + if (!ptr) + return -ENOMEM; + + memset(ptr[channel->req_nr], 0, channel->req_nr * sizeof(*channel->reqs)); + channel->reqs = ptr; + channel->req_cap = new_cap; + + return 0; +} + void gw_ares_getaddrinfo(gw_ares_channel_t *channel, const char *name, const char *service, - const struct gw_ares_addrinfo_hints *hints, - gw_ares_addrinfo_callback callback, void *arg) + const struct gw_ares_addrinfo_hints *hints, void *arg) { - struct gw_ares_addrinfo *result; - struct gw_addrinfo_node *tail; - struct gwp_sockaddr *addr; - socklen_t addrlen; - int ret, sockfd; + struct gw_ares_addrinfo *results; + struct gw_ares_request *req; uint16_t nport; uint8_t mask; + int ret; + + if (channel->req_nr == channel->req_cap) { + ret = resize_reqs(channel); + if (ret) { + ret = GW_ARES_ENOMEM; + goto error; + } + } + assert(channel->req_nr < channel->req_cap); + + req = malloc(sizeof(*req)); + if (!req) { + ret = GW_ARES_ENOMEM; + goto error; + } + channel->reqs[channel->req_nr] = req; + channel->req_nr++; + + req->tail = NULL; + req->txid = (uint16_t)rand(); switch (hints->ai_family) { case AF_UNSPEC: mask = I6_BIT | I4_BIT; + req->refcnt = 2; break; case AF_INET: + req->refcnt = 1; mask = I4_BIT; break; case AF_INET6: + req->refcnt = 1; mask = I6_BIT; break; default: ret = GW_ARES_EINVAL; - goto error; + goto error_free_req; } nport = (uint16_t)atoi(service); if (!nport) { ret = GW_ARES_EINVAL; - goto error; + goto error_free_req; } nport = htons(nport); - result = malloc(sizeof(*result)); - if (!result) { + results = malloc(sizeof(*results)); + if (!results) { ret = GW_ARES_ENOMEM; - goto error; - } - - addr = &channel->servers[0]; - sockfd = __sys_socket(addr->sa.sa_family, SOCK_DGRAM, 0); - if (sockfd < 0) { - ret = GW_ARES_INTERNAL_ERR; - goto error_free; + goto error_free_req; } - addrlen = addr->sa.sa_family == AF_INET ? sizeof(addr->i4) : sizeof(addr->i6); - ret = __sys_connect(sockfd, &addr->sa, addrlen); - if (ret < 0) { - ret = GW_ARES_INTERNAL_ERR; - goto error_close; - } + req->dst_port = nport; + req->results = results; - tail = NULL; if (IS_I6(mask)) { - ret = resolve_name(sockfd, name, TYPE_AAAA, nport, result, &tail); + ret = resolve_name(channel, name, TYPE_AAAA, req->txid); if (ret) - goto error_close; + goto error_free_all; } if (IS_I4(mask)) { - ret = resolve_name(sockfd, name, TYPE_A, nport, result, &tail); + ret = resolve_name(channel, name, TYPE_A, req->txid); if (ret) - goto error_close; + goto error_free_all; } - callback(arg, GW_ARES_SUCCESS, result); return; -error_close: - __sys_close(sockfd); -error_free: - free(result); +error_free_all: + free(results); +error_free_req: + free(req); + channel->req_nr--; + channel->reqs[channel->req_nr] = NULL; error: - callback(arg, ret, result); + channel->getaddrinfo_cb(arg, ret, results); } void gw_ares_freeaddrinfo(struct gw_ares_addrinfo *ai) @@ -218,68 +297,136 @@ int gw_ares_init(gw_ares_channel_t **channel, struct gw_ares_options *opts) *channel = c; c->nr_server = opts->nr_server; + + ret = -ENOMEM; + + c->reqs = calloc(DEFAULT_REQ_CAP, sizeof(*c->reqs)); + if (!c->reqs) + goto error_free_c; + c->servers = malloc(c->nr_server * sizeof(*c->servers)); - if (!c->servers) { - free(c); - return -ENOMEM; - } + if (!c->servers) + goto error_free_reqs; + /* - * TODO(reyuki): validate flags and - * for now use it to control recursion desired (RD) bit? + * TODO(reyuki): validate flags + * For now flags is unused but it may be used to control + * recursion desired (RD) bit and other things in the future. */ c->flags = opts->flags; - for (int i = 0; i < c->nr_server; i++) { - ret = convert_str_to_ssaddr(opts->servers[i], &c->servers[i], DEFAULT_DOMAIN_PORT); - if (ret) { - free(c->servers); - free(c); - return ret; - } + int i; + for (i = 0; i < c->nr_server; i++) { + ret = convert_str_to_ssaddr( + opts->servers[i], &c->servers[i], DEFAULT_DOMAIN_PORT + ); + if (ret) + goto error_free_all; } + ret = __sys_socket(c->servers[0].sa.sa_family, SOCK_DGRAM | SOCK_NONBLOCK, 0); + if (ret < 0) + goto error_free_all; + + opts->sockstate_cb(opts->sockstate_data, ret); + c->req_cap = DEFAULT_REQ_CAP; + c->req_nr = 0; + c->getaddrinfo_cb = opts->getaddrinfo_cb; + c->sockfd = ret; + c->addr = &c->servers[0]; + c->addrlen = c->addr->sa.sa_family == AF_INET ? + sizeof(c->addr->i4) : sizeof(c->addr->i6); + return 0; +error_free_all: + free(c->servers); +error_free_reqs: + free(c->reqs); +error_free_c: + free(c); + return ret; } void gw_ares_deinit(gw_ares_channel_t *channel) { free(channel->servers); + free(channel->reqs); free(channel); } -static void gw_ares_cb(void *arg, int status, struct gw_ares_addrinfo *result) +static void sockstate_cb(void *data, int socket) +{ + int epfd = *(int *)data; + struct epoll_event ev = { + .events = EPOLLIN + }; + + __sys_epoll_ctl(epfd, EPOLL_CTL_ADD, socket, &ev); +} + +static void getaddrinfo_cb(void *arg, int status, struct gw_ares_addrinfo *result) { struct gw_addrinfo_node *node; char buf[FULL_ADDRSTRLEN]; (void)arg; + if (status) + printf("status=%d\n", status); assert(!status); node = result->nodes; while (node) { int r = convert_ssaddr_to_str(buf, &node->ai_addr); assert(!r); - printf("%s: %s\n", node->ai_family == AF_INET6 ? "IPv6" : "IPv4", buf); + printf( + "%s: %s\n", + node->ai_family == AF_INET6 ? "IPv6" : "IPv4", buf + ); node = node->ai_next; } + gw_ares_freeaddrinfo(result); } int main(void) { + struct gw_ares_addrinfo_hints hints; + struct gw_ares_options opts; gw_ares_channel_t *channel; - struct gw_ares_addrinfo_hints hints = { - .ai_family = AF_UNSPEC - }; + struct epoll_event ev; + int ret, epfd; + const char *servers[] = {"1.1.1.1", "8.8.8.8"}; - struct gw_ares_options opts = { - .flags = 0, - .nr_server = 1, - .servers = servers - }; - int ret; + + epfd = __sys_epoll_create1(0); + if (epfd < 0) + return -EXIT_FAILURE; + + opts.flags = 0; + opts.nr_server = 1; + opts.servers = servers; + opts.sockstate_data = &epfd; + opts.sockstate_cb = sockstate_cb; + opts.getaddrinfo_cb = getaddrinfo_cb; ret = gw_ares_init(&channel, &opts); if (ret) return -EXIT_FAILURE; - gw_ares_getaddrinfo(channel, "google.com", "80", &hints, gw_ares_cb, NULL); + + hints.ai_family = AF_UNSPEC; + gw_ares_getaddrinfo(channel, "google.com", "80", &hints, NULL); + gw_ares_getaddrinfo(channel, "facebook.com", "80", &hints, NULL); + gw_ares_getaddrinfo(channel, "github.com", "80", &hints, NULL); + + while (true) { + if (!channel->req_nr) + break; + + ret = __sys_epoll_wait(epfd, &ev, 1024, -1); + if (ret < 0) + return -EXIT_FAILURE; + + if (ev.events & EPOLLIN) + gw_ares_process_event(channel); + } + gw_ares_deinit(channel); } diff --git a/src/gwproxy/dnslookup.h b/src/gwproxy/dnslookup.h index a4ed02cab5f1..a98ed6cff23c 100644 --- a/src/gwproxy/dnslookup.h +++ b/src/gwproxy/dnslookup.h @@ -3,6 +3,7 @@ #include #include +#define DEFAULT_REQ_CAP 50 #define DEFAULT_DOMAIN_PORT 53 #define I6_BIT (1u << 0) #define I4_BIT (1u << 1) @@ -18,31 +19,35 @@ enum { * internal error can be interpreted as system call failure, * however, the cause of it can be vary. */ - GW_ARES_INTERNAL_ERR = 3 + GW_ARES_INTERNAL_ERR = 3, + GW_ARES_WAITING = 4, + GW_ARES_NOENT = 5 }; -struct gw_ares_options { - int flags; - int nr_server; +typedef void (*gw_ares_sockstate_cb)(void *data, int socket); - /* - * the string format is ip:port, the ip may be encapsulated with square - * brackets ([]), and must be if using IPv6. - */ - const char **servers; +struct gw_ares_addrinfo { + struct gw_addrinfo_node *nodes; + char *name; }; -struct gw_ares_channeldata { +/* + * result is only initialized if status == GW_ARES_SUCCESS. + */ +typedef void (*gw_ares_addrinfo_cb)(void *arg, int status, + struct gw_ares_addrinfo *result); + +struct gw_ares_options { int flags; int nr_server; - /* currently only index 0 is used, and others are ignored. */ - struct gwp_sockaddr *servers; -}; - -typedef struct gw_ares_channeldata gw_ares_channel_t; - -struct gw_ares_addrinfo_hints { - int ai_family; + /* + * the string format is ip:port, the ip may be encapsulated with square + * brackets ([]), and must be if using IPv6. + */ + const char **servers; + gw_ares_sockstate_cb sockstate_cb; + gw_ares_addrinfo_cb getaddrinfo_cb; + void *sockstate_data; }; /* @@ -59,16 +64,36 @@ struct gw_addrinfo_node { struct gw_addrinfo_node *ai_next; }; -struct gw_ares_addrinfo { - struct gw_addrinfo_node *nodes; - char *name; +struct gw_ares_request { + int refcnt; + uint16_t dst_port; + uint16_t txid; + struct gw_ares_addrinfo *results; + struct gw_addrinfo_node *tail; + void *callback_args; }; -/* - * result is only initialized if status == GW_ARES_SUCCESS. - */ -typedef void (*gw_ares_addrinfo_callback)(void *arg, int status, - struct gw_ares_addrinfo *result); +struct gw_ares_channeldata { + int flags; + int nr_server; + /* currently only index 0 is used, and others are ignored. */ + struct gwp_sockaddr *servers; + gw_ares_addrinfo_cb getaddrinfo_cb; + + int sockfd; + struct gwp_sockaddr *addr; + socklen_t addrlen; + + struct gw_ares_request **reqs; + int req_nr; + int req_cap; +}; + +typedef struct gw_ares_channeldata gw_ares_channel_t; + +struct gw_ares_addrinfo_hints { + int ai_family; +}; /* * Initiate a host query by name and service @@ -86,8 +111,7 @@ typedef void (*gw_ares_addrinfo_callback)(void *arg, int status, */ void gw_ares_getaddrinfo(gw_ares_channel_t *channel, const char *name, const char *service, - const struct gw_ares_addrinfo_hints *hints, - gw_ares_addrinfo_callback callback, void *arg); + const struct gw_ares_addrinfo_hints *hints, void *arg); /* * Free the resources allocated by gw_ares_getaddrinfo. @@ -115,4 +139,23 @@ void gw_ares_freeaddrinfo(struct gw_ares_addrinfo *ai); * @return zero on success and negative integer on error */ int gw_ares_init(gw_ares_channel_t **channel, struct gw_ares_options *opts); + +/* + * Free the resources allocated by gw_ares_init. + * + * @param channel + */ void gw_ares_deinit(gw_ares_channel_t *channel); + +/* + * Process event + * + * Description: + * the gw_ares_process_event function performing socket-related operation, + * you MUST call this when certain I/O in DNS socket file descriptor + * is ready to use. + * + * @param channel + * @return zero on success and negative integer on error + */ +int gw_ares_process_event(gw_ares_channel_t *channel); diff --git a/src/gwproxy/dnsparser.c b/src/gwproxy/dnsparser.c index 2d8af1da2893..442ea5558174 100644 --- a/src/gwproxy/dnsparser.c +++ b/src/gwproxy/dnsparser.c @@ -122,7 +122,8 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o if (!out->rr_answ) return -ENOMEM; - for (size_t i = 0; i < hdr->ancount; i++) { + size_t i; + for (i = 0; i < hdr->ancount; i++) { uint16_t is_compressed, rdlength; gwdns_serialized_answ *item = malloc(sizeof(gwdns_serialized_answ)); if (!item) { @@ -211,7 +212,7 @@ int serialize_answ(uint16_t txid, uint8_t *in, size_t in_len, gwdns_answ_data *o return 0; exit_free: - for (size_t i = 0; i < out->hdr.ancount; i++) { + for (i = 0; i < out->hdr.ancount; i++) { free(out->rr_answ[i]->rdata); free(out->rr_answ[i]); } @@ -221,7 +222,8 @@ exit_free: void free_serialize_answ(gwdns_answ_data *answ) { - for (size_t i = 0; i < answ->hdr.ancount; i++) { + size_t i; + for (i = 0; i < answ->hdr.ancount; i++) { free(answ->rr_answ[i]->rdata); free(answ->rr_answ[i]); } @@ -300,7 +302,8 @@ void test_parse_ipv4(void) memcpy(&txid, recv_pkt, 2); assert(!serialize_answ(txid, recv_pkt, sizeof(recv_pkt), &d)); - for (size_t i = 0; i < d.hdr.ancount; i++) { + size_t i; + for (i = 0; i < d.hdr.ancount; i++) { struct gwp_sockaddr gs; gwdns_serialized_answ *answ; char buff[FULL_ADDRSTRLEN]; @@ -367,7 +370,8 @@ void test_parse_ipv6(void) ret = serialize_answ(txid, recv_pkt, sizeof(recv_pkt), &d); assert(!ret); - for (size_t i = 0; i < d.hdr.ancount; i++) { + size_t i; + for (i = 0; i < d.hdr.ancount; i++) { struct gwp_sockaddr gs; gwdns_serialized_answ *answ; char buff[FULL_ADDRSTRLEN]; -- Ahmad Gani