import 'dart:convert'; import 'dart:io'; import 'package:waylume_server/core/utils.dart'; import 'package:waylume_server/wireguard/utils.dart'; import 'package:waylume_server/wireguard/traffic_control.dart'; class WireGuardPeer { final String privateKey; final String publicKey; final String ip; WireGuardPeer({ required this.privateKey, required this.publicKey, required this.ip, }); Map toJson() => { 'privateKey': privateKey, 'publicKey': publicKey, 'ip': ip, }; factory WireGuardPeer.fromJson(Map json) => WireGuardPeer( privateKey: json['privateKey'], publicKey: json['publicKey'], ip: json['ip'], ); String generateClientConfig({ required String serverPublicKey, required String serverEndpoint, String dns = '1.1.1.1, 8.8.8.8', // Cloudflare and Google DNS String allowedIPs = '0.0.0.0/0', }) { return ''' [Interface] PrivateKey = $privateKey Address = $ip/32 DNS = $dns [Peer] PublicKey = $serverPublicKey Endpoint = $serverEndpoint AllowedIPs = $allowedIPs PersistentKeepalive = 25 '''; } } Future generateClientConfigForPeer(WireGuardPeer peer) async { String serverEndpoint = '${await getWanIp()}:51820'; String serverPublicKey = await getServerPublicKey(); return peer.generateClientConfig( serverPublicKey: serverPublicKey, serverEndpoint: serverEndpoint, ); } /// Creates a new WireGuard peer and returns the peer info Future createPeer() async { // Generate private key final privateKeyResult = await Process.run('wg', ['genkey']); final privateKey = privateKeyResult.stdout.toString().trim(); // Generate public key from private key final pubProcess = await Process.start('wg', ['pubkey']); pubProcess.stdin.writeln(privateKey); await pubProcess.stdin.close(); final publicKey = await pubProcess.stdout.transform(utf8.decoder).join(); // Get next available IP final ip = await _getNextIP(); // Add peer to WireGuard await Process.run('wg', ['set', 'wg0', 'peer', publicKey.trim(), 'allowed-ips', '$ip/32']); return WireGuardPeer( privateKey: privateKey, publicKey: publicKey.trim(), ip: ip, ); } Future deletePeer(String publicKey) async { // Remove peer from WireGuard final result = await Process.run('wg', ['set', 'wg0', 'peer', publicKey, 'remove']); if (result.exitCode != 0) { print('Error removing peer: ${result.stderr}'); return false; } return true; } Future setSpeedLimit(String publicKey, int speedKbps) async { print('Setting speed limit for peer $publicKey to ${speedKbps}kbps'); final peerIP = await _getPeerIP(publicKey); print('Found peer IP: $peerIP for publicKey: $publicKey'); if (peerIP == null) throw Exception('Peer not found'); await TrafficControlService.setSpeedLimit(peerIP, speedKbps); } Future setDataCap(String publicKey, int dataCapMB) async { final peerIP = await _getPeerIP(publicKey); if (peerIP == null) throw Exception('Peer not found'); await TrafficControlService.setDataCap(peerIP, dataCapMB); } Future _getPeerIP(String publicKey) async { final result = await Process.run('wg', ['show', 'wg0', 'allowed-ips']); if (result.exitCode != 0) return null; final lines = result.stdout.toString().split('\n'); for (final line in lines) { if (line.contains(publicKey)) { final match = RegExp(r'(\d+\.\d+\.\d+\.\d+)').firstMatch(line); return match?.group(1); } } return null; } Future _getNextIP() async { final result = await Process.run('wg', ['show', 'wg0', 'allowed-ips']); final usedIPs = RegExp(r'10\.(\d+)\.(\d+)\.(\d+)') .allMatches(result.stdout.toString()) .map((m) => '${m.group(1)}.${m.group(2)}.${m.group(3)}') .toSet(); // Start from 10.0.0.2 (skip .1 for server) for (int a = 0; a <= 255; a++) { for (int b = 0; b <= 255; b++) { for (int c = (a == 0 && b == 0 ? 2 : 1); c <= 254; c++) { final ip = '10.$a.$b.$c'; if (!usedIPs.contains('$a.$b.$c')) return ip; } } } throw Exception('No available IPs'); }