blob: 4d51165513c1686b34c305143fa694f11c7b1e5b [file] [log] [blame]
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001
Harald Welted38c8b82011-08-30 11:32:56 +02002/*! \mainpage libosmovty Documentation
3 *
4 * \section sec_intro Introduction
5 * This library is a collection of common code used in various
6 * GSM related sub-projects inside the Osmocom family of projects. It
7 * has been imported/derived from the GNU Zebra project.
8 * \n\n
9 * libosmovty implements the interactive command-line on the VTY
10 * (Virtual TTY) as well as configuration file parsing.
11 * \n\n
12 * Please note that C language projects inside Osmocom are typically
13 * single-threaded event-loop state machine designs. As such,
14 * routines in libosmovty are not thread-safe. If you must use them in
15 * a multi-threaded context, you have to add your own locking.
16 *
Harald Welte96e2a002017-06-12 21:44:18 +020017 * libosmovty is developed as part of the Osmocom (Open Source Mobile
Harald Welte71658802017-06-12 15:40:52 +020018 * Communications) project, a community-based, collaborative development
19 * project to create Free and Open Source implementations of mobile
20 * communications systems. For more information about Osmocom, please
21 * see https://osmocom.org/
22 *
Harald Welted38c8b82011-08-30 11:32:56 +020023 * \section sec_copyright Copyright and License
24 * Copyright © 1997-2007 - Kuninhiro Ishiguro\n
Harald Weltee08da972017-11-13 01:00:26 +090025 * Copyright © 2008-2012 - Harald Welte, Holger Freyther and contributors\n
Harald Welted38c8b82011-08-30 11:32:56 +020026 * All rights reserved. \n\n
27 * The source code of libosmovty is licensed under the terms of the GNU
28 * General Public License as published by the Free Software Foundation;
29 * either version 2 of the License, or (at your option) any later
30 * version.\n
31 * See <http://www.gnu.org/licenses/> or COPYING included in the source
32 * code package istelf.\n
33 * The information detailed here is provided AS IS with NO WARRANTY OF
34 * ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND
35 * FITNESS FOR A PARTICULAR PURPOSE.
36 * \n\n
37 *
Harald Welte71658802017-06-12 15:40:52 +020038 * \section sec_tracker Homepage + Issue Tracker
39 * libosmovty is distributed as part of libosmocore and shares its
40 * project page at http://osmocom.org/projects/libosmocore
41 *
Harald Welted38c8b82011-08-30 11:32:56 +020042 * \section sec_contact Contact and Support
43 * Community-based support is available at the OpenBSC mailing list
44 * <http://lists.osmocom.org/mailman/listinfo/openbsc>\n
45 * Commercial support options available upon request from
46 * <http://sysmocom.de/>
47 */
48
Harald Weltee08da972017-11-13 01:00:26 +090049/* SPDX-License-Identifier: GPL-2.0+ */
Harald Welted38c8b82011-08-30 11:32:56 +020050
Harald Welte3fb0b6f2010-05-19 19:02:52 +020051#include <stdio.h>
52#include <stdarg.h>
53#include <stdlib.h>
54#include <unistd.h>
55#include <string.h>
56#include <errno.h>
57#include <ctype.h>
58#include <termios.h>
59
60#include <sys/utsname.h>
61#include <sys/param.h>
62
63#include <arpa/telnet.h>
64
65#include <osmocom/vty/vty.h>
66#include <osmocom/vty/command.h>
67#include <osmocom/vty/buffer.h>
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010068#include <osmocom/core/talloc.h>
Vadim Yanitskiy024e1952020-10-02 18:23:38 +070069#include <osmocom/core/utils.h>
Harald Welte3fb0b6f2010-05-19 19:02:52 +020070
Ruben Undheim766f77c2018-11-18 13:02:47 +010071#ifndef MAXPATHLEN
72 #define MAXPATHLEN 4096
73#endif
74
75
Harald Welte7acb30c2011-08-17 17:13:48 +020076/* \addtogroup vty
77 * @{
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020078 * \file vty.c */
Harald Welte7acb30c2011-08-17 17:13:48 +020079
Harald Welte3fb0b6f2010-05-19 19:02:52 +020080#define SYSCONFDIR "/usr/local/etc"
81
82/* our callback, located in telnet_interface.c */
83void vty_event(enum event event, int sock, struct vty *vty);
84
85extern struct host host;
86
87/* Vector which store each vty structure. */
88static vector vtyvec;
89
90vector Vvty_serv_thread;
91
92char *vty_cwd = NULL;
93
Neels Hofmeyr96172f02016-02-23 14:01:41 +010094/* IP address passed to the 'line vty'/'bind' command.
95 * Setting the default as vty_bind_addr = "127.0.0.1" doesn't allow freeing, so
96 * use NULL and VTY_BIND_ADDR_DEFAULT instead. */
97static const char *vty_bind_addr = NULL;
98#define VTY_BIND_ADDR_DEFAULT "127.0.0.1"
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +000099/* Port the VTY should bind to. -1 means not configured */
100static int vty_bind_port = -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +0100101
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200102/* Configure lock. */
103static int vty_config;
104
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -0700105static int password_check;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200106
107void *tall_vty_ctx;
108
109static void vty_clear_buf(struct vty *vty)
110{
111 memset(vty->buf, 0, vty->max);
112}
113
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200114/*! Allocate a new vty interface structure */
Harald Welte95b2b472011-07-16 11:58:09 +0200115struct vty *vty_new(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200116{
117 struct vty *new = talloc_zero(tall_vty_ctx, struct vty);
118
119 if (!new)
120 goto out;
121
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +0200122 INIT_LLIST_HEAD(&new->parent_nodes);
123
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200124 new->obuf = buffer_new(new, 0); /* Use default buffer size. */
125 if (!new->obuf)
126 goto out_new;
127 new->buf = _talloc_zero(new, VTY_BUFSIZ, "vty_new->buf");
128 if (!new->buf)
129 goto out_obuf;
130
131 new->max = VTY_BUFSIZ;
Vadim Yanitskiy54df08e2019-11-21 21:44:30 +0700132 new->fd = -1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200133
134 return new;
135
136out_obuf:
137 buffer_free(new->obuf);
138out_new:
139 talloc_free(new);
140 new = NULL;
141out:
142 return new;
143}
144
145/* Authentication of vty */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700146static void vty_auth(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200147{
148 char *passwd = NULL;
149 enum node_type next_node = 0;
150 int fail;
151 char *crypt(const char *, const char *);
152
153 switch (vty->node) {
154 case AUTH_NODE:
155#ifdef VTY_CRYPT_PW
156 if (host.encrypt)
157 passwd = host.password_encrypt;
158 else
159#endif
160 passwd = host.password;
161 if (host.advanced)
162 next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
163 else
164 next_node = VIEW_NODE;
165 break;
166 case AUTH_ENABLE_NODE:
167#ifdef VTY_CRYPT_PW
168 if (host.encrypt)
169 passwd = host.enable_encrypt;
170 else
171#endif
172 passwd = host.enable;
173 next_node = ENABLE_NODE;
174 break;
175 }
176
177 if (passwd) {
178#ifdef VTY_CRYPT_PW
179 if (host.encrypt)
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700180 fail = strcmp(crypt(vty->buf, passwd), passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200181 else
182#endif
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700183 fail = strcmp(vty->buf, passwd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200184 } else
185 fail = 1;
186
187 if (!fail) {
188 vty->fail = 0;
189 vty->node = next_node; /* Success ! */
190 } else {
191 vty->fail++;
192 if (vty->fail >= 3) {
193 if (vty->node == AUTH_NODE) {
194 vty_out(vty,
195 "%% Bad passwords, too many failures!%s",
196 VTY_NEWLINE);
197 vty->status = VTY_CLOSE;
198 } else {
199 /* AUTH_ENABLE_NODE */
200 vty->fail = 0;
201 vty_out(vty,
202 "%% Bad enable passwords, too many failures!%s",
203 VTY_NEWLINE);
204 vty->node = VIEW_NODE;
205 }
206 }
207 }
208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! Close a given vty interface. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200211void vty_close(struct vty *vty)
212{
213 int i;
214
Neels Hofmeyrf8fe48e2019-08-30 00:15:26 +0200215 /* VTY_CLOSED is handled by the telnet_interface */
216 vty_event(VTY_CLOSED, vty->fd, vty);
217
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200218 if (vty->obuf) {
219 /* Flush buffer. */
220 buffer_flush_all(vty->obuf, vty->fd);
221
222 /* Free input buffer. */
223 buffer_free(vty->obuf);
224 vty->obuf = NULL;
225 }
226
227 /* Free command history. */
228 for (i = 0; i < VTY_MAXHIST; i++)
229 if (vty->hist[i])
230 talloc_free(vty->hist[i]);
231
232 /* Unset vector. */
233 vector_unset(vtyvec, vty->fd);
234
Vadim Yanitskiy7a35b782019-11-21 21:51:23 +0700235 /* Close socket (ignore standard I/O streams). */
236 if (vty->fd > 2) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200237 close(vty->fd);
Harald Welte2e0a9452018-10-21 13:22:52 +0200238 vty->fd = -1;
239 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200240
241 if (vty->buf) {
242 talloc_free(vty->buf);
243 vty->buf = NULL;
244 }
245
246 /* Check configure. */
247 vty_config_unlock(vty);
248
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200249 /* OK free vty. */
250 talloc_free(vty);
251}
252
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200253/*! Return if this VTY is a shell or not */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200254int vty_shell(struct vty *vty)
255{
256 return vty->type == VTY_SHELL ? 1 : 0;
257}
258
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100259int vty_out_va(struct vty *vty, const char *format, va_list ap)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200260{
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200261 int len = 0;
262 int size = 1024;
263 char buf[1024];
264 char *p = NULL;
265
266 if (vty_shell(vty)) {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100267 vprintf(format, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200268 } else {
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100269 va_list args;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200270 /* Try to write to initial buffer. */
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100271 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200272 len = vsnprintf(buf, sizeof buf, format, args);
273 va_end(args);
274
275 /* Initial buffer is not enough. */
276 if (len < 0 || len >= size) {
277 while (1) {
278 if (len > -1)
279 size = len + 1;
280 else
281 size = size * 2;
282
283 p = talloc_realloc_size(vty, p, size);
284 if (!p)
285 return -1;
286
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100287 va_copy(args, ap);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200288 len = vsnprintf(p, size, format, args);
289 va_end(args);
290
291 if (len > -1 && len < size)
292 break;
293 }
294 }
295
296 /* When initial buffer is enough to store all output. */
297 if (!p)
298 p = buf;
299
300 /* Pointer p must point out buffer. */
Harald Welte7b74dda2014-03-11 10:31:19 +0100301 buffer_put(vty->obuf, (unsigned char *) p, len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200302
303 /* If p is not different with buf, it is allocated buffer. */
304 if (p != buf)
305 talloc_free(p);
306 }
307
308 vty_event(VTY_WRITE, vty->fd, vty);
309
310 return len;
311}
312
Neels Hofmeyrc1aa1782019-02-01 05:38:44 +0100313/*! VTY standard output function
314 * \param[in] vty VTY to which we should print
315 * \param[in] format variable-length format string
316 */
317int vty_out(struct vty *vty, const char *format, ...)
318{
319 va_list args;
320 int rc;
321 va_start(args, format);
322 rc = vty_out_va(vty, format, args);
323 va_end(args);
324 return rc;
325}
326
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200327/*! print a newline on the given VTY */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200328int vty_out_newline(struct vty *vty)
329{
Holger Hans Peter Freyther314c0102012-09-11 10:40:07 +0200330 const char *p = vty_newline(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200331 buffer_put(vty->obuf, p, strlen(p));
332 return 0;
333}
334
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200335/*! return the current index of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800336void *vty_current_index(struct vty *vty)
337{
338 return vty->index;
339}
Harald Welte7acb30c2011-08-17 17:13:48 +0200340
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200341/*! return the current node of a given VTY */
Holger Hans Peter Freyther08aaded2010-09-14 02:24:03 +0800342int vty_current_node(struct vty *vty)
343{
344 return vty->node;
345}
346
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200347/*! Lock the configuration to a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200348 * \param[in] vty VTY to which the config shall be locked
349 * \returns 1 on success, 0 on error
350 *
351 * This shall be used to make sure only one VTY at a given time has
352 * access to modify the configuration */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200353int vty_config_lock(struct vty *vty)
354{
355 if (vty_config == 0) {
356 vty->config = 1;
357 vty_config = 1;
358 }
359 return vty->config;
360}
361
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200362/*! Unlock the configuration from a given VTY
Harald Welte7acb30c2011-08-17 17:13:48 +0200363 * \param[in] vty VTY from which the configuration shall be unlocked
364 * \returns 0 in case of success
365 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200366int vty_config_unlock(struct vty *vty)
367{
368 if (vty_config == 1 && vty->config == 1) {
369 vty->config = 0;
370 vty_config = 0;
371 }
372 return vty->config;
373}
374
375/* Say hello to vty interface. */
376void vty_hello(struct vty *vty)
377{
Harald Welte8b0d5b32012-06-03 12:41:24 +0200378 const char *app_name = "<unnamed>";
379
380 if (host.app_info->name)
381 app_name = host.app_info->name;
382
Harald Weltea2501a22018-03-23 19:33:27 +0100383 vty_out(vty, "Welcome to the %s VTY interface%s%s",
Harald Welte2d52d102012-06-16 17:01:29 +0800384 app_name, VTY_NEWLINE, VTY_NEWLINE);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200385
386 if (host.app_info->copyright)
Holger Hans Peter Freytherea8f2382012-08-02 21:26:02 +0200387 vty_out(vty, "%s", host.app_info->copyright);
Harald Welte8b0d5b32012-06-03 12:41:24 +0200388
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200389 if (host.motdfile) {
390 FILE *f;
391 char buf[4096];
392
393 f = fopen(host.motdfile, "r");
394 if (f) {
395 while (fgets(buf, sizeof(buf), f)) {
396 char *s;
397 /* work backwards to ignore trailling isspace() */
398 for (s = buf + strlen(buf);
399 (s > buf) && isspace(*(s - 1)); s--) ;
400 *s = '\0';
401 vty_out(vty, "%s%s", buf, VTY_NEWLINE);
402 }
403 fclose(f);
404 } else
405 vty_out(vty, "MOTD file not found%s", VTY_NEWLINE);
406 } else if (host.motd)
407 vty_out(vty, "%s", host.motd);
408}
409
410/* Put out prompt and wait input from user. */
411static void vty_prompt(struct vty *vty)
412{
413 struct utsname names;
414 const char *hostname;
415
416 if (vty->type == VTY_TERM) {
Harald Weltedf327f62010-12-24 15:10:14 +0100417 hostname = host.app_info->name;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200418 if (!hostname) {
419 uname(&names);
420 hostname = names.nodename;
421 }
422 vty_out(vty, cmd_prompt(vty->node), hostname);
423 }
424}
425
426/* Command execution over the vty interface. */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700427static int vty_command(struct vty *vty)
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200428{
429 int ret;
430 vector vline;
431
432 /* Split readline string up into the vector */
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700433 vline = cmd_make_strvec(vty->buf);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200434
435 if (vline == NULL)
436 return CMD_SUCCESS;
437
438 ret = cmd_execute_command(vline, vty, NULL, 0);
439 if (ret != CMD_SUCCESS)
440 switch (ret) {
441 case CMD_WARNING:
442 if (vty->type == VTY_FILE)
443 vty_out(vty, "Warning...%s", VTY_NEWLINE);
444 break;
445 case CMD_ERR_AMBIGUOUS:
446 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
447 break;
448 case CMD_ERR_NO_MATCH:
449 vty_out(vty, "%% Unknown command.%s", VTY_NEWLINE);
450 break;
451 case CMD_ERR_INCOMPLETE:
452 vty_out(vty, "%% Command incomplete.%s", VTY_NEWLINE);
453 break;
454 }
455 cmd_free_strvec(vline);
456
457 return ret;
458}
459
460static const char telnet_backward_char = 0x08;
461static const char telnet_space_char = ' ';
462
463/* Basic function to write buffer to vty. */
464static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
465{
466 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
467 return;
468
469 /* Should we do buffering here ? And make vty_flush (vty) ? */
470 buffer_put(vty->obuf, buf, nbytes);
471}
472
473/* Ensure length of input buffer. Is buffer is short, double it. */
474static void vty_ensure(struct vty *vty, int length)
475{
476 if (vty->max <= length) {
477 vty->max *= 2;
478 vty->buf = talloc_realloc_size(vty, vty->buf, vty->max);
479 // FIXME: check return
480 }
481}
482
483/* Basic function to insert character into vty. */
484static void vty_self_insert(struct vty *vty, char c)
485{
486 int i;
487 int length;
488
489 vty_ensure(vty, vty->length + 1);
490 length = vty->length - vty->cp;
491 memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
492 vty->buf[vty->cp] = c;
493
494 vty_write(vty, &vty->buf[vty->cp], length + 1);
495 for (i = 0; i < length; i++)
496 vty_write(vty, &telnet_backward_char, 1);
497
498 vty->cp++;
499 vty->length++;
500}
501
502/* Self insert character 'c' in overwrite mode. */
503static void vty_self_insert_overwrite(struct vty *vty, char c)
504{
505 vty_ensure(vty, vty->length + 1);
506 vty->buf[vty->cp++] = c;
507
508 if (vty->cp > vty->length)
509 vty->length++;
510
511 if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
512 return;
513
514 vty_write(vty, &c, 1);
515}
516
517/* Insert a word into vty interface with overwrite mode. */
518static void vty_insert_word_overwrite(struct vty *vty, char *str)
519{
520 int len = strlen(str);
521 vty_write(vty, str, len);
522 strcpy(&vty->buf[vty->cp], str);
523 vty->cp += len;
524 vty->length = vty->cp;
525}
526
527/* Forward character. */
528static void vty_forward_char(struct vty *vty)
529{
530 if (vty->cp < vty->length) {
531 vty_write(vty, &vty->buf[vty->cp], 1);
532 vty->cp++;
533 }
534}
535
536/* Backward character. */
537static void vty_backward_char(struct vty *vty)
538{
539 if (vty->cp > 0) {
540 vty->cp--;
541 vty_write(vty, &telnet_backward_char, 1);
542 }
543}
544
545/* Move to the beginning of the line. */
546static void vty_beginning_of_line(struct vty *vty)
547{
548 while (vty->cp)
549 vty_backward_char(vty);
550}
551
552/* Move to the end of the line. */
553static void vty_end_of_line(struct vty *vty)
554{
555 while (vty->cp < vty->length)
556 vty_forward_char(vty);
557}
558
559/* Add current command line to the history buffer. */
560static void vty_hist_add(struct vty *vty)
561{
562 int index;
563
564 if (vty->length == 0)
565 return;
566
567 index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
568
569 /* Ignore the same string as previous one. */
570 if (vty->hist[index])
571 if (strcmp(vty->buf, vty->hist[index]) == 0) {
572 vty->hp = vty->hindex;
573 return;
574 }
575
576 /* Insert history entry. */
577 if (vty->hist[vty->hindex])
578 talloc_free(vty->hist[vty->hindex]);
579 vty->hist[vty->hindex] = talloc_strdup(vty, vty->buf);
580
581 /* History index rotation. */
582 vty->hindex++;
583 if (vty->hindex == VTY_MAXHIST)
584 vty->hindex = 0;
585
586 vty->hp = vty->hindex;
587}
588
589/* Get telnet window size. */
590static int
591vty_telnet_option (struct vty *vty, unsigned char *buf, int nbytes)
592{
593#ifdef TELNET_OPTION_DEBUG
594 int i;
595
596 for (i = 0; i < nbytes; i++)
597 {
598 switch (buf[i])
599 {
600 case IAC:
601 vty_out (vty, "IAC ");
602 break;
603 case WILL:
604 vty_out (vty, "WILL ");
605 break;
606 case WONT:
607 vty_out (vty, "WONT ");
608 break;
609 case DO:
610 vty_out (vty, "DO ");
611 break;
612 case DONT:
613 vty_out (vty, "DONT ");
614 break;
615 case SB:
616 vty_out (vty, "SB ");
617 break;
618 case SE:
619 vty_out (vty, "SE ");
620 break;
621 case TELOPT_ECHO:
622 vty_out (vty, "TELOPT_ECHO %s", VTY_NEWLINE);
623 break;
624 case TELOPT_SGA:
625 vty_out (vty, "TELOPT_SGA %s", VTY_NEWLINE);
626 break;
627 case TELOPT_NAWS:
628 vty_out (vty, "TELOPT_NAWS %s", VTY_NEWLINE);
629 break;
630 default:
631 vty_out (vty, "%x ", buf[i]);
632 break;
633 }
634 }
635 vty_out (vty, "%s", VTY_NEWLINE);
636
637#endif /* TELNET_OPTION_DEBUG */
638
639 switch (buf[0])
640 {
641 case SB:
642 vty->sb_len = 0;
643 vty->iac_sb_in_progress = 1;
644 return 0;
645 break;
646 case SE:
647 {
648 if (!vty->iac_sb_in_progress)
649 return 0;
650
651 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0'))
652 {
653 vty->iac_sb_in_progress = 0;
654 return 0;
655 }
656 switch (vty->sb_buf[0])
657 {
658 case TELOPT_NAWS:
659 if (vty->sb_len != TELNET_NAWS_SB_LEN)
660 vty_out(vty,"RFC 1073 violation detected: telnet NAWS option "
661 "should send %d characters, but we received %lu",
Harald Welte78a870e2014-03-11 10:45:38 +0100662 TELNET_NAWS_SB_LEN, (unsigned long)vty->sb_len);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200663 else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
664 vty_out(vty, "Bug detected: sizeof(vty->sb_buf) %lu < %d, "
665 "too small to handle the telnet NAWS option",
Harald Welte78a870e2014-03-11 10:45:38 +0100666 (unsigned long)sizeof(vty->sb_buf), TELNET_NAWS_SB_LEN);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200667 else
668 {
669 vty->width = ((vty->sb_buf[1] << 8)|vty->sb_buf[2]);
670 vty->height = ((vty->sb_buf[3] << 8)|vty->sb_buf[4]);
671#ifdef TELNET_OPTION_DEBUG
672 vty_out(vty, "TELNET NAWS window size negotiation completed: "
673 "width %d, height %d%s",
674 vty->width, vty->height, VTY_NEWLINE);
675#endif
676 }
677 break;
678 }
679 vty->iac_sb_in_progress = 0;
680 return 0;
681 break;
682 }
683 default:
684 break;
685 }
686 return 1;
687}
688
689/* Execute current command line. */
690static int vty_execute(struct vty *vty)
691{
692 int ret;
693
694 ret = CMD_SUCCESS;
695
696 switch (vty->node) {
697 case AUTH_NODE:
698 case AUTH_ENABLE_NODE:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700699 vty_auth(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200700 break;
701 default:
Vadim Yanitskiy8c00f9d2019-07-30 10:29:38 +0700702 ret = vty_command(vty);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200703 if (vty->type == VTY_TERM)
704 vty_hist_add(vty);
705 break;
706 }
707
708 /* Clear command line buffer. */
709 vty->cp = vty->length = 0;
710 vty_clear_buf(vty);
711
712 if (vty->status != VTY_CLOSE)
713 vty_prompt(vty);
714
715 return ret;
716}
717
718/* Send WILL TELOPT_ECHO to remote server. */
719static void
720vty_will_echo (struct vty *vty)
721{
722 unsigned char cmd[] = { IAC, WILL, TELOPT_ECHO, '\0' };
723 vty_out (vty, "%s", cmd);
724}
725
726/* Make suppress Go-Ahead telnet option. */
727static void
728vty_will_suppress_go_ahead (struct vty *vty)
729{
730 unsigned char cmd[] = { IAC, WILL, TELOPT_SGA, '\0' };
731 vty_out (vty, "%s", cmd);
732}
733
734/* Make don't use linemode over telnet. */
735static void
736vty_dont_linemode (struct vty *vty)
737{
738 unsigned char cmd[] = { IAC, DONT, TELOPT_LINEMODE, '\0' };
739 vty_out (vty, "%s", cmd);
740}
741
742/* Use window size. */
743static void
744vty_do_window_size (struct vty *vty)
745{
746 unsigned char cmd[] = { IAC, DO, TELOPT_NAWS, '\0' };
747 vty_out (vty, "%s", cmd);
748}
749
750static void vty_kill_line_from_beginning(struct vty *);
751static void vty_redraw_line(struct vty *);
752
753/* Print command line history. This function is called from
754 vty_next_line and vty_previous_line. */
755static void vty_history_print(struct vty *vty)
756{
757 int length;
758
759 vty_kill_line_from_beginning(vty);
760
761 /* Get previous line from history buffer */
762 length = strlen(vty->hist[vty->hp]);
763 memcpy(vty->buf, vty->hist[vty->hp], length);
764 vty->cp = vty->length = length;
765
766 /* Redraw current line */
767 vty_redraw_line(vty);
768}
769
770/* Show next command line history. */
771static void vty_next_line(struct vty *vty)
772{
773 int try_index;
774
775 if (vty->hp == vty->hindex)
776 return;
777
778 /* Try is there history exist or not. */
779 try_index = vty->hp;
780 if (try_index == (VTY_MAXHIST - 1))
781 try_index = 0;
782 else
783 try_index++;
784
785 /* If there is not history return. */
786 if (vty->hist[try_index] == NULL)
787 return;
788 else
789 vty->hp = try_index;
790
791 vty_history_print(vty);
792}
793
794/* Show previous command line history. */
795static void vty_previous_line(struct vty *vty)
796{
797 int try_index;
798
799 try_index = vty->hp;
800 if (try_index == 0)
801 try_index = VTY_MAXHIST - 1;
802 else
803 try_index--;
804
805 if (vty->hist[try_index] == NULL)
806 return;
807 else
808 vty->hp = try_index;
809
810 vty_history_print(vty);
811}
812
813/* This function redraw all of the command line character. */
814static void vty_redraw_line(struct vty *vty)
815{
816 vty_write(vty, vty->buf, vty->length);
817 vty->cp = vty->length;
818}
819
820/* Forward word. */
821static void vty_forward_word(struct vty *vty)
822{
823 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
824 vty_forward_char(vty);
825
826 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
827 vty_forward_char(vty);
828}
829
830/* Backward word without skipping training space. */
831static void vty_backward_pure_word(struct vty *vty)
832{
833 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
834 vty_backward_char(vty);
835}
836
837/* Backward word. */
838static void vty_backward_word(struct vty *vty)
839{
840 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
841 vty_backward_char(vty);
842
843 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
844 vty_backward_char(vty);
845}
846
847/* When '^D' is typed at the beginning of the line we move to the down
848 level. */
849static void vty_down_level(struct vty *vty)
850{
851 vty_out(vty, "%s", VTY_NEWLINE);
Andreas.Eversbergf948dbc2011-11-10 23:09:35 +0100852 /* call the exit function of the specific node */
853 if (vty->node > CONFIG_NODE)
854 vty_go_parent(vty);
855 else
856 (*config_exit_cmd.func) (NULL, vty, 0, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200857 vty_prompt(vty);
858 vty->cp = 0;
859}
860
861/* When '^Z' is received from vty, move down to the enable mode. */
862static void vty_end_config(struct vty *vty)
863{
864 vty_out(vty, "%s", VTY_NEWLINE);
865
866 /* FIXME: we need to call the exit function of the specific node
867 * in question, not this generic one that doesn't know all nodes */
868 switch (vty->node) {
869 case VIEW_NODE:
870 case ENABLE_NODE:
871 /* Nothing to do. */
872 break;
873 case CONFIG_NODE:
874 case VTY_NODE:
875 vty_config_unlock(vty);
876 vty->node = ENABLE_NODE;
877 break;
Harald Welte28222962011-02-18 20:37:04 +0100878 case CFG_LOG_NODE:
879 vty->node = CONFIG_NODE;
880 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +0200881 default:
882 /* Unknown node, we have to ignore it. */
883 break;
884 }
885
886 vty_prompt(vty);
887 vty->cp = 0;
888}
889
890/* Delete a charcter at the current point. */
891static void vty_delete_char(struct vty *vty)
892{
893 int i;
894 int size;
895
896 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
897 return;
898
899 if (vty->length == 0) {
900 vty_down_level(vty);
901 return;
902 }
903
904 if (vty->cp == vty->length)
905 return; /* completion need here? */
906
907 size = vty->length - vty->cp;
908
909 vty->length--;
910 memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
911 vty->buf[vty->length] = '\0';
912
913 vty_write(vty, &vty->buf[vty->cp], size - 1);
914 vty_write(vty, &telnet_space_char, 1);
915
916 for (i = 0; i < size; i++)
917 vty_write(vty, &telnet_backward_char, 1);
918}
919
920/* Delete a character before the point. */
921static void vty_delete_backward_char(struct vty *vty)
922{
923 if (vty->cp == 0)
924 return;
925
926 vty_backward_char(vty);
927 vty_delete_char(vty);
928}
929
930/* Kill rest of line from current point. */
931static void vty_kill_line(struct vty *vty)
932{
933 int i;
934 int size;
935
936 size = vty->length - vty->cp;
937
938 if (size == 0)
939 return;
940
941 for (i = 0; i < size; i++)
942 vty_write(vty, &telnet_space_char, 1);
943 for (i = 0; i < size; i++)
944 vty_write(vty, &telnet_backward_char, 1);
945
946 memset(&vty->buf[vty->cp], 0, size);
947 vty->length = vty->cp;
948}
949
950/* Kill line from the beginning. */
951static void vty_kill_line_from_beginning(struct vty *vty)
952{
953 vty_beginning_of_line(vty);
954 vty_kill_line(vty);
955}
956
957/* Delete a word before the point. */
958static void vty_forward_kill_word(struct vty *vty)
959{
960 while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
961 vty_delete_char(vty);
962 while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
963 vty_delete_char(vty);
964}
965
966/* Delete a word before the point. */
967static void vty_backward_kill_word(struct vty *vty)
968{
969 while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
970 vty_delete_backward_char(vty);
971 while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
972 vty_delete_backward_char(vty);
973}
974
975/* Transpose chars before or at the point. */
976static void vty_transpose_chars(struct vty *vty)
977{
978 char c1, c2;
979
980 /* If length is short or point is near by the beginning of line then
981 return. */
982 if (vty->length < 2 || vty->cp < 1)
983 return;
984
985 /* In case of point is located at the end of the line. */
986 if (vty->cp == vty->length) {
987 c1 = vty->buf[vty->cp - 1];
988 c2 = vty->buf[vty->cp - 2];
989
990 vty_backward_char(vty);
991 vty_backward_char(vty);
992 vty_self_insert_overwrite(vty, c1);
993 vty_self_insert_overwrite(vty, c2);
994 } else {
995 c1 = vty->buf[vty->cp];
996 c2 = vty->buf[vty->cp - 1];
997
998 vty_backward_char(vty);
999 vty_self_insert_overwrite(vty, c1);
1000 vty_self_insert_overwrite(vty, c2);
1001 }
1002}
1003
1004/* Do completion at vty interface. */
1005static void vty_complete_command(struct vty *vty)
1006{
1007 int i;
1008 int ret;
1009 char **matched = NULL;
1010 vector vline;
1011
1012 if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
1013 return;
1014
1015 vline = cmd_make_strvec(vty->buf);
1016 if (vline == NULL)
1017 return;
1018
1019 /* In case of 'help \t'. */
1020 if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001021 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001022
1023 matched = cmd_complete_command(vline, vty, &ret);
1024
1025 cmd_free_strvec(vline);
1026
1027 vty_out(vty, "%s", VTY_NEWLINE);
1028 switch (ret) {
1029 case CMD_ERR_AMBIGUOUS:
1030 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1031 vty_prompt(vty);
1032 vty_redraw_line(vty);
1033 break;
1034 case CMD_ERR_NO_MATCH:
1035 /* vty_out (vty, "%% There is no matched command.%s", VTY_NEWLINE); */
1036 vty_prompt(vty);
1037 vty_redraw_line(vty);
1038 break;
1039 case CMD_COMPLETE_FULL_MATCH:
1040 vty_prompt(vty);
1041 vty_redraw_line(vty);
1042 vty_backward_pure_word(vty);
1043 vty_insert_word_overwrite(vty, matched[0]);
1044 vty_self_insert(vty, ' ');
1045 talloc_free(matched[0]);
1046 break;
1047 case CMD_COMPLETE_MATCH:
1048 vty_prompt(vty);
1049 vty_redraw_line(vty);
1050 vty_backward_pure_word(vty);
1051 vty_insert_word_overwrite(vty, matched[0]);
1052 talloc_free(matched[0]);
1053 break;
1054 case CMD_COMPLETE_LIST_MATCH:
1055 for (i = 0; matched[i] != NULL; i++) {
1056 if (i != 0 && ((i % 6) == 0))
1057 vty_out(vty, "%s", VTY_NEWLINE);
1058 vty_out(vty, "%-10s ", matched[i]);
1059 talloc_free(matched[i]);
1060 }
1061 vty_out(vty, "%s", VTY_NEWLINE);
1062
1063 vty_prompt(vty);
1064 vty_redraw_line(vty);
1065 break;
1066 case CMD_ERR_NOTHING_TODO:
1067 vty_prompt(vty);
1068 vty_redraw_line(vty);
1069 break;
1070 default:
1071 break;
1072 }
1073 if (matched)
1074 vector_only_index_free(matched);
1075}
1076
1077static void
1078vty_describe_fold(struct vty *vty, int cmd_width,
1079 unsigned int desc_width, struct desc *desc)
1080{
1081 char *buf;
1082 const char *cmd, *p;
1083 int pos;
1084
1085 cmd = desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd;
1086
1087 if (desc_width <= 0) {
1088 vty_out(vty, " %-*s %s%s", cmd_width, cmd, desc->str,
1089 VTY_NEWLINE);
1090 return;
1091 }
1092
1093 buf = _talloc_zero(vty, strlen(desc->str) + 1, "describe_fold");
1094 if (!buf)
1095 return;
1096
1097 for (p = desc->str; strlen(p) > desc_width; p += pos + 1) {
1098 for (pos = desc_width; pos > 0; pos--)
1099 if (*(p + pos) == ' ')
1100 break;
1101
1102 if (pos == 0)
1103 break;
1104
1105 strncpy(buf, p, pos);
1106 buf[pos] = '\0';
1107 vty_out(vty, " %-*s %s%s", cmd_width, cmd, buf, VTY_NEWLINE);
1108
1109 cmd = "";
1110 }
1111
1112 vty_out(vty, " %-*s %s%s", cmd_width, cmd, p, VTY_NEWLINE);
1113
1114 talloc_free(buf);
1115}
1116
1117/* Describe matched command function. */
1118static void vty_describe_command(struct vty *vty)
1119{
1120 int ret;
1121 vector vline;
1122 vector describe;
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001123 unsigned int i, cmd_width, desc_width;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001124 struct desc *desc, *desc_cr = NULL;
1125
1126 vline = cmd_make_strvec(vty->buf);
1127
1128 /* In case of '> ?'. */
1129 if (vline == NULL) {
1130 vline = vector_init(1);
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001131 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001132 } else if (isspace((int)vty->buf[vty->length - 1]))
Holger Hans Peter Freytherd56c3cb2015-11-09 16:16:00 +00001133 vector_set(vline, NULL);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001134
1135 describe = cmd_describe_command(vline, vty, &ret);
1136
1137 vty_out(vty, "%s", VTY_NEWLINE);
1138
1139 /* Ambiguous error. */
1140 switch (ret) {
1141 case CMD_ERR_AMBIGUOUS:
1142 cmd_free_strvec(vline);
1143 vty_out(vty, "%% Ambiguous command.%s", VTY_NEWLINE);
1144 vty_prompt(vty);
1145 vty_redraw_line(vty);
1146 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001147 case CMD_ERR_NO_MATCH:
1148 cmd_free_strvec(vline);
1149 vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE);
1150 vty_prompt(vty);
1151 vty_redraw_line(vty);
1152 return;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001153 }
1154
1155 /* Get width of command string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001156 cmd_width = 0;
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001157 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001158 if ((desc = vector_slot(describe, i)) != NULL) {
1159 unsigned int len;
1160
1161 if (desc->cmd[0] == '\0')
1162 continue;
1163
1164 len = strlen(desc->cmd);
1165 if (desc->cmd[0] == '.')
1166 len--;
1167
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001168 if (cmd_width < len)
1169 cmd_width = len;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001170 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001171 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001172
1173 /* Get width of description string. */
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001174 desc_width = vty->width - (cmd_width + 6);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001175
1176 /* Print out description. */
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001177 for (i = 0; i < vector_active(describe); i++) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001178 if ((desc = vector_slot(describe, i)) != NULL) {
1179 if (desc->cmd[0] == '\0')
1180 continue;
1181
1182 if (strcmp(desc->cmd, "<cr>") == 0) {
1183 desc_cr = desc;
1184 continue;
1185 }
1186
1187 if (!desc->str)
1188 vty_out(vty, " %-s%s",
1189 desc->cmd[0] ==
1190 '.' ? desc->cmd + 1 : desc->cmd,
1191 VTY_NEWLINE);
1192 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001193 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001194 desc->cmd[0] ==
1195 '.' ? desc->cmd + 1 : desc->cmd,
1196 desc->str, VTY_NEWLINE);
1197 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001198 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001199
1200#if 0
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001201 vty_out(vty, " %-*s %s%s", cmd_width
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001202 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1203 desc->str ? desc->str : "", VTY_NEWLINE);
1204#endif /* 0 */
1205 }
Vadim Yanitskiy349d3482020-09-21 23:34:12 +07001206 }
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001207
1208 if ((desc = desc_cr)) {
1209 if (!desc->str)
1210 vty_out(vty, " %-s%s",
1211 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1212 VTY_NEWLINE);
1213 else if (desc_width >= strlen(desc->str))
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001214 vty_out(vty, " %-*s %s%s", cmd_width,
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001215 desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
1216 desc->str, VTY_NEWLINE);
1217 else
Vadim Yanitskiyf909d332020-09-21 23:30:14 +07001218 vty_describe_fold(vty, cmd_width, desc_width, desc);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001219 }
1220
1221 cmd_free_strvec(vline);
1222 vector_free(describe);
1223
1224 vty_prompt(vty);
1225 vty_redraw_line(vty);
1226}
1227
1228/* ^C stop current input and do not add command line to the history. */
1229static void vty_stop_input(struct vty *vty)
1230{
1231 vty->cp = vty->length = 0;
1232 vty_clear_buf(vty);
1233 vty_out(vty, "%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001234 vty_prompt(vty);
1235
1236 /* Set history pointer to the latest one. */
1237 vty->hp = vty->hindex;
1238}
1239
1240#define CONTROL(X) ((X) - '@')
1241#define VTY_NORMAL 0
1242#define VTY_PRE_ESCAPE 1
1243#define VTY_ESCAPE 2
1244
1245/* Escape character command map. */
1246static void vty_escape_map(unsigned char c, struct vty *vty)
1247{
1248 switch (c) {
1249 case ('A'):
1250 vty_previous_line(vty);
1251 break;
1252 case ('B'):
1253 vty_next_line(vty);
1254 break;
1255 case ('C'):
1256 vty_forward_char(vty);
1257 break;
1258 case ('D'):
1259 vty_backward_char(vty);
1260 break;
1261 default:
1262 break;
1263 }
1264
1265 /* Go back to normal mode. */
1266 vty->escape = VTY_NORMAL;
1267}
1268
1269/* Quit print out to the buffer. */
1270static void vty_buffer_reset(struct vty *vty)
1271{
1272 buffer_reset(vty->obuf);
1273 vty_prompt(vty);
1274 vty_redraw_line(vty);
1275}
1276
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001277/*! Read data via vty socket. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001278int vty_read(struct vty *vty)
1279{
1280 int i;
1281 int nbytes;
1282 unsigned char buf[VTY_READ_BUFSIZ];
1283
1284 int vty_sock = vty->fd;
1285
1286 /* Read raw data from socket */
1287 if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
1288 if (nbytes < 0) {
1289 if (ERRNO_IO_RETRY(errno)) {
1290 vty_event(VTY_READ, vty_sock, vty);
1291 return 0;
1292 }
1293 }
1294 buffer_reset(vty->obuf);
1295 vty->status = VTY_CLOSE;
1296 }
1297
1298 for (i = 0; i < nbytes; i++) {
1299 if (buf[i] == IAC) {
1300 if (!vty->iac) {
1301 vty->iac = 1;
1302 continue;
1303 } else {
1304 vty->iac = 0;
1305 }
1306 }
1307
1308 if (vty->iac_sb_in_progress && !vty->iac) {
1309 if (vty->sb_len < sizeof(vty->sb_buf))
1310 vty->sb_buf[vty->sb_len] = buf[i];
1311 vty->sb_len++;
1312 continue;
1313 }
1314
1315 if (vty->iac) {
1316 /* In case of telnet command */
1317 int ret = 0;
1318 ret = vty_telnet_option(vty, buf + i, nbytes - i);
1319 vty->iac = 0;
1320 i += ret;
1321 continue;
1322 }
1323
1324 if (vty->status == VTY_MORE) {
1325 switch (buf[i]) {
1326 case CONTROL('C'):
1327 case 'q':
1328 case 'Q':
1329 vty_buffer_reset(vty);
1330 break;
1331#if 0 /* More line does not work for "show ip bgp". */
1332 case '\n':
1333 case '\r':
1334 vty->status = VTY_MORELINE;
1335 break;
1336#endif
1337 default:
1338 break;
1339 }
1340 continue;
1341 }
1342
1343 /* Escape character. */
1344 if (vty->escape == VTY_ESCAPE) {
1345 vty_escape_map(buf[i], vty);
1346 continue;
1347 }
1348
1349 /* Pre-escape status. */
1350 if (vty->escape == VTY_PRE_ESCAPE) {
1351 switch (buf[i]) {
1352 case '[':
1353 vty->escape = VTY_ESCAPE;
1354 break;
1355 case 'b':
1356 vty_backward_word(vty);
1357 vty->escape = VTY_NORMAL;
1358 break;
1359 case 'f':
1360 vty_forward_word(vty);
1361 vty->escape = VTY_NORMAL;
1362 break;
1363 case 'd':
1364 vty_forward_kill_word(vty);
1365 vty->escape = VTY_NORMAL;
1366 break;
1367 case CONTROL('H'):
1368 case 0x7f:
1369 vty_backward_kill_word(vty);
1370 vty->escape = VTY_NORMAL;
1371 break;
1372 default:
1373 vty->escape = VTY_NORMAL;
1374 break;
1375 }
1376 continue;
1377 }
1378
1379 switch (buf[i]) {
1380 case CONTROL('A'):
1381 vty_beginning_of_line(vty);
1382 break;
1383 case CONTROL('B'):
1384 vty_backward_char(vty);
1385 break;
1386 case CONTROL('C'):
1387 vty_stop_input(vty);
1388 break;
1389 case CONTROL('D'):
1390 vty_delete_char(vty);
1391 break;
1392 case CONTROL('E'):
1393 vty_end_of_line(vty);
1394 break;
1395 case CONTROL('F'):
1396 vty_forward_char(vty);
1397 break;
1398 case CONTROL('H'):
1399 case 0x7f:
1400 vty_delete_backward_char(vty);
1401 break;
1402 case CONTROL('K'):
1403 vty_kill_line(vty);
1404 break;
1405 case CONTROL('N'):
1406 vty_next_line(vty);
1407 break;
1408 case CONTROL('P'):
1409 vty_previous_line(vty);
1410 break;
1411 case CONTROL('T'):
1412 vty_transpose_chars(vty);
1413 break;
1414 case CONTROL('U'):
1415 vty_kill_line_from_beginning(vty);
1416 break;
1417 case CONTROL('W'):
1418 vty_backward_kill_word(vty);
1419 break;
1420 case CONTROL('Z'):
1421 vty_end_config(vty);
1422 break;
1423 case '\n':
1424 case '\r':
1425 vty_out(vty, "%s", VTY_NEWLINE);
Vadim Yanitskiy757dea82019-07-27 23:57:12 +07001426 /* '\0'-terminate the command buffer */
1427 vty->buf[vty->length] = '\0';
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001428 vty_execute(vty);
1429 break;
1430 case '\t':
1431 vty_complete_command(vty);
1432 break;
1433 case '?':
1434 if (vty->node == AUTH_NODE
1435 || vty->node == AUTH_ENABLE_NODE)
1436 vty_self_insert(vty, buf[i]);
1437 else
1438 vty_describe_command(vty);
1439 break;
1440 case '\033':
1441 if (i + 1 < nbytes && buf[i + 1] == '[') {
1442 vty->escape = VTY_ESCAPE;
1443 i++;
1444 } else
1445 vty->escape = VTY_PRE_ESCAPE;
1446 break;
1447 default:
1448 if (buf[i] > 31 && buf[i] < 127)
1449 vty_self_insert(vty, buf[i]);
1450 break;
1451 }
1452 }
1453
1454 /* Check status. */
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001455 if (vty->status == VTY_CLOSE) {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001456 vty_close(vty);
Holger Hans Peter Freythereb55e6a2014-07-01 19:39:26 +02001457 return -EBADF;
Daniel Willmann77ab2f72014-05-21 15:08:19 +02001458 } else {
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001459 vty_event(VTY_WRITE, vty_sock, vty);
1460 vty_event(VTY_READ, vty_sock, vty);
1461 }
1462 return 0;
1463}
1464
1465/* Read up configuration file */
1466static int
1467vty_read_file(FILE *confp, void *priv)
1468{
1469 int ret;
1470 struct vty *vty;
1471
1472 vty = vty_new();
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001473 vty->type = VTY_FILE;
1474 vty->node = CONFIG_NODE;
1475 vty->priv = priv;
1476
Vadim Yanitskiyb639b4d2019-11-21 02:20:11 +07001477 /* By default, write to stderr. Otherwise, during parsing of the logging
1478 * configuration, all invocations to vty_out() would make the process
1479 * write() to its own stdin (fd=0)! */
1480 vty->fd = fileno(stderr);
1481
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001482 ret = config_from_file(vty, confp);
1483
1484 if (ret != CMD_SUCCESS) {
1485 switch (ret) {
1486 case CMD_ERR_AMBIGUOUS:
1487 fprintf(stderr, "Ambiguous command.\n");
1488 break;
1489 case CMD_ERR_NO_MATCH:
1490 fprintf(stderr, "There is no such command.\n");
1491 break;
Neels Hofmeyr4a31ffa2017-09-07 03:08:06 +02001492 case CMD_ERR_INVALID_INDENT:
1493 fprintf(stderr,
1494 "Inconsistent indentation -- leading whitespace must match adjacent lines, and\n"
1495 "indentation must reflect child node levels. A mix of tabs and spaces is\n"
1496 "allowed, but their sequence must not change within a child block.\n");
1497 break;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001498 }
Keith03516d62017-09-04 11:19:13 +02001499 fprintf(stderr, "Error occurred during reading the below "
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001500 "line:\n%s\n", vty->buf);
1501 vty_close(vty);
1502 return -EINVAL;
1503 }
1504
1505 vty_close(vty);
1506 return 0;
1507}
1508
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001509/*! Create new vty structure. */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001510struct vty *
1511vty_create (int vty_sock, void *priv)
1512{
1513 struct vty *vty;
1514
Alexander Couzensab383e62018-07-17 19:02:34 +02001515 struct termios t = {};
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001516
1517 tcgetattr(vty_sock, &t);
1518 cfmakeraw(&t);
1519 tcsetattr(vty_sock, TCSANOW, &t);
1520
1521 /* Allocate new vty structure and set up default values. */
1522 vty = vty_new ();
1523 vty->fd = vty_sock;
1524 vty->priv = priv;
1525 vty->type = VTY_TERM;
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001526 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001527 {
1528 if (host.advanced)
1529 vty->node = ENABLE_NODE;
1530 else
1531 vty->node = VIEW_NODE;
1532 }
1533 else
1534 vty->node = AUTH_NODE;
1535 vty->fail = 0;
1536 vty->cp = 0;
1537 vty_clear_buf (vty);
1538 vty->length = 0;
1539 memset (vty->hist, 0, sizeof (vty->hist));
1540 vty->hp = 0;
1541 vty->hindex = 0;
1542 vector_set_index (vtyvec, vty_sock, vty);
1543 vty->status = VTY_NORMAL;
1544 if (host.lines >= 0)
1545 vty->lines = host.lines;
1546 else
1547 vty->lines = -1;
1548
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001549 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001550 {
1551 /* Vty is not available if password isn't set. */
1552 if (host.password == NULL && host.password_encrypt == NULL)
1553 {
1554 vty_out (vty, "Vty password is not set.%s", VTY_NEWLINE);
1555 vty->status = VTY_CLOSE;
1556 vty_close (vty);
1557 return NULL;
1558 }
1559 }
1560
1561 /* Say hello to the world. */
1562 vty_hello (vty);
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001563 if (password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001564 vty_out (vty, "%sUser Access Verification%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1565
1566 /* Setting up terminal. */
1567 vty_will_echo (vty);
1568 vty_will_suppress_go_ahead (vty);
1569
1570 vty_dont_linemode (vty);
1571 vty_do_window_size (vty);
1572 /* vty_dont_lflow_ahead (vty); */
1573
1574 vty_prompt (vty);
1575
1576 /* Add read/write thread. */
1577 vty_event (VTY_WRITE, vty_sock, vty);
1578 vty_event (VTY_READ, vty_sock, vty);
1579
1580 return vty;
1581}
1582
1583DEFUN(config_who, config_who_cmd, "who", "Display who is on vty\n")
1584{
1585 unsigned int i;
1586 struct vty *v;
1587
1588 for (i = 0; i < vector_active(vtyvec); i++)
1589 if ((v = vector_slot(vtyvec, i)) != NULL)
1590 vty_out(vty, "%svty[%d] %s",
1591 v->config ? "*" : " ", i, VTY_NEWLINE);
1592 return CMD_SUCCESS;
1593}
1594
1595/* Move to vty configuration mode. */
1596DEFUN(line_vty,
1597 line_vty_cmd,
1598 "line vty", "Configure a terminal line\n" "Virtual terminal\n")
1599{
1600 vty->node = VTY_NODE;
1601 return CMD_SUCCESS;
1602}
1603
1604/* vty login. */
1605DEFUN(vty_login, vty_login_cmd, "login", "Enable password checking\n")
1606{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001607 password_check = 1;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001608 return CMD_SUCCESS;
1609}
1610
1611DEFUN(no_vty_login,
1612 no_vty_login_cmd, "no login", NO_STR "Enable password checking\n")
1613{
Diego Elio Pettenòf3ba8ab2012-06-29 13:01:28 -07001614 password_check = 0;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001615 return CMD_SUCCESS;
1616}
1617
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001618/* vty bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001619DEFUN(vty_bind, vty_bind_cmd, "bind A.B.C.D [<0-65535>]",
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001620 "Accept VTY telnet connections on local interface\n"
Harald Welte34d54b22018-12-23 10:26:19 +01001621 "Local interface IP address (default: " VTY_BIND_ADDR_DEFAULT ")\n"
1622 "Local TCP port number\n")
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001623{
1624 talloc_free((void*)vty_bind_addr);
1625 vty_bind_addr = talloc_strdup(tall_vty_ctx, argv[0]);
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001626 vty_bind_port = argc > 1 ? atoi(argv[1]) : -1;
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001627 return CMD_SUCCESS;
1628}
1629
1630const char *vty_get_bind_addr(void)
1631{
1632 if (!vty_bind_addr)
1633 return VTY_BIND_ADDR_DEFAULT;
1634 return vty_bind_addr;
1635}
1636
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001637int vty_get_bind_port(int default_port)
1638{
1639 if (vty_bind_port >= 0)
1640 return vty_bind_port;
1641 return default_port;
1642}
1643
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001644DEFUN(service_advanced_vty,
1645 service_advanced_vty_cmd,
1646 "service advanced-vty",
1647 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1648{
1649 host.advanced = 1;
1650 return CMD_SUCCESS;
1651}
1652
1653DEFUN(no_service_advanced_vty,
1654 no_service_advanced_vty_cmd,
1655 "no service advanced-vty",
1656 NO_STR
1657 "Set up miscellaneous service\n" "Enable advanced mode vty interface\n")
1658{
1659 host.advanced = 0;
1660 return CMD_SUCCESS;
1661}
1662
1663DEFUN(terminal_monitor,
1664 terminal_monitor_cmd,
1665 "terminal monitor",
1666 "Set terminal line parameters\n"
1667 "Copy debug output to the current terminal line\n")
1668{
1669 vty->monitor = 1;
1670 return CMD_SUCCESS;
1671}
1672
1673DEFUN(terminal_no_monitor,
1674 terminal_no_monitor_cmd,
1675 "terminal no monitor",
1676 "Set terminal line parameters\n"
1677 NO_STR "Copy debug output to the current terminal line\n")
1678{
1679 vty->monitor = 0;
1680 return CMD_SUCCESS;
1681}
1682
1683DEFUN(show_history,
1684 show_history_cmd,
1685 "show history", SHOW_STR "Display the session command history\n")
1686{
1687 int index;
1688
1689 for (index = vty->hindex + 1; index != vty->hindex;) {
1690 if (index == VTY_MAXHIST) {
1691 index = 0;
1692 continue;
1693 }
1694
1695 if (vty->hist[index] != NULL)
1696 vty_out(vty, " %s%s", vty->hist[index], VTY_NEWLINE);
1697
1698 index++;
1699 }
1700
1701 return CMD_SUCCESS;
1702}
1703
1704/* Display current configuration. */
1705static int vty_config_write(struct vty *vty)
1706{
1707 vty_out(vty, "line vty%s", VTY_NEWLINE);
1708
1709 /* login */
Alexander Huemere62651f2012-07-04 11:31:54 +02001710 if (!password_check)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001711 vty_out(vty, " no login%s", VTY_NEWLINE);
Mykola Shchetinin893e49e2018-08-03 16:44:07 +03001712 else
1713 vty_out(vty, " login%s", VTY_NEWLINE);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001714
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001715 /* bind */
Holger Hans Peter Freyther99ae4012018-12-15 17:36:41 +00001716 if (vty_bind_addr && (strcmp(vty_bind_addr, VTY_BIND_ADDR_DEFAULT) != 0 || vty_bind_port >= 0)) {
1717 if (vty_bind_port >= 0) {
1718 vty_out(vty, " bind %s %d%s", vty_bind_addr,
1719 vty_bind_port, VTY_NEWLINE);
1720 } else {
1721 vty_out(vty, " bind %s%s", vty_bind_addr, VTY_NEWLINE);
1722 }
1723 }
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001724
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001725 vty_out(vty, "!%s", VTY_NEWLINE);
1726
1727 return CMD_SUCCESS;
1728}
1729
1730struct cmd_node vty_node = {
1731 VTY_NODE,
1732 "%s(config-line)# ",
1733 1,
1734};
1735
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001736/*! Reset all VTY status. */
Harald Welte95b2b472011-07-16 11:58:09 +02001737void vty_reset(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001738{
1739 unsigned int i;
1740 struct vty *vty;
1741 struct thread *vty_serv_thread;
1742
1743 for (i = 0; i < vector_active(vtyvec); i++)
1744 if ((vty = vector_slot(vtyvec, i)) != NULL) {
1745 buffer_reset(vty->obuf);
1746 vty->status = VTY_CLOSE;
1747 vty_close(vty);
1748 }
1749
1750 for (i = 0; i < vector_active(Vvty_serv_thread); i++)
1751 if ((vty_serv_thread =
1752 vector_slot(Vvty_serv_thread, i)) != NULL) {
1753 //thread_cancel (vty_serv_thread);
1754 vector_slot(Vvty_serv_thread, i) = NULL;
1755 close(i);
1756 }
1757}
1758
1759static void vty_save_cwd(void)
1760{
1761 char cwd[MAXPATHLEN];
1762 char *c ;
1763
1764 c = getcwd(cwd, MAXPATHLEN);
1765
1766 if (!c) {
1767 if (chdir(SYSCONFDIR) != 0)
1768 perror("chdir failed");
1769 if (getcwd(cwd, MAXPATHLEN) == NULL)
1770 perror("getcwd failed");
1771 }
1772
1773 vty_cwd = _talloc_zero(tall_vty_ctx, strlen(cwd) + 1, "save_cwd");
1774 strcpy(vty_cwd, cwd);
1775}
1776
Harald Welte95b2b472011-07-16 11:58:09 +02001777char *vty_get_cwd(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001778{
1779 return vty_cwd;
1780}
1781
1782int vty_shell_serv(struct vty *vty)
1783{
1784 return vty->type == VTY_SHELL_SERV ? 1 : 0;
1785}
1786
Harald Welte95b2b472011-07-16 11:58:09 +02001787void vty_init_vtysh(void)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001788{
1789 vtyvec = vector_init(VECTOR_MIN_SIZE);
1790}
1791
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001792/*! Initialize VTY layer
Harald Welte7acb30c2011-08-17 17:13:48 +02001793 * \param[in] app_info application information
1794 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001795/* Install vty's own commands like `who' command. */
Harald Welte237f6242010-05-25 23:00:45 +02001796void vty_init(struct vty_app_info *app_info)
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001797{
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001798 unsigned int i, j;
1799
Vadim Yanitskiy5584a142017-09-23 18:12:18 +03301800 tall_vty_ctx = talloc_named_const(NULL, 0, "vty");
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001801 tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector");
1802 tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command");
1803
1804 cmd_init(1);
1805
Harald Welte237f6242010-05-25 23:00:45 +02001806 host.app_info = app_info;
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001807
Vadim Yanitskiy024e1952020-10-02 18:23:38 +07001808 /* Check for duplicate flags in application specific attributes (if any) */
1809 for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) {
1810 if (app_info->usr_attr_letters[i] == '\0')
1811 continue;
1812 for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) {
1813 if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i])
1814 continue;
1815 fprintf(stderr, "Found duplicate flag letter '%c' in application "
1816 "specific attributes (index %u vs %u)! Please fix.\n",
1817 app_info->usr_attr_letters[i], i, j);
1818 }
1819 }
1820
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001821 /* For further configuration read, preserve current directory. */
1822 vty_save_cwd();
1823
1824 vtyvec = vector_init(VECTOR_MIN_SIZE);
1825
1826 /* Install bgp top node. */
1827 install_node(&vty_node, vty_config_write);
1828
1829 install_element_ve(&config_who_cmd);
1830 install_element_ve(&show_history_cmd);
1831 install_element(CONFIG_NODE, &line_vty_cmd);
1832 install_element(CONFIG_NODE, &service_advanced_vty_cmd);
1833 install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
1834 install_element(CONFIG_NODE, &show_history_cmd);
1835 install_element(ENABLE_NODE, &terminal_monitor_cmd);
1836 install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
1837
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001838 install_element(VTY_NODE, &vty_login_cmd);
1839 install_element(VTY_NODE, &no_vty_login_cmd);
Neels Hofmeyr96172f02016-02-23 14:01:41 +01001840 install_element(VTY_NODE, &vty_bind_cmd);
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001841}
1842
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001843/*! Read the configuration file using the VTY code
Harald Welte7acb30c2011-08-17 17:13:48 +02001844 * \param[in] file_name file name of the configuration file
1845 * \param[in] priv private data to be passed to \ref vty_read_file
1846 */
Harald Welte3fb0b6f2010-05-19 19:02:52 +02001847int vty_read_config_file(const char *file_name, void *priv)
1848{
1849 FILE *cfile;
1850 int rc;
1851
1852 cfile = fopen(file_name, "r");
1853 if (!cfile)
1854 return -ENOENT;
1855
1856 rc = vty_read_file(cfile, priv);
1857 fclose(cfile);
1858
1859 host_config_set(file_name);
1860
1861 return rc;
1862}
Harald Welte7acb30c2011-08-17 17:13:48 +02001863
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001864/*! @} */