diff options
Diffstat (limited to 'accel-pppd/cli')
-rw-r--r-- | accel-pppd/cli/CMakeLists.txt | 1 | ||||
-rw-r--r-- | accel-pppd/cli/cli.c | 227 | ||||
-rw-r--r-- | accel-pppd/cli/cli.h | 48 | ||||
-rw-r--r-- | accel-pppd/cli/cli_p.h | 22 | ||||
-rw-r--r-- | accel-pppd/cli/show_sessions.c | 434 | ||||
-rw-r--r-- | accel-pppd/cli/std_cmd.c | 324 | ||||
-rw-r--r-- | accel-pppd/cli/tcp.c | 371 | ||||
-rw-r--r-- | accel-pppd/cli/telnet.c | 757 |
8 files changed, 2184 insertions, 0 deletions
diff --git a/accel-pppd/cli/CMakeLists.txt b/accel-pppd/cli/CMakeLists.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/accel-pppd/cli/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/accel-pppd/cli/cli.c b/accel-pppd/cli/cli.c new file mode 100644 index 00000000..5009a742 --- /dev/null +++ b/accel-pppd/cli/cli.c @@ -0,0 +1,227 @@ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "triton.h" + +#include "cli.h" +#include "cli_p.h" +#include "log.h" +#include "events.h" + +#include "memdebug.h" + +#define MAX_CMD_ITEMS 100 +#define MSG_SYNTAX_ERROR "syntax error\r\n" +#define MSG_INVAL_ERROR "invalid argument\r\n" +#define MSG_UNKNOWN_CMD "command unknown\r\n" + +char *conf_cli_passwd; +static const char *def_cli_prompt = "accel-ppp"; +char *conf_cli_prompt; + +static LIST_HEAD(simple_cmd_list); +static LIST_HEAD(regexp_cmd_list); + +void __export cli_register_simple_cmd(struct cli_simple_cmd_t *cmd) +{ + list_add_tail(&cmd->entry, &simple_cmd_list); +} + +void __export cli_register_simple_cmd2( + int (*exec)(const char *cmd, char * const *fields, int fields_cnt, void *client), + void (*help)(char * const *fields, int fields_cnt, void *client), + int hdr_len, + ... + ) +{ + struct cli_simple_cmd_t *c; + int i; + va_list ap; + + va_start(ap, hdr_len); + + c = malloc(sizeof(*c)); + memset(c, 0, sizeof(*c)); + + c->exec = exec; + c->help = help; + c->hdr_len = hdr_len; + c->hdr = malloc(hdr_len * sizeof(void*)); + + for (i = 0; i < hdr_len; i++) + c->hdr[i] = va_arg(ap, char *); + + list_add_tail(&c->entry, &simple_cmd_list); +} + +void __export cli_register_regexp_cmd(struct cli_regexp_cmd_t *cmd) +{ + int err; + cmd->re = pcre_compile2(cmd->pattern, cmd->options, &err, NULL, NULL, NULL); + if (!cmd->re) { + log_emerg("cli: failed to compile regexp %s: %i\n", cmd->pattern, err); + _exit(EXIT_FAILURE); + } + list_add_tail(&cmd->entry, &simple_cmd_list); +} + +int __export cli_send(void *client, const char *data) +{ + struct cli_client_t *cln = (struct cli_client_t *)client; + + return cln->send(cln, data, strlen(data)); +} + +int __export cli_sendv(void *client, const char *fmt, ...) +{ + struct cli_client_t *cln = (struct cli_client_t *)client; + int r; + + va_list ap; + va_start(ap, fmt); + r = cln->sendv(cln, fmt, ap); + va_end(ap); + + return r; +} + + +static char *skip_word(char *ptr) +{ + for(; *ptr; ptr++) + if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n') + break; + return ptr; +} +static char *skip_space(char *ptr) +{ + for(; *ptr; ptr++) + if (*ptr != ' ' && *ptr != '\t') + break; + return ptr; +} +static int split(char *buf, char **ptr) +{ + int i; + + ptr[0] = buf; + + for (i = 1; i <= MAX_CMD_ITEMS; i++) { + buf = skip_word(buf); + if (!*buf) + return i; + + *buf = 0; + + buf = skip_space(buf + 1); + if (!*buf) + return i; + + ptr[i] = buf; + } + + buf = skip_word(buf); + *buf = 0; + + return i; +} + +int cli_process_cmd(struct cli_client_t *cln) +{ + struct cli_simple_cmd_t *cmd1; + struct cli_regexp_cmd_t *cmd2; + char *f[MAX_CMD_ITEMS]; + int r, i, n, found = 0; + + n = split((char *)cln->cmdline, f); + + if (n >= 1 && !strcmp(f[0], "help")) { + list_for_each_entry(cmd1, &simple_cmd_list, entry) + if (cmd1->help) + cmd1->help(f, n, cln); + + list_for_each_entry(cmd2, ®exp_cmd_list, entry) + if (cmd2->help) + cmd1->help(f, n, cln); + + return 0; + } + + list_for_each_entry(cmd1, &simple_cmd_list, entry) { + if (cmd1->hdr_len && n >= cmd1->hdr_len) { + for (i = 0; i < cmd1->hdr_len; i++) { + if (strcmp(cmd1->hdr[i], f[i])) + break; + } + if (i < cmd1->hdr_len) + continue; + r = cmd1->exec((char *)cln->cmdline, f, n, cln); + switch (r) { + case CLI_CMD_EXIT: + cln->disconnect(cln); + case CLI_CMD_FAILED: + return -1; + case CLI_CMD_SYNTAX: + cli_send(cln, MSG_SYNTAX_ERROR); + return 0; + case CLI_CMD_INVAL: + cli_send(cln, MSG_INVAL_ERROR); + return 0; + case CLI_CMD_OK: + found = 1; + } + } + } + + list_for_each_entry(cmd2, ®exp_cmd_list, entry) { + r = cmd2->exec((char *)cln->cmdline, cln); + switch (r) { + case CLI_CMD_EXIT: + cln->disconnect(cln); + case CLI_CMD_FAILED: + return 0; + case CLI_CMD_SYNTAX: + cli_send(cln, MSG_SYNTAX_ERROR); + return 0; + case CLI_CMD_OK: + found = 1; + } + } + + if (!found) { + if (cli_send(cln, MSG_UNKNOWN_CMD)) + return -1; + } + + return 0; +} + +static void load_config(void) +{ + const char *opt; + + if (conf_cli_passwd) + _free(conf_cli_passwd); + opt = conf_get_opt("cli", "password"); + if (opt) + conf_cli_passwd = _strdup(opt); + else + conf_cli_passwd = NULL; + + if (conf_cli_prompt && conf_cli_prompt != def_cli_prompt) + _free(conf_cli_prompt); + opt = conf_get_opt("cli", "prompt"); + if (opt) + conf_cli_prompt = _strdup(opt); + else + conf_cli_prompt = (char *)def_cli_prompt; +} + +static void __init init(void) +{ + load_config(); + + triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); +} diff --git a/accel-pppd/cli/cli.h b/accel-pppd/cli/cli.h new file mode 100644 index 00000000..cdceb2fa --- /dev/null +++ b/accel-pppd/cli/cli.h @@ -0,0 +1,48 @@ +#ifndef __CLI_H +#define __CLI_H + +#include <pcre.h> +#include <list.h> + +#define CLI_CMD_OK 0 +#define CLI_CMD_FAILED -1 +#define CLI_CMD_EXIT -2 +#define CLI_CMD_SYNTAX 1 +#define CLI_CMD_INVAL 2 + +struct cli_simple_cmd_t +{ + struct list_head entry; + int hdr_len; + const char **hdr; + int (*exec)(const char *cmd, char * const *fields, int fields_cnt, void *client); + void (*help)(char * const *fields, int field_cnt, void *client); +}; + +struct cli_regexp_cmd_t +{ + struct list_head entry; + pcre *re; + const char *pattern; + int options; + int (*exec)(const char *cmd, void *client); + int (*help)(char * const *fields, int field_cnt, void *client); +}; + +struct ppp_t; + +void cli_register_simple_cmd(struct cli_simple_cmd_t *cmd); +void cli_register_simple_cmd2( + int (*exec)(const char *cmd, char * const *fields, int fields_cnt, void *client), + void (*help)(char * const *fields, int fields_cnt, void *client), + int hdr_len, + ... + ); +void cli_register_regexp_cmd(struct cli_regexp_cmd_t *cmd); +void cli_show_ses_register(const char *name, const char *desc, void (*print)(const struct ppp_t *ppp, char *buf)); + +int cli_send(void *client, const char *data); +int cli_sendv(void *client, const char *fmt, ...); + +#endif + diff --git a/accel-pppd/cli/cli_p.h b/accel-pppd/cli/cli_p.h new file mode 100644 index 00000000..0fcba309 --- /dev/null +++ b/accel-pppd/cli/cli_p.h @@ -0,0 +1,22 @@ +#ifndef __CLI_P_H +#define __CLI_P_H + +#include <stdarg.h> + +#include "triton.h" + +struct cli_client_t +{ + uint8_t *cmdline; + int (*send)(struct cli_client_t *, const void *buf, int size); + int (*sendv)(struct cli_client_t *, const char *fmt, va_list ap); + void (*disconnect)(struct cli_client_t *); +}; + +int cli_process_cmd(struct cli_client_t *cln); + +extern char *conf_cli_passwd; +extern char *conf_cli_prompt; + +#endif + diff --git a/accel-pppd/cli/show_sessions.c b/accel-pppd/cli/show_sessions.c new file mode 100644 index 00000000..90aeb3fc --- /dev/null +++ b/accel-pppd/cli/show_sessions.c @@ -0,0 +1,434 @@ +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <arpa/inet.h> + +#include "triton.h" +#include "events.h" +#include "ppp.h" +#include "cli.h" +#include "utils.h" +#include "log.h" +#include "memdebug.h" + +#define CELL_SIZE 128 +#define DEF_COLUMNS "ifname,username,calling-sid,ip,rate-limit,type,state,uptime" + +struct column_t +{ + struct list_head entry; + const char *name; + const char *desc; + void (*print)(const struct ppp_t *ppp, char *buf); +}; + +struct col_t +{ + struct list_head entry; + struct column_t *column; + int width; +}; + +struct row_t +{ + struct list_head entry; + char *match_key; + char *order_key; + struct list_head cell_list; +}; + +struct cell_t +{ + struct list_head entry; + struct col_t *col; + char buf[CELL_SIZE + 1]; +}; + +static LIST_HEAD(col_list); + +void __export cli_show_ses_register(const char *name, const char *desc, void (*print)(const struct ppp_t *ppp, char *buf)) +{ + struct column_t *c = malloc(sizeof(*c)); + c->name = name; + c->desc = desc; + c->print = print; + list_add_tail(&c->entry, &col_list); +} + +static void show_ses_help(char * const *f, int f_cnt, void *cli) +{ + struct column_t *col; + char buf[129]; + + cli_send(cli, "show sessions [columns] [order <column>] [match <column> <regexp>] - shows sessions\r\n"); + cli_send(cli, "\tcolumns:\r\n"); + + list_for_each_entry(col, &col_list, entry) { + snprintf(buf, 128, "\t\t%s - %s\r\n", col->name, col->desc); + cli_send(cli, buf); + } +} + +static struct column_t *find_column(const char *name) +{ + struct column_t *col; + + list_for_each_entry(col, &col_list, entry) { + if (strcmp(col->name, name)) + continue; + return col; + } + + return NULL; +} + +static void free_row(struct row_t *row) +{ + struct cell_t *cell; + + while (!list_empty(&row->cell_list)) { + cell = list_entry(row->cell_list.next, typeof(*cell), entry); + list_del(&cell->entry); + _free(cell); + } + + _free(row); +} + +static void insert_row(struct list_head *list, struct row_t *row) +{ + struct row_t *row2, *row3; + + row3 = NULL; + list_for_each_entry(row2, list, entry) { + if (strcmp(row->order_key, row2->order_key) <= 0) { + row3 = row2; + break; + } + } + if (row3) + list_add_tail(&row->entry, &row3->entry); + else + list_add_tail(&row->entry, list); +} + +static int show_ses_exec(const char *cmd, char * const *f, int f_cnt, void *cli) +{ + char *columns = NULL; + struct column_t *match_key = NULL; + char *match_pattern = NULL; + struct column_t *order_key = NULL; + pcre *re = NULL; + const char *pcre_err; + int pcre_offset; + struct column_t *column; + struct col_t *col; + struct row_t *row; + struct cell_t *cell; + char *ptr1, *ptr2; + int i, n, total_width, def_columns = 0; + struct ppp_t *ppp; + char *buf = NULL; + LIST_HEAD(c_list); + LIST_HEAD(r_list); + LIST_HEAD(t_list); + + for (i = 2; i < f_cnt; i++) { + if (!strcmp(f[i], "order")) { + if (i == f_cnt - 1) + return CLI_CMD_SYNTAX; + order_key = find_column(f[++i]); + if (!order_key) { + cli_sendv(cli, "unknown column %s\r\n", f[i]); + return CLI_CMD_OK; + } + } else if (!strcmp(f[i], "match")) { + if (i == f_cnt - 2) + return CLI_CMD_SYNTAX; + match_key = find_column(f[++i]); + if (!match_key) { + cli_sendv(cli, "unknown column %s\r\n", f[i]); + return CLI_CMD_OK; + } + match_pattern = f[++i]; + } else if (!columns) + columns = f[i]; + else + return CLI_CMD_SYNTAX; + } + + if (match_key) { + re = pcre_compile2(match_pattern, 0, NULL, &pcre_err, &pcre_offset, NULL); + if (!re) { + cli_sendv(cli, "match: %s at %i\r\n", pcre_err, pcre_offset); + return CLI_CMD_OK; + } + } + + if (!columns) { + columns = DEF_COLUMNS; + def_columns = 1; + } + + columns = _strdup(columns); + ptr1 = columns; + while (1) { + ptr2 = strchr(ptr1, ','); + if (ptr2) + *ptr2 = 0; + column = find_column(ptr1); + if (column) { + col = _malloc(sizeof(*col)); + col->column = column; + col->width = strlen(column->name); + list_add_tail(&col->entry, &c_list); + } else { + if (!def_columns) { + cli_sendv(cli, "unknown column %s\r\n", ptr1); + _free(columns); + goto out; + } + } + if (!ptr2) + break; + ptr1 = ptr2 + 1; + } + _free(columns); + + pthread_rwlock_rdlock(&ppp_lock); + list_for_each_entry(ppp, &ppp_list, entry) { + row = _malloc(sizeof(*row)); + if (!row) + goto oom; + memset(row, 0, sizeof(*row)); + INIT_LIST_HEAD(&row->cell_list); + if (match_key || order_key) + list_add_tail(&row->entry, &t_list); + else + list_add_tail(&row->entry, &r_list); + list_for_each_entry(col, &c_list, entry) { + cell = _malloc(sizeof(*cell)); + if (!cell) + goto oom; + cell->col = col; + list_add_tail(&cell->entry, &row->cell_list); + col->column->print(ppp, cell->buf); + n = strlen(cell->buf); + if (n > col->width) + col->width = n; + if (col->column == order_key) + row->order_key = cell->buf; + if (col->column == match_key) + row->match_key = cell->buf; + } + } + pthread_rwlock_unlock(&ppp_lock); + + if (order_key || match_key) { + while(!list_empty(&t_list)) { + row = list_entry(t_list.next, typeof(*row), entry); + list_del(&row->entry); + if (match_key) { + if (pcre_exec(re, NULL, row->match_key, strlen(row->match_key), 0, 0, NULL, 0) < 0) { + free_row(row); + continue; + } + } + if (order_key) + insert_row(&r_list, row); + else + list_add_tail(&row->entry, &r_list); + } + } + + total_width = -1; + list_for_each_entry(col, &c_list, entry) + total_width += col->width + 3; + + buf = _malloc(total_width + 3); + if (!buf) + goto oom; + + ptr1 = buf; + list_for_each_entry(col, &c_list, entry) { + n = strlen(col->column->name); + if (col->width > n + 1) { + ptr2 = ptr1; + memset(ptr1, ' ', col->width/2 - n/2 + 1); + ptr1 += col->width/2 - n/2 + 1; + sprintf(ptr1, "%s", col->column->name); + ptr1 = strchr(ptr1, 0); + memset(ptr1, ' ', col->width + 2 - (ptr1 - ptr2)); + ptr1 += col->width + 2 - (ptr1 - ptr2); + *ptr1 = '|'; + ptr1++; + } else if (col->width > n) { + sprintf(ptr1, " %s |", col->column->name); + ptr1 = strchr(ptr1, 0); + } else { + sprintf(ptr1, " %s |", col->column->name); + ptr1 = strchr(ptr1, 0); + } + } + + strcpy(ptr1 - 1, "\r\n"); + cli_send(cli, buf); + + ptr1 = buf; + list_for_each_entry(col, &c_list, entry) { + memset(ptr1, '-', col->width + 2); + ptr1 += col->width + 2; + *ptr1 = '+'; + ptr1++; + } + + strcpy(ptr1 - 1, "\r\n"); + cli_send(cli, buf); + + while (!list_empty(&r_list)) { + row = list_entry(r_list.next, typeof(*row), entry); + ptr1 = buf; + list_for_each_entry(cell, &row->cell_list, entry) { + ptr2 = ptr1; + sprintf(ptr1, " %s ", cell->buf); + ptr1 = strchr(ptr1, 0); + n = ptr1 - ptr2; + if (n - 2 < cell->col->width) { + memset(ptr1, ' ', cell->col->width + 2 - (ptr1 - ptr2)); + ptr1 += cell->col->width + 2 - (ptr1 - ptr2); + } + *ptr1 = '|'; + ptr1++; + } + strcpy(ptr1 - 1, "\r\n"); + cli_send(cli, buf); + list_del(&row->entry); + free_row(row); + } + + _free(buf); + +out: + while (!list_empty(&c_list)) { + col = list_entry(c_list.next, typeof(*col), entry); + list_del(&col->entry); + _free(col); + } + + if (re) + pcre_free(re); + + return CLI_CMD_OK; + +oom: + if (buf) + _free(buf); + + while (!list_empty(&t_list)) { + row = list_entry(t_list.next, typeof(*row), entry); + list_del(&row->entry); + free_row(row); + } + cli_send(cli, "out of memory"); + goto out; +} + +static void print_ifname(const struct ppp_t *ppp, char *buf) +{ + snprintf(buf, CELL_SIZE, "%s", ppp->ifname); +} + +static void print_username(const struct ppp_t *ppp, char *buf) +{ + if (ppp->username) + snprintf(buf, CELL_SIZE, "%s", ppp->username); +} + +static void print_ip(const struct ppp_t *ppp, char *buf) +{ + char str[17]; + u_inet_ntoa(ppp->peer_ipaddr, str); + sprintf(buf, "%s", str); +} + +static void print_type(const struct ppp_t *ppp, char *buf) +{ + snprintf(buf, CELL_SIZE, "%s", ppp->ctrl->name); +} + +static void print_state(const struct ppp_t *ppp, char *buf) +{ + char *state; + switch (ppp->state) { + case PPP_STATE_STARTING: + state = "start"; + break; + case PPP_STATE_ACTIVE: + state = "active"; + break; + case PPP_STATE_FINISHING: + state = "finish"; + break; + default: + state = "unk"; + } + sprintf(buf, "%s", state); +} + +static void print_uptime(const struct ppp_t *ppp, char *buf) +{ + time_t uptime; + int day,hour,min,sec; + char time_str[14]; + + if (ppp->stop_time) + uptime = ppp->stop_time - ppp->start_time; + else { + time(&uptime); + uptime -= ppp->start_time; + } + + day = uptime/ (24*60*60); uptime %= (24*60*60); + hour = uptime / (60*60); uptime %= (60*60); + min = uptime / 60; + sec = uptime % 60; + if (day) + snprintf(time_str, 13, "%i.%02i:%02i:%02i", day, hour, min, sec); + else + snprintf(time_str, 13, "%02i:%02i:%02i", hour, min, sec); + + sprintf(buf, "%s", time_str); +} + +static void print_calling_sid(const struct ppp_t *ppp, char *buf) +{ + snprintf(buf, CELL_SIZE, "%s", ppp->ctrl->calling_station_id); +} + +static void print_called_sid(const struct ppp_t *ppp, char *buf) +{ + snprintf(buf, CELL_SIZE, "%s", ppp->ctrl->called_station_id); +} + +static void print_sid(const struct ppp_t *ppp, char *buf) +{ + snprintf(buf, CELL_SIZE, "%s", ppp->sessionid); +} + +void __init init(void) +{ + cli_register_simple_cmd2(show_ses_exec, show_ses_help, 2, "show", "sessions"); + + cli_show_ses_register("ifname", "interface name", print_ifname); + cli_show_ses_register("username", "user name", print_username); + cli_show_ses_register("ip", "IP address", print_ip); + cli_show_ses_register("type", "VPN type", print_type); + cli_show_ses_register("state", "state of session", print_state); + cli_show_ses_register("uptime", "uptime", print_uptime); + cli_show_ses_register("calling-sid", "calling station id", print_calling_sid); + cli_show_ses_register("called-sid", "called station id", print_called_sid); + cli_show_ses_register("sid", "session id", print_sid); +} + diff --git a/accel-pppd/cli/std_cmd.c b/accel-pppd/cli/std_cmd.c new file mode 100644 index 00000000..a49bbce6 --- /dev/null +++ b/accel-pppd/cli/std_cmd.c @@ -0,0 +1,324 @@ +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <arpa/inet.h> + +#include "triton.h" +#include "events.h" +#include "ppp.h" +#include "cli.h" +#include "utils.h" +#include "log.h" +#include "memdebug.h" + +static int show_stat_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) +{ + time_t dt; + int day,hour; + char statm_fname[128]; + FILE *f; + unsigned long vmsize = 0, vmrss = 0; + unsigned long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; + + sprintf(statm_fname, "/proc/%i/statm", getpid()); + f = fopen(statm_fname, "r"); + if (f) { + fscanf(f, "%lu %lu", &vmsize, &vmrss); + fclose(f); + } + + time(&dt); + dt -= triton_stat.start_time; + day = dt / (60 * 60 * 24); + dt %= 60 * 60 * 24; + hour = dt / (60 * 60); + dt %= 60 * 60; + + cli_sendv(client, "uptime: %i.%02i:%02i:%02i\r\n", day, hour, dt / 60, dt % 60); + cli_sendv(client, "cpu: %i%%\r\n", triton_stat.cpu); + cli_sendv(client, "mem(rss/virt): %lu/%lu kB\r\n", vmrss * page_size_kb, vmsize * page_size_kb); + cli_send(client, "core:\r\n"); + cli_sendv(client, " mempool_allocated: %u\r\n", triton_stat.mempool_allocated); + cli_sendv(client, " mempool_available: %u\r\n", triton_stat.mempool_available); + cli_sendv(client, " thread_count: %u\r\n", triton_stat.thread_count); + cli_sendv(client, " thread_active: %u\r\n", triton_stat.thread_active); + cli_sendv(client, " context_count: %u\r\n", triton_stat.context_count); + cli_sendv(client, " context_sleeping: %u\r\n", triton_stat.context_sleeping); + cli_sendv(client, " context_pending: %u\r\n", triton_stat.context_pending); + cli_sendv(client, " md_handler_count: %u\r\n", triton_stat.md_handler_count); + cli_sendv(client, " md_handler_pending: %u\r\n", triton_stat.md_handler_pending); + cli_sendv(client, " timer_count: %u\r\n", triton_stat.timer_count); + cli_sendv(client, " timer_pending: %u\r\n", triton_stat.timer_pending); + +//=========== + cli_send(client, "ppp:\r\n"); + cli_sendv(client, " staring: %u\r\n", ppp_stat.starting); + cli_sendv(client, " active: %u\r\n", ppp_stat.active); + cli_sendv(client, " finishing: %u\r\n", ppp_stat.finishing); + + return CLI_CMD_OK; +} + +static void show_stat_help(char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "show stat - shows various statistics information\r\n"); +} +//============================= + +static int exit_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) +{ + return CLI_CMD_EXIT; +} + +static void exit_help(char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "exit - exit cli\r\n"); +} + +//============================= + +static void ppp_terminate_soft(struct ppp_t *ppp) +{ + ppp_terminate(ppp, TERM_NAS_REQUEST, 0); +} + +static void ppp_terminate_hard(struct ppp_t *ppp) +{ + ppp_terminate(ppp, TERM_NAS_REQUEST, 1); +} + +static int terminate_exec1(char * const *f, int f_cnt, void *cli) +{ + struct ppp_t *ppp; + int hard = 0; + pcre *re; + const char *pcre_err; + int pcre_offset; + + if (f_cnt == 5) { + if (!strcmp(f[4], "hard")) + hard = 1; + else if (strcmp(f[4], "soft")) + return CLI_CMD_SYNTAX; + } else if (f_cnt != 4) + return CLI_CMD_SYNTAX; + + re = pcre_compile2(f[3], 0, NULL, &pcre_err, &pcre_offset, NULL); + if (!re) { + cli_sendv(cli, "match: %s at %i\r\n", pcre_err, pcre_offset); + return CLI_CMD_OK; + } + + pthread_rwlock_rdlock(&ppp_lock); + list_for_each_entry(ppp, &ppp_list, entry) { + if (pcre_exec(re, NULL, ppp->username, strlen(ppp->username), 0, 0, NULL, 0) < 0) + continue; + if (hard) + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_hard, ppp); + else + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_soft, ppp); + } + pthread_rwlock_unlock(&ppp_lock); + + pcre_free(re); + + return CLI_CMD_OK; +} + +static int terminate_exec2(int key, char * const *f, int f_cnt, void *cli) +{ + struct ppp_t *ppp; + int hard = 0; + in_addr_t ipaddr = 0; + + if (f_cnt == 4) { + if (!strcmp(f[3], "hard")) + hard = 1; + else if (strcmp(f[3], "soft")) + return CLI_CMD_SYNTAX; + } else if (f_cnt != 3) + return CLI_CMD_SYNTAX; + + if (key == 1) + ipaddr = inet_addr(f[2]); + + pthread_rwlock_rdlock(&ppp_lock); + list_for_each_entry(ppp, &ppp_list, entry) { + switch (key) { + case 0: + if (strcmp(ppp->username, f[2])) + continue; + break; + case 1: + if (ppp->peer_ipaddr != ipaddr) + continue; + break; + case 2: + if (strcmp(ppp->ctrl->calling_station_id, f[2])) + continue; + break; + case 3: + if (strcmp(ppp->sessionid, f[2])) + continue; + break; + case 4: + if (strcmp(ppp->ifname, f[2])) + continue; + break; + } + if (hard) + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_hard, ppp); + else + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_soft, ppp); + break; + } + pthread_rwlock_unlock(&ppp_lock); + + return CLI_CMD_OK; +} + +static int terminate_exec(const char *cmd, char * const *fields, int fields_cnt, void *client) +{ + struct ppp_t *ppp; + int hard = 0; + + if (fields_cnt == 1) + return CLI_CMD_SYNTAX; + + if (!strcmp(fields[1], "match") && fields_cnt > 3 && !strcmp(fields[2], "username")) + return terminate_exec1(fields, fields_cnt, client); + else if (!strcmp(fields[1], "username")) + return terminate_exec2(0, fields, fields_cnt, client); + else if (!strcmp(fields[1], "ip")) + return terminate_exec2(1, fields, fields_cnt, client); + else if (!strcmp(fields[1], "csid")) + return terminate_exec2(2, fields, fields_cnt, client); + else if (!strcmp(fields[1], "sid")) + return terminate_exec2(3, fields, fields_cnt, client); + else if (!strcmp(fields[1], "if")) + return terminate_exec2(4, fields, fields_cnt, client); + else if (strcmp(fields[1], "all")) + return CLI_CMD_SYNTAX; + + if (fields_cnt == 3) { + if (!strcmp(fields[2], "hard")) + hard = 1; + else if (strcmp(fields[2], "soft")) + return CLI_CMD_SYNTAX; + } else if (fields_cnt != 2) + return CLI_CMD_SYNTAX; + + pthread_rwlock_rdlock(&ppp_lock); + list_for_each_entry(ppp, &ppp_list, entry) { + if (hard) + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_hard, ppp); + else + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_soft, ppp); + } + pthread_rwlock_unlock(&ppp_lock); + + return CLI_CMD_OK; +} + +static void terminate_help(char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "terminate if <interface> [soft|hard]- terminate session by interface name\r\n"); + cli_send(client, "\t[match] username <username> [soft|hard]- terminate session by username\r\n"); + cli_send(client, "\tip <addresss> [soft|hard]- terminate session by ip address\r\n"); + cli_send(client, "\tcsid <id> [soft|hard]- terminate session by calling station id\r\n"); + cli_send(client, "\tsid <id> [soft|hard]- terminate session by session id\r\n"); + cli_send(client, "\tall [soft|hard]- terminate all sessions\r\n"); +} + +//============================= + +static void shutdown_help(char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "shutdown [soft|hard|cancel]- shutdown daemon\r\n"); + cli_send(client, "\t\tdefault action - send termination signals to all clients and wait everybody disconnects\r\n"); + cli_send(client, "\t\tsoft - wait until all clients disconnects, don't accept new connections\r\n"); + cli_send(client, "\t\thard - shutdown now, don't wait anything\r\n"); + cli_send(client, "\t\tcancel - cancel 'shutdown soft' and return to normal operation\r\n"); +} + +static void ppp_terminate_soft2(struct ppp_t *ppp) +{ + ppp_terminate(ppp, TERM_NAS_REBOOT, 0); +} + +static void ppp_terminate_hard2(struct ppp_t *ppp) +{ + ppp_terminate(ppp, TERM_NAS_REBOOT, 1); +} + +static int shutdown_exec(const char *cmd, char * const *f, int f_cnt, void *cli) +{ + int hard = 0; + struct ppp_t *ppp; + + if (f_cnt == 2) { + if (!strcmp(f[1], "soft")) { + ppp_shutdown_soft(); + return CLI_CMD_OK; + } else if (!strcmp(f[1], "hard")) + hard = 1; + else if (!strcmp(f[1], "cancel")) { + ppp_shutdown = 0; + return CLI_CMD_OK; + } else + return CLI_CMD_SYNTAX; + } + + ppp_shutdown_soft(); + + pthread_rwlock_rdlock(&ppp_lock); + list_for_each_entry(ppp, &ppp_list, entry) { + if (hard) + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_hard2, ppp); + else + triton_context_call(ppp->ctrl->ctx, (triton_event_func)ppp_terminate_soft2, ppp); + } + pthread_rwlock_unlock(&ppp_lock); + + return CLI_CMD_OK; +} + +//========================== +static int conf_reload_res; +static struct triton_context_t *conf_reload_ctx; +static void conf_reload_notify(int r) +{ + if (!r) + triton_event_fire(EV_CONFIG_RELOAD, NULL); + conf_reload_res = r; + triton_context_wakeup(conf_reload_ctx); +} +static int reload_exec(const char *cmd, char * const *f, int f_cnt, void *cli) +{ + if (f_cnt == 1) { + conf_reload_ctx = triton_context_self(); + triton_conf_reload(conf_reload_notify); + triton_context_schedule(); + if (conf_reload_res) + cli_send(cli, "failed\r\n"); + return CLI_CMD_OK; + } else + return CLI_CMD_SYNTAX; +} + +static void reload_help(char * const *fields, int fields_cnt, void *client) +{ + cli_send(client, "reload - reload config file\r\n"); +} + +static void __init init(void) +{ + cli_register_simple_cmd2(show_stat_exec, show_stat_help, 2, "show", "stat"); + cli_register_simple_cmd2(terminate_exec, terminate_help, 1, "terminate"); + cli_register_simple_cmd2(reload_exec, reload_help, 1, "reload"); + cli_register_simple_cmd2(shutdown_exec, shutdown_help, 1, "shutdown"); + cli_register_simple_cmd2(exit_exec, exit_help, 1, "exit"); +} + diff --git a/accel-pppd/cli/tcp.c b/accel-pppd/cli/tcp.c new file mode 100644 index 00000000..260225f1 --- /dev/null +++ b/accel-pppd/cli/tcp.c @@ -0,0 +1,371 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include "triton.h" +#include "log.h" +#include "list.h" +#include "memdebug.h" + +#include "cli_p.h" + +#define RECV_BUF_SIZE 1024 + +struct tcp_client_t +{ + struct cli_client_t cli_client; + struct list_head entry; + struct triton_md_handler_t hnd; + struct list_head xmit_queue; + struct buffer_t *xmit_buf; + uint8_t *cmdline; + int xmit_pos; + int recv_pos; + int auth:1; + int disconnect:1; +}; + +struct buffer_t +{ + struct list_head entry; + int size; + uint8_t buf[0]; +}; + +static struct triton_context_t serv_ctx; +static struct triton_md_handler_t serv_hnd; +static LIST_HEAD(clients); + +static uint8_t *temp_buf; + +static void disconnect(struct tcp_client_t *cln) +{ + struct buffer_t *b; + + log_debug("cli: disconnect\n"); + + list_del(&cln->entry); + + triton_md_unregister_handler(&cln->hnd); + close(cln->hnd.fd); + + if (cln->xmit_buf) + _free(cln->xmit_buf); + + while (!list_empty(&cln->xmit_queue)) { + b = list_entry(cln->xmit_queue.next, typeof(*b), entry); + list_del(&b->entry); + _free(b); + } + + _free(cln->cmdline); + _free(cln); +} + +static void cli_client_disconnect(struct cli_client_t *tcln) +{ + struct tcp_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + cln->disconnect = 1; +} + +static void queue_buffer(struct tcp_client_t *cln, struct buffer_t *b) +{ + if (cln->xmit_buf) + list_add_tail(&b->entry, &cln->xmit_queue); + else + cln->xmit_buf = b; +} + +static int cli_client_send(struct cli_client_t *tcln, const void *_buf, int size) +{ + struct tcp_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + int n, k; + struct buffer_t *b; + const uint8_t *buf = (const uint8_t *)_buf; + + if (cln->disconnect) + return -1; + + if (!list_empty(&cln->xmit_queue)) { + b = _malloc(sizeof(*b) + size); + b->size = size; + memcpy(b->buf, buf, size); + queue_buffer(cln, b); + return 0; + } + + for (n = 0; n < size; n += k) { + k = write(cln->hnd.fd, buf + n, size - n); + if (k < 0) { + if (errno == EAGAIN) { + b = _malloc(sizeof(*b) + size - n); + b->size = size - n; + memcpy(b->buf, buf, size - n); + queue_buffer(cln, b); + + triton_md_enable_handler(&cln->hnd, MD_MODE_WRITE); + break; + } + if (errno != EPIPE) + log_error("cli: write: %s\n", strerror(errno)); + //disconnect(cln); + cln->disconnect = 1; + return -1; + } + } + return 0; +} + +static int cli_client_sendv(struct cli_client_t *tcln, const char *fmt, va_list ap) +{ + struct tcp_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + int r = vsnprintf((char *)temp_buf, RECV_BUF_SIZE, fmt, ap); + + if (r >= RECV_BUF_SIZE) { + strcpy((char *)temp_buf + RECV_BUF_SIZE - 5, "...\n"); + r = RECV_BUF_SIZE; + } + + return cli_client_send(tcln, temp_buf, r); +} + +static int cln_read(struct triton_md_handler_t *h) +{ + struct tcp_client_t *cln = container_of(h, typeof(*cln), hnd); + int n; + char *d; + + while (1) { + n = read(h->fd, cln->cmdline + cln->recv_pos, RECV_BUF_SIZE - cln->recv_pos); + if (n == 0) + break; + if (n < 0) { + if (errno != EAGAIN) + log_error("cli: read: %s\n", strerror(errno)); + return 0; + } + + cln->recv_pos += n; + + while (cln->recv_pos) { + d = strchr((char *)cln->cmdline, '\n'); + if (!d) { + if (cln->recv_pos == RECV_BUF_SIZE) { + log_warn("cli: tcp: recv buffer overflow\n"); + goto drop; + } + break; + } + + *d = 0; + + if (!cln->auth) { + if (strcmp((char *)cln->cmdline, conf_cli_passwd)) + goto drop; + cln->auth = 1; + } else + cli_process_cmd(&cln->cli_client); + + if (cln->disconnect) + goto drop; + + cln->recv_pos -= (uint8_t *)d + 1 - cln->cmdline; + memmove(cln->cmdline, d + 1, cln->recv_pos); + } + } + +drop: + disconnect(cln); + return -1; +} + +static int cln_write(struct triton_md_handler_t *h) +{ + struct tcp_client_t *cln = container_of(h, typeof(*cln), hnd); + int k; + + while (1) { + for (; cln->xmit_pos < cln->xmit_buf->size; cln->xmit_pos += k) { + k = write(cln->hnd.fd, cln->xmit_buf->buf + cln->xmit_pos, cln->xmit_buf->size - cln->xmit_pos); + if (k < 0) { + if (errno == EAGAIN) + return 0; + if (errno != EPIPE) + log_error("cli: tcp: write: %s\n", strerror(errno)); + disconnect(cln); + return -1; + } + } + + _free(cln->xmit_buf); + cln->xmit_pos = 0; + + if (list_empty(&cln->xmit_queue)) + break; + + cln->xmit_buf = list_entry(cln->xmit_queue.next, typeof(*cln->xmit_buf), entry); + list_del(&cln->xmit_buf->entry); + } + + triton_md_disable_handler(&cln->hnd, MD_MODE_WRITE); + + return 0; +} + +static int serv_read(struct triton_md_handler_t *h) +{ + struct sockaddr_in addr; + socklen_t size = sizeof(addr); + int sock; + struct tcp_client_t *conn; + + while(1) { + sock = accept(h->fd, (struct sockaddr *)&addr, &size); + if (sock < 0) { + if (errno == EAGAIN) + return 0; + log_error("cli: tcp: accept failed: %s\n", strerror(errno)); + continue; + } + + log_info2("cli: tcp: new connection from %s\n", inet_ntoa(addr.sin_addr)); + + if (fcntl(sock, F_SETFL, O_NONBLOCK)) { + log_error("cli: tcp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno)); + close(sock); + continue; + } + + conn = _malloc(sizeof(*conn)); + memset(conn, 0, sizeof(*conn)); + conn->hnd.fd = sock; + conn->hnd.read = cln_read; + conn->hnd.write = cln_write; + conn->cmdline = _malloc(RECV_BUF_SIZE); + INIT_LIST_HEAD(&conn->xmit_queue); + + conn->cli_client.cmdline = conn->cmdline; + conn->cli_client.send = cli_client_send; + conn->cli_client.sendv = cli_client_sendv; + conn->cli_client.disconnect = cli_client_disconnect; + + triton_md_register_handler(&serv_ctx, &conn->hnd); + triton_md_enable_handler(&conn->hnd,MD_MODE_READ); + + list_add_tail(&conn->entry, &clients); + + if (!conf_cli_passwd) + conn->auth = 1; + } + return 0; +} + +static void serv_close(struct triton_context_t *ctx) +{ + struct tcp_client_t *cln; + + while (!list_empty(&clients)) { + cln = list_entry(clients.next, typeof(*cln), entry); + disconnect(cln); + } + + triton_md_unregister_handler(&serv_hnd); + close(serv_hnd.fd); + triton_context_unregister(ctx); +} + +static struct triton_context_t serv_ctx = { + .close = serv_close, + .before_switch = log_switch, +}; + +static struct triton_md_handler_t serv_hnd = { + .read = serv_read, +}; + +static void start_server(const char *host, int port) +{ + struct sockaddr_in addr; + + serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0); + if (serv_hnd.fd < 0) { + log_emerg("cli: tcp: failed to create server socket: %s\n", strerror(errno)); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (host) + addr.sin_addr.s_addr = inet_addr(host); + 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: tcp: failed to bind socket: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + if (listen (serv_hnd.fd, 1) < 0) { + log_emerg("cli: tcp: failed to listen socket: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + if (fcntl(serv_hnd.fd, F_SETFL, O_NONBLOCK)) { + log_emerg("cli: tcp: failed to set nonblocking mode: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(host); + + triton_context_register(&serv_ctx, NULL); + triton_context_set_priority(&serv_ctx, 1); + triton_md_register_handler(&serv_ctx, &serv_hnd); + triton_md_enable_handler(&serv_hnd, MD_MODE_READ); + triton_context_wakeup(&serv_ctx); +} + +static void __init init(void) +{ + const char *opt; + char *host, *d; + int port; + + opt = conf_get_opt("cli", "tcp"); + if (!opt) + return; + + host = strdup(opt); + d = strstr(host, ":"); + if (!d) + goto err_fmt; + + *d = 0; + port = atoi(d + 1); + if (port <= 0) + goto err_fmt; + + temp_buf = malloc(RECV_BUF_SIZE); + + start_server(host, port); + + return; +err_fmt: + log_emerg("cli: tcp: invalid format\n"); + free(host); +} + diff --git a/accel-pppd/cli/telnet.c b/accel-pppd/cli/telnet.c new file mode 100644 index 00000000..5176df29 --- /dev/null +++ b/accel-pppd/cli/telnet.c @@ -0,0 +1,757 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <ctype.h> +#include <arpa/inet.h> +#include <arpa/telnet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "triton.h" +#include "log.h" +#include "ppp.h" +#include "list.h" +#include "memdebug.h" + +#include "cli_p.h" + +#define RECV_BUF_SIZE 1024 + +#define MSG_AUTH_FAILED "\r\nAuthentication failed\r\n" +#define MSG_SHUTDOWN_IN_PROGRESS "note: 'shutdown soft' is in progress...\r\n" + +#define ESC_LEFT "[D" +#define ESC_RIGHT "[C" +#define ESC_UP "[A" +#define ESC_DOWN "[B" + +struct telnet_client_t +{ + struct cli_client_t cli_client; + struct list_head entry; + struct triton_md_handler_t hnd; + 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 disconnect:1; +}; + +struct buffer_t +{ + struct list_head entry; + int size; + struct buffer_t *p_buf; + uint8_t buf[0]; +}; + +static struct triton_context_t serv_ctx; +static struct triton_md_handler_t serv_hnd; +static LIST_HEAD(clients); + +static uint8_t *recv_buf; +static uint8_t *temp_buf; + +static int conf_history_len = 100; +static const char *conf_history_file = "/var/run/accel-ppp/history"; +static LIST_HEAD(history); +static int history_len; +static pthread_mutex_t history_lock = PTHREAD_MUTEX_INITIALIZER; + +static void disconnect(struct telnet_client_t *cln) +{ + struct buffer_t *b, *b2; + + log_debug("cli: disconnect\n"); + + triton_stop_collect_cpu_usage(); + + list_del(&cln->entry); + + triton_md_unregister_handler(&cln->hnd); + close(cln->hnd.fd); + + if (cln->xmit_buf) + _free(cln->xmit_buf); + + while (!list_empty(&cln->xmit_queue)) { + b = list_entry(cln->xmit_queue.next, typeof(*b), entry); + list_del(&b->entry); + _free(b); + } + + pthread_mutex_lock(&history_lock); + while (!list_empty(&cln->history)) { + b = list_entry(cln->history.prev, typeof(*b), entry); + list_del(&b->entry); + if (!b->p_buf) { + if (history_len == conf_history_len) { + b2 = list_entry(history.next, typeof(*b2), entry); + list_del(&b2->entry); + _free(b2); + } else + history_len++; + list_add_tail(&b->entry, &history); + } else + _free(b); + } + pthread_mutex_unlock(&history_lock); + + _free(cln->cmdline); + _free(cln); +} + +static void cli_client_disconnect(struct cli_client_t *tcln) +{ + struct telnet_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + cln->disconnect = 1; +} + +static void queue_buffer(struct telnet_client_t *cln, struct buffer_t *b) +{ + if (cln->xmit_buf) + list_add_tail(&b->entry, &cln->xmit_queue); + else + cln->xmit_buf = b; +} + +static int telnet_send(struct telnet_client_t *cln, const void *_buf, int size) +{ + int n, k; + struct buffer_t *b; + const uint8_t *buf = (const uint8_t *)_buf; + + if (cln->disconnect) + return -1; + + if (!list_empty(&cln->xmit_queue)) { + b = _malloc(sizeof(*b) + size); + b->size = size; + memcpy(b->buf, buf, size); + queue_buffer(cln, b); + return 0; + } + + for (n = 0; n < size; n += k) { + k = write(cln->hnd.fd, buf + n, size - n); + if (k < 0) { + if (errno == EAGAIN) { + b = _malloc(sizeof(*b) + size - n); + b->size = size - n; + memcpy(b->buf, buf, size - n); + queue_buffer(cln, b); + + triton_md_enable_handler(&cln->hnd, MD_MODE_WRITE); + break; + } + if (errno != EPIPE) + log_error("cli: write: %s\n", strerror(errno)); + //disconnect(cln); + cln->disconnect = 1; + return -1; + } + } + return 0; +} + +static int cli_client_send(struct cli_client_t *tcln, const void *buf, int size) +{ + struct telnet_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + return telnet_send(cln, buf, size); +} + +static int cli_client_sendv(struct cli_client_t *tcln, const char *fmt, va_list ap) +{ + struct telnet_client_t *cln = container_of(tcln, typeof(*cln), cli_client); + int r = vsnprintf((char *)temp_buf, RECV_BUF_SIZE, fmt, ap); + + if (r >= RECV_BUF_SIZE) { + strcpy((char *)temp_buf + RECV_BUF_SIZE - 6, "...\r\n"); + r = RECV_BUF_SIZE; + } + + return telnet_send(cln, temp_buf, r); +} + +static int send_banner(struct telnet_client_t *cln) +{ + if (telnet_send(cln, "accel-ppp version " ACCEL_PPP_VERSION "\r\n", sizeof("accel-ppp version " ACCEL_PPP_VERSION "\r\n"))) + return -1; + return 0; +} + +static int send_config(struct telnet_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 telnet_client_t *cln) +{ + uint8_t buf0[] = {IAC, WILL, TELOPT_ECHO}; + uint8_t buf1[] = "Password: "; + + if (telnet_send(cln, buf0, sizeof(buf0))) + return -1; + + if (telnet_send(cln, buf1, sizeof(buf1))) + return -1; + + return 0; +} + +static int send_prompt(struct telnet_client_t *cln) +{ + sprintf((char *)temp_buf, "%s%s# ", conf_cli_prompt, ppp_shutdown ? "(shutdown)" : ""); + return telnet_send(cln, temp_buf, strlen((char *)temp_buf)); +} + +/*static void print_buf(const uint8_t *buf, int size) +{ + int i; + + for (i = 0; i < size; i++) + log_debug("%x ", buf[i]); + log_debug("\n"); +}*/ + +static int send_cmdline_tail(struct telnet_client_t *cln, int corr) +{ + 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 telnet_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->p_buf ? b->p_buf->buf : b->buf, b->size); + cln->cmdline_pos = b->size; + cln->cmdline_len = b->size; + if (telnet_send(cln, b->p_buf ? b->p_buf->buf : b->buf, b->size)) + return -1; + + return 0; +} + +static int telnet_input_char(struct telnet_client_t *cln, uint8_t c) +{ + uint8_t buf[] = {IAC, DONT, 0}; + struct buffer_t *b; + + if (c == '\n') + return 0; + + if (c == '\r') { + cln->cmdline[cln->cmdline_len] = 0; + + if (cln->echo) { + if (telnet_send(cln, "\r\n", 2)) + return -1; + } + + if (!cln->auth) { + if (strcmp((char *)cln->cmdline, conf_cli_passwd)) { + if (telnet_send(cln, MSG_AUTH_FAILED, sizeof(MSG_AUTH_FAILED))) + return -1; + cln->disconnect = 1; + return -1; + } + cln->auth = 1; + if (ppp_shutdown) { + if (telnet_send(cln, MSG_SHUTDOWN_IN_PROGRESS, sizeof(MSG_SHUTDOWN_IN_PROGRESS))) + return -1; + } + } else if (cln->cmdline_len) { + b = _malloc(sizeof(*b) + cln->cmdline_len); + b->p_buf = NULL; + 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; + + if (cli_process_cmd(&cln->cli_client)) + return -1; + } + + cln->cmdline_pos = 0; + cln->cmdline_len = 0; + + return send_prompt(cln); + } + + if (cln->telcmd) { + 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[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 { + 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; + case 3: + cln->disconnect = 1; + return -1; + } + + if (isprint(c)) { + if (cln->cmdline_len == RECV_BUF_SIZE - 1) + return 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; +} + +static int cln_read(struct triton_md_handler_t *h) +{ + struct telnet_client_t *cln = container_of(h, typeof(*cln), hnd); + int i, n; + + while (1) { + n = read(h->fd, recv_buf, RECV_BUF_SIZE); + if (n == 0) { + disconnect(cln); + return -1; + } + if (n < 0) { + if (errno != EAGAIN) + log_error("cli: telnet: read: %s\n", strerror(errno)); + return 0; + } + /*log_debug("cli: read(%i): ", n); + print_buf(cln->recv_buf + cln->recv_pos, n);*/ + for (i = 0; i < n; i++) { + if (telnet_input_char(cln, recv_buf[i])) + break; + } + if (cln->disconnect) { + disconnect(cln); + return -1; + } + } + + return 0; +} + +static int cln_write(struct triton_md_handler_t *h) +{ + struct telnet_client_t *cln = container_of(h, typeof(*cln), hnd); + int k; + + while (1) { + for (; cln->xmit_pos < cln->xmit_buf->size; cln->xmit_pos += k) { + k = write(cln->hnd.fd, cln->xmit_buf->buf + cln->xmit_pos, cln->xmit_buf->size - cln->xmit_pos); + if (k < 0) { + if (errno == EAGAIN) + return 0; + if (errno != EPIPE) + log_error("cli: telnet: write: %s\n", strerror(errno)); + disconnect(cln); + return -1; + } + } + + _free(cln->xmit_buf); + cln->xmit_pos = 0; + + if (list_empty(&cln->xmit_queue)) + break; + + cln->xmit_buf = list_entry(cln->xmit_queue.next, typeof(*cln->xmit_buf), entry); + list_del(&cln->xmit_buf->entry); + } + + triton_md_disable_handler(&cln->hnd, MD_MODE_WRITE); + + return 0; +} + +static int serv_read(struct triton_md_handler_t *h) +{ + struct sockaddr_in addr; + socklen_t size = sizeof(addr); + int sock; + struct telnet_client_t *conn; + struct buffer_t *b, *b2; + + while(1) { + sock = accept(h->fd, (struct sockaddr *)&addr, &size); + if (sock < 0) { + if (errno == EAGAIN) + return 0; + log_error("cli: telnet: accept failed: %s\n", strerror(errno)); + continue; + } + + log_info2("cli: telnet: new connection from %s\n", inet_ntoa(addr.sin_addr)); + + if (fcntl(sock, F_SETFL, O_NONBLOCK)) { + log_error("cli: telnet: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno)); + close(sock); + continue; + } + + conn = _malloc(sizeof(*conn)); + memset(conn, 0, sizeof(*conn)); + conn->hnd.fd = sock; + conn->hnd.read = cln_read; + conn->hnd.write = cln_write; + 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->p_buf = b; + b->size = 0; + list_add_tail(&b->entry, &conn->history); + + pthread_mutex_lock(&history_lock); + list_for_each_entry(b, &history, entry) { + b2 = _malloc(sizeof(*b)); + b2->p_buf = b; + b2->size = b->size; + list_add(&b2->entry, conn->history.next); + } + pthread_mutex_unlock(&history_lock); + + conn->history_pos = conn->history.next; + + conn->cli_client.cmdline = conn->cmdline; + conn->cli_client.send = cli_client_send; + conn->cli_client.sendv = cli_client_sendv; + conn->cli_client.disconnect = cli_client_disconnect; + + triton_md_register_handler(&serv_ctx, &conn->hnd); + triton_md_enable_handler(&conn->hnd,MD_MODE_READ); + + list_add_tail(&conn->entry, &clients); + + if (send_banner(conn)) + continue; + + if (send_config(conn)) + continue; + + if (conf_cli_passwd) + send_password_request(conn); + else { + conn->auth = 1; + if (ppp_shutdown) { + if (telnet_send(conn, MSG_SHUTDOWN_IN_PROGRESS, sizeof(MSG_SHUTDOWN_IN_PROGRESS))) + continue; + } + send_prompt(conn); + } + triton_collect_cpu_usage(); + } + return 0; +} +static void serv_close(struct triton_context_t *ctx) +{ + struct telnet_client_t *cln; + + while (!list_empty(&clients)) { + cln = list_entry(clients.next, typeof(*cln), entry); + disconnect(cln); + } + + triton_md_unregister_handler(&serv_hnd); + close(serv_hnd.fd); + triton_context_unregister(ctx); +} + +static struct triton_context_t serv_ctx = { + .close = serv_close, + .before_switch = log_switch, +}; + +static struct triton_md_handler_t serv_hnd = { + .read = serv_read, +}; + +static void start_server(const char *host, int port) +{ + struct sockaddr_in addr; + + serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0); + if (serv_hnd.fd < 0) { + log_emerg("cli: telnet: failed to create server socket: %s\n", strerror(errno)); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (host) + addr.sin_addr.s_addr = inet_addr(host); + 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: telnet: failed to bind socket: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + if (listen (serv_hnd.fd, 1) < 0) { + log_emerg("cli: telnet: failed to listen socket: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + if (fcntl(serv_hnd.fd, F_SETFL, O_NONBLOCK)) { + log_emerg("cli: telnet: failed to set nonblocking mode: %s\n", strerror(errno)); + close(serv_hnd.fd); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(host); + + triton_context_register(&serv_ctx, NULL); + triton_context_set_priority(&serv_ctx, 1); + triton_md_register_handler(&serv_ctx, &serv_hnd); + triton_md_enable_handler(&serv_hnd, MD_MODE_READ); + triton_context_wakeup(&serv_ctx); +} + +static void save_history_file(void) +{ + int fd; + struct buffer_t *b; + + fd = open(conf_history_file, O_WRONLY | O_TRUNC | O_CREAT, S_IREAD | S_IWRITE); + if (!fd) + return; + + list_for_each_entry(b, &history, entry) { + b->buf[b->size] = '\n'; + write(fd, b->buf, b->size + 1); + } + + close(fd); +} + +static void load_history_file(void) +{ + struct buffer_t *b; + FILE *f; + + f = fopen(conf_history_file, "r"); + if (!f) + return; + + while (fgets((char *)temp_buf, RECV_BUF_SIZE, f)) { + b = _malloc(sizeof(*b) + strlen((char *)temp_buf)); + b->p_buf = NULL; + b->size = strlen((char *)temp_buf) - 1; + memcpy(b->buf, temp_buf, b->size); + list_add_tail(&b->entry, &history); + } + + fclose(f); +} + +static void __init init(void) +{ + const char *opt; + char *host, *d; + int port; + + opt = conf_get_opt("cli", "telnet"); + if (!opt) + return; + + host = strdup(opt); + d = strstr(host, ":"); + if (!d) + goto err_fmt; + + *d = 0; + port = atoi(d + 1); + if (port <= 0) + goto err_fmt; + + opt = conf_get_opt("cli", "history-file"); + if (opt) + conf_history_file = _strdup(opt); + + recv_buf = malloc(RECV_BUF_SIZE); + temp_buf = malloc(RECV_BUF_SIZE); + + load_history_file(); + + start_server(host, port); + + atexit(save_history_file); + + return; +err_fmt: + log_emerg("cli: telnet: invalid format\n"); + free(host); +} + |