Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
Signed-off-by: Kimmo Lehto <[email protected]>
  • Loading branch information
kke committed Mar 15, 2024
1 parent 4f2185d commit 1245aa5
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 344 deletions.
5 changes: 5 additions & 0 deletions log/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ func SetTraceLogger(l TraceLogger) {
trace = sync.OnceValue(func() TraceLogger { return l })
}

// GetTraceLogger gets the current value of trace logger.
func GetTraceLogger() TraceLogger {
return trace()
}

// TraceLogger is a logger for rig's internal trace logging.
type TraceLogger interface {
Log(ctx context.Context, level slog.Level, msg string, keysAndValues ...any)
Expand Down
178 changes: 58 additions & 120 deletions protocol/ssh/sshconfig/configvalue.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"errors"
"fmt"
"math"
"net"
"os/user"
"path/filepath"
"slices"
Expand Down Expand Up @@ -60,6 +59,8 @@ const (
boolFalse = "false"
boolYes = "yes"
boolNo = "no"

fkHost = "host"
)

// Value is a generic type for a configuration value. It is necessary to track the origin of the value
Expand All @@ -72,6 +73,10 @@ type Value[T any] struct {

// Set the value and its origin.
func (cv *Value[T]) Set(value T, originType ValueOriginType, origin string) {
// if the value is already set and the origin is not defaults, don't override it
if cv.IsSet() && cv.originType != ValueOriginDefault {
return
}
cv.value = value
cv.originType = originType
cv.origin = origin
Expand All @@ -97,6 +102,7 @@ func (cv Value[T]) Origin() string {
return cv.origin
}

// IsDefault returns true if the value is set from the defaults.
func (cv Value[T]) IsDefault() bool {
return cv.originType == ValueOriginDefault
}
Expand Down Expand Up @@ -145,21 +151,21 @@ func (v *BoolValue) String() string {
return boolNo
}

// MultiStateValue is a configuration value that can be a boolean or a string. Fields like this are used in the
// MultiStateBoolValue is a configuration value that can be a boolean or a string. Fields like this are used in the
// ssh configuration for things like "yes/no/ask" or "yes/no/auto". The Bool() function returns the value as a boolean.
type MultiStateValue struct {
type MultiStateBoolValue struct {
Value[string]
}

// SetString sets the value of the multi-state value and its origin. It accepts "yes", "true", "no" and "false" as boolean values.
func (v *MultiStateValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *MultiStateBoolValue) SetString(value string, originType ValueOriginType, origin string) error {
v.Set(value, originType, origin)
return nil
}

// Bool returns the value as a boolean. It returns the boolean value and a boolean indicating if the value was set to a boolean value.
// If the value is not set to a boolean value, the string can be retrieved using the Get() function.
func (v *MultiStateValue) Bool() (bool, bool) {
func (v *MultiStateBoolValue) Bool() (bool, bool) {
val, ok := v.Get()
if !ok {
return false, false
Expand All @@ -174,7 +180,7 @@ func (v *MultiStateValue) Bool() (bool, bool) {
}

// String returns the value as a string.
func (v *MultiStateValue) String() string {
func (v *MultiStateBoolValue) String() string {
val, _ := v.Get()
return val
}
Expand Down Expand Up @@ -216,7 +222,7 @@ func (v *DurationValue) SetString(value string, originType ValueOriginType, orig
return nil
}
unit := value[len(value)-1]
if unit >= '0' || unit <= '9' {
if unit >= '0' && unit <= '9' {
value += "s"
}
d, err := time.ParseDuration(value)
Expand All @@ -233,18 +239,18 @@ func (v *DurationValue) String() string {
return val.String()
}

// StringSliceValue is a configuration value that holds a slice of strings. When setting the value, it accepts a
// StringListValueis a configuration value that holds a slice of strings. When setting the value, it accepts a

Check warning on line 242 in protocol/ssh/sshconfig/configvalue.go

View workflow job for this annotation

GitHub Actions / Lint

exported: comment on exported type StringListValue should be of the form "StringListValue ..." (with optional leading article) (revive)
// comma-separated or whitespace-separated list of values. The values can be quoted using single or double quotes.
// If the existing value is set from the defaults, the slice is cleared before setting the new value. Duplicate
// values are ignored.
type StringSliceValue struct {
type StringListValue struct {
Value[[]string]
}

// SetString appends the value to the slice and sets the origin of the value. If the value is already set from any
// other origin than the defaults, it appends the new value to the slice. If the value is set from the defaults, it
// clears the slice before setting the new values.
func (v *StringSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *StringListValue) SetString(value string, originType ValueOriginType, origin string) error {
var oldVals []string
if v.OriginType() != ValueOriginDefault {
oldVals, _ = v.Get()
Expand Down Expand Up @@ -302,21 +308,21 @@ func formatStringSlice(vals []string, sep rune) string {
}

// String returns the value as a string.
func (v *StringSliceValue) String() string {
func (v *StringListValue) String() string {
val, _ := v.Get()
return formatStringSlice(val, ',')
}

// IntSliceValue is a configuration value that holds a slice of integers. When setting the value, it accepts a
// IntListValue is a configuration value that holds a slice of integers. When setting the value, it accepts a
// comma-separated or whitespace-separated list of values. The values can be quoted using single or double quotes.
// If the existing value is set from the defaults, the slice is cleared before setting the new value.
type IntSliceValue struct {
type IntListValue struct {
Value[[]int]
}

// SetString appends the value to the slice and sets the origin of the value. If the value is set from the defaults, it
// clears the slice before setting the new values. Duplicate values are ignored.
func (v *IntSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *IntListValue) SetString(value string, originType ValueOriginType, origin string) error {
var oldVals []int
if v.OriginType() != ValueOriginDefault {
oldVals, _ = v.Get()
Expand Down Expand Up @@ -364,7 +370,7 @@ func (v *IntSliceValue) SetString(value string, originType ValueOriginType, orig
}

// String returns the value as a string.
func (v *IntSliceValue) String() string {
func (v *IntListValue) String() string {
val, _ := v.Get()
strSlice := make([]string, len(val))
for i, v := range val {
Expand Down Expand Up @@ -414,14 +420,14 @@ func (v *PathValue) String() string {
return shellescape.Quote(val)
}

// PathSliceValue is a list of [PathValue] entries. Duplicates are skipped. If the existing list
// PathListValue a list of [PathValue] entries. Duplicates are skipped. If the existing list
// is set from the defaults, the list is cleared before setting the new value.
type PathSliceValue struct {
StringSliceValue
type PathListValue struct {
StringListValue
}

// SetString appends the value to the slice and sets the origin of the value.
func (v *PathSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *PathListValue) SetString(value string, originType ValueOriginType, origin string) error {
var oldVals []string
if originType == ValueOriginDefault || v.OriginType() != ValueOriginDefault {
if val, ok := v.Get(); ok {
Expand All @@ -436,7 +442,9 @@ func (v *PathSliceValue) SetString(value string, originType ValueOriginType, ori

for _, path := range paths {
np := &PathValue{}
np.SetString(path, originType, origin)
if err := np.SetString(path, originType, origin); err != nil {
return err
}
path, _ := np.Get()
if !slices.Contains(oldVals, path) {
oldVals = append(oldVals, path)
Expand All @@ -447,44 +455,47 @@ func (v *PathSliceValue) SetString(value string, originType ValueOriginType, ori
}

// String returns the value as a string.
func (v *PathSliceValue) String() string {
func (v *PathListValue) String() string {
val, _ := v.Get()
return formatStringSlice(val, ' ')
}

// AlwaysAppendStringSliceValue is like [StringValue] but it always appends the value to the existing value.
type AlwaysAppendStringSliceValue struct {
StringSliceValue
// AppendingStringListValue is like a [StringListValue] but it always appends the value to the existing value
// even if a value was already set.
type AppendingStringListValue struct {
StringListValue
}

// SetString appends the value to the existing value.
func (v *AlwaysAppendStringSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *AppendingStringListValue) SetString(value string, originType ValueOriginType, origin string) error {
if value == "" {
return nil
}
vals, _ := v.Get()
newVals, err := parseStringSliceValue(value)
if err != nil {
return fmt.Errorf("can't parse string slice value %q: %w", value, err)
}
vals = append(vals, newVals...)
v.Set(vals, originType, origin)
v.value = append(v.value, newVals...)
v.originType = originType
v.origin = origin
return nil
}

// SpecialStringSliceValue is like [StringSliceValue] but the list can be prefixed with +, - or ^ to alter how
// ModifiableStringListValue is like [StringSliceValue] but the list can be prefixed with +, - or ^ to alter how
// the list is modified.
//
// + - appends the value to the existing list
// - - removes the given value from the existing list. the values can be wildcard patterns.
// ^ - clears the list and sets the value
type SpecialStringSliceValue struct {
StringSliceValue
//
// This is used in several fields in the ssh configuration, such as the lists of algorithms.
type ModifiableStringListValue struct {
StringListValue
}

// SetString appends the value to the slice and sets the origin of the value or if a prefix is used,
// it modifies the list accordingly.
func (v *SpecialStringSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *ModifiableStringListValue) SetString(value string, originType ValueOriginType, origin string) error {
if value == "" {
return nil
}
Expand Down Expand Up @@ -512,7 +523,9 @@ func (v *SpecialStringSliceValue) SetString(value string, originType ValueOrigin
finalValues = valuesToSet
}

v.Set(finalValues, originType, origin)
v.value = finalValues
v.originType = originType
v.origin = origin

return nil
}
Expand All @@ -538,7 +551,7 @@ func parseStringSliceValue(value string) ([]string, error) {
}

// appendUniqueValues adds new values to the slice, avoiding duplicates.
func (v *SpecialStringSliceValue) appendUniqueValues(newValues []string) []string {
func (v *ModifiableStringListValue) appendUniqueValues(newValues []string) []string {
existingValues, _ := v.Get()
for _, newVal := range newValues {
if !slices.Contains(existingValues, newVal) {
Expand All @@ -549,7 +562,7 @@ func (v *SpecialStringSliceValue) appendUniqueValues(newValues []string) []strin
}

// removeAllOccurrences removes all occurrences of the specified values from the slice.
func (v *SpecialStringSliceValue) removeAllOccurrences(valuesToRemove []string) []string {
func (v *ModifiableStringListValue) removeAllOccurrences(valuesToRemove []string) []string {
existingValues, _ := v.Get()
for _, removeVal := range valuesToRemove {
existingValues = filterOutMatchPattern(existingValues, removeVal)
Expand All @@ -558,7 +571,7 @@ func (v *SpecialStringSliceValue) removeAllOccurrences(valuesToRemove []string)
}

// prependUniqueValues prepends new values to the slice after removing any existing occurrences.
func (v *SpecialStringSliceValue) prependUniqueValues(newValues []string) []string {
func (v *ModifiableStringListValue) prependUniqueValues(newValues []string) []string {
existingValues, _ := v.Get()
existingValues = filterOutMultiple(existingValues, newValues)
return append(newValues, existingValues...)
Expand Down Expand Up @@ -595,93 +608,16 @@ func filterOutMultiple(slice []string, valuesToRemove []string) []string {
return slice
}

// NetAddrValue is a configuration value that holds a net.Addr.
type NetAddrValue struct {
Value[net.Addr]
}

// SetString parses the value as an address and sets the origin of the value.
// It accepts an address in the forms of:
// - hostname
// - hostname:port
// - ipv4:port
// - ipv4
// - [ipv6-address]
// - [ipv6-address]:port
// - ipv4/netmask
// - [ipv6]/netmask
// - port
// And possibly some others through net.ResolveTCPAddr.
func (v *NetAddrValue) SetString(value string, originType ValueOriginType, origin string) error {
value, err := shellescape.Unquote(value)
if err != nil {
return fmt.Errorf("can't parse address value %q: %w", value, err)
}
if value == "" {
return fmt.Errorf("%w: can't set empty address", ErrInvalidValue)
}
if strings.Contains(value, "/") {
ip, ipnet, err := net.ParseCIDR(value)
if err != nil {
return fmt.Errorf("can't parse CIDR address %q: %w", value, err)
}
v.Set(&net.IPNet{IP: ip, Mask: ipnet.Mask}, originType, origin)
return nil
}
if strings.Contains(value, "[") {
ip, err := net.ResolveIPAddr("ip", value)
if err != nil {
return fmt.Errorf("can't parse IP address %q: %w", value, err)
}
v.Set(ip, originType, origin)
return nil
}
if strings.Contains(value, ":") {
host, port, err := net.SplitHostPort(value)
if err != nil {
return fmt.Errorf("can't parse address %q: %w", value, err)
}
if host == "" {
port = ":" + port
}
addr, err := net.ResolveTCPAddr("tcp", port)
if err != nil {
return fmt.Errorf("can't resolve TCP address %q: %w", value, err)
}
v.Set(addr, originType, origin)
return nil
}
if _, err := strconv.Atoi(value); err == nil {
addr, err := net.ResolveTCPAddr("tcp", value)
if err != nil {
return fmt.Errorf("can't resolve TCP address %q: %w", value, err)
}
v.Set(addr, originType, origin)
return nil
}
addr, err := net.ResolveTCPAddr("tcp", value)
if err != nil {
return fmt.Errorf("can't resolve TCP address %q: %w", value, err)
}
v.Set(addr, originType, origin)
return nil
}

// String returns the value as a string.
func (v *NetAddrValue) String() string {
val, _ := v.Get()
return val.String()
}

// SendEnvSliceValue is a configuration value that holds a slice of strings. It is used in the ssh configuration
// for the SendEnv field. Prefixing the value with - removes the value from the list.
type SendEnvSliceValue struct {
StringSliceValue
// RemovableStringListValue is a configuration value that holds a slice of strings. It is used in the ssh configuration
// for the SendEnv field. Prefixing the value with - removes the value from the list. Like [AppendingStringListValue],
// without the prefix it always appends even if a value was set before.
type RemovableStringListValue struct {
StringListValue
}

// SetString appends the value to the slice and sets the origin of the value or if a prefix is used,
// it modifies the list accordingly.
func (v *SendEnvSliceValue) SetString(value string, originType ValueOriginType, origin string) error {
func (v *RemovableStringListValue) SetString(value string, originType ValueOriginType, origin string) error {
if value == "" {
return nil
}
Expand All @@ -701,6 +637,8 @@ func (v *SendEnvSliceValue) SetString(value string, originType ValueOriginType,
} else {
vals = append(vals, newVals...)
}
v.Set(vals, originType, origin)
v.value = vals
v.originType = originType
v.origin = origin
return nil
}
Loading

0 comments on commit 1245aa5

Please sign in to comment.