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