BEncoding編碼及asp提取Torrent檔案資訊
1、BEncoding編碼
Bencoding編碼是BT協議用來說明、組織資訊的的一種資料格式,來源於PYTHON語言。主要資料結構為:
數 字 = < ascii碼錶示的0-9 >
字 符 = <任意單位元組二進位字元>
整 數 = (字元”i”)<1-n>(數字)(字元”e”)
字串 = <1-n>(數字)(字元”:”)<1-m>(字元) *數字為後面字串的長度
列 表 = (字元”l”)<1-n>(整數|字串|列表|字典)(字元”e”)
字 典 = (字元”d”)<1-n>((字串)(整數|字串|列表|字典))(字元”e”)
Torrent檔案 = (字典)
大家可以看到,這種資料結構把資訊分成東一塊,西一塊,以樹狀結構組成,更本沒法順序讀取,但可以用遞迴方式解析成對象,在記憶體中形成一個和邏輯結構相當的對象樹,然後通過邏輯位置讀取。由於是二進位結構,由必須全部解析成對象才能讀取,用asp來搞,代價很大。
2、Torrent檔案
Torrent檔案即一個“單一”的字典檔案,其資訊以以階層儲存在字典項中,字典關鍵字區分大小寫。主要字典資料項目為:
announce:(字串)種子伺服器
announce-list:(列表)備用種子伺服器
creation-date:(整數)unix時間
created by:(字串)建立人
info:(字典)
length:(整數)檔案大小
name:(字串)最上層目錄名
piece length:(整數)下載結構資訊,這裡我們不管
pieces:(字串)下載結構資訊,這裡我們不管
files:(字典)僅多檔案時存在
length:(整數)檔案大小
Path:(列表)檔案路徑,採用超JB煩人的方式列表逐層列出目錄民,最後到檔案名稱
從邏輯結構,可以知道他們解析後的讀取方式。
例如:
發布時間=Dictionary(“creation date”).readInteger
某個節點=Dictionary(“nodes”).List(i).list(0).readString & ":" & Dictionary(“nodes”).List(i).list(0).readInteger
列表中第3個檔案的路徑為
假設tmp= Dictionary(“info”).Dictionary(“files”).List(3).Dictionary(“path”)
strPath=tmp.list(0).readString & "/" & tmp.list(1).readString & "/"...
3、具體編程提要
a、幾點要注意的:
Bencoding中的整數沒有長度限制,應而最好用dbl儲存,不過為為了避免溢出,可以設立一個限制,比方最大20位整數。
解析過程調試很麻煩,最好主動設定錯誤資訊,比方解析整數沒有'i'頭,就放一個err.raise vbobjectError,"readInteger()","'i' head missing",而不要等到指令碼級資料轉換錯誤時在找毛病。
同是字串類型,字典關鍵字是asc編碼,而某些字串字典的資料,例如name.utf-8,path.utf-8,通常用utf8編碼,以防止亂碼。因而建議字典關鍵字則用ascii直接轉化。而全部字典資料項目字串都用byte()結構,使用時在具體處理。
我看了一些asp的bencoding解析方式,採用instrb(bin,":")這類的辦法尋找標誌,然後直接midb->cdbl轉換,個人覺得這違背詞法分析原則,可能有不可預料後果,例如小數負數。最好採用while midb(bin,i,1)&chr(0)<="9" and .. >="0" 的方式
資訊儲存為檔案的尾碼,由於Bencoding解析無法過濾<%%>,應而以asp等web應用程式尾碼儲存可能導致網站漏洞。
要不要class_terminate時遞迴erase,setnoting清除對象呢?我覺得這些對象不需要反覆用,不會在運行過程中膨脹,asp結束系統會自動回收,還是不要多事的號。
b、Bencoding以層次組織,以遞迴解析,可以每種資料結構建立一個類,每次解析一個資料結構,即將其封為對象,返回上一層,通過遞迴,最終將返回倒跟節點。asp不支援靜態過程,所以每種資料結構的解析過程最好放在類外面。
主要過程如下
class CBencodingInteger
public lngPosition '在檔案中的位置
public lngLength '其全部結構包括子資料的長度
public dblValue '值
end class
class CBencodingString
public lngPosition '在檔案中的位置
public lngLength '其全部結構包括子資料的長度
public binValue '值
end class
Class CBencodingList
Public lngPosition,lngLength
Private m_aobj,m_intCount
Private Sub class_initialize()
ReDim m_aobj(0)
m_intCount=0
End Sub
Public Property Get Count()
Count=m_intCount
End Property
Public Default Function Item(intIndex)
If intIndex<0 Or intIndex>=m_intCount Then Err.raise vbObjectError,"Item()","index overflow"
Set Item=m_aobj(intIndex)
End Function
Public Function Add(objItem)
ReDim preserve m_aobj(m_intCount)
Set m_aobj(m_intCount)=objItem
m_intCount=m_intCount+1
End Function
End Class
Class CBencodingDictionary
Public lngPosition,lngLength
private m_objDict
Public Sub class_initialize()
Set m_objDict=CreateObject("scripting.dictionary")
End Sub
Public Default Function Item(strIndex)
If m_objDict.Exists(strIndex) then
Set Item=m_objDict.item(strIndex)
else
Err.raise vbObjectError,"Item()","not such key in dictionary."
End If
End Function
Public Function Add(strKey,objItem)
m_objDict.add strkey,objItem
End Function
Public Property Get Count()
Count=mobjDict.count
End Property
End Class
'將byte()字元轉化為vbString
Function PickBinChar(binSrc,lngPos)
PickBinChar=midb(binSrc,lngPos,1)&Chrb(0)
End Function
'讀取整數
'[in]binSrc
'[in,out]lngCurpos
'[in]lngMaxPos
'[out]objRet
Function readInteger(binSrc,lngCurPos,objRet)
Dim i,j
Dim chrBuf
Dim strNum,dblNum
Dim lngBakPos
lngBakPos=lngCurPos '直接更改解析位置lngCurPos好處是發生err,可以知道錯誤的字元位置
Set objRet=Nothing
If PickBinChar(binSrc,lngCurPos)<>"i" Then Err.raise vbObjectError,"readInteger()","'i' head missing."
lngCurPos=lngCurPos+1
i=0
strNum=""
chrBuf=PickBinChar(binSrc,lngCurPos)
Do While chrBuf<="9" And chrBuf>="0"
strNum=strNum & chrBuf
lngCurPos=lngCurPos+1
chrBuf=PickBinChar(binSrc,lngCurPos)
i=i+1
If i>MAX_INT_LEN Then Err.raise vbobjectError,"readInteger()","Integer overflow."
Loop
If chrBuf<>"e" Then Err.raise vbobjectError,"readInteger()","'e' rare missing."
If strNum="" Then Err.raise vbObjectError,"readInteger()","empty between 'i' and 'e'."
Set objRet=new CBencodingInteger
objRet.lngPosition=lngBakPos
objRet.lngLength=lngCurPos-lngBakPos
objRet.dblValue=CDbl(strNum)
lngCurPos=lngCurPos+1
End Function
'readString,readList,readDictionary我就不貼了,基本類似,就是dictionary和list的位置指標要小心一些
'測試
Function LoadFromFile(strPath,bin) '也可以改成request.binaryread,不過要先處理一下multiform資料格式
Dim objAdoStm
Set objAdoStm=CreateObject("adodb.stream")
objAdoStm.Type=1
objAdoStm.Mode=3
objAdoStm.Open
objAdoStm.LoadFromFile strPath
objAdoStm.Position=0
bin=objAdoStm.Read(-1)
objAdoStm.close
Set objAdoStm=Nothing
End Function
Debug:Sub Debug()
Dim bin
LoadFromFile "test.torrent",bin
Dim objTmp
'解析根節點,以此遞迴解析全部檔案
readDictionary bin,1,0,objTmp
'顯示第0個節點的IP位置
MsgBox byte2Str(objTmp.item("nodes").item(0).item(0).binValue)
End Sub
============
真是遺憾啊,自己搞了幾天終於搞定,確因為表達能力太爛無法寫出體驗,只能放些乾巴巴的東西上來。