Initial commit

This commit is contained in:
2024-04-27 11:20:46 +02:00
commit 648178bce9
10 changed files with 2823 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
debug
release

47
Makefile Normal file
View File

@@ -0,0 +1,47 @@
debug_builddir = $(CURDIR)/debug
debug_target = $(debug_builddir)/wg_quicker
release_builddir = $(CURDIR)/release
release_target = $(release_builddir)/wg_quicker
CFLAGS = -g
ALL_CFLAGS += -std=c11
ALL_CFLAGS += -Wall
ALL_CFLAGS += -DLOG_LEVEL_DEBUG
ALL_CFLAGS += $(CFLAGS)
source += wg_quicker.c
source += lstring.c
source += wireguard.c
include += log.h
include += types.h
include += lstring.h
include += wireguard.h
all: debug
clean:
@rm -Rf $(debug_builddir)
@rm -Rf $(release_builddir)
debug: $(debug_target)
release: $(release_target)
.PHONY: all clean run release debug
$(debug_target): $(source) $(include) Makefile
@echo Modified files: $?
@mkdir -p $(debug_builddir)
@$(CC) -o $@ $(ALL_CFLAGS) $(source)
$(release_target): $(source) $(include) Makefile
@echo Modified files: $?
@mkdir -p $(release_builddir)
@$(CC) -o $@ -DRELEASE $(ALL_CFLAGS) $(source)

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# Wireguard utility programs
Utility programs to work with Wireguard VPNs.
## wg-quicker
Utility to create and update wg-quick configuration files.

61
log.h Normal file
View File

@@ -0,0 +1,61 @@
// LCZ libraries v0.1b
#ifndef _LCZ_LOG_H_
#define _LCZ_LOG_H_
#include <stdio.h>
#define LOG_COLOR_RESET "\033[0m"
#define LOG_COLOR_DEBUG "\033[1;92m"
#define LOG_COLOR_INFO "\033[1;96m"
#define LOG_COLOR_WARNING "\033[1;93m"
#define LOG_COLOR_ERROR "\033[1;91m"
#define LOG_PREFIX_DEBUG LOG_COLOR_DEBUG "DEBUG" LOG_COLOR_RESET
#define LOG_PREFIX_INFO LOG_COLOR_INFO "INFO" LOG_COLOR_RESET
#define LOG_PREFIX_WARNING LOG_COLOR_WARNING "WARNING" LOG_COLOR_RESET
#define LOG_PREFIX_ERROR LOG_COLOR_ERROR "ERROR" LOG_COLOR_RESET
#ifdef LOG_LEVEL_DEBUG
#define LOG_LEVEL_INFO 1
#endif
#ifdef LOG_LEVEL_INFO
#define LOG_LEVEL_WARN 1
#endif
#ifdef LOG_LEVEL_WARN
#define LOG_LEVEL_ERROR 1
#endif
#ifdef LOG_LEVEL_DEBUG
#define LogDebug(FORMAT_STR, ...) fprintf(stderr, LOG_PREFIX_DEBUG " %s:%d: " FORMAT_STR "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LogDebug(...) ((void)0)
#endif
#ifdef LOG_LEVEL_INFO
#define LogInfo(FORMAT_STR, ...) fprintf(stderr, LOG_PREFIX_INFO " %s:%d: " FORMAT_STR "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LogInfo(...) ((void)0)
#endif
#ifdef LOG_LEVEL_WARN
#define LogWarning(FORMAT_STR, ...) fprintf(stderr, LOG_PREFIX_WARNING " %s:%d: " FORMAT_STR "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LogWarning(...) ((void)0)
#endif
#ifdef LOG_LEVEL_ERROR
#define LogError(FORMAT_STR, ...) fprintf(stderr, LOG_PREFIX_ERROR " %s:%d: " FORMAT_STR "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define LogError(...) ((void)0)
#endif
#endif

301
lstring.c Normal file
View File

