144 lines
3.9 KiB
C
144 lines
3.9 KiB
C
#include <main.h>
|
|
|
|
const char* argp_program_version = "ft_ping 1.0";
|
|
const char* argp_program_bug_address = "<jrathelo@student.42nice.fr>";
|
|
static char doc[] = "Send ICMP ECHO_REQUEST packets to network hosts.\vMandatory or optional arguments to long options "
|
|
"are also mandatory or optional for any corresponding short options.\n\nOptions marked with (root "
|
|
"only) are available only to superuser.";
|
|
static char args_doc[] = "HOST ...";
|
|
|
|
#define ARG_TTL 256
|
|
|
|
static struct argp_option options[] = {
|
|
{0, 0, 0, 0, "Options valid for all request types:\n", 0},
|
|
{"verbose", 'v', 0, 0, "verbose output", 0},
|
|
{"ttl", ARG_TTL, "N", 0, "specify N as time-to-live", 0},
|
|
{0},
|
|
};
|
|
|
|
static error_t parse_opt(int key, char* arg, struct argp_state* state) {
|
|
struct arguments* arguments = state->input;
|
|
|
|
switch (key) {
|
|
case 'v':
|
|
arguments->verbose = 1;
|
|
break;
|
|
case ARG_TTL:
|
|
arguments->ttl = strtoul(arg, NULL, 10);
|
|
__attribute__ ((fallthrough));
|
|
case ARGP_KEY_ARG:
|
|
if (state->arg_num >= 2)
|
|
argp_usage(state);
|
|
arguments->args[state->arg_num] = arg;
|
|
break;
|
|
case ARGP_KEY_END:
|
|
if (state->arg_num < 1)
|
|
argp_usage(state);
|
|
break;
|
|
default:
|
|
return ARGP_ERR_UNKNOWN;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct argp argp = {options, parse_opt, args_doc, doc, NULL, NULL, NULL};
|
|
|
|
static bool stop = false;
|
|
|
|
int sock = 0;
|
|
|
|
static struct sockaddr_in destination;
|
|
|
|
unsigned long xmit = 0;
|
|
unsigned long recvd = 0;
|
|
|
|
double min = 0;
|
|
double max = 0;
|
|
double avg = 0;
|
|
|
|
uint16_t calc_checksum(void * b, const size_t l) {
|
|
uint16_t sum = 0;
|
|
|
|
for (size_t i = 0; i < l; i++) {
|
|
sum += *((uint16_t*)b + i);
|
|
}
|
|
|
|
return ~sum;
|
|
}
|
|
|
|
void send_ping() {
|
|
struct ping_pkt packet = {0};
|
|
|
|
packet.hdr.type = ICMP_ECHO;
|
|
packet.hdr.un.echo.id = getpid();
|
|
packet.hdr.un.echo.sequence = xmit++;
|
|
|
|
for (long unsigned i = 0; i < sizeof(packet.msg) - 1; ++i) {
|
|
packet.msg[i] = (char)i;
|
|
}
|
|
packet.msg[sizeof(packet.msg) - 1] = '\0';
|
|
|
|
packet.hdr.checksum = calc_checksum(&packet, sizeof(packet));
|
|
|
|
sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr*)&destination, sizeof(destination));
|
|
alarm(1);
|
|
}
|
|
|
|
void sig_handler(int sig) {
|
|
if (sig == SIGINT) {
|
|
stop = true;
|
|
} else if (sig == SIGALRM) {
|
|
send_ping();
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
struct arguments arguments;
|
|
arguments.verbose = 0;
|
|
arguments.ttl = 64;
|
|
argp_parse(&argp, argc, argv, 0, 0, &arguments);
|
|
|
|
memset(&destination, 0, sizeof(destination));
|
|
|
|
destination.sin_family = AF_INET;
|
|
destination.sin_addr.s_addr = inet_addr(arguments.args[0]);
|
|
if (destination.sin_addr.s_addr == INADDR_NONE) {
|
|
dprintf(sock, "%s: unknown host\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
destination.sin_port = htons(IPPORT_ECHO);
|
|
|
|
sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
|
|
|
if (sock < 0) {
|
|
dprintf(STDERR_FILENO, "%s: socket() failed: %s\n", argv[0], strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
if (setsockopt(sock, IPPROTO_IP, IP_TTL, &arguments.ttl, sizeof(arguments.ttl)) < 0) {
|
|
dprintf(STDERR_FILENO, "%s: setsockopt() failed: %s\n", argv[0], strerror(errno));
|
|
close(sock);
|
|
exit(1);
|
|
}
|
|
|
|
struct sigaction sig_handle;
|
|
|
|
sig_handle.sa_handler = sig_handler;
|
|
|
|
sigaction(SIGINT, &sig_handle, 0);
|
|
sigaction(SIGALRM, &sig_handle, 0);
|
|
|
|
alarm(1);
|
|
|
|
while (true) {
|
|
|
|
if (stop) {
|
|
break;
|
|
}
|
|
}
|
|
printf("---- %s ping statistics ----\n", arguments.args[0]);
|
|
printf("%zu packets transmitted, %zu packets received, %%zu%% packet loss\n", xmit,
|
|
recvd /*, (xmit - recv) / xmit * 100*/);
|
|
// printf("round-trip min/avg/max/stddev = %f/%f/%f/%f ms\n");
|
|
close(sock);
|
|
} |