/*
Copyright (C) HWPORT.COM.
All rights reserved.
Author: JAEHYUK CHO <mailto:minzkn@minzkn.com>
*/
#if !defined(_ISOC99_SOURCE)
# define _ISOC99_SOURCE (1L)
#endif
#if !defined(_GNU_SOURCE)
# define _GNU_SOURCE (1L)
#endif
/* ---- */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <dirent.h>
#include <netinet/in.h>
#define def_hwport_portscan_timeout (4000)
typedef unsigned long long __hwport_portscan_time_stamp_t;
#define hwport_portscan_time_stamp_t __hwport_portscan_time_stamp_t
typedef struct hwport_portscan_timer_ts {
/* need shadow */
long m_clock_tick;
clock_t m_prev_clock;
hwport_portscan_time_stamp_t m_time_stamp;
/* timer stat */
hwport_portscan_time_stamp_t m_start_time_stamp;
hwport_portscan_time_stamp_t m_timeout;
hwport_portscan_time_stamp_t m_duration;
}__hwport_portscan_timer_t;
#define hwport_portscan_timer_t __hwport_portscan_timer_t
typedef struct hwport_portscan_info_ts {
const char *m_hostname;
const char *m_peername;
uint8_t m_try_table[ 65536 / 8 ];
uint8_t m_syn_table[ 65536 / 8 ];
uint8_t m_ack_table[ 65536 / 8 ];
uint8_t m_open_table[ 65536 / 8 ];
int m_timeout_msec;
}__hwport_portscan_info_t;
#define hwport_portscan_info_t __hwport_portscan_info_t
typedef struct hwport_portscan_pseudo_header_ts {
uint32_t m_srcaddr;
uint32_t m_dstaddr;
uint8_t m_unused;
uint8_t m_protocol;
uint16_t m_length;
}__hwport_portscan_pseudo_header_t;
#define hwport_portscan_pseudo_header_t __hwport_portscan_pseudo_header_t
static void usage(const char *s_name);
static hwport_portscan_time_stamp_t hwport_portscan_time_stamp(hwport_portscan_timer_t *s_timer);
static void hwport_portscan_timer_init(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout);
static void hwport_portscan_timer_set(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout);
#if 0L
static void hwport_portscan_timer_update(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout);
#endif
static int hwport_portscan_timer_check(hwport_portscan_timer_t *s_timer);
static unsigned int hwport_portscan_checksum(const void *s_data, size_t s_size);
static hwport_portscan_info_t *hwport_portscan_info_init(hwport_portscan_info_t *s_info);
static __inline void hwport_portscan_set_mask(void *s_table, int s_port);
static __inline int hwport_portscan_get_mask(const void *s_table, int s_port);
static int hwport_portscan(hwport_portscan_info_t *s_info);
int main(int s_argc, char **s_argv);
static void usage(const char *s_name)
{
(void)fprintf(stdout, "usage: %s <hostname> <peername> [<start port> [<end port>]]\n", s_name);
}
static hwport_portscan_time_stamp_t hwport_portscan_time_stamp(hwport_portscan_timer_t *s_timer)
{
clock_t s_clock;
#if defined(__linux__)
/* get linux kernel's jiffes (tick counter) */
s_clock = times((struct tms *)0);
#else
do {
struct tms s_tms;
s_clock = times((struct tms *)(&s_tms));
}while(0);
#endif
if(s_clock == ((clock_t)(-1))) {
/* overflow clock timing */
return(s_timer->m_time_stamp);
}
if(s_timer->m_clock_tick <= 0l) {
/* get ticks per second */
s_timer->m_clock_tick = sysconf(_SC_CLK_TCK);
if(s_timer->m_clock_tick <= 0l) {
/* invalid clock tick */
return(s_timer->m_time_stamp);
}
s_timer->m_prev_clock = s_clock;
}
/* update time stamp (clock to upscale) */
s_timer->m_time_stamp += (((hwport_portscan_time_stamp_t)(s_clock - s_timer->m_prev_clock)) * ((hwport_portscan_time_stamp_t)1000u)) / ((hwport_portscan_time_stamp_t)s_timer->m_clock_tick);
s_timer->m_prev_clock = s_clock;
return(s_timer->m_time_stamp);
}
static void hwport_portscan_timer_init(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout)
{
/* shadow part */
s_timer->m_clock_tick = 0l;
s_timer->m_prev_clock = (clock_t)0;
s_timer->m_time_stamp = (hwport_portscan_time_stamp_t)1000u;
/* timer stat part */
hwport_portscan_timer_set(s_timer, s_timeout);
}
static void hwport_portscan_timer_set(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout)
{
s_timer->m_start_time_stamp = hwport_portscan_time_stamp(s_timer);
s_timer->m_timeout = s_timeout;
s_timer->m_duration = (hwport_portscan_time_stamp_t)0u;
}
#if 0L
static void hwport_portscan_timer_update(hwport_portscan_timer_t *s_timer, hwport_portscan_time_stamp_t s_timeout)
{
/* increment timeout */
s_timer->m_timeout += s_timeout;
}
#endif
static int hwport_portscan_timer_check(hwport_portscan_timer_t *s_timer)
{
s_timer->m_duration = hwport_portscan_time_stamp(s_timer) - s_timer->m_start_time_stamp;
return((s_timer->m_duration >= s_timer->m_timeout) ? 1 : 0);
}
static unsigned int hwport_portscan_checksum(const void *s_data, size_t s_size)
{
register unsigned int s_result = 0u;
while(s_size > ((size_t)1)) {
s_result += (unsigned int)(*((const unsigned short int *)s_data));
s_data = ((const unsigned short int *)s_data) + ((size_t)1);
s_size -= (size_t)2;
}
if(s_size > ((size_t)0)) {
s_result += (unsigned int)(*((const unsigned char *)s_data));
}
s_result = (s_result >> 16) + (s_result & 0xffffu);
s_result += s_result >> 16;
return((~s_result) & 0xffffu);
}
static hwport_portscan_info_t *hwport_portscan_info_init(hwport_portscan_info_t *s_info)
{
s_info->m_hostname = (const char *)0;
s_info->m_peername = (const char *)0;
s_info->m_timeout_msec = def_hwport_portscan_timeout;
(void)memset((void *)(&s_info->m_try_table[0]), 0, sizeof(s_info->m_try_table));
(void)memset((void *)(&s_info->m_syn_table[0]), 0, sizeof(s_info->m_syn_table));
(void)memset((void *)(&s_info->m_ack_table[0]), 0, sizeof(s_info->m_ack_table));
(void)memset((void *)(&s_info->m_open_table[0]), 0, sizeof(s_info->m_open_table));
return(s_info);
}
static __inline void hwport_portscan_set_mask(void *s_table, int s_port)
{
uint8_t *s_table8 = (uint8_t *)s_table;
s_table8[s_port >> 3] |= (uint8_t)(1u << (s_port & 0x07));
}
static __inline int hwport_portscan_get_mask(const void *s_table, int s_port)
{
const uint8_t *s_table8 = (const uint8_t *)s_table;
return(((s_table8[s_port >> 3] & ((uint8_t)(1u << (s_port & 0x07)))) == ((uint8_t)0)) ? 0 : 1);
}
static int hwport_portscan(hwport_portscan_info_t *s_info)
{
int s_result = (-1);
struct hostent *s_hostent;
struct in_addr s_hostaddr;
struct in_addr s_peeraddr;
int s_socket, s_tcp_socket;
static int s_on = 1;
int s_bind_port;
struct iphdr s_ip_header;
hwport_portscan_pseudo_header_t s_pseudo_header;
struct tcphdr s_tcp_header;
int s_current_port;
size_t s_buffer_size;
uint8_t *s_buffer;
struct iphdr *s_ip_header_ptr;
hwport_portscan_pseudo_header_t *s_pseudo_header_ptr;
struct tcphdr *s_tcp_header_ptr;
struct sockaddr_in s_sockaddr_in;
socklen_t s_socklen;
ssize_t s_send_bytes;
hwport_portscan_timer_t s_timer;
struct timeval s_timeval;
fd_set s_fd_set_rx;
fd_set s_fd_set_tx;
int s_select_check;
int s_need_more_rx_check; /* socket buffer to prevention overflowing */
ssize_t s_recv_bytes;
/* resolv host */
s_hostent = gethostbyname(s_info->m_hostname);
if(s_hostent == ((struct hostent *)0)) {
return(-1);
}
s_hostaddr = *((struct in_addr *)(s_hostent->h_addr));
/* resolv peer */
s_hostent = gethostbyname(s_info->m_peername);
if(s_hostent == ((struct hostent *)0)) {
goto l_return;
}
s_peeraddr = *((struct in_addr *)(s_hostent->h_addr));
/* open raw socket */
s_socket = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
if(s_socket == (-1)) {
perror("socket");
goto l_return;
}
/* open tcp socket for bind port */
s_tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(s_tcp_socket == (-1)) {
perror("tcp socket");
goto l_close_with_return;
}
/* include IP header to recv packet */
if(setsockopt(s_socket, IPPROTO_IP, IP_HDRINCL, &s_on, sizeof(s_on)) == (-1)) {
perror("setsockopt");
goto l_close_with_return;
}
/* bind tcp socket */
(void)memset((void *)(&s_sockaddr_in), 0, sizeof(s_sockaddr_in));
s_sockaddr_in.sin_family = AF_INET;
s_sockaddr_in.sin_addr.s_addr = htonl(INADDR_ANY);
s_sockaddr_in.sin_port = htons(0);
s_socklen = (socklen_t)sizeof(s_sockaddr_in);
if(bind(s_tcp_socket, (struct sockaddr *)(&s_sockaddr_in), s_socklen) == (-1)) {
perror("bind");
goto l_close_with_return;
}
if(getsockname(s_tcp_socket, (struct sockaddr *)(&s_sockaddr_in), (socklen_t *)(&s_socklen)) == (-1)) {
perror("getsockname");
goto l_close_with_return;
}
s_bind_port = (int)ntohs(s_sockaddr_in.sin_port);
/* build default header */
(void)memset((void *)(&s_ip_header), 0, sizeof(s_ip_header));
(void)memset((void *)(&s_pseudo_header), 0, sizeof(s_pseudo_header));
(void)memset((void *)(&s_tcp_header), 0, sizeof(s_tcp_header));
s_ip_header.ihl = sizeof(s_ip_header) >> 2;
s_ip_header.version = 4;
s_ip_header.tos = 0;
s_ip_header.tot_len = htons(sizeof(s_ip_header) + sizeof(s_tcp_header));
s_ip_header.id = htons(0);
s_ip_header.frag_off = 0;
s_ip_header.ttl = IPDEFTTL;
s_ip_header.protocol = IPPROTO_TCP;
s_ip_header.check = hwport_portscan_checksum((void *)(&s_ip_header), sizeof(s_ip_header));
s_ip_header.saddr = s_hostaddr.s_addr;
s_ip_header.daddr = s_peeraddr.s_addr;
s_pseudo_header.m_srcaddr = s_ip_header.saddr;
s_pseudo_header.m_dstaddr = s_ip_header.daddr;
s_pseudo_header.m_unused = 0;
s_pseudo_header.m_protocol = s_ip_header.protocol;
s_pseudo_header.m_length = htons(sizeof(s_tcp_header));
s_tcp_header.source = htons(s_bind_port);
s_tcp_header.dest = htons(0);
s_tcp_header.seq = htonl(random());
s_tcp_header.ack_seq = htonl(0);
s_tcp_header.doff = 5;
s_tcp_header.res1 = 0;
s_tcp_header.fin = 0;
s_tcp_header.syn = 1;
s_tcp_header.rst = 0;
s_tcp_header.psh = 0;
s_tcp_header.ack = 0;
s_tcp_header.urg = 0;
s_tcp_header.window = htons(65535);
s_tcp_header.check = htons(0);
/* build for sendto sockaddr_in */
(void)memset((void *)(&s_sockaddr_in), 0, sizeof(s_sockaddr_in));
s_sockaddr_in.sin_family = AF_INET;
s_sockaddr_in.sin_addr = s_peeraddr;
s_sockaddr_in.sin_port = htons(0);
s_socklen = (socklen_t)sizeof(s_sockaddr_in);
s_buffer_size = (size_t)(4 << 10);
s_buffer = (uint8_t *)malloc(s_buffer_size);
if(s_buffer == ((uint8_t *)0)) {
goto l_close_with_return;
}
s_need_more_rx_check = 0;
s_current_port = 0;
FD_ZERO(&s_fd_set_rx);
FD_ZERO(&s_fd_set_tx);
hwport_portscan_timer_init((hwport_portscan_timer_t *)(&s_timer), (hwport_portscan_time_stamp_t)s_info->m_timeout_msec);
do {
if(s_current_port != (-1)) {
while(s_current_port <= ((int)(sizeof(s_info->m_try_table) * 8))) {
if(hwport_portscan_get_mask((const void *)(&s_info->m_try_table[0]), s_current_port) != 0) {
break;
}
++s_current_port;
}
if(s_current_port >= ((int)(sizeof(s_info->m_try_table) * 8))) {
s_current_port = (-1);
}
}
if((s_current_port == (-1)) && (memcmp((const void *)(&s_info->m_syn_table[0]), (const void *)(&s_info->m_ack_table[0]), sizeof(s_info->m_try_table)) == 0)) {
/* all checked & all try port opened */
break;
}
if((s_need_more_rx_check == 0) && (s_timer.m_duration <= ((hwport_portscan_time_stamp_t)s_info->m_timeout_msec))) {
hwport_portscan_time_stamp_t s_timeout_msec;
s_timeout_msec = ((hwport_portscan_time_stamp_t)s_info->m_timeout_msec) - s_timer.m_duration;
s_timeval.tv_sec = s_timeout_msec / 1000;
s_timeval.tv_usec = (s_timeout_msec % 1000) * 1000;
}
else {
s_timeval.tv_sec = 0;
s_timeval.tv_usec = 0;
}
FD_SET(s_socket, &s_fd_set_rx);
if((s_need_more_rx_check == 0) && (s_current_port != (-1))) {
FD_SET(s_socket, &s_fd_set_tx);
}
s_need_more_rx_check = 0;
s_select_check = select(s_socket + 1, (fd_set *)(&s_fd_set_rx), (fd_set *)(&s_fd_set_tx), (fd_set *)0, (struct timeval *)(&s_timeval));
if(s_select_check == (-1)) {
perror("select");
break;
}
if(s_select_check == 0) { /* timeout */
continue;
}
if((s_select_check > 0) && (FD_ISSET(s_socket, &s_fd_set_tx) != 0)) {
--s_select_check;
FD_CLR(s_socket, &s_fd_set_tx);
/* make packet */
s_pseudo_header_ptr = (hwport_portscan_pseudo_header_t *)memcpy((void *)(&s_buffer[sizeof(s_ip_header) - sizeof(hwport_portscan_pseudo_header_t)]), (const void *)(&s_pseudo_header), sizeof(s_pseudo_header));
s_tcp_header_ptr = (struct tcphdr *)memcpy((void *)(&s_buffer[sizeof(s_ip_header)]), (const void *)(&s_tcp_header), sizeof(s_tcp_header));
s_tcp_header_ptr->dest = htons(s_current_port);
s_tcp_header_ptr->check = hwport_portscan_checksum((void *)s_pseudo_header_ptr, sizeof(s_pseudo_header) + sizeof(s_tcp_header));
s_ip_header_ptr = (struct iphdr *)memcpy((void *)(&s_buffer[0]), (const void *)(&s_ip_header), sizeof(s_ip_header));
s_sockaddr_in.sin_port = s_tcp_header_ptr->dest;
s_send_bytes = sendto(s_socket, (const void *)(&s_buffer[0]), sizeof(s_ip_header) + sizeof(s_tcp_header), MSG_NOSIGNAL, (struct sockaddr *)(&s_sockaddr_in), s_socklen);
if(s_send_bytes > ((ssize_t)0)) {
hwport_portscan_set_mask((void *)(&s_info->m_syn_table[0]), s_current_port);
}
++s_current_port;
/* reset timer */
hwport_portscan_timer_set((hwport_portscan_timer_t *)(&s_timer), (hwport_portscan_time_stamp_t)s_info->m_timeout_msec);
}
if((s_select_check > 0) && (FD_ISSET(s_socket, &s_fd_set_rx) != 0)) {
--s_select_check;
FD_CLR(s_socket, &s_fd_set_rx);
s_socklen = (socklen_t)sizeof(s_sockaddr_in);
s_recv_bytes = recvfrom(s_socket, (void *)(&s_buffer[0]), s_buffer_size, MSG_NOSIGNAL, (struct sockaddr *)(&s_sockaddr_in), (socklen_t *)(&s_socklen));
if(s_recv_bytes > ((ssize_t)0)) {
int s_this_port;
if(((size_t)s_recv_bytes) < s_buffer_size) {
(void)memset((void *)(&s_buffer[s_recv_bytes]), 0, s_buffer_size - ((size_t)s_recv_bytes));
}
s_ip_header_ptr = (struct iphdr *)(&s_buffer[0]);
s_tcp_header_ptr = (struct tcphdr *)(&s_buffer[s_ip_header_ptr->ihl << 2]);
s_this_port = (int)ntohs(s_tcp_header_ptr->source);
if((s_ip_header_ptr->saddr == s_peeraddr.s_addr) &&
(s_ip_header_ptr->daddr == s_hostaddr.s_addr) &&
(s_bind_port == ((int)ntohs(s_tcp_header_ptr->dest))) &&
(hwport_portscan_get_mask((const void *)(&s_info->m_syn_table[0]), s_this_port) != 0)
) { /* filtering my event only */
hwport_portscan_set_mask((void *)(&s_info->m_ack_table[0]), s_this_port);
if((s_tcp_header_ptr->syn != 0) && (s_tcp_header_ptr->ack != 0)) {
hwport_portscan_set_mask((void *)(&s_info->m_open_table[0]), s_this_port);
}
else if(s_tcp_header_ptr->rst != 0) {
/* closed */
}
}
s_need_more_rx_check = 1;
}
}
}while(hwport_portscan_timer_check((hwport_portscan_timer_t *)(&s_timer)) == 0);
s_result = 0;
free((void *)s_buffer);
l_close_with_return:;
if(s_tcp_socket != (-1)) {
while((close(s_tcp_socket) == (-1)) && (errno == EINTR));
}
/* close socket */
while((close(s_socket) == (-1)) && (errno == EINTR));
l_return:;
return(s_result);
}
int main(int s_argc, char **s_argv)
{
static hwport_portscan_info_t s_info_local;
hwport_portscan_info_t *s_info = &s_info_local;
int s_start_port = 1, s_end_port = 65535, s_current_port;
struct servent *s_servent;
(void)fprintf(stdout, "HWPORT STELTH PORTSCAN - Copyright (c) HWPORT.COM. All rights reserved.\n");
s_info = hwport_portscan_info_init((hwport_portscan_info_t *)(&s_info_local));
if(s_argc <= 2) {
usage((const char *)s_argv[0]);
return(EXIT_FAILURE);
}
s_info->m_hostname = (const char *)(s_argv[1]);
s_info->m_peername = (const char *)(s_argv[2]);
if(s_argc >= 4) {
if((sscanf(s_argv[3], "%i", &s_start_port) != 1) || (s_start_port < 1) || (s_start_port > 65535)) {
usage((const char *)s_argv[0]);
(void)fprintf(stderr, "incorrect start_port !\n");
return(EXIT_FAILURE);
}
s_end_port = s_start_port;
if(s_argc >= 5) {
if((sscanf(s_argv[4], "%i", &s_end_port) != 1) || (s_end_port < s_start_port) || (s_end_port > 65535)) {
usage((const char *)s_argv[0]);
(void)fprintf(stderr, "incorrect end_port !\n");
return(EXIT_FAILURE);
}
}
}
(void)fprintf(stdout, "(host=\"%s\", peer=\"%s\", port=%d-%d, timeout=%dmsec)\n\n",
s_info->m_hostname, s_info->m_peername, s_start_port, s_end_port, s_info->m_timeout_msec);
/* set mask */
for(s_current_port = s_start_port;s_current_port <= s_end_port;s_current_port++) {
hwport_portscan_set_mask((void *)(&s_info->m_try_table[0]), s_current_port);
}
if(hwport_portscan(s_info) == (-1)) {
return(EXIT_FAILURE);
}
setservent(1);
for(s_current_port = 0;s_current_port < ((int)(sizeof(s_info->m_try_table) * 8));s_current_port++) {
if(hwport_portscan_get_mask((const void *)(&s_info->m_try_table[0]), s_current_port) == 0) {
continue;
}
if(hwport_portscan_get_mask((const void *)(&s_info->m_open_table[0]), s_current_port) != 0) {
s_servent = getservbyport(htons(s_current_port), "tcp");
(void)fprintf(stdout, "%d/tcp %s %s\n", s_current_port, "open", (s_servent == ((struct servent *)0)) ? "unknown" : s_servent->s_name);
}
else if((s_end_port - s_start_port) < 25) {
s_servent = getservbyport(htons(s_current_port), "tcp");
if(hwport_portscan_get_mask((const void *)(&s_info->m_ack_table[0]), s_current_port) != 0) {
(void)fprintf(stdout, "%d/tcp %s %s\n", s_current_port, "closed", (s_servent == ((struct servent *)0)) ? "unknown" : s_servent->s_name);
}
else {
(void)fprintf(stdout, "%d/tcp %s %s\n", s_current_port, "filtered", (s_servent == ((struct servent *)0)) ? "unknown" : s_servent->s_name);
}
}
}
endservent();
return(EXIT_SUCCESS);
}
/* vim: set expandtab: */
/* End of source */