Compare commits

38 Commits
main ... master

Author SHA1 Message Date
a5d663fa9a fix: parsing handling negative values for the ports and threads 2025-06-17 21:47:13 +02:00
ff89de7fdc fix: timeout changed to 2s + invalid free fix when the socket creation fails 2025-06-16 01:16:16 +02:00
ddbaef1826 fix: remove breakloop 2025-06-15 22:58:06 +02:00
67e6707d7d fix: parsing continue with short options 2025-06-13 11:52:16 +02:00
8299154ee1 fix: print and free last opened port 2025-06-12 07:37:24 -05:00
3ebface620 clean: add ttl and max_retries in usage print 2025-06-12 07:17:40 -05:00
f91bc39762 fix: leak when -h 2025-06-12 07:13:36 -05:00
957bb06d97 clean: remove useless arg 2025-06-12 07:12:34 -05:00
31530c83a4 fix: data races with getservent
feature: time elapsed in the print
2025-06-05 11:10:42 +02:00
eaf5913c29 feature: better print output
fix: remove useless mutex for the routine start
2025-06-05 10:45:00 +02:00
954f59649a core: remove nb_threads from struct scan and pass it as a parameter 2025-06-03 15:34:46 +02:00
9ae1e29f71 core: use only one struct data instead of thead_data and scan_data
core: do parsing in the main, put all in struct general
fix: code support port > 1024
2025-06-03 16:49:41 +02:00
d640c95224 feature: print (wip) 2025-06-03 13:05:52 +02:00
abb1d1f364 fix: ports parsing and change timeout to 5s for no_response 2025-05-31 17:18:21 +02:00
6164363cce fix: better response handling for prints 2025-05-31 17:10:48 +02:00
0f8d184aee feature: ttl complete 2025-05-31 16:44:27 +02:00
dbed6dc7cd feature: flags -F, --max_retries and --ttl (wip) 2025-05-31 16:37:24 +02:00
ee2af274d9 fix: overwrite of ports when multithreading
feature: service name
2025-05-31 10:21:18 +02:00
f35cad887d fix: pcap_dispatch is non blocking
feature: no response handle
2025-05-30 16:12:51 +02:00
936c277d02 feature: SCAN_ALL implemented (to be tested) 2025-05-30 14:42:56 +02:00
227000cf97 feature: use pthread_join() instead of pthread_detach and weird counter thing 2025-05-29 15:01:57 +02:00
a94bbf0671 fix: loop freeing parsing missing an element 2025-05-29 14:54:45 +02:00
2040551742 fix: thead leaks fixed 2025-05-29 16:52:38 +02:00
d0b1fa6329 fix: parsing leaks fixed 2025-05-29 14:52:09 +02:00
331d1077b9 wip: file of hosts as an argument 2025-05-29 14:25:03 +02:00
623b3ad0d7 feature: multithreading wip 2025-05-29 02:16:04 +02:00
6538a085b9 feature: parsing with getopt pretty much done (some checks tbd) 2025-05-29 00:10:41 +02:00
60f23e9250 core: improve opti with else if instead of if 2025-05-27 13:28:03 +02:00
e7ca63eaa6 clean: upgrade readability 2025-05-27 13:25:37 +02:00
ad367d6792 fix: struct were not passed correctly as the argument of *_response 2025-05-26 16:01:49 +02:00
675248cbff fix: various bug fixes on allocations and stuff (still wip) 2025-05-26 15:46:58 +02:00
1ce702d169 fix: check scan return 2025-05-26 16:48:02 +02:00
eb2d9b1ecf fix: get src addr
modified:   src/get_interface.c
2025-05-26 15:17:04 +02:00
3213a56d1b fix: add the dest address to conn_addr (bozo) 2025-05-26 12:43:50 +02:00
1ffb554a32 feature: send packets (reponse not working tho) 2025-05-26 12:18:06 +02:00
9378251248 fix: better response handling (to be tested once send packets will be done) 2025-05-23 11:33:55 +02:00
84d5960900 feature: start to implement responses 2025-05-22 17:08:26 +02:00
1361cd9f9c feature: default interface and basic start 2025-05-21 18:22:46 +02:00
23 changed files with 1163 additions and 11 deletions

6
.gitignore vendored
View File

@ -1,2 +1,6 @@
obj
ft_nmap
ft_nmap
.cache
compile_commands.json
tags
dlopen.supp

View File

