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:
vauden
2024-11-16 02:23:41 +01:00
parent eb125bde1a
commit 26d9f40030
7 changed files with 188 additions and 71 deletions

View File

View File

@@ -1,7 +1,8 @@
NAME = ./ft_ping
SRCS = src/main.c \
src/utils.c
src/utils.c \
src/flags.c
OBJ_DIR = objs

View File

@@ -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"
}
]

View File

@@ -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
View 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);
}

View File

@@ -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);
}

View File

@@ -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));
}