/*
** CBLM v3
** $Id: socket.c,v 1.5 2008/02/04 03:27:33 sella Exp $
** Copyright (c) 2004-2007 James M. Sella. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
** 
** Cheap Bastard Latency Monitor v3
*/

static const char rcsid[] = "$Id: socket.c,v 1.5 2008/02/04 03:27:33 sella Exp $";

#include "socket.h"

#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

/* Global variables */
extern struct args_t args;
extern struct prefs_t prefs;

int setup_socket() {
	struct protoent *protocol;
	int s = 0, optval;

	/* Setup transmit socket */
	protocol = (struct protoent*) getprotobyname("udp");
	if (!protocol) {
		syslog(LOG_CRIT, "Failed to get protocol. Verify that /etc/services is readable from current root directory.");

		return -1;
	}

	if ((s = socket(AF_INET, SOCK_DGRAM, protocol->p_proto)) < 0) {
		syslog(LOG_CRIT, "Failed to open socket: %s", strerror(errno));

		return -1;
	}

#ifdef SO_SNDBUF
	optval = SNDBUF_SIZE;
	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt SO_SNDBUF to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef IP_TTL
	optval = prefs.ttl;
	if (setsockopt(s, IPPROTO_IP, IP_TTL, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt IP_TTL to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef IP_DONTFRAGMENT
	if (prefs.fragment == 1) {
		optval = 0;
		if (setsockopt(s, IPPROTO_IP, IP_DONTFRAGMENT, (void*) &optval, sizeof(optval)) < 0) {
			syslog(LOG_CRIT, "Failed to setsockopt IP_DONTFRAGMENT to %d: %s", optval, strerror(errno));
		}
	}
#endif

#ifdef IP_RECVTOS
	optval = 1;
	if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt IP_RECVTOS to %d: %s", optval, strerror(errno));
	}
#endif

	return s;
}

int bind_port(fd_set *fdset) {
	int s = 0, port, optval, len, flags;
	struct protoent *protocol;
	struct sockaddr_in socketaddr;

	protocol = (struct protoent*) getprotobyname("udp");
	if (!protocol) {
		syslog(LOG_CRIT, "Failed to get protocol. Verify that /etc/services is readable from current root directory.");

		return -1;
	}

	if (args.debug >= DEBUG_INTERNAL) {
		syslog(LOG_INFO, "Binding %s:%d", inet_ntoa(args.interface), ntohs(prefs.port));
	}

	/* Zero and setup our sockaddr_in structure. */
	memset(&socketaddr, 0, sizeof(socketaddr));
	socketaddr.sin_family = AF_INET;
	socketaddr.sin_addr = args.interface;
	socketaddr.sin_port = prefs.port;

	if ((s = socket(AF_INET, SOCK_DGRAM, protocol->p_proto)) < 0) {
		syslog(LOG_CRIT, "Failed to open socket: %s", strerror(errno));

		return -1;
	}

#ifdef SO_RCVBUF
	/* Increase the receive buffer. */
	optval = RCVBUF_SIZE;
	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt SO_RCVBUF to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef SO_SNDBUF
	/* Increase the send buffer. */
	optval = SNDBUF_SIZE;
	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt SO_SNDBUF to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef IP_TTL
	optval = prefs.ttl;
	if (setsockopt(s, IPPROTO_IP, IP_TTL, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt IP_TTL to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef IP_DONTFRAGMENT
	if (prefs.fragment == 1) {
		optval = 0;
		if (setsockopt(s, IPPROTO_IP, IP_DONTFRAGMENT, (void*) &optval, sizeof(optval)) < 0) {
			syslog(LOG_CRIT, "Failed to setsockopt IP_DONTFRAGMENT to %d: %s", optval, strerror(errno));
		}
	}
#endif

#ifdef IP_RECVTOS
	optval = 1;
	if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt IP_RECVTOS to %d: %s", optval, strerror(errno));
	}
#endif

#ifdef SO_NO_CHECK /* Linux only feature */
	optval = 1; /* 1 == disable checking of checksum */
	if (setsockopt(s, SOL_SOCKET, SO_NO_CHECK, (void*) &optval, sizeof(optval)) < 0) {
		syslog(LOG_CRIT, "Failed to setsockopt SO_NO_CHECK to %d: %s", optval, strerror(errno));
	}
#endif

	/* Set non-blocking */
	flags = fcntl(s, F_GETFL);
	if (fcntl(s, F_SETFL, flags | O_NONBLOCK) == -1) {
		syslog(LOG_CRIT, "Failed to fcntl O_NONBLOCK: %s", strerror(errno));
	}

	if (bind(s, (struct sockaddr*) &socketaddr, sizeof(socketaddr)) < 0) {
		syslog(LOG_CRIT, "Failed to bind to %s:%d: %s", inet_ntoa(args.interface), ntohs(prefs.port), strerror(errno));
		return -1;
	}

	/* Add new socket to our FDSET */
	FD_ZERO(fdset);
	FD_SET(s, fdset);

	if (args.debug >= DEBUG_INTERNAL) {
		syslog(LOG_INFO, "Bound socket '%d'.", s);
	}

	return s;
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim: noet ts=3 sw=3
*/
