/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of either:
 * 
 *   a) The GNU Lesser General Public License as published by the Free
 *      Software Foundation; either version 2.1, or (at your option) any
 *      later version,
 * 
 *   OR
 * 
 *   b) The two-clause BSD license.
 *
 * These licenses can be found with the distribution in the file LICENSES
 *
 *
 *
 * This program is really a badly smashed together copy of spfquery.c and
 * the public domain "helloserver" example daemon.
 *
 * The original helloserver code contained the following copyright notice:
 *
 * HELLOSERVER.C - a 'Hello World' TCP/IP based server daemon
 *
 * Implements a skeleton of a single process iterative server
 * daemon.
 *
 * Wherever possible the code adheres to POSIX.
 *
 * David Gillies <daggillies@yahoo.com> Sep 2003
 *
 * Placed in the public domain. Unrestricted use or modification
 * of this code is permitted without attribution to the author.
 */


#ifdef __GNUC__
#define _GNU_SOURCE /* for strsignal() */
#endif

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef STDC_HEADERS
# include <stdio.h>
# include <stdlib.h>       /* malloc / free */
# include <stddef.h>
# include <stdarg.h>
#endif

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>    /* types (u_char .. etc..) */
#endif

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif

#ifdef HAVE_STRING_H
# include <string.h>       /* strstr / strdup */
#else
# ifdef HAVE_STRINGS_H
#  include <strings.h>       /* strstr / strdup */
# endif
#endif

#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>   /* inet_ functions / structs */
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>   /* inet_ functions / structs */
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>    /* in_addr struct */
#endif

#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h> /* DNS HEADER struct */
#endif

#include <sys/types.h>

#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#ifdef HAVE_GRP_H
#include <grp.h>
#endif

#ifdef HAVE_GETOPT_LONG_ONLY
#define _GNU_SOURCE
#include <getopt.h>
#else
#include "libreplace/getopt.h"
#endif

#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <ctype.h>
#include <sys/wait.h>

#include <pthread.h>

#include "spf.h"
#include "spf_dns.h"
#include "spf_dns_null.h"
#include "spf_dns_resolv.h"
#include "spf_dns_test.h"
#include "spf_dns_cache.h"


#define TRUE 1
#define FALSE 0

#define bool int

#define FREE(x, f) do { if ((x)) (f)((x)); (x) = NULL; } while(0)
#define FREE_REQUEST(x) FREE((x), SPF_request_free)
#define FREE_RESPONSE(x) FREE((x), SPF_response_free)
#define FREE_STRING(x) FREE((x), free)

typedef
struct _config_t {
	int		 tcpport;
	int		 udpport;
	char	*path;
#ifdef HAVE_PWD_H
	uid_t	 pathuser;
#endif
#ifdef HAVE_GRP_H
	gid_t	 pathgroup;
#endif
	int		 pathmode;
#ifdef HAVE_PWD_H
	uid_t	 setuser;
#endif
#ifdef HAVE_GRP_H
	gid_t	 setgroup;
#endif

	int		 debug;
	bool	 sec_mx;
	char	*fallback;

	char	*rec_dom;
	bool	 sanitize;
	int		 max_lookup;
	char	*localpolicy;
	bool	 use_trusted;
	char	*explanation;
} config_t;

typedef
struct _request_t {
	int		 sock;
	union {
		struct sockaddr_in	in;
		struct sockaddr_un	un;
	} addr;
	int		 addrlen;
	char	*data;
	int		 datalen;

	char	*ip;
	char	*helo;
	char	*sender;
	char	*rcpt_to;

	SPF_errcode_t	 spf_err;
	SPF_request_t	*spf_request;
	SPF_response_t	*spf_response;

	char	 fmt[4096];
	int		 fmtlen;
} request_t;

typedef
struct _state_t {
	int	sock_udp;
	int	sock_tcp;
	int	sock_unix;
} state_t;

static SPF_server_t	*spf_server;
static config_t		 spfd_config;
static state_t		 spfd_state;

