telnet.c 17.0 KB
Newer Older
1 2 3 4 5 6 7 8
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
D
Dmitry Kozlov 已提交
9
#include <ctype.h>
10
#include <arpa/inet.h>
K
Kozlov Dmitry 已提交
11
#include <arpa/telnet.h>
12 13 14 15 16
#include <netinet/in.h>
#include <sys/socket.h>

#include "triton.h"
#include "log.h"
D
Dmitry Kozlov 已提交
17
#include "ppp.h"
18 19 20
#include "list.h"
#include "memdebug.h"

21
#include "cli_p.h"
K
Kozlov Dmitry 已提交
22

D
Dmitry Kozlov 已提交
23
#define RECV_BUF_SIZE 1024
K
Kozlov Dmitry 已提交
24
#define AUTH_FAILED "\r\nAuthentication failed\r\n"
25

D
Dmitry Kozlov 已提交
26 27 28 29 30
#define ESC_LEFT "[D"
#define ESC_RIGHT "[C"
#define ESC_UP "[A"
#define ESC_DOWN "[B"

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
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;
};

K
Kozlov Dmitry 已提交
52 53 54 55
struct buffer_t
{
	struct list_head entry;
	int size;
D
Dmitry Kozlov 已提交
56
	struct buffer_t *p_buf;
K
Kozlov Dmitry 已提交
57 58 59
	uint8_t buf[0];
};

60 61
static struct triton_context_t serv_ctx;
static struct triton_md_handler_t serv_hnd;
62
static LIST_HEAD(clients);
63

D
Dmitry Kozlov 已提交
64 65 66
static uint8_t *recv_buf;
static uint8_t *temp_buf;

D
Dmitry Kozlov 已提交
67 68 69 70 71 72
static int conf_history_len = 100;
static const char *conf_history_file = "/var/run/accel-pptp/history";
static LIST_HEAD(history);
static int history_len;
static pthread_mutex_t history_lock = PTHREAD_MUTEX_INITIALIZER;

73
static void disconnect(struct telnet_client_t *cln)
K
Kozlov Dmitry 已提交
74
{
D
Dmitry Kozlov 已提交
75
	struct buffer_t *b, *b2;
K
Kozlov Dmitry 已提交
76 77 78

	log_debug("cli: disconnect\n");

79 80
	triton_stop_collect_cpu_usage();

81 82
	list_del(&cln->entry);

K
Kozlov Dmitry 已提交
83 84 85 86 87 88 89 90 91 92 93 94
	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);
	}

D
Dmitry Kozlov 已提交
95
	pthread_mutex_lock(&history_lock);
D
Dmitry Kozlov 已提交
96
	while (!list_empty(&cln->history)) {
D
Dmitry Kozlov 已提交
97
		b = list_entry(cln->history.prev, typeof(*b), entry);
D
Dmitry Kozlov 已提交
98
		list_del(&b->entry);
D
Dmitry Kozlov 已提交
99 100 101 102 103 104 105 106 107 108
		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);
D
Dmitry Kozlov 已提交
109
	}
D
Dmitry Kozlov 已提交
110
	pthread_mutex_unlock(&history_lock);
D
Dmitry Kozlov 已提交
111 112

	_free(cln->cmdline);
K
Kozlov Dmitry 已提交
113 114 115
	_free(cln);
}

116
static void cli_client_disconnect(struct cli_client_t *tcln)
K
Kozlov Dmitry 已提交
117
{
118
	struct telnet_client_t *cln = container_of(tcln, typeof(*cln), cli_client);
K
Kozlov Dmitry 已提交
119 120 121
	disconnect(cln);
}

122
static void queue_buffer(struct telnet_client_t *cln, struct buffer_t *b)
K
Kozlov Dmitry 已提交
123 124 125 126 127 128 129
{
	if (cln->xmit_buf)
		list_add_tail(&b->entry, &cln->xmit_queue);
	else
		cln->xmit_buf = b;
}

130
static int telnet_send(struct telnet_client_t *cln, const void *_buf, int size)
K
Kozlov Dmitry 已提交
131 132 133 134 135
{
	int n, k;
	struct buffer_t *b;
	const uint8_t *buf = (const uint8_t *)_buf;

K
Kozlov Dmitry 已提交
136 137
	if (cln->disconnect)
		return -1;
138 139 140 141 142 143 144 145
	
	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;
	}
K
Kozlov Dmitry 已提交
146

K
Kozlov Dmitry 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159 160
	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));
K
Kozlov Dmitry 已提交
161 162
			//disconnect(cln);
			cln->disconnect = 1;
K
Kozlov Dmitry 已提交
163 164 165 166 167 168
			return -1;
		}
	}
	return 0;
}

