static int __msgread (int sock, void *data, size_t cnt) { struct iovec iov; struct msghdr msg; int len; iov.iov_base = data; iov.iov_len = cnt; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; #ifdef SCM_CREDENTIALS msg.msg_control = (caddr_t) &cm; msg.msg_controllen = sizeof (struct cmessage); #endif msg.msg_flags = 0; #ifdef SO_PASSCRED { int on = 1; if (__setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on))) return -1; } #endif restart: len = __recvmsg (sock, &msg, 0); if (len >= 0) { if (msg.msg_flags & MSG_CTRUNC || len == 0) return 0; else return len; } if (errno == EINTR) goto restart; return -1; }
/* Try to get a file descriptor for the shared meory segment containing the database. */ static struct mapped_database * get_mapping (request_type type, const char *key, struct mapped_database **mappedp) { struct mapped_database *result = NO_MAPPING; #ifdef SCM_RIGHTS const size_t keylen = strlen (key) + 1; char resdata[keylen]; int saved_errno = errno; int mapfd = -1; /* Send the request. */ struct iovec iov[2]; request_header req; int sock = open_socket (); if (sock < 0) goto out; req.version = NSCD_VERSION; req.type = type; req.key_len = keylen; iov[0].iov_base = &req; iov[0].iov_len = sizeof (req); iov[1].iov_base = (void *) key; iov[1].iov_len = keylen; if (__builtin_expect (TEMP_FAILURE_RETRY (__writev (sock, iov, 2)) != iov[0].iov_len + iov[1].iov_len, 0)) /* We cannot even write the request. */ goto out_close2; /* Room for the data sent along with the file descriptor. We expect the key name back. */ iov[0].iov_base = resdata; iov[0].iov_len = keylen; union { struct cmsghdr hdr; char bytes[CMSG_SPACE (sizeof (int))]; } buf; struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1, .msg_control = buf.bytes, .msg_controllen = sizeof (buf) }; struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN (sizeof (int)); /* This access is well-aligned since BUF is correctly aligned for an int and CMSG_DATA preserves this alignment. */ *(int *) CMSG_DATA (cmsg) = -1; msg.msg_controllen = cmsg->cmsg_len; if (wait_on_socket (sock) <= 0) goto out_close2; # ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 # endif if (__builtin_expect (TEMP_FAILURE_RETRY (__recvmsg (sock, &msg, MSG_NOSIGNAL)) != keylen, 0)) goto out_close2; mapfd = *(int *) CMSG_DATA (cmsg); if (__builtin_expect (CMSG_FIRSTHDR (&msg)->cmsg_len != CMSG_LEN (sizeof (int)), 0)) goto out_close; struct stat64 st; if (__builtin_expect (strcmp (resdata, key) != 0, 0) || __builtin_expect (fstat64 (mapfd, &st) != 0, 0) || __builtin_expect (st.st_size < sizeof (struct database_pers_head), 0)) goto out_close; struct database_pers_head head; if (__builtin_expect (TEMP_FAILURE_RETRY (__pread (mapfd, &head, sizeof (head), 0)) != sizeof (head), 0)) goto out_close; if (__builtin_expect (head.version != DB_VERSION, 0) || __builtin_expect (head.header_size != sizeof (head), 0) /* This really should not happen but who knows, maybe the update thread got stuck. */ || __builtin_expect (! head.nscd_certainly_running && head.timestamp + MAPPING_TIMEOUT < time (NULL), 0)) goto out_close; size_t size = (sizeof (head) + roundup (head.module * sizeof (ref_t), ALIGN) + head.data_size); if (__builtin_expect (st.st_size < size, 0)) goto out_close; /* The file is large enough, map it now. */ void *mapping = __mmap (NULL, size, PROT_READ, MAP_SHARED, mapfd, 0); if (__builtin_expect (mapping != MAP_FAILED, 1)) { /* Allocate a record for the mapping. */ struct mapped_database *newp = malloc (sizeof (*newp)); if (newp == NULL) { /* Ugh, after all we went through the memory allocation failed. */ __munmap (mapping, size); goto out_close; } newp->head = mapping; newp->data = ((char *) mapping + head.header_size + roundup (head.module * sizeof (ref_t), ALIGN)); newp->mapsize = size; /* Set counter to 1 to show it is usable. */ newp->counter = 1; result = newp; } out_close: __close (mapfd); out_close2: __close (sock); out: __set_errno (saved_errno); #endif /* SCM_RIGHTS */ struct mapped_database *oldval = *mappedp; *mappedp = result; if (oldval != NULL && atomic_decrement_val (&oldval->counter) == 0) __nscd_unmap (oldval); return result; } struct mapped_database * __nscd_get_map_ref (request_type type, const char *name, struct locked_map_ptr *mapptr, int *gc_cyclep) { struct mapped_database *cur = mapptr->mapped; if (cur == NO_MAPPING) return cur; int cnt = 0; while (atomic_compare_and_exchange_val_acq (&mapptr->lock, 1, 0) != 0) { // XXX Best number of rounds? if (++cnt > 5) return NO_MAPPING; atomic_delay (); } cur = mapptr->mapped; if (__builtin_expect (cur != NO_MAPPING, 1)) { /* If not mapped or timestamp not updated, request new map. */ if (cur == NULL || (cur->head->nscd_certainly_running == 0 && cur->head->timestamp + MAPPING_TIMEOUT < time (NULL))) cur = get_mapping (type, name, &mapptr->mapped); if (__builtin_expect (cur != NO_MAPPING, 1)) { if (__builtin_expect (((*gc_cyclep = cur->head->gc_cycle) & 1) != 0, 0)) cur = NO_MAPPING; else atomic_increment (&cur->counter); } } mapptr->lock = 0; return cur; } const struct datahead * __nscd_cache_search (request_type type, const char *key, size_t keylen, const struct mapped_database *mapped) { unsigned long int hash = __nis_hash (key, keylen) % mapped->head->module; ref_t work = mapped->head->array[hash]; while (work != ENDREF) { struct hashentry *here = (struct hashentry *) (mapped->data + work); if (type == here->type && keylen == here->len && memcmp (key, mapped->data + here->key, keylen) == 0) { /* We found the entry. Increment the appropriate counter. */ const struct datahead *dh = (struct datahead *) (mapped->data + here->packet); /* See whether we must ignore the entry or whether something is wrong because garbage collection is in progress. */ if (dh->usable && ((char *) dh + dh->allocsize <= (char *) mapped->head + mapped->mapsize)) return dh; } work = here->next; } return NULL; }
int __netlink_request (struct netlink_handle *h, int type) { struct netlink_res *nlm_next; struct netlink_res **new_nlm_list; static volatile size_t buf_size = 4096; char *buf; struct sockaddr_nl nladdr; struct nlmsghdr *nlmh; ssize_t read_len; bool done = false; bool use_malloc = false; if (__netlink_sendreq (h, type) < 0) return -1; size_t this_buf_size = buf_size; if (__libc_use_alloca (this_buf_size)) buf = alloca (this_buf_size); else { buf = malloc (this_buf_size); if (buf != NULL) use_malloc = true; else goto out_fail; } struct iovec iov = { buf, this_buf_size }; if (h->nlm_list != NULL) new_nlm_list = &h->end_ptr->next; else new_nlm_list = &h->nlm_list; while (! done) { struct msghdr msg = { (void *) &nladdr, sizeof (nladdr), &iov, 1, NULL, 0, 0 }; read_len = TEMP_FAILURE_RETRY (__recvmsg (h->fd, &msg, 0)); if (read_len < 0) goto out_fail; if (nladdr.nl_pid != 0) continue; if (__builtin_expect (msg.msg_flags & MSG_TRUNC, 0)) { if (this_buf_size >= SIZE_MAX / 2) goto out_fail; nlm_next = *new_nlm_list; while (nlm_next != NULL) { struct netlink_res *tmpptr; tmpptr = nlm_next->next; free (nlm_next); nlm_next = tmpptr; } *new_nlm_list = NULL; if (__libc_use_alloca (2 * this_buf_size)) buf = extend_alloca (buf, this_buf_size, 2 * this_buf_size); else { this_buf_size *= 2; char *new_buf = realloc (use_malloc ? buf : NULL, this_buf_size); if (new_buf == NULL) goto out_fail; new_buf = buf; use_malloc = true; } buf_size = this_buf_size; iov.iov_base = buf; iov.iov_len = this_buf_size; /* Increase sequence number, so that we can distinguish between old and new request messages. */ h->seq++; if (__netlink_sendreq (h, type) < 0) goto out_fail; continue; } size_t count = 0; size_t remaining_len = read_len; for (nlmh = (struct nlmsghdr *) buf; NLMSG_OK (nlmh, remaining_len); nlmh = (struct nlmsghdr *) NLMSG_NEXT (nlmh, remaining_len)) { if ((pid_t) nlmh->nlmsg_pid != h->pid || nlmh->nlmsg_seq != h->seq) continue; ++count; if (nlmh->nlmsg_type == NLMSG_DONE) { /* We found the end, leave the loop. */ done = true; break; } if (nlmh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA (nlmh); if (nlmh->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) errno = EIO; else errno = -nlerr->error; goto out_fail; } } /* If there was nothing with the expected nlmsg_pid and nlmsg_seq, there is no point to record it. */ if (count == 0) continue; nlm_next = (struct netlink_res *) malloc (sizeof (struct netlink_res) + read_len); if (nlm_next == NULL) goto out_fail; nlm_next->next = NULL; nlm_next->nlh = memcpy (nlm_next + 1, buf, read_len); nlm_next->size = read_len; nlm_next->seq = h->seq; if (h->nlm_list == NULL) h->nlm_list = nlm_next; else h->end_ptr->next = nlm_next; h->end_ptr = nlm_next; } if (use_malloc) free (buf); return 0; out_fail: if (use_malloc) free (buf); return -1; }
static bool_t rendezvous_request (SVCXPRT *xprt, struct rpc_msg *errmsg) { int sock; struct unix_rendezvous *r; struct sockaddr_un addr; struct sockaddr_in in_addr; socklen_t len; r = (struct unix_rendezvous *) xprt->xp_p1; again: len = sizeof (struct sockaddr_un); if ((sock = accept (xprt->xp_sock, (struct sockaddr *) &addr, &len)) < 0) { if (errno == EINTR) goto again; if (errno == EMFILE) { struct timespec ts = { .tv_sec = 0, .tv_nsec = 50000000 }; __nanosleep(&ts , NULL); } return FALSE; } /* * make a new transporter (re-uses xprt) */ memset (&in_addr, '\0', sizeof (in_addr)); in_addr.sin_family = AF_UNIX; xprt = makefd_xprt (sock, r->sendsize, r->recvsize); memcpy (&xprt->xp_raddr, &in_addr, sizeof (in_addr)); xprt->xp_addrlen = len; return FALSE; /* there is never an rpc msg to be processed */ } static enum xprt_stat rendezvous_stat (SVCXPRT *xprt) { return XPRT_IDLE; } static void svcunix_destroy (SVCXPRT *xprt) { struct unix_conn *cd = (struct unix_conn *) xprt->xp_p1; xprt_unregister (xprt); __close (xprt->xp_sock); if (xprt->xp_port != 0) { /* a rendezvouser socket */ xprt->xp_port = 0; } else { /* an actual connection socket */ XDR_DESTROY (&(cd->xdrs)); } mem_free ((caddr_t) cd, sizeof (struct unix_conn)); mem_free ((caddr_t) xprt, sizeof (SVCXPRT)); } #ifdef SCM_CREDENTIALS struct cmessage { struct cmsghdr cmsg; struct ucred cmcred; /* hack to make sure we have enough memory */ char dummy[(CMSG_ALIGN (sizeof (struct ucred)) - sizeof (struct ucred) + sizeof (long))]; }; /* XXX This is not thread safe, but since the main functions in svc.c and the rpcgen generated *_svc functions for the daemon are also not thread safe and uses static global variables, it doesn't matter. */ static struct cmessage cm; #endif static int __msgread (int sock, void *data, size_t cnt) { struct iovec iov; struct msghdr msg; int len; iov.iov_base = data; iov.iov_len = cnt; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; #ifdef SCM_CREDENTIALS msg.msg_control = (caddr_t) &cm; msg.msg_controllen = sizeof (struct cmessage); #endif msg.msg_flags = 0; #ifdef SO_PASSCRED { int on = 1; if (__setsockopt (sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof (on))) return -1; } #endif restart: len = __recvmsg (sock, &msg, 0); if (len >= 0) { if (msg.msg_flags & MSG_CTRUNC || len == 0) return 0; else return len; } if (errno == EINTR) goto restart; return -1; } static int __msgwrite (int sock, void *data, size_t cnt) { #ifndef SCM_CREDENTIALS /* We cannot implement this reliably. */ __set_errno (ENOSYS); return -1; #else struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg = &cm.cmsg; struct ucred cred; int len; /* XXX I'm not sure, if gete?id() is always correct, or if we should use get?id(). But since keyserv needs geteuid(), we have no other chance. It would be much better, if the kernel could pass both to the server. */ cred.pid = __getpid (); cred.uid = __geteuid (); cred.gid = __getegid (); memcpy (CMSG_DATA(cmsg), &cred, sizeof (struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = sizeof(*cmsg) + sizeof(struct ucred); iov.iov_base = data; iov.iov_len = cnt; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmsg; msg.msg_controllen = CMSG_ALIGN(cmsg->cmsg_len); msg.msg_flags = 0; restart: len = __sendmsg (sock, &msg, 0); if (len >= 0) return len; if (errno == EINTR) goto restart; return -1; #endif }
static enum clnt_stat clntudp_call (/* client handle */ CLIENT *cl, /* procedure number */ u_long proc, /* xdr routine for args */ xdrproc_t xargs, /* pointer to args */ caddr_t argsp, /* xdr routine for results */ xdrproc_t xresults, /* pointer to results */ caddr_t resultsp, /* seconds to wait before giving up */ struct timeval utimeout) { struct cu_data *cu = (struct cu_data *) cl->cl_private; XDR *xdrs; int outlen = 0; int inlen; socklen_t fromlen; struct pollfd fd; int milliseconds = (cu->cu_wait.tv_sec * 1000) + (cu->cu_wait.tv_usec / 1000); struct sockaddr_in from; struct rpc_msg reply_msg; XDR reply_xdrs; struct timeval time_waited; bool_t ok; int nrefreshes = 2; /* number of times to refresh cred */ struct timeval timeout; int anyup; /* any network interface up */ if (cu->cu_total.tv_usec == -1) { timeout = utimeout; /* use supplied timeout */ } else { timeout = cu->cu_total; /* use default timeout */ } time_waited.tv_sec = 0; time_waited.tv_usec = 0; call_again: xdrs = &(cu->cu_outxdrs); if (xargs == NULL) goto get_reply; xdrs->x_op = XDR_ENCODE; XDR_SETPOS (xdrs, cu->cu_xdrpos); /* * the transaction is the first thing in the out buffer */ (*(uint32_t *) (cu->cu_outbuf))++; if ((!XDR_PUTLONG (xdrs, (long *) &proc)) || (!AUTH_MARSHALL (cl->cl_auth, xdrs)) || (!(*xargs) (xdrs, argsp))) return (cu->cu_error.re_status = RPC_CANTENCODEARGS); outlen = (int) XDR_GETPOS (xdrs); send_again: if (__sendto (cu->cu_sock, cu->cu_outbuf, outlen, 0, (struct sockaddr *) &(cu->cu_raddr), cu->cu_rlen) != outlen) { cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTSEND); } /* * Hack to provide rpc-based message passing */ if (timeout.tv_sec == 0 && timeout.tv_usec == 0) { return (cu->cu_error.re_status = RPC_TIMEDOUT); } get_reply: /* * sub-optimal code appears here because we have * some clock time to spare while the packets are in flight. * (We assume that this is actually only executed once.) */ reply_msg.acpted_rply.ar_verf = _null_auth; reply_msg.acpted_rply.ar_results.where = resultsp; reply_msg.acpted_rply.ar_results.proc = xresults; fd.fd = cu->cu_sock; fd.events = POLLIN; anyup = 0; for (;;) { switch (__poll (&fd, 1, milliseconds)) { case 0: if (anyup == 0) { anyup = is_network_up (cu->cu_sock); if (!anyup) return (cu->cu_error.re_status = RPC_CANTRECV); } time_waited.tv_sec += cu->cu_wait.tv_sec; time_waited.tv_usec += cu->cu_wait.tv_usec; while (time_waited.tv_usec >= 1000000) { time_waited.tv_sec++; time_waited.tv_usec -= 1000000; } if ((time_waited.tv_sec < timeout.tv_sec) || ((time_waited.tv_sec == timeout.tv_sec) && (time_waited.tv_usec < timeout.tv_usec))) goto send_again; return (cu->cu_error.re_status = RPC_TIMEDOUT); /* * buggy in other cases because time_waited is not being * updated. */ case -1: if (errno == EINTR) continue; cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTRECV); } #ifdef IP_RECVERR if (fd.revents & POLLERR) { struct msghdr msg; struct cmsghdr *cmsg; struct sock_extended_err *e; struct sockaddr_in err_addr; struct iovec iov; char *cbuf = malloc (outlen + 256); int ret; if (cbuf == NULL) { cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTRECV); } iov.iov_base = cbuf + 256; iov.iov_len = outlen; msg.msg_name = (void *) &err_addr; msg.msg_namelen = sizeof (err_addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_flags = 0; msg.msg_control = cbuf; msg.msg_controllen = 256; ret = __recvmsg (cu->cu_sock, &msg, MSG_ERRQUEUE); if (ret >= 0 && memcmp (cbuf + 256, cu->cu_outbuf, ret) == 0 && (msg.msg_flags & MSG_ERRQUEUE) && ((msg.msg_namelen == 0 && ret >= 12) || (msg.msg_namelen == sizeof (err_addr) && err_addr.sin_family == AF_INET && memcmp (&err_addr.sin_addr, &cu->cu_raddr.sin_addr, sizeof (err_addr.sin_addr)) == 0 && err_addr.sin_port == cu->cu_raddr.sin_port))) for (cmsg = CMSG_FIRSTHDR (&msg); cmsg; cmsg = CMSG_NXTHDR (&msg, cmsg)) if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) { free (cbuf); e = (struct sock_extended_err *) CMSG_DATA(cmsg); cu->cu_error.re_errno = e->ee_errno; return (cu->cu_error.re_status = RPC_CANTRECV); } free (cbuf); } #endif do { fromlen = sizeof (struct sockaddr); inlen = __recvfrom (cu->cu_sock, cu->cu_inbuf, (int) cu->cu_recvsz, MSG_DONTWAIT, (struct sockaddr *) &from, &fromlen); } while (inlen < 0 && errno == EINTR); if (inlen < 0) { if (errno == EWOULDBLOCK) continue; cu->cu_error.re_errno = errno; return (cu->cu_error.re_status = RPC_CANTRECV); } if (inlen < 4) continue; /* see if reply transaction id matches sent id. Don't do this if we only wait for a replay */ if (xargs != NULL && memcmp (cu->cu_inbuf, cu->cu_outbuf, sizeof (u_int32_t)) != 0) continue; /* we now assume we have the proper reply */ break; } /* * now decode and validate the response */ xdrmem_create (&reply_xdrs, cu->cu_inbuf, (u_int) inlen, XDR_DECODE); ok = xdr_replymsg (&reply_xdrs, &reply_msg); /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */ if (ok) { _seterr_reply (&reply_msg, &(cu->cu_error)); if (cu->cu_error.re_status == RPC_SUCCESS) { if (!AUTH_VALIDATE (cl->cl_auth, &reply_msg.acpted_rply.ar_verf)) { cu->cu_error.re_status = RPC_AUTHERROR; cu->cu_error.re_why = AUTH_INVALIDRESP; } if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) { xdrs->x_op = XDR_FREE; (void) xdr_opaque_auth (xdrs, &(reply_msg.acpted_rply.ar_verf)); } } /* end successful completion */ else { /* maybe our credentials need to be refreshed ... */ if (nrefreshes > 0 && AUTH_REFRESH (cl->cl_auth)) { nrefreshes--; goto call_again; } } /* end of unsuccessful completion */ } /* end of valid reply message */ else { cu->cu_error.re_status = RPC_CANTDECODERES; } return cu->cu_error.re_status; }