@@ -150,7 +150,48 @@ public IEnumerable<string> ExpandWildcard(string wildcard, int startRange = 1, i
150150 }
151151 }
152152
153- public async Task < IEnumerable < string > > ResolveHostnameAsync ( string hostname , CancellationToken cancellationToken = default )
153+ /// <summary>
154+ /// Expands a single IPv6 host into compressed (::) and full (0000:0000:...) forms.
155+ /// Handles optional zone index.
156+ /// </summary>
157+ private IEnumerable < string > ExpandIPv6Host ( string host )
158+ {
159+ string hostPart = host ;
160+ string ? zoneIndex = null ;
161+
162+ if ( host . Contains ( '%' ) )
163+ {
164+ var split = host . Split ( '%' ) ;
165+ hostPart = split [ 0 ] ;
166+ zoneIndex = split [ 1 ] ;
167+ }
168+
169+ if ( ! IPAddress . TryParse ( hostPart , out var ip ) ) yield break ;
170+ if ( ip . AddressFamily != AddressFamily . InterNetworkV6 ) yield break ;
171+
172+ // Compressed form
173+ string compressed = ip . ToString ( ) ;
174+
175+ // Full form
176+ string full = string . Join ( ":" , ip . GetAddressBytes ( )
177+ . Select ( ( b , i ) => i % 2 == 0 ? $ "{ b : X2} " : $ "{ b : X2} ")
178+ . Select ( ( s , i ) => i % 2 == 1 ? s : s ) // ensure proper grouping
179+ ) ;
180+
181+ if ( zoneIndex != null )
182+ {
183+ yield return $ "{ compressed } %{ zoneIndex } ";
184+ yield return $ "{ full } %{ zoneIndex } ";
185+ }
186+ else
187+ {
188+ yield return compressed ;
189+ yield return full ;
190+ }
191+ }
192+
193+
194+ public async Task < IEnumerable < string > > ResolveHostnameAsync ( string hostname , CancellationToken cancellationToken = default )
154195 {
155196 try
156197 {
@@ -159,9 +200,10 @@ public async Task<IEnumerable<string>> ResolveHostnameAsync(string hostname, Can
159200 . Where ( addr => addr . AddressFamily == AddressFamily . InterNetwork || addr . AddressFamily == AddressFamily . InterNetworkV6 )
160201 . Select ( addr =>
161202 {
162- // Include zone index for IPv6 link-local if needed
163- if ( addr . IsIPv6LinkLocal && addr . ScopeId != 0 )
203+ // Include zone index for IPv6 link-local
204+ if ( addr . AddressFamily == AddressFamily . InterNetworkV6 && addr . IsIPv6LinkLocal && addr . ScopeId != 0 )
164205 return addr + "%" + addr . ScopeId ;
206+
165207 return addr . ToString ( ) ;
166208 } )
167209 . ToList ( ) ;
@@ -209,15 +251,28 @@ public async Task<IEnumerable<string>> ResolveHostnameAsync(string hostname, Can
209251 {
210252 string host = ( string ) target . host ;
211253
212- // Check if it's an IP address or hostname
213- if ( IPAddress . TryParse ( host , out _ ) )
254+ if ( IPAddress . TryParse ( host , out var ip ) )
214255 {
215- hosts . Add ( host ) ;
256+ if ( ip . AddressFamily == AddressFamily . InterNetworkV6 )
257+ {
258+ // Expand IPv6 host into compressed and full forms
259+ hosts . AddRange ( ExpandIPv6Host ( host ) ) ;
260+ }
261+ else
262+ {
263+ hosts . Add ( host ) ; // IPv4
264+ }
216265 }
217266 else
218267 {
219268 IEnumerable < string > resolvedHosts = await ResolveHostnameAsync ( host , cancellationToken ) ;
220- hosts . AddRange ( resolvedHosts ) ;
269+ foreach ( var resolvedHost in resolvedHosts )
270+ {
271+ if ( IPAddress . TryParse ( resolvedHost , out var resolvedIp ) && resolvedIp . AddressFamily == AddressFamily . InterNetworkV6 )
272+ hosts . AddRange ( ExpandIPv6Host ( resolvedHost ) ) ;
273+ else
274+ hosts . Add ( resolvedHost ) ;
275+ }
221276 }
222277 }
223278 else if ( target . cidr != null )
@@ -231,7 +286,6 @@ public async Task<IEnumerable<string>> ResolveHostnameAsync(string hostname, Can
231286 hosts . AddRange ( ExpandWildcard ( wildcard ) ) ;
232287 }
233288
234- // Create endpoint for each expanded host
235289 foreach ( string host in hosts )
236290 {
237291 dynamic endpoint = CreateEndpointFromTarget ( target , host ) ;
0 commit comments