wireguard-tools/contrib/nat-hole-punching/nat-punch-client.c
Greg Kroah-Hartman 01d00bc035 global: add SPDX tags to all files
It's good to have SPDX identifiers in all files as the Linux kernel
developers are working to add these identifiers to all files.

Update all files with the correct SPDX license identifier based on the license
text of the project or based on the license in the file itself.  The SPDX
identifier is a legally binding shorthand, which can be used instead of the
full boiler plate text.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Modified-by: Jason A. Donenfeld <Jason@zx2c4.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
2017-12-09 22:29:28 +01:00

209 lines
4.9 KiB
C

/* SPDX-License-Identifier: GPL-2.0
*
* Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*
* Example only. Do not run in production.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <netinet/ip.h>
#include <linux/filter.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdarg.h>
#include <string.h>
#include <resolv.h>
#include <stdint.h>
#include <stdbool.h>
enum { MAX_PEERS = 65536, PORT = 49918 };
static struct {
uint8_t base64_key[45];
bool have_seen;
} peers[MAX_PEERS];
static unsigned int total_peers;
static const char *cmd(const char *line, ...)
{
static char buf[2048];
char full_cmd[2048] = { 0 };
size_t len;
FILE *f;
va_list args;
va_start(args, line);
vsnprintf(full_cmd, 2047, line, args);
va_end(args);
f = popen(full_cmd, "r");
if (!f) {
perror("popen");
exit(errno);
}
if (!fgets(buf, 2048, f)) {
pclose(f);
return NULL;
}
pclose(f);
len = strlen(buf);
if (!len)
return NULL;
if (buf[len - 1] == '\n')
buf[len - 1] = '\0';
return buf;
}
static void read_peers(const char *interface)
{
char full_cmd[2048] = { 0 };
size_t len;
FILE *f;
snprintf(full_cmd, 2047, "wg show %s peers", interface);
f = popen(full_cmd, "r");
if (!f) {
perror("popen");
exit(errno);
}
for (;;) {
if (!fgets(peers[total_peers].base64_key, 45, f))
break;
len = strlen(peers[total_peers].base64_key);
if (len != 44 && len != 45)
continue;
if (peers[total_peers].base64_key[len - 1] == '\n')
peers[total_peers].base64_key[len - 1] = '\0';
++total_peers;
}
pclose(f);
}
static void unbase64(uint8_t dstkey[32], const char *srckey)
{
uint8_t buf[33];
if (b64_pton(srckey, buf, 33) != 32) {
fprintf(stderr, "Could not parse base64 key: %s\n", srckey);
exit(EINVAL);
}
memcpy(dstkey, buf, 32);
}
static void apply_bpf(int sock, uint16_t port, uint32_t ip)
{
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3),
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1),
BPF_STMT(BPF_RET + BPF_K, -1),
BPF_STMT(BPF_RET + BPF_K, 0)
};
struct sock_fprog filter_prog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) {
perror("setsockopt(bpf)");
exit(errno);
}
}
int main(int argc, char *argv[])
{
struct sockaddr_in addr = {
.sin_family = AF_INET
};
struct {
struct udphdr udp;
uint8_t my_pubkey[32];
uint8_t their_pubkey[32];
} __attribute__((packed)) packet = {
.udp = {
.len = htons(sizeof(packet)),
.dest = htons(PORT)
}
};
struct {
struct iphdr iphdr;
struct udphdr udp;
uint32_t ip;
uint16_t port;
} __attribute__((packed)) reply;
ssize_t len;
int sock, i;
bool repeat;
struct hostent *ent;
const char *server = argv[1], *interface = argv[2];
if (argc < 3) {
fprintf(stderr, "Usage: %s SERVER WIREGUARD_INTERFACE\nExample:\n %s demo.wireguard.com wg0\n", argv[0], argv[0]);
return EINVAL;
}
if (getuid() != 0) {
fprintf(stderr, "Must be root!\n");
return EPERM;
}
ent = gethostbyname2(server, AF_INET);
if (!ent) {
herror("gethostbyname2");
return h_errno;
}
addr.sin_addr = *(struct in_addr *)ent->h_addr;
read_peers(interface);
cmd("ip link set %s up", interface);
unbase64(packet.my_pubkey, cmd("wg show %s public-key", interface));
packet.udp.source = htons(atoi(cmd("wg show %s listen-port", interface)));
/* We use raw sockets so that the WireGuard interface can actually own the real socket. */
sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
if (sock < 0) {
perror("socket");
return errno;
}
apply_bpf(sock, ntohs(packet.udp.source), ntohl(addr.sin_addr.s_addr));
check_again:
repeat = false;
for (i = 0; i < total_peers; ++i) {
if (peers[i].have_seen)
continue;
printf("[+] Requesting IP and port of %s: ", peers[i].base64_key);
unbase64(packet.their_pubkey, peers[i].base64_key);
if (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
putchar('\n');
perror("sendto");
return errno;
}
len = recv(sock, &reply, sizeof(reply), 0);
if (len < 0) {
putchar('\n');
perror("recv");
return errno;
}
if (len != sizeof(reply)) {
printf("server does not yet have it\n");
repeat = true;
} else {
printf("%s:%d\n", inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));
peers[i].have_seen = true;
cmd("wg set %s peer %s persistent-keepalive 25 endpoint %s:%d", interface, peers[i].base64_key, inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));
}
}
if (repeat) {
sleep(2);
goto check_again;
}
close(sock);
return 0;
}