#include "dns.h" #include "host.h" #include "packet.h" #include "parsing.h" #include "print.h" #include "setting.h" #include "statistics.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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, 0, 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; } 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; 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); if (ret < 0) { print_err("error: receive packet failed.%zd", ret); goto error3; } gettimeofday(&stop, NULL); } while (((size_t) ret != recv_packet_size) || (check_reply(&settings.dest.ip, &sender, packet, buffer + sizeof(struct iphdr), packet_size))); if (((struct icmphdr *) buffer + sizeof(struct iphdr))->type == 0) stats.packets_received++; print_recv(&settings, (struct icmphdr*) (buffer + sizeof(struct iphdr)), &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; }