summaryrefslogtreecommitdiffstats
path: root/loader/telnetd.c
blob: d1ae82fa19a2e2e3774ffab13fb58f4d1488ef30 (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
/* Glue to tie telnet.c from ttywatch to the loader */

#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <pty.h>
#include <string.h>
#include <sys/poll.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>

#include "log.h"
#include "telnet.h"

/* Forks, keeping the loader as our child (so we know when it dies). */
int beTelnet(void) {
    int sock;
    int conn;
    int addrLength;
    pid_t child;
    int i;
    int masterFd;
    struct sockaddr_in address;
    char buf[4096];
    struct pollfd fds[3];
    telnet_state ts = TS_DATA;
    struct termios orig, new;

    if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
	logMessage("socket: %s", strerror(errno));
	return -1;
    }

    address.sin_family = AF_INET;
    address.sin_port = htons(IPPORT_TELNET);
    memset(&address.sin_addr, 0, sizeof(address.sin_addr));
    addrLength = sizeof(address);

    /* Let the kernel reuse the socket address. This lets us run
       twice in a row, without waiting for the (ip, port) tuple
       to time out. Makes testing much easier*/
    conn = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &conn, sizeof(conn));

    bind(sock, (struct sockaddr *) &address, sizeof(address));
    listen(sock, 5);

    printf("Waiting for telnet connection on port 23...");

    if ((conn = accept(sock, (struct sockaddr *) &address, 
                          &addrLength)) < 0) {
	logMessage("accept: %s", strerror(errno));
	exit(0);
	return -1;
    }

    printf(" got a connection.\n");

    close(sock);

    telnet_negotiate(conn);
    child = forkpty(&masterFd, NULL, NULL, NULL);

    if (child < 0) {
	logMessage("forkpty: %s", strerror(errno));
	close(conn);
	return -1;
    } else if (child) {
	fds[0].events = POLLIN;
	fds[0].fd = masterFd;

	fds[1].events = POLLIN;
	fds[1].fd = conn;

	fds[2].events = POLLIN;
	fds[2].fd = STDIN_FILENO;

	tcgetattr(STDIN_FILENO, &orig);
	tcgetattr(STDIN_FILENO, &new);
	new.c_lflag &= ~(ICANON | ECHO | ECHOCTL | ECHONL);
	new.c_oflag &= ~ONLCR;
	new.c_iflag &= ~ICRNL;
	new.c_cc[VSUSP] = 0;
	tcsetattr(STDIN_FILENO, 0, &new);

	while ((i = poll(fds, 3, -1)) > 0) {
	    if (fds[0].revents) {
		i = read(masterFd, buf, sizeof(buf));

		/* child died */
		if (i < 0)
		    break;

		telnet_send_output(conn, buf, i);
		write(STDOUT_FILENO, buf, i);
	    }

	    if (fds[1].revents) {
		i = read(conn, buf, sizeof(buf));

		/* connection went away */
		if (!i)
		    break;

		i = telnet_process_input(&ts, buf, i);
		write(masterFd, buf, i);
	    }

	    if (fds[2].revents) {
		i = read(STDIN_FILENO, buf, sizeof(buf));
		write(masterFd, buf, i);
	    }
	}

	printf("out of poll %d\n", i);

	if (i < 0) {
	    logMessage("poll: %s", strerror(errno));
	} 

	kill(child, SIGTERM);
	close(conn);
	tcsetattr(STDIN_FILENO, 0, &orig);

	exit(0);
    }

    return 0;
}