Skip to content

Sipxi/Layer-4-Port-Scanner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Layer-4-Port-Scanner

Layer-4-Port-Scanner is a command-line Layer 4 port scanner written in C. The project scans TCP and UDP ports on a target host and prints port states in text form.

Project Structure

.
├── includes/
├── src/
├── tests/
├── Makefile
└── README.md

Build and Run Instructions

Source and Includes Tree

.
├── includes/
│   ├── arg_parser.h
│   ├── dns_parser.h
│   ├── net_utils.h
│   ├── portlist.h
│   ├── scanner.h
│   ├── tcp_scanner.h
│   └── udp_scanner.h
└── src/
    ├── arg_parser.c
    ├── dns_parser.c
    ├── main.c
    ├── net_utils.c
    ├── portlist.c
    ├── scanner.c
    ├── tcp_scanner.c
    └── udp_scanner.c

Dependencies

Required tools and libraries:

  • Linux system
  • GCC with C17 support
  • make
  • libpcap runtime and development headers

For integration tests that manipulate firewall rules, these tools are also needed:

sudo apt install -y iptables

Build

Build scanner binary:

make

or

make all

Clean binaries:

make clean

Run

Help:

./ipk-L4-scan -h

List available interfaces:

./ipk-L4-scan -i

Run scans (requires elevated privileges for raw sockets/capture):

sudo ./ipk-L4-scan -i lo -t 80 127.0.0.1
sudo ./ipk-L4-scan -i lo -u 80 127.0.0.1
sudo ./ipk-L4-scan -i lo -t 1,80,443 -u 80 localhost
sudo ./ipk-L4-scan -i lo -t 1-100 -w 500  localhost

General syntax:

sudo ./ipk-L4-scan -i INTERFACE [-u PORTS] [-t PORTS] HOST [-w TIMEOUT] [-h | --help]

Implemented Features and Behavior

  • CLI argument parsing with validation:

    • required: interface, host, and at least one protocol set (-t or -u)
    • supports timeout override via -w
    • supports -h/--help
    • special -i alone prints interfaces
  • Port set parsing:

    • single port: 80
    • comma list: 22,80,443
    • range: 1-1024
    • validates boundaries 1-65535
    • rejects malformed and mixed unsupported format (22,25-30,35)
  • Host resolution:

    • resolves hostnames to unique IP list
    • supports IPv4 and IPv6 addresses from DNS
  • TCP scanning:

    • IPv4: raw TCP SYN probe with manually built IPv4 + TCP header and checksum
    • IPv6: raw TCP probe using kernel checksum option (IPV6_CHECKSUM)
    • packet capture with BPF filter through libpcap
    • interpretation:
      • SYN+ACK -> open
      • RST -> closed
      • timeout/no valid reply -> filtered
  • UDP scanning:

    • sends UDP probe to target port
    • listens for ICMP/ICMPv6 port unreachable replies with libpcap
    • interpretation:
      • ICMP port unreachable -> closed
      • no ICMP error before timeout -> open
  • Interruption:

    • SIGINT/SIGTERM handled through shared stop flag
    • scanner and test runner stop safely on interruption

Design Decisions

  • Raw sockets were used for TCP scanning to avoid full connect() handshakes and to control packet-level behavior.
  • libpcap was used for response capture because it provides reliable filtering and cross-protocol packet inspection.
  • Scanner orchestration is split into modules:
    • arg_parser for user input
    • portlist for port expression parsing
    • dns_parser for target resolution
    • tcp_scanner and udp_scanner for protocol-specific logic
    • scanner for execution flow and result printing
  • Tests are separated into unit-like parser checks and integration-like TCP/UDP behavior checks on loopback.
  • UDP behavior intentionally follows assignment interpretation where no error response is treated as open.

Testing

For testing i decided to implement a custom test runner in C with a simple assertion framework. This allows for more realistic integration tests that can manipulate firewall rules and start temporary servers, which would be difficult to achieve with a shell script or external testing framework.

Tests Tree

tests/
├── cases/
│   ├── arg_parser_tests.c
│   ├── arg_parser_tests.h
│   ├── dns_parser_tests.c
│   ├── dns_parser_tests.h
│   ├── portlist_tests.c
│   ├── portlist_tests.h
│   ├── tcp_tests.c
│   ├── tcp_tests.h
│   ├── udp_tests.c
│   └── udp_tests.h
├── helpers/
│   ├── test_env.c
│   └── test_env.h
├── runner/
│   ├── test_runner.c
│   └── test_runner.h
└── main.c

How to Execute Automated Tests

Build and run automated tests:

sudo make test

Test Groups and Results

Args Tests

What was tested:

  • CLI option parsing (-i, -t, -u, -w, -h, --help)
  • required argument combinations
  • invalid option/value handling

Why:

  • scanner behavior depends on valid inputs and safe early rejection of invalid usage.

How:

  • tests call parse_arguments() with handcrafted argc/argv arrays and assert return codes/fields.

Testing environment:

  • same environment as listed in Reproducible Environment section.

Inputs, expected outputs, actual outputs:

  • Normal input:
    • input: {"scanner","-w","3000","-u","53","-i","eth0","1.1.1.1"}
    • expected: EX_OK, timeout 3000, udp ports 53
    • actual: pass
  • Edge input:
    • input: {"scanner","-i","eth0","-t","22","-w","abc","example.com"}
    • expected: EX_USAGE and error text for invalid timeout
    • actual: pass, printed Error: Invalid timeout value 'abc'.

