1 /******************************************************************************
2     (c) 2001-2008 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 // LAT login Program (llogin)
16 
17 #include <sys/types.h>
18 #include <sys/uio.h>
19 #include <sys/socket.h>
20 #include <sys/un.h>
21 #include <sys/ioctl.h>
22 #include <sys/wait.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/utsname.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <syslog.h>
33 #include <ctype.h>
34 #include <regex.h>
35 #include <stdlib.h>
36 // #include <utmp.h>
37 #include <signal.h>
38 #include <limits.h>
39 #include <assert.h>
40 #include <termios.h>
41 #ifdef HAVE_LOCKDEV_H
42 #include <lockdev.h>
43 #endif
44 
45 #include <list>
46 #include <queue>
47 #include <map>
48 #include <string>
49 #include <algorithm>
50 #include <iterator>
51 #include <string>
52 #include <sstream>
53 #include <iostream>
54 
55 #include "lat.h"
56 #include "latcp.h"
57 #include "utils.h"
58 #include "dn_endian.h"
59 
60 static int latcp_socket;
61 
62 static void make_upper(char *str);
63 static int  read_reply(int fd, int &cmd, unsigned char *&cmdbuf, int &len);
64 static bool send_msg(int fd, int cmd, char *buf, int len);
65 static bool open_socket(bool);
66 static int  terminal(int latfd, int, int, int, int, char *);
67 static int  do_use_port(char *service, int quit_char, int crlf, int bsdel, int lfvt, int nolock, char *logfile);
68 
usage(char * cmd)69 static int usage(char *cmd)
70 {
71     printf ("Usage: llogin [<option>] <service>\n");
72     printf ("     where option is one of the following:\n");
73     printf ("       -d         show learned services\n");
74     printf ("       -d -v      show learned services verbosely\n");
75     printf ("       -p         connect to a local device rather than a service\n");
76     printf ("       -f <file>  log all output to <file>\n");
77 #ifdef HAVE_LOCKDEV_H
78     printf ("       -L         Don't do device locking when using -p\n");
79 #endif
80     printf ("       -H <node>  remote node name\n");
81     printf ("       -R <port>  remote port name\n");
82     printf ("       -r <port>  remote port name\n");
83     printf ("       -Q         connect to a queued service\n");
84     printf ("       -c         convert input CR to LF\n");
85     printf ("       -b         convert input DEL to BS\n");
86     printf ("       -l         convert output LF to VT\n");
87     printf ("       -n <name>  Local port name\n");
88     printf ("       -w <pass>  Service password (-w- will prompt)\n");
89     printf ("       -q <char>  quit character\n");
90     printf ("       -h         display this usage message\n");
91     return 0;
92 }
93 
main(int argc,char * argv[])94 int main(int argc, char *argv[])
95 {
96     char msg[1024];
97     char node[256] = {'\0'};
98     char service[256] = {'\0'};
99     char port[256] = {'\0'};
100     char localport[256] = {'\0'};
101     char password[256] = {'\0'};
102     char logfile[PATH_MAX] = {'\0'};
103     signed char opt;
104     int verbose = 0;
105     int crlf = 1;
106     int bsdel = 0;
107     int lfvt = 0;
108     int show_services = 0;
109     int use_port = 0;
110     int is_queued = 0;
111     int quit_char = 0x1d; // Ctrl-]
112     int nolock = 0;
113 
114     if (argc == 1)
115     {
116 	exit(usage(argv[0]));
117     }
118 
119     // Set the default local port name
120     if (ttyname(0)) strcpy(localport, ttyname(0));
121 
122     while ((opt=getopt(argc,argv,"dpcvhlbQWLH:R:r:q:n:w:f:")) != EOF)
123     {
124 	switch(opt)
125 	{
126 	case 'd':
127 	    show_services = 1;
128 	    break;
129 
130 	case 'c':
131 	    crlf = 0;
132 	    break;
133 
134 	case 'b':
135 	    bsdel = 1;
136 	    break;
137 
138 	case 'l':
139 	    lfvt = 1;
140 	    break;
141 
142 	case 'L':
143 	    nolock = 1;
144 	    break;
145 
146 	case 'p':
147 	    use_port = 1;
148 	    break;
149 
150 	case 'q':
151 	    if (optarg[0] == '0')
152 		quit_char = -1;
153 	    else
154 		quit_char = toupper(optarg[0]) - '@';
155 	    break;
156 
157 	case 'Q':
158 	    is_queued = 1;
159 	    break;
160 
161 	case 'v':
162 	    verbose = 1;
163 	    break;
164 
165 	case 'H':
166 	    strcpy(node, optarg);
167 	    break;
168 
169 	case 'w':
170 	    strcpy(password, optarg);
171 	    break;
172 
173 	case 'W':
174 	    strcpy(password, "-");
175 	    break;
176 
177 	case 'R':
178 	case 'r':
179 	    strcpy(port, optarg);
180 	    break;
181 
182 	case 'f':
183 	    strcpy(logfile, optarg);
184 	    break;
185 
186 	case 'n':
187 	    strcpy(localport, optarg);
188 	    break;
189 
190 	default:
191 	    exit(usage(argv[0]));
192 	}
193     }
194 
195     // Parameter is the remote service name, but there
196     // must be only one.
197     if (argc == optind+1)
198     {
199 	strcpy(service, argv[argc-1]);
200     }
201     else
202     {
203 	if (!show_services) exit(usage(argv[0]));
204     }
205 
206     // This is just a bit like microcom...
207     if (use_port)
208     {
209 	do_use_port(service, quit_char, crlf, bsdel, lfvt, nolock, logfile);
210 	return 0;
211     }
212 
213     // If password is "-" then prompt so the user doesn't have to
214     // expose it on the command line.
215     if (password[0] == '-' && password[1] == '\0' && isatty(0))
216     {
217 	char *newpwd;
218 	newpwd = getpass("Password: ");
219 	if (newpwd == NULL || strlen(newpwd) > sizeof(password))
220 	{
221 	    printf("Password input cancelled");
222 	    return 0;
223 	}
224 	strcpy(password, newpwd);
225     }
226 
227     // Open socket to latd
228     if (!open_socket(false)) return 2;
229 
230     // -d just lists the available services
231     if (show_services)
232     {
233 	char verboseflag[1] = {verbose};
234 
235 	// This is the same as latcp -d -l
236 	send_msg(latcp_socket, LATCP_SHOWSERVICE, verboseflag, 1);
237 
238 	unsigned char *result = NULL;
239 	int len;
240 	int cmd;
241 	read_reply(latcp_socket, cmd, result, len);
242 
243 	std::cout << result;
244 
245 	delete[] result;
246 	return 0;
247     }
248 
249     make_upper(node);
250     make_upper(service);
251     make_upper(port);
252 
253 // Build up a terminal message for latd
254     int ptr=0;
255     msg[ptr++] = is_queued;
256     add_string((unsigned char*)msg, &ptr, (unsigned char*)service);
257     add_string((unsigned char*)msg, &ptr, (unsigned char*)node);
258     add_string((unsigned char*)msg, &ptr, (unsigned char*)port);
259     add_string((unsigned char*)msg, &ptr, (unsigned char*)localport);
260     add_string((unsigned char*)msg, &ptr, (unsigned char*)password);
261 
262     send_msg(latcp_socket, LATCP_TERMINALSESSION, msg, ptr);
263 
264     unsigned char *result = NULL;
265     int len;
266     int cmd;
267     int ret;
268     ret = read_reply(latcp_socket, cmd, result, len);
269     if (ret)
270 	return ret;
271 
272     delete[] result;
273 
274     // If the reply was good then go into terminal mode.
275     terminal(latcp_socket, quit_char, crlf, bsdel, lfvt, logfile);
276 
277     shutdown(latcp_socket, 3);
278     close(latcp_socket);
279     return 0;
280 }
281 
282 
283 // Return 0 for success and -1 for failure
read_reply(int fd,int & cmd,unsigned char * & cmdbuf,int & len)284 static int read_reply(int fd, int &cmd, unsigned char *&cmdbuf, int &len)
285 {
286     unsigned char head[3];
287 
288     // Get the message header (cmd & length)
289     if (read(fd, head, sizeof(head)) != 3)
290 	return -1; // Bad header
291 
292     len = head[1] * 256 + head[2];
293     cmd = head[0];
294     cmdbuf = new unsigned char[len];
295     if (cmdbuf == NULL)
296 	return -1;
297     // Read the message buffer
298     if (read(fd, cmdbuf, len) != len)
299     {
300 	return -1; // Bad message
301     }
302 
303     if (cmd == LATCP_ERRORMSG)
304     {
305 	fprintf(stderr, "%s\n", cmdbuf);
306 	return -1;
307     }
308 
309     return 0;
310 }
311 
open_socket(bool quiet)312 static bool open_socket(bool quiet)
313 {
314     struct sockaddr_un sockaddr;
315 
316     latcp_socket = socket(PF_UNIX, SOCK_STREAM, 0);
317     if (latcp_socket == -1)
318     {
319 	if (!quiet) perror("Can't create socket");
320 	return false; /* arggh ! */
321     }
322 
323     strcpy(sockaddr.sun_path, LLOGIN_SOCKNAME);
324     sockaddr.sun_family = AF_UNIX;
325     if (connect(latcp_socket, (struct sockaddr *)&sockaddr, sizeof(sockaddr)))
326     {
327 	if (!quiet) perror("Can't connect to latd");
328 	close(latcp_socket);
329 	return false;
330     }
331 
332     unsigned char *result = NULL;
333     int len;
334     int cmd;
335 
336     // Send our version
337     send_msg(latcp_socket, LATCP_VERSION, (char *)VERSION, strlen(VERSION)+1);
338     read_reply(latcp_socket, cmd, result, len); // Read version number back
339 
340     delete[] result;
341     return true;
342 }
343 
make_upper(char * str)344 static void make_upper(char *str)
345 {
346     unsigned int i;
347 
348     for (i=0; i<strlen(str); i++)
349     {
350 	str[i] = toupper(str[i]);
351     }
352 }
353 
354 
send_msg(int fd,int cmd,char * buf,int len)355 static bool send_msg(int fd, int cmd, char *buf, int len)
356 {
357     unsigned char outhead[3];
358 
359     outhead[0] = cmd;
360     outhead[1] = len/256;
361     outhead[2] = len%256;
362     if (write(fd, outhead, 3) != 3) return false;
363     if (write(fd, buf, len) != len) return false;
364 
365     return true;
366 }
367 
368 // Pretend to be a terminal connected to a LAT service
terminal(int latfd,int endchar,int crlf,int bsdel,int lfvt,char * logfile)369 static int terminal(int latfd, int endchar, int crlf, int bsdel, int lfvt, char *logfile)
370 {
371     int termfd = STDIN_FILENO;
372     struct termios old_term;
373     struct termios new_term;
374     FILE *logstream = NULL;
375 
376     tcgetattr(termfd, &old_term);
377     new_term = old_term;
378 
379 // Open the logfile, this is not fatal if it fails but
380 // have the courtesy to tell the user.
381     if (logfile[0])
382     {
383 	logstream = fopen(logfile, "w+");
384 	if (!logstream)
385 	    perror("Can't open logfile");
386     }
387 
388 // Set local terminal characteristics
389     new_term.c_iflag &= ~BRKINT;
390     new_term.c_iflag |= IGNBRK;
391     new_term.c_lflag &= ~ISIG;
392 #ifdef OCRNL
393     new_term.c_oflag &= ~OCRNL;
394 #endif
395     new_term.c_oflag &= ~ONLCR;
396     new_term.c_cc[VMIN] = 1;
397     new_term.c_cc[VTIME] = 0;
398     new_term.c_lflag &= ~ICANON;
399     new_term.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
400     tcsetattr(termfd, TCSANOW, &new_term);
401 
402     while(true)
403     {
404 	char inbuf[1024];
405 	fd_set in_set;
406 	FD_ZERO(&in_set);
407 	FD_SET(termfd, &in_set);
408 	FD_SET(latfd, &in_set);
409 
410 	if (select(FD_SETSIZE, &in_set, NULL, NULL, NULL) < 0)
411 	{
412 	    break;
413 	}
414 
415 	// Read from keyboard
416 	if (FD_ISSET(termfd, &in_set))
417 	{
418 	    int len;
419 	    int i;
420 
421 	    if (( (len=read(termfd, &inbuf, sizeof(inbuf))) < 1))
422 	    {
423 		break;
424 	    }
425 
426 	    for (i=0; i<len; i++)
427 	    {
428 		if (endchar >0 && inbuf[i] == endchar)
429 		    goto quit;
430 
431 		if (inbuf[i] == '\n' && crlf)
432 		    inbuf[i] = '\r';
433 
434 		if (inbuf[i] == '\177' && bsdel)
435 		    inbuf[i] = '\010';
436 	    }
437 	    write(latfd, inbuf, len);
438 	    if (logstream)
439 		fwrite(inbuf, len, 1, logstream);
440 	}
441 
442 	// Read from LAT socket. buffered.
443 	if (FD_ISSET(latfd, &in_set))
444 	{
445 	    int len;
446 	    if ( (len = read(latfd, &inbuf, sizeof(inbuf))) < 1)
447 		break;
448 	    else
449 	    {
450 		if (lfvt)
451 		{
452 		    for (int i=0; i<len; i++)
453 			if (inbuf[i] == '\n')
454 			    inbuf[i] = '\v';
455 		}
456 		write(termfd, inbuf, len);
457 		if (logstream)
458 		    fwrite(inbuf, len, 1, logstream);
459 
460 	    }
461 	}
462     }
463  quit:
464     // Reset terminal attributes
465     tcsetattr(termfd, TCSANOW, &old_term);
466     printf("\n");
467 
468     return 0;
469 }
470 
do_use_port(char * portname,int quit_char,int crlf,int bsdel,int lfvt,int nolock,char * logfile)471 static int do_use_port(char *portname, int quit_char, int crlf, int bsdel, int lfvt, int nolock, char *logfile)
472 {
473     int termfd;
474     struct termios old_term;
475     struct termios new_term;
476 
477 #ifdef HAVE_LOCKDEV_H
478     if (!nolock)
479     {
480 	if (dev_lock(portname))
481 	{
482 	    fprintf(stderr, "Can't lock Device %s\n", portname);
483 	    return -1;
484 	}
485     }
486 #endif
487 
488     termfd = open(portname, O_RDWR);
489     if (termfd < 0)
490     {
491 	fprintf(stderr, "Cannot open device %s: %s\n", portname, strerror(errno));
492 #ifdef HAVE_LOCKDEV_H
493 	dev_unlock(portname, getpid());
494 #endif
495 	return -1;
496     }
497 
498     tcgetattr(termfd, &old_term);
499     new_term = old_term;
500 
501     // Set local terminal characteristics
502     new_term.c_iflag &= ~(BRKINT | ICRNL);
503     new_term.c_iflag |= IGNBRK;
504     new_term.c_lflag &= ~ISIG;
505 #ifdef OCRNL
506     new_term.c_oflag &= ~OCRNL;
507 #endif
508     new_term.c_cc[VMIN] = 1;
509     new_term.c_cc[VTIME] = 0;
510     new_term.c_lflag &= ~ICANON;
511     new_term.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
512     tcsetattr(termfd, TCSANOW, &new_term);
513 
514     // Be a terminal
515     terminal(termfd, quit_char, crlf, bsdel, lfvt, logfile);
516 
517     // Reset terminal attributes
518     tcsetattr(termfd, TCSANOW, &old_term);
519     close(termfd);
520 
521 #ifdef HAVE_LOCKDEV_H
522     if (!nolock) dev_unlock(portname, getpid());
523 #endif
524     return 0;
525 }
526