From 45243dd2f2be49cd91be1dc28932e6c9040db6a1 Mon Sep 17 00:00:00 2001
From: Kozlov Dmitry <dima@server>
Date: Mon, 6 Sep 2010 18:27:02 +0400
Subject: working on radius module

---
 accel-pptpd/accel-pptpd.conf      |   4 +
 accel-pptpd/ctrl/pptp.c           |   1 -
 accel-pptpd/pwdb.c                |  31 ++----
 accel-pptpd/pwdb.h                |   6 +-
 accel-pptpd/radius/CMakeLists.txt |   2 +
 accel-pptpd/radius/dict.c         |  55 +++++-----
 accel-pptpd/radius/packet.c       | 172 ++++++++++++++++++++++++++++++
 accel-pptpd/radius/radius.c       | 105 +++++++++++++++---
 accel-pptpd/radius/radius.h       |  74 ++++++++++++-
 accel-pptpd/radius/req.c          | 216 ++++++++++++++++++++++++++++++++++++++
 accel-pptpd/triton/CMakeLists.txt |   1 +
 accel-pptpd/triton/event.c        | 103 ++++++++++++++++++
 accel-pptpd/triton/triton.h       |   4 +
 accel-pptpd/triton/triton_p.h     |   9 +-
 14 files changed, 714 insertions(+), 69 deletions(-)
 create mode 100644 accel-pptpd/radius/packet.c
 create mode 100644 accel-pptpd/radius/req.c
 create mode 100644 accel-pptpd/triton/event.c

diff --git a/accel-pptpd/accel-pptpd.conf b/accel-pptpd/accel-pptpd.conf
index 9b63534..4748022 100644
--- a/accel-pptpd/accel-pptpd.conf
+++ b/accel-pptpd/accel-pptpd.conf
@@ -15,3 +15,7 @@ echo-interval=3
 
 [radius]
 dictionary=dictionary
+nas-identifier=pptp
+nas-ip-address=192.168.10.20
+auth_server=127.0.0.1:1812,secret
+acct_server=127.0.0.1:1812,secret
diff --git a/accel-pptpd/ctrl/pptp.c b/accel-pptpd/ctrl/pptp.c
index b503d85..66c0978 100644
--- a/accel-pptpd/ctrl/pptp.c
+++ b/accel-pptpd/ctrl/pptp.c
@@ -529,7 +529,6 @@ static void __init pptp_init(void)
   addr.sin_port = htons (PPTP_PORT);
   addr.sin_addr.s_addr = htonl (INADDR_ANY);
   if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
-  	perror("pptp: bind");
     log_error("pptp: failed to bind socket: %s\n", strerror(errno));
 		close(serv.hnd.fd);
     return;
diff --git a/accel-pptpd/pwdb.c b/accel-pptpd/pwdb.c
index 8bde8ff..1b682a4 100644
--- a/accel-pptpd/pwdb.c
+++ b/accel-pptpd/pwdb.c
@@ -6,41 +6,26 @@
 
 static LIST_HEAD(pwdb_handlers);
 
