VPN/src/vpn.c

313 lines
6.7 KiB
C

/* $Id$ */
/* Copyright (c) 2010-2014 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/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <System.h>
#include "VPN.h"
#include "vpn.h"
#include "../config.h"
#ifndef PROGNAME
# define PROGNAME PACKAGE
#endif
/* VPN */
/* private */
/* types */
typedef struct _VPNClient
{
AppServerClient * id;
int32_t * sockets;
size_t sockets_cnt;
} VPNClient;
typedef enum _VPNProtocol
{
VP_IP_TCP = VPN_PROTOCOL_IP_TCP
} VPNProtocol;
/* variables */
static VPNClient * _clients;
static size_t _clients_cnt;
/* prototypes */
/* client management */
static void _client_init(void);
static void _client_destroy(void);
/* accessors */
static VPNClient * _client_get(AppServerClient * asc);
static VPNClient * _client_check(AppServerClient * asc, int32_t fd);
/* useful */
static VPNClient * _client_add(AppServerClient * asc);
static VPNClient * _client_add_socket(AppServerClient * asc, int32_t fd);
static VPNClient * _client_remove_socket(VPNClient * client, int32_t fd);
/* public */
/* functions */
/* vpn */
int vpn(AppServerOptions options)
{
AppServer * appserver;
if((appserver = appserver_new(NULL, options, "VPN", NULL)) == NULL)
{
error_print(PROGNAME);
return 1;
}
_client_init();
appserver_loop(appserver);
_client_destroy();
appserver_delete(appserver);
return 0;
}
/* interface */
/* VPN_close */
int32_t VPN_close(App * app, AppServerClient * asc, int32_t fd)
{
VPNClient * client;
int32_t ret;
if((client = _client_check(asc, fd)) == NULL)
return -1;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d)\n", __func__, fd);
#endif
if((ret = close(fd)) == 0)
/* XXX ignore errors */
_client_remove_socket(client, fd);
return ret;
}
/* VPN_connect */
int32_t VPN_connect(App * app, AppServerClient * asc, uint32_t protocol,
String const * uri)
{
int32_t ret;
VPNProtocol vprotocol = protocol;
int sdomain;
int stype;
int sprotocol = 0;
struct sockaddr * sockaddr;
socklen_t ssize;
struct sockaddr_in sa_in;
switch(vprotocol)
{
case VP_IP_TCP:
sdomain = AF_INET;
stype = SOCK_STREAM;
sa_in.sin_family = AF_INET;
/* FIXME parse uri to initialize sa_in */
sa_in.sin_addr.s_addr = htonl(0x7f000001);
sa_in.sin_port = htons(80);
sockaddr = (struct sockaddr *)&sa_in;
ssize = sizeof(sa_in);
break;
default:
return -1;
}
if((ret = socket(sdomain, stype, sprotocol)) == -1)
return -1;
if(connect(ret, sockaddr, ssize) != 0
|| _client_add_socket(asc, ret) != 0)
{
close(ret); /* XXX necessary when connect() failed? */
return -1;
}
return ret;
}
/* VPN_recv */
int32_t VPN_recv(App * app, AppServerClient * asc, int32_t fd, Buffer * buffer,
uint32_t size, uint32_t flags)
{
int32_t ret;
if(_client_check(asc, fd) == NULL)
return -1;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, buf, %u, %u)\n", __func__, fd, size,
flags);
#endif
if(buffer_set_size(buffer, size) != 0)
return -1;
/* FIXME implement flags */
ret = recv(fd, buffer_get_data(buffer), size, 0);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, buf, %u, %u) => %d\n", __func__, fd,
size, flags, ret);
#endif
if(buffer_set_size(buffer, (ret < 0) ? 0 : ret) != 0)
{
memset(buffer_get_data(buffer), 0, size);
return -1;
}
return ret;
}
/* VPN_send */
int32_t VPN_send(App * app, AppServerClient * asc, int32_t fd,
Buffer const * buffer, uint32_t size, uint32_t flags)
{
if(_client_check(asc, fd) == NULL)
return -1;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d, buf, %u, %u)\n", __func__, fd, size,
flags);
#endif
if(buffer_get_size(buffer) < size)
return -error_set_code(1, "%s", strerror(EINVAL));
/* FIXME implement flags */
return send(fd, buffer_get_data(buffer), size, 0);
}
/* private */
/* functions */
/* client management */
/* clients_init */
static void _client_init(void)
{
_clients = NULL;
_clients_cnt = 0;
}
/* clients_destroy */
static void _client_destroy(void)
{
free(_clients);
_clients = NULL;
_clients_cnt = 0;
}
/* accessors */
/* client_get */
static VPNClient * _client_get(AppServerClient * client)
{
size_t i;
for(i = 0; i < _clients_cnt; i++)
if(_clients[i].id == client)
return &_clients[i];
return NULL;
}
/* client_check */
static VPNClient * _client_check(AppServerClient * asc, int32_t fd)
{
VPNClient * client;
size_t i;
if((client = _client_get(asc)) == NULL)
return NULL;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d)\n", __func__, fd);
#endif
for(i = 0; i < client->sockets_cnt; i++)
if(client->sockets[i] == fd)
return client;
return NULL;
}
/* useful */
/* client_add */
static VPNClient * _client_add(AppServerClient * asc)
{
VPNClient * p;
if((p = _client_get(asc)) != NULL)
return p;
if((p = realloc(_clients, sizeof(*p) * (_clients_cnt + 1))) == NULL)
{
error_set_print(PROGNAME, 1, "%s", strerror(errno));
return NULL;
}
_clients = p;
p = &_clients[_clients_cnt++];
p->id = asc;
p->sockets = NULL;
p->sockets_cnt = 0;
return p;
}
/* client_add_socket */
static VPNClient * _client_add_socket(AppServerClient * asc, int32_t fd)
{
VPNClient * client;
int32_t * p;
if((client = _client_add(asc)) == NULL)
return NULL;
if((p = realloc(client->sockets, sizeof(*p)
* (client->sockets_cnt + 1))) == NULL)
{
error_set_print(PROGNAME, 1, "%s", strerror(errno));
return NULL;
}
client->sockets = p;
client->sockets[client->sockets_cnt++] = fd;
return client;
}
/* client_remove_socket */
static VPNClient * _client_remove_socket(VPNClient * client, int32_t fd)
{
size_t i;
int32_t * p;
if(fd < 0) /* XXX should never happen */
{
error_set_print(PROGNAME, 1, "%s", strerror(EINVAL));
return NULL;
}
for(i = 0; i < client->sockets_cnt; i++)
if(client->sockets[i] == fd)
break;
if(i == client->sockets_cnt)
return client;
p = &client->sockets[i];
memmove(p, p + 1, (--client->sockets_cnt - i) * sizeof(*p));
if((p = realloc(client->sockets, sizeof(*p) * client->sockets_cnt))
!= NULL || client->sockets_cnt == 0)
client->sockets = p; /* we can ignore errors */
return client;
}