Sample textual output:

===== ARG PARSER ======
help short... [PASS]
help long... [PASS]
default timeout... [PASS]
custom timeout... [PASS]
missing interface... Error: Interface (-i) is required for scanning.
[PASS]
invalid timeout... Error: Invalid timeout value 'abc'.
[PASS]
==============

Port Tests

What was tested:

  • parser for single ports, lists, ranges, full range, invalid values, null inputs

Why:

  • wrong port parsing would produce wrong scans or memory errors.

How:

  • tests call parse_ports() and assert EX_OK/EX_USAGE, counts, and content of parsed arrays.

Testing environment:

  • same environment as listed in Reproducible Environment section.

Inputs, expected outputs, actual outputs:

  • Normal input:
    • input: "22,80,443"
    • expected: count 3 and ports [22,80,443]
    • actual: pass
  • Edge input:
    • input: "30-20"
    • expected: EX_USAGE (reverse range rejected)
    • actual: pass, printed Error: Invalid port range '30-20'. End must be greater than start.

Sample textual output:

===== PORTLIST ======
single_port_valid... [PASS]
comma_list_valid... [PASS]
range_valid... [PASS]
invalid_zero_port... Error: Invalid port number '0'.
[PASS]
invalid_range_reverse_order... Error: Invalid port range '30-20'. End must be greater than start.
[PASS]
==============

3. DNS Tests

What was tested:

  • resolving valid literal/hostname
  • handling invalid hostnames and invalid IPv4 literals
  • safe free of null list

Why:

  • scanning requires valid resolved addresses; failures must be explicit and safe.

How:

  • tests call get_host_ips() and verify list shape, uniqueness, address family validity, and return codes.

Testing environment:

  • same environment as listed in Reproducible Environment section.

Inputs, expected outputs, actual outputs:

  • Normal input:
    • input: "127.0.0.1" and "localhost"
    • expected: EX_OK and at least one valid IP node
    • actual: pass
  • Edge input:
    • input: "definitely-invalid-hostname.example.invalid"
    • expected: EX_NOHOST
    • actual: pass with resolver error text

Sample textual output:

===== DNS PARSER ======
resolve_ipv4_literal... [PASS]
resolve_localhost_has_at_least_one_ip... [PASS]
invalid_hostname_returns_nohost... Error resolving host 'definitely-invalid-hostname.example.invalid': Name or service not known
[PASS]
==============

4. UDP/TCP Tests

What was tested:

  • integration behavior of scan_tcp() and scan_udp() for open/closed/filtered-like scenarios
  • both IPv4 and IPv6 loopback paths
  • repeated response consistency checks

Why:

  • parser/unit tests are not enough; scanner state detection must be validated against real packets and local servers.

How:

  • helper code starts temporary TCP/UDP servers on loopback ports, then scan functions are executed.
  • filtered scenarios are emulated with temporary iptables/ip6tables DROP rules.

Testing environment:

  • same environment as listed in Reproducible Environment section.

Inputs, expected outputs, actual outputs:

  • Normal target examples:
    • TCP open probe to local server port (e.g., 8080)
    • UDP open probe to local server port (e.g., 8080)
  • Edge target examples:
    • closed port with no server
    • filtered port using temporary firewall rule

Sample textual output:

===== TCP ======
tcp_open_ipv4... [PASS]
tcp_closed_ipv4... [PASS]
tcp_filtered_ipv4... [PASS]
tcp_open_closed_filtered_ipv4... [PASS]
tcp_multiple_responses_ipv4... [PASS]
tcp_open_ipv6... [PASS]
tcp_closed_ipv6... [PASS]
tcp_filtered_ipv6... [PASS]
tcp_open_closed_filtered_ipv6... [PASS]
tcp_multiple_responses_ipv6... [PASS]
==============

===== UDP ======
udp_open_ipv4... [PASS]
udp_closed_ipv4... [PASS]
udp_filtered_ipv4... [PASS]
udp_open_closed_filtered_ipv4... [PASS]
udp_multiple_responses_ipv4... [PASS]
udp_open_ipv6... [PASS]
udp_closed_ipv6... [PASS]
udp_filtered_ipv6... [PASS]
udp_open_closed_filtered_ipv6... [PASS]
udp_multiple_responses_ipv6... [PASS]

Note

Due to the repetitive nature of certain test cases, parts of the argument, DNS, and port list tests were initially generated using an LLM. These outputs were carefully reviewed, validated, and significantly refined. The generated content served only as a starting point for specific edge cases.

Known Limitations

  • Elevated privileges are required for real scanning.
  • Integration tests can be unstable across restricted environments (setups without usable sudo for firewall commands).
  • Linux only behavior
// SO_BINDTODEVICE is Linux-specific behavior.
    if (interface != NULL) {
        if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, interface, strlen(interface)) < 0) {
            perror("Error binding UDP socket to interface");
            close(sock);
            return EX_OSERR;
        }
    }

About

VUT FIT IPK Project 1 - Layer 4 port scanner written in C. It scans TCP and UDP ports using raw sockets and libpcap, with support for IPv4/IPv6, port ranges, and filtered state detection via firewall rules.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors