mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-03-29 16:27:53 +08:00
updated server
This commit is contained in:
11
Makefile
11
Makefile
@@ -1,7 +1,10 @@
|
||||
CC=gcc
|
||||
CFLAGS=-O2
|
||||
CFLAGS=-O2 -I. -Wall -Ihttp-parser/
|
||||
|
||||
all: server
|
||||
all: ocserv
|
||||
|
||||
server: server.c
|
||||
$(CC) $(CFLAGS) -o $@ $^ -lgnutls
|
||||
FILES=main.c vpn.c auth.c tlslib.c cookies.c \
|
||||
http-parser/http_parser.c
|
||||
|
||||
ocserv: $(FILES)
|
||||
$(CC) $(CFLAGS) -o $@ $^ -L/usr/local/lib -lgnutls -lgdbm
|
||||
|
||||
423
auth.c
Normal file
423
auth.c
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (C) 2012, 2013 David Woodhouse
|
||||
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <vpn.h>
|
||||
#include <auth.h>
|
||||
#include <cookies.h>
|
||||
#include <tlslib.h>
|
||||
|
||||
#include <http-parser/http_parser.h>
|
||||
|
||||
#define SUCCESS_MSG "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" \
|
||||
"<auth id=\"success\">\r\n" \
|
||||
"<banner>Success</banner>\r\n" \
|
||||
"</auth>\r\n"
|
||||
|
||||
int get_auth_handler(server_st *server)
|
||||
{
|
||||
char file[PATH_MAX];
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
snprintf(file, sizeof(file), "%s/%s", server->config->root_dir, "index.xml");
|
||||
|
||||
ret = stat(file, &st);
|
||||
if (ret == 0) {
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
/*tls_print(server->session, "Connection: close\r\n");*/
|
||||
tls_printf(server->session, "Content-Length: %u\r\n", (unsigned int)st.st_size);
|
||||
tls_print(server->session, "Content-Type: text/html\r\n");
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_print(server->session, "\r\n");
|
||||
|
||||
ret = tls_send_file(server->session, file);
|
||||
|
||||
fprintf(stderr, "file: %d, sent: %d\n", (int)st.st_size, ret);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
tls_print(server->session, "Connection: close\r\n");
|
||||
tls_print(server->session, "Content-Type: text/xml\r\n");
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_print(server->session, "\r\n");
|
||||
tls_print(server->session,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
|
||||
tls_print(server->session, "<auth id=\"main\">\r\n");
|
||||
tls_print(server->session,
|
||||
"<message>Please enter your username and password.</message>\r\n");
|
||||
tls_print(server->session,
|
||||
"<form method=\"post\" action=\"/auth.xml\">\r\n");
|
||||
tls_print(server->session,
|
||||
"<input type=\"text\" name=\"username\" label=\"Username:\" />\r\n");
|
||||
tls_print(server->session,
|
||||
"<input type=\"password\" name=\"password\" label=\"Password:\" />\r\n");
|
||||
tls_print(server->session, "</form></auth>\r\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int post_auth_handler(server_st *server)
|
||||
{
|
||||
int ret;
|
||||
struct req_data_st *req = server->parser->data;
|
||||
const char* reason = "Authentication failed";
|
||||
unsigned char cookie[COOKIE_SIZE];
|
||||
char str_cookie[2*COOKIE_SIZE+1];
|
||||
char * username = NULL;
|
||||
char * password = NULL;
|
||||
char *p;
|
||||
unsigned int i;
|
||||
struct stored_cookie_st sc;
|
||||
|
||||
if (server->config->auth_types & AUTH_TYPE_USERNAME_PASS) {
|
||||
/* body should be "username=test&password=test" */
|
||||
username = strstr(req->body, "username=");
|
||||
if (username == NULL) {
|
||||
reason = "No username";
|
||||
goto auth_fail;
|
||||
}
|
||||
username += sizeof("username=")-1;
|
||||
|
||||
password = strstr(req->body, "password=");
|
||||
if (password == NULL) {
|
||||
reason = "No password";
|
||||
goto auth_fail;
|
||||
}
|
||||
password += sizeof("password=")-1;
|
||||
|
||||
/* modify body */
|
||||
p = username;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
p = password;
|
||||
while(*p != 0) {
|
||||
if (*p == '&') {
|
||||
*p = 0;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
/* now verify username and passwords */
|
||||
if (strcmp(username, "test") != 0 || strcmp(password, "test") != 0)
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
if (server->config->auth_types & AUTH_TYPE_CERTIFICATE) {
|
||||
const gnutls_datum_t * cert;
|
||||
unsigned int ncerts;
|
||||
|
||||
/* this is superflous. Verification has already been performed
|
||||
* during handshake. */
|
||||
cert = gnutls_certificate_get_peers (server->session, &ncerts);
|
||||
|
||||
if (cert == NULL) {
|
||||
reason = "No certificate found";
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
/* XXX if there is no username get it from the certificate */
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "User '%s' logged in\n", username);
|
||||
|
||||
/* generate cookie */
|
||||
ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie, sizeof(cookie));
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
p = str_cookie;
|
||||
for (i=0;i<sizeof(cookie);i++) {
|
||||
sprintf(p, "%.2x", (unsigned int)cookie[i]);
|
||||
p+=2;
|
||||
}
|
||||
|
||||
memset(&sc, 0, sizeof(sc));
|
||||
sc.expiration = time(0) + server->config->cookie_validity;
|
||||
if (username)
|
||||
snprintf(sc.username, sizeof(sc.username), "%s", username);
|
||||
|
||||
/* store cookie */
|
||||
ret = store_cookie(server, cookie, sizeof(cookie), &sc);
|
||||
if (ret < 0) {
|
||||
reason = "Storage issue";
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
/* reply */
|
||||
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
tls_print(server->session, "Content-Type: text/xml\r\n");
|
||||
tls_printf(server->session, "Content-Length: %u\r\n", (unsigned)(sizeof(SUCCESS_MSG)-1));
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_printf(server->session, "Set-Cookie: webvpn=%s\r\n", str_cookie);
|
||||
tls_print(server->session, "\r\n"SUCCESS_MSG);
|
||||
|
||||
return 0;
|
||||
|
||||
auth_fail:
|
||||
tls_print(server->session, "HTTP/1.1 503 Service Unavailable\r\n");
|
||||
tls_printf(server->session,
|
||||
"X-Reason: %s\r\n\r\n", reason);
|
||||
tls_fatal_close(server->session, GNUTLS_A_ACCESS_DENIED);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int get_login_handler(server_st *server)
|
||||
{
|
||||
char file[PATH_MAX];
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
snprintf(file, sizeof(file), "%s/%s", server->config->root_dir, "login.xml");
|
||||
|
||||
ret = stat(file, &st);
|
||||
if (ret == 0) {
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
tls_printf(server->session, "Content-Length: %u\r\n", (unsigned int)st.st_size);
|
||||
tls_print(server->session, "Content-Type: text/html\r\n");
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_print(server->session, "\r\n");
|
||||
|
||||
tls_send_file(server->session, file);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
tls_print(server->session, "Connection: close\r\n");
|
||||
tls_print(server->session, "Content-Type: text/xml\r\n");
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_print(server->session, "\r\n");
|
||||
tls_print(server->session,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
|
||||
tls_print(server->session, "<auth id=\"main\">\r\n");
|
||||
tls_print(server->session,
|
||||
"<message>Please enter your login cookie.</message>\r\n");
|
||||
tls_print(server->session,
|
||||
"<form method=\"post\" action=\"/login.xml\">\r\n");
|
||||
tls_print(server->session,
|
||||
"<input type=\"text\" name=\"cookie\" label=\"Cookie:\" />\r\n");
|
||||
tls_print(server->session, "</form></auth>\r\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checks cookie if present and retrieves it. Returns negative error code
|
||||
* if cannot be found */
|
||||
static int check_cookie(server_st *server, struct stored_cookie_st *sc)
|
||||
{
|
||||
struct req_data_st *req = server->parser->data;
|
||||
int ret;
|
||||
|
||||
if (req->cookie_set == 0) {
|
||||
syslog(LOG_INFO, "No cookie found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = retrieve_cookie(server, req->cookie, sizeof(req->cookie), sc);
|
||||
if (ret < 0) {
|
||||
syslog(LOG_INFO, "Cookie not recognised\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int post_login_handler(server_st *server)
|
||||
{
|
||||
int ret;
|
||||
struct req_data_st *req = server->parser->data;
|
||||
char str_cookie[2*COOKIE_SIZE+1];
|
||||
char *p;
|
||||
unsigned int i;
|
||||
struct stored_cookie_st sc;
|
||||
|
||||
ret = check_cookie(server, &sc);
|
||||
if (ret < 0) {
|
||||
goto auth_fail;
|
||||
}
|
||||
|
||||
p = str_cookie;
|
||||
for (i=0;i<sizeof(req->cookie);i++) {
|
||||
sprintf(p, "%.2x", (unsigned int)req->cookie[i]);
|
||||
p+=2;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "User '%s' logged in via cookie\n", sc.username);
|
||||
|
||||
tls_print(server->session, "HTTP/1.1 200 OK\r\n");
|
||||
tls_print(server->session, "Content-Type: text/xml\r\n");
|
||||
tls_print(server->session, "X-Transcend-Version: 1\r\n");
|
||||
tls_printf(server->session, "Content-Length: %u\r\n", (unsigned)(sizeof(SUCCESS_MSG)-1));
|
||||
tls_printf(server->session, "Set-Cookie: webvpn=%s\r\n",
|
||||
str_cookie);
|
||||
tls_print(server->session, "\r\n"SUCCESS_MSG);
|
||||
|
||||
return 0;
|
||||
|
||||
auth_fail:
|
||||
return get_login_handler(server);
|
||||
}
|
||||
|
||||
|
||||
int connect_handler(server_st *server)
|
||||
{
|
||||
int ret;
|
||||
struct req_data_st *req = server->parser->data;
|
||||
char buf[256];
|
||||
fd_set rfds;
|
||||
int l, pktlen;
|
||||
int tls_fd, max;
|
||||
struct stored_cookie_st sc;
|
||||
unsigned int tun_nr = 0;
|
||||
|
||||
ret = check_cookie(server, &sc);
|
||||
if (ret < 0) {
|
||||
syslog(LOG_INFO, "Connect request without authentication");
|
||||
tls_print(server->session, "HTTP/1.1 503 Service Unavailable\r\n\r\n");
|
||||
tls_fatal_close(server->session, GNUTLS_A_ACCESS_DENIED);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strcmp(req->url, "/CSCOSSLC/tunnel") != 0) {
|
||||
syslog(LOG_INFO, "Bad connect request: '%s'\n", req->url);
|
||||
tls_print(server->session, "HTTP/1.1 404 Nah, go away\r\n\r\n");
|
||||
tls_fatal_close(server->session, GNUTLS_A_ACCESS_DENIED);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "Connected\n");
|
||||
|
||||
tls_print(server->session, "HTTP/1.1 200 CONNECTED\r\n");
|
||||
tls_print(server->session, "X-CSTP-MTU: 1500\r\n");
|
||||
tls_print(server->session, "X-CSTP-DPD: 60\r\n");
|
||||
tls_printf(server->session, "X-CSTP-Address: 172.31.255.%d\r\n",
|
||||
100 + tun_nr);
|
||||
tls_print(server->session, "X-CSTP-Netmask: 255.255.255.255\r\n");
|
||||
tls_print(server->session, "X-CSTP-DNS: 172.31.255.1\r\n");
|
||||
tls_printf(server->session, "X-CSTP-Address: 2001:770:15f::%x\r\n",
|
||||
0x100 + tun_nr);
|
||||
tls_printf(server->session, "X-CSTP-Netmask: 2001:770:15f::%x/128\r\n",
|
||||
0x100 + tun_nr);
|
||||
tls_print(server->session,
|
||||
"X-CSTP-Split-Include: 172.31.255.0/255.255.255.0\r\n");
|
||||
tls_print(server->session, "X-CSTP-Banner: Hello there\r\n");
|
||||
tls_print(server->session, "\r\n");
|
||||
|
||||
tls_fd = (long)gnutls_transport_get_ptr(server->session);
|
||||
|
||||
for(;;) {
|
||||
FD_ZERO(&rfds);
|
||||
|
||||
FD_SET(tls_fd, &rfds);
|
||||
FD_SET(server->tunfd, &rfds);
|
||||
max = MAX(server->tunfd,tls_fd);
|
||||
|
||||
if (gnutls_record_check_pending(server->session) == 0) {
|
||||
ret = select(max + 1, &rfds, NULL, NULL, NULL);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(server->tunfd, &rfds)) {
|
||||
int l = read(server->tunfd, buf + 8, sizeof(buf) - 8);
|
||||
buf[0] = 'S';
|
||||
buf[1] = 'T';
|
||||
buf[2] = 'F';
|
||||
buf[3] = 1;
|
||||
buf[4] = l >> 8;
|
||||
buf[5] = l & 0xff;
|
||||
buf[6] = 0;
|
||||
buf[7] = 0;
|
||||
|
||||
ret = tls_send(server->session, buf, l + 8);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
}
|
||||
|
||||
if (FD_ISSET(tls_fd, &rfds) || gnutls_record_check_pending(server->session)) {
|
||||
l = tls_recv(server->session, buf, sizeof(buf));
|
||||
GNUTLS_FATAL_ERR(l);
|
||||
|
||||
if (l < 8) {
|
||||
syslog(LOG_INFO,
|
||||
"Can't read CSTP header\n");
|
||||
exit(1);
|
||||
}
|
||||
if (buf[0] != 'S' || buf[1] != 'T' ||
|
||||
buf[2] != 'F' || buf[3] != 1 || buf[7]) {
|
||||
syslog(LOG_INFO,
|
||||
"Can't recognise CSTP header\n");
|
||||
exit(1);
|
||||
}
|
||||
pktlen = (buf[4] << 8) + buf[5];
|
||||
if (l != 8 + pktlen) {
|
||||
syslog(LOG_INFO, "Unexpected length\n");
|
||||
exit(1);
|
||||
}
|
||||
switch (buf[6]) {
|
||||
case AC_PKT_DPD_RESP:
|
||||
case AC_PKT_KEEPALIVE:
|
||||
break;
|
||||
|
||||
case AC_PKT_DPD_OUT:
|
||||
ret =
|
||||
tls_send(server->session, "STF\x1\x0\x0\x4\x0",
|
||||
8);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
break;
|
||||
|
||||
case AC_PKT_DISCONN:
|
||||
syslog(LOG_INFO, "Received BYE packet\n");
|
||||
break;
|
||||
|
||||
case AC_PKT_DATA:
|
||||
write(server->tunfd, buf + 8, pktlen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
auth.h
Normal file
10
auth.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
int get_auth_handler(server_st *server);
|
||||
int post_auth_handler(server_st *server);
|
||||
int get_login_handler(server_st *server);
|
||||
int post_login_handler(server_st *server);
|
||||
int connect_handler(server_st *server);
|
||||
|
||||
#endif
|
||||
23
common.h
Normal file
23
common.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define AC_PKT_DATA 0 /* Uncompressed data */
|
||||
#define AC_PKT_DPD_OUT 3 /* Dead Peer Detection */
|
||||
#define AC_PKT_DPD_RESP 4 /* DPD response */
|
||||
#define AC_PKT_DISCONN 5 /* Client disconnection notice */
|
||||
#define AC_PKT_KEEPALIVE 7 /* Keepalive */
|
||||
#define AC_PKT_COMPRESSED 8 /* Compressed data */
|
||||
#define AC_PKT_TERM_SERVER 9 /* Server kick */
|
||||
|
||||
extern int syslog_open;
|
||||
|
||||
#define MAX(x,y) ((x)>(y)?(x):(y))
|
||||
|
||||
const char *human_addr(const struct sockaddr *sa, socklen_t salen,
|
||||
void *buf, size_t buflen);
|
||||
|
||||
#endif
|
||||
140
cookies.c
Normal file
140
cookies.c
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <gdbm.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <common.h>
|
||||
#include <cookies.h>
|
||||
|
||||
/* All the functions return zero on success and a negative value on error */
|
||||
|
||||
int store_cookie(server_st *server, const void* cookie, unsigned cookie_size,
|
||||
const struct stored_cookie_st* sc)
|
||||
{
|
||||
GDBM_FILE dbf;
|
||||
datum key;
|
||||
datum data;
|
||||
int ret;
|
||||
|
||||
dbf = gdbm_open((char*)server->config->db_file, 0, GDBM_WRCREAT, S_IRUSR|S_IWUSR, NULL);
|
||||
if (dbf == NULL)
|
||||
return -1;
|
||||
|
||||
key.dptr = (void*)cookie;
|
||||
key.dsize = cookie_size;
|
||||
data.dptr = (void*)sc;
|
||||
data.dsize = sizeof(*sc);
|
||||
|
||||
ret = gdbm_store( dbf, key, data, GDBM_INSERT);
|
||||
if (ret != 0) {
|
||||
ret = -1;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
finish:
|
||||
gdbm_close(dbf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int retrieve_cookie(server_st *server, const void* cookie, unsigned cookie_size,
|
||||
struct stored_cookie_st* sc)
|
||||
{
|
||||
GDBM_FILE dbf;
|
||||
datum key;
|
||||
datum data;
|
||||
int ret;
|
||||
|
||||
dbf = gdbm_open((char*)server->config->db_file, 0, GDBM_READER, 0, NULL);
|
||||
if (dbf == NULL)
|
||||
return -1;
|
||||
|
||||
key.dptr = (void*)cookie;
|
||||
key.dsize = cookie_size;
|
||||
|
||||
data = gdbm_fetch( dbf, key);
|
||||
if (data.dsize != sizeof(*sc)) {
|
||||
ret = -1;
|
||||
goto finish;
|
||||
}
|
||||
memcpy(sc, data.dptr, data.dsize);
|
||||
|
||||
if (sc->expiration >= time(0))
|
||||
ret = 0;
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
finish:
|
||||
gdbm_close(dbf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void expire_cookies(struct cfg_st *cfg)
|
||||
{
|
||||
GDBM_FILE dbf;
|
||||
datum key;
|
||||
datum data;
|
||||
int deleted = 0;
|
||||
struct stored_cookie_st sc;
|
||||
time_t now = time(0);
|
||||
|
||||
dbf = gdbm_open((char*)cfg->db_file, 0, GDBM_WRITER, 0, NULL);
|
||||
if (dbf == NULL)
|
||||
return;
|
||||
|
||||
key = gdbm_firstkey(dbf);
|
||||
if (key.dptr == NULL)
|
||||
goto finish;
|
||||
|
||||
while(key.dptr != NULL) {
|
||||
data = gdbm_fetch( dbf, key);
|
||||
if (data.dsize != sizeof(sc)) {
|
||||
gdbm_delete(dbf, key);
|
||||
deleted++;
|
||||
} else {
|
||||
memcpy(&sc, data.dptr, data.dsize);
|
||||
if (sc.expiration <= now) {
|
||||
gdbm_delete(dbf, key);
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
key = gdbm_nextkey(dbf, key);
|
||||
}
|
||||
|
||||
if (deleted > 0)
|
||||
gdbm_reorganize(dbf);
|
||||
|
||||
finish:
|
||||
gdbm_close(dbf);
|
||||
}
|
||||
18
cookies.h
Normal file
18
cookies.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef COOKIES_H
|
||||
#define COOKIES_H
|
||||
|
||||
#include <vpn.h>
|
||||
|
||||
struct __attribute__ ((__packed__)) stored_cookie_st {
|
||||
char username[128];
|
||||
time_t expiration;
|
||||
};
|
||||
|
||||
int store_cookie(server_st *server, const void* cookie, unsigned cookie_size,
|
||||
const struct stored_cookie_st* sc);
|
||||
void expire_cookies(struct cfg_st *cfg);
|
||||
|
||||
int retrieve_cookie(server_st *server, const void* cookie, unsigned cookie_size,
|
||||
struct stored_cookie_st* sc);
|
||||
|
||||
#endif
|
||||
2175
http-parser/http_parser.c
Normal file
2175
http-parser/http_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
304
http-parser/http_parser.h
Normal file
304
http-parser/http_parser.h
Normal file
@@ -0,0 +1,304 @@
|
||||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||
#define HTTP_PARSER_VERSION_MINOR 0
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
||||
#include <BaseTsd.h>
|
||||
#include <stddef.h>
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#endif
|
||||
|
||||
/* Maximium header size allowed */
|
||||
#define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
typedef struct http_parser_settings http_parser_settings;
|
||||
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parser will
|
||||
* then halt execution.
|
||||
*
|
||||
* The one exception is on_headers_complete. In a HTTP_RESPONSE parser
|
||||
* returning '1' from on_headers_complete will tell the parser that it
|
||||
* should not expect a body. This is used when receiving a response to a
|
||||
* HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
|
||||
* chunked' headers that indicate the presence of a body.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be call arbitrarally
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_url"
|
||||
* each providing just a few characters more data.
|
||||
*/
|
||||
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
||||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
/* pathological */ \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
/* webdav */ \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
/* subversion */ \
|
||||
XX(16, REPORT, REPORT) \
|
||||
XX(17, MKACTIVITY, MKACTIVITY) \
|
||||
XX(18, CHECKOUT, CHECKOUT) \
|
||||
XX(19, MERGE, MERGE) \
|
||||
/* upnp */ \
|
||||
XX(20, MSEARCH, M-SEARCH) \
|
||||
XX(21, NOTIFY, NOTIFY) \
|
||||
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
/* RFC-5789 */ \
|
||||
XX(24, PATCH, PATCH) \
|
||||
XX(25, PURGE, PURGE) \
|
||||
|
||||
enum http_method
|
||||
{
|
||||
#define XX(num, name, string) HTTP_##name = num,
|
||||
HTTP_METHOD_MAP(XX)
|
||||
#undef XX
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
|
||||
|
||||
|
||||
/* Flag values for http_parser.flags field */
|
||||
enum flags
|
||||
{ F_CHUNKED = 1 << 0
|
||||
, F_CONNECTION_KEEP_ALIVE = 1 << 1
|
||||
, F_CONNECTION_CLOSE = 1 << 2
|
||||
, F_TRAILING = 1 << 3
|
||||
, F_UPGRADE = 1 << 4
|
||||
, F_SKIPBODY = 1 << 5
|
||||
};
|
||||
|
||||
|
||||
/* Map for errno-related constants
|
||||
*
|
||||
* The provided argument should be a macro that takes 2 arguments.
|
||||
*/
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
/* No error */ \
|
||||
XX(OK, "success") \
|
||||
\
|
||||
/* Callback-related errors */ \
|
||||
XX(CB_message_begin, "the on_message_begin callback failed") \
|
||||
XX(CB_status_complete, "the on_status_complete callback failed") \
|
||||
XX(CB_url, "the on_url callback failed") \
|
||||
XX(CB_header_field, "the on_header_field callback failed") \
|
||||
XX(CB_header_value, "the on_header_value callback failed") \
|
||||
XX(CB_headers_complete, "the on_headers_complete callback failed") \
|
||||
XX(CB_body, "the on_body callback failed") \
|
||||
XX(CB_message_complete, "the on_message_complete callback failed") \
|
||||
\
|
||||
/* Parsing-related errors */ \
|
||||
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
|
||||
XX(HEADER_OVERFLOW, \
|
||||
"too many header bytes seen; overflow detected") \
|
||||
XX(CLOSED_CONNECTION, \
|
||||
"data received after completed connection: close message") \
|
||||
XX(INVALID_VERSION, "invalid HTTP version") \
|
||||
XX(INVALID_STATUS, "invalid HTTP status code") \
|
||||
XX(INVALID_METHOD, "invalid HTTP method") \
|
||||
XX(INVALID_URL, "invalid URL") \
|
||||
XX(INVALID_HOST, "invalid host") \
|
||||
XX(INVALID_PORT, "invalid port") \
|
||||
XX(INVALID_PATH, "invalid path") \
|
||||
XX(INVALID_QUERY_STRING, "invalid query string") \
|
||||
XX(INVALID_FRAGMENT, "invalid fragment") \
|
||||
XX(LF_EXPECTED, "LF character expected") \
|
||||
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
|
||||
XX(INVALID_CONTENT_LENGTH, \
|
||||
"invalid character in content-length header") \
|
||||
XX(INVALID_CHUNK_SIZE, \
|
||||
"invalid character in chunk size header") \
|
||||
XX(INVALID_CONSTANT, "invalid constant string") \
|
||||
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
|
||||
XX(STRICT, "strict mode assertion failed") \
|
||||
XX(PAUSED, "parser is paused") \
|
||||
XX(UNKNOWN, "an unknown error occurred")
|
||||
|
||||
|
||||
/* Define HPE_* values for each errno value above */
|
||||
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
|
||||
enum http_errno {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
};
|
||||
#undef HTTP_ERRNO_GEN
|
||||
|
||||
|
||||
/* Get an http_errno value from an http_parser */
|
||||
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
|
||||
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
unsigned char type : 2; /* enum http_parser_type */
|
||||
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
|
||||
unsigned char state; /* enum state from http_parser.c */
|
||||
unsigned char header_state; /* enum header_state from http_parser.c */
|
||||
unsigned char index; /* index into current matcher */
|
||||
|
||||
uint32_t nread; /* # bytes read in various scenarios */
|
||||
uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
unsigned short status_code; /* responses only */
|
||||
unsigned char method; /* requests only */
|
||||
unsigned char http_errno : 7;
|
||||
|
||||
/* 1 = Upgrade header was present and the parser has exited because of that.
|
||||
* 0 = No upgrade header present.
|
||||
* Should be checked when http_parser_execute() returns in addition to
|
||||
* error checking.
|
||||
*/
|
||||
unsigned char upgrade : 1;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
};
|
||||
|
||||
|
||||
struct http_parser_settings {
|
||||
http_cb on_message_begin;
|
||||
http_data_cb on_url;
|
||||
http_cb on_status_complete;
|
||||
http_data_cb on_header_field;
|
||||
http_data_cb on_header_value;
|
||||
http_cb on_headers_complete;
|
||||
http_data_cb on_body;
|
||||
http_cb on_message_complete;
|
||||
};
|
||||
|
||||
|
||||
enum http_parser_url_fields
|
||||
{ UF_SCHEMA = 0
|
||||
, UF_HOST = 1
|
||||
, UF_PORT = 2
|
||||
, UF_PATH = 3
|
||||
, UF_QUERY = 4
|
||||
, UF_FRAGMENT = 5
|
||||
, UF_USERINFO = 6
|
||||
, UF_MAX = 7
|
||||
};
|
||||
|
||||
|
||||
/* Result structure for http_parser_parse_url().
|
||||
*
|
||||
* Callers should index into field_data[] with UF_* values iff field_set
|
||||
* has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
|
||||
* because we probably have padding left over), we convert any port to
|
||||
* a uint16_t.
|
||||
*/
|
||||
struct http_parser_url {
|
||||
uint16_t field_set; /* Bitmask of (1 << UF_*) values */
|
||||
uint16_t port; /* Converted UF_PORT string */
|
||||
|
||||
struct {
|
||||
uint16_t off; /* Offset into buffer in which field starts */
|
||||
uint16_t len; /* Length of run in buffer */
|
||||
} field_data[UF_MAX];
|
||||
};
|
||||
|
||||
|
||||
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||
|
||||
|
||||
size_t http_parser_execute(http_parser *parser,
|
||||
const http_parser_settings *settings,
|
||||
const char *data,
|
||||
size_t len);
|
||||
|
||||
|
||||
/* If http_should_keep_alive() in the on_headers_complete or
|
||||
* on_message_complete callback returns 0, then this should be
|
||||
* the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header.
|
||||
* If you are the client, close the connection.
|
||||
*/
|
||||
int http_should_keep_alive(const http_parser *parser);
|
||||
|
||||
/* Returns a string version of the HTTP method. */
|
||||
const char *http_method_str(enum http_method m);
|
||||
|
||||
/* Return a string name of the given error */
|
||||
const char *http_errno_name(enum http_errno err);
|
||||
|
||||
/* Return a string description of the given error */
|
||||
const char *http_errno_description(enum http_errno err);
|
||||
|
||||
/* Parse a URL; return nonzero on failure */
|
||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||
int is_connect,
|
||||
struct http_parser_url *u);
|
||||
|
||||
/* Pause or un-pause the parser; a nonzero value pauses */
|
||||
void http_parser_pause(http_parser *parser, int paused);
|
||||
|
||||
/* Checks if this is the final chunk of the body. */
|
||||
int http_body_is_final(const http_parser *parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
578
list.h
Normal file
578
list.h
Normal file
@@ -0,0 +1,578 @@
|
||||
#ifndef _LINUX_LIST_H
|
||||
#define _LINUX_LIST_H
|
||||
|
||||
#undef offsetof
|
||||
#ifdef __compiler_offsetof
|
||||
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
|
||||
#else
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple doubly linked list implementation.
|
||||
*
|
||||
* Some of the internal functions ("__xxx") are useful when
|
||||
* manipulating whole lists rather than single entries, as
|
||||
* sometimes we already know the next/prev entries and we can
|
||||
* generate better code by using them directly rather than
|
||||
* using the generic single-entry routines.
|
||||
*/
|
||||
|
||||
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||
|
||||
#define LIST_HEAD(name) \
|
||||
struct list_head name = LIST_HEAD_INIT(name)
|
||||
|
||||
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new entry between two known consecutive entries.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
#ifndef CONFIG_DEBUG_LIST
|
||||
static inline void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
next->prev = new;
|
||||
new->next = next;
|
||||
new->prev = prev;
|
||||
prev->next = new;
|
||||
}
|
||||
#else
|
||||
extern void __list_add(struct list_head *new,
|
||||
struct list_head *prev,
|
||||
struct list_head *next);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* list_add - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it after
|
||||
*
|
||||
* Insert a new entry after the specified head.
|
||||
* This is good for implementing stacks.
|
||||
*/
|
||||
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head, head->next);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* list_add_tail - add a new entry
|
||||
* @new: new entry to be added
|
||||
* @head: list head to add it before
|
||||
*
|
||||
* Insert a new entry before the specified head.
|
||||
* This is useful for implementing queues.
|
||||
*/
|
||||
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||
{
|
||||
__list_add(new, head->prev, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a list entry by making the prev/next entries
|
||||
* point to each other.
|
||||
*
|
||||
* This is only for internal list manipulation where we know
|
||||
* the prev/next entries already!
|
||||
*/
|
||||
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||
{
|
||||
next->prev = prev;
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del - deletes entry from list.
|
||||
* @entry: the element to delete from the list.
|
||||
* Note: list_empty() on entry does not return true after this, the entry is
|
||||
* in an undefined state.
|
||||
*/
|
||||
#ifndef CONFIG_DEBUG_LIST
|
||||
static inline void __list_del_entry(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
}
|
||||
|
||||
static inline void list_del(struct list_head *entry)
|
||||
{
|
||||
__list_del(entry->prev, entry->next);
|
||||
entry->next = NULL;
|
||||
entry->prev = NULL;
|
||||
}
|
||||
#else
|
||||
extern void __list_del_entry(struct list_head *entry);
|
||||
extern void list_del(struct list_head *entry);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* list_replace - replace old entry by new one
|
||||
* @old : the element to be replaced
|
||||
* @new : the new element to insert
|
||||
*
|
||||
* If @old was empty, it will be overwritten.
|
||||
*/
|
||||
static inline void list_replace(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
new->next = old->next;
|
||||
new->next->prev = new;
|
||||
new->prev = old->prev;
|
||||
new->prev->next = new;
|
||||
}
|
||||
|
||||
static inline void list_replace_init(struct list_head *old,
|
||||
struct list_head *new)
|
||||
{
|
||||
list_replace(old, new);
|
||||
INIT_LIST_HEAD(old);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_del_init - deletes entry from list and reinitialize it.
|
||||
* @entry: the element to delete from the list.
|
||||
*/
|
||||
static inline void list_del_init(struct list_head *entry)
|
||||
{
|
||||
__list_del_entry(entry);
|
||||
INIT_LIST_HEAD(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move - delete from one list and add as another's head
|
||||
* @list: the entry to move
|
||||
* @head: the head that will precede our entry
|
||||
*/
|
||||
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
__list_del_entry(list);
|
||||
list_add(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_move_tail - delete from one list and add as another's tail
|
||||
* @list: the entry to move
|
||||
* @head: the head that will follow our entry
|
||||
*/
|
||||
static inline void list_move_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
__list_del_entry(list);
|
||||
list_add_tail(list, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_last - tests whether @list is the last entry in list @head
|
||||
* @list: the entry to test
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline int list_is_last(const struct list_head *list,
|
||||
const struct list_head *head)
|
||||
{
|
||||
return list->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty - tests whether a list is empty
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_empty(const struct list_head *head)
|
||||
{
|
||||
return head->next == head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_empty_careful - tests whether a list is empty and not being modified
|
||||
* @head: the list to test
|
||||
*
|
||||
* Description:
|
||||
* tests whether a list is empty _and_ checks that no other CPU might be
|
||||
* in the process of modifying either member (next or prev)
|
||||
*
|
||||
* NOTE: using list_empty_careful() without synchronization
|
||||
* can only be safe if the only activity that can happen
|
||||
* to the list entry is list_del_init(). Eg. it cannot be used
|
||||
* if another CPU could re-list_add() it.
|
||||
*/
|
||||
static inline int list_empty_careful(const struct list_head *head)
|
||||
{
|
||||
struct list_head *next = head->next;
|
||||
return (next == head) && (next == head->prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_rotate_left - rotate the list to the left
|
||||
* @head: the head of the list
|
||||
*/
|
||||
static inline void list_rotate_left(struct list_head *head)
|
||||
{
|
||||
struct list_head *first;
|
||||
|
||||
if (!list_empty(head)) {
|
||||
first = head->next;
|
||||
list_move_tail(first, head);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_is_singular - tests whether a list has just one entry.
|
||||
* @head: the list to test.
|
||||
*/
|
||||
static inline int list_is_singular(const struct list_head *head)
|
||||
{
|
||||
return !list_empty(head) && (head->next == head->prev);
|
||||
}
|
||||
|
||||
static inline void __list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
struct list_head *new_first = entry->next;
|
||||
list->next = head->next;
|
||||
list->next->prev = list;
|
||||
list->prev = entry;
|
||||
entry->next = list;
|
||||
head->next = new_first;
|
||||
new_first->prev = head;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_cut_position - cut a list into two
|
||||
* @list: a new list to add all removed entries
|
||||
* @head: a list with entries
|
||||
* @entry: an entry within head, could be the head itself
|
||||
* and if so we won't cut the list
|
||||
*
|
||||
* This helper moves the initial part of @head, up to and
|
||||
* including @entry, from @head to @list. You should
|
||||
* pass on @entry an element you know is on @head. @list
|
||||
* should be an empty list or a list you do not care about
|
||||
* losing its data.
|
||||
*
|
||||
*/
|
||||
static inline void list_cut_position(struct list_head *list,
|
||||
struct list_head *head, struct list_head *entry)
|
||||
{
|
||||
if (list_empty(head))
|
||||
return;
|
||||
if (list_is_singular(head) &&
|
||||
(head->next != entry && head != entry))
|
||||
return;
|
||||
if (entry == head)
|
||||
INIT_LIST_HEAD(list);
|
||||
else
|
||||
__list_cut_position(list, head, entry);
|
||||
}
|
||||
|
||||
static inline void __list_splice(const struct list_head *list,
|
||||
struct list_head *prev,
|
||||
struct list_head *next)
|
||||
{
|
||||
struct list_head *first = list->next;
|
||||
struct list_head *last = list->prev;
|
||||
|
||||
first->prev = prev;
|
||||
prev->next = first;
|
||||
|
||||
last->next = next;
|
||||
next->prev = last;
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice - join two lists, this is designed for stacks
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice(const struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head, head->next);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail - join two lists, each list being a queue
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*/
|
||||
static inline void list_splice_tail(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list))
|
||||
__list_splice(list, head->prev, head);
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head, head->next);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_splice_tail_init - join two lists and reinitialise the emptied list
|
||||
* @list: the new list to add.
|
||||
* @head: the place to add it in the first list.
|
||||
*
|
||||
* Each of the lists is a queue.
|
||||
* The list at @list is reinitialised
|
||||
*/
|
||||
static inline void list_splice_tail_init(struct list_head *list,
|
||||
struct list_head *head)
|
||||
{
|
||||
if (!list_empty(list)) {
|
||||
__list_splice(list, head->prev, head);
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list_entry - get the struct for this entry
|
||||
* @ptr: the &struct list_head pointer.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
/**
|
||||
* list_first_entry - get the first element from a list
|
||||
* @ptr: the list head to take the element from.
|
||||
* @type: the type of the struct this is embedded in.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Note, that list is expected to be not empty.
|
||||
*/
|
||||
#define list_first_entry(ptr, type, member) \
|
||||
list_entry((ptr)->next, type, member)
|
||||
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* __list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*
|
||||
* This variant doesn't differ from list_for_each() any more.
|
||||
* We don't do prefetching in either case.
|
||||
*/
|
||||
#define __list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev - iterate over a list backwards
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev(pos, head) \
|
||||
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_safe(pos, n, head) \
|
||||
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||
pos = n, n = pos->next)
|
||||
|
||||
/**
|
||||
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
|
||||
* @pos: the &struct list_head to use as a loop cursor.
|
||||
* @n: another &struct list_head to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
*/
|
||||
#define list_for_each_prev_safe(pos, n, head) \
|
||||
for (pos = (head)->prev, n = pos->prev; \
|
||||
pos != (head); \
|
||||
pos = n, n = pos->prev)
|
||||
|
||||
/**
|
||||
* list_for_each_entry - iterate over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry(pos, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_reverse(pos, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
|
||||
* @pos: the type * to use as a start point
|
||||
* @head: the head of the list
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
|
||||
*/
|
||||
#define list_prepare_entry(pos, head, member) \
|
||||
((pos) ? : list_entry(head, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue - continue iteration over list of given type
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Continue to iterate over list of given type, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_continue_reverse - iterate backwards from the given point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Start to iterate over list of given type backwards, continuing after
|
||||
* the current position.
|
||||
*/
|
||||
#define list_for_each_entry_continue_reverse(pos, head, member) \
|
||||
for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_from - iterate over list of given type from the current point
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing from current position.
|
||||
*/
|
||||
#define list_for_each_entry_from(pos, head, member) \
|
||||
for (; &pos->member != (head); \
|
||||
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*/
|
||||
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_continue - continue list iteration safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type, continuing after current point,
|
||||
* safe against removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate over list of given type from current point, safe against
|
||||
* removal of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
||||
for (n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
|
||||
* @pos: the type * to use as a loop cursor.
|
||||
* @n: another type * to use as temporary storage
|
||||
* @head: the head for your list.
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* Iterate backwards over list of given type, safe against removal
|
||||
* of list entry.
|
||||
*/
|
||||
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||
for (pos = list_entry((head)->prev, typeof(*pos), member), \
|
||||
n = list_entry(pos->member.prev, typeof(*pos), member); \
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
/**
|
||||
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
|
||||
* @pos: the loop cursor used in the list_for_each_entry_safe loop
|
||||
* @n: temporary storage used in list_for_each_entry_safe
|
||||
* @member: the name of the list_struct within the struct.
|
||||
*
|
||||
* list_safe_reset_next is not safe to use in general if the list may be
|
||||
* modified concurrently (eg. the lock is dropped in the loop body). An
|
||||
* exception to this is if the cursor element (pos) is pinned in the list,
|
||||
* and list_safe_reset_next is called after re-taking the lock and before
|
||||
* completing the current iteration of the loop body.
|
||||
*/
|
||||
#define list_safe_reset_next(pos, n, member) \
|
||||
n = list_entry(pos->member.next, typeof(*pos), member)
|
||||
|
||||
|
||||
#endif
|
||||
444
main.c
Normal file
444
main.c
Normal file
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <tlslib.h>
|
||||
|
||||
#include <vpn.h>
|
||||
#include <cookies.h>
|
||||
#include <common.h>
|
||||
#include <list.h>
|
||||
|
||||
int syslog_open = 0;
|
||||
static unsigned int need_to_expire_cookies = 0;
|
||||
|
||||
struct listen_list_st {
|
||||
struct list_head list;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static void tls_log_func(int level, const char *str)
|
||||
{
|
||||
syslog(LOG_DEBUG, "Debug[<%d>]: %s", level, str);
|
||||
}
|
||||
|
||||
static void tls_audit_log_func(gnutls_session_t session, const char *str)
|
||||
{
|
||||
syslog(LOG_AUTH, "Warning: %s", str);
|
||||
}
|
||||
|
||||
static struct cfg_st config = {
|
||||
.auth_types = AUTH_TYPE_USERNAME_PASS,
|
||||
.name = NULL,
|
||||
.port = 3333,
|
||||
.cert = "./test.pem",
|
||||
.key = "./test.pem",
|
||||
.cert_req = GNUTLS_CERT_IGNORE,
|
||||
.root_dir = "root/",
|
||||
.cookie_validity = 3600,
|
||||
.db_file = "/tmp/db",
|
||||
.ca = NULL
|
||||
};
|
||||
|
||||
const char *human_addr(const struct sockaddr *sa, socklen_t salen,
|
||||
void *_buf, size_t buflen)
|
||||
{
|
||||
const char *save_buf = _buf;
|
||||
char *buf = _buf;
|
||||
size_t l;
|
||||
|
||||
if (!buf || !buflen)
|
||||
return NULL;
|
||||
|
||||
*buf = '\0';
|
||||
|
||||
switch (sa->sa_family) {
|
||||
#if HAVE_IPV6
|
||||
case AF_INET6:
|
||||
snprintf(buf, buflen, "IPv6 ");
|
||||
break;
|
||||
#endif
|
||||
|
||||
case AF_INET:
|
||||
snprintf(buf, buflen, "IPv4 ");
|
||||
break;
|
||||
}
|
||||
|
||||
l = strlen(buf);
|
||||
buf += l;
|
||||
buflen -= l;
|
||||
|
||||
if (getnameinfo(sa, salen, buf, buflen, NULL, 0, NI_NUMERICHOST) !=
|
||||
0)
|
||||
return NULL;
|
||||
|
||||
l = strlen(buf);
|
||||
buf += l;
|
||||
buflen -= l;
|
||||
|
||||
strncat(buf, " port ", buflen);
|
||||
|
||||
l = strlen(buf);
|
||||
buf += l;
|
||||
buflen -= l;
|
||||
|
||||
if (getnameinfo(sa, salen, NULL, 0, buf, buflen, NI_NUMERICSERV) !=
|
||||
0)
|
||||
return NULL;
|
||||
|
||||
return save_buf;
|
||||
}
|
||||
|
||||
/* Returns 0 on success or negative value on error.
|
||||
*/
|
||||
static int
|
||||
listen_ports(struct listen_list_st *list, const char *node,
|
||||
int listen_port, int socktype)
|
||||
{
|
||||
struct addrinfo hints, *res, *ptr;
|
||||
char portname[6];
|
||||
int s, y;
|
||||
char buf[512];
|
||||
struct listen_list_st *tmp;
|
||||
|
||||
INIT_LIST_HEAD(&list->list);
|
||||
|
||||
snprintf(portname, sizeof(portname), "%d", listen_port);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = socktype;
|
||||
hints.ai_flags = AI_PASSIVE
|
||||
#ifdef AI_ADDRCONFIG
|
||||
| AI_ADDRCONFIG
|
||||
#endif
|
||||
;
|
||||
|
||||
s = getaddrinfo(node, portname, &hints, &res);
|
||||
if (s != 0) {
|
||||
fprintf(stderr, "getaddrinfo() failed: %s\n",
|
||||
gai_strerror(s));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ptr = res; ptr != NULL; ptr = ptr->ai_next) {
|
||||
#ifndef HAVE_IPV6
|
||||
if (ptr->ai_family != AF_INET)
|
||||
continue;
|
||||
#endif
|
||||
fprintf(stderr, "listening on %s...\n",
|
||||
human_addr(ptr->ai_addr, ptr->ai_addrlen,
|
||||
buf, sizeof(buf)));
|
||||
|
||||
s = socket(ptr->ai_family, ptr->ai_socktype,
|
||||
ptr->ai_protocol);
|
||||
if (s < 0) {
|
||||
perror("socket() failed");
|
||||
continue;
|
||||
}
|
||||
#if defined(HAVE_IPV6) && !defined(_WIN32)
|
||||
if (ptr->ai_family == AF_INET6) {
|
||||
y = 1;
|
||||
/* avoid listen on ipv6 addresses failing
|
||||
* because already listening on ipv4 addresses: */
|
||||
setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(const void *) &y, sizeof(y));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (socktype == SOCK_STREAM) {
|
||||
y = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
|
||||
(const void *) &y, sizeof(y)) < 0) {
|
||||
perror("setsockopt() failed");
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
#if defined(IP_DONTFRAG)
|
||||
y = 1;
|
||||
if (setsockopt(s, IPPROTO_IP, IP_DONTFRAG,
|
||||
(const void *) &y, sizeof(y)) < 0)
|
||||
perror("setsockopt(IP_DF) failed");
|
||||
#elif defined(IP_MTU_DISCOVER)
|
||||
y = IP_PMTUDISC_DO;
|
||||
if (setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER,
|
||||
(const void *) &y, sizeof(y)) < 0)
|
||||
perror("setsockopt(IP_DF) failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (bind(s, ptr->ai_addr, ptr->ai_addrlen) < 0) {
|
||||
perror("bind() failed");
|
||||
close(s);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (socktype == SOCK_STREAM) {
|
||||
if (listen(s, 10) < 0) {
|
||||
perror("listen() failed");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = calloc(1, sizeof(struct listen_list_st));
|
||||
tmp->fd = s;
|
||||
list_add(&(tmp->list), &(list->list));
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
freeaddrinfo(res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handle_children(int signo)
|
||||
{
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0);
|
||||
}
|
||||
|
||||
static void handle_alarm(int signo)
|
||||
{
|
||||
need_to_expire_cookies = 1;
|
||||
}
|
||||
|
||||
static int open_tun(void)
|
||||
{
|
||||
int tunfd;
|
||||
struct ifreq ifr;
|
||||
|
||||
tunfd = open("/dev/net/tun", O_RDWR);
|
||||
if (tunfd < 0) {
|
||||
int e = errno;
|
||||
syslog(LOG_ERR, "Can't open /dev/net/tun: %s\n",
|
||||
strerror(e));
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
|
||||
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "vpns%d", 0);
|
||||
if (ioctl(tunfd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||
int e = errno;
|
||||
syslog(LOG_ERR, "TUNSETIFF: %s\n", strerror(e));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return tunfd;
|
||||
}
|
||||
|
||||
static int verify_certificate_cb(gnutls_session_t session)
|
||||
{
|
||||
unsigned int status;
|
||||
int ret, type;
|
||||
gnutls_datum_t out;
|
||||
|
||||
/* This verification function uses the trusted CAs in the credentials
|
||||
* structure. So you must have installed one or more CA certificates.
|
||||
*/
|
||||
ret = gnutls_certificate_verify_peers2(session, &status);
|
||||
if (ret < 0) {
|
||||
syslog(LOG_ERR, "Error verifying client certificate");
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
type = gnutls_certificate_type_get(session);
|
||||
|
||||
ret =
|
||||
gnutls_certificate_verification_status_print(status, type,
|
||||
&out, 0);
|
||||
if (ret < 0) {
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "verification: %s", out.data);
|
||||
|
||||
gnutls_free(out.data);
|
||||
|
||||
if (status != 0) /* Certificate is not trusted */
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
|
||||
/* notify gnutls to continue handshake normally */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
|
||||
int fd, pid; // newly accept()ed socket descriptor
|
||||
struct tls_st creds;
|
||||
struct listen_list_st llist;
|
||||
struct listen_list_st *tmp;
|
||||
struct list_head *pos;
|
||||
fd_set rd;
|
||||
int val, n = 0, ret, tunfd;
|
||||
struct timeval tv;
|
||||
|
||||
/*signal(SIGINT, SIG_IGN); */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
signal(SIGCHLD, handle_children);
|
||||
signal(SIGALRM, handle_alarm);
|
||||
|
||||
/* XXX load configuration */
|
||||
|
||||
ret = listen_ports(&llist, config.name, config.port, SOCK_STREAM);
|
||||
if (ret < 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tunfd = open_tun();
|
||||
if (tunfd < 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* XXX drop any privileges */
|
||||
|
||||
gnutls_global_set_log_function(tls_log_func);
|
||||
gnutls_global_set_audit_log_function(tls_audit_log_func);
|
||||
gnutls_global_set_log_level(0);
|
||||
|
||||
ret = gnutls_global_init();
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret = gnutls_certificate_allocate_credentials(&creds.xcred);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret =
|
||||
gnutls_certificate_set_x509_key_file(creds.xcred, config.cert,
|
||||
config.key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
if (config.ca != NULL) {
|
||||
ret =
|
||||
gnutls_certificate_set_x509_trust_file(creds.xcred,
|
||||
config.ca,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
printf("Processed %d CA certificate(s).\n", ret);
|
||||
}
|
||||
|
||||
if (config.crl != NULL) {
|
||||
ret =
|
||||
gnutls_certificate_set_x509_crl_file(creds.xcred,
|
||||
config.crl,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
}
|
||||
|
||||
|
||||
if (config.cert_req != GNUTLS_CERT_IGNORE) {
|
||||
gnutls_certificate_set_verify_function(creds.xcred,
|
||||
verify_certificate_cb);
|
||||
}
|
||||
|
||||
ret = gnutls_priority_init(&creds.cprio, config.priorities, NULL);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
alarm(config.cookie_validity+300);
|
||||
openlog("ocserv", LOG_PID, LOG_LOCAL0);
|
||||
syslog_open = 1;
|
||||
|
||||
for (;;) {
|
||||
FD_ZERO(&rd);
|
||||
|
||||
list_for_each(pos, &llist.list) {
|
||||
tmp = list_entry(pos, struct listen_list_st, list);
|
||||
#ifndef _WIN32
|
||||
val = fcntl(tmp->fd, F_GETFL, 0);
|
||||
if ((val == -1)
|
||||
|| (fcntl(tmp->fd, F_SETFL, val | O_NONBLOCK) <
|
||||
0)) {
|
||||
perror("fcntl()");
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
FD_SET(tmp->fd, &rd);
|
||||
n = MAX(n, tmp->fd);
|
||||
}
|
||||
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = 10;
|
||||
n = select(n + 1, &rd, NULL, NULL, &tv);
|
||||
if (n == -1 && errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (n < 0) {
|
||||
syslog(LOG_ERR, "Error in select(): %s", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (need_to_expire_cookies != 0) {
|
||||
need_to_expire_cookies = 0;
|
||||
pid = fork();
|
||||
if (pid == 0) { /* child */
|
||||
list_for_each(pos, &llist.list) {
|
||||
tmp = list_entry(pos, struct listen_list_st, list);
|
||||
close(tmp->fd);
|
||||
}
|
||||
|
||||
expire_cookies(&config);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(pos, &llist.list) {
|
||||
tmp = list_entry(pos, struct listen_list_st, list);
|
||||
if (FD_ISSET(tmp->fd, &rd)) {
|
||||
fd = accept(tmp->fd, NULL, NULL);
|
||||
if (fd < 0) {
|
||||
syslog(LOG_ERR, "Error in accept(): %s", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) { /* child */
|
||||
list_for_each(pos, &llist.list) {
|
||||
tmp = list_entry(pos, struct listen_list_st, list);
|
||||
close(tmp->fd);
|
||||
}
|
||||
|
||||
vpn_server(&config, &creds,
|
||||
tunfd, fd);
|
||||
exit(0);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
11
root/index.xml
Normal file
11
root/index.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<auth id="main">
|
||||
<message>Please enter your credentials.</message>
|
||||
<form method="post" action="/auth.xml">
|
||||
|
||||
<input type="text" name="username" label="Username:" />
|
||||
<input type="password" name="password" label="Password:" />
|
||||
|
||||
</form>
|
||||
</auth>
|
||||
8
root/login.xml
Normal file
8
root/login.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<auth id="main">
|
||||
<message>Please enter your login cookie.</message>
|
||||
<form method="post" action="/login.xml">
|
||||
<input type="text" name="cookie" label="Cookie:" />
|
||||
</form></auth>
|
||||
|
||||
106
tlslib.c
Normal file
106
tlslib.c
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <tlslib.h>
|
||||
#include <common.h>
|
||||
|
||||
|
||||
ssize_t tls_send(gnutls_session_t session, const void *data,
|
||||
size_t data_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = gnutls_record_send(session, data, data_size);
|
||||
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t tls_send_file(gnutls_session_t session, const char *file)
|
||||
{
|
||||
FILE* fp;
|
||||
char buf[512];
|
||||
ssize_t len, total = 0;
|
||||
int ret;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
|
||||
while ( (len = fread( buf, 1, sizeof(buf), fp)) > 0) {
|
||||
ret = tls_send(session, buf, len);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
total += ret;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
ssize_t tls_recv(gnutls_session_t session, void *data, size_t data_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = gnutls_record_recv(session, data, data_size);
|
||||
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __attribute__ ((format(printf, 2, 3)))
|
||||
tls_printf(gnutls_session_t session, const char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
|
||||
buf[1023] = 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, 1023, fmt, args);
|
||||
va_end(args);
|
||||
return tls_send(session, buf, strlen(buf));
|
||||
|
||||
}
|
||||
|
||||
void tls_close(gnutls_session_t session)
|
||||
{
|
||||
gnutls_bye(session, GNUTLS_SHUT_WR);
|
||||
gnutls_deinit(session);
|
||||
}
|
||||
|
||||
void tls_fatal_close(gnutls_session_t session,
|
||||
gnutls_alert_description_t a)
|
||||
{
|
||||
gnutls_alert_send(session, GNUTLS_AL_FATAL, a);
|
||||
gnutls_deinit(session);
|
||||
}
|
||||
31
tlslib.h
Normal file
31
tlslib.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef TLS_H
|
||||
#define TLS_H
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#define tls_print(s, str) tls_send(s, str, sizeof(str)-1)
|
||||
|
||||
int __attribute__ ((format(printf, 2, 3)))
|
||||
tls_printf(gnutls_session_t session, const char *fmt, ...);
|
||||
|
||||
ssize_t tls_recv(gnutls_session_t session, void *data, size_t data_size);
|
||||
ssize_t tls_send(gnutls_session_t session, const void *data,
|
||||
size_t data_size);
|
||||
|
||||
ssize_t tls_send_file(gnutls_session_t session, const char *file);
|
||||
|
||||
#define GNUTLS_FATAL_ERR(x) \
|
||||
if (x < 0 && gnutls_error_is_fatal (x) != 0) { \
|
||||
if (syslog_open) \
|
||||
syslog(LOG_ERR, "GnuTLS error (at %s:%d): %s", __FILE__, __LINE__, gnutls_strerror(x)); \
|
||||
else \
|
||||
fprintf(stderr, "GnuTLS error (at %s:%d): %s\n", __FILE__, __LINE__, gnutls_strerror(x)); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
void tls_close(gnutls_session_t session);
|
||||
|
||||
void tls_fatal_close(gnutls_session_t session,
|
||||
gnutls_alert_description_t a);
|
||||
|
||||
#endif
|
||||
373
server.c → vpn.c
373
server.c → vpn.c
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2012, 2013 David Woodhouse
|
||||
* Copyright (C) 2013 Nikos Mavrogiannopoulos
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
@@ -9,18 +28,59 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define AC_PKT_DATA 0 /* Uncompressed data */
|
||||
#define AC_PKT_DPD_OUT 3 /* Dead Peer Detection */
|
||||
#define AC_PKT_DPD_RESP 4 /* DPD response */
|
||||
#define AC_PKT_DISCONN 5 /* Client disconnection notice */
|
||||
#define AC_PKT_KEEPALIVE 7 /* Keepalive */
|
||||
#define AC_PKT_COMPRESSED 8 /* Compressed data */
|
||||
#define AC_PKT_TERM_SERVER 9 /* Server kick */
|
||||
#include <common.h>
|
||||
#include <vpn.h>
|
||||
#include <auth.h>
|
||||
#include <tlslib.h>
|
||||
|
||||
#include <http-parser/http_parser.h>
|
||||
|
||||
typedef int (*url_handler_fn)(server_st*);
|
||||
struct known_urls_st {
|
||||
const char* url;
|
||||
url_handler_fn get_handler;
|
||||
url_handler_fn post_handler;
|
||||
};
|
||||
|
||||
struct known_urls_st known_urls[] = {
|
||||
{"/", get_auth_handler, NULL},
|
||||
{"/auth.xml", get_auth_handler, post_auth_handler},
|
||||
{"/login.xml", get_login_handler, post_login_handler},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static url_handler_fn get_url_handler(const char* url)
|
||||
{
|
||||
struct known_urls_st *p;
|
||||
|
||||
p = known_urls;
|
||||
do {
|
||||
if (p->url != NULL && strcmp(p->url, url)==0)
|
||||
return p->get_handler;
|
||||
p++;
|
||||
} while(p->url != NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static url_handler_fn post_url_handler(const char* url)
|
||||
{
|
||||
struct known_urls_st *p;
|
||||
|
||||
p = known_urls;
|
||||
do {
|
||||
if (p->url != NULL && strcmp(p->url, url)==0)
|
||||
return p->post_handler;
|
||||
p++;
|
||||
} while(p->url != NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#define CERTFILE "/tmp/test.pem"
|
||||
|
||||
static const char *const cookies[] = {
|
||||
@@ -28,41 +88,6 @@ static const char *const cookies[] = {
|
||||
|
||||
#define nr_cookies (sizeof(cookies) / sizeof(cookies[0]))
|
||||
|
||||
static int syslog_open = 0;
|
||||
|
||||
#define GNUTLS_FATAL_ERR(x) \
|
||||
if (x < 0 && gnutls_error_is_fatal (x) != 0) { \
|
||||
if (syslog_open) \
|
||||
syslog(LOG_ERR, "GnuTLS error (at %d): %s", __LINE__, gnutls_strerror(x)); \
|
||||
else \
|
||||
fprintf(stderr, "GnuTLS error (at %d): %s\n", __LINE__, gnutls_strerror(x)); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
static ssize_t tls_send(gnutls_session_t session, const void *data,
|
||||
size_t data_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = gnutls_record_send(session, data, data_size);
|
||||
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t tls_recv(gnutls_session_t session, void *data,
|
||||
size_t data_size)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = gnutls_record_recv(session, data, data_size);
|
||||
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tls_gets(gnutls_session_t session, char *buf, size_t len)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -91,21 +116,6 @@ static int tls_gets(gnutls_session_t session, char *buf, size_t len)
|
||||
return i ? : ret;
|
||||
}
|
||||
|
||||
static int __attribute__ ((format(printf, 2, 3)))
|
||||
tls_printf(gnutls_session_t session, const char *fmt, ...)
|
||||
{
|
||||
char buf[1024];
|
||||
va_list args;
|
||||
|
||||
buf[1023] = 0;
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, 1023, fmt, args);
|
||||
va_end(args);
|
||||
return tls_send(session, buf, strlen(buf));
|
||||
|
||||
}
|
||||
|
||||
static int hexnybble(char x)
|
||||
{
|
||||
if (x >= '0' && x <= '9')
|
||||
@@ -119,80 +129,228 @@ static int hexnybble(char x)
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void tls_close(gnutls_session_t session)
|
||||
|
||||
int url_cb(http_parser* parser, const char *at, size_t length)
|
||||
{
|
||||
gnutls_bye(session, GNUTLS_SHUT_WR);
|
||||
gnutls_deinit(session);
|
||||
struct req_data_st *req = parser->data;
|
||||
|
||||
if (length >= sizeof(req->url)) {
|
||||
req->url[0] = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(req->url, at, length);
|
||||
req->url[length] = 0;
|
||||
|
||||
fprintf(stderr, "request %s %s\n", http_method_str(parser->method), req->url);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tls_fatal_close(gnutls_session_t session,
|
||||
gnutls_alert_description_t a)
|
||||
int header_field_cb(http_parser* parser, const char *at, size_t length)
|
||||
{
|
||||
gnutls_alert_send(session, GNUTLS_AL_FATAL, a);
|
||||
gnutls_deinit(session);
|
||||
struct req_data_st *req = parser->data;
|
||||
|
||||
if (strncmp(at, "Cookie", length) == 0) {
|
||||
req->cookie_set = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int header_complete_cb(http_parser* parser)
|
||||
{
|
||||
struct req_data_st *req = parser->data;
|
||||
|
||||
req->headers_complete = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int message_complete_cb(http_parser* parser)
|
||||
{
|
||||
struct req_data_st *req = parser->data;
|
||||
|
||||
req->message_complete = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int header_value_cb(http_parser* parser, const char *at, size_t length)
|
||||
{
|
||||
struct req_data_st *req = parser->data;
|
||||
char *p;
|
||||
size_t nlen;
|
||||
|
||||
if (req->cookie_set == -1) {
|
||||
p = strstr(at, "webvpn=");
|
||||
if (p == NULL || length <= 7) {
|
||||
req->cookie_set = 0;
|
||||
return 0;
|
||||
}
|
||||
p += 7;
|
||||
length -= 7;
|
||||
|
||||
if (length < COOKIE_SIZE*2) {
|
||||
req->cookie_set = 0;
|
||||
return 0;
|
||||
}
|
||||
length = COOKIE_SIZE*2;
|
||||
|
||||
nlen = sizeof(req->cookie);
|
||||
gnutls_hex2bin(p, length, req->cookie, &nlen);
|
||||
req->cookie_set = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int body_cb(http_parser* parser, const char *at, size_t length)
|
||||
{
|
||||
struct req_data_st *req = parser->data;
|
||||
char* tmp = malloc(length+1);
|
||||
|
||||
if (tmp == NULL)
|
||||
return 1;
|
||||
|
||||
memcpy(tmp, at, length);
|
||||
tmp[length] = 0;
|
||||
|
||||
req->body = tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void)
|
||||
void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd)
|
||||
{
|
||||
int tun_nr = -1;
|
||||
struct ifreq ifr;
|
||||
// int tun_nr = -1;
|
||||
// struct ifreq ifr;
|
||||
unsigned char buf[2048];
|
||||
int tunfd;
|
||||
int i, ret;
|
||||
gnutls_certificate_credentials_t x509_cred;
|
||||
// int i;
|
||||
int ret;
|
||||
ssize_t nparsed, nrecvd;
|
||||
gnutls_session_t session;
|
||||
gnutls_priority_t priority_cache;
|
||||
|
||||
ret = gnutls_global_init();
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret = gnutls_certificate_allocate_credentials(&x509_cred);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret =
|
||||
gnutls_certificate_set_x509_key_file(x509_cred, CERTFILE,
|
||||
CERTFILE,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
/*
|
||||
ret = gnutls_certificate_set_x509_trust_file (x509_cred, TRUSTFILE,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
*/
|
||||
|
||||
ret = gnutls_priority_init(&priority_cache, "NORMAL", NULL);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
http_parser parser;
|
||||
http_parser_settings settings;
|
||||
struct sockaddr_storage remote_addr;
|
||||
socklen_t remote_addr_len;
|
||||
struct req_data_st req;
|
||||
server_st server;
|
||||
url_handler_fn fn;
|
||||
|
||||
remote_addr_len = sizeof(remote_addr);
|
||||
ret = getpeername (fd, (void*)&remote_addr, &remote_addr_len);
|
||||
if (ret < 0)
|
||||
syslog(LOG_INFO, "Accepted connection from unknown");
|
||||
else
|
||||
syslog(LOG_INFO, "Accepted connection from %s",
|
||||
human_addr((void*)&remote_addr, remote_addr_len,
|
||||
buf, sizeof(buf)));
|
||||
|
||||
/* initialize the session */
|
||||
ret = gnutls_init(&session, GNUTLS_SERVER);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret = gnutls_priority_set(session, priority_cache);
|
||||
ret = gnutls_priority_set(session, creds->cprio);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
ret =
|
||||
gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
|
||||
x509_cred);
|
||||
creds->xcred);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
gnutls_certificate_server_set_request(session, GNUTLS_CERT_IGNORE);
|
||||
|
||||
gnutls_transport_set_ptr2(session, (gnutls_transport_ptr_t) 0,
|
||||
(gnutls_transport_ptr_t) 1);
|
||||
|
||||
openlog("ocserv", LOG_PID, LOG_LOCAL0);
|
||||
syslog_open = 1;
|
||||
gnutls_certificate_server_set_request(session, config->cert_req);
|
||||
gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) (long)fd);
|
||||
|
||||
do {
|
||||
ret = gnutls_handshake(session);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
|
||||
GNUTLS_FATAL_ERR(ret);
|
||||
|
||||
syslog(LOG_INFO, "Accepted connection\n");
|
||||
memset(&settings, 0, sizeof(settings));
|
||||
|
||||
settings.on_url = url_cb;
|
||||
settings.on_header_field = header_field_cb;
|
||||
settings.on_header_value = header_value_cb;
|
||||
settings.on_headers_complete = header_complete_cb;
|
||||
settings.on_message_complete = message_complete_cb;
|
||||
settings.on_body = body_cb;
|
||||
|
||||
server.config = config;
|
||||
server.session = session;
|
||||
server.parser = &parser;
|
||||
server.tunfd = tunfd;
|
||||
|
||||
restart:
|
||||
http_parser_init(&parser, HTTP_REQUEST);
|
||||
memset(&req, 0, sizeof(req));
|
||||
parser.data = &req;
|
||||
|
||||
/* parse as we go */
|
||||
do {
|
||||
nrecvd = tls_recv(session, buf, sizeof(buf));
|
||||
GNUTLS_FATAL_ERR(nrecvd);
|
||||
|
||||
nparsed = http_parser_execute(&parser, &settings, (void*)buf, nrecvd);
|
||||
if (nparsed == 0) {
|
||||
syslog(LOG_INFO, "Error parsing HTTP request");
|
||||
exit(1);
|
||||
}
|
||||
} while(req.headers_complete == 0);
|
||||
|
||||
if (parser.method == HTTP_GET) {
|
||||
fn = get_url_handler(req.url);
|
||||
if (fn == NULL) {
|
||||
syslog(LOG_INFO, "Unexpected URL %s", req.url);
|
||||
tls_print(session, "HTTP/1.1 404 Nah, go away\r\n\r\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ret = fn(&server);
|
||||
if (ret == 0 && (parser.http_major != 1 || parser.http_minor != 0))
|
||||
goto restart;
|
||||
|
||||
} else if (parser.method == HTTP_POST) {
|
||||
/* continue reading */
|
||||
while(req.message_complete == 0) {
|
||||
nrecvd = tls_recv(session, buf, sizeof(buf));
|
||||
GNUTLS_FATAL_ERR(nrecvd);
|
||||
|
||||
nparsed = http_parser_execute(&parser, &settings, (void*)buf, nrecvd);
|
||||
if (nparsed == 0) {
|
||||
syslog(LOG_INFO, "Error parsing HTTP request");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn = post_url_handler(req.url);
|
||||
if (fn == NULL) {
|
||||
syslog(LOG_INFO, "Unexpected POST URL %s", req.url);
|
||||
tls_print(session, "HTTP/1.1 404 Nah, go away\r\n\r\n");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ret = fn(&server);
|
||||
if (ret == 0 && (parser.http_major != 1 || parser.http_minor != 0))
|
||||
goto restart;
|
||||
|
||||
} else if (parser.method == HTTP_CONNECT) {
|
||||
ret = connect_handler(&server);
|
||||
if (ret == 0 && (parser.http_major != 1 || parser.http_minor != 0))
|
||||
goto restart;
|
||||
|
||||
} else {
|
||||
syslog(LOG_INFO, "Unexpected method %s", http_method_str(parser.method));
|
||||
tls_print(session, "HTTP/1.1 404 Nah, go away\r\n\r\n");
|
||||
}
|
||||
|
||||
finish:
|
||||
tls_close(session);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
next:
|
||||
if (tls_gets(session, buf, sizeof(buf)) <= 0) {
|
||||
@@ -448,3 +606,4 @@ int main(void)
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
50
vpn.h
Normal file
50
vpn.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <http-parser/http_parser.h>
|
||||
|
||||
#define AUTH_TYPE_USERNAME_PASS (1<<0)
|
||||
#define AUTH_TYPE_CERTIFICATE (1<<1)
|
||||
|
||||
struct cfg_st {
|
||||
const char *name;
|
||||
unsigned int port;
|
||||
const char *cert;
|
||||
const char *key;
|
||||
const char *ca;
|
||||
const char *crl;
|
||||
gnutls_certificate_request_t cert_req;
|
||||
const char *priorities;
|
||||
const char *root_dir; /* where the xml files are served from */
|
||||
unsigned int auth_types; /* or'ed sequence of AUTH_TYPE */
|
||||
time_t cookie_validity; /* in seconds */
|
||||
const char* db_file;
|
||||
};
|
||||
|
||||
struct tls_st {
|
||||
gnutls_certificate_credentials_t xcred;
|
||||
gnutls_priority_t cprio;
|
||||
};
|
||||
|
||||
typedef struct server_st {
|
||||
gnutls_session_t session;
|
||||
http_parser* parser;
|
||||
struct cfg_st *config;
|
||||
int tunfd;
|
||||
} server_st;
|
||||
|
||||
#define COOKIE_SIZE 32
|
||||
|
||||
struct req_data_st {
|
||||
char url[256];
|
||||
unsigned char cookie[COOKIE_SIZE];
|
||||
unsigned int cookie_set;
|
||||
char *body;
|
||||
unsigned int headers_complete;
|
||||
unsigned int message_complete;
|
||||
};
|
||||
|
||||
void vpn_server(struct cfg_st *config, struct tls_st *creds, int tunfd, int fd);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user