(in string)
| 368 | var specRegexp = regexp.MustCompile(`^((\[[0-9a-fA-F:]+]|\d+\.\d+\.\d+\.\d+):)?(\d+)(-(\d+))?(:(\d+)(-(\d+))?)?$`) |
| 369 | |
| 370 | func parseSrcDestPorts(in string) ([]portForwardSpec, error) { |
| 371 | groups := specRegexp.FindStringSubmatch(in) |
| 372 | if len(groups) == 0 { |
| 373 | return nil, xerrors.Errorf("invalid port specification %q", in) |
| 374 | } |
| 375 | |
| 376 | var localAddr netip.Addr |
| 377 | if groups[2] != "" { |
| 378 | parsedAddr, err := netip.ParseAddr(strings.Trim(groups[2], "[]")) |
| 379 | if err != nil { |
| 380 | return nil, xerrors.Errorf("invalid IP address %q", groups[2]) |
| 381 | } |
| 382 | localAddr = parsedAddr |
| 383 | } |
| 384 | |
| 385 | local, err := parsePortRange(groups[3], groups[5]) |
| 386 | if err != nil { |
| 387 | return nil, xerrors.Errorf("parse local port range from %q: %w", in, err) |
| 388 | } |
| 389 | remote := local |
| 390 | if groups[7] != "" { |
| 391 | remote, err = parsePortRange(groups[7], groups[9]) |
| 392 | if err != nil { |
| 393 | return nil, xerrors.Errorf("parse remote port range from %q: %w", in, err) |
| 394 | } |
| 395 | } |
| 396 | if len(local) != len(remote) { |
| 397 | return nil, xerrors.Errorf("port ranges must be the same length, got %d ports forwarded to %d ports", len(local), len(remote)) |
| 398 | } |
| 399 | var out []portForwardSpec |
| 400 | for i := range local { |
| 401 | out = append(out, portForwardSpec{ |
| 402 | listenHost: localAddr, |
| 403 | listenPort: local[i], |
| 404 | dialPort: remote[i], |
| 405 | }) |
| 406 | } |
| 407 | return out, nil |
| 408 | } |
| 409 | |
| 410 | func parsePortRange(s, e string) ([]uint16, error) { |
| 411 | start, err := parsePort(s) |
no test coverage detected