/* 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; }