static void
response_print_errors(const char *context,
				SPF_response_t *spf_response, SPF_errcode_t err)
{
	SPF_error_t	*spf_error;
	int			 i;

	if (context != NULL)
		printf("Context: %s\n", context);
	if (err != SPF_E_SUCCESS)
		printf("ErrorCode: (%d) %s\n", err, SPF_strerror(err));

	if (spf_response != NULL) {
		for (i = 0; i < SPF_response_messages(spf_response); i++) {
			spf_error = SPF_response_message(spf_response, i);
			printf( "%s: %s%s\n",
					SPF_error_errorp(spf_error) ? "Error" : "Warning",
					((SPF_error_errorp(spf_error) && (!err))
							? "[UNRETURNED] "
							: ""),
					SPF_error_message(spf_error) );
		}
	}
	else {
		printf("Error: libspf2 gave a NULL spf_response");
	}
}

static void
response_print(const char *context, SPF_response_t *spf_response)
{
	printf("--vv--\n");
	printf("Context: %s\n", context);
	if (spf_response == NULL) {
		printf("NULL RESPONSE!\n");
	}
	else {
		printf("Response result: %s\n",
					SPF_strresult(SPF_response_result(spf_response)));
		printf("Response reason: %s\n",
					SPF_strreason(SPF_response_reason(spf_response)));
		printf("Response err: %s\n",
					SPF_strerror(SPF_response_errcode(spf_response)));
		response_print_errors(NULL, spf_response,
						SPF_response_errcode(spf_response));
	}
	printf("--^^--\n");
}

static const char *
request_check(request_t *req)
{
	const char	*msg = NULL;
	if (!req->ip)
		msg = "No IP address given";
	else if (!req->sender)
		msg = "No sender address given";
	else
		return NULL;
	snprintf(req->fmt, 4095,
		"result=unknown\n"
		"reason=%s\n",
		msg);
	return msg;
}

static void
request_query(request_t *req)
{
	SPF_request_t	*spf_request = NULL;
	SPF_response_t	*spf_response = NULL;
	SPF_response_t	*spf_response_2mx = NULL;
	SPF_errcode_t	 err;
	char			*p, *p_end;

#define UNLESS(x) err = (x); if (err)
// #define FAIL(x) do { response_print_errors((x), spf_response, err); goto fail; } while(0)
#define FAIL(x) do { goto fail; } while(0)
#define WARN(x, r) response_print_errors((x), (r), err)

	spf_request = SPF_request_new(spf_server);

	if (strchr(req->ip, ':')) {
		UNLESS(SPF_request_set_ipv6_str(spf_request, req->ip)) {
			FAIL("Setting IPv6 address");
		}
	}
	else {
		UNLESS(SPF_request_set_ipv4_str(spf_request, req->ip)) {
			FAIL("Setting IPv4 address");
		}
	}

	if (req->helo) {
		UNLESS(SPF_request_set_helo_dom(spf_request, req->helo)) {
			FAIL("Failed to set HELO domain");
		}
		/* XXX Set some flag saying to query on helo */
	}

	if (req->sender) {
		UNLESS(SPF_request_set_env_from(spf_request, req->sender)) {
			FAIL("Failed to set envelope-from address");
		}
		/* XXX Set some flag saying to query on sender */
	}

	/* XXX If flag not set, FAIL() */

	UNLESS(SPF_request_query_mailfrom(spf_request, &spf_response)) {
		FAIL("Failed to query based on mail-from address");
	}

	if (spfd_config.sec_mx) {
		if (req->rcpt_to && *req->rcpt_to) {
			p = req->rcpt_to;
			p_end = p + strcspn(p, " ,;");
			while (SPF_response_result(spf_response)!=SPF_RESULT_PASS) {
				if (*p_end)
					*p_end = '\0';
				else
					p_end = NULL;   /* Note this is last rcpt */
				UNLESS(SPF_request_query_rcptto(spf_request,
								&spf_response_2mx, p)) {
					WARN("Failed to query based on 2mx recipient",
									spf_response_2mx);
					FREE_RESPONSE(spf_response_2mx);
				}
				else {
					spf_response = SPF_response_combine(spf_response,
									spf_response_2mx);
					spf_response_2mx = NULL;	/* freed */
				}

				if (!p_end)
					break;
				p = p_end + 1;
			}
		}
	}

	if (spfd_config.fallback) {
		UNLESS(SPF_request_query_fallback(spf_request,
						&spf_response, spfd_config.fallback)) {
			FAIL("Querying fallback record");
		}
	}

	goto ok;

fail:
	req->spf_err = err;
	FREE_RESPONSE(spf_response);
	FREE_REQUEST(spf_request);

ok:
	// response_print("Result: ", spf_response);

	req->spf_response = spf_response;
	req->spf_request = spf_request;
}

