/*
 * Copyright (c) 2003-2016
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2016\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: netlib.c 2877 2016-05-16 23:01:45Z brachman $";
#endif

#ifdef DSSLIB
#include "dsslib.h"
#else
#include "local.h"
#endif

#include <sys/ioctl.h>

#if defined(DACS_OS_SOLARIS)
#include <sys/filio.h>
#endif

static const char *log_module_name = "netlib";

/* Originally, this was enabled, but it should change to disabled by default. */
static int net_debug_flag = 1;

/*
 * Return the socket type (the second argument to socket(2)) of SD,
 * or -1.
 */
int
net_socket_type(int sd)
{
  int so_type;
  socklen_t so_len;

  so_len = sizeof(so_type);
  if (getsockopt(sd, SOL_SOCKET, SO_TYPE, &so_type, &so_len) == -1) {
	/* Assumed to be an invalid socket type. */
	return(-1);
  }

  return(so_type);
}

int
net_get_debug_flag(void)
{

  return(net_debug_flag);
}

int
net_set_debug_flag(int flag)
{
  int prev_flag;

  prev_flag = net_debug_flag;
  net_debug_flag = flag;

  return(prev_flag);
}

int
net_tcp_nodelay(int sd)
{
  int flag;

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, TCP_NODELAY, &flag, sizeof(flag)) == -1)
	return(-1);

  return(0);
}

int
net_socket_reuseaddr(int sd)
{
  int flag;

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
    return(-1);

  return(0);
}

static int
set_blocking_status(int sd, int block, char **errmsg)
{
  int flags;

  if (sd < 0) {
	if (errmsg != NULL)
		*errmsg = "Bad file descriptor";
	return(-1);
  }

  if ((flags = fcntl(sd, F_GETFL)) == -1) {
	if (errmsg != NULL)
	  *errmsg = ds_xprintf("set_blocking_status fcntl(F_GETFL): %s",
						   strerror(errno));
	return(-1);
  }

  if (block)
	flags &= ~O_NONBLOCK;
  else
	flags |= O_NONBLOCK;

  if (fcntl(sd, F_SETFL, flags) == -1) {
	int en;

	en = errno;
	if (ioctl(sd, FIONBIO, &flags) == -1) {
	  if (errmsg != NULL)
		*errmsg = ds_xprintf("set_blocking_status ioctl(FIONBIO): %s, fcntl(F_SETFL): %s",
							 strerror(errno), strerror(en));
	  return(-1);
	}
  }

  return(0);
}

int
net_set_min_input(int sd, int flag)
{

  if (setsockopt(sd, SOL_SOCKET, SO_RCVLOWAT, &flag, sizeof(flag)) == -1) {
	log_err((LOG_ERROR_LEVEL, "setsockopt SO_RCVLOWAT"));
	return(-1);
  }

  return(0);
}

/*
 * Set the given file descriptor to be non-blocking.
 * Return -1 on error, 0 otherwise.
 */
int
net_set_nonblocking(int sd, char **errmsg)
{

  return(set_blocking_status(sd, 0, errmsg));
}

/*
 * Set the given file descriptor to be blocking.
 * Return -1 on error, 0 otherwise.
 */
int
net_set_blocking(int sd, char **errmsg)
{

  return(set_blocking_status(sd, 1, errmsg));
}

int
net_write(int sd, void *buf, size_t buflen)
{
  char *p;
  ssize_t n;
  size_t nrem;

  nrem = buflen;
  p = buf;
  while (1) {
    n = write(sd, (void *) p, nrem);
    if (n == -1) {
      if (errno == EAGAIN) {
        /* XXX Block until sd is writable? */
        continue;
      }
      return(-1);
    }

    if ((size_t) n == nrem)
      break;

    nrem -= n;
    p += n;
  }

  return(0);
}

int
net_write_str(int sd, char *mesg)
{
  size_t len;

  len = strlen(mesg);
  return(net_write(sd, mesg, len));
}

int
net_write_strln(int sd, char *mesg)
{
  size_t len;

  len = strlen(mesg);
  if (net_write(sd, mesg, len) == -1)
	return(-1);
  return(net_write(sd, "\n", 1));
}