@@ -0,0 +1,301 @@
// LCZ libraries v0.1b
#include "lstring.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
// String building / constructors
String string_take(char *src)
{
String res;
res.text = src;
res.length = rstring_length(src);
return res;
}
DString dstring_take(char *src)
{
DString res;
res.text = src;
res.length = rstring_length(src);
res.capacity = res.length;
return res;
}
String string_copy(const char *src, u64 length)
{
String res;
res.text = malloc(length + 1);
assert(res.text != NULL);
memcpy(res.text, src, length);
res.text[length] = '\0';
res.length = length;
return res;
}
DString dstring_copy(const char *src, u64 length)
{
DString res;
res.text = malloc(length + 1);
assert(res.text != NULL);
memcpy(res.text, src, length);
res.text[length] = '\0';
res.length = length;
res.capacity = length;
return res;
}
DString dstring_new(u64 capacity)
{
DString res;
res.text = malloc(capacity + 1);
assert(res.text != NULL);
res.text[0] = '\0';
res.length = 0;
res.capacity = capacity;
return res;
}
// n = number of strings, ... = , const char *src1, u64 length1, ...
String string_concat(u32 n, ...)
{
va_list ap;
va_start(ap, n);
const char *strings[n];
u64 sizes[n];
u64 tot_len = 0;
for (u32 i = 0; i < n; i++)
{
strings[i] = va_arg(ap, const char *);
sizes[i] = va_arg(ap, u64);
tot_len += sizes[i];
}
String res;
res.text = malloc(tot_len + 1);
assert(res.text != NULL);
res.length = tot_len;
char *cursor = res.text;
for (u32 i = 0; i < n; i++)
{
memcpy(cursor, strings[i], sizes[i]);
cursor += sizes[i];
}
*cursor = '\0';
va_end(ap);
return res;
}
// n = number of strings, ... = , const char *src1, u64 length1, ...
DString dstring_concat(u32 n, ...)
{
va_list ap;
const char *strings[n];
u64 sizes[n];
u64 tot_len = 0;
va_start(ap, n);
for (u32 i = 0; i < n; i++)
{
strings[i] = va_arg(ap, const char *);
sizes[i] = va_arg(ap, u64);
tot_len += sizes[i];
}
DString res;
res.text = malloc(tot_len + 1);
assert(res.text != NULL);
res.length = tot_len;
res.capacity = tot_len;
char *cursor = res.text;
for (u32 i = 0; i < n; i++)
{
memcpy(cursor, strings[i], sizes[i]);
cursor += sizes[i];
}
*cursor = '\0';
va_end(ap);
return res;
}
// Compute string length, excluding zero-terminator
u64 rstring_length(const char *src)
{
u64 length = 0;
while(*(src++) != '\0')
length++;
return length;
}
// Comparison
int rstring_compare(const char* left, const char *right)
{
do
{
if (*left < *right)
return -1;
if (*left > *right)
return +1;
right++;
} while (*(left++));
return 0;
}
bool string_equal(String left, String right)
{
if (left.length != right.length)
return false;
char *l = left.text;
char *r = right.text;
char *l_end = l + left.length;
while (l != l_end)
{
if (*l != *r)
return false;
l++; r++;
}
return true;
}
bool string_find(String str, u64 start_index, String to_find, u64 *found_index)
{
// @TODO: Replace with better algorithm (Knuth-Morris-Pratt?)
char *end = str.text + str.length;
char *last_valid_start = end - to_find.length;
for (char *curr = str.text + start_index; curr <= last_valid_start; curr++)
{
bool found = true;
for (u64 i = 0; i < to_find.length; i++)
{
if (curr[i] != to_find.text[i])
{
found = false;
break;
}
}
if (found)
{
*found_index = curr - str.text;
return true;
}
}
return false;
}
String string_substring(String *str, u64 start, u64 end)
{
String res;
res.text = &str->text[start];
end = (end < str->length ? end : str->length);
end = (start < end ? end : start);
res.length = end - start;
return res;
}
String string_trim(String *str)
{
u64 start = 0;
u64 end = str->length;
while(start < str->length && isspace(str->text[start]))
start++;
while(end > start && isspace(str->text[end-1]))
end--;
return string_substring(str, start, end);
}
// DString specific operations
void dstring_reserve(DString *dstr, u64 new_length)
{
if (dstr->length < new_length)
dstr->text = realloc(dstr->text, new_length + 1); // + 1 --> reserve space for '\0'
assert(dstr->text != NULL);
}
void dstring_append(DString *dstr, const char *to_append, u64 count)
{
u64 len;
// Make sure we have enought space
len = dstr->length + count;
dstring_reserve(dstr, len);
// Copy bytes
memcpy(dstr->text + dstr->length, to_append, count);
dstr->text[len] = '\0';
dstr->length = len;
}
void dstring_insert(DString *dstr, u64 index, const char *to_insert, u64 count)
{
u64 len;
char *insert_start;
char *insert_end;
// Make sure we have enought space
len = dstr->length + count;
dstring_reserve(dstr, len);
// Move content to make space for the data we have to insert
insert_start = dstr->text + index;
insert_end = insert_start + count;
memmove(insert_end, insert_start, dstr->length - index + 1);
// Insert
memcpy(insert_start, to_insert, count);
dstr->length = len;
}
void dstring_erase(DString *dstr, u64 index, u64 count)
{
if (index + count > dstr->length)
count = dstr->length - index;
char *start = dstr->text + index;
char *end = start + count;
u64 to_move_from_end = dstr->length - index - count + 1; // Remember to move terminating zero
memmove(start, end, to_move_from_end);
dstr->length -= count;
}
void dstring_replace(DString *dstr, u64 index, u64 rem_count, const char *to_insert, u64 ins_count)
{
if (index + rem_count > dstr->length)
rem_count = dstr->length - index;
u64 len = dstr->length - rem_count + ins_count;
dstring_reserve(dstr, len);
char *start = dstr->text + index;
char *rem_end = start + rem_count;
char *ins_end = start + ins_count;
u64 to_move_from_end = dstr->length - index - rem_count + 1;
memmove(ins_end, rem_end, to_move_from_end);
memcpy(start, to_insert, ins_count);
dstr->length = len;
}

