244 lines
6.0 KiB
C
244 lines
6.0 KiB
C
#include "dns.h"
|
|
#include "host.h"
|
|
#include "packet.h"
|
|
#include "parsing.h"
|
|
#include "print.h"
|
|
#include "setting.h"
|
|
#include "statistics.h"
|
|
|
|
#include <netinet/ip.h>
|
|
#include <signal.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <stdarg.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
#include <stdbool.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <ctype.h>
|
|
#include <net/ethernet.h>
|
|
|
|
bool loop = true;
|
|
|
|
static void signal_handler(int code)
|
|
{
|
|
(void) code;
|
|
loop = false;
|
|
}
|
|
|
|
static int parsing_number(const char *str, size_t min, size_t max, size_t *out)
|
|
{
|
|
size_t number = 0;
|
|
const char *start = str;
|
|
|
|
while (*start != '\0')
|
|
{
|
|
if (!isdigit(*start))
|
|
goto value_error;
|
|
size_t tmp = number * 10 + *start - '0';
|
|
if (tmp < number)
|
|
goto range_error;
|
|
number = tmp;
|
|
start++;
|
|
}
|
|
|
|
if (min > number || number > max)
|
|
goto range_error;
|
|
|
|
*out = number;
|
|
|
|
return 0;
|
|
|
|
range_error:
|
|
print_err("invalid argument: '%s': out of range: %zu <= value <= %zu", str, min, max);
|
|
return 1;
|
|
|
|
value_error:
|
|
print_err("invalid argument: in '%s' '%c' is not a digit", str, *start);
|
|
return 2;
|
|
}
|
|
|
|
static int get_setting(char * const *av, struct setting *settings)
|
|
{
|
|
struct param parameters[] = {
|
|
{NULL, "?", OPTION, false},
|
|
{NULL, "v", OPTION, false},
|
|
{NULL, "n", OPTION, false},
|
|
{"ttl", NULL, ARGUMENT, "116"},
|
|
{NULL, "s", ARGUMENT, "56"},
|
|
{NULL, "p", ARGUMENT, "0"},
|
|
{NULL, NULL, 0, NULL},
|
|
};
|
|
char *hostname = parsing(av, parameters);
|
|
if (hostname == NULL)
|
|
return 1;
|
|
settings->dest.hostname = hostname;
|
|
|
|
settings->help = parameters[0].value;
|
|
settings->verbose = parameters[1].value;
|
|
settings->numeric_only = parameters[2].value;
|
|
size_t ttl;
|
|
if (parsing_number(parameters[3].value, 1, 255, &ttl))
|
|
return 2;
|
|
settings->ttl = ttl;
|
|
if (parsing_number(parameters[4].value, 0, 2147483647, &settings->payload_size))
|
|
return 3;
|
|
if (parsing_number(parameters[5].value, 0, 2147483647, &settings->preload))
|
|
return 4;
|
|
return 0;
|
|
}
|
|
|
|
static int create_socket(struct setting const *settings)
|
|
{
|
|
int ret;
|
|
(void)settings;
|
|
|
|
int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
|
|
|
if (sockfd == -1)
|
|
{
|
|
print_err("open socket failed, maybe try in root (:");
|
|
return -1;
|
|
}
|
|
|
|
ret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, &settings->ttl, sizeof(settings->ttl));
|
|
if (ret != 0)
|
|
{
|
|
close(sockfd);
|
|
print_err("Failed to setsockopt(): ttl");
|
|
return -1;
|
|
}
|
|
struct timeval tv = {2, 0};
|
|
ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
if (ret != 0)
|
|
{
|
|
close(sockfd);
|
|
print_err("Failed to setsockopt(): recv timeout");
|
|
return -1;
|
|
}
|
|
return sockfd;
|
|
}
|
|
|
|
static int preload(struct setting const *settings, int sockfd, char const *packet, size_t packet_size)
|
|
{
|
|
for (size_t i = 0; i < settings->preload; i++)
|
|
{
|
|
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings->dest.ip, sizeof(settings->dest.ip)) == -1)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int check_reply(struct sockaddr_in const *me, struct sockaddr_in const *sender, char *sent, char *received, size_t sent_size, size_t recv_size)
|
|
{
|
|
if (sent_size != recv_size)
|
|
return 0;
|
|
if (((struct icmphdr *)sent)->type != 0)
|
|
return 0;
|
|
if (check_packet_conformity(sent, received, sent_size))
|
|
return 1;
|
|
return memcmp(sender, me, sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
int main(int ac, char **av)
|
|
{
|
|
(void) ac;
|
|
struct statistics stats;
|
|
struct setting settings;
|
|
|
|
if (get_setting(av + 1, &settings))
|
|
goto error0;
|
|
|
|
size_t packet_size = sizeof(struct icmphdr) + settings.payload_size;
|
|
size_t recv_packet_size = packet_size + sizeof(struct iphdr);
|
|
|
|
signal(SIGINT, signal_handler);
|
|
|
|
if (dns_lookup(&settings.dest))
|
|
goto error0;
|
|
|
|
int sockfd = create_socket(&settings);
|
|
if (sockfd == -1)
|
|
goto error0;
|
|
|
|
char *packet = packet_create(settings.payload_size);
|
|
if (packet == NULL)
|
|
goto error1;
|
|
|
|
char *buffer;
|
|
buffer = malloc((recv_packet_size) * sizeof(char));
|
|
if (buffer == NULL)
|
|
{
|
|
print_err("error: allocation failed.");
|
|
goto error2;
|
|
}
|
|
|
|
struct sockaddr_in sender;
|
|
socklen_t len = sizeof(sender);
|
|
|
|
bzero(&stats, sizeof(struct statistics));
|
|
print_header(&settings);
|
|
|
|
if (preload(&settings, sockfd, packet, packet_size))
|
|
goto error3;
|
|
|
|
while (loop) {
|
|
|
|
struct timeval stop, start;
|
|
|
|
gettimeofday(&start, NULL);
|
|
if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1)
|
|
{
|
|
print_err("error: send packet failed.");
|
|
goto error2;
|
|
}
|
|
stats.packets_sent++;
|
|
|
|
ssize_t ret = 0;
|
|
do
|
|
{
|
|
ret = recvfrom(sockfd, buffer, recv_packet_size, 0, (struct sockaddr *) &sender, &len);
|
|
gettimeofday(&stop, NULL);
|
|
}
|
|
while (ret >= 0 && check_reply(&settings.dest.ip, &sender, packet, buffer + sizeof(struct iphdr), packet_size, ret));
|
|
|
|
if (ret >= (ssize_t) (sizeof(struct icmphdr) + sizeof(struct iphdr)) && ((struct icmphdr *) buffer + sizeof(struct iphdr))->type == 0)
|
|
{
|
|
stats.packets_received++;
|
|
print_recv(&settings, buffer, &start, &stop, &sender);
|
|
}
|
|
|
|
sleep(1);
|
|
|
|
if (packet_update(packet, settings.payload_size))
|
|
goto error3;
|
|
}
|
|
|
|
free(packet);
|
|
free(buffer);
|
|
close(sockfd);
|
|
|
|
print_statistics(&stats, &settings);
|
|
|
|
return 0;
|
|
|
|
error3:
|
|
free(buffer);
|
|
error2:
|
|
free(packet);
|
|
error1:
|
|
close(sockfd);
|
|
error0:
|
|
return 1;
|
|
} |