home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / TELNET.C < prev    next >
Text File  |  1994-06-15  |  15KB  |  581 lines

  1. /* Internet Telnet client
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. /* Mods by PA0GRI */
  5. #ifdef  __TURBOC__
  6. #include <io.h>
  7. #include <fcntl.h>
  8. #endif
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "socket.h"
  12. #include "telnet.h"
  13. #include "session.h"
  14. #include "proc.h"
  15. #include "tty.h"
  16. #include "commands.h"
  17. #include "netuser.h"
  18. #ifdef LZW
  19. #include "lzw.h"
  20. #endif
  21.   
  22. static int filemode __ARGS((FILE *fp,int mode));
  23. #define CTLZ    26
  24.   
  25. int Refuse_echo = 0;
  26. int Tn_cr_mode = 0;    /* if true turn <cr> to <cr-nul> */
  27.   
  28. #undef DEBUG 1
  29.   
  30. #ifdef  DEBUG
  31. char *T_options[] = {
  32.     "Transmit Binary",      /* 0 */
  33.     "Echo",             /* 1 */
  34.     "",             /* 2 */
  35.     "Suppress Go Ahead",        /* 3 */
  36.     "",             /* 4 */
  37.     "Status",           /* 5 */
  38.     "Timing Mark"           /* 6 */
  39. #ifdef  TN_KK6JQ
  40.     ,
  41.     "Remote Controlled Transmission/Echo",
  42.     "Negotiate Line Width",
  43.     "Negotiate Page Size",
  44.     "Negotiate CR Disposition",
  45.     "Negotiate Horz Tab Stops",
  46.     "Negotiate Horz tab Disposition",
  47.     "Negotiate FormFeed Disposition",
  48.     "Negotiate Vert Tab Stops",
  49.     "Negotiate Vert Tab Disposition",
  50.     "Negotiate LineFeed Disposition",
  51.     "Extended ASCII",
  52.     "Force Logout",
  53.     "Byte Macro",
  54.     "Data Entry Terminal",
  55.     "Protocol supdup",
  56.     "supdup",
  57.     "Send Location",
  58.     "Terminal Type",
  59.     "End of Record"
  60. #endif  /* TN_KK6JQ */
  61. };
  62. #endif  /* DEBUG */
  63.   
  64. #if defined BBSSESSION || defined TELNETSESSION || defined TTYLINKSESSION
  65. /* Execute user telnet,ttylink or bbs command */
  66. int
  67. dotelnet(argc,argv,p)
  68. int argc;
  69. char *argv[];
  70. void *p;
  71. {
  72.     struct session *sp;
  73.     struct sockaddr_in fsocket;
  74.     char *name;
  75.     int split = 0;
  76.     int bbs = 0;
  77.   
  78.     /*Make sure this comes from console - WG7J*/
  79.     if(Curproc->input != Command->input)
  80.         return 0;
  81.   
  82.     name = argv[1];     /* This is not valid for 'bbs' command ! */
  83. #ifdef BBSSESSION
  84.     if(*argv[0] == 'b') {
  85.         name = "Local BBS";
  86.         bbs = 1;
  87.     }
  88. #ifdef TTYLINKSESSION
  89.     else
  90. #endif
  91. #endif
  92. #ifdef TTYLINKSESSION
  93.         if((strlen(argv[0]) > 1) && (argv[0][1] == 't')) /* tty-link command */
  94.             split = 1;
  95. #endif
  96.   
  97.     /* Allocate a session descriptor */
  98.     if((sp = newsession(name,TELNET,split)) == NULLSESSION){
  99.         tputs(TooManySessions);
  100.         return 1;
  101.     }
  102.     fsocket.sin_family = AF_INET;
  103.     if(argc < 3) {
  104.         if(split)
  105.             fsocket.sin_port = IPPORT_TTYLINK;
  106.         else
  107.             fsocket.sin_port = IPPORT_TELNET;
  108.     } else {
  109.         fsocket.sin_port = atoip(argv[2]);
  110.     }
  111. #ifdef BBSSESSION
  112.     if(bbs) {
  113.         /* 127.0.0.1 is the loopback interface */
  114.         fsocket.sin_addr.s_addr = 0x7f000001L;
  115.     } else
  116. #endif
  117.     {
  118.         tprintf("Resolving %s... ",sp->name);
  119.         if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  120.             tprintf(Badhost,sp->name);
  121.             keywait(NULLCHAR,1);
  122.             freesession(sp);
  123.             return 1;
  124.         }
  125.     }
  126.     if((sp->s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  127.         tputs(Nosock);
  128.         keywait(NULLCHAR,1);
  129.         freesession(sp);
  130.         return 1;
  131.     }
  132. #if defined LZW && defined LZW_TELNET_3601
  133.     if (fsocket.sin_port == IPPORT_XCONVERS)
  134.         lzwinit(sp->s,Lzwbits,Lzwmode);
  135. #endif
  136.     return tel_connect(sp,(char *)&fsocket,SOCKSIZE);
  137. }
  138. #endif /* TELNET || TTYLINK || BBS */
  139.   
  140. /* Generic interactive connect routine, used by Telnet, AX.25, NET/ROM */
  141. int
  142. tel_connect(sp,fsocket,len)
  143. struct session *sp;
  144. char *fsocket;
  145. int len;
  146. {
  147.     unsigned int index;
  148.     struct telnet tn;
  149.   
  150.     index = (unsigned int) (sp - Sessions);
  151.     memset((char *)&tn,0,sizeof(tn));
  152.     tn.eolmode = Tn_cr_mode;
  153.     tn.session = sp;    /* Upward pointer */
  154.     sp->cb.telnet = &tn;    /* Downward pointer */
  155.     sockmode(sp->s,SOCK_ASCII); /* Default to ascii mode */
  156.   
  157.     tprintf("Trying %s...\n",psocket((struct sockaddr *)fsocket));
  158.     if(connect(sp->s,fsocket,len) == -1){
  159.         tprintf("%s session %u failed: %s errno %d\n",
  160.         Sestypes[sp->type], index, sockerr(sp->s),errno);
  161.   
  162.         keywait(NULLCHAR,1);
  163.         freesession(sp);
  164.         return 1;
  165.     }
  166.     tprintf("%s session %u connected to %s\n",
  167.     Sestypes[sp->type],index,sp->name);
  168.     tnrecv(&tn);
  169.     return 0;
  170. }
  171.   
  172. /* Telnet input routine, common to both telnet and ttylink */
  173. void
  174. tnrecv(tn)
  175. struct telnet *tn;
  176. {
  177.     int c,s,index;
  178.     struct session *sp;
  179.     char *cp;
  180.   
  181.     sp = tn->session;
  182.     s = sp->s;
  183.   
  184.     index = (unsigned int) (sp - Sessions);
  185.   
  186.     /* Fork off the transmit process */
  187.     sp->proc1 = newproc("tel_out",1024,tel_output,0,tn,NULL,0);
  188.   
  189.     /* Process input on the connection */
  190.     while((c = recvchar(s)) != -1){
  191.         if(c != IAC){
  192. #ifdef notdef
  193.             /* Allow international character sets to pass - WG7J */
  194.             /* Ordinary character */
  195.             if(!tn->remote[TN_TRANSMIT_BINARY])
  196.                 c &= 0x7f;
  197. #endif
  198.             tputc((char)c);
  199.             continue;
  200.         }
  201.         /* IAC received, get command sequence */
  202.         c = recvchar(s);
  203.         switch(c){
  204.             case WILL:
  205.                 c = recvchar(s);
  206.                 willopt(tn,c);
  207.                 break;
  208.             case WONT:
  209.                 c = recvchar(s);
  210.                 wontopt(tn,c);
  211.                 break;
  212.             case DO:
  213.                     c = recvchar(s);
  214.                     doopt(tn,c);
  215.                     break;
  216.             case DONT:
  217.                 c = recvchar(s);
  218.                 dontopt(tn,c);
  219.                 break;
  220. #ifdef  TN_KK6JQ
  221.             case SB:
  222.                 c = recvchar(s);
  223.                 dosb(tn,c);
  224.                 break;
  225. #endif  /* TN_KK6JQ */
  226.             case IAC:   /* Escaped IAC */
  227.                 tputc(IAC);
  228.                 break;
  229.         }
  230.     }
  231.     /* A close was received from the remote host.
  232.      * Notify the user, kill the output task and wait for a response
  233.      * from the user before freeing the session.
  234.      */
  235.     sockmode(sp->output,SOCK_ASCII); /* Restore newline translation */
  236.     cp = sockerr(s);
  237.     tprintf("%s session %u", Sestypes[sp->type],index);
  238.     tprintf(" closed: %s\n", cp != NULLCHAR ? cp : "EOF");
  239.     killproc(sp->proc1);
  240.     sp->proc1 = NULLPROC;
  241.     close_s(sp->s);
  242.     sp->s = -1;
  243.     keywait(NULLCHAR,1);
  244.     freesession(sp);
  245. }
  246.   
  247. /* User telnet output task, started by user telnet command */
  248. void
  249. tel_output(unused,tn1,p)
  250. int unused;
  251. void *tn1;
  252. void *p;
  253. {
  254.     struct session *sp;
  255.     int c;
  256.     struct telnet *tn;
  257.   
  258.     tn = (struct telnet *)tn1;
  259.     sp = tn->session;
  260.   
  261.     /* Send whatever's typed on the terminal */
  262.     while((c = recvchar(sp->input)) != EOF){
  263.         usputc(sp->s,(char)c);
  264.         if(!tn->remote[TN_ECHO] && sp->record != NULLFILE)
  265.             putc(c,sp->record);
  266.   
  267.         /* By default, output is transparent in remote echo mode.
  268.          * If eolmode is set, turn a cr into cr-null.
  269.          * This can only happen when in remote echo (raw) mode, since
  270.          * the tty driver normally maps \r to \n in cooked mode.
  271.          */
  272.         if(c == '\r' && tn->eolmode)
  273.             usputc(sp->s,'\0');
  274.   
  275.         if(tn->remote[TN_ECHO])
  276.             usflush(sp->s);
  277.     }
  278.     /* Make sure our parent doesn't try to kill us after we exit */
  279.     sp->proc1 = NULLPROC;
  280. }
  281. int
  282. doecho(argc,argv,p)
  283. int argc;
  284. char *argv[];
  285. void *p;
  286. {
  287.     if(argc < 2){
  288.         if(Refuse_echo)
  289.             tputs("Refuse\n");
  290.         else
  291.             tputs("Accept\n");
  292.     } else {
  293.         if(argv[1][0] == 'r')
  294.             Refuse_echo = 1;
  295.         else if(argv[1][0] == 'a')
  296.             Refuse_echo = 0;
  297.         else
  298.             return -1;
  299.     }
  300.     return 0;
  301. }
  302. /* set for unix end of line for remote echo mode telnet */
  303. int
  304. doeol(argc,argv,p)
  305. int argc;
  306. char *argv[];
  307. void *p;
  308. {
  309.     if(argc < 2){
  310.         if(Tn_cr_mode)
  311.             tputs("null\n");
  312.         else
  313.             tputs("standard\n");
  314.     } else {
  315.         if(argv[1][0] == 'n')
  316.             Tn_cr_mode = 1;
  317.         else if(argv[1][0] == 's')
  318.             Tn_cr_mode = 0;
  319.         else {
  320.             tprintf("Usage: %s [standard|null]\n",argv[0]);
  321.             return -1;
  322.         }
  323.     }
  324.     return 0;
  325. }
  326.   
  327. /* The guts of the actual Telnet protocol: negotiating options */
  328. void
  329. willopt(tn,opt)
  330. struct telnet *tn;
  331. int opt;
  332. {
  333.     int ack;
  334.   
  335. #ifdef  DEBUG
  336.     printf("recv: will ");
  337.     if(uchar(opt) <= NOPTIONS)
  338.         printf("%s\n",T_options[opt]);
  339.     else
  340.         printf("%u\n",opt);
  341. #endif
  342.   
  343.     switch(uchar(opt)){
  344.         case TN_TRANSMIT_BINARY:
  345.         case TN_ECHO:
  346.         case TN_SUPPRESS_GA:
  347.             if(tn->remote[uchar(opt)] == 1)
  348.                 return;     /* Already set, ignore to prevent loop */
  349.             if(uchar(opt) == TN_ECHO){
  350.                 if(Refuse_echo){
  351.                 /* User doesn't want to accept */
  352.                     ack = DONT;
  353.                     break;
  354.                 } else {
  355.                 /* Put tty into raw mode */
  356.                     tn->session->ttystate.edit = 0;
  357.                     tn->session->ttystate.echo = 0;
  358.                     sockmode(tn->session->s,SOCK_BINARY);
  359.                     sockmode(tn->session->input,SOCK_BINARY);
  360.                     sockmode(tn->session->output,SOCK_BINARY);
  361.                     if(tn->session->record != NULLFILE)
  362.                         filemode(tn->session->record,SOCK_BINARY);
  363.   
  364.                 }
  365.             }
  366.             tn->remote[uchar(opt)] = 1;
  367.             ack = DO;
  368.                 break;
  369. #ifdef TN_KK6JQ
  370.         case TN_TERM_TYPE:  /* We are the client, we don't want this */
  371. #endif /* TN_KK6JQ */
  372.         default:
  373.             ack = DONT; /* We don't know what he's offering; refuse */
  374.     }
  375.     answer(tn,ack,opt);
  376. }
  377. void
  378. wontopt(tn,opt)
  379. struct telnet *tn;
  380. int opt;
  381. {
  382. #ifdef  DEBUG
  383.     printf("recv: wont ");
  384.     if(uchar(opt) <= NOPTIONS)
  385.         printf("%s\n",T_options[uchar(opt)]);
  386.     else
  387.         printf("%u\n",uchar(opt));
  388. #endif
  389.     if(uchar(opt) <= NOPTIONS){
  390.         if(tn->remote[uchar(opt)] == 0)
  391.             return;     /* Already clear, ignore to prevent loop */
  392.             tn->remote[uchar(opt)] = 0;
  393.             if(uchar(opt) == TN_ECHO){
  394.             /* Put tty into cooked mode */
  395.                 tn->session->ttystate.edit = 1;
  396.                 tn->session->ttystate.echo = 1;
  397.                 sockmode(tn->session->s,SOCK_ASCII);
  398.                 sockmode(tn->session->input,SOCK_ASCII);
  399.                 sockmode(tn->session->output,SOCK_ASCII);
  400.                 if(tn->session->record != NULLFILE)
  401.                     filemode(tn->session->record,SOCK_ASCII);
  402.             }
  403.     }
  404.     answer(tn,DONT,opt);    /* Must always accept */
  405. }
  406.   
  407. #ifdef  TN_KK6JQ
  408. void
  409. dosb(tn, opt)
  410. struct telnet *tn;
  411. int opt;
  412. {
  413.     struct session *sp;
  414.     int s, c;
  415.     char    *name, resp_buf[64];
  416.     int resp_len = 0;
  417.   
  418.     sp = tn->session;
  419.     s = sp->s;
  420.   
  421.     switch (uchar(opt)) {
  422.         case TN_TERM_TYPE:
  423.         /* make sure qualifier is SEND */
  424.         /* ignore otherwise */
  425.   
  426.         /* Karn code is cavalier about EOF when in an IAC sequence.. */
  427.             c = recvchar(s);
  428.             if (c == TS_SEND) {
  429.                 c = recvchar(s);    /* should be IAC */
  430.                 c = recvchar(s);    /* should be SE */
  431. /*
  432.  * Should check to make sure sequence includes IAC, SE and somehow fail
  433.  * if it doesn't.
  434.  */
  435.                 name = getenv("TERM");
  436.                 if ((name==NULL) || ((resp_len = strlen(name)) > 58))
  437.                     name = "UNKNOWN";
  438.                     sprintf(resp_buf, "%c%c%c%c%s%c%c",
  439.                     IAC, SB, TN_TERM_TYPE, TS_IS, name,
  440.                     IAC, SE);
  441. /*
  442. original code:
  443.             resp_len += 6;
  444.   
  445.    Oops! What is the REAL length of this string? It's not just the control
  446.    characters... the length of the terminal name needs to be added in
  447. */
  448.                     resp_len += strlen(resp_buf);
  449.             }
  450.             break;
  451.         default:
  452.             break;
  453.     }
  454.   
  455.     if (resp_len > 0)
  456.         send(s, resp_buf, resp_len, 0);
  457. }
  458. #endif  /* TN_KK6JQ */
  459.   
  460. void
  461. doopt(tn,opt)
  462. struct telnet *tn;
  463. int opt;
  464. {
  465.     int ack;
  466.   
  467. #ifdef  DEBUG
  468.     printf("recv: do ");
  469.     if(uchar(opt) <= NOPTIONS)
  470.         printf("%s\n",T_options[uchar(opt)]);
  471.     else
  472.         printf("%u\n",uchar(opt));
  473. #endif
  474.     switch(uchar(opt)){
  475.         case TN_SUPPRESS_GA:
  476. #ifdef  TN_KK6JQ
  477.         case TN_TERM_TYPE:
  478. #endif  /* TN_KK6JQ */
  479.             if(tn->local[uchar(opt)] == 1)
  480.                 return;     /* Already set, ignore to prevent loop */
  481.                 tn->local[uchar(opt)] = 1;
  482.                 ack = WILL;
  483.                 break;
  484.         default:
  485.             ack = WONT; /* Don't know what it is */
  486.     }
  487.     answer(tn,ack,opt);
  488. }
  489. void
  490. dontopt(tn,opt)
  491. struct telnet *tn;
  492. int opt;
  493. {
  494. #ifdef  DEBUG
  495.     printf("recv: dont ");
  496.     if(uchar(opt) <= NOPTIONS)
  497.         printf("%s\n",T_options[uchar(opt)]);
  498.     else
  499.         printf("%u\n",uchar(opt));
  500. #endif
  501.     if(uchar(opt) <= NOPTIONS){
  502.         if(tn->local[uchar(opt)] == 0){
  503.             /* Already clear, ignore to prevent loop */
  504.             return;
  505.         }
  506.         tn->local[uchar(opt)] = 0;
  507.     }
  508.     answer(tn,WONT,opt);
  509. }
  510. void
  511. answer(tn,r1,r2)
  512. struct telnet *tn;
  513. int r1,r2;
  514. {
  515.     char s[3];
  516.   
  517. #ifdef  DEBUG
  518.     switch(r1){
  519.         case WILL:
  520.             printf("sent: will ");
  521.             break;
  522.         case WONT:
  523.             printf("sent: wont ");
  524.             break;
  525.         case DO:
  526.             printf("sent: do ");
  527.             break;
  528.         case DONT:
  529.             printf("sent: dont ");
  530.             break;
  531.     }
  532.     if(r2 <= NOPTIONS)
  533.         printf("%s\n",T_options[r2]);
  534.     else
  535.         printf("%u\n",r2);
  536. #endif
  537.   
  538.     s[0] = IAC;
  539.     s[1] = r1;
  540.     s[2] = r2;
  541.     send(tn->session->s,s,3,0);
  542. }
  543. #ifdef  __TURBOC__
  544. /* Set end-of-line translation mode on file */
  545. static int
  546. filemode(fp,mode)
  547. FILE *fp;
  548. int mode;
  549. {
  550.     int omode;
  551.   
  552.     if(fp == NULLFILE)
  553.         return -1;
  554.   
  555.     if(fp->flags & _F_BIN)
  556.         omode = SOCK_BINARY;
  557.     else
  558.         omode = SOCK_ASCII;
  559.   
  560.     switch(mode){
  561.         case SOCK_BINARY:
  562.             fp->flags = _F_BIN;
  563.             setmode(fileno(fp),O_BINARY);
  564.             break;
  565.         case SOCK_ASCII:
  566.             fp->flags &= ~_F_BIN;
  567.             setmode(fileno(fp),O_TEXT);
  568.             break;
  569.     }
  570.     return omode;
  571. }
  572. #else
  573. static int
  574. filemode(fp,mode)
  575. FILE *fp;
  576. int mode;
  577. {
  578.     return 0;
  579. }
  580. #endif
  581.