94
lstring.h Normal file
View File

@@ -0,0 +1,94 @@
// LCZ libraries v0.1b
#ifndef _LCZ_STRING_H_
#define _LCZ_STRING_H_
#ifndef _LCZ_TYPES_H_
#include "types.h"
#endif
/* String: constant length string.
* DString: dynamic length string.
* RString: raw c-style string. Not an actual type.
*
* USAGE INTERFACE:
* Length does not include '\0', but most strings should still be zero-terminated.
*
* DString is purposefully made to have the same memory layout as String,
* so you can use DString everywhere you can use String (but not vice-versa) by converting it with TO_STRING(dstr).
*
* Advanced: Actually String is not constant length. It's just unknown how much space has been allocated for it.
* Make sure you have enough space before modifying the lenght.
* DStrings change their size automatically if you use the functions in this library.
*
* IMPLEMENTER INTERFACE
* The "text" pointer should always be valid, unless you are building the String objects yourself.
* "capacity" and "length" do not include the zero terminator of the string. Make sure you malloc 1 more byte to fit it.
*
* String and DString are small containers, so they are not expensive to copy around.
* (It's PROBABLY more expensive to dereference a pointer than to copy a String container. Did not profile though.)
*/
/* @TODO: Add allocator function to parameters?
* @TODO: Use String type instead of passing text + length everywhere?
*
* @Note: I considered adding a SString (Static string, immutable), but it would be so inconvenient to use
* that I decided to leave it out (it's just like the "const" keyword)
*/
struct String
{
char *text;
u64 length;
};
struct DString
{
char *text;
u64 length;
u64 capacity;
};
typedef struct String String;
typedef struct DString DString;
#define TO_STRING(dstr) (*((String*)&dstr))
// String building / constructors
String string_take(char *src);
DString dstring_take(char *src);
String string_copy(const char *src, u64 length);
DString dstring_copy(const char *src, u64 length);
DString dstring_new(u64 capacity);
// n = number of strings, ... = , const char *src1, u64 length1, ...
String string_concat(u32 n, ...);
// n = number of strings, ... = , const char *src1, u64 length1, ...
DString dstring_concat(u32 n, ...);
// Compute string length, excluding zero-terminator
u64 rstring_length(const char *src);
// Comparison
int rstring_compare(const char *left, const char *right);
bool string_equal(String left, String right);
bool string_find(String str, u64 start_index, String to_find, u64 *found_index);
// String specific operations
// Warning: You have to make sure the space pointed by str is big enough to fit to_append.
void string_append(String *str, const char *to_append, u64 count);
String string_substring(String *str, u64 start, u64 end);
String string_trim(String *str);
// DString specific operations
void dstring_reserve(DString *dstr, u64 new_length);
void dstring_append(DString *dstr, const char *to_append, u64 count);
void dstring_insert(DString *dstr, u64 index, const char *to_insert, u64 count);
void dstring_erase(DString *dstr, u64 index, u64 count);
void dstring_replace(DString *dstr, u64 index, u64 rem_count, const char *to_insert, u64 ins_count);
#endif

