Пример #1
0
/**
 * 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 */
}
Пример #2
0
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 */
}
Пример #3
0
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;
}
Пример #4
0
/**
 * 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;
}
Пример #5
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;
}
Пример #6
0
/**
 * 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;
}
Пример #7
0
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;
}
Пример #8
0
/**
 * 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;
}
Пример #9
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);
        }
    }
}
Пример #10
0
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;
}
Пример #11
0
/**
 * 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;
}
Пример #12
0
/**
 * 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);
		}
	}
}
Пример #13
0
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 */
}
Пример #14
0
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;
}
Пример #15
0
/**
 * 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);
}