如何用socket模拟tun接口通信c++? (已解决)[关闭]

问题描述 投票:0回答:0

我已经构建并实现了 IOS 平台的 OpenVpn 源代码。 这些来源没有 ios 支持,因为 Apple 没有让我们能够在 iPhone 上制作 tun 接口,而是为我们提供了一些 API (PacketTunnelProvider)。现在我有了可以正常工作的 PacketTunnelProvider Extension。但我需要将数据包传输到 OpenVpn 客户端库,反之亦然。它可以通过必须传递给 ovpn cli 的单个文件描述符进行通信来完成。

在 openvpn 库的普通实现中,这个文件描述符是 tun 接口的文件描述符,但在这种情况下我们无法访问它(感谢 Apple)。因此,我们需要通过文件描述符在通信方面“模拟”tun 接口的功能。

OpenVpn Lib 使用带有“write_some()”和“async_read_some()”方法的 boost::asio::posix::stream_descriptor 来读取和写入数据包到“tun 文件描述符”。 我正在尝试创建一个 unix 域数据报套接字,但是当我或 ovpn 尝试写入一些字节时,它会抛出错误 ->“需要目标地址”。 我哪里错了?

    struct sockaddr_un addr;
    ScopedFD fd(socket(AF_UNIX, SOCK_DGRAM, 0));
    
    if (fd.defined()) {
        memset(&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
        strcpy(addr.sun_path, socket_path);
        unlink(socket_path);

        if (bind(fd(), (struct sockaddr *)&addr, sizeof(addr)) < 0) {
            perror("bind");
        }else{
        
            boost::asio::io_service io_service;
            stream = new boost::asio::posix::stream_descriptor(io_service, fd());
            io_service.run();

        }
    }

更新!!

好的,我修好了!问题出在套接字实现中。 多亏了这个文档,我才能够找到问题并解决它:https://webcache.googleusercontent.com/search?q=cache:https://www.ibm.com/support/knowledgecenter/en/SSB23S_1。 1.0.13/gtpc1/unixsock.html

简而言之,我通过 SOCK_STREAM 类型的 unix 套接字创建了一个客户端/服务器接口来模拟 tun 接口。

这里是完整的实现:

//
//  tunemu.cpp
//  OpenVpnClient
//
//  Created by Emanuele on 17/03/17.
//  Copyright © 2017 Emanuele. All rights reserved.
//

#import <Foundation/Foundation.h>

#import <string>
#import <iostream>
#import <asio.hpp>
#import <boost/bind.hpp>
#import <boost/array.hpp>
#import <boost/foreach.hpp>
#import <boost/typeof/typeof.hpp>

#import <openvpn/io/io.hpp>

#define TUNEMU_BUFF_SIZE 2048

class TunEmu
{

    
public:
    
    TunEmu(NSString *path){
        
        SERVER_PATH = [[path stringByAppendingPathComponent:@"tun.server"] UTF8String];
        CLIENT_PATH = [[path stringByAppendingPathComponent:@"tun.client"] UTF8String];
        
        startUnixSockClient();
        startUnixSockServer();
        
    }
    
    
    int fd(){
        
        //Wait for connection
        dispatch_semaphore_wait(semaphore_client_ready, DISPATCH_TIME_FOREVER);
        
        if(tun_fd < 0) exit(0);
        
        return tun_fd;
    }
    
    
    void close(){
        if(tun_fd) ::close(tun_fd);
        if(ser_fd) ::close(ser_fd);
        if(cli_fd) ::close(cli_fd);
    }
    
    int write_some(NSArray *packets, NSArray *protocols)
    {
        
        int bytes_w = 0;
        
        try{
            
            @autoreleasepool {
                for (int i = 0; i < packets.count; i++){
                    NSData *data = [packets objectAtIndex:i];
                    
                    NSMutableData *buffer = [[NSMutableData alloc] init];
                    NSMutableData *packet = [[NSMutableData alloc] init];
                    uint16_t packet_size = data.length;
                    
                    /*         Prepend prefix (tcp/ip layer)     */
#ifdef TUNEMU_PREFIX
                    const std::uint32_t net_value = htonl([protocols[i] intValue]);
                    [packet appendBytes:(unsigned char *)&net_value length:4];
                    packet_size += 4;
#endif
                    //Verify buffer size
                    if(packet_size + 2 > TUNEMU_BUFF_SIZE){
                        continue; // packet too big, skip this packet
                    }
                    
                    [packet appendData:data];
                    /**********************************************/
                    
                    
                    /* Prepend packet size (g4b-tunemu protocol) */
                    unsigned char packet_size_data[2];
                    packet_size_data[1] = packet_size & 0xff;
                    packet_size_data[0] = (packet_size >> 8) & 0xff;
                    buffer = [NSMutableData dataWithBytes:&packet_size_data length:2]; // 2 bytes
                    
                    [buffer appendData:packet]; //Append packet data
                    [buffer increaseLengthBy:TUNEMU_BUFF_SIZE - buffer.length]; // Fill the buffer
                    /**********************************************/
                    
                    //Write packet on tun
                    bytes_w += stream->write_some(openvpn_io::const_buffer([buffer bytes], TUNEMU_BUFF_SIZE));
                    
                    //[packet dealloc];
                }
            }
            
            
            
        }catch (asio::system_error& e){
            NSString *error_s = [NSString stringWithCString:e.what() encoding:[NSString defaultCStringEncoding]];
            NSLog(@"TUN EMU write error: %@", error_s);
        }
        
        return bytes_w;
    }
    
    
    
    void set_read_some_handler(std::function<void(NSArray *packets, NSArray *protocols)> handler)
    {
        handle_read_callb = handler;
    }
    
    
    
    
private:
    const char *SERVER_PATH;
    const char *CLIENT_PATH;
    int tun_fd = -2;
    int ser_fd = -1;
    int cli_fd = -1;
    dispatch_semaphore_t semaphore_server_ready = dispatch_semaphore_create(0);
    dispatch_semaphore_t semaphore_client_ready = dispatch_semaphore_create(0);
    asio::posix::stream_descriptor *stream;
    boost::array<uint8_t, 0x800> recv_buffs;
    asio::error_code error;
    std::function<void(NSArray *packets, NSArray *protocols)> handle_read_callb;

    
    void onRead(const size_t bytes_transferred, const openvpn_io::error_code& ec) {
        if ((!ec) && (bytes_transferred > 0)) {
            @autoreleasepool{
                
                /* Remove header & resize (g4b-tunemu protocol) */
                uint16_t packet_size = ((uint16_t)(recv_buffs[0]) << 8) + (uint16_t)recv_buffs[1];
                NSData *packet = [NSData dataWithBytes:&recv_buffs[2] length:packet_size];
                /************************************************/
                
                /*         Remove prefix (tcp/ip layer)     */
#ifdef TUNEMU_PREFIX
                packet = [packet subdataWithRange:NSMakeRange(4, packet_size - 4)];
#endif
                /**********************************************/
                
                //handle_read_callb(@[packet], @[@(AF_INET)]);
                handle_read_callb([NSArray arrayWithObject:packet],
                                [NSArray arrayWithObject:[NSNumber numberWithInt:AF_INET]]);
                
                //[packet release];
            }

        }
        readMore();
    }
    
    
    
    void readMore() {
        
        //recv_buffs.assign(0);
        
        stream->async_read_some(openvpn_io::buffer(recv_buffs, TUNEMU_BUFF_SIZE),
                                [&](const openvpn_io::error_code& error, const size_t bytes_recvd)
                                {
                                    @autoreleasepool{
                                        TunEmu::onRead(bytes_recvd, error);
                                    }
                                });
    }
    
    
    void startUnixSockClient(){
        
        NSString *server_path = [NSString stringWithUTF8String:SERVER_PATH];
        NSString *client_path = [NSString stringWithUTF8String:CLIENT_PATH];
        
        /**************************************/
        /* Create a UNIX domain stream socket */
        /**************************************/
        tun_fd = socket(AF_UNIX, SOCK_STREAM, 0);
        if (tun_fd == -1) {
            exit(1);
        }
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            @autoreleasepool {
                
                int res, len;
                socklen_t buff_size = TUNEMU_BUFF_SIZE;
                struct sockaddr_un server_sockaddr;
                struct sockaddr_un client_sockaddr;
                memset(&server_sockaddr, 0, sizeof(struct sockaddr_un));
                memset(&client_sockaddr, 0, sizeof(struct sockaddr_un));
                
                
                /***************************************/
                /* Set up the UNIX sockaddr structure  */
                /* by using AF_UNIX for the family and */
                /* giving it a filepath to bind to.    */
                /*                                     */
                /* Unlink the file so the bind will    */
                /* succeed, then bind to that file.    */
                /***************************************/
                client_sockaddr.sun_family = AF_UNIX;
                strcpy(client_sockaddr.sun_path, [client_path UTF8String]);
                len = sizeof(client_sockaddr);
                
                unlink([client_path UTF8String]);
                res = ::bind(tun_fd, (struct sockaddr *) &client_sockaddr, len);
                if (res == -1){
                    close();
                    exit(1);
                }
                
                /***************************************/
                /* Set up the UNIX sockaddr structure  */
                /* for the server socket and connect   */
                /* to it.                              */
                /***************************************/
                server_sockaddr.sun_family = AF_UNIX;
                strcpy(server_sockaddr.sun_path, [server_path UTF8String]);
                
                /***************************************/
                /* Set blocking mode                   */
                /***************************************/
                set_blocking_mode(tun_fd, true);
                
                /***************************************/
                /* Set socket option                   */
                /***************************************/
                res = setsockopt(tun_fd, SOL_SOCKET, SO_SNDBUF, &buff_size, sizeof(buff_size));
                if (res == -1){
                    close();
                    exit(1);
                }
                res = setsockopt(tun_fd, SOL_SOCKET, SO_RCVBUF, &buff_size, sizeof(buff_size));
                if (res == -1){
                    close();
                    exit(1);
                }
                
                dispatch_semaphore_wait(semaphore_server_ready, DISPATCH_TIME_FOREVER);
                res = -1;
                while(res == -1){
                    res = ::connect(tun_fd, (struct sockaddr *) &server_sockaddr, len);
                    sleep(0.2);
                }
                
            }
        });
        
    }
    
    
    void startUnixSockServer(){
        
        NSString *server_path = [NSString stringWithUTF8String:SERVER_PATH];
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            @autoreleasepool {
                
                int len, res;
                socklen_t buff_size = TUNEMU_BUFF_SIZE;
                struct sockaddr_un server_sockaddr;
                struct sockaddr_un client_sockaddr;
                int backlog = 10;
                memset(&server_sockaddr, 0, sizeof(struct sockaddr_un));
                memset(&client_sockaddr, 0, sizeof(struct sockaddr_un));
                
                /**************************************/
                /* Create a UNIX domain stream socket */
                /**************************************/
                ser_fd = socket(AF_UNIX, SOCK_STREAM, 0);
                if (ser_fd == -1){
                    exit(1);
                }
                
                /***************************************/
                /* Set up the UNIX sockaddr structure  */
                /* by using AF_UNIX for the family and */
                /* giving it a filepath to bind to.    */
                /*                                     */
                /* Unlink the file so the bind will    */
                /* succeed, then bind to that file.    */
                /***************************************/
                server_sockaddr.sun_family = AF_UNIX;
                strcpy(server_sockaddr.sun_path, [server_path UTF8String]);
                len = sizeof(server_sockaddr);
                
                unlink([server_path UTF8String]);
                res = ::bind(ser_fd, (struct sockaddr *) &server_sockaddr, len);
                if (res == -1){
                    close();
                    exit(1);
                }
                
                /*********************************/
                /* Listen for any client sockets */
                /*********************************/
                res = listen(ser_fd, backlog);
                if (res == -1){
                    close();
                    exit(1);
                }
                
                /***************************************/
                /* Set blocking mode                   */
                /***************************************/
                set_blocking_mode(ser_fd, true);
                
                /***************************************/
                /* Set socket option                   */
                /***************************************/
                res = setsockopt(ser_fd, SOL_SOCKET, SO_SNDBUF, &buff_size, sizeof(buff_size));
                if (res == -1){
                    close();
                    exit(1);
                }
                res = setsockopt(ser_fd, SOL_SOCKET, SO_RCVBUF, &buff_size, sizeof(buff_size));
                if (res == -1){
                    close();
                    exit(1);
                }
                
                // Server ready
                dispatch_semaphore_signal(semaphore_server_ready);
                
                
                /*********************************/
                /* Accept an incoming connection */
                /*********************************/
                cli_fd = ::accept(ser_fd, (struct sockaddr *) &client_sockaddr, (socklen_t *) &len);
                if (cli_fd == -1){
                    close();
                    exit(1);
                }
                
                
                /****************************************/
                /* Read data on the server from clients */
                /****************************************/
                asio::io_context io_context;
                stream = new asio::posix::stream_descriptor(io_context, cli_fd);
                
                TunEmu::readMore();
                
                // Release the resource and signal the semaphore
                dispatch_semaphore_signal(semaphore_client_ready);
                
                io_context.run();
                
            }
            // Release the resource and signal the semaphore
        });
        //dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        
    }
    
    
    
    bool set_blocking_mode(const int &socket, bool is_blocking)
    {
        bool ret = true;
        
        const int flags = fcntl(socket, F_GETFL, 0);
        if ((flags & O_NONBLOCK) && !is_blocking) {
            NSLog(@"set_blocking_mode(): socket was already in non-blocking mode");
            return ret;
        }
        if (!(flags & O_NONBLOCK) && is_blocking) {
            NSLog(@"set_blocking_mode(): socket was already in blocking mode");
            return ret;
        }
        ret = 0 == fcntl(socket, F_SETFL, is_blocking ? flags ^ O_NONBLOCK : flags | O_NONBLOCK);
        
        return ret;
    }
    
};
c++ ios sockets file-descriptor openvpn
© www.soinside.com 2019 - 2024. All rights reserved.