@ -4,7 +4,7 @@ CC := gcc
CFLAGS := -Wall -Wextra -Werror -iquoteinclude -g
LD := $(CC)
LDFLAGS :=
LDFLAGS := -lpcap
SRC := $(shell find src -name '*.c')
OBJ := $(patsubst src/%.c,obj/%.o,$(SRC))
@ -16,7 +16,7 @@ obj/%.o: src/%.c
$(CC) $(CFLAGS) -c $< -o $@
$(NAME): $(OBJ)
$(LD) $(LDFLAGS) -o $(NAME) $(OBJ)
$(LD) -o $(NAME) $(OBJ) $(LDFLAGS)
clean:
rm -rf obj
@ -28,4 +28,4 @@ re:
$(MAKE) fclean
$(MAKE) all
.PHONY: all clean fclean re
.PHONY: all clean fclean re

6
include/dns.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <netinet/in.h>
int dns_lookup(char *ip_addr, const char *hostname);
int reverse_dns_lookup(char *ip_addr, char *host);

3
include/error.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
int err(char *str);

11
include/host.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <limits.h>
#include <linux/if.h>
#include <netinet/in.h>
struct host {
char name[HOST_NAME_MAX];
char ip[INET_ADDRSTRLEN];
char interface[IFNAMSIZ];
};

5
include/interface.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include "host.h"
int get_interface_name(struct host *host);

16
include/packet.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <stdint.h>
#include "scan.h"
struct pshdr {
uint32_t src_addr;
uint32_t dst_addr;
uint8_t placeholder;
uint8_t protocol;
uint16_t tcp_length;
};
int send_packet(const struct scan *data, int sockfd);
unsigned short checksum(void *data, int len);

33
include/parsing.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "scan.h"
typedef enum {
// long options
FL_HELP,
FL_PORTS,
FL_IP,
FL_FILE,
FL_SPEEDUP,
FL_TYPE,
FL_MAXRETRIES,
FL_TTL,
// short options
FL_FAST,
} e_flag;
struct option_lst {
e_flag flag;
char *arg;
struct option_lst *next;
};
struct option_lst *parse_options(int ac, char *const *av);
char *get_option_arg(const struct option_lst *options, e_flag flag);
bool option_isset(const struct option_lst *options, e_flag flag);
e_scantype parse_type(const char *arg);
void free_options(struct option_lst *options);
int parsing(struct scan *general, const struct option_lst *options);

9
include/print.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include "parsing.h"
#include "response.h"
void print_usage(void);
void print_config(const struct scan *general, const char *hosts_path,
uint8_t nb_threads);
void print_host_results(const struct scan *general, double scan_time);

32
include/response.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdint.h>
#include "scan.h"
#define TIMEOUT 2
typedef enum {
CLOSED,
OPENED,
FILTERED,
UNFILTERED,
OPENFILTERED,
} e_state;
[[__maybe_unused__]] static const char *states_str[] = {
"CLOSED", "OPENED", "FILTERED", "UNFILTERED", "OPENFILTERED",
};
struct response {
e_state states[SCAN_ALL];
char *service;
};
void tcp_response(const struct tcphdr *tcphdr, const struct scan *data);
void udp_response(const struct udphdr *udphdr, const struct scan *data);
void icmp_response(const struct icmphdr *icmphdr, const struct scan *data);
void no_response(const struct scan *data);

32
include/scan.h Normal file
View File

@ -0,0 +1,32 @@
#pragma once
#include <stdint.h>
#include "host.h"
typedef enum {
SCAN_NULL,
SCAN_SYN,
SCAN_ACK,
SCAN_FIN,
SCAN_XMAS,
SCAN_UDP,
SCAN_ALL,
} e_scantype;
[[__maybe_unused__]] static const char *types_str[] = {
"NULL", "SYN", "ACK", "FIN", "XMAS", "UDP",
};
struct scan {
struct host host;
char *dest_addr;
uint16_t port_start;
uint16_t port_end;
e_scantype type;
uint8_t ttl;
uint8_t max_retries;
struct response *responses;
};
int scan(struct scan *data);

7
include/thread.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include "scan.h"
int create_threads(struct scan *general, uint8_t nb_threads);

13
src/checksum.c Normal file
View File

@ -0,0 +1,13 @@
#include <stddef.h>
unsigned short checksum(void *data, int len)
{
unsigned short *cpy = data;
unsigned int sum = 0;
for (int i = 0; i < len; i += 2)
sum += *cpy++;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return ~sum;
}

37
src/dns.c Normal file
View File

