diff --git a/Makefile b/Makefile index 914bbc4c..09454818 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ kblayout \ kblayout-compiler \ login \ mkinitrd \ +ping \ regress \ rw \ sf \ diff --git a/ping/.gitignore b/ping/.gitignore new file mode 100644 index 00000000..f68190ac --- /dev/null +++ b/ping/.gitignore @@ -0,0 +1 @@ +ping diff --git a/ping/Makefile b/ping/Makefile new file mode 100644 index 00000000..d7e73f9b --- /dev/null +++ b/ping/Makefile @@ -0,0 +1,29 @@ +SOFTWARE_MEANT_FOR_SORTIX=1 +include ../build-aux/platform.mak +include ../build-aux/compiler.mak +include ../build-aux/version.mak +include ../build-aux/dirs.mak + +OPTLEVEL?=$(DEFAULT_OPTLEVEL) +CFLAGS?=$(OPTLEVEL) + +CFLAGS += -Wall -Wextra + +BINARIES = ping +#MANPAGES8 = ping.8 + +all: $(BINARIES) + +.PHONY: all install clean + +install: all + mkdir -p $(DESTDIR)$(SBINDIR) + install $(BINARIES) $(DESTDIR)$(SBINDIR) + #mkdir -p $(DESTDIR)$(MANDIR)/man8 + #install $(MANPAGES8) $(DESTDIR)$(MANDIR)/man8 + +%: %.c + $(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@ + +clean: + rm -f $(BINARIES) diff --git a/ping/ping.c b/ping/ping.c new file mode 100644 index 00000000..bdeee431 --- /dev/null +++ b/ping/ping.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2017 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * ping.c + * Internet Control Message Protocol Echo. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAYLOAD_SIZE 56 + +static void compact_arguments(int* argc, char*** argv) +{ + for ( int i = 0; i < *argc; i++ ) + { + while ( i < *argc && !(*argv)[i] ) + { + for ( int n = i; n < *argc; n++ ) + (*argv)[n] = (*argv)[n+1]; + (*argc)--; + } + } +} + +int main(int argc, char* argv[]) +{ + bool ipv4 = false; + bool ipv6 = false; + + for ( int i = 1; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' || !arg[1] ) + continue; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) + { + char c; + while ( (c = *++arg) ) switch ( c ) + { + case '4': ipv4 = true; break; + case '6': ipv6 = true; break; + default: + errx(1, "unknown option -- '%c'", c); + } + } + else + errx(1, "unknown option: %s", arg); + } + + compact_arguments(&argc, &argv); + + if ( argc < 2 ) + errx(1, "No host given"); + const char* host = argv[1]; + if ( 2 < argc ) + errx(1, "Unexpected extra operand `%s'", argv[3]); + + if ( 1 < ipv4 + ipv6 ) + errx(1, "The -4 and -6 options are mutually incompatible"); + + struct addrinfo hint; + memset(&hint, 0, sizeof(hint)); + if ( ipv4 ) + hint.ai_family = AF_INET; + if ( ipv6 ) + hint.ai_family = AF_INET6; + hint.ai_socktype = SOCK_DGRAM; + hint.ai_protocol = IPPROTO_PING; + + struct addrinfo* res0 = NULL; + int status = getaddrinfo(host, NULL, &hint, &res0); + if ( status == EAI_SYSTEM ) + err(1, "%s", host); + if ( status ) + errx(1, "%s: %s", host, gai_strerror(status)); + if ( !res0 ) + errx(1, "%s: %s", host, gai_strerror(EAI_NONAME)); + + char host_address[NI_MAXHOST]; + int fd; + for ( struct addrinfo* res = res0; res; res = res->ai_next ) + { + if ( (fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0 ) + { + if ( res->ai_next ) + continue; + err(1, "socket"); + } + if ( connect(fd, res->ai_addr, res->ai_addrlen) < 0 ) + { + close(fd); + if ( res->ai_next ) + continue; + err(1, "connect: %s", host); + } + if ( getnameinfo(res->ai_addr, res->ai_addrlen, host_address, + sizeof(host_address), NULL, 0, NI_NUMERICHOST) < 0 ) + strlcpy(host_address, "unknown", sizeof(host_address)); + break; + } + + freeaddrinfo(res0); + + printf("PING %s (%s) %zu bytes of data.\n", + host, host_address, (size_t) (PAYLOAD_SIZE + 8)); + + while ( true ) + { + unsigned char expected[PAYLOAD_SIZE]; + arc4random_buf(expected, sizeof(expected)); + struct timespec begun; + clock_gettime(CLOCK_MONOTONIC, &begun); + // TODO: Don't fail on network errors. + if ( send(fd, expected, sizeof(expected), 0) < 0 ) + err(1, "send"); + struct timespec timeout = timespec_add(timespec_make(1, 0), begun); + while ( true ) + { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + struct timespec remaining = timespec_sub(timeout, now); + if ( remaining.tv_sec < 0 ) + break; + struct pollfd pfd = { 0 }; + pfd.fd = fd; + pfd.events = POLLIN; + if ( ppoll(&pfd, 1, &remaining, NULL) <= 0 ) + break; + unsigned char gotten[PAYLOAD_SIZE]; + ssize_t amount = recv(fd, gotten, sizeof(gotten), 0); + struct timespec end; + clock_gettime(CLOCK_MONOTONIC, &end); + // TODO: Don't fail on network errors. + if ( amount < 0 ) + err(1, "recv"); + if ( amount == PAYLOAD_SIZE && + memcmp(expected, gotten, PAYLOAD_SIZE) == 0 ) + { + // TODO: Reverse DNS. + uint16_t sequence = gotten[0] << 8 | gotten[1] << 0; + struct timespec duration = timespec_sub(end, begun); + uintmax_t ms = (uintmax_t) duration.tv_sec * (uintmax_t) 1000 + + (uintmax_t) ((duration.tv_nsec / 1000) / 1000); + unsigned int us = (duration.tv_nsec / 1000) % 1000; + printf("%zu bytes from %s (%s): icmp_seq=%u time=%ju.%03u ms\n", + (size_t) (PAYLOAD_SIZE + 8), host, host_address, + sequence, ms, us); + } + } + } + + return 0; +} diff --git a/share/man/man4/ping.4 b/share/man/man4/ping.4 index 3b37e965..e00d2aba 100644 --- a/share/man/man4/ping.4 +++ b/share/man/man4/ping.4 @@ -474,7 +474,8 @@ socket options was attempted to be set to a non-zero value. .Xr if 4 , .Xr inet 4 , .Xr ip 4 , -.Xr kernel 7 +.Xr kernel 7 , +.Xr ping 8 .Sh STANDARDS .Rs .%A J. Postel (ed.)