Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4512368
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/Makefile.am b/Makefile.am
index 9889f45e..eb94c43b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,31 +1,40 @@
MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \
config.guess config.sub missing install-sh \
ltmain.sh compile config.h.in config.h.in~
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = tests
if BUILD_DOCS
SUBDIRS += docs
endif
dist_doc_DATA = \
COPYING.applications \
COPYING.libraries \
COPYRIGHT \
README.licence
-noinst_HEADERS =
+noinst_HEADERS = knet.h \
+ ring.h \
+ utils.h \
+ vty.h \
+ vty_auth.h \
+ vty_utils.h
sbin_PROGRAMS = kronosnetd
kronosnetd_SOURCES = \
main.c \
utils.c \
- vty.c
+ vty.c \
+ vty_auth.c \
+ vty_utils.c
+
+kronosnetd_LDFLAGS = -lpthread
maintainer-clean-local:
rm -rf m4
diff --git a/configure.ac b/configure.ac
index 922634a5..6c85a4bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,215 +1,224 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.63])
AC_INIT([kronosnetd], [0.1], [fabbione@kronosnet.org])
AM_INIT_AUTOMAKE([1.11.1 dist-xz color-tests -Wno-portability])
LT_PREREQ([2.2.6])
LT_INIT
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([main.c])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_HOST
AC_PROG_LIBTOOL
AC_LANG([C])
if test "$prefix" = "NONE"; then
prefix="/usr"
if test "$localstatedir" = "\${prefix}/var"; then
localstatedir="/var"
fi
if test "$sysconfdir" = "\${prefix}/etc"; then
sysconfdir="/etc"
fi
fi
# Checks for programs.
if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then
AC_MSG_ERROR([you don't seem to have GNU make; it is required])
fi
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_LN_S
AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PROG_CXX
AC_PROG_RANLIB
AC_CHECK_PROGS([PUBLICAN], [publican], [:])
AC_CHECK_PROGS([PKGCONFIG], [pkg-config])
## local helper functions
# this function checks if CC support options passed as
# args. Global CFLAGS are ignored during this test.
cc_supports_flag() {
local CFLAGS="$@"
AC_MSG_CHECKING([whether $CC supports "$@"])
AC_COMPILE_IFELSE([int main(){return 0;}] ,
[RC=0; AC_MSG_RESULT([yes])],
[RC=1; AC_MSG_RESULT([no])])
return $RC
}
# helper macro to check libs without adding them to LIBS
check_lib_no_libs() {
lib_no_libs_arg1=$1
shift
lib_no_libs_arg2=$1
shift
lib_no_libs_args=$@
AC_CHECK_LIB([$lib_no_libs_arg1],
[$lib_no_libs_arg2],,,
[$lib_no_libs_args])
LIBS=$ac_check_lib_save_LIBS
}
# Checks for C features
AC_C_INLINE
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h])
AC_CHECK_HEADERS([stdlib.h])
AC_CHECK_HEADERS([string.h])
AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([syslog.h])
AC_CHECK_HEADERS([unistd.h])
AC_CHECK_HEADERS([netinet/in.h])
AC_CHECK_HEADERS([sys/socket.h])
AC_CHECK_HEADERS([arpa/inet.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
AC_TYPE_PID_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT8_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
# Checks for library functions.
AC_FUNC_ALLOCA
AC_FUNC_FORK
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([memset])
AC_CHECK_FUNCS([strdup])
AC_CHECK_FUNCS([strerror])
AC_CHECK_FUNCS([dup2])
AC_CHECK_FUNCS([select])
AC_CHECK_FUNCS([socket])
AC_CHECK_FUNCS([inet_ntoa])
AC_CHECK_FUNCS([memmove])
AC_CHECK_FUNCS([strchr])
AC_CHECK_FUNCS([atexit])
AC_CHECK_FUNCS([ftruncate])
+# PAM check
+AC_CHECK_HEADERS([security/pam_appl.h],
+ [AC_CHECK_LIB([pam], [pam_start])],
+ [AC_MSG_ERROR([Unable to find LinuxPAM devel files])])
+
+AC_CHECK_HEADERS([security/pam_misc.h],
+ [AC_CHECK_LIB([pam_misc], [misc_conv])],
+ [AC_MSG_ERROR([Unable to find LinuxPAM MISC devel files])])
+
# local options
AC_ARG_ENABLE([debug],
[ --enable-debug enable debug build. ],
[ default="no" ])
AC_ARG_ENABLE([publicandocs],
[ --enable-publicandocs enable docs build. ],
[ default="no" ])
AC_ARG_WITH([syslogfacility],
[ --syslogfacility=FACILITY
default syslog facility. ],
[ SYSLOGFACILITY="$withval" ],
[ SYSLOGFACILITY="LOG_LOCAL4" ])
AC_ARG_WITH([sysloglevel],
[ --sysloglevel=LEVEL
default syslog level. ],
[ SYSLOGLEVEL="$withval" ],
[ SYSLOGLEVEL="LOG_INFO" ])
## random vars
LOGDIR=${localstatedir}/log/
RUNDIR=${localstatedir}/run/
## do subst
AM_CONDITIONAL(BUILD_DOCS, test "x${enable_publicandocs}" = xyes)
AC_DEFINE_UNQUOTED([CONFFILE], "$(eval echo ${sysconfdir}/kronosnetd.conf)",
[Default config file])
AC_DEFINE_UNQUOTED([LOGDIR], "$(eval echo ${LOGDIR})",
[Default logging directory])
AC_DEFINE_UNQUOTED([RUNDIR], "$(eval echo ${RUNDIR})",
[Default run directory])
AC_DEFINE_UNQUOTED([SYSLOGFACILITY], $(eval echo ${SYSLOGFACILITY}),
[Default syslog facility])
AC_DEFINE_UNQUOTED([SYSLOGLEVEL], $(eval echo ${SYSLOGLEVEL}),
[Default syslog level])
## *FLAGS handling
ENV_CFLAGS="$CFLAGS"
ENV_CPPFLAGS="$CPPFLAGS"
ENV_LDFLAGS="$LDFLAGS"
# debug build stuff
if test "x${enable_debug}" = xyes; then
AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code])
OPT_CFLAGS="-O0"
else
OPT_CFLAGS="-O3"
fi
# gdb flags
if test "x${GCC}" = xyes; then
GDB_FLAGS="-ggdb3"
else
GDB_FLAGS="-g"
fi
# extra warnings
EXTRA_WARNINGS=""
WARNLIST="
all
shadow
missing-prototypes
missing-declarations
strict-prototypes
declaration-after-statement
pointer-arith
write-strings
cast-align
bad-function-cast
missing-format-attribute
format=2
format-security
format-nonliteral
no-long-long
unsigned-char
gnu89-inline
no-strict-aliasing
error
"
for j in $WARNLIST; do
if cc_supports_flag -W$j; then
EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";
fi
done
CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS \
$EXTRA_WARNINGS $WERROR_CFLAGS"
CPPFLAGS="$ENV_CPPFLAGS"
LDFLAGS="$ENV_LDFLAGS"
AC_CONFIG_FILES([
Makefile
docs/Makefile
tests/Makefile
])
AC_OUTPUT
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 712eb511..14d8cf75 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,47 +1,49 @@
MAINTAINERCLEANFILES = Makefile.in
VALGRIND = valgrind -q --error-exitcode=127 --leak-check=full
check-valgrind: $(check_PROGRAMS)
$(MAKE) TESTS_ENVIRONMENT="$(VALGRIND)" check
noinst_PROGRAMS = \
ping_test \
khandle_test \
$(check_PROGRAMS)
check_PROGRAMS = \
knet_test \
vty_test \
netutils_test
TESTS = $(check_PROGRAMS)
CFLAGS += -DTEST
LDFLAGS += -lpthread
knet_test_SOURCES = \
knet_test.c \
../knet.c \
../utils.c
ping_test_SOURCES = \
ping_test.c \
../ring.c \
../utils.c
khandle_test_SOURCES = \
khandle_test.c \
../ring.c \
../utils.c
vty_test_SOURCES = \
vty_test.c \
../vty.c \
+ ../vty_auth.c \
+ ../vty_utils.c \
../utils.c
netutils_test_SOURCES = \
netutils_test.c \
../netutils.c \
../utils.c
diff --git a/vty.c b/vty.c
index d2cb0232..3961d15e 100644
--- a/vty.c
+++ b/vty.c
@@ -1,230 +1,261 @@
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include "utils.h"
#include "vty.h"
+#include "vty_auth.h"
+#include "vty_utils.h"
STATIC pthread_mutex_t knet_vty_mutex = PTHREAD_MUTEX_INITIALIZER;
STATIC int vty_max_connections = KNET_VTY_DEFAULT_MAX_CONN;
STATIC int vty_current_connections = 0;
+STATIC pthread_t vty_thread[KNET_VTY_TOTAL_MAX_CONN];
+
STATIC int daemon_quit = 0;
static void sigterm_handler(int sig)
{
daemon_quit = 1;
}
static void sigpipe_handler(int sig)
{
return;
}
+static void *vty_accept_thread(void *arg)
+{
+ int vty_sock = *(int *)arg;
+
+ knet_vty_print_banner(vty_sock);
+
+ if (knet_vty_auth_user(vty_sock) < 0)
+ goto out_clean;
+
+out_clean:
+ pthread_mutex_lock(&knet_vty_mutex);
+ close(vty_sock);
+ vty_current_connections--;
+ pthread_mutex_unlock(&knet_vty_mutex);
+
+ return NULL;
+}
+
/*
* mainloop is not thread safe as there should only be one
*/
int knet_vty_main_loop(const char *configfile, const char *ip_addr,
const unsigned short port)
{
int vty_listener_fd;
int vty_accept_fd;
struct sockaddr incoming_sa;
socklen_t salen;
fd_set rfds;
int se_result = 0;
struct timeval tv;
int err = 0;
signal(SIGTERM, sigterm_handler);
signal(SIGPIPE, sigpipe_handler);
// read and process config file here
vty_listener_fd = knet_vty_init_listener(ip_addr, port);
if (vty_listener_fd < 0) {
log_error("Unable to setup vty listener");
return -1;
}
while (se_result >= 0 && !daemon_quit) {
FD_ZERO (&rfds);
FD_SET (vty_listener_fd, &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
se_result = select((vty_listener_fd + 1), &rfds, 0, 0, &tv);
if ((se_result == -1) && (daemon_quit)) {
log_info("Got a SIGTERM, goodbye");
goto out;
}
if (se_result == -1) {
err = se_result;
log_error("Unable to select on vty listener socket!");
goto out;
}
if ((se_result == 0) || (!FD_ISSET(vty_listener_fd, &rfds)))
continue;
+ memset(&incoming_sa, 0, sizeof(struct sockaddr));
+ salen = 0;
+
vty_accept_fd = accept(vty_listener_fd, &incoming_sa, &salen);
if (vty_accept_fd < 0) {
log_error("Unable to accept connection to vty");
continue;
}
// check for ip address access list here against incoming_sa
pthread_mutex_lock(&knet_vty_mutex);
+
if (vty_current_connections == vty_max_connections) {
errno = ECONNREFUSED;
log_error("Too many connections to VTY");
close(vty_accept_fd);
pthread_mutex_unlock(&knet_vty_mutex);
continue;
}
- // start vty thread here
- // vty_current_connections++; should be incremented once
- // the thread is started and operating
+ vty_current_connections++;
+ err = pthread_create(&vty_thread[vty_current_connections],
+ NULL, vty_accept_thread,
+ (void *)&vty_accept_fd);
+ if (err < 0) {
+ log_error("Unable to spawn vty thread");
+ vty_current_connections--;
+ }
pthread_mutex_unlock(&knet_vty_mutex);
}
out:
knet_vty_close_listener(vty_listener_fd);
// reverse running config to close/release resources;
return err;
}
void knet_vty_set_max_connections(const int max_connections)
{
pthread_mutex_lock(&knet_vty_mutex);
vty_max_connections = max_connections;
pthread_mutex_unlock(&knet_vty_mutex);
}
int knet_vty_init_listener(const char *ip_addr, const unsigned short port)
{
int sockfd = -1, sockopt = 1;
int socktype = SOCK_STREAM;
int af_family = AF_INET6;
int salen = 0, err = 0;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
pthread_mutex_lock(&knet_vty_mutex);
/* handle sigpipe if we decide to use KEEPALIVE */
/*
* I REALLY HATE MYSELF FOR WRITING THIS PIECE OF CRAP
* but it gets the job done
*/
if ((ip_addr) &&
(strlen(ip_addr)) &&
(!strchr(ip_addr, ':'))) {
af_family = AF_INET;
}
sockfd = socket(af_family, socktype, 0);
if ((sockfd < 0) &&
(errno == EAFNOSUPPORT) &&
(af_family = AF_INET6)) {
af_family = AF_INET;
sockfd = socket(af_family, socktype, 0);
}
if (sockfd < 0) {
err = sockfd;
goto out_clean;
}
err = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
(void *)&sockopt, sizeof(sockopt));
if (err)
goto out_clean;
if (knet_fdset_cloexec(sockfd) < 0) {
err = -1;
goto out_clean;
}
if (af_family == AF_INET) {
salen = sizeof(struct sockaddr_in);
memset(&sin, 0, salen);
sin.sin_family = af_family;
sin.sin_port = htons(port);
if ((!ip_addr) || (!strlen(ip_addr)))
sin.sin_addr.s_addr = htonl(INADDR_ANY);
else
if (inet_pton(af_family, ip_addr, &sin.sin_addr) <= 0) {
err = -1;
goto out_clean;
}
sin.sin_port = htons(port);
err = bind(sockfd, (struct sockaddr *)&sin, salen);
} else {
salen = sizeof(struct sockaddr_in6);
memset(&sin6, 0, salen);
sin6.sin6_family = af_family;
sin6.sin6_port = htons(port);
if ((!ip_addr) || (!strlen(ip_addr)))
memcpy(&sin6.sin6_addr, &in6addr_any, sizeof(struct in6_addr));
else
if (inet_pton(af_family, ip_addr, &sin6.sin6_addr) <= 0) {
err = -1;
goto out_clean;
}
err = bind(sockfd, (struct sockaddr *)&sin6, salen);
}
if (err)
goto out_clean;
err = listen(sockfd, 0);
if (err)
goto out_clean;
pthread_mutex_unlock(&knet_vty_mutex);
return sockfd;
out_clean:
if (sockfd >= 0)
close(sockfd);
pthread_mutex_unlock(&knet_vty_mutex);
return err;
}
void knet_vty_close_listener(int listener_fd)
{
pthread_mutex_lock(&knet_vty_mutex);
if (listener_fd <= 0)
goto out_clean;
close(listener_fd);
listener_fd = 0;
out_clean:
pthread_mutex_unlock(&knet_vty_mutex);
return;
}
diff --git a/vty.h b/vty.h
index 05eb9538..442ec4a8 100644
--- a/vty.h
+++ b/vty.h
@@ -1,15 +1,17 @@
#ifndef __VTY_H__
#define __VTY_H__
#define KNET_VTY_DEFAULT_PORT 50000
+
#define KNET_VTY_DEFAULT_MAX_CONN 4
+#define KNET_VTY_TOTAL_MAX_CONN 16
int knet_vty_main_loop(const char *configfile, const char *ip_addr,
const unsigned short port);
int knet_vty_init_listener(const char *address, const unsigned short port);
void knet_vty_close_listener(int listener_fd);
void knet_vty_set_max_connections(const int max_connections);
#endif
diff --git a/vty_auth.c b/vty_auth.c
new file mode 100644
index 00000000..d09b4cbe
--- /dev/null
+++ b/vty_auth.c
@@ -0,0 +1,168 @@
+#include "config.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+
+#include "utils.h"
+#include "vty_auth.h"
+#include "vty_utils.h"
+
+static int knet_pam_misc_conv(int num_msg, const struct pam_message **msgm,
+ struct pam_response **response, void *appdata_ptr)
+{
+ int count = 0;
+ struct pam_response *reply;
+ int vty_sock = *(int *)appdata_ptr;
+
+ if (num_msg <= 0)
+ return PAM_CONV_ERR;
+
+ reply = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response));
+
+ if (reply == NULL)
+ return PAM_CONV_ERR;
+
+ for (count=0; count < num_msg; ++count) {
+ unsigned char readbuf[VTY_MAX_BUFFER_SIZE];
+ char *string=NULL;
+ int nc;
+
+ memset(readbuf, 0, sizeof(readbuf));
+
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_OFF:
+ if (knet_vty_set_echo(vty_sock, 0) < 0) {
+ knet_vty_write(vty_sock, "Unable to turn off terminal/telnet echo");
+ goto failed_conversation;
+ }
+ knet_vty_write(vty_sock, "%s", msgm[count]->msg);
+ nc = knet_vty_read(vty_sock, readbuf, sizeof(readbuf));
+ if (nc < 0)
+ goto failed_conversation;
+ if (knet_vty_set_echo(vty_sock, 1) < 0) {
+ /* doesn't really make a lot of sense tho.... */
+ knet_vty_write(vty_sock, "Unable to turn on terminal/telnet echo");
+ goto failed_conversation;
+ }
+ knet_vty_write(vty_sock, "\n");
+ readbuf[nc-2] = 0;
+ string = strdup((const char*)readbuf);
+ if (!string)
+ goto failed_conversation;
+ break;
+ case PAM_PROMPT_ECHO_ON:
+ knet_vty_write(vty_sock, "\n%s", msgm[count]->msg);
+ nc = knet_vty_read(vty_sock, readbuf, sizeof(readbuf));
+ if (nc < 0)
+ goto failed_conversation;
+ readbuf[nc-2] = 0;
+ string = strdup((const char*)readbuf);
+ if (!string)
+ goto failed_conversation;
+ break;
+ case PAM_ERROR_MSG:
+ log_error("Received PAM error message %s", msgm[count]->msg);
+ knet_vty_write(vty_sock, "%s", msgm[count]->msg);
+ break;
+ case PAM_TEXT_INFO:
+ log_error("Received PAM text info: %s", msgm[count]->msg);
+ knet_vty_write(vty_sock, "%s", msgm[count]->msg);
+ break;
+ default:
+ log_error("Unknown PAM conversation message");
+ knet_vty_write(vty_sock, "Unknown PAM conversation message");
+ goto failed_conversation;
+ }
+
+ if (string) {
+ reply[count].resp_retcode = 0;
+ reply[count].resp = string;
+ string = NULL;
+ }
+ }
+
+ *response = reply;
+ reply = NULL;
+
+ return PAM_SUCCESS;
+
+failed_conversation:
+ log_error("PAM conversation error");
+ knet_vty_write(vty_sock, "PAM conversation error");
+ if (reply) {
+ for (count=0; count < num_msg; ++count) {
+ if (reply[count].resp == NULL)
+ continue;
+ switch (msgm[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF:
+ _pam_overwrite(reply[count].resp);
+ free(reply[count].resp);
+ break;
+ case PAM_BINARY_PROMPT:
+ {
+ void *bt_ptr = reply[count].resp;
+ pam_binary_handler_free(appdata_ptr, bt_ptr);
+ break;
+ }
+ case PAM_ERROR_MSG:
+ case PAM_TEXT_INFO:
+ free(reply[count].resp);
+ }
+ }
+ free(reply);
+ reply = NULL;
+ }
+
+ return PAM_CONV_ERR;
+}
+
+#define AUTH_MAX_RETRY 3
+
+int knet_vty_auth_user(int vty_sock)
+{
+ pam_handle_t *pamh=NULL;
+ struct pam_conv conv;
+ int err;
+ int retry = 1;
+
+ conv.conv = knet_pam_misc_conv;
+ conv.appdata_ptr = (void *)&vty_sock;
+
+retry_auth:
+ err = pam_start("kronosnet", NULL, &conv, &pamh);
+ if (err != PAM_SUCCESS)
+ goto out_clean;
+
+ err = pam_authenticate(pamh, 0);
+ if (err != PAM_SUCCESS)
+ goto out_clean;
+
+ err = pam_acct_mgmt(pamh, 0);
+ if (err != PAM_SUCCESS)
+ goto out_clean;
+
+out_clean:
+ if (pamh) {
+ pam_end(pamh, err);
+ pamh = NULL;
+ }
+
+ if ((err != PAM_SUCCESS) && (retry < AUTH_MAX_RETRY)) {
+ retry++;
+ goto retry_auth;
+ }
+
+ if ((err != PAM_SUCCESS) && (retry = AUTH_MAX_RETRY))
+ knet_vty_write(vty_sock, "%s", pam_strerror(pamh, err));
+
+ knet_vty_write(vty_sock, "\n");
+
+ return err;
+}
diff --git a/vty_auth.h b/vty_auth.h
new file mode 100644
index 00000000..18e4cd3e
--- /dev/null
+++ b/vty_auth.h
@@ -0,0 +1,6 @@
+#ifndef __VTY_AUTH_H__
+#define __VTY_AUTH_H__
+
+int knet_vty_auth_user(int vty_sock);
+
+#endif
diff --git a/vty_utils.c b/vty_utils.c
new file mode 100644
index 00000000..a6e3b42f
--- /dev/null
+++ b/vty_utils.c
@@ -0,0 +1,129 @@
+#include "config.h"
+
+#include <stdarg.h>
+#include <arpa/telnet.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "vty_utils.h"
+
+/*
+ * TODO: implement loopy_write here
+ * should sock be non-blocking?
+ */
+static int knet_vty_loopy_write(int vty_sock, const char *buf, size_t bufsize)
+{
+ return write(vty_sock, buf, bufsize);
+}
+
+int knet_vty_write(int vty_sock, const char *format, ...)
+{
+ va_list args;
+ int len = 0;
+ char buf[VTY_MAX_BUFFER_SIZE];
+
+ if (!vty_sock) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ va_start (args, format);
+ len = vsnprintf (buf, VTY_MAX_BUFFER_SIZE, format, args);
+ va_end (args);
+
+ if (len < 0)
+ return -1;
+
+ return knet_vty_loopy_write(vty_sock, buf, len);
+}
+
+static int knet_vty_read_real(int vty_sock, unsigned char *buf, size_t bufsize,
+ int ignore_iac)
+{
+ ssize_t readlen;
+
+iac_retry:
+ readlen = read(vty_sock, buf, bufsize);
+ if (readlen < 0)
+ return readlen;
+
+ /* at somepoint we *might* have to add IAC parsing */
+ if ((buf[0] == IAC) && (ignore_iac))
+ goto iac_retry;
+
+ return readlen;
+}
+
+int knet_vty_read(int vty_sock, unsigned char *buf, size_t bufsize)
+{
+ if ((!vty_sock) || (!buf) || (bufsize == 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+ return knet_vty_read_real(vty_sock, buf, bufsize, 1);
+}
+
+static int knet_vty_set_echooff(int vty_sock)
+{
+ unsigned char cmdreply[VTY_MAX_BUFFER_SIZE];
+ unsigned char cmdechooff[] = { IAC, WILL, TELOPT_ECHO, '\0' };
+ unsigned char cmdechooffreply[] = { IAC, DO, TELOPT_ECHO, '\0' };
+ ssize_t readlen;
+
+ if (knet_vty_write(vty_sock, "%s", cmdechooff) < 0)
+ return -1;
+
+ readlen = knet_vty_read_real(vty_sock, cmdreply, VTY_MAX_BUFFER_SIZE, 0);
+ if (readlen < 0)
+ return readlen;
+
+ if (memcmp(&cmdreply, &cmdechooffreply, readlen))
+ return -1;
+
+ return 0;
+}
+
+static int knet_vty_set_echoon(int vty_sock)
+{
+ unsigned char cmdreply[VTY_MAX_BUFFER_SIZE];
+ unsigned char cmdechoon[] = { IAC, WONT, TELOPT_ECHO, '\0' };
+ unsigned char cmdechoonreply[] = { IAC, DONT, TELOPT_ECHO, '\0' };
+ ssize_t readlen;
+
+ if (knet_vty_write(vty_sock, "%s", cmdechoon) < 0)
+ return -1;
+
+ readlen = knet_vty_read_real(vty_sock, cmdreply, VTY_MAX_BUFFER_SIZE, 0);
+ if (readlen < 0)
+ return readlen;
+
+ if (memcmp(&cmdreply, &cmdechoonreply, readlen))
+ return -1;
+
+ return 0;
+}
+
+int knet_vty_set_echo(int vty_sock, int on)
+{
+ if (!vty_sock) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (on)
+ return knet_vty_set_echoon(vty_sock);
+
+ return knet_vty_set_echooff(vty_sock);
+}
+
+void knet_vty_print_banner(int vty_sock)
+{
+ if (!vty_sock)
+ return;
+
+ knet_vty_write(vty_sock,
+ "Welcome to " PACKAGE " " PACKAGE_VERSION " (built " __DATE__
+ " " __TIME__ ") Management CLI\n");
+}
diff --git a/vty_utils.h b/vty_utils.h
new file mode 100644
index 00000000..7f4780e2
--- /dev/null
+++ b/vty_utils.h
@@ -0,0 +1,15 @@
+#ifndef __VTY_UTILS_H__
+#define __VTY_UTILS_H__
+
+#define VTY_MAX_BUFFER_SIZE 4096
+
+int knet_vty_write(int vty_sock, const char *format, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)));
+
+int knet_vty_read(int vty_sock, unsigned char *buf, size_t bufsize);
+
+int knet_vty_set_echo(int vty_sock, int on);
+
+void knet_vty_print_banner(int vty_sock);
+
+#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Jun 25, 5:18 AM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952253
Default Alt Text
(21 KB)
Attached To
Mode
rK kronosnet
Attached
Detach File
Event Timeline
Log In to Comment