xref: /freebsd/sbin/hastd/proto_common.c (revision 1de7b4b805ddbf2429da511c053686ac4591ed89)
132115b10SPawel Jakub Dawidek /*-
2*1de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*1de7b4b8SPedro F. Giffuni  *
432115b10SPawel Jakub Dawidek  * Copyright (c) 2009-2010 The FreeBSD Foundation
5a7ebb3ebSPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
632115b10SPawel Jakub Dawidek  * All rights reserved.
732115b10SPawel Jakub Dawidek  *
832115b10SPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
932115b10SPawel Jakub Dawidek  * the FreeBSD Foundation.
1032115b10SPawel Jakub Dawidek  *
1132115b10SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
1232115b10SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
1332115b10SPawel Jakub Dawidek  * are met:
1432115b10SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
1532115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
1632115b10SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
1732115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
1832115b10SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
1932115b10SPawel Jakub Dawidek  *
2032115b10SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
2132115b10SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2232115b10SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2332115b10SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2432115b10SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2532115b10SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2632115b10SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2732115b10SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2832115b10SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2932115b10SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3032115b10SPawel Jakub Dawidek  * SUCH DAMAGE.
3132115b10SPawel Jakub Dawidek  */
3232115b10SPawel Jakub Dawidek 
3332115b10SPawel Jakub Dawidek #include <sys/cdefs.h>
3432115b10SPawel Jakub Dawidek __FBSDID("$FreeBSD$");
3532115b10SPawel Jakub Dawidek 
3632115b10SPawel Jakub Dawidek #include <sys/types.h>
3732115b10SPawel Jakub Dawidek #include <sys/socket.h>
3832115b10SPawel Jakub Dawidek 
3932115b10SPawel Jakub Dawidek #include <errno.h>
40a7ebb3ebSPawel Jakub Dawidek #include <fcntl.h>
41a7ebb3ebSPawel Jakub Dawidek #include <stdbool.h>
4232115b10SPawel Jakub Dawidek #include <stdlib.h>
4332115b10SPawel Jakub Dawidek #include <strings.h>
44a7ebb3ebSPawel Jakub Dawidek #include <unistd.h>
4532115b10SPawel Jakub Dawidek 
462ec483c5SPawel Jakub Dawidek #include "pjdlog.h"
4732115b10SPawel Jakub Dawidek #include "proto_impl.h"
4832115b10SPawel Jakub Dawidek 
4932115b10SPawel Jakub Dawidek /* Maximum size of packet we want to use when sending data. */
5032115b10SPawel Jakub Dawidek #ifndef MAX_SEND_SIZE
5128df1f23SPawel Jakub Dawidek #define	MAX_SEND_SIZE	32768
5232115b10SPawel Jakub Dawidek #endif
5332115b10SPawel Jakub Dawidek 
54a7ebb3ebSPawel Jakub Dawidek static bool
55a7ebb3ebSPawel Jakub Dawidek blocking_socket(int sock)
56a7ebb3ebSPawel Jakub Dawidek {
57a7ebb3ebSPawel Jakub Dawidek 	int flags;
58a7ebb3ebSPawel Jakub Dawidek 
59a7ebb3ebSPawel Jakub Dawidek 	flags = fcntl(sock, F_GETFL);
60a7ebb3ebSPawel Jakub Dawidek 	PJDLOG_ASSERT(flags >= 0);
61a7ebb3ebSPawel Jakub Dawidek 	return ((flags & O_NONBLOCK) == 0);
62a7ebb3ebSPawel Jakub Dawidek }
63a7ebb3ebSPawel Jakub Dawidek 
6401ab52c0SPawel Jakub Dawidek static int
6501ab52c0SPawel Jakub Dawidek proto_descriptor_send(int sock, int fd)
668046c499SPawel Jakub Dawidek {
678046c499SPawel Jakub Dawidek 	unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
688046c499SPawel Jakub Dawidek 	struct msghdr msg;
698046c499SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
708046c499SPawel Jakub Dawidek 
718046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
728046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(fd >= 0);
738046c499SPawel Jakub Dawidek 
748046c499SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
758046c499SPawel Jakub Dawidek 	bzero(&ctrl, sizeof(ctrl));
768046c499SPawel Jakub Dawidek 
778046c499SPawel Jakub Dawidek 	msg.msg_iov = NULL;
788046c499SPawel Jakub Dawidek 	msg.msg_iovlen = 0;
798046c499SPawel Jakub Dawidek 	msg.msg_control = ctrl;
808046c499SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(ctrl);
818046c499SPawel Jakub Dawidek 
828046c499SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
838046c499SPawel Jakub Dawidek 	cmsg->cmsg_level = SOL_SOCKET;
848046c499SPawel Jakub Dawidek 	cmsg->cmsg_type = SCM_RIGHTS;
858046c499SPawel Jakub Dawidek 	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
8694486ae2SPawel Jakub Dawidek 	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
878046c499SPawel Jakub Dawidek 
888046c499SPawel Jakub Dawidek 	if (sendmsg(sock, &msg, 0) == -1)
898046c499SPawel Jakub Dawidek 		return (errno);
908046c499SPawel Jakub Dawidek 
918046c499SPawel Jakub Dawidek 	return (0);
928046c499SPawel Jakub Dawidek }
938046c499SPawel Jakub Dawidek 
948046c499SPawel Jakub Dawidek int
9501ab52c0SPawel Jakub Dawidek proto_common_send(int sock, const unsigned char *data, size_t size, int fd)
9601ab52c0SPawel Jakub Dawidek {
9701ab52c0SPawel Jakub Dawidek 	ssize_t done;
9801ab52c0SPawel Jakub Dawidek 	size_t sendsize;
9941bb8514SPawel Jakub Dawidek 	int errcount = 0;
10001ab52c0SPawel Jakub Dawidek 
10101ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
1023a0b818fSPawel Jakub Dawidek 
1033a0b818fSPawel Jakub Dawidek 	if (data == NULL) {
1043a0b818fSPawel Jakub Dawidek 		/* The caller is just trying to decide about direction. */
1053a0b818fSPawel Jakub Dawidek 
1063a0b818fSPawel Jakub Dawidek 		PJDLOG_ASSERT(size == 0);
1073a0b818fSPawel Jakub Dawidek 
1083a0b818fSPawel Jakub Dawidek 		if (shutdown(sock, SHUT_RD) == -1)
1093a0b818fSPawel Jakub Dawidek 			return (errno);
1103a0b818fSPawel Jakub Dawidek 		return (0);
1113a0b818fSPawel Jakub Dawidek 	}
1123a0b818fSPawel Jakub Dawidek 
11301ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(data != NULL);
11401ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(size > 0);
11501ab52c0SPawel Jakub Dawidek 
11601ab52c0SPawel Jakub Dawidek 	do {
11701ab52c0SPawel Jakub Dawidek 		sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE;
11801ab52c0SPawel Jakub Dawidek 		done = send(sock, data, sendsize, MSG_NOSIGNAL);
119a7ebb3ebSPawel Jakub Dawidek 		if (done == 0) {
12001ab52c0SPawel Jakub Dawidek 			return (ENOTCONN);
1212b1b224dSPawel Jakub Dawidek 		} else if (done == -1) {
12201ab52c0SPawel Jakub Dawidek 			if (errno == EINTR)
12301ab52c0SPawel Jakub Dawidek 				continue;
12441bb8514SPawel Jakub Dawidek 			if (errno == ENOBUFS) {
12541bb8514SPawel Jakub Dawidek 				/*
12641bb8514SPawel Jakub Dawidek 				 * If there are no buffers we retry.
12741bb8514SPawel Jakub Dawidek 				 * After each try we increase delay before the
12841bb8514SPawel Jakub Dawidek 				 * next one and we give up after fifteen times.
12941bb8514SPawel Jakub Dawidek 				 * This gives 11s of total wait time.
13041bb8514SPawel Jakub Dawidek 				 */
13141bb8514SPawel Jakub Dawidek 				if (errcount == 15) {
13241bb8514SPawel Jakub Dawidek 					pjdlog_warning("Getting ENOBUFS errors for 11s on send(), giving up.");
13341bb8514SPawel Jakub Dawidek 				} else {
13441bb8514SPawel Jakub Dawidek 					if (errcount == 0)
13541bb8514SPawel Jakub Dawidek 						pjdlog_warning("Got ENOBUFS error on send(), retrying for a bit.");
13641bb8514SPawel Jakub Dawidek 					errcount++;
13741bb8514SPawel Jakub Dawidek 					usleep(100000 * errcount);
13841bb8514SPawel Jakub Dawidek 					continue;
13941bb8514SPawel Jakub Dawidek 				}
14041bb8514SPawel Jakub Dawidek 			}
141a7ebb3ebSPawel Jakub Dawidek 			/*
142a7ebb3ebSPawel Jakub Dawidek 			 * If this is blocking socket and we got EAGAIN, this
143a7ebb3ebSPawel Jakub Dawidek 			 * means the request timed out. Translate errno to
144a7ebb3ebSPawel Jakub Dawidek 			 * ETIMEDOUT, to give administrator a hint to
145a7ebb3ebSPawel Jakub Dawidek 			 * eventually increase timeout.
146a7ebb3ebSPawel Jakub Dawidek 			 */
147a7ebb3ebSPawel Jakub Dawidek 			if (errno == EAGAIN && blocking_socket(sock))
148a7ebb3ebSPawel Jakub Dawidek 				errno = ETIMEDOUT;
14901ab52c0SPawel Jakub Dawidek 			return (errno);
15001ab52c0SPawel Jakub Dawidek 		}
15101ab52c0SPawel Jakub Dawidek 		data += done;
15201ab52c0SPawel Jakub Dawidek 		size -= done;
15301ab52c0SPawel Jakub Dawidek 	} while (size > 0);
15441bb8514SPawel Jakub Dawidek 	if (errcount > 0) {
15541bb8514SPawel Jakub Dawidek 		pjdlog_info("Data sent successfully after %d ENOBUFS error%s.",
15641bb8514SPawel Jakub Dawidek 		    errcount, errcount == 1 ? "" : "s");
15741bb8514SPawel Jakub Dawidek 	}
15801ab52c0SPawel Jakub Dawidek 
15901ab52c0SPawel Jakub Dawidek 	if (fd == -1)
16001ab52c0SPawel Jakub Dawidek 		return (0);
16101ab52c0SPawel Jakub Dawidek 	return (proto_descriptor_send(sock, fd));
16201ab52c0SPawel Jakub Dawidek }
16301ab52c0SPawel Jakub Dawidek 
16401ab52c0SPawel Jakub Dawidek static int
16501ab52c0SPawel Jakub Dawidek proto_descriptor_recv(int sock, int *fdp)
1668046c499SPawel Jakub Dawidek {
1678046c499SPawel Jakub Dawidek 	unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
1688046c499SPawel Jakub Dawidek 	struct msghdr msg;
1698046c499SPawel Jakub Dawidek 	struct cmsghdr *cmsg;
1708046c499SPawel Jakub Dawidek 
1718046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
1728046c499SPawel Jakub Dawidek 	PJDLOG_ASSERT(fdp != NULL);
1738046c499SPawel Jakub Dawidek 
1748046c499SPawel Jakub Dawidek 	bzero(&msg, sizeof(msg));
1758046c499SPawel Jakub Dawidek 	bzero(&ctrl, sizeof(ctrl));
1768046c499SPawel Jakub Dawidek 
1778046c499SPawel Jakub Dawidek 	msg.msg_iov = NULL;
1788046c499SPawel Jakub Dawidek 	msg.msg_iovlen = 0;
1798046c499SPawel Jakub Dawidek 	msg.msg_control = ctrl;
1808046c499SPawel Jakub Dawidek 	msg.msg_controllen = sizeof(ctrl);
1818046c499SPawel Jakub Dawidek 
1828046c499SPawel Jakub Dawidek 	if (recvmsg(sock, &msg, 0) == -1)
1838046c499SPawel Jakub Dawidek 		return (errno);
1848046c499SPawel Jakub Dawidek 
18594486ae2SPawel Jakub Dawidek 	cmsg = CMSG_FIRSTHDR(&msg);
1864c13f63cSPawel Jakub Dawidek 	if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
18701ab52c0SPawel Jakub Dawidek 	    cmsg->cmsg_type != SCM_RIGHTS) {
18894486ae2SPawel Jakub Dawidek 		return (EINVAL);
1898046c499SPawel Jakub Dawidek 	}
19094486ae2SPawel Jakub Dawidek 	bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
1918046c499SPawel Jakub Dawidek 
19294486ae2SPawel Jakub Dawidek 	return (0);
1938046c499SPawel Jakub Dawidek }
19401ab52c0SPawel Jakub Dawidek 
19501ab52c0SPawel Jakub Dawidek int
19601ab52c0SPawel Jakub Dawidek proto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
19701ab52c0SPawel Jakub Dawidek {
19801ab52c0SPawel Jakub Dawidek 	ssize_t done;
19901ab52c0SPawel Jakub Dawidek 
20001ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(sock >= 0);
2013a0b818fSPawel Jakub Dawidek 
2023a0b818fSPawel Jakub Dawidek 	if (data == NULL) {
2033a0b818fSPawel Jakub Dawidek 		/* The caller is just trying to decide about direction. */
2043a0b818fSPawel Jakub Dawidek 
2053a0b818fSPawel Jakub Dawidek 		PJDLOG_ASSERT(size == 0);
2063a0b818fSPawel Jakub Dawidek 
2073a0b818fSPawel Jakub Dawidek 		if (shutdown(sock, SHUT_WR) == -1)
2083a0b818fSPawel Jakub Dawidek 			return (errno);
2093a0b818fSPawel Jakub Dawidek 		return (0);
2103a0b818fSPawel Jakub Dawidek 	}
2113a0b818fSPawel Jakub Dawidek 
21201ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(data != NULL);
21301ab52c0SPawel Jakub Dawidek 	PJDLOG_ASSERT(size > 0);
21401ab52c0SPawel Jakub Dawidek 
21501ab52c0SPawel Jakub Dawidek 	do {
216e0455434SMaxim Sobolev 		done = recv(sock, data, size, MSG_WAITALL);
217e0455434SMaxim Sobolev 	} while (done == -1 && errno == EINTR);
218a7ebb3ebSPawel Jakub Dawidek 	if (done == 0) {
21901ab52c0SPawel Jakub Dawidek 		return (ENOTCONN);
2202b1b224dSPawel Jakub Dawidek 	} else if (done == -1) {
221a7ebb3ebSPawel Jakub Dawidek 		/*
222a7ebb3ebSPawel Jakub Dawidek 		 * If this is blocking socket and we got EAGAIN, this
223a7ebb3ebSPawel Jakub Dawidek 		 * means the request timed out. Translate errno to
224a7ebb3ebSPawel Jakub Dawidek 		 * ETIMEDOUT, to give administrator a hint to
225a7ebb3ebSPawel Jakub Dawidek 		 * eventually increase timeout.
226a7ebb3ebSPawel Jakub Dawidek 		 */
227a7ebb3ebSPawel Jakub Dawidek 		if (errno == EAGAIN && blocking_socket(sock))
228a7ebb3ebSPawel Jakub Dawidek 			errno = ETIMEDOUT;
22901ab52c0SPawel Jakub Dawidek 		return (errno);
230a7ebb3ebSPawel Jakub Dawidek 	}
23101ab52c0SPawel Jakub Dawidek 	if (fdp == NULL)
23201ab52c0SPawel Jakub Dawidek 		return (0);
23301ab52c0SPawel Jakub Dawidek 	return (proto_descriptor_recv(sock, fdp));
23401ab52c0SPawel Jakub Dawidek }
235