summaryrefslogtreecommitdiffhomepage
path: root/networking/isrv_identd.c
blob: 2d4399cea89bd9d72bd0bb242ec0be479648de94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* vi: set sw=4 ts=4: */
/*
 * Fake identd server.
 *
 * Copyright (C) 2007 Denis Vlasenko
 *
 * Licensed under GPL version 2, see file LICENSE in this tarball for details.
 */

#include <syslog.h>
#include "busybox.h"
#include "isrv.h"

enum { TIMEOUT = 20 };

typedef struct identd_buf_t {
	int pos;
	int fd_flag;
	char buf[64 - 2*sizeof(int)];
} identd_buf_t;

static const char *bogouser = "nobody";

static int new_peer(isrv_state_t *state, int fd)
{
	int peer;
	identd_buf_t *buf = xzalloc(sizeof(*buf));

	peer = isrv_register_peer(state, buf);
	if (peer < 0)
		return 0; /* failure */
	if (isrv_register_fd(state, peer, fd) < 0)
		return peer; /* failure, unregister peer */

	buf->fd_flag = fcntl(fd, F_GETFL, 0) | O_NONBLOCK;
	isrv_want_rd(state, fd);
	return 0;
}

static int do_rd(int fd, void **paramp)
{
	identd_buf_t *buf = *paramp;
	char *cur, *p;
	int retval = 0; /* session is ok (so far) */
	int sz;

	cur = buf->buf + buf->pos;

	if (buf->fd_flag & O_NONBLOCK)
		fcntl(fd, F_SETFL, buf->fd_flag);
	sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);

	if (sz < 0) {
		if (errno != EAGAIN)
			goto term; /* terminate this session if !EAGAIN */
		goto ok;
	}

	buf->pos += sz;
	buf->buf[buf->pos] = '\0';
	p = strpbrk(cur, "\r\n");
	if (p)
		*p = '\0';
	if (!p && sz && buf->pos <= sizeof(buf->buf))
		goto ok;
	/* Terminate session. If we are in server mode, then
	 * fd is still in nonblocking mode - we never block here */
	if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
	fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
 term:
	free(buf);
	retval = 1; /* terminate */
 ok:
	if (buf->fd_flag & O_NONBLOCK)
		fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
	return retval;
}

static int do_timeout(void **paramp)
{
	return 1; /* terminate session */
}

static void inetd_mode(void)
{
	identd_buf_t *buf = xzalloc(sizeof(*buf));
	/* buf->pos = 0; - xzalloc did it */
	/* We do NOT want nonblocking I/O here! */
	/* buf->fd_flag = 0; - xzalloc did it */
	do
		alarm(TIMEOUT);
	while (do_rd(0, (void*)&buf) == 0);
}

int fakeidentd_main(int argc, char **argv)
{
	enum {
		OPT_foreground = 0x1,
		OPT_inetd      = 0x2,
		OPT_inetdwait  = 0x4,
		OPT_fiw        = 0x7,
		OPT_bindaddr   = 0x8,
	};

	const char *bind_address = NULL;
	unsigned opt;
	int fd;

	opt = getopt32(argc, argv, "fiwb:", &bind_address);
	if (optind < argc)
		bogouser = argv[optind];

	/* Daemonize if no -f and no -i and no -w */
	bb_sanitize_stdio_maybe_daemonize(!(opt & OPT_fiw));
	/* Where to log in inetd modes? "Classic" inetd
	 * probably has its stderr /dev/null'ed (we need log to syslog?),
	 * but daemontools-like utilities usually expect that children
	 * log to stderr. I like daemontools more. Go their way.
	 * (Or maybe we need yet another option "log to syslog") */
	if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) {
		openlog(applet_name, 0, LOG_DAEMON);
		logmode = LOGMODE_SYSLOG;
	}

	if (opt & OPT_inetd) {
		inetd_mode();
		return 0;
	}

	/* Ignore closed connections when writing */
	signal(SIGPIPE, SIG_IGN);

	fd = 0;
	if (!(opt & OPT_inetdwait)) {
		fd = create_and_bind_stream_or_die(bind_address,
				bb_lookup_port("identd", "tcp", 113));
		xlisten(fd, 5);
	}

	isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
			TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
	return 0;
}