summaryrefslogtreecommitdiff
path: root/accel-pptpd
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pptpd')
-rw-r--r--accel-pptpd/cli/cli.c6
-rw-r--r--accel-pptpd/cli/telnet.c294
-rw-r--r--accel-pptpd/cli/telnet.h11
-rw-r--r--accel-pptpd/ppp/ppp.c2
4 files changed, 259 insertions, 54 deletions
diff --git a/accel-pptpd/cli/cli.c b/accel-pptpd/cli/cli.c
index 92df1c38..28416e14 100644
--- a/accel-pptpd/cli/cli.c
+++ b/accel-pptpd/cli/cli.c
@@ -86,7 +86,7 @@ int process_cmd(struct client_t *cln)
char *f[MAX_CMD_ITEMS];
int r, i, n, found = 0;
- n = split((char *)cln->recv_buf, f);
+ n = split((char *)cln->cmdline, f);
if (n >= 1 && !strcmp(f[0], "help")) {
list_for_each_entry(cmd1, &simple_cmd_list, entry) {
@@ -109,7 +109,7 @@ int process_cmd(struct client_t *cln)
}
if (i < cmd1->hdr_len)
continue;
- r = cmd1->exec((char *)cln->recv_buf, f, n, cln);
+ r = cmd1->exec((char *)cln->cmdline, f, n, cln);
switch (r) {
case CLI_CMD_EXIT:
telnet_disconnect(cln);
@@ -126,7 +126,7 @@ int process_cmd(struct client_t *cln)
}
list_for_each_entry(cmd2, &regexp_cmd_list, entry) {
- r = cmd2->exec((char *)cln->recv_buf, cln);
+ r = cmd2->exec((char *)cln->cmdline, cln);
switch (r) {
case CLI_CMD_EXIT:
telnet_disconnect(cln);
diff --git a/accel-pptpd/cli/telnet.c b/accel-pptpd/cli/telnet.c
index 377f5f04..c7b5f15c 100644
--- a/accel-pptpd/cli/telnet.c
+++ b/accel-pptpd/cli/telnet.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <fcntl.h>
#include <time.h>
+#include <ctype.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netinet/in.h>
@@ -18,10 +19,15 @@
#include "telnet.h"
-#define RECV_BUF_SIZE 4096
+#define RECV_BUF_SIZE 1024
#define BANNER "accel-pptp-1.3-rc1\r\n"
#define AUTH_FAILED "\r\nAuthentication failed\r\n"
+#define ESC_LEFT "[D"
+#define ESC_RIGHT "[C"
+#define ESC_UP "[A"
+#define ESC_DOWN "[B"
+
struct buffer_t
{
struct list_head entry;
@@ -35,6 +41,9 @@ static const char *conf_prompt = "accel-pptp# ";
static struct triton_context_t serv_ctx;
static struct triton_md_handler_t serv_hnd;
+static uint8_t *recv_buf;
+static uint8_t *temp_buf;
+
static void disconnect(struct client_t *cln)
{
struct buffer_t *b;
@@ -53,7 +62,13 @@ static void disconnect(struct client_t *cln)
_free(b);
}
- _free(cln->recv_buf);
+ while (!list_empty(&cln->history)) {
+ b = list_entry(cln->history.next, typeof(*b), entry);
+ list_del(&b->entry);
+ _free(b);
+ }
+
+ _free(cln->cmdline);
_free(cln);
}
@@ -102,6 +117,12 @@ static int send_banner(struct client_t *cln)
return telnet_send(cln, BANNER, sizeof(BANNER));
}
+static int send_config(struct client_t *cln)
+{
+ uint8_t buf[] = {IAC, WILL, TELOPT_ECHO, IAC, WILL, TELOPT_SGA, IAC, DONT, TELOPT_LINEMODE};
+ return telnet_send(cln, buf, sizeof(buf));
+}
+
static int send_password_request(struct client_t *cln)
{
uint8_t buf0[] = {IAC, WILL, TELOPT_ECHO};
@@ -130,65 +151,227 @@ static int send_prompt(struct client_t *cln)
log_debug("\n");
}*/
-static int process_data(struct client_t *cln)
+static int send_cmdline_tail(struct client_t *cln, int corr)
{
- int i, n;
- char *eof;
- uint8_t buf[] = {IAC, DONT, 0, '\r', '\n'};
+ if (telnet_send(cln, cln->cmdline + cln->cmdline_pos, cln->cmdline_len - cln->cmdline_pos))
+ return -1;
+
+ memset(temp_buf, '\b', cln->cmdline_len - cln->cmdline_pos - corr);
+
+ if (telnet_send(cln, temp_buf, cln->cmdline_len - cln->cmdline_pos - corr))
+ return -1;
+
+ return 0;
+}
+
+static int load_history(struct client_t *cln)
+{
+ struct buffer_t *b = list_entry(cln->history_pos, typeof(*b), entry);
+ if (b->size < cln->cmdline_len) {
+ memset(temp_buf, '\b', cln->cmdline_len - b->size);
+ memset(temp_buf + cln->cmdline_len - b->size, ' ', cln->cmdline_len - b->size);
+ if (telnet_send(cln, temp_buf, (cln->cmdline_len - b->size) * 2))
+ return -1;
+ }
+ if (telnet_send(cln, "\r", 1))
+ return -1;
+ if (send_prompt(cln))
+ return -1;
+ memcpy(cln->cmdline, b->buf, b->size);
+ cln->cmdline_pos = b->size;
+ cln->cmdline_len = b->size;
+ if (telnet_send(cln, b->buf, b->size))
+ return -1;
+
+ return 0;
+}
- eof = strstr((const char*)cln->recv_buf, "\r\n");
- if (!eof)
+static int telnet_input_char(struct client_t *cln, uint8_t c)
+{
+ uint8_t buf[] = {IAC, DONT, 0};
+ struct buffer_t *b;
+
+ if (c == '\n')
return 0;
- *eof = 0;
+ if (c == '\r') {
+ cln->cmdline[cln->cmdline_len] = 0;
- for (i = 0; i < cln->recv_pos; i++) {
- if (cln->recv_buf[i] == 0xff) {
- if (i >= cln->recv_pos - 1)
- return 0;
- if (cln->recv_buf[i + 1] == WILL || cln->recv_buf[i + 1] == WONT) {
- if (i >= cln->recv_pos - 2)
- return 0;
- buf[2] = cln->recv_buf[i + 2];
- if (telnet_send(cln, buf, 3))
+ if (cln->echo) {
+ if (telnet_send(cln, "\r\n", 2))
+ return -1;
+ }
+
+ if (!cln->auth) {
+ if (strcmp((char *)cln->cmdline, conf_passwd)) {
+ if (telnet_send(cln, AUTH_FAILED, sizeof(AUTH_FAILED)))
return -1;
+ disconnect(cln);
+ return -1;
}
-
- if (cln->recv_buf[i + 1] >= 251 && cln->recv_buf[i + 1] <= 254) {
- if (i >= cln->recv_pos - 2)
- return 0;
- n = 3;
- } else
- n = 2;
+ cln->auth = 1;
+ } else {
+ b = _malloc(sizeof(*b) + cln->cmdline_len);
+ memcpy(b->buf, cln->cmdline, cln->cmdline_len);
+ b->size = cln->cmdline_len;
+ list_add(&b->entry, cln->history.next);
+ cln->history_pos = cln->history.next;
- memmove(cln->recv_buf + i, cln->recv_buf + i + n, cln->recv_pos - i - n);
- cln->recv_pos -= n;
- i--;
+ if (process_cmd(cln))
+ return -1;
}
+
+ cln->cmdline_pos = 0;
+ cln->cmdline_len = 0;
+
+ return send_prompt(cln);
}
- if (!cln->auth) {
- if (strcmp((const char*)cln->recv_buf, conf_passwd)) {
- if (telnet_send(cln, AUTH_FAILED, sizeof(AUTH_FAILED)))
- return -1;
+ if (cln->telcmd) {
+ if (cln->cmdline_pos2 == RECV_BUF_SIZE - 1) {
+ log_error("cli: buffer overflow, dropping connection ...\n");
disconnect(cln);
return -1;
}
- cln->auth = 1;
- buf[1] = WONT;
- buf[2] = TELOPT_ECHO;
- if (telnet_send(cln, buf, 5))
+
+ cln->cmdline[cln->cmdline_pos2] = c;
+ cln->cmdline_pos2++;
+
+ if (cln->cmdline[cln->cmdline_len] >= WILL && cln->cmdline[cln->cmdline_len] <= DONT && cln->cmdline_pos2 - cln->cmdline_len != 2)
+ return 0;
+
+ switch (cln->cmdline[cln->cmdline_len]) {
+ case WILL:
+ case WONT:
+ buf[2] = c;
+ if (telnet_send(cln, buf, 3))
+ return -1;
+ break;
+ case DO:
+ if (c == TELOPT_ECHO)
+ cln->echo = 1;
+ break;
+ case SB:
+ if (c != SE)
+ return 0;
+ }
+
+ cln->telcmd = 0;
+ } else if (cln->esc) {
+ if (cln->cmdline_pos2 == RECV_BUF_SIZE - 1) {
+ log_error("cli: buffer overflow, dropping connection ...\n");
+ disconnect(cln);
return -1;
+ }
+ cln->cmdline[cln->cmdline_pos2] = c;
+ cln->cmdline_pos2++;
+
+ if (cln->cmdline_pos2 - cln->cmdline_len != 2)
+ return 0;
+
+ cln->esc = 0;
+
+ if (cln->auth) {
+ if (!memcmp(cln->cmdline + cln->cmdline_len, ESC_LEFT, 2)) {
+ if (cln->cmdline_pos) {
+ if (telnet_send(cln, "\b", 1))
+ return -1;
+ cln->cmdline_pos--;
+ }
+ } else if (!memcmp(cln->cmdline + cln->cmdline_len, ESC_RIGHT, 2)) {
+ if (cln->cmdline_pos < cln->cmdline_len) {
+ if (send_cmdline_tail(cln, 1))
+ return -1;
+ cln->cmdline_pos++;
+ }
+ } else if (!memcmp(cln->cmdline + cln->cmdline_len, ESC_UP, 2)) {
+ if (cln->history_pos == cln->history.next) {
+ b = list_entry(cln->history_pos, typeof(*b), entry);
+ memcpy(b->buf, cln->cmdline, cln->cmdline_len);
+ b->size = cln->cmdline_len;
+ }
+ cln->history_pos = cln->history_pos->next;
+ if (cln->history_pos == &cln->history) {
+ cln->history_pos = cln->history_pos->prev;
+ return 0;
+ }
+ if (load_history(cln))
+ return -1;
+ } else if (!memcmp(cln->cmdline + cln->cmdline_len, ESC_DOWN, 2)) {
+ cln->history_pos = cln->history_pos->prev;
+ if (cln->history_pos == &cln->history) {
+ cln->history_pos = cln->history_pos->next;
+ return 0;
+ }
+ if (load_history(cln))
+ return -1;
+ }
+ }
} else {
- if (process_cmd(cln))
- return -1;
- }
+ switch (c) {
+ case 0xff:
+ cln->cmdline_pos2 = cln->cmdline_len;
+ cln->telcmd = 1;
+ return 0;
+ case 0x1b:
+ cln->cmdline_pos2 = cln->cmdline_len;
+ cln->esc = 1;
+ return 0;
+ case 0x7f:
+ if (cln->cmdline_pos) {
+ if (cln->cmdline_pos < cln->cmdline_len) {
+ memmove(cln->cmdline + cln->cmdline_pos - 1, cln->cmdline + cln->cmdline_pos, cln->cmdline_len - cln->cmdline_pos);
+
+ cln->cmdline[cln->cmdline_len - 1] = ' ';
+
+ if (telnet_send(cln, "\b", 1))
+ return -1;
+
+ cln->cmdline_pos--;
+
+ if (send_cmdline_tail(cln, 0))
+ return -1;
+ } else {
+ buf[0] = '\b';
+ buf[1] = ' ';
+ buf[2] = '\b';
+ if (telnet_send(cln, buf, 3))
+ return -1;
+ cln->cmdline_pos--;
+ }
+
+ cln->cmdline_len--;
+ }
+ return 0;
+ }
- if (send_prompt(cln))
- return -1;
+ if (isprint(c)) {
+ if (cln->cmdline_len == RECV_BUF_SIZE - 1)
+ return 0;
- cln->recv_pos = 0;
+ if (cln->cmdline_pos < cln->cmdline_len)
+ memmove(cln->cmdline + cln->cmdline_pos + 1, cln->cmdline + cln->cmdline_pos, cln->cmdline_len - cln->cmdline_pos);
+ cln->cmdline[cln->cmdline_pos] = c;
+ cln->cmdline_pos++;
+ cln->cmdline_len++;
+
+ if (cln->echo) {
+ if (!cln->auth) {
+ if (telnet_send(cln, "*", 1))
+ return -1;
+ } else {
+ if (telnet_send(cln, &c, 1))
+ return -1;
+ }
+ }
+
+ if (cln->cmdline_pos < cln->cmdline_len) {
+ if (send_cmdline_tail(cln, 0))
+ return -1;
+ }
+ }
+ }
return 0;
}
@@ -196,10 +379,10 @@ static int process_data(struct client_t *cln)
static int cln_read(struct triton_md_handler_t *h)
{
struct client_t *cln = container_of(h, typeof(*cln), hnd);
- int n;
+ int i, n;
while (1) {
- n = read(h->fd, cln->recv_buf + cln->recv_pos, RECV_BUF_SIZE - cln->recv_pos);
+ n = read(h->fd, recv_buf, RECV_BUF_SIZE);
if (n == 0) {
disconnect(cln);
return 0;
@@ -211,9 +394,10 @@ static int cln_read(struct triton_md_handler_t *h)
}
/*log_debug("cli: read(%i): ", n);
print_buf(cln->recv_buf + cln->recv_pos, n);*/
- cln->recv_pos += n;
- if (process_data(cln))
- return -1;
+ for (i = 0; i < n; i++) {
+ if (telnet_input_char(cln, recv_buf[i]))
+ return -1;
+ }
}
return 0;
@@ -258,6 +442,7 @@ static int serv_read(struct triton_md_handler_t *h)
socklen_t size = sizeof(addr);
int sock;
struct client_t *conn;
+ struct buffer_t *b;
while(1) {
sock = accept(h->fd, (struct sockaddr *)&addr, &size);
@@ -281,8 +466,14 @@ static int serv_read(struct triton_md_handler_t *h)
conn->hnd.fd = sock;
conn->hnd.read = cln_read;
conn->hnd.write = cln_write;
- conn->recv_buf = _malloc(RECV_BUF_SIZE);
+ conn->cmdline = _malloc(RECV_BUF_SIZE);
INIT_LIST_HEAD(&conn->xmit_queue);
+ INIT_LIST_HEAD(&conn->history);
+
+ b = _malloc(sizeof(*b) + RECV_BUF_SIZE);
+ b->size = 0;
+ list_add_tail(&b->entry, &conn->history);
+ conn->history_pos = conn->history.next;
triton_md_register_handler(&serv_ctx, &conn->hnd);
triton_md_enable_handler(&conn->hnd,MD_MODE_READ);
@@ -290,6 +481,9 @@ static int serv_read(struct triton_md_handler_t *h)
if (send_banner(conn))
continue;
+ if (send_config(conn))
+ continue;
+
if (conf_passwd)
send_password_request(conn);
else {
@@ -332,6 +526,7 @@ static void start_server(const char *host, int port)
else
addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4);
if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
log_emerg("cli: failed to bind socket: %s\n", strerror(errno));
close(serv_hnd.fd);
@@ -384,6 +579,9 @@ static void __init init(void)
if (opt)
conf_prompt = opt;
+ recv_buf = malloc(RECV_BUF_SIZE);
+ temp_buf = malloc(RECV_BUF_SIZE);
+
start_server(host, port);
}
diff --git a/accel-pptpd/cli/telnet.h b/accel-pptpd/cli/telnet.h
index eab156c5..adbb7b90 100644
--- a/accel-pptpd/cli/telnet.h
+++ b/accel-pptpd/cli/telnet.h
@@ -5,12 +5,19 @@ struct client_t
{
struct list_head entry;
struct triton_md_handler_t hnd;
- uint8_t *recv_buf;
- int recv_pos;
struct list_head xmit_queue;
struct buffer_t *xmit_buf;
int xmit_pos;
+ struct list_head history;
+ struct list_head *history_pos;
+ uint8_t *cmdline;
+ int cmdline_pos;
+ int cmdline_pos2;
+ int cmdline_len;
int auth:1;
+ int echo:1;
+ int telcmd:1;
+ int esc:1;
};
int telnet_send(struct client_t *cln, const void *buf, int size);
diff --git a/accel-pptpd/ppp/ppp.c b/accel-pptpd/ppp/ppp.c
index 5b1dd7cd..ceefc1ea 100644
--- a/accel-pptpd/ppp/ppp.c
+++ b/accel-pptpd/ppp/ppp.c
@@ -24,7 +24,7 @@
int __export conf_ppp_verbose;
-pthread_rwlock_t __export ppp_lock;
+pthread_rwlock_t __export ppp_lock = PTHREAD_RWLOCK_INITIALIZER;
__export LIST_HEAD(ppp_list);
static LIST_HEAD(layers);