use the -newos toolchain even if -elf is present.
[newos.git] / apps / telnetd / main.c
blob97715a9542901ef780408fcd8927625daa2035d4
1 /*
2 ** Copyright 2002, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <sys/syscalls.h>
6 #include <newos/errors.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <newos/tty_priv.h>
13 static int debug_fd; // debug spew to the console
14 static int tty_master_fd;
15 static int tty_slave_fd;
16 static int tty_num;
17 static int socket_fd;
18 static sem_id wait_sem;
20 // XXX fix for big endian (move to libnet or whatever it's gonna be called)
21 static short ntohs(short value)
23 return ((value>>8)&0xff) | ((value&0xff)<<8);
26 enum {
27 SE = 240,
28 SB = 250,
29 WILL = 251,
30 WONT,
31 DO,
32 DONT,
33 IAC = 255
36 enum {
37 OPT_ECHO = 1,
38 OPT_SUPPRESS_GO_AHEAD = 3,
39 OPT_NAWS = 31,
42 static void process_subblock(int sb_type, unsigned char *buf, int len)
44 char temp[128];
46 sprintf(temp, "process_subblock: type %d, len %d\r\n", sb_type, len);
47 write(debug_fd, temp, strlen(temp));
49 if(sb_type == OPT_NAWS) {
50 struct tty_winsize ws;
52 ws.cols = ntohs(*(unsigned short *)&buf[0]);
53 ws.rows = ntohs(*(unsigned short *)&buf[2]);
54 sprintf(temp, "NAWS %d %d\r\n", ws.cols, ws.rows);
55 write(debug_fd, temp, strlen(temp));
57 ioctl(tty_master_fd, _TTY_IOCTL_SET_WINSIZE, &ws, sizeof(ws));
61 static int telnet_reader(void *arg)
63 unsigned char buf[4096];
64 unsigned char sb[4096];
65 ssize_t len;
66 int i;
67 enum {
68 NORMAL = 0,
69 SEEN_IAC,
70 SEEN_OPT_NEGOTIATION,
71 SEEN_SB,
72 IN_SB,
73 } state = NORMAL;
74 int output_start, output_len;
75 int curr_sb_type = 0;
76 int sb_len = 0;
77 char temp[128];
79 for(;;) {
80 /* read from the socket */
81 len = read(socket_fd, buf, sizeof(buf));
82 if(len <= 0)
83 break;
85 output_start = 0;
86 output_len = 0;
87 for(i = 0; i < len; i++) {
88 // try to remove commands
89 switch(state) {
90 case NORMAL:
91 if(buf[i] == IAC) {
92 state = SEEN_IAC;
93 if(output_len > 0)
94 write(tty_master_fd, &buf[output_start], output_len);
95 output_len = 0;
96 write(debug_fd, "NORMAL: IAC\r\n", strlen("NORMAL: IAC\r\n"));
97 } else {
98 output_len++;
100 break;
101 case SEEN_IAC:
102 sprintf(temp, "SEEN_IAC: 0x%x\r\n", buf[i]);
103 write(debug_fd, temp, strlen(temp));
104 if(buf[i] == SB) {
105 state = SEEN_SB;
106 } else if(buf[i] == IAC) {
107 output_start = i;
108 output_len = 1;
109 state = NORMAL;
110 } else {
111 state = SEEN_OPT_NEGOTIATION;
113 break;
114 case SEEN_OPT_NEGOTIATION:
115 sprintf(temp, "SEEN_OPT_NEGOTIATION: 0x%x\r\n", buf[i]);
116 write(debug_fd, temp, strlen(temp));
117 // we can transition back to normal now, we've eaten this option
118 state = NORMAL;
119 output_len = 0;
120 output_start = i+1;
121 break;
122 case SEEN_SB:
123 sprintf(temp, "SEEN_SB: 0x%x\r\n", buf[i]);
124 write(debug_fd, temp, strlen(temp));
125 if(buf[i] == SE) {
126 state = NORMAL;
127 output_len = 0;
128 output_start = i+1;
129 } else {
130 curr_sb_type = buf[i];
131 state = IN_SB;
132 sb_len = 0;
134 break;
135 case IN_SB:
136 sprintf(temp, "IN_SB: 0x%x\r\n", buf[i]);
137 write(debug_fd, temp, strlen(temp));
138 if(buf[i] == SE) {
139 state = NORMAL;
140 output_len = 0;
141 output_start = i+1;
142 } else if(buf[i] == IAC) {
143 process_subblock(curr_sb_type, sb, sb_len);
144 } else {
145 sb[sb_len++] = buf[i];
147 break;
150 if(output_len > 0)
151 write(tty_master_fd, &buf[output_start], output_len);
154 _kern_sem_release(wait_sem, 1);
156 _kern_exit(0);
157 return 0;
160 static int telnet_writer(void *arg)
162 char buf[4096];
163 ssize_t len;
164 ssize_t write_len;
166 for(;;) {
167 /* read from the tty's master end */
168 len = read(tty_master_fd, buf, sizeof(buf));
169 if(len <= 0)
170 break;
172 write_len = write(socket_fd, buf, len);
173 if(write_len < 0)
174 break;
177 _kern_sem_release(wait_sem, 1);
179 _kern_exit(0);
180 return 0;
183 static int send_opts()
185 char buf[4096];
187 // negotiate the only options I care about
188 buf[0] = IAC;
189 buf[1] = WILL;
190 buf[2] = OPT_ECHO;
191 buf[3] = IAC;
192 buf[4] = WILL;
193 buf[5] = OPT_SUPPRESS_GO_AHEAD;
194 buf[6] = IAC;
195 buf[7] = DO;
196 buf[8] = OPT_NAWS;
198 return write(socket_fd, buf, 9);
201 static void sigchld_handler(int signal)
203 _kern_sem_release(wait_sem, 1);
206 int main(int argc, char **argv)
208 char **spawn_argv;
209 int spawn_argc;
210 int i;
211 proc_id pid;
212 thread_id tid;
214 if(argc < 2) {
215 printf("%s: not enough arguments\n", argv[0]);
216 return -1;
219 // we're a session leader
220 setsid();
222 // build an array of args to pass anything we start up
223 spawn_argc = argc - 1;
224 spawn_argv = (char **)malloc(sizeof(char *) * spawn_argc);
225 if(spawn_argv == NULL)
226 return -1;
227 for(i = 0; i < spawn_argc; i++) {
228 spawn_argv[i] = argv[i + 1];
231 // register for SIGCHLD signals
232 signal(SIGCHLD, &sigchld_handler);
234 // debug_fd = open("/dev/console", 0);
235 debug_fd = -1;
237 wait_sem = _kern_sem_create(0, "telnetd wait sem");
238 if(wait_sem < 0)
239 return -1;
241 tty_master_fd = open("/dev/tty/master", 0);
242 if(tty_master_fd < 0)
243 return -2;
245 tty_num = ioctl(tty_master_fd, _TTY_IOCTL_GET_TTY_NUM, NULL, 0);
246 if(tty_num < 0)
247 return -3;
250 struct tty_flags flags;
252 ioctl(tty_master_fd, _TTY_IOCTL_GET_TTY_FLAGS, &flags, sizeof(flags));
253 flags.input_flags |= TTY_FLAG_CRNL;
254 ioctl(tty_master_fd, _TTY_IOCTL_SET_TTY_FLAGS, &flags, sizeof(flags));
258 char temp[128];
259 sprintf(temp, "/dev/tty/slave/%d", tty_num);
261 tty_slave_fd = open(temp, 0);
262 if(tty_slave_fd < 0)
263 return -4;
266 // move the stdin and stdout out of the way
267 socket_fd = dup(0); // assume stdin, stdout, and stderr are the same socket
268 close(0);
269 close(1);
270 close(2);
272 // set the endpoints to the tty slave endpoint
273 dup2(tty_slave_fd, 0);
274 dup2(tty_slave_fd, 1);
275 dup2(tty_slave_fd, 2);
276 close(tty_slave_fd);
278 // send some options over to the other side
279 send_opts();
281 // now start the app
282 pid = _kern_proc_create_proc(spawn_argv[0], spawn_argv[0], spawn_argv, spawn_argc, 5, PROC_FLAG_NEW_PGROUP);
283 if(pid < 0)
284 return -1;
286 tid = _kern_thread_create_thread("telnet reader", &telnet_reader, NULL);
287 _kern_thread_set_priority(tid, 30);
288 _kern_thread_resume_thread(tid);
290 tid = _kern_thread_create_thread("telnet writer", &telnet_writer, NULL);
291 _kern_thread_set_priority(tid, 30);
292 _kern_thread_resume_thread(tid);
294 _kern_sem_acquire(wait_sem, 1);
296 return 0;