Nothing to see here, move along
1#![no_std]
2#![no_main]
3
4use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter};
5use lancer_user::io::write_bytes;
6use lancer_user::net;
7use lancer_user::syscall;
8
9const MAX_PINGS: u8 = 8;
10const DEFAULT_COUNT: u8 = 4;
11
12enum Target {
13 V4([u8; 4]),
14 V6([u8; 16]),
15}
16
17struct DnsResult {
18 v6: Option<[u8; 16]>,
19 v4: Option<[u8; 4]>,
20}
21
22#[unsafe(no_mangle)]
23pub extern "C" fn lancer_main() -> ! {
24 let (rx, tx) = match net::init() {
25 Some(pair) => pair,
26 None => {
27 lancer_user::show!(net, error, "netsock init failed");
28 syscall::exit();
29 }
30 };
31
32 if !net::has_relay() {
33 write_bytes(b"networking not available (use SSH)\n");
34 syscall::exit();
35 }
36
37 let mut args_buf = [0u8; 128];
38 let args_len = net::recv_args(&rx, &mut args_buf);
39
40 let args = &args_buf[..args_len];
41
42 let (cmd_tok, rest) = net::next_token(args);
43 let after_cmd = match net::bytes_eq(cmd_tok, b"ping") {
44 true => rest,
45 false => args,
46 };
47
48 let (host_tok, remainder) = net::next_token(after_cmd);
49 if host_tok.is_empty() {
50 write_bytes(b"usage: ping <host|ip> [count]\n");
51 syscall::exit();
52 }
53
54 let (count_tok, _) = net::next_token(remainder);
55 let count = match count_tok.is_empty() {
56 true => DEFAULT_COUNT,
57 false => match net::parse_decimal_u16(count_tok) {
58 Some(n) if n >= 1 && n <= MAX_PINGS as u16 => n as u8,
59 Some(_) => MAX_PINGS,
60 None => DEFAULT_COUNT,
61 },
62 };
63
64 let target = match net::parse_ip(host_tok) {
65 Some(ip) => Target::V4(ip),
66 None => match net::parse_ipv6(host_tok) {
67 Some(addr) => Target::V6(addr),
68 None => pick_target(&rx, &tx, host_tok),
69 },
70 };
71
72 write_bytes(b"pinging...\n");
73 send_ping(&tx, &target, count);
74 recv_results(&rx, true);
75 syscall::exit()
76}
77
78fn pick_target(rx: &PacketRingReader, tx: &PacketRingWriter, hostname: &[u8]) -> Target {
79 let dns = resolve_both(rx, tx, hostname);
80 match (dns.v6, dns.v4) {
81 (None, None) => {
82 write_bytes(b"DNS lookup failed\n");
83 syscall::exit()
84 }
85 (Some(v6), None) => Target::V6(v6),
86 (None, Some(v4)) => Target::V4(v4),
87 (Some(v6), Some(v4)) => {
88 send_ping(tx, &Target::V6(v6), 1);
89 let (received, _) = recv_results(rx, false);
90 match received > 0 {
91 true => Target::V6(v6),
92 false => Target::V4(v4),
93 }
94 }
95 }
96}
97
98fn resolve_both(rx: &PacketRingReader, tx: &PacketRingWriter, hostname: &[u8]) -> DnsResult {
99 let mut msg = [0u8; 62];
100 let len = hostname.len().min(61);
101 msg[1..1 + len].copy_from_slice(&hostname[..len]);
102 let mut resp_buf = [0u8; 64];
103
104 msg[0] = net::MSG_DNS_QUERY6;
105 net::send_request(tx, &msg[..1 + len]);
106 let n = net::recv_response_blocking(rx, &mut resp_buf);
107 let v6 = match n >= 18 && resp_buf[0] == net::MSG_DNS_RESULT6 && resp_buf[1] == 0 {
108 true => {
109 let mut addr = [0u8; 16];
110 addr.copy_from_slice(&resp_buf[2..18]);
111 Some(addr)
112 }
113 false => None,
114 };
115
116 msg[0] = net::MSG_DNS_QUERY;
117 net::send_request(tx, &msg[..1 + len]);
118 let n = net::recv_response_blocking(rx, &mut resp_buf);
119 let v4 = match n >= 6 && resp_buf[0] == net::MSG_DNS_RESULT && resp_buf[1] == 0 {
120 true => Some([resp_buf[2], resp_buf[3], resp_buf[4], resp_buf[5]]),
121 false => None,
122 };
123
124 DnsResult { v6, v4 }
125}
126
127fn send_ping(tx: &PacketRingWriter, target: &Target, count: u8) {
128 match target {
129 Target::V6(addr) => {
130 let mut req = [0u8; 18];
131 req[0] = net::MSG_PING_REQUEST6;
132 req[1..17].copy_from_slice(addr);
133 req[17] = count;
134 net::send_request(tx, &req);
135 }
136 Target::V4(ip) => {
137 let req = [net::MSG_PING_REQUEST, ip[0], ip[1], ip[2], ip[3], count];
138 net::send_request(tx, &req);
139 }
140 }
141}
142
143fn recv_results(rx: &PacketRingReader, print: bool) -> (u8, u8) {
144 let mut resp_buf = [0u8; 64];
145 let mut result = (0u8, 0u8);
146 let mut done = false;
147 core::iter::from_fn(|| {
148 if done {
149 return None;
150 }
151 let n = net::recv_response_blocking(rx, &mut resp_buf);
152 if n < 1 {
153 return Some(());
154 }
155 match resp_buf[0] {
156 net::MSG_PING_RESULT if n >= 4 && print => {
157 print_ping_line(&resp_buf, n);
158 }
159 net::MSG_PING_DONE if n >= 3 => {
160 let (received, total) = (resp_buf[1], resp_buf[2]);
161 if print {
162 print_done_line(received, total);
163 }
164 result = (received, total);
165 done = true;
166 }
167 _ => {}
168 }
169 Some(())
170 })
171 .take(64)
172 .count();
173 result
174}
175
176fn print_ping_line(resp_buf: &[u8], n: usize) {
177 let seq = resp_buf[1];
178 let status = resp_buf[2];
179 let mut out = [0u8; 40];
180 let mut pos = 0usize;
181 let prefix = b"seq=";
182 out[..prefix.len()].copy_from_slice(prefix);
183 pos += prefix.len();
184 pos += net::write_u8_decimal(&mut out[pos..], seq);
185 match status {
186 0 if n >= 5 => {
187 let rtt = ((resp_buf[3] as u16) << 8) | resp_buf[4] as u16;
188 let mid = b" time=";
189 out[pos..pos + mid.len()].copy_from_slice(mid);
190 pos += mid.len();
191 pos += net::write_u16_decimal(&mut out[pos..], rtt);
192 let suffix = b"ms\n";
193 out[pos..pos + suffix.len()].copy_from_slice(suffix);
194 pos += suffix.len();
195 }
196 _ => {
197 let suffix = b" timeout\n";
198 out[pos..pos + suffix.len()].copy_from_slice(suffix);
199 pos += suffix.len();
200 }
201 }
202 write_bytes(&out[..pos]);
203}
204
205fn print_done_line(received: u8, total: u8) {
206 let mut out = [0u8; 24];
207 let mut pos = 0usize;
208 pos += net::write_u8_decimal(&mut out[pos..], received);
209 out[pos] = b'/';
210 pos += 1;
211 pos += net::write_u8_decimal(&mut out[pos..], total);
212 let suffix = b" received\n";
213 out[pos..pos + suffix.len()].copy_from_slice(suffix);
214 pos += suffix.len();
215 write_bytes(&out[..pos]);
216}