/*
 * Assuming that STR is a domain name optionally followed by
 * a colon and a port number or service name, extract the port number (or
 * service name) as PORT and determine PORTNUM.
 * If an error does not occur, also set HOSTNAME.
 * Any of these arguments can be NULL if their value is not needed.
 * Return 1 if a port number is found, 0 if it is not found, or -1 if it
 * is found but is invalid.
 * If no port component is present and PORTSTR is non-NULL, then PORTSTR
 * is set to NULL.
 */
int
net_parse_hostname_port(char *str, char **hostname, char **portstr,
						in_port_t *portnum)
{
  int rc;
  char *p, *s;
  in_port_t aport;

  s = strdup(str);

  if (portstr != NULL)
	*portstr = NULL;

  rc = 0;
  if ((p = strchr(s, (int) ':')) != NULL) {
	/* Get the port number. */
	*p++ = '\0';
	if ((aport = net_get_service_port(p, NULL, NULL)) == 0)
	  return(-1);

	if (portnum != NULL)
	  *portnum = aport;

	if (portstr != NULL)
	  *portstr = p;

	rc = 1;
  }

  if (hostname != NULL)
	*hostname = s;

  return(rc);
}

/*
 * Create a TCP/IP socket for HOSTNAME:PORT and return a (struct sockaddr_in *)
 * for it, or NULL if an error occurs.
 * XXX The first address returned by getaddrinfo() is used.
 */
struct sockaddr_in *
net_make_sockaddr(char *hostname, in_port_t port)
{
  int gai_code;
  char *port_str;
  struct addrinfo ai_hints, *ai;
  struct sockaddr_in *sai;

  if (hostname == NULL) {
	log_msg((LOG_ERROR_LEVEL, "No hostname available?"));
	return(NULL);
  }

  sai = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
  memset(sai, 0, sizeof(struct sockaddr_in));

  port_str = ds_xprintf("%u", (unsigned int) port);

  memset(&ai_hints, 0, sizeof(ai_hints));
  ai_hints.ai_family = AF_INET;
  ai_hints.ai_socktype = SOCK_STREAM;
  ai_hints.ai_protocol = IPPROTO_TCP;
  ai_hints.ai_flags = AI_CANONNAME;

  ai = NULL;
  gai_code = getaddrinfo(hostname, port_str, &ai_hints, &ai);
  if (gai_code != 0) {
	const char *errmsg;

	log_err((LOG_ERROR_LEVEL, "getaddrinfo: Bad hostname: \"%s\"\n",
			 hostname));
	errmsg = gai_strerror(gai_code);
	log_msg((LOG_ERROR_LEVEL, "getaddrinfo: %s (%d)", errmsg, gai_code));
	return(NULL);
  }
  *sai = *(struct sockaddr_in *) ai->ai_addr;

  freeaddrinfo(ai);

  return(sai);
}

int
net_tcp_connect(struct sockaddr_in *from, struct sockaddr_in *to,
				int *read_fd, int *write_fd)
{
  int flag, sd;

  if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	log_err((LOG_ERROR_LEVEL, "socket"));
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, TCP_NODELAY, &flag, sizeof(flag)) == -1) {
	log_err((LOG_ERROR_LEVEL, "setsockopt TCP_NODELAY"));
	close(sd);
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
	log_err((LOG_ERROR_LEVEL, "setsockopt SO_REUSEADDR"));
	close(sd);
	return(-1);
  }

  if (from != NULL) {
	if (bind(sd, (struct sockaddr *) from, sizeof(struct sockaddr_in)) == -1) {
	  close(sd);
	  log_err((LOG_ERROR_LEVEL, "bind"));
	  return(-1);
	}
  }

  log_msg((LOG_DEBUG_LEVEL, "Connecting to %s",
		   net_sockaddr_name((struct sockaddr *) to,
							 sizeof(struct sockaddr_in), NI_NUMERICSERV)));
  if (connect(sd, (struct sockaddr *) to, sizeof(struct sockaddr_in)) == -1) {
	log_err((LOG_ERROR_LEVEL, "connect"));
	close(sd);
	return(-1);
  }

  if (read_fd != NULL) {
    *read_fd = sd;
    if (write_fd != NULL) {
      if ((*write_fd = dup(sd)) == -1) {
        log_err((LOG_ERROR_LEVEL, "dup"));
        close(sd);
        return(-1);
      }
    }
  }
  else if (write_fd != NULL)
    *write_fd = sd;
  else {
	close(sd);
    return(-1);
  }

  return(0);
}

