install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
package homekit
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/hap"
|
||||
"github.com/AlexxIT/go2rtc/pkg/hap/camera"
|
||||
"github.com/AlexxIT/go2rtc/pkg/hap/hds"
|
||||
"github.com/AlexxIT/go2rtc/pkg/hap/tlv8"
|
||||
)
|
||||
|
||||
type ServerProxy interface {
|
||||
ServerPair
|
||||
AddConn(conn any)
|
||||
DelConn(conn any)
|
||||
}
|
||||
|
||||
func ProxyHandler(srv ServerProxy, acc net.Conn) HandlerFunc {
|
||||
return func(con net.Conn) error {
|
||||
defer con.Close()
|
||||
|
||||
pr := &Proxy{
|
||||
con: con.(*hap.Conn),
|
||||
acc: acc.(*hap.Conn),
|
||||
res: make(chan *http.Response),
|
||||
}
|
||||
|
||||
// accessory (ex. Camera) => controller (ex. iPhone)
|
||||
go pr.handleAcc()
|
||||
|
||||
// controller => accessory
|
||||
return pr.handleCon(srv)
|
||||
}
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
con *hap.Conn
|
||||
acc *hap.Conn
|
||||
res chan *http.Response
|
||||
}
|
||||
|
||||
func (p *Proxy) handleCon(srv ServerProxy) error {
|
||||
var hdsCharIID uint64
|
||||
|
||||
rd := bufio.NewReader(p.con)
|
||||
for {
|
||||
req, err := http.ReadRequest(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hdsConSalt string
|
||||
|
||||
switch {
|
||||
case req.Method == "POST" && req.URL.Path == hap.PathPairings:
|
||||
var res *http.Response
|
||||
if res, err = handlePairings(req, srv); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = res.Write(p.con); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
case req.Method == "PUT" && req.URL.Path == hap.PathCharacteristics && hdsCharIID != 0:
|
||||
body, _ := io.ReadAll(req.Body)
|
||||
var v hap.JSONCharacters
|
||||
_ = json.Unmarshal(body, &v)
|
||||
for _, char := range v.Value {
|
||||
if char.IID == hdsCharIID {
|
||||
var hdsReq camera.SetupDataStreamTransportRequest
|
||||
_ = tlv8.UnmarshalBase64(char.Value, &hdsReq)
|
||||
hdsConSalt = hdsReq.ControllerKeySalt
|
||||
break
|
||||
}
|
||||
}
|
||||
req.Body = io.NopCloser(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
if err = req.Write(p.acc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res := <-p.res
|
||||
|
||||
switch {
|
||||
case req.Method == "GET" && req.URL.Path == hap.PathAccessories:
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
var v hap.JSONAccessories
|
||||
if err = json.Unmarshal(body, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, acc := range v.Value {
|
||||
if char := acc.GetCharacter(camera.TypeSetupDataStreamTransport); char != nil {
|
||||
hdsCharIID = char.IID
|
||||
}
|
||||
break
|
||||
}
|
||||
res.Body = io.NopCloser(bytes.NewReader(body))
|
||||
|
||||
case hdsConSalt != "":
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
var v hap.JSONCharacters
|
||||
_ = json.Unmarshal(body, &v)
|
||||
for i, char := range v.Value {
|
||||
if char.IID == hdsCharIID {
|
||||
var hdsRes camera.SetupDataStreamTransportResponse
|
||||
_ = tlv8.UnmarshalBase64(char.Value, &hdsRes)
|
||||
|
||||
hdsAccSalt := hdsRes.AccessoryKeySalt
|
||||
hdsPort := int(hdsRes.TransportTypeSessionParameters.TCPListeningPort)
|
||||
|
||||
// swtich accPort to conPort
|
||||
hdsPort, err = p.listenHDS(srv, hdsPort, hdsConSalt+hdsAccSalt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hdsRes.TransportTypeSessionParameters.TCPListeningPort = uint16(hdsPort)
|
||||
if v.Value[i].Value, err = tlv8.MarshalBase64(hdsRes); err != nil {
|
||||
return err
|
||||
}
|
||||
body, _ = json.Marshal(v)
|
||||
res.ContentLength = int64(len(body))
|
||||
break
|
||||
}
|
||||
}
|
||||
res.Body = io.NopCloser(bytes.NewReader(body))
|
||||
}
|
||||
|
||||
if err = res.Write(p.con); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) handleAcc() error {
|
||||
rd := bufio.NewReader(p.acc)
|
||||
for {
|
||||
res, err := hap.ReadResponse(rd, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.Proto == hap.ProtoEvent {
|
||||
if err = hap.WriteEvent(p.con, res); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// important to read body before next read response
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res.Body = io.NopCloser(bytes.NewReader(body))
|
||||
|
||||
p.res <- res
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proxy) listenHDS(srv ServerProxy, accPort int, salt string) (int, error) {
|
||||
// The TCP port range for HDS must be >= 32768.
|
||||
ln, err := net.ListenTCP("tcp", nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer ln.Close()
|
||||
|
||||
_ = ln.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
|
||||
// raw controller conn
|
||||
conn1, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
defer conn1.Close()
|
||||
|
||||
// secured controller conn (controlle=false because we are accessory)
|
||||
con, err := hds.NewConn(conn1, p.con.SharedKey, salt, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
srv.AddConn(con)
|
||||
defer srv.DelConn(con)
|
||||
|
||||
accIP := p.acc.RemoteAddr().(*net.TCPAddr).IP
|
||||
|
||||
// raw accessory conn
|
||||
conn2, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: accIP, Port: accPort})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn2.Close()
|
||||
|
||||
// secured accessory conn (controller=true because we are controller)
|
||||
acc, err := hds.NewConn(conn2, p.acc.SharedKey, salt, true)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
go io.Copy(con, acc)
|
||||
_, _ = io.Copy(acc, con)
|
||||
}()
|
||||
|
||||
conPort := ln.Addr().(*net.TCPAddr).Port
|
||||
return conPort, nil
|
||||
}
|
||||
Reference in New Issue
Block a user