// Connects to a remote node and negotiates a session. func Dial(host string, port int, key *rsa.PrivateKey) (*Session, error) { // Open the stream connection addr := fmt.Sprintf("%s:%d", host, port) strm, err := stream.Dial(addr, config.SessionDialTimeout) if err != nil { return nil, err } // Set up the authenticated session secret, err := clientAuth(strm, key) if err != nil { log.Printf("session: failed to authenticate connection: %v.", err) if err := strm.Close(); err != nil { log.Printf("session: failed to close unauthenticated connection: %v.", err) } } // Link a new data connection to it sess := newSession(strm, secret, false) if err = clientLink(sess); err != nil { log.Printf("session: failed to link data connection: %v.", err) if err := strm.Close(); err != nil { log.Printf("session: failed to close unlinked connection: %v.", err) } return nil, err } return sess, nil }
func client(msg string, ch chan string) { // Open a TCP connection to the stream server addr := fmt.Sprintf("%s:%d", host, port) strm, err := stream.Dial(addr, time.Second) if err != nil { fmt.Println("Failed to connect to stream server:", err) return } defer strm.Close() // Send the message and receive a reply if err = strm.Send(msg); err != nil { fmt.Println("Failed to send the message:", err) return } if err = strm.Flush(); err != nil { fmt.Println("Failed to flush the message:", err) return } if err = strm.Recv(&msg); err != nil { fmt.Println("Failed to receive the reply:", err) return } // Return the reply to the caller and terminate ch <- msg }
// Accepts an incoming tunneling request from a remote, initializes and stores // the new tunnel into the connection state. func (c *Connection) buildTunnel(remote uint64, id uint64, key []byte, addrs []string, timeout time.Duration) (*Tunnel, error) { deadline := time.Now().Add(timeout) // Create the local tunnel endpoint c.tunLock.Lock() tunId := c.tunIdx tun := &Tunnel{ id: tunId, owner: c, term: make(chan struct{}), } c.tunIdx++ c.tunLive[tunId] = tun c.tunLock.Unlock() // Dial the remote tunnel listener var err error var strm *stream.Stream for _, addr := range addrs { strm, err = stream.Dial(addr, timeout) if err == nil { break } } // If no error occurred, initialize the client endpoint if err == nil { var conn *link.Link conn, err = c.initClientTunnel(strm, remote, id, key, deadline) if err != nil { if err := strm.Close(); err != nil { log.Printf("iris: failed to close uninitialized client tunnel stream: %v.", err) } } else { // Make sure the tunnel wasn't terminated since (init/close race) tun.lock.Lock() select { case <-tun.term: conn.Close() err = ErrTerminating default: tun.conn = conn } tun.lock.Unlock() } } // Tunneling failed, clean up and report error if err != nil { c.tunLock.Lock() delete(c.tunLive, tunId) c.tunLock.Unlock() return nil, err } return tun, nil }
// Initiates a data channel link to the specified control channel. func clientLink(sess *Session) error { // Wait for the server to specify the session id msg, err := sess.CtrlLink.RecvDirect() if err != nil { return fmt.Errorf("failed to retrieve session id: %v", err) } // Initiate a new stream connection to the server addr := sess.CtrlLink.Sock().RemoteAddr().String() strm, err := stream.Dial(addr, config.SessionDialTimeout) if err != nil { return fmt.Errorf("failed to establish data link: %v", err) } // Send the temporary id back on the data stream req := &initRequest{ Link: &linkRequest{msg.Head.Meta.(*linkRequest).Id}, } if err = strm.Send(req); err != nil { strm.Close() return fmt.Errorf("failed to send link request: %v", err) } if err = strm.Flush(); err != nil { strm.Close() return fmt.Errorf("failed to flush link request: %v", err) } // Finalize the session with the data stream sess.init(strm, false) // Send the data link authentication auth := &proto.Message{ Head: proto.Header{ Meta: req.Link, }, } // Retrieve the remote data link authentication if err = sess.DataLink.SendDirect(auth); err != nil { return fmt.Errorf("failed to send data auth: %v", err) } if msg, err := sess.DataLink.RecvDirect(); err != nil { return fmt.Errorf("failed to retrieve data auth: %v", err) } else if res, ok := msg.Head.Meta.(*linkRequest); !ok { return errors.New("corrupt authentication message") } else if res.Id != req.Link.Id { return errors.New("mismatched authentication message") } return nil }
// Tests the low level send and receive methods. func TestDirectSendRecv(t *testing.T) { t.Parallel() // Start a stream listener addr, err := net.ResolveTCPAddr("tcp", "localhost:0") if err != nil { t.Fatalf("failed to resolve local address: %v.", err) } listener, err := stream.Listen(addr) if err != nil { t.Fatalf("failed to listen for incoming streams: %v.", err) } listener.Accept(10 * time.Millisecond) defer listener.Close() // Establish a stream connection to the listener host := fmt.Sprintf("%s:%d", "localhost", addr.Port) clientStrm, err := stream.Dial(host, time.Millisecond) if err != nil { t.Fatalf("failed to connect to stream listener: %v.", err) } serverStrm := <-listener.Sink defer clientStrm.Close() defer serverStrm.Close() // Initialize the stream based encrypted links secret := make([]byte, 16) io.ReadFull(rand.Reader, secret) clientHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) serverHKDF := hkdf.New(sha1.New, secret, []byte("HKDF salt"), []byte("HKDF info")) clientLink := New(clientStrm, clientHKDF, false) serverLink := New(serverStrm, serverHKDF, true) // Generate some random messages and pass around both ways for i := 0; i < 1000; i++ { // Generate the message to send send := &proto.Message{ Head: proto.Header{ Meta: make([]byte, 32), }, Data: make([]byte, 32), } io.ReadFull(rand.Reader, send.Head.Meta.([]byte)) io.ReadFull(rand.Reader, send.Data) send.Encrypt() // Send the message from client to server if err := clientLink.SendDirect(send); err != nil { t.Fatalf("failed to send message to server: %v.", err) } if recv, err := serverLink.RecvDirect(); err != nil { t.Fatalf("failed to receive message from client: %v.", err) } else if bytes.Compare(send.Head.Meta.([]byte), recv.Head.Meta.([]byte)) != 0 || bytes.Compare(send.Data, recv.Data) != 0 { t.Fatalf("send/receive mismatch: have %+v, want %+v.", recv, send) } // Send the message from server to client if err := serverLink.SendDirect(send); err != nil { t.Fatalf("failed to send message to client: %v.", err) } if recv, err := clientLink.RecvDirect(); err != nil { t.Fatalf("failed to receive message from server: %v.", err) } else if bytes.Compare(send.Head.Meta.([]byte), recv.Head.Meta.([]byte)) != 0 || bytes.Compare(send.Data, recv.Data) != 0 { t.Fatalf("send/receive mismatch: have %+v, want %+v.", recv, send) } } }