Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
cmake_minimum_required(VERSION 3.20)
project(hans VERSION 1.1 LANGUAGES C CXX)

# Remove all debug information from Release binaries
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "")
string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "/Zi" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
string(REPLACE "/ZI" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "/ZI" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG:NONE /EMITPOGOPHASEINFO:NO")

# ---------------------------------------------------------------------------
# Language standard
# ---------------------------------------------------------------------------
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# ---------------------------------------------------------------------------
# MSVC-specific flags (supports x64 and ARM64)
# ---------------------------------------------------------------------------
if(MSVC)
add_compile_options(
/W3
/wd4996 # deprecated CRT functions (_CRT_SECURE_NO_WARNINGS)
/wd4267 # size_t → int conversion (64-bit)
/wd4244 # intptr_t → int conversion (fd casts)
/wd4200 # nonstandard zero-size array (not used but silences noise)
)
add_compile_definitions(
WIN32 # legacy guard used in original code
_WIN32 # standard MSVC pre-define (explicit)
_CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
WIN32_LEAN_AND_MEAN
NOMINMAX # prevent windows.h min/max macros
)
endif()

# ---------------------------------------------------------------------------
# Source files common to all platforms
# ---------------------------------------------------------------------------
set(COMMON_SOURCES
src/main.cpp
src/client.cpp
src/server.cpp
src/worker.cpp
src/tun.cpp
src/echo.cpp
src/auth.cpp
src/sha1.cpp
src/hans_time.cpp
src/exception.cpp
src/utility.cpp
)

# ---------------------------------------------------------------------------
# Platform-specific TUN/TAP driver
# ---------------------------------------------------------------------------
if(WIN32)
# Native MSVC build — uses TAP-Windows (OpenVPN TAP driver)
list(APPEND COMMON_SOURCES
src/win32_compat.cpp
src/tun_dev_win32.c
)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND COMMON_SOURCES src/tun_dev_linux.c)
add_compile_definitions(HAVE_LINUX_IF_TUN_H LINUX)
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
list(APPEND COMMON_SOURCES src/tun_dev_freebsd.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
list(APPEND COMMON_SOURCES src/tun_dev_openbsd.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(APPEND COMMON_SOURCES src/tun_dev_utun.c)
else()
list(APPEND COMMON_SOURCES src/tun_dev_generic.c)
endif()

# ---------------------------------------------------------------------------
# Executable
# ---------------------------------------------------------------------------
add_executable(hans ${COMMON_SOURCES})

target_include_directories(hans PRIVATE src)

set_target_properties(hans PROPERTIES
LINK_FLAGS_RELEASE "/INCREMENTAL:NO /DEBUG:NONE"
)

# ---------------------------------------------------------------------------
# Linker dependencies
# ---------------------------------------------------------------------------
if(WIN32)
target_link_libraries(hans PRIVATE
ws2_32 # Winsock2
iphlpapi # IP helper API (used indirectly by Winsock)
)
elseif(UNIX)
find_package(Threads REQUIRED)
target_link_libraries(hans PRIVATE Threads::Threads)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# tunemu mode would also need -lpcap, but utun mode does not
endif()
endif()

# ---------------------------------------------------------------------------
# Install
# ---------------------------------------------------------------------------
install(TARGETS hans RUNTIME DESTINATION bin)
110 changes: 110 additions & 0 deletions WIN-ARM64.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Building Hans -- Windows ARM64 (MSVC)

This document describes how to build Hans natively on **Windows ARM64** using Visual Studio and CMake. This replaces the legacy CYGWIN/Makefile build for x86.

---

## Requirements

| Tool | Version | Notes |
|---|---|---|
| Visual Studio | 2026 Community (or 2022) | With **Desktop development with C++** workload |
| CMake | Bundled with Visual Studio | Must be added to PATH |
| TAP-Windows driver | Any | Included with [OpenVPN](https://openvpn.net/community-downloads/) |

---

## 1. Add CMake to PATH

CMake ships with Visual Studio but is not added to PATH automatically.

Open PowerShell as **Administrator** and run:

```powershell
$cmakePath = "C:\Program Files\Microsoft Visual Studio\18\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin"
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";$cmakePath", "Machine")
```

> For Visual Studio 2022, replace `\18\` with `\17\` or `\2022\` in the path above.

Close and reopen PowerShell, then verify:

```powershell
cmake --version
```

---

## 2. Configure the Build

Open PowerShell, navigate to the project folder and run:

```powershell
cd C:\path\to\hans
cmake -B build -A ARM64
```

You should see output ending with:
```
-- Build files have been written to: ...\hans\build
```

---

## 3. Compile

```powershell
cmake --build build --config Release
```

The executable will be at:
```
build\Release\hans.exe
```

---

## 4. Clean Build (if needed)

If you are recompiling after source changes:

```powershell
Remove-Item -Recurse -Force build
cmake -B build -A ARM64
cmake --build build --config Release
```

---

## 5. Verify No Debug Info (optional)

To confirm the Release binary has no embedded debug information:

```powershell
& "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Tools\MSVC\14.50.35717\bin\Hostarm64\arm64\dumpbin.exe" /headers build\Release\hans.exe | Select-String "debug"
```

Expected output:
```
0 [ 0] RVA [size] of Debug Directory
```

---

## Runtime Requirements

The **TAP-Windows** driver must be installed before running `hans.exe`. It is bundled with [OpenVPN](https://openvpn.net/community-downloads/) -- installing OpenVPN is sufficient.

---

## Windows-specific Files

These files were added for the Windows/MSVC port and have no effect on Linux builds:

| File | Purpose |
|---|---|
| `src/win32_compat.h` / `.cpp` | POSIX compatibility layer (sockets, threads) |
| `src/tun_dev_win32.c` | TAP-Windows adapter driver with Overlapped I/O |
| `CMakeLists.txt` | CMake build system replacing the original Makefile |

> `src/hans_time.h` / `hans_time.cpp` were renamed from `time.h` / `time.cpp` to avoid a naming conflict with the MSVC system header `<time.h>`.
9 changes: 8 additions & 1 deletion src/auth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@
*
*/

/* win32_compat.h must be the first include on Windows. */
#ifdef _WIN32
# include "win32_compat.h"
#endif

#include "auth.h"
#include "sha1.h"
#include "utility.h"

#include <arpa/inet.h>
#ifndef _WIN32
# include <arpa/inet.h>
#endif

Auth::Auth(const std::string &passphrase)
: passphrase(passphrase)
Expand Down
14 changes: 11 additions & 3 deletions src/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,24 @@
*
*/

/* win32_compat.h must be the first include on Windows. */
#ifdef _WIN32
# include "win32_compat.h"
#endif

#include "client.h"
#include "server.h"
#include "exception.h"
#include "config.h"
#include "utility.h"

#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <syslog.h>

#ifndef _WIN32
# include <arpa/inet.h>
# include <netinet/in.h>
# include <syslog.h>
#endif

using std::vector;
using std::string;
Expand Down
47 changes: 32 additions & 15 deletions src/echo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,39 @@
*
*/

/* win32_compat.h must be the first include on Windows. */
#ifdef _WIN32
# include "win32_compat.h"
#endif

#include "echo.h"
#include "exception.h"

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#ifdef _WIN32
/* struct ip defined in win32_compat.h; winsock2 provides socket API */
# define CLOSE_SOCKET(fd) closesocket((SOCKET)(uintptr_t)(fd))
#else
# include <sys/socket.h>
# include <sys/types.h>
# include <netinet/in_systm.h>
# include <netinet/in.h>
# include <netinet/ip.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <errno.h>
# include <syslog.h>
# define CLOSE_SOCKET(fd) close(fd)
#endif

#include <stdio.h>
#include <string.h>
#include <sys/types.h>

typedef ip IpHeader;

Echo::Echo(int maxPayloadSize)
{
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd == -1)
fd = (hans_fd_t)socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd == (hans_fd_t)-1)
throw Exception("creating icmp socket", true);

bufferSize = maxPayloadSize + headerSize();
Expand All @@ -48,7 +59,7 @@ Echo::Echo(int maxPayloadSize)

Echo::~Echo()
{
close(fd);
CLOSE_SOCKET(fd);
}

int Echo::headerSize()
Expand All @@ -73,7 +84,10 @@ void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uin
header->chksum = 0;
header->chksum = icmpChecksum(sendBuffer.data() + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));

int result = sendto(fd, sendBuffer.data() + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
int result = sendto((SOCKET)(uintptr_t)fd,
sendBuffer.data() + sizeof(IpHeader),
payloadLength + (int)sizeof(EchoHeader), 0,
(struct sockaddr *)&target, sizeof(struct sockaddr_in));
if (result == -1)
syslog(LOG_ERR, "error sending icmp packet: %s", strerror(errno));
}
Expand All @@ -83,7 +97,10 @@ int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
struct sockaddr_in source;
int source_addr_len = sizeof(struct sockaddr_in);

int dataLength = recvfrom(fd, receiveBuffer.data(), bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
int dataLength = recvfrom((SOCKET)(uintptr_t)fd,
receiveBuffer.data(), bufferSize, 0,
(struct sockaddr *)&source,
(socklen_t *)&source_addr_len);
if (dataLength == -1)
{
syslog(LOG_ERR, "error receiving icmp packet: %s", strerror(errno));
Expand Down
14 changes: 12 additions & 2 deletions src/echo.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@
#include <vector>
#include <stdint.h>

/* hans_fd_t: intptr_t on Windows (covers 64-bit SOCKET), int on POSIX */
#ifdef _WIN32
# include "win32_compat.h"
#else
# ifndef HANS_FD_T_DEFINED
# define HANS_FD_T_DEFINED
typedef int hans_fd_t;
# endif
#endif

class Echo
{
public:
Echo(int maxPayloadSize);
~Echo();

int getFd() { return fd; }
hans_fd_t getFd() { return fd; }

void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
Expand All @@ -51,7 +61,7 @@ class Echo

uint16_t icmpChecksum(const char *data, int length);

int fd;
hans_fd_t fd;
int bufferSize;
std::vector<char> sendBuffer;
std::vector<char> receiveBuffer;
Expand Down
Loading