Added bonus and refactor again
Added the following flags : -c to make a NUMBER of pings -f to add flood mode -i to set an interval of NUMBER seconds between pings -l to send a NUMBER of pings as fast as possible then swapping to normal mode -q to add quiet mode Refactored main.c : Made flags.c for avoiding the main() function becoming a masterclass and make the file more readable Moved some function from main.c to utils.c Made some change to mimic the behavior of the ping binary of inetutils2.0
This commit is contained in:
3
Makefile
3
Makefile
@@ -1,7 +1,8 @@
|
||||
NAME = ./ft_ping
|
||||
|
||||
SRCS = src/main.c \
|
||||
src/utils.c
|
||||
src/utils.c \
|
||||
src/flags.c
|
||||
|
||||
|
||||
OBJ_DIR = objs
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
[
|
||||
{
|
||||
"directory": "/nfs/homes/vvaas/42/ft_ping",
|
||||
"command": "clang++ -std=c++20 -I includes -c /nfs/homes/vvaas/42/ft_ping/src/main.c -o /nfs/homes/vvaas/42/ft_ping/objs/ma.o",
|
||||
"file": "/nfs/homes/vvaas/42/ft_ping/src/main.c"
|
||||
},
|
||||
{
|
||||
"directory": "/nfs/homes/vvaas/42/ft_ping",
|
||||
"command": "clang++ -std=c++20 -I includes -c /nfs/homes/vvaas/42/ft_ping/src/utils.c -o /nfs/homes/vvaas/42/ft_ping/objs/uti.o",
|
||||
"file": "/nfs/homes/vvaas/42/ft_ping/src/utils.c"
|
||||
}
|
||||
]
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define PACKET_SIZE 56
|
||||
#define PACKET_SIZE 64
|
||||
|
||||
struct icmp_header
|
||||
{
|
||||
@@ -35,11 +35,32 @@ struct packet_stats
|
||||
double timestamp_array[65536];
|
||||
};
|
||||
|
||||
struct flags
|
||||
{
|
||||
bool verbose;
|
||||
bool quiet;
|
||||
bool flood;
|
||||
double interval;
|
||||
int preload_count;
|
||||
int count;
|
||||
};
|
||||
|
||||
// PACKET UTILS
|
||||
uint16_t make_checksum(uint16_t *data, int len);
|
||||
double get_timestamp();
|
||||
struct in_addr get_addr_by_hostname(char *hostname);
|
||||
double get_stddev(double *timestamp_array);
|
||||
|
||||
// MATH
|
||||
double get_stddev(const double *timestamp_array);
|
||||
double get_avg(const double *timestamp_array);
|
||||
double get_min(const double *timestamp_array);
|
||||
double get_max(const double *timestamp_array);
|
||||
long ft_atoi(const char *nptr);
|
||||
|
||||
// VERBOSE
|
||||
void print_help(void);
|
||||
bool parse_opt(int argc, char **argv, struct flags *flags);
|
||||
|
||||
// STATS
|
||||
void print_recap(char *ip, const struct packet_stats stats);
|
||||
void fill_timestamp_array(struct packet_stats *sockstats, double time);
|
||||
double get_avg(double *timestamp_array);
|
||||
double get_min(double *timestamp_array);
|
||||
double get_max(double *timestamp_array);
|
||||
double get_timestamp();
|
||||
91
src/flags.c
Normal file
91
src/flags.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "ft_ping.h"
|
||||
|
||||
const char *flag_list[] =
|
||||
{
|
||||
"usage : ping [OPTIONS ...] [ADDRESS]",
|
||||
" -c [COUNT] stop after COUNT packets have been sent",
|
||||
" -f flood mode",
|
||||
" -i [NUMBER] send a ping at NUMBER seconds intervals",
|
||||
" -l [NUMBER] send NUMBER ping as fast as possible then continue in normal mode",
|
||||
" -q quiet mode",
|
||||
" -v, verbose output",
|
||||
" -?, print this help page",
|
||||
|
||||
};
|
||||
|
||||
void print_help(void)
|
||||
{
|
||||
for (int i = 0; flag_list[i]; i++)
|
||||
printf("%s\n", flag_list[i]);
|
||||
}
|
||||
|
||||
bool parse_opt(int argc, char **argv, struct flags *flags)
|
||||
{
|
||||
int flag;
|
||||
while ((flag = getopt(argc, argv, "c:fi:l:qv?")) != -1)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case 'v':
|
||||
flags->verbose = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
flags->quiet = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
print_help();
|
||||
return (false);
|
||||
|
||||
case 'i':
|
||||
if (flags->flood)
|
||||
{
|
||||
fprintf(stderr, "%s: -f and -i flags are incompatible\n%s\n", argv[0], flag_list[0]);
|
||||
return (false);
|
||||
}
|
||||
if (atof(optarg) <= 0)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid value '%s'\n%s\n", argv[0], optarg, flag_list[0]);
|
||||
return (false);
|
||||
}
|
||||
flags->interval = atof(optarg);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (ft_atoi(optarg) == -1)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid value '%s'\n%s\n", argv[0], optarg, flag_list[0]);
|
||||
return (false);
|
||||
}
|
||||
flags->count = ft_atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (flags->interval != -1)
|
||||
{
|
||||
fprintf(stderr, "%s: -f and -i flags are incompatible\n%s\n", argv[0], flag_list[0]);
|
||||
return (false);
|
||||
}
|
||||
flags->flood = true;
|
||||
flags->quiet = true;
|
||||
flags->interval = 0;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (ft_atoi(optarg) == -1)
|
||||
{
|
||||
fprintf(stderr, "%s: invalid value '%s'\n%s\n", argv[0], optarg, flag_list[0]);
|
||||
return (false);
|
||||
}
|
||||
flags->preload_count = ft_atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (argc < 2 || argv[optind] == NULL || argv[optind][0] == 0) // optind is a value given by getopt() that's equal to the first argument that isn't a flag
|
||||
{
|
||||
fprintf(stderr, "ERROR : usage : %s [OPTIONS ...] [ADDRESS]\n", argv[0]);
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
71
src/main.c
71
src/main.c
@@ -3,7 +3,7 @@
|
||||
struct packet_stats stats;
|
||||
bool loop = true;
|
||||
|
||||
int ft_ping(int sock, uint16_t seq, struct sockaddr_in *dst)
|
||||
static int ft_ping(const int sock, const uint16_t seq, const struct sockaddr_in *dst)
|
||||
{
|
||||
unsigned char data[PACKET_SIZE];
|
||||
struct icmp_header *icmp_hdr = (struct icmp_header *)data;
|
||||
@@ -25,7 +25,7 @@ int ft_ping(int sock, uint16_t seq, struct sockaddr_in *dst)
|
||||
return (1);
|
||||
}
|
||||
|
||||
int ft_recv(int sock, uint16_t seq, double start)
|
||||
static int ft_recv(const int sock, const uint16_t seq, const double start, const bool quiet)
|
||||
{
|
||||
unsigned char data[PACKET_SIZE];
|
||||
struct icmp_header *icmp_hdr = (struct icmp_header *)(data + 20);
|
||||
@@ -46,22 +46,23 @@ int ft_recv(int sock, uint16_t seq, double start)
|
||||
return (0);
|
||||
fill_timestamp_array(&stats, time);
|
||||
stats.n_packet_recv++;
|
||||
if (!quiet)
|
||||
printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%5.3fms\n", n_bytes, inet_ntoa(addr.sin_addr), icmp_hdr->seq, (uint8_t)data[8], time);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void handler(int code)
|
||||
static void signal_handler(int code)
|
||||
{
|
||||
(void)code;
|
||||
loop = 0;
|
||||
loop = false;
|
||||
}
|
||||
|
||||
void init_signal()
|
||||
static void init_signal()
|
||||
{
|
||||
signal(SIGINT, handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
}
|
||||
|
||||
bool init_socket(int *sock, struct sockaddr_in *dst, char *host)
|
||||
static bool init_socket(int *sock, struct sockaddr_in *dst, char *host)
|
||||
{
|
||||
struct timeval timeout;
|
||||
|
||||
@@ -87,58 +88,42 @@ bool init_socket(int *sock, struct sockaddr_in *dst, char *host)
|
||||
return (true);
|
||||
}
|
||||
|
||||
void print_recap(char *ip)
|
||||
{
|
||||
printf("--- %s ping statistics ---\n", ip);
|
||||
printf("%d packed transmitted, %d received, %0.0f%% packet loss\n", stats.n_packet_sent, stats.n_packet_recv, (double)(stats.n_packet_sent - stats.n_packet_recv) / stats.n_packet_sent * 100);
|
||||
printf("round-trip min/avg/max/stddev = %5.3f/%5.3f/%5.3f/%5.3f ms\n", get_min(stats.timestamp_array), get_avg(stats.timestamp_array), get_max(stats.timestamp_array), get_stddev(stats.timestamp_array));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int sock;
|
||||
struct sockaddr_in dst;
|
||||
int socket_fd;
|
||||
struct sockaddr_in dst_addr;
|
||||
char *ip;
|
||||
uint16_t seq = 1;
|
||||
double start;
|
||||
int flags;
|
||||
bool verbose = false;
|
||||
struct flags flags = {false, false, false, 1, -1, -1};
|
||||
|
||||
while ((flags = getopt(argc, argv, "v?")) != -1)
|
||||
{
|
||||
switch (flags)
|
||||
{
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stdout, "usage : %s {-v?} [ADRESS]\n", argv[0]);
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
if (argc < 2 || argv[optind] == NULL || argv[optind][0] == 0) // optind is a value given by getopt() that's equal to the first argument that isn't a flag
|
||||
{
|
||||
fprintf(stderr, "ERROR : usage : %s {-v?} [ADRESS]\n", argv[0]);
|
||||
if (parse_opt(argc, argv, &flags) == false)
|
||||
return (0);
|
||||
}
|
||||
init_signal();
|
||||
if (!init_socket(&sock, &dst, argv[optind]))
|
||||
if (!init_socket(&socket_fd, &dst_addr, argv[optind]))
|
||||
return (-1);
|
||||
ip = inet_ntoa(get_addr_by_hostname(argv[optind]));
|
||||
if (verbose)
|
||||
fprintf(stdout, "PING %s (%s) : %d data bytes, id 0x%04x = %d\n", argv[optind], ip, PACKET_SIZE, getpid(), getpid());
|
||||
if (flags.verbose)
|
||||
printf("PING %s (%s) : %d data bytes, id 0x%04x = %d\n", argv[optind], ip, (PACKET_SIZE - 8), getpid(), getpid());
|
||||
else
|
||||
fprintf(stdout, "PING %s (%s) : %d data bytes\n", argv[optind], ip, PACKET_SIZE);
|
||||
printf("PING %s (%s) : %d data bytes\n", argv[optind], ip, (PACKET_SIZE - 8));
|
||||
while (loop)
|
||||
{
|
||||
start = get_timestamp();
|
||||
if (ft_ping(sock, seq, &dst) == 0)
|
||||
if (ft_ping(socket_fd, seq, &dst_addr) == 0)
|
||||
break;
|
||||
while (!ft_recv(sock, seq, start));
|
||||
while (!ft_recv(socket_fd, seq, start, flags.quiet));
|
||||
seq++;
|
||||
sleep(1);
|
||||
if (flags.count != -1)
|
||||
flags.count--;
|
||||
if (flags.count == 0)
|
||||
break;
|
||||
if (flags.preload_count <= 0)
|
||||
usleep(flags.interval * 1000000);
|
||||
else
|
||||
flags.preload_count--;
|
||||
}
|
||||
print_recap(argv[optind]);
|
||||
close(sock);
|
||||
print_recap(argv[optind], stats);
|
||||
close(socket_fd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
39
src/utils.c
39
src/utils.c
@@ -42,7 +42,31 @@ void fill_timestamp_array(struct packet_stats *stats, double time)
|
||||
stats->timestamp_array[stats->n_packet_recv] = time;
|
||||
}
|
||||
|
||||
double get_min(double *timestamp_array)
|
||||
bool ft_isdigit(const char n)
|
||||
{
|
||||
return (n >= '0' && n <= '9');
|
||||
}
|
||||
|
||||
long ft_atoi(const char *nptr)
|
||||
{
|
||||
unsigned long i = 0;
|
||||
|
||||
if (nptr == NULL)
|
||||
return (-1);
|
||||
for (int j = 0; nptr[j]; j++)
|
||||
{
|
||||
if (!ft_isdigit(nptr[j]))
|
||||
return (-1);
|
||||
}
|
||||
while (*nptr)
|
||||
{
|
||||
i = i * 10 + *nptr - '0';
|
||||
nptr++;
|
||||
}
|
||||
return (i);
|
||||
}
|
||||
|
||||
double get_min(const double *timestamp_array)
|
||||
{
|
||||
// get the smallest element of the timestamp_array
|
||||
double min = timestamp_array[0];
|
||||
@@ -54,7 +78,7 @@ double get_min(double *timestamp_array)
|
||||
return (min);
|
||||
}
|
||||
|
||||
double get_max(double *timestamp_array)
|
||||
double get_max(const double *timestamp_array)
|
||||
{
|
||||
// get the biggest element of the timestamp_array
|
||||
double max = timestamp_array[0];
|
||||
@@ -66,7 +90,7 @@ double get_max(double *timestamp_array)
|
||||
return (max);
|
||||
}
|
||||
|
||||
double get_avg(double *timestamp_array)
|
||||
double get_avg(const double *timestamp_array)
|
||||
{
|
||||
// get the average of elements in timestamp_array
|
||||
double avg = 0;
|
||||
@@ -82,7 +106,7 @@ double get_avg(double *timestamp_array)
|
||||
return (avg /= i);
|
||||
}
|
||||
|
||||
double get_stddev(double *timestamp_array)
|
||||
double get_stddev(const double *timestamp_array)
|
||||
{
|
||||
// get the standard deviation of elements in timestamp_array
|
||||
float avg = get_avg(timestamp_array);
|
||||
@@ -96,3 +120,10 @@ double get_stddev(double *timestamp_array)
|
||||
}
|
||||
return (sqrt(variance));
|
||||
}
|
||||
|
||||
void print_recap(char *ip, const struct packet_stats stats)
|
||||
{
|
||||
printf("--- %s ping statistics ---\n", ip);
|
||||
printf("%d packed transmitted, %d received, %0.0f%% packet loss\n", stats.n_packet_sent, stats.n_packet_recv, (double)(stats.n_packet_sent - stats.n_packet_recv) / stats.n_packet_sent * 100);
|
||||
printf("round-trip min/avg/max/stddev = %5.3f/%5.3f/%5.3f/%5.3f ms\n", get_min(stats.timestamp_array), get_avg(stats.timestamp_array), get_max(stats.timestamp_array), get_stddev(stats.timestamp_array));
|
||||
}
|
||||
Reference in New Issue
Block a user