Page MenuHomeClusterLabs Projects

No OneTemporary

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

Mime Type
text/x-diff
Expires
Wed, Jun 25, 5:18 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1952253
Default Alt Text
(21 KB)

Event Timeline