Codebase list golang-github-kevinburke-ssh-config / a6a936b2-db2d-43a1-8032-ef72c3ca840e/upstream
Import upstream version 1.1.0 Debian Janitor 2 years ago
10 changed file(s) with 314 addition(s) and 40 deletion(s). Raw diff Collapse all Expand all
0 on: [push, pull_request]
1 name: Test
2 jobs:
3 test:
4 strategy:
5 matrix:
6 go-version: [1.16.x]
7 platform: [ubuntu-latest]
8 runs-on: ${{ matrix.platform }}
9 steps:
10 - name: Install Go
11 uses: WillAbides/setup-go-faster@main
12 with:
13 go-version: ${{ matrix.go-version }}
14 - uses: actions/checkout@v2
15 with:
16 path: './src/github.com/kevinburke/ssh_config'
17 # staticcheck needs this for GOPATH
18 - run: |
19 echo "GO111MODULE=off" >> $GITHUB_ENV
20 echo "GOPATH=$GITHUB_WORKSPACE" >> $GITHUB_ENV
21 echo "PATH=$GITHUB_WORKSPACE/bin:$PATH" >> $GITHUB_ENV
22 - name: Run tests
23 run: make lint race-test
24 working-directory: './src/github.com/kevinburke/ssh_config'
0 Kevin Burke <kevin@burke.dev> Kevin Burke <kev@inburke.com>
+0
-14
.travis.yml less more
0 go_import_path: github.com/kevinburke/ssh_config
1
2 language: go
3
4 go:
5 - 1.11.x
6 - 1.12.x
7 - master
8
9 before_script:
10 - go get -u ./...
11
12 script:
13 - make race-test
00 Eugene Terentev <eugene@terentev.net>
1 Kevin Burke <kev@inburke.com>
1 Kevin Burke <kevin@burke.dev>
2 Mark Nevill <nev@improbable.io>
23 Sergey Lukjanov <me@slukjanov.name>
34 Wayne Ashley Berry <wayneashleyberry@gmail.com>
5 santosh653 <70637961+santosh653@users.noreply.github.com>
1414
1515 ```go
1616 port := ssh_config.Get("myhost", "Port")
17 ```
18
19 Certain directives can occur multiple times for a host (such as `IdentityFile`),
20 so you should use the `GetAll` or `GetAllStrict` directive to retrieve those
21 instead.
22
23 ```go
24 files := ssh_config.GetAll("myhost", "IdentityFile")
1725 ```
1826
1927 You can also load a config file and read values from it.
101101 return val, nil
102102 }
103103
104 func findAll(c *Config, alias, key string) ([]string, error) {
105 if c == nil {
106 return nil, nil
107 }
108 return c.GetAll(alias, key)
109 }
110
104111 // Get finds the first value for key within a declaration that matches the
105112 // alias. Get returns the empty string if no value was found, or if IgnoreErrors
106113 // is false and we could not parse the configuration file. Use GetStrict to
113120 return DefaultUserSettings.Get(alias, key)
114121 }
115122
123 // GetAll retrieves zero or more directives for key for the given alias. GetAll
124 // returns nil if no value was found, or if IgnoreErrors is false and we could
125 // not parse the configuration file. Use GetAllStrict to disambiguate the
126 // latter cases.
127 //
128 // In most cases you want to use Get or GetStrict, which returns a single value.
129 // However, a subset of ssh configuration values (IdentityFile, for example)
130 // allow you to specify multiple directives.
131 //
132 // The match for key is case insensitive.
133 //
134 // GetAll is a wrapper around DefaultUserSettings.GetAll.
135 func GetAll(alias, key string) []string {
136 return DefaultUserSettings.GetAll(alias, key)
137 }
138
116139 // GetStrict finds the first value for key within a declaration that matches the
117140 // alias. If key has a default value and no matching configuration is found, the
118141 // default will be returned. For more information on default values and the way
119142 // patterns are matched, see the manpage for ssh_config.
120143 //
121 // error will be non-nil if and only if a user's configuration file or the
122 // system configuration file could not be parsed, and u.IgnoreErrors is false.
144 // The returned error will be non-nil if and only if a user's configuration file
145 // or the system configuration file could not be parsed, and u.IgnoreErrors is
146 // false.
123147 //
124148 // GetStrict is a wrapper around DefaultUserSettings.GetStrict.
125149 func GetStrict(alias, key string) (string, error) {
126150 return DefaultUserSettings.GetStrict(alias, key)
151 }
152
153 // GetAllStrict retrieves zero or more directives for key for the given alias.
154 //
155 // In most cases you want to use Get or GetStrict, which returns a single value.
156 // However, a subset of ssh configuration values (IdentityFile, for example)
157 // allow you to specify multiple directives.
158 //
159 // The returned error will be non-nil if and only if a user's configuration file
160 // or the system configuration file could not be parsed, and u.IgnoreErrors is
161 // false.
162 //
163 // GetAllStrict is a wrapper around DefaultUserSettings.GetAllStrict.
164 func GetAllStrict(alias, key string) ([]string, error) {
165 return DefaultUserSettings.GetAllStrict(alias, key)
127166 }
128167
129168 // Get finds the first value for key within a declaration that matches the
140179 return val
141180 }
142181
182 // GetAll retrieves zero or more directives for key for the given alias. GetAll
183 // returns nil if no value was found, or if IgnoreErrors is false and we could
184 // not parse the configuration file. Use GetStrict to disambiguate the latter
185 // cases.
186 //
187 // The match for key is case insensitive.
188 func (u *UserSettings) GetAll(alias, key string) []string {
189 val, _ := u.GetAllStrict(alias, key)
190 return val
191 }
192
143193 // GetStrict finds the first value for key within a declaration that matches the
144194 // alias. If key has a default value and no matching configuration is found, the
145195 // default will be returned. For more information on default values and the way
148198 // error will be non-nil if and only if a user's configuration file or the
149199 // system configuration file could not be parsed, and u.IgnoreErrors is false.
150200 func (u *UserSettings) GetStrict(alias, key string) (string, error) {
201 u.doLoadConfigs()
202 //lint:ignore S1002 I prefer it this way
203 if u.onceErr != nil && u.IgnoreErrors == false {
204 return "", u.onceErr
205 }
206 val, err := findVal(u.userConfig, alias, key)
207 if err != nil || val != "" {
208 return val, err
209 }
210 val2, err2 := findVal(u.systemConfig, alias, key)
211 if err2 != nil || val2 != "" {
212 return val2, err2
213 }
214 return Default(key), nil
215 }
216
217 // GetAllStrict retrieves zero or more directives for key for the given alias.
218 // If key has a default value and no matching configuration is found, the
219 // default will be returned. For more information on default values and the way
220 // patterns are matched, see the manpage for ssh_config.
221 //
222 // The returned error will be non-nil if and only if a user's configuration file
223 // or the system configuration file could not be parsed, and u.IgnoreErrors is
224 // false.
225 func (u *UserSettings) GetAllStrict(alias, key string) ([]string, error) {
226 u.doLoadConfigs()
227 //lint:ignore S1002 I prefer it this way
228 if u.onceErr != nil && u.IgnoreErrors == false {
229 return nil, u.onceErr
230 }
231 val, err := findAll(u.userConfig, alias, key)
232 if err != nil || val != nil {
233 return val, err
234 }
235 val2, err2 := findAll(u.systemConfig, alias, key)
236 if err2 != nil || val2 != nil {
237 return val2, err2
238 }
239 // TODO: IdentityFile has multiple default values that we should return.
240 if def := Default(key); def != "" {
241 return []string{def}, nil
242 }
243 return []string{}, nil
244 }
245
246 func (u *UserSettings) doLoadConfigs() {
151247 u.loadConfigs.Do(func() {
152248 // can't parse user file, that's ok.
153249 var filename string
175271 return
176272 }
177273 })
178 //lint:ignore S1002 I prefer it this way
179 if u.onceErr != nil && u.IgnoreErrors == false {
180 return "", u.onceErr
181 }
182 val, err := findVal(u.userConfig, alias, key)
183 if err != nil || val != "" {
184 return val, err
185 }
186 val2, err2 := findVal(u.systemConfig, alias, key)
187 if err2 != nil || val2 != "" {
188 return val2, err2
189 }
190 return Default(key), nil
191274 }
192275
193276 func parseFile(filename string) (*Config, error) {
281364 return "", nil
282365 }
283366
367 // GetAll returns all values in the configuration that match the alias and
368 // contains key, or nil if none are present.
369 func (c *Config) GetAll(alias, key string) ([]string, error) {
370 lowerKey := strings.ToLower(key)
371 all := []string(nil)
372 for _, host := range c.Hosts {
373 if !host.Matches(alias) {
374 continue
375 }
376 for _, node := range host.Nodes {
377 switch t := node.(type) {
378 case *Empty:
379 continue
380 case *KV:
381 // "keys are case insensitive" per the spec
382 lkey := strings.ToLower(t.Key)
383 if lkey == "match" {
384 panic("can't handle Match directives")
385 }
386 if lkey == lowerKey {
387 all = append(all, t.Value)
388 }
389 case *Include:
390 val, _ := t.GetAll(alias, key)
391 if len(val) > 0 {
392 all = append(all, val...)
393 }
394 default:
395 return nil, fmt.Errorf("unknown Node type %v", t)
396 }
397 }
398 }
399
400 return all, nil
401 }
402
284403 // String returns a string representation of the Config file.
285404 func (c Config) String() string {
286405 return marshal(c).String()
610729 return ""
611730 }
612731
732 // GetAll finds all values in the Include statement matching the alias and the
733 // given key.
734 func (inc *Include) GetAll(alias, key string) ([]string, error) {
735 inc.mu.Lock()
736 defer inc.mu.Unlock()
737 var vals []string
738
739 // TODO: we search files in any order which is not correct
740 for i := range inc.matches {
741 cfg := inc.files[inc.matches[i]]
742 if cfg == nil {
743 panic("nil cfg")
744 }
745 val, err := cfg.GetAll(alias, key)
746 if err == nil && len(val) != 0 {
747 // In theory if SupportsMultiple was false for this key we could
748 // stop looking here. But the caller has asked us to find all
749 // instances of the keyword (and could use Get() if they wanted) so
750 // let's keep looking.
751 vals = append(vals, val...)
752 }
753 }
754 return vals, nil
755 }
756
613757 // String prints out a string representation of this Include directive. Note
614758 // included Config files are not printed as part of this representation.
615759 func (inc *Include) String() string {
6666 }
6767 }
6868
69 func TestGetAllWithDefault(t *testing.T) {
70 us := &UserSettings{
71 userConfigFinder: testConfigFinder("testdata/config1"),
72 }
73
74 val, err := us.GetAllStrict("wap", "PasswordAuthentication")
75 if err != nil {
76 t.Fatalf("expected nil err, got %v", err)
77 }
78 if len(val) != 1 || val[0] != "yes" {
79 t.Errorf("expected to get PasswordAuthentication yes, got %q", val)
80 }
81 }
82
83 func TestGetIdentities(t *testing.T) {
84 us := &UserSettings{
85 userConfigFinder: testConfigFinder("testdata/identities"),
86 }
87
88 val, err := us.GetAllStrict("hasidentity", "IdentityFile")
89 if err != nil {
90 t.Errorf("expected nil err, got %v", err)
91 }
92 if len(val) != 1 || val[0] != "file1" {
93 t.Errorf(`expected ["file1"], got %v`, val)
94 }
95
96 val, err = us.GetAllStrict("has2identity", "IdentityFile")
97 if err != nil {
98 t.Errorf("expected nil err, got %v", err)
99 }
100 if len(val) != 2 || val[0] != "f1" || val[1] != "f2" {
101 t.Errorf(`expected [\"f1\", \"f2\"], got %v`, val)
102 }
103
104 val, err = us.GetAllStrict("randomhost", "IdentityFile")
105 if err != nil {
106 t.Errorf("expected nil err, got %v", err)
107 }
108 if len(val) != len(defaultProtocol2Identities) {
109 // TODO: return the right values here.
110 log.Printf("expected defaults, got %v", val)
111 } else {
112 for i, v := range defaultProtocol2Identities {
113 if val[i] != v {
114 t.Errorf("invalid %d in val, expected %s got %s", i, v, val[i])
115 }
116 }
117 }
118
119 val, err = us.GetAllStrict("protocol1", "IdentityFile")
120 if err != nil {
121 t.Errorf("expected nil err, got %v", err)
122 }
123 if len(val) != 1 || val[0] != "~/.ssh/identity" {
124 t.Errorf("expected [\"~/.ssh/identity\"], got %v", val)
125 }
126 }
127
69128 func TestGetInvalidPort(t *testing.T) {
70129 us := &UserSettings{
71130 userConfigFinder: testConfigFinder("testdata/invalid-port"),
93152 t.Fatalf("expected nil err, got %v", err)
94153 }
95154 if val != "" {
155 t.Errorf("expected to get CanonicalDomains '', got %q", val)
156 }
157 }
158
159 func TestGetAllNotFoundNoDefault(t *testing.T) {
160 us := &UserSettings{
161 userConfigFinder: testConfigFinder("testdata/config1"),
162 }
163
164 val, err := us.GetAllStrict("wap", "CanonicalDomains")
165 if err != nil {
166 t.Fatalf("expected nil err, got %v", err)
167 }
168 if len(val) != 0 {
96169 t.Errorf("expected to get CanonicalDomains '', got %q", val)
97170 }
98171 }
0 # Config file with dos line endings
1 Host wap
2 HostName wap.example.org
3 Port 22
4 User root
5 KexAlgorithms diffie-hellman-group1-sha1
6
7 Host wap2
8 HostName 8.8.8.8
9 User google
0 # Config file with dos line endings
1 Host wap
2 HostName wap.example.org
3 Port 22
4 User root
5 KexAlgorithms diffie-hellman-group1-sha1
6
7 Host wap2
8 HostName 8.8.8.8
9 User google
0
1 Host hasidentity
2 IdentityFile file1
3
4 Host has2identity
5 IdentityFile f1
6 IdentityFile f2
7
8 Host protocol1
9 Protocol 1
10
159159 strings.ToLower("VisualHostKey"): "no",
160160 strings.ToLower("XAuthLocation"): "/usr/X11R6/bin/xauth",
161161 }
162
163 // these identities are used for SSH protocol 2
164 var defaultProtocol2Identities = []string{
165 "~/.ssh/id_dsa",
166 "~/.ssh/id_ecdsa",
167 "~/.ssh/id_ed25519",
168 "~/.ssh/id_rsa",
169 }
170
171 // these directives support multiple items that can be collected
172 // across multiple files
173 var pluralDirectives = map[string]bool{
174 "CertificateFile": true,
175 "IdentityFile": true,
176 "DynamicForward": true,
177 "RemoteForward": true,
178 "SendEnv": true,
179 "SetEnv": true,
180 }
181
182 // SupportsMultiple reports whether a directive can be specified multiple times.
183 func SupportsMultiple(key string) bool {
184 return pluralDirectives[strings.ToLower(key)]
185 }