forked from forgejo/forgejo
Implement git refs API for listing references (branches, tags and other) (#5354)
* Inital routes to git refs api * Git refs API implementation * Update swagger * Fix copyright * Make swagger happy add basic test * Fix test * Fix test again :)
This commit is contained in:
parent
294904321c
commit
08bf443016
268 changed files with 48603 additions and 10 deletions
4
vendor/github.com/kevinburke/ssh_config/AUTHORS.txt
generated
vendored
Normal file
4
vendor/github.com/kevinburke/ssh_config/AUTHORS.txt
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
Eugene Terentev <eugene@terentev.net>
|
||||
Kevin Burke <kev@inburke.com>
|
||||
Sergey Lukjanov <me@slukjanov.name>
|
||||
Wayne Ashley Berry <wayneashleyberry@gmail.com>
|
49
vendor/github.com/kevinburke/ssh_config/LICENSE
generated
vendored
Normal file
49
vendor/github.com/kevinburke/ssh_config/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
Copyright (c) 2017 Kevin Burke.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
===================
|
||||
|
||||
The lexer and parser borrow heavily from github.com/pelletier/go-toml. The
|
||||
license for that project is copied below.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
639
vendor/github.com/kevinburke/ssh_config/config.go
generated
vendored
Normal file
639
vendor/github.com/kevinburke/ssh_config/config.go
generated
vendored
Normal file
|
@ -0,0 +1,639 @@
|
|||
// Package ssh_config provides tools for manipulating SSH config files.
|
||||
//
|
||||
// Importantly, this parser attempts to preserve comments in a given file, so
|
||||
// you can manipulate a `ssh_config` file from a program, if your heart desires.
|
||||
//
|
||||
// The Get() and GetStrict() functions will attempt to read values from
|
||||
// $HOME/.ssh/config, falling back to /etc/ssh/ssh_config. The first argument is
|
||||
// the host name to match on ("example.com"), and the second argument is the key
|
||||
// you want to retrieve ("Port"). The keywords are case insensitive.
|
||||
//
|
||||
// port := ssh_config.Get("myhost", "Port")
|
||||
//
|
||||
// You can also manipulate an SSH config file and then print it or write it back
|
||||
// to disk.
|
||||
//
|
||||
// f, _ := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "config"))
|
||||
// cfg, _ := ssh_config.Decode(f)
|
||||
// for _, host := range cfg.Hosts {
|
||||
// fmt.Println("patterns:", host.Patterns)
|
||||
// for _, node := range host.Nodes {
|
||||
// fmt.Println(node.String())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Write the cfg back to disk:
|
||||
// fmt.Println(cfg.String())
|
||||
//
|
||||
// BUG: the Match directive is currently unsupported; parsing a config with
|
||||
// a Match directive will trigger an error.
|
||||
package ssh_config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
osuser "os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const version = "0.5"
|
||||
|
||||
type configFinder func() string
|
||||
|
||||
// UserSettings checks ~/.ssh and /etc/ssh for configuration files. The config
|
||||
// files are parsed and cached the first time Get() or GetStrict() is called.
|
||||
type UserSettings struct {
|
||||
IgnoreErrors bool
|
||||
systemConfig *Config
|
||||
systemConfigFinder configFinder
|
||||
userConfig *Config
|
||||
userConfigFinder configFinder
|
||||
loadConfigs sync.Once
|
||||
onceErr error
|
||||
}
|
||||
|
||||
func homedir() string {
|
||||
user, err := osuser.Current()
|
||||
if err == nil {
|
||||
return user.HomeDir
|
||||
} else {
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
}
|
||||
|
||||
func userConfigFinder() string {
|
||||
return filepath.Join(homedir(), ".ssh", "config")
|
||||
}
|
||||
|
||||
// DefaultUserSettings is the default UserSettings and is used by Get and
|
||||
// GetStrict. It checks both $HOME/.ssh/config and /etc/ssh/ssh_config for keys,
|
||||
// and it will return parse errors (if any) instead of swallowing them.
|
||||
var DefaultUserSettings = &UserSettings{
|
||||
IgnoreErrors: false,
|
||||
systemConfigFinder: systemConfigFinder,
|
||||
userConfigFinder: userConfigFinder,
|
||||
}
|
||||
|
||||
func systemConfigFinder() string {
|
||||
return filepath.Join("/", "etc", "ssh", "ssh_config")
|
||||
}
|
||||
|
||||
func findVal(c *Config, alias, key string) (string, error) {
|
||||
if c == nil {
|
||||
return "", nil
|
||||
}
|
||||
val, err := c.Get(alias, key)
|
||||
if err != nil || val == "" {
|
||||
return "", err
|
||||
}
|
||||
if err := validate(key, val); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Get finds the first value for key within a declaration that matches the
|
||||
// alias. Get returns the empty string if no value was found, or if IgnoreErrors
|
||||
// is false and we could not parse the configuration file. Use GetStrict to
|
||||
// disambiguate the latter cases.
|
||||
//
|
||||
// The match for key is case insensitive.
|
||||
//
|
||||
// Get is a wrapper around DefaultUserSettings.Get.
|
||||
func Get(alias, key string) string {
|
||||
return DefaultUserSettings.Get(alias, key)
|
||||
}
|
||||
|
||||
// GetStrict finds the first value for key within a declaration that matches the
|
||||
// alias. If key has a default value and no matching configuration is found, the
|
||||
// default will be returned. For more information on default values and the way
|
||||
// patterns are matched, see the manpage for ssh_config.
|
||||
//
|
||||
// error will be non-nil if and only if a user's configuration file or the
|
||||
// system configuration file could not be parsed, and u.IgnoreErrors is false.
|
||||
//
|
||||
// GetStrict is a wrapper around DefaultUserSettings.GetStrict.
|
||||
func GetStrict(alias, key string) (string, error) {
|
||||
return DefaultUserSettings.GetStrict(alias, key)
|
||||
}
|
||||
|
||||
// Get finds the first value for key within a declaration that matches the
|
||||
// alias. Get returns the empty string if no value was found, or if IgnoreErrors
|
||||
// is false and we could not parse the configuration file. Use GetStrict to
|
||||
// disambiguate the latter cases.
|
||||
//
|
||||
// The match for key is case insensitive.
|
||||
func (u *UserSettings) Get(alias, key string) string {
|
||||
val, err := u.GetStrict(alias, key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// GetStrict finds the first value for key within a declaration that matches the
|
||||
// alias. If key has a default value and no matching configuration is found, the
|
||||
// default will be returned. For more information on default values and the way
|
||||
// patterns are matched, see the manpage for ssh_config.
|
||||
//
|
||||
// error will be non-nil if and only if a user's configuration file or the
|
||||
// system configuration file could not be parsed, and u.IgnoreErrors is false.
|
||||
func (u *UserSettings) GetStrict(alias, key string) (string, error) {
|
||||
u.loadConfigs.Do(func() {
|
||||
// can't parse user file, that's ok.
|
||||
var filename string
|
||||
if u.userConfigFinder == nil {
|
||||
filename = userConfigFinder()
|
||||
} else {
|
||||
filename = u.userConfigFinder()
|
||||
}
|
||||
var err error
|
||||
u.userConfig, err = parseFile(filename)
|
||||
if err != nil && os.IsNotExist(err) == false {
|
||||
u.onceErr = err
|
||||
return
|
||||
}
|
||||
if u.systemConfigFinder == nil {
|
||||
filename = systemConfigFinder()
|
||||
} else {
|
||||
filename = u.systemConfigFinder()
|
||||
}
|
||||
u.systemConfig, err = parseFile(filename)
|
||||
if err != nil && os.IsNotExist(err) == false {
|
||||
u.onceErr = err
|
||||
return
|
||||
}
|
||||
})
|
||||
if u.onceErr != nil && u.IgnoreErrors == false {
|
||||
return "", u.onceErr
|
||||
}
|
||||
val, err := findVal(u.userConfig, alias, key)
|
||||
if err != nil || val != "" {
|
||||
return val, err
|
||||
}
|
||||
val2, err2 := findVal(u.systemConfig, alias, key)
|
||||
if err2 != nil || val2 != "" {
|
||||
return val2, err2
|
||||
}
|
||||
return Default(key), nil
|
||||
}
|
||||
|
||||
func parseFile(filename string) (*Config, error) {
|
||||
return parseWithDepth(filename, 0)
|
||||
}
|
||||
|
||||
func parseWithDepth(filename string, depth uint8) (*Config, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return decode(f, isSystem(filename), depth)
|
||||
}
|
||||
|
||||
func isSystem(filename string) bool {
|
||||
// TODO i'm not sure this is the best way to detect a system repo
|
||||
return strings.HasPrefix(filepath.Clean(filename), "/etc/ssh")
|
||||
}
|
||||
|
||||
// Decode reads r into a Config, or returns an error if r could not be parsed as
|
||||
// an SSH config file.
|
||||
func Decode(r io.Reader) (*Config, error) {
|
||||
return decode(r, false, 0)
|
||||
}
|
||||
|
||||
func decode(r io.Reader, system bool, depth uint8) (c *Config, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(runtime.Error); ok {
|
||||
panic(r)
|
||||
}
|
||||
if e, ok := r.(error); ok && e == ErrDepthExceeded {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
err = errors.New(r.(string))
|
||||
}
|
||||
}()
|
||||
|
||||
c = parseSSH(lexSSH(r), system, depth)
|
||||
return c, err
|
||||
}
|
||||
|
||||
// Config represents an SSH config file.
|
||||
type Config struct {
|
||||
// A list of hosts to match against. The file begins with an implicit
|
||||
// "Host *" declaration matching all hosts.
|
||||
Hosts []*Host
|
||||
depth uint8
|
||||
position Position
|
||||
}
|
||||
|
||||
// Get finds the first value in the configuration that matches the alias and
|
||||
// contains key. Get returns the empty string if no value was found, or if the
|
||||
// Config contains an invalid conditional Include value.
|
||||
//
|
||||
// The match for key is case insensitive.
|
||||
func (c *Config) Get(alias, key string) (string, error) {
|
||||
lowerKey := strings.ToLower(key)
|
||||
for _, host := range c.Hosts {
|
||||
if !host.Matches(alias) {
|
||||
continue
|
||||
}
|
||||
for _, node := range host.Nodes {
|
||||
switch t := node.(type) {
|
||||
case *Empty:
|
||||
continue
|
||||
case *KV:
|
||||
// "keys are case insensitive" per the spec
|
||||
lkey := strings.ToLower(t.Key)
|
||||
if lkey == "match" {
|
||||
panic("can't handle Match directives")
|
||||
}
|
||||
if lkey == lowerKey {
|
||||
return t.Value, nil
|
||||
}
|
||||
case *Include:
|
||||
val := t.Get(alias, key)
|
||||
if val != "" {
|
||||
return val, nil
|
||||
}
|
||||
default:
|
||||
return "", fmt.Errorf("unknown Node type %v", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// String returns a string representation of the Config file.
|
||||
func (c Config) String() string {
|
||||
return marshal(c).String()
|
||||
}
|
||||
|
||||
func (c Config) MarshalText() ([]byte, error) {
|
||||
return marshal(c).Bytes(), nil
|
||||
}
|
||||
|
||||
func marshal(c Config) *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
for i := range c.Hosts {
|
||||
buf.WriteString(c.Hosts[i].String())
|
||||
}
|
||||
return &buf
|
||||
}
|
||||
|
||||
// Pattern is a pattern in a Host declaration. Patterns are read-only values;
|
||||
// create a new one with NewPattern().
|
||||
type Pattern struct {
|
||||
str string // Its appearance in the file, not the value that gets compiled.
|
||||
regex *regexp.Regexp
|
||||
not bool // True if this is a negated match
|
||||
}
|
||||
|
||||
// String prints the string representation of the pattern.
|
||||
func (p Pattern) String() string {
|
||||
return p.str
|
||||
}
|
||||
|
||||
// Copied from regexp.go with * and ? removed.
|
||||
var specialBytes = []byte(`\.+()|[]{}^$`)
|
||||
|
||||
func special(b byte) bool {
|
||||
return bytes.IndexByte(specialBytes, b) >= 0
|
||||
}
|
||||
|
||||
// NewPattern creates a new Pattern for matching hosts. NewPattern("*") creates
|
||||
// a Pattern that matches all hosts.
|
||||
//
|
||||
// From the manpage, a pattern consists of zero or more non-whitespace
|
||||
// characters, `*' (a wildcard that matches zero or more characters), or `?' (a
|
||||
// wildcard that matches exactly one character). For example, to specify a set
|
||||
// of declarations for any host in the ".co.uk" set of domains, the following
|
||||
// pattern could be used:
|
||||
//
|
||||
// Host *.co.uk
|
||||
//
|
||||
// The following pattern would match any host in the 192.168.0.[0-9] network range:
|
||||
//
|
||||
// Host 192.168.0.?
|
||||
func NewPattern(s string) (*Pattern, error) {
|
||||
if s == "" {
|
||||
return nil, errors.New("ssh_config: empty pattern")
|
||||
}
|
||||
negated := false
|
||||
if s[0] == '!' {
|
||||
negated = true
|
||||
s = s[1:]
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('^')
|
||||
for i := 0; i < len(s); i++ {
|
||||
// A byte loop is correct because all metacharacters are ASCII.
|
||||
switch b := s[i]; b {
|
||||
case '*':
|
||||
buf.WriteString(".*")
|
||||
case '?':
|
||||
buf.WriteString(".?")
|
||||
default:
|
||||
// borrowing from QuoteMeta here.
|
||||
if special(b) {
|
||||
buf.WriteByte('\\')
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('$')
|
||||
r, err := regexp.Compile(buf.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Pattern{str: s, regex: r, not: negated}, nil
|
||||
}
|
||||
|
||||
// Host describes a Host directive and the keywords that follow it.
|
||||
type Host struct {
|
||||
// A list of host patterns that should match this host.
|
||||
Patterns []*Pattern
|
||||
// A Node is either a key/value pair or a comment line.
|
||||
Nodes []Node
|
||||
// EOLComment is the comment (if any) terminating the Host line.
|
||||
EOLComment string
|
||||
hasEquals bool
|
||||
leadingSpace uint16 // TODO: handle spaces vs tabs here.
|
||||
// The file starts with an implicit "Host *" declaration.
|
||||
implicit bool
|
||||
}
|
||||
|
||||
// Matches returns true if the Host matches for the given alias. For
|
||||
// a description of the rules that provide a match, see the manpage for
|
||||
// ssh_config.
|
||||
func (h *Host) Matches(alias string) bool {
|
||||
found := false
|
||||
for i := range h.Patterns {
|
||||
if h.Patterns[i].regex.MatchString(alias) {
|
||||
if h.Patterns[i].not == true {
|
||||
// Negated match. "A pattern entry may be negated by prefixing
|
||||
// it with an exclamation mark (`!'). If a negated entry is
|
||||
// matched, then the Host entry is ignored, regardless of
|
||||
// whether any other patterns on the line match. Negated matches
|
||||
// are therefore useful to provide exceptions for wildcard
|
||||
// matches."
|
||||
return false
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// String prints h as it would appear in a config file. Minor tweaks may be
|
||||
// present in the whitespace in the printed file.
|
||||
func (h *Host) String() string {
|
||||
var buf bytes.Buffer
|
||||
if h.implicit == false {
|
||||
buf.WriteString(strings.Repeat(" ", int(h.leadingSpace)))
|
||||
buf.WriteString("Host")
|
||||
if h.hasEquals {
|
||||
buf.WriteString(" = ")
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
for i, pat := range h.Patterns {
|
||||
buf.WriteString(pat.String())
|
||||
if i < len(h.Patterns)-1 {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
}
|
||||
if h.EOLComment != "" {
|
||||
buf.WriteString(" #")
|
||||
buf.WriteString(h.EOLComment)
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
for i := range h.Nodes {
|
||||
buf.WriteString(h.Nodes[i].String())
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// Node represents a line in a Config.
|
||||
type Node interface {
|
||||
Pos() Position
|
||||
String() string
|
||||
}
|
||||
|
||||
// KV is a line in the config file that contains a key, a value, and possibly
|
||||
// a comment.
|
||||
type KV struct {
|
||||
Key string
|
||||
Value string
|
||||
Comment string
|
||||
hasEquals bool
|
||||
leadingSpace uint16 // Space before the key. TODO handle spaces vs tabs.
|
||||
position Position
|
||||
}
|
||||
|
||||
// Pos returns k's Position.
|
||||
func (k *KV) Pos() Position {
|
||||
return k.position
|
||||
}
|
||||
|
||||
// String prints k as it was parsed in the config file. There may be slight
|
||||
// changes to the whitespace between values.
|
||||
func (k *KV) String() string {
|
||||
if k == nil {
|
||||
return ""
|
||||
}
|
||||
equals := " "
|
||||
if k.hasEquals {
|
||||
equals = " = "
|
||||
}
|
||||
line := fmt.Sprintf("%s%s%s%s", strings.Repeat(" ", int(k.leadingSpace)), k.Key, equals, k.Value)
|
||||
if k.Comment != "" {
|
||||
line += " #" + k.Comment
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
// Empty is a line in the config file that contains only whitespace or comments.
|
||||
type Empty struct {
|
||||
Comment string
|
||||
leadingSpace uint16 // TODO handle spaces vs tabs.
|
||||
position Position
|
||||
}
|
||||
|
||||
// Pos returns e's Position.
|
||||
func (e *Empty) Pos() Position {
|
||||
return e.position
|
||||
}
|
||||
|
||||
// String prints e as it was parsed in the config file.
|
||||
func (e *Empty) String() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
if e.Comment == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s#%s", strings.Repeat(" ", int(e.leadingSpace)), e.Comment)
|
||||
}
|
||||
|
||||
// Include holds the result of an Include directive, including the config files
|
||||
// that have been parsed as part of that directive. At most 5 levels of Include
|
||||
// statements will be parsed.
|
||||
type Include struct {
|
||||
// Comment is the contents of any comment at the end of the Include
|
||||
// statement.
|
||||
Comment string
|
||||
parsed bool
|
||||
// an include directive can include several different files, and wildcards
|
||||
directives []string
|
||||
|
||||
mu sync.Mutex
|
||||
// 1:1 mapping between matches and keys in files array; matches preserves
|
||||
// ordering
|
||||
matches []string
|
||||
// actual filenames are listed here
|
||||
files map[string]*Config
|
||||
leadingSpace uint16
|
||||
position Position
|
||||
depth uint8
|
||||
hasEquals bool
|
||||
}
|
||||
|
||||
const maxRecurseDepth = 5
|
||||
|
||||
// ErrDepthExceeded is returned if too many Include directives are parsed.
|
||||
// Usually this indicates a recursive loop (an Include directive pointing to the
|
||||
// file it contains).
|
||||
var ErrDepthExceeded = errors.New("ssh_config: max recurse depth exceeded")
|
||||
|
||||
func removeDups(arr []string) []string {
|
||||
// Use map to record duplicates as we find them.
|
||||
encountered := make(map[string]bool, len(arr))
|
||||
result := make([]string, 0)
|
||||
|
||||
for v := range arr {
|
||||
if encountered[arr[v]] == false {
|
||||
encountered[arr[v]] = true
|
||||
result = append(result, arr[v])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NewInclude creates a new Include with a list of file globs to include.
|
||||
// Configuration files are parsed greedily (e.g. as soon as this function runs).
|
||||
// Any error encountered while parsing nested configuration files will be
|
||||
// returned.
|
||||
func NewInclude(directives []string, hasEquals bool, pos Position, comment string, system bool, depth uint8) (*Include, error) {
|
||||
if depth > maxRecurseDepth {
|
||||
return nil, ErrDepthExceeded
|
||||
}
|
||||
inc := &Include{
|
||||
Comment: comment,
|
||||
directives: directives,
|
||||
files: make(map[string]*Config),
|
||||
position: pos,
|
||||
leadingSpace: uint16(pos.Col) - 1,
|
||||
depth: depth,
|
||||
hasEquals: hasEquals,
|
||||
}
|
||||
// no need for inc.mu.Lock() since nothing else can access this inc
|
||||
matches := make([]string, 0)
|
||||
for i := range directives {
|
||||
var path string
|
||||
if filepath.IsAbs(directives[i]) {
|
||||
path = directives[i]
|
||||
} else if system {
|
||||
path = filepath.Join("/etc/ssh", directives[i])
|
||||
} else {
|
||||
path = filepath.Join(homedir(), ".ssh", directives[i])
|
||||
}
|
||||
theseMatches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matches = append(matches, theseMatches...)
|
||||
}
|
||||
matches = removeDups(matches)
|
||||
inc.matches = matches
|
||||
for i := range matches {
|
||||
config, err := parseWithDepth(matches[i], depth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inc.files[matches[i]] = config
|
||||
}
|
||||
return inc, nil
|
||||
}
|
||||
|
||||
// Pos returns the position of the Include directive in the larger file.
|
||||
func (i *Include) Pos() Position {
|
||||
return i.position
|
||||
}
|
||||
|
||||
// Get finds the first value in the Include statement matching the alias and the
|
||||
// given key.
|
||||
func (inc *Include) Get(alias, key string) string {
|
||||
inc.mu.Lock()
|
||||
defer inc.mu.Unlock()
|
||||
// TODO: we search files in any order which is not correct
|
||||
for i := range inc.matches {
|
||||
cfg := inc.files[inc.matches[i]]
|
||||
if cfg == nil {
|
||||
panic("nil cfg")
|
||||
}
|
||||
val, err := cfg.Get(alias, key)
|
||||
if err == nil && val != "" {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// String prints out a string representation of this Include directive. Note
|
||||
// included Config files are not printed as part of this representation.
|
||||
func (inc *Include) String() string {
|
||||
equals := " "
|
||||
if inc.hasEquals {
|
||||
equals = " = "
|
||||
}
|
||||
line := fmt.Sprintf("%sInclude%s%s", strings.Repeat(" ", int(inc.leadingSpace)), equals, strings.Join(inc.directives, " "))
|
||||
if inc.Comment != "" {
|
||||
line += " #" + inc.Comment
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
var matchAll *Pattern
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
matchAll, err = NewPattern("*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func newConfig() *Config {
|
||||
return &Config{
|
||||
Hosts: []*Host{
|
||||
&Host{
|
||||
implicit: true,
|
||||
Patterns: []*Pattern{matchAll},
|
||||
Nodes: make([]Node, 0),
|
||||
},
|
||||
},
|
||||
depth: 0,
|
||||
}
|
||||
}
|
241
vendor/github.com/kevinburke/ssh_config/lexer.go
generated
vendored
Normal file
241
vendor/github.com/kevinburke/ssh_config/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
package ssh_config
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
buffruneio "github.com/pelletier/go-buffruneio"
|
||||
)
|
||||
|
||||
// Define state functions
|
||||
type sshLexStateFn func() sshLexStateFn
|
||||
|
||||
type sshLexer struct {
|
||||
input *buffruneio.Reader // Textual source
|
||||
buffer []rune // Runes composing the current token
|
||||
tokens chan token
|
||||
line uint32
|
||||
col uint16
|
||||
endbufferLine uint32
|
||||
endbufferCol uint16
|
||||
}
|
||||
|
||||
func (s *sshLexer) lexComment(previousState sshLexStateFn) sshLexStateFn {
|
||||
return func() sshLexStateFn {
|
||||
growingString := ""
|
||||
for next := s.peek(); next != '\n' && next != eof; next = s.peek() {
|
||||
if next == '\r' && s.follow("\r\n") {
|
||||
break
|
||||
}
|
||||
growingString += string(next)
|
||||
s.next()
|
||||
}
|
||||
s.emitWithValue(tokenComment, growingString)
|
||||
s.skip()
|
||||
return previousState
|
||||
}
|
||||
}
|
||||
|
||||
// lex the space after an equals sign in a function
|
||||
func (s *sshLexer) lexRspace() sshLexStateFn {
|
||||
for {
|
||||
next := s.peek()
|
||||
if !isSpace(next) {
|
||||
break
|
||||
}
|
||||
s.skip()
|
||||
}
|
||||
return s.lexRvalue
|
||||
}
|
||||
|
||||
func (s *sshLexer) lexEquals() sshLexStateFn {
|
||||
for {
|
||||
next := s.peek()
|
||||
if next == '=' {
|
||||
s.emit(tokenEquals)
|
||||
s.skip()
|
||||
return s.lexRspace
|
||||
}
|
||||
// TODO error handling here; newline eof etc.
|
||||
if !isSpace(next) {
|
||||
break
|
||||
}
|
||||
s.skip()
|
||||
}
|
||||
return s.lexRvalue
|
||||
}
|
||||
|
||||
func (s *sshLexer) lexKey() sshLexStateFn {
|
||||
growingString := ""
|
||||
|
||||
for r := s.peek(); isKeyChar(r); r = s.peek() {
|
||||
// simplified a lot here
|
||||
if isSpace(r) || r == '=' {
|
||||
s.emitWithValue(tokenKey, growingString)
|
||||
s.skip()
|
||||
return s.lexEquals
|
||||
}
|
||||
growingString += string(r)
|
||||
s.next()
|
||||
}
|
||||
s.emitWithValue(tokenKey, growingString)
|
||||
return s.lexEquals
|
||||
}
|
||||
|
||||
func (s *sshLexer) lexRvalue() sshLexStateFn {
|
||||
growingString := ""
|
||||
for {
|
||||
next := s.peek()
|
||||
switch next {
|
||||
case '\r':
|
||||
if s.follow("\r\n") {
|
||||
s.emitWithValue(tokenString, growingString)
|
||||
s.skip()
|
||||
return s.lexVoid
|
||||
}
|
||||
case '\n':
|
||||
s.emitWithValue(tokenString, growingString)
|
||||
s.skip()
|
||||
return s.lexVoid
|
||||
case '#':
|
||||
s.emitWithValue(tokenString, growingString)
|
||||
s.skip()
|
||||
return s.lexComment(s.lexVoid)
|
||||
case eof:
|
||||
s.next()
|
||||
}
|
||||
if next == eof {
|
||||
break
|
||||
}
|
||||
growingString += string(next)
|
||||
s.next()
|
||||
}
|
||||
s.emit(tokenEOF)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sshLexer) read() rune {
|
||||
r, _, err := s.input.ReadRune()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if r == '\n' {
|
||||
s.endbufferLine++
|
||||
s.endbufferCol = 1
|
||||
} else {
|
||||
s.endbufferCol++
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *sshLexer) next() rune {
|
||||
r := s.read()
|
||||
|
||||
if r != eof {
|
||||
s.buffer = append(s.buffer, r)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *sshLexer) lexVoid() sshLexStateFn {
|
||||
for {
|
||||
next := s.peek()
|
||||
switch next {
|
||||
case '#':
|
||||
s.skip()
|
||||
return s.lexComment(s.lexVoid)
|
||||
case '\r':
|
||||
fallthrough
|
||||
case '\n':
|
||||
s.emit(tokenEmptyLine)
|
||||
s.skip()
|
||||
continue
|
||||
}
|
||||
|
||||
if isSpace(next) {
|
||||
s.skip()
|
||||
}
|
||||
|
||||
if isKeyStartChar(next) {
|
||||
return s.lexKey
|
||||
}
|
||||
|
||||
// removed IsKeyStartChar and lexKey. probably will need to readd
|
||||
|
||||
if next == eof {
|
||||
s.next()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
s.emit(tokenEOF)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sshLexer) ignore() {
|
||||
s.buffer = make([]rune, 0)
|
||||
s.line = s.endbufferLine
|
||||
s.col = s.endbufferCol
|
||||
}
|
||||
|
||||
func (s *sshLexer) skip() {
|
||||
s.next()
|
||||
s.ignore()
|
||||
}
|
||||
|
||||
func (s *sshLexer) emit(t tokenType) {
|
||||
s.emitWithValue(t, string(s.buffer))
|
||||
}
|
||||
|
||||
func (s *sshLexer) emitWithValue(t tokenType, value string) {
|
||||
tok := token{
|
||||
Position: Position{s.line, s.col},
|
||||
typ: t,
|
||||
val: value,
|
||||
}
|
||||
s.tokens <- tok
|
||||
s.ignore()
|
||||
}
|
||||
|
||||
func (s *sshLexer) peek() rune {
|
||||
r, _, err := s.input.ReadRune()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.input.UnreadRune()
|
||||
return r
|
||||
}
|
||||
|
||||
func (s *sshLexer) follow(next string) bool {
|
||||
for _, expectedRune := range next {
|
||||
r, _, err := s.input.ReadRune()
|
||||
defer s.input.UnreadRune()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if expectedRune != r {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *sshLexer) run() {
|
||||
for state := s.lexVoid; state != nil; {
|
||||
state = state()
|
||||
}
|
||||
close(s.tokens)
|
||||
}
|
||||
|
||||
func lexSSH(input io.Reader) chan token {
|
||||
bufferedInput := buffruneio.NewReader(input)
|
||||
l := &sshLexer{
|
||||
input: bufferedInput,
|
||||
tokens: make(chan token),
|
||||
line: 1,
|
||||
col: 1,
|
||||
endbufferLine: 1,
|
||||
endbufferCol: 1,
|
||||
}
|
||||
go l.run()
|
||||
return l.tokens
|
||||
}
|
185
vendor/github.com/kevinburke/ssh_config/parser.go
generated
vendored
Normal file
185
vendor/github.com/kevinburke/ssh_config/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,185 @@
|
|||
package ssh_config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type sshParser struct {
|
||||
flow chan token
|
||||
config *Config
|
||||
tokensBuffer []token
|
||||
currentTable []string
|
||||
seenTableKeys []string
|
||||
// /etc/ssh parser or local parser - used to find the default for relative
|
||||
// filepaths in the Include directive
|
||||
system bool
|
||||
depth uint8
|
||||
}
|
||||
|
||||
type sshParserStateFn func() sshParserStateFn
|
||||
|
||||
// Formats and panics an error message based on a token
|
||||
func (p *sshParser) raiseErrorf(tok *token, msg string, args ...interface{}) {
|
||||
// TODO this format is ugly
|
||||
panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
func (p *sshParser) raiseError(tok *token, err error) {
|
||||
if err == ErrDepthExceeded {
|
||||
panic(err)
|
||||
}
|
||||
// TODO this format is ugly
|
||||
panic(tok.Position.String() + ": " + err.Error())
|
||||
}
|
||||
|
||||
func (p *sshParser) run() {
|
||||
for state := p.parseStart; state != nil; {
|
||||
state = state()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *sshParser) peek() *token {
|
||||
if len(p.tokensBuffer) != 0 {
|
||||
return &(p.tokensBuffer[0])
|
||||
}
|
||||
|
||||
tok, ok := <-p.flow
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
p.tokensBuffer = append(p.tokensBuffer, tok)
|
||||
return &tok
|
||||
}
|
||||
|
||||
func (p *sshParser) getToken() *token {
|
||||
if len(p.tokensBuffer) != 0 {
|
||||
tok := p.tokensBuffer[0]
|
||||
p.tokensBuffer = p.tokensBuffer[1:]
|
||||
return &tok
|
||||
}
|
||||
tok, ok := <-p.flow
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &tok
|
||||
}
|
||||
|
||||
func (p *sshParser) parseStart() sshParserStateFn {
|
||||
tok := p.peek()
|
||||
|
||||
// end of stream, parsing is finished
|
||||
if tok == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch tok.typ {
|
||||
case tokenComment, tokenEmptyLine:
|
||||
return p.parseComment
|
||||
case tokenKey:
|
||||
return p.parseKV
|
||||
case tokenEOF:
|
||||
return nil
|
||||
default:
|
||||
p.raiseErrorf(tok, fmt.Sprintf("unexpected token %q\n", tok))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *sshParser) parseKV() sshParserStateFn {
|
||||
key := p.getToken()
|
||||
hasEquals := false
|
||||
val := p.getToken()
|
||||
if val.typ == tokenEquals {
|
||||
hasEquals = true
|
||||
val = p.getToken()
|
||||
}
|
||||
comment := ""
|
||||
tok := p.peek()
|
||||
if tok == nil {
|
||||
tok = &token{typ: tokenEOF}
|
||||
}
|
||||
if tok.typ == tokenComment && tok.Position.Line == val.Position.Line {
|
||||
tok = p.getToken()
|
||||
comment = tok.val
|
||||
}
|
||||
if strings.ToLower(key.val) == "match" {
|
||||
// https://github.com/kevinburke/ssh_config/issues/6
|
||||
p.raiseErrorf(val, "ssh_config: Match directive parsing is unsupported")
|
||||
return nil
|
||||
}
|
||||
if strings.ToLower(key.val) == "host" {
|
||||
strPatterns := strings.Split(val.val, " ")
|
||||
patterns := make([]*Pattern, 0)
|
||||
for i := range strPatterns {
|
||||
if strPatterns[i] == "" {
|
||||
continue
|
||||
}
|
||||
pat, err := NewPattern(strPatterns[i])
|
||||
if err != nil {
|
||||
p.raiseErrorf(val, "Invalid host pattern: %v", err)
|
||||
return nil
|
||||
}
|
||||
patterns = append(patterns, pat)
|
||||
}
|
||||
p.config.Hosts = append(p.config.Hosts, &Host{
|
||||
Patterns: patterns,
|
||||
Nodes: make([]Node, 0),
|
||||
EOLComment: comment,
|
||||
hasEquals: hasEquals,
|
||||
})
|
||||
return p.parseStart
|
||||
}
|
||||
lastHost := p.config.Hosts[len(p.config.Hosts)-1]
|
||||
if strings.ToLower(key.val) == "include" {
|
||||
inc, err := NewInclude(strings.Split(val.val, " "), hasEquals, key.Position, comment, p.system, p.depth+1)
|
||||
if err == ErrDepthExceeded {
|
||||
p.raiseError(val, err)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
p.raiseErrorf(val, "Error parsing Include directive: %v", err)
|
||||
return nil
|
||||
}
|
||||
lastHost.Nodes = append(lastHost.Nodes, inc)
|
||||
return p.parseStart
|
||||
}
|
||||
kv := &KV{
|
||||
Key: key.val,
|
||||
Value: val.val,
|
||||
Comment: comment,
|
||||
hasEquals: hasEquals,
|
||||
leadingSpace: uint16(key.Position.Col) - 1,
|
||||
position: key.Position,
|
||||
}
|
||||
lastHost.Nodes = append(lastHost.Nodes, kv)
|
||||
return p.parseStart
|
||||
}
|
||||
|
||||
func (p *sshParser) parseComment() sshParserStateFn {
|
||||
comment := p.getToken()
|
||||
lastHost := p.config.Hosts[len(p.config.Hosts)-1]
|
||||
lastHost.Nodes = append(lastHost.Nodes, &Empty{
|
||||
Comment: comment.val,
|
||||
// account for the "#" as well
|
||||
leadingSpace: comment.Position.Col - 2,
|
||||
position: comment.Position,
|
||||
})
|
||||
return p.parseStart
|
||||
}
|
||||
|
||||
func parseSSH(flow chan token, system bool, depth uint8) *Config {
|
||||
result := newConfig()
|
||||
result.position = Position{1, 1}
|
||||
parser := &sshParser{
|
||||
flow: flow,
|
||||
config: result,
|
||||
tokensBuffer: make([]token, 0),
|
||||
currentTable: make([]string, 0),
|
||||
seenTableKeys: make([]string, 0),
|
||||
system: system,
|
||||
depth: depth,
|
||||
}
|
||||
parser.run()
|
||||
return result
|
||||
}
|
25
vendor/github.com/kevinburke/ssh_config/position.go
generated
vendored
Normal file
25
vendor/github.com/kevinburke/ssh_config/position.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package ssh_config
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Position of a document element within a SSH document.
|
||||
//
|
||||
// Line and Col are both 1-indexed positions for the element's line number and
|
||||
// column number, respectively. Values of zero or less will cause Invalid(),
|
||||
// to return true.
|
||||
type Position struct {
|
||||
Line uint32 // line within the document
|
||||
Col uint16 // column within the line
|
||||
}
|
||||
|
||||
// String representation of the position.
|
||||
// Displays 1-indexed line and column numbers.
|
||||
func (p Position) String() string {
|
||||
return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
|
||||
}
|
||||
|
||||
// Invalid returns whether or not the position is valid (i.e. with negative or
|
||||
// null values)
|
||||
func (p Position) Invalid() bool {
|
||||
return p.Line <= 0 || p.Col <= 0
|
||||
}
|
49
vendor/github.com/kevinburke/ssh_config/token.go
generated
vendored
Normal file
49
vendor/github.com/kevinburke/ssh_config/token.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package ssh_config
|
||||
|
||||
import "fmt"
|
||||
|
||||
type token struct {
|
||||
Position
|
||||
typ tokenType
|
||||
val string
|
||||
}
|
||||
|
||||
func (t token) String() string {
|
||||
switch t.typ {
|
||||
case tokenEOF:
|
||||
return "EOF"
|
||||
}
|
||||
return fmt.Sprintf("%q", t.val)
|
||||
}
|
||||
|
||||
type tokenType int
|
||||
|
||||
const (
|
||||
eof = -(iota + 1)
|
||||
)
|
||||
|
||||
const (
|
||||
tokenError tokenType = iota
|
||||
tokenEOF
|
||||
tokenEmptyLine
|
||||
tokenComment
|
||||
tokenKey
|
||||
tokenEquals
|
||||
tokenString
|
||||
)
|
||||
|
||||
func isSpace(r rune) bool {
|
||||
return r == ' ' || r == '\t'
|
||||
}
|
||||
|
||||
func isKeyStartChar(r rune) bool {
|
||||
return !(isSpace(r) || r == '\r' || r == '\n' || r == eof)
|
||||
}
|
||||
|
||||
// I'm not sure that this is correct
|
||||
func isKeyChar(r rune) bool {
|
||||
// Keys start with the first character that isn't whitespace or [ and end
|
||||
// with the last non-whitespace character before the equals sign. Keys
|
||||
// cannot contain a # character."
|
||||
return !(r == '\r' || r == '\n' || r == eof || r == '=')
|
||||
}
|
162
vendor/github.com/kevinburke/ssh_config/validators.go
generated
vendored
Normal file
162
vendor/github.com/kevinburke/ssh_config/validators.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
package ssh_config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Default returns the default value for the given keyword, for example "22" if
|
||||
// the keyword is "Port". Default returns the empty string if the keyword has no
|
||||
// default, or if the keyword is unknown. Keyword matching is case-insensitive.
|
||||
//
|
||||
// Default values are provided by OpenSSH_7.4p1 on a Mac.
|
||||
func Default(keyword string) string {
|
||||
return defaults[strings.ToLower(keyword)]
|
||||
}
|
||||
|
||||
// Arguments where the value must be "yes" or "no" and *only* yes or no.
|
||||
var yesnos = map[string]bool{
|
||||
strings.ToLower("BatchMode"): true,
|
||||
strings.ToLower("CanonicalizeFallbackLocal"): true,
|
||||
strings.ToLower("ChallengeResponseAuthentication"): true,
|
||||
strings.ToLower("CheckHostIP"): true,
|
||||
strings.ToLower("ClearAllForwardings"): true,
|
||||
strings.ToLower("Compression"): true,
|
||||
strings.ToLower("EnableSSHKeysign"): true,
|
||||
strings.ToLower("ExitOnForwardFailure"): true,
|
||||
strings.ToLower("ForwardAgent"): true,
|
||||
strings.ToLower("ForwardX11"): true,
|
||||
strings.ToLower("ForwardX11Trusted"): true,
|
||||
strings.ToLower("GatewayPorts"): true,
|
||||
strings.ToLower("GSSAPIAuthentication"): true,
|
||||
strings.ToLower("GSSAPIDelegateCredentials"): true,
|
||||
strings.ToLower("HostbasedAuthentication"): true,
|
||||
strings.ToLower("IdentitiesOnly"): true,
|
||||
strings.ToLower("KbdInteractiveAuthentication"): true,
|
||||
strings.ToLower("NoHostAuthenticationForLocalhost"): true,
|
||||
strings.ToLower("PasswordAuthentication"): true,
|
||||
strings.ToLower("PermitLocalCommand"): true,
|
||||
strings.ToLower("PubkeyAuthentication"): true,
|
||||
strings.ToLower("RhostsRSAAuthentication"): true,
|
||||
strings.ToLower("RSAAuthentication"): true,
|
||||
strings.ToLower("StreamLocalBindUnlink"): true,
|
||||
strings.ToLower("TCPKeepAlive"): true,
|
||||
strings.ToLower("UseKeychain"): true,
|
||||
strings.ToLower("UsePrivilegedPort"): true,
|
||||
strings.ToLower("VisualHostKey"): true,
|
||||
}
|
||||
|
||||
var uints = map[string]bool{
|
||||
strings.ToLower("CanonicalizeMaxDots"): true,
|
||||
strings.ToLower("CompressionLevel"): true, // 1 to 9
|
||||
strings.ToLower("ConnectionAttempts"): true,
|
||||
strings.ToLower("ConnectTimeout"): true,
|
||||
strings.ToLower("NumberOfPasswordPrompts"): true,
|
||||
strings.ToLower("Port"): true,
|
||||
strings.ToLower("ServerAliveCountMax"): true,
|
||||
strings.ToLower("ServerAliveInterval"): true,
|
||||
}
|
||||
|
||||
func mustBeYesOrNo(lkey string) bool {
|
||||
return yesnos[lkey]
|
||||
}
|
||||
|
||||
func mustBeUint(lkey string) bool {
|
||||
return uints[lkey]
|
||||
}
|
||||
|
||||
func validate(key, val string) error {
|
||||
lkey := strings.ToLower(key)
|
||||
if mustBeYesOrNo(lkey) && (val != "yes" && val != "no") {
|
||||
return fmt.Errorf("ssh_config: value for key %q must be 'yes' or 'no', got %q", key, val)
|
||||
}
|
||||
if mustBeUint(lkey) {
|
||||
_, err := strconv.ParseUint(val, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ssh_config: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var defaults = map[string]string{
|
||||
strings.ToLower("AddKeysToAgent"): "no",
|
||||
strings.ToLower("AddressFamily"): "any",
|
||||
strings.ToLower("BatchMode"): "no",
|
||||
strings.ToLower("CanonicalizeFallbackLocal"): "yes",
|
||||
strings.ToLower("CanonicalizeHostname"): "no",
|
||||
strings.ToLower("CanonicalizeMaxDots"): "1",
|
||||
strings.ToLower("ChallengeResponseAuthentication"): "yes",
|
||||
strings.ToLower("CheckHostIP"): "yes",
|
||||
// TODO is this still the correct cipher
|
||||
strings.ToLower("Cipher"): "3des",
|
||||
strings.ToLower("Ciphers"): "chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc",
|
||||
strings.ToLower("ClearAllForwardings"): "no",
|
||||
strings.ToLower("Compression"): "no",
|
||||
strings.ToLower("CompressionLevel"): "6",
|
||||
strings.ToLower("ConnectionAttempts"): "1",
|
||||
strings.ToLower("ControlMaster"): "no",
|
||||
strings.ToLower("EnableSSHKeysign"): "no",
|
||||
strings.ToLower("EscapeChar"): "~",
|
||||
strings.ToLower("ExitOnForwardFailure"): "no",
|
||||
strings.ToLower("FingerprintHash"): "sha256",
|
||||
strings.ToLower("ForwardAgent"): "no",
|
||||
strings.ToLower("ForwardX11"): "no",
|
||||
strings.ToLower("ForwardX11Timeout"): "20m",
|
||||
strings.ToLower("ForwardX11Trusted"): "no",
|
||||
strings.ToLower("GatewayPorts"): "no",
|
||||
strings.ToLower("GlobalKnownHostsFile"): "/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2",
|
||||
strings.ToLower("GSSAPIAuthentication"): "no",
|
||||
strings.ToLower("GSSAPIDelegateCredentials"): "no",
|
||||
strings.ToLower("HashKnownHosts"): "no",
|
||||
strings.ToLower("HostbasedAuthentication"): "no",
|
||||
|
||||
strings.ToLower("HostbasedKeyTypes"): "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
|
||||
strings.ToLower("HostKeyAlgorithms"): "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
|
||||
// HostName has a dynamic default (the value passed at the command line).
|
||||
|
||||
strings.ToLower("IdentitiesOnly"): "no",
|
||||
strings.ToLower("IdentityFile"): "~/.ssh/identity",
|
||||
|
||||
// IPQoS has a dynamic default based on interactive or non-interactive
|
||||
// sessions.
|
||||
|
||||
strings.ToLower("KbdInteractiveAuthentication"): "yes",
|
||||
|
||||
strings.ToLower("KexAlgorithms"): "curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1",
|
||||
strings.ToLower("LogLevel"): "INFO",
|
||||
strings.ToLower("MACs"): "umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1",
|
||||
|
||||
strings.ToLower("NoHostAuthenticationForLocalhost"): "no",
|
||||
strings.ToLower("NumberOfPasswordPrompts"): "3",
|
||||
strings.ToLower("PasswordAuthentication"): "yes",
|
||||
strings.ToLower("PermitLocalCommand"): "no",
|
||||
strings.ToLower("Port"): "22",
|
||||
|
||||
strings.ToLower("PreferredAuthentications"): "gssapi-with-mic,hostbased,publickey,keyboard-interactive,password",
|
||||
strings.ToLower("Protocol"): "2",
|
||||
strings.ToLower("ProxyUseFdpass"): "no",
|
||||
strings.ToLower("PubkeyAcceptedKeyTypes"): "ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa",
|
||||
strings.ToLower("PubkeyAuthentication"): "yes",
|
||||
strings.ToLower("RekeyLimit"): "default none",
|
||||
strings.ToLower("RhostsRSAAuthentication"): "no",
|
||||
strings.ToLower("RSAAuthentication"): "yes",
|
||||
|
||||
strings.ToLower("ServerAliveCountMax"): "3",
|
||||
strings.ToLower("ServerAliveInterval"): "0",
|
||||
strings.ToLower("StreamLocalBindMask"): "0177",
|
||||
strings.ToLower("StreamLocalBindUnlink"): "no",
|
||||
strings.ToLower("StrictHostKeyChecking"): "ask",
|
||||
strings.ToLower("TCPKeepAlive"): "yes",
|
||||
strings.ToLower("Tunnel"): "no",
|
||||
strings.ToLower("TunnelDevice"): "any:any",
|
||||
strings.ToLower("UpdateHostKeys"): "no",
|
||||
strings.ToLower("UseKeychain"): "no",
|
||||
strings.ToLower("UsePrivilegedPort"): "no",
|
||||
|
||||
strings.ToLower("UserKnownHostsFile"): "~/.ssh/known_hosts ~/.ssh/known_hosts2",
|
||||
strings.ToLower("VerifyHostKeyDNS"): "no",
|
||||
strings.ToLower("VisualHostKey"): "no",
|
||||
strings.ToLower("XAuthLocation"): "/usr/X11R6/bin/xauth",
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue