process_header(struct msghdr *msg, unsigned int msg_length, int sock_fd, SCK_Message *message)
{
  struct cmsghdr *cmsg;

  if (msg->msg_iovlen != 1) {
    DEBUG_LOG("Unexpected iovlen");
    return 0;
  }

  if (msg->msg_namelen > sizeof (union sockaddr_all)) {
    DEBUG_LOG("Truncated source address");
    return 0;
  }

  if (msg->msg_namelen > sizeof (((struct sockaddr *)msg->msg_name)->sa_family)) {
    switch (((struct sockaddr *)msg->msg_name)->sa_family) {
      case AF_INET:
#ifdef FEAT_IPV6
      case AF_INET6:
#endif
        init_message_addresses(message, SCK_ADDR_IP);
        SCK_SockaddrToIPSockAddr(msg->msg_name, msg->msg_namelen, &message->remote_addr.ip);
        break;
      case AF_UNIX:
        init_message_addresses(message, SCK_ADDR_UNIX);
        message->remote_addr.path = ((struct sockaddr_un *)msg->msg_name)->sun_path;
        break;
      default:
        DEBUG_LOG("Unexpected address");
        return 0;
    }
  } else {
    init_message_addresses(message, SCK_ADDR_UNSPEC);
  }

  init_message_nonaddress(message);

  message->data = msg->msg_iov[0].iov_base;
  message->length = msg_length;

  if (msg->msg_flags & MSG_TRUNC) {
    log_message(sock_fd, 1, message, "Truncated", NULL);
    return 0;
  }

  if (msg->msg_flags & MSG_CTRUNC) {
    log_message(sock_fd, 1, message, "Truncated cmsg in", NULL);
    return 0;
  }

  for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
#ifdef HAVE_IN_PKTINFO
    if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
      struct in_pktinfo ipi;

      if (message->addr_type != SCK_ADDR_IP)
        init_message_addresses(message, SCK_ADDR_IP);

      memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi));
      message->local_addr.ip.addr.in4 = ntohl(ipi.ipi_addr.s_addr);
      message->local_addr.ip.family = IPADDR_INET4;
      message->if_index = ipi.ipi_ifindex;
    }
#elif defined(IP_RECVDSTADDR)
    if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
      struct in_addr addr;

      if (message->addr_type != SCK_ADDR_IP)
        init_message_addresses(message, SCK_ADDR_IP);

      memcpy(&addr, CMSG_DATA(cmsg), sizeof (addr));
      message->local_addr.ip.addr.in4 = ntohl(addr.s_addr);
      message->local_addr.ip.family = IPADDR_INET4;
    }
#endif

#ifdef HAVE_IN6_PKTINFO
    if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
      struct in6_pktinfo ipi;

      if (message->addr_type != SCK_ADDR_IP)
        init_message_addresses(message, SCK_ADDR_IP);

      memcpy(&ipi, CMSG_DATA(cmsg), sizeof (ipi));
      memcpy(&message->local_addr.ip.addr.in6, &ipi.ipi6_addr.s6_addr,
             sizeof (message->local_addr.ip.addr.in6));
      message->local_addr.ip.family = IPADDR_INET6;
      message->if_index = ipi.ipi6_ifindex;
    }
#endif

#ifdef SCM_TIMESTAMP
    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
      struct timeval tv;

      memcpy(&tv, CMSG_DATA(cmsg), sizeof (tv));
      UTI_TimevalToTimespec(&tv, &message->timestamp.kernel);
    }
#endif

#ifdef SCM_TIMESTAMPNS
    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
      memcpy(&message->timestamp.kernel, CMSG_DATA(cmsg), sizeof (message->timestamp.kernel));
    }
#endif

#ifdef HAVE_LINUX_TIMESTAMPING
#ifdef HAVE_LINUX_TIMESTAMPING_OPT_PKTINFO
    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING_PKTINFO) {
      struct scm_ts_pktinfo ts_pktinfo;

      memcpy(&ts_pktinfo, CMSG_DATA(cmsg), sizeof (ts_pktinfo));
      message->timestamp.if_index = ts_pktinfo.if_index;
      message->timestamp.l2_length = ts_pktinfo.pkt_length;
    }
#endif

    if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) {
      struct scm_timestamping ts3;

      memcpy(&ts3, CMSG_DATA(cmsg), sizeof (ts3));
      message->timestamp.kernel = ts3.ts[0];
      message->timestamp.hw = ts3.ts[2];
    }

    if ((cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR) ||
        (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_RECVERR)) {
      struct sock_extended_err err;

      memcpy(&err, CMSG_DATA(cmsg), sizeof (err));

      if (err.ee_errno != ENOMSG || err.ee_info != SCM_TSTAMP_SND ||
          err.ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
        log_message(sock_fd, 1, message, "Unexpected extended error in", NULL);
        return 0;
      }
    }
#endif
  }

  return 1;
}