diff --git a/.gitignore b/.gitignore
index a7018dfb..0e8e97cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,8 @@ config.status
 libtool
 pam_tacplus.spec
 stamp-h1
+libtac.pc
+libtac-event.pc
 *.o
 *.lo
 *.la
diff --git a/Makefile.am b/Makefile.am
index 7e09e981..be16df61 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,6 +24,9 @@ libtac/include/libtac.h \
 libtac/include/cdefs.h
 
 lib_LTLIBRARIES = libtac.la
+if WITH_LIBEVENT
+lib_LTLIBRARIES += libtac-event.la
+endif
 libtac_la_SOURCES = \
 libtac/lib/acct_r.c \
 libtac/lib/acct_s.c \
@@ -45,6 +48,8 @@ libtac/lib/read_wait.c \
 libtac/lib/version.c \
 libtac/lib/xalloc.c \
 libtac/lib/xalloc.h \
+libtac/lib/session.c \
+libtac/lib/parse.c \
 $(libtac_include_HEADERS)
 if MY_MD5
 libtac_la_SOURCES += \
@@ -54,6 +59,14 @@ endif
 libtac_la_CFLAGS = $(AM_CFLAGS) -I $(top_srcdir)/libtac/include @rt_debug_defines@
 libtac_la_LDFLAGS = -version-info 2:0:0 -shared
 
+if WITH_LIBEVENT
+libtac_event_la_SOURCES = \
+libtac/lib/wrappers.c \
+$(libtac_include_HEADERS)
+libtac_event_la_CFLAGS = $(AM_CFLAGS) -Ilibtac/include
+libtac_event_la_LDFLAGS = -version-info 1:0:0 -shared -levent
+endif
+
 moduledir = @pamdir@
 module_LTLIBRARIES = pam_tacplus.la
 pam_tacplus_la_SOURCES = pam_tacplus.h \
@@ -65,6 +78,9 @@ pam_tacplus_la_LDFLAGS = -module -avoid-version
 pam_tacplus_la_LIBADD = libtac.la
 
 EXTRA_DIST = pam_tacplus.spec libtac.pc.in
+if WITH_LIBEVENT
+EXTRA_DIST += libtac-event.pc.in
+endif
 if DOC
 dist_doc_DATA = sample.pam README.md AUTHORS ChangeLog
 endif
@@ -75,6 +91,9 @@ MAINTAINERCLEANFILES = Makefile.in config.h.in configure aclocal.m4 \
 
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = libtac.pc
+if WITH_LIBEVENT
+pkgconfig_DATA += libtac-event.pc
+endif
 
 coverity:
 	rm -rf cov-int
diff --git a/configure.ac b/configure.ac
index e34c7694..446dbdf5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,6 +48,7 @@ dnl --------------------------------------------------------------------
 dnl Checks for header files.
 AC_HEADER_STDC
 AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h sys/time.h ])
+AC_CHECK_HEADERS([stdbool.h limits.h assert.h])
 AC_CHECK_HEADERS([syslog.h unistd.h openssl/md5.h openssl/rand.h linux/random.h sys/random.h])
 AC_CHECK_HEADER(security/pam_appl.h, [], [AC_MSG_ERROR([PAM libraries missing. Install with "yum install pam-devel" or "apt-get install libpam-dev".])] )
 AM_CONDITIONAL(MY_MD5, [test "$ac_cv_header_openssl_md5_h" = "no" ])
@@ -102,8 +103,21 @@ AC_SUBST(rt_debug_defines)
 AM_SUBST_NOTMAKE(rt_debug_defines)
 
 dnl --------------------------------------------------------------------
+dnl Switch for libevent support
+AC_ARG_WITH(libevent,
+            [AS_HELP_STRING([--with-libevent], [build libevent support])],
+            AC_DEFINE([WITH_LIBEVENT], 1,
+                      [Define to 1 if building with libevent])
+            [with_libevent=1]
+)
+AM_CONDITIONAL(WITH_LIBEVENT, [test -n "$with_libevent"])
+
+
+dnl --------------------------------------------------------------------
+
 dnl Generate made files
 AC_CONFIG_FILES([Makefile
 		 libtac.pc
+		 libtac-event.pc
                  pam_tacplus.spec])
 AC_OUTPUT
