1 /******************************************************************************
2 (c) 2001,2005 Christine Caulfield christine.caulfield@googlemail.com
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 ******************************************************************************/
14
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <sys/ioctl.h>
18 #include <sys/wait.h>
19 #include <sys/stat.h>
20 #include <sys/time.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <syslog.h>
29 #include <ctype.h>
30 #include <regex.h>
31 #include <stdlib.h>
32 // #include <utmp.h>
33 #include <pwd.h>
34 #include <assert.h>
35 #include <termios.h>
36
37 #include <list>
38 #include <string>
39 #include <map>
40 #include <queue>
41 #include <sstream>
42
43 #include "lat.h"
44 #include "utils.h"
45 #include "session.h"
46 #include "localport.h"
47 #include "connection.h"
48 #include "circuit.h"
49 #include "latcpcircuit.h"
50 #include "server.h"
51
add_credit(signed short c)52 void LATSession::add_credit(signed short c)
53 {
54 if (credit < 0)
55 {
56 debuglog(("ARGGHH: credit is negative\n"));
57 credit = 0;
58 }
59 credit += c;
60
61 if (stopped && credit)
62 {
63 debuglog(("Got some more credit, (+%d=%d) carrying on\n", c, credit));
64 stopped = false;
65 LATServer::Instance()->set_fd_state(master_fd, false);
66 // tcflow(master_fd, TCOON);
67 }
68 }
69
70 #define REALLY_VERBOSE_DEBUGLOG
71 // Try to write all the data to the PTY
writeall(int fd,unsigned char * buf,int len)72 int LATSession::writeall(int fd, unsigned char *buf, int len)
73 {
74 int done = 0;
75
76 while (done < len)
77 {
78 int count;
79 debuglog(("writeall::done = %d, len = %d\n", done, len));
80 count = ::write(fd, buf+done, len-done);
81 if (count > 0)
82 {
83 done += count;
84 }
85 else
86 {
87 debuglog(("writeall: write returned %d, errno = %s\n", count, strerror(errno)));
88 return -1;
89 }
90 }
91 return len;
92 }
93
94 /* Got some data from the terminal - send it to the PTY */
send_data_to_process(unsigned char * buf,int len)95 int LATSession::send_data_to_process(unsigned char *buf, int len)
96 {
97 // If there's anything to send, do so
98 if (len)
99 {
100 #ifdef REALLY_VERBOSE_DEBUGLOG
101 char debugbuf[1024];
102 memcpy(debugbuf, buf, len);
103 debugbuf[len] = 0;
104 debuglog(("To PTY(%d): %s\n", len, debugbuf));
105 #endif
106 remote_credit--;
107
108 // Replace LF/CR with LF if we are a server.
109 if (!parent.isClient() && !clean)
110 {
111 unsigned char newbuf[len*2];
112 int newlen;
113
114 crlf_to_lf(buf, len, newbuf, &newlen);
115 writeall(master_fd, newbuf, newlen);
116 }
117 else
118 {
119 writeall(master_fd, buf, len);
120 }
121 }
122
123 // See if there's any echo. Anything longer than the data sent
124 // is not a character echo and so will be sent as
125 // unsolicited data.
126 int numbytes;
127 sleep(0); // Give it a slight chance of generating an echo
128 ioctl(master_fd, FIONREAD, &numbytes);
129 debuglog(("%d echo bytes available\n", numbytes));
130 if (numbytes == 0 || numbytes != len)
131 {
132 echo_expected = false;
133 return 1;
134 }
135 echo_expected = true;
136 return 0;
137 }
138
139 /* Read some data from the PTY and send it to the LAT terminal */
read_pty()140 int LATSession::read_pty()
141 {
142 unsigned char buf[256];
143 int command = 0x00;
144 int msglen;
145
146 if (credit <= 0)
147 {
148 // Disable the FD so we don't start looping
149 if (!stopped)
150 {
151 LATServer::Instance()->set_fd_state(master_fd, true);
152 stopped = true;
153 // tcflow(master_fd, TCOOFF);
154 }
155 return 0; // Not allowed!
156 }
157 msglen = read(master_fd, buf, max_read_size);
158 // TODO BUG:
159 // If the the PTY is closed while we are stopped then we
160 // don't notice until the remote end sends more credit...
161
162
163 #ifdef REALLY_VERBOSE_DEBUGLOG
164 if (msglen > 0)
165 {
166 buf[msglen] = '\0';
167 char tmp = buf[10];
168 buf[10] = '\0'; // Just a sample
169 debuglog(("Session %d From PTY(%d): '%s%s'\n", local_session, msglen,
170 buf, (msglen>10)?"...":""));
171 buf[10] = tmp;
172 }
173 #else
174 // debuglog(("Session %d From PTY(%d)\n", local_session, msglen));
175 #endif
176
177 // EOF or error on PTY - tell remote end to disconnect
178 if (msglen <= 0)
179 {
180 if (msglen < 0 && errno == EAGAIN) return 0; // Just no data.
181
182 debuglog(("EOF on PTY\n"));
183 unsigned char slotbuf[5];
184 int ptr = 0;
185
186 // Send attention & disconnect
187 slotbuf[0] = 0x40;
188 add_slot(buf, ptr, 0xb0, slotbuf, 1);
189
190 // Tru64 (and DS500 I think) don't like the
191 // local session ID to be set on the final disconnect slot
192 int s=local_session;
193 local_session=0;
194
195 add_slot(buf, ptr, 0xd1, slotbuf, 0);
196 local_session=s; // Restore it for our benefit.
197
198 parent.queue_message(buf, ptr);
199
200 disconnect_session(1); // User requested
201 return 0;
202 }
203
204 // Got break!
205 if (msglen == 1 && buf[0] == '\0' && !clean)
206 {
207 return send_break();
208 }
209 else
210 {
211 return send_data(buf, msglen, command);
212 }
213 }
214
send_break()215 int LATSession::send_break()
216 {
217 unsigned char buf[1600];
218 unsigned char slotbuf[10];
219 int ptr = 0;
220 int command = 0xA0;
221
222 if (remote_credit <= 1)
223 {
224 debuglog(("Sending more remote credit with break\n"));
225 remote_credit += 5;
226 command |= 0x5;
227 }
228
229 slotbuf[0] = 0x10;
230 add_slot(buf, ptr, command, slotbuf, 1);
231
232 parent.queue_message(buf, ptr);
233 return 0;
234 }
235
send_data(unsigned char * buf,int msglen,int command)236 int LATSession::send_data(unsigned char *buf, int msglen, int command)
237 {
238 unsigned char reply[1600];
239 LAT_SlotCmd *header = (LAT_SlotCmd *)reply;
240 int ptr;
241
242 #ifdef REALLY_VERBOSE_DEBUGLOG
243 debuglog(("Local Credit stands at %d\n", credit));
244 debuglog(("Remote Credit stands at %d\n", remote_credit));
245 #endif
246
247 if (remote_credit <= 1)
248 {
249 debuglog(("Sending more remote credit\n"));
250 remote_credit += 5;
251 command |= 0x5;
252 }
253
254 // Send response...
255 header->remote_session = local_session;
256 header->local_session = remote_session;
257 header->length = msglen;
258 header->cmd = command;
259
260 ptr = sizeof(LAT_SlotCmd);
261 memcpy(reply+ptr, buf, msglen);
262
263 parent.send_slot_message(reply, ptr+msglen);
264
265 // Have we now run out of credit ??
266 if (--credit <= 0)
267 {
268 LATServer::Instance()->set_fd_state(master_fd, true);
269 debuglog(("Out of credit...Stop\n"));
270 stopped = true;
271 // tcflow(master_fd, TCOOFF);
272 }
273
274 return 0;
275 }
276
277
278 // Remote end disconnects
disconnect_session(int reason)279 void LATSession::disconnect_session(int reason)
280 {
281 // Get the server to delete us when we are off the stack.
282 if (connected)
283 LATServer::Instance()->delete_session(parent.get_connection_id(), local_session, master_fd);
284 connected = false;
285 return;
286 }
287
288
send_disabled_message()289 void LATSession::send_disabled_message()
290 {
291 unsigned char replybuf[1600];
292 LAT_SessionReply *reply = (LAT_SessionReply *)replybuf;
293
294 debuglog(("Sending DISABLED message\n"));
295
296 reply->header.cmd = LAT_CCMD_SREPLY;
297 reply->header.num_slots = 1;
298 reply->slot.remote_session = local_session;
299 reply->slot.local_session = remote_session;
300 reply->slot.length = 0;
301 reply->slot.cmd = 0xc8; // Disconnect session.
302
303 parent.send_message(replybuf, sizeof(LAT_SessionReply),
304 LATConnection::REPLY);
305 }
306
~LATSession()307 LATSession::~LATSession()
308 {
309 if (pid != -1) kill(pid, SIGTERM);
310 if (master_fd > -1) close(master_fd);
311 disconnect_session(0);
312 }
313
314
315 // Send the issue.net greeting file
send_issue()316 void LATSession::send_issue()
317 {
318 char issue_name[PATH_MAX];
319 struct stat st;
320
321 // Look for /etc/issue.lat.<service>, /etc/issue.lat, /etc/issue.net
322 sprintf(issue_name, "/etc/issue.lat.%s", parent.get_servicename());
323 if (stat(issue_name, &st))
324 {
325 strcpy(issue_name, "/etc/issue.lat");
326 if (stat(issue_name, &st))
327 {
328 strcpy(issue_name, "/etc/issue.net");
329 }
330 }
331
332 // Send /etc/issue.net
333 int f = open(issue_name, O_RDONLY);
334 if (f >= 0)
335 {
336 char *issue = new char[255];
337 char *newissue = new char[512];
338
339 size_t len = read(f, issue, 255);
340 close(f);
341
342 size_t newlen = 0;
343 if (len == 0) return;
344 if (len > 255) len = 255;
345
346 // Start with a new line
347 newissue[newlen++] = '\r';
348 newissue[newlen++] = '\n';
349
350 newlen = expand_issue(issue, len, newissue, 255, parent.get_servicename());
351 echo_expected = false;
352 if (newlen > 255) newlen = 255;
353
354 send_data((unsigned char *)newissue, newlen, 0x01);
355 delete[] issue;
356 delete[] newissue;
357 }
358 }
359
set_port(unsigned char * inbuf)360 void LATSession::set_port(unsigned char *inbuf)
361 {
362 unsigned char *ptr = inbuf+sizeof(LAT_SlotCmd);
363
364 if (*ptr & 0x10) /* BREAK */
365 {
366 debuglog(("Sending break\n"));
367 tcsendbreak(master_fd, 0);
368 }
369 }
370
371 // Add a slot to an existing message
add_slot(unsigned char * buf,int & ptr,int slotcmd,unsigned char * slotdata,int len)372 void LATSession::add_slot(unsigned char *buf, int &ptr, int slotcmd,
373 unsigned char *slotdata, int len)
374 {
375 // Allow the caller to initialize ptr to zero and we will do the rest
376 if (ptr == 0)
377 {
378 ptr = sizeof(LAT_Header);
379 LAT_Header *h = (LAT_Header *)buf;
380 h->num_slots = 0;
381 }
382
383 // Write the slot header
384 LAT_SlotCmd *slot = (LAT_SlotCmd *)(buf+ptr);
385 ptr += sizeof(LAT_SlotCmd);
386 slot->length = len;
387 slot->cmd = slotcmd;
388 slot->remote_session = local_session;
389 slot->local_session = remote_session;
390
391
392 // Copy the data
393 memcpy(buf+ptr, slotdata, len);
394 ptr += len;
395 if (ptr%2) ptr++; // Word aligned
396
397
398 // Increment the number of slots.
399 LAT_Header *header = (LAT_Header *)buf;
400 header->num_slots++;
401 if (parent.isClient())
402 header->cmd = LAT_CCMD_SESSION;
403 else
404 header->cmd = LAT_CCMD_SDATA;
405 }
406
crlf_to_lf(unsigned char * buf,int len,unsigned char * newbuf,int * newlen)407 void LATSession::crlf_to_lf(unsigned char *buf, int len,
408 unsigned char *newbuf, int *newlen)
409 {
410 int i;
411 int len2 = 0;
412
413 len2 = 0;
414
415 for (i=0; i<len; i++)
416 {
417 if (i>0 && i<len && (buf[i] == '\r') && (buf[i-1] == '\n'))
418 {
419 continue;
420 }
421 if (i>0 && i<len && (buf[i] == '\n') && (buf[i-1] == '\r'))
422 {
423 newbuf[len2-1] = '\n';
424 continue;
425 }
426 newbuf[len2++] = buf[i];
427 }
428 *newlen = len2;
429 }
430
431
connect()432 void LATSession::connect()
433 {
434 state = RUNNING;
435 debuglog(("connecting client session to '%s'\n", remote_service));
436
437 // OK, now send a Start message to the remote end.
438 unsigned char buf[1600];
439 memset(buf, 0, sizeof(buf));
440 LAT_SessionData *reply = (LAT_SessionData *)buf;
441 int ptr = sizeof(LAT_SessionData);
442
443 buf[ptr++] = 0x01; // Service Class
444 buf[ptr++] = 0x01; // Max Attention slot size..
445 buf[ptr++] = 0xfe; // Max Data slot size
446
447 add_string(buf, &ptr, (unsigned char *)remote_service);
448 buf[ptr++] = 0x00; // Source service length/name
449
450 buf[ptr++] = 0x01; // Param type 1
451 buf[ptr++] = 0x02; // Param Length 2
452 buf[ptr++] = 0x04; // Value 1024
453 buf[ptr++] = 0x00; //
454
455 buf[ptr++] = 0x05; // Param type 5 (Local PTY name)
456 add_string(buf, &ptr, (unsigned char *)ltaname);
457
458 // If the user wanted a particular port number then add it
459 // into the message
460 if (remote_port[0] != '\0')
461 {
462 buf[ptr++] = 0x04; // Param type 4 (Remote port name)
463 add_string(buf, &ptr, (unsigned char *)remote_port);
464 buf[ptr++] = 0x00; // NUL terminated (??)
465 }
466
467
468 // Add in the request ID for reverse connections
469 if (request_id)
470 {
471 debuglog(("Sending request id %d, and port %s\n", request_id, ptyname));
472 buf[ptr++] = 2; // Parameter number
473 buf[ptr++] = 2; // Length of the short int
474 buf[ptr++] = request_id & 0xFF;
475 buf[ptr++] = request_id >> 8;
476
477 buf[ptr++] = 5;
478 add_string(buf, &ptr, (unsigned char *)ptyname);
479 }
480
481 // Send message...
482 reply->header.cmd = LAT_CCMD_SESSION;
483 reply->header.num_slots = 1;
484 reply->slot.remote_session = local_session;
485 reply->slot.local_session = remote_session;
486 reply->slot.length = ptr - sizeof(LAT_SessionData);
487 reply->slot.cmd = 0x9f;
488
489 parent.queue_message(buf, ptr);
490 }
491
492
got_connection(unsigned char _remid)493 void LATSession::got_connection(unsigned char _remid)
494 {
495 unsigned char buf[1600];
496 LAT_SessionReply *reply = (LAT_SessionReply *)buf;
497 int ptr = 0;
498
499 debuglog(("Session:: got connection for rem session %d\n", _remid));
500 LATServer::Instance()->set_fd_state(master_fd, false);
501 remote_session = _remid;
502
503 // Send a data_b slot
504 if (parent.isClient())
505 reply->header.cmd = LAT_CCMD_SESSION;
506 else
507 reply->header.cmd = LAT_CCMD_SREPLY;
508 reply->header.num_slots = 0;
509 reply->slot.length = 0;
510 reply->slot.cmd = 0x0;
511 reply->slot.local_session = 0;
512 reply->slot.remote_session = 0;
513
514 // data_b slots count against credit
515 if (credit)
516 {
517 unsigned char slotbuf[256];
518 int slotptr = 0;
519
520 slotbuf[slotptr++] = 0x26; // Flags
521 slotbuf[slotptr++] = 0x13; // Stop output char XOFF
522 slotbuf[slotptr++] = 0x11; // Start output char XON
523 slotbuf[slotptr++] = 0x13; // Stop input char XOFF
524 slotbuf[slotptr++] = 0x11; // Start input char XON
525
526 add_slot(buf, ptr, 0xaf, slotbuf, slotptr);
527 credit--;
528 }
529
530 parent.queue_message(buf, ptr);
531
532 connected = true;
533 }
534