1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23/*	  All Rights Reserved	*/
24
25/*
26 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
27 * Use is subject to license terms.
28 */
29
30#include "mt.h"
31#include "uucp.h"
32
33static void alarmtr(int);
34static jmp_buf Sjbuf;
35static char *fdig(char *);
36#ifndef SMALL
37static char *strecpy(char *, char *, char *);
38#endif
39static int interface(const char *);
40static int fd_mklock(int);
41static int getdialline(char *, int);
42static int chat(int, char *[], int, char *, char *);
43static void fixline(), fd_rmlock();
44static void translate(char *, char *);
45static int gdial(char *, char *[], int);
46static int	Modemctrl;
47static unsigned connecttime;
48static int (*Setup)();
49
50/*
51 *	to add a new caller:
52 *	declare the function that knows how to call on the device,
53 *	add a line to the callers table giving the name of the device
54 *	(from Devices file) and the name of the function
55 *	add the function to the end of this file
56 */
57
58#ifdef TLI
59static int	tlicall(char *[], char *[]);
60#endif /* TLI */
61
62static struct caller Caller[] = {
63
64#ifdef TLI
65	{"TLI",		tlicall},	/* AT&T Transport Layer Interface */
66#ifdef TLIS
67	{"TLIS",	tlicall},	/* AT&T Transport Layer Interface */
68#endif /*  TLIS  */
69#endif /* TLI */
70
71	{NULL,		NULL}		/* this line must be last */
72};
73
74/*
75 *	exphone - expand phone number for given prefix and number
76 *
77 *	return code - none
78 */
79
80static void
81exphone(char *in, char *out)
82{
83	FILE *fn;
84	char pre[MAXPH], npart[MAXPH], tpre[MAXPH], p[MAXPH];
85	char buf[BUFSIZ];
86	char *s1;
87
88	if (!isalpha(*in)) {
89		(void) strcpy(out, in);
90		return;
91	}
92
93	s1 = pre;
94	while (isalpha(*in))
95		*s1++ = *in++;
96	*s1 = NULLCHAR;
97	s1 = npart;
98	while (*in != NULLCHAR)
99		*s1++ = *in++;
100	*s1 = NULLCHAR;
101
102	tpre[0] = NULLCHAR;
103	fn = fopen(DIALCODES, "rF");
104	if (fn != NULL) {
105		while (fgets(buf, BUFSIZ, fn)) {
106			if (sscanf(buf, "%60s%60s", p, tpre) < 1)
107				continue;
108			if (EQUALS(p, pre))
109				break;
110			tpre[0] = NULLCHAR;
111		}
112		(void) fclose(fn);
113	}
114
115	(void) strcpy(out, tpre);
116	(void) strcat(out, npart);
117}
118
119/*
120 * repphone - Replace \D and \T sequences in arg with phone
121 * expanding and translating as appropriate.
122 */
123static char *
124repphone(char *arg, char *phone, char *trstr)
125{
126	static char *pbuf;	/* dynamically allocated below */
127	char *fp, *tp;
128
129	if (pbuf == NULL) {
130		pbuf = malloc(2*(MAXPH+2));
131		if (pbuf == NULL)
132			return (arg);
133	}
134	for (tp = pbuf; *arg; arg++) {
135		if (*arg != '\\') {
136			*tp++ = *arg;
137			continue;
138		} else {
139			switch (*(arg+1)) {
140			case 'T':
141				exphone(phone, tp);
142				translate(trstr, tp);
143				for (; *tp; tp++)
144					;
145				arg++;
146				break;
147			case 'D':
148				for (fp = phone; *tp = *fp++; tp++)
149					;
150				arg++;
151				break;
152			default:
153				*tp++ = *arg;
154				break;
155			}
156		}
157	}
158	*tp = '\0';
159	return (pbuf);
160}
161
162static uint_t saved_mode;
163static char saved_dcname[20];
164
165static int pop_push(int);
166static void setdevcfg(char *, char *);
167static void ttygenbrk(int);
168/*
169 * processdev - Process a line from the Devices file
170 *
171 * return codes:
172 *	file descriptor  -  succeeded
173 *	FAIL  -  failed
174 */
175static int
176processdev(char *flds[], char *dev[])
177{
178	int dcf = -1;
179	struct caller	*ca;
180	char *args[D_MAX+1], dcname[20];
181	char **sdev;
182	int nullfd;
183	char *phonecl;			/* clear phone string */
184	char phoneex[2*(MAXPH+2)];	/* expanded phone string */
185	struct termio tty_orig;
186	int ret_orig = -1;
187
188	sdev = dev;
189	/*	set up default "break" routine	*/
190	genbrk = ttygenbrk;
191
192	/*	initialize Devconfig info	*/
193	DEBUG(5, "processdev: calling setdevcfg(%s, ", Progname);
194	DEBUG(5, "%s)\n", flds[F_TYPE]);
195	setdevcfg(Progname, flds[F_TYPE]);
196
197	for (ca = Caller; ca->CA_type != NULL; ca++) {
198		/* This will find built-in caller functions */
199		if (EQUALS(ca->CA_type, dev[D_CALLER])) {
200			DEBUG(5, "Internal caller type %s\n", dev[D_CALLER]);
201			if (dev[D_ARG] == NULL) {
202				/* if NULL - assume translate */
203				/* needed for for loop later to mark the end */
204				dev[D_ARG+1] = NULL;
205				dev[D_ARG] = "\\T";
206			}
207			dev[D_ARG] = repphone(dev[D_ARG], flds[F_PHONE], "");
208			if ((dcf = (*(ca->CA_caller))(flds, dev)) < 0)
209				return (dcf);
210			if (interface(ca->CA_type)) {
211				DEBUG(5, "interface(%s) failed", ca->CA_type);
212				Uerror = SS_DEVICE_FAILED;
213				/*	restore vanilla unix interface	*/
214				(void) interface("UNIX");
215				return (FAIL);
216			}
217			dev += 2; /* Skip to next CALLER and ARG */
218			break;
219		}
220	}
221	if (dcf == -1) {
222		/* Here if not a built-in caller function */
223
224		/* We do locking (file and advisory) after open	*/
225
226		/*
227		 * Open the line
228		 */
229		if (*dev[D_LINE] != '/') {
230			(void) snprintf(dcname, sizeof (dcname),
231			    "/dev/%s", dev[D_LINE]);
232		} else {
233			(void) strcpy(dcname, dev[D_LINE]);
234		}
235		/* take care of the possible partial open fd */
236		(void) close(nullfd = open("/", O_RDONLY));
237		if (setjmp(Sjbuf)) {
238			(void) close(nullfd);
239			DEBUG(1, "generic open timeout\n%s", "");
240			logent("generic open", "TIMEOUT");
241			Uerror = SS_CANT_ACCESS_DEVICE;
242			goto bad;
243		}
244		(void) signal(SIGALRM, alarmtr);
245		(void) alarm(10);
246		if (Modemctrl) {
247			DEBUG(7, "opening with O_NDELAY set\n%s", "");
248			dcf = open(dcname, (O_RDWR | O_NDELAY));
249			saved_mode = O_RDWR | O_NDELAY;
250		} else {
251			dcf = open(dcname, O_RDWR);
252			saved_mode = O_RDWR;
253		}
254		(void) strcpy(saved_dcname, dcname);
255		(void) alarm(0);
256		if (dcf < 0) {
257			DEBUG(1, "generic open failed, errno = %d\n", errno);
258			(void) close(nullfd);
259			logent("generic open", "FAILED");
260			Uerror = SS_CANT_ACCESS_DEVICE;
261			goto bad;
262		}
263
264		/* check locks BEFORE modifying the stream */
265
266		if (fd_mklock(dcf) != SUCCESS) {
267			DEBUG(1, "failed to lock device %s\n", dcname);
268			Uerror = SS_LOCKED_DEVICE;
269			goto bad;
270		}
271
272		if (Modemctrl) {
273			DEBUG(7, "clear O_NDELAY\n%s", "");
274			if (fcntl(dcf, F_SETFL,
275			    (fcntl(dcf, F_GETFL, 0) & ~O_NDELAY)) < 0) {
276				DEBUG(7, "clear O_NDELAY failed, errno %d\n",
277				    errno);
278				Uerror = SS_DEVICE_FAILED;
279				goto bad;
280			}
281		}
282	}
283
284	if ((*Setup)(MASTER, &dcf, &dcf)) {
285		/*	any device|system lock files we should remove?	*/
286		DEBUG(5, "MASTER Setup failed%s", "");
287		Uerror = SS_DEVICE_FAILED;
288		goto bad;
289	}
290
291	/* configure any requested streams modules */
292	if (!pop_push(dcf)) {
293		DEBUG(5, "STREAMS module configuration failed%s\n", "");
294		Uerror = SS_DEVICE_FAILED;
295		goto bad;
296	}
297
298	/* save initial state of line in case script fails */
299	ret_orig = ioctl(dcf, TCGETA, &tty_orig);
300
301	/* use sdev[] since dev[] is incremented for internal callers */
302	fixline(dcf, atoi(fdig(sdev[D_CLASS])), D_DIRECT);
303
304	/*
305	 * Now loop through the remaining callers and chat
306	 * according to scripts in dialers file.
307	 */
308	for (; dev[D_CALLER] != NULL; dev += 2) {
309		int w;
310		/*
311		 * Scan Dialers file to find an entry
312		 */
313		if ((w = gdial(dev[D_CALLER], args, D_MAX)) < 1) {
314			logent("generic call to gdial", "FAILED");
315			Uerror = SS_CANT_ACCESS_DEVICE;
316			goto bad;
317		}
318		if (w <= 2)	/* do nothing - no chat */
319			break;
320		/*
321		 * Translate the phone number
322		 */
323		if (dev[D_ARG] == NULL) {
324			/* if NULL - assume no translation */
325			/* needed for for loop to mark the end */
326			dev[D_ARG+1] = NULL;
327			dev[D_ARG] = "\\D";
328		}
329
330		phonecl = repphone(dev[D_ARG], flds[F_PHONE], args[1]);
331		exphone(phonecl, phoneex);
332		translate(args[1], phoneex);
333		/*
334		 * Chat
335		 */
336		if (chat(w-2, &args[2], dcf, phonecl, phoneex) != SUCCESS) {
337			CDEBUG(5, "\nCHAT gdial(%s) FAILED\n", dev[D_CALLER]);
338			Uerror = SS_CHAT_FAILED;
339			goto bad;
340		}
341	}
342	/*
343	 * Success at last!
344	 */
345	(void) strcpy(Dc, sdev[D_LINE]);
346	return (dcf);
347bad:
348	if (dcf >= 0) {
349		/* reset line settings if we got them in the beginning */
350		if (ret_orig == 0)
351			(void) ioctl(dcf, TCSETAW, &tty_orig);
352		fd_rmlock(dcf);
353		(void) close(dcf);
354	}
355	/*	restore vanilla unix interface	*/
356	(void) interface("UNIX");
357	return (FAIL);
358}
359
360/*
361 * clear_hup()	clear the hangup state of the given device
362 */
363static int
364clear_hup(int dcf)
365{
366	int ndcf;
367	if ((ndcf = open(saved_dcname, saved_mode)) < 0) {
368		return (FAIL);
369	}
370	if (ndcf != dcf) {
371		(void) close(ndcf);
372	}
373	return (SUCCESS);
374}
375
376
377/*
378 * translate the pairs of characters present in the first
379 * string whenever the first of the pair appears in the second
380 * string.
381 */
382static void
383translate(char *ttab, char *str)
384{
385	char *s;
386
387	for (; *ttab && *(ttab+1); ttab += 2)
388		for (s = str; *s; s++)
389			if (*ttab == *s)
390				*s = *(ttab+1);
391}
392
393#define	MAXLINE	512
394
395static void dialreset(void);
396#ifndef SMALL
397static char *currdial(void);
398#endif
399/*
400 * Get the information about the dialer.
401 * gdial(type, arps, narps)
402 *	type	-> type of dialer (e.g., penril)
403 *	arps	-> array of pointers returned by gdial
404 *	narps	-> number of elements in array returned by gdial
405 * Return value:
406 *	-1	-> Can't open DIALERFILE
407 *	0	-> requested type not found
408 *	>0	-> success - number of fields filled in
409 */
410static int
411gdial(char *type, char *arps[], int narps)
412{
413	static char *info;	/* dynamically allocated MAXLINE */
414	int na;
415
416	DEBUG(2, "gdial(%s) called\n", type);
417	if (info == NULL) {
418		info = malloc(MAXLINE);
419		if (info == NULL) {
420			DEBUG(1, "malloc failed for info in gdial\n", 0);
421			return (0);
422		}
423	}
424	while (getdialline(info, MAXLINE)) {
425		if ((info[0] == '#') || (info[0] == ' ') ||
426		    (info[0] == '\t') || (info[0] == '\n'))
427			continue;
428		if ((na = getargs(info, arps, narps)) == 0)
429			continue;
430		if (EQUALS(arps[0], type)) {
431			DEBUG(5, "Trying caller script '%s'", type);
432			DEBUG(5, " from '%s'.\n", currdial());
433			dialreset();
434			bsfix(arps);
435			return (na);
436		}
437	}
438	DEBUG(1, "%s not found in Dialers file\n", type);
439	dialreset();
440	return (0);
441}
442
443#ifdef TLI
444/*
445 *
446 * AT&T Transport Layer Interface
447 *
448 * expected in Devices
449 *	TLI line1 - - TLI
450 * or
451 *	TLIS line1 - - TLIS
452 *
453 */
454
455#include <tiuser.h>
456
457static void tfaillog(int fd, const char *s);
458
459#define	CONNECT_ATTEMPTS	3
460#define	TFREE(p, type)	if ((p)) (void) t_free((char *)(p), (type))
461
462static struct netbuf	*stoa(char *, struct netbuf *);
463/*
464 * returns fd to remote uucp daemon
465 */
466/*ARGSUSED*/
467static int
468tlicall(char *flds[], char *dev[])
469{
470	char		addrbuf[ BUFSIZ ];
471	char		devname[MAXNAMESIZE];
472	int		fd;
473	int		i, j;
474	struct t_bind	*bind_ret = 0;
475	struct t_info	tinfo;
476	struct t_call	*sndcall = 0, *rcvcall = 0;
477
478
479	if (dev[D_LINE][0] != '/') {
480		/*	dev holds device name relative to /dev	*/
481		(void) snprintf(devname, sizeof (devname),
482		    "/dev/%s", dev[D_LINE]);
483	} else {
484		/*	dev holds full path name of device	*/
485		(void) strcpy(devname, dev[D_LINE]);
486	}
487	/* gimme local transport endpoint */
488	errno = t_errno = 0;
489	if (setjmp(Sjbuf)) {
490		DEBUG(1, "t_open timeout\n%s", "");
491		logent("t_open", "TIMEOUT");
492		Uerror = SS_NO_DEVICE;
493		return (FAIL);
494	}
495	(void) signal(SIGALRM, alarmtr);
496	(void) alarm(5);
497	fd = t_open(devname, O_RDWR, &tinfo);
498	(void) alarm(0);
499	if (fd < 0) {
500		tfaillog(fd, "t_open");
501		Uerror = SS_NO_DEVICE;
502		return (FAIL);
503	}
504	if (fd_mklock(fd) != SUCCESS) {
505		(void) t_close(fd);
506		DEBUG(1, "tlicall: failed to lock device %s\n", devname);
507		Uerror = SS_LOCKED_DEVICE;
508		return (FAIL);
509	}
510
511	/* allocate tli structures	*/
512	errno = t_errno = 0;
513	/* LINTED pointer cast */
514	if ((bind_ret = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL)) == NULL ||
515	    /* LINTED pointer cast */
516	    (sndcall = (struct t_call *)t_alloc(fd, T_CALL, T_ALL)) == NULL ||
517	    /* LINTED pointer cast */
518	    (rcvcall = (struct t_call *)t_alloc(fd, T_CALL, T_ALL)) == NULL) {
519		tfaillog(fd, "t_alloc");
520		TFREE(bind_ret, T_BIND);
521		TFREE(sndcall, T_CALL);
522		TFREE(rcvcall, T_CALL);
523		Uerror = SS_NO_DEVICE;
524		return (FAIL);
525	}
526
527	/* bind */
528	errno = t_errno = 0;
529	if (t_bind(fd, (struct t_bind *)0, bind_ret) < 0) {
530		tfaillog(fd, "t_bind");
531		TFREE(bind_ret, T_BIND);
532		TFREE(sndcall, T_CALL);
533		TFREE(rcvcall, T_CALL);
534		Uerror = SS_NO_DEVICE;
535		fd_rmlock(fd);
536		(void) t_close(fd);
537		return (FAIL);
538	}
539	DEBUG(5, "tlicall: bound to %s\n", bind_ret->addr.buf);
540
541	/*
542	 * Prepare to connect.
543	 *
544	 * If address begins with "\x", "\X", "\o", or "\O",
545	 * assume is hexadecimal or octal address and use stoa()
546	 * to convert it.
547	 *
548	 * Else is usual uucico address -- only \N's left to process.
549	 * Walk thru connection address, changing \N's to NULLCHARs.
550	 * Note:  If a NULLCHAR must be part of the connection address,
551	 * it must be overtly included in the address.  One recommended
552	 * way is to do it in the Devices file, thusly:
553	 *		Netname /dev/netport - - TLI \D\000
554	 * bsfix() turns \000 into \N and then the loop below makes it a
555	 * real, included-in-the-length null-byte.
556	 *
557	 * The DEBUG must print the strecpy'd address (so that
558	 * non-printables will have been replaced with C escapes).
559	 */
560
561	DEBUG(5, "t_connect to addr \"%s\"\n",
562	    strecpy(addrbuf, dev[D_ARG], "\\"));
563
564	if (dev[D_ARG][0] == '\\' && (dev[D_ARG][1] == 'x' ||
565	    dev[D_ARG][1] == 'X' || dev[D_ARG][1] == 'o' ||
566	    dev[D_ARG][1] == 'O')) {
567		if (stoa(dev[D_ARG], &(sndcall->addr)) == NULL) {
568			DEBUG(5, "tlicall: stoa failed\n%s", "");
569			logent("tlicall", "string-to-address failed");
570			TFREE(bind_ret, T_BIND);
571			TFREE(sndcall, T_CALL);
572			TFREE(rcvcall, T_CALL);
573			Uerror = SS_NO_DEVICE;
574			fd_rmlock(fd);
575			(void) t_close(fd);
576			return (FAIL);
577		}
578	} else {
579		for (i = j = 0; i < BUFSIZ && dev[D_ARG][i] != NULLCHAR;
580		    ++i, ++j) {
581			if (dev[D_ARG][i] == '\\' && dev[D_ARG][i+1] == 'N') {
582				addrbuf[j] = NULLCHAR;
583				++i;
584			} else {
585				addrbuf[j] = dev[D_ARG][i];
586			}
587		}
588		sndcall->addr.buf = addrbuf;
589		sndcall->addr.len = j;
590	}
591
592	if (setjmp(Sjbuf)) {
593		DEBUG(4, "timeout tlicall\n%s", "");
594		logent("tlicall", "TIMEOUT");
595		TFREE(bind_ret, T_BIND);
596		TFREE(sndcall, T_CALL);
597		TFREE(rcvcall, T_CALL);
598		Uerror = SS_NO_DEVICE;
599		fd_rmlock(fd);
600		(void) t_close(fd);
601		return (FAIL);
602	}
603	(void) signal(SIGALRM, alarmtr);
604	(void) alarm(connecttime);
605
606	/* connect to the service -- some listeners can't handle */
607	/* multiple connect requests, so try it a few times */
608	errno = t_errno = 0;
609	for (i = 0; i < CONNECT_ATTEMPTS; ++i) {
610		if (t_connect(fd, sndcall, rcvcall) == 0)
611			break;
612		if ((t_errno == TLOOK) && (t_look(fd) == T_DISCONNECT)) {
613			(void) t_rcvdis(fd, NULL);
614			(void) alarm(0);
615		} else {
616			(void) alarm(0);
617			tfaillog(fd, "t_connect");
618			TFREE(bind_ret, T_BIND);
619			TFREE(sndcall, T_CALL);
620			TFREE(rcvcall, T_CALL);
621			Uerror = SS_DIAL_FAILED;
622			fd_rmlock(fd);
623			(void) t_close(fd);
624			return (FAIL);
625		}
626	}
627	(void) alarm(0);
628	TFREE(bind_ret, T_BIND);
629	TFREE(sndcall, T_CALL);
630	TFREE(rcvcall, T_CALL);
631	if (i == CONNECT_ATTEMPTS) {
632		tfaillog(fd, "t_connect");
633		Uerror = SS_DIAL_FAILED;
634		fd_rmlock(fd);
635		(void) t_close(fd);
636		return (FAIL);
637	}
638	errno = t_errno = 0;
639	(void) strcpy(Dc, dev[D_CALLER]);
640	return (fd);
641}
642#endif /* TLI */
643