~blainsmith/hare-icmp

5b4af293e0775765d226bedb35d8ddd8ea345b07 — Conrad Hoffmann 1 year, 3 months ago ea4577b main
Add a _very_ basic ping utility

Currently IPv4 only, for various reasons.

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>
1 files changed, 78 insertions(+), 0 deletions(-)

A cmd/ping/main.ha
A cmd/ping/main.ha => cmd/ping/main.ha +78 -0
@@ 0,0 1,78 @@
use fmt;
use getopt;
use io;
use memio;
use net::icmp;
use net::ip;
use os;
use rt;

export fn main() void = {

	const cmd = getopt::parse(os::args,
		"send ICMP echo requests",
		"address",
	);
	defer getopt::finish(&cmd);

	if (len(cmd.args) != 1) {
		fmt::fatal("Must specify exactly one target address");
	};
	let addr = ip::parse(cmd.args[0])!;
	let (icmp_proto, domain, proto) = match (addr) {
	case ip::addr4 =>
		yield (icmp::IP_V4, rt::AF_INET, rt::IPPROTO_ICMP);
	case ip::addr6 =>
		// TODO imcp::encode does not handle v6 atm
		// TODO rt is missing IPPROTO_ICMP6
		//yield (icmp::IP_V6, rt::AF_INET6, 58);
		fmt::fatal("Error: IPv6 is currently not supported!");
	};

	static let buf: [512]u8 = [0...];

	let s = match (rt::socket(domain: int, rt::SOCK_DGRAM, proto)) {
	case let s: int =>
		yield s;
	case let err: rt::errno =>
		fmt::fatalf("Failed to create socket: {}", rt::strerror(err));
	};
	defer io::close(s)!;

	// This would be nice, but rt currently doesn't know about SO_RCVTIMEO
	//let timeout = rt::timeval {
	//	tv_sec = 2,
	//	tv_usec = 0,
	//};
	//let i = rt::setsockopt(s, rt::SOL_SOCKET, rt::SO_RCVTIMEO, &timeout: *void, size(rt::timeval))!;

	let echo = icmp::echo {
		id = 1234,
		seq = 1,
		data = ['1': u8, '2': u8, '3': u8, '4': u8, '5': u8],
	};

	let inmsg = icmp::message {
		proto = proto,
		code = 0,
		checksum = 0,
		body = echo,
	};

	let outbuf = memio::fixed(buf[..]);
	let encsz = icmp::encode(&outbuf, &inmsg)!;
	let sa = ip::to_native(addr, 0);
	let data = memio::buffer(&outbuf);
	let addrsz = size(rt::sockaddr): u32;
	let sz = rt::sendto(s, data: *[*]u8, encsz, 0, &sa, addrsz);
	match (sz) {
	case let err: rt::errno =>
		fmt::fatalf("sendto: {}", rt::strerror(err));
	case let s: size =>
		yield s;
	};

	sz = rt::recvfrom(s, &buf: *[*]u8, 512, 0, &sa, &addrsz)!;
	let (from, port) = ip::from_native(sa);
	fmt::printfln("Received response from {}", ip::string(from))!;
};