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

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 213a06a7ce81 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 (
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 // output and error. 122 // output and error.
123 // 123 //
124 // If either is nil, Run connects the corresponding file 124 // If either is nil, Run connects the corresponding file
125 // descriptor to an instance of ioutil.Discard. There is a 125 // descriptor to an instance of ioutil.Discard. There is a
126 // fixed amount of buffering that is shared for the two streams. 126 // fixed amount of buffering that is shared for the two streams.
127 // If either blocks it may eventually cause the remote 127 // If either blocks it may eventually cause the remote
128 // command to block. 128 // command to block.
129 Stdout io.Writer 129 Stdout io.Writer
130 Stderr io.Writer 130 Stderr io.Writer
131 131
132 » Channel // the channel backing this session 132 » ch *channel // the channel backing this session
133 133
134 started bool // true once Start, Run or Shell is invoked. 134 started bool // true once Start, Run or Shell is invoked.
135 copyFuncs []func() error 135 copyFuncs []func() error
136 errors chan error // one send per copyFunc 136 errors chan error // one send per copyFunc
137 137
138 // true if pipe method is active 138 // true if pipe method is active
139 stdinpipe, stdoutpipe, stderrpipe bool 139 stdinpipe, stdoutpipe, stderrpipe bool
140 }
141
142 func (s *Session) Close() error {
143 return s.ch.Close()
140 } 144 }
141 145
142 // RFC 4254 Section 6.4. 146 // RFC 4254 Section 6.4.
143 type setenvRequest struct { 147 type setenvRequest struct {
144 Name string 148 Name string
145 Value string 149 Value string
146 } 150 }
147 151
148 // Setenv sets an environment variable that will be applied to any 152 // Setenv sets an environment variable that will be applied to any
149 // command executed by Shell or Run. 153 // command executed by Shell or Run.
150 func (s *Session) Setenv(name, value string) error { 154 func (s *Session) Setenv(name, value string) error {
151 msg := setenvRequest{ 155 msg := setenvRequest{
152 Name: name, 156 Name: name,
153 Value: value, 157 Value: value,
154 } 158 }
155 » ok, err := s.Channel.SendRequest("env", true, marshal(0, msg)) 159 » ok, err := s.ch.SendRequest("env", true, marshal(0, msg))
156 if err == nil && !ok { 160 if err == nil && !ok {
157 err = errors.New("ssh: setenv failed") 161 err = errors.New("ssh: setenv failed")
158 } 162 }
159 return err 163 return err
160 } 164 }
161 165
162 // RFC 4254 Section 6.2. 166 // RFC 4254 Section 6.2.
163 type ptyRequestMsg struct { 167 type ptyRequestMsg struct {
164 Term string 168 Term string
165 Columns uint32 169 Columns uint32
(...skipping 12 matching lines...) Expand all
178 } 182 }
179 tm = append(tm, tty_OP_END) 183 tm = append(tm, tty_OP_END)
180 req := ptyRequestMsg{ 184 req := ptyRequestMsg{
181 Term: term, 185 Term: term,
182 Columns: uint32(w), 186 Columns: uint32(w),
183 Rows: uint32(h), 187 Rows: uint32(h),
184 Width: uint32(w * 8), 188 Width: uint32(w * 8),
185 Height: uint32(h * 8), 189 Height: uint32(h * 8),
186 Modelist: string(tm), 190 Modelist: string(tm),
187 } 191 }
188 » ok, err := s.SendRequest("pty-req", true, marshal(0, req)) 192 » ok, err := s.ch.SendRequest("pty-req", true, marshal(0, req))
189 if err == nil && !ok { 193 if err == nil && !ok {
190 err = errors.New("ssh: pty-req failed") 194 err = errors.New("ssh: pty-req failed")
191 } 195 }
192 return err 196 return err
193 } 197 }
194 198
195 // RFC 4254 Section 6.5. 199 // RFC 4254 Section 6.5.
196 type subsystemRequestMsg struct { 200 type subsystemRequestMsg struct {
197 Subsystem string 201 Subsystem string
198 } 202 }
199 203
200 // 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.
201 // 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
202 func (s *Session) RequestSubsystem(subsystem string) error { 206 func (s *Session) RequestSubsystem(subsystem string) error {
203 msg := subsystemRequestMsg{ 207 msg := subsystemRequestMsg{
204 Subsystem: subsystem, 208 Subsystem: subsystem,
205 } 209 }
206 » ok, err := s.SendRequest("subsystem", true, marshal(0, msg)) 210 » ok, err := s.ch.SendRequest("subsystem", true, marshal(0, msg))
207 if err == nil && !ok { 211 if err == nil && !ok {
208 err = errors.New("ssh: subsystem request failed") 212 err = errors.New("ssh: subsystem request failed")
209 } 213 }
210 return err 214 return err
211 } 215 }
212 216
213 // RFC 4254 Section 6.9. 217 // RFC 4254 Section 6.9.
214 type signalMsg struct { 218 type signalMsg struct {
215 Signal string 219 Signal string
216 } 220 }
217 221
218 // Signal sends the given signal to the remote process. 222 // Signal sends the given signal to the remote process.
219 // sig is one of the SIG* constants. 223 // sig is one of the SIG* constants.
220 func (s *Session) Signal(sig Signal) error { 224 func (s *Session) Signal(sig Signal) error {
221 msg := signalMsg{ 225 msg := signalMsg{
222 Signal: string(sig), 226 Signal: string(sig),
223 } 227 }
224 228
225 » _, err := s.SendRequest("signal", false, marshal(0, msg)) 229 » _, err := s.ch.SendRequest("signal", false, marshal(0, msg))
226 return err 230 return err
227 } 231 }
228 232
229 // RFC 4254 Section 6.5. 233 // RFC 4254 Section 6.5.
230 type execMsg struct { 234 type execMsg struct {
231 Command string 235 Command string
232 } 236 }
233 237
234 // Start runs cmd on the remote host. Typically, the remote 238 // Start runs cmd on the remote host. Typically, the remote
235 // server passes cmd to the shell for interpretation. 239 // server passes cmd to the shell for interpretation.
236 // A Session only accepts one call to Run, Start or Shell. 240 // A Session only accepts one call to Run, Start or Shell.
237 func (s *Session) Start(cmd string) error { 241 func (s *Session) Start(cmd string) error {
238 if s.started { 242 if s.started {
239 return errors.New("ssh: session already started") 243 return errors.New("ssh: session already started")
240 } 244 }
241 req := execMsg{ 245 req := execMsg{
242 Command: cmd, 246 Command: cmd,
243 } 247 }
244 248
245 » ok, err := s.SendRequest("exec", true, marshal(0, req)) 249 » ok, err := s.ch.SendRequest("exec", true, marshal(0, req))
246 if err == nil && !ok { 250 if err == nil && !ok {
247 err = fmt.Errorf("ssh: command %v failed", cmd) 251 err = fmt.Errorf("ssh: command %v failed", cmd)
248 } 252 }
249 if err != nil { 253 if err != nil {
250 return err 254 return err
251 } 255 }
252 return s.start() 256 return s.start()
253 } 257 }
254 258
255 // 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
310 return b.b.Bytes(), err 314 return b.b.Bytes(), err
311 } 315 }
312 316
313 // 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
314 // accepts one call to Run, Start, Shell, Output, or CombinedOutput. 318 // accepts one call to Run, Start, Shell, Output, or CombinedOutput.
315 func (s *Session) Shell() error { 319 func (s *Session) Shell() error {
316 if s.started { 320 if s.started {
317 return errors.New("ssh: session already started") 321 return errors.New("ssh: session already started")
318 } 322 }
319 323
320 » ok, err := s.SendRequest("shell", true, nil) 324 » ok, err := s.ch.SendRequest("shell", true, nil)
321 if err == nil && !ok { 325 if err == nil && !ok {
322 return fmt.Errorf("ssh: cound not execute") 326 return fmt.Errorf("ssh: cound not execute")
323 } 327 }
324 if err != nil { 328 if err != nil {
325 return err 329 return err
326 } 330 }
327 return s.start() 331 return s.start()
328 } 332 }
329 333
330 func (s *Session) start() error { 334 func (s *Session) start() error {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 if waitErr != nil { 372 if waitErr != nil {
369 return waitErr 373 return waitErr
370 } 374 }
371 return copyError 375 return copyError
372 } 376 }
373 377
374 func (s *Session) wait() error { 378 func (s *Session) wait() error {
375 wm := Waitmsg{status: -1} 379 wm := Waitmsg{status: -1}
376 380
377 // Wait for msg channel to be closed before returning. 381 // Wait for msg channel to be closed before returning.
378 » for msg := range s.IncomingRequests() { 382 » for msg := range s.ch.incomingRequests {
379 switch msg.Request { 383 switch msg.Request {
380 case "exit-status": 384 case "exit-status":
381 d := msg.Payload 385 d := msg.Payload
382 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])
383 case "exit-signal": 387 case "exit-signal":
384 signal, rest, ok := parseString(msg.Payload) 388 signal, rest, ok := parseString(msg.Payload)
385 if !ok { 389 if !ok {
386 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)
387 } 391 }
388 wm.signal = safeString(string(signal)) 392 wm.signal = safeString(string(signal))
(...skipping 12 matching lines...) Expand all
401 405
402 lang, _, ok := parseString(rest) 406 lang, _, ok := parseString(rest)
403 if !ok { 407 if !ok {
404 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)
405 } 409 }
406 wm.lang = safeString(string(lang)) 410 wm.lang = safeString(string(lang))
407 default: 411 default:
408 // This handles keepalives and matches 412 // This handles keepalives and matches
409 // OpenSSH's behaviour. 413 // OpenSSH's behaviour.
410 if msg.WantReply { 414 if msg.WantReply {
411 » » » » s.AckRequest(false) 415 » » » » s.ch.AckRequest(false)
412 } 416 }
413 } 417 }
414 } 418 }
415 if wm.status == 0 { 419 if wm.status == 0 {
416 return nil 420 return nil
417 } 421 }
418 if wm.status == -1 { 422 if wm.status == -1 {
419 // exit-status was never sent from server 423 // exit-status was never sent from server
420 if wm.signal == "" { 424 if wm.signal == "" {
421 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")
422 } 426 }
423 wm.status = 128 427 wm.status = 128
424 if _, ok := signals[Signal(wm.signal)]; ok { 428 if _, ok := signals[Signal(wm.signal)]; ok {
425 wm.status += signals[Signal(wm.signal)] 429 wm.status += signals[Signal(wm.signal)]
426 } 430 }
427 } 431 }
428 return &ExitError{wm} 432 return &ExitError{wm}
429 } 433 }
430 434
431 func (s *Session) stdin() { 435 func (s *Session) stdin() {
432 if s.stdinpipe { 436 if s.stdinpipe {
433 return 437 return
434 } 438 }
435 if s.Stdin == nil { 439 if s.Stdin == nil {
436 s.Stdin = new(bytes.Buffer) 440 s.Stdin = new(bytes.Buffer)
437 } 441 }
438 s.copyFuncs = append(s.copyFuncs, func() error { 442 s.copyFuncs = append(s.copyFuncs, func() error {
439 » » _, err := io.Copy(s.Channel, s.Stdin) 443 » » _, err := io.Copy(s.ch, s.Stdin)
440 » » if err1 := s.Channel.CloseWrite(); err == nil && err1 != io.EOF { 444 » » if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF {
441 err = err1 445 err = err1
442 } 446 }
443 return err 447 return err
444 }) 448 })
445 } 449 }
446 450
447 func (s *Session) stdout() { 451 func (s *Session) stdout() {
448 if s.stdoutpipe { 452 if s.stdoutpipe {
449 return 453 return
450 } 454 }
451 if s.Stdout == nil { 455 if s.Stdout == nil {
452 s.Stdout = ioutil.Discard 456 s.Stdout = ioutil.Discard
453 } 457 }
454 s.copyFuncs = append(s.copyFuncs, func() error { 458 s.copyFuncs = append(s.copyFuncs, func() error {
455 » » _, err := io.Copy(s.Stdout, s.Channel) 459 » » _, err := io.Copy(s.Stdout, s.ch)
456 return err 460 return err
457 }) 461 })
458 } 462 }
459 463
460 func (s *Session) stderr() { 464 func (s *Session) stderr() {
461 if s.stderrpipe { 465 if s.stderrpipe {
462 return 466 return
463 } 467 }
464 if s.Stderr == nil { 468 if s.Stderr == nil {
465 s.Stderr = ioutil.Discard 469 s.Stderr = ioutil.Discard
466 } 470 }
467 s.copyFuncs = append(s.copyFuncs, func() error { 471 s.copyFuncs = append(s.copyFuncs, func() error {
468 » » _, err := io.Copy(s.Stderr, s.Channel.Stderr()) 472 » » _, err := io.Copy(s.Stderr, s.ch.Extended(1))
469 return err 473 return err
470 }) 474 })
471 } 475 }
472 476
473 // sessionStdin reroutes Close to CloseWrite. 477 // sessionStdin reroutes Close to CloseWrite.
474 type sessionStdin struct { 478 type sessionStdin struct {
475 » Channel 479 » io.Writer
480 » ch *channel
476 } 481 }
477 482
478 func (s *sessionStdin) Close() error { 483 func (s *sessionStdin) Close() error {
479 » return s.CloseWrite() 484 » return s.ch.CloseWrite()
480 } 485 }
481 486
482 // StdinPipe returns a pipe that will be connected to the 487 // StdinPipe returns a pipe that will be connected to the
483 // remote command's standard input when the command starts. 488 // remote command's standard input when the command starts.
484 func (s *Session) StdinPipe() (io.WriteCloser, error) { 489 func (s *Session) StdinPipe() (io.WriteCloser, error) {
485 if s.Stdin != nil { 490 if s.Stdin != nil {
486 return nil, errors.New("ssh: Stdin already set") 491 return nil, errors.New("ssh: Stdin already set")
487 } 492 }
488 if s.started { 493 if s.started {
489 return nil, errors.New("ssh: StdinPipe after process started") 494 return nil, errors.New("ssh: StdinPipe after process started")
490 } 495 }
491 s.stdinpipe = true 496 s.stdinpipe = true
492 » return &sessionStdin{s.Channel}, nil 497 » return &sessionStdin{s.ch, s.ch}, nil
493 } 498 }
494 499
495 // StdoutPipe returns a pipe that will be connected to the 500 // StdoutPipe returns a pipe that will be connected to the
496 // remote command's standard output when the command starts. 501 // remote command's standard output when the command starts.
497 // There is a fixed amount of buffering that is shared between 502 // There is a fixed amount of buffering that is shared between
498 // stdout and stderr streams. If the StdoutPipe reader is 503 // stdout and stderr streams. If the StdoutPipe reader is
499 // not serviced fast enough it may eventually cause the 504 // not serviced fast enough it may eventually cause the
500 // remote command to block. 505 // remote command to block.
501 func (s *Session) StdoutPipe() (io.Reader, error) { 506 func (s *Session) StdoutPipe() (io.Reader, error) {
502 if s.Stdout != nil { 507 if s.Stdout != nil {
503 return nil, errors.New("ssh: Stdout already set") 508 return nil, errors.New("ssh: Stdout already set")
504 } 509 }
505 if s.started { 510 if s.started {
506 return nil, errors.New("ssh: StdoutPipe after process started") 511 return nil, errors.New("ssh: StdoutPipe after process started")
507 } 512 }
508 s.stdoutpipe = true 513 s.stdoutpipe = true
509 » return s.Channel, nil 514 » return s.ch, nil
510 } 515 }
511 516
512 // StderrPipe returns a pipe that will be connected to the 517 // StderrPipe returns a pipe that will be connected to the
513 // remote command's standard error when the command starts. 518 // remote command's standard error when the command starts.
514 // There is a fixed amount of buffering that is shared between 519 // There is a fixed amount of buffering that is shared between
515 // stdout and stderr streams. If the StderrPipe reader is 520 // stdout and stderr streams. If the StderrPipe reader is
516 // not serviced fast enough it may eventually cause the 521 // not serviced fast enough it may eventually cause the
517 // remote command to block. 522 // remote command to block.
518 func (s *Session) StderrPipe() (io.Reader, error) { 523 func (s *Session) StderrPipe() (io.Reader, error) {
519 if s.Stderr != nil { 524 if s.Stderr != nil {
520 return nil, errors.New("ssh: Stderr already set") 525 return nil, errors.New("ssh: Stderr already set")
521 } 526 }
522 if s.started { 527 if s.started {
523 return nil, errors.New("ssh: StderrPipe after process started") 528 return nil, errors.New("ssh: StderrPipe after process started")
524 } 529 }
525 s.stderrpipe = true 530 s.stderrpipe = true
526 » return s.Channel.Stderr(), nil 531 » return s.ch.Extended(1), nil
527 } 532 }
528 533
529 // NewSession returns a new interactive session on the remote host. 534 // NewSession returns a new interactive session on the remote host.
530 func (c *ClientConn) NewSession() (*Session, error) { 535 func (c *ClientConn) NewSession() (*Session, error) {
531 ch, err := c.mux.OpenChannel("session", nil) 536 ch, err := c.mux.OpenChannel("session", nil)
532 if err != nil { 537 if err != nil {
533 return nil, err 538 return nil, err
534 } 539 }
535 return &Session{ 540 return &Session{
536 » » Channel: ch, 541 » » ch: ch,
537 }, nil 542 }, nil
538 } 543 }
539 544
540 // An ExitError reports unsuccessful completion of a remote command. 545 // An ExitError reports unsuccessful completion of a remote command.
541 type ExitError struct { 546 type ExitError struct {
542 Waitmsg 547 Waitmsg
543 } 548 }
544 549
545 func (e *ExitError) Error() string { 550 func (e *ExitError) Error() string {
546 return e.Waitmsg.String() 551 return e.Waitmsg.String()
(...skipping 25 matching lines...) Expand all
572 } 577 }
573 578
574 // Lang returns the language tag. See RFC 3066 579 // Lang returns the language tag. See RFC 3066
575 func (w Waitmsg) Lang() string { 580 func (w Waitmsg) Lang() string {
576 return w.lang 581 return w.lang
577 } 582 }
578 583
579 func (w Waitmsg) String() string { 584 func (w Waitmsg) String() string {
580 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)
581 } 586 }
LEFTRIGHT

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