summaryrefslogtreecommitdiffhomepage
path: root/networking/isrv_identd.c
blob: c6b0f6528487c92f9fdc7f6fa63f3a81c2b48eee (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
144
145
146
147
148
149
150
151
152
153
154
155
156
/* vi: set sw=4 ts=4: */
/*
 * Fake identd server.
 *
 * Copyright (C) 2007 Denys Vlasenko
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */

//usage:#define fakeidentd_trivial_usage
//usage:       "[-fiw] [-b ADDR] [STRING]"
//usage:#define fakeidentd_full_usage "\n\n"
//usage:       "Provide fake ident (auth) service\n"
//usage:     "\n	-f	Run in foreground"
//usage:     "\n	-i	Inetd mode"
//usage:     "\n	-w	Inetd 'wait' mode"
//usage:     "\n	-b ADDR	Bind to specified address"
//usage:     "\n	STRING	Ident answer string (default: nobody)"

#include "libbb.h"
#include <syslog.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;

#define bogouser bb_common_bufsiz1

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) | 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;

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

	if (sz < 0) {
		if (errno != EAGAIN)
			goto term;
		return 0; /* "session is ok" */
	}

	buf->pos += sz;
	buf->buf[buf->pos] = '\0';
	p = strpbrk(cur, "\r\n");
	if (p)
		*p = '\0';
	if (!p && sz && buf->pos < (int)sizeof(buf->buf))
		return 0;  /* "session is 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);
	if (buf->fd_flag & O_NONBLOCK)
		fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
 term:
	free(buf);
	return 1; /* "terminate" */
}

static int do_timeout(void **paramp UNUSED_PARAM)
{
	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) MAIN_EXTERNALLY_VISIBLE;
int fakeidentd_main(int argc UNUSED_PARAM, 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(argv, "fiwb:", &bind_address);
	strcpy(bogouser, "nobody");
	if (argv[optind])
		strncpy(bogouser, argv[optind], sizeof(bogouser) - 1);

	/* Daemonize if no -f and no -i and no -w */
	if (!(opt & OPT_fiw))
		bb_daemonize_or_rexec(0, argv);

	/* 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, LOG_PID, 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;
}