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