@ -0,0 +1,37 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
int dns_lookup(char *ip_addr, const char *hostname)
{
struct hostent *host = gethostbyname2(hostname, AF_INET);
if (!host) {
dprintf(2,
"Hostname '%s' doesn't exist or has invalid format.\n",
hostname);
return -1;
}
strcpy(ip_addr, inet_ntoa(*(struct in_addr *)host->h_addr));
return 0;
}
int reverse_dns_lookup(char *ip_addr, char *host)
{
struct sockaddr_in tmp_addr;
tmp_addr.sin_family = AF_INET;
tmp_addr.sin_addr.s_addr = inet_addr(ip_addr);
if (getnameinfo((struct sockaddr *)&tmp_addr,
sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0,
NI_NAMEREQD)) {
dprintf(2, "Could not resolve reverse lookup of %s\n", ip_addr);
return -1;
}
return 0;
}

9
src/error.c Normal file
View File

@ -0,0 +1,9 @@
#include <errno.h>
#include <stdio.h>
int err(char *str)
{
int err = errno;
perror(str);
return err;
}

32
src/get_interface.c Normal file
View File

@ -0,0 +1,32 @@
#include <pcap.h>
#include <string.h>
#include "host.h"
int get_interface_name(struct host *host)
{
pcap_if_t *alldevs;
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs(&alldevs, errbuf) == -1) {
dprintf(2, "Error finding devices: %s\n", errbuf);
return -1;
}
for (pcap_if_t *it = alldevs; it != NULL; it = it->next) {
if (!(it->flags & PCAP_IF_LOOPBACK)) {
strcpy(host->interface, it->name);
for (pcap_addr_t *a = it->addresses; a != NULL; a = a->next) {
if (a->addr && a->addr->sa_family == AF_INET) {
struct sockaddr_in *sa = (struct sockaddr_in *)a->addr;
strcpy(host->ip, inet_ntoa(sa->sin_addr));
pcap_freealldevs(alldevs);
return 0;
}
}
}
}
dprintf(2, "No non-loopback interface found.\n");
return -1;
}

View File

@ -1,12 +1,111 @@
#include <getopt.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "dns.h"
#include "interface.h"
#include "parsing.h"
#include "print.h"
#include "scan.h"
#include "thread.h"
static int scan_host(struct scan *general, uint8_t nb_threads)
{
if (dns_lookup(general->dest_addr, general->dest_addr)) {
return -1;
}
struct response responses[1024] = {0};
struct timespec start, end;
general->responses = responses;
printf("Scanning...\n");
clock_gettime(CLOCK_MONOTONIC, &start);
if (create_threads(general, nb_threads) < 0)
return -1;
clock_gettime(CLOCK_MONOTONIC, &end);
double time_elapsed =
(end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9;
print_host_results(general, time_elapsed);
return 0;
}
int main(int ac, char **av)
{
(void) ac;
(void) av;
printf("%s %s\n",
"gros gras grand grain d'orge, quand te dégros-gras-grand-grain-d'orgeras-tu ?",
"Je me dégros-gras-grand-grain-d'orgerai, quand tous les gros gras grains d'orge se seront dégros-gras-grand-grain- d'orgés.");
if (ac < 2) {
dprintf(2, "Usage: ft_nmap [Scan Type(s)] [Options] {target "
"specification}\n");
print_usage();
return 0;
}
return 0;
}
struct option_lst *options = parse_options(ac, av);
if (options == NULL) {
print_usage();
return 1;
}
if (option_isset(options, FL_HELP)) {
free_options(options);
print_usage();
return 0;
}
struct scan general;
if (parsing(&general, options)) {
free_options(options);
return 1;
}
if (get_interface_name(&general.host) < 0)
return -1;
char *nb_threads_str = get_option_arg(options, FL_SPEEDUP);
uint8_t nb_threads = 1;
if (nb_threads_str)
nb_threads = atoi(nb_threads_str);
char *dest_addr = get_option_arg(options, FL_IP);
if (dest_addr) {
general.dest_addr = dest_addr;
print_config(&general, NULL, nb_threads);
int rv = scan_host(&general, nb_threads);
free_options(options);
return rv;
}
const char *hosts_path = get_option_arg(options, FL_FILE);
if (!hosts_path) {
dprintf(2, "ft_nmap: address/hostname required\n");
goto error;
}
FILE *hosts_file = fopen(hosts_path, "r");
if (hosts_file == NULL) {
dprintf(2, "ft_nmap: unable to open file '%s'\n", hosts_path);
goto error;
}
print_config(&general, hosts_path, nb_threads);
char line[256];
while (fgets(line, sizeof(line), hosts_file)) {
line[strcspn(line, "\n")] = '\0';
general.dest_addr = line;
if (scan_host(&general, nb_threads) < 0) {
fclose(hosts_file);
goto error;
}
}
fclose(hosts_file);
free_options(options);
return EXIT_SUCCESS;
error:
free_options(options);
return EXIT_FAILURE;
}

