xref: /freebsd/sbin/hastd/proto_common.c (revision a7ebb3eb8b7bc265fbcbd5ff7a1cdea7e3d9dd23)
132115b10SPawel Jakub Dawidek /*-
232115b10SPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
3*a7ebb3ebSPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
432115b10SPawel Jakub Dawidek  * All rights reserved.
532115b10SPawel Jakub Dawidek  *
632115b10SPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
732115b10SPawel Jakub Dawidek  * the FreeBSD Foundation.
832115b10SPawel Jakub Dawidek  *
932115b10SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
1032115b10SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
1132115b10SPawel Jakub Dawidek  * are met:
1232115b10SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
1332115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
1432115b10SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
1532115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
1632115b10SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
1732115b10SPawel Jakub Dawidek  *
1832115b10SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1932115b10SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2032115b10SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2132115b10SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2232115b10SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2332115b10SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2432115b10SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2532115b10SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2632115b10SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2732115b10SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2832115b10SPawel Jakub Dawidek  * SUCH DAMAGE.
2932115b10SPawel Jakub Dawidek  */
3032115b10SPawel Jakub Dawidek 
3132115b10SPawel Jakub Dawidek #include <sys/cdefs.h>
3232115b10SPawel Jakub Dawidek __FBSDID("$FreeBSD$");
3332115b10SPawel Jakub Dawidek 
3432115b10SPawel Jakub Dawidek #include <sys/types.h>
3532115b10SPawel Jakub Dawidek #include <sys/socket.h>
3632115b10SPawel Jakub Dawidek 
3732115b10SPawel Jakub Dawidek #include <errno.h>
38*a7ebb3ebSPawel Jakub Dawidek #include <fcntl.h>
39*a7ebb3ebSPawel Jakub Dawidek #include <stdbool.h>
4032115b10SPawel Jakub Dawidek #include <stdlib.h>
4132115b10SPawel Jakub Dawidek #include <strings.h>
42*a7ebb3ebSPawel Jakub Dawidek #include <unistd.h>
4332115b10SPawel Jakub Dawidek 
442ec483c5SPawel Jakub Dawidek #include "pjdlog.h"
4532115b10SPawel Jakub Dawidek #include "proto_impl.h"
4632115b10SPawel Jakub Dawidek 
4732115b10SPawel Jakub Dawidek /* Maximum size of packet we want to use when sending data. */
4832115b10SPawel Jakub Dawidek #ifndef MAX_SEND_SIZE
4928df1f23SPawel Jakub Dawidek #define	MAX_SEND_SIZE	32768
5032115b10SPawel Jakub Dawidek #endif
5132115b10SPawel Jakub Dawidek 
52*a7ebb3ebSPawel Jakub Dawidek static bool
53*a7ebb3ebSPawel Jakub Dawidek blocking_socket(int sock)
54*a7ebb3ebSPawel Jakub Dawidek {
55*a7ebb3ebSPawel Jakub Dawidek 	int flags;
56*a7ebb3ebSPawel Jakub Dawidek 
57*a7ebb3ebSPawel Jakub Dawidek 	flags = fcntl(sock, F_GETFL);
58*a7ebb3ebSPawel Jakub Dawidek 	PJDLOG_ASSERT(flags >= 0);
59*a7ebb3ebSPawel Jakub Dawidek 	return ((flags & O_NONBLOCK) == 0);
60*a7ebb3ebSPawel Jakub Dawidek }
61*a7ebb3ebSPawel Jakub Dawidek 
6201ab52c0SPawel Jakub Dawidek static int
6301ab52c0SPawel Jakub Dawidek proto_descriptor_send(int sock, int fd)
648046c499SPawel Jakub Dawidek {
658046c499SPawel Jakub Dawidek 	unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
668046c499SPawel Jakub Dawidek 	struct msghdr msg;
678046c499SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
688046c499SPawel Jakub Dawidek 
698046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
708046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
718046c499SPawel Jakub Dawidek 
728046c499SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
738046c499SPawel Jakub Dawidek 	bzero(&ctrl, sizeof(ctrl));
748046c499SPawel Jakub Dawidek 
758046c499SPawel Jakub Dawidek 	msg.msg_iov = NULL;
768046c499SPawel Jakub Dawidek 	msg.msg_iovlen = 0;
778046c499SPawel Jakub Dawidek 	msg.msg_control = ctrl;
788046c499SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(ctrl);
798046c499SPawel Jakub Dawidek 
808046c499SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
818046c499SPawel Jakub Dawidek 	cmsg->cmsg_level = SOL_SOCKET;
828046c499SPawel Jakub Dawidek 	cmsg->cmsg_type = SCM_RIGHTS;
838046c499SPawel Jakub Dawidek 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
8494486ae2SPawel Jakub Dawidek 	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
858046c499SPawel Jakub Dawidek 
868046c499SPawel Jakub Dawidek 	if (sendmsg(sock, &msg, 0) == -1)
878046c499SPawel Jakub Dawidek 		return (errno);
888046c499SPawel Jakub Dawidek 
898046c499SPawel Jakub Dawidek 	return (0);
908046c499SPawel Jakub Dawidek }
918046c499SPawel Jakub Dawidek 
928046c499SPawel Jakub Dawidek int
9301ab52c0SPawel Jakub Dawidek proto_common_send(int sock, const unsigned char *data, size_t size, int fd)
9401ab52c0SPawel Jakub Dawidek {
9501ab52c0SPawel Jakub Dawidek 	ssize_t done;
9601ab52c0SPawel Jakub Dawidek 	size_t sendsize;
9701ab52c0SPawel Jakub Dawidek 
9801ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
993a0b818fSPawel Jakub Dawidek 
1003a0b818fSPawel Jakub Dawidek 	if (data == NULL) {
1013a0b818fSPawel Jakub Dawidek 		/* The caller is just trying to decide about direction. */
1023a0b818fSPawel Jakub Dawidek 
1033a0b818fSPawel Jakub Dawidek 		PJDLOG_ASSERT(size == 0);
1043a0b818fSPawel Jakub Dawidek 
1053a0b818fSPawel Jakub Dawidek 		if (shutdown(sock, SHUT_RD) == -1)
1063a0b818fSPawel Jakub Dawidek 			return (errno);
1073a0b818fSPawel Jakub Dawidek 		return (0);
1083a0b818fSPawel Jakub Dawidek 	}
1093a0b818fSPawel Jakub Dawidek 
11001ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(data != NULL);
11101ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(size > 0);
11201ab52c0SPawel Jakub Dawidek 
11301ab52c0SPawel Jakub Dawidek 	do {
11401ab52c0SPawel Jakub Dawidek 		sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE;
11501ab52c0SPawel Jakub Dawidek 		done = send(sock, data, sendsize, MSG_NOSIGNAL);
116*a7ebb3ebSPawel Jakub Dawidek 		if (done == 0) {
11701ab52c0SPawel Jakub Dawidek 			return (ENOTCONN);
118*a7ebb3ebSPawel Jakub Dawidek 		} else if (done < 0) {
11901ab52c0SPawel Jakub Dawidek 			if (errno == EINTR)
12001ab52c0SPawel Jakub Dawidek 				continue;
121*a7ebb3ebSPawel Jakub Dawidek 			/*
122*a7ebb3ebSPawel Jakub Dawidek 			 * If this is blocking socket and we got EAGAIN, this
123*a7ebb3ebSPawel Jakub Dawidek 			 * means the request timed out. Translate errno to
124*a7ebb3ebSPawel Jakub Dawidek 			 * ETIMEDOUT, to give administrator a hint to
125*a7ebb3ebSPawel Jakub Dawidek 			 * eventually increase timeout.
126*a7ebb3ebSPawel Jakub Dawidek 			 */
127*a7ebb3ebSPawel Jakub Dawidek 			if (errno == EAGAIN && blocking_socket(sock))
128*a7ebb3ebSPawel Jakub Dawidek 				errno = ETIMEDOUT;
12901ab52c0SPawel Jakub Dawidek 			return (errno);
13001ab52c0SPawel Jakub Dawidek 		}
13101ab52c0SPawel Jakub Dawidek 		data += done;
13201ab52c0SPawel Jakub Dawidek 		size -= done;
13301ab52c0SPawel Jakub Dawidek 	} while (size > 0);
13401ab52c0SPawel Jakub Dawidek 
13501ab52c0SPawel Jakub Dawidek 	if (fd == -1)
13601ab52c0SPawel Jakub Dawidek 		return (0);
13701ab52c0SPawel Jakub Dawidek 	return (proto_descriptor_send(sock, fd));
13801ab52c0SPawel Jakub Dawidek }
13901ab52c0SPawel Jakub Dawidek 
14001ab52c0SPawel Jakub Dawidek static int
14101ab52c0SPawel Jakub Dawidek proto_descriptor_recv(int sock, int *fdp)
1428046c499SPawel Jakub Dawidek {
1438046c499SPawel Jakub Dawidek 	unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
1448046c499SPawel Jakub Dawidek 	struct msghdr msg;
1458046c499SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
1468046c499SPawel Jakub Dawidek 
1478046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
1488046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp != NULL);
1498046c499SPawel Jakub Dawidek 
1508046c499SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
1518046c499SPawel Jakub Dawidek 	bzero(&ctrl, sizeof(ctrl));
1528046c499SPawel Jakub Dawidek 
1538046c499SPawel Jakub Dawidek 	msg.msg_iov = NULL;
1548046c499SPawel Jakub Dawidek 	msg.msg_iovlen = 0;
1558046c499SPawel Jakub Dawidek 	msg.msg_control = ctrl;
1568046c499SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(ctrl);
1578046c499SPawel Jakub Dawidek 
1588046c499SPawel Jakub Dawidek 	if (recvmsg(sock, &msg, 0) == -1)
1598046c499SPawel Jakub Dawidek 		return (errno);
1608046c499SPawel Jakub Dawidek 
16194486ae2SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
16294486ae2SPawel Jakub Dawidek 	if (cmsg->cmsg_level != SOL_SOCKET ||
16301ab52c0SPawel Jakub Dawidek 	    cmsg->cmsg_type != SCM_RIGHTS) {
16494486ae2SPawel Jakub Dawidek 		return (EINVAL);
1658046c499SPawel Jakub Dawidek 	}
16694486ae2SPawel Jakub Dawidek 	bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
1678046c499SPawel Jakub Dawidek 
16894486ae2SPawel Jakub Dawidek 	return (0);
1698046c499SPawel Jakub Dawidek }
17001ab52c0SPawel Jakub Dawidek 
17101ab52c0SPawel Jakub Dawidek int
17201ab52c0SPawel Jakub Dawidek proto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
17301ab52c0SPawel Jakub Dawidek {
17401ab52c0SPawel Jakub Dawidek 	ssize_t done;
17501ab52c0SPawel Jakub Dawidek 
17601ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
1773a0b818fSPawel Jakub Dawidek 
1783a0b818fSPawel Jakub Dawidek 	if (data == NULL) {
1793a0b818fSPawel Jakub Dawidek 		/* The caller is just trying to decide about direction. */
1803a0b818fSPawel Jakub Dawidek 
1813a0b818fSPawel Jakub Dawidek 		PJDLOG_ASSERT(size == 0);
1823a0b818fSPawel Jakub Dawidek 
1833a0b818fSPawel Jakub Dawidek 		if (shutdown(sock, SHUT_WR) == -1)
1843a0b818fSPawel Jakub Dawidek 			return (errno);
1853a0b818fSPawel Jakub Dawidek 		return (0);
1863a0b818fSPawel Jakub Dawidek 	}
1873a0b818fSPawel Jakub Dawidek 
18801ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(data != NULL);
18901ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(size > 0);
19001ab52c0SPawel Jakub Dawidek 
19101ab52c0SPawel Jakub Dawidek 	do {
19201ab52c0SPawel Jakub Dawidek 		done = recv(sock, data, size, MSG_WAITALL);
19301ab52c0SPawel Jakub Dawidek 	} while (done == -1 && errno == EINTR);
194*a7ebb3ebSPawel Jakub Dawidek 	if (done == 0) {
19501ab52c0SPawel Jakub Dawidek 		return (ENOTCONN);
196*a7ebb3ebSPawel Jakub Dawidek 	} else if (done < 0) {
197*a7ebb3ebSPawel Jakub Dawidek 		/*
198*a7ebb3ebSPawel Jakub Dawidek 		 * If this is blocking socket and we got EAGAIN, this
199*a7ebb3ebSPawel Jakub Dawidek 		 * means the request timed out. Translate errno to
200*a7ebb3ebSPawel Jakub Dawidek 		 * ETIMEDOUT, to give administrator a hint to
201*a7ebb3ebSPawel Jakub Dawidek 		 * eventually increase timeout.
202*a7ebb3ebSPawel Jakub Dawidek 		 */
203*a7ebb3ebSPawel Jakub Dawidek 		if (errno == EAGAIN && blocking_socket(sock))
204*a7ebb3ebSPawel Jakub Dawidek 			errno = ETIMEDOUT;
20501ab52c0SPawel Jakub Dawidek 		return (errno);
206*a7ebb3ebSPawel Jakub Dawidek 	}
20701ab52c0SPawel Jakub Dawidek 	if (fdp == NULL)
20801ab52c0SPawel Jakub Dawidek 		return (0);
20901ab52c0SPawel Jakub Dawidek 	return (proto_descriptor_recv(sock, fdp));
21001ab52c0SPawel Jakub Dawidek }
211