aboutsummaryrefslogblamecommitdiffstats
path: root/send.c
blob: 62c12a7d71f7b777fc67c01ad5e0ea3de3bbc099 (plain) (tree)















































































































































                                                                                                                               


                     


 








                                                                                                                              
                                            










                                                                                            


                     


 









                                                                              














                                                                                                              
/* See LICENSE file for copyright and license details. */
#include "internal.h"


void
liberror_send_failed(int fd, const void *buf, size_t n, int flags, const char *fname)
{
	const char *desc;
	int saved_errno, val;
	struct sockaddr_storage addr;
	socklen_t len = 0;
	switch (errno) {
	case EACCES:
		desc = "The calling process does not have appropriate privileges";
		saved_errno = errno;
		if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
			/* Do nothing */
		} else if (val == PF_LOCAL) {
			desc = "Write or search permission to the destination socket file is denied";
		} else if (val != PF_INET && val != PF_INET6) {
			/* Do nothing */
		} else if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
			/* Do nothing */
		} else if (val != SOCK_DGRAM) {
			/* Do nothing */
		} else if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
			/* Do nothing */
		} else if (val == IPPROTO_UDP) {
			desc = "An attempt was made to send to a network/broadcast address as though it was a unicast address";
		}
		errno = saved_errno;
		break;
#if defined(EAGAIN)
	case EAGAIN:
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || EAGAIN != EWOULDBLOCK)
	case EWOULDBLOCK:
#endif
#if defined(EAGAIN) || defined(EWOULDBLOCK)
		desc = "Attempting to send in nonblocking mode but the operation would block "
			"or the socket is unbound and all ephemeral ports are currently in used";
# if defined(EAGAIN)
#  if defined(EWOULDBLOCK) && (EAGAIN != EWOULDBLOCK)
		if (errno == EAGAIN) {
#  endif
			saved_errno = errno;
			if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
				/* Do nothing */
			} else if (val != SOCK_DGRAM) {
				goto first_case_in_eagain;
			} else if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
				/* Do nothing */
			} else if (val != PF_INET && val != PF_INET6) {
				goto first_case_in_eagain;
			} else if (len = 0, getsockname(fd, (void *)&addr, &len)) {
				/* Do nothing */
			} else if ((len && addr.ss_family == AF_INET && ((struct sockaddr_in *)&addr)->sin_port) ||
				   (len && addr.ss_family == AF_INET6 && ((struct sockaddr_in6 *)&addr)->sin6_port)) {
			first_case_in_eagain:
				desc = "Attempting to send in nonblocking mode but the operation would block";
			}
			errno = saved_errno;
#  if defined(EWOULDBLOCK) && (EAGAIN != EWOULDBLOCK)
		} else {
			goto first_case_in_eagain:
		}
#  endif
# endif
		break;
#endif
	case EALREADY:
		desc = "Another Fast Open is already in progress";
		break;
	case EBADF:
		if (fd < 0)
			desc = "Negative file descriptor number specified";
		else
			desc = "Unassigned file descriptor number specified";
		break;
	case ECONNRESET:
		desc = "The peer has closed the connection without reading all received messages";
		break;
	case EDESTADDRREQ:
		desc = "The no peer address is set";
		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
			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 EISCONN:
		desc = "The recipient is specified but the socket is already connected";
		break;
	case EMSGSIZE:
		desc = "Message is to large to be sent atomically as required by the socket type";
		break;
	case ENETDOWN:
		desc = "All local network interfaces that can be used to reach the destination are down";
		break;
	case ENETUNREACH:
		desc = "No route to the network it present";
		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 EPIPE:
		desc = "The socket is shut down for writing or is no longer connected";
		saved_errno = errno;
		if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &(socklen_t){(socklen_t)sizeof(val)})) {
			/* Do nothing */
		} else if (val == SOCK_DGRAM) {
			desc = "The socket is shut down";
		} else if (getpeername(fd, (void *)&addr, &len) && errno == ENOTCONN) {
			desc = "The socket is no longer connected";
		}
		errno = saved_errno;
		break;
	default:
		desc = "";
		break;
	}
	liberror_libc_set_error_one_file(desc, "send", "Socket file", fd, fname);
	(void) buf;
	(void) n;
	(void) flags;
}


void
liberror_send_short(int fd, const 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 = "Sent message was shorter than expected";
	} else if ((size_t)returned > max) {
		desc = "Sent message was larger than expected";
	} else {
		desc = "Sent message was of an unexpected size";
	}
	liberror_set_error(desc, "send", "liberror-libc", LIBERROR_LIBC_ERRROR_SHORT_WRITE);
	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_send(int fd, const void *buf, size_t n, int flags, const char *fname)
{
	ssize_t r = send(fd, buf, n, flags);
	if (r >= 0)
		return r;
	liberror_save_backtrace(NULL);
	liberror_send_failed(fd, buf, n, flags, fname);
	return -1;
}


ssize_t
liberror_send_require(int fd, const void *buf, size_t n, int flags, size_t min, size_t max, const char *fname)
{
	ssize_t r = send(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_send_failed(fd, buf, n, flags, fname);
	else
		liberror_send_short(fd, buf, n, flags, min, max, r, fname);
	return -1;
}