103
src/packet.c Normal file
View File

@ -0,0 +1,103 @@
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "packet.h"
#include "scan.h"
void create_udp_packet(struct udphdr *udphdr, const struct scan *data)
{
udphdr->source = htons(1234);
udphdr->dest = htons(data->port_start);
udphdr->len = sizeof(struct udphdr);
udphdr->check = checksum(udphdr, sizeof(struct udphdr));
}
int create_tcp_packet(struct tcphdr *tcphdr, const struct scan *data)
{
tcphdr->source = htons(1234);
tcphdr->dest = htons(data->port_start);
tcphdr->doff = sizeof(struct tcphdr) / sizeof(int);
tcphdr->fin = data->type == SCAN_XMAS || data->type == SCAN_FIN;
tcphdr->syn = data->type == SCAN_SYN;
tcphdr->psh = data->type == SCAN_XMAS;
tcphdr->ack = data->type == SCAN_ACK;
tcphdr->urg = data->type == SCAN_XMAS;
tcphdr->window = htons(5840);
tcphdr->urg_ptr = 0;
struct pshdr pshdr;
pshdr.src_addr = inet_addr(data->host.ip);
pshdr.dst_addr = inet_addr(data->dest_addr);
pshdr.placeholder = 0;
pshdr.protocol = IPPROTO_TCP;
pshdr.tcp_length = htons(sizeof(struct tcphdr));
size_t pseudogram_size = sizeof(struct pshdr) + sizeof(struct tcphdr);
void *pseudogram = malloc(pseudogram_size);
if (!pseudogram)
return -1;
memcpy(pseudogram, &pshdr, sizeof(pshdr));
memcpy(pseudogram + sizeof(pshdr), tcphdr, sizeof(struct tcphdr));
tcphdr->check = checksum(pseudogram, pseudogram_size);
free(pseudogram);
return 0;
}
static void *create_packet(const struct scan *data, size_t packet_size)
{
const bool isudp = data->type == SCAN_UDP;
void *packet = calloc(packet_size, 1);
if (!packet) {
dprintf(2,
"ft_nmap: allocation failed during packet creation\n");
return NULL;
}
struct iphdr *iphdr = packet;
iphdr->ihl = sizeof(struct iphdr) / sizeof(int);
iphdr->version = 4;
iphdr->tos = 0;
iphdr->tot_len = packet_size;
iphdr->id = htons(54321);
iphdr->frag_off = 0;
iphdr->ttl = data->ttl;
iphdr->protocol = isudp ? IPPROTO_UDP : IPPROTO_TCP;
iphdr->saddr = inet_addr(data->host.ip);
iphdr->daddr = inet_addr(data->dest_addr);
iphdr->check = checksum(packet, packet_size);
if (isudp)
create_udp_packet(packet + sizeof(struct iphdr), data);
else
create_tcp_packet(packet + sizeof(struct iphdr), data);
return packet;
}
int send_packet(const struct scan *data, int sockfd)
{
struct sockaddr_in conn_addr;
conn_addr.sin_family = AF_INET;
conn_addr.sin_port = htons(80);
conn_addr.sin_addr.s_addr = inet_addr(data->dest_addr);
size_t packet_size = sizeof(struct iphdr) +
(data->type == SCAN_UDP ? sizeof(struct udphdr)
: sizeof(struct tcphdr));
void *packet = create_packet(data, packet_size);
sendto(sockfd, packet, packet_size, 0, (struct sockaddr *)&conn_addr,
sizeof(struct sockaddr_in));
free(packet);
return 0;
}

266
src/parsing.c Normal file
View File

