nx_peer_metadata.nx source
↩ module page · 182 lines · 10540 B
1// nx_peer_metadata.nx -- BEP-9 ut_metadata FETCH + sha1-verify the info-dict (X-TORRENT-LIVE-001
2// R3b): COMPLETES the engine. A magnet has no .torrent, so the client fetches the info-dict from a
3// peer over BEP-10/BEP-9: ext handshake -> learn ut_metadata id + metadata_size -> request each
4// metadata piece -> peer replies a bencoded header + RAW info-dict bytes -> assemble -> the BEP-9
5// integrity check sha1(info-dict)==info_hash -> nx_bc_dict_get the piece map (name/piece length/
6// length). Reuses nx_peerwire + nx_bencode + nx_sha1. GATE = SOVEREIGN LOOPBACK: a synthetic
7// info-dict whose sha1 IS the handshake info_hash; the mock serves it; the client fetches,
8// assembles, verifies, and parses. license_tier: ORIGINAL
9//
10// module: nishi-core.torrent.peer_metadata
11// depends: nishi-core.torrent.peerwire, nishi-core.torrent.bencode, nishi-core.crypto.sha1
12import "nx_peerwire.nx"
13import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
14import "nx_bencode.nx"
15import "nx_sha1.nx"
16const PW_MAGIC_65536: i64 = 65536
17const PW_MAGIC_53461: i64 = 53461
18const PW_MAGIC_1024: i64 = 1024
19const PW_MAGIC_2048: i64 = 2048
20
21const PW_EXTENDED: i64 = 20
22
23func md_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 }
24func md_wn(fd: i64, v: i64) -> i64 {
25 let t: *u8=sys_mmap(28); var m: i64=v; if m<0 {m=0-m; sys_write(fd,"-" as *u8,1)}
26 var k: i64=0; if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}
27 let b: *u8=sys_mmap(28); var i: i64=0; while i<k {b[i]=t[k-1-i]; i=i+1}; sys_write(fd,b,k); return 0
28}
29func md_bstr(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i }
30func md_bint(buf: *u8, off: i64, v: i64) -> i64 {
31 let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0; if m==0 {t[0]=48 as u8;k=1}; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1}
32 var o: i64=off; var j: i64=0; while j<k { buf[o]=t[k-1-j]; o=o+1; j=j+1 } return o
33}
34func md_sockaddr(sa: *u8, port: i64, i0: i64, i1: i64, i2: i64, i3: i64) -> i64 {
35 sa[0]=2 as u8; sa[1]=0 as u8
36 sa[2]=((port>>8)&0xff) as u8; sa[3]=(port&0xff) as u8
37 sa[4]=i0 as u8; sa[5]=i1 as u8; sa[6]=i2 as u8; sa[7]=i3 as u8
38 var k: i64=8; while k<16 { sa[k]=0 as u8; k=k+1 }
39 return 0
40}
41func md_read_n(fd: i64, buf: *u8, n: i64) -> i64 {
42 var got: i64 = 0
43 while got < n { let r: i64 = sys_read(fd, buf + got, n - got); if r <= 0 { return 0 - 1 } got = got + r }
44 return got
45}
46func md_write_n(fd: i64, buf: *u8, n: i64) -> i64 {
47 var off: i64 = 0
48 while off < n { let w: i64 = sys_write(fd, buf + off, n - off); if w <= 0 { return 0 - 1 } off = off + w }
49 return n
50}
51func md_read_msg(fd: i64, mb: *u8, cap: i64) -> i64 {
52 if md_read_n(fd, mb, 4) != 4 { return 0 - 1 }
53 let blen: i64 = _pw_get_u32(mb, 0)
54 if blen == 0 { return 0 }
55 if blen > cap - 4 { return 0 - 1 }
56 if md_read_n(fd, mb + 4, blen) != blen { return 0 - 1 }
57 return blen
58}
59// extended message id=20 with EXPLICIT data length (data may contain NUL -- raw metadata). out=msg.
60func md_build_ext(ext_id: i64, data: *u8, dlen: i64, out: *u8) -> i64 {
61 let pl: *u8 = sys_mmap(PW_MAGIC_65536)
62 pl[0] = ext_id as u8
63 var i: i64 = 0; while i < dlen { pl[1 + i] = data[i]; i = i + 1 }
64 return nx_pw_build_msg(PW_EXTENDED, pl, 1 + dlen, out)
65}
66
67func main() -> i64 {
68 let PORT: i64 = PW_MAGIC_53461
69 // synthetic info-dict; info_hash = sha1(info-dict) (BEP-9 identity)
70 let idd: *u8 = sys_mmap(512)
71 var p: i64 = md_bstr(idd, 0, "d6:lengthi32e4:name8:test.bin12:piece lengthi16384e6:pieces20:" as *u8)
72 var i: i64 = 0; while i < 20 { idd[p] = (i+1) as u8; p = p + 1; i = i + 1 }
73 idd[p] = 101 as u8; p = p + 1 // 'e' closes the dict
74 let N: i64 = p
75 let info_hash: *u8 = sys_mmap(20); sha1(idd, N, info_hash)
76 let pidb: *u8 = sys_mmap(20); var z: i64 = 0; while z < 20 { pidb[z] = 80 as u8; z = z + 1 }
77 let sa: *u8 = sys_mmap(16); md_sockaddr(sa, PORT, 127, 0, 0, 1)
78
79 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
80 var bound: i64 = 0
81 if lfd >= 0 {
82 let one: *u8 = sys_mmap(4); one[0]=1 as u8; one[1]=0 as u8; one[2]=0 as u8; one[3]=0 as u8
83 sys_setsockopt(lfd, SOL_SOCKET, 2, one, 4)
84 sys_set_socket_timeout(lfd, 3)
85 if sys_bind(lfd, sa, 16) >= 0 { if sys_listen(lfd, 1) >= 0 { bound = 1 } }
86 }
87 if bound == 0 { md_w(1, "META-GATE authored=organ bound=0 verdict=RED\n" as *u8); sys_exit(1); return 1 }
88
89 let pid: i64 = sys_fork()
90 if pid == 0 {
91 // MOCK PEER
92 let afd: i64 = sys_accept(lfd)
93 if afd >= 0 {
94 sys_set_socket_timeout(afd, 3)
95 let hb: *u8 = sys_mmap(128); md_read_n(afd, hb, 68)
96 let oh: *u8 = sys_mmap(128); nx_pw_build_handshake(info_hash, pidb, oh); oh[25] = (oh[25] as i64 | 0x10) as u8; md_write_n(afd, oh, 68)
97 let mb: *u8 = sys_mmap(PW_MAGIC_1024); md_read_msg(afd, mb, PW_MAGIC_1024) // client ext handshake
98 // mock ext handshake: ut_metadata=2, metadata_size=N
99 let eb: *u8 = sys_mmap(256)
100 var o: i64 = md_bstr(eb, 0, "d1:md11:ut_metadatai2ee13:metadata_sizei" as *u8)
101 o = md_bint(eb, o, N); o = md_bstr(eb, o, "ee" as *u8)
102 let om: *u8 = sys_mmap(512); let oml: i64 = md_build_ext(0, eb, o, om); md_write_n(afd, om, oml)
103 md_read_msg(afd, mb, PW_MAGIC_1024) // client ut_metadata request
104 // ut_metadata data: header + RAW info-dict
105 let db: *u8 = sys_mmap(PW_MAGIC_1024)
106 var d: i64 = md_bstr(db, 0, "d8:msg_typei1e5:piecei0e10:total_sizei" as *u8)
107 d = md_bint(db, d, N); d = md_bstr(db, d, "ee" as *u8)
108 var j: i64 = 0; while j < N { db[d + j] = idd[j]; j = j + 1 }
109 let dm: *u8 = sys_mmap(PW_MAGIC_2048); let dml: i64 = md_build_ext(1, db, d + N, dm); md_write_n(afd, dm, dml)
110 sys_close(afd)
111 }
112 sys_close(lfd)
113 sys_exit(0)
114 }
115
116 // CLIENT
117 let cfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
118 sys_set_socket_timeout(cfd, 3)
119 var conn_ok: i64 = 0
120 if nx_connect_bounded(cfd, sa, 16, NX_CONN_DEFAULT_MS) >= 0 { conn_ok = 1 }
121 var hs_ok: i64 = 0; var size_ok: i64 = 0; var fetched: i64 = 0; var verify: i64 = 0; var name_ok: i64 = 0
122 if conn_ok == 1 {
123 let ch: *u8 = sys_mmap(128); nx_pw_build_handshake(info_hash, pidb, ch); ch[25] = (ch[25] as i64 | 0x10) as u8; md_write_n(cfd, ch, 68)
124 let ph2: *u8 = sys_mmap(128)
125 if md_read_n(cfd, ph2, 68) == 68 {
126 let gih: *u8 = sys_mmap(20); let gpid: *u8 = sys_mmap(20)
127 if nx_pw_parse_handshake(ph2, 68, gih, gpid) == 1 { hs_ok = 1 }
128 }
129 if hs_ok == 1 {
130 // client ext handshake (ut_metadata=1)
131 let ceb: *u8 = sys_mmap(64); let cebl: i64 = md_bstr(ceb, 0, "d1:md11:ut_metadatai1eee" as *u8)
132 let cm: *u8 = sys_mmap(256); let cml: i64 = md_build_ext(0, ceb, cebl, cm); md_write_n(cfd, cm, cml)
133 let mb: *u8 = sys_mmap(PW_MAGIC_2048); let blen: i64 = md_read_msg(cfd, mb, PW_MAGIC_2048)
134 var utm_id: i64 = 0; var msize: i64 = 0
135 if blen > 2 { if nx_pw_msg_id(mb) == PW_EXTENDED { if (mb[5] as i64) == 0 {
136 let ntot: i64 = 4 + blen
137 let m_off: i64 = nx_bc_dict_get(mb, 6, ntot, "m" as *u8, 1)
138 if m_off >= 0 { let uo: i64 = nx_bc_dict_get(mb, m_off, ntot, "ut_metadata" as *u8, 11); if uo >= 0 { let iv: *i64 = sys_mmap(16) as *i64; if nx_bc_int(mb, uo, ntot, iv) >= 0 { utm_id = iv[0] } } }
139 let so: i64 = nx_bc_dict_get(mb, 6, ntot, "metadata_size" as *u8, 13); if so >= 0 { let iv2: *i64 = sys_mmap(16) as *i64; if nx_bc_int(mb, so, ntot, iv2) >= 0 { msize = iv2[0] } }
140 } } }
141 if msize == N { size_ok = 1 }
142 if utm_id > 0 {
143 // request metadata piece 0
144 let rqb: *u8 = sys_mmap(64); let rqbl: i64 = md_bstr(rqb, 0, "d8:msg_typei0e5:piecei0ee" as *u8)
145 let rm: *u8 = sys_mmap(128); let rml: i64 = md_build_ext(utm_id, rqb, rqbl, rm); md_write_n(cfd, rm, rml)
146 // read data msg
147 let db2: *u8 = sys_mmap(PW_MAGIC_2048); let dblen: i64 = md_read_msg(cfd, db2, PW_MAGIC_2048)
148 if dblen > 2 { if nx_pw_msg_id(db2) == PW_EXTENDED { if (db2[5] as i64) == 1 {
149 let dtot: i64 = 4 + dblen
150 let raw_off: i64 = nx_bc_skip(db2, 6, dtot) // end of bencoded header = start of raw info-dict
151 if raw_off > 0 {
152 let rawlen: i64 = dtot - raw_off
153 if rawlen == N { fetched = 1 }
154 // assemble (single piece) + verify
155 let meta: *u8 = sys_mmap(512); var q: i64 = 0; while q < rawlen { meta[q] = db2[raw_off + q]; q = q + 1 }
156 let gh: *u8 = sys_mmap(20); sha1(meta, rawlen, gh)
157 var se: i64 = 1; var x: i64 = 0; while x < 20 { if gh[x] != info_hash[x] { se = 0; x = 20 } else { x = x + 1 } }
158 verify = se
159 // parse the info-dict piece map
160 let nm: i64 = nx_bc_dict_get(meta, 0, rawlen, "name" as *u8, 4)
161 if nm >= 0 { let so2: *i64 = sys_mmap(16) as *i64; let sl2: *i64 = sys_mmap(16) as *i64
162 if nx_bc_str(meta, nm, rawlen, so2, sl2) >= 0 { if sl2[0] == 8 { if meta[so2[0]] == (116 as u8) { name_ok = 1 } } } } // 't' of test.bin
163 }
164 } } }
165 }
166 }
167 }
168 let st: *i64 = sys_mmap(16) as *i64; sys_wait4(pid, st, 0); sys_close(cfd); sys_close(lfd)
169
170 md_w(1, "META-GATE authored=organ conn_ok=" as *u8); md_wn(1, conn_ok)
171 md_w(1, " handshake_ok=" as *u8); md_wn(1, hs_ok)
172 md_w(1, " metadata_size_ok=" as *u8); md_wn(1, size_ok)
173 md_w(1, " fetched_len_ok=" as *u8); md_wn(1, fetched)
174 md_w(1, " sha1_eq_info_hash=" as *u8); md_wn(1, verify)
175 md_w(1, " name_parsed=" as *u8); md_wn(1, name_ok)
176 md_w(1, " N=" as *u8); md_wn(1, N)
177 var allok: i64 = 0
178 if conn_ok==1 { if hs_ok==1 { if size_ok==1 { if fetched==1 { if verify==1 { if name_ok==1 { allok = 1 } } } } } }
179 if allok == 1 { md_w(1, " verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
180 md_w(1, " verdict=RED\n" as *u8); sys_exit(1)
181 return 1
182}