/* This is needed on HP/UX, IIRC */
static inline const char *
W(const char *c)
{
	if (c)
		return c;
	return "(null)";
}

static void
request_format(request_t *req)
{
	SPF_response_t	*spf_response;

	spf_response = req->spf_response;

	if (spf_response) {
		req->fmtlen = snprintf(req->fmt, 4095,
			"ip=%s\n"
			"sender=%s\n"
			"result=%s\n"
			"reason=%s\n"
			"smtp_comment=%s\n"
			"header_comment=%s\n"
			"error=%s\n"
			, req->ip, req->sender
			, W(SPF_strresult(SPF_response_result(spf_response)))
			, W(SPF_strreason(SPF_response_reason(spf_response)))
			, W(SPF_response_get_smtp_comment(spf_response))
			, W(SPF_response_get_header_comment(spf_response))
			, W(SPF_strerror(SPF_response_errcode(spf_response)))
			);
	}
	else {
		req->fmtlen = snprintf(req->fmt, 4095,
			"ip=%s\n"
			"sender=%s\n"
			"result=unknown\n"
			"error=%s\n"
			, req->ip, req->sender
			, SPF_strerror(req->spf_err)
			);
	}

	req->fmt[4095] = '\0';
}

static void
request_handle(request_t *req)
{
	printf("| %s\n", req->sender); fflush(stdout);
	if (!request_check(req)) {
		request_query(req);
		request_format(req);
	}
	// printf("==\n%s\n", req->fmt);
}

static const struct option longopts[] = {
	{ "debug",		required_argument,	NULL,	'd', },
	{ "tcpport",	required_argument,	NULL,	't', },
	{ "udpport",	required_argument,	NULL,	'p', },
	{ "path",		required_argument,	NULL,	'f', },
#ifdef HAVE_PWD_H
	{ "pathuser",	required_argument,	NULL,	'x', },
#endif
#ifdef HAVE_GRP_H
	{ "pathgroup",	required_argument,	NULL,	'y', },
#endif
	{ "pathmode",	required_argument,	NULL,	'm', },
#ifdef HAVE_PWD_H
	{ "setuser",	required_argument,	NULL,	'u', },
#endif
#ifdef HAVE_GRP_H
	{ "setgroup",	required_argument,	NULL,	'g', },
#endif
	{ "help",       no_argument,		NULL,	'h', },
};

static const char *shortopts = "d:t:p:f:x:y:m:u:g:h:";

void usage (void) {
	fprintf(stdout,"Flags\n");
	fprintf(stdout,"\t-tcpport\n");
	fprintf(stdout,"\t-udpport\n");
	fprintf(stdout,"\t-path\n");
#ifdef HAVE_PWD_H
	fprintf(stdout,"\t-pathuser\n");
#endif
#ifdef HAVE_GRP_H
	fprintf(stdout,"\t-pathgroup\n");
#endif
	fprintf(stdout,"\t-pathmode\n");
#ifdef HAVE_PWD_H
	fprintf(stdout,"\t-setuser\n");
#endif
#ifdef HAVE_GRP_H
	fprintf(stdout,"\t-setgroup\n");
#endif
	fprintf(stdout,"\t-help\n");

}

#define DIE(x) do { fprintf(stderr, "%s\n", x); exit(1); } while(0)

#ifdef HAVE_PWD_H
static gid_t
daemon_get_user(const char *arg)
{
	struct passwd	*pwd;
	if (isdigit(arg[0]))
		pwd = getpwuid(atol(arg));
	else
		pwd = getpwnam(arg);
	if (pwd == NULL) {
		fprintf(stderr, "Failed to find user %s\n", arg);
		DIE("Unknown user");
	}
	return pwd->pw_uid;
}
#endif