-int __export pwdb_cleartext_check(struct ppp_t *ppp, const char *username,const char *password)
+int __export pwdb_check(struct ppp_t *ppp, const char *username, int type, ...)
 {
 	struct pwdb_t *pwdb;
-	int r = PWDB_NO_IMPL;
-
-	list_for_each_entry(pwdb, &pwdb_handlers, entry) {
-		if (!pwdb->cleartext_check)
-			continue;
-		r = pwdb->cleartext_check(pwdb, ppp, username, password);
-		if (r == PWDB_NO_IMPL)
-			continue;
-		break;
-	}
-
-	return r;
-}
-int __export pwdb_encrypted_check(struct ppp_t *ppp, const char *username, int type, ...)
-{
-	struct pwdb_t *pwdb;
-	int r = PWDB_NO_IMPL;
+	int r, res = PWDB_NO_IMPL;
 	va_list args;
 
 	va_start(args, type);
 
 	list_for_each_entry(pwdb, &pwdb_handlers, entry) {
-		if (!pwdb->encrypted_check)
+		if (!pwdb->check)
 			continue;
-		r = pwdb->encrypted_check(pwdb, ppp, username, type, args);
+		r = pwdb->check(pwdb, ppp, username, type, args);
 		if (r == PWDB_NO_IMPL)
 			continue;
-		break;
+		if (r == PWDB_SUCCESS)
+			return PWDB_SUCCESS;
+		res = r;
 	}
 
-	return r;
-
+	return res;
 }
 __export const char *pwdb_get_passwd(struct ppp_t *ppp, const char *username)
 {
diff --git a/accel-pptpd/pwdb.h b/accel-pptpd/pwdb.h
index 42f9133..d349543 100644
--- a/accel-pptpd/pwdb.h
+++ b/accel-pptpd/pwdb.h
@@ -13,13 +13,11 @@ struct ppp_t;
 struct pwdb_t
 {
 	struct list_head entry;
-	int (*cleartext_check)(struct pwdb_t *, struct ppp_t *, const char *username, const char *password);
-	int (*encrypted_check)(struct pwdb_t *, struct ppp_t *, const char *username, int type, va_list args);
+	int (*check)(struct pwdb_t *, struct ppp_t *, const char *username, int type, va_list args);
 	const char* (*get_passwd)(struct pwdb_t *, struct ppp_t *, const char *username);
 };
 
-int pwdb_cleartext_check(struct ppp_t *, const char *username,const char *password);
-int pwdb_encrypted_check(struct ppp_t *, const char *username, int type, ...);
+int pwdb_check(struct ppp_t *, const char *username, int type, ...);
 const char *pwdb_get_passwd(struct ppp_t *, const char *username);
 
 void pwdb_register(struct pwdb_t *);
diff --git a/accel-pptpd/radius/CMakeLists.txt b/accel-pptpd/radius/CMakeLists.txt
index a53491f..17a8578 100644
--- a/accel-pptpd/radius/CMakeLists.txt
+++ b/accel-pptpd/radius/CMakeLists.txt
@@ -2,6 +2,8 @@ SET(target radius)
 SET(sources
 	radius.c
 	dict.c
+	req.c
+	packet.c
 )
 
 ADD_LIBRARY(radius SHARED ${sources})
diff --git a/accel-pptpd/radius/dict.c b/accel-pptpd/radius/dict.c
index 62a25cf..992d7d4 100644
--- a/accel-pptpd/radius/dict.c
+++ b/accel-pptpd/radius/dict.c
@@ -7,23 +7,7 @@
 #include "radius.h"
 #include "log.h"
 
-
-struct dict_value_t
-{
-	struct list_head entry;
-	rad_value_t val;
-	const char *name;
-};
-
-struct dict_attr_t
-{
-	struct list_head entry;
-	const char *name;
-	int id;
-	int type;
-	rad_value_t val;
-	struct list_head values;
-};
+static struct rad_dict_t *dict;
 
 static char *skip_word(char *ptr)
 {
@@ -78,26 +62,25 @@ struct dict_attr_t *find_attr(struct rad_dict_t *dict, const char *name)
 }
 
 #define BUF_SIZE 1024
-void *rad_load_dict(const char *fname)
+int rad_dict_load(const char *fname)
 {
 	FILE *f;
 	char *buf, *ptr[3], *endptr;
 	int n = 0;
-	struct rad_dict_t *dict;
 	struct dict_attr_t *attr;
 	struct dict_value_t *val;
 	
 	f = fopen(fname, "r");
 	if (!f) {
 		log_error("radius: open dictioanary '%s': %s\n", fname, strerror(errno));
-		return NULL;
+		return -1;
 	}
 	
 	buf = malloc(BUF_SIZE);
 	if (!buf) {
 		log_error("radius: out of memory\n");
 		fclose(f);
-		return NULL;
+		return -1;
 	}
 
 	dict = malloc(sizeof(*dict));
@@ -105,7 +88,7 @@ void *rad_load_dict(const char *fname)
 		log_error("radius: out of memory\n");
 		fclose(f);
 		free(buf);
-		return NULL;
+		return -1;
 	}
 
 	INIT_LIST_HEAD(&dict->items);
@@ -186,16 +169,16 @@ void *rad_load_dict(const char *fname)
 	free(buf);
 	fclose(f);
 
-	return dict;
+	return 0;
 
 out_err:
-	rad_free_dict(dict);
+	rad_dict_free(dict);
 	free(buf);
 	fclose(f);
-	return NULL;
+	return -1;
 }
 
