Initial commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
debug
|
||||
release
|
||||
47
Makefile
Normal file
47
Makefile
Normal 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
5
README.md
Normal 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
61
log.h
Normal 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
301
lstring.c
Normal 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
94
lstring.h
Normal 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
31
types.h
Normal 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
424
wg_quicker.c
Normal 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
1755
wireguard.c
Normal file
File diff suppressed because it is too large
Load Diff
103
wireguard.h
Normal file
103
wireguard.h
Normal 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
|
||||
Reference in New Issue
Block a user