int
net_connect_to_server_ssl(const char *hostname, in_port_t port,
						  Net_connection_type ct,
						  char *ssl_prog, char *ssl_prog_args,
						  char *ssl_prog_client_crt,
						  char *ssl_prog_ca_crt,
						  int *read_fd, int *write_fd)
{
  int argc, i, rc;
  char **argv, *remote;
  Dsvec *v;

  if (hostname == NULL) {
	log_msg((LOG_ERROR_LEVEL, "No hostname given"));
	return(-1);
  }
  if (port == 0) {
	log_msg((LOG_ERROR_LEVEL, "No port given"));
	return(-1);
  }

  v = dsvec_init(NULL, sizeof(char *));
  dsvec_add_ptr(v, ssl_prog);

  argc = 0;
  if (ssl_prog_args != NULL) {
	static Mkargv conf = { 0, 0, " ", NULL, NULL };

	if ((argc = mkargv(ssl_prog_args, &conf, &argv)) == -1) {
	  log_msg((LOG_ERROR_LEVEL, "Invalid ssl_prog_args argument"));
	  return(-1);
	}
  }

  if (ssl_prog_client_crt != NULL) {
	dsvec_add_ptr(v, (void *) "-ccf");
	dsvec_add_ptr(v, (void *) ssl_prog_client_crt);
  }

  if (ct == NET_SSL_VERIFY) {
	dsvec_add_ptr(v, (void *) "-vt");
	dsvec_add_ptr(v, (void *) "peer");
	dsvec_add_ptr(v, (void *) "-caf");
	dsvec_add_ptr(v, ssl_prog_ca_crt);
  }

  for (i = 0; i < argc; i++)
	dsvec_add_ptr(v, argv[i]);

  remote = ds_xprintf("%s:%u", hostname, port);
  dsvec_add_ptr(v, remote);
  dsvec_add_ptr(v, NULL);

  rc = filterthru((char **) dsvec_base(v), NULL,
				  read_fd, write_fd, NULL, NULL);
  if (rc == -1) {
	log_msg((LOG_ERROR_LEVEL, "SSL filter failed"));
	return(-1);
  }

  return(0);
}

/*
 * Establish a TCP connection to HOSTNAME:PORT returning 0 if successful
 * (setting READ_FD and/or WRITE_FD to its read/write descriptors),
 * -1 otherwise.
 */
int
net_connect_to_server(const char *hostname, in_port_t port,
					  int *read_fd, int *write_fd)
{
  int flag, gai_code, sd;
  char *port_str;
  struct addrinfo ai_hints, *ai;
  struct sockaddr_in *sai;

  if (hostname == NULL) {
	log_msg((LOG_ERROR_LEVEL, "No hostname given"));
	return(-1);
  }

  if (port == 0) {
	log_msg((LOG_ERROR_LEVEL, "No port given"));
	return(-1);
  }

  if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	log_err((LOG_ERROR_LEVEL, "socket"));
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, TCP_NODELAY, &flag, sizeof(flag)) == -1) {
	close(sd);
	log_err((LOG_ERROR_LEVEL, "setsockopt TCP_NODELAY"));
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
	log_err((LOG_ERROR_LEVEL, "setsockopt SO_REUSEADDR"));
	close(sd);
	return(-1);
  }

  port_str = ds_xprintf("%u", (unsigned int) port);

  memset(&ai_hints, 0, sizeof(ai_hints));
  ai_hints.ai_family = AF_INET;
  ai_hints.ai_socktype = SOCK_STREAM;
  ai_hints.ai_protocol = IPPROTO_TCP;
  ai_hints.ai_flags = AI_CANONNAME;

  ai = NULL;
  gai_code = getaddrinfo(hostname, port_str, &ai_hints, &ai);
  if (gai_code != 0) {
	const char *errmsg;

    errmsg = gai_strerror(gai_code);
	log_msg((LOG_ERROR_LEVEL, "getaddrinfo: %s (%d): \"%s\"",
			 errmsg, gai_code, hostname));
	close(sd);
	return(-1);
  }

  sai = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
  memset(sai, 0, sizeof(struct sockaddr_in));
  *sai = *(struct sockaddr_in *) ai->ai_addr;
  freeaddrinfo(ai);

  log_msg((LOG_DEBUG_LEVEL, "Connecting to %s:%s", hostname, port_str));

  if (connect(sd, (struct sockaddr *) sai, sizeof(struct sockaddr_in)) == -1) {
	log_err((LOG_ERROR_LEVEL, "connect"));
	free(sai);
	close(sd);
	return(-1);
  }
  free(sai);

  if (read_fd != NULL) {
	*read_fd = sd;
	if (write_fd != NULL) {
	  if ((*write_fd = dup(sd)) == -1) {
		log_err((LOG_ERROR_LEVEL, "dup"));
		close(sd);
		return(-1);
	  }
	}
  }
  else if (write_fd != NULL)
	*write_fd = sd;
  else {
	close(sd);
	return(-1);
  }

  return(0);
}