@ -0,0 +1,266 @@
#include <ctype.h>
#include <getopt.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parsing.h"
#include "scan.h"
void free_options(struct option_lst *options)
{
struct option_lst *it = options;
while (it) {
struct option_lst *tmp = it;
it = it->next;
free(tmp);
}
}
bool option_isset(const struct option_lst *options, e_flag flag)
{
if (!options)
return false;
for (const struct option_lst *it = options; it; it = it->next)
if (it->flag == flag)
return true;
return false;
}
char *get_option_arg(const struct option_lst *options, e_flag flag)
{
if (!options)
return NULL;
for (const struct option_lst *it = options; it; it = it->next)
if (it->flag == flag)
return it->arg;
return NULL;
}
e_scantype parse_type(const char *arg)
{
if (!arg)
return SCAN_ALL;
for (size_t i = 0; i < 6; i++)
if (!strcmp(arg, types_str[i]))
return i;
dprintf(2, "ft_nmap: invalid argument to --scan: '%s'\n", arg);
return -1;
}
int parse_ports(const char *arg, uint16_t *p_start, uint16_t *p_end)
{
size_t i = 0;
if (!arg)
return 0;
for (; isdigit(arg[i]); i++)
;
int start = atoi(arg);
if (arg[i] == '\0') {
*p_start = start;
*p_end = start;
return 0;
}
if (arg[i++] != '-')
goto error;
int end = atoi(arg + i);
for (; isdigit(arg[i]); i++)
;
if (arg[i])
goto error;
if (start >= end)
goto error;
if (end > UINT16_MAX) {
dprintf(2,
"ft_nmap: invalid argument: '%s': out of range: 0 "
"<= value <= %d\n",
arg, UINT16_MAX);
return -1;
}
if (start < 0 || end < 0) {
dprintf(2, "ft_nmap: invalid port value: %d\n", start);
return -1;
}
*p_start = start;
*p_end = end;
return 0;
error:
dprintf(2, "ft_nmap: port: invalid argument: '%s'\n", arg);
return -1;
}
static int check_arg(e_flag flag, const char *arg)
{
switch (flag) {
case FL_SPEEDUP: {
for (size_t i = 0; arg[i]; i++) {
if (!isdigit(arg[i])) {
dprintf(2, "ft_nmap: invalid argument: %s\n",
arg);
return -1;
}
}
int value = atoi(arg);
if (value < 0 || value > 250) {
dprintf(2,
"ft_nmap: invalid argument: '%s': out "
"of range: 0 "
"<= value <= 250\n",
arg);
return -1;
}
break;
}
case FL_TYPE:
if (!strcmp(arg, "SYN"))
break;
if (!strcmp(arg, "NULL"))
break;
if (!strcmp(arg, "ACK"))
break;
if (!strcmp(arg, "FIN"))
break;
if (!strcmp(arg, "XMAS"))
break;
if (!strcmp(arg, "UDP"))
break;
return -1;
case FL_TTL: {
int nb = atoi(arg);
if (nb == 0) {
dprintf(2, "ft_nmap: cannot set unicast time-to-live: "
"Invalid argument\n");
return -1;
} else if (nb < 0 || nb > 255) {
dprintf(2,
"ft_nmap: invalid argument: '%s': out "
"of range: 0 "
"<= value <= 255\n",
arg);
return -1;
}
break;
}
case FL_MAXRETRIES: {
int nb = atoi(arg);
if (nb == 0) {
dprintf(2, "ft_nmap: cannot set max_retries to 0\n");
return -1;
} else if (nb < 0 || nb > 255) {
dprintf(2,
"ft_nmap: invalid argument: '%s': out "
"of range: 0 "
"<= value <= 255\n",
arg);
return -1;
}
break;
}
default:
break;
}
return 0;
}
static int add_option(struct option_lst **head, e_flag flag, char *arg)
{
struct option_lst *new_option = malloc(sizeof(struct option_lst));
if (!new_option) {
dprintf(2, "ft_nmap: allocation of option failed\n");
for (struct option_lst *it = *head; it;) {
struct option_lst *tmp = it;
it = it->next;
free(tmp);
}
return -1;
}
new_option->flag = flag;
new_option->arg = arg;
new_option->next = NULL;
if (*head == NULL) {
*head = new_option;
return 0;
}
for (struct option_lst *it = *head; it; it = it->next) {
if (it->next == NULL) {
it->next = new_option;
break;
}
}
return 0;
}
struct option_lst *parse_options(int ac, char *const *av)
{
struct option_lst *head = NULL;
const struct option options[] = {
{"help", no_argument, 0, 0},
{"ports", required_argument, 0, 0},
{"ip", required_argument, 0, 0},
{"file", required_argument, 0, 0},
{"speedup", required_argument, 0, 0},
{"scan", required_argument, 0, 0},
{"max_retries", required_argument, 0, 0},
{"ttl", required_argument, 0, 0},
};
int c;
while (1) {
int option_index = 0;
c = getopt_long(ac, av, "F", options, &option_index);
if (c == -1)
break;
switch (c) {
case 'F':
add_option(&head, FL_FAST, optarg);
continue;
case '?':
return NULL;
default:
break;
}
if (check_arg(option_index, optarg) < 0)
return NULL;
if (add_option(&head, option_index, optarg) < 0)
return NULL;
}
return head;
}
int parsing(struct scan *general, const struct option_lst *options)
{
general->port_start = 1;
general->port_end = 1024;
if (option_isset(options, FL_FAST))
general->port_end = 128;
if (parse_ports(get_option_arg(options, FL_PORTS), &general->port_start,
&general->port_end))
return -1;
if (general->port_start <= 0 || general->port_end <= 0) {
dprintf(2, "ft_nmap: invalid port value: %d\n",
general->port_start <= 0 ? general->port_start
: general->port_end);
return -1;
}
general->type = parse_type(get_option_arg(options, FL_TYPE));
if ((int)general->type == -1)
return -1;
const char *max_retries = get_option_arg(options, FL_MAXRETRIES);
general->max_retries = max_retries ? atoi(max_retries) : 1;
const char *ttl = get_option_arg(options, FL_TTL);
general->ttl = ttl ? atoi(ttl) : 48;
const char *nb_threads_str = get_option_arg(options, FL_SPEEDUP);
uint8_t nb_threads = nb_threads_str ? atoi(nb_threads_str) : 1;
if (general->port_end - general->port_start + 1 < nb_threads) {
dprintf(2, "ft_nmap: number of threads to use must be "
"superior "
"or equals to the ports range\n");
return -1;
}
return 0;
}

