1: #ifndef lint
2: static char RCSid[] = "$Header: gap2d.c,v 2.0 85/11/21 07:23:00 jqj Exp $";
3: #endif
4: /*
5: * server for GAP-style (TransportObject=server,teletype) telnet connections
6: * Note that we support only GAP version 2, although a server for version 3
7: * exists. The version 2 server has not been tested as thoroughly as has the
8: * version 3; it does NOT support RESERVE functionality.
9: */
10:
11: /* $Log: gap2d.c,v $
12: * Revision 2.0 85/11/21 07:23:00 jqj
13: * 4.3BSD standard release
14: *
15: * Revision 1.2 85/05/23 06:22:18 jqj
16: * *** empty log message ***
17: *
18: * Revision 1.2 85/05/23 06:22:18 jqj
19: * *** empty log message ***
20: *
21: * Revision 1.1 85/05/22 09:46:52 jqj
22: * Initial revision
23: */
24: #include <stdio.h>
25: #include <signal.h>
26: #include <sgtty.h>
27: #include <sys/types.h>
28: #include <sys/time.h>
29: #include <sys/uio.h>
30: #include <sys/socket.h>
31: #include <netns/ns.h>
32: #include <netns/idp.h>
33: #include <netns/sp.h>
34: #include <sys/wait.h>
35: #include <xnscourier/realcourierconnection.h>
36: #include "GAP2.h"
37: #include "gapcontrols.h"
38: #include <xnscourier/except.h>
39: #include <errno.h>
40:
41: #define BELL '\07'
42: #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
43:
44: int pty, net;
45: extern CourierConnection *_serverConnection;
46: char buf[sizeof(struct sphdr)+SPPMAXDATA];
47: struct sphdr our_sphdr;
48: struct iovec our_iovec[2] = {{((caddr_t)&our_sphdr), sizeof(our_sphdr)}};
49: /*
50: * I/O data buffers, pointers, and counters.
51: */
52: char ptyibuf[512], *ptyip = ptyibuf;
53: char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
54: char *netip = buf;
55: char netobuf[512], *nfrontp = netobuf, *nbackp = netobuf;
56: int pcc, ncc;
57: char line[12];
58: extern char **environ;
59: extern int errno;
60:
61: char *envinit[3];
62: char wsenv[50];
63:
64: /*
65: * session parameters
66: */
67: Cardinal frametimeout; /* 0 or time in seconds to wait */
68:
69:
70: /*
71: * This modified version of the server is necessary since GAP specifies
72: * that the telnet data-transfer session occurs after the RPC to create
73: * it has returned!
74: */
75: Server(skipcount,skippedwords)
76: int skipcount;
77: Unspecified skippedwords[];
78: {
79: Cardinal _procedure;
80: register Unspecified *_buf;
81: LongCardinal programnum;
82: Cardinal versionnum;
83: Cardinal _n;
84:
85: #ifdef DEBUG
86: BUGOUT("Server: %d %d",skipcount,skippedwords);
87: #endif
88: for (;;) {
89: _buf = ReceiveCallMessage(&_procedure, skipcount, skippedwords);
90: DURING switch (_procedure) {
91: case 3:
92: server_GAP2_Delete(_buf);
93: break;
94: case 2:
95: server_GAP2_Create(_buf);
96: net = _serverConnection->fd;
97: gaptelnet(); /* returns on connection close */
98: break;
99: case 0:
100: server_GAP2_Reset(_buf);
101: break;
102: default:
103: NoSuchProcedureValue("GAP", _procedure);
104: break;
105: } HANDLER {
106: Deallocate(_buf);
107: switch (Exception.Code) {
108: case GAP2_terminalAddressInvalid:
109: case GAP2_terminalAddressInUse:
110: case GAP2_controllerDoesNotExist:
111: case GAP2_controllerAlreadyExists:
112: case GAP2_gapCommunicationError:
113: case GAP2_gapNotExported:
114: case GAP2_bugInGAPCode:
115: case GAP2_tooManyGateStreams:
116: case GAP2_inconsistentParams:
117: case GAP2_transmissionMediumUnavailable:
118: case GAP2_dialingHardwareProblem:
119: case GAP2_noDialingHardware:
120: case GAP2_badAddressFormat:
121: case GAP2_mediumConnectFailed:
122: case GAP2_illegalTransport:
123: case GAP2_noCommunicationHardware:
124: case GAP2_unimplemented:
125: _buf = Allocate(0);
126: SendAbortMessage(Exception.Code-ERROR_OFFSET, 0, _buf);
127: break;
128: default:
129: _buf = Allocate(0);
130: SendRejectMessage(unspecifiedError, 0, _buf);
131: break;
132: }
133: } END_HANDLER;
134: Deallocate(_buf);
135: for (;;) {
136: skipcount = LookAheadCallMsg(&programnum, &versionnum,
137: skippedwords);
138: if (skipcount < 0) return(0); /* timed out */
139: if (programnum != 3 || versionnum != 2)
140: ExecCourierProgram(programnum, versionnum,
141: skipcount, skippedwords);
142: } /* loop if can't exec that program */
143: }
144: }
145:
146: void
147: GAP2_Delete(session)
148: GAP2_SessionHandle session;
149: {
150: }
151:
152: void
153: GAP2_Reset()
154: {
155: }
156:
157: GAP2_CreateResults
158: GAP2_Create(conn, BDTproc, sessionparams, transports,
159: createTimeout)
160: CourierConnection *conn;
161: int BDTproc;
162: GAP2_SessionParameterObject sessionparams;
163: struct {Cardinal length;
164: GAP2_TransportObject *sequence;
165: } transports;
166: GAP2_WaitTime createTimeout;
167: {
168: GAP2_CreateResults result;
169: char *c1, *c2, *host;
170: int t, pid;
171: struct sgttyb b;
172: char *xntoa(), *wsname();
173: struct sockaddr_ns who;
174: int whosize = sizeof(who);
175: LongCardinal servicetype;
176: GAP2_CommParamObject *cp;
177: GAP2_Duplexity duplexity; /* fullDuplex, halfDuplex */
178:
179: #ifdef DEBUG
180: BUGOUT("CREATE");
181: #endif
182: switch (sessionparams.designator) {
183: case ttyHost:
184: frametimeout = sessionparams.ttyHost_case.frameTimeout/1000;
185: /* could set other parameters here */
186: break;
187: default:
188: raise(GAP2_unimplemented, 0);
189: /*NOTREACHED*/
190: }
191: if (transports.length != 2) {
192: raise(GAP2_illegalTransport);
193: /*NOTREACHED*/
194: }
195: switch (transports.sequence[0].designator) {
196: case rs232c: /* maybe some day */
197: cp = &transports.sequence[0].rs232c_case.commParams;
198: if (cp->accessDetail.designator == directConn) {
199: duplexity = cp->duplex;
200: servicetype = 0; /* fake it */
201: break;
202: }
203: raise(GAP2_noCommunicationHardware, 0);
204: /*NOTREACHED*/
205: default:
206: raise(GAP2_illegalTransport, 0);
207: /*NOTREACHED*/
208: }
209: if (transports.sequence[1].designator != teletype)
210: raise(GAP2_illegalTransport, 0);
211: /* ignore createTimeout */
212: /* ignore credentials and verifier */
213:
214: for (c1 = "pq"; *c1 != 0; c1++)
215: for (c2 = "0123456789abcdef"; *c2 != 0; c2++) {
216: sprintf(line, "/dev/pty%c%c", *c1, *c2);
217: pty = open(line, 2);
218: if (pty < 0) continue;
219: line[strlen("/dev/")] = 't';
220: t = open(line, 2);
221: if (t > 0) goto gotpty;
222: close(pty);
223: }
224: raise(GAP2_tooManyGateStreams, 0);
225: /*NOTREACHED*/
226: gotpty:
227: getpeername(_serverConnection->fd, &who, &whosize);
228: host = wsname(who.sns_addr);
229: #ifdef DEBUG
230: BUGOUT("gotpty <%s> %d <%s>",line, pty, host);
231: #endif
232: ioctl(t, TIOCGETP, &b);
233: b.sg_flags = CRMOD|XTABS|ANYP;
234: ioctl(t, TIOCSETP, &b);
235: ioctl(pty, TIOCGETP, &b);
236: if (duplexity == fullduplex)
237: b.sg_flags |= ECHO;
238: else
239: b.sg_flags &= ~ECHO;
240: ioctl(pty, TIOCSETP, &b);
241: /* we do the fork now so we can return failures as REPORTS */
242: pid = fork();
243: if (pid < 0) {
244: close(pty); close(t);
245: raise(GAP2_tooManyGateStreams, 0);
246: /*NOTREACHED*/
247: }
248: else if (pid == 0) { /* in the execed fork */
249: sleep(1); /* let parent get ready for us */
250: close(_serverConnection->fd); /* close net */
251: close(pty);
252: dup2(t, 0);
253: dup2(t, 1);
254: dup2(t, 2);
255: if (t > 2) close(t);
256: envinit[0] = "TERM=network";
257: envinit[1] = sprintf(wsenv, "WORKSTATION=%s",
258: xntoa(who.sns_addr));
259: envinit[2] = (char*) 0;
260: #ifdef DEBUG
261: BUGOUT("about to exec /bin/login");
262: #endif
263: execl("/bin/login","login", "-h", host, 0);
264: #ifdef DEBUG
265: BUGOUT("exec of /bin/login failed");
266: #endif
267: perror("/bin/login");
268: exit(1);
269: /*NOTREACHED*/
270: }
271: close(t);
272: #ifdef DEBUG
273: BUGOUT("fork successful");
274: #endif
275: result.session[0] = pid;
276: return(result);
277: }
278:
279: jmp_buf childdiedbuf;
280:
281: /*
282: * Main loop. Select from pty and network, and
283: * hand data to telnet receiver finite state machine.
284: * Returns 0 on orderly shutdown, 1 on abnormal shutdown.
285: */
286: gaptelnet()
287: {
288: int on = 1;
289: char hostname[32];
290: int childdied();
291: int ibits = 0, obits = 0;
292: register int c;
293: struct sphdr *si = (struct sphdr *)buf;
294: static struct timeval timeout = {600,0};
295: int keepalives = 0;
296: int i;
297:
298: #ifdef DEBUG
299: BUGOUT("gaptelnet net=%d,pty=%d",net,pty);
300: #endif
301: if (setjmp(childdiedbuf) != 0)
302: return(0); /* child died */
303: signal(SIGCHLD, childdied);
304: signal(SIGTSTP, SIG_IGN);
305: ioctl(net, FIONBIO, &on);
306: ioctl(pty, FIONBIO, &on);
307:
308:
309: /*
310: * Show banner that getty never gave.
311: */
312: gethostname(hostname, sizeof (hostname));
313: sprintf(nfrontp, BANNER, hostname, "");
314: nfrontp += strlen(nfrontp);
315: /*
316: * Send status message indicating we're ready to go
317: */
318: changeSPPopts(net, GAPCTLnone, 1);
319: sendoobdata(GAPCTLmediumUp);
320: for (;;) {
321: #ifdef DEBUG
322: BUGOUT("looping in gaptelnet");
323: #endif
324: ibits = obits = 0;
325: /*
326: * Never look for input if there's still
327: * stuff in the corresponding output buffer
328: */
329: if (nfrontp - nbackp || pcc > 0)
330: obits |= (1 << net);
331: else
332: ibits |= (1 << pty);
333: if (pfrontp - pbackp || ncc > 0)
334: obits |= (1 << pty);
335: else
336: ibits |= (1 << net);
337: if (ncc < 0 && pcc < 0)
338: break;
339: timeout.tv_sec = 600;
340: timeout.tv_usec = 0;
341: select(16, &ibits, &obits, 0, &timeout);
342: if (ibits == 0 && obits == 0) {
343: /* timeout means no activity for a long time */
344: #ifdef DEBUG
345: BUGOUT("timeout from select");
346: #endif
347: if (keepalives++ < 2) {
348: /* first 2 times through send warning */
349: if (nfrontp == nbackp && pcc == 0) {
350: /* but only if not blocked on output */
351: #define WARNING "\r\nYou've been idle much too long. Respond or log off.\r\n"
352: strcpy(nfrontp, WARNING);
353: nfrontp += sizeof(WARNING);
354: }
355: sleep(5);
356: continue;
357: }
358: #ifdef DEBUG
359: BUGOUT("keepalive expired -- calling cleanup");
360: #endif
361: /* keepalive count has expired */
362: cleanup();
363: return(1);
364: }
365:
366: /*
367: * Something to read from the network...
368: */
369: if (ibits & (1 << net)) {
370: ncc = read(net, buf, sizeof(buf));
371: #ifdef DEBUG
372: BUGOUT("read from net %d",ncc);
373: #endif
374: if (ncc < 0 && errno == EWOULDBLOCK)
375: ncc = 0;
376: else if (ncc < sizeof(struct sphdr)) {
377: #ifdef DEBUG
378: BUGOUT("short read, %d. calling cleanup",ncc);
379: #endif
380: cleanup(); /* will probably fail or block */
381: return(1);
382: }
383: else if (si->sp_cc & SP_OB) {
384: /* a status or OOB control */
385: switch (buf[sizeof(struct sphdr)]) {
386: case GAPCTLinterrupt:
387: /* shove interrupt char in buffer */
388: interrupt();
389: break; /* from switch */
390: case GAPCTLareYouThere:
391: sendoobdata(GAPCTLiAmHere);
392: break; /* from switch */
393: default:
394: /* Ignore other controls instead of:
395: * sendoobdata(
396: * GAPCTLunexpectedRemoteBehavior);
397: */
398: break; /* from switch */
399: }
400: ncc = 0; /* no chars here */
401: }
402: else if (si->sp_dt==GAPCTLnone) {
403: /* the normal case */
404: ncc -= sizeof(struct sphdr);
405: netip = buf + sizeof(struct sphdr);
406: keepalives = 0;
407: }
408: else if(si->sp_dt==GAPCTLcleanup) {
409: #ifdef DEBUG
410: BUGOUT("got CLEANUP packet. Done");
411: #endif
412: cleanup(); /* normal termination */
413: return(0);
414: }
415: else if (si->sp_dt==SPPSST_END) {
416: /* got premature termination */
417: quitquit(net, pty);
418: return(1);
419: }
420: }
421:
422: /*
423: * Something to read from the pty...
424: */
425: if (ibits & (1 << pty)) {
426: if (frametimeout > 0) sleep(frametimeout);
427: pcc = read(pty, ptyibuf, sizeof(ptyibuf));
428: #ifdef DEBUG
429: BUGOUT("read from pty %d",pcc);
430: #endif
431: if (pcc < 0 && errno == EWOULDBLOCK)
432: pcc = 0;
433: else if (pcc <= 0) {
434: #ifdef DEBUG
435: BUGOUT("short read from pty. Calling cleanup");
436: #endif
437: cleanup();
438: return(1); /* ?? abnormal termination */
439: }
440: ptyip = ptyibuf;
441: }
442:
443: while (pcc > 0) {
444: if ((&netobuf[sizeof(netobuf)] - nfrontp) < 2)
445: break;
446: *nfrontp++ = *ptyip++ & 0377; pcc--;
447: }
448: if ((obits & (1 << net)) && (nfrontp - nbackp) > 0)
449: netflush();
450: while (ncc > 0) {
451: if ((&ptyobuf[sizeof(ptyobuf)] - pfrontp) < 2) break;
452: *pfrontp++ = *netip++ & 0377;
453: ncc--;
454: }
455: if ((obits & (1 << pty)) && (pfrontp - pbackp) > 0)
456: ptyflush();
457: }
458: /* we should never get to here */
459: #ifdef DEBUG
460: BUGOUT("broke out of for(;;) somehow. calling cleanup");
461: #endif
462: cleanup();
463: return(0);
464: }
465:
466: /*
467: * Send out of band data to other end of network
468: */
469: sendoobdata(value)
470: u_char value;
471: {
472: struct {
473: struct sphdr hdr;
474: char val;
475: } oob;
476: oob.hdr = our_sphdr;
477: oob.val = value;
478: #ifdef DEBUG
479: BUGOUT("sendoobdata 0%o",value);
480: #endif
481: send(net, &oob, sizeof(oob), MSG_OOB);
482: }
483:
484: /*
485: * Send interrupt to process on other side of pty.
486: * If it is in raw mode, just write NULL;
487: * otherwise, write intr char.
488: */
489: interrupt()
490: {
491: struct sgttyb b;
492: struct tchars tchars;
493:
494: ptyflush(); /* half-hearted */
495: ioctl(pty, TIOCGETP, &b);
496: if (b.sg_flags & RAW) {
497: *pfrontp++ = '\0';
498: return;
499: }
500: *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
501: '\177' : tchars.t_intrc;
502: }
503:
504: ptyflush()
505: {
506: register int n;
507:
508: if ((n = pfrontp - pbackp) > 0)
509: n = write(pty, pbackp, n);
510: #ifdef DEBUG
511: BUGOUT("ptyflush wrote %d",n);
512: #endif
513: if (n < 0)
514: return;
515: pbackp += n;
516: if (pbackp >= pfrontp) /* actually, > is an error */
517: pbackp = pfrontp = ptyobuf;
518: }
519:
520: netflush()
521: {
522: register int n;
523:
524: if ((n = nfrontp - nbackp) > 0) {
525: our_iovec[1].iov_len = ((n > SPPMAXDATA) ? SPPMAXDATA : n);
526: our_iovec[1].iov_base = nbackp;
527: n = writev(net, our_iovec, 2) - sizeof(struct sphdr);
528: }
529: #ifdef DEBUG
530: BUGOUT("netflush wrote %d",n);
531: if (our_iovec[0].iov_base != (char*)&our_sphdr)
532: BUGOUT("Oops: our_iovec clobbered");
533: BUGOUT("header: %d %d, %d %d %d %d %d %d",
534: our_sphdr.sp_cc, our_sphdr.sp_dt,
535: our_sphdr.sp_sid, our_sphdr.sp_did, our_sphdr.sp_seq,
536: our_sphdr.sp_ack, our_sphdr.sp_alo);
537: #endif
538: if (n < 0) {
539: if (errno == EWOULDBLOCK)
540: return;
541: /* should blow this guy away... */
542: return;
543: }
544: nbackp += n;
545: if (nbackp >= nfrontp) /* actually , > is an error */
546: nbackp = nfrontp = netobuf;
547: }
548:
549: /*
550: * handle receipt of an SPPSST_END packet
551: * This is currently an error, since client didn't send "cleanup" first
552: */
553: quitquit()
554: {
555: changeSPPopts(net, SPPSST_ENDREPLY, 1);
556: write(net, &our_sphdr, sizeof(our_sphdr));
557: sleep(3);
558:
559: rmut();
560: vhangup(); /* XXX */
561: shutdown(net, 1);
562: close(net);
563: }
564:
565: /*
566: * shut down the data connection for one reason or another
567: */
568: cleanup()
569: {
570: int fdmask;
571: struct timeval timeout;
572: struct sphdr *si = (struct sphdr *)buf;
573: int off = 0;
574:
575: signal(SIGCHLD, SIG_IGN);
576: sendoobdata(GAPCTLcleanup);
577: changeSPPopts(net, SPPSST_END, 1);
578: if (write(net, &our_sphdr, sizeof(our_sphdr)) < 0) {
579: fdmask = 1<<net;
580: timeout.tv_sec = 10;
581: while (select(net+1,&fdmask,(int*)0, (int*)0, &timeout) > 0 &&
582: read(net,buf,sizeof(buf)) >= sizeof(struct sphdr)) {
583: #ifdef DEBUG
584: BUGOUT("cleanup -- got packet");
585: #endif
586: if ((si->sp_cc & SP_OB)
587: && si->sp_dt == SPPSST_ENDREPLY) {
588: changeSPPopts(net, SPPSST_ENDREPLY, 1);
589: write(net, &our_sphdr, sizeof(our_sphdr));
590: #ifdef DEBUG
591: BUGOUT("cleanup -- wrote ENDREPLY");
592: #endif
593: sleep(1);
594: changeSPPopts(net,0,0);
595: ioctl(net, FIONBIO, &off);
596: rmut();
597: vhangup(); /* XXX */
598: return;
599: }
600: /* loop: ignore everything except ENDREPLY */
601: fdmask = 1<<net;
602: timeout.tv_sec = 10;
603: }
604: /* timed out or read failed */
605: changeSPPopts(net, SPPSST_ENDREPLY, 1);
606: write(net, &our_sphdr, sizeof(our_sphdr));
607: sleep(1);
608: }
609: shutdown(net, 1);
610: close(net);
611: rmut();
612: vhangup(); /* XXX */
613: }
614:
615: /*
616: * SIGCHLD interrupt handler
617: */
618: childdied()
619: {
620: #ifdef DEBUG
621: BUGOUT("child died");
622: #endif
623: cleanup();
624: longjmp(childdiedbuf, -1);
625: }
626:
627: changeSPPopts(s, stream, eom)
628: int s; /* SPP socket */
629: u_char stream; /* datastream type */
630: char eom; /* Boolean EOM */
631: {
632: our_sphdr.sp_dt = stream;
633: our_sphdr.sp_cc = (eom ? SP_EM : 0);
634: }
635:
636:
637: #include <utmp.h>
638:
639: struct utmp wtmp;
640: char wtmpf[] = "/usr/adm/wtmp";
641: char utmp[] = "/etc/utmp";
642: #define SCPYN(a, b) strncpy(a, b, sizeof (a))
643: #define SCMPN(a, b) strncmp(a, b, sizeof (a))
644:
645: rmut()
646: {
647: register f;
648: int found = 0;
649:
650: f = open(utmp, 2);
651: if (f >= 0) {
652: while(read(f, (char *)&wtmp, sizeof (wtmp)) == sizeof (wtmp)) {
653: if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
654: continue;
655: lseek(f, -(long)sizeof (wtmp), 1);
656: SCPYN(wtmp.ut_name, "");
657: SCPYN(wtmp.ut_host, "");
658: time(&wtmp.ut_time);
659: write(f, (char *)&wtmp, sizeof (wtmp));
660: found++;
661: }
662: close(f);
663: }
664: if (found) {
665: f = open(wtmpf, 1);
666: if (f >= 0) {
667: SCPYN(wtmp.ut_line, line+5);
668: SCPYN(wtmp.ut_name, "");
669: SCPYN(wtmp.ut_host, "");
670: time(&wtmp.ut_time);
671: lseek(f, (long)0, 2);
672: write(f, (char *)&wtmp, sizeof (wtmp));
673: close(f);
674: }
675: }
676: chmod(line, 0666);
677: chown(line, 0, 0);
678: line[strlen("/dev/")] = 'p';
679: chmod(line, 0666);
680: chown(line, 0, 0);
681: }
682:
683: /*
684: * Convert network-format xns address
685: * to ascii
686: * --Replace this with a clearinghouse name lookup someday.
687: */
688: char *
689: wsname(addr)
690: struct ns_addr addr;
691: {
692: static char b[50];
693: char temp[10];
694: int i;
695:
696: /* net */
697: sprintf(b, "%D.", ntohl(ns_netof(addr)));
698: /* skip leading zeros */
699: for(i=0; (addr.x_host.c_host[i] == (char) 0); i++) ;
700: /* print the rest */
701: for(; i < 6; i++) {
702: sprintf(temp,"%x", addr.x_host.c_host[i]);
703: strcat(b, temp);
704: if(i != 5) strcat(b, ":");
705: }
706: return (b);
707: }
708:
709: /*
710: * generate an xns address that "DE" can parse.
711: * This goes in the environment. Should be the same as above
712: */
713: char *
714: xntoa(addr)
715: struct ns_addr addr;
716: {
717: static char b[50];
718: char temp[10];
719: int i;
720:
721: /* net */
722: sprintf(b, "%X#", ntohl(ns_netof(addr)));
723: /* print the rest */
724: for(i=0; i < 6; i++) {
725: sprintf(temp,"%x", addr.x_host.c_host[i]);
726: strcat(b, temp);
727: if(i != 5) strcat(b, ".");
728: }
729: return (b);
730: }
731:
732: #ifdef DEBUG
733: BUGOUT(str,a,b,c,d,e,f,g,h)
734: char *str;
735: {
736: FILE *fd;
737: fd = fopen("/tmp/GAP2d.log","a");
738: fprintf(fd,str,a,b,c,d,e,f,g,h);
739: putc('\n',fd);
740: fclose(fd);
741: }
742: #endif