438 lines
10 KiB
C
438 lines
10 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2011-2020 Pierre Pronchery <khorben@defora.org> */
|
|
/* This file is part of DeforaOS System VPN */
|
|
/* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, version 3 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include <sys/resource.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/socket.h>
|
|
#include <System.h>
|
|
#include <System/App.h>
|
|
#include "../src/common.c"
|
|
|
|
|
|
/* libVPN */
|
|
/* private */
|
|
/* types */
|
|
typedef struct _VPNAppClient
|
|
{
|
|
String const * name;
|
|
AppClient * appclient;
|
|
} VPNAppClient;
|
|
|
|
typedef struct _VPNAppClientFD
|
|
{
|
|
AppClient * appclient;
|
|
int32_t fd;
|
|
} VPNAppClientFD;
|
|
|
|
|
|
/* constants */
|
|
#define APPINTERFACE "VPN"
|
|
#define PROGNAME "libVPN"
|
|
|
|
|
|
/* variables */
|
|
static VPNAppClient * _vpn_clients = NULL;
|
|
static size_t _vpn_clients_cnt = 0;
|
|
|
|
static VPNAppClientFD * _vpn_clients_fd = NULL;
|
|
static size_t _vpn_clients_fd_cnt = 0;
|
|
|
|
static int _vpn_offset = 1024;
|
|
|
|
/* local functions */
|
|
static int (*old_close)(int fd);
|
|
static int (*old_connect)(int fd, const struct sockaddr * name,
|
|
socklen_t namelen);
|
|
static ssize_t (*old_read)(int fd, void * buf, size_t count);
|
|
static ssize_t (*old_recv)(int fd, void * buf, size_t count, int flags);
|
|
static ssize_t (*old_send)(int fd, void const * buf, size_t count, int flags);
|
|
static ssize_t (*old_write)(int fd, void const * buf, size_t count);
|
|
|
|
|
|
/* prototypes */
|
|
static void _libvpn_init(void);
|
|
|
|
/* accessors */
|
|
static AppClient * _libvpn_get_appclient(const struct sockaddr * name,
|
|
socklen_t namelen);
|
|
static AppClient * _libvpn_get_appclient_fd(int32_t * fd);
|
|
static String * _libvpn_get_remote_host(
|
|
const struct sockaddr * name, socklen_t namelen);
|
|
static int _libvpn_get_remote_name(
|
|
const struct sockaddr * name, socklen_t namelen,
|
|
struct sockaddr ** rname, socklen_t * rnamelen);
|
|
static unsigned int _libvpn_is_remote(const struct sockaddr * name,
|
|
socklen_t namelen);
|
|
|
|
/* useful */
|
|
static int _libvpn_deregister_fd(AppClient * appclient, int32_t fd);
|
|
static int _libvpn_register_fd(AppClient * appclient, int32_t * fd);
|
|
|
|
|
|
/* functions */
|
|
static void _libvpn_init(void)
|
|
{
|
|
static void * hdl = NULL;
|
|
/* FIXME some symbols may be in libsocket.so instead */
|
|
static char * libc[] = { "/lib/libc.so", "/lib/libc.so.6" };
|
|
size_t i;
|
|
#ifdef RLIMIT_NOFILE
|
|
struct rlimit r;
|
|
#endif
|
|
|
|
if(hdl != NULL)
|
|
return;
|
|
for(i = 0; i < sizeof(libc) / sizeof(*libc); i++)
|
|
if((hdl = dlopen(libc[i], RTLD_LAZY)) != NULL)
|
|
break;
|
|
if(hdl == NULL)
|
|
{
|
|
fprintf(stderr, "%s: %s\n", PROGNAME, dlerror());
|
|
exit(1);
|
|
}
|
|
if((old_close = dlsym(hdl, "close")) == NULL
|
|
|| (old_connect = dlsym(hdl, "connect")) == NULL
|
|
|| (old_read = dlsym(hdl, "read")) == NULL
|
|
|| (old_recv = dlsym(hdl, "recv")) == NULL
|
|
|| (old_send = dlsym(hdl, "send")) == NULL
|
|
|| (old_write = dlsym(hdl, "write")) == NULL)
|
|
{
|
|
fprintf(stderr, "%s: %s\n", PROGNAME, dlerror());
|
|
dlclose(hdl);
|
|
exit(1);
|
|
}
|
|
dlclose(hdl);
|
|
#ifdef RLIMIT_NOFILE
|
|
if(getrlimit(RLIMIT_NOFILE, &r) == 0)
|
|
{
|
|
if(r.rlim_max > INT_MAX)
|
|
{
|
|
fprintf(stderr, "%s: %s\n", PROGNAME, strerror(ERANGE));
|
|
exit(1);
|
|
}
|
|
if(_vpn_offset < r.rlim_max)
|
|
_vpn_offset = (int)r.rlim_max;
|
|
}
|
|
# ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() %u\n", __func__, _vpn_offset);
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
|
|
/* accessors */
|
|
/* libvpn_get_appclient */
|
|
static AppClient * _libvpn_get_appclient(const struct sockaddr * name,
|
|
socklen_t namelen)
|
|
{
|
|
String * host;
|
|
size_t i;
|
|
VPNAppClient * p;
|
|
|
|
if((host = _libvpn_get_remote_host(name, namelen)) == NULL)
|
|
return NULL;
|
|
for(i = 0; i < _vpn_clients_cnt; i++)
|
|
if(_vpn_clients[i].name == NULL)
|
|
continue;
|
|
else if(string_compare(_vpn_clients[i].name, host) == 0)
|
|
{
|
|
string_delete(host);
|
|
return _vpn_clients[i].appclient;
|
|
}
|
|
if((p = realloc(_vpn_clients, sizeof(*_vpn_clients) * (i + 1))) == NULL)
|
|
{
|
|
string_delete(host);
|
|
return NULL;
|
|
}
|
|
_vpn_clients = p;
|
|
p = &_vpn_clients[_vpn_clients_cnt++];
|
|
if((p->appclient = appclient_new(NULL, APPINTERFACE, host)) == NULL)
|
|
{
|
|
string_delete(host);
|
|
return NULL;
|
|
}
|
|
p->name = host;
|
|
return p->appclient;
|
|
}
|
|
|
|
|
|
/* libvpn_get_appclient_fd */
|
|
static AppClient * _libvpn_get_appclient_fd(int32_t * fd)
|
|
{
|
|
size_t i;
|
|
|
|
if(*fd < _vpn_offset)
|
|
return NULL;
|
|
i = (size_t)*fd - _vpn_offset;
|
|
if(i >= _vpn_clients_fd_cnt)
|
|
return NULL;
|
|
*fd = _vpn_clients_fd[i].fd;
|
|
return _vpn_clients_fd[i].appclient;
|
|
}
|
|
|
|
|
|
/* libvpn_get_remote_host */
|
|
static String * _libvpn_get_remote_host(
|
|
const struct sockaddr * name, socklen_t namelen)
|
|
{
|
|
(void) name;
|
|
(void) namelen;
|
|
|
|
/* FIXME really implement through gethostbyname()/getaddrinfo() */
|
|
return getenv("APPSERVER_VPN");
|
|
}
|
|
|
|
|
|
/* libvpn_get_remote_name */
|
|
static int _libvpn_get_remote_name(
|
|
const struct sockaddr * name, socklen_t namelen,
|
|
struct sockaddr ** rname, socklen_t * rnamelen)
|
|
{
|
|
/* FIXME really implement through gethostbyname()/getaddrinfo() */
|
|
if(rname == NULL && rnamelen == NULL)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* libvpn_is_remote */
|
|
static unsigned int _libvpn_is_remote(const struct sockaddr * name,
|
|
socklen_t namelen)
|
|
{
|
|
return (_libvpn_get_remote_name(name, namelen, NULL, NULL) == 0)
|
|
? 1 : 0;
|
|
}
|
|
|
|
|
|
/* useful */
|
|
/* libvpn_deregister_fd */
|
|
static int _libvpn_deregister_fd(AppClient * appclient, int32_t fd)
|
|
{
|
|
if(fd < 0 || (size_t)fd >= _vpn_clients_fd_cnt)
|
|
return -1;
|
|
/* sanity check */
|
|
if(_vpn_clients_fd[fd].appclient != appclient)
|
|
return -1;
|
|
_vpn_clients_fd[fd].appclient = NULL;
|
|
_vpn_clients_fd[fd].fd = -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* libvpn_register_fd */
|
|
static int _libvpn_register_fd(AppClient * appclient, int32_t * fd)
|
|
{
|
|
size_t i;
|
|
VPNAppClientFD * p;
|
|
|
|
for(i = 0; i < _vpn_clients_fd_cnt; i++)
|
|
if(_vpn_clients_fd[i].fd < 0)
|
|
{
|
|
_vpn_clients_fd[i].appclient = appclient;
|
|
_vpn_clients_fd[i].fd = *fd;
|
|
*fd = _vpn_offset + i;
|
|
return 0;
|
|
}
|
|
if((p = realloc(_vpn_clients_fd, sizeof(*p) * (i + 1))) == NULL)
|
|
return -1;
|
|
_vpn_clients_fd = p;
|
|
p = &_vpn_clients_fd[_vpn_clients_fd_cnt++];
|
|
p->appclient = appclient;
|
|
p->fd = *fd;
|
|
*fd = _vpn_offset + i;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* public */
|
|
/* interface */
|
|
/* close */
|
|
int close(int fd)
|
|
{
|
|
int ret;
|
|
AppClient * appclient;
|
|
|
|
_libvpn_init();
|
|
if((appclient = _libvpn_get_appclient_fd(&fd)) == NULL)
|
|
return old_close(fd);
|
|
if(appclient_call(appclient, (void **)&ret, "close", fd) != 0)
|
|
return -1;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p:%d) => %d\n", __func__,
|
|
(void *)appclient, fd, ret);
|
|
#endif
|
|
if(ret != 0)
|
|
return _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
else
|
|
_libvpn_deregister_fd(appclient, fd);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* connect */
|
|
int connect(int fd, const struct sockaddr * name, socklen_t namelen)
|
|
{
|
|
int ret;
|
|
AppClient * appclient;
|
|
|
|
_libvpn_init();
|
|
if(_libvpn_is_remote(name, namelen) == 0)
|
|
return old_connect(fd, name, namelen);
|
|
if((appclient = _libvpn_get_appclient(name, namelen)) == NULL)
|
|
return -1;
|
|
if(appclient_call(appclient, (void **)&ret, "connect", fd) != 0)
|
|
return -1;
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: connect(%d) => %d\n", fd, ret);
|
|
#endif
|
|
if(ret < 0)
|
|
return _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
if(_libvpn_register_fd(appclient, &ret) != 0)
|
|
{
|
|
appclient_call(appclient, NULL, "close", ret);
|
|
return -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* read */
|
|
ssize_t read(int fd, void * buf, size_t count)
|
|
{
|
|
int32_t ret;
|
|
AppClient * appclient;
|
|
Buffer * b;
|
|
|
|
_libvpn_init();
|
|
if((appclient = _libvpn_get_appclient_fd(&fd)) == NULL)
|
|
return old_read(fd, buf, count);
|
|
if((b = buffer_new(0, NULL)) == NULL)
|
|
return -1;
|
|
if(appclient_call(appclient, (void **)&ret, "recv", fd, b, count,
|
|
0) != 0)
|
|
{
|
|
buffer_delete(b);
|
|
/* FIXME define errno */
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p:%d, buf, %lu) => %d\n", __func__,
|
|
(void *)appclient, fd, count, ret);
|
|
#endif
|
|
if(ret < 0)
|
|
ret = _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
else if(ret > 0)
|
|
memcpy(buf, buffer_get_data(b), ret);
|
|
buffer_delete(b);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* recv */
|
|
ssize_t recv(int fd, void * buf, size_t count, int flags)
|
|
{
|
|
int32_t ret;
|
|
AppClient * appclient;
|
|
Buffer * b;
|
|
|
|
_libvpn_init();
|
|
if((appclient = _libvpn_get_appclient_fd(&fd)) == NULL)
|
|
return old_read(fd, buf, count);
|
|
if((b = buffer_new(0, NULL)) == NULL)
|
|
return -1;
|
|
if(appclient_call(appclient, (void **)&ret, "recv", fd, b, count,
|
|
flags) != 0)
|
|
{
|
|
buffer_delete(b);
|
|
/* FIXME define errno */
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%p:%d, buf, %lu, %#x) => %d\n", __func__,
|
|
(void *)appclient, fd, count, flags, ret);
|
|
#endif
|
|
if(ret < 0)
|
|
ret = _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
else if(ret > 0)
|
|
memcpy(buf, buffer_get_data(b), ret);
|
|
buffer_delete(b);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* send */
|
|
ssize_t send(int fd, void const * buf, size_t count, int flags)
|
|
{
|
|
int32_t ret;
|
|
AppClient * appclient;
|
|
Buffer * b;
|
|
|
|
_libvpn_init();
|
|
if((appclient = _libvpn_get_appclient_fd(&fd)) == NULL)
|
|
return old_write(fd, buf, count);
|
|
if((b = buffer_new(count, buf)) == NULL)
|
|
return -1;
|
|
if(appclient_call(appclient, (void **)&ret, "send", fd, b, count,
|
|
flags) != 0)
|
|
{
|
|
buffer_delete(b);
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%d, buf, %lu, %#x) => %d\n", __func__,
|
|
fd, count, flags, ret);
|
|
#endif
|
|
buffer_delete(b);
|
|
if(ret < 0)
|
|
return _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* write */
|
|
ssize_t write(int fd, void const * buf, size_t count)
|
|
{
|
|
int32_t ret;
|
|
AppClient * appclient;
|
|
Buffer * b;
|
|
|
|
_libvpn_init();
|
|
if((appclient = _libvpn_get_appclient_fd(&fd)) == NULL)
|
|
return old_write(fd, buf, count);
|
|
if((b = buffer_new(count, buf)) == NULL)
|
|
return -1;
|
|
if(appclient_call(appclient, (void **)&ret, "send", fd, b, count,
|
|
0) != 0)
|
|
{
|
|
buffer_delete(b);
|
|
return -1;
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(%d, buf, %lu) => %d\n", __func__,
|
|
fd, count, ret);
|
|
#endif
|
|
buffer_delete(b);
|
|
if(ret < 0)
|
|
return _vpn_errno(_vpn_error, _vpn_error_cnt, -ret, 1);
|
|
return ret;
|
|
}
|