Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
a5d663fa9a | |||
ff89de7fdc | |||
ddbaef1826 | |||
67e6707d7d | |||
8299154ee1 | |||
3ebface620 | |||
f91bc39762 | |||
957bb06d97 | |||
31530c83a4 | |||
eaf5913c29 | |||
954f59649a | |||
9ae1e29f71 | |||
d640c95224 | |||
abb1d1f364 | |||
6164363cce | |||
0f8d184aee | |||
dbed6dc7cd | |||
ee2af274d9 | |||
f35cad887d | |||
936c277d02 | |||
227000cf97 | |||
a94bbf0671 | |||
2040551742 | |||
d0b1fa6329 | |||
331d1077b9 | |||
623b3ad0d7 | |||
6538a085b9 | |||
60f23e9250 | |||
e7ca63eaa6 | |||
ad367d6792 | |||
675248cbff | |||
1ce702d169 | |||
eb2d9b1ecf | |||
3213a56d1b | |||
1ffb554a32 | |||
9378251248 | |||
84d5960900 | |||
1361cd9f9c |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,6 @@
|
||||
obj
|
||||
ft_nmap
|
||||
.cache
|
||||
compile_commands.json
|
||||
tags
|
||||
dlopen.supp
|
||||
|
4
Makefile
4
Makefile
@ -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
|
||||
|
6
include/dns.h
Normal file
6
include/dns.h
Normal 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
3
include/error.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
int err(char *str);
|
11
include/host.h
Normal file
11
include/host.h
Normal 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
5
include/interface.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "host.h"
|
||||
|
||||
int get_interface_name(struct host *host);
|
16
include/packet.h
Normal file
16
include/packet.h
Normal 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
33
include/parsing.h
Normal 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
9
include/print.h
Normal 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
32
include/response.h
Normal 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
32
include/scan.h
Normal 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
7
include/thread.h
Normal 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
13
src/checksum.c
Normal 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
37
src/dns.c
Normal 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
9
src/error.c
Normal 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
32
src/get_interface.c
Normal 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;
|
||||
}
|
111
src/main.c
111
src/main.c
@ -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
103
src/packet.c
Normal 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
266
src/parsing.c
Normal 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
93
src/print.c
Normal 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
110
src/response.c
Normal 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
130
src/scan.c
Normal 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
102
src/thread.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user