#ifdef HAVE_GRP_H
static gid_t
daemon_get_group(const char *arg)
{
	struct group	*grp;
	if (isdigit(arg[0]))
		grp = getgrgid(atol(arg));
	else
		grp = getgrnam(arg);
	if (grp == NULL) {
		fprintf(stderr, "Failed to find user %s\n", arg);
		DIE("Unknown group");
	}
	return grp->gr_gid;
}
#endif

static void
daemon_config(int argc, char *argv[])
{
	int		 idx;
	char	 c;

	memset(&spfd_config, 0, sizeof(spfd_config));

	while ((c =
		getopt_long(argc, argv, shortopts, longopts, &idx)
			) != -1) {
		switch (c) {
			case 't':
				spfd_config.tcpport = atol(optarg);
				break;
			case 'p':
				spfd_config.udpport = atol(optarg);
				break;
			case 'f':
				spfd_config.path = optarg;
				break;

			case 'd':
				spfd_config.debug = atol(optarg);
				break;

#ifdef HAVE_PWD_H
			case 'x':
				spfd_config.pathuser = daemon_get_user(optarg);
				break;
#endif
#ifdef HAVE_GRP_H
			case 'y':
				spfd_config.pathgroup = daemon_get_group(optarg);
				break;
#endif

			case 'm':
				spfd_config.pathmode = atol(optarg);
				break;

#ifdef HAVE_PWD_H
			case 'u':
				spfd_config.setuser = daemon_get_user(optarg);
				break;
#endif
#ifdef HAVE_GRP_H
			case 'g':
				spfd_config.setgroup = daemon_get_group(optarg);
				break;
#endif

			case 0:
			case '?':
				usage();
				DIE("Invalid argument");
				break;
			case 'h' :
				usage();
				DIE("");
				break;

			default:
				fprintf(stderr, "Error: getopt returned character code 0%o ??\n", c);
				DIE("WHAT?");
		}
	}
}

static int
daemon_bind_inet_udp()
{
	struct sockaddr_in	 addr;
	int					 sock;

	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		DIE("Failed to create socket");
	}
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(spfd_config.udpport);
	addr.sin_addr.s_addr = INADDR_ANY;
	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
		perror("bind");
		DIE("Failed to bind socket");
	}

	fprintf(stderr, "Accepting datagrams on %d\n", spfd_config.udpport);

	return sock;
}

static int
daemon_bind_inet_tcp()
{
	struct sockaddr_in	 addr;
	int					 sock;

	int					 optval;
	size_t				 optlen;

	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		DIE("Failed to create socket");
	}

	optval = 1;
	optlen = sizeof(int);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, optlen);

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(spfd_config.tcpport);
	addr.sin_addr.s_addr = INADDR_ANY;
	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
		perror("bind");
		DIE("Failed to bind socket");
	}

	if (listen(sock, 5) < 0) {
		perror("listen");
		DIE("Failed to listen on socket");
	}

	fprintf(stderr, "Accepting connections on %d\n", spfd_config.tcpport);

	return sock;
}

static int
daemon_bind_unix()
{
	struct sockaddr_un	 addr;
	int					 sock;

	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		DIE("Failed to create socket");
	}
	memset(&addr, 0, sizeof(addr));
	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path, spfd_config.path);
	if (unlink(spfd_config.path < 0)) {
		if (errno != ENOENT) {
			perror("unlink");
			DIE("Failed to unlink socket");
		}
	}
	if (bind(sock, (struct sockaddr *)(&addr), sizeof(addr)) < 0) {
		perror("bind");
		DIE("Failed to bind socket");
	}
	if (listen(sock, 5) < 0) {
		perror("listen");
		DIE("Failed to listen on socket");
	}

	fprintf(stderr, "Accepting connections on %s\n", spfd_config.path);

	return sock;
}

