aboutsummaryrefslogtreecommitdiffstats
path: root/recv.c
blob: 831850a35b5a698b28a3872a3f63563579bb8002 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/* See LICENSE file for copyright and license details. */
#include "internal.h"


void
liberror_recv_failed(int fd, void *buf, size_t n, int flags, const char *fname)
{
	const char *desc;
	int saved_errno, val;
	struct timeval tv;
	switch (errno) {
#if defined(EAGAIN)
	case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EAGAIN != EWOULDBLOCK)
	case EWOULDBLOCK:
#endif
#if defined(EAGAIN) || defined(EWOULDBLOCK)
		saved_errno = errno;
		if (flags & MSG_OOB) {
			desc = "Attempting to receive in nonblocking mode but the operation would block, "
			       "attempting to receive with time out and the operation timed out "
			       "or the socket does not support blocking to await out-of-band data";
			if (flags & MSG_DONTWAIT) {
			msg_oob_nonblocking:
				desc = "Attempting to receive in nonblocking mode but the operation would block or"
				       " the socket does not support blocking to await out-of-band data";
			} else if ((val = fcntl(fd, F_GETFL)) < 0) {
				/* Do nothing */
			} else if (val & O_NONBLOCK) {
				goto msg_oob_nonblocking;
			} else if (getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &(socklen_t){(socklen_t)sizeof(tv)}) ||
			           tv.tv_sec || tv.tv_usec) {
				desc = "Attempting to receive with time out and the operation timed out or"
				       " the socket does not support blocking to await out-of-band data";
			} else {
				desc = "The socket does not support blocking to await out-of-band data";
			}
		} else {
			desc = "Attempting to receive in nonblocking mode but the operation would block "
			       "or attempting to receive with time out and the operation timed out";
			if (flags & MSG_DONTWAIT) {
			no_msg_oob_nonblocking:
				desc = "Attempting to receive in nonblocking mode but the operation would block";
			} else if ((val = fcntl(fd, F_GETFL)) < 0) {
				/* Do nothing */
			} else if (val & O_NONBLOCK) {
				goto no_msg_oob_nonblocking;
			} else if (!getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &(socklen_t){(socklen_t)sizeof(tv)}) &&
			           !tv.tv_sec && !tv.tv_usec) {
				desc = "Attempting to receive with time out and the operation timed out";
			}
		}
		errno = saved_errno;
		break;
#endif
	case EBADF:
		if (fd < 0)
			desc = "Negative file descriptor number specified";
		else
			desc = "Unassigned file descriptor number specified";
		break;
	case ECONNREFUSED:
		desc = "The remote host refused to allow the network connection";
		break;
	case ECONNRESET:
		desc = "The connection was forcibly closed by a peer";
		break;
	case EFAULT:
		desc = "Invalid user space address specified in the second argument";
		break;
	case EINTR:
		desc = "System call was interrupted by a signal or machine suspension";
		break;
	case EINVAL:
		if (fd < 0)
			desc = "Negative file descriptor number specified";
		else if ((flags & ~(MSG_PEEK | MSG_WAITALL)) == MSG_OOB)
			desc = "No out-of-band data is available";
		else if (flags & MSG_OOB)
			desc = "No out-of-band data is available or non-existing flag is specified";
		else
			desc = "Non-existing flag is specified";
		break;
	case EIO:
		desc = "An I/O error occurred while reading from or writing to the file system";
		break;
	case ENOBUFS:
		desc = "Insufficient resource available in the system to perform the operation";
		break;
	case ENOMEM:
		desc = "Out of memory";
		break;
	case ENOTCONN:
		desc = "The socket is not connected";
		break;
	case ENOTSOCK:
		desc = "The file descriptor refer to a non-socket file";
		break;
	case EOPNOTSUPP:
		desc = "A flag that is not supported for the socket protocol is specified";
		break;
	case ETIMEDOUT:
		desc = "The connection timed out during connection establishment, "
		       "or due to a transmission timeout on active connection";
		break;
	default:
		desc = "";
		break;
	}
	liberror_libc_set_error_one_file(desc, "recv", "Socket file", fd, fname);
	(void) buf;
	(void) n;
	(void) flags;
}


void
liberror_recv_short(int fd, void *buf, size_t n, int flags, size_t min, size_t max, ssize_t returned, const char *fname)
{
	const char *desc;
	struct liberror_error *error;
	int saved_errno = errno;
	liberror_save_backtrace(NULL);
	if (returned < 0 || (size_t)returned < min) {
		desc = "Received message was shorter than expected";
	} else if ((size_t) returned > max) {
		desc = "Received message was larger than expected";
	} else {
		desc = "Received message was of an unexpected size";
	}
	liberror_set_error(desc, "recv", "liberror-libc", LIBERROR_LIBC_ERRROR_SHORT_READ);
	error = liberror_get_error();
	error->details_type = LIBERROR_DETAILS_ONE_FILE;
	error->details.one_file.fd = fd;
	error->details.one_file.name = fname ? strdup(fname) : NULL;
	error->details.one_file.role = "Socket file";
	errno = saved_errno;
	(void) buf;
	(void) n;
	(void) flags;
}


ssize_t
liberror_recv(int fd, void *buf, size_t n, int flags, const char *fname)
{
	ssize_t r = recv(fd, buf, n, flags);
	if (r >= 0)
		return r;
	liberror_save_backtrace(NULL);
	liberror_recv_failed(fd, buf, n, flags, fname);
	return -1;
}


ssize_t
liberror_recv_require(int fd, void *buf, size_t n, int flags, size_t min, size_t max, const char *fname)
{
	ssize_t r = recv(fd, buf, n, flags);
	if (r >= 0 && (size_t)r >= min && (size_t)r <= max)
		return r;
	liberror_save_backtrace(NULL);
	if (r < 0)
		liberror_recv_failed(fd, buf, n, flags, fname);
	else
		liberror_recv_short(fd, buf, n, flags, min, max, r, fname);
	return -1;
}