/** * Log TX error if unusual. * * @return TRUE if the error was fatal, FALSE if it's a temporary error and * the message needs to be enqueued. */ static bool udp_sched_write_error(const udp_sched_t *us, const gnet_host_t *to, const pmsg_t *mb, const char *func) { (void) us; /* FIXME -- no longer used */ if (is_temporary_error(errno) || ENOBUFS == errno) return FALSE; switch (errno) { /* * The following are probably due to bugs in the libc, but this is in * the same vein as write() failing with -1 whereas errno == 0! Be more * robust against bugs in the components we rely on. --RAM, 09/10/2003 */ case EINPROGRESS: /* Weird, but seen it -- RAM, 07/10/2003 */ { g_warning("%s(to=%s, len=%d) failed with weird errno = %m -- " "assuming EAGAIN", func, gnet_host_to_string(to), pmsg_size(mb)); } break; case EPIPE: case ENOSPC: case ENOMEM: case EINVAL: /* Seen this with "reserved" IP addresses */ #ifdef EDQUOT case EDQUOT: #endif /* EDQUOT */ case EMSGSIZE: /* Message too large */ case EFBIG: case EIO: case EADDRNOTAVAIL: case ECONNABORTED: case ECONNRESET: case ECONNREFUSED: case ENETRESET: case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: case ENOPROTOOPT: case EPROTONOSUPPORT: case ETIMEDOUT: case EACCES: case EPERM: /* * We don't care about lost packets. */ g_warning("%s(): UDP write of %d bytes to %s failed: %m", func, pmsg_size(mb), gnet_host_to_string(to)); break; default: g_critical("%s(): UDP write of %d bytes to %s failed " "with unexpected errno %d: %m", func, pmsg_size(mb), gnet_host_to_string(to), errno); break; } return TRUE; /* Fatal error */ }
static inline int tx_link_write_error(txdrv_t *tx, const char *func) { struct attr *attr = tx->opaque; if (is_temporary_error(errno) || ENOBUFS == errno) return 0; switch (errno) { /* * The following are probably due to bugs in the libc, but this is in * the same vein as write() failing with -1 whereas errno == 0! Be more * robust against bugs in the components we rely on. --RAM, 09/10/2003 */ case EINPROGRESS: /* Weird, but seen it -- RAM, 07/10/2003 */ g_warning("%s(fd=%d) failed with weird errno = %d (%s), " "assuming EAGAIN", func, attr->wio->fd(attr->wio), errno, g_strerror(errno)); return 0; case EPIPE: case ECONNRESET: case ECONNABORTED: tx->flags |= TX_ERROR; attr->cb->eof_remove(tx->owner, _("Write failed: %s"), g_strerror(errno)); return -1; default: { int saved_errno = errno; wrap_io_t *wio = ((struct attr *) tx->opaque)->wio; int fd = wio->fd(wio); g_warning( "%s: write failed on fd #%d with unexpected errno: %d (%s)", func, fd, saved_errno, g_strerror(saved_errno)); errno = saved_errno; } /* FALL THROUGH */ case ENOSPC: #ifdef EDQUOT case EDQUOT: #endif /* EDQUOT */ case EACCES: case EFBIG: case EHOSTDOWN: case EHOSTUNREACH: case EIO: case ENETDOWN: case ENETUNREACH: case ETIMEDOUT: tx->flags |= TX_ERROR; attr->cb->eof_shutdown(tx->owner, _("Write failed: %s"), g_strerror(errno)); return -1; } return 0; /* Just in case */ }
static size_t fill_buffer_from_fd(const int fd, void * const dst, const size_t buf_size) { char *buf = dst; size_t pos = 0; RUNTIME_ASSERT(buf); RUNTIME_ASSERT(buf_size > 0); RUNTIME_ASSERT((size_t) -1 != buf_size); while (pos < buf_size) { ssize_t ret; size_t size; size = buf_size - pos; ret = read(fd, &buf[pos], size); if ((ssize_t) -1 == ret) { if (!is_temporary_error(errno) || wait_for_fd(fd) < 0) { return -1; } } else if (0 == ret) { if (pos != 0) { errno = EIO; return -1; } return 0; /* EOF */ } else { RUNTIME_ASSERT((size_t) ret <= size); pos += (size_t) ret; } } return 1; }
/** * Attempts to fill the shell buffer from the given file descriptor, however, * the buffer is not further filled before it is completely empty. */ static int read_data(int fd, struct shell_buf *sb) { if (!sb) { return -1; } if (0 == sb->fill && sb->readable) { ssize_t ret; ret = unix_read(fd, sb->buf, sb->size); switch (ret) { case 0: sb->eof = 1; break; case -1: if (!is_temporary_error(errno)) { if (sb->server) { perror("read() from server failed"); } else { perror("read() failed"); } return -1; } break; default: sb->fill = ret; } } return 0; }
static inline ssize_t tls_pull(gnutls_transport_ptr ptr, void *buf, size_t size) { struct gnutella_socket *s = ptr; ssize_t ret; int saved_errno; socket_check(s); g_assert(is_valid_fd(s->file_desc)); ret = s_read(s->file_desc, buf, size); saved_errno = errno; tls_signal_pending(s); if ((ssize_t) -1 == ret) { tls_set_errno(s, saved_errno); if (!is_temporary_error(saved_errno)) { socket_connection_reset(s); } } else if (0 == ret) { socket_eof(s); } tls_transport_debug("tls_pull", s, size, ret); errno = saved_errno; return ret; }
/** * Callback function for inputevt_add(). This function pipes the query to * the server using the pipe in non-blocking mode, partial writes are handled * appropriately. In case of an unrecoverable error the query pipe will be * closed and the blocking adns_fallback() will be invoked. */ static void adns_query_callback(void *data, int dest, inputevt_cond_t condition) { adns_async_write_t *remain = data; g_assert(NULL != remain); g_assert(NULL != remain->buf); g_assert(remain->pos < remain->size); g_assert(dest == adns_query_fd); g_assert(0 != adns_query_event_id); if (condition & INPUT_EVENT_EXCEPTION) { g_warning("%s: write exception", G_STRFUNC); goto abort; } while (remain->pos < remain->size) { ssize_t ret; size_t n; n = remain->size - remain->pos; ret = write(dest, &remain->buf[remain->pos], n); if (0 == ret) { errno = ECONNRESET; ret = (ssize_t) -1; } /* FALL THROUGH */ if ((ssize_t) -1 == ret) { if (!is_temporary_error(errno)) goto error; return; } g_assert(ret > 0); g_assert(UNSIGNED(ret) <= n); remain->pos += (size_t) ret; } g_assert(remain->pos == remain->size); inputevt_remove(&adns_query_event_id); goto done; error: g_warning("%s: write() failed: %m", G_STRFUNC); abort: g_warning("%s: removed myself", G_STRFUNC); inputevt_remove(&adns_query_event_id); fd_close(&adns_query_fd); g_warning("%s: using fallback", G_STRFUNC); adns_fallback(&remain->req); done: adns_async_write_free(remain); return; }
static int wait_for_fd(const int fd) { static const struct pollfd zero_fds; struct pollfd fds; int ret; fds = zero_fds; fds.fd = fd; fds.events = POLLIN; do { ret = poll(&fds, 1, -1); } while (0 == ret || (-1 == ret && is_temporary_error(errno))); return ret; }
/** * Attempts to fill the shell buffer using readline(), however, * the buffer is not further filled before it is completely empty. */ static int read_data_with_readline(struct line_buf *line, struct shell_buf *sb) #ifdef USE_READLINE { if (!line || !sb) { return -1; } if (0 == sb->fill) { if (!line->buf) { errno = 0; line->buf = readline(""); if (!line->buf && !is_temporary_error(errno)) { sb->eof = 1; } line->length = line->buf ? vstrlen(line->buf) : 0; line->pos = 0; } if (line->buf) { if (line->pos < line->length) { size_t n; n = line->length - line->pos; if (n > sb->size) { n = sb->size; } memcpy(sb->buf, &line->buf[line->pos], n); sb->fill = n; line->pos += n; } if (line->pos == line->length && sb->fill < sb->size) { sb->buf[sb->fill] = '\n'; sb->fill++; free(line->buf); line->buf = NULL; line->length = 0; line->pos = 0; } } } return 0; }
static inline void tls_transport_debug(const char *op, const struct gnutella_socket *s, size_t size, ssize_t ret) { if ((ssize_t) -1 == ret) { unsigned level = is_temporary_error(errno) ? 2 : 0; if (GNET_PROPERTY(tls_debug) > level) { g_debug("%s(): fd=%d size=%zu host=%s ret=-1 errno=%m", op, s->file_desc, size, host_addr_port_to_string(s->addr, s->port)); } } else { if (GNET_PROPERTY(tls_debug) > 2) { g_debug("%s(): fd=%d size=%zu host=%s ret=%zu", op, s->file_desc, size, host_addr_port_to_string(s->addr, s->port), ret); } } }
static int receive_descriptor(int s) #ifdef HAVE_MSGHDR_ACCRIGHTS { int fd = -1; for (;;) { static const struct msghdr zero_msg; struct msghdr msg; struct iovec iov[1]; char buf[1]; int fd_buf[1]; ssize_t ret; memset(buf, 0, sizeof buf); iov[0].iov_base = buf; iov[0].iov_len = sizeof buf; msg = zero_msg; msg.msg_iov = iov; msg.msg_iovlen = ARRAY_LEN(iov); msg.msg_accrights = cast_to_void_ptr(fd_buf); msg.msg_accrightslen = sizeof fd_buf; ret = recvmsg(s, &msg, 0); if ((ssize_t) -1 == ret) { if (!is_temporary_error(errno)) { debug_error("recvmsg() failed"); break; } } else if (ret > 0) { if (msg.msg_accrights && msg.msg_accrightslen == sizeof fd) memcpy(&fd, msg.msg_accrights, sizeof fd); break; } else { break; } } return fd; }
/** * Transfers the data in `buf' of size `len' through `fd'. If `do_write' is * FALSE the buffer will be filled from `fd'. Otherwise, the data from the * buffer will be written to `fd'. The function returns only if all data * has been transferred or if an unrecoverable error occurs. This function * should only be used with a blocking `fd'. */ static bool adns_do_transfer(int fd, void *buf, size_t len, bool do_write) { ssize_t ret; size_t n = len; while (n > 0) { if (common_dbg > 2) g_debug("%s (%s): n=%zu", G_STRFUNC, do_write ? "write" : "read", n); if (do_write) ret = write(fd, buf, n); else ret = read(fd, buf, n); if ((ssize_t) -1 == ret && !is_temporary_error(errno)) { /* Ignore the failure, if the parent process is gone. This prevents an unnecessary warning when quitting. */ if (!is_helper || getppid() != 1) g_warning("%s (%s): %m", G_STRFUNC, do_write ? "write" : "read"); return FALSE; } else if (0 == ret) { /* * Don't warn on EOF if this is the child process and the * parent is gone. */ if (!do_write && !(is_helper && getppid() == 1)) g_warning("%s (%s): EOF", G_STRFUNC, do_write ? "write" : "read"); return FALSE; } else if (ret > 0) { n -= ret; buf = (char *) buf + ret; } } return TRUE; }
/** * Flush buffered data. */ static void dump_flush(struct dump *dump) { while (dump->fill > 0) { ssize_t written; iovec_t *iov; int iov_cnt; iov = pmsg_slist_to_iovec(dump->slist, &iov_cnt, NULL); written = writev(dump->fd, iov, iov_cnt); HFREE_NULL(iov); if ((ssize_t)-1 == written) { if (!is_temporary_error(errno)) { g_warning("error writing to %s: %s -- disabling dumping", dump->filename, g_strerror(errno)); dump_disable(dump); } if (dump->fill >= 256 * 1024UL) { g_warning( "queue is full: %s -- disabling dumping", dump->filename); dump_disable(dump); } break; } else if (0 == written) { g_warning("error writing to %s: hang up -- disabling dumping", dump->filename); dump_disable(dump); break; } else { g_assert(dump->fill >= (size_t) written); dump->fill -= written; pmsg_slist_discard(dump->slist, written); } } }
static inline int tx_dgram_write_error(txdrv_t *tx, const gnet_host_t *to, const char *func) { if (is_temporary_error(errno) || ENOBUFS == errno) return 0; switch (errno) { /* * The following are probably due to bugs in the libc, but this is in * the same vein as write() failing with -1 whereas errno == 0! Be more * robust against bugs in the components we rely on. --RAM, 09/10/2003 */ case EINPROGRESS: /* Weird, but seen it -- RAM, 07/10/2003 */ { const struct attr *attr = tx->opaque; g_warning("%s(fd=%d) failed with weird errno = %d (%s), " "assuming EAGAIN", func, attr->wio->fd(attr->wio), errno, g_strerror(errno)); } return 0; case EPIPE: case ENOSPC: case ENOMEM: case EINVAL: /* Seen this with "reserved" IP addresses */ #ifdef EDQUOT case EDQUOT: #endif /* EDQUOT */ case EFBIG: case EIO: case EADDRNOTAVAIL: case ECONNABORTED: case ECONNRESET: case ECONNREFUSED: case ENETRESET: case ENETDOWN: case ENETUNREACH: case EHOSTDOWN: case EHOSTUNREACH: case ENOPROTOOPT: case EPROTONOSUPPORT: case ETIMEDOUT: case EACCES: case EPERM: /* * Don't set TX_ERROR here, we don't care about lost packets. */ g_warning("UDP write to %s failed: %s", gnet_host_to_string(to), g_strerror(errno)); return -1; default: { int terr = errno; tx->flags |= TX_ERROR; /* This should be fatal! */ g_error("%s: UDP write to %s failed with unexpected errno: %d (%s)", func, gnet_host_to_string(to), terr, g_strerror(terr)); } } return 0; /* Just in case */ }
static ssize_t tls_read(struct wrap_io *wio, void *buf, size_t size) { struct gnutella_socket *s = wio->ctx; ssize_t ret; socket_check(s); g_assert(socket_uses_tls(s)); g_assert(NULL != buf); g_assert(size_is_positive(size)); if (tls_flush(wio) && !is_temporary_error(errno)) { if (GNET_PROPERTY(tls_debug)) { g_warning("%s(): tls_flush(fd=%d) error: %m", G_STRFUNC, s->file_desc); } return -1; } ret = gnutls_record_recv(tls_socket_get_session(s), buf, size); if (ret < 0) { switch (ret) { case GNUTLS_E_INTERRUPTED: case GNUTLS_E_AGAIN: errno = VAL_EAGAIN; break; case GNUTLS_E_PULL_ERROR: case GNUTLS_E_PUSH_ERROR: /* Logging already done by tls_transport_debug() */ errno = (SOCK_F_CONNRESET & s->flags) ? ECONNRESET : EIO; break; case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: if (SOCK_F_EOF & s->flags) { /* * Remote peer has hung up. * * This is not exceptional, so we make it appear to upper * layers (who do not necessarily know they're dealing with * a TLS socket) as a regular EOF condition: the read() * operation return 0. */ ret = 0; goto no_error; } else if (SOCK_F_CONNRESET & s->flags) { errno = ECONNRESET; break; } /* FALLTHROUGH */ default: if (GNET_PROPERTY(tls_debug)) { g_carp("tls_read(): gnutls_record_recv(fd=%d) failed: " "host=%s error=\"%s\"", s->file_desc, host_addr_port_to_string(s->addr, s->port), gnutls_strerror(ret)); } errno = EIO; } ret = -1; } no_error: if (s->gdk_tag && 0 == s->tls.snarf) { tls_socket_evt_change(s, INPUT_EVENT_RX); } g_assert(ret == (ssize_t) -1 || (size_t) ret <= size); tls_signal_pending(s); return ret; }
/** * Callback function for inputevt_add(). This function invokes the callback * function given in DNS query on the client-side i.e., gtk-gnutella itself. * It handles partial reads if necessary. In case of an unrecoverable error * the reply pipe will be closed and the callback will be lost. */ static void adns_reply_callback(void *data, int source, inputevt_cond_t condition) { static struct adns_response ans; static void *buf; static size_t size, pos; g_assert(NULL == data); g_assert(condition & INPUT_EVENT_RX); /* * Consume all the data available in the pipe, potentially handling * several pending replies. */ for (;;) { ssize_t ret; size_t n; if (pos == size) { pos = 0; if (cast_to_pointer(&ans.common) == buf) { /* * Finished reading the generic reply header, now read * the specific part. */ g_assert(ADNS_COMMON_MAGIC == ans.common.magic); if (ans.common.reverse) { buf = &ans.reply.reverse; size = sizeof ans.reply.reverse; } else { buf = &ans.reply.by_addr; size = sizeof ans.reply.by_addr; } } else { if (buf) { /* * Completed reading the specific part of the reply. * Inform issuer of request by invoking the user callback. */ adns_reply_ready(&ans); } /* * Continue reading the next reply, if any, which will start * by the generic header. */ buf = &ans.common; size = sizeof ans.common; ans.common.magic = 0; } } g_assert(buf); g_assert(size > 0); g_assert(pos < size); n = size - pos; ret = read(source, cast_to_gchar_ptr(buf) + pos, n); if ((ssize_t) -1 == ret) { if (!is_temporary_error(errno)) { g_warning("%s: read() failed: %m", G_STRFUNC); goto error; } break; } else if (0 == ret) { g_warning("%s: read() failed: EOF", G_STRFUNC); goto error; } else { g_assert(ret > 0); g_assert(UNSIGNED(ret) <= n); pos += (size_t) ret; } } return; error: inputevt_remove(&adns_reply_event_id); g_warning("%s: removed myself", G_STRFUNC); fd_close(&source); }