77#endif
88
99#include "dns.h"
10+ #include "dns_cache.h"
1011
1112#include <assert.h>
1213#include <stdlib.h>
@@ -31,21 +32,6 @@ struct gwp_dns_wrk {
3132 pthread_t thread ;
3233};
3334
34- struct cache_entry {
35- char * name ;
36- char * service ;
37- struct addrinfo * res ; /* Cached result. */
38- time_t expired_at ; /* Expiration time. */
39- };
40-
41- struct gwp_dns_cache {
42- pthread_rwlock_t lock ; /* Lock for the cache. */
43- struct cache_entry * entries ; /* Array of entries. */
44- uint32_t nr ; /* Number of entries. */
45- uint32_t cap ; /* Capacity of the array. */
46- time_t last_scan ; /* Last scan time. */
47- };
48-
4935struct gwp_dns_ctx {
5036 volatile bool should_stop ;
5137 pthread_mutex_t lock ;
@@ -56,6 +42,7 @@ struct gwp_dns_ctx {
5642 struct gwp_dns_entry * tail ;
5743 struct gwp_dns_wrk * workers ;
5844 struct gwp_dns_cache * cache ;
45+ time_t last_scan ;
5946 struct gwp_dns_cfg cfg ;
6047};
6148
@@ -160,125 +147,16 @@ static void prep_hints(struct addrinfo *hints, uint32_t restyp)
160147 hints -> ai_family = AF_INET6 ;
161148}
162149
163- static void free_cache_entry (struct cache_entry * e )
164- {
165- if (!e )
166- return ;
167-
168- free (e -> name );
169- if (e -> res )
170- freeaddrinfo (e -> res );
171- }
172-
173- static void gwp_dns_cache_scan_and_delete_expired_entries (struct gwp_dns_cache * cache )
174- {
175- uint32_t i ;
176-
177- pthread_rwlock_wrlock (& cache -> lock );
178- for (i = 0 ; i < cache -> nr ; i ++ ) {
179- struct cache_entry * last , * e = & cache -> entries [i ];
180- time_t now = time (NULL );
181-
182- if (e -> expired_at > now )
183- continue ;
184-
185- free_cache_entry (e );
186- last = & cache -> entries [-- cache -> nr ];
187- if (e != last ) {
188- /*
189- * Move the last entry to the current position
190- * to avoid holes in the array.
191- */
192- * e = * last ;
193- } else {
194- /*
195- * If the last entry is the one we are deleting,
196- * just clear it.
197- */
198- memset (e , 0 , sizeof (* e ));
199- }
200-
201- /*
202- * The next iteration will check the current index
203- * again as it now contains an untraversed entry
204- * that may also be expired.
205- */
206- i -- ;
207- }
208- pthread_rwlock_unlock (& cache -> lock );
209- }
210-
211- static int gwp_dns_cache_insert (struct gwp_dns_ctx * ctx , const char * name ,
212- const char * service , struct addrinfo * res )
150+ static void try_pass_result_to_cache (struct gwp_dns_ctx * ctx , const char * name ,
151+ const struct addrinfo * ai )
213152{
214- struct gwp_dns_cache * cache = ctx -> cache ;
215- char * cname , * cservice ;
216- struct cache_entry * e ;
217- size_t nl , sl ;
218- int r ;
219-
220- if (!cache )
221- return - ENOSYS ;
222-
223- pthread_rwlock_wrlock (& cache -> lock );
224- if (cache -> nr >= cache -> cap ) {
225- size_t new_cap = cache -> cap ? cache -> cap * 2 : 16 ;
226- struct cache_entry * nentries ;
227-
228- nentries = realloc (cache -> entries , new_cap * sizeof (* nentries ));
229- if (!nentries ) {
230- r = - ENOMEM ;
231- goto out ;
232- }
233-
234- cache -> entries = nentries ;
235- cache -> cap = new_cap ;
236- }
237-
238- nl = strlen (name );
239- sl = service ? strlen (service ) : 0 ;
240- cname = malloc (nl + 1 + sl + 1 );
241- if (!cname ) {
242- r = - ENOMEM ;
243- goto out ;
244- }
245-
246- cservice = cname + nl + 1 ;
247- memcpy (cname , name , nl + 1 );
248- if (service )
249- memcpy (cservice , service , sl + 1 );
250- else
251- cservice [0 ] = '\0' ;
252-
253- e = & cache -> entries [cache -> nr ++ ];
254- e -> name = cname ;
255- e -> service = cservice ;
256- e -> res = res ;
257- e -> expired_at = time (NULL ) + ctx -> cfg .cache_expiry ;
258- r = 0 ;
259- out :
260- pthread_rwlock_unlock (& cache -> lock );
261- return r ;
262- }
153+ int x ;
263154
264- /**
265- * Try to pass the result of the DNS resolution to the cache. If successful,
266- * the `ai` pointer is owned by the cache and the caller MUST not free it.
267- *
268- * @param ctx The DNS context.
269- * @param name Name of the DNS entry.
270- * @param service Service of the DNS entry.
271- * @param ai The addrinfo result to be cached.
272- * @return True if the result was successfully passed to the
273- * cache, false otherwise.
274- */
275- static bool try_pass_result_to_cache (struct gwp_dns_ctx * ctx , const char * name ,
276- const char * service , struct addrinfo * ai )
277- {
278155 if (!ctx -> cache )
279- return false ;
156+ return ;
280157
281- return gwp_dns_cache_insert (ctx , name , service , ai ) == 0 ;
158+ x = ctx -> cfg .cache_expiry ;
159+ gwp_dns_cache_insert (ctx -> cache , name , ai , time (NULL ) + x );
282160}
283161
284162int gwp_dns_resolve (struct gwp_dns_ctx * ctx , const char * name ,
@@ -295,10 +173,8 @@ int gwp_dns_resolve(struct gwp_dns_ctx *ctx, const char *name,
295173 return - r ;
296174
297175 found = iterate_addr_list (res , addr , restyp );
298- if (found ) {
299- if (try_pass_result_to_cache (ctx , name , service , res ))
300- res = NULL ;
301- }
176+ if (found )
177+ try_pass_result_to_cache (ctx , name , res );
302178
303179 if (res )
304180 freeaddrinfo (res );
@@ -332,19 +208,19 @@ static void cond_scan_cache(struct gwp_dns_ctx *ctx)
332208 if (!ctx -> cache )
333209 return ;
334210
335- if (time (NULL ) - ctx -> cache -> last_scan < ctx -> cfg .cache_expiry )
211+ if (time (NULL ) - ctx -> last_scan < ctx -> cfg .cache_expiry )
336212 return ;
337213
338214 pthread_mutex_unlock (& ctx -> lock );
339- gwp_dns_cache_scan_and_delete_expired_entries (ctx -> cache );
215+ gwp_dns_cache_housekeep (ctx -> cache );
340216 pthread_mutex_lock (& ctx -> lock );
341217
342218 /*
343219 * Call time(NULL) again because the scan might have taken
344220 * several seconds if the number of entries is large or
345221 * it got contended by other threads.
346222 */
347- ctx -> cache -> last_scan = time (NULL );
223+ ctx -> last_scan = time (NULL );
348224}
349225
350226/*
@@ -499,10 +375,8 @@ static void dispatch_batch_result(int r, struct gwp_dns_ctx *ctx,
499375 }
500376
501377 eventfd_write (e -> ev_fd , 1 );
502- if (!e -> res ) {
503- if (try_pass_result_to_cache (ctx , e -> name , e -> service , ai ))
504- dbq -> reqs [i ]-> ar_result = NULL ;
505- }
378+ if (!e -> res )
379+ try_pass_result_to_cache (ctx , e -> name , ai );
506380 }
507381}
508382
@@ -703,49 +577,75 @@ static int init_workers(struct gwp_dns_ctx *ctx)
703577 return r ;
704578}
705579
706- int gwp_dns_cache_lookup (struct gwp_dns_ctx * ctx , const char * name ,
707- const char * service , struct gwp_sockaddr * addr )
580+ static bool fetch_i4 (struct gwp_dns_cache_entry * e , struct gwp_sockaddr * addr ,
581+ uint16_t port )
708582{
709- struct gwp_dns_cache * cache ;
710- bool found_expired ;
711- time_t now ;
712- uint32_t i ;
713- int r ;
583+ uint8_t * b = gwp_dns_cache_entget_i4 (e );
584+ if (!b )
585+ return false;
714586
715- if (!ctx -> cache )
716- return - ENOSYS ;
587+ memset (addr , 0 , sizeof (* addr ));
588+ addr -> i4 .sin_family = AF_INET ;
589+ addr -> i4 .sin_port = htons (port );
590+ memcpy (& addr -> i4 .sin_addr , b , 4 );
591+ return true;
592+ }
717593
718- cache = ctx -> cache ;
719- pthread_rwlock_rdlock (& cache -> lock );
720- r = - ENOENT ;
721- now = time (NULL );
722- found_expired = false;
723- for (i = 0 ; i < cache -> nr ; i ++ ) {
724- struct cache_entry * e = & cache -> entries [i ];
725- bool exp = (e -> expired_at <= now );
594+ static bool fetch_i6 (struct gwp_dns_cache_entry * e , struct gwp_sockaddr * addr ,
595+ uint16_t port )
596+ {
597+ uint8_t * b = gwp_dns_cache_entget_i6 (e );
598+ if (!b )
599+ return false;
726600
727- found_expired |= exp ;
601+ memset (addr , 0 , sizeof (* addr ));
602+ addr -> i6 .sin6_family = AF_INET6 ;
603+ addr -> i6 .sin6_port = htons (port );
604+ memcpy (& addr -> i6 .sin6_addr , b , 16 );
605+ return true;
606+ }
607+
608+ static int fetch_addr (struct gwp_dns_cache_entry * e , struct gwp_sockaddr * addr ,
609+ uint16_t port , uint32_t restyp )
610+ {
611+ if (restyp == GWP_DNS_RESTYP_IPV4_ONLY ) {
612+ if (!fetch_i4 (e , addr , port ))
613+ return - EHOSTUNREACH ;
614+ } else if (restyp == GWP_DNS_RESTYP_IPV6_ONLY ) {
615+ if (!fetch_i6 (e , addr , port ))
616+ return - EHOSTUNREACH ;
617+ } else if (restyp == GWP_DNS_RESTYP_PREFER_IPV6 ) {
618+ if (!fetch_i6 (e , addr , port )) {
619+ if (!fetch_i4 (e , addr , port ))
620+ return - EHOSTUNREACH ;
621+ }
622+ } else if (restyp == GWP_DNS_RESTYP_PREFER_IPV4 ) {
623+ if (!fetch_i4 (e , addr , port )) {
624+ if (!fetch_i6 (e , addr , port ))
625+ return - EHOSTUNREACH ;
626+ }
627+ } else {
628+ return - EINVAL ;
629+ }
728630
729- if ( exp )
730- continue ;
631+ return 0 ;
632+ }
731633
732- if (strcmp (e -> name , name ))
733- continue ;
634+ int gwp_dns_cache_lookup (struct gwp_dns_ctx * ctx , const char * name ,
635+ const char * service , struct gwp_sockaddr * addr )
636+ {
637+ struct gwp_dns_cache_entry * e ;
638+ int r ;
734639
735- if (service && strcmp (e -> service , service ))
736- continue ;
737- else if (!service && e -> service [0 ] != '\0' )
738- continue ;
640+ if (!ctx -> cache )
641+ return - ENOSYS ;
739642
740- if (iterate_addr_list (e -> res , addr , ctx -> cfg .restyp )) {
741- r = 0 ;
742- break ;
743- }
744- }
745- pthread_rwlock_unlock (& cache -> lock );
746- if (found_expired )
747- gwp_dns_cache_scan_and_delete_expired_entries (cache );
643+ r = gwp_dns_cache_getent (ctx -> cache , name , & e );
644+ if (r )
645+ return r ;
748646
647+ r = fetch_addr (e , addr , service ? atoi (service ) : 0 , ctx -> cfg .restyp );
648+ gwp_dns_cache_putent (e );
749649 return r ;
750650}
751651
@@ -762,37 +662,21 @@ static int init_cache(struct gwp_dns_ctx *ctx)
762662 return 0 ;
763663 }
764664
765- cache = calloc (1 , sizeof (* cache ));
766- if (!cache )
767- return - ENOMEM ;
768-
769- r = pthread_rwlock_init (& cache -> lock , NULL );
770- if (r ) {
771- r = - r ;
772- free (cache );
665+ r = gwp_dns_cache_init (& cache , 8192 );
666+ if (r )
773667 return r ;
774- }
775668
776669 ctx -> cache = cache ;
777670 return 0 ;
778671}
779672
780673static void free_cache (struct gwp_dns_cache * cache )
781674{
782- uint32_t i ;
783-
784675 if (!cache )
785676 return ;
786677
787- pthread_rwlock_destroy (& cache -> lock );
788- for (i = 0 ; i < cache -> nr ; i ++ ) {
789- struct cache_entry * e = & cache -> entries [i ];
790- free (e -> name );
791- if (e -> res )
792- freeaddrinfo (e -> res );
793- }
794- free (cache -> entries );
795- free (cache );
678+ gwp_dns_cache_free (cache );
679+ cache = NULL ;
796680}
797681
798682int gwp_dns_ctx_init (struct gwp_dns_ctx * * ctx_p , const struct gwp_dns_cfg * cfg )
@@ -827,6 +711,7 @@ int gwp_dns_ctx_init(struct gwp_dns_ctx **ctx_p, const struct gwp_dns_cfg *cfg)
827711 ctx -> head = NULL ;
828712 ctx -> tail = NULL ;
829713 ctx -> should_stop = false;
714+ ctx -> last_scan = time (NULL );
830715 r = init_workers (ctx );
831716 if (r )
832717 goto out_free_cache ;
0 commit comments