From dde20e1a7bd625605c83d55374b98f76d59b8baa Mon Sep 17 00:00:00 2001 From: Dan Kortschak Date: Fri, 7 Sep 2018 12:30:25 +0930 Subject: [PATCH] rtmp: more simplification of url parsing --- rtmp/amf_headers.go | 9 ++ rtmp/parseurl.go | 328 +++++++------------------------------------- rtmp/rtmp.go | 36 +---- 3 files changed, 63 insertions(+), 310 deletions(-) diff --git a/rtmp/amf_headers.go b/rtmp/amf_headers.go index 02473d6e..f706fdb1 100644 --- a/rtmp/amf_headers.go +++ b/rtmp/amf_headers.go @@ -32,6 +32,7 @@ LICENSE package rtmp import ( + "fmt" "unsafe" ) @@ -68,6 +69,8 @@ type C_AVal struct { av_len int32 } +func (s *C_AVal) String() string { return CAV(s) } + // C_AVal is in amf.h // amf.h +62 func AVC(str string) C_AVal { @@ -82,6 +85,12 @@ func AVC(str string) C_AVal { } func CAV(av *C_AVal) string { + if av.av_len <= 0 || av.av_val == nil { + if av.av_len < 0 { + panic(fmt.Sprintf("invalid length: %d", av.av_len)) + } + return "" + } return string((*[_Gi]byte)(unsafe.Pointer(av.av_val))[:av.av_len]) } diff --git a/rtmp/parseurl.go b/rtmp/parseurl.go index 4c541fdc..19df4d19 100644 --- a/rtmp/parseurl.go +++ b/rtmp/parseurl.go @@ -6,6 +6,7 @@ DESCRIPTION See Readme.md AUTHOR + Dan Kortschak Saxon Nelson-Milton LICENSE @@ -32,303 +33,72 @@ LICENSE package rtmp import ( - "fmt" "log" + "net/url" + "path" "strconv" "strings" - "unsafe" ) // int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app); // parseurl.c +33 -func C_RTMP_ParseURL(url *byte, host *C_AVal, port *uint16, playpath, app *C_AVal) (protocol int32, ok bool) { - var p, end, col, ques, slash *byte - // TODO: use our logger here - // RTMP_Log(RTMP_LOGDEBUG, "Parsing..."); - - protocol = RTMP_PROTOCOL_RTMP - *port = 0 - playpath.av_len = 0 - playpath.av_val = nil - app.av_len = 0 - app.av_val = nil - - p = strstr(url, goStrToCStr("://")) - - if p == nil { - // TODO: use our logger here - log.Println("RTMP URL: No :// in url!") - return protocol, false - } - /* - NOTE: the following code nees to be ported if we're using anything other than - rtmp! - { - int len = (int)(p-url); - - if(len == 4 && strncasecmp(url, "rtmp", 4)==0) - *protocol = RTMP_PROTOCOL_RTMP; - else if(len == 5 && strncasecmp(url, "rtmpt", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPT; - else if(len == 5 && strncasecmp(url, "rtmps", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPS; - else if(len == 5 && strncasecmp(url, "rtmpe", 5)==0) - *protocol = RTMP_PROTOCOL_RTMPE; - else if(len == 5 && strncasecmp(url, "rtmfp", 5)==0) - *protocol = RTMP_PROTOCOL_RTMFP; - else if(len == 6 && strncasecmp(url, "rtmpte", 6)==0) - *protocol = RTMP_PROTOCOL_RTMPTE; - else if(len == 6 && strncasecmp(url, "rtmpts", 6)==0) - *protocol = RTMP_PROTOCOL_RTMPTS; - else { - RTMP_Log(RTMP_LOGWARNING, "Unknown protocol!\n"); - goto parsehost; - } - } - */ - // TODO: implement new logger here - // RTMP_Log(RTMP_LOGDEBUG, "Parsed protocol: %d", *protocol); - - // Get the hostname - p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) - - // check for sudden death - if *p == 0 { - // TODO: use new logger here - // RTMP_Log(RTMP_LOGWARNING, "No hostname in URL!"); - return protocol, false +func C_RTMP_ParseURL(addr string) (protocol int32, host C_AVal, port uint16, playpath, app C_AVal, ok bool) { + u, err := url.Parse(addr) + if err != nil { + log.Printf("failed to parse addr: %v", err) + return protocol, host, port, app, playpath, false } - end = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(strlen(p)))) - col = strchr(p, ':') - ques = strchr(p, '?') - slash = strchr(p, '/') - - { - var hostlen int32 - if slash != nil { - hostlen = int32(uintptr(unsafe.Pointer(slash)) - uintptr(unsafe.Pointer(p))) - } else { - hostlen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) - } - if col != nil && int32(uintptr(unsafe.Pointer(col))-uintptr(unsafe.Pointer(p))) < hostlen { - hostlen = int32(uintptr(unsafe.Pointer(col)) - uintptr(unsafe.Pointer(p))) - } - - if hostlen < 256 { - host.av_val = (*byte)(unsafe.Pointer(p)) - host.av_len = hostlen - // TODO: use new logger with this - //RTMP_Log(RTMP_LOGDEBUG, "Parsed host : %.*s", hostlen, host.av_val); - } else { - // TODO: use new logger with this - // RTMP_Log(RTMP_LOGWARNING, "Hostname exceeds 255 characters!"); - } - - p = (*byte)(incBytePtr(unsafe.Pointer(p), int(hostlen))) + switch u.Scheme { + case "rtmp": + protocol = RTMP_PROTOCOL_RTMP + case "rtmpt": + protocol = RTMP_PROTOCOL_RTMPT + case "rtmps": + protocol = RTMP_PROTOCOL_RTMPS + case "rtmpe": + protocol = RTMP_PROTOCOL_RTMPE + case "rtmfp": + protocol = RTMP_PROTOCOL_RTMFP + case "rtmpte": + protocol = RTMP_PROTOCOL_RTMPTE + case "rtmpts": + protocol = RTMP_PROTOCOL_RTMPTS + default: + log.Printf("unknown scheme: %q", u.Scheme) + return protocol, host, port, app, playpath, false } - // get port number if available - if *p == ':' { - var p2 uint32 - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - tmp, err := strconv.Atoi(cStrToGoStr(p)) + host = AVC(u.Host) + if p := u.Port(); p != "" { + pi, err := strconv.Atoi(p) if err != nil { - return protocol, false - } - p2 = uint32(tmp) - if p2 > 65535 { - // TODO: use new logger with this - // RTMP_Log(RTMP_LOGWARNING, "Invalid port number!"); - } else { - *port = uint16(p2) + return protocol, host, port, app, playpath, false } + port = uint16(pi) } - if slash == nil { - // TODO: use new logger - // RTMP_Log(RTMP_LOGWARNING, "No application or playpath in URL!"); - return protocol, true + if !path.IsAbs(u.Path) { + return protocol, host, port, playpath, app, true } - - p = (*byte)(incBytePtr(unsafe.Pointer(slash), 1)) - - { - /* parse application - * - * rtmp://host[:port]/app[/appinstance][/...] - * application = app[/appinstance] - */ - - var slash2 *byte - var slash3 *byte = nil - var slash4 *byte = nil - var applen, appnamelen int32 - - slash2 = strchr(p, '/') - - if slash2 != nil { - slash3 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash2))+ - uintptr(1))), '/') + elems := strings.SplitN(u.Path[1:], "/", 3) + app = AVC(elems[0]) + playpath = AVC(elems[1]) + if len(elems) == 3 && len(elems[2]) != 0 { + inst := path.Join(elems[1:]...) + switch ext := path.Ext(inst); ext { + case ".f4v", ".mp4": + inst = "mp4:" + inst[:len(inst)-len(ext)] + case ".mp3": + inst = "mp3:" + inst[:len(inst)-len(ext)] + case ".flv": + inst = inst[:len(inst)-len(ext)] } - if slash3 != nil { - slash4 = strchr((*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(slash3))+ - uintptr(1))), '/') + if u.RawQuery != "" { + inst += "?" + u.RawQuery } - - // ondemand, pass all parameters as app - applen = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) - appnamelen = applen - - switch { - case ques != nil && strstr(p, goStrToCStr("slist=")) != nil: - appnamelen = int32(uintptr(unsafe.Pointer(ques)) - uintptr(unsafe.Pointer(p))) - case strings.Compare(cStrToGoStr(p)[:9], "ondemand/") == 0: - /* app = ondemand/foobar, only pass app=ondemand */ - applen = 8 - appnamelen = 8 - default: - switch { - case slash4 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash4)) - uintptr( - unsafe.Pointer(p))) - case slash3 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash3)) - uintptr( - unsafe.Pointer(p))) - case slash2 != nil: - appnamelen = int32(uintptr(unsafe.Pointer(slash2)) - uintptr( - unsafe.Pointer(p))) - } - - applen = appnamelen - } - - app.av_val = (*byte)(unsafe.Pointer(p)) - app.av_len = applen - // TODO: use new logging here - // RTMP_Log(RTMP_LOGDEBUG, "Parsed app : %.*s", applen, p); - - p = (*byte)(incBytePtr(unsafe.Pointer(p), int(appnamelen))) + playpath = AVC(inst) } - if *p == '/' { - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - } - // NOTE: don't think we currently need this section - see 787 for this func - - if int(uintptr(unsafe.Pointer(end))-uintptr(unsafe.Pointer(p))) != 0 { - var av C_AVal - av.av_val = (*byte)(unsafe.Pointer(p)) - av.av_len = int32(uintptr(unsafe.Pointer(end)) - uintptr(unsafe.Pointer(p))) - // TODO: port THis - //C.RTMP_ParsePlaypath(&av, playpath) - C_RTMP_ParsePlaypath(&av, playpath) - } - - return protocol, true -} - -// void RTMP_ParsePlaypath(AVal *in, AVal *out); -// parseurl.c +201 -func C_RTMP_ParsePlaypath(in, out *C_AVal) { - var addMP4 int32 = 0 - var addMP3 int32 = 0 - var subExt int32 = 0 - playpath := in.av_val - var temp, q *byte - var ext *byte = nil - ppstart := (*byte)(unsafe.Pointer(playpath)) - var streamname, destptr, p *byte - - pplen := in.av_len - - out.av_val = nil - out.av_len = 0 - - temp = strstr((*byte)(unsafe.Pointer(ppstart)), goStrToCStr("slist=")) - if *ppstart == '?' && temp != nil { - ppstart = (*byte)(incBytePtr(unsafe.Pointer(temp), 6)) - pplen = strlen(ppstart) - - temp = strchr(ppstart, '&') - - if temp != nil { - pplen = int32(uintptr(unsafe.Pointer(temp)) - uintptr(unsafe.Pointer(ppstart))) - } - } - - q = strchr(ppstart, '?') - - if pplen >= 4 { - if q != nil { - ext = (*byte)(decBytePtr(unsafe.Pointer(q), 4)) - } else { - ext = (*byte)(sliceToPtr((*[_Gi]byte)(unsafe.Pointer(ppstart))[uintptr(pplen)-4:])) - } - switch { - case strings.Compare(cStrToGoStr(ext)[:4], ".f4v") == 0 || - strings.Compare(cStrToGoStr(ext)[:4], ".mp4") == 0: - addMP4 = 1 - subExt = 1 - case ppstart == (*byte)(unsafe.Pointer(playpath)) && strings.Compare( - cStrToGoStr(ext)[:4], ".flv") == 0: - subExt = 1 - case strings.Compare(cStrToGoStr(ext)[:4], ".mp3") == 0: - addMP3 = 1 - subExt = 1 - } - } - - streamname = (*byte)(malloc(uintptr(pplen + 4 + 1))) - - if streamname == nil { - return - } - - destptr = streamname - switch { - case addMP4 != 0: - if strings.Compare(cStrToGoStr(ppstart)[:4], "mp4") != 0 { - memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp4:")), - uintptr(len("mp4:"))) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) - } else { - subExt = 0 - } - case addMP3 != 0: - if strings.Compare(cStrToGoStr(ppstart)[:4], "mp3") != 0 { - memmove(unsafe.Pointer(destptr), unsafe.Pointer(goStrToCStr("mp3:")), - uintptr(len("mp4:"))) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 4)) - } else { - subExt = 0 - } - } - - p = ppstart - for pplen > 0 { - if subExt != 0 && p == ext { - p = (*byte)(incBytePtr(unsafe.Pointer(p), 4)) - pplen -= 4 - continue - } - if *p == '%' { - var c uint32 - fmt.Sscanf(cStrToGoStr((*byte)(incBytePtr(unsafe.Pointer(p), 1))), "%02x", &c) - (*[_Gi]byte)(unsafe.Pointer(destptr))[0] = byte(c) - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) - pplen -= 3 - p = (*byte)(incBytePtr(unsafe.Pointer(p), 3)) - } else { - (*[_Gi]byte)(unsafe.Pointer(destptr))[0] = *p - destptr = (*byte)(incBytePtr(unsafe.Pointer(destptr), 1)) - p = (*byte)(incBytePtr(unsafe.Pointer(p), 1)) - pplen-- - } - } - *destptr = '\x00' - - out.av_val = (*byte)(unsafe.Pointer(streamname)) - out.av_len = int32(uintptr(unsafe.Pointer(destptr)) - uintptr(unsafe.Pointer( - streamname))) + return protocol, host, port, app, playpath, true } diff --git a/rtmp/rtmp.go b/rtmp/rtmp.go index 3c574d04..61484af0 100644 --- a/rtmp/rtmp.go +++ b/rtmp/rtmp.go @@ -296,45 +296,19 @@ func C_SocksSetup(r *C_RTMP, sockshost *C_AVal) { // rtmp.c +757 // NOTE: code dealing with rtmp over http has been disregarded func C_RTMP_SetupURL(r *C_RTMP, addr string) (ok bool) { - u := goStrToCStr(addr) - length := strlen(u) - - r.Link.protocol, ok = C_RTMP_ParseURL(u, &r.Link.hostname, &r.Link.port, &r.Link.playpath0, &r.Link.app) + r.Link.protocol, r.Link.hostname, r.Link.port, r.Link.app, r.Link.playpath0, ok = C_RTMP_ParseURL(addr) if !ok { return false } r.Link.playpath = r.Link.playpath0 if r.Link.tcUrl.av_len == 0 { - r.Link.tcUrl.av_val = (*byte)(unsafe.Pointer(u)) if r.Link.app.av_len != 0 { - if int(uintptr(unsafe.Pointer(r.Link.app.av_val))) < - int(uintptr(incBytePtr(unsafe.Pointer(u), int(length)))) { - - r.Link.tcUrl.av_len = int32(int(r.Link.app.av_len) + - int(uintptr(decBytePtr(unsafe.Pointer(r.Link.app.av_val), - int(uintptr(unsafe.Pointer(u))))))) - } else { - length = int32(r.Link.hostname.av_len) + int32(r.Link.app.av_len) + - int32(len("rtmpte://:65535/\x00")) - - r.Link.tcUrl.av_val = (*byte)(malloc(uintptr(uintptr(length)))) - hostname := string((*[_Gi]byte)(unsafe.Pointer(r.Link.hostname.av_val))[:r.Link.hostname.av_len]) - - app := string((*[_Gi]byte)(unsafe.Pointer(r.Link.app.av_val))[:r.Link.app.av_len]) - - fString := fmt.Sprintf("%v://%v:%v/%v", - RTMPProtocolStringsLower[r.Link.protocol], hostname, r.Link.port, app) - - r.Link.tcUrl.av_val = (*byte)(bToUP(goStrToCStr(fString))) - r.Link.tcUrl.av_len = int32(strLen(RTMPProtocolStringsLower[r.Link.protocol]) + - strLen(string("://")) + strLen(hostname) + strLen(string(":")) + - strLen(strconv.Itoa(int(r.Link.port))) + strLen(string("/")) + strLen(app)) - - r.Link.lFlags |= RTMP_LF_FTCU - } + r.Link.tcUrl = AVC(fmt.Sprintf("%v://%v:%v/%v", + RTMPProtocolStringsLower[r.Link.protocol], &r.Link.hostname, r.Link.port, &r.Link.app)) + r.Link.lFlags |= RTMP_LF_FTCU } else { - r.Link.tcUrl.av_len = int32(strlen(u)) + r.Link.tcUrl = AVC(addr) } }