From c6289081e7d486e89370e0379e510e1cbf3d3d24 Mon Sep 17 00:00:00 2001 From: starnakin Date: Thu, 20 Nov 2025 10:37:16 -0600 Subject: [PATCH] add: error message --- src/dns.c | 8 ++- src/icmp_error.h | 87 ++++++++++++++++++++++++++++++++ src/main.c | 126 +++++++++++++++++++++++------------------------ src/packet.c | 40 +++++++-------- src/packet.h | 3 +- src/print.c | 31 ++++++++++-- src/print.h | 2 +- 7 files changed, 200 insertions(+), 97 deletions(-) create mode 100644 src/icmp_error.h diff --git a/src/dns.c b/src/dns.c index 59b5106..546a3b0 100644 --- a/src/dns.c +++ b/src/dns.c @@ -24,9 +24,13 @@ int dns_lookup(struct hostenv *host) } struct addrinfo *first = responses; - struct in_addr *addr = &(((struct sockaddr_in *) first->ai_addr)->sin_addr); + + host->ip.sin_addr.s_addr = ((struct sockaddr_in *) first->ai_addr)->sin_addr.s_addr; + host->ip.sin_port = ((struct sockaddr_in *) first->ai_addr)->sin_port; + memcpy(host->ip.sin_zero, ((struct sockaddr_in *) first->ai_addr)->sin_zero, sizeof(host->ip.sin_zero)); + host->ip.sin_family = ((struct sockaddr_in *) first->ai_addr)->sin_family; - inet_ntop(first->ai_family, addr, host->ipstr, sizeof(char) * INET_ADDRSTRLEN); + inet_ntop(first->ai_family, &host->ip.sin_addr, host->ipstr, sizeof(char) * INET_ADDRSTRLEN); freeaddrinfo(responses); diff --git a/src/icmp_error.h b/src/icmp_error.h new file mode 100644 index 0000000..7d8954b --- /dev/null +++ b/src/icmp_error.h @@ -0,0 +1,87 @@ +#pragma once + +typedef enum +{ + ICMP_ECHOREPLY = 0, /* Echo Reply */ + ICMP_DEST_UNREACH = 3, /* Destination Unreachable */ + ICMP_SOURCE_QUENCH = 4, /* Source Quench */ + ICMP_REDIRECT = 5, /* Redirect (change route) */ + ICMP_ECHO = 8, /* Echo Request */ + ICMP_TIME_EXCEEDED = 11, /* Time Exceeded */ + ICMP_PARAMETERPROB = 12, /* Parameter Problem */ + ICMP_TIMESTAMP = 13, /* Timestamp Request */ + ICMP_TIMESTAMPREPLY = 14, /* Timestamp Reply */ + ICMP_INFO_REQUEST = 15, /* Information Request */ + ICMP_INFO_REPLY = 16, /* Information Reply */ + ICMP_ADDRESS = 17, /* Address Mask Request */ + ICMP_ADDRESSREPLY = 18 /* Address Mask Reply */ +} net_icmp_types; + + +typedef enum +{ + ICMP_NET_UNREACH = 0, /* Network Unreachable */ + ICMP_HOST_UNREACH = 1, /* Host Unreachable */ + ICMP_PROT_UNREACH = 2, /* Protocol Unreachable */ + ICMP_PORT_UNREACH = 3, /* Port Unreachable */ + ICMP_FRAG_NEEDED = 4, /* Fragmentation Needed/DF set */ + ICMP_SR_FAILED = 5, /* Source Route failed */ + ICMP_NET_UNKNOWN = 6, + ICMP_HOST_UNKNOWN = 7, + ICMP_HOST_ISOLATED = 8, + ICMP_NET_ANO = 9, + ICMP_HOST_ANO = 10, + ICMP_NET_UNR_TOS = 11, + ICMP_HOST_UNR_TOS = 12, + ICMP_PKT_FILTERED = 13, /* Packet filtered */ + ICMP_PREC_VIOLATION = 14, /* Precedence violation */ + ICMP_PREC_CUTOFF = 15 /* Precedence cut off */ +} net_icmp_unreach_codes; /* Codes for UNREACH*/ + +typedef enum +{ + ICMP_REDIR_NET = 0, + ICMP_REDIR_HOST = 1, + ICMP_REDIR_NETTOS = 2, + ICMP_REDIR_HOSTTOS = 3 +} net_icmp_redirect_code; /* Codes for REDIRECT*/ + +typedef enum +{ + ICMP_EXC_TTL = 0, + ICMP_EXC_FRAGTIME = 1 +} net_icmp_time_code; /* Codes for TIME_EXCEEDED. */ + +static const char *net_icmp_unreach_messages[] = +{ + [ICMP_NET_UNREACH] = "Network Unreachable", + [ICMP_HOST_UNREACH] = "Host Unreachable", + [ICMP_PROT_UNREACH] = "Protocol Unreachable", + [ICMP_PORT_UNREACH] = "Port Unreachable", + [ICMP_FRAG_NEEDED] = "Fragmentation Needed/DF set", + [ICMP_SR_FAILED] = "Source Route failed", + [ICMP_NET_UNKNOWN] = "Network Unknown", + [ICMP_HOST_UNKNOWN] = "Host Unknown", + [ICMP_HOST_ISOLATED] = "Host Isolated", + [ICMP_NET_ANO] = "Network Administratively Prohibited", + [ICMP_HOST_ANO] = "Host Administratively Prohibited", + [ICMP_NET_UNR_TOS] = "Network Unreachable for TOS", + [ICMP_HOST_UNR_TOS] = "Host Unreachable for TOS", + [ICMP_PKT_FILTERED] = "Packet filtered", + [ICMP_PREC_VIOLATION] = "Precedence violation", + [ICMP_PREC_CUTOFF] = "Precedence cut off" +}; + +static const char *net_icmp_redirect_messages[] = +{ + [ICMP_REDIR_NET] = "Redirect for Network", + [ICMP_REDIR_HOST] = "Redirect for Host", + [ICMP_REDIR_NETTOS] = "Redirect for Network with TOS", + [ICMP_REDIR_HOSTTOS] = "Redirect for Host with TOS" +}; + +static const char *net_icmp_time_messages[] = +{ + [ICMP_EXC_TTL] = "Time-to-live exceeded", + [ICMP_EXC_FRAGTIME] = "Fragment Reassembly Timeout" +}; \ No newline at end of file diff --git a/src/main.c b/src/main.c index d2ee26c..28948f2 100644 --- a/src/main.c +++ b/src/main.c @@ -69,7 +69,7 @@ value_error: return 2; } -static int get_setting(char * const *av, struct setting *setting) +static int get_setting(char * const *av, struct setting *settings) { struct param parameters[] = { {NULL, "?", OPTION, false}, @@ -83,25 +83,26 @@ static int get_setting(char * const *av, struct setting *setting) char *hostname = parsing(av, parameters); if (hostname == NULL) return 1; - setting->dest.hostname = hostname; + settings->dest.hostname = hostname; - setting->help = parameters[0].value; - setting->verbose = parameters[1].value; - setting->numeric_only = parameters[2].value; + 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, 0, 255, &ttl)) return 2; - setting->ttl = ttl; - if (parsing_number(parameters[4].value, 0, 2147483647, &setting->payload_size)) + settings->ttl = ttl; + if (parsing_number(parameters[4].value, 0, 2147483647, &settings->payload_size)) return 3; - if (parsing_number(parameters[5].value, 0, 2147483647, &setting->preload)) + if (parsing_number(parameters[5].value, 0, 2147483647, &settings->preload)) return 4; return 0; } -static int create_socket(struct setting const *setting) +static int create_socket(struct setting const *settings) { int ret; + (void)settings; int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); @@ -111,13 +112,13 @@ static int create_socket(struct setting const *setting) return -1; } - ret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, &setting->ttl, sizeof(uint8_t)); + ret = setsockopt(sockfd, IPPROTO_IP, IP_TTL, &settings->ttl, sizeof(uint8_t)); if (ret != 0) { print_err("Failed to setsockopt(): ttl"); return -1; } - + struct timeval tv = {1, 0}; ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); if (ret != 0) @@ -129,18 +130,25 @@ static int create_socket(struct setting const *setting) return sockfd; } -static int set_destination(struct hostenv *host) +static int preload(struct setting const *settings, int sockfd, char const *packet, size_t packet_size) { - if (dns_lookup(host)) - return 1; - - host->ip.sin_family = AF_INET; - host->ip.sin_port = htons(0); - inet_pton(AF_INET, host->ipstr, &host->ip.sin_addr); - + 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; } +int check_reply(struct sockaddr_in const *me, struct sockaddr_in const *sender, char *sent, char *received, size_t packet_size) +{ + if (((struct icmphdr *)sent)->type != 0) + return 0; + if (check_packet_conformity(sent, received, packet_size)) + return 1; + return memcmp(sender, me, sizeof(struct sockaddr_in)); +} + int main(int ac, char **av) { (void) ac; @@ -148,51 +156,40 @@ int main(int ac, char **av) struct setting settings; if (get_setting(av + 1, &settings)) - return 1; + 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 (set_destination(&settings.dest)) - return 1; - + if (dns_lookup(&settings.dest)) + goto error0; + int sockfd = create_socket(&settings); if (sockfd == -1) - return 1; + goto error0; char *packet = packet_create(settings.payload_size); if (packet == NULL) - { - close(sockfd); - return 2; - } + goto error1; char *buffer; - buffer = malloc(packet_size * sizeof(char)); + buffer = malloc((recv_packet_size) * sizeof(char)); if (buffer == NULL) { print_err("error: allocation failed."); - free(packet); - close(sockfd); - return 10; + goto error2; } + struct sockaddr_in sender; socklen_t len = sizeof(sender); bzero(&stats, sizeof(struct statistics)); print_header(&settings); - for (size_t i = 0; i < settings.payload_size; i++) - { - if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1) - { - print_err("error: send packet failed."); - free(packet); - close(sockfd); - return 2; - } - } + if (preload(&settings, sockfd, packet, packet_size)) + goto error3; while (loop) { @@ -202,47 +199,48 @@ int main(int ac, char **av) if (sendto(sockfd, packet, packet_size, 0, (struct sockaddr *) &settings.dest.ip, sizeof(settings.dest.ip)) == -1) { print_err("error: send packet failed."); - free(packet); - close(sockfd); - return 2; + goto error2; } - stats.packets_sent++; - int ret = 0; + ssize_t ret = 0; do { - ret = recvfrom(sockfd, buffer, packet_size, 0, (struct sockaddr *) &sender, &len) < 0; - if (ret == -1) + ret = recvfrom(sockfd, buffer, recv_packet_size, 0, (struct sockaddr *) &sender, &len); + if (ret < 0) { print_err("error: receive packet failed."); - close(sockfd); - free(buffer); - free(packet); - return 3; + goto error3; } gettimeofday(&stop, NULL); } - while ((ret > 1) && (len != packet_size && packet_check(buffer, packet_size) && packet_compare(packet, buffer, packet_size))); + while (((size_t) ret != recv_packet_size) || (check_reply(&settings.dest.ip, &sender, packet, buffer + sizeof(struct iphdr), packet_size))); - if (ret > 1) - { + if (((struct icmphdr *) buffer + sizeof(struct iphdr))->type == 0) stats.packets_received++; - print_recv(&settings, (struct icmphdr *)buffer, &start, &stop); - } - + + print_recv(&settings, (struct icmphdr*) (buffer + sizeof(struct iphdr)), &start, &stop, &sender); + sleep(1); if (packet_update(packet, settings.payload_size)) - { - close(sockfd); - free(buffer); - free(packet); - } + 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; } \ No newline at end of file diff --git a/src/packet.c b/src/packet.c index bc2701d..b7578c8 100644 --- a/src/packet.c +++ b/src/packet.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include #include @@ -26,8 +28,8 @@ static int fill_with_random(char *str, size_t nb) return 0; } -static unsigned short checksum(void *b, int len) { - unsigned short *buf = b; +static unsigned short checksum(void const *b, int len) { + unsigned short const *buf = b; unsigned int sum = 0; unsigned short result; @@ -41,12 +43,20 @@ static unsigned short checksum(void *b, int len) { return result; } -int packet_compare(const char *sent, const char *received, size_t packet_size) +int check_packet_conformity(char const *sent, char const *received, size_t packet_size) { - const struct icmphdr *sent_hdr = (const struct icmphdr*) sent; - const struct icmphdr *received_hdr = (const struct icmphdr*) received; + struct icmphdr const *sent_hdr = (struct icmphdr const *) sent; + struct icmphdr *received_hdr = (struct icmphdr *) received; - if (received_hdr->un.echo.sequence == sent_hdr->un.echo.sequence) + if (received_hdr->un.echo.sequence != sent_hdr->un.echo.sequence) + return 1; + + const uint16_t checksum_bak = received_hdr->checksum; + received_hdr->checksum = 0; + const uint16_t tmp = checksum(received, packet_size); + received_hdr->checksum = checksum_bak; + + if (checksum_bak != tmp) return 2; const char *sent_payload = sent + sizeof(struct icmphdr); @@ -55,25 +65,9 @@ int packet_compare(const char *sent, const char *received, size_t packet_size) return memcmp(sent_payload, received_payload, packet_size - sizeof(struct icmphdr)); } -int packet_check(char *packet, size_t packet_size) -{ - struct icmphdr *hdr = (struct icmphdr *) packet; - - if (hdr->type != 0 || hdr->code != 8) - return 1; - - const uint16_t checksum_bak = hdr->checksum; - hdr->checksum = 0; - int tmp = checksum(packet, packet_size); - hdr->checksum = checksum_bak; - - return tmp - checksum_bak; -} - - int packet_update(char *packet, size_t payload_size) { - static size_t sequence = 1; + static uint16_t sequence = 0; size_t hdr_size = sizeof(struct icmphdr); size_t packet_size = hdr_size + payload_size; diff --git a/src/packet.h b/src/packet.h index 32ffe59..5adbc63 100644 --- a/src/packet.h +++ b/src/packet.h @@ -2,7 +2,6 @@ #include -int packet_compare(const char *sent, const char *received, size_t packet_size); -int packet_check(char *packet, size_t packet_size); +int check_packet_conformity(char *sent, char *received, size_t packet_size); int packet_update(char *packet, size_t payload_size); void *packet_create(size_t payload_size); \ No newline at end of file diff --git a/src/print.c b/src/print.c index db4b288..5744e0d 100644 --- a/src/print.c +++ b/src/print.c @@ -1,8 +1,10 @@ +#include "icmp_error.h" #include "interval.h" #include "setting.h" #include "statistics.h" +#include #include #include #include @@ -47,16 +49,35 @@ void print_statistics(struct statistics const *stats, struct setting const *sett stats->packets_received, (double) stats->packets_received * 100 / (double) stats->packets_sent ); + // TODO fix packet lost calcul } -void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop) +static const char *get_message_description(const uint8_t type, const uint8_t code) { + switch(type) + { + case 0: + return "icmp_seq=%d ttl=%d time=%.3f ms"; + case ICMP_DEST_UNREACH: + return (net_icmp_unreach_messages[code]); + case ICMP_REDIRECT: + return (net_icmp_redirect_messages[code]); + case ICMP_TIME_EXCEEDED: + return (net_icmp_time_messages[code]); + } + return (NULL); +} - printf("%zu bytes from %s: icmp_seq=%d ttl=%d time=%.3f ms\n", - settings->payload_size + sizeof(struct icmphdr), - settings->dest.ipstr, +void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop, struct sockaddr_in const *sender) +{ + char *ipstr = inet_ntoa(sender->sin_addr); + printf("%zu", settings->payload_size + sizeof(struct icmphdr) + ((header->type == 0) ? 0 : sizeof(struct iphdr))); + printf(" bytes from %s: ", ipstr); + printf(get_message_description(header->type, header->code), htons(header->un.echo.sequence), settings->ttl, get_interval(start, stop) ); -} \ No newline at end of file + printf("\n"); +} + diff --git a/src/print.h b/src/print.h index 1044abd..b3241cb 100644 --- a/src/print.h +++ b/src/print.h @@ -9,4 +9,4 @@ void print_err(const char *format, ...); void print_header(struct setting const *settings); void print_statistics(struct statistics const *stats, struct setting const *settings); -void print_recv(struct setting const *settings, struct icmphdr const *header, struct timeval const *start, struct timeval const *stop); \ No newline at end of file +void print_recv(const struct setting *settings, const struct icmphdr *header, const struct timeval *start, const struct timeval *stop, struct sockaddr_in const *sender); \ No newline at end of file