Kernel/debug - Clean up Debug() method, bind to #define config
[tpg/acess2.git] / Usermode / Applications / telnetd_src / main.c
1 /*
2  * Acess2 Telnet Server (TCP server test case)
3  * - By John Hodge (thePowersGang)
4  *
5  * main.c
6  * - All
7  */
8 #include <stddef.h>
9 #include <net.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <acess/sys.h>
13 #include <assert.h>
14 #include <errno.h>
15 #include <acess/devices/pty.h>
16 #include <stdbool.h>
17
18 // === TYPES ===
19 enum eTelnetMode
20 {
21         MODE_DATA,
22         MODE_IAC,
23         MODE_WILL,
24         MODE_WONT,
25         MODE_DO,
26         MODE_DONT,
27         MODE_SUBNEG_OPTION,
28         MODE_SUBNEG_DATA,
29         MODE_SUBNEG_IAC,
30 };
31
32 typedef struct sClient
33 {
34         enum eTelnetMode        Mode;
35          int    Socket;
36          int    pty;
37 } tClient;
38
39 // === PROTOTYPES ===
40 void    EventLoop(void);
41 void    Server_NewClient(int FD);
42 void    HandleServerBoundData(tClient *Client);
43 void    HandleClientBoundData(tClient *Client);
44 void    HandleOptionRequest(tClient *Client, int Option, bool Value, bool IsRequest);
45
46 // === GLOBALS ===
47 // --- Configuration ---
48  int    giConfig_MaxClients = 5;
49 // --- State ---
50  int    giServerFD;
51 tClient *gaClients;
52  int    giNumClients;
53
54 // === CODE ===
55 int main(int argc, char *argv[])
56 {
57         // TODO: Configure
58
59         // Initialise
60         gaClients = calloc(giConfig_MaxClients, sizeof(tClient));
61
62         // Open server
63         {
64                 uint8_t data[16];
65                  int    addrtype = Net_ParseAddress("10.0.2.10", data);
66                  int    port = 23;
67                 giServerFD = Net_OpenSocket(addrtype, data, "tcps");
68                 _SysIOCtl(giServerFD, 4, &port);        // Set port and start listening
69         }
70
71         // Event loop
72         EventLoop();
73         
74         return 0;
75 }
76
77 static void FD_SET_MAX(fd_set *set, int fd, int *maxfd)
78 {
79         FD_SET(fd, set);
80         if(*maxfd < fd) *maxfd = fd;
81 }
82
83 void EventLoop(void)
84 {
85         fd_set  fds;
86          int    maxfd;
87
88         for( ;; )
89         {
90                 FD_ZERO(&fds);
91                 maxfd = 0;
92                 // Fill select
93                 FD_SET_MAX(&fds, giServerFD, &maxfd);
94                 for( int i = 0; i < giConfig_MaxClients; i ++ )
95                 {
96                         if( gaClients[i].Socket == 0 )  continue ;
97                         FD_SET_MAX(&fds, gaClients[i].Socket, &maxfd);
98                         FD_SET_MAX(&fds, gaClients[i].pty,  &maxfd);
99                 }
100                 
101                 // Select!
102                 int rv = _SysSelect( maxfd+1, &fds, NULL, NULL, NULL, 0 );
103                 _SysDebug("Select return %i", rv);
104                 
105                 // Check events
106                 if( FD_ISSET(giServerFD, &fds) )
107                 {
108                         Server_NewClient(giServerFD);
109                 }
110                 for( int i = 0; i < giConfig_MaxClients; i ++ )
111                 {
112                         if( FD_ISSET(gaClients[i].Socket, &fds) )
113                         {
114                                 // Handle client data
115                                 HandleServerBoundData(&gaClients[i]);
116                         }
117                         if( FD_ISSET(gaClients[i].pty, &fds) )
118                         {
119                                 // Handle output from terminal
120                                 HandleClientBoundData(&gaClients[i]);
121                         }
122                 }
123         }
124 }
125
126 void Server_NewClient(int FD)
127 {
128         tClient *clt = NULL;
129         
130         // TODO: Is this done in the IPStack?
131         if( giNumClients == giConfig_MaxClients )
132         {
133                 // Open, reject
134                 _SysClose( _SysOpenChild(FD, "", OPENFLAG_READ) );
135                 return ;
136         }
137         
138         // Allocate client structure and open socket
139         for( int i = 0; i < giConfig_MaxClients; i ++ )
140         {
141                 if( gaClients[i].Socket == 0 ) {
142                         clt = &gaClients[i];
143                         break;
144                 }
145         }
146         assert(clt);
147         // Accept the connection
148         clt->Socket = _SysOpenChild(FD, "", OPENFLAG_READ|OPENFLAG_WRITE);
149         giNumClients ++;
150         
151         // Create PTY
152         // TODO: Use PTYs
153         clt->pty = _SysOpen("/Devices/pts/ptmx", OPENFLAG_READ|OPENFLAG_WRITE);
154         if( clt->pty < 0 ) {
155                 perror("Unable to create/open PTY");
156                 _SysDebug("Unable to create/open PTY: %s", strerror(errno));
157                 _SysClose(clt->Socket);
158                 clt->Socket = 0;
159                 return ;
160         }
161         // - Initialise
162         {
163                 _SysIOCtl(clt->pty, PTY_IOCTL_SETID, "telnetd#");
164                 struct ptymode  mode = {.InputMode = 0, .OutputMode=0};
165                 struct ptydims  dims = {.W = 80, .H = 25};
166                 _SysIOCtl(clt->pty, PTY_IOCTL_SETMODE, &mode);
167                 _SysIOCtl(clt->pty, PTY_IOCTL_SETDIMS, &dims);
168         }
169         
170         // TODO: Arguments and envp
171         {
172                 char pty_path[] = "/Devices/pts/telnetd000";
173                 _SysIOCtl(clt->pty, PTY_IOCTL_GETID, pty_path+13);
174                  int    clientfd = _SysOpen(pty_path, OPENFLAG_READ|OPENFLAG_WRITE);
175                 if(clientfd < 0) {
176                         perror("Unable to open login PTY");
177                         _SysClose(clt->Socket);
178                         _SysClose(clt->pty);
179                         clt->Socket = 0;
180                         return ;
181                 }
182                 _SysDebug("Using client PTY %s", pty_path);
183                 int fds[3] = {clientfd, clientfd, clientfd};
184                 const char      *argv[] = {"login", NULL};
185                 _SysSpawn("/Acess/SBin/login", argv, argv, 3, fds, NULL);
186                 _SysClose(clientfd);
187         }
188 }
189
190 void HandleServerBoundData(tClient *Client)
191 {
192         uint8_t buf[BUFSIZ];
193         size_t  len;
194
195         _SysDebug("Client %p", Client); 
196         len = _SysRead(Client->Socket, buf, BUFSIZ);
197         _SysDebug("%i bytes for %p", len, Client);
198         if( len == 0 )  return ;
199         if( len == -1 ) {
200                 return ;
201         }
202         // handle options
203         // int  last_flush = 0;
204         for( int i = 0; i < len; i ++ )
205         {
206                 switch(Client->Mode)
207                 {
208                 case MODE_IAC:
209                         Client->Mode = MODE_DATA;
210                         switch(buf[i])
211                         {
212                         case 240:       // End of subnegotiation parameters
213                                 _SysDebug("End Subnegotiation");
214                                 break;
215                         case 241:       // Nop
216                                 break;
217                         case 242:       // SYNCH
218                         case 243:       // NVT Break
219                         case 244:       // Function 'IP' (Ctrl-C)
220                                 
221                                 break;
222                         case 245:       // Function 'AO'
223                         case 246:       // Function 'AYT'
224                         case 247:       // Function 'EC'
225                         case 248:       // Function 'EL'
226                         case 249:       // GA Signal
227                                 break;
228                         case 250:       // Subnegotation
229                                 _SysDebug("Subnegotiation");
230                                 // TODO: Get option ID, and then cache until 'END SB' (240)
231                                 Client->Mode = MODE_SUBNEG_OPTION;
232                                 break;
233                         case 251:       // WILL
234                                 Client->Mode = MODE_WILL;
235                                 break;
236                         case 252:       // WONT
237                                 Client->Mode = MODE_WONT;
238                                 break;
239                         case 253:       // DO
240                                 Client->Mode = MODE_DO;
241                                 break;
242                         case 254:       // DONT
243                                 Client->Mode = MODE_DONT;
244                                 break;
245                         case 255:       // Literal 255
246                                 _SysWrite(Client->pty, buf+i, 1);
247                                 break;
248                         }
249                         break;
250                 case MODE_WILL:
251                         _SysDebug("Option %i WILL", buf[i]);
252                         HandleOptionRequest(Client, buf[i], true, false);
253                         Client->Mode = MODE_DATA;
254                         break;
255                 case MODE_WONT:
256                         _SysDebug("Option %i WONT", buf[i]);
257                         HandleOptionRequest(Client, buf[i], false, false);
258                         Client->Mode = MODE_DATA;
259                         break;
260                 case MODE_DO:
261                         _SysDebug("Option %i DO", buf[i]);
262                         HandleOptionRequest(Client, buf[i], true, true);
263                         Client->Mode = MODE_DATA;
264                         break;
265                 case MODE_DONT:
266                         _SysDebug("Option %i DONT", buf[i]);
267                         HandleOptionRequest(Client, buf[i], false, true);
268                         Client->Mode = MODE_DATA;
269                         break;
270                 case MODE_SUBNEG_OPTION:
271                         _SysDebug("Option %i subnegotation", buf[i]);
272                         Client->Mode = MODE_SUBNEG_DATA;
273                         break;
274                 case MODE_SUBNEG_IAC:
275                         switch(buf[i])
276                         {
277                         case 240:       // End subnegotation
278                                 // TODO: Handle subnegotation data
279                                 Client->Mode = MODE_DATA;
280                                 break;
281                         case 255:
282                                 // TODO: Literal 255
283                                 Client->Mode = MODE_SUBNEG_DATA;
284                                 break;
285                         default:
286                                 _SysDebug("Unexpected %i in SUBNEG IAC", buf[i]);
287                                 Client->Mode = MODE_SUBNEG_DATA;
288                                 break;
289                         }
290                 case MODE_SUBNEG_DATA:
291                         if( buf[i] == 255 )
292                                 Client->Mode = MODE_SUBNEG_IAC;
293                         else
294                                 ;//_SysWrite(Client->pty, buf+i, 1);
295                         break;
296                 
297                 case MODE_DATA:
298                         if( buf[i] == 255 )
299                                 Client->Mode = MODE_IAC;
300                         else
301                                 _SysWrite(Client->pty, buf+i, 1);
302                         break;
303                 }
304         }
305 }
306
307 void HandleClientBoundData(tClient *Client)
308 {
309         char    buf[BUFSIZ];
310          int    len;
311         
312         len = _SysRead(Client->pty, buf, BUFSIZ);
313         if( len <= 0 )  return ;
314         _SysWrite(Client->Socket, buf, len);
315 }
316
317 void HandleOptionRequest(tClient *Client, int Option, bool Value, bool IsRequest)
318 {
319         switch(Option)
320         {
321         default:
322                 _SysDebug("Unknown option %i", Option);
323                 break;
324         }
325 }
326

UCC git Repository :: git.ucc.asn.au