Glimesh / rtmp-ingest

RTMP Ingest for Glimesh.tv -- Work in progress!
MIT License
8 stars 3 forks source link

Handshake fails for certain RTMP clients #12

Closed clone1018 closed 2 years ago

clone1018 commented 2 years ago

Seems for some reason the handshake does not shake successfully with restream, so connecting does not work.

Server closed by error: Err = Random echo is not matched
github.com/yutopp/go-rtmp/handshake.HandshakeWithClient
    /root/go/pkg/mod/github.com/yutopp/go-rtmp@v0.0.1/handshake/handshake.go:102
github.com/yutopp/go-rtmp.(*serverConn).Serve
    /root/go/pkg/mod/github.com/yutopp/go-rtmp@v0.0.1/server_conn.go:28
github.com/yutopp/go-rtmp.(*Server).handleConn
    /root/go/pkg/mod/github.com/yutopp/go-rtmp@v0.0.1/server.go:115
runtime.goexit
    /usr/local/go/src/runtime/asm_amd64.s:1581

Failed to handshake
github.com/yutopp/go-rtmp.(*serverConn).Serve
    /root/go/pkg/mod/github.com/yutopp/go-rtmp@v0.0.1/server_conn.go:31
github.com/yutopp/go-rtmp.(*Server).handleConn
    /root/go/pkg/mod/github.com/yutopp/go-rtmp@v0.0.1/server.go:115
runtime.goexit
    /usr/local/go/src/runtime/asm_amd64.s:1581

The primary example of this is restream.io

clone1018 commented 2 years ago

Seems owncast had the same issue: https://github.com/owncast/owncast/issues/34

clone1018 commented 2 years ago

Need to look into if they maintain a fork of the RTMP library we should be using instead!

clone1018 commented 2 years ago

Here's some quick code to do a basic RTMP handshake (and send an invalid one for testing):

package main

import (
    "bufio"
    "crypto/rand"
    "fmt"
    "io"
    "net"
)

func main() {
    token := make([]byte, 1536)
    rand.Read(token)

    wrongResponse := make([]byte, 1536)
    rand.Read(wrongResponse)

    conn, err := net.Dial("tcp", "localhost:1935")
    if err != nil {
        panic(err)
    }
    reader := bufio.NewReader(conn)

    // Write 0x03 and token
    conn.Write([]byte{0x03})
    conn.Write(token)

    // Check status response
    status := make([]byte, 1)
    _, err = reader.Read(status)
    if err != nil {
        panic(err)
    }
    fmt.Printf("First Res: %#v\n", status)

    // Generate handshake
    handshake := make([]byte, 1536)
    _, err = io.ReadFull(conn, handshake)
    if err != nil {
        panic(err)
    }
    conn.Write(handshake)
    // Uncomment this if you want the handshake to fail
    // conn.Write(wrongResponse)
    _, err = reader.Read(status)
    if err != nil {
        panic(err)
    }
    fmt.Printf("Second Res: %#v\n", status)
}
clone1018 commented 2 years ago

Forked go-rtmp so I could add in lal's handshake code. Seems to be working but will need to actually test.

clone1018 commented 2 years ago

That fixed it it seems, there were two issues:

  1. Digest handshake was not implemented. https://github.com/Glimesh/go-rtmp/commit/a33e29d65c032e74c18565bb5aa90ea19fb74491
  2. Publishing Type is sometimes missing from certain services, I made a patch to go-rtmp to default to live (it is a live streaming server after all). https://github.com/Glimesh/go-rtmp/commit/4f0095b34ee64480e26c8bf9d2bdc040cdfc1a59#diff-985ac45622f50b5032960af0abd0cca4e5b9bd93ee9b1d76c72093de0d0cd8b5R205-R207