31
types.h Normal file
View File

@@ -0,0 +1,31 @@
// LCZ libraries v0.1b
#ifndef _LCZ_TYPES_H_
#define _LCZ_TYPES_H_
#include <stdint.h>
#include <stdbool.h>
// Standard types with better names
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef float f32;
typedef double f64;
// Status code for error managemet
enum StatusCode {
STATUS_ERR = 0,
STATUS_OK = 1
};
typedef enum StatusCode StatusCode;
#endif

424
wg_quicker.c Normal file
View File

@@ -0,0 +1,424 @@
#include "types.h"
#include "log.h"
#include "lstring.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include "wireguard.h"
const char *program_name = "wg_quicker";
struct IP4
{
u8 a;
u8 b;
u8 c;
u8 d;
};
typedef struct IP4 IP4;
struct VPN_Data
{
String name;
IP4 network;
String server_host;
String server_port;
String pre_shared_key;
String server_public_key;
IP4 last_ip;
};
typedef struct VPN_Data VPN_Data;
bool IP4_from_String(String *str, IP4 *ip)
{
IP4 res;
// Warning: str might not be zero terminated
int read = sscanf(str->text, "%hhu.%hhu.%hhu.%hhu", &res.a, &res.b, &res.c, &res.d);
// TODO: Check for parsing errors
*ip = res;
return true;
}
void String_split(String *str, char divider, String **result_arr, u64 *result_count)
{
u64 capacity = 8;
String *res = malloc(sizeof(String) * capacity);
u64 count = 0;
u64 start = 0;
for(u64 i = 0; i < str->length; i++)
{
if(str->text[i] == divider)
{
// Resize backing storage if not big enought
if(count >= capacity)
{
capacity *= 1.5;
res = realloc(res, sizeof(String) * capacity);
}
// Add line substring to array
res[count++] = string_substring(str, start, i);
start = i;
}
}
*result_arr = res;
*result_count = count;
}
bool VPN_Data_from_String(String *str, VPN_Data *vpn)
{
String *lines;
u64 lines_count;
String_split(str, '\n', &lines, &lines_count);
if(lines_count != 7)
{
LogError("Error parsing data file,");
return false;
}
VPN_Data res;
res.name = string_trim(&lines[0]);
if(! IP4_from_String(&lines[1], &res.network))
{
LogError("Error parsing network address.");
return false;
}
res.server_host = string_trim(&lines[2]);
res.server_port = string_trim(&lines[3]);
res.pre_shared_key = string_trim(&lines[4]);
res.server_public_key = string_trim(&lines[5]);
if(! IP4_from_String(&lines[6], &res.last_ip))
{
LogError("Error parsing last IP address.");
return false;
}
free(lines);
*vpn = res;
return true;
}
String VPN_Data_to_String(VPN_Data *vpn)
{
DString res = dstring_new(2048);
res.length += snprintf(res.text, res.capacity,
"%.*s\n"
"%hhu.%hhu.%hhu.%hhu\n"
"%.*s\n"
"%.*s\n"
"%.*s\n"
"%.*s\n"
"%hhu.%hhu.%hhu.%hhu\n",
vpn->name.length, vpn->name.text,
vpn->network.a, vpn->network.b, vpn->network.c, vpn->network.d,
vpn->server_host.length, vpn->server_host.text,
vpn->server_port.length, vpn->server_port.text,
vpn->pre_shared_key.length, vpn->pre_shared_key.text,
vpn->server_public_key.length, vpn->server_public_key.text,
vpn->last_ip.a, vpn->last_ip.b, vpn->last_ip.c, vpn->last_ip.d
);
return TO_STRING(res);
}
String Stream_ReadAll(FILE *file, bool zero_terminated)
{
u64 end, start;
u64 fsize;
u64 read;
String res;
// Get file size
fseek(file, 0, SEEK_END);
end = ftell(file);
fseek(file, 0, SEEK_SET);
start = ftell(file);
fsize = end - start;
LogDebug("File size is %lu", fsize);
// Reserve memory for str
res.length = fsize;
if (zero_terminated)
res.length++;
res.text = malloc(res.length);
assert(res.text != NULL);
// Actually read data from file
read = fread(res.text, 1, fsize, file);
assert(read == fsize);
if (zero_terminated)
res.text[res.length] = '\0';
return res;
}
void Print_ErrorAndUsage(const char *error_msg)
{
LogError(
"%s\n"
"Usage: %s <command> ...\n"
"Commands:\n"
" new_vpn <vpn_name> <vpn_net_addr> <server_host_addr> <server_port>\n"
" add_client <vpn_name> <client_name>\n"
, error_msg, program_name
);
}
int main(int argc, char *argv[])
{
// Get program name from args
if(argc < 1)
{
LogError("Internal error (missing program name from args. This should never happen. There is a bug.)");
return 1;
}
program_name = argv[0];
// Get command name from args
if (argc < 2)
{
Print_ErrorAndUsage("Missing command.");
return 1;
}
String command = string_take(argv[1]);
if(string_equal(command, string_take("new_vpn")))
{
if(argc < 6)
{
Print_ErrorAndUsage("Missing argument.");
return 1;
}
String arg_vpn_name = string_take(argv[2]);
String arg_vpn_net_addr = string_take(argv[3]);
String arg_server_host_addr = string_take(argv[4]);
String arg_server_port = string_take(argv[5]);
VPN_Data vpn;
vpn.name = arg_vpn_name;
if(! IP4_from_String(&arg_vpn_net_addr, &vpn.network))
{
LogError("Error parsing argument: network address.");
return 1;
}
vpn.server_host = arg_server_host_addr;
vpn.server_port = arg_server_port;
if(vpn.network.d != 0)
{
LogError("Address %hhu.%hhu.%hhu.%hhu is not a valid /24 network address.", vpn.network.a, vpn.network.b, vpn.network.c, vpn.network.d);
return 2;
}
// Generate private/public key for server and pre shared key
wg_key_b64_string priv_b64, publ_b64, pre_shared_b64;
{
wg_key priv, publ, pre_shared;
wg_generate_private_key(priv);
wg_generate_public_key(publ, priv);
wg_generate_preshared_key(pre_shared);
wg_key_to_base64(priv_b64, priv);
wg_key_to_base64(publ_b64, publ);
wg_key_to_base64(pre_shared_b64, pre_shared);
}
vpn.pre_shared_key = string_take(pre_shared_b64);
vpn.server_public_key = string_take(publ_b64);
vpn.last_ip = vpn.network;
vpn.last_ip.d = 1;
// Save config data
String vpn_str = VPN_Data_to_String(&vpn);
String data_filename = string_concat(
2,
vpn.name.text, vpn.name.length,
".txt", rstring_length(".txt")
);
if(access(data_filename.text, F_OK) == 0)
{
LogError("File \"%s\" already exists.", data_filename.text);
return 3;
}
FILE *data_f = fopen(data_filename.text, "w");
if (! data_f)
{
LogError("Cannot open \"%s\"", data_filename.text);
return 2;
}
fprintf(data_f, "%.*s", vpn_str.length, vpn_str.text);
fclose(data_f);
free(data_filename.text);
free(vpn_str.text);
// Create wg-quick configuration file
String wg_config_filename = string_concat(
3,
"/etc/wireguard/", rstring_length("/etc/wireguard/"),
vpn.name.text, vpn.name.length,
".conf", rstring_length(".conf")
);
if(access(wg_config_filename.text, F_OK) == 0)
{
LogError("File \"%s\" already exists.", wg_config_filename.text);
return 3;
}
FILE *wg_config_f = fopen(wg_config_filename.text, "w");
if (! wg_config_f)
{
LogError("Cannot open \"%s\"", wg_config_filename.text);
return 2;
}
fprintf(wg_config_f, "[Interface]\n");
fprintf(wg_config_f, "Address = %hhu.%hhu.%hhu.%hhu/24\n", vpn.last_ip.a, vpn.last_ip.b, vpn.last_ip.c, vpn.last_ip.d);
fprintf(wg_config_f, "PrivateKey = %s\n", priv_b64);
fprintf(wg_config_f, "PostUp = firewall-cmd --zone=public --add-port=%.*s/udp\n", vpn.server_port.length, vpn.server_port.text);
fprintf(wg_config_f, "PostUp = firewall-cmd --zone=public --remove-port=%.*s/udp\n", vpn.server_port.length, vpn.server_port.text);
fprintf(wg_config_f, "ListenPort = %.*s\n", vpn.server_port.length, vpn.server_port.text);
fclose(wg_config_f);
free(wg_config_filename.text);
printf("You can now activate the Wireguard service with the command \"systemctl start wg-quick@%.*s\"\n", vpn.name.length, vpn.name.text);
}
else if(string_equal(command, string_take("add_client")))
{
if(argc < 4)
{
Print_ErrorAndUsage("Missing argument.");
return 1;
}
String vpn_name = string_take(argv[2]);
String client_name = string_take(argv[3]);
// Read data file
String data_filename = string_concat(
2,
vpn_name.text, vpn_name.length,
".txt", rstring_length(".txt")
);
FILE *data_f = fopen(data_filename.text, "r+");
if (! data_f)
{
LogError("Cannot open \"%s\"", data_filename.text);
return 2;
}
String vpn_str = Stream_ReadAll(data_f, true);
VPN_Data vpn;
if(! VPN_Data_from_String(&vpn_str, &vpn))
{
LogError("Cannot parse data file.");
return 2;
}
if (vpn.last_ip.d >= 254)
{
LogError("Address space full. (You already generated configs for 253 clients)");
return 2;
}
vpn.last_ip.d += 1;
// Generate client private and public keys
wg_key_b64_string priv_b64, publ_b64;
{
wg_key priv, publ;
wg_generate_private_key(priv);
wg_key_to_base64(priv_b64, priv);
wg_generate_public_key(publ, priv);
wg_key_to_base64(publ_b64, publ);
}
// Update server config file
String wg_config_filename = string_concat(
3,
"/etc/wireguard/", rstring_length("/etc/wireguard/"),
vpn.name.text, vpn.name.length,
".conf", rstring_length(".conf")
);
FILE *wg_config_f = fopen(wg_config_filename.text, "a");
if(access(wg_config_filename.text, F_OK) != 0)
{
LogError("File \"%s\" does not exist.", wg_config_filename.text);
return 3;
}
if (! wg_config_f)
{
LogError("Cannot open \"%s\"", wg_config_filename.text);
return 2;
}
fprintf(wg_config_f, "\n");
fprintf(wg_config_f, "[Peer]\n");
fprintf(wg_config_f, "# User: %.*s\n", client_name.length, client_name.text);
fprintf(wg_config_f, "PublicKey = %s\n", publ_b64);
fprintf(wg_config_f, "PresharedKey = %.*s\n", vpn.pre_shared_key.length, vpn.pre_shared_key.text);
fprintf(wg_config_f, "AllowedIPs = %hhu.%hhu.%hhu.%hhu/32\n", vpn.last_ip.a, vpn.last_ip.b, vpn.last_ip.c, vpn.last_ip.d);
fclose(wg_config_f);
free(wg_config_filename.text);
// Create client config file
String client_conf_path = string_concat(
2,
client_name.text, client_name.length,
".conf", rstring_length(".conf")
);
FILE *client_conf_f = fopen(client_conf_path.text, "w");
if (!client_conf_f)
{
LogError("Cannot open \"%s\"", client_conf_path.text);
return 2;
}
fprintf(client_conf_f, "[Interface]\n");
fprintf(client_conf_f, "Address = %hhu.%hhu.%hhu.%hhu/32\n", vpn.last_ip.a, vpn.last_ip.b, vpn.last_ip.c, vpn.last_ip.d);
fprintf(client_conf_f, "PrivateKey = %s\n", priv_b64);
fprintf(client_conf_f, "\n");
fprintf(client_conf_f, "[Peer]\n");
fprintf(client_conf_f, "PublicKey = %.*s\n", vpn.server_public_key.length, vpn.server_public_key.text);
fprintf(client_conf_f, "PresharedKey = %.*s\n", vpn.pre_shared_key.length, vpn.pre_shared_key.text);
fprintf(client_conf_f, "AllowedIPs = %hhu.%hhu.%hhu.%hhu/24\n", vpn.network.a, vpn.network.b, vpn.network.c, vpn.network.d);
fprintf(client_conf_f, "\n");
fprintf(client_conf_f, "Endpoint = %.*s:%.*s\n", vpn.server_host.length, vpn.server_host.text, vpn.server_port.length, vpn.server_port.text);
fprintf(client_conf_f, "PersistentKeepalive = 30\n");
fclose(client_conf_f);
// Save config data (last ip changed)
String vpn_str_bis = VPN_Data_to_String(&vpn);
fseek(data_f, 0, SEEK_SET);
fprintf(data_f, "%.*s", vpn_str_bis.length, vpn_str_bis.text);
printf("Client config file created: \"%.*s\".\n", client_conf_path.length, client_conf_path.text);
printf("Remember to restart the Wireguard service with the command \"systemctl restart wg-quick@%.*s\" to apply the changes.\n", vpn.name.length, vpn.name.text);
free(client_conf_path.text);
free(vpn_str_bis.text);
free(vpn_str.text);
fclose(data_f);
free(data_filename.text);
}
else
{
Print_ErrorAndUsage("Unrecognized command.");
return 1;
}
return 0;
}

