@@ -174,62 +174,37 @@ public enum IPv6Format
174174        Full 
175175    } 
176176
177-     private  IEnumerable < string >  ExpandIPv6Host ( string  host ,  IPv6Format  format  =  IPv6Format . Compressed ) 
178-     { 
179-         string  hostPart  =  host ; 
180-         string ?  zoneIndex  =  null ; 
181- 
182-         if  ( host . Contains ( '%' ) ) 
183-         { 
184-             var  split  =  host . Split ( '%' ) ; 
185-             hostPart  =  split [ 0 ] ; 
186-             zoneIndex  =  split [ 1 ] ; 
187-         } 
188- 
189-         if  ( ! IPAddress . TryParse ( hostPart ,  out  var  ip ) )  yield  break ; 
190-         if  ( ip . AddressFamily  !=  AddressFamily . InterNetworkV6 )  yield  break ; 
191- 
192-         string  compressed  =  ip . ToString ( ) ; 
193- 
194-         // Full form (always 8 groups of 4 hex digits) 
195-         string  full  =  string . Join ( ":" ,  Enumerable . Range ( 0 ,  8 ) 
196-             . Select ( i =>  ( ( ushort ) ( ( ip . GetAddressBytes ( ) [ i  *  2 ]  <<  8 )  | 
197-                                     ip . GetAddressBytes ( ) [ i  *  2  +  1 ] ) ) . ToString ( "x4" ) ) ) ; 
198- 
199-         string  ApplyZone ( string  addr )  =>  zoneIndex  !=  null  ?  $ "{ addr } %{ zoneIndex } ":  addr ; 
200- 
201-         if  ( format  ==  IPv6Format . Compressed ) 
202-         { 
203-             yield  return  ApplyZone ( compressed ) ; 
204-         } 
205-         else  if  ( format  ==  IPv6Format . Full ) 
206-         { 
207-             yield  return  ApplyZone ( full ) ; 
208-         } 
209-     } 
210- 
211177   public  async  Task < IEnumerable < string > >  ResolveHostnameAsync ( string  hostname ,  CancellationToken  cancellationToken  =  default ) 
212178    { 
179+         var  ipv4Addresses  =  new  List < string > ( ) ; 
180+         var  ipv6Addresses  =  new  List < string > ( ) ; 
181+ 
213182        try 
214183        { 
215184            IPAddress [ ]  addresses  =  await  Dns . GetHostAddressesAsync ( hostname ) ; 
216-             return  addresses 
217-                 . Where ( addr =>  addr . AddressFamily  ==  AddressFamily . InterNetwork  ||  addr . AddressFamily  ==  AddressFamily . InterNetworkV6 ) 
218-                 . Select ( addr => 
219-                 { 
220-                     // Include zone index for IPv6 link-local 
221-                     if  ( addr . AddressFamily  ==  AddressFamily . InterNetworkV6  &&  addr . IsIPv6LinkLocal  &&  addr . ScopeId  !=  0 ) 
222-                         return  addr  +  "%"  +  addr . ScopeId ; 
223185
224-                     return  addr . ToString ( ) ; 
225-                 } ) 
226-                 . ToList ( ) ; 
186+             foreach  ( var  addr  in  addresses ) 
187+             { 
188+                 if  ( addr . AddressFamily  ==  AddressFamily . InterNetwork )  // IPv4 
189+                 { 
190+                     ipv4Addresses . Add ( addr . ToString ( ) ) ; 
191+                 } 
192+                 else  if  ( addr . AddressFamily  ==  AddressFamily . InterNetworkV6 )  // IPv6 
193+                 { 
194+                     string  address  =  addr . IsIPv6LinkLocal  &&  addr . ScopeId  !=  0 
195+                         ?  addr  +  "%"  +  addr . ScopeId 
196+                         :  addr . ToString ( ) ; 
197+                     ipv6Addresses . Add ( address ) ; 
198+                 } 
199+             } 
227200        } 
228201        catch  ( Exception  ex ) 
229202        { 
230203            _logger . LogWarning ( ex ,  "Failed to resolve hostname: {Hostname}" ,  hostname ) ; 
231-             return  Enumerable . Empty < string > ( ) ; 
232204        } 
205+ 
206+         // Always return both IPv4 and IPv6 if available 
207+         return  ipv6Addresses . Concat ( ipv4Addresses ) ; 
233208    } 
234209
235210    public  async  Task < IEnumerable < Data . Endpoint > >  ExpandTargetsAsync ( IEnumerable < dynamic >  configTargets , 
@@ -257,6 +232,40 @@ public async Task<IEnumerable<string>> ResolveHostnameAsync(string hostname, Can
257232        return  endpoints ; 
258233    } 
259234
235+     private  IEnumerable < string >  ExpandIPv6Host ( string  host ,  IPv6Format  format  =  IPv6Format . Compressed ) 
236+     { 
237+         string  hostPart  =  host ; 
238+         string ?  zoneIndex  =  null ; 
239+ 
240+         if  ( host . Contains ( '%' ) ) 
241+         { 
242+             var  split  =  host . Split ( '%' ) ; 
243+             hostPart  =  split [ 0 ] ; 
244+             zoneIndex  =  split [ 1 ] ; 
245+         } 
246+ 
247+         if  ( ! IPAddress . TryParse ( hostPart ,  out  var  ip ) )  yield  break ; 
248+         if  ( ip . AddressFamily  !=  AddressFamily . InterNetworkV6 )  yield  break ; 
249+ 
250+         string  compressed  =  ip . ToString ( ) ; 
251+ 
252+         // Full form (always 8 groups of 4 hex digits) 
253+         string  full  =  string . Join ( ":" ,  Enumerable . Range ( 0 ,  8 ) 
254+             . Select ( i =>  ( ( ushort ) ( ( ip . GetAddressBytes ( ) [ i  *  2 ]  <<  8 )  | 
255+                                     ip . GetAddressBytes ( ) [ i  *  2  +  1 ] ) ) . ToString ( "x4" ) ) ) ; 
256+ 
257+         string  ApplyZone ( string  addr )  =>  zoneIndex  !=  null  ?  $ "{ addr } %{ zoneIndex } ":  addr ; 
258+ 
259+         if  ( format  ==  IPv6Format . Compressed ) 
260+         { 
261+             yield  return  ApplyZone ( compressed ) ; 
262+         } 
263+         else  if  ( format  ==  IPv6Format . Full ) 
264+         { 
265+             yield  return  ApplyZone ( full ) ; 
266+         } 
267+     } 
268+ 
260269    private  async  Task < IEnumerable < Data . Endpoint > >  ExpandSingleTargetAsync ( dynamic  target , 
261270        CancellationToken  cancellationToken ) 
262271    { 
@@ -270,25 +279,28 @@ public async Task<IEnumerable<string>> ResolveHostnameAsync(string hostname, Can
270279
271280            if  ( IPAddress . TryParse ( host ,  out  var  ip ) ) 
272281            { 
273-                 // Expand IPv6 host into compressed and full forms 
274282                if  ( ip . AddressFamily  ==  AddressFamily . InterNetworkV6 ) 
283+                 { 
275284                    hosts . AddRange ( ExpandIPv6Host ( host ) ) ; 
285+                 } 
276286                else 
287+                 { 
277288                    hosts . Add ( host ) ;  // IPv4 
289+                 } 
278290            } 
279291            else 
280292            { 
281293                // Hostname 
282294                IEnumerable < string >  resolvedHosts  =  await  ResolveHostnameAsync ( host ,  cancellationToken ) ; 
283-                 hosts . AddRange ( resolvedHosts . SelectMany ( resolvedHost => 
295+                 hosts . AddRange ( resolvedHosts . Select ( resolvedHost => 
284296                { 
285297                    if  ( IPAddress . TryParse ( resolvedHost ,  out  var  resolvedIp )  && 
286298                        resolvedIp . AddressFamily  ==  AddressFamily . InterNetworkV6 ) 
287299                    { 
288-                         return  ExpandIPv6Host ( resolvedHost ) ; 
300+                         return  ExpandIPv6Host ( resolvedHost ,   IPv6Format . Compressed ) ; 
289301                    } 
290302                    return  new [ ]  {  resolvedHost  } ; 
291-                 } ) ) ; 
303+                 } ) . SelectMany ( x  =>   x ) ) ; 
292304            } 
293305        } 
294306        else  if  ( target . cidr  !=  null ) 
0 commit comments