proto_common.c revision 220272
1173444Sups/*-
2173444Sups * Copyright (c) 2009-2010 The FreeBSD Foundation
3173444Sups * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4173444Sups * All rights reserved.
5173444Sups *
6173444Sups * This software was developed by Pawel Jakub Dawidek under sponsorship from
7173444Sups * the FreeBSD Foundation.
8173444Sups *
9173444Sups * Redistribution and use in source and binary forms, with or without
10173444Sups * modification, are permitted provided that the following conditions
11173444Sups * are met:
12173444Sups * 1. Redistributions of source code must retain the above copyright
13173444Sups *    notice, this list of conditions and the following disclaimer.
14173444Sups * 2. Redistributions in binary form must reproduce the above copyright
15173444Sups *    notice, this list of conditions and the following disclaimer in the
16173444Sups *    documentation and/or other materials provided with the distribution.
17173444Sups *
18173444Sups * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19173444Sups * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20173444Sups * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21173444Sups * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22173444Sups * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23173444Sups * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24173444Sups * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25173444Sups * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26173444Sups * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27173444Sups * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28173444Sups * SUCH DAMAGE.
29173444Sups */
30173444Sups
31173444Sups#include <sys/cdefs.h>
32173444Sups__FBSDID("$FreeBSD: head/sbin/hastd/proto_common.c 220272 2011-04-02 09:29:53Z pjd $");
33173444Sups
34173444Sups#include <sys/types.h>
35173444Sups#include <sys/socket.h>
36212112Smlaier
37173444Sups#include <errno.h>
38173444Sups#include <fcntl.h>
39173444Sups#include <stdbool.h>
40173444Sups#include <stdlib.h>
41173444Sups#include <strings.h>
42193030Srwatson#include <unistd.h>
43247588Sjhb
44193030Srwatson#include "pjdlog.h"
45193030Srwatson#include "proto_impl.h"
46193030Srwatson
47212112Smlaier/* Maximum size of packet we want to use when sending data. */
48323870Smarius#ifndef MAX_SEND_SIZE
49193030Srwatson#define	MAX_SEND_SIZE	32768
50193030Srwatson#endif
51193030Srwatson
52173444Supsstatic bool
53227588Spjdblocking_socket(int sock)
54173444Sups{
55193030Srwatson	int flags;
56173444Sups
57173444Sups	flags = fcntl(sock, F_GETFL);
58173444Sups	PJDLOG_ASSERT(flags >= 0);
59212112Smlaier	return ((flags & O_NONBLOCK) == 0);
60212112Smlaier}
61173444Sups
62193026Srwatsonstatic int
63173444Supsproto_descriptor_send(int sock, int fd)
64173444Sups{
65173444Sups	unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
66212112Smlaier	struct msghdr msg;
67212112Smlaier	struct cmsghdr *cmsg;
68173444Sups
69252209Sjhb	PJDLOG_ASSERT(sock >= 0);
70252209Sjhb	PJDLOG_ASSERT(fd >= 0);
71252209Sjhb
72252209Sjhb	bzero(&msg, sizeof(msg));
73173444Sups	bzero(&ctrl, sizeof(ctrl));
74173444Sups
75173444Sups	msg.msg_iov = NULL;
76173444Sups	msg.msg_iovlen = 0;
77173444Sups	msg.msg_control = ctrl;
78173444Sups	msg.msg_controllen = sizeof(ctrl);
79173444Sups
80173444Sups	cmsg = CMSG_FIRSTHDR(&msg);
81173444Sups	cmsg->cmsg_level = SOL_SOCKET;
82173444Sups	cmsg->cmsg_type = SCM_RIGHTS;
83173444Sups	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
84173444Sups	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
85212112Smlaier
86212112Smlaier	if (sendmsg(sock, &msg, 0) == -1)
87212112Smlaier		return (errno);
88173444Sups
89173444Sups	return (0);
90173444Sups}
91212112Smlaier
92212112Smlaierint
93212112Smlaierproto_common_send(int sock, const unsigned char *data, size_t size, int fd)
94212112Smlaier{
95212112Smlaier	ssize_t done;
96173444Sups	size_t sendsize;
97252209Sjhb
98252209Sjhb	PJDLOG_ASSERT(sock >= 0);
99252209Sjhb
100173444Sups	if (data == NULL) {
101173444Sups		/* The caller is just trying to decide about direction. */
102173444Sups
103173444Sups		PJDLOG_ASSERT(size == 0);
104193030Srwatson
105193030Srwatson		if (shutdown(sock, SHUT_RD) == -1)
106193030Srwatson			return (errno);
107193030Srwatson		return (0);
108193030Srwatson	}
109173444Sups
110173444Sups	PJDLOG_ASSERT(data != NULL);
111173444Sups	PJDLOG_ASSERT(size > 0);
112193030Srwatson
113173444Sups	do {
114173444Sups		sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE;
115173444Sups		done = send(sock, data, sendsize, MSG_NOSIGNAL);
116173444Sups		if (done == 0) {
117173444Sups			return (ENOTCONN);
118173444Sups		} else if (done < 0) {
119173444Sups			if (errno == EINTR)
120173444Sups				continue;
121173444Sups			/*
122193030Srwatson			 * If this is blocking socket and we got EAGAIN, this
123193030Srwatson			 * means the request timed out. Translate errno to
124193030Srwatson			 * ETIMEDOUT, to give administrator a hint to
125193030Srwatson			 * eventually increase timeout.
126193030Srwatson			 */
127193030Srwatson			if (errno == EAGAIN && blocking_socket(sock))
128193030Srwatson				errno = ETIMEDOUT;
129193030Srwatson			return (errno);
130193030Srwatson		}
131193030Srwatson		data += done;
132193030Srwatson		size -= done;
133193030Srwatson	} while (size > 0);
134252209Sjhb
135252209Sjhb	if (fd == -1)
136252209Sjhb		return (0);
137252209Sjhb	return (proto_descriptor_send(sock, fd));
138252209Sjhb}
139252209Sjhb
140252209Sjhbstatic int
141252209Sjhbproto_descriptor_recv(int sock, int *fdp)
142252209Sjhb{
143252209Sjhb	unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
144252209Sjhb	struct msghdr msg;
145252209Sjhb	struct cmsghdr *cmsg;
146252209Sjhb
147252209Sjhb	PJDLOG_ASSERT(sock >= 0);
148252209Sjhb	PJDLOG_ASSERT(fdp != NULL);
149173444Sups
150173444Sups	bzero(&msg, sizeof(msg));
151	bzero(&ctrl, sizeof(ctrl));
152
153	msg.msg_iov = NULL;
154	msg.msg_iovlen = 0;
155	msg.msg_control = ctrl;
156	msg.msg_controllen = sizeof(ctrl);
157
158	if (recvmsg(sock, &msg, 0) == -1)
159		return (errno);
160
161	cmsg = CMSG_FIRSTHDR(&msg);
162	if (cmsg->cmsg_level != SOL_SOCKET ||
163	    cmsg->cmsg_type != SCM_RIGHTS) {
164		return (EINVAL);
165	}
166	bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
167
168	return (0);
169}
170
171int
172proto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
173{
174	ssize_t done;
175
176	PJDLOG_ASSERT(sock >= 0);
177
178	if (data == NULL) {
179		/* The caller is just trying to decide about direction. */
180
181		PJDLOG_ASSERT(size == 0);
182
183		if (shutdown(sock, SHUT_WR) == -1)
184			return (errno);
185		return (0);
186	}
187
188	PJDLOG_ASSERT(data != NULL);
189	PJDLOG_ASSERT(size > 0);
190
191	do {
192		done = recv(sock, data, size, MSG_WAITALL);
193	} while (done == -1 && errno == EINTR);
194	if (done == 0) {
195		return (ENOTCONN);
196	} else if (done < 0) {
197		/*
198		 * If this is blocking socket and we got EAGAIN, this
199		 * means the request timed out. Translate errno to
200		 * ETIMEDOUT, to give administrator a hint to
201		 * eventually increase timeout.
202		 */
203		if (errno == EAGAIN && blocking_socket(sock))
204			errno = ETIMEDOUT;
205		return (errno);
206	}
207	if (fdp == NULL)
208		return (0);
209	return (proto_descriptor_recv(sock, fdp));
210}
211