1755
wireguard.c Normal file

File diff suppressed because it is too large Load Diff

103
wireguard.h Normal file
View File

@@ -0,0 +1,103 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
/*
* Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#ifndef WIREGUARD_H
#define WIREGUARD_H
#include <net/if.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t wg_key[32];
typedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];
/* Cross platform __kernel_timespec */
struct timespec64 {
int64_t tv_sec;
int64_t tv_nsec;
};
typedef struct wg_allowedip {
uint16_t family;
union {
struct in_addr ip4;
struct in6_addr ip6;
};
uint8_t cidr;
struct wg_allowedip *next_allowedip;
} wg_allowedip;
enum wg_peer_flags {
WGPEER_REMOVE_ME = 1U << 0,
WGPEER_REPLACE_ALLOWEDIPS = 1U << 1,
WGPEER_HAS_PUBLIC_KEY = 1U << 2,
WGPEER_HAS_PRESHARED_KEY = 1U << 3,
WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4
};
typedef struct wg_peer {
enum wg_peer_flags flags;
wg_key public_key;
wg_key preshared_key;
union {
struct sockaddr addr;
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
} endpoint;
struct timespec64 last_handshake_time;
uint64_t rx_bytes, tx_bytes;
uint16_t persistent_keepalive_interval;
struct wg_allowedip *first_allowedip, *last_allowedip;
struct wg_peer *next_peer;
} wg_peer;
enum wg_device_flags {
WGDEVICE_REPLACE_PEERS = 1U << 0,
WGDEVICE_HAS_PRIVATE_KEY = 1U << 1,
WGDEVICE_HAS_PUBLIC_KEY = 1U << 2,
WGDEVICE_HAS_LISTEN_PORT = 1U << 3,
WGDEVICE_HAS_FWMARK = 1U << 4
};
typedef struct wg_device {
char name[IF_NAMESIZE];
uint32_t ifindex;
enum wg_device_flags flags;
wg_key public_key;
wg_key private_key;
uint32_t fwmark;
uint16_t listen_port;
struct wg_peer *first_peer, *last_peer;
} wg_device;
#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)
#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)
#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)
int wg_set_device(wg_device *dev);
int wg_get_device(wg_device **dev, const char *device_name);
int wg_add_device(const char *device_name);
int wg_del_device(const char *device_name);
void wg_free_device(wg_device *dev);
char *wg_list_device_names(void); /* first\0second\0third\0forth\0last\0\0 */
void wg_key_to_base64(wg_key_b64_string base64, const wg_key key);
int wg_key_from_base64(wg_key key, const wg_key_b64_string base64);
bool wg_key_is_zero(const wg_key key);
void wg_generate_public_key(wg_key public_key, const wg_key private_key);
void wg_generate_private_key(wg_key private_key);
void wg_generate_preshared_key(wg_key preshared_key);
#endif