static void
daemon_init()
{
	SPF_response_t	*spf_response = NULL;
	SPF_errcode_t	 err;

	memset(&spfd_state, 0, sizeof(spfd_state));

	spf_server = SPF_server_new(SPF_DNS_CACHE, spfd_config.debug);

	if (spfd_config.rec_dom) {
		UNLESS(SPF_server_set_rec_dom(spf_server,
						spfd_config.rec_dom)) {
			DIE("Failed to set receiving domain name");
		}
	}

	if (spfd_config.sanitize) {
		UNLESS(SPF_server_set_sanitize(spf_server,
						spfd_config.sanitize)) {
			DIE("Failed to set server sanitize flag");
		}
	}

	if (spfd_config.max_lookup) {
		UNLESS(SPF_server_set_max_dns_mech(spf_server,
						spfd_config.max_lookup)){
			DIE("Failed to set maximum DNS requests");
		}
	}

	if (spfd_config.localpolicy) {
		UNLESS(SPF_server_set_localpolicy(spf_server,
						spfd_config.localpolicy,
						spfd_config.use_trusted,
						&spf_response)){
			response_print_errors("Compiling local policy",
							spf_response, err);
			DIE("Failed to set local policy");
		}
		FREE_RESPONSE(spf_response);
	}

	if (spfd_config.explanation) {
		UNLESS(SPF_server_set_explanation(spf_server,
						spfd_config.explanation,
						&spf_response)){
			response_print_errors("Setting default explanation",
							spf_response, err);
			DIE("Failed to set default explanation");
		}
		FREE_RESPONSE(spf_response);
	}

	if (spfd_config.udpport)
		spfd_state.sock_udp = daemon_bind_inet_udp();
	if (spfd_config.tcpport)
		spfd_state.sock_tcp = daemon_bind_inet_tcp();
	if (spfd_config.path)
		spfd_state.sock_unix = daemon_bind_unix();
	/* XXX Die if none of the above. */
}

/* This has a return value so we can decide whether to malloc and/or
 * free in the caller. */
static char **
find_field(request_t *req, const char *key)
{
#define STREQ(a, b) (strcmp((a), (b)) == 0)

	if (STREQ(key, "ip"))
		return &req->ip;
	if (STREQ(key, "helo"))
		return &req->helo;
	if (STREQ(key, "sender"))
		return &req->sender;
	if (STREQ(key, "rcpt"))
		return &req->rcpt_to;
	fprintf(stderr, "Invalid key %s\n", key);
	return NULL;
}

/* This is called with req->data malloc'd */
static void *
handle_datagram(void *arg)
{
	request_t	*req;
	char		**fp;
	char		*key;
	char		*value;
	char		*end;
	int			 err;

	req = (request_t *)arg;
	key = req->data;

	// printf("req: %s\n", key);

	while (key < (req->data + req->datalen)) {
		end = key + strcspn(key, "\r\n");
		*end = '\0';
		value = strchr(key, '=');

		/* Did that line contain an '='? */
		if (!value)	/* XXX WARN */
			continue;

		*value++ = '\0';
		fp = find_field(req, key);
		if (fp != NULL)
			*fp = value;
		else
			/* warned already */ ;

		key = end + 1;
		while (key < (req->data + req->datalen)) {
			if (strchr("\r\n", *key))
				key++;
			else
				break;
		}
	}

	request_handle(req);

#ifdef DEBUG
	printf("Target address length is %d: %s:%d\n", req->addrlen,
					inet_ntoa(req->addr.in.sin_addr),
					req->addr.in.sin_port);
#endif

	printf("- %s\n", req->sender); fflush(stdout);
	err = sendto(req->sock, req->fmt, req->fmtlen, 0,
			(struct sockaddr *)(&req->addr.in), req->addrlen);
	if (err == -1)
		perror("sendto");

	FREE_RESPONSE(req->spf_response);
	FREE_REQUEST(req->spf_request);

	FREE_STRING(req->data);
	free(arg);
	return NULL;
}