-void rad_free_dict(struct rad_dict_t *dict)
+void rad_dict_free(struct rad_dict_t *dict)
 {
 	struct dict_attr_t *attr;
 	struct dict_value_t *val;
@@ -217,3 +200,23 @@ void rad_free_dict(struct rad_dict_t *dict)
 	free(dict);
 }
 
+struct rad_dict_attr_t *rad_dict_find_attr(const char *name)
+{
+	struct rad_dict_attr_t *attr;
+
+	list_for_each_entry(attr, &dict->items, entry)
+		if (!strcmp(attr->name, name))
+			return attr;
+
+	return NULL;
+}
+struct rad_dict_value_t *rad_dict_find_val(struct rad_dict_attr_t *attr, const char *name)
+{
+	struct rad_dict_value_t *val;
+
+	list_for_each_entry(val, &attr->values, entry)
+		if (!strcmp(val->name, name))
+			return val;
+
+	return NULL;
+}
diff --git a/accel-pptpd/radius/packet.c b/accel-pptpd/radius/packet.c
new file mode 100644
index 0000000..a22a611
--- /dev/null
+++ b/accel-pptpd/radius/packet.c
@@ -0,0 +1,172 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdin.h>
+
+#include "radius.h"
+
+static int urandom_fd;
+
+int rad_packet_build(struct rad_packet_t *pack)
+{
+	struct rad_req_attr_t *attr;
+	uint8_t *ptr;
+
+	ptr = malloc(pack->len);
+	if (!ptr) {
+		log_error("radius:packet: out of memory\n");
+		return -1;
+	}
+	
+	*ptr = pack->code; ptr++;
+	*ptr = pack->id; ptr++;
+	*(uint16_t*)ptr = pack->len; pt r+= 2;
+	while (1) {
+		if (read(erandom_fd, ptr, 16) != 16) {
+			if (errno == EINTR)
+				continue;
+			log_error("radius:packet:read urandom: %s\n", strerror(errno));
+			goto out_err;
+		}
+		break;
+	}
+	ptr+=16;
+
+	list_for_each_entry(attr, &pack->attrs, entry) {
+		*ptr = attr->attr.id; ptr++;
+		*ptr = attr->len; ptr++;
+		switch(attr->attr.type) {
+			case ATTR_TYPE_INTEGER:
+				*(uint32_t*)ptr = attr->val.integer;
+				break;
+			case ATTR_TYPE_STRING:
+				memcpy(ptr, attr->val.string);
+				break;
+			case ATTR_TYPE_IPADDR:
+				*(in_addr_t*)ptr = attr->val.ipaddr;
+				break;
+			case ATTR_TYPE_DATE:
+				*(uint32_t*)ptr = attr->val.date;
+				break;
+			default:
+				log_error("radius:packet: unknown attribute type\n");
+				abort();
+		}
+		ptr += attr->len;
+	}
+
+	return 0;
+}
+
+struct rad_packet_t *rad_packet_recv(int fd)
+{
+	struct rad_packet_t *pack;
+	struct rad_req_attr_t *attr;
+	struct rad_dict_attr_t *da;
+	uint8_t *ptr;
+	int n, type, len;
+	
+	pack = malloc(sizeof(*pack));
+	if (!pack) {
+		log_error("radius:packet: out of memory\n");
+		return NULL;
+	}
+
+	memset(pack, 0, sizeof(*pack));
+	INIT_LIST_HEAD(&pack->attrs);
+
+	pack->buf = malloc(REQ_MAX_LENGTH);
+	if (!pack->buf) {
+		log_error("radius:packet: out of memory\n");
+		free(pack);
+		return NULL;
+	}
+
+	while (1) {
+		n = read(fd, pack->buf, REQ_MAX_LENGTH);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			log_error("radius:packet:read: %s\n", strerror(errno));
+			goto out_err;
+		}
+		break;
+	}
+
+	if (n < 20) {
+		log_warn("radius:packet: short packed received (%i)\n", n);
+		goto out_err;
+	}
+
+	ptr = (uint8_t *)pack->buf;
+
+	pack->code = *ptr; ptr++;
+	pack->id = *ptr; ptr++;
+	pack->len = *(uint16_t*)ptr; ptr += 2;
+
+	if (pack->len > n) {
+		log_warn("radius:packet: short packet received %i, expected %i\n", pack->len, n);
+		goto out_err;
+	}
+
+	ptr += 16;
+	n -= 20;
+
+	while (n>0) {
+		type = *ptr; ptr++;
+		len = *ptr; ptr++;
+		if (2 + len > n) {
+			log_error("radius:packet: too long attribute received (%i, %i)\n", type, len);
+			goto out_err;
+		}
+		da = rad_dict_find_attr_type(n);
+		if (da) {
+			attr = malloc(sizeof(*attr));
+			if (!attr) {
+				log_error("radius:packet: out of memory\n");
+				goto out_err;
+			}
+			attr->attr = da;
+			attr->type = type;
+			attr->len = len;
+			if (type == ATTR_TYPE_STRING) {
+				attr->val.string = malloc(len);
+				if (!attr->val.string) {
+					log_error("radius:packet: out of memory\n");
+					free(attr);
+					goto out_err;
+				}
+			} else
+				memcpy(&attr->type.integer, ptr, 4);
+			list_add_tail(&attr->entry, &pack->attrs);
+		} else
+			log_warn("radius:packet: unknown attribute type received (%i)\n", type);
+		ptr += len;
+		n -= 2 + len;
+	}
+
+	return pack;
+
+out_err:
+	rad_packet_free(pack);
+	return NULL;
+}
+
+void rad_packet_free(struct rad_packet_t *pack)
+{
+	struct rad_req_attr_t *attr;
+	
+	if (pack->buf)
+		free(pack->buf);
+
+	while(!list_empty(&pack->attrs)) {
+		attr = list_entry(pack->attrs.next, typeof(*attr), entry);
+		if (attr->attr.type == ATTR_TYPE_STRING)
+			free(attr->val.string);
+		list_del(&attr->entry);
+		free(attr);
+	}
+
+	free(pack);
+}
diff --git a/accel-pptpd/radius/radius.c b/accel-pptpd/radius/radius.c
index 10fef65..4d020d5 100644
--- a/accel-pptpd/radius/radius.c
+++ b/accel-pptpd/radius/radius.c
@@ -7,23 +7,105 @@
 #include "pwdb.h"
 #include "radius.h"
 
