/* $Id$ */ /* Copyright (c) 2010-2014 Pierre Pronchery */ /* 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 . */ #include #include #include #include #include #include #include #include #include #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; }