我已经构建并实现了 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;
}
};