Files
MeBox/internal/service/qbittorrent_bencode.go
T
2026-06-26 14:44:05 +08:00

92 lines
1.9 KiB
Go

package service
import (
"crypto/sha1" // #nosec G505 -- BitTorrent v1 info-hash is SHA-1 by protocol.
"encoding/hex"
)
func torrentInfoHash(data []byte) string {
start, end, ok := torrentInfoBounds(data)
if !ok {
return ""
}
sum := sha1.Sum(data[start:end]) // #nosec G401 -- BitTorrent v1 info-hash is SHA-1 by protocol, not a security hash.
return hex.EncodeToString(sum[:])
}
func torrentInfoBounds(data []byte) (int, int, bool) {
if len(data) == 0 || data[0] != 'd' {
return 0, 0, false
}
pos := 1
for pos < len(data) && data[pos] != 'e' {
keyStart, keyEnd, next, ok := parseBencodeString(data, pos)
if !ok {
return 0, 0, false
}
valueStart := next
valueEnd, ok := bencodeValueEnd(data, valueStart)
if !ok {
return 0, 0, false
}
if string(data[keyStart:keyEnd]) == "info" {
return valueStart, valueEnd, true
}
pos = valueEnd
}
return 0, 0, false
}
func parseBencodeString(data []byte, pos int) (int, int, int, bool) {
if pos >= len(data) || data[pos] < '0' || data[pos] > '9' {
return 0, 0, 0, false
}
length := 0
for pos < len(data) && data[pos] >= '0' && data[pos] <= '9' {
length = length*10 + int(data[pos]-'0')
pos++
}
if pos >= len(data) || data[pos] != ':' {
return 0, 0, 0, false
}
start := pos + 1
end := start + length
if end > len(data) {
return 0, 0, 0, false
}
return start, end, end, true
}
func bencodeValueEnd(data []byte, pos int) (int, bool) {
if pos >= len(data) {
return 0, false
}
switch data[pos] {
case 'i':
end := pos + 1
for end < len(data) && data[end] != 'e' {
end++
}
if end >= len(data) {
return 0, false
}
return end + 1, true
case 'l', 'd':
end := pos + 1
for end < len(data) && data[end] != 'e' {
next, ok := bencodeValueEnd(data, end)
if !ok {
return 0, false
}
end = next
}
if end >= len(data) {
return 0, false
}
return end + 1, true
default:
_, _, next, ok := parseBencodeString(data, pos)
return next, ok
}
}