7
7
"net/netip"
8
8
"os"
9
9
"os/signal"
10
+ "regexp"
10
11
"strconv"
11
12
"strings"
12
13
"sync"
@@ -263,7 +264,7 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
263
264
264
265
for _ , specEntry := range tcpSpecs {
265
266
for _ , spec := range strings .Split (specEntry , "," ) {
266
- ports , err := parseSrcDestPorts (spec )
267
+ ports , err := parseSrcDestPorts (strings . TrimSpace ( spec ) )
267
268
if err != nil {
268
269
return nil , xerrors .Errorf ("failed to parse TCP port-forward specification %q: %w" , spec , err )
269
270
}
@@ -281,7 +282,7 @@ func parsePortForwards(tcpSpecs, udpSpecs []string) ([]portForwardSpec, error) {
281
282
282
283
for _ , specEntry := range udpSpecs {
283
284
for _ , spec := range strings .Split (specEntry , "," ) {
284
- ports , err := parseSrcDestPorts (spec )
285
+ ports , err := parseSrcDestPorts (strings . TrimSpace ( spec ) )
285
286
if err != nil {
286
287
return nil , xerrors .Errorf ("failed to parse UDP port-forward specification %q: %w" , spec , err )
287
288
}
@@ -326,63 +327,53 @@ type parsedSrcDestPort struct {
326
327
local , remote netip.AddrPort
327
328
}
328
329
330
+ // specRegexp matches port specs. It handles all the following formats:
331
+ //
332
+ // 8000
333
+ // 8888:9999
334
+ // 1-5:6-10
335
+ // 8000-8005
336
+ // 127.0.0.1:4000:4000
337
+ // [::1]:8080:8081
338
+ // 127.0.0.1:4000-4005
339
+ // [::1]:4000-4001:5000-5001
340
+ //
341
+ // Important capturing groups:
342
+ //
343
+ // 2: local IP address (including [] for IPv6)
344
+ // 3: local port, or start of local port range
345
+ // 5: end of local port range
346
+ // 7: remote port, or start of remote port range
347
+ // 9: end or remote port range
348
+ var specRegexp = regexp .MustCompile (`^((\[[0-9a-fA-F:]+]|\d+\.\d+\.\d+\.\d+):)?(\d+)(-(\d+))?(:(\d+)(-(\d+))?)?$` )
349
+
329
350
func parseSrcDestPorts (in string ) ([]parsedSrcDestPort , error ) {
330
351
var (
331
352
err error
332
- parts = strings .Split (in , ":" )
333
353
localAddr = netip .AddrFrom4 ([4 ]byte {127 , 0 , 0 , 1 })
334
354
remoteAddr = netip .AddrFrom4 ([4 ]byte {127 , 0 , 0 , 1 })
335
355
)
336
-
337
- switch len (parts ) {
338
- case 1 :
339
- // Duplicate the single part
340
- parts = append (parts , parts [0 ])
341
- case 2 :
342
- // Check to see if the first part is an IP address.
343
- _localAddr , err := netip .ParseAddr (parts [0 ])
344
- if err != nil {
345
- break
346
- }
347
- // The first part is the local address, so duplicate the port.
348
- localAddr = _localAddr
349
- parts = []string {parts [1 ], parts [1 ]}
350
-
351
- case 3 :
352
- _localAddr , err := netip .ParseAddr (parts [0 ])
353
- if err != nil {
354
- return nil , xerrors .Errorf ("invalid port specification %q; invalid ip %q: %w" , in , parts [0 ], err )
355
- }
356
- localAddr = _localAddr
357
- parts = parts [1 :]
358
-
359
- default :
356
+ groups := specRegexp .FindStringSubmatch (in )
357
+ if len (groups ) == 0 {
360
358
return nil , xerrors .Errorf ("invalid port specification %q" , in )
361
359
}
362
-
363
- if ! strings .Contains (parts [0 ], "-" ) {
364
- localPort , err := parsePort (parts [0 ])
360
+ if groups [2 ] != "" {
361
+ localAddr , err = netip .ParseAddr (strings .Trim (groups [2 ], "[]" ))
365
362
if err != nil {
366
- return nil , xerrors .Errorf ("parse local port from %q: %w " , in , err )
363
+ return nil , xerrors .Errorf ("invalid IP address %q " , groups [ 2 ] )
367
364
}
368
- remotePort , err := parsePort (parts [1 ])
369
- if err != nil {
370
- return nil , xerrors .Errorf ("parse remote port from %q: %w" , in , err )
371
- }
372
-
373
- return []parsedSrcDestPort {{
374
- local : netip .AddrPortFrom (localAddr , localPort ),
375
- remote : netip .AddrPortFrom (remoteAddr , remotePort ),
376
- }}, nil
377
365
}
378
366
379
- local , err := parsePortRange (parts [ 0 ])
367
+ local , err := parsePortRange (groups [ 3 ], groups [ 5 ])
380
368
if err != nil {
381
369
return nil , xerrors .Errorf ("parse local port range from %q: %w" , in , err )
382
370
}
383
- remote , err := parsePortRange (parts [1 ])
384
- if err != nil {
385
- return nil , xerrors .Errorf ("parse remote port range from %q: %w" , in , err )
371
+ remote := local
372
+ if groups [7 ] != "" {
373
+ remote , err = parsePortRange (groups [7 ], groups [9 ])
374
+ if err != nil {
375
+ return nil , xerrors .Errorf ("parse remote port range from %q: %w" , in , err )
376
+ }
386
377
}
387
378
if len (local ) != len (remote ) {
388
379
return nil , xerrors .Errorf ("port ranges must be the same length, got %d ports forwarded to %d ports" , len (local ), len (remote ))
@@ -397,18 +388,17 @@ func parseSrcDestPorts(in string) ([]parsedSrcDestPort, error) {
397
388
return out , nil
398
389
}
399
390
400
- func parsePortRange (in string ) ([]uint16 , error ) {
401
- parts := strings .Split (in , "-" )
402
- if len (parts ) != 2 {
403
- return nil , xerrors .Errorf ("invalid port range specification %q" , in )
404
- }
405
- start , err := parsePort (parts [0 ])
391
+ func parsePortRange (s , e string ) ([]uint16 , error ) {
392
+ start , err := parsePort (s )
406
393
if err != nil {
407
- return nil , xerrors .Errorf ("parse range start port from %q: %w" , in , err )
394
+ return nil , xerrors .Errorf ("parse range start port from %q: %w" , s , err )
408
395
}
409
- end , err := parsePort (parts [1 ])
410
- if err != nil {
411
- return nil , xerrors .Errorf ("parse range end port from %q: %w" , in , err )
396
+ end := start
397
+ if len (e ) != 0 {
398
+ end , err = parsePort (e )
399
+ if err != nil {
400
+ return nil , xerrors .Errorf ("parse range end port from %q: %w" , e , err )
401
+ }
412
402
}
413
403
if end < start {
414
404
return nil , xerrors .Errorf ("range end port %v is less than start port %v" , end , start )
0 commit comments