169 170 171 172 173 174 175
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)
K
Kozlov Dmitry 已提交
176
{
177
	struct telnet_client_t *cln = container_of(tcln, typeof(*cln), cli_client);
K
Kozlov Dmitry 已提交
178 179 180 181 182 183 184 185 186 187
	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);
}

188
static int send_banner(struct telnet_client_t *cln)
K
Kozlov Dmitry 已提交
189
{
D
Dmitry Kozlov 已提交
190 191 192 193 194 195 196
	if (telnet_send(cln, "accel-pptp version " ACCEL_PPTP_VERSION "\r\n", sizeof("accel-pptp version " ACCEL_PPTP_VERSION "\r\n")))
		return -1;
	if (cln->auth && ppp_shutdown) {
		if (telnet_send(cln, "warning: 'shutdown soft' is in progress...\r\n", sizeof("warning: 'shutdown soft' is in progress...\r\n")))
			return -1;
	}
	return 0;
K
Kozlov Dmitry 已提交
197 198
}

199
static int send_config(struct telnet_client_t *cln)
D
Dmitry Kozlov 已提交
200 201 202 203 204
{
	uint8_t buf[] = {IAC, WILL, TELOPT_ECHO, IAC, WILL,	TELOPT_SGA, IAC, DONT, TELOPT_LINEMODE};
	return telnet_send(cln, buf, sizeof(buf));
}

205
static int send_password_request(struct telnet_client_t *cln)
206
{
K
Kozlov Dmitry 已提交
207 208 209 210 211 212 213 214 215
	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;
	
K
Kozlov Dmitry 已提交
216 217 218
	return 0;
}

219
static int send_prompt(struct telnet_client_t *cln)
K
Kozlov Dmitry 已提交
220
{
221
	return telnet_send(cln, conf_cli_prompt, strlen(conf_cli_prompt));
K
Kozlov Dmitry 已提交
222 223
}

224
/*static void print_buf(const uint8_t *buf, int size)
K
Kozlov Dmitry 已提交
225 226 227 228 229 230
{
	int i;

	for (i = 0; i < size; i++)
		log_debug("%x ", buf[i]);
	log_debug("\n");
231
}*/
232

233
static int send_cmdline_tail(struct telnet_client_t *cln, int corr)
K
Kozlov Dmitry 已提交
234
{
D
Dmitry Kozlov 已提交
235 236 237 238 239 240 241 242 243 244 245
	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;
}

246
static int load_history(struct telnet_client_t *cln)
D
Dmitry Kozlov 已提交
247 248 249 250 251 252 253 254 255 256 257 258
{
	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;
D
Dmitry Kozlov 已提交
259
	memcpy(cln->cmdline, b->p_buf ? b->p_buf->buf : b->buf, b->size);
D
Dmitry Kozlov 已提交
260 261
	cln->cmdline_pos = b->size;
	cln->cmdline_len = b->size;
D
Dmitry Kozlov 已提交
262
	if (telnet_send(cln, b->p_buf ? b->p_buf->buf : b->buf, b->size))
D
Dmitry Kozlov 已提交
263 264 265 266
		return -1;

	return 0;
}
K
Kozlov Dmitry 已提交
267

268
static int telnet_input_char(struct telnet_client_t *cln, uint8_t c)
D
Dmitry Kozlov 已提交
269 270 271
{
	uint8_t buf[] = {IAC, DONT, 0};
	struct buffer_t *b;
D
Dmitry Kozlov 已提交
272

D
Dmitry Kozlov 已提交
273
	if (c == '\n')
K
Kozlov Dmitry 已提交
274 275
		return 0;
	
D
Dmitry Kozlov 已提交
276 277
	if (c == '\r') {
		cln->cmdline[cln->cmdline_len] = 0;
K
Kozlov Dmitry 已提交
278

D
Dmitry Kozlov 已提交
279 280 281 282 283 284
		if (cln->echo) {
			if (telnet_send(cln, "\r\n", 2))
				return -1;
		}

		if (!cln->auth) {
285
			if (strcmp((char *)cln->cmdline, conf_cli_passwd)) {
D
Dmitry Kozlov 已提交
286
				if (telnet_send(cln, AUTH_FAILED, sizeof(AUTH_FAILED)))
K
Kozlov Dmitry 已提交
287
					return -1;
D
Dmitry Kozlov 已提交
288 289
				disconnect(cln);
				return -1;
K
Kozlov Dmitry 已提交
290
			}
D
Dmitry Kozlov 已提交
291
			cln->auth = 1;
D
Dmitry Kozlov 已提交
292 293 294 295
			if (ppp_shutdown) {
				if (telnet_send(cln, "warning: 'shutdown soft' is in progress...\r\n", sizeof("warning: 'shutdown soft' is in progress...\r\n")))
					return -1;
			}
D
Dmitry Kozlov 已提交
296
		} else if (cln->cmdline_len) {
D
Dmitry Kozlov 已提交
297
			b = _malloc(sizeof(*b) + cln->cmdline_len);
D
Dmitry Kozlov 已提交
298
			b->p_buf = NULL;
D
Dmitry Kozlov 已提交
299 300 301 302
			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;
K
Kozlov Dmitry 已提交
303
			
304
			if (cli_process_cmd(&cln->cli_client))
D
Dmitry Kozlov 已提交
305
				return -1;
K
Kozlov Dmitry 已提交
306
		}
D
Dmitry Kozlov 已提交
307 308 309 310 311
	
		cln->cmdline_pos = 0;
		cln->cmdline_len = 0;

		return send_prompt(cln);
K
Kozlov Dmitry 已提交
312 313
	}

D
Dmitry Kozlov 已提交
314 315 316
	if (cln->telcmd) {
		if (cln->cmdline_pos2 == RECV_BUF_SIZE - 1) {
			log_error("cli: buffer overflow, dropping connection ...\n");
K
Kozlov Dmitry 已提交
317 318 319
			disconnect(cln);
			return -1;
		}
D
Dmitry Kozlov 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

		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);
K
Kozlov Dmitry 已提交
348
			return -1;
D
Dmitry Kozlov 已提交
349
		}
