send_message(int sock_fd, SCK_Message *message, int flags)
{
  struct cmsghdr cmsg_buf[CMSG_BUF_SIZE / sizeof (struct cmsghdr)];
  union sockaddr_all saddr;
  socklen_t saddr_len;
  struct msghdr msg;
  struct iovec iov;

  assert(flags == 0);

  switch (message->addr_type) {
    case SCK_ADDR_UNSPEC:
      saddr_len = 0;
      break;
    case SCK_ADDR_IP:
      saddr_len = SCK_IPSockAddrToSockaddr(&message->remote_addr.ip,
                                           (struct sockaddr *)&saddr, sizeof (saddr));
      break;
    case SCK_ADDR_UNIX:
      memset(&saddr, 0, sizeof (saddr));
      if (snprintf(saddr.un.sun_path, sizeof (saddr.un.sun_path), "%s",
                   message->remote_addr.path) >= sizeof (saddr.un.sun_path)) {
        DEBUG_LOG("Unix socket path %s too long", message->remote_addr.path);
        return 0;
      }
      saddr.un.sun_family = AF_UNIX;
      saddr_len = sizeof (saddr.un);
      break;
    default:
      assert(0);
  }

  if (saddr_len) {
    msg.msg_name = &saddr.un;
    msg.msg_namelen = saddr_len;
  } else {
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
  }

  iov.iov_base = message->data;
  iov.iov_len = message->length;
  msg.msg_iov = &iov;
  msg.msg_iovlen = 1;
  msg.msg_control = cmsg_buf;
  msg.msg_controllen = 0;
  msg.msg_flags = 0;

  if (message->addr_type == SCK_ADDR_IP) {
    if (message->local_addr.ip.family == IPADDR_INET4) {
#ifdef HAVE_IN_PKTINFO
      struct in_pktinfo *ipi;

      ipi = add_control_message(&msg, IPPROTO_IP, IP_PKTINFO, sizeof (*ipi),
                                sizeof (cmsg_buf));
      if (!ipi)
        return 0;

      ipi->ipi_spec_dst.s_addr = htonl(message->local_addr.ip.addr.in4);
      if (message->if_index != INVALID_IF_INDEX)
        ipi->ipi_ifindex = message->if_index;

#elif defined(IP_SENDSRCADDR)
      struct in_addr *addr;

      addr = add_control_message(&msg, IPPROTO_IP, IP_SENDSRCADDR, sizeof (*addr),
                                 sizeof (cmsg_buf));
      if (!addr)
        return 0;

      addr->s_addr = htonl(message->local_addr.ip.addr.in4);
#endif
    }

#ifdef HAVE_IN6_PKTINFO
    if (message->local_addr.ip.family == IPADDR_INET6) {
      struct in6_pktinfo *ipi;

      ipi = add_control_message(&msg, IPPROTO_IPV6, IPV6_PKTINFO, sizeof (*ipi),
                                sizeof (cmsg_buf));
      if (!ipi)
        return 0;

      memcpy(&ipi->ipi6_addr.s6_addr, &message->local_addr.ip.addr.in6,
             sizeof(ipi->ipi6_addr.s6_addr));
      if (message->if_index != INVALID_IF_INDEX)
        ipi->ipi6_ifindex = message->if_index;
    }
#endif
  }

#ifdef HAVE_LINUX_TIMESTAMPING
  if (message->timestamp.tx_flags) {
    int *ts_tx_flags;

    /* Set timestamping flags for this message */

    ts_tx_flags = add_control_message(&msg, SOL_SOCKET, SO_TIMESTAMPING,
                                      sizeof (*ts_tx_flags), sizeof (cmsg_buf));
    if (!ts_tx_flags)
      return 0;

    *ts_tx_flags = message->timestamp.tx_flags;
  }
#endif

  /* This is apparently required on some systems */
  if (msg.msg_controllen == 0)
    msg.msg_control = NULL;

  if (sendmsg(sock_fd, &msg, 0) < 0) {
    log_message(sock_fd, -1, message, "Could not send", strerror(errno));
    return 0;
  }

  log_message(sock_fd, -1, message, "Sent", NULL);

  return 1;
}