Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(54)

Delta Between Two Patch Sets: ssh/session.go

Issue 14225043: code review 14225043: go.crypto/ssh: reimplement SSH connection protocol modu... (Closed)
Left Patch Set: diff -r 2cd6b3b93cdb https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Right Patch Set: diff -r cd1eea1eb828 https://code.google.com/p/go.crypto Created 10 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « ssh/server_terminal.go ('k') | ssh/session_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package ssh 5 package ssh
6 6
7 // Session implements an interactive session described in 7 // Session implements an interactive session described in
8 // "RFC 4254, section 6". 8 // "RFC 4254, section 6".
9 9
10 import ( 10 import (
11 "bytes" 11 "bytes"
12 "errors" 12 "errors"
13 "fmt" 13 "fmt"
14 "io" 14 "io"
15 "io/ioutil" 15 "io/ioutil"
16 "log"
17 "sync" 16 "sync"
18 ) 17 )
19
20 var _ = log.Println
21 18
22 type Signal string 19 type Signal string
23 20
24 // POSIX signals as listed in RFC 4254 Section 6.10. 21 // POSIX signals as listed in RFC 4254 Section 6.10.
25 const ( 22 const (
26 SIGABRT Signal = "ABRT" 23 SIGABRT Signal = "ABRT"
27 SIGALRM Signal = "ALRM" 24 SIGALRM Signal = "ALRM"
28 SIGFPE Signal = "FPE" 25 SIGFPE Signal = "FPE"
29 SIGHUP Signal = "HUP" 26 SIGHUP Signal = "HUP"
30 SIGILL Signal = "ILL" 27 SIGILL Signal = "ILL"
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 // output and error. 122 // output and error.
126 // 123 //
127 // If either is nil, Run connects the corresponding file 124 // If either is nil, Run connects the corresponding file
128 // descriptor to an instance of ioutil.Discard. There is a 125 // descriptor to an instance of ioutil.Discard. There is a
129 // fixed amount of buffering that is shared for the two streams. 126 // fixed amount of buffering that is shared for the two streams.
130 // If either blocks it may eventually cause the remote 127 // If either blocks it may eventually cause the remote
131 // command to block. 128 // command to block.
132 Stdout io.Writer 129 Stdout io.Writer
133 Stderr io.Writer 130 Stderr io.Writer
134 131
135 » Channel // the channel backing this session 132 » ch *channel // the channel backing this session
136 133
137 started bool // true once Start, Run or Shell is invoked. 134 started bool // true once Start, Run or Shell is invoked.
138 copyFuncs []func() error 135 copyFuncs []func() error
139 errors chan error // one send per copyFunc 136 errors chan error // one send per copyFunc
140 137
141 // true if pipe method is active 138 // true if pipe method is active
142 stdinpipe, stdoutpipe, stderrpipe bool 139 stdinpipe, stdoutpipe, stderrpipe bool
140 }
141
142 func (s *Session) Close() error {
143 return s.ch.Close()
143 } 144 }
144 145
145 // RFC 4254 Section 6.4. 146 // RFC 4254 Section 6.4.
146 type setenvRequest struct { 147 type setenvRequest struct {
147 Name string 148 Name string
148 Value string 149 Value string
149 } 150 }
150 151
151 // Setenv sets an environment variable that will be applied to any 152 // Setenv sets an environment variable that will be applied to any
152 // command executed by Shell or Run. 153 // command executed by Shell or Run.
153 func (s *Session) Setenv(name, value string) error { 154 func (s *Session) Setenv(name, value string) error {
154 msg := setenvRequest{ 155 msg := setenvRequest{
155 Name: name, 156 Name: name,
156 Value: value, 157 Value: value,
157 } 158 }
158 » ok, err := s.Channel.SendRequest("env", true, marshal(0, msg)) 159 » ok, err := s.ch.SendRequest("env", true, marshal(0, msg))
159 if err == nil && !ok { 160 if err == nil && !ok {
160 err = errors.New("ssh: setenv failed") 161 err = errors.New("ssh: setenv failed")
161 } 162 }
162 return err 163 return err
163 } 164 }
164 165
165 // RFC 4254 Section 6.2. 166 // RFC 4254 Section 6.2.
166 type ptyRequestMsg struct { 167 type ptyRequestMsg struct {
167 Term string 168 Term string
168 Columns uint32 169 Columns uint32
(...skipping 12 matching lines...) Expand all
181 } 182 }
182 tm = append(tm, tty_OP_END) 183 tm = append(tm, tty_OP_END)
183 req := ptyRequestMsg{ 184 req := ptyRequestMsg{
184 Term: term, 185 Term: term,
185 Columns: uint32(w), 186 Columns: uint32(w),
186 Rows: uint32(h), 187 Rows: uint32(h),
187 Width: uint32(w * 8), 188 Width: uint32(w * 8),
188 Height: uint32(h * 8), 189 Height: uint32(h * 8),
189 Modelist: string(tm), 190 Modelist: string(tm),
190 } 191 }
191 » ok, err := s.SendRequest("pty-req", true, marshal(0, req)) 192 » ok, err := s.ch.SendRequest("pty-req", true, marshal(0, req))
192 if err == nil && !ok { 193 if err == nil && !ok {
193 err = errors.New("ssh: pty-req failed") 194 err = errors.New("ssh: pty-req failed")
194 } 195 }
195 return err 196 return err
196 } 197 }
197 198
198 // RFC 4254 Section 6.5. 199 // RFC 4254 Section 6.5.
199 type subsystemRequestMsg struct { 200 type subsystemRequestMsg struct {
200 Subsystem string 201 Subsystem string
201 } 202 }
202 203
203 // RequestSubsystem requests the association of a subsystem with the session on the remote host. 204 // RequestSubsystem requests the association of a subsystem with the session on the remote host.
204 // A subsystem is a predefined command that runs in the background when the ssh session is initiated 205 // A subsystem is a predefined command that runs in the background when the ssh session is initiated
205 func (s *Session) RequestSubsystem(subsystem string) error { 206 func (s *Session) RequestSubsystem(subsystem string) error {
206 msg := subsystemRequestMsg{ 207 msg := subsystemRequestMsg{
207 Subsystem: subsystem, 208 Subsystem: subsystem,
208 } 209 }
209 » ok, err := s.SendRequest("subsystem", true, marshal(0, msg)) 210 » ok, err := s.ch.SendRequest("subsystem", true, marshal(0, msg))
210 if err == nil && !ok { 211 if err == nil && !ok {
211 err = errors.New("ssh: subsystem request failed") 212 err = errors.New("ssh: subsystem request failed")
212 } 213 }
213 return err 214 return err
214 } 215 }
215 216
216 // RFC 4254 Section 6.9. 217 // RFC 4254 Section 6.9.
217 type signalMsg struct { 218 type signalMsg struct {
218 Signal string 219 Signal string
219 } 220 }
220 221
221 // Signal sends the given signal to the remote process. 222 // Signal sends the given signal to the remote process.
222 // sig is one of the SIG* constants. 223 // sig is one of the SIG* constants.
223 func (s *Session) Signal(sig Signal) error { 224 func (s *Session) Signal(sig Signal) error {
224 msg := signalMsg{ 225 msg := signalMsg{
225 Signal: string(sig), 226 Signal: string(sig),
226 } 227 }
227 228
228 » _, err := s.SendRequest("signal", false, marshal(0, msg)) 229 » _, err := s.ch.SendRequest("signal", false, marshal(0, msg))
229 return err 230 return err
230 } 231 }
231 232
232 // RFC 4254 Section 6.5. 233 // RFC 4254 Section 6.5.
233 type execMsg struct { 234 type execMsg struct {
234 Command string 235 Command string
235 } 236 }
236 237
237 // Start runs cmd on the remote host. Typically, the remote 238 // Start runs cmd on the remote host. Typically, the remote
238 // server passes cmd to the shell for interpretation. 239 // server passes cmd to the shell for interpretation.
239 // A Session only accepts one call to Run, Start or Shell. 240 // A Session only accepts one call to Run, Start or Shell.
240 func (s *Session) Start(cmd string) error { 241 func (s *Session) Start(cmd string) error {
241 if s.started { 242 if s.started {
242 return errors.New("ssh: session already started") 243 return errors.New("ssh: session already started")
243 } 244 }
244 req := execMsg{ 245 req := execMsg{
245 Command: cmd, 246 Command: cmd,
246 } 247 }
247 248
248 » ok, err := s.SendRequest("exec", true, marshal(0, req)) 249 » ok, err := s.ch.SendRequest("exec", true, marshal(0, req))
249 if err == nil && !ok { 250 if err == nil && !ok {
250 err = fmt.Errorf("ssh: command %v failed", cmd) 251 err = fmt.Errorf("ssh: command %v failed", cmd)
251 } 252 }
252 if err != nil { 253 if err != nil {
253 return err 254 return err
254 } 255 }
255 return s.start() 256 return s.start()
256 } 257 }
257 258
258 // Run runs cmd on the remote host. Typically, the remote 259 // Run runs cmd on the remote host. Typically, the remote
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
313 return b.b.Bytes(), err 314 return b.b.Bytes(), err
314 } 315 }
315 316
316 // Shell starts a login shell on the remote host. A Session only 317 // Shell starts a login shell on the remote host. A Session only
317 // accepts one call to Run, Start, Shell, Output, or CombinedOutput. 318 // accepts one call to Run, Start, Shell, Output, or CombinedOutput.
318 func (s *Session) Shell() error { 319 func (s *Session) Shell() error {
319 if s.started { 320 if s.started {
320 return errors.New("ssh: session already started") 321 return errors.New("ssh: session already started")
321 } 322 }
322 323
323 » ok, err := s.SendRequest("shell", true, nil) 324 » ok, err := s.ch.SendRequest("shell", true, nil)
324 if err == nil && !ok { 325 if err == nil && !ok {
325 return fmt.Errorf("ssh: cound not execute") 326 return fmt.Errorf("ssh: cound not execute")
326 } 327 }
327 if err != nil { 328 if err != nil {
328 return err 329 return err
329 } 330 }
330 return s.start() 331 return s.start()
331 } 332 }
332 333
333 func (s *Session) start() error { 334 func (s *Session) start() error {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 if waitErr != nil { 372 if waitErr != nil {
372 return waitErr 373 return waitErr
373 } 374 }
374 return copyError 375 return copyError
375 } 376 }
376 377
377 func (s *Session) wait() error { 378 func (s *Session) wait() error {
378 wm := Waitmsg{status: -1} 379 wm := Waitmsg{status: -1}
379 380
380 // Wait for msg channel to be closed before returning. 381 // Wait for msg channel to be closed before returning.
381 » for msg := range s.ReceivedRequests() { 382 » for msg := range s.ch.incomingRequests {
382 switch msg.Request { 383 switch msg.Request {
383 case "exit-status": 384 case "exit-status":
384 d := msg.Payload 385 d := msg.Payload
385 wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3]) 386 wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
386 case "exit-signal": 387 case "exit-signal":
387 signal, rest, ok := parseString(msg.Payload) 388 signal, rest, ok := parseString(msg.Payload)
388 if !ok { 389 if !ok {
389 return fmt.Errorf("wait: could not parse request data: %v", msg.Payload) 390 return fmt.Errorf("wait: could not parse request data: %v", msg.Payload)
390 } 391 }
391 wm.signal = safeString(string(signal)) 392 wm.signal = safeString(string(signal))
(...skipping 12 matching lines...) Expand all
404 405
405 lang, _, ok := parseString(rest) 406 lang, _, ok := parseString(rest)
406 if !ok { 407 if !ok {
407 return fmt.Errorf("wait: could not parse request data: %v", msg.Payload) 408 return fmt.Errorf("wait: could not parse request data: %v", msg.Payload)
408 } 409 }
409 wm.lang = safeString(string(lang)) 410 wm.lang = safeString(string(lang))
410 default: 411 default:
411 // This handles keepalives and matches 412 // This handles keepalives and matches
412 // OpenSSH's behaviour. 413 // OpenSSH's behaviour.
413 if msg.WantReply { 414 if msg.WantReply {
414 » » » » s.AckRequest(false) 415 » » » » s.ch.AckRequest(false)
415 } 416 }
416 } 417 }
417 } 418 }
418 if wm.status == 0 { 419 if wm.status == 0 {
419 return nil 420 return nil
420 } 421 }
421 if wm.status == -1 { 422 if wm.status == -1 {
422 // exit-status was never sent from server 423 // exit-status was never sent from server
423 if wm.signal == "" { 424 if wm.signal == "" {
424 return errors.New("wait: remote command exited without e xit status or exit signal") 425 return errors.New("wait: remote command exited without e xit status or exit signal")
425 } 426 }
426 wm.status = 128 427 wm.status = 128
427 if _, ok := signals[Signal(wm.signal)]; ok { 428 if _, ok := signals[Signal(wm.signal)]; ok {
428 wm.status += signals[Signal(wm.signal)] 429 wm.status += signals[Signal(wm.signal)]
429 } 430 }
430 } 431 }
431 return &ExitError{wm} 432 return &ExitError{wm}
432 } 433 }
433 434
434 func (s *Session) stdin() { 435 func (s *Session) stdin() {
435 if s.stdinpipe { 436 if s.stdinpipe {
436 return 437 return
437 } 438 }
438 if s.Stdin == nil { 439 if s.Stdin == nil {
439 s.Stdin = new(bytes.Buffer) 440 s.Stdin = new(bytes.Buffer)
440 } 441 }
441 s.copyFuncs = append(s.copyFuncs, func() error { 442 s.copyFuncs = append(s.copyFuncs, func() error {
442 » » _, err := io.Copy(s.Channel, s.Stdin) 443 » » _, err := io.Copy(s.ch, s.Stdin)
443 » » if err1 := s.Channel.CloseWrite(); err == nil && err1 != io.EOF { 444 » » if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF {
444 err = err1 445 err = err1
445 } 446 }
446 return err 447 return err
447 }) 448 })
448 } 449 }
449 450
450 func (s *Session) stdout() { 451 func (s *Session) stdout() {
451 if s.stdoutpipe { 452 if s.stdoutpipe {
452 return 453 return
453 } 454 }
454 if s.Stdout == nil { 455 if s.Stdout == nil {
455 s.Stdout = ioutil.Discard 456 s.Stdout = ioutil.Discard
456 } 457 }
457 s.copyFuncs = append(s.copyFuncs, func() error { 458 s.copyFuncs = append(s.copyFuncs, func() error {
458 » » _, err := io.Copy(s.Stdout, s.Channel) 459 » » _, err := io.Copy(s.Stdout, s.ch)
459 return err 460 return err
460 }) 461 })
461 } 462 }
462 463
463 func (s *Session) stderr() { 464 func (s *Session) stderr() {
464 if s.stderrpipe { 465 if s.stderrpipe {
465 return 466 return
466 } 467 }
467 if s.Stderr == nil { 468 if s.Stderr == nil {
468 s.Stderr = ioutil.Discard 469 s.Stderr = ioutil.Discard
469 } 470 }
470 s.copyFuncs = append(s.copyFuncs, func() error { 471 s.copyFuncs = append(s.copyFuncs, func() error {
471 » » _, err := io.Copy(s.Stderr, s.Channel.Stderr()) 472 » » _, err := io.Copy(s.Stderr, s.ch.Extended(1))
472 return err 473 return err
473 }) 474 })
475 }
476
477 // sessionStdin reroutes Close to CloseWrite.
478 type sessionStdin struct {
479 io.Writer
480 ch *channel
481 }
482
483 func (s *sessionStdin) Close() error {
484 return s.ch.CloseWrite()
474 } 485 }
475 486
476 // StdinPipe returns a pipe that will be connected to the 487 // StdinPipe returns a pipe that will be connected to the
477 // remote command's standard input when the command starts. 488 // remote command's standard input when the command starts.
478 func (s *Session) StdinPipe() (io.WriteCloser, error) { 489 func (s *Session) StdinPipe() (io.WriteCloser, error) {
479 if s.Stdin != nil { 490 if s.Stdin != nil {
480 return nil, errors.New("ssh: Stdin already set") 491 return nil, errors.New("ssh: Stdin already set")
481 } 492 }
482 if s.started { 493 if s.started {
483 return nil, errors.New("ssh: StdinPipe after process started") 494 return nil, errors.New("ssh: StdinPipe after process started")
484 } 495 }
485 s.stdinpipe = true 496 s.stdinpipe = true
486 » return s.Channel, nil 497 » return &sessionStdin{s.ch, s.ch}, nil
487 } 498 }
488 499
489 // StdoutPipe returns a pipe that will be connected to the 500 // StdoutPipe returns a pipe that will be connected to the
490 // remote command's standard output when the command starts. 501 // remote command's standard output when the command starts.
491 // There is a fixed amount of buffering that is shared between 502 // There is a fixed amount of buffering that is shared between
492 // stdout and stderr streams. If the StdoutPipe reader is 503 // stdout and stderr streams. If the StdoutPipe reader is
493 // not serviced fast enought it may eventually cause the 504 // not serviced fast enough it may eventually cause the
494 // remote command to block. 505 // remote command to block.
495 func (s *Session) StdoutPipe() (io.Reader, error) { 506 func (s *Session) StdoutPipe() (io.Reader, error) {
496 if s.Stdout != nil { 507 if s.Stdout != nil {
497 return nil, errors.New("ssh: Stdout already set") 508 return nil, errors.New("ssh: Stdout already set")
498 } 509 }
499 if s.started { 510 if s.started {
500 return nil, errors.New("ssh: StdoutPipe after process started") 511 return nil, errors.New("ssh: StdoutPipe after process started")
501 } 512 }
502 s.stdoutpipe = true 513 s.stdoutpipe = true
503 » return s.Channel, nil 514 » return s.ch, nil
504 } 515 }
505 516
506 // StderrPipe returns a pipe that will be connected to the 517 // StderrPipe returns a pipe that will be connected to the
507 // remote command's standard error when the command starts. 518 // remote command's standard error when the command starts.
508 // There is a fixed amount of buffering that is shared between 519 // There is a fixed amount of buffering that is shared between
509 // stdout and stderr streams. If the StderrPipe reader is 520 // stdout and stderr streams. If the StderrPipe reader is
510 // not serviced fast enought it may eventually cause the 521 // not serviced fast enough it may eventually cause the
511 // remote command to block. 522 // remote command to block.
512 func (s *Session) StderrPipe() (io.Reader, error) { 523 func (s *Session) StderrPipe() (io.Reader, error) {
513 if s.Stderr != nil { 524 if s.Stderr != nil {
514 return nil, errors.New("ssh: Stderr already set") 525 return nil, errors.New("ssh: Stderr already set")
515 } 526 }
516 if s.started { 527 if s.started {
517 return nil, errors.New("ssh: StderrPipe after process started") 528 return nil, errors.New("ssh: StderrPipe after process started")
518 } 529 }
519 s.stderrpipe = true 530 s.stderrpipe = true
520 » return s.Channel.Stderr(), nil 531 » return s.ch.Extended(1), nil
521 } 532 }
522 533
523 // NewSession returns a new interactive session on the remote host. 534 // NewSession returns a new interactive session on the remote host.
524 func (c *ClientConn) NewSession() (*Session, error) { 535 func (c *ClientConn) NewSession() (*Session, error) {
525 ch, err := c.mux.OpenChannel("session", nil) 536 ch, err := c.mux.OpenChannel("session", nil)
526 if err != nil { 537 if err != nil {
527 return nil, err 538 return nil, err
528 } 539 }
529 return &Session{ 540 return &Session{
530 » » Channel: ch, 541 » » ch: ch,
531 }, nil 542 }, nil
532 } 543 }
533 544
534 // An ExitError reports unsuccessful completion of a remote command. 545 // An ExitError reports unsuccessful completion of a remote command.
535 type ExitError struct { 546 type ExitError struct {
536 Waitmsg 547 Waitmsg
537 } 548 }
538 549
539 func (e *ExitError) Error() string { 550 func (e *ExitError) Error() string {
540 return e.Waitmsg.String() 551 return e.Waitmsg.String()
(...skipping 25 matching lines...) Expand all
566 } 577 }
567 578
568 // Lang returns the language tag. See RFC 3066 579 // Lang returns the language tag. See RFC 3066
569 func (w Waitmsg) Lang() string { 580 func (w Waitmsg) Lang() string {
570 return w.lang 581 return w.lang
571 } 582 }
572 583
573 func (w Waitmsg) String() string { 584 func (w Waitmsg) String() string {
574 return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.sta tus, w.msg, w.signal) 585 return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.sta tus, w.msg, w.signal)
575 } 586 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b