diff options
Diffstat (limited to 'libtac/lib/connect.c')
-rw-r--r-- | libtac/lib/connect.c | 267 |
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 */ |