2 * Copyright (c) 2005 Andrey Simonenko
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/socket.h>
44 #include "uc_common.h"
59 #define LOGMSG_SIZE 128
62 uc_output(const char *format, ...)
64 char buf[LOGMSG_SIZE];
68 if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
69 err(EXIT_FAILURE, "output: vsnprintf failed");
70 write(STDOUT_FILENO, buf, strlen(buf));
75 uc_logmsg(const char *format, ...)
77 char buf[LOGMSG_SIZE];
83 if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
84 err(EXIT_FAILURE, "logmsg: vsnprintf failed");
86 uc_output("%s: %s\n", uc_cfg.proc_name, buf);
88 uc_output("%s: %s: %s\n", uc_cfg.proc_name, buf,
89 strerror(errno_save));
95 uc_vlogmsgx(const char *format, va_list ap)
97 char buf[LOGMSG_SIZE];
99 if (vsnprintf(buf, sizeof(buf), format, ap) < 0)
100 err(EXIT_FAILURE, "uc_logmsgx: vsnprintf failed");
101 uc_output("%s: %s\n", uc_cfg.proc_name, buf);
105 uc_logmsgx(const char *format, ...)
109 va_start(ap, format);
110 uc_vlogmsgx(format, ap);
115 uc_dbgmsg(const char *format, ...)
120 va_start(ap, format);
121 uc_vlogmsgx(format, ap);
127 uc_socket_create(void)
132 fd = socket(PF_LOCAL, uc_cfg.sock_type, 0);
134 uc_logmsg("socket_create: socket(PF_LOCAL, %s, 0)", uc_cfg.sock_type_str);
137 if (uc_cfg.server_flag)
138 uc_cfg.serv_sock_fd = fd;
142 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0 ||
143 setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
144 uc_logmsg("socket_create: setsockopt(SO_RCVTIMEO/SO_SNDTIMEO)");
148 if (uc_cfg.server_flag) {
149 if (bind(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
150 uc_cfg.serv_addr_sun.sun_len) < 0) {
151 uc_logmsg("socket_create: bind(%s)",
152 uc_cfg.serv_addr_sun.sun_path);
155 if (uc_cfg.sock_type == SOCK_STREAM) {
158 if (listen(fd, LISTENQ) < 0) {
159 uc_logmsg("socket_create: listen");
162 val = fcntl(fd, F_GETFL, 0);
164 uc_logmsg("socket_create: fcntl(F_GETFL)");
167 if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
168 uc_logmsg("socket_create: fcntl(F_SETFL)");
178 uc_logmsg("socket_create: close");
179 if (uc_cfg.server_flag)
180 if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0)
181 uc_logmsg("socket_close: unlink(%s)",
182 uc_cfg.serv_addr_sun.sun_path);
187 uc_socket_close(int fd)
193 uc_logmsg("socket_close: close");
196 if (uc_cfg.server_flag && fd == uc_cfg.serv_sock_fd)
197 if (unlink(uc_cfg.serv_addr_sun.sun_path) < 0) {
198 uc_logmsg("socket_close: unlink(%s)",
199 uc_cfg.serv_addr_sun.sun_path);
206 uc_socket_connect(int fd)
208 uc_dbgmsg("connect");
210 if (connect(fd, (struct sockaddr *)&uc_cfg.serv_addr_sun,
211 uc_cfg.serv_addr_sun.sun_len) < 0) {
212 uc_logmsg("socket_connect: connect(%s)", uc_cfg.serv_addr_sun.sun_path);
225 uc_dbgmsg("sync: wait");
227 fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_SERVER : SYNC_CLIENT][SYNC_RECV];
229 ssize = read(fd, &buf, 1);
231 uc_logmsg("sync_recv: read");
235 uc_logmsgx("sync_recv: read %zd of 1 byte", ssize);
239 uc_dbgmsg("sync: received");
250 uc_dbgmsg("sync: send");
252 fd = uc_cfg.sync_fd[uc_cfg.server_flag ? SYNC_CLIENT : SYNC_SERVER][SYNC_SEND];
254 ssize = write(fd, "", 1);
256 uc_logmsg("uc_sync_send: write");
260 uc_logmsgx("uc_sync_send: sent %zd of 1 byte", ssize);
268 uc_message_send(int fd, const struct msghdr *msghdr)
270 const struct cmsghdr *cmsghdr;
274 size = msghdr->msg_iov != 0 ? msghdr->msg_iov->iov_len : 0;
275 uc_dbgmsg("send: data size %zu", size);
276 uc_dbgmsg("send: msghdr.msg_controllen %u",
277 (u_int)msghdr->msg_controllen);
278 cmsghdr = CMSG_FIRSTHDR(msghdr);
280 uc_dbgmsg("send: cmsghdr.cmsg_len %u",
281 (u_int)cmsghdr->cmsg_len);
283 ssize = sendmsg(fd, msghdr, 0);
285 uc_logmsg("message_send: sendmsg");
288 if ((size_t)ssize != size) {
289 uc_logmsgx("message_send: sendmsg: sent %zd of %zu bytes",
294 if (!uc_cfg.send_data_flag)
295 if (uc_sync_send() < 0)
302 uc_message_sendn(int fd, struct msghdr *msghdr)
306 for (i = 1; i <= uc_cfg.ipc_msg.msg_num; ++i) {
307 uc_dbgmsg("message #%u", i);
308 if (uc_message_send(fd, msghdr) < 0)
315 uc_message_recv(int fd, struct msghdr *msghdr)
317 const struct cmsghdr *cmsghdr;
321 if (!uc_cfg.send_data_flag)
322 if (uc_sync_recv() < 0)
325 size = msghdr->msg_iov != NULL ? msghdr->msg_iov->iov_len : 0;
326 ssize = recvmsg(fd, msghdr, MSG_WAITALL);
328 uc_logmsg("message_recv: recvmsg");
331 if ((size_t)ssize != size) {
332 uc_logmsgx("message_recv: recvmsg: received %zd of %zu bytes",
337 uc_dbgmsg("recv: data size %zd", ssize);
338 uc_dbgmsg("recv: msghdr.msg_controllen %u",
339 (u_int)msghdr->msg_controllen);
340 cmsghdr = CMSG_FIRSTHDR(msghdr);
342 uc_dbgmsg("recv: cmsghdr.cmsg_len %u",
343 (u_int)cmsghdr->cmsg_len);
345 if (memcmp(uc_cfg.ipc_msg.buf_recv, uc_cfg.ipc_msg.buf_send, size) != 0) {
346 uc_logmsgx("message_recv: received message has wrong content");
354 uc_socket_accept(int listenfd)
363 FD_SET(listenfd, &rset);
366 rv = select(listenfd + 1, &rset, (fd_set *)NULL, (fd_set *)NULL, &tv);
368 uc_logmsg("socket_accept: select");
372 uc_logmsgx("socket_accept: select timeout");
376 fd = accept(listenfd, (struct sockaddr *)NULL, (socklen_t *)NULL);
378 uc_logmsg("socket_accept: accept");
382 val = fcntl(fd, F_GETFL, 0);
384 uc_logmsg("socket_accept: fcntl(F_GETFL)");
387 if (fcntl(fd, F_SETFL, val & ~O_NONBLOCK) < 0) {
388 uc_logmsg("socket_accept: fcntl(F_SETFL)");
396 uc_logmsg("socket_accept: close");
401 uc_check_msghdr(const struct msghdr *msghdr, size_t size)
403 if (msghdr->msg_flags & MSG_TRUNC) {
404 uc_logmsgx("msghdr.msg_flags has MSG_TRUNC");
407 if (msghdr->msg_flags & MSG_CTRUNC) {
408 uc_logmsgx("msghdr.msg_flags has MSG_CTRUNC");
411 if (msghdr->msg_controllen < size) {
412 uc_logmsgx("msghdr.msg_controllen %u < %zu",
413 (u_int)msghdr->msg_controllen, size);
416 if (msghdr->msg_controllen > 0 && size == 0) {
417 uc_logmsgx("msghdr.msg_controllen %u > 0",
418 (u_int)msghdr->msg_controllen);
425 uc_check_cmsghdr(const struct cmsghdr *cmsghdr, int type, size_t size)
427 if (cmsghdr == NULL) {
428 uc_logmsgx("cmsghdr is NULL");
431 if (cmsghdr->cmsg_level != SOL_SOCKET) {
432 uc_logmsgx("cmsghdr.cmsg_level %d != SOL_SOCKET",
433 cmsghdr->cmsg_level);
436 if (cmsghdr->cmsg_type != type) {
437 uc_logmsgx("cmsghdr.cmsg_type %d != %d",
438 cmsghdr->cmsg_type, type);
441 if (cmsghdr->cmsg_len != CMSG_LEN(size)) {
442 uc_logmsgx("cmsghdr.cmsg_len %u != %zu",
443 (u_int)cmsghdr->cmsg_len, CMSG_LEN(size));
450 uc_msghdr_init_generic(struct msghdr *msghdr, struct iovec *iov, void *cmsg_data)
452 msghdr->msg_name = NULL;
453 msghdr->msg_namelen = 0;
454 if (uc_cfg.send_data_flag) {
455 iov->iov_base = uc_cfg.server_flag ?
456 uc_cfg.ipc_msg.buf_recv : uc_cfg.ipc_msg.buf_send;
457 iov->iov_len = uc_cfg.ipc_msg.buf_size;
458 msghdr->msg_iov = iov;
459 msghdr->msg_iovlen = 1;
461 msghdr->msg_iov = NULL;
462 msghdr->msg_iovlen = 0;
464 msghdr->msg_control = cmsg_data;
465 msghdr->msg_flags = 0;
469 uc_msghdr_init_server(struct msghdr *msghdr, struct iovec *iov,
470 void *cmsg_data, size_t cmsg_size)
472 uc_msghdr_init_generic(msghdr, iov, cmsg_data);
473 msghdr->msg_controllen = cmsg_size;
474 uc_dbgmsg("init: data size %zu", msghdr->msg_iov != NULL ?
475 msghdr->msg_iov->iov_len : (size_t)0);
476 uc_dbgmsg("init: msghdr.msg_controllen %u",
477 (u_int)msghdr->msg_controllen);
481 uc_msghdr_init_client(struct msghdr *msghdr, struct iovec *iov,
482 void *cmsg_data, size_t cmsg_size, int type, size_t arr_size)
484 struct cmsghdr *cmsghdr;
486 uc_msghdr_init_generic(msghdr, iov, cmsg_data);
487 if (cmsg_data != NULL) {
488 if (uc_cfg.send_array_flag)
489 uc_dbgmsg("sending an array");
491 uc_dbgmsg("sending a scalar");
492 msghdr->msg_controllen = uc_cfg.send_array_flag ?
493 cmsg_size : CMSG_SPACE(0);
494 cmsghdr = CMSG_FIRSTHDR(msghdr);
495 cmsghdr->cmsg_level = SOL_SOCKET;
496 cmsghdr->cmsg_type = type;
497 cmsghdr->cmsg_len = CMSG_LEN(uc_cfg.send_array_flag ? arr_size : 0);
499 msghdr->msg_controllen = 0;
507 if (pipe(uc_cfg.sync_fd[SYNC_SERVER]) < 0 ||
508 pipe(uc_cfg.sync_fd[SYNC_CLIENT]) < 0) {
509 uc_logmsg("client_fork: pipe");
512 uc_cfg.client_pid = fork();
513 if (uc_cfg.client_pid == (pid_t)-1) {
514 uc_logmsg("client_fork: fork");
517 if (uc_cfg.client_pid == 0) {
518 uc_cfg.proc_name = "CLIENT";
519 uc_cfg.server_flag = false;
520 fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV];
521 fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND];
523 fd1 = uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND];
524 fd2 = uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV];
526 if (close(fd1) < 0 || close(fd2) < 0) {
527 uc_logmsg("client_fork: close");
530 return (uc_cfg.client_pid != 0);
534 uc_client_exit(int rv)
536 if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_SEND]) < 0 ||
537 close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_RECV]) < 0) {
538 uc_logmsg("client_exit: close");
541 rv = rv == 0 ? EXIT_SUCCESS : -rv;
542 uc_dbgmsg("exit: code %d", rv);
552 uc_dbgmsg("waiting for client");
554 if (close(uc_cfg.sync_fd[SYNC_SERVER][SYNC_RECV]) < 0 ||
555 close(uc_cfg.sync_fd[SYNC_CLIENT][SYNC_SEND]) < 0) {
556 uc_logmsg("client_wait: close");
560 pid = waitpid(uc_cfg.client_pid, &status, 0);
561 if (pid == (pid_t)-1) {
562 uc_logmsg("client_wait: waitpid");
566 if (WIFEXITED(status)) {
567 if (WEXITSTATUS(status) != EXIT_SUCCESS) {
568 uc_logmsgx("client exit status is %d",
569 WEXITSTATUS(status));
570 return (-WEXITSTATUS(status));
573 if (WIFSIGNALED(status))
574 uc_logmsgx("abnormal termination of client, signal %d%s",
575 WTERMSIG(status), WCOREDUMP(status) ?
576 " (core file generated)" : "");
578 uc_logmsgx("termination of client, unknown status");
586 uc_check_groups(const char *gid_arr_str, const gid_t *gid_arr,
587 const char *gid_num_str, int gid_num, bool all_gids)
591 for (i = 0; i < gid_num; ++i)
592 uc_dbgmsg("%s[%d] %lu", gid_arr_str, i, (u_long)gid_arr[i]);
595 if (gid_num != uc_cfg.proc_cred.gid_num) {
596 uc_logmsgx("%s %d != %d", gid_num_str, gid_num,
597 uc_cfg.proc_cred.gid_num);
601 if (gid_num > uc_cfg.proc_cred.gid_num) {
602 uc_logmsgx("%s %d > %d", gid_num_str, gid_num,
603 uc_cfg.proc_cred.gid_num);
607 if (memcmp(gid_arr, uc_cfg.proc_cred.gid_arr,
608 gid_num * sizeof(*gid_arr)) != 0) {
609 uc_logmsgx("%s content is wrong", gid_arr_str);
610 for (i = 0; i < gid_num; ++i)
611 if (gid_arr[i] != uc_cfg.proc_cred.gid_arr[i]) {
612 uc_logmsgx("%s[%d] %lu != %lu",
613 gid_arr_str, i, (u_long)gid_arr[i],
614 (u_long)uc_cfg.proc_cred.gid_arr[i]);
623 uc_check_scm_creds_cmsgcred(struct cmsghdr *cmsghdr)
625 const struct cmsgcred *cmcred;
628 if (uc_check_cmsghdr(cmsghdr, SCM_CREDS, sizeof(struct cmsgcred)) < 0)
631 cmcred = (struct cmsgcred *)CMSG_DATA(cmsghdr);
633 uc_dbgmsg("cmsgcred.cmcred_pid %ld", (long)cmcred->cmcred_pid);
634 uc_dbgmsg("cmsgcred.cmcred_uid %lu", (u_long)cmcred->cmcred_uid);
635 uc_dbgmsg("cmsgcred.cmcred_euid %lu", (u_long)cmcred->cmcred_euid);
636 uc_dbgmsg("cmsgcred.cmcred_gid %lu", (u_long)cmcred->cmcred_gid);
637 uc_dbgmsg("cmsgcred.cmcred_ngroups %d", cmcred->cmcred_ngroups);
641 if (cmcred->cmcred_pid != uc_cfg.client_pid) {
642 uc_logmsgx("cmsgcred.cmcred_pid %ld != %ld",
643 (long)cmcred->cmcred_pid, (long)uc_cfg.client_pid);
646 if (cmcred->cmcred_uid != uc_cfg.proc_cred.uid) {
647 uc_logmsgx("cmsgcred.cmcred_uid %lu != %lu",
648 (u_long)cmcred->cmcred_uid, (u_long)uc_cfg.proc_cred.uid);
651 if (cmcred->cmcred_euid != uc_cfg.proc_cred.euid) {
652 uc_logmsgx("cmsgcred.cmcred_euid %lu != %lu",
653 (u_long)cmcred->cmcred_euid, (u_long)uc_cfg.proc_cred.euid);
656 if (cmcred->cmcred_gid != uc_cfg.proc_cred.gid) {
657 uc_logmsgx("cmsgcred.cmcred_gid %lu != %lu",
658 (u_long)cmcred->cmcred_gid, (u_long)uc_cfg.proc_cred.gid);
661 if (cmcred->cmcred_ngroups == 0) {
662 uc_logmsgx("cmsgcred.cmcred_ngroups == 0");
665 if (cmcred->cmcred_ngroups < 0) {
666 uc_logmsgx("cmsgcred.cmcred_ngroups %d < 0",
667 cmcred->cmcred_ngroups);
670 if (cmcred->cmcred_ngroups > CMGROUP_MAX) {
671 uc_logmsgx("cmsgcred.cmcred_ngroups %d > %d",
672 cmcred->cmcred_ngroups, CMGROUP_MAX);
675 if (cmcred->cmcred_groups[0] != uc_cfg.proc_cred.egid) {
676 uc_logmsgx("cmsgcred.cmcred_groups[0] %lu != %lu (EGID)",
677 (u_long)cmcred->cmcred_groups[0], (u_long)uc_cfg.proc_cred.egid);
680 if (uc_check_groups("cmsgcred.cmcred_groups", cmcred->cmcred_groups,
681 "cmsgcred.cmcred_ngroups", cmcred->cmcred_ngroups, false) < 0)
687 uc_check_scm_creds_sockcred(struct cmsghdr *cmsghdr)
689 const struct sockcred *sc;
692 if (uc_check_cmsghdr(cmsghdr, SCM_CREDS,
693 SOCKCREDSIZE(uc_cfg.proc_cred.gid_num)) < 0)
696 sc = (struct sockcred *)CMSG_DATA(cmsghdr);
700 uc_dbgmsg("sockcred.sc_uid %lu", (u_long)sc->sc_uid);
701 uc_dbgmsg("sockcred.sc_euid %lu", (u_long)sc->sc_euid);
702 uc_dbgmsg("sockcred.sc_gid %lu", (u_long)sc->sc_gid);
703 uc_dbgmsg("sockcred.sc_egid %lu", (u_long)sc->sc_egid);
704 uc_dbgmsg("sockcred.sc_ngroups %d", sc->sc_ngroups);
706 if (sc->sc_uid != uc_cfg.proc_cred.uid) {
707 uc_logmsgx("sockcred.sc_uid %lu != %lu",
708 (u_long)sc->sc_uid, (u_long)uc_cfg.proc_cred.uid);
711 if (sc->sc_euid != uc_cfg.proc_cred.euid) {
712 uc_logmsgx("sockcred.sc_euid %lu != %lu",
713 (u_long)sc->sc_euid, (u_long)uc_cfg.proc_cred.euid);
716 if (sc->sc_gid != uc_cfg.proc_cred.gid) {
717 uc_logmsgx("sockcred.sc_gid %lu != %lu",
718 (u_long)sc->sc_gid, (u_long)uc_cfg.proc_cred.gid);
721 if (sc->sc_egid != uc_cfg.proc_cred.egid) {
722 uc_logmsgx("sockcred.sc_egid %lu != %lu",
723 (u_long)sc->sc_egid, (u_long)uc_cfg.proc_cred.egid);
726 if (sc->sc_ngroups == 0) {
727 uc_logmsgx("sockcred.sc_ngroups == 0");
730 if (sc->sc_ngroups < 0) {
731 uc_logmsgx("sockcred.sc_ngroups %d < 0",
735 if (sc->sc_ngroups != uc_cfg.proc_cred.gid_num) {
736 uc_logmsgx("sockcred.sc_ngroups %d != %u",
737 sc->sc_ngroups, uc_cfg.proc_cred.gid_num);
740 if (uc_check_groups("sockcred.sc_groups", sc->sc_groups,
741 "sockcred.sc_ngroups", sc->sc_ngroups, true) < 0)