93
src/print.c Normal file
View File

@ -0,0 +1,93 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "response.h"
#include "scan.h"
void print_usage(void)
{
printf("--help: Print this help screen\n");
printf("--ports: ports to scan (eg: 42-69 or 666)\n");
printf("--ip: ip address to scan in dot format\n");
printf("--file: File name containing IP addresses to scan\n");
printf("--speedup: [250 max] number of parallel threads to use\n");
printf("--scan: NULL/SYN/FIN/XMAS/ACK/UDP\n");
printf("--max_retries: number of retries when host doesn't reply\n");
printf("--ttl: precise the ttl\n");
}
void print_config(const struct scan *general, const char *hosts_path,
uint8_t nb_threads)
{
printf("Scan configurations\n");
if (hosts_path)
printf("Target hosts list filename: %s\n", hosts_path);
else
printf("Target ip address: %s\n", general->dest_addr);
if (general->port_start != general->port_end)
printf("Number of scans to be performed: %d\n",
general->port_end - general->port_start + 1);
printf("Scans to be performed: ");
if (general->type == SCAN_ALL)
for (e_scantype type = SCAN_NULL; type < SCAN_ALL; type++)
printf("%s ", types_str[type]);
else
printf("%s", types_str[general->type]);
printf("\n");
if (nb_threads > 1)
printf("No of threads: %d\n", nb_threads);
}
bool is_port_opened(const e_state states[6], e_scantype type)
{
if (type == SCAN_ALL) {
for (e_scantype i = SCAN_NULL; i < SCAN_ALL; i++)
if (states[i] == OPENED)
return true;
return false;
}
return states[type] == OPENED;
}
static void print_port_state(uint16_t port, e_scantype type,
const struct response *response)
{
printf("%-5d %-12s ", port,
response->service ? response->service : "Unassigned");
if (response->service)
free(response->service);
if (type == SCAN_ALL)
for (e_scantype i = SCAN_NULL; i < SCAN_ALL; i++)
printf("%s(%s) ", types_str[i],
states_str[response->states[i]]);
else
printf("%s(%s) ", types_str[type],
states_str[response->states[type]]);
printf("\n");
}
void print_host_results(const struct scan *general, double scan_time)
{
printf("IP address: %s\n", general->dest_addr);
printf("Opened ports:\n");
for (uint16_t port = general->port_start; port <= general->port_end;
port++) {
const struct response *response =
&general->responses[port - general->port_start];
if (is_port_opened(response->states, general->type))
print_port_state(port, general->type, response);
}
printf("\n");
printf("Closed/filtered/unfiltered ports:\n");
for (uint16_t port = general->port_start; port <= general->port_end;
port++) {
const struct response *response =
&general->responses[port - general->port_start];
if (!is_port_opened(response->states, general->type))
print_port_state(port, general->type, response);
}
printf("\nScan took %lf secs\n", scan_time);
}

110
src/response.c Normal file
View File