-struct radius_pd_t
+static struct ppp_notified_t notified;
+
+static int check_pap(struct radius_pd_t *rpd, const char *username, va_list args)
 {
-	struct ppp_pd_t pd;
-	struct ppp_t *ppp;
-};
+	struct rad_req_t *req;
+	int i, r = PWDB_DENIED;
+	int id = va_arg(args, int);
+	const char *passwd = va_arg(args, const char *);
 
-static struct ppp_notified_t notified;
+	req = rad_req_alloc(rpd, CODE_ACCESS_REQUEST);
+	if (!req)
+		return PWDB_DENIED;
+
+	if (rad_req_add_str(req, "User-Password", passwd, strlen(passwd)))
+		goto out;
+
+	for(i = 0; i < max_try; i++) {
+		if (rad_req_send(req))
+			goto out;
+
+		if (rad_req_wait(req, timeout))
+			goto out;
+
+		if (req->answer)
+			break;
+	}
+
+out:
+	rad_req_free(req);
+
+	return r;
+}
+
+static int check_chap_md5(struct radius_pd_t *rpd, const char *username, va_list args)
+{
+	int id = va_arg(args, int);
+	const uint8_t *challenge = va_arg(args, const uint8_t *);
+}
 
-int cleartext_check(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username, const char *password)
+static int check_mschap_v1(struct radius_pd_t *rpd, const char *username, va_list args)
 {
-	return PWDB_NO_IMPL;
+	int id = va_arg(args, int);
+	const uint8_t *challenge = va_arg(args, const uint8_t *);
+	const uint8_t *lm_response = va_arg(args, const uint8_t *);
+	const uint8_t *nt_response = va_arg(args, const uint8_t *);
+	int flags = va_arg(args, int);
 }