/*
 * Test if descriptor SD is ready for reading.
 * If TIMEOUT is NULL, block until it is ready, otherwise wait for an interval
 * of at most TIMEOUT.
 * Return -1 if an error occurs, 0 if the interval expires without SD becoming
 * ready to read, or 1 if SD is ready.
 */
int
net_input_or_timeout(int sd, struct timeval *timeout)
{
  int n;
  fd_set fdset;

  FD_ZERO(&fdset);
  FD_SET(sd, &fdset);

 again:
  if ((n = select(sd + 1, &fdset, NULL, NULL, timeout)) == -1) {
	log_err((LOG_ERROR_LEVEL, "select"));
	if (errno == EINTR) {
	  /* XXX find out how much time remains and try again... */
	  sleep(1);
	  goto again;
	}
	return(-1);
  }

  if (n == 0) {
	log_msg((LOG_TRACE_LEVEL, "Timeout"));
	return(0);
  }

  /* Just in case... */
  if (FD_ISSET(sd, &fdset))
	return(1);

  return(-1);
}

char *
net_addr(struct sockaddr_in *addr, in_port_t *portp)
{
  char *dot_addr;

  dot_addr = strdup(inet_ntoa(addr->sin_addr));

  if (portp != NULL)
	*portp = (in_port_t) ntohs(addr->sin_port);

  return(dot_addr);
}

char *
net_sockaddr_name(struct sockaddr *sa, socklen_t salen, int flags)
{
  char *p;
  char localhost[128], localport[16];

  if (getnameinfo(sa, salen,
				  localhost, sizeof(localhost),
				  localport, sizeof(localport), flags) == 0)
	p = ds_xprintf("%s:%s", localhost, localport);
  else
	p = NULL;

  return(p);
}

/*
 * Return the local address associated with SD, or NULL.
 */
struct sockaddr *
net_socket_laddr(int sd)
{
  struct sockaddr *addr;
  socklen_t namelen;

  addr = ALLOC(struct sockaddr);
  namelen = sizeof(struct sockaddr_in);
  if (getsockname(sd, addr, &namelen) == -1) {
    log_err((LOG_ERROR_LEVEL, "getsockname"));
    return(NULL);
  }

  return(addr);
}

/*
 * Return the remote address associated with SD, or NULL.
 */
struct sockaddr *
net_socket_raddr(int sd)
{
  struct sockaddr *addr;
  socklen_t namelen;

  addr = ALLOC(struct sockaddr);
  namelen = sizeof(struct sockaddr_in);
  if (getpeername(sd, addr, &namelen) == -1) {
    log_err((LOG_ERROR_LEVEL, "getpeername"));
    return(NULL);
  }

  return(addr);
}

char *
net_socket_lname(int sd)
{
  char *sname;
  struct sockaddr_in addr;
  socklen_t namelen;

  namelen = sizeof(struct sockaddr_in);
  if (getsockname(sd, (struct sockaddr *) &addr, &namelen) == -1) {
    log_err((LOG_ERROR_LEVEL, "getsockname"));
    return(NULL);
  }

  sname = net_sockaddr_name((struct sockaddr *) &addr,
							sizeof(struct sockaddr_in),
							NI_NUMERICHOST | NI_NUMERICSERV);

  return(sname);
}

char *
net_socket_rname(int sd)
{
  char *sname;
  struct sockaddr_in addr;
  socklen_t namelen;

  namelen = sizeof(struct sockaddr_in);
  if (getpeername(sd, (struct sockaddr *) &addr, &namelen) == -1) {
    log_err((LOG_ERROR_LEVEL, "getpeername"));
    return(NULL);
  }

  sname = net_sockaddr_name((struct sockaddr *) &addr,
							sizeof(struct sockaddr_in),
							NI_NUMERICHOST | NI_NUMERICSERV);

  return(sname);
}

