+ if (!sa1 || !sa2)
+ return false;
+ if (sa1->sa_family != sa2->sa_family)
+ return false;
+ if (sa1->sa_family == AF_INET) {
+ struct sockaddr_in *a1 = (typeof(a1))sa1,
+ *a2 = (typeof (a2))sa2;
+ return a1->sin_addr.s_addr == a2->sin_addr.s_addr;
+ } else if (sa1->sa_family == AF_INET6) {
+ struct sockaddr_in6 *a1 = (typeof(a1))sa1,
+ *a2 = (typeof (a2))sa2;
+ return !memcmp(a1, a2, sizeof(*a1));
+ } else
+ return false;
+}
+
+/**
+ * Receive data from a file descriptor.
+ *
+ * \param fd The file descriptor.
+ * \param buf The buffer to write the data to.
+ * \param size The size of \a buf.
+ *
+ * Receive at most \a size bytes from file descriptor \a fd.
+ *
+ * \return The number of bytes received on success, negative on errors, zero if
+ * the peer has performed an orderly shutdown.
+ *
+ * \sa recv(2).
+ */
+__must_check int recv_bin_buffer(int fd, char *buf, size_t size)
+{
+ ssize_t n;
+
+ n = recv(fd, buf, size, 0);
+ if (n == -1)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ return n;
+}
+
+/**
+ * Receive and write terminating NULL byte.
+ *
+ * \param fd The file descriptor.
+ * \param buf The buffer to write the data to.
+ * \param size The size of \a buf.
+ *
+ * Read at most \a size - 1 bytes from file descriptor \a fd and
+ * write a NULL byte at the end of the received data.
+ *
+ * \return The return value of the underlying call to \a recv_bin_buffer().
+ *
+ * \sa recv_bin_buffer()
+ */
+int recv_buffer(int fd, char *buf, size_t size)
+{
+ int n;
+
+ assert(size);
+ n = recv_bin_buffer(fd, buf, size - 1);
+ if (n >= 0)
+ buf[n] = '\0';
+ else
+ *buf = '\0';
+ return n;
+}
+
+/**
+ * Wrapper around the accept system call.
+ *
+ * \param fd The listening socket.
+ * \param rfds An optional fd_set pointer.
+ * \param addr Structure which is filled in with the address of the peer socket.
+ * \param size Should contain the size of the structure pointed to by \a addr.
+ * \param new_fd Result pointer.
+ *
+ * Accept incoming connections on \a addr, retry if interrupted. If \a rfds is
+ * not \p NULL, return 0 if \a fd is not set in \a rfds without calling accept().
+ *
+ * \return Negative on errors, zero if no connections are present to be accepted,
+ * one otherwise.
+ *
+ * \sa accept(2).
+ */
+int para_accept(int fd, fd_set *rfds, void *addr, socklen_t size, int *new_fd)
+{
+ int ret;
+
+ if (rfds && !FD_ISSET(fd, rfds))
+ return 0;
+ do
+ ret = accept(fd, (struct sockaddr *) addr, &size);
+ while (ret < 0 && errno == EINTR);
+
+ if (ret >= 0) {
+ *new_fd = ret;
+ return 1;
+ }
+ if (errno == EAGAIN || errno == EWOULDBLOCK)
+ return 0;
+ return -ERRNO_TO_PARA_ERROR(errno);
+}
+
+/**
+ * Probe the list of DCCP CCIDs configured on this host.
+ * \param ccid_array Pointer to return statically allocated array in.
+ * \return Number of elements returned in \a ccid_array or error.
+ *
+ * NB: This feature is only available on Linux > 2.6.30; on older kernels
+ * ENOPROTOOPT ("Protocol not available") will be returned.
+ */
+int dccp_available_ccids(uint8_t **ccid_array)
+{
+ static uint8_t ccids[DCCP_MAX_HOST_CCIDS];
+ socklen_t nccids = sizeof(ccids);
+ int ret, fd;
+
+ ret = fd = makesock(IPPROTO_DCCP, 1, NULL, 0, NULL);
+ if (ret < 0)
+ return ret;
+
+ if (getsockopt(fd, SOL_DCCP, DCCP_SOCKOPT_AVAILABLE_CCIDS,
+ ccids, &nccids) < 0) {
+ ret = errno;
+ close(fd);
+ PARA_ERROR_LOG("No DCCP_SOCKOPT_AVAILABLE_CCIDS: %s\n",
+ strerror(ret));
+ return -ERRNO_TO_PARA_ERROR(ret);
+ }
+
+ close(fd);
+ *ccid_array = ccids;
+ return nccids;