-int encrypted_check(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username, int type, va_list args)
+
+static int check_mschap_v2(struct radius_pd_t *rpd, const char *username, va_list args)
 {
-	return PWDB_NO_IMPL;
+	int id = va_arg(args, int);
+	const uint8_t *challenge = va_arg(args, const uint8_t *);
+	const uint8_t *peer_challenge = va_arg(args, const uint8_t *);
+	const uint8_t *response = va_arg(args, const uint8_t *);
+	int flags = va_arg(args, int);
+	uint8_t *authenticator = va_arg(args, uint8_t *);
 }
 
+static int check(struct pwdb_t *pwdb, struct ppp_t *ppp, const char *username, int type, va_list _args)
+{
+	int r = PWDB_NO_IMPL;
+	va_list args;
+	int chap_type;
+	struct ppp_pd_t *pd;
+	struct radius_pd_t *rpd = NULL;
+
+	list_for_each_entry(pd, &ppp->pd_list, entry) {
+		if (pd->key == &notified) {
+			rpd = container_of(pd, typeof(*rpd), pd);
+			break;
+		}
+	}
+
+	va_copy(args, _args);
+
+	switch(type) {
+		case PPP_PAP:
+			r = check_pap(rpd, username, args);
+			break;
+		case PPP_CHAP:
+			chap_type = va_arg(args, int);
+			switch(chap_type) {
+				case 0x05:
+					r = check_chap_md5(rpd, username, args);
+					break;
+				case 0x80:
+					r = check_mschap_v1(rpd, username, args);
+					break;
+				case 0x81:
+					r = check_mschap_v2(rpd, username, args);
+					break;
+			}
+			break;
+	}
+
+	va_end(args);
+
+	return r;
+}
 
 static void ppp_started(struct ppp_notified_t *n, struct ppp_t *ppp)
 {
@@ -50,9 +132,8 @@ static void ppp_finished(struct ppp_notified_t *n, struct ppp_t *ppp)
 	}
 }
 
