func ssh_acceptChannels(chans <-chan ssh.NewChannel, peerConn *ssh_peerConnection, peer *Peer) { var meta ssh_channelData for newCh := range chans { switch newCh.ChannelType() { case "@duplex": go func() { err := ssh.Unmarshal(newCh.ExtraData(), &meta) if err != nil { newCh.Reject(ssh.UnknownChannelType, "failed to parse channel data") return } ch, reqs, err := newCh.Accept() if err != nil { debug("accept error:", err) return } peerConn.attachedCh[ch.LocalID()] = make(chan interface{}, 1024) duplexChan := &ssh_channel{ Channel: ch, ssh_channelData: meta, peerConn: peerConn, } go duplexChan.handleRequests(reqs) if meta.FlagAttached { peerConn.attachedCh[meta.Attach] <- duplexChan } else { peer.incomingCh <- duplexChan } }() } } }
func (c *ssh_channel) handleRequests(in <-chan *ssh.Request) { for req := range in { switch req.Type { case "trailers": var trailers ssh_trailerPayload if err := ssh.Unmarshal(req.Payload, &trailers); err != nil { req.Reply(false, nil) } c.ssh_channelData.Trailers = trailers.Trailers req.Reply(true, nil) default: if req.WantReply { req.Reply(false, nil) } } } }
func newPeerConnection_ssh(peer *Peer, u *url.URL) (peerConnection, error) { pk, err := loadPrivateKey(peer.GetOption(OptPrivateKey).(string)) if err != nil { return nil, err } config := &ssh.ClientConfig{ User: peer.GetOption(OptName).(string), Auth: []ssh.AuthMethod{ssh.PublicKeys(pk)}, } var addr string if u.Scheme == "unix" { addr = u.Path } else { addr = u.Host } netConn, err := net.Dial(u.Scheme, addr) if err != nil { return nil, err } conn, chans, reqs, err := ssh.NewClientConn(netConn, addr, config) if err != nil { return nil, err } nameCh := make(chan string) go func() { for r := range reqs { switch r.Type { case "@duplex-greeting": var greeting ssh_greetingPayload err := ssh.Unmarshal(r.Payload, &greeting) if err != nil { continue } nameCh <- greeting.Name r.Reply(true, nil) default: // This handles keepalive messages and matches // the behaviour of OpenSSH. r.Reply(false, nil) } } }() var name string select { case name = <-nameCh: case <-time.After(time.Second * 5): return nil, errors.New("greeting timeout") } go func() { conn.Wait() //debug("client disconnection: ", err) // TODO: handle unexpected disconnect }() pc := &ssh_peerConnection{ endpoint: u.String(), remote: name, conn: conn, local: peer.GetOption(OptName).(string), attachedCh: make(map[uint32]chan interface{}), } go ssh_acceptChannels(chans, pc, peer) return pc, nil }