K
Kozlov Dmitry 已提交
350

D
Dmitry Kozlov 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
		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;
			}
		}
K
Kozlov Dmitry 已提交
395
	} else {
D
Dmitry Kozlov 已提交
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
		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;
D
Dmitry Kozlov 已提交
431 432 433
			case 3:
				disconnect(cln);
				return -1;
D
Dmitry Kozlov 已提交
434
		}
K
Kozlov Dmitry 已提交
435

D
Dmitry Kozlov 已提交
436 437 438
		if (isprint(c)) {
			if (cln->cmdline_len == RECV_BUF_SIZE - 1)
				return 0;
K
Kozlov Dmitry 已提交
439

D
Dmitry Kozlov 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
			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;
			}
		}
	}
K
Kozlov Dmitry 已提交
462 463 464 465

	return 0;
}

466 467
static int cln_read(struct triton_md_handler_t *h)
{
468
	struct telnet_client_t *cln = container_of(h, typeof(*cln), hnd);
D
Dmitry Kozlov 已提交
469
	int i, n;
470 471

	while (1) {
D
Dmitry Kozlov 已提交
472
		n = read(h->fd, recv_buf, RECV_BUF_SIZE);
473
		if (n == 0) {
K
Kozlov Dmitry 已提交
474
			disconnect(cln);
475 476 477 478
			return 0;
		}
		if (n < 0) {
			if (errno != EAGAIN)
479
				log_error("cli: telnet: read: %s\n", strerror(errno));
480 481
			return 0;
		}
482 483
		/*log_debug("cli: read(%i): ", n);
		print_buf(cln->recv_buf + cln->recv_pos, n);*/
D
Dmitry Kozlov 已提交
484 485 486 487
		for (i = 0; i < n; i++) {
			if (telnet_input_char(cln, recv_buf[i]))
				return -1;
		}
K
Kozlov Dmitry 已提交
488 489 490 491
		if (cln->disconnect) {
			disconnect(cln);
			return 0;
		}
492 493 494 495 496
	}

	return 0;
}

K
Kozlov Dmitry 已提交
497 498
static int cln_write(struct triton_md_handler_t *h)
{
499
	struct telnet_client_t *cln = container_of(h, typeof(*cln), hnd);
K
Kozlov Dmitry 已提交
500 501 502 503 504 505 506 507 508
	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)
509
					log_error("cli: telnet: write: %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
				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;
}

