client.vala 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. namespace AstrogateTunnel {
  2. private static Socket socket;
  3. private static int tun_fd;
  4. private static string ifname;
  5. private static InetSocketAddress server_address;
  6. private static InetSocketAddress client_address;
  7. private static InetAddress vpn_network;
  8. private static InetAddress vpn_netmask;
  9. private static InetAddress vpn_ip;
  10. private static int mtu = 1500;
  11. public static int main(string[] args) {
  12. int client_port = 0;
  13. int server_port = 0;
  14. if(args.length < 6 || !int.try_parse(args[3], out client_port) || !int.try_parse(args[5], out server_port)) {
  15. print(@"USAGE: $(args[0]) interface client_ip client_port server_ip server_port\n");
  16. return -1;
  17. }
  18. try {
  19. server_address = new InetSocketAddress.from_string(args[4], server_port);
  20. client_address = new InetSocketAddress.from_string(args[2], client_port);
  21. ifname = args[1];
  22. socket = new Socket(server_address.family, SocketType.DATAGRAM, SocketProtocol.UDP);
  23. socket.bind(client_address, false);
  24. // Open tun device
  25. tun_fd = handle_posix_error("opening tun device", Posix.open("/dev/net/tun", Posix.O_RDWR));
  26. // Create tunnel device
  27. var request = generate_ifreq();
  28. handle_posix_error("creating tun interface", Linux.ioctl(tun_fd, IfTun.Tun.SETIFF, &request));
  29. // Register with server
  30. var registration_message = new uint8[] { 'R' };
  31. socket.connect(server_address);
  32. socket.send(registration_message);
  33. print(@"Waiting for $(server_address)...\n");
  34. var ack_message = new uint8[1024];
  35. socket.receive(ack_message);
  36. if(ack_message[0] != 'A') {
  37. print("Received invalid reply from server. Exiting.\n");
  38. return -2;
  39. }
  40. var mtu_dat = new uint8[2];
  41. if(ack_message[1] == '4') {
  42. vpn_network = new InetAddress.from_bytes(ack_message[2:6], SocketFamily.IPV4);
  43. vpn_netmask = new InetAddress.from_bytes(ack_message[6:10], SocketFamily.IPV4);
  44. vpn_ip = new InetAddress.from_bytes(ack_message[10:14], SocketFamily.IPV4);
  45. mtu_dat = ack_message[14:16];
  46. }
  47. else if(ack_message[1] == '6') {
  48. vpn_network = new InetAddress.from_bytes(ack_message[2:18], SocketFamily.IPV6);
  49. vpn_netmask = new InetAddress.from_bytes(ack_message[18:34], SocketFamily.IPV6);
  50. vpn_ip = new InetAddress.from_bytes(ack_message[34:50], SocketFamily.IPV6);
  51. mtu_dat = ack_message[50:52];
  52. }
  53. else {
  54. print("Received unsupported network type from server. Exiting.\n");
  55. return -3;
  56. }
  57. uint16 mtu_val = 0;
  58. Memory.copy(&mtu_val, mtu_dat, sizeof(uint16));
  59. mtu = mtu_val.to_big_endian();
  60. print(@"Registered with Astrogate server at $server_address:\n");
  61. print(@" Network: $vpn_network\n");
  62. print(@" Subnet mask: $vpn_netmask\n");
  63. print(@" Allocated IP: $vpn_ip\n");
  64. print(@" MTU: $mtu\n\n");
  65. request.ifr_flags = 0;
  66. request.ifr_mtu = mtu;
  67. handle_posix_error(@"setting MTU for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFMTU, &request));
  68. request.ifr_flags |= Linux.Network.IfFlag.UP;
  69. handle_posix_error(@"bringing $ifname up", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFFLAGS, &request));
  70. address_to_posix(vpn_ip, &request.ifr_addr);
  71. handle_posix_error(@"setting IP address for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFADDR, &request));
  72. address_to_posix(vpn_netmask, &request.ifr_addr);
  73. handle_posix_error(@"setting subnet mask for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFNETMASK, &request));
  74. address_to_posix(vpn_network, &request.ifr_addr);
  75. handle_posix_error(@"setting destination network for $ifname", Linux.ioctl(socket.fd, Linux.Network.SIOCSIFDSTADDR, &request));
  76. }
  77. catch(Error e) {
  78. print(@"Fatal error: $(e.message)\n");
  79. return -999;
  80. }
  81. new Thread<int>("udp listener", listen_udp);
  82. return listen_tun();
  83. }
  84. private int listen_udp() {
  85. while(true) {
  86. try {
  87. // Receive the next datagram
  88. var buffer = new uint8[mtu+5];
  89. var size = socket.receive(buffer);
  90. buffer.length = (int)size;
  91. if(buffer[0] == 'L') {
  92. link_ip(buffer);
  93. continue;
  94. }
  95. else if(buffer[0] == 'U') {
  96. unlink_ip(buffer);
  97. continue;
  98. }
  99. else if(buffer[0] != 'D') {
  100. print("Ignoring invalid datagram from server\n");
  101. continue;
  102. }
  103. handle_posix_error(@"piping datagram to $ifname", (int)Posix.write(tun_fd, buffer[1:buffer.length], buffer.length));
  104. }
  105. catch(Error e) {
  106. print(@"Exception handling incoming tunnel server datagram: $(e.message)\n");
  107. }
  108. }
  109. }
  110. private int listen_tun() {
  111. while(true) {
  112. var buffer = new uint8[mtu+4];
  113. var size = Posix.read(tun_fd, buffer, buffer.length);
  114. if(size < 0) {
  115. print(@"Got $(size) from Posix.read.\n");
  116. continue;
  117. }
  118. buffer.length = (int)size;
  119. try {
  120. var message = new uint8[buffer.length + 1];
  121. message[0] = 'D';
  122. Memory.copy(message[1:], buffer, buffer.length);
  123. socket.send(message);
  124. }
  125. catch (Error e) {
  126. print(@"Error forwarding datagram to server: $(e.message)\n");
  127. }
  128. }
  129. }
  130. private void link_ip(uint8[] buffer) throws Error {
  131. var address = new InetAddress.from_bytes(buffer[1:buffer.length], vpn_ip.family);
  132. print(@"Linking IP $(address)\n");
  133. var route = build_rtentry(address);
  134. var res = Linux.ioctl(socket.fd, Linux.Network.SIOCADDRT, &route);
  135. if(res < 0 && errno != Posix.EEXIST) {
  136. handle_posix_error(@"adding route to $address", res);
  137. }
  138. else if(errno == Posix.EEXIST) {
  139. print(@"Already linked to $(address)\n");
  140. }
  141. }
  142. private void unlink_ip(uint8[] buffer) throws Error {
  143. var address = new InetAddress.from_bytes(buffer[1:buffer.length], vpn_ip.family);
  144. print(@"Uninking IP $(address)\n");
  145. var route = build_rtentry(address);
  146. handle_posix_error(@"adding route to $address", Linux.ioctl(socket.fd, Linux.Network.SIOCDELRT, &route));
  147. }
  148. private Linux.Network.RtEntry build_rtentry(InetAddress address) {
  149. var route = Linux.Network.RtEntry();
  150. route.rt_dev = ifname;
  151. address_to_posix(address, &route.rt_dst);
  152. address_to_posix(new InetAddress.from_string("255.255.255.255"), &route.rt_genmask);
  153. route.rt_flags = Linux.Network.RtFlag.UP;
  154. return route;
  155. }
  156. private int handle_posix_error(string task, int result) throws Error {
  157. if(result < 0) {
  158. var code = Posix.errno;
  159. var message = Posix.strerror(code);
  160. throw new Error.literal(Quark.from_string("posix-error"), code, @"Got result $(result) while $(task): $message");
  161. }
  162. return result;
  163. }
  164. private void address_to_posix(InetAddress address, Posix.SockAddr *addr) {
  165. var addr_str = address.to_string();
  166. switch (address.family) {
  167. case SocketFamily.IPV4:
  168. var sin4 = Posix.SockAddrIn();
  169. sin4.sin_family = Posix.AF_INET;
  170. Posix.inet_pton(Posix.AF_INET, addr_str, &sin4.sin_addr.s_addr);
  171. Memory.copy(addr, &sin4, sizeof(Posix.SockAddr));
  172. break;
  173. case SocketFamily.IPV6:
  174. var sin6 = Posix.SockAddrIn();
  175. sin6.sin_family = Posix.AF_INET;
  176. Posix.inet_pton(Posix.AF_INET, addr_str, &sin6.sin_addr.s_addr);
  177. Memory.copy(addr, &sin6, sizeof(Posix.SockAddr));
  178. break;
  179. default:
  180. assert_not_reached();
  181. }
  182. }
  183. private Linux.Network.IfReq generate_ifreq() throws Error {
  184. var request = Linux.Network.IfReq();
  185. request.ifr_flags = IfTun.IfFlag.TUN;
  186. if(ifname.length > request.ifr_name.length) {
  187. error(@"Max length for interface name is $(request.ifr_name.length) characters, provided name is $(ifname.length) characters.");
  188. }
  189. for(int i = 0; i < ifname.length; i++){
  190. request.ifr_name[i] = ifname[i];
  191. }
  192. return request;
  193. }
  194. }