/* Returns 0 on success */ static int ptload(const char *identifier, struct auth_state **state) { struct auth_state *fetched = NULL; size_t id_len; const char *data = NULL; size_t dsize; const char *fname = NULL; char *tofree = NULL; struct db *ptdb; int s; struct sockaddr_un srvaddr; int r, rc=0; static char response[1024]; struct iovec iov[10]; int niov, n; unsigned int start; const char *config_dir = libcyrus_config_getstring(CYRUSOPT_CONFIG_DIR); /* xxx this sucks, but it seems to be the only way to satisfy the linker */ if(the_ptscache_db == NULL) { the_ptscache_db = libcyrus_config_getstring(CYRUSOPT_PTSCACHE_DB); } if(!state || *state) { fatal("bad state pointer passed to ptload()", EC_TEMPFAIL); } fname = libcyrus_config_getstring(CYRUSOPT_PTSCACHE_DB_PATH); if (!fname) { tofree = strconcat(config_dir, PTS_DBFIL, (char *)NULL); fname = tofree; } r = cyrusdb_open(the_ptscache_db, fname, CYRUSDB_CREATE, &ptdb); if (r != 0) { syslog(LOG_ERR, "DBERROR: opening %s: %s", fname, cyrusdb_strerror(ret)); free(tofree); *state = NULL; return -1; } free(tofree); tofree = NULL; id_len = strlen(identifier); if(id_len > PTS_DB_KEYSIZE) { syslog(LOG_ERR, "identifier too long in auth_newstate"); *state = NULL; return -1; } /* fetch the current record for the user */ r = cyrusdb_fetch(ptdb, identifier, id_len, &data, &dsize, NULL); if (r && r != CYRUSDB_NOTFOUND) { syslog(LOG_ERR, "auth_newstate: error fetching record: %s", cyrusdb_strerror(r)); rc = -1; goto done; } /* if it's expired (or nonexistent), * ask the ptloader to reload it and reread it */ fetched = (struct auth_state *) data; if(fetched) { time_t now = time(NULL); int timeout = libcyrus_config_getint(CYRUSOPT_PTS_CACHE_TIMEOUT); syslog(LOG_DEBUG, "ptload(): fetched cache record (%s)" \ "(mark %ld, current %ld, limit %ld)", identifier, fetched->mark, now, now - timeout); if (fetched->mark > (now - timeout)) { /* not expired; let's return it */ goto done; } } syslog(LOG_DEBUG, "ptload(): pinging ptloader"); s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == -1) { syslog(LOG_ERR, "ptload(): unable to create socket for ptloader: %m"); rc = -1; goto done; } fname = libcyrus_config_getstring(CYRUSOPT_PTLOADER_SOCK); if (!fname) { tofree = strconcat(config_dir, PTS_DBSOCKET, (char *)NULL); fname = tofree; } if (strlen(fname) >= sizeof(srvaddr.sun_path)) { syslog(LOG_ERR, "ptload(): socket filename %s too long for " SIZE_T_FMT "-byte buffer", fname, sizeof(srvaddr.sun_path)); rc = -1; goto done; } memset((char *)&srvaddr, 0, sizeof(srvaddr)); srvaddr.sun_family = AF_UNIX; strlcpy(srvaddr.sun_path, fname, sizeof(srvaddr.sun_path)); r = nb_connect(s, (struct sockaddr *)&srvaddr, sizeof(srvaddr), PT_TIMEOUT_SEC); free(tofree); if (r == -1) { syslog(LOG_ERR, "ptload(): can't connect to ptloader server: %m"); close(s); rc = -1; goto done; } syslog(LOG_DEBUG, "ptload(): connected"); niov = 0; WRITEV_ADD_TO_IOVEC(iov, niov, (char *) &id_len, sizeof(id_len)); WRITEV_ADD_TO_IOVEC(iov, niov, (char *) identifier, id_len); if (timeout_select(s, TS_WRITE, PT_TIMEOUT_SEC) < 0) { syslog(LOG_ERR, "timeoutselect: writing to ptloader %m"); rc = -1; goto done; } retry_writev(s, iov, niov); syslog(LOG_DEBUG, "ptload sent data"); start = 0; while (start < sizeof(response) - 1) { if (timeout_select(s, TS_READ, PT_TIMEOUT_SEC) < 0) { syslog(LOG_ERR, "timeout_select: reading from ptloader: %m"); rc = -1; goto done; } n = read(s, response+start, sizeof(response) - 1 - start); if (n < 1) break; start += n; } response[sizeof(response)-1] = '\0'; close(s); syslog(LOG_DEBUG, "ptload read data back"); if (start <= 1 || strncmp(response, "OK", 2)) { if(start > 1) { syslog(LOG_ERR, "ptload(): bad response from ptloader server: %s", response); } else { syslog(LOG_ERR, "ptload(): empty response from ptloader server"); } rc = -1; goto done; } /* fetch the current record for the user */ r = cyrusdb_fetch(ptdb, identifier, id_len, &data, &dsize, NULL); if (r != 0 || !data) { syslog(LOG_ERR, "ptload(): error fetching record: %s" "(did ptloader add the record?)", cyrusdb_strerror(r)); data = NULL; rc = -1; goto done; } done: /* ok, we got real data, let's use it */ if (data != NULL) { fetched = (struct auth_state *) data; } if (fetched == NULL) { *state = NULL; syslog(LOG_DEBUG, "No data available at all from ptload()"); } else { /* copy it into our structure */ *state = (struct auth_state *)xmalloc(dsize); memcpy(*state, fetched, dsize); syslog(LOG_DEBUG, "ptload returning data"); } /* close and unlock the database */ (cyrusdb_close)(ptdb); return rc; }
/** Connect to Tomcat * @param addr address to connect to * @param keepalive should we set SO_KEEPALIVE (if !=0) * @param timeout connect timeout in seconds * (<=0: no timeout=blocking) * @param sock_buf size of send and recv buffer * (<=0: use default) * @param l logger * @return JK_INVALID_SOCKET: some kind of error occured * created socket: success * @remark Cares about errno */ jk_sock_t jk_open_socket(jk_sockaddr_t *addr, jk_sockaddr_t *source, int keepalive, int timeout, int connect_timeout, int sock_buf, jk_logger_t *l) { char buf[DUMP_SINFO_BUF_SZ]; jk_sock_t sd; int set = 1; int ret = 0; int flags = 0; #ifdef SO_LINGER struct linger li; #endif JK_TRACE_ENTER(l); errno = 0; #if defined(SOCK_CLOEXEC) && defined(USE_SOCK_CLOEXEC) flags |= SOCK_CLOEXEC; #endif sd = socket(addr->family, SOCK_STREAM | flags, 0); if (!IS_VALID_SOCKET(sd)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "socket() failed (errno=%d)", errno); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #if defined(FD_CLOEXEC) && !defined(USE_SOCK_CLOEXEC) if ((flags = fcntl(sd, F_GETFD)) == -1) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "fcntl() failed (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } flags |= FD_CLOEXEC; if (fcntl(sd, F_SETFD, flags) == -1) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "fcntl() failed (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif /* Disable Nagle algorithm */ if (setsockopt(sd, IPPROTO_TCP, TCP_NODELAY, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting TCP_NODELAY (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket TCP_NODELAY set to On"); if (keepalive) { #if defined(WIN32) && !defined(NETWARE) DWORD dw; struct tcp_keepalive ka = { 0 }, ks = { 0 }; if (timeout) ka.keepalivetime = timeout * 10000; else ka.keepalivetime = 60 * 10000; /* 10 minutes */ ka.keepaliveinterval = 1000; ka.onoff = 1; if (WSAIoctl(sd, SIO_KEEPALIVE_VALS, &ka, sizeof(ka), &ks, sizeof(ks), &dw, NULL, NULL)) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SIO_KEEPALIVE_VALS (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_KEEPALIVE set to %d seconds", ka.keepalivetime / 1000); #else set = 1; if (setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_KEEPALIVE (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_KEEPALIVE set to On"); #endif } if (sock_buf > 0) { set = sock_buf; /* Set socket send buffer size */ if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_SNDBUF (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } set = sock_buf; /* Set socket receive buffer size */ if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, (SET_TYPE)&set, sizeof(set))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_RCVBUF (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket SO_SNDBUF and SO_RCVBUF set to %d", sock_buf); } if (timeout > 0) { #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) int tmout = timeout * 1000; setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tmout, sizeof(int)); setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (const char *) &tmout, sizeof(int)); JK_GET_SOCKET_ERRNO(); #elif defined(SO_RCVTIMEO) && defined(USE_SO_RCVTIMEO) && defined(SO_SNDTIMEO) && defined(USE_SO_SNDTIMEO) struct timeval tv; tv.tv_sec = timeout; tv.tv_usec = 0; setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (const void *) &tv, sizeof(tv)); setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO, (const void *) &tv, sizeof(tv)); #endif if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "timeout %d set for socket=%d", timeout, sd); } #ifdef SO_NOSIGPIPE /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when * sending data to a dead peer. Possibly also existing and in use on other BSD * systems? */ set = 1; if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (const char *)&set, sizeof(int))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_NOSIGPIPE (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif #ifdef SO_LINGER /* Make hard closesocket by disabling lingering */ li.l_linger = li.l_onoff = 0; if (setsockopt(sd, SOL_SOCKET, SO_LINGER, (SET_TYPE)&li, sizeof(li))) { JK_GET_SOCKET_ERRNO(); jk_log(l, JK_LOG_ERROR, "failed setting SO_LINGER (errno=%d)", errno); jk_close_socket(sd, l); JK_TRACE_EXIT(l); return JK_INVALID_SOCKET; } #endif /* Tries to connect to Tomcat (continues trying while error is EINTR) */ if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "trying to connect socket %d to %s", sd, jk_dump_hinfo(addr, buf, sizeof(buf))); /* Need more infos for BSD 4.4 and Unix 98 defines, for now only iSeries when Unix98 is required at compile time */ #if (_XOPEN_SOURCE >= 520) && defined(AS400) ((struct sockaddr *)addr)->sa.sin.sa_len = sizeof(struct sockaddr_in); #endif ret = nb_connect(sd, addr, source, connect_timeout, l); #if defined(WIN32) || (defined(NETWARE) && defined(__NOVELL_LIBC__)) if (JK_IS_SOCKET_ERROR(ret)) { JK_GET_SOCKET_ERRNO(); } #endif /* WIN32 */ /* Check if we are connected */ if (ret) { jk_log(l, JK_LOG_INFO, "connect to %s failed (errno=%d)", jk_dump_hinfo(addr, buf, sizeof(buf)), errno); jk_close_socket(sd, l); sd = JK_INVALID_SOCKET; } else { if (JK_IS_DEBUG_LEVEL(l)) jk_log(l, JK_LOG_DEBUG, "socket %d [%s] connected", sd, jk_dump_sinfo(sd, buf, sizeof(buf))); } JK_TRACE_EXIT(l); return sd; }