530 531 532 533 534
static int serv_read(struct triton_md_handler_t *h)
{
  struct sockaddr_in addr;
	socklen_t size = sizeof(addr);
	int sock;
535
	struct telnet_client_t *conn;
D
Dmitry Kozlov 已提交
536
	struct buffer_t *b, *b2;
537 538 539 540 541 542

	while(1) {
		sock = accept(h->fd, (struct sockaddr *)&addr, &size);
		if (sock < 0) {
			if (errno == EAGAIN)
				return 0;
543
			log_error("cli: telnet: accept failed: %s\n", strerror(errno));
544 545 546
			continue;
		}

547
		log_info2("cli: telnet: new connection from %s\n", inet_ntoa(addr.sin_addr));
548 549

		if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
550
			log_error("cli: telnet: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno));
551 552 553 554 555 556 557 558
			close(sock);
			continue;
		}

		conn = _malloc(sizeof(*conn));
		memset(conn, 0, sizeof(*conn));
		conn->hnd.fd = sock;
		conn->hnd.read = cln_read;
K
Kozlov Dmitry 已提交
559
		conn->hnd.write = cln_write;
D
Dmitry Kozlov 已提交
560
		conn->cmdline = _malloc(RECV_BUF_SIZE);
561
		INIT_LIST_HEAD(&conn->xmit_queue);
D
Dmitry Kozlov 已提交
562 563 564
		INIT_LIST_HEAD(&conn->history);

		b = _malloc(sizeof(*b) + RECV_BUF_SIZE);
D
Dmitry Kozlov 已提交
565
		b->p_buf = b;
D
Dmitry Kozlov 已提交
566 567
		b->size = 0;
		list_add_tail(&b->entry, &conn->history);
D
Dmitry Kozlov 已提交
568 569 570 571 572 573 574 575 576 577

		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);
		
D
Dmitry Kozlov 已提交
578
		conn->history_pos = conn->history.next;
579
		
580 581 582 583 584
		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;

585 586 587
		triton_md_register_handler(&serv_ctx, &conn->hnd);
		triton_md_enable_handler(&conn->hnd,MD_MODE_READ);

588 589
		list_add_tail(&conn->entry, &clients);

K
Kozlov Dmitry 已提交
590 591
		if (send_banner(conn))
			continue;
K
Kozlov Dmitry 已提交
592

D
Dmitry Kozlov 已提交
593 594 595
		if (send_config(conn))
			continue;

596
		if (conf_cli_passwd)
K
Kozlov Dmitry 已提交
597 598 599 600 601
			send_password_request(conn);
		else {
			conn->auth = 1;
			send_prompt(conn);
		}
602
		triton_collect_cpu_usage();
603 604 605 606 607
	}
	return 0;
}
static void serv_close(struct triton_context_t *ctx)
{
608 609 610 611 612 613 614
	struct telnet_client_t *cln;

	while (!list_empty(&clients)) {
		cln = list_entry(clients.next, typeof(*cln), entry);
		disconnect(cln);
	}

615 616 617 618 619 620 621
	triton_md_unregister_handler(&serv_hnd);
	close(serv_hnd.fd);
	triton_context_unregister(ctx);
}

static struct triton_context_t serv_ctx = {
	.close = serv_close,
622
	.before_switch = log_switch,
623 624 625 626 627 628 629 630 631 632 633 634
};

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) {
635
    log_emerg("cli: telnet: failed to create server socket: %s\n", strerror(errno));
636 637 638
    return;
  }

K
Kozlov Dmitry 已提交
639 640 641 642 643 644 645 646
	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);

D
Dmitry Kozlov 已提交
647
  setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4);  
648
  if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
649
    log_emerg("cli: telnet: failed to bind socket: %s\n", strerror(errno));
650 651 652 653 654
		close(serv_hnd.fd);
    return;
	}

  if (listen (serv_hnd.fd, 1) < 0) {
655
    log_emerg("cli: telnet: failed to listen socket: %s\n", strerror(errno));
656 657 658 659 660
		close(serv_hnd.fd);
    return;
  }

	if (fcntl(serv_hnd.fd, F_SETFL, O_NONBLOCK)) {
661
    log_emerg("cli: telnet: failed to set nonblocking mode: %s\n", strerror(errno));
662 663 664 665 666 667 668 669 670
		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);
671
	triton_context_set_priority(&serv_ctx, 1);
672 673 674 675 676
	triton_md_register_handler(&serv_ctx, &serv_hnd);
	triton_md_enable_handler(&serv_hnd, MD_MODE_READ);
	triton_context_wakeup(&serv_ctx);
}

D
Dmitry Kozlov 已提交
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
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);
}

714 715 716
static void __init init(void)
{
	const char *opt;
717 718 719 720
	char *host, *d;
	int port;

	opt = conf_get_opt("cli", "telnet");
721 722 723 724 725 726 727 728 729 730 731 732
	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;
733

D
Dmitry Kozlov 已提交
734 735 736 737
	opt = conf_get_opt("cli", "history-file");
	if (opt)
		conf_history_file = opt;

D
Dmitry Kozlov 已提交
738 739 740
	recv_buf = malloc(RECV_BUF_SIZE);
	temp_buf = malloc(RECV_BUF_SIZE);

D
Dmitry Kozlov 已提交
741 742
	load_history_file();

743
	start_server(host, port);
744
	
D
Dmitry Kozlov 已提交
745 746
	atexit(save_history_file);
	
747 748 749 750
	return;
err_fmt:
	log_emerg("cli: telnet: invalid format\n");
	free(host);
751 752
}