/*
* (c) 2001 Dirk-Willem van Gulik, All Rights Reserved. See
* http://www.webweaving.org/LICENSE for the license
*
* $Id: widentd.c 1573 2004-01-27 15:33:28Z dirkx $
*
*/
#define USAGE "widentd [-i ip] [-s port/service] [-v] [-o OS] [-u User]\n"
#include <ctype.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <signal.h>
#include <term.h>
#include <sys/select.h>
#include <sys/time.h>
#include <syslog.h>
#include <stdarg.h>
#include <fcntl.h>
#define pdie(x) { perror(x); exit(4); }
#define BLOG (5) /* Listen queue - should not bee too high */
#define TIMEOUT (10) /* 10 second timeout - total transaction
* less than 4 x Timeout */
#define UID "chuck-the-bsd-deamon"
#define OS "OTHER"
#define REPLY "%d , %d : USERID : %s : %s\r\n"
#define SERVICE ("ident") /* 113 */
int verbose = 0;
const char *user = UID;
const char *os = OS;
static void
usage(void)
{
fprintf(stderr, USAGE);
exit(1);
}
int
main(int argc, char **argv)
{
char ch;
const int yes = 1;
int blog = BLOG;
unsigned int i;
char *myname = argv[0];
fd_set *lst = malloc(FD_SETSIZE), *xst = malloc(FD_SETSIZE),
*wst = malloc(FD_SETSIZE);
struct addrinfo *ports, *p;
const char * node = NULL, * service = SERVICE;
struct addrinfo hints;
int s = -1;
int e;
while ((ch = getopt(argc, argv, "vo:u:s:i:")) != -1)
switch (ch) {
case 'v':
verbose = 1;
break;
case 'u':
user = optarg;
for (i = 0; i < strlen(user); i++)
if (!(isalnum(user[i]) || (user[i] == '@') || (user[i] == '-') || (user[i] == '.'))) {
fprintf(stderr, "User name can only contiain a-z, '-', '@' and '.'.\n");
exit(1);
}
break;
case 'o':
os = optarg;
for (i = 0; i < strlen(os); i++)
if (!isalnum(os[i])) {
fprintf(stderr, "OS name can only contain a-z.\n");
exit(1);
}
break;
case 'i':
node = optarg;
break;
case 's':
service = optarg;
break;
case 'h': /* no break */
case '?': /* no break */
default:
usage();
break;
}
argc -= optind;
argv += optind;
if (argc)
usage();
if (verbose)
printf("Inbound (i.e. listening to)\n\t");
else
openlog(myname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); /* no error returned */
memset(&hints,0,sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((e=getaddrinfo(node, service, & hints , &ports))) {
fprintf(stderr,"interface/service errror: %s\n",gai_strerror(e));
exit(3);
};
for( p = ports; p; p = p->ai_next ) {
if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
pdie("Failed to open socket");
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
pdie("Set socket reuse");
if (bind(s, p->ai_addr, p->ai_addrlen) < 0)
pdie("Failed to bind in-port");
if (listen(s, blog) < 0)
pdie("Listen");
}
freeaddrinfo(ports);
if (s == -1)
pdie("No addresses to bind to");
if (verbose)
printf("Wident waiting for requests\n");
else
daemon(0, 0);
signal(SIGPIPE, SIG_IGN);
while (1) {
unsigned char buff[1024];
struct sockaddr_storage con;
int len = sizeof(con);
int cons = accept(s, (struct sockaddr *)&con, &len);
struct timeval to;
int noreply = 0;
enum {
READ, WRITE
} r_or_w = READ;
unsigned int inbuff = 0;
char ip[128];
char clienthost[ NI_MAXHOST ];
char clientserv[ NI_MAXSERV ];
if (cons == -1) {
perror("Error on accept");
continue;
};
if (fcntl(cons, O_NONBLOCK, &yes) == -1) {
perror("Error when making non blocking");
close(cons);
continue;
};
memset(ip,0,sizeof(ip));
memset(clienthost,0,sizeof(clienthost));
memset(clientserv,0,sizeof(clientserv));
getnameinfo((struct sockaddr *)&con, len,
clienthost,sizeof(clienthost), clientserv,sizeof(clientserv),
NI_NUMERICHOST | NI_NUMERICSERV);
snprintf(ip, sizeof(ip) - 1, "%s:%s", clienthost, clientserv);
if (verbose)
printf("Incoming commection from %s\n", ip);
else
syslog(LOG_INFO, "widentd rq from %s\n", ip);
to.tv_sec = TIMEOUT;
to.tv_usec = 0;
memset(buff, sizeof(buff), 0);
do {
int n;
FD_ZERO(lst);
FD_ZERO(xst);
FD_ZERO(wst);
if (r_or_w == READ)
FD_SET(cons, lst);
else
FD_SET(cons, wst);
n = select(cons + 1, lst, wst, xst, &to);
if (n == 0) {
if (verbose)
fprintf(stderr, "Timeout waiting for %s\n", ip);
else
syslog(LOG_NOTICE, "widentd timeout for %s\n", ip);
break;
} else if ((n == -1) && (errno == EAGAIN))
continue;
else if (n == -1) {
if (verbose)
fprintf(stderr, "Select error:%s for %s\n",
strerror(errno), ip);
else
syslog(LOG_ERR, "widentd select error:%s for %s\n",
strerror(errno), ip);
break;
}
if (FD_ISSET(cons, xst))
break;
if (r_or_w == READ)
n = read(cons, buff + inbuff, sizeof(buff) - inbuff);
else
n = write(cons, buff + inbuff, strlen(buff) - inbuff);
if (n == 0)
break; /* normal close */
else if ((n == -1) && (errno == EAGAIN))
continue; /* normal interrupt. */
else if (n == -1) {
if (verbose)
fprintf(stderr, "I/O error:%s\n",
strerror(errno));
else
syslog(LOG_ERR, "widentd i/o error:%s\n",
strerror(errno));
break;
}
if (r_or_w == READ) {
/* We are still in reading mode */
for (i = inbuff; (r_or_w == READ) && (i < inbuff + n); i++) {
/*
* if we see any CR or LF - consider
* the read done
*/
if ((buff[i] == '\r') || (buff[i] == '\n')) {
int a, b;
buff[i] = '\0';
if (sscanf(buff, "%d , %d", &a, &b) != 2) {
syslog(LOG_NOTICE,"Malformed auth line from %s",ip);
a = b = 1;
}
snprintf(buff, sizeof(buff) - 1, REPLY, a, b, os, user);
if (verbose)
printf("Sending %s\n", buff);
/* no further reads expected. */
shutdown(cons, SHUT_RD);
r_or_w = WRITE;
inbuff = 0;
}
}
/*
* If we've not yet seen a CR or LF, keep
* listening but keep a counter to guard
* against slow feeding DOS style things.
*/
if (r_or_w == READ) {
inbuff += n;
/*
* just bail out if things get too
* big
*/
if (inbuff >= sizeof(buff)) {
syslog(LOG_NOTICE,"Extremely long line from %s",ip);
break;
};
};
} else {
inbuff += n;
if (inbuff >= strlen(buff))
break; /* we're done */
}
/*
* Try the read/write's about three times; and then
* bail out.
*/
if ((noreply++) > 3) {
syslog(LOG_NOTICE,"Extremely Slow feed from %s",ip);
break;
}
} while (1);
if (verbose)
printf("And closing\n");
/* shutdown for read and write */
shutdown(cons, SHUT_RDWR);
close(cons);
} while (1);
/* NOT REACHED */
}
syntax highlighted by Code2HTML, v. 0.9.1