/* Only req is malloc'd in this. */
static void *
handle_stream(void *arg)
{
	request_t	*req;
	char		**fp;
	FILE		*stream;
	char		 key[BUFSIZ];
	char		*value;
	char		*end;

	req = (request_t *)arg;
	stream = fdopen(req->sock, "r");

	do {
		while (fgets(key, BUFSIZ, stream) != NULL) {
			key[strcspn(key, "\r\n")] = '\0';

			/* Break on a blank line and permit another query */
			if (*key == '\0')
				break;

			end = key + strcspn(key, "\r\n");
			*end = '\0';
			value = strchr(key, '=');

			if (!value)	/* XXX WARN */
				continue;

			*value++ = '\0';
			fp = find_field(req, key);
			if (fp != NULL)
				*fp = strdup(value);
			else
				/* warned already */ ;
		}

		request_handle(req);

		printf("- %s\n", req->sender); fflush(stdout);
		send(req->sock, req->fmt, req->fmtlen, 0);

		FREE_STRING(req->ip);
		FREE_STRING(req->helo);
		FREE_STRING(req->sender);
		FREE_STRING(req->rcpt_to);
	} while (!feof(stream));

	free(arg);
	return NULL;
}

static void
daemon_main()
{
	pthread_attr_t	 attr;
	pthread_t		 th;

	request_t		*req;
	char			 buf[4096];
	fd_set			 rfd;
	fd_set			 sfd;
	int				 maxfd;


	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	FD_ZERO(&rfd);
	maxfd = 0;

	if (spfd_state.sock_udp) {
		// printf("UDP socket is %d\n", spfd_state.sock_udp);
		FD_SET(spfd_state.sock_udp, &rfd);
		if (spfd_state.sock_udp > maxfd)
			maxfd = spfd_state.sock_udp;
	}
	if (spfd_state.sock_tcp) {
		// printf("TCP socket is %d\n", spfd_state.sock_tcp);
		FD_SET(spfd_state.sock_tcp, &rfd);
		if (spfd_state.sock_tcp > maxfd)
			maxfd = spfd_state.sock_tcp;
	}
	if (spfd_state.sock_unix) {
		// printf("UNIX socket is %d\n", spfd_state.sock_unix);
		FD_SET(spfd_state.sock_unix, &rfd);
		if (spfd_state.sock_unix > maxfd)
			maxfd = spfd_state.sock_unix;
	}
	// printf("MaxFD is %d\n", maxfd);

#define NEW_REQUEST	((request_t *)calloc(1, sizeof(request_t)));

	for (;;) {
		memcpy(&sfd, &rfd, sizeof(rfd));
		if (select(maxfd + 1, &sfd, NULL, NULL, NULL) == -1)
			break;

		if (spfd_state.sock_udp) {
			if (FD_ISSET(spfd_state.sock_udp, &sfd)) {
				req = NEW_REQUEST;
				req->addrlen = sizeof(req->addr);
				// printf("UDP\n");
				req->sock = spfd_state.sock_udp;
				req->datalen = recvfrom(spfd_state.sock_udp, buf,4095,0,
					(struct sockaddr *)(&req->addr.in), &req->addrlen);
				buf[req->datalen] = '\0';
				req->data = strdup(buf);
				pthread_create(&th, &attr, handle_datagram, req);
			}
		}
		if (spfd_state.sock_tcp) {
			if (FD_ISSET(spfd_state.sock_tcp, &sfd)) {
				req = NEW_REQUEST;
				req->addrlen = sizeof(req->addr);
				// printf("TCP\n");
				req->sock = accept(spfd_state.sock_tcp,
					(struct sockaddr *)(&req->addr.in), &req->addrlen);
				pthread_create(&th, &attr, handle_stream, req);
			}
		}
		if (spfd_state.sock_unix) {
			if (FD_ISSET(spfd_state.sock_unix, &sfd)) {
				req = NEW_REQUEST;
				req->addrlen = sizeof(req->addr);
				// printf("UNIX\n");
				req->sock = accept(spfd_state.sock_unix,
					(struct sockaddr *)(&req->addr.un), &req->addrlen);
				pthread_create(&th, &attr, handle_stream, req);
			}
		}
	}

	pthread_attr_destroy(&attr);
}

int
main(int argc, char *argv[])
{
	daemon_config(argc, argv);
	daemon_init();
	daemon_main();
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1