@ -0,0 +1,110 @@
#include <netdb.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include "response.h"
#include "scan.h"
extern pthread_mutex_t g_getservent;
static char *get_service_name(int port, char *proto)
{
char *name = NULL;
pthread_mutex_lock(&g_getservent);
struct servent *servent = getservbyport(htons(port), proto);
if (servent)
name = strdup(servent->s_name);
pthread_mutex_unlock(&g_getservent);
return name;
}
void tcp_response(const struct tcphdr *tcphdr, const struct scan *data)
{
const e_scantype type = data->type;
if (type == SCAN_UDP) {
dprintf(2,
"ft_nmap: error: received a TCP responses for an UDP "
"scan\n");
return;
}
if (data->responses->service == NULL)
data->responses->service =
get_service_name(data->port_start, "tcp");
if (type == SCAN_SYN) {
if (tcphdr->ack == 1 && tcphdr->syn == 1)
data->responses->states[type] = OPENED;
else if (tcphdr->ack == 1 && tcphdr->rst == 1)
data->responses->states[type] = CLOSED;
} else if (type == SCAN_ACK && tcphdr->rst == 1)
data->responses->states[type] = UNFILTERED;
else if (type == SCAN_NULL && tcphdr->rst == 1)
data->responses->states[type] = CLOSED;
else if (type == SCAN_FIN && tcphdr->rst == 1)
data->responses->states[type] = CLOSED;
else if (type == SCAN_XMAS && tcphdr->rst == 1)
data->responses->states[type] = CLOSED;
}
void udp_response(const struct udphdr *udphdr, const struct scan *data)
{
(void)udphdr;
if (data->type != SCAN_UDP) {
dprintf(2,
"ft_nmap: error: received an UDP responses for a TCP "
"scan\n");
return;
}
if (data->responses->service == NULL)
data->responses->service =
get_service_name(data->port_start, "udp");
data->responses->states[SCAN_UDP] = OPENED;
}
void icmp_response(const struct icmphdr *icmphdr, const struct scan *data)
{
const e_scantype type = data->type;
data->responses->service = get_service_name(data->port_start, "udp");
if (data->responses->service == NULL)
data->responses->service =
get_service_name(data->port_start, "tcp");
if (type == SCAN_SYN && icmphdr->type == 3)
data->responses->states[type] = FILTERED;
else if (type == SCAN_ACK && icmphdr->type == 3)
data->responses->states[type] = FILTERED;
else if (type == SCAN_NULL && icmphdr->type == 3)
data->responses->states[type] = FILTERED;
else if (type == SCAN_FIN && icmphdr->type == 3)
data->responses->states[type] = FILTERED;
else if (type == SCAN_XMAS && icmphdr->type == 3)
data->responses->states[type] = FILTERED;
else if (type == SCAN_UDP && icmphdr->type == 3 && icmphdr->code == 3)
data->responses->states[type] = CLOSED;
else if (type == SCAN_UDP && icmphdr->type == 3 && icmphdr->code != 3)
data->responses->states[type] = FILTERED;
}
void no_response(const struct scan *data)
{
const e_scantype type = data->type;
data->responses->service = get_service_name(data->port_start, "udp");
if (data->responses->service == NULL)
data->responses->service =
get_service_name(data->port_start, "tcp");
if (type == SCAN_SYN)
data->responses->states[type] = FILTERED;
else if (type == SCAN_ACK)
data->responses->states[type] = FILTERED;
else if (type == SCAN_NULL)
data->responses->states[type] = OPENFILTERED;
else if (type == SCAN_FIN)
data->responses->states[type] = OPENFILTERED;
else if (type == SCAN_XMAS)
data->responses->states[type] = OPENFILTERED;
else if (type == SCAN_UDP)
data->responses->states[type] = OPENFILTERED;
}

130
src/scan.c Normal file
View File

