From 3717e01ab990d402af00e92f3cbb8600e50a9fac Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:45:38 +0200 Subject: [PATCH] feat: traceroute skel --- configure.ac | 1 + includes/internal/traceroute/cli_handlers.h | 14 ++++ includes/internal/traceroute/options.h | 65 +++++++++++++++++++ includes/internal/traceroute/parse_utils.h | 9 +++ includes/traceroute/cli.h | 11 ++++ includes/traceroute/ft_traceroute_const.h | 11 ++++ includes/traceroute/ft_traceroute_flags.h | 10 +++ includes/traceroute/traceroute.h | 18 +++++ src/Makefile.am | 2 +- src/traceroute/Makefile.am | 54 +++++++++++++++ src/traceroute/cli/config_free.c | 7 ++ src/traceroute/cli/handlers/handle_help.c | 14 ++++ src/traceroute/cli/handlers/handle_max_ttl.c | 15 +++++ src/traceroute/cli/handlers/handle_verbose.c | 11 ++++ src/traceroute/cli/handlers/handle_version.c | 13 ++++ src/traceroute/cli/handlers/handle_waittime.c | 14 ++++ src/traceroute/cli/handlers/option_map.c | 13 ++++ src/traceroute/cli/messages/error.c | 32 +++++++++ src/traceroute/cli/messages/help.c | 40 ++++++++++++ src/traceroute/cli/messages/version.c | 19 ++++++ src/traceroute/cli/parse.c | 39 +++++++++++ .../cli/parse_utils/parse_destination.c | 25 +++++++ src/traceroute/core/traceroute.c | 8 +++ src/traceroute/main.c | 25 +++++++ 24 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 includes/internal/traceroute/cli_handlers.h create mode 100644 includes/internal/traceroute/options.h create mode 100644 includes/internal/traceroute/parse_utils.h create mode 100644 includes/traceroute/cli.h create mode 100644 includes/traceroute/ft_traceroute_const.h create mode 100644 includes/traceroute/ft_traceroute_flags.h create mode 100644 includes/traceroute/traceroute.h create mode 100644 src/traceroute/Makefile.am create mode 100644 src/traceroute/cli/config_free.c create mode 100644 src/traceroute/cli/handlers/handle_help.c create mode 100644 src/traceroute/cli/handlers/handle_max_ttl.c create mode 100644 src/traceroute/cli/handlers/handle_verbose.c create mode 100644 src/traceroute/cli/handlers/handle_version.c create mode 100644 src/traceroute/cli/handlers/handle_waittime.c create mode 100644 src/traceroute/cli/handlers/option_map.c create mode 100644 src/traceroute/cli/messages/error.c create mode 100644 src/traceroute/cli/messages/help.c create mode 100644 src/traceroute/cli/messages/version.c create mode 100644 src/traceroute/cli/parse.c create mode 100644 src/traceroute/cli/parse_utils/parse_destination.c create mode 100644 src/traceroute/core/traceroute.c create mode 100644 src/traceroute/main.c diff --git a/configure.ac b/configure.ac index 0f4a4e5..02a0d22 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,7 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/ping/Makefile + src/traceroute/Makefile tests/Makefile ]) AC_OUTPUT diff --git a/includes/internal/traceroute/cli_handlers.h b/includes/internal/traceroute/cli_handlers.h new file mode 100644 index 0000000..21bb557 --- /dev/null +++ b/includes/internal/traceroute/cli_handlers.h @@ -0,0 +1,14 @@ +#ifndef TRACEROUTE_CLI_HANDLERS_H +#define TRACEROUTE_CLI_HANDLERS_H + +#include "cli.h" + +extern const struct option_descriptor g_options[]; + +int cli_handle_help(const char *arg, void *config); +int cli_handle_version(const char *arg, void *config); +int cli_handle_verbose(const char *arg, void *config); +int cli_handle_max_ttl(const char *arg, void *config); +int cli_handle_waittime(const char *arg, void *config); + +#endif diff --git a/includes/internal/traceroute/options.h b/includes/internal/traceroute/options.h new file mode 100644 index 0000000..64830c0 --- /dev/null +++ b/includes/internal/traceroute/options.h @@ -0,0 +1,65 @@ +#ifndef TRACEROUTE_CLI_OPT_H +#define TRACEROUTE_CLI_OPT_H + +#include +#include "cli.h" + +enum { + OPT_TR_ARG_HOPS = OPT_ARG_STRING + 1, + OPT_TR_ARG_SECONDS, +}; + +#define TRACEROUTE_OPTIONS_LIST \ + X( \ + 'h', \ + "help", \ + no_argument, \ + cli_handle_help, \ + OPT_ARG_NONE, \ + "Display this help and exit" \ + ) \ + X( \ + 'V', \ + "version", \ + no_argument, \ + cli_handle_version, \ + OPT_ARG_NONE, \ + "Display version information and exit" \ + ) \ + X( \ + 'v', \ + "verbose", \ + no_argument, \ + cli_handle_verbose, \ + OPT_ARG_NONE, \ + "Verbose output" \ + ) \ + X( \ + 'm', \ + "max-ttl", \ + required_argument, \ + cli_handle_max_ttl, \ + OPT_TR_ARG_HOPS, \ + "Set the max number of hops" \ + ) \ + X( \ + 'w', \ + "waittime", \ + required_argument, \ + cli_handle_waittime, \ + OPT_TR_ARG_SECONDS, \ + "Set the time to wait for a response" \ + ) + +#undef X +#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) + 1 +enum { TR_OPT_LEN = (0 TRACEROUTE_OPTIONS_LIST) }; + +#undef X +#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) \ + + (1 * (has_arg == no_argument) \ + + (2 * (has_arg == required_argument) \ + + (3 * (has_arg == optional_argument)))) +enum { TR_OPTSTR_LEN = (2 TRACEROUTE_OPTIONS_LIST) }; + +#endif diff --git a/includes/internal/traceroute/parse_utils.h b/includes/internal/traceroute/parse_utils.h new file mode 100644 index 0000000..7560c0b --- /dev/null +++ b/includes/internal/traceroute/parse_utils.h @@ -0,0 +1,9 @@ +#ifndef TRACEROUTE_CLI_PARSE_UTILS_H +#define TRACEROUTE_CLI_PARSE_UTILS_H + +#include +#include "traceroute/traceroute.h" + +int cli_parse_destination(const char *s, struct traceroute_config *config); + +#endif diff --git a/includes/traceroute/cli.h b/includes/traceroute/cli.h new file mode 100644 index 0000000..f850883 --- /dev/null +++ b/includes/traceroute/cli.h @@ -0,0 +1,11 @@ +#ifndef TRACEROUTE_CLI_H +#define TRACEROUTE_CLI_H + +#include +#include "traceroute/traceroute.h" + +enum cli_code cli_parse_arguments(int argc, char **argv, struct + traceroute_config *config); +void cli_config_free(struct traceroute_config *config); + +#endif diff --git a/includes/traceroute/ft_traceroute_const.h b/includes/traceroute/ft_traceroute_const.h new file mode 100644 index 0000000..8845c21 --- /dev/null +++ b/includes/traceroute/ft_traceroute_const.h @@ -0,0 +1,11 @@ +#ifndef FT_TRACEROUTE_CONST_H +#define FT_TRACEROUTE_CONST_H + +#define DEFAULT_MAX_TTL 30 +#define DEFAULT_NQUERIES 3 +#define DEFAULT_WAITTIME 5.0 + +#define MAX_TRACEROUTE_TTL 255 +#define MAX_NQUERIES 10 + +#endif diff --git a/includes/traceroute/ft_traceroute_flags.h b/includes/traceroute/ft_traceroute_flags.h new file mode 100644 index 0000000..4d94d09 --- /dev/null +++ b/includes/traceroute/ft_traceroute_flags.h @@ -0,0 +1,10 @@ +#ifndef FT_TRACEROUTE_FLAGS_H +#define FT_TRACEROUTE_FLAGS_H + +#define FLAG_TR_VERBOSE (1 << 0) +#define FLAG_TR_NO_DNS (1 << 1) + +#define HAS_FLAG(flags, flag) ((flags) & (flag)) +#define SET_FLAG(flags, flag) ((flags) |= (flag)) + +#endif diff --git a/includes/traceroute/traceroute.h b/includes/traceroute/traceroute.h new file mode 100644 index 0000000..5780bf3 --- /dev/null +++ b/includes/traceroute/traceroute.h @@ -0,0 +1,18 @@ +#ifndef TRACEROUTE_H +#define TRACEROUTE_H + +#include +#include + +struct traceroute_config { + const char *destination; + struct in_addr dest_ip; + uint8_t max_ttl; + uint8_t nqueries; + double waittime; + uint8_t flags; +}; + +int traceroute_run(const struct traceroute_config *config); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 8016aa9..85bcf87 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1 +1 @@ -SUBDIRS = ping +SUBDIRS = ping traceroute diff --git a/src/traceroute/Makefile.am b/src/traceroute/Makefile.am new file mode 100644 index 0000000..69b0eed --- /dev/null +++ b/src/traceroute/Makefile.am @@ -0,0 +1,54 @@ +bin_PROGRAMS = ft_traceroute +noinst_LTLIBRARIES = libtraceroute_core.la + +TRACEROUTE_VERSION = 0.0.1 +VERSION_HEADER = $(top_srcdir)/includes/version_gen.h + +BUILT_SOURCES = $(VERSION_HEADER) + +libtraceroute_core_la_SOURCES = \ + cli/parse.c \ + cli/config_free.c \ + cli/handlers/option_map.c \ + cli/handlers/handle_help.c \ + cli/handlers/handle_version.c \ + cli/handlers/handle_verbose.c \ + cli/handlers/handle_max_ttl.c \ + cli/handlers/handle_waittime.c \ + cli/messages/help.c \ + cli/messages/version.c \ + cli/messages/error.c \ + cli/parse_utils/parse_destination.c \ + core/traceroute.c + +BASE_CFLAGS = -std=c99 $(STRICT_CFLAGS) + +if ENABLE_DEBUG +EXTRA_CFLAGS = -g -O0 +else +EXTRA_CFLAGS = +endif + +if ENABLE_SANITIZERS +SANITIZER_FLAGS = -fsanitize=address,undefined +else +SANITIZER_FLAGS = +endif + +TRACEROUTE_CPPFLAGS = \ + -I $(top_srcdir)/includes \ + -I $(top_srcdir)/libcli/include \ + -D_GNU_SOURCE + +TRACEROUTE_CFLAGS = $(BASE_CFLAGS) $(EXTRA_CFLAGS) $(SANITIZER_FLAGS) + +libtraceroute_core_la_CPPFLAGS = $(TRACEROUTE_CPPFLAGS) +libtraceroute_core_la_CFLAGS = $(TRACEROUTE_CFLAGS) + +ft_traceroute_SOURCES = main.c +ft_traceroute_CPPFLAGS = $(TRACEROUTE_CPPFLAGS) +ft_traceroute_CFLAGS = $(TRACEROUTE_CFLAGS) +ft_traceroute_LDFLAGS = $(SANITIZER_FLAGS) +ft_traceroute_LDADD = \ + libtraceroute_core.la \ + $(top_builddir)/libcli/src/libcli.la diff --git a/src/traceroute/cli/config_free.c b/src/traceroute/cli/config_free.c new file mode 100644 index 0000000..9e86d0c --- /dev/null +++ b/src/traceroute/cli/config_free.c @@ -0,0 +1,7 @@ +#include "traceroute/cli.h" + +void +cli_config_free(struct traceroute_config *config) +{ + config->destination = NULL; +} diff --git a/src/traceroute/cli/handlers/handle_help.c b/src/traceroute/cli/handlers/handle_help.c new file mode 100644 index 0000000..392e4c1 --- /dev/null +++ b/src/traceroute/cli/handlers/handle_help.c @@ -0,0 +1,14 @@ +#include "compiler.h" +#include "traceroute/cli.h" +#include "internal/traceroute/cli_handlers.h" + +/* Forward declaration */ +void print_help(void); +/* ------------------- */ + +int +cli_handle_help(__unused const char *arg, __unused void *config) +{ + print_help(); + return CLI_EXIT_SUCCESS; +} diff --git a/src/traceroute/cli/handlers/handle_max_ttl.c b/src/traceroute/cli/handlers/handle_max_ttl.c new file mode 100644 index 0000000..cb7c2b9 --- /dev/null +++ b/src/traceroute/cli/handlers/handle_max_ttl.c @@ -0,0 +1,15 @@ +#include "traceroute/cli.h" +#include "traceroute/ft_traceroute_const.h" +#include "internal/cli/parse_utils.h" + +int +cli_handle_max_ttl(const char *arg, void *config_void) +{ + struct traceroute_config *config = (struct traceroute_config *)config_void; + uint64_t val; + + if (0 != cli_parse_uint64(arg, &val) || 0 == val || val > MAX_TRACEROUTE_TTL) + return CLI_ERROR; + config->max_ttl = (uint8_t)val; + return CLI_SUCCESS; +} diff --git a/src/traceroute/cli/handlers/handle_verbose.c b/src/traceroute/cli/handlers/handle_verbose.c new file mode 100644 index 0000000..4c06562 --- /dev/null +++ b/src/traceroute/cli/handlers/handle_verbose.c @@ -0,0 +1,11 @@ +#include "compiler.h" +#include "traceroute/cli.h" +#include "traceroute/ft_traceroute_flags.h" + +int +cli_handle_verbose(__unused const char *arg, void *config_void) +{ + struct traceroute_config *config = (struct traceroute_config *)config_void; + SET_FLAG(config->flags, FLAG_TR_VERBOSE); + return CLI_SUCCESS; +} diff --git a/src/traceroute/cli/handlers/handle_version.c b/src/traceroute/cli/handlers/handle_version.c new file mode 100644 index 0000000..e206ee9 --- /dev/null +++ b/src/traceroute/cli/handlers/handle_version.c @@ -0,0 +1,13 @@ +#include "compiler.h" +#include "traceroute/cli.h" + +/* Forward declaration */ +void print_version(void); +/* ------------------- */ + +int +cli_handle_version(__unused const char *arg, __unused void *config) +{ + print_version(); + return CLI_EXIT_SUCCESS; +} diff --git a/src/traceroute/cli/handlers/handle_waittime.c b/src/traceroute/cli/handlers/handle_waittime.c new file mode 100644 index 0000000..d53a813 --- /dev/null +++ b/src/traceroute/cli/handlers/handle_waittime.c @@ -0,0 +1,14 @@ +#include "traceroute/cli.h" +#include "internal/cli/parse_utils.h" + +int +cli_handle_waittime(const char *arg, void *config_void) +{ + struct traceroute_config *config = (struct traceroute_config *)config_void; + float val; + + if (0 != cli_parse_ufloat(arg, &val) || val <= 0.0f) + return CLI_ERROR; + config->waittime = (double)val; + return CLI_SUCCESS; +} diff --git a/src/traceroute/cli/handlers/option_map.c b/src/traceroute/cli/handlers/option_map.c new file mode 100644 index 0000000..4541ec4 --- /dev/null +++ b/src/traceroute/cli/handlers/option_map.c @@ -0,0 +1,13 @@ +#include "internal/traceroute/cli_handlers.h" +#include "internal/traceroute/options.h" + +#undef X +#define X(short_opt, long_opt, has_arg, handler, arg_type, desc) \ +{ short_opt, long_opt, has_arg, handler, arg_type, desc }, + +const struct option_descriptor g_options[] = { + TRACEROUTE_OPTIONS_LIST + {0, NULL, 0, NULL, OPT_ARG_NONE, NULL} +}; + +#undef X diff --git a/src/traceroute/cli/messages/error.c b/src/traceroute/cli/messages/error.c new file mode 100644 index 0000000..873a074 --- /dev/null +++ b/src/traceroute/cli/messages/error.c @@ -0,0 +1,32 @@ +#include +#include + +#include "traceroute/cli.h" +#include "version_gen.h" + +/* Forward declaration */ +static inline void print_suggest_help(void); +/* ------------------- */ + +enum cli_code +error_missing_dest(void) +{ + dprintf(STDERR_FILENO, "%s: usage error: Destination address required\n", + g_prog_name); + print_suggest_help(); + return CLI_ERROR; +} + +enum cli_code +error_invalid_dest(void) +{ + dprintf(STDERR_FILENO, "%s: unknown host\n", g_prog_name); + return CLI_ERROR; +} + +static inline void +print_suggest_help(void) +{ + dprintf(STDERR_FILENO, "Try '%s --help' for more information.\n", + g_prog_name); +} diff --git a/src/traceroute/cli/messages/help.c b/src/traceroute/cli/messages/help.c new file mode 100644 index 0000000..35c5b90 --- /dev/null +++ b/src/traceroute/cli/messages/help.c @@ -0,0 +1,40 @@ +#include + +#include "internal/traceroute/cli_handlers.h" +#include "internal/traceroute/options.h" + +/* Forward declaration */ +static inline const char *option_arg_type_to_str(int type); +/* ------------------- */ + +void +print_help(void) +{ + printf("ft_traceroute - Trace the route to a network host\n"); + printf("Usage: ft_traceroute [options] \n\n"); + printf("Options:\n"); + + for (size_t i = 0; i < TR_OPT_LEN; ++i) + { + const struct option_descriptor *opt = &g_options[i]; + const char *argstr = option_arg_type_to_str(opt->arg_type); + + printf(" -%c, --%-10s %-8s %s\n", + opt->short_opt, + opt->long_opt, + argstr[0] ? argstr : "", + opt->description); + } +} + +static inline const char * +option_arg_type_to_str(int type) +{ + switch (type) + { + case OPT_ARG_NONE: return ""; + case OPT_TR_ARG_HOPS: return ""; + case OPT_TR_ARG_SECONDS: return ""; + default: return ""; + } +} diff --git a/src/traceroute/cli/messages/version.c b/src/traceroute/cli/messages/version.c new file mode 100644 index 0000000..59ecb17 --- /dev/null +++ b/src/traceroute/cli/messages/version.c @@ -0,0 +1,19 @@ +#include + +#include "version_gen.h" + +void +print_version(void) +{ + printf("%s version %s\n", g_prog_name, PING_VERSION); + printf("Copyright (C) 2026 lohhiicccc\n"); + printf("License GPLv3+:"); + printf("GNU GPL version 3 or later \n"); + printf("This is free software: "); + printf("you are free to change and redistribute it.\n"); + printf("There is NO WARRANTY, to the extent permitted by law.\n"); + printf("\n"); +#if PING_HAS_GIT_COMMIT + printf("Build: %s (commit %s)\n", PING_BUILD_DATE, PING_GIT_COMMIT); +#endif +} diff --git a/src/traceroute/cli/parse.c b/src/traceroute/cli/parse.c new file mode 100644 index 0000000..da0caca --- /dev/null +++ b/src/traceroute/cli/parse.c @@ -0,0 +1,39 @@ +#include +#include + +#include "traceroute/cli.h" +#include "traceroute/ft_traceroute_const.h" +#include "internal/traceroute/cli_handlers.h" +#include "internal/traceroute/parse_utils.h" +#include "internal/traceroute/options.h" +#include "internal/cli/messages.h" + +/* Forward declaration */ +static void init_config(struct traceroute_config *config); +/* ------------------- */ + +enum cli_code +cli_parse_arguments(int argc, char **argv, struct traceroute_config *config) +{ + char opt_str[TR_OPTSTR_LEN]; + struct option long_opts[TR_OPT_LEN + 1]; + enum cli_code ret; + + init_config(config); + ret = cli_parse(argc, argv, config, g_options, TR_OPT_LEN, + opt_str, long_opts); + if (CLI_SUCCESS != ret) + return ret; + if (optind >= argc) + return error_missing_dest(); + return cli_parse_destination(argv[optind], config); +} + +static void +init_config(struct traceroute_config *config) +{ + memset(config, 0, sizeof(struct traceroute_config)); + config->max_ttl = DEFAULT_MAX_TTL; + config->nqueries = DEFAULT_NQUERIES; + config->waittime = DEFAULT_WAITTIME; +} diff --git a/src/traceroute/cli/parse_utils/parse_destination.c b/src/traceroute/cli/parse_utils/parse_destination.c new file mode 100644 index 0000000..adc4573 --- /dev/null +++ b/src/traceroute/cli/parse_utils/parse_destination.c @@ -0,0 +1,25 @@ +#include +#include + +#include "traceroute/traceroute.h" +#include "internal/traceroute/parse_utils.h" +#include "internal/cli/messages.h" + +int +cli_parse_destination(const char *s, struct traceroute_config *config) +{ + struct addrinfo hints; + struct addrinfo *res; + struct sockaddr_in sin; + + bzero(&hints, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + + if (0 != getaddrinfo(s, NULL, &hints, &res)) + return error_invalid_dest(); + memcpy(&sin, res->ai_addr, sizeof(sin)); + freeaddrinfo(res); + config->dest_ip = sin.sin_addr; + config->destination = s; + return 0; +} diff --git a/src/traceroute/core/traceroute.c b/src/traceroute/core/traceroute.c new file mode 100644 index 0000000..43c738c --- /dev/null +++ b/src/traceroute/core/traceroute.c @@ -0,0 +1,8 @@ +#include "compiler.h" +#include "traceroute/traceroute.h" + +int +traceroute_run(__unused const struct traceroute_config *config) +{ + return 0; +} diff --git a/src/traceroute/main.c b/src/traceroute/main.c new file mode 100644 index 0000000..66dc64b --- /dev/null +++ b/src/traceroute/main.c @@ -0,0 +1,25 @@ +#include + +#include "traceroute/cli.h" +#include "version_gen.h" + +char *g_prog_name = NULL; + +int +main(int argc, char **argv) +{ + struct traceroute_config config = {0}; + int ret = CLI_ERROR; + + g_prog_name = argv[0]; + cli_set_prog_name(g_prog_name); + ret = cli_parse_arguments(argc, argv, &config); + if (ret != CLI_SUCCESS) + goto cleanup; + + ret = traceroute_run(&config); + +cleanup: + cli_config_free(&config); + return (ret == CLI_ERROR) ? 2 : EXIT_SUCCESS; +}