diff --git a/libtac-event.pc.in b/libtac-event.pc.in
new file mode 100644
index 00000000..b907adcb
--- /dev/null
+++ b/libtac-event.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/libtac
+
+Name: libtac-event
+Description: A TACACS+ protocol implementation
+URL: https://github.com/jeroennijhof/pam_tacplus
+Version: @VERSION@
+Libs: -L${libdir} -ltac-event
+Cflags: -I${includedir}
diff --git a/libtac/include/libtac.h b/libtac/include/libtac.h
index c872ff71..471e6a40 100644
--- a/libtac/include/libtac.h
+++ b/libtac/include/libtac.h
@@ -42,6 +42,9 @@ extern "C" {
 #else
 #include "cdefs.h"
 #endif
+#include <assert.h>
+#include <stdbool.h>
+#include <limits.h>
 #include "tacplus.h"
 
 #if defined(DEBUGTAC) && !defined(TACDEBUG)
@@ -87,6 +90,7 @@ struct tac_attrib {
 struct areply {
 	struct tac_attrib *attr;
 	char *msg;
+	char *data;
 	int status :8;
 	int flags :8;
 	int seq_no :8;
@@ -132,50 +136,172 @@ extern int tac_ver_major;
 extern int tac_ver_minor;
 extern int tac_ver_patch;
 
-/* header.c */
-extern int session_id;
-extern int tac_encryption;
-extern const char *tac_secret;
-extern char tac_login[64];
-extern int tac_priv_lvl;
-extern int tac_authen_method;
-extern int tac_authen_service;
+/* session.c */
+struct bufferevent;
+struct tac_session;
+
+struct cb_ctx {
+    struct tac_session *sess;
+    void *user_ctx;
+    const char *login;
+    const char *pass;
+};
+
+typedef void (*response_cb_t)(struct tac_session *, struct cb_ctx *,
+    int, uint8_t, struct areply *);
+
+typedef enum { UNINITIALIZED, CONNECTED, CLOSED, ERROR, TIMEOUT } session_event_t;
+
+typedef void (*oob_cb_t)(struct tac_session *, struct cb_ctx *,
+    session_event_t);
+
+struct tac_session {
+    unsigned tac_timeout;
+    const char *tac_secret;
+    uint32_t tac_session_id;
+    bool tac_encryption;
+    bool tac_multiplex;
+    bool tac_idle;			/* not exposed via API */
+    uint8_t tac_priv_lvl;
+    uint8_t tac_authen_method;
+    uint8_t tac_authen_service;
+    uint8_t tac_authen_type;
+    uint8_t seq_no;
+    int fd;
+
+    struct bufferevent *bufev;
+    void *cookie;
+
+    response_cb_t response_cb;
+    oob_cb_t oob_cb;
+    struct cb_ctx context;
 
+    /* user defined stuff */
+    uint8_t user_data[0];
+};
+
+struct tac_session *tac_session_alloc(void);
+struct tac_session *tac_session_alloc_extra(unsigned);
+void tac_session_set_authen_type(struct tac_session *, uint8_t);
+void tac_session_set_secret(struct tac_session *, const char *);
+void tac_session_set_timeout(struct tac_session *, unsigned);
+void tac_session_set_multiplex(struct tac_session *, bool);
+void tac_session_reset_timeouts(struct tac_session *, bool);
+void tac_session_set_response(struct tac_session *, response_cb_t);
+void tac_session_set_oob(struct tac_session *, oob_cb_t);
+struct cb_ctx *tac_session_get_context(struct tac_session *);
+void tac_session_new_session_id(struct tac_session *);
+void tac_session_reset_seq(struct tac_session *);
+void *tac_session_get_user_data(struct tac_session *);
+void tac_session_free(struct tac_session *);
+
+/* header.c */
 extern int tac_debug_enable;
 extern int tac_readtimeout_enable;
 
+/* we return a void * because there are different types of bodies */
+static inline void *tac_hdr_to_body(HDR *th)
+{
+    return (void *)((u_char *)th + TAC_PLUS_HDR_SIZE);
+}
+
+HDR *_tac_req_header(struct tac_session *, u_char, bool);
+
 /* connect.c */
 extern int tac_timeout;
 
-int tac_connect(struct addrinfo **, char **, int);
-int tac_connect_single(const struct addrinfo *, const char *, struct addrinfo *,
-		int);
+int tac_connect(struct tac_session *, struct addrinfo **, unsigned);
+int tac_connect_single(struct tac_session *, const struct addrinfo *, struct addrinfo *, int);
+void tac_close(struct tac_session *);
 char *tac_ntop(const struct sockaddr *);
 
-int tac_authen_send(int, const char *, const char *, const char *, const char *,
-		u_char);
-int tac_authen_read(int, struct areply *);
-int tac_cont_send_seq(int, const char *, int);
-#define tac_cont_send(fd, pass) tac_cont_send_seq((fd), (pass), 3)
-HDR *_tac_req_header(u_char, int);
-void _tac_crypt(u_char *, const HDR *);
+/* authen_s.c */
+u_char tac_get_authen_type(const char *);
+const char *tag_get_authen_string(uint8_t);
+
+void tac_authen_send_pkt(struct tac_session *,
+    const char *, const char *, const char *, const char *, u_char,
+    u_char **, unsigned *);
+int tac_authen_send(struct tac_session *,
+    const char *, const char *, const char *, const char *, u_char);
+
+/* authen_r.c */
+int tac_authen_parse(struct tac_session *, struct areply *, u_char *, unsigned);
+int tac_authen_read(struct tac_session *, struct areply *);
+
+/* cont_s.c */
+void tac_cont_send_pkt(struct tac_session *, const char *,
+   u_char **, unsigned *);
+int tac_cont_send(struct tac_session *, const char *);
+
+/* crypt.c */
+void _tac_crypt(const struct tac_session *, u_char *, const HDR *);
+
+/* author_r.c */
+int tac_author_parse(struct tac_session *, u_char *, unsigned, struct areply *);
+int tac_author_read(struct tac_session *, struct areply *);
+
+/* author_s.c */
+void tac_author_send_pkt(struct tac_session *, const char *, const char *,
+    const char *, struct tac_attrib *, u_char **, unsigned *);
+int tac_author_send(struct tac_session *, const char *, const char *,
+    const char *, struct tac_attrib *);
+
+/* attrib.c */
 void tac_add_attrib(struct tac_attrib **, char *, char *);
+void tac_add_attrib_pair(struct tac_attrib **, char *, char, char *);
 void tac_free_attrib(struct tac_attrib **);
-char *tac_acct_flag2str(int);
-int tac_acct_send(int, int, const char *, char *, char *, struct tac_attrib *);
-int tac_acct_read(int, struct areply *);
+
+/* acct_s.c */
+char *tac_acct_flag2str(u_char);
+void tac_acct_send_pkt(struct tac_session *, u_char, const char *,
+    const char *, const char *, struct tac_attrib *, u_char **, unsigned *);
+int tac_acct_send(struct tac_session *, u_char, const char *,
+    const char *, const char *, struct tac_attrib *);
+
+/* acct_r.c */
+int tac_acct_parse(struct tac_session *, u_char *, unsigned,
+    struct areply *);
+int tac_acct_read(struct tac_session *, struct areply *);
+
+/* xalloc.c */
 void *xcalloc(size_t, size_t);
 void *xrealloc(void *, size_t);
 char *xstrcpy(char *, const char *, size_t);
-char *_tac_check_header(HDR *, int);
-int tac_author_send(int, const char *, char *, char *, struct tac_attrib *);
-int tac_author_read(int, struct areply *);
-void tac_add_attrib_pair(struct tac_attrib **, char *, char, char *);
-int tac_read_wait(int, int, int, int *);
+
+/* hdr_check.c */
+char *_tac_check_header(struct tac_session *, HDR *, uint8_t);
 
 /* magic.c */
 u_int32_t magic(void);
 
+/* read_wait.c */
+int tac_read_wait(int, int, int, int *);
+
+/* parser.c */
+void tac_parse_pkt(struct tac_session *, struct cb_ctx *, u_char *, unsigned);
+
+/* wrappers.c */
+void *tac_event_loop_initialize(void);
+int tac_event_loop(void *tac_event);
+void tac_event_loop_end(void *tac_event);
+void tac_event_loop_shutdown(void *tac_event);
+void tac_event_loop_global_shutdown(void);
+
+bool tac_connect_single_ev(struct tac_session *,
+    void *, struct addrinfo *server, struct addrinfo *srcaddr, unsigned timeout);
+bool tac_authen_send_ev(struct tac_session *sess,
+    const char *user, const char *pass, const char *tty,
+    const char *r_addr, u_char action);
+bool tac_author_send_ev(struct tac_session *sess,
+    const char *user, const char *tty, const char *r_addr,
+    struct tac_attrib *attr);
+bool tac_acct_send_ev(struct tac_session *sess,
+    u_char type, const char *user, const char *tty,
+    const char *r_addr, struct tac_attrib *attr);
+bool tac_cont_send_ev(struct tac_session *sess,
+    const char *pass);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libtac/include/tacplus.h b/libtac/include/tacplus.h
index 90d7c8bf..c7f0c598 100644
--- a/libtac/include/tacplus.h
+++ b/libtac/include/tacplus.h
@@ -107,6 +107,7 @@ struct authen_cont {
     u_short user_msg_len;
     u_short user_data_len;
     u_char flags;
+    u_char msg[0];
 
 #define TAC_PLUS_CONTINUE_FLAG_ABORT 0x01
 
@@ -133,6 +134,7 @@ struct authen_reply {
 
     u_short msg_len;
     u_short data_len;
+    u_char  msg[0];
 };
 
 #define TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE 6
@@ -173,6 +175,7 @@ struct acct {
     u_char port_len;
     u_char r_addr_len;
     u_char arg_cnt;    /* the number of cmd args */
+    u_char arg_len[0];
 };
 
 #define TAC_ACCT_REQ_FIXED_FIELDS_SIZE 9
@@ -201,6 +204,7 @@ struct author {
     u_char port_len;
     u_char r_addr_len;
     u_char arg_cnt;    /* the number of args */
+    u_char arg_len[0];
 };
 
 #define TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE 8
@@ -211,6 +215,7 @@ struct author_reply {
     u_char arg_cnt;
     u_short msg_len;
     u_short data_len;
+    u_char  arg_len[0];
 
 #define TAC_PLUS_AUTHOR_STATUS_PASS_ADD  0x01
 #define TAC_PLUS_AUTHOR_STATUS_PASS_REPL 0x02
diff --git a/libtac/lib/acct_r.c b/libtac/lib/acct_r.c
index 6d0d04d9..2a25e197 100644
--- a/libtac/lib/acct_r.c
+++ b/libtac/lib/acct_r.c
@@ -32,118 +32,81 @@
  *             LIBTAC_STATUS_PROTOCOL_ERR
  *   >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
  */
-int tac_acct_read(int fd, struct areply *re) {
-    HDR th;
+int tac_acct_parse(struct tac_session *sess, u_char *pkt, unsigned pkt_total,
+    struct areply *re) {
+    HDR *th = (HDR *)pkt;
     struct acct_reply *tb = NULL;
     size_t ulen_from_header, len_from_body;
-    ssize_t spacket_read;
     char *msg = NULL;
-	int timeleft = 0;
-    re->attr = NULL; /* unused */
-    re->msg = NULL;
-
-    if (tac_readtimeout_enable &&
-        tac_read_wait(fd,tac_timeout*1000, TAC_PLUS_HDR_SIZE,&timeleft) < 0 ) {
-        TACSYSLOG(LOG_ERR,\
-            "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-        re->msg = xstrdup(acct_syserr_msg);
-        re->status = LIBTAC_STATUS_READ_TIMEOUT;
-        free(tb);
-        return re->status;
-    }
 
-    spacket_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
-    if(spacket_read  < TAC_PLUS_HDR_SIZE) {
-        TACSYSLOG(LOG_ERR,\
-            "%s: short reply header, read %zd of %u expected: %m", __FUNCTION__,\
-            spacket_read, TAC_PLUS_HDR_SIZE);
-        re->msg = xstrdup(acct_syserr_msg);
-        re->status = LIBTAC_STATUS_SHORT_HDR;
-        free(tb);
-        return re->status;
-    }
+    re->attr = NULL; /* unused */
+    re->msg = re->data = NULL;
 
     /* check the reply fields in header */
-    msg = _tac_check_header(&th, TAC_PLUS_ACCT);
+    msg = _tac_check_header(sess, th, TAC_PLUS_ACCT);
     if(msg != NULL) {
         re->msg = xstrdup(msg);
         re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-        free(tb);
         TACDEBUG(LOG_DEBUG, "%s: exit status=%d, status message \"%s\"",\
             __FUNCTION__, re->status, re->msg != NULL ? re->msg : "");
         return re->status;
     }
 
-    ulen_from_header = ntohl(th.datalength);
-    if (ulen_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
-        TACSYSLOG(LOG_ERR,\
-            "%s: length declared in the packet %zu exceeds max allowed packet size %d",\
-            __FUNCTION__,\
-            ulen_from_header, TAC_PLUS_MAX_PACKET_SIZE);
-        re->status=LIBTAC_STATUS_SHORT_HDR;
-        free(tb);
-        return re->status;
-    }
-    tb=(struct acct_reply *) xcalloc(1, ulen_from_header);
+    ulen_from_header = ntohl(th->datalength);
 
-    /* read reply packet body */
-    if (tac_readtimeout_enable &&
-        tac_read_wait(fd,timeleft,ulen_from_header,NULL) < 0 ) {
-        TACSYSLOG(LOG_ERR,\
-            "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-        re->msg = xstrdup(acct_syserr_msg);
-        re->status = LIBTAC_STATUS_READ_TIMEOUT;
-        free(tb);
-        return re->status;
-    }
+    tb = tac_hdr_to_body(th);
 
-    spacket_read = read(fd, tb, ulen_from_header);
-    if(spacket_read < (ssize_t) ulen_from_header) {
+    if (pkt_total != ulen_from_header) {
         TACSYSLOG(LOG_ERR,\
-            "%s: short reply body, read %zd of %zu: %m",\
+            "%s: short packet, got %u expected %zu: %m",\
             __FUNCTION__,\
-			spacket_read, ulen_from_header);
+	    pkt_total, TAC_PLUS_HDR_SIZE + ulen_from_header);
         re->msg = xstrdup(acct_syserr_msg);
         re->status = LIBTAC_STATUS_SHORT_BODY;
-        free(tb);
         return re->status;
     }
 
     /* decrypt the body */
-    _tac_crypt((u_char *) tb, &th);
+    _tac_crypt(sess, (u_char *) tb, th);
 
     /* Convert network byte order to host byte order */
     tb->msg_len  = ntohs(tb->msg_len);
     tb->data_len = ntohs(tb->data_len);
 
     /* check the length fields */
-    len_from_body=sizeof(tb->msg_len) + sizeof(tb->data_len) +
-        sizeof(tb->status) + tb->msg_len + tb->data_len;
+    len_from_body = TAC_ACCT_REPLY_FIXED_FIELDS_SIZE + \
+        tb->msg_len + tb->data_len;
 
     if(ulen_from_header != len_from_body) {
         TACSYSLOG(LOG_ERR,\
-            "%s: inconsistent reply body, incorrect key?",\
-            __FUNCTION__);
+            "%s: inconsistent reply body, header len %zu versus parsed len %zu",\
+            __FUNCTION__, ulen_from_header, len_from_body);
         re->msg = xstrdup(acct_syserr_msg);
         re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-        free(tb);
         return re->status;
     }
 
     /* save status and clean up */
     if(tb->msg_len) {
-        msg=(char *) xcalloc(1, tb->msg_len+1);
+        msg = xcalloc(1, tb->msg_len+1);
         bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE, msg, tb->msg_len);
-        msg[(int)tb->msg_len] = '\0';
+        msg[tb->msg_len] = '\0';
         re->msg = msg;      /* Freed by caller */
     }
 
+    if(tb->data_len) {
+        msg = xcalloc(1, tb->data_len+1);
+        bcopy((u_char *) tb+TAC_ACCT_REPLY_FIXED_FIELDS_SIZE+tb->data_len,
+            msg, tb->data_len);
+        msg[tb->data_len] = '\0';
+        re->data = msg;     /* Freed by caller */
+    }
+
     /* server logged our request successfully */
     if (tb->status == TAC_PLUS_ACCT_STATUS_SUCCESS) {
         TACDEBUG(LOG_DEBUG, "%s: accounted ok", __FUNCTION__);
         if (!re->msg) re->msg = xstrdup(acct_ok_msg);
         re->status = tb->status;
-        free(tb);
         return re->status;
     }
 
@@ -162,6 +125,95 @@ int tac_acct_read(int fd, struct areply *re) {
             break;
     }
 
-    free(tb);
     return re->status;
-}
+}    /* tac_acct_parse */
+
+/*
+ * return value:
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *             LIBTAC_STATUS_READ_TIMEOUT
+ *             LIBTAC_STATUS_SHORT_HDR
+ *             LIBTAC_STATUS_SHORT_BODY
+ *             LIBTAC_STATUS_PROTOCOL_ERR
+ *   >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
+ */
+int tac_acct_read(struct tac_session *sess, struct areply *re) {
+    HDR *th;
+    struct acct_reply *tb = NULL;
+    size_t ulen_from_header;
+    ssize_t spacket_read;
+    int status, timeleft = 0;
+
+    re->attr = NULL; /* unused */
+    re->msg = re->data = NULL;
+
+    if (tac_readtimeout_enable &&
+        tac_read_wait(sess->fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, &timeleft) < 0 ) {
+        TACSYSLOG(LOG_ERR,\
+            "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
+        re->msg = xstrdup(acct_syserr_msg);
+        re->status = LIBTAC_STATUS_READ_TIMEOUT;
+        return re->status;
+    }
+
+    th = xcalloc(1, TAC_PLUS_HDR_SIZE);
+
+    spacket_read = read(sess->fd, th, TAC_PLUS_HDR_SIZE);
+    if(spacket_read < TAC_PLUS_HDR_SIZE) {
+        TACSYSLOG(LOG_ERR,\
+            "%s: short reply header, read %zd of %u expected: %m", __FUNCTION__,\
+            ((spacket_read >= 0) ? spacket_read : 0), TAC_PLUS_HDR_SIZE);
+        re->msg = xstrdup(acct_syserr_msg);
+        re->status = LIBTAC_STATUS_SHORT_HDR;
+        free(th);
+        return re->status;
+    }
+
+    ulen_from_header = ntohl(th->datalength);
+    if (ulen_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+        TACSYSLOG(LOG_ERR,\
+            "%s: length declared in the packet %zu exceeds max allowed packet size %u", __FUNCTION__,\
+            ulen_from_header, TAC_PLUS_MAX_PACKET_SIZE);
+        re->msg = xstrdup(acct_syserr_msg);
+        re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+        free(th);
+        return re->status;
+    }
+
+    /* now make room for entire contiguous packet */
+    th = xrealloc(th, TAC_PLUS_HDR_SIZE + ulen_from_header);
+    tb = tac_hdr_to_body(th);
+
+    /* read reply packet body */
+    if (tac_readtimeout_enable &&
+        tac_read_wait(sess->fd, timeleft, ulen_from_header, NULL) < 0 ) {
+        TACSYSLOG(LOG_ERR,\
+            "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
+        re->msg = xstrdup(acct_syserr_msg);
+        re->status = LIBTAC_STATUS_READ_TIMEOUT;
+        free(th);
+        return re->status;
+    }
+
+    spacket_read = read(sess->fd, tb, ulen_from_header);
+    if(spacket_read < 0 || (size_t) spacket_read < ulen_from_header) {
+        TACSYSLOG(LOG_ERR,\
+            "%s: short reply body, read %zd of %zu: %m",\
+            __FUNCTION__,\
+            ((spacket_read >= 0) ? spacket_read : 0), ulen_from_header);
+        re->msg = xstrdup(acct_syserr_msg);
+        re->status = LIBTAC_STATUS_SHORT_BODY;
+        free(th);
+        return re->status;
+    }
+
+    /* now parse remaining packet fields */
+    status = tac_acct_parse(sess, (u_char *)th,
+		TAC_PLUS_HDR_SIZE + ulen_from_header, re);
+
+    /* all useful data has been copied out */
+    free(th);
+
+    return status;
+}    /* tac_acct_read */
+
diff --git a/libtac/lib/acct_s.c b/libtac/lib/acct_s.c
index db680672..5bc6e511 100644
--- a/libtac/lib/acct_s.c
+++ b/libtac/lib/acct_s.c
@@ -22,7 +22,7 @@
 #include "libtac.h"
 #include "xalloc.h"
 
-char *tac_acct_flag2str(int flag) {
+char *tac_acct_flag2str(u_char flag) {
     switch(flag) {
         case TAC_PLUS_ACCT_FLAG_MORE:
             return "more";
@@ -37,143 +37,127 @@ char *tac_acct_flag2str(int flag) {
     }
 }
 
-/*
- * return value:
- *      0 : success
- *   <  0 : error status code, see LIBTAC_STATUS_...
- *             LIBTAC_STATUS_WRITE_ERR
- *             LIBTAC_STATUS_WRITE_TIMEOUT  (pending impl)
- *             LIBTAC_STATUS_ASSEMBLY_ERR   (pending impl)
- */
-int tac_acct_send(int fd, int type, const char *user, char *tty,
-    char *r_addr, struct tac_attrib *attr) {
+/* allocate and format an Accounting Start packet */
+void tac_acct_send_pkt(struct tac_session *sess, u_char type,
+    const char *user, const char *tty, const char *r_addr,
+    struct tac_attrib *attr, u_char **_pkt, unsigned *_len) {
 
     HDR *th;
-    struct acct tb;
-    u_char user_len, port_len, r_addr_len;
+    struct acct *tb;
+    unsigned user_len, port_len, r_addr_len;
     struct tac_attrib *a;
-    int i = 0;    /* arg count */
-    int pkt_len = 0;
-    int pktl = 0;
-    int w;    /* write count */
+    unsigned i;  /* arg count */
     u_char *pkt=NULL;
-    /* u_char *pktp; */             /* obsolute */
-    int ret = 0;
-
-    th = _tac_req_header(TAC_PLUS_ACCT, 0);
-
-    /* set header options */
-    th->version=TAC_PLUS_VER_0;
-    th->encryption=tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+    unsigned pkt_total, pkt_len = 0;
 
     TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s, type: %s", \
         __FUNCTION__, user, tty, r_addr, \
-        (tac_encryption) ? "yes" : "no", \
+        (sess->tac_encryption) ? "yes" : "no", \
         tac_acct_flag2str(type));
-        
-    user_len=(u_char) strlen(user);
-    port_len=(u_char) strlen(tty);
-    r_addr_len=(u_char) strlen(r_addr);
-
-    tb.flags=(u_char) type;
-    tb.authen_method=tac_authen_method;
-    tb.priv_lvl=tac_priv_lvl;
-    if (!*tac_login) {
-        /* default to PAP */
-        tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
-    } else {
-        if (strcmp(tac_login,"chap") == 0) {
-            tb.authen_type=TAC_PLUS_AUTHEN_TYPE_CHAP;
-        } else if(strcmp(tac_login,"login") == 0) {
-            tb.authen_type=TAC_PLUS_AUTHEN_TYPE_ASCII;
-        } else {
-            tb.authen_type=TAC_PLUS_AUTHEN_TYPE_PAP;
-        }
-    }
-    tb.authen_service=tac_authen_service;
-    tb.user_len=user_len;
-    tb.port_len=port_len;
-    tb.r_addr_len=r_addr_len;
-
-    /* allocate packet */
-    pkt=(u_char *) xcalloc(1, TAC_ACCT_REQ_FIXED_FIELDS_SIZE);
-    pkt_len=sizeof(tb);
-
-    /* fill attribute length fields */
-    a = attr;
-    while (a) {
-        pktl = pkt_len;
-        pkt_len += sizeof(a->attr_len);
-        pkt = (u_char*) xrealloc(pkt, pkt_len);
-
-        /* see comments in author_s.c
-        pktp=pkt + pkt_len;
-        pkt_len += sizeof(a->attr_len);
-        pkt = xrealloc(pkt, pkt_len);   
-        */
-
-        bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len));
-        i++;
-
-        a = a->next;
+
+    /*
+     * precompute the buffer size so we don't need to keep resizing/copying it
+     */
+    user_len = strlen(user);
+    port_len = strlen(tty);
+    r_addr_len = strlen(r_addr);
+
+    assert(user_len <= UCHAR_MAX);
+    assert(port_len <= UCHAR_MAX);
+    assert(r_addr_len <= UCHAR_MAX);
+
+#define TAC_ACCT_REQ_FIXED_TOTAL \
+            (TAC_PLUS_HDR_SIZE + TAC_ACCT_REQ_FIXED_FIELDS_SIZE)
+
+    pkt_total = TAC_ACCT_REQ_FIXED_TOTAL + user_len + port_len + r_addr_len;
+
+    /* ... add in attributes */
+    for (i = 0, a = attr; a; a = a->next, ++i) {
+        pkt_total += a->attr_len + 1;          /* count length byte too */
     }
 
+    pkt = xcalloc(1, pkt_total);
+    th = (HDR *)pkt;
+
+    /* tacacs header */
+    th->version = TAC_PLUS_VER_0;
+    th->type = TAC_PLUS_ACCT;
+    th->seq_no = ++sess->seq_no;
+    th->encryption = sess->tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG
+	| (sess->tac_multiplex ? TAC_PLUS_SINGLE_CONNECT_FLAG : 0);
+    th->session_id = htonl(sess->tac_session_id);
+    th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE);
+
+    /* fixed part of tacacs body */
+    tb = tac_hdr_to_body(th);
+    tb->flags = type;
+    tb->authen_method = sess->tac_authen_method;
+    tb->priv_lvl = sess->tac_priv_lvl;
+    tb->authen_type = sess->tac_authen_type;
+    tb->authen_service = sess->tac_authen_service;
+    tb->user_len = user_len;
+    tb->port_len = port_len;
+    tb->r_addr_len = r_addr_len;
+
     /* fill the arg count field and add the fixed fields to packet */
-    tb.arg_cnt = i;
-    bcopy(&tb, pkt, TAC_ACCT_REQ_FIXED_FIELDS_SIZE);
+    tb->arg_cnt = i;
+
+    pkt_len = TAC_ACCT_REQ_FIXED_TOTAL + i;    /* reserve room for lengths */
 
-    /*
-#define PUTATTR(data, len) \
-        pktp = pkt + pkt_len; \
-        pkt_len += len; \
-        pkt = xrealloc(pkt, pkt_len); \
-        bcopy(data, pktp, len);
-    */
 #define PUTATTR(data, len) \
-    pktl = pkt_len; \
-    pkt_len += len; \
-    pkt = (u_char*) xrealloc(pkt, pkt_len); \
-    bcopy(data, pkt + pktl, len);
+    bcopy(data, pkt + pkt_len, len); \
+    pkt_len += len
 
     /* fill user and port fields */
-    PUTATTR(user, user_len)
-    PUTATTR(tty, port_len)
-    PUTATTR(r_addr, r_addr_len)
+    PUTATTR(user, user_len);
+    PUTATTR(tty, port_len);
+    PUTATTR(r_addr, r_addr_len);
 
     /* fill attributes */
-    a = attr;
-    while(a) {
-        PUTATTR(a->attr, a->attr_len)
-        a = a->next;
+    for (i = 0, a = attr; a; a = a->next, ++i) {
+        tb->arg_len[i] = a->attr_len;
+        PUTATTR(a->attr, a->attr_len);
     }
 
-    /* finished building packet, fill len_from_header in header */
-    th->datalength = htonl(pkt_len);
-
-    /* write header */
-    w = write(fd, th, TAC_PLUS_HDR_SIZE);
+    assert(pkt_len == pkt_total);
 
-    if(w < TAC_PLUS_HDR_SIZE) {
-        TACSYSLOG(LOG_ERR, "%s: short write on header, wrote %d of %d: %m",\
-            __FUNCTION__, w, TAC_PLUS_HDR_SIZE);
-        free(pkt);
-        free(th);
-        return LIBTAC_STATUS_WRITE_ERR;
-    }
-        
     /* encrypt packet body  */
-    _tac_crypt(pkt, th);
+    _tac_crypt(sess, (u_char *)tb, th);
+
+    *_pkt = pkt;
+    *_len = pkt_total;
+}    /* tac_acct_send_pkt */
 
-    /* write body */
-    w=write(fd, pkt, pkt_len);
-    if(w < pkt_len) {
-        TACSYSLOG(LOG_ERR, "%s: short write on body, wrote %d of %d: %m",\
-            __FUNCTION__, w, pkt_len);
+/*
+ * return value:
+ *      0 : success
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *             LIBTAC_STATUS_WRITE_ERR
+ *             LIBTAC_STATUS_WRITE_TIMEOUT  (pending impl)
+ *             LIBTAC_STATUS_ASSEMBLY_ERR   (pending impl)
+ */
+int tac_acct_send(struct tac_session *sess,
+    u_char type, const char *user,
+    const char *tty, const char *r_addr, struct tac_attrib *attr) {
+
+    u_char *pkt = NULL;
+    unsigned pkt_total = 0;
+    int w, ret = 0;
+
+    /* generate the packet */
+    tac_acct_send_pkt(sess, type, user, tty, r_addr, attr, &pkt, &pkt_total);
+
+    /* write packet */
+    w = write(sess->fd, pkt, pkt_total);
+
+    if(w < 0 || (unsigned) w < pkt_total) {
+        TACSYSLOG(LOG_ERR, "%s: short write of packet, wrote %d of %d: %m",\
+            __FUNCTION__, w, pkt_total);
         ret = LIBTAC_STATUS_WRITE_ERR;
     }
 
     free(pkt);
-    free(th);
     TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret);
     return ret;
-}
+}    /* tac_acct_send */
+
diff --git a/libtac/lib/attrib.c b/libtac/lib/attrib.c
index b8a7d822..80e1ad6e 100644
--- a/libtac/lib/attrib.c
+++ b/libtac/lib/attrib.c
@@ -29,14 +29,14 @@ void tac_add_attrib(struct tac_attrib **attr, char *name, char *value) {
 
 void tac_add_attrib_pair(struct tac_attrib **attr, char *name, char sep, char *value) {
     struct tac_attrib *a;
-    u_char l1 = (u_char) strlen(name);
-    u_char l2;
-    int total_len;
+    unsigned l1 = (u_char) strlen(name);
+    unsigned l2;
+    unsigned total_len;
 
     if (value == NULL) {
         l2 = 0;
     } else {
-        l2 = (u_char) strlen(value);
+        l2 = strlen(value);
     }
     total_len = l1 + l2 + 1; /* "name" + "=" + "value" */
 
@@ -49,7 +49,7 @@ void tac_add_attrib_pair(struct tac_attrib **attr, char *name, char sep, char *v
 
     /* initialize the list if application passed us a null pointer */
     if(*attr == NULL) {
-        *attr = (struct tac_attrib *) xcalloc(1, sizeof(struct tac_attrib));
+        *attr = xcalloc(1, sizeof(struct tac_attrib));
         a = *attr;
     } else {
         /* find the last allocated block */
@@ -57,7 +57,7 @@ void tac_add_attrib_pair(struct tac_attrib **attr, char *name, char sep, char *v
         while(a->next != NULL)
             a = a->next; /* a holds last allocated block */
 
-        a->next = (struct tac_attrib *) xcalloc(1, sizeof(struct tac_attrib));
+        a->next = xcalloc(1, sizeof(struct tac_attrib));
         a = a->next; /* set current block pointer to the new one */
     }
 
@@ -67,7 +67,7 @@ void tac_add_attrib_pair(struct tac_attrib **attr, char *name, char sep, char *v
 
     /* fill the block */
     a->attr_len=total_len;
-    a->attr = (char *) xcalloc(1, total_len+1);
+    a->attr = xcalloc(1, total_len+1);
     bcopy(name, a->attr, l1);    /* paste name */
     *(a->attr+l1)=sep;           /* insert seperator "[=*]" */
     if (value != NULL) {
diff --git a/libtac/lib/authen_r.c b/libtac/lib/authen_r.c
index e89faa6f..7b773cb9 100644
--- a/libtac/lib/authen_r.c
+++ b/libtac/lib/authen_r.c
@@ -23,10 +23,7 @@
 #include "libtac.h"
 #include "messages.h"
 
-/* reads packet from TACACS+ server; returns:
- *  TAC_PLUS_AUTHEN_STATUS_PASS if the authentication succeded
- *  an other integer if failed. Check tacplus.h for all possible values
- *
+/*
  * return value:
  *   <  0 : error status code, see LIBTAC_STATUS_...
  *         LIBTAC_STATUS_READ_TIMEOUT
@@ -35,93 +32,54 @@
  *         LIBTAC_STATUS_PROTOCOL_ERR
  *   >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
  */
-int tac_authen_read(int fd, struct areply *re) {
-	HDR th;
+int tac_authen_parse(struct tac_session *sess, struct areply *re,
+	u_char *pkt, unsigned pkt_total) {
+
+	HDR *th = (HDR *)pkt;
 	struct authen_reply *tb = NULL;
 	size_t len_from_header, len_from_body;
-	ssize_t spacket_read;
 	char *msg = NULL;
-	int timeleft = 0;
 
-	memset(re, 0, sizeof(struct areply));
-
-	/* read the reply header */
-	if (tac_readtimeout_enable
-			&& tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE,
-					&timeleft) < 0) {
-		TACSYSLOG(
-				LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-		re->status = LIBTAC_STATUS_READ_TIMEOUT;
-		free(tb);
-		return re->status;
-	}
-	spacket_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
-	if (spacket_read < TAC_PLUS_HDR_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short reply header, read %zd of %d: %m", __FUNCTION__, spacket_read, TAC_PLUS_HDR_SIZE);
-		re->status = LIBTAC_STATUS_SHORT_HDR;
-		free(tb);
-		return re->status;
-	}
+	memset(re, 0, sizeof(*re));
 
 	/* check the reply fields in header */
-	msg = _tac_check_header(&th, TAC_PLUS_AUTHEN);
+	msg = _tac_check_header(sess, th, TAC_PLUS_AUTHEN);
 	if (msg != NULL) {
 		re->msg = xstrdup(msg);
 		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
 		return re->status;
 	}
 
-	re->seq_no = th.seq_no;
+	len_from_header = ntohl(th->datalength);
 
-	len_from_header = ntohl(th.datalength);
-	if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: length declared in the packet %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE);
-		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
-		return re->status;
-	}
-	tb = (struct authen_reply *) xcalloc(1, len_from_header);
+	tb = tac_hdr_to_body(th);
 
-	/* read reply packet body */
-	if (tac_readtimeout_enable
-			&& tac_read_wait(fd, timeleft, len_from_header, NULL) < 0) {
-		TACSYSLOG(
-				LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-		re->msg = xstrdup(authen_syserr_msg);
-		re->status = LIBTAC_STATUS_READ_TIMEOUT;
-		free(tb);
-		return re->status;
-	}
-	spacket_read = read(fd, tb, len_from_header);
-	if (spacket_read < (ssize_t) len_from_header) {
+	if (pkt_total != TAC_PLUS_HDR_SIZE + len_from_header) {
 		TACSYSLOG(
-				LOG_ERR, "%s: short reply body, read %zd of %zu: %m", __FUNCTION__, spacket_read, len_from_header);
+				LOG_ERR, "%s: short packet, got %u expected %zu: %m", __FUNCTION__,
+				pkt_total, TAC_PLUS_HDR_SIZE + len_from_header);
 		re->msg = xstrdup(authen_syserr_msg);
 		re->status = LIBTAC_STATUS_SHORT_BODY;
-		free(tb);
 		return re->status;
 	}
 
 	/* decrypt the body */
-	_tac_crypt((u_char *) tb, &th);
+	_tac_crypt(sess, (u_char *) tb, th);
 
 	/* Convert network byte order to host byte order */
 	tb->msg_len = ntohs(tb->msg_len);
 	tb->data_len = ntohs(tb->data_len);
 
 	/* check the length fields */
-	len_from_body = sizeof(tb->status) + sizeof(tb->flags) + sizeof(tb->msg_len)
-			+ sizeof(tb->data_len) + tb->msg_len + tb->data_len;
+	len_from_body = TAC_AUTHEN_REPLY_FIXED_FIELDS_SIZE + \
+		tb->msg_len + tb->data_len;
 
 	if (len_from_header != len_from_body) {
 		TACSYSLOG(
-				LOG_ERR, "%s: inconsistent reply body, incorrect key?", __FUNCTION__);
+				LOG_ERR, "%s: inconsistent reply body, header len %zu versus parsed len %zu",
+				__FUNCTION__, len_from_header, len_from_body);
 		re->msg = xstrdup(protocol_err_msg);
 		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
 		return re->status;
 	}
 
@@ -130,23 +88,28 @@ int tac_authen_read(int fd, struct areply *re) {
 
 	if (0 < tb->msg_len) {
 		msg = xcalloc(tb->msg_len + 1, sizeof(char));
-		memset(msg, 0, (tb->msg_len + 1));
-		memcpy(msg, (char*) tb + sizeof(struct authen_reply), tb->msg_len);
-
+		memcpy(msg, tb->msg, tb->msg_len);
+		msg[tb->msg_len] = '\0';
 		re->msg = msg;
 	}
 
+	if (0 < tb->data_len) {
+		msg = xcalloc(tb->data_len + 1, sizeof(char));
+		/* first byte beyond msg is data */
+		memcpy(msg, &tb->msg[tb->msg_len], tb->data_len);
+		msg[tb->data_len] = '\0';
+		re->data = msg;
+	}
+
 	/* server authenticated username and password successfully */
 	if (re->status == TAC_PLUS_AUTHEN_STATUS_PASS) {
 		TACDEBUG(LOG_DEBUG, "%s: authentication ok", __FUNCTION__);
-		free(tb);
 		return re->status;
 	}
 
 	/* server ask for continue packet with password */
 	if (re->status == TAC_PLUS_AUTHEN_STATUS_GETPASS) {
 		TACDEBUG(LOG_DEBUG, "%s: continue packet with password needed", __FUNCTION__);
-		free(tb);
 		return re->status;
 	}
 
@@ -155,15 +118,97 @@ int tac_authen_read(int fd, struct areply *re) {
 		re->flags = tb->flags;
 
 		TACDEBUG(LOG_DEBUG, "%s: continue packet with data request: msg=%.*s",
-						__func__, tb->msg_len, (char*)tb + sizeof(struct authen_reply));
-		free(tb);
+						__func__, tb->msg_len, tb->msg);
 		return re->status;
 	}
 
 	TACDEBUG(LOG_DEBUG, "%s: authentication failed, server reply status=%d",
 					__FUNCTION__, re->status);
 
-	free(tb);
 	return re->status;
+} /* tac_authen_parse_pkt */
+
+/* reads packet from TACACS+ server; returns:
+ *  TAC_PLUS_AUTHEN_STATUS_PASS if the authentication succeded
+ *  an other integer if failed. Check tacplus.h for all possible values
+ *
+ * return value:
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *         LIBTAC_STATUS_READ_TIMEOUT
+ *         LIBTAC_STATUS_SHORT_HDR
+ *         LIBTAC_STATUS_SHORT_BODY
+ *         LIBTAC_STATUS_PROTOCOL_ERR
+ *   >= 0 : server response, see TAC_PLUS_AUTHEN_STATUS_...
+ */
+int tac_authen_read(struct tac_session *sess, struct areply *re) {
+	HDR *th;
+	struct authen_reply *tb = NULL;
+	size_t len_from_header;
+	int r, status, timeleft = 0;
+
+	memset(re, 0, sizeof(*re));
+
+	/* read the reply header */
+	if (tac_readtimeout_enable &&
+		tac_read_wait(sess->fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE, &timeleft) < 0 ) {
+		TACSYSLOG(LOG_ERR,
+			"%s: reply timeout after %d secs", __FUNCTION__, tac_timeout);
+		re->status = LIBTAC_STATUS_READ_TIMEOUT;
+		return re->status;
+	}
+
+	th = xcalloc(1, TAC_PLUS_HDR_SIZE);
+
+	r = read(sess->fd, th, TAC_PLUS_HDR_SIZE);
+	if (r < TAC_PLUS_HDR_SIZE) {
+		TACSYSLOG(LOG_ERR,
+			"%s: short reply header, read %d of %u: %m", __FUNCTION__,
+			((r >= 0) ? r : 0), TAC_PLUS_HDR_SIZE);
+		re->status = LIBTAC_STATUS_SHORT_HDR;
+		free(th);
+		return re->status;
+	}
+
+	len_from_header = ntohl(th->datalength);
+
+	if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+		TACSYSLOG(LOG_ERR,
+			"%s: excessively long packet, got %zu bytes", __FUNCTION__,
+			TAC_PLUS_HDR_SIZE + len_from_header);
+		status = LIBTAC_STATUS_PROTOCOL_ERR;
+		free(th);
+		return status;
+	}
+
+	/* now make room for entire contiguous packet */
+	th = xrealloc(th, TAC_PLUS_HDR_SIZE + len_from_header);
+	tb = tac_hdr_to_body(th);
+
+	/* read reply packet body */
+	if (tac_readtimeout_enable &&
+		tac_read_wait(sess->fd, timeleft, len_from_header, NULL) < 0 ) {
+		TACSYSLOG(LOG_ERR,
+			"%s: reply timeout after %d secs", __FUNCTION__, tac_timeout);
+		status = LIBTAC_STATUS_READ_TIMEOUT;
+	}
+
+	r = read(sess->fd, tb, len_from_header);
+	if (r < 0 || (unsigned) r < len_from_header) {
+		TACSYSLOG(LOG_ERR,
+			"%s: short reply body, read %d of %zu: %m",
+			__FUNCTION__,
+			((r >= 0) ? r : 0), len_from_header);
+		status = LIBTAC_STATUS_SHORT_BODY;
+		free(th);
+		return status;
+	}
+
+	/* now parse remaining packet fields */
+	status = tac_authen_parse(sess, re, (u_char *)th, TAC_PLUS_HDR_SIZE + len_from_header);
+
+	/* all useful data has been copied out */
+	free(th);
+
+	return status;
 } /* tac_authen_read */
 
diff --git a/libtac/lib/authen_s.c b/libtac/lib/authen_s.c
index 8c18b779..e4f9042a 100644
--- a/libtac/lib/authen_s.c
+++ b/libtac/lib/authen_s.c
@@ -51,6 +51,32 @@ digest_chap(u_char digest[MD5_LBLOCK], uint8_t id,
     MD5_Final(digest, &mdcontext);
 }
 
+uint8_t tac_get_authen_type(const char *login)
+{
+	if (login && *login) {
+		if (!strcmp(login, "chap")) {
+			return TAC_PLUS_AUTHEN_TYPE_CHAP;
+		} else if (!strcmp(login, "login")) {
+			return TAC_PLUS_AUTHEN_TYPE_ASCII;
+		}
+	}
+	/* default to PAP */
+	return TAC_PLUS_AUTHEN_TYPE_PAP;
+}
+
+const char *tac_get_authen_string(uint8_t type)
+{
+	const char *authen_types[5] = {
+		"ascii", "pap", "chap", "arap", "mschap"
+	};
+
+	if (TAC_PLUS_AUTHEN_TYPE_ASCII <= type
+	  && type <= TAC_PLUS_AUTHEN_TYPE_MSCHAP)
+		return authen_types[type - TAC_PLUS_AUTHEN_TYPE_ASCII];
+
+	return "???";
+}
+
 /* this function sends a packet do TACACS+ server, asking
  * for validation of given username and password
  *
@@ -61,35 +87,23 @@ digest_chap(u_char digest[MD5_LBLOCK], uint8_t id,
  *             LIBTAC_STATUS_WRITE_TIMEOUT
  *             LIBTAC_STATUS_ASSEMBLY_ERR
  */
-int tac_authen_send(int fd, const char *user, const char *pass, const char *tty,
-		const char *r_addr, u_char action) {
+/* allocate and format an Authentication Start packet */
+void tac_authen_send_pkt(struct tac_session *sess,
+    const char *user, const char *pass, const char *tty,
+    const char *r_addr, u_char action, u_char **_pkt, unsigned *_len) {
 
 	HDR *th; /* TACACS+ packet header */
-	struct authen_start tb; /* message body */
-	int user_len, pass_len, port_len, chal_len, token_len, bodylength, w;
-	int r_addr_len;
-	int pkt_len = 0;
-	int ret = 0;
-	char *chal = "1234123412341234";
+	struct authen_start *tb;     /* message body */
+	unsigned user_len, pass_len, port_len, chal_len, r_addr_len, token_len;
+	const char *chal = "1234123412341234";
 	char *token = NULL;
 	u_char *pkt = NULL;
+	unsigned pkt_total, pkt_len = 0;
 	const uint8_t id = 5;
 
-	th = _tac_req_header(TAC_PLUS_AUTHEN, 0);
-
-	/* set some header options */
-	if (!strcmp(tac_login, "login")) {
-		th->version = TAC_PLUS_VER_0;
-	} else {
-		th->version = TAC_PLUS_VER_1;
-	}
-	th->encryption =
-			tac_encryption ?
-					TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
-
 	TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s",
 					__FUNCTION__, user, tty, r_addr,
-					(tac_encryption) ? "yes" : "no");
+					(sess->tac_encryption) ? "yes" : "no");
 
 	/* get size of submitted data */
 	user_len = strlen(user);
@@ -98,7 +112,7 @@ int tac_authen_send(int fd, const char *user, const char *pass, const char *tty,
 	port_len = strlen(tty);
 	r_addr_len = strlen(r_addr);
 
-	if (!strcmp(tac_login, "chap")) {
+	if (sess->tac_authen_type == TAC_PLUS_AUTHEN_TYPE_CHAP) {
 		u_char digest[MD5_LBLOCK];
 
 		digest_chap(digest, id, pass, pass_len, chal, chal_len);
@@ -113,84 +127,108 @@ int tac_authen_send(int fd, const char *user, const char *pass, const char *tty,
 		token_len = strlen(token);
 	}
 
-	/* fill the body of message */
-	tb.action = action;
-	tb.priv_lvl = tac_priv_lvl;
-	if (!*tac_login) {
-		/* default to PAP */
-		tb.authen_type =
+	assert(user_len <= UCHAR_MAX);
+	assert(port_len <= UCHAR_MAX);
+	assert(r_addr_len <= UCHAR_MAX);
+	assert(token_len <= UCHAR_MAX);
+
+#define TAC_AUTHEN_START_FIXED_TOTAL \
+	(TAC_PLUS_HDR_SIZE + TAC_AUTHEN_START_FIXED_FIELDS_SIZE)
+
+	/*
+	 * precompute the buffer size so we don't need to keep resizing/copying it
+	 */
+	pkt_total = TAC_AUTHEN_START_FIXED_TOTAL +
+		user_len + port_len + r_addr_len + token_len;
+
+	pkt = xcalloc(1, pkt_total);
+	th = (HDR *)pkt;
+
+	/* set some header options */
+	if (sess->tac_authen_type == TAC_PLUS_AUTHEN_TYPE_ASCII) {
+		th->version = TAC_PLUS_VER_0;
+	} else {
+		th->version = TAC_PLUS_VER_1;
+	}
+	th->type = TAC_PLUS_AUTHEN;
+	th->seq_no = ++sess->seq_no;
+	th->encryption = (sess->tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG)
+	    | (sess->tac_multiplex ? TAC_PLUS_SINGLE_CONNECT_FLAG : 0);
+	th->session_id = htonl(sess->tac_session_id);
+	th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE);
+
+	/* fixed part of tacacs body */
+	tb = tac_hdr_to_body(th);
+	tb->action = TAC_PLUS_AUTHEN_LOGIN;
+	tb->priv_lvl = sess->tac_priv_lvl;
+	if (sess->tac_authen_type == TAC_PLUS_AUTHEN_TYPE_PAP) {
+		tb->authen_type =
 				TAC_PLUS_AUTHEN_CHPASS == action ?
 						TAC_PLUS_AUTHEN_TYPE_ASCII : TAC_PLUS_AUTHEN_TYPE_PAP;
 	} else {
-		if (!strcmp(tac_login, "chap")) {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_CHAP;
-		} else if (!strcmp(tac_login, "login")) {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII;
-		} else {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
-		}
+		tb->authen_type = sess->tac_authen_type;
 	}
-	tb.service = tac_authen_service;
-	tb.user_len = user_len;
-	tb.port_len = port_len;
-	tb.r_addr_len = r_addr_len; /* may be e.g Caller-ID in future */
-	tb.data_len = token_len;
+	tb->service = sess->tac_authen_service;
+	tb->user_len = user_len;
+	tb->port_len = port_len;
+	tb->r_addr_len = r_addr_len; /* may be e.g Caller-ID in future */
+	tb->data_len = token_len;
 
-	/* fill body length in header */
-	bodylength = sizeof(tb) + user_len + port_len + r_addr_len + token_len;
+	pkt_len = TAC_AUTHEN_START_FIXED_TOTAL;
 
-	th->datalength = htonl(bodylength);
+#define PUTATTR(data, len) \
+	bcopy(data, pkt + pkt_len, len); \
+	pkt_len += len
 
-	/* we can now write the header */
-	w = write(fd, th, TAC_PLUS_HDR_SIZE);
-	if (w < 0 || w < TAC_PLUS_HDR_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short write on header, wrote %d of %d: %m", __FUNCTION__, w, TAC_PLUS_HDR_SIZE);
-		free(token);
-		free(pkt);
-		free(th);
-		return LIBTAC_STATUS_WRITE_ERR;
-	}
+	/* fill user, port, rem_addr, data fields */
+	PUTATTR(user, user_len);
+	PUTATTR(tty, port_len);
+	PUTATTR(r_addr, r_addr_len);
+	PUTATTR(token, token_len);
 
-	/* build the packet */
-	pkt = (u_char *) xcalloc(1, bodylength + 10);
+	/* no longer need token */
+	free(token);
 
-	bcopy(&tb, pkt + pkt_len, sizeof(tb)); /* packet body beginning */
-	pkt_len += sizeof(tb);
-	bcopy(user, pkt + pkt_len, user_len); /* user */
-	pkt_len += user_len;
-	bcopy(tty, pkt + pkt_len, port_len); /* tty */
-	pkt_len += port_len;
-	bcopy(r_addr, pkt + pkt_len, r_addr_len); /* rem addr */
-	pkt_len += r_addr_len;
+	/* encrypt packet body */
+	_tac_crypt(sess, (u_char *)tb, th);
 
-	bcopy(token, pkt + pkt_len, token_len); /* password */
-	pkt_len += token_len;
+	*_pkt = pkt;
+	*_len = pkt_total;
+}    /* tac_authen_send_pkt */
 
-	/* pkt_len == bodylength ? */
-	if (pkt_len != bodylength) {
-		TACSYSLOG(
-				LOG_ERR, "%s: bodylength %d != pkt_len %d", __FUNCTION__, bodylength, pkt_len);
-		free(token);
-		free(pkt);
-		free(th);
-		return LIBTAC_STATUS_ASSEMBLY_ERR;
-	}
+/* this function sends a packet do TACACS+ server, asking
+ * for validation of given username and password
+ *
+ * return value:
+ *      0 : success
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *             LIBTAC_STATUS_WRITE_ERR
+ *             LIBTAC_STATUS_WRITE_TIMEOUT
+ *             LIBTAC_STATUS_ASSEMBLY_ERR
+ */
+int tac_authen_send(struct tac_session *sess,
+		const char *user, const char *pass, const char *tty,
+		const char *r_addr, u_char action) {
 
-	/* encrypt the body */
-	_tac_crypt(pkt, th);
+	u_char *pkt = NULL;
+	unsigned pkt_total = 0;
+	int w, ret = 0;
+
+	/* generate the packet */
+	tac_authen_send_pkt(sess, user, pass, tty, r_addr, action, &pkt, &pkt_total);
 
-	w = write(fd, pkt, pkt_len);
-	if (w < 0 || w < pkt_len) {
+	/* we can now write the packet */
+	w = write(sess->fd, pkt, pkt_total);
+	if (w < 0 || (unsigned) w < pkt_total) {
 		TACSYSLOG(
-				LOG_ERR, "%s: short write on body, wrote %d of %d: %m", __FUNCTION__, w, pkt_len);
+				LOG_ERR, "%s: short write on packet, wrote %d of %u: %m", __FUNCTION__, w, pkt_total);
 		ret = LIBTAC_STATUS_WRITE_ERR;
 	}
 
-	free(token);
 	free(pkt);
-	free(th);
+
 	TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret);
+
 	return ret;
 } /* tac_authen_send */
 
diff --git a/libtac/lib/author_r.c b/libtac/lib/author_r.c
index 148f7eaa..fe5f3b0a 100644
--- a/libtac/lib/author_r.c
+++ b/libtac/lib/author_r.c
@@ -23,13 +23,7 @@
 #include "libtac.h"
 #include "messages.h"
 
-/* This function returns structure containing
- 1. status (granted/denied)
- 2. message for the user
- 3. list of attributes returned by server
- The attributes should be applied to service authorization
- is requested for.
- *
+/*
  * return value:
  *   <  0 : error status code, see LIBTAC_STATUS_...
  *         LIBTAC_STATUS_READ_TIMEOUT
@@ -38,83 +32,40 @@
  *         LIBTAC_STATUS_PROTOCOL_ERR
  *   >= 0 : server response, see TAC_PLUS_AUTHOR_STATUS_...
  */
-int tac_author_read(int fd, struct areply *re) {
-	HDR th;
+int tac_author_parse(struct tac_session *sess,
+	u_char *pkt, unsigned pkt_total, struct areply *re) {
+
+	HDR *th = (HDR *)pkt;
 	struct author_reply *tb = NULL;
 	size_t len_from_header, len_from_body;
-	ssize_t packet_read;
-	u_char *pktp = NULL;
 	char *msg = NULL;
-	int timeleft = 0;
-	re->msg = NULL;
 	unsigned int r = 0;
 
-	bzero(re, sizeof(struct areply));
-	if (tac_readtimeout_enable
-			&& tac_read_wait(fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE,
-					&timeleft) < 0) {
-
-		TACSYSLOG(
-				LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-		re->msg = xstrdup(author_syserr_msg);
-		re->status = LIBTAC_STATUS_READ_TIMEOUT;
-		free(tb);
-		return re->status;
-	}
-
-	packet_read = read(fd, &th, TAC_PLUS_HDR_SIZE);
-	if (packet_read < TAC_PLUS_HDR_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short reply header, read %zd of %d: %m", __FUNCTION__, packet_read, TAC_PLUS_HDR_SIZE);
-		re->msg = xstrdup(author_syserr_msg);
-		re->status = LIBTAC_STATUS_SHORT_HDR;
-		free(tb);
-		return re->status;
-	}
+	bzero(re, sizeof(*re));
 
 	/* check header consistency */
-	msg = _tac_check_header(&th, TAC_PLUS_AUTHOR);
+	msg = _tac_check_header(sess, th, TAC_PLUS_AUTHOR);
 	if (msg != NULL) {
 		/* no need to process body if header is broken */
 		re->msg = xstrdup(msg);
 		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
 		return re->status;
 	}
 
-	len_from_header = ntohl(th.datalength);
-	if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: length declared in the packet %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE);
-		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
-		return re->status;
-	}
-	tb = (struct author_reply *) xcalloc(1, len_from_header);
+	len_from_header = ntohl(th->datalength);
 
-	/* read reply packet body */
-	if (tac_readtimeout_enable
-			&& tac_read_wait(fd, timeleft, len_from_header, NULL) < 0) {
+	tb = tac_hdr_to_body(th);
 
+	if (pkt_total != TAC_PLUS_HDR_SIZE + len_from_header) {
 		TACSYSLOG(
-				LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
-		re->msg = xstrdup(author_syserr_msg);
-		re->status = LIBTAC_STATUS_READ_TIMEOUT;
-		free(tb);
-		return re->status;
-	}
-	packet_read = read(fd, tb, len_from_header);
-	if (packet_read < (ssize_t) len_from_header) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short reply body, read %zd of %zu", __FUNCTION__, packet_read, len_from_header);
+				LOG_ERR, "%s: short packet, got %u of %zu", __FUNCTION__, pkt_total, len_from_header);
 		re->msg = xstrdup(author_syserr_msg);
 		re->status = LIBTAC_STATUS_SHORT_BODY;
-		free(tb);
 		return re->status;
 	}
 
 	/* decrypt the body */
-	_tac_crypt((u_char *) tb, &th);
+	_tac_crypt(sess, (u_char *) tb, th);
 
 	/* Convert network byte order to host byte order */
 	tb->msg_len = ntohs(tb->msg_len);
@@ -127,55 +78,52 @@ int tac_author_read(int fd, struct areply *re) {
 	len_from_body = TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE + tb->msg_len
 			+ tb->data_len;
 
-	pktp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
-
 	/* cycle through the arguments supplied in the packet */
-	for (r = 0; r < tb->arg_cnt && r < TAC_PLUS_MAX_ARGCOUNT;
-			r++) {
-		if ((ssize_t) len_from_body > packet_read
-				|| ((void *) pktp - (void *) tb) > packet_read) {
+	for (r = 0; ; r++) {
+		if (len_from_body > pkt_total) {
 			TACSYSLOG(
 					LOG_ERR, "%s: arguments supplied in packet seem to exceed its size", __FUNCTION__);
 			re->msg = xstrdup(protocol_err_msg);
 			re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-			free(tb);
 			return re->status;
 		}
-		len_from_body += sizeof(u_char); /* add arg length field's size*/
-		len_from_body += *pktp; /* add arg length itself */
-		pktp++;
+
+		if (r == tb->arg_cnt)
+			break;
+
+		len_from_body += sizeof(tb->arg_len[0]) + tb->arg_len[r];
 	}
 
 	if (len_from_header != len_from_body) {
 		TACSYSLOG(
-				LOG_ERR, "%s: inconsistent reply body, incorrect key?", __FUNCTION__);
+				LOG_ERR, "%s: inconsistent reply body, header len %zu versus parsed len %zu", __FUNCTION__, len_from_header, len_from_body);
 		re->msg = xstrdup(protocol_err_msg);
 		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
-		free(tb);
 		return re->status;
 	}
 
 	/* packet seems to be consistent, prepare return messages */
+
 	/* server message for user */
 	if (tb->msg_len) {
-		char *msg = (char *) xcalloc(1, tb->msg_len + 1);
+		char *msg = xcalloc(1, tb->msg_len + 1);
 		bcopy(
 				(u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE
-						+ (tb->arg_cnt) * sizeof(u_char), msg, tb->msg_len);
-		msg[(int) tb->msg_len] = '\0';
+						+ (tb->arg_cnt) * sizeof(tb->arg_len[0]), msg, tb->msg_len);
+		msg[tb->msg_len] = '\0';
 		re->msg = msg; /* freed by caller */
 	}
 
 	/* server message to syslog */
 	if (tb->data_len) {
-		char *smsg = (char *) xcalloc(1, tb->data_len + 1);
+		char *smsg = xcalloc(1, tb->data_len + 1);
 		bcopy(
 				(u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE
-						+ (tb->arg_cnt) * sizeof(u_char) + tb->msg_len, smsg,
+						+ (tb->arg_cnt) * sizeof(tb->arg_len[0]) + tb->msg_len, smsg,
 				tb->data_len);
-		smsg[(int) tb->data_len] = '\0';
+		smsg[tb->data_len] = '\0';
+		re->data = smsg;     /* Freed by caller */
 		TACSYSLOG(LOG_ERR, "%s: reply message: %s", __FUNCTION__, smsg);
-		free(smsg);
 	}
 
 	TACDEBUG(LOG_DEBUG, "%s: authorization reply status=%d",
@@ -186,6 +134,9 @@ int tac_author_read(int fd, struct areply *re) {
 	/* success conditions */
 	/* XXX support optional vs mandatory arguments */
 	case TAC_PLUS_AUTHOR_STATUS_PASS_REPL:
+		/* @@@ we bzero'd the pointer at the top of this function,
+		 * so there's nothing left to free here!
+		 */
 		tac_free_attrib(&re->attr);
 
 	case TAC_PLUS_AUTHOR_STATUS_PASS_ADD: {
@@ -197,10 +148,11 @@ int tac_author_read(int fd, struct areply *re) {
 
 		/* add attributes received to attribute list returned to
 		 the client */
-		pktp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE;
-		argp = pktp + (tb->arg_cnt * sizeof(u_char)) + tb->msg_len
-				+ tb->data_len;
+		argp = (u_char *) tb + TAC_AUTHOR_REPLY_FIXED_FIELDS_SIZE +
+		       (tb->arg_cnt * sizeof(tb->arg_len[0])) + tb->msg_len +
+			tb->data_len;
 		TACSYSLOG(LOG_DEBUG, "Args cnt %d", tb->arg_cnt);
+
 		/* argp points to current argument string
 		 pktp points to current argument length */
 		for (r = 0; r < tb->arg_cnt && r < TAC_PLUS_MAX_ARGCOUNT;
@@ -210,12 +162,15 @@ int tac_author_read(int fd, struct areply *re) {
 			char *value;
 			char sepchar = '=';
 
-			bcopy(argp, buff, *pktp);
-			buff[*pktp] = '\0';
-			sep = strchr(buff, '=');
+			bcopy(argp, buff, tb->arg_len[r]);
+			buff[tb->arg_len[r]] = '\0';
+
+			sep = strchr(buff, sepchar);
+
 			if (sep == NULL) {
 				sep = strchr(buff, '*');
 			}
+
 			if (sep == NULL) {
 				TACSYSLOG(
 						LOG_WARNING, "AUTHOR_STATUS_PASS_ADD/REPL: av pair does not contain a separator: %s", buff);
@@ -227,20 +182,17 @@ int tac_author_read(int fd, struct areply *re) {
 				*sep = '\0';
 				value = ++sep;
 				/* now buff points to attribute name,
-				 value to the attribute value */
+				 value to the attribute value (only
+				 buff needs to be freed). */
 			}
 			TACSYSLOG(LOG_DEBUG, "Adding buf/value pair (%s,%s)", buff, value);
 			tac_add_attrib_pair(&re->attr, buff, sepchar, value);
-			argp += *pktp;
-			pktp++;
+			argp += tb->arg_len[r];
 		}
-	}
-		free(tb);
-		return re->status;
+
 		break;
 	}
 
-	switch (tb->status) {
 	/* authorization failure conditions */
 	/* failing to follow is allowed by RFC, page 23  */
 	case TAC_PLUS_AUTHOR_STATUS_FOLLOW:
@@ -255,8 +207,101 @@ int tac_author_read(int fd, struct areply *re) {
 		if (!re->msg)
 			re->msg = xstrdup(author_err_msg);
 		re->status = TAC_PLUS_AUTHOR_STATUS_ERROR;
+		break;
 	}
 
-	free(tb);
 	return re->status;
 }
+
+/* This function returns structure containing
+    1. status (granted/denied)
+    2. message for the user
+    3. list of attributes returned by server
+   The attributes should be applied to service authorization
+   is requested for.
+ *
+ * return value:
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *         LIBTAC_STATUS_READ_TIMEOUT
+ *         LIBTAC_STATUS_SHORT_HDR
+ *         LIBTAC_STATUS_SHORT_BODY
+ *         LIBTAC_STATUS_PROTOCOL_ERR
+ *   >= 0 : server response, see TAC_PLUS_AUTHOR_STATUS_...
+ */
+int tac_author_read(struct tac_session *sess, struct areply *re) {
+	HDR *th;
+	struct author_reply *tb = NULL;
+	size_t len_from_header;
+	ssize_t packet_read;
+	int timeleft = 0;
+
+	bzero(re, sizeof(*re));
+
+	if (tac_readtimeout_enable
+			&& tac_read_wait(sess->fd, tac_timeout * 1000, TAC_PLUS_HDR_SIZE,
+					&timeleft) < 0) {
+
+		TACSYSLOG(
+				LOG_ERR, "%s: reply timeout after %d secs", __FUNCTION__, tac_timeout);
+		re->msg = xstrdup(author_syserr_msg);
+		re->status = LIBTAC_STATUS_READ_TIMEOUT;
+		return re->status;
+	}
+
+	th = xcalloc(1, TAC_PLUS_HDR_SIZE);
+
+	packet_read = read(sess->fd, th, TAC_PLUS_HDR_SIZE);
+	if (packet_read < TAC_PLUS_HDR_SIZE) {
+		TACSYSLOG(
+				LOG_ERR, "%s: short reply header, read %zd of %u: %m", __FUNCTION__,
+				((packet_read >= 0) ? packet_read : 0), TAC_PLUS_HDR_SIZE);
+		re->msg = xstrdup(author_syserr_msg);
+		re->status = LIBTAC_STATUS_SHORT_HDR;
+		free(th);
+		return re->status;
+	}
+
+	len_from_header = ntohl(th->datalength);
+	if (len_from_header > TAC_PLUS_MAX_PACKET_SIZE) {
+		TACSYSLOG(
+				LOG_ERR, "%s: length declared in the packet %zu exceeds max packet size %d", __FUNCTION__, len_from_header, TAC_PLUS_MAX_PACKET_SIZE);
+		re->msg = xstrdup(author_syserr_msg);
+		re->status = LIBTAC_STATUS_PROTOCOL_ERR;
+		free(th);
+		return re->status;
+	}
+
+	/* now make room for entire contiguous packet */
+	th = xrealloc(th, TAC_PLUS_HDR_SIZE + len_from_header);
+	tb = tac_hdr_to_body(th);
+
+	/* read reply packet body */
+	if (tac_readtimeout_enable
+			&& tac_read_wait(sess->fd, timeleft, len_from_header, NULL) < 0) {
+		TACSYSLOG(
+				LOG_ERR, "%s: reply timeout after %u secs", __FUNCTION__, tac_timeout);
+		re->msg = xstrdup(author_syserr_msg);
+		re->status = LIBTAC_STATUS_READ_TIMEOUT;
+		free(th);
+		return re->status;
+	}
+	packet_read = read(sess->fd, tb, len_from_header);
+	if (packet_read < 0 || (size_t) packet_read < len_from_header) {
+		TACSYSLOG(
+				LOG_ERR, "%s: short reply body, read %zd of %zu: %m", __FUNCTION__, ((packet_read >= 0) ? packet_read : 0), len_from_header);
+		re->msg = xstrdup(author_syserr_msg);
+		re->status = LIBTAC_STATUS_SHORT_BODY;
+		free(th);
+		return re->status;
+	}
+
+	/* now parse remaining packet fields */
+	(void) tac_author_parse(sess, (u_char *)th, TAC_PLUS_HDR_SIZE + len_from_header,
+				 re);
+
+	/* all useful data has been copied out */
+	free(th);
+
+	return re->status;
+}	/* tac_author_read */
+
diff --git a/libtac/lib/author_s.c b/libtac/lib/author_s.c
index d067e2c0..4109a8ec 100644
--- a/libtac/lib/author_s.c
+++ b/libtac/lib/author_s.c
@@ -22,147 +22,126 @@
 #include "libtac.h"
 #include "xalloc.h"
 
-/* Send authorization request to the server, along with attributes
- specified in attribute list prepared with tac_add_attrib.
- *
- * return value:
- *      0 : success
- *   <  0 : error status code, see LIBTAC_STATUS_...
- *         LIBTAC_STATUS_WRITE_ERR
- *         LIBTAC_STATUS_WRITE_TIMEOUT (pending impl)
- *         LIBTAC_STATUS_ASSEMBLY_ERR  (pending impl)
- */
-int tac_author_send(int fd, const char *user, char *tty, char *r_addr,
-		struct tac_attrib *attr) {
+/* allocate and format an Authorization Start packet */
+void tac_author_send_pkt(struct tac_session *sess,
+	const char *user, const char *tty, const char *r_addr,
+	struct tac_attrib *attr, u_char **_pkt, unsigned *_len) {
 
 	HDR *th;
-	struct author tb;
-	u_char user_len, port_len, r_addr_len;
+	struct author *tb;
+	unsigned user_len, port_len, r_addr_len;
 	struct tac_attrib *a;
-	int i = 0; /* attributes count */
-	int pkt_len = 0; /* current packet length */
-	int pktl = 0; /* temporary storage for previous pkt_len values */
-	int w; /* write() return value */
+	unsigned i; /* attributes count */
 	u_char *pkt = NULL; /* packet building pointer */
-	/* u_char *pktp; *//* obsolete */
-	int ret = 0;
-
-	th = _tac_req_header(TAC_PLUS_AUTHOR, 0);
-
-	/* set header options */
-	th->version = TAC_PLUS_VER_0;
-	th->encryption =
-			tac_encryption ?
-					TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+	unsigned pkt_total, pkt_len = 0;
 
 	TACDEBUG(LOG_DEBUG, "%s: user '%s', tty '%s', rem_addr '%s', encrypt: %s",
 					__FUNCTION__, user,
-					tty, r_addr, tac_encryption ? "yes" : "no");
-
-	user_len = (u_char) strlen(user);
-	port_len = (u_char) strlen(tty);
-	r_addr_len = (u_char) strlen(r_addr);
-
-	tb.authen_method = tac_authen_method;
-	tb.priv_lvl = tac_priv_lvl;
-	if (!*tac_login) {
-		/* default to PAP */
-		tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
-	} else {
-		if (strcmp(tac_login, "chap") == 0) {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_CHAP;
-		} else if (strcmp(tac_login, "login") == 0) {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_ASCII;
-		} else {
-			tb.authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
-		}
-	}
-	tb.service = tac_authen_service;
-	tb.user_len = user_len;
-	tb.port_len = port_len;
-	tb.r_addr_len = r_addr_len;
-
-	/* allocate packet */
-	pkt = (u_char *) xcalloc(1, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE);
-	pkt_len = sizeof(tb);
-
-	/* fill attribute length fields */
-	a = attr;
-	while (a) {
-		pktl = pkt_len;
-		pkt_len += sizeof(a->attr_len);
-		pkt = (u_char*) xrealloc(pkt, pkt_len);
-
-		/* bad method: realloc() is allowed to return different pointer
-		 with each call
-		 pktp=pkt + pkt_len; 
-		 pkt_len += sizeof(a->attr_len);
-		 pkt = xrealloc(pkt, pkt_len);   
-		 */
-
-		bcopy(&a->attr_len, pkt + pktl, sizeof(a->attr_len));
-		i++;
-
-		a = a->next;
-	}
+					tty, r_addr, sess->tac_encryption ? "yes" : "no");
 
-	/* fill the arg count field and add the fixed fields to packet */
-	tb.arg_cnt = i;
-	bcopy(&tb, pkt, TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE);
 	/*
-	 #define PUTATTR(data, len) \
-    pktp = pkt + pkt_len; \
-    pkt_len += len; \
-    pkt = xrealloc(pkt, pkt_len); \
-    bcopy(data, pktp, len);
+	 * precompute the buffer size so we don't need to keep resizing/copying it
 	 */
+	user_len = strlen(user);
+	port_len = strlen(tty);
+	r_addr_len = strlen(r_addr);
 
-#define PUTATTR(data, len) \
-    pktl = pkt_len; \
-    pkt_len += len; \
-    pkt = (u_char*) xrealloc(pkt, pkt_len); \
-    bcopy(data, pkt + pktl, len);
+	assert(user_len <= UCHAR_MAX);
+	assert(port_len <= UCHAR_MAX);
+	assert(r_addr_len <= UCHAR_MAX);
 
-	/* fill user and port fields */
-	PUTATTR(user, user_len)
-	PUTATTR(tty, port_len)
-	PUTATTR(r_addr, r_addr_len)
+#define TAC_AUTHOR_REQ_FIXED_TOTAL \
+			(TAC_PLUS_HDR_SIZE + TAC_AUTHOR_REQ_FIXED_FIELDS_SIZE)
 
-	/* fill attributes */
-	a = attr;
-	while (a) {
-		PUTATTR(a->attr, a->attr_len)
+	pkt_total = TAC_AUTHOR_REQ_FIXED_TOTAL + user_len + port_len + r_addr_len;
 
-		a = a->next;
+	/* ... add in attributes */
+	for (i = 0, a = attr; a; a = a->next, ++i) {
+	   pkt_total += a->attr_len + 1;		   /* count length byte too */
 	}
 
-	/* finished building packet, fill len_from_header in header */
-	th->datalength = htonl(pkt_len);
+	pkt = xcalloc(1, pkt_total);
+	th = (HDR *)pkt;
 
-	/* write header */
-	w = write(fd, th, TAC_PLUS_HDR_SIZE);
+	/* tacacs header */
+	th->version = TAC_PLUS_VER_0;
+	th->type = TAC_PLUS_AUTHOR;
+	th->seq_no = ++sess->seq_no;
+	th->encryption = (sess->tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG)
+	    | (sess->tac_multiplex ? TAC_PLUS_SINGLE_CONNECT_FLAG : 0);
+	th->session_id = htonl(sess->tac_session_id);
+	th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE);
+
+	/* fixed part of tacacs body */
+	tb = tac_hdr_to_body(th);
+	tb->authen_method = sess->tac_authen_method;
+	tb->priv_lvl = sess->tac_priv_lvl;
+	tb->authen_type = sess->tac_authen_type;
+	tb->service = sess->tac_authen_service;
+	tb->user_len = user_len;
+	tb->port_len = port_len;
+	tb->r_addr_len = r_addr_len;
 
-	if (w < TAC_PLUS_HDR_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short write on header, wrote %d of %d: %m", __FUNCTION__, w, TAC_PLUS_HDR_SIZE);
-		free(pkt);
-		free(th);
-		return LIBTAC_STATUS_WRITE_ERR;
+	/* fill the arg count field and add the fixed fields to packet */
+	tb->arg_cnt = i;
+
+	pkt_len = TAC_AUTHOR_REQ_FIXED_TOTAL + i;  /* reserve room for lengths */
+
+#define PUTATTR(data, len) \
+	bcopy(data, pkt + pkt_len, len); \
+	pkt_len += len
+
+	/* fill user and port fields */
+	PUTATTR(user, user_len);
+	PUTATTR(tty, port_len);
+	PUTATTR(r_addr, r_addr_len);
+
+	/* fill attributes */
+	for (i = 0, a = attr; a; a = a->next, i++) {
+		tb->arg_len[i] = a->attr_len;
+		PUTATTR(a->attr, a->attr_len);
 	}
 
+	assert(pkt_len == pkt_total);
+
 	/* encrypt packet body  */
-	_tac_crypt(pkt, th);
+	_tac_crypt(sess, (u_char *)tb, th);
 
-	/* write body */
-	w = write(fd, pkt, pkt_len);
-	if (w < pkt_len) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short write on body, wrote %d of %d: %m", __FUNCTION__, w, pkt_len);
+	*_pkt = pkt;
+	*_len = pkt_total;
+}	/* tac_author_send_pkt */
+
+/* Send authorization request to the server, along with attributes
+ specified in attribute list prepared with tac_add_attrib.
+ *
+ * return value:
+ *	  0 : success
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *		 LIBTAC_STATUS_WRITE_ERR
+ *		 LIBTAC_STATUS_WRITE_TIMEOUT (pending impl)
+ *		 LIBTAC_STATUS_ASSEMBLY_ERR  (pending impl)
+ */
+int tac_author_send(struct tac_session *sess,
+	const char *user, const char *tty, const char *r_addr,
+	struct tac_attrib *attr) {
+
+	u_char *pkt = NULL;
+	unsigned pkt_total = 0;
+	int w, ret = 0;
+
+	/* generate the packet */
+	tac_author_send_pkt(sess, user, tty, r_addr, attr, &pkt, &pkt_total);
+
+	/* write packet */
+	w = write(sess->fd, pkt, pkt_total);
+	if (w < 0 || (unsigned) w < pkt_total) {
+		TACSYSLOG(LOG_ERR, "%s: short write on packet, wrote %d of %d: %m",\
+			__FUNCTION__, w, pkt_total);
 		ret = LIBTAC_STATUS_WRITE_ERR;
 	}
 
 	free(pkt);
-	free(th);
 	TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret);
 	return ret;
-}
+} /* tac_author_send */
+
diff --git a/libtac/lib/connect.c b/libtac/lib/connect.c
index 314d60e1..bf0b515e 100644
--- a/libtac/lib/connect.c
+++ b/libtac/lib/connect.c
@@ -39,35 +39,38 @@ int tac_timeout = 5;
    in server table.
 
  * return value:
- *   >= 0 : valid fd
+ *   == 0 : success
  *   <  0 : error status code, see LIBTAC_STATUS_...
  */
-int tac_connect(struct addrinfo **server, char **key, int servers) {
-    int tries;
-    int fd=-1;
+int tac_connect(struct tac_session *sess,
+    struct addrinfo **server, unsigned servers) {
+    unsigned tries;
+    int retval = -1;
+
+    TACDEBUG(LOG_DEBUG, "session %p connect (%u addrs)", sess, servers);
 
     if(servers == 0 || server == NULL) {
         TACSYSLOG(LOG_ERR, "%s: no TACACS+ servers defined", __FUNCTION__);
     } else {
         for ( tries = 0; tries < servers; tries++ ) {   
-            if((fd=tac_connect_single(server[tries], key[tries], NULL, tac_timeout)) >= 0 ) {
-                /* tac_secret was set in tac_connect_single on success */
+            if ((retval = tac_connect_single(sess, server[tries], NULL, tac_timeout)) >= 0 ) {
                 break;
             }
         }
     }
 
     /* all attempts failed if fd is still < 0 */
-    TACDEBUG(LOG_DEBUG, "%s: exit status=%d",__FUNCTION__, fd);
-    return fd;
+    TACDEBUG(LOG_DEBUG, "%s: exit status=%d: %m", __FUNCTION__, retval);
+    return retval;
 } /* tac_connect */
 
 
 /* return value:
- *   >= 0 : valid fd
+ *   == 0 : success
  *   <  0 : error status code, see LIBTAC_STATUS_...
  */
-int tac_connect_single(const struct addrinfo *server, const char *key, struct addrinfo *srcaddr, int timeout) {
+int tac_connect_single(struct tac_session *sess,
+    const struct addrinfo *server, struct addrinfo *srcaddr, int timeout) {
     int retval = LIBTAC_STATUS_CONN_ERR; /* default retval */
     int fd = -1;
     int flags, rc;
@@ -77,6 +80,8 @@ int tac_connect_single(const struct addrinfo *server, const char *key, struct ad
     struct sockaddr_storage addr;
     char *ip;
 
+    TACDEBUG(LOG_DEBUG, "session %p connect", sess);
+
     if(server == NULL) {
         TACSYSLOG(LOG_ERR, "%s: no TACACS+ server defined", __FUNCTION__);
         goto bomb;
@@ -86,7 +91,7 @@ int tac_connect_single(const struct addrinfo *server, const char *key, struct ad
     ip = tac_ntop(server->ai_addr);
 
     if((fd=socket(server->ai_family, server->ai_socktype, server->ai_protocol)) < 0) {
-        TACSYSLOG(LOG_ERR,"%s: socket creation error: %s", __FUNCTION__,
+        TACSYSLOG(LOG_ERR,"%s: socket creation error: %s", __FUNCTION__,\
             strerror(errno));
         goto bomb;
     }
@@ -104,7 +109,7 @@ int tac_connect_single(const struct addrinfo *server, const char *key, struct ad
     /* bind if source address got explicity defined */
     if (srcaddr) {
         if (bind(fd, srcaddr->ai_addr, srcaddr->ai_addrlen) < 0) {
-            TACSYSLOG(LOG_ERR, "%s: Failed to bind source address: %s",
+            TACSYSLOG(LOG_ERR, "%s: Failed to bind source address: %s",\
                 __FUNCTION__, strerror(errno));
             goto bomb;
         }
@@ -133,6 +138,7 @@ int tac_connect_single(const struct addrinfo *server, const char *key, struct ad
 
     /* timeout */
     if ( rc == 0 ) {
+        TACDEBUG(LOG_ERR, "%s: timeout", __FUNCTION__);
         retval = LIBTAC_STATUS_CONN_TIMEOUT;
         goto bomb;
     }
@@ -161,24 +167,32 @@ int tac_connect_single(const struct addrinfo *server, const char *key, struct ad
 
     /* connected ok */
     TACDEBUG(LOG_DEBUG, "%s: connected to %s", __FUNCTION__, ip);
-    retval = fd;
+    retval = 0;
 
-    /* set current tac_secret */
-    tac_encryption = 0;
-    if (key != NULL && *key) {
-        tac_encryption = 1;
-        tac_secret = key;
-    }
+    /* now stuff the fd into the tac_session */
+    if (sess->fd >= 0)
+        close(sess->fd);
+    sess->fd = fd;
 
 bomb:
     if (retval < 0 && fd != -1)
        close(fd);
 
     TACDEBUG(LOG_DEBUG, "%s: exit status=%d (fd=%d)",\
-        __FUNCTION__, retval < 0 ? retval:0, fd);
+        __FUNCTION__, retval, fd);
     return retval;
 } /* tac_connect_single */
 
+void
+tac_close(struct tac_session *sess)
+{
+    TACDEBUG(LOG_DEBUG, "closing %p", sess);
+
+    if (sess->fd >= 0) {
+        close(sess->fd);
+        sess->fd = -1;
+    }
+}
 
 /* return value:
  *   ptr to char* with format IP address
diff --git a/libtac/lib/cont_s.c b/libtac/lib/cont_s.c
index e2815672..89e1f317 100644
--- a/libtac/lib/cont_s.c
+++ b/libtac/lib/cont_s.c
@@ -25,84 +25,95 @@
 # include "md5.h"
 #endif
 
-/* this function sends a continue packet do TACACS+ server, asking
- * for validation of given password
- *
- * return value:
- *      0 : success
- *   <  0 : error status code, see LIBTAC_STATUS_...
- *         LIBTAC_STATUS_WRITE_ERR
- *         LIBTAC_STATUS_WRITE_TIMEOUT  (pending impl)
- *         LIBTAC_STATUS_ASSEMBLY_ERR
- */
-int tac_cont_send_seq(int fd, const char *pass, int seq) {
+/* allocate and format an continue packet */
+void tac_cont_send_pkt(struct tac_session *sess, const char *pass,
+   u_char **_pkt, unsigned *_len) {
+
 	HDR *th; /* TACACS+ packet header */
-	struct authen_cont tb; /* continue body */
-	int pass_len, bodylength, w;
-	int pkt_len = 0;
-	int ret = 0;
+	struct authen_cont *tb; /* continue body */
+	unsigned pass_len;
 	u_char *pkt = NULL;
+	unsigned pkt_total, pkt_len = 0;
 
-	th = _tac_req_header(TAC_PLUS_AUTHEN, 1);
+	/* get size of submitted data */
+	pass_len = strlen(pass);
+
+	assert(pass_len <= UCHAR_MAX);
+
+#define TAC_AUTHEN_CONT_FIXED_TOTAL \
+	(TAC_PLUS_HDR_SIZE + TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE)
+
+	/*
+	 * precompute the buffer size so we don't need to keep resizing/copying it
+	 */
+	pkt_total = TAC_AUTHEN_CONT_FIXED_TOTAL + pass_len;
+
+	/* build the packet */
+	pkt = xcalloc(1, pkt_total);
+	th = (HDR *)pkt;
 
 	/* set some header options */
 	th->version = TAC_PLUS_VER_0;
-	th->seq_no = seq; /* 1 = request, 2 = reply, 3 = continue, 4 = reply */
-	th->encryption =
-			tac_encryption ?
-					TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG;
+	th->type = TAC_PLUS_AUTHEN;
+	th->seq_no = ++sess->seq_no;
+	th->encryption = (sess->tac_encryption ?  TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG)
+	    | (sess->tac_multiplex ? TAC_PLUS_SINGLE_CONNECT_FLAG : 0);
+	th->session_id = htonl(sess->tac_session_id);
+	th->datalength = htonl(pkt_total - TAC_PLUS_HDR_SIZE);
 
-	/* get size of submitted data */
-	pass_len = strlen(pass);
+	/* fixed part of tacacs body */
+	tb = tac_hdr_to_body(th);
+	tb->flags = 0;
+	tb->user_msg_len = htons(pass_len);
+	tb->user_data_len = 0;
 
-	/* fill the body of message */
-	tb.user_msg_len = htons(pass_len);
-	tb.user_data_len = tb.flags = 0;
+	pkt_len = TAC_AUTHEN_CONT_FIXED_TOTAL;	/* reserve room for lengths */
 
-	/* fill body length in header */
-	bodylength = TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE + 0 + pass_len;
+#define PUTATTR(data, len) \
+	bcopy(data, pkt + pkt_len, len); \
+	pkt_len += len
 
-	th->datalength = htonl(bodylength);
+	PUTATTR(pass, pass_len);
 
-	/* we can now write the header */
-	w = write(fd, th, TAC_PLUS_HDR_SIZE);
-	if (w < 0 || w < TAC_PLUS_HDR_SIZE) {
-		TACSYSLOG(
-				LOG_ERR, "%s: short write on header, wrote %d of %d: %m", __FUNCTION__, w, TAC_PLUS_HDR_SIZE);
-		free(pkt);
-		free(th);
-		return LIBTAC_STATUS_WRITE_ERR;
-	}
+	assert(pkt_len == pkt_total);
 
-	/* build the packet */
-	pkt = (u_char *) xcalloc(1, bodylength);
+	/* encrypt the body */
+	_tac_crypt(sess, (u_char *)tb, th);
 
-	bcopy(&tb, pkt + pkt_len, TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE); /* packet body beginning */
-	pkt_len += TAC_AUTHEN_CONT_FIXED_FIELDS_SIZE;
-	bcopy(pass, pkt + pkt_len, pass_len); /* password */
-	pkt_len += pass_len;
+	*_pkt = pkt;
+	*_len = pkt_total;
+} /* tac_cont_send */
 
-	/* pkt_len == bodylength ? */
-	if (pkt_len != bodylength) {
-		TACSYSLOG(
-				LOG_ERR, "%s: bodylength %d != pkt_len %d", __FUNCTION__, bodylength, pkt_len);
-		free(pkt);
-		free(th);
-		return LIBTAC_STATUS_ASSEMBLY_ERR;
-	}
+/* this function sends a continue packet do TACACS+ server, asking
+ * for validation of given password
+ *
+ * return value:
+ *      0 : success
+ *   <  0 : error status code, see LIBTAC_STATUS_...
+ *         LIBTAC_STATUS_WRITE_ERR
+ *         LIBTAC_STATUS_WRITE_TIMEOUT  (pending impl)
+ *         LIBTAC_STATUS_ASSEMBLY_ERR
+ */
+int tac_cont_send(struct tac_session *sess, const char *pass) {
 
-	/* encrypt the body */
-	_tac_crypt(pkt, th);
+	u_char *pkt = NULL;
+	unsigned pkt_total = 0;
+	int w, ret = 0;
+
+	/* generate the packet */
+	tac_cont_send_pkt(sess, pass, &pkt, &pkt_total);
 
-	w = write(fd, pkt, pkt_len);
-	if (w < 0 || w < pkt_len) {
+	w = write(sess->fd, pkt, pkt_total);
+	if (w < 0 || (unsigned) w < pkt_total) {
 		TACSYSLOG(
-				LOG_ERR, "%s: short write on body, wrote %d of %d: %m", __FUNCTION__, w, pkt_len);
+				LOG_ERR, "%s: short write on packet, wrote %d of %u: %m", __FUNCTION__, w, pkt_total);
 		ret = LIBTAC_STATUS_WRITE_ERR;
 	}
 
 	free(pkt);
-	free(th);
+
 	TACDEBUG(LOG_DEBUG, "%s: exit status=%d", __FUNCTION__, ret);
+
 	return ret;
 } /* tac_cont_send */
+
diff --git a/libtac/lib/crypt.c b/libtac/lib/crypt.c
index b3e3158d..38c7b184 100644
--- a/libtac/lib/crypt.c
+++ b/libtac/lib/crypt.c
@@ -35,9 +35,9 @@
 /* Produce MD5 pseudo-random pad for TACACS+ encryption.
    Use data from packet header and secret, which
    should be a global variable */
-static void _tac_md5_pad(const HDR *hdr,
+static void _tac_md5_pad(const struct tac_session *sess, const HDR *hdr,
         u_char *new_digest, u_char *old_digest)  {
-    unsigned tac_secret_len = strlen(tac_secret);
+    unsigned tac_secret_len = strlen(sess->tac_secret);
     MD5_CTX mdcontext;
 
     /* MD5_1 = MD5{session_id, secret, version, seq_no}
@@ -46,7 +46,7 @@ static void _tac_md5_pad(const HDR *hdr,
     /* place session_id, key, version and seq_no in buffer */
     MD5_Init(&mdcontext);
     MD5_Update(&mdcontext, (const u_char *) &hdr->session_id, sizeof(hdr->session_id));
-    MD5_Update(&mdcontext, (const u_char *) tac_secret, tac_secret_len);
+    MD5_Update(&mdcontext, (const u_char *) sess->tac_secret, tac_secret_len);
     MD5_Update(&mdcontext, &hdr->version, sizeof(hdr->version));
     MD5_Update(&mdcontext, &hdr->seq_no, sizeof(hdr->seq_no));
 
@@ -62,11 +62,11 @@ static void _tac_md5_pad(const HDR *hdr,
 /* Perform encryption/decryption on buffer. This means simply XORing
    each byte from buffer with according byte from pseudo-random
    pad. */
-void _tac_crypt(u_char *buf, const HDR *th) {
+void _tac_crypt(const struct tac_session *sess, u_char *buf, const HDR *th) {
     unsigned i, j, length = ntohl(th->datalength);
  
     /* null operation if no encryption requested */
-    if((tac_secret != NULL) && (th->encryption & TAC_PLUS_UNENCRYPTED_FLAG) != TAC_PLUS_UNENCRYPTED_FLAG) {
+    if ((sess->tac_secret != NULL) && (th->encryption & TAC_PLUS_UNENCRYPTED_FLAG) != TAC_PLUS_UNENCRYPTED_FLAG) {
         u_char digest[MD5_LBLOCK];
  
         for (i=0; i<length; i++) {
@@ -78,7 +78,7 @@ void _tac_crypt(u_char *buf, const HDR *th) {
              * the previous digest.
              */
             if (j == 0)
-                _tac_md5_pad(th, digest, ((i > 0) ? digest : NULL));
+                _tac_md5_pad(sess, th, digest, ((i > 0) ? digest : NULL));
 
             buf[i] ^= digest[j];
         }
diff --git a/libtac/lib/hdr_check.c b/libtac/lib/hdr_check.c
index 36866ff8..4804d9fc 100644
--- a/libtac/lib/hdr_check.c
+++ b/libtac/lib/hdr_check.c
@@ -29,22 +29,22 @@
  * Returns pointer to error message
  * or NULL when the header seems to be correct
  */
-char *_tac_check_header(HDR *th, int type) {
+char *_tac_check_header(struct tac_session *sess, HDR *th, uint8_t type) {
     if(th->type != type) {
         TACSYSLOG(LOG_ERR,\
             "%s: unrelated reply, type %d, expected %d",\
             __FUNCTION__, th->type, type);
         return protocol_err_msg;
-    } else if (1 == (th->seq_no % 2)) {
-        TACSYSLOG(LOG_ERR, "%s: not a reply - seq_no %d not even",\
-            __FUNCTION__, th->seq_no);
+    } else if (++sess->seq_no != th->seq_no) {
+        TACSYSLOG(LOG_ERR, "%s: expected reply seq no %d but got %d",\
+            __FUNCTION__, sess->seq_no, th->seq_no);
         return protocol_err_msg;
-    } /* else if(ntohl(th->session_id) != session_id) {
+    } else if(ntohl(th->session_id) != sess->tac_session_id) {
         TACSYSLOG(LOG_ERR,\
-            "%s: unrelated reply, received session_id %d != sent %d",\
-            __FUNCTION__, ntohl(th->session_id), session_id);
+            "%s: unrelated reply, received session_id %u != sent %u",\
+            __FUNCTION__, ntohl(th->session_id), sess->tac_session_id);
         return protocol_err_msg;
-    } */
+    }
     
     return NULL; /* header is ok */    
 } /* check header */
diff --git a/libtac/lib/header.c b/libtac/lib/header.c
index 2156d2a4..89c71e79 100644
--- a/libtac/lib/header.c
+++ b/libtac/lib/header.c
@@ -28,33 +28,6 @@
 
 #include "magic.h"
 
-/* Miscellaneous variables that are global, because we need
- * store their values between different functions and connections.
- */
-/* Session identifier. */
-int session_id;
-
-/* Encryption flag. */
-int tac_encryption = 0;
-
-/* Pointer to TACACS+ shared secret string. */
-/* note: tac_secret will point to tacplus_server[i].key */
-const char *tac_secret = NULL;
-
-/* TACACS+ shared login string. */
-char tac_login[64]; /* default is PAP */
-
-/* priv_lvl */
-int tac_priv_lvl = TAC_PLUS_PRIV_LVL_MIN;
-
-/* Authentication Method */
-int tac_authen_method = TAC_PLUS_AUTHEN_METH_TACACSPLUS;
-
-/* Service requesting authentication */
-int tac_authen_service = TAC_PLUS_AUTHEN_SVC_PPP;
-
-/* additional runtime flags */
-
 int tac_debug_enable = 0;
 int tac_readtimeout_enable = 0;
 
@@ -66,21 +39,22 @@ int tac_readtimeout_enable = 0;
  * field depends on the TACACS+ request type and thus it
  * cannot be predefined.
  */
-HDR *_tac_req_header(u_char type, int cont_session) {
+HDR *_tac_req_header(struct tac_session *sess, u_char type, bool cont_session) {
     HDR *th;
 
     th=(HDR *) xcalloc(1, TAC_PLUS_HDR_SIZE);
 
     /* preset some packet options in header */
     th->type=type;
-    th->seq_no=1; /* always 1 for request */
-    th->encryption=TAC_PLUS_ENCRYPTED_FLAG;
+    th->seq_no=++sess->seq_no;
+    th->encryption=(sess->tac_encryption ? TAC_PLUS_ENCRYPTED_FLAG : TAC_PLUS_UNENCRYPTED_FLAG)
+	| (sess->tac_multiplex ? TAC_PLUS_SINGLE_CONNECT_FLAG : 0);
  
     /* make session_id from pseudo-random number */
     if (!cont_session) {
-        session_id = magic();
+        tac_session_new_session_id(sess);
     }
-    th->session_id = htonl(session_id);
+    th->session_id = htonl(sess->tac_session_id);
 
     return th;
 }
diff --git a/libtac/lib/parse.c b/libtac/lib/parse.c
new file mode 100644
index 00000000..67c1dd4c
--- /dev/null
+++ b/libtac/lib/parse.c
@@ -0,0 +1,64 @@
+/* parse.c - Callback for dispatching received packets
+ *
+ * Copyright (C) 2016, Philip Prindeville <philipp@redfish-solutions.com>
+ * Copyright (C) 2016, Brocade Communications Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include <stdio.h>
+
+#include "libtac.h"
+
+void tac_parse_pkt(struct tac_session *sess, struct cb_ctx *ctx, u_char *pkt, unsigned len)
+{
+    HDR *th = (HDR *)pkt;
+    int status = -1;
+    struct areply re = { 0 };
+
+    switch (th->type) {
+    case TAC_PLUS_AUTHEN:
+	TACDEBUG(LOG_DEBUG, "session %p got authen %u bytes", sess, len);
+	status = tac_authen_parse(sess, &re, pkt, len);
+	break;
+
+    case TAC_PLUS_AUTHOR:
+	TACDEBUG(LOG_DEBUG, "session %p got author %u bytes", sess, len);
+	status = tac_author_parse(sess, pkt, len, &re);
+	break;
+
+    case TAC_PLUS_ACCT:
+	TACDEBUG(LOG_DEBUG, "session %p got account %u bytes", sess, len);
+	status = tac_acct_parse(sess, pkt, len, &re);
+	break;
+
+    default:
+	TACDEBUG(LOG_INFO, "session %p got %u byte packet of %02x type; ignoring", \
+	    sess, len, th->type);
+
+	break;
+    }
+
+    if (sess->response_cb) {
+	TACDEBUG(LOG_DEBUG, "session %p user callback", sess);
+	sess->response_cb(sess, ctx, status, th->type, &re);
+    }
+
+    tac_free_attrib(&re.attr);
+    free(re.msg);
+    free(re.data);
+}
+
diff --git a/libtac/lib/session.c b/libtac/lib/session.c
new file mode 100644
index 00000000..19292830
--- /dev/null
+++ b/libtac/lib/session.c
@@ -0,0 +1,136 @@
+/* session.c - Parameters for concurrent Tacacs+ sessions
+ *
+ * Copyright (C) 2016, Philip Prindeville <philipp@redfish-solutions.com>
+ * Copyright (C) 2016, Brocade Communications Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+
+#include "libtac.h"
+#include "xalloc.h"
+
+/*
+ * like tac_session_alloc() but with n extra bytes of room at the bottom
+ */
+
+struct tac_session *
+tac_session_alloc_extra(unsigned n)
+{
+    struct tac_session *sess = (struct tac_session *)xcalloc(1, sizeof(*sess) + n);
+
+    TACDEBUG(LOG_DEBUG, "session %p constructed (%u extra)", sess, n);
+
+    sess->tac_timeout = 5;
+    sess->tac_secret = NULL;
+    sess->tac_session_id = magic();
+    sess->tac_encryption = sess->tac_multiplex = false;
+    sess->tac_idle = true;
+    sess->tac_priv_lvl = TAC_PLUS_PRIV_LVL_MIN;
+    sess->tac_authen_service = TAC_PLUS_AUTHEN_SVC_PPP;
+    sess->tac_authen_method = TAC_PLUS_AUTHEN_METH_TACACSPLUS;
+    sess->tac_authen_type = TAC_PLUS_AUTHEN_TYPE_PAP;
+    sess->seq_no = 0;
+    sess->fd = -1;
+    sess->context.sess = sess;
+
+    return sess;
+}
+
+struct tac_session *
+tac_session_alloc(void)
+{
+    return tac_session_alloc_extra(0);
+}
+
+void
+tac_session_set_secret(struct tac_session *sess, const char *secret)
+{
+    if (secret == NULL || !*secret) {
+        TACDEBUG(LOG_DEBUG, "session %p clearing session secret", sess);
+        sess->tac_encryption = false;
+        sess->tac_secret = NULL;
+    } else {
+        TACDEBUG(LOG_DEBUG, "session %p setting session secret", sess);
+        sess->tac_encryption = true;
+        sess->tac_secret = secret;
+    }
+}
+
+void
+tac_session_set_authen_type(struct tac_session *sess, uint8_t type)
+{
+    sess->tac_authen_type = type;
+}
+
+void
+tac_session_set_timeout(struct tac_session *sess, unsigned timeout)
+{
+    sess->tac_timeout = timeout;
+}
+
+void
+tac_session_set_multiplex(struct tac_session *sess, bool multiplex)
+{
+    sess->tac_multiplex = multiplex;
+}
+
+void
+tac_session_set_response(struct tac_session *sess, response_cb_t cb)
+{
+    sess->response_cb = cb;
+}
+
+void
+tac_session_set_oob(struct tac_session *sess, oob_cb_t cb)
+{
+    sess->oob_cb = cb;
+}
+
+struct cb_ctx *
+tac_session_get_context(struct tac_session *sess)
+{
+    return &sess->context;
+}
+
+void
+tac_session_new_session_id(struct tac_session *sess)
+{
+    uint32_t id = magic();
+
+    TACDEBUG(LOG_DEBUG, "session %p new id %#08x", sess, id);
+    sess->tac_session_id = id;
+}
+
+void
+tac_session_reset_seq(struct tac_session *sess)
+{
+    sess->seq_no = 0;
+}
+
+void *
+tac_session_get_user_data(struct tac_session *sess)
+{
+    return &sess->user_data;
+}
+
+void
+tac_session_free(struct tac_session *sess)
+{
+    TACDEBUG(LOG_DEBUG, "session %p destroy", sess);
+    tac_close(sess);
+    free(sess);
+}
+
diff --git a/libtac/lib/wrappers.c b/libtac/lib/wrappers.c
new file mode 100644
index 00000000..b4315bed
--- /dev/null
+++ b/libtac/lib/wrappers.c
@@ -0,0 +1,470 @@
+/* wrappers.c - Add libevent2 wrappers
+ *
+ * Copyright (C) 2016, Philip Prindeville <philipp@redfish-solutions.com>
+ * Copyright (C) 2016, Brocade Communications Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program - see the file COPYING.
+ *
+ * See `CHANGES' file for revision history.
+ */
+#include <stdio.h>
+
+#include "libtac.h"
+#include "xalloc.h"
+#include "../../config.h"
+
+#include <event2/event.h>
+#include <event2/buffer.h>
+#include <event2/bufferevent.h>
+
+void *
+tac_event_loop_initialize()
+{
+    struct event_base *ev_base = NULL;
+
+    struct event_config *cfg = event_config_new();
+
+    TACDEBUG(LOG_DEBUG, "constructing event base");
+
+    /* can tailor methods later */
+    ev_base = event_base_new_with_config(cfg);
+
+    /* event_base_priority_init(ev_base, ev_priorities); */
+
+    /* no longer needed */
+    event_config_free(cfg);
+
+    TACDEBUG(LOG_DEBUG, "return event_base %p", ev_base);
+
+    return ev_base;
+}
+
+/*
+ * Loops until there are no more events.
+ * @return: 0 on normal exit, 1 if no more events are queued and -1 on error.
+ */
+int
+tac_event_loop(void *tac_event)
+{
+    struct event_base *ev_base = (struct event_base *) tac_event;
+    int ret;
+
+    TACDEBUG(LOG_DEBUG, "running event_base loop %p", ev_base);
+
+    /* run until an event handler tells us to shut down via tac_end_loop() */
+    /* EVLOOP_NO_EXIT_ON_EMPTY is not supported in older libevent versions. */
+    ret = event_base_loop(ev_base, 0);
+
+    TACDEBUG(LOG_DEBUG, "event_base loop returns %d", ret);
+
+    return ret;
+}
+
+void
+tac_event_loop_end(void *tac_event)
+{
+    struct event_base *ev_base = (struct event_base *) tac_event;
+
+    TACDEBUG(LOG_DEBUG, "ending event_base %p", ev_base);
+
+    (void)event_base_loopexit(ev_base, NULL);
+}
+
+void
+tac_event_loop_free(void *tac_event)
+{
+    struct event_base *ev_base = (struct event_base *) tac_event;
+
+    TACDEBUG(LOG_DEBUG, "destroying event_base %p", ev_base);
+
+    event_base_free(ev_base);
+}
+
+void
+tac_event_loop_global_shutdown(void)
+{
+#if LIBEVENT_VERSION_NUMBER >= 0x02010100
+    libevent_global_shutdown();
+#endif
+}
+
+void tac_session_reset_timeouts(struct tac_session *sess, bool on)
+{
+    struct timeval tv = { sess->tac_timeout, 0 };
+
+    TACDEBUG(LOG_DEBUG, "session %p reset_timeouts %u %s", sess, sess->tac_timeout, (on ? "on" : "off"));
+
+    if (!sess->bufev)
+        return;
+
+    /* nothing will be enabled if we haven't yet connected... */
+    if (bufferevent_get_enabled(sess->bufev) == 0)
+        return;
+
+    if (on)
+        bufferevent_set_timeouts(sess->bufev, &tv, &tv);
+    else
+        bufferevent_set_timeouts(sess->bufev, NULL, NULL);
+}
+
+static void eventcb(struct bufferevent *bev, short events, void *ptr)
+{
+    struct cb_ctx *ctx = (struct cb_ctx *)ptr;
+    struct tac_session *sess = ctx->sess;
+
+#if 0
+    fprintf(stderr, "eventcb: bev=%p, events=%#x, ptr=%p\n", bev, (unsigned)events, ptr);
+    fputs("  flags =", stderr);
+    if (events & BEV_EVENT_READING)
+        fputs(" reading", stderr);
+    if (events & BEV_EVENT_WRITING)
+        fputs(" writing", stderr);
+    if (events & BEV_EVENT_EOF)
+        fputs(" eof", stderr);
+    if (events & BEV_EVENT_ERROR)
+        fputs(" error", stderr);
+    if (events & BEV_EVENT_TIMEOUT)
+        fputs(" timeout", stderr);
+    if (events & BEV_EVENT_CONNECTED)
+        fputs(" connected", stderr);
+    fputc('\n', stderr);
+#endif
+
+    if (sess->oob_cb) {
+        if (events & BEV_EVENT_CONNECTED) {
+                TACDEBUG(LOG_DEBUG, "session %p connected", sess);
+		/*
+		 * if we had enqueued a request before the connect
+		 * completed, then the idle flag would be false
+		 * and we would want to reset the timer; if we didn't
+		 * have a request on-the-wire, then the timeout gets
+		 * cleared once we're connected.
+		 */
+		tac_session_reset_timeouts(sess, !sess->tac_idle);
+		(sess->oob_cb)(sess, &sess->context, CONNECTED);
+        }
+        if (events & BEV_EVENT_ERROR) {
+                TACDEBUG(LOG_DEBUG, "session %p errored", sess);
+                (sess->oob_cb)(sess, &sess->context, ERROR);
+        }
+        if (events & BEV_EVENT_TIMEOUT) {
+                TACDEBUG(LOG_DEBUG, "session %p timeout", sess);
+                (sess->oob_cb)(sess, &sess->context, TIMEOUT);
+        }
+        if (events & BEV_EVENT_EOF) {
+                TACDEBUG(LOG_DEBUG, "session %p closing", sess);
+                (sess->oob_cb)(sess, &sess->context, CLOSED);
+        }
+    }
+
+#if 0
+    /* we don't close on a timeout because it will keep trying; if you
+     * want to call tac_session_free() on the first TIMEOUT, you can
+     * or you can wait for the eventual ERROR.  We might want to let
+     * the user do all the cleanup via tac_session_free()...
+     */
+    if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
+        bufferevent_free(bev);
+        sess->bufev = NULL;
+    }
+#endif
+
+    /* now notify us of inbound data, etc */
+    if (events & BEV_EVENT_CONNECTED) {
+	TACDEBUG(LOG_DEBUG, "session %p enabling r/w callbacks", sess);
+        bufferevent_enable(bev, EV_READ|EV_WRITE);
+    }
+}
+
+static void writecb(struct bufferevent *bev, void *ptr)
+{
+    struct cb_ctx *ctx = (struct cb_ctx *)ptr;
+    struct tac_session *sess = ctx->sess;
+    struct evbuffer *evbuf = bufferevent_get_output(bev);
+    size_t n = evbuffer_get_length(evbuf);
+
+    sess = sess;		/* unused */
+    n = n;
+
+    TACDEBUG(LOG_DEBUG, "session %p write cb %ld bytes", sess, n);
+}
+
+static void readcb(struct bufferevent *bev, void *ptr)
+{
+    struct cb_ctx *ctx = (struct cb_ctx *)ptr;
+    struct tac_session *sess = ctx->sess;
+    unsigned length;
+    struct evbuffer *evbuf = bufferevent_get_input(bev);
+    size_t n = evbuffer_get_length(evbuf);
+    u_char *start;
+    HDR *th;
+    int i;
+
+    sess = sess;		/* unused */
+
+    TACDEBUG(LOG_DEBUG, "session %p read cb %ld bytes", sess, n);
+
+    /* evbuffer_pullup() returns NULL if there aren't enough bytes in
+     * the buffer yet to pull-up as many as are requested.
+     */
+    start = evbuffer_pullup(evbuf, TAC_PLUS_HDR_SIZE);
+    if (! start) {
+        TACDEBUG(LOG_DEBUG, "session %p not enough data to pullup", sess);
+        return;
+    }
+
+    th = (HDR *)start;
+
+    length = ntohl(th->datalength) + TAC_PLUS_HDR_SIZE;
+
+    TACDEBUG(LOG_DEBUG, "session %p header shows %u bytes", sess, length);
+
+    /* if we're short, we'll get called again when more data arrives */
+    if (n < length) {
+         TACDEBUG(LOG_DEBUG, "session %p want %u bytes but have %ld", sess, length, n);
+         return;
+    }
+
+    u_char *pkt = xcalloc(1, length);
+
+    /* copy out... */
+    i = evbuffer_remove(evbuf, pkt, length);
+
+    if (i < 0 || (unsigned) i != length)
+        TACDEBUG(LOG_DEBUG, "%s: evbuffer_remove want %u got %d", __FUNCTION__, length, i);
+
+    /* turn off timeouts */
+    tac_session_reset_timeouts(sess, false);
+
+    /* received response, so connection is idle again */
+    sess->tac_idle = true;
+
+    tac_parse_pkt(sess, ctx, pkt, ((i > 0) ? i : 0));
+
+    free(pkt);
+}
+
+bool
+tac_connect_single_ev(struct tac_session *sess, void *tac_event,
+    struct addrinfo *server, struct addrinfo *srcaddr, unsigned timeout)
+{
+    struct event_base *ev_base = (struct event_base *)tac_event;
+    struct bufferevent *bev;
+
+    TACDEBUG(LOG_DEBUG, "sess %p starting connect", sess);
+
+    bev = bufferevent_socket_new(ev_base, -1, BEV_OPT_CLOSE_ON_FREE);
+
+    /* bind if source address got explicitly defined */
+    if (srcaddr != NULL) {
+        if (bind(bufferevent_getfd(bev), srcaddr->ai_addr, srcaddr->ai_addrlen) < 0) {
+	    TACDEBUG(LOG_DEBUG, "couldn't bind src address: %m");
+        }
+    }
+
+    if (timeout != 0) {
+        struct timeval tv = { timeout, 0 };
+
+        TACDEBUG(LOG_DEBUG, "session %p timeout %ld.%06ld", sess, tv.tv_sec, tv.tv_usec);
+        bufferevent_set_timeouts(bev, &tv, &tv);
+    }
+
+    bufferevent_setwatermark(bev, EV_READ, TAC_PLUS_HDR_SIZE, 0);
+    bufferevent_setcb(bev, readcb, writecb, eventcb, &sess->context);
+
+    if (bufferevent_socket_connect(bev, server->ai_addr, server->ai_addrlen) < 0) {
+        TACDEBUG(LOG_DEBUG, "session %p bufferevent connect fails: %m", sess);
+        bufferevent_free(bev);
+        return false;
+    }
+
+    /* don't bind bufferevent to session until connect initiated */
+    sess->bufev = bev;
+
+    return true;
+}
+
+static void cleanup_malloc(const void *data, size_t len, void *ptr)
+{
+    len = len;
+    ptr = ptr;			/* unused */
+
+    free((void *)data);
+}
+
+/*
+ * return value:
+ *	true/false success
+ */
+bool
+tac_authen_send_ev(struct tac_session *sess,
+    const char *user, const char *pass, const char *tty,
+    const char *r_addr, u_char action) {
+
+    u_char *pkt = NULL;
+    unsigned pkt_total = 0;
+    struct evbuffer *evbuf = evbuffer_new();
+    int ret;
+
+    TACDEBUG(LOG_DEBUG, "session %p authen %s/%s/%s", sess, user, tty, r_addr);
+
+    sess->context.login = user;
+    sess->context.pass = pass;
+
+    /* generate the packet */
+    tac_authen_send_pkt(sess, user, pass, tty, r_addr, action, &pkt, &pkt_total);
+
+    /* if reusing connection, reset timeouts */
+    tac_session_reset_timeouts(sess, true);
+
+    /*
+     * make evbuffer wrap around our packet, and call cleanup (free)
+     * when done
+     */
+    evbuffer_add_reference(evbuf, pkt, pkt_total, cleanup_malloc, NULL);
+
+    ret = bufferevent_write_buffer(sess->bufev, evbuf);
+    evbuffer_free(evbuf);
+
+    /* we have a request on-the-wire */
+    sess->tac_idle = false;
+
+    TACDEBUG(LOG_DEBUG, "session %p: write status=%d", sess, ret);
+
+    return (ret == 0);
+}
+
+/*
+ * return value:
+ *	true/false
+ */
+bool
+tac_author_send_ev(struct tac_session *sess,
+    const char *user, const char *tty, const char *r_addr,
+    struct tac_attrib *attr) {
+
+    u_char *pkt = NULL;
+    unsigned pkt_total = 0;
+    struct evbuffer *evbuf = evbuffer_new();
+    int ret;
+
+    TACDEBUG(LOG_DEBUG, "session %p author %s/%s/%s", sess, user, tty, r_addr);
+
+    sess->context.login = user;
+    sess->context.pass = NULL;
+
+    /* generate the packet */
+    tac_author_send_pkt(sess, user, tty, r_addr, attr, &pkt, &pkt_total);
+
+    /* if reusing connection, reset timeouts */
+    tac_session_reset_timeouts(sess, true);
+
+    /*
+     * make evbuffer wrap around our packet, and call cleanup (free)
+     * when done
+     */
+    evbuffer_add_reference(evbuf, pkt, pkt_total, cleanup_malloc, NULL);
+
+    ret = bufferevent_write_buffer(sess->bufev, evbuf);
+    evbuffer_free(evbuf);
+
+    /* we have a request on-the-wire */
+    sess->tac_idle = false;
+
+    TACDEBUG(LOG_DEBUG, "session %p write status=%d", sess, ret);
+
+    return (ret == 0);
+}
+
+/*
+ * return value:
+ *      true/false
+ */
+bool
+tac_acct_send_ev(struct tac_session *sess,
+    u_char type, const char *user, const char *tty,
+    const char *r_addr, struct tac_attrib *attr) {
+
+    u_char *pkt = NULL;
+    unsigned pkt_total = 0;
+    struct evbuffer *evbuf = evbuffer_new();
+    int ret;
+
+    TACDEBUG(LOG_DEBUG, "session %p account %s/%s/%s", sess, user, tty, r_addr);
+
+    sess->context.login = user;
+    sess->context.pass = NULL;
+
+    /* generate the packet */
+    tac_acct_send_pkt(sess, type, user, tty, r_addr, attr, &pkt, &pkt_total);
+
+    /* if reusing connection, reset timeouts */
+    tac_session_reset_timeouts(sess, true);
+
+    /*
+     * make evbuffer wrap around our packet, and call cleanup (free)
+     * when done
+     */
+    evbuffer_add_reference(evbuf, pkt, pkt_total, cleanup_malloc, NULL);
+
+    ret = bufferevent_write_buffer(sess->bufev, evbuf);
+    evbuffer_free(evbuf);
+
+    /* we have a request on-the-wire */
+    sess->tac_idle = false;
+
+    TACDEBUG(LOG_DEBUG, "session %p write status=%d", sess, ret);
+
+    return (ret == 0);
+}
+
+/*
+ * return value:
+ *      true/false
+ */
+bool
+tac_cont_send_ev(struct tac_session *sess, const char *pass) {
+
+    u_char *pkt = NULL;
+    unsigned pkt_total = 0;
+    struct evbuffer *evbuf = evbuffer_new();
+    int ret;
+
+    TACDEBUG(LOG_DEBUG, "session %p authen-cont %s", sess, "********");
+
+    sess->context.pass = pass;
+
+    /* generate the packet */
+    tac_cont_send_pkt(sess, pass, &pkt, &pkt_total);
+
+    /* if reusing connection, reset timeouts */
+    tac_session_reset_timeouts(sess, true);
+
+    /*
+     * make evbuffer wrap around our packet, and call cleanup (free)
+     * when done
+     */
+    evbuffer_add_reference(evbuf, pkt, pkt_total, cleanup_malloc, NULL);
+
+    ret = bufferevent_write_buffer(sess->bufev, evbuf);
+    evbuffer_free(evbuf);
+
+    /* we have a request on-the-wire */
+    sess->tac_idle = false;
+
+    TACDEBUG(LOG_DEBUG, "session %p write status=%d", sess, ret);
+
+    return (ret == 0);
+}
+
diff --git a/libtac/lib/xalloc.c b/libtac/lib/xalloc.c
index d077bf26..81db39f7 100644
--- a/libtac/lib/xalloc.c
+++ b/libtac/lib/xalloc.c
@@ -26,7 +26,7 @@ void *xcalloc(size_t nmemb, size_t size) {
 	void *val = calloc(nmemb, size);
 	if (val == 0) {
 		TACSYSLOG(
-				LOG_ERR, "%s: calloc(%u,%u) failed", __FUNCTION__, (unsigned) nmemb, (unsigned) size);
+				LOG_ERR, "%s: calloc(%zd,%zd) failed", __FUNCTION__, nmemb, size);
 		exit(1);
 	}
 	return val;
@@ -36,7 +36,7 @@ void *xrealloc(void *ptr, size_t size) {
 	void *val = realloc(ptr, size);
 	if (val == 0) {
 		TACSYSLOG(
-				LOG_ERR, "%s: realloc(%u) failed", __FUNCTION__, (unsigned) size);
+				LOG_ERR, "%s: realloc(%zd) failed", __FUNCTION__, size);
 		exit(1);
 	}
 	return val;
diff --git a/pam_tacplus.c b/pam_tacplus.c
index 324cd5d4..b1e014f7 100644
--- a/pam_tacplus.c
+++ b/pam_tacplus.c
@@ -47,6 +47,8 @@
 # include "magic.h"
 #endif
 
+char tac_login[64];
+
 /* address of server discovered by pam_sm_authenticate */
 static tacplus_server_t active_server;
 struct addrinfo active_addrinfo;
@@ -66,9 +68,11 @@ static void set_active_server (const tacplus_server_t *tac_svr)
 	active_server.key = active_key;
 }
 
+struct tac_session *sess = NULL;
+
 /* Helper functions */
-int _pam_send_account(int tac_fd, int type, const char *user, char *tty,
-		char *r_addr, char *cmd) {
+int _pam_send_account(struct tac_session *sess, int type,
+		const char *user, char *tty, char *r_addr, char *cmd) {
 
 	char buf[64];
 	struct tac_attrib *attr;
@@ -92,7 +96,7 @@ int _pam_send_account(int tac_fd, int type, const char *user, char *tty,
 		tac_add_attrib(&attr, "cmd", cmd);
 	}
 
-	retval = tac_acct_send(tac_fd, type, user, tty, r_addr, attr);
+	retval = tac_acct_send(sess, type, user, tty, r_addr, attr);
 
 	/* this is no longer needed */
 	tac_free_attrib(&attr);
@@ -100,26 +104,23 @@ int _pam_send_account(int tac_fd, int type, const char *user, char *tty,
 	if (retval < 0) {
 		_pam_log(LOG_WARNING, "%s: send %s accounting failed (task %hu)",
 				__FUNCTION__, tac_acct_flag2str(type), task_id);
-		close(tac_fd);
 		return -1;
 	}
 
 	struct areply re;
-	if (tac_acct_read(tac_fd, &re) != TAC_PLUS_ACCT_STATUS_SUCCESS) {
+	if (tac_acct_read(sess, &re) != TAC_PLUS_ACCT_STATUS_SUCCESS) {
 		_pam_log(LOG_WARNING, "%s: accounting %s failed (task %hu)",
 				__FUNCTION__, tac_acct_flag2str(type), task_id);
 
 		if (re.msg != NULL)
 			free(re.msg);
 
-		close(tac_fd);
 		return -1;
 	}
 
 	if (re.msg != NULL)
 		free(re.msg);
 
-	close(tac_fd);
 	return 0;
 }
 
@@ -133,7 +134,7 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type,
 	char *r_addr = NULL;
 	char *typemsg;
 	int status = PAM_SESSION_ERR;
-	int srv_i, tac_fd;
+	int srv_i;
 
 	typemsg = tac_acct_flag2str(type);
 	ctrl = _pam_parse(argc, argv);
@@ -183,20 +184,27 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type,
 		signal(SIGHUP, SIG_IGN);
 	}
 
+	sess = tac_session_alloc();
+
+	tac_session_set_authen_type(sess, tac_get_authen_type(tac_login));
+
 	status = PAM_SESSION_ERR;
 	for (srv_i = 0; srv_i < tac_srv_no; srv_i++) {
-		tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key,
-				NULL, tac_timeout);
-		if (tac_fd < 0) {
+		retval = tac_connect_single(sess, tac_srv[srv_i].addr, NULL, tac_timeout);
+		if (retval < 0) {
 			_pam_log(LOG_WARNING, "%s: error sending %s (fd)", __FUNCTION__,
 					typemsg);
 			continue;
 		}
+
+		tac_session_set_secret(sess, tac_srv[srv_i].key);
+
 		if (ctrl & PAM_TAC_DEBUG)
 			syslog(LOG_DEBUG, "%s: connected with fd=%d (srv %d)", __FUNCTION__,
-					tac_fd, srv_i);
+					sess->fd, srv_i);
+
+		retval = _pam_send_account(sess, type, user, tty, r_addr, cmd);
 
-		retval = _pam_send_account(tac_fd, type, user, tty, r_addr, cmd);
 		if (retval < 0) {
 			_pam_log(LOG_WARNING, "%s: error sending %s (acct)", __FUNCTION__,
 					typemsg);
@@ -206,7 +214,7 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type,
 				syslog(LOG_DEBUG, "%s: [%s] for [%s] sent", __FUNCTION__,
 						typemsg, user);
 		}
-		close(tac_fd);
+		tac_close(sess);
 
 		if ((status == PAM_SUCCESS) && !(ctrl & PAM_TAC_ACCT)) {
 			/* do not send acct start/stop packets to _all_ servers */
@@ -214,6 +222,8 @@ int _pam_account(pam_handle_t *pamh, int argc, const char **argv, int type,
 		}
 	}
 
+	tac_session_free(sess);
+
 	if (type == TAC_PLUS_ACCT_FLAG_STOP) {
 		signal(SIGALRM, SIG_DFL);
 		signal(SIGCHLD, SIG_DFL);
@@ -238,7 +248,7 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
 	char *tty;
 	char *r_addr;
 	int srv_i;
-	int tac_fd, status, msg, communicating;
+	int status, msg, communicating;
 
 	user = pass = tty = r_addr = NULL;
 
@@ -281,33 +291,39 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
 	if (ctrl & PAM_TAC_DEBUG)
 		syslog(LOG_DEBUG, "%s: rhost [%s] obtained", __FUNCTION__, r_addr);
 
+	sess = tac_session_alloc();
+
+	tac_session_set_authen_type(sess, tac_get_authen_type(tac_login));
+
 	status = PAM_AUTHINFO_UNAVAIL;
 	for (srv_i = 0; srv_i < tac_srv_no; srv_i++) {
 		if (ctrl & PAM_TAC_DEBUG)
 			syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i);
 
-		tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key,
-				NULL, tac_timeout);
-		if (tac_fd < 0) {
+		retval = tac_connect_single(sess, tac_srv[srv_i].addr, NULL, tac_timeout);
+		if (retval < 0) {
 			_pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i);
 			active_server.addr = NULL;
 			continue;
 		}
-		if (tac_authen_send(tac_fd, user, pass, tty, r_addr,
+
+		tac_session_set_secret(sess, tac_srv[srv_i].key);
+
+		if (tac_authen_send(sess, user, pass, tty, r_addr,
 				TAC_PLUS_AUTHEN_LOGIN) < 0) {
-			close(tac_fd);
+			tac_close(sess);
 			_pam_log(LOG_ERR, "error sending auth req to TACACS+ server");
 			active_server.addr = NULL;
 			continue;
 		}
-		communicating = 1;
-		while (communicating) {
+
+		for (communicating = 1; communicating; ) {
 			struct areply re = { .attr = NULL, .msg = NULL, .status = 0,
 					.flags = 0 };
 			struct pam_message conv_msg = { .msg_style = 0, .msg = NULL };
 			struct pam_response *resp = NULL;
 
-			msg = tac_authen_read(tac_fd, &re);
+			msg = tac_authen_read(sess, &re);
 
 			if (NULL != re.msg) {
 				conv_msg.msg = re.msg;
@@ -389,8 +405,7 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
 									__FUNCTION__);
 
 						if (0
-								> tac_cont_send_seq(tac_fd, resp->resp,
-										re.seq_no + 1)) {
+								> tac_cont_send(sess, resp->resp)) {
 							_pam_log(LOG_ERR,
 									"error sending continue req to TACACS+ server");
 							status = PAM_AUTH_ERR;
@@ -431,7 +446,7 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
 				if (ctrl & PAM_TAC_DEBUG)
 					syslog(LOG_DEBUG, "%s: tac_cont_send called", __FUNCTION__);
 
-				if (tac_cont_send(tac_fd, pass) < 0) {
+				if (tac_cont_send(sess, pass) < 0) {
 					_pam_log(LOG_ERR,
 							"error sending continue req to TACACS+ server");
 					communicating = 0;
@@ -497,11 +512,14 @@ int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc,
 			free(re.msg);
 
 		} /* end while(communicating) */
-		close(tac_fd);
+		tac_close(sess);
 
 		if (status == PAM_SUCCESS || status == PAM_AUTH_ERR)
 			break;
 	}
+
+	tac_session_free(sess);
+
 	if (status != PAM_SUCCESS && status != PAM_AUTH_ERR)
 		_pam_log(LOG_ERR, "no more servers to connect");
 
@@ -547,7 +565,6 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 	char *r_addr;
 	struct areply arep;
 	struct tac_attrib *attr = NULL;
-	int tac_fd;
 
 	flags = flags;				/* unused */
 
@@ -607,16 +624,22 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 	if (tac_protocol[0] != '\0')
 		tac_add_attrib(&attr, "protocol", tac_protocol);
 
-	tac_fd = tac_connect_single(active_server.addr, active_server.key, NULL,
-			tac_timeout);
-	if (tac_fd < 0) {
+	sess = tac_session_alloc();
+
+	tac_session_set_authen_type(sess, tac_get_authen_type(tac_login));
+
+	retval = tac_connect_single(sess, active_server.addr, NULL, tac_timeout);
+	if (retval < 0) {
 		_pam_log(LOG_ERR, "TACACS+ server unavailable");
 		if (arep.msg != NULL)
 			free(arep.msg);
+		tac_session_free(sess);
 		return PAM_AUTH_ERR;
 	}
 
-	retval = tac_author_send(tac_fd, user, tty, r_addr, attr);
+	tac_session_set_secret(sess, active_server.key);
+
+	retval = tac_author_send(sess, user, tty, r_addr, attr);
 
 	tac_free_attrib(&attr);
 
@@ -625,7 +648,7 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 		if (arep.msg != NULL)
 			free(arep.msg);
 
-		close(tac_fd);
+		tac_session_free(sess);
 		active_server.addr = NULL;
 		return PAM_AUTH_ERR;
 	}
@@ -633,7 +656,9 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 	if (ctrl & PAM_TAC_DEBUG)
 		syslog(LOG_DEBUG, "%s: sent authorization request", __FUNCTION__);
 
-	tac_author_read(tac_fd, &arep);
+	tac_author_read(sess, &arep);
+
+	tac_session_free(sess);
 
 	if (arep.status != AUTHOR_STATUS_PASS_ADD
 			&& arep.status != AUTHOR_STATUS_PASS_REPL) {
@@ -642,7 +667,6 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 		if (arep.msg != NULL)
 			free(arep.msg);
 
-		close(tac_fd);
 		return PAM_PERM_DENIED;
 	}
 
@@ -698,8 +722,6 @@ int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc,
 	if (arep.msg != NULL)
 		free(arep.msg);
 
-	close(tac_fd);
-
 	return status;
 } /* pam_sm_acct_mgmt */
 
@@ -750,7 +772,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 	char *r_addr;
 	const void *pam_pass = NULL;
 	int srv_i;
-	int tac_fd, status, msg, communicating;
+	int status, msg, communicating;
 
 	user = pass = tty = r_addr = NULL;
 
@@ -795,14 +817,15 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 		goto finish;
 	}
 
+	sess = tac_session_alloc();
+
 	status = PAM_TRY_AGAIN;
 	for (srv_i = 0; srv_i < tac_srv_no; srv_i++) {
 		if (ctrl & PAM_TAC_DEBUG)
 			syslog(LOG_DEBUG, "%s: trying srv %d", __FUNCTION__, srv_i);
 
-		tac_fd = tac_connect_single(tac_srv[srv_i].addr, tac_srv[srv_i].key,
-				NULL, tac_timeout);
-		if (tac_fd < 0) {
+		retval = tac_connect_single(sess, tac_srv[srv_i].addr, NULL, tac_timeout);
+		if (retval < 0) {
 			_pam_log(LOG_ERR, "connection failed srv %d: %m", srv_i);
 			continue;
 		}
@@ -811,14 +834,16 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 				syslog(LOG_DEBUG, "%s: finishing PAM_PRELIM_CHECK with srv %d",
 						__FUNCTION__, srv_i);
 
-			close(tac_fd);
+			tac_close(sess);
 			status = PAM_SUCCESS;
 			goto finish;
 		}
 
-		if (tac_authen_send(tac_fd, user, "", tty, r_addr,
+		tac_session_set_secret(sess, tac_srv[srv_i].key);
+
+		if (tac_authen_send(sess, user, "", tty, r_addr,
 				TAC_PLUS_AUTHEN_CHPASS) < 0) {
-			close(tac_fd);
+			tac_close(sess);
 			_pam_log(LOG_ERR, "error sending auth req to TACACS+ server");
 			continue;
 		}
@@ -829,7 +854,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 			struct pam_message conv_msg = { .msg_style = 0, .msg = NULL };
 			struct pam_response *resp = NULL;
 
-			msg = tac_authen_read(tac_fd, &re);
+			msg = tac_authen_read(sess, &re);
 
 			if (NULL != re.msg) {
 				conv_msg.msg = re.msg;
@@ -911,8 +936,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 									__FUNCTION__);
 
 						if (0
-								> tac_cont_send_seq(tac_fd, resp->resp,
-										re.seq_no + 1)) {
+								> tac_cont_send(sess, resp->resp)) {
 							_pam_log(LOG_ERR,
 									"error sending continue req to TACACS+ server");
 							communicating = 0;
@@ -949,7 +973,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 					syslog(LOG_DEBUG, "%s: calling tac_cont_send",
 							__FUNCTION__);
 
-				if (tac_cont_send(tac_fd, pass) < 0) {
+				if (tac_cont_send(sess, pass) < 0) {
 					_pam_log(LOG_ERR,
 							"error sending continue req to TACACS+ server");
 					communicating = 0;
@@ -1006,6 +1030,7 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 				if (ctrl & PAM_TAC_DEBUG)
 					syslog(LOG_DEBUG, "tacacs status: unknown response 0x%02x",
 							msg);
+				break;
 			}
 
 			if (NULL != resp) {
@@ -1016,12 +1041,14 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
 			free(re.msg);
 
 		} /* end while(communicating) */
-		close(tac_fd);
+		tac_close(sess);
 
 		if (status == PAM_SUCCESS || status == PAM_AUTHTOK_ERR)
 			break;
 	}
 
+	tac_session_free(sess);
+
 	finish: if (status != PAM_SUCCESS && status != PAM_AUTHTOK_ERR)
 		_pam_log(LOG_ERR, "no more servers to connect");
 
diff --git a/support.c b/support.c
index 2406b321..a1867c00 100644
--- a/support.c
+++ b/support.c
@@ -56,7 +56,7 @@ char *_pam_get_user(pam_handle_t *pamh) {
     int retval;
     char *user;
 
-    retval = pam_get_user(pamh, (void *)&user, "Username: ");
+    retval = pam_get_user(pamh, (const char **)&user, "Username: ");
     if (retval != PAM_SUCCESS || user == NULL || *user == '\0') {
         _pam_log(LOG_ERR, "unable to obtain username");
         user = NULL;
@@ -68,7 +68,7 @@ char *_pam_get_terminal(pam_handle_t *pamh) {
     int retval;
     char *tty;
 
-    retval = pam_get_item(pamh, PAM_TTY, (void *)&tty);
+    retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty);
     if (retval != PAM_SUCCESS || tty == NULL || *tty == '\0') {
         tty = ttyname(STDIN_FILENO);
         if(tty == NULL || *tty == '\0')
@@ -81,7 +81,7 @@ char *_pam_get_rhost(pam_handle_t *pamh) {
     int retval;
     char *rhost;
 
-    retval = pam_get_item(pamh, PAM_RHOST, (void *)&rhost);
+    retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
     if (retval != PAM_SUCCESS || rhost == NULL || *rhost == '\0') {
         rhost = "unknown";
     }
diff --git a/support.h b/support.h
index 07c2f9dd..ff90644f 100644
--- a/support.h
+++ b/support.h
@@ -40,6 +40,7 @@ extern char tac_service[64];
 extern char tac_protocol[64];
 extern char tac_prompt[64];
 void tac_copy_addr_info (struct addrinfo *p_dst, const struct addrinfo *p_src);
+extern char tac_login[64];
 
 int _pam_parse (int, const char **);
 unsigned long _resolve_name (char *);
diff --git a/tacc.c b/tacc.c
index 80693238..154be9b6 100644
--- a/tacc.c
+++ b/tacc.c
@@ -79,6 +79,10 @@ typedef unsigned char flag;
 flag quiet = 0;
 char *user = NULL; /* global, because of signal handler */
 
+char tac_login[64];
+
+struct tac_session *sess = NULL;
+
 /* command line options */
 static struct option long_options[] =
 		{
@@ -117,7 +121,6 @@ int main(int argc, char **argv) {
 	struct addrinfo *tac_server;
 	char *tac_server_name = NULL;
 	char *tac_secret = NULL;
-	int tac_fd;
 	short int task_id = 0;
 	char buf[40];
 	int ret;
@@ -172,7 +175,6 @@ int main(int argc, char **argv) {
 				remote_addr = optarg;
 				break;
 			case 'L':
-				// tac_login is a global variable initialized in libtac
 				bzero(tac_login, sizeof(tac_login));
 				strncpy(tac_login, optarg, sizeof(tac_login) - 1);
 				break;
@@ -272,28 +274,41 @@ int main(int argc, char **argv) {
 		exit(EXIT_ERR);
 	}
 
+	bzero(&arep, sizeof(arep));
+
 	/* open syslog before any TACACS+ calls */
 	openlog("tacc", LOG_CONS | LOG_PID, LOG_AUTHPRIV);
 
+	sess = tac_session_alloc();
+
+	ret = tac_connect_single(sess, tac_server, NULL, 60);
+	if (ret < 0) {
+		if (!quiet)
+			printf("Error connecting to TACACS+ server: %m\n");
+		exit(EXIT_ERR);
+	}
+
+	tac_session_set_secret(sess, tac_secret);
+	tac_session_set_authen_type(sess, tac_get_authen_type(tac_login));
+
+	tac_session_set_multiplex(sess, true);
+
 	if (do_authen)
 		authenticate(tac_server, tac_secret, user, pass, tty, remote_addr);
 
+	/* we no longer need the password in our address space */
+	bzero(pass, strlen(pass));
+	pass = NULL;
+
 	if (do_author) {
 		/* authorize user */
 		struct tac_attrib *attr = NULL;
 		tac_add_attrib(&attr, "service", service);
 		tac_add_attrib(&attr, "protocol", protocol);
 
-		tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
-		if (tac_fd < 0) {
-			if (!quiet)
-				printf("Error connecting to TACACS+ server: %m\n");
-			exit(EXIT_ERR);
-		}
-
-		tac_author_send(tac_fd, user, tty, remote_addr, attr);
+		tac_author_send(sess, user, tty, remote_addr, attr);
 
-		tac_author_read(tac_fd, &arep);
+		tac_author_read(sess, &arep);
 		if (arep.status != AUTHOR_STATUS_PASS_ADD
 				&& arep.status != AUTHOR_STATUS_PASS_REPL) {
 			if (!quiet)
@@ -307,10 +322,6 @@ int main(int argc, char **argv) {
 		tac_free_attrib(&attr);
 	}
 
-	/* we no longer need the password in our address space */
-	bzero(pass, strlen(pass));
-	pass = NULL;
-
 	if (do_account) {
 		/* start accounting */
 		struct tac_attrib *attr = NULL;
@@ -322,17 +333,10 @@ int main(int argc, char **argv) {
 		tac_add_attrib(&attr, "service", service);
 		tac_add_attrib(&attr, "protocol", protocol);
 
-		tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
-		if (tac_fd < 0) {
-			if (!quiet)
-				printf("Error connecting to TACACS+ server: %m\n");
-			exit(EXIT_ERR);
-		}
-
-		tac_acct_send(tac_fd, TAC_PLUS_ACCT_FLAG_START, user, tty, remote_addr,
+		tac_acct_send(sess, TAC_PLUS_ACCT_FLAG_START, user, tty, remote_addr,
 				attr);
 
-		ret = tac_acct_read(tac_fd, &arep);
+		ret = tac_acct_read(sess, &arep);
 		if (ret == 0) {
 			if (!quiet)
 				printf("Accounting: START failed: %s\n", arep.msg);
@@ -340,7 +344,7 @@ int main(int argc, char **argv) {
 		} else if (!login_mode && !quiet)
 			printf("Accounting: START OK\n");
 
-		close(tac_fd);
+		tac_close(sess);
 
 		tac_free_attrib(&attr);
 
@@ -378,6 +382,8 @@ int main(int argc, char **argv) {
 		if(pid == 0) {
 			/* child */
 
+			tac_session_free(sess);
+
 			execl(DEFAULT_COMMAND, DEFAULT_COMMAND, ARGS, NULL);
 			syslog(LOG_ERR, "execl() failed: %m");
 			_exit(EXIT_FAIL);
@@ -406,16 +412,9 @@ int main(int argc, char **argv) {
 		sprintf(buf, "%hu", task_id);
 		tac_add_attrib(&attr, "task_id", buf);
 
-		tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
-		if (tac_fd < 0) {
-			if (!quiet)
-				printf("Error connecting to TACACS+ server: %m\n");
-			exit(EXIT_ERR);
-		}
-
-		tac_acct_send(tac_fd, TAC_PLUS_ACCT_FLAG_STOP, user, tty, remote_addr,
+		tac_acct_send(sess, TAC_PLUS_ACCT_FLAG_STOP, user, tty, remote_addr,
 				attr);
-		ret = tac_acct_read(tac_fd, &arep);
+		ret = tac_acct_read(sess, &arep);
 		if (ret == 0) {
 			if (!quiet)
 				printf("Accounting: STOP failed: %s", arep.msg);
@@ -423,8 +422,6 @@ int main(int argc, char **argv) {
 		} else if (!login_mode && !quiet)
 			printf("Accounting: STOP OK\n");
 
-		close(tac_fd);
-
 		tac_free_attrib(&attr);
 	}
 
@@ -434,6 +431,8 @@ int main(int argc, char **argv) {
 		logwtmp(tty, "", "");
 #endif
 
+	tac_session_free(sess);
+
 	exit(EXIT_OK);
 }
 
@@ -446,37 +445,41 @@ void sighandler(int sig) {
 void authenticate(const struct addrinfo *tac_server, const char *tac_secret,
 		const char *user, const char *pass, const char *tty,
 		const char *remote_addr) {
-	int tac_fd;
 	int ret;
 	struct areply arep;
 
-	tac_fd = tac_connect_single(tac_server, tac_secret, NULL, 60);
-	if (tac_fd < 0) {
+	tac_session_new_session_id(sess);
+	tac_session_reset_seq(sess);
+
+	ret = tac_connect_single(sess, tac_server, NULL, 60);
+	if (ret < 0) {
 		if (!quiet)
 			printf("Error connecting to TACACS+ server: %m\n");
 		exit(EXIT_ERR);
 	}
 
+	tac_session_set_secret(sess, tac_secret);
+
 	/* start authentication */
 
-	if (tac_authen_send(tac_fd, user, pass, tty, remote_addr,
+	if (tac_authen_send(sess, user, pass, tty, remote_addr,
 			TAC_PLUS_AUTHEN_LOGIN) < 0) {
 		if (!quiet)
 			printf("Error sending query to TACACS+ server\n");
 		exit(EXIT_ERR);
 	}
 
-	ret = tac_authen_read(tac_fd, &arep);
+	ret = tac_authen_read(sess, &arep);
 
 	if (ret == TAC_PLUS_AUTHEN_STATUS_GETPASS) {
 
-		if (tac_cont_send(tac_fd, pass) < 0) {
+		if (tac_cont_send(sess, pass) < 0) {
 			if (!quiet)
 				printf("Error sending query to TACACS+ server\n");
 			exit(EXIT_ERR);
 		}
 
-		ret = tac_authen_read(tac_fd, &arep);
+		ret = tac_authen_read(sess, &arep);
 	}
 
 	if (ret != TAC_PLUS_AUTHEN_STATUS_PASS) {
@@ -489,8 +492,6 @@ void authenticate(const struct addrinfo *tac_server, const char *tac_secret,
 	if (!quiet)
 		printf("Authentication OK\n");
 	syslog(LOG_INFO, "authentication OK for %s", user);
-
-	close(tac_fd);
 }
 
 void showusage(char *progname) {