aboutsummaryrefslogtreecommitdiff
path: root/networking/isrv_identd.c
blob: 30f9a7a26344e07bb0f6f976e214d49ff5d9158a (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
/* 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 sz;

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

	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)) {
		/* fd is still in nonblocking mode - we never block here */
		fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
		goto term;
	}
 ok:
	fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
	return 0;
 term:
	fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
	free(buf);
	return 1;
}

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

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

int fakeidentd_main(int argc, char **argv)
{
	enum {
		OPT_foreground = 0x1,
		OPT_inetd      = 0x2,
		OPT_inetdwait  = 0x4,
		OPT_nodeamon   = 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 or -i or -w */
	bb_sanitize_stdio(!(opt & OPT_nodeamon));
	if (!(opt & OPT_nodeamon)) {
		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);

	if (opt & OPT_inetdwait) {
		fd = 0;
	} else {
		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;
}