static int get_tunnel(char *port, char *secret) { // We use an IPv6 socket to cover both IPv4 and IPv6. int tunnel = socket(AF_INET6, SOCK_DGRAM, 0); //创建一个socket描述符,见3.1节 int flag = 1; setsockopt(tunnel, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); //打开地址复用功能 flag = 0; setsockopt(tunnel, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)); //同时允许ipv6和ipv4 // 只接收本地端口消息 sockaddr_in6 addr; //IPV6结构体,成员包括地址,端口,flow information以及scope-id等 memset(&addr, 0, sizeof(addr)); //初始化addr addr.sin6_family = AF_INET6; addr.sin6_port = htons(atoi(port)); //atoi()字符串转整数,htons()将主机字节顺序转为网络字节顺序 // Call bind(2) in a loop since Linux does not have SO_REUSEPORT. while (bind(tunnel, (sockaddr *)&addr, sizeof(addr))) { if (errno != EADDRINUSE) { //错误如果不是端口被占用,则退出。否则继续循环 return -1; } usleep(100000); //停止0.1s,此函数包含在unistd.h中 } // Receive packets till the secret matches. char packet[1024]; socklen_t addrlen; do { addrlen = sizeof(addr); int n = recvfrom(tunnel, packet, sizeof(packet), 0, (sockaddr *)&addr, &addrlen); //接收数据,成功返回接收到的字符数 if (n <= 0) { return -1; } packet[n] = 0; } while (packet[0] != 0 || strcmp(secret, &packet[1])); //strcmp 字符串比较,相等返回0,否则为正或者负数 // Connect to the client as we only handle one client at a time. connect(tunnel, (sockaddr *)&addr, addrlen); //连接客户端 return tunnel; }
static void build_parameters(char *parameters, int size, int argc, char **argv) { // 为了简单,只是盲目的连接各个参数 int offset = 0; for (int i = 4; i < argc; ++i) { char *parameter = argv[i]; int length = strlen(parameter); char delimiter = ','; // If it looks like an option, prepend a space instead of a comma. if (length == 2 && parameter[0] == '-') { ++parameter; --length; delimiter = ' '; } // This is just a demo app, really. if (offset + length >= size) { puts("Parameters are too large"); exit(1); } // Append the delimiter and the parameter. parameters[offset] = delimiter; memcpy(¶meters[offset + 1], parameter, length); offset += 1 + length; } // Fill the rest of the space with spaces. memset(¶meters[offset], ' ', size - offset); // Control messages always start with zero. parameters[0] = 0; }
int main(int argc, char **argv) { if (argc < 5) { printf("Usage: %s <tunN> <port> <secret> options...\n" "\n" "Options:\n" " -m <MTU> for the maximum transmission unit\n" " -a <address> <prefix-length> for the private address\n" " -r <address> <prefix-length> for the forwarding route\n" " -d <address> for the domain name server\n" " -s <domain> for the search domain\n" "\n" "Note that TUN interface needs to be configured properly\n" "BEFORE running this program. For more information, please\n" "read the comments in the source code.\n\n", argv[0]); exit(1); } // Parse the arguments and set the parameters. char parameters[1024]; build_parameters(parameters, sizeof(parameters), argc, argv); // Get TUN interface. int interface = get_interface(argv[1]); // Wait for a tunnel. int tunnel; while ((tunnel = get_tunnel(argv[2], argv[3])) != -1) { printf("%s: Here comes a new tunnel\n", argv[1]); // On UN*X, there are many ways to deal with multiple file // descriptors, such as poll(2), select(2), epoll(7) on Linux, // kqueue(2) on FreeBSD, pthread(3), or even fork(2). Here we // mimic everything from the client, so their source code can // be easily compared side by side. // Put the tunnel into non-blocking mode. fcntl(tunnel, F_SETFL, O_NONBLOCK); // Send the parameters several times in case of packet loss. for (int i = 0; i < 3; ++i) { send(tunnel, parameters, sizeof(parameters), MSG_NOSIGNAL); } // Allocate the buffer for a single packet. char packet[32767]; // We use a timer to determine the status of the tunnel. It // works on both sides. A positive value means sending, and // any other means receiving. We start with receiving. int timer = 0; // We keep forwarding packets till something goes wrong. while (true) { // Assume that we did not make any progress in this iteration. bool idle = true; // Read the outgoing packet from the input stream. int length = read(interface, packet, sizeof(packet)); if (length > 0) { // Write the outgoing packet to the tunnel. send(tunnel, packet, length, MSG_NOSIGNAL); // There might be more outgoing packets. idle = false; // If we were receiving, switch to sending. if (timer < 1) { timer = 1; } } // Read the incoming packet from the tunnel. length = recv(tunnel, packet, sizeof(packet), 0); if (length == 0) { break; } if (length > 0) { // Ignore control messages, which start with zero. if (packet[0] != 0) { // Write the incoming packet to the output stream. write(interface, packet, length); } // There might be more incoming packets. idle = false; // If we were sending, switch to receiving. if (timer > 0) { timer = 0; } } // If we are idle or waiting for the network, sleep for a // fraction of time to avoid busy looping. if (idle) { usleep(100000); // Increase the timer. This is inaccurate but good enough, // since everything is operated in non-blocking mode. timer += (timer > 0) ? 100 : -100; // We are receiving for a long time but not sending. // Can you figure out why we use a different value? :) if (timer < -16000) { // Send empty control messages. packet[0] = 0; for (int i = 0; i < 3; ++i) { send(tunnel, packet, 1, MSG_NOSIGNAL); } // Switch to sending. timer = 1; } // We are sending for a long time but not receiving. if (timer > 20000) { break; } } } printf("%s: The tunnel is broken\n", argv[1]); close(tunnel); } perror("Cannot create tunnels"); exit(1); }