/*
 * PORTNAME is either a port number, a service name, or NULL.
 * Return the corresponding numeric port number, or zero if an error occurs
 * zero is an invalid port number in this context).
 * The protocol is implicitly TCP.
 * If PORTNAME is NULL, use DEFAULT_PORTNAME instead.
 */
in_port_t
net_get_service_port(char *portname, char *default_portname, char **errmsg)
{
  in_port_t port;

  port = 0;
  if (portname == NULL) {
    if ((portname = default_portname) == NULL)
	  return(0);
  }

  if (is_digit_string(portname)) {
    /* Must be a port number. */
    if (strnum(portname, STRNUM_IN_PORT_T, &port) == -1) {
      if (errmsg != NULL)
        *errmsg = "Invalid port number";
      return(0);
    }
  }
  else {
    struct servent *serv;

    /* Must be a service name, or invalid. */
    if ((serv = getservbyname(portname, "tcp")) == NULL) {
      if (errmsg != NULL)
        *errmsg = ds_xprintf("Can't find TCP service \"%s\"", portname);
      return(0);
    }
    port = htons(serv->s_port);
    endservent();
  }

  return(port);
}

/*
 * Accept a connection on socket SD, but block for at most TIMEOUT
 * (if TIMEOUT is NULL, wait indefinitely).
 * Return the new socket descriptor and set FROM to the identity of the
 * client if successful, -1 if an error occurs (including a timeout).
 */
int
net_accept_or_timeout(int sd, struct sockaddr *from, struct timeval *timeout)
{
  int new_sd, rc;
  socklen_t fromlen;

  if (log_would_log(LOG_TRACE_LEVEL)) {
	char *sname;

	sname = net_socket_lname(sd);
	log_msg((LOG_TRACE_LEVEL, "Waiting to accept connection to %s",
			 (sname == NULL) ? "???" : sname));
  }

  if ((rc = net_input_or_timeout(sd, timeout)) != 1)
	return(-1);

  fromlen = sizeof(struct sockaddr);
  if ((new_sd = accept(sd, from, &fromlen)) == -1) {
	log_err((LOG_ERROR_LEVEL, "accept"));
	return(-1);
  }

  if (log_would_log(LOG_TRACE_LEVEL)) {
	char *sname;

	if ((sname = net_sockaddr_name(from, fromlen,
								   NI_NUMERICHOST | NI_NUMERICSERV)) != NULL)
	  log_msg((LOG_TRACE_LEVEL, "Got new connection from %s", sname));
	else
	  log_msg((LOG_TRACE_LEVEL, "Got new connection from ???"));
  }

  return(new_sd);
}

/*
 * Create and configure a server-side socket for HOSTNAME:PORT.
 * The address bound to the socket depends on the implementation of
 * net_make_sockaddr().
 * If successful, set SDP to the socket descriptor and NAMEP to the socket's
 * (struct sockaddr_in), if NAMEP is non-NULL, and return 0; otherwise,
 * return -1.
 *
 * XXX The backlog argument to listen(2) is zero.
 */
int
net_make_server_socket(char *hostname, in_port_t port,
					   int *sdp, struct sockaddr_in **namep)
{
  int flag, sd;
  char *sname;
  struct sockaddr_in *my_addr;
  socklen_t namelen;

  if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	log_err((LOG_ERROR_LEVEL, "socket"));
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, TCP_NODELAY, &flag, sizeof(flag)) == -1) {
	close(sd);
	log_err((LOG_ERROR_LEVEL, "setsockopt TCP_NODELAY"));
	return(-1);
  }

  flag = 1;
  if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
	close(sd);
	log_err((LOG_ERROR_LEVEL, "setsockopt SO_REUSEADDR"));
	return(-1);
  }

  if ((my_addr = net_make_sockaddr(hostname, port)) == NULL) {
	close(sd);
	return(-1);
  }

  if (bind(sd, (const struct sockaddr *) my_addr, sizeof(struct sockaddr_in))
	  == -1) {
	close(sd);
	log_err((LOG_ERROR_LEVEL, "bind"));
	return(-1);
  }

  if (listen(sd, 0) == -1) {
	close(sd);
	log_err((LOG_ERROR_LEVEL, "listen"));
	return(-1);
  }

  /* This is necessary to find out which local port has been assigned. */
  namelen = sizeof(struct sockaddr_in);
  if (getsockname(sd, (struct sockaddr *) my_addr, &namelen) == -1) {
	close(sd);
    log_err((LOG_ERROR_LEVEL, "getsockname"));
    return(-1);
  }

  if ((sname = net_sockaddr_name((struct sockaddr *) my_addr,
								 sizeof(struct sockaddr_in),
								 NI_NUMERICHOST | NI_NUMERICSERV)) != NULL)
	log_msg((LOG_TRACE_LEVEL, "Created server socket at %s", sname));

  *sdp = sd;
  *namep = my_addr;

  return(0);
}

