+static int is_blocked(int error)
+{
+ return EWOULDBLOCK == error
+#ifdef ENOMEM
+ || ENOMEM == error
+#endif
+#ifdef ENOBUFS
+ || ENOBUFS == error
+#endif
+ || EAGAIN == error;
+}
+
+static void sockaddr_in_to_irc(const struct sockaddr_in *v4,
+ struct irc_sockaddr *irc)
+{
+ memset(&irc->addr, 0, 5*sizeof(int16_t));
+ irc->addr.in6_16[5] = 0xffff;
+ memcpy(&irc->addr.in6_16[6], &v4->sin_addr, sizeof(v4->sin_addr));
+ irc->port = ntohs(v4->sin_port);
+}
+
+
+#ifdef IPV6
+/** Native socket address type. */
+#define sockaddr_native sockaddr_in6
+/** Field name inside sockaddr_native to find address family. */
+#define sn_family sin6_family
+
+/** Convert native socket address to IRC format.
+ * @param[in] v6 Native socket address.
+ * @param[out] irc IRC format socket address.
+ */
+void sockaddr_to_irc(const struct sockaddr_in6 *v6, struct irc_sockaddr *irc)
+{
+ if (v6->sin6_family == AF_INET6) {
+ memcpy(&irc->addr.in6_16[0], &v6->sin6_addr, sizeof(v6->sin6_addr));
+ irc->port = ntohs(v6->sin6_port);
+ }
+ else if (v6->sin6_family == AF_INET) {
+ sockaddr_in_to_irc((struct sockaddr_in *)v6, irc);
+ }
+ else assert(0 && "Unhandled native address family");
+}
+
+/** Convert IRC socket address to native format.
+ * @param[out] v6 Native socket address.
+ * @param[in] irc IRC socket address.
+ * @param[in] compat_fd If non-negative, an FD specifying address family.
+ * @return Length of address written to \a v6.
+ */
+int sockaddr_from_irc(struct sockaddr_in6 *v6, const struct irc_sockaddr *irc, int compat_fd, int family)
+{
+ struct sockaddr_in6 sin6;
+ socklen_t slen;
+
+ assert(irc != 0);
+ slen = sizeof(sin6);
+ if (family) {
+ /* accept whatever user specified */
+ } else if ((0 <= compat_fd)
+ && (0 == getsockname(compat_fd, (struct sockaddr*)&sin6, &slen)))
+ family = sin6.sin6_family;
+ else if ((irc == &VirtualHost_v4) || irc_in_addr_is_ipv4(&irc->addr))
+ family = AF_INET;
+ else
+ family = AF_INET6;
+
+ memset(v6, 0, sizeof(*v6));
+ if (family == AF_INET) {
+ struct sockaddr_in *v4 = (struct sockaddr_in*)v6;
+ v4->sin_family = AF_INET;
+ memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
+ v4->sin_port = htons(irc->port);
+ return sizeof(*v4);
+ }
+ else {
+ v6->sin6_family = AF_INET6;
+ memcpy(&v6->sin6_addr, &irc->addr.in6_16[0], sizeof(v6->sin6_addr));
+ v6->sin6_port = htons(irc->port);
+ return sizeof(*v6);
+ }
+}
+
+#else
+#define sockaddr_native sockaddr_in
+#define sn_family sin_family
+#define sockaddr_to_irc sockaddr_in_to_irc
+
+int sockaddr_from_irc(struct sockaddr_in *v4, const struct irc_sockaddr *irc, int compat_fd, int family)
+{
+ assert(irc != 0);
+ memset(v4, 0, sizeof(*v4));
+ v4->sin_family = AF_INET;
+ if (irc) {
+ assert(!irc->addr.in6_16[0] && !irc->addr.in6_16[1] && !irc->addr.in6_16[2] && !irc->addr.in6_16[3] && !irc->addr.in6_16[4] && (!irc->addr.in6_16[5] || irc->addr.in6_16[5] == 0xffff));
+ memcpy(&v4->sin_addr, &irc->addr.in6_16[6], sizeof(v4->sin_addr));
+ v4->sin_port = htons(irc->port);
+ }
+ (void)compat_fd; (void)family;
+ return sizeof(*v4);
+}
+
+#endif
+
+#ifdef DEBUGMODE
+/** Send resource usage information to an enumerator function.
+ * @param[in] cptr Client requesting information.
+ * @param[in] uptime Wall time in seconds since the server started.
+ * @param[in] enumerator Function to call to send a line to \a cptr.
+ * @return Zero if some usage reports could not be sent, non-zero on success.