// create client on the mock pipe func mockBackChannel(ctx context.Context) (net.Conn, error) { log.Info("opening ttyS0 pipe pair for backchannel (client)") c, err := os.OpenFile(pathPrefix+"/ttyS0c", os.O_RDONLY|syscall.O_NOCTTY, 0777) if err != nil { detail := fmt.Sprintf("failed to open cpipe for backchannel: %s", err) log.Error(detail) return nil, errors.New(detail) } s, err := os.OpenFile(pathPrefix+"/ttyS0s", os.O_WRONLY|syscall.O_NOCTTY, 0777) if err != nil { detail := fmt.Sprintf("failed to open spipe for backchannel: %s", err) log.Error(detail) return nil, errors.New(detail) } log.Infof("creating raw connection from ttyS0 pipe pair (c=%d, s=%d)\n", c.Fd(), s.Fd()) conn, err := serial.NewHalfDuplexFileConn(c, s, pathPrefix+"/ttyS0", "file") if err != nil { detail := fmt.Sprintf("failed to create raw connection from ttyS0 pipe pair: %s", err) log.Error(detail) return nil, errors.New(detail) } // HACK: currently RawConn dosn't implement timeout so throttle the spinning ticker := time.NewTicker(1000 * time.Millisecond) for { select { case <-ticker.C: // FIXME: need to implement timeout of purging hangs with no content // on the pipe // serial.PurgeIncoming(ctx, conn) err := serial.HandshakeClient(conn, true) if err != nil { if err == io.EOF { // with unix pipes the open will block until both ends are open, therefore // EOF means the other end has been intentionally closed return nil, err } log.Error(err) } else { return conn, nil } case <-ctx.Done(): conn.Close() ticker.Stop() return nil, ctx.Err() } } }
// takes the base connection, determines the ID of the source and stashes it in the map func (c *Connector) processIncoming(conn net.Conn) { var err error defer func() { if err != nil && conn != nil { conn.Close() } }() for { if conn == nil { log.Infof("attach connector: connection closed") return } serial.PurgeIncoming(conn) // TODO needs timeout handling. This could take 30s. // Timeout for client handshake should be reasonably small. // Server will try to drain a buffer and if the buffer doesn't contain // 2 or more bytes it will just wait, so client should timeout. // However, if timeout is too short, client will flood server with Syn requests. ctx, cancel := context.WithTimeout(context.TODO(), time.Second) deadline, ok := ctx.Deadline() if ok { conn.SetReadDeadline(deadline) } if err = serial.HandshakeClient(conn, c.debug); err == nil { conn.SetReadDeadline(time.Time{}) log.Debugf("attach connector: New connection") cancel() break } else if err == io.EOF { log.Debugf("caught EOF") conn.Close() return } else if _, ok := err.(*serial.HandshakeError); ok { log.Debugf("HandshakeClient: %v", err) } else { log.Errorf("HandshakeClient: %v", err) } } callback := func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil } config := &ssh.ClientConfig{ User: "******", HostKeyCallback: callback, } log.Debugf("Initiating ssh handshake with new connection attempt") var ( ccon ssh.Conn newchan <-chan ssh.NewChannel request <-chan *ssh.Request ) ccon, newchan, request, err = ssh.NewClientConn(conn, "", config) if err != nil { log.Errorf("SSH connection could not be established: %s", errors.ErrorStack(err)) return } client := ssh.NewClient(ccon, newchan, request) var ids []string ids, err = SSHls(client) if err != nil { log.Errorf("SSH connection could not be established: %s", errors.ErrorStack(err)) return } var si SessionInteraction for _, id := range ids { si, err = SSHAttach(client, id) if err != nil { log.Errorf("SSH connection could not be established (id=%s): %s", id, errors.ErrorStack(err)) return } log.Infof("Established connection with container VM: %s", id) c.mutex.Lock() connection := &Connection{ spty: si, id: id, } c.connections[connection.id] = connection c.cond.Broadcast() c.mutex.Unlock() } return }
// takes the base connection, determines the ID of the source and stashes it in the map func (c *Connector) processIncoming(conn net.Conn) { var err error defer func() { if err != nil && conn != nil { conn.Close() } }() for { if conn == nil { log.Infof("attach connector: connection closed") return } serial.PurgeIncoming(conn) // TODO needs timeout handling. This could take 30s. // This needs to timeout with a *longer* wait than the ticker set on // the tether side (in tether_linux.go) or alignment may not happen. // The PL sends the first SYN in the handshake and if the tether is not // waiting, the handshake may never succeed. ctx, cancel := context.WithTimeout(context.TODO(), 50*time.Millisecond) if err = serial.HandshakeClient(ctx, conn); err == nil { log.Debugf("attach connector: New connection") cancel() break } else if err == io.EOF { log.Debugf("caught EOF") conn.Close() return } } callback := func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil } config := &ssh.ClientConfig{ User: "******", HostKeyCallback: callback, } log.Debugf("Initiating ssh handshake with new connection attempt") var ( ccon ssh.Conn newchan <-chan ssh.NewChannel request <-chan *ssh.Request ) ccon, newchan, request, err = ssh.NewClientConn(conn, "", config) if err != nil { log.Errorf("SSH connection could not be established: %s", errors.ErrorStack(err)) return } client := ssh.NewClient(ccon, newchan, request) var ids []string ids, err = SSHls(client) if err != nil { log.Errorf("SSH connection could not be established: %s", errors.ErrorStack(err)) return } var si SessionInteraction for _, id := range ids { si, err = SSHAttach(client, id) if err != nil { log.Errorf("SSH connection could not be established (id=%s): %s", id, errors.ErrorStack(err)) return } log.Infof("Established connection with container VM: %s", id) c.mutex.Lock() connection := &Connection{ spty: si, id: id, } c.connections[connection.id] = connection c.cond.Broadcast() c.mutex.Unlock() } return }