-struct pwdb_t pwdb = {
-	.cleartext_check = cleartext_check,
-	.encrypted_check = encrypted_check,
+static struct pwdb_t pwdb = {
+	.check = check,
 };
 
 static struct ppp_notified_t notified = {
diff --git a/accel-pptpd/radius/radius.h b/accel-pptpd/radius/radius.h
index aac1cd0..3fe7862 100644
--- a/accel-pptpd/radius/radius.h
+++ b/accel-pptpd/radius/radius.h
@@ -2,12 +2,23 @@
 #define __RADIUS_H
 
 #include <netinet/in.h>
+#include "triton.h"
+
+#define REQ_LENGTH_MAX 4096
 
 #define ATTR_TYPE_INTEGER 0
 #define ATTR_TYPE_STRING  1
 #define ATTR_TYPE_DATE    2
 #define ATTR_TYPE_IPADDR  3
 
+#define CODE_ACCESS_REQUEST 1
+
+struct radius_pd_t
+{
+	struct ppp_pd_t pd;
+	struct ppp_t *ppp;
+};
+
 typedef union
 {
 		int integer;
@@ -21,8 +32,67 @@ struct rad_dict_t
 	struct list_head items;
 };
 
-void *rad_load_dict(const char *fname);
-void rad_free_dict(struct rad_dict_t *dict);
+struct rad_dict_value_t
+{
+	struct list_head entry;
+	rad_value_t val;
+	const char *name;
+};
+
+struct rad_dict_attr_t
+{
+	struct list_head entry;
+	const char *name;
+	int id;
+	int type;
+	struct list_head values;
+};
+
+struct rad_req_attr_t
+{
+	struct list_head entry;
+	struct rad_dict_attr_t *attr;
+	//struct rad_dict_value_t *val;
+	rad_value_t val;
+};
+
+struct rad_packet_t
+{
+	int code;
+	int id;
+	struct list_head attrs;
+	void *buf;
+};
+struct rad_req_t
+{
+	struct triton_md_handler_t hnd;
+	struct triton_timer_t timeout;
+	struct rad_packet_t pack;
+	struct rad_packet_t *answer;
+	const char *server_host;
+	int server_port;
+
+	struct radius_pd_t *rpd;
+};
+
+
+int rad_dict_load(const char *fname);
+void rad_dict_free(struct rad_dict_t *dict);
+struct rad_dict_attr_t *rad_dict_find_attr(const char *name);
+struct rad_dict_attr_t *rad_dict_find_attr_type(int type);
+struct rad_dict_value_t *rad_dict_find_val(struct rad_dict_attr_t *, const char *name);
+
+struct rad_req_t *rad_rec_alloc(struct radius_pd_t *rpd, int code);
+void rad_rec_free(struct rad_req_t *);
+int rad_req_send(struct rad_req_t *);
+int rad_req_add_int(struct rad_req_t *req, const char *name, int val);
+int rad_req_add_val(struct rad_req_t *req, const char *name, const char *val, int len);
+int rad_req_add_str(struct rad_req_t *req, const char *name, const char *val, int len);
+
+int rad_packet_build(struct rad_packet_t *pack);
+struct rad_packet_t *rad_packet_recv(int fd);
+void rad_packet_free(struct rad_packet_t *);
+
 
 #endif
 
diff --git a/accel-pptpd/radius/req.c b/accel-pptpd/radius/req.c
new file mode 100644
index 0000000..d03ea9a
--- /dev/null
+++ b/accel-pptpd/radius/req.c
@@ -0,0 +1,216 @@
+#include <stdlib.h>
+#include <sys/scket.h>
+
+#include "triton.h"
+#include "radius.h"
+
+
+static int rad_req_read(struct triton_md_handler_t *h);
+static void rd_req_timeout(struct triton_timer_t *t);
+
+struct rad_req_t *rad_rec_alloc(struct radius_pd_t *rpd, int code, const char *username);
+{
+	struct rad_req_t *req = malloc(sizeof(*req));
+
+	if (!req)
+		return NULL;
+
+	memset(req, 0, sizeof(*req));
+	INIT_LIST_HEAD(&req->pack.attrs);
+	req->rpd = rpd;
+	req->pack.code = code;
+	req->pack.len = 20;
+	req->hnd.fd = -1;
+	req->hnd.read = rad_req_read;
+	req->hnd.timeout.exoire = rad_req_timeout;
+
+	if (rad_req_add_str(req, "User-Name", username, strlen(username)))
+		goto out_err;
+	if (conf_nas_identifier)
+		if (rad_req_add_str(req, "NAS-Identifier", conf_nas_identifier, strlen(conf_nas_identifier)))
+			goto out_err;
+	if (rad_req_add_int(req, "NAS-Port-Id", rpd->ppp->unit_idx, 4))
+		goto out_err;
+	if (rad_req_add_str(req, "NAS-Port-Type", "Sync", 4))
+		goto out_err;
+	if (rad_req_add_str(req, "Service-Type", "Framed-User", 11))
+		goto out_err;
+	if (rad_req_add_str(req, "Framed-Protocol", "PPP", 3))
+		goto out_err;
+
+	return req;
+}
+
+void rad_rec_free(struct rad_req_t *)
+{
+
+}
+
+int rad_req_send(struct rad_req_t *req)
+{
+  struct sockaddr_in addr;
+	int n;
+
+	if (req->hnd.fd == -1) {
+		req->hnd.fd = socket(PF_INET, SOCK_DGRAM ,0);
+		if (!req->hnd.fd) {
+			log_error("radius:socket: %s\n", strerror(errno));
+			return -1;
+		}
+
+		if (conf_nas_ip_address) {
+			addr.sin_family = AF_INET;
+			addr.sin_addr.s_addr = htonl(conf_nas_ip_address);
+			if (bind(req->hnd.fd, (struct sockaddr *) &addr, sizeof(addr))) {
+				log_error("radius:bind: %s\n", strerror(errno));
+				goto out_err;
+			}
+		}
+
+		addr.sin_addr.s_addr = htonl(req->server_name);
+		addr.sin_port = htons(req->server_port);
+
+		if (connect(req->hnd.fd (struct sockaddr *) &addr, sizeof(addr))) {
+			log_error("radius:connect: %s\n", strerror(errno));
+			goto out_err;
+		}
+
+		if (fcntl(req->hnd.fd, F_SETFL, O_NONBLOCK)) {
+			log_error("radius: failed to set nonblocking mode: %s\n", strerror(errno));
+			goto out_err;
+		}
+
+		if (rad_packet_build(&req->pack))
+			goto out_err;
+	}
+	
+	while (1) {
+		n = write(req->hnd.fd, req->pack.buf, req->pack.len);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			log_error("radius:write: %s\n", strerror(errno));
+			goto out_err_free_pack;
+		} else if (n != req->pack.len) {
+			log_error("radius:write: short write %i, excpected %i\n", n, req->pack.len);
+			goto out_err_free_pack;
+		}
+		break;
+	}
+
+	return 0;
+
+out_err_free_pack:
+	rad_packet_free(&req->pack);
+out_err:
+	close(req->hnd.fd);
+	req->hnd.fd = -1;
+	return -1;
+}
+
+int rad_req_add_int(struct rad_req_t *req, const char *name, int val)
+{
+	struct rad_req_attr_t *ra;
+	struct rad_dict_attr_t *attr;
+
+	if (req->len + 2 + 4 >= REQ_LENGTH_MAX)
+		return -1;
+
+	attr = rad_dict_find_attr(name);
+	if (!attr)
+		return -1;
+	
+	ra = malloc(sizeof(*ra));
+	if (!ra)
+		return -1;
+
+	ra->attr = attr;
+	ra->val.integer = val;
+	list_add_tail(&ra->entry, &req->pack.attrs);
+	req->len += 2 + 4;
+
+	return 0;
+}
+
+int rad_req_add_str(struct rad_req_t *req, const char *name, const char *val, int len)
+{
+	struct rad_req_attr_t *ra;
+	struct rad_dict_attr_t *attr;
+
+	if (req->len + 2 + len >= REQ_LENGTH_MAX)
+		return -1;
+
+	attr = rad_dict_find_attr(name);
+	if (!attr)
+		return -1;
+	
+	ra = malloc(sizeof(*ra));
+	if (!ra)
+		return -1;
+
+	ra->attr = attr;
+	ra->val.string = stdrdup(val);
+	list_add_tail(&ra->entry, &req->pack.attrs);
+	req->len += 2 + len;
+
+	return 0;
+}
+
+int rad_req_add_val(struct rad_req_t *req, const char *name, const char *val, int len)
+{
+	struct rad_req_attr_t *ra;
+	struct rad_dict_attr_t *attr;
+	struct rad_dict_value_t *v;
+
+	if (req->len + 2 + len >= REQ_LENGTH_MAX)
+		return -1;
+
+	attr = rad_dict_find_attr(name);
+	if (!attr)
+		return -1;
+	
+	v = rad_dict_find_val(attr, val);
+	if (!v)
+		return -1;
+	
+	ra = malloc(sizeof(*ra));
+	if (!ra)
+		return -1;
+
+	ra->attr = attr;
+	ra->val = v->val;
+	list_add_tail(&ra->entry, &req->attrs.pack);
+	req->len += 2 + len;
+
+	return 0;
+}
+
+static int rad_req_read(struct triton_md_handler_t *h)
+{
+	struct rad_req_t *req = container_of(h, typeof(*req), hnd);
+
+	req->answer = rad_packet_recv(h->hnd.fd);
+}
+static void rd_req_timeout(struct triton_timer_t *t)
+{
+}
+
+int rad_req_wait(struct rad_req_t *req)
+{
+	if (triton_md_register_handler(req->rpd->ppp->ctrl->ctx, &req->hnd))
+		return -1;
+	if (triton_md_enable_handler(&req->hnd, MD_MODE_READ))
+		return -1;
+
+	req->timeout.period = conf_timeout * 1000;
+	if (triton_timer_add(&req->timeout))
+		return -1;
+
+	triton_ctx_schedule(&req->hnd, &req->timeout);
+
+	triton_timer_del(&req->timeout);
+	triton_md_unregister_handler(&req->hnd);
+
+	return 0;
+}
+
diff --git a/accel-pptpd/triton/CMakeLists.txt b/accel-pptpd/triton/CMakeLists.txt
index fd6b518..41339d9 100644
--- a/accel-pptpd/triton/CMakeLists.txt
+++ b/accel-pptpd/triton/CMakeLists.txt
@@ -7,6 +7,7 @@ SET(sources_c
 	loader.c
 	log.c
 	mempool.c
+	event.c
 )
 INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
 ADD_DEFINITIONS("-DUSE_SPINLOCK")
diff --git a/accel-pptpd/triton/event.c b/accel-pptpd/triton/event.c
new file mode 100644
index 0000000..17483ec
--- /dev/null
+++ b/accel-pptpd/triton/event.c
@@ -0,0 +1,103 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "triton_p.h"
+
+static int max_events = 1024;
+static struct _triton_event_t **events;
+
+struct event_handler_t
+{
+	struct list_head entry;
+	triton_event_func func;
+};
+
+int event_init(void)
+{
+	events = malloc(max_events * sizeof(void *));
+	if (!events) {
+		fprintf(stderr,"event:cann't allocate memory\n");
+		return -1;
+	}
+
+	memset(events, 0, max_events * sizeof(void *));
+
+	return 0;
+}
+
+int triton_event_register_handler(int ev_id, triton_event_func func)
+{
+	struct _triton_event_t *ev;
+	struct event_handler_t *h;
+
+	if (ev_id >= max_events)
+		return -1;
+
+	ev = events[ev_id];
+	if (!ev) {
+		ev = malloc(sizeof(*ev));
+		if (!ev) {
+			triton_log_error("event: out of memory\n");
+			return -1;
+		}
+		INIT_LIST_HEAD(&ev->handlers);
+		events[ev_id] = ev;
+	}
+
+	h = malloc(sizeof(*h));
+	if (!h) {
+		triton_log_error("event: out of memory\n");
+		return -1;
+	}
+
+	h->func = func;
+	list_add_tail(&h->entry, &ev->handlers);
+
+	return 0;
+}
+
+/*int triton_event_unregister_handler(int ev_id, triton_event_func func)
+{
+	struct _triton_event_t *ev;
+	struct event_handler_t *h;
+
+	if (ev_id >= max_events)
+		return -1;
+	
+	ev = events[ev_id];
+	if (!ev) {
+		return -1;
+	}
+
+	list_for_each_entry(h, &ev->handlers, entry) {
+		if (h->func == func) {
+			if (ev->in_progress)
+				h->func = NULL;
+			else {
+				list_del(&h->entry);
+				free(h);
+			}
+			return 0;
+		}
+	}
+
+	return -1;
+}*/
+
+void triton_event_fire(int ev_id, void *arg)
+{
+	struct _triton_event_t *ev;
+	struct event_handler_t *h;
+
+	if (ev_id >= max_events)
+		return;
+	
+	ev = events[ev_id];
+	if (!ev)
+		return;
+
+	list_for_each_entry(h, &ev->handlers, entry)
+		h->func(arg);
+}
+
diff --git a/accel-pptpd/triton/triton.h b/accel-pptpd/triton/triton.h
index 1009d67..d130d86 100644
--- a/accel-pptpd/triton/triton.h
+++ b/accel-pptpd/triton/triton.h
@@ -55,6 +55,10 @@ int triton_timer_add(struct triton_ctx_t *ctx, struct triton_timer_t*,int abs_ti
 int triton_timer_mod(struct triton_timer_t *,int abs_time);
 void triton_timer_del(struct triton_timer_t *);
 
+typedef void (*triton_event_func)(void *);
+int triton_event_register_handler(int ev_id, triton_event_func func);
+void triton_event_fire(int ev_id, void *arg);
+
 struct conf_sect_t *conf_get_section(const char *name);
 char *conf_get_opt(const char *sect, const char *name);
 
diff --git a/accel-pptpd/triton/triton_p.h b/accel-pptpd/triton/triton_p.h
index 0f7e716..c31efd6 100644
--- a/accel-pptpd/triton/triton_p.h
+++ b/accel-pptpd/triton/triton_p.h
@@ -57,6 +57,11 @@ struct _triton_timer_t
 	struct triton_timer_t *ud;
 };
 
+struct _triton_event_t
+{
+	struct list_head handlers;
+};
+
 typedef void * mempool_t;
 mempool_t *mempool_create(int size);
 void *mempool_alloc(mempool_t*);
@@ -64,9 +69,11 @@ void mempool_free(void*);
 
 int log_init(void);
 int md_init();
+int timer_init();
+int event_init();
+
 void md_run();
 void md_terminate();
-int timer_init();
 void timer_run();
 void timer_terminate();
 struct triton_ctx_t *default_ctx;
-- 
cgit v1.2.3