summaryrefslogtreecommitdiff
path: root/libtac/lib/connect.c
diff options
context:
space:
mode:
authorJeroen <jeroen@nijhofnet.nl>2011-08-19 22:05:10 +0200
committerJeroen <jeroen@nijhofnet.nl>2011-08-19 22:05:10 +0200
commitd1134977b9317c6161ae12608684ea857915a63c (patch)
tree90ed16ae4c1c0781e95f4f3ffe022c7fb6693342 /libtac/lib/connect.c
parenteb6cf3c69186698f0d5fcc5a89dd81a823794937 (diff)
downloadpam_tacplus-d1134977b9317c6161ae12608684ea857915a63c.tar.gz
pam_tacplus-d1134977b9317c6161ae12608684ea857915a63c.zip
Major contribution by Darren Besler
Diffstat (limited to 'libtac/lib/connect.c')
-rw-r--r--libtac/lib/connect.c267
1 files changed, 151 insertions, 116 deletions
diff --git a/libtac/lib/connect.c b/libtac/lib/connect.c
index c65edf8..bbbb270 100644
--- a/libtac/lib/connect.c
+++ b/libtac/lib/connect.c
@@ -1,6 +1,6 @@
/* connect.c - Open connection to server.
*
- * Copyright (C) 2010, Pawel Krawczyk <kravietz@ceti.pl> and
+ * Copyright (C) 2010, Pawel Krawczyk <pawel.krawczyk@hush.com> and
* Jeroen Nijhof <jeroen@nijhofnet.nl>
*
* This program is free software; you can redistribute it and/or modify
@@ -26,7 +26,7 @@
#include <errno.h>
#ifdef _AIX
- #include <sys/socket.h>
+ #include <sys/socket.h>
#endif
#include "tacplus.h"
@@ -38,126 +38,161 @@ int tac_timeout = 5;
/* Returns file descriptor of open connection
to the first available server from list passed
in server table.
-*/
+
+ * return value:
+ * >= 0 : valid fd
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ */
int tac_connect(struct addrinfo **server, char **key, int servers) {
- int tries = 0;
- int fd, flags, retval;
- fd_set readfds, writefds;
- struct timeval tv;
- socklen_t len;
- struct sockaddr_storage addr;
-
- if(!servers) {
- syslog(LOG_ERR, "%s: no TACACS+ servers defined", __FUNCTION__);
- return(-1);
- }
-
- while(tries < servers) {
- if((fd=socket(server[tries]->ai_family, server[tries]->ai_socktype, server[tries]->ai_protocol)) == -1) {
- syslog(LOG_WARNING,
- "%s: socket creation error", __FUNCTION__);
- tries++;
- continue;
- }
-
- /* put socket in non blocking mode for timeout support */
- flags = fcntl(fd, F_GETFL, 0);
- if(fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
- syslog(LOG_WARNING, "%s: cannot set socket non blocking",
- __FUNCTION__);
- tries++;
- continue;
- }
-
- retval = connect(fd, server[tries]->ai_addr, server[tries]->ai_addrlen);
- if((retval == -1) && (errno != EINPROGRESS)) {
- syslog(LOG_WARNING,
- "%s: connection to %s failed: %m", __FUNCTION__,
- tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
- if(fcntl(fd, F_SETFL, flags)) {
- syslog(LOG_WARNING, "%s: cannot restore socket flags",
- __FUNCTION__);
- }
- tries++;
- continue;
- }
-
- /* set fds for select */
- FD_ZERO(&readfds);
- FD_SET(fd, &readfds);
- writefds = readfds;
-
- /* set timeout seconds */
- tv.tv_sec = tac_timeout;
- tv.tv_usec = 0;
-
- /* check if socket is ready for read and write */
- if(select(fd+1, &readfds, &writefds, NULL, &tv) < 1) {
- syslog(LOG_WARNING,
- "%s: connection failed with %s : %m", __FUNCTION__,
- tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
- if(fcntl(fd, F_SETFL, flags)) {
- syslog(LOG_WARNING, "%s: cannot restore socket flags",
- __FUNCTION__);
- }
- tries++;
- continue;
- } else {
- /* check with getpeername if we have a valid connection */
- len = sizeof addr;
- if(getpeername(fd, (struct sockaddr*)&addr, &len) == -1) {
- syslog(LOG_WARNING,
- "%s: connection failed with %s : %m", __FUNCTION__,
- tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen));
- if(fcntl(fd, F_SETFL, flags)) {
- syslog(LOG_WARNING, "%s: cannot restore socket flags",
- __FUNCTION__);
- }
- tries++;
- continue;
- }
- }
-
- /* connected ok */
- if(fcntl(fd, F_SETFL, flags)) {
- syslog(LOG_WARNING, "%s: cannot restore socket flags",
- __FUNCTION__);
- }
- TACDEBUG((LOG_DEBUG, "%s: connected to %s", __FUNCTION__, \
- tac_ntop(server[tries]->ai_addr, server[tries]->ai_addrlen)));
-
- /* set current tac_secret */
- tac_secret = key[tries];
- return(fd);
- }
-
- /* all attempts failed */
- return(-1);
+ int tries;
+ int fd=-1;
+
+ 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])) >= 0 ) {
+ /* tac_secret was set in tac_connect_single on success */
+ break;
+ }
+ }
+ }
+
+ /* all attempts failed if fd is still < 0 */
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d",__FUNCTION__, fd))
+ return(fd);
} /* tac_connect */
+/* return value:
+ * >= 0 : valid fd
+ * < 0 : error status code, see LIBTAC_STATUS_...
+ */
int tac_connect_single(struct addrinfo *server, char *key) {
- struct addrinfo *tmpaddr[1];
- tmpaddr[0] = server;
- char *tmpkey[1];
- tmpkey[0] = key;
- return(tac_connect(tmpaddr, tmpkey, 1));
+ int retval = LIBTAC_STATUS_CONN_ERR; /* default retval */
+ int fd = -1;
+ int flags, rc;
+ fd_set readfds, writefds;
+ struct timeval tv;
+ socklen_t len;
+ struct sockaddr_storage addr;
+ char *ip = NULL;
+
+ if(server == NULL) {
+ TACSYSLOG((LOG_ERR, "%s: no TACACS+ server defined", __FUNCTION__))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* format server address into a string for use in messages */
+ ip = tac_ntop(server->ai_addr, 0);
+
+ if((fd=socket(server->ai_family, server->ai_socktype, server->ai_protocol)) < 0) {
+ TACSYSLOG((LOG_ERR,"%s: socket creation error", __FUNCTION__))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* get flags for restoration later */
+ flags = fcntl(fd, F_GETFL, 0);
+
+ /* put socket in non blocking mode for timeout support */
+ if( fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ) {
+ TACSYSLOG((LOG_ERR, "%s: cannot set socket non blocking",\
+ __FUNCTION__))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ rc = connect(fd, server->ai_addr, server->ai_addrlen);
+ if((rc == -1) && (errno != EINPROGRESS)) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: connection to %s failed: %m", __FUNCTION__, ip))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* set fds for select */
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(fd, &readfds);
+ FD_SET(fd, &writefds);
+
+ /* set timeout seconds */
+ tv.tv_sec = tac_timeout;
+ tv.tv_usec = 0;
+
+ /* check if socket is ready for read and write */
+ rc = select(fd+1, &readfds, &writefds, NULL, &tv);
+
+ /* timeout */
+ if ( rc == 0 ) {
+ return LIBTAC_STATUS_CONN_TIMEOUT;
+ }
+
+ /* some other error or interrupt before timeout */
+ if ( rc < 0 ) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: connection failed with %s: %m", __FUNCTION__, ip))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* check with getpeername if we have a valid connection */
+ len = sizeof addr;
+ if(getpeername(fd, (struct sockaddr*)&addr, &len) == -1) {
+ TACSYSLOG((LOG_ERR,\
+ "%s: connection failed with %s: %m", __FUNCTION__, ip))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* restore flags on socket - flags was set only when fd >= 0 */
+ if(fcntl(fd, F_SETFL, flags) == -1) {
+ TACSYSLOG((LOG_ERR, "%s: cannot restore socket flags: %m",\
+ __FUNCTION__))
+ return LIBTAC_STATUS_CONN_ERR;
+ }
+
+ /* connected ok */
+ TACDEBUG((LOG_DEBUG, "%s: connected to %s", __FUNCTION__, ip))
+ retval = fd;
+
+ /* set current tac_secret */
+ tac_secret = key;
+
+ free(ip);
+
+ /* if valid fd, but error experienced after open, close fd */
+ if ( fd >= 0 && retval < 0 ) {
+ close(fd);
+ }
+
+ TACDEBUG((LOG_DEBUG, "%s: exit status=%d (fd=%d)",\
+ __FUNCTION__, retval < 0 ? retval:0, fd))
+ return(retval);
} /* tac_connect_single */
-char *tac_ntop(const struct sockaddr *sa, size_t ai_addrlen) {
- char *str = (char *) xcalloc(1, ai_addrlen);
- switch(sa->sa_family) {
- case AF_INET:
- inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
- str, ai_addrlen);
- break;
- case AF_INET6:
- inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
- str, ai_addrlen);
- break;
- default:
- strncpy(str, "Unknown AF", ai_addrlen);
- }
- return str;
+/* return value:
+ * ptr to char* with format IP address
+ * must be freed by caller
+ */
+char *tac_ntop(const struct sockaddr *sa, size_t unused) {
+ char portstr[7];
+ char *str = (char *) xcalloc(1, INET6_ADDRSTRLEN+sizeof(portstr));
+
+ switch(sa->sa_family) {
+ case AF_INET:
+ inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
+ str, INET_ADDRSTRLEN);
+ snprintf(portstr, sizeof(portstr), ":%hu",
+ htons(((struct sockaddr_in *)sa)->sin_port));
+ strncat(str, portstr, sizeof(portstr));
+ break;
+ case AF_INET6:
+ inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
+ str, INET6_ADDRSTRLEN);
+ snprintf(portstr, sizeof(portstr), ":%hu",
+ htons(((struct sockaddr_in6 *)sa)->sin6_port));
+ strncat(str, portstr, sizeof(portstr));
+ break;
+ default:
+ strncpy(str, "Unknown AF", INET6_ADDRSTRLEN);
+ }
+ return str;
} /* tac_ntop */