code wiki / _hdl_build / nx_hr_visitor_log.nx
nx_hr_visitor_log.nx source
↩ module page · 66 lines · 3831 B
1// nx_hr_visitor_log.nx -- NISHI HR, rung C: drive the front desk from REAL request logs. Parses a daemon access
2// log (one request per line: `ip <TAB> path <TAB> user-agent`), aggregates per-IP request counts over the window,
3// classifies each distinct visitor via hv_classify (rate + bot-UA + lists), writes a verdict report, and
4// AUTO-BLOCKLISTS the ones it BLOCKs (so the next request is dropped at the door). This turns the visitor policy
5// from a static list into BEHAVIOR-driven enforcement. Composes nx_hr_visitor. (Per-path anomaly + log rotation
6// + dedup of the auto-blocklist = later rungs; this is the core behavior sweep.) license_tier: ORIGINAL
7import "nx_hr_visitor.nx" // hv_classify / HV_BLOCK / HV_WELCOME
8import "nx_hr.nx" // hr_read / hr_line_end / hr_field_end / hr_eqrange
9import "nx_syscalls.nx"
10const K_MAGIC_1048576: i64 = 1048576
11
12// number of request lines whose field0 (ip) == ip.
13func hvl_count_ip(buf: *u8, n: i64, ip: *u8, iplen: i64) -> i64 {
14 var c: i64=0; var i: i64=0
15 while i < n { let le: i64=hr_line_end(buf,i,n); let f0e: i64=hr_field_end(buf,i,le); if hr_eqrange(buf,i,f0e,ip,iplen)==1 { c=c+1 } i=le+1 }
16 return c
17}
18// is the line at `before` the FIRST occurrence of this ip? (so each distinct visitor is classified once)
19func hvl_is_first(buf: *u8, before: i64, ip: *u8, iplen: i64) -> i64 {
20 var i: i64=0
21 while i < before { let le: i64=hr_line_end(buf,i,before); let f0e: i64=hr_field_end(buf,i,le); if hr_eqrange(buf,i,f0e,ip,iplen)==1 { return 0 } i=le+1 }
22 return 1
23}
24// copy field2 (user-agent) of the line buf[ls..le) into out (NUL-term).
25func hvl_ua_of_line(buf: *u8, ls: i64, le: i64, out: *u8, cap: i64) -> i64 {
26 let f0e: i64=hr_field_end(buf,ls,le); let f1e: i64=hr_field_end(buf,f0e+1,le); let f2s: i64=f1e+1
27 var o: i64=0; var i: i64=f2s
28 while i < le { if o < cap-1 { out[o]=buf[i]; o=o+1 } i=i+1 }
29 out[o]=0 as u8; return o
30}
31// append an ip to the blocklist (atomic O_APPEND).
32func hvl_append_block(block_path: *u8, ip: *u8, iplen: i64) -> i64 {
33 let line: *u8 = sys_mmap(128); var k: i64=0; while k < iplen { line[k]=ip[k]; k=k+1 } line[k]=10 as u8; k=k+1
34 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, block_path as i64, O_WRONLY_CA, 0x1a4, 0, 0)
35 if fd < 0 { return 0-1 }
36 sys_write(fd, line, k); sys_close(fd); return 0
37}
38
39// THE BEHAVIOR SWEEP: classify every distinct visitor in the log; write `ip <TAB> verdict` to report; auto-block
40// each BLOCK. Returns the number auto-blocked.
41func hvl_sweep(log_path: *u8, window_sec: i64, block_path: *u8, welcome_path: *u8, report_path: *u8) -> i64 {
42 let buf: *u8 = sys_mmap(K_MAGIC_1048576); let n: i64 = hr_read(log_path, buf, K_MAGIC_1048576)
43 let rfd: i64 = sys_openat_wr(report_path, 0x1a4)
44 var blocked: i64 = 0; var i: i64 = 0
45 while i < n {
46 let le: i64 = hr_line_end(buf, i, n)
47 let f0e: i64 = hr_field_end(buf, i, le)
48 let iplen: i64 = f0e - i
49 if iplen > 0 {
50 let ip: *u8 = ((buf as i64) + i) as *u8
51 if hvl_is_first(buf, i, ip, iplen) == 1 {
52 let count: i64 = hvl_count_ip(buf, n, ip, iplen)
53 let ua: *u8 = sys_mmap(512); hvl_ua_of_line(buf, i, le, ua, 512)
54 let v: i64 = hv_classify(ip, iplen, count, window_sec, ua, block_path, welcome_path)
55 if rfd >= 0 {
56 sys_write(rfd, ip, iplen)
57 if v == HV_BLOCK { sys_write(rfd, "\tBLOCK\n" as *u8, 7) } else { if v == HV_WELCOME { sys_write(rfd, "\tWELCOME\n" as *u8, 9) } else { sys_write(rfd, "\tNEUTRAL\n" as *u8, 9) } }
58 }
59 if v == HV_BLOCK { hvl_append_block(block_path, ip, iplen); blocked = blocked + 1 }
60 }
61 }
62 i = le + 1
63 }
64 if rfd >= 0 { sys_close(rfd) }
65 return blocked
66}