@ -0,0 +1,130 @@
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <pcap.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include "packet.h"
#include "response.h"
#include "scan.h"
static void dispatch_callback(u_char *user, const struct pcap_pkthdr *h,
const u_char *bytes)
{
const struct scan *data = (struct scan *)user;
const struct iphdr *iphdr =
(struct iphdr *)(bytes + sizeof(struct ether_header));
const void *packet =
bytes + sizeof(struct ether_header) + sizeof(struct iphdr);
if (iphdr->protocol == IPPROTO_TCP &&
h->caplen >= sizeof(struct ether_header) + sizeof(struct iphdr) +
sizeof(struct tcphdr)) {
tcp_response(packet, data);
} else if (iphdr->protocol == IPPROTO_UDP &&
h->caplen >= sizeof(struct ether_header) +
sizeof(struct iphdr) +
sizeof(struct udphdr)) {
udp_response(packet, data);
} else if (iphdr->protocol == IPPROTO_ICMP &&
h->caplen >= sizeof(struct ether_header) +
sizeof(struct iphdr) +
sizeof(struct icmphdr)) {
icmp_response(packet, data);
}
}
static int send_recv_packet(const struct scan *data, int sockfd, pcap_t *handle)
{
send_packet(data, sockfd);
uint8_t timer = 0;
for (; timer < TIMEOUT * 10; timer++) {
if (pcap_dispatch(handle, 1, dispatch_callback, (u_char *)data))
break;
usleep(100000);
}
if (timer == TIMEOUT * 10) {
no_response(data);
return 0;
}
return 1;
}
int scan(struct scan *data)
{
int sockfd = socket(AF_INET, SOCK_RAW,
data->type == SCAN_UDP ? IPPROTO_UDP : IPPROTO_TCP);
if (sockfd < 0) {
dprintf(2, "ft_nmap: failed to create socket\n");
return -1;
}
int one = 1;
const int *val = &one;
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
perror("Error setting IP_HDRINCL");
return -1;
}
char errbuf[PCAP_ERRBUF_SIZE];
bpf_u_int32 net, mask;
if (pcap_lookupnet(data->host.interface, &net, &mask, errbuf) < 0) {
dprintf(2, "ft_nmap: failed to lookup net/mask\n");
return -1;
}
pcap_t *handle =
pcap_open_live(data->host.interface, BUFSIZ, 0, 1000, errbuf);
if (handle == NULL) {
dprintf(2, "ft_nmap: failed to open device capture\n");
return -1;
}
struct bpf_program fp;
char pcap_expr[BUFSIZ];
sprintf(pcap_expr, "src host %s and src port %d and dst host %s",
data->dest_addr, data->port_start, data->host.ip);
if (pcap_compile(handle, &fp, pcap_expr, 0, mask) < 0) {
dprintf(2, "ft_nmap: failed to compile pcap expression\n");
pcap_close(handle);
return -1;
}
if (pcap_setfilter(handle, &fp) < 0) {
dprintf(2, "ft_nmap: failed to set the filter\n");
pcap_close(handle);
return -1;
}
pcap_freecode(&fp);
pcap_setnonblock(handle, 1, errbuf);
if (data->type == SCAN_ALL) {
for (e_scantype type = SCAN_NULL; type < SCAN_ALL; type++) {
data->type = type;
for (uint8_t retries = 0; retries < data->max_retries;
retries++)
if (send_recv_packet(data, sockfd, handle))
break;
}
data->type = SCAN_ALL;
} else {
for (uint8_t retries = 0; retries < data->max_retries;
retries++)
if (send_recv_packet(data, sockfd, handle))
break;
}
pcap_close(handle);
close(sockfd);
return 0;
}

102
src/thread.c Normal file
View File

@ -0,0 +1,102 @@
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "response.h"
#include "scan.h"
#include "thread.h"
pthread_mutex_t g_getservent;
void *routine(void *p_data)
{
struct scan *thread_data = p_data;
struct scan scan_data;
memcpy(&scan_data, thread_data, sizeof(struct scan));
for (uint16_t port = thread_data->port_start;
port <= thread_data->port_end; port++) {
scan_data.port_start = port;
scan_data.port_end = port;
scan_data.responses =
&thread_data->responses[port - thread_data->port_start];
if (scan(&scan_data))
return NULL;
}
return NULL;
}
static struct scan *init_threads_data(const struct scan *general,
uint8_t nb_threads)
{
struct scan *threads_data = malloc(sizeof(struct scan) * nb_threads);
if (!threads_data) {
dprintf(2, "ft_nmap: allocation of threads failed\n");
return NULL;
}
const uint16_t port_range = general->port_end - general->port_start + 1;
const uint16_t ports_per_thread = port_range / nb_threads;
uint16_t remaining_ports = port_range % nb_threads;
uint16_t port_start = general->port_start;
for (uint8_t i = 0; i < nb_threads; i++) {
memcpy(&threads_data[i], general, sizeof(struct scan));
threads_data[i].port_start = port_start + i * ports_per_thread;
threads_data[i].port_end = (port_start - 1) +
(i + 1) * ports_per_thread +
(remaining_ports ? 1 : 0);
threads_data[i].responses =
general->responses +
(threads_data[i].port_start - general->port_start);
if (remaining_ports) {
remaining_ports--;
port_start++;
}
}
return threads_data;
}
int create_threads(struct scan *general, uint8_t nb_threads)
{
if (nb_threads == 1) {
routine(general);
return 0;
}
struct scan *threads_data = init_threads_data(general, nb_threads);
if (threads_data == NULL)
return -1;
pthread_t *threads = malloc(sizeof(pthread_t) * nb_threads);
if (!threads) {
free(threads_data);
return -1;
}
pthread_mutex_init(&g_getservent, NULL);
for (uint8_t i = 0; i < nb_threads; i++) {
if (pthread_create(&threads[i], NULL, routine,
&threads_data[i])) {
dprintf(2, "ft_nmap: error during pthread_create()\n");
return -1;
}
}
for (uint8_t i = 0; i < nb_threads; i++) {
if (pthread_join(threads[i], NULL)) {
dprintf(2, "ft_nmap: error during pthread_join()\n");
return -1;
}
}
free(threads_data);
free(threads);
return 0;
}