/*
 * Receive a UDP message sent to socket SD (already bound to an address
 * and port).
 * If MAX_SECS is negative, poll (do not block), if it is zero block
 * indefinitely, and if it is positive wait for up to that many seconds
 * before timing out.
 * The message is put in BUFP, the size in bytes of which is BUFLEN;
 * BUFLEN is set to the actual length of the received message.
 * The identity of the sender is returned in FROM and its size is FROMLEN.
 *
 * Return 0 if no message is available to be read, -1 if an error occurs,
 * or 1 if a message is returned.
 */
int
net_udp_recv(int sd, int max_secs, unsigned char *bufp, size_t *buflen,
			 struct sockaddr *from, socklen_t *fromlen)
{
  int max_fds, st;
  struct timeval timeout, *tv;
  fd_set readfds;

  if (log_would_log(LOG_DEBUG_LEVEL)) {
	char *xxx;

	xxx = net_socket_lname(sd);
	log_msg((LOG_DEBUG_LEVEL, "Waiting for datagram on %s", xxx));
  }

  FD_ZERO(&readfds);
  FD_SET(sd, &readfds);

  if (max_secs < 0)
	tv = NULL;
  else {
	timeout.tv_usec = 0;
	timeout.tv_sec = (time_t) max_secs;
	tv = &timeout;
  }

  max_fds = sd;
  if ((st = select(max_fds + 1, &readfds, NULL, NULL, tv)) == -1) {
	if (st == -1) {
	  log_err((LOG_ERROR_LEVEL, "select"));
	  return(-1);
	}
	if (st == 0)
	  return(0);
  }

  if (FD_ISSET(sd, &readfds)) {
	ssize_t len;

	*fromlen = sizeof(struct sockaddr);
	len = recvfrom(sd, bufp, *buflen, 0, from, fromlen);
	if (len == -1) {
	  log_err((LOG_ERROR_LEVEL, "recvfrom"));
	  return(-1);
	}
	*buflen = (size_t) len;
	log_msg((LOG_TRACE_LEVEL, "Read %u bytes [UDP]", (unsigned int) len));
  }
  else
	return(0);

  return(1);
}

/*
 * Send the DATALEN byte UDP message in DATAP using socket SD.
 * If TO is NULL, SD has been connected to its destination address, otherwise
 * use TO.
 * Return 0 if ok, otherwise -1.
 */
int
net_udp_send(int sd, struct sockaddr_in *to, unsigned char *datap,
			 size_t datalen)
{
  char *str;
  in_port_t port;
  ssize_t len;

  if (to == NULL) {
	if (log_would_log(LOG_TRACE_LEVEL)) {
	  char *xxx, *yyy;

	  xxx = net_socket_lname(sd);
	  yyy = net_socket_rname(sd);
	  log_msg((LOG_TRACE_LEVEL, "Sending datagram from %s to %s", xxx, yyy));
	}

	len = send(sd, datap, datalen, 0);
  }
  else {
	if (log_would_log(LOG_TRACE_LEVEL)) {
	  char *yyy;

	  yyy = net_sockaddr_name((struct sockaddr *) to, sizeof(struct sockaddr_in),
							  NI_NAMEREQD | NI_NUMERICSERV);
	  log_msg((LOG_TRACE_LEVEL, "Sending datagram to %s", yyy));
	}

	len = sendto(sd, datap, datalen, 0, (struct sockaddr *) to,
				 sizeof(struct sockaddr_in));
  }

  if (len != datalen) {
	if (len == -1) {
	  log_err((LOG_ERROR_LEVEL, "sendto"));
	  return(-1);
	}

	log_msg((LOG_ERROR_LEVEL, "sendto: incomplete write"));
	return(-1);
  }

  log_msg((LOG_TRACE_LEVEL, "Wrote %u bytes [UDP]", (unsigned int) len));

  return(0);
}
