Usermode/telnetd - Updated to use a PTY
[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
14 // === TYPES ===
15 enum eTelnetMode
16 {
17         MODE_DATA,
18         MODE_IAC,
19         MODE_WILL,
20         MODE_WONT,
21         MODE_DO,
22         MODE_DONT
23 };
24
25 typedef struct sClient
26 {
27         enum eTelnetMode        Mode;
28          int    Socket;
29          int    pty;
30 } tClient;
31
32 // === PROTOTYPES ===
33 void    EventLoop(void);
34 void    Server_NewClient(int FD);
35 void    HandleServerBoundData(tClient *Client);
36 void    HandleClientBoundData(tClient *Client);
37
38 // === GLOBALS ===
39 // --- Configuration ---
40  int    giConfig_MaxClients = 5;
41 // --- State ---
42  int    giServerFD;
43 tClient *gaClients;
44  int    giNumClients;
45
46 // === CODE ===
47 int main(int argc, char *argv[])
48 {
49         // TODO: Configure
50
51         // Initialise
52         gaClients = calloc(giConfig_MaxClients, sizeof(tClient));
53
54         // Open server
55         {
56                 uint8_t data[16];
57                  int    addrtype = Net_ParseAddress("10.0.2.10", data);
58                  int    port = 23;
59                 giServerFD = Net_OpenSocket(addrtype, data, "tcps");
60                 _SysIOCtl(giServerFD, 4, &port);        // Set port and start listening
61         }
62
63         // Event loop
64         EventLoop();
65         
66         return 0;
67 }
68
69 static void FD_SET_MAX(fd_set *set, int fd, int *maxfd)
70 {
71         FD_SET(fd, set);
72         if(*maxfd < fd) *maxfd = fd;
73 }
74
75 void EventLoop(void)
76 {
77         fd_set  fds;
78          int    maxfd;
79
80         for( ;; )
81         {
82                 FD_ZERO(&fds);
83                 maxfd = 0;
84                 // Fill select
85                 FD_SET_MAX(&fds, giServerFD, &maxfd);
86                 for( int i = 0; i < giConfig_MaxClients; i ++ )
87                 {
88                         if( gaClients[i].Socket == 0 )  continue ;
89                         _SysDebug("Socket = %i, pty = %i",
90                                 gaClients[i].Socket, gaClients[i].pty);
91                         FD_SET_MAX(&fds, gaClients[i].Socket, &maxfd);
92                         FD_SET_MAX(&fds, gaClients[i].pty,  &maxfd);
93                 }
94                 
95                 // Select!
96                 _SysSelect( maxfd+1, &fds, NULL, NULL, NULL, 0 );
97                 
98                 // Check events
99                 if( FD_ISSET(giServerFD, &fds) )
100                 {
101                         Server_NewClient(giServerFD);
102                 }
103                 for( int i = 0; i < giConfig_MaxClients; i ++ )
104                 {
105                         if( FD_ISSET(gaClients[i].Socket, &fds) )
106                         {
107                                 // Handle client data
108                                 HandleServerBoundData(&gaClients[i]);
109                         }
110                         if( FD_ISSET(gaClients[i].pty, &fds) )
111                         {
112                                 // Handle output from terminal
113                                 HandleClientBoundData(&gaClients[i]);
114                         }
115                 }
116         }
117 }
118
119 void Server_NewClient(int FD)
120 {
121         tClient *clt;
122         
123         // TODO: Is this done in the IPStack?
124         if( giNumClients == giConfig_MaxClients )
125         {
126                 // Open, reject
127                 _SysClose( _SysOpenChild(FD, "", OPENFLAG_READ) );
128                 return ;
129         }
130         
131         // Allocate client structure and open socket
132         for( int i = 0; i < giConfig_MaxClients; i ++ )
133         {
134                 if( gaClients[i].Socket == 0 ) {
135                         clt = &gaClients[i];
136                         break;
137                 }
138         }
139         // Accept the connection
140         clt->Socket = _SysOpenChild(FD, "", OPENFLAG_READ|OPENFLAG_WRITE);
141         giNumClients ++;
142         
143         // Create stdin/stdout
144         // - Current PTY code is strange with mknod
145         clt->pty = _SysOpen("/Devices/pts/telnetd0", OPENFLAG_CREATE|OPENFLAG_READ|OPENFLAG_WRITE);
146         if( clt->pty < 0 ) {
147                 perror("Unable to open server PTY");
148                 _SysClose(clt->Socket);
149                 clt->Socket = 0;
150                 return ;
151         }
152         
153         // TODO: Arguments and envp
154         {
155                  int    clientfd = _SysOpen("/Devices/pts/telnetd0c", OPENFLAG_READ|OPENFLAG_WRITE);
156                 if(clientfd < 0) {
157                         perror("Unable to open login PTY");
158                         _SysClose(clt->Socket);
159                         _SysClose(clt->pty);
160                         clt->Socket = 0;
161                         return ;
162                 }
163                 int fds[3] = {clientfd, clientfd, clientfd};
164                 const char      *argv[] = {"login", NULL};
165                 _SysSpawn("/Acess/SBin/login", argv, argv, 3, fds, NULL);
166         }
167 }
168
169 void HandleServerBoundData(tClient *Client)
170 {
171         uint8_t buf[BUFSIZ];
172         size_t  len;
173         
174         len = _SysRead(Client->Socket, buf, BUFSIZ);
175         if( len == 0 )  return ;
176         if( len == -1 ) {
177                 return ;
178         }
179         // handle options
180          int    last_flush = 0;
181         for( int i = 0; i < len; i ++ )
182         {
183                 switch(Client->Mode)
184                 {
185                 case MODE_IAC:
186                         Client->Mode = MODE_DATA;
187                         switch(buf[i])
188                         {
189                         case 240:       // End of subnegotiation parameters
190                         case 241:       // Nop
191                                 break;
192                         case 242:       // SYNCH
193                         case 243:       // NVT Break
194                         case 244:       // Function 'IP' (Ctrl-C)
195                                 
196                                 break;
197                         case 245:       // Function 'AO'
198                         case 246:       // Function 'AYT'
199                         case 247:       // Function 'EC'
200                         case 248:       // Function 'EL'
201                         case 249:       // GA Signal
202                         case 250:       // Subnegotation
203                                 break;
204                         case 251:       // WILL
205                                 Client->Mode = MODE_WILL;
206                                 break;
207                         case 252:       // WONT
208                                 Client->Mode = MODE_WILL;
209                                 break;
210                         case 253:       // DO
211                                 Client->Mode = MODE_WILL;
212                                 break;
213                         case 254:       // DONT
214                                 Client->Mode = MODE_WILL;
215                                 break;
216                         case 255:       // Literal 255
217                                 Client->Mode = MODE_DATA;
218                                 i --;   // hacky!
219                                 break;
220                         }
221                         break;
222                 case MODE_WILL:
223                         _SysDebug("Option %i WILL", buf[i]);
224                         Client->Mode = MODE_DATA;
225                         break;
226                 case MODE_WONT:
227                         _SysDebug("Option %i WONT", buf[i]);
228                         Client->Mode = MODE_DATA;
229                         break;
230                 case MODE_DO:
231                         _SysDebug("Option %i DO", buf[i]);
232                         Client->Mode = MODE_DATA;
233                         break;
234                 case MODE_DONT:
235                         _SysDebug("Option %i DONT", buf[i]);
236                         Client->Mode = MODE_DATA;
237                         break;
238                 case MODE_DATA:
239                         if( buf[i] == 255 )
240                                 Client->Mode = MODE_IAC;
241                         else
242                                 _SysWrite(Client->pty, buf+i, 1);
243                         break;
244                 }
245         }
246 }
247
248 void HandleClientBoundData(tClient *Client)
249 {
250         char    buf[BUFSIZ];
251          int    len;
252         
253         len = _SysRead(Client->pty, buf, BUFSIZ);
254         if( len <= 0 )  return ;
255         _SysWrite(Client->Socket, buf, len);
256 }
257

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