132115b10SPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro 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/types.h>
3432115b10SPawel Jakub Dawidek #include <sys/socket.h>
3532115b10SPawel Jakub Dawidek
3632115b10SPawel Jakub Dawidek #include <errno.h>
37a7ebb3ebSPawel Jakub Dawidek #include <fcntl.h>
38a7ebb3ebSPawel Jakub Dawidek #include <stdbool.h>
3932115b10SPawel Jakub Dawidek #include <stdlib.h>
4032115b10SPawel Jakub Dawidek #include <strings.h>
41a7ebb3ebSPawel Jakub Dawidek #include <unistd.h>
4232115b10SPawel Jakub Dawidek
432ec483c5SPawel Jakub Dawidek #include "pjdlog.h"
4432115b10SPawel Jakub Dawidek #include "proto_impl.h"
4532115b10SPawel Jakub Dawidek
4632115b10SPawel Jakub Dawidek /* Maximum size of packet we want to use when sending data. */
4732115b10SPawel Jakub Dawidek #ifndef MAX_SEND_SIZE
4828df1f23SPawel Jakub Dawidek #define MAX_SEND_SIZE 32768
4932115b10SPawel Jakub Dawidek #endif
5032115b10SPawel Jakub Dawidek
51a7ebb3ebSPawel Jakub Dawidek static bool
blocking_socket(int sock)52a7ebb3ebSPawel Jakub Dawidek blocking_socket(int sock)
53a7ebb3ebSPawel Jakub Dawidek {
54a7ebb3ebSPawel Jakub Dawidek int flags;
55a7ebb3ebSPawel Jakub Dawidek
56a7ebb3ebSPawel Jakub Dawidek flags = fcntl(sock, F_GETFL);
57a7ebb3ebSPawel Jakub Dawidek PJDLOG_ASSERT(flags >= 0);
58a7ebb3ebSPawel Jakub Dawidek return ((flags & O_NONBLOCK) == 0);
59a7ebb3ebSPawel Jakub Dawidek }
60a7ebb3ebSPawel Jakub Dawidek
6101ab52c0SPawel Jakub Dawidek static int
proto_descriptor_send(int sock,int fd)6201ab52c0SPawel Jakub Dawidek proto_descriptor_send(int sock, int fd)
638046c499SPawel Jakub Dawidek {
648046c499SPawel Jakub Dawidek unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
658046c499SPawel Jakub Dawidek struct msghdr msg;
668046c499SPawel Jakub Dawidek struct cmsghdr *cmsg;
678046c499SPawel Jakub Dawidek
688046c499SPawel Jakub Dawidek PJDLOG_ASSERT(sock >= 0);
698046c499SPawel Jakub Dawidek PJDLOG_ASSERT(fd >= 0);
708046c499SPawel Jakub Dawidek
718046c499SPawel Jakub Dawidek bzero(&msg, sizeof(msg));
728046c499SPawel Jakub Dawidek bzero(&ctrl, sizeof(ctrl));
738046c499SPawel Jakub Dawidek
748046c499SPawel Jakub Dawidek msg.msg_iov = NULL;
758046c499SPawel Jakub Dawidek msg.msg_iovlen = 0;
768046c499SPawel Jakub Dawidek msg.msg_control = ctrl;
778046c499SPawel Jakub Dawidek msg.msg_controllen = sizeof(ctrl);
788046c499SPawel Jakub Dawidek
798046c499SPawel Jakub Dawidek cmsg = CMSG_FIRSTHDR(&msg);
808046c499SPawel Jakub Dawidek cmsg->cmsg_level = SOL_SOCKET;
818046c499SPawel Jakub Dawidek cmsg->cmsg_type = SCM_RIGHTS;
828046c499SPawel Jakub Dawidek cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
8394486ae2SPawel Jakub Dawidek bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
848046c499SPawel Jakub Dawidek
858046c499SPawel Jakub Dawidek if (sendmsg(sock, &msg, 0) == -1)
868046c499SPawel Jakub Dawidek return (errno);
878046c499SPawel Jakub Dawidek
888046c499SPawel Jakub Dawidek return (0);
898046c499SPawel Jakub Dawidek }
908046c499SPawel Jakub Dawidek
918046c499SPawel Jakub Dawidek int
proto_common_send(int sock,const unsigned char * data,size_t size,int fd)9201ab52c0SPawel Jakub Dawidek proto_common_send(int sock, const unsigned char *data, size_t size, int fd)
9301ab52c0SPawel Jakub Dawidek {
9401ab52c0SPawel Jakub Dawidek ssize_t done;
9501ab52c0SPawel Jakub Dawidek size_t sendsize;
9641bb8514SPawel Jakub Dawidek int errcount = 0;
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);
116a7ebb3ebSPawel Jakub Dawidek if (done == 0) {
11701ab52c0SPawel Jakub Dawidek return (ENOTCONN);
1182b1b224dSPawel Jakub Dawidek } else if (done == -1) {
11901ab52c0SPawel Jakub Dawidek if (errno == EINTR)
12001ab52c0SPawel Jakub Dawidek continue;
12141bb8514SPawel Jakub Dawidek if (errno == ENOBUFS) {
12241bb8514SPawel Jakub Dawidek /*
12341bb8514SPawel Jakub Dawidek * If there are no buffers we retry.
12441bb8514SPawel Jakub Dawidek * After each try we increase delay before the
12541bb8514SPawel Jakub Dawidek * next one and we give up after fifteen times.
12641bb8514SPawel Jakub Dawidek * This gives 11s of total wait time.
12741bb8514SPawel Jakub Dawidek */
12841bb8514SPawel Jakub Dawidek if (errcount == 15) {
12941bb8514SPawel Jakub Dawidek pjdlog_warning("Getting ENOBUFS errors for 11s on send(), giving up.");
13041bb8514SPawel Jakub Dawidek } else {
13141bb8514SPawel Jakub Dawidek if (errcount == 0)
13241bb8514SPawel Jakub Dawidek pjdlog_warning("Got ENOBUFS error on send(), retrying for a bit.");
13341bb8514SPawel Jakub Dawidek errcount++;
13441bb8514SPawel Jakub Dawidek usleep(100000 * errcount);
13541bb8514SPawel Jakub Dawidek continue;
13641bb8514SPawel Jakub Dawidek }
13741bb8514SPawel Jakub Dawidek }
138a7ebb3ebSPawel Jakub Dawidek /*
139a7ebb3ebSPawel Jakub Dawidek * If this is blocking socket and we got EAGAIN, this
140a7ebb3ebSPawel Jakub Dawidek * means the request timed out. Translate errno to
141a7ebb3ebSPawel Jakub Dawidek * ETIMEDOUT, to give administrator a hint to
142a7ebb3ebSPawel Jakub Dawidek * eventually increase timeout.
143a7ebb3ebSPawel Jakub Dawidek */
144a7ebb3ebSPawel Jakub Dawidek if (errno == EAGAIN && blocking_socket(sock))
145a7ebb3ebSPawel Jakub Dawidek errno = ETIMEDOUT;
14601ab52c0SPawel Jakub Dawidek return (errno);
14701ab52c0SPawel Jakub Dawidek }
14801ab52c0SPawel Jakub Dawidek data += done;
14901ab52c0SPawel Jakub Dawidek size -= done;
15001ab52c0SPawel Jakub Dawidek } while (size > 0);
15141bb8514SPawel Jakub Dawidek if (errcount > 0) {
15241bb8514SPawel Jakub Dawidek pjdlog_info("Data sent successfully after %d ENOBUFS error%s.",
15341bb8514SPawel Jakub Dawidek errcount, errcount == 1 ? "" : "s");
15441bb8514SPawel Jakub Dawidek }
15501ab52c0SPawel Jakub Dawidek
15601ab52c0SPawel Jakub Dawidek if (fd == -1)
15701ab52c0SPawel Jakub Dawidek return (0);
15801ab52c0SPawel Jakub Dawidek return (proto_descriptor_send(sock, fd));
15901ab52c0SPawel Jakub Dawidek }
16001ab52c0SPawel Jakub Dawidek
16101ab52c0SPawel Jakub Dawidek static int
proto_descriptor_recv(int sock,int * fdp)16201ab52c0SPawel Jakub Dawidek proto_descriptor_recv(int sock, int *fdp)
1638046c499SPawel Jakub Dawidek {
1648046c499SPawel Jakub Dawidek unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
1658046c499SPawel Jakub Dawidek struct msghdr msg;
1668046c499SPawel Jakub Dawidek struct cmsghdr *cmsg;
1678046c499SPawel Jakub Dawidek
1688046c499SPawel Jakub Dawidek PJDLOG_ASSERT(sock >= 0);
1698046c499SPawel Jakub Dawidek PJDLOG_ASSERT(fdp != NULL);
1708046c499SPawel Jakub Dawidek
1718046c499SPawel Jakub Dawidek bzero(&msg, sizeof(msg));
1728046c499SPawel Jakub Dawidek bzero(&ctrl, sizeof(ctrl));
1738046c499SPawel Jakub Dawidek
1748046c499SPawel Jakub Dawidek msg.msg_iov = NULL;
1758046c499SPawel Jakub Dawidek msg.msg_iovlen = 0;
1768046c499SPawel Jakub Dawidek msg.msg_control = ctrl;
1778046c499SPawel Jakub Dawidek msg.msg_controllen = sizeof(ctrl);
1788046c499SPawel Jakub Dawidek
1798046c499SPawel Jakub Dawidek if (recvmsg(sock, &msg, 0) == -1)
1808046c499SPawel Jakub Dawidek return (errno);
1818046c499SPawel Jakub Dawidek
18294486ae2SPawel Jakub Dawidek cmsg = CMSG_FIRSTHDR(&msg);
1834c13f63cSPawel Jakub Dawidek if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
18401ab52c0SPawel Jakub Dawidek cmsg->cmsg_type != SCM_RIGHTS) {
18594486ae2SPawel Jakub Dawidek return (EINVAL);
1868046c499SPawel Jakub Dawidek }
18794486ae2SPawel Jakub Dawidek bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
1888046c499SPawel Jakub Dawidek
18994486ae2SPawel Jakub Dawidek return (0);
1908046c499SPawel Jakub Dawidek }
19101ab52c0SPawel Jakub Dawidek
19201ab52c0SPawel Jakub Dawidek int
proto_common_recv(int sock,unsigned char * data,size_t size,int * fdp)19301ab52c0SPawel Jakub Dawidek proto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
19401ab52c0SPawel Jakub Dawidek {
19501ab52c0SPawel Jakub Dawidek ssize_t done;
19601ab52c0SPawel Jakub Dawidek
19701ab52c0SPawel Jakub Dawidek PJDLOG_ASSERT(sock >= 0);
1983a0b818fSPawel Jakub Dawidek
1993a0b818fSPawel Jakub Dawidek if (data == NULL) {
2003a0b818fSPawel Jakub Dawidek /* The caller is just trying to decide about direction. */
2013a0b818fSPawel Jakub Dawidek
2023a0b818fSPawel Jakub Dawidek PJDLOG_ASSERT(size == 0);
2033a0b818fSPawel Jakub Dawidek
2043a0b818fSPawel Jakub Dawidek if (shutdown(sock, SHUT_WR) == -1)
2053a0b818fSPawel Jakub Dawidek return (errno);
2063a0b818fSPawel Jakub Dawidek return (0);
2073a0b818fSPawel Jakub Dawidek }
2083a0b818fSPawel Jakub Dawidek
20901ab52c0SPawel Jakub Dawidek PJDLOG_ASSERT(data != NULL);
21001ab52c0SPawel Jakub Dawidek PJDLOG_ASSERT(size > 0);
21101ab52c0SPawel Jakub Dawidek
21201ab52c0SPawel Jakub Dawidek do {
213e0455434SMaxim Sobolev done = recv(sock, data, size, MSG_WAITALL);
214e0455434SMaxim Sobolev } while (done == -1 && errno == EINTR);
215a7ebb3ebSPawel Jakub Dawidek if (done == 0) {
21601ab52c0SPawel Jakub Dawidek return (ENOTCONN);
2172b1b224dSPawel Jakub Dawidek } else if (done == -1) {
218a7ebb3ebSPawel Jakub Dawidek /*
219a7ebb3ebSPawel Jakub Dawidek * If this is blocking socket and we got EAGAIN, this
220a7ebb3ebSPawel Jakub Dawidek * means the request timed out. Translate errno to
221a7ebb3ebSPawel Jakub Dawidek * ETIMEDOUT, to give administrator a hint to
222a7ebb3ebSPawel Jakub Dawidek * eventually increase timeout.
223a7ebb3ebSPawel Jakub Dawidek */
224a7ebb3ebSPawel Jakub Dawidek if (errno == EAGAIN && blocking_socket(sock))
225a7ebb3ebSPawel Jakub Dawidek errno = ETIMEDOUT;
22601ab52c0SPawel Jakub Dawidek return (errno);
227a7ebb3ebSPawel Jakub Dawidek }
22801ab52c0SPawel Jakub Dawidek if (fdp == NULL)
22901ab52c0SPawel Jakub Dawidek return (0);
23001ab52c0SPawel Jakub Dawidek return (proto_descriptor_recv(sock, fdp));
23101ab52c0SPawel Jakub Dawidek }
232