/*
 *  udp.c - functions logging UDP datagrams
 *
 *  Copyright (C) 1998-2000 Hugo Haas
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <pthread.h>
#include <grp.h>

#include "defines.h"
#include "udp.h"
#include "netutils.h"
#include "log.h"
#include "filter.h"
#include "configuration.h"
#include "ident.h"

/* Socket */
int udp_socket;

extern unsigned short resolve_protocols;

struct loginfo udp_log;
extern struct loginfo log;

/*
 * Structure of a UDP packet
 */

#define IPHDR (*((struct iphdr *) pkt))
#define UDPHDR (*((struct udphdr *) udppkt))
#define UDP_CAPTURE_LENGTH MAX_IPHDR_LENGTH + sizeof(struct udphdr)

/*
 * log_udppacket & threaded_log_udppacket
 *
 * Log a UDP datagram
 */

void *log_udppacket(__u8 *pkt) {
  char service[SERVICE_LENGTH];
  struct log_info info;
  __u8 *udppkt;

  udppkt = (pkt + (IPHDR.ihl << 2));

  /* Host filter */
  info = do_log(IPHDR.saddr,
                IPHDR.daddr,
                UDPHDR.dest,
		UDPHDR.source, IPPROTO_UDP);
  
  if (info.log == TRUE) {
    char details[DETAILS_LENGTH];
    char remote_host[HOST_LENGTH];
    *details ='\0';
    host_print(remote_host, IPHDR.saddr,
               info.resolve);
    service_lookup("udp", service, UDPHDR.dest);
    if (info.logformat == LOGFORMAT_DETAILED) {
      get_details(details,
                  IPHDR.saddr,
                  UDPHDR.source,
                  IPHDR.daddr,
                  UDPHDR.dest);
    }
    switch (info.logformat) {
    case LOGFORMAT_SHORT:
      udp_log.log(udp_log.level_or_fd, "UDP %s%s - %s",
                  service, IPOPTIONS(pkt), remote_host);
      break;
    case LOGFORMAT_NORMAL:
      udp_log.log(udp_log.level_or_fd, "%s UDP datagram%s from %s",
                  service, IPOPTIONS(pkt), remote_host);
      break;
    case LOGFORMAT_DETAILED:
      udp_log.log(udp_log.level_or_fd, "%s UDP datagram%s from %s%s",
                  service, IPOPTIONS(pkt), remote_host, details);
      break;
    }
  }
  return NULL;
}

#ifdef _MULTITHREAD_
void threaded_log_udppacket(__u8 *pkt) {
  pthread_attr_t attr_t;
  pthread_t t;
  __u8 *image;

  image = (__u8 *) malloc(UDP_CAPTURE_LENGTH);
  memcpy(image, pkt, UDP_CAPTURE_LENGTH);
  pthread_attr_init(&attr_t);
  pthread_attr_setdetachstate(&attr_t, PTHREAD_CREATE_DETACHED);
  pthread_create(&t, &attr_t, log_udppacket, image);
  pthread_attr_destroy(&attr_t);
  free(image);
}
#endif

/*
 * log_udp
 *
 * Main thread logging UDP datagrams
 *
 */

void *log_udp(void *nobody) {
  __u8 pkt[UDP_CAPTURE_LENGTH];

  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

  udp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
  if (udp_socket <= 0) {
	log.log(log.level_or_fd, "FATAL: Unable to open udp raw socket");
    exit(1);
  }

  setgid(((struct passwd *)nobody)->pw_gid);
  initgroups(((struct passwd *)nobody)->pw_name,
	     ((struct passwd *)nobody)->pw_gid);
  setuid(((struct passwd *)nobody)->pw_uid);

  for(;;) {
    if (read(udp_socket, (__u8 *) &pkt, UDP_CAPTURE_LENGTH) == -1) {
	  log.log(log.level_or_fd, "FATAL: Unable to read udp raw socket");
      exit(1);
	}

#ifdef _MULTITHREAD_
    threaded_log_udppacket((__u8 *) &pkt);
#else
    log_udppacket((__u8 *) &pkt);
#endif
  }
  /*@NOTREACHED@*/ 
  return NULL;
}
