home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / tcpipsrc / telnet.c < prev    next >
C/C++ Source or Header  |  1991-01-26  |  9KB  |  447 lines

  1. /* Internet Telnet client
  2.  * Copyright 1991 Phil Karn, KA9Q
  3.  */
  4. #include <stdio.h>
  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.  
  19. static int filemode __ARGS((FILE *fp,int mode));
  20. #define    CTLZ    26
  21.  
  22. int Refuse_echo = 0;
  23. int Tn_cr_mode = 0;    /* if true turn <cr> to <cr-nul> */
  24.  
  25. #ifdef    DEBUG
  26. char *T_options[] = {
  27.     "Transmit Binary",
  28.     "Echo",
  29.     "",
  30.     "Suppress Go Ahead",
  31.     "",
  32.     "Status",
  33.     "Timing Mark"
  34. };
  35. #endif
  36.  
  37. /* Execute user telnet command */
  38. int
  39. dotelnet(argc,argv,p)
  40. int argc;
  41. char *argv[];
  42. void *p;
  43. {
  44.     struct session *sp;
  45.     struct sockaddr_in fsocket;
  46.  
  47.     /* Allocate a session descriptor */
  48.     if((sp = newsession(argv[1],TELNET)) == NULLSESSION){
  49.         tprintf("Too many sessions\n");
  50.         return 1;
  51.     }
  52.     fsocket.sin_family = AF_INET;
  53.     if(argc < 3)
  54.         fsocket.sin_port = IPPORT_TELNET;
  55.     else
  56.         fsocket.sin_port = atoi(argv[2]);
  57.  
  58.     tprintf("Resolving %s... ",sp->name);
  59.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  60.         tprintf(Badhost,sp->name);
  61.         keywait(NULLCHAR,1);
  62.         freesession(sp);
  63.         return 1;
  64.     }
  65.     if((sp->s = socket(AF_INET,SOCK_STREAM,0)) == -1){
  66.         tprintf("Can't create socket\n");
  67.         keywait(NULLCHAR,1);
  68.         freesession(sp);
  69.         return 1;
  70.     }
  71.     return tel_connect(sp,(char *)&fsocket,SOCKSIZE);
  72. }
  73. /* Generic interactive connect routine, used by Telnet, AX.25, NET/ROM */
  74. int
  75. tel_connect(sp,fsocket,len)
  76. struct session *sp;
  77. char *fsocket;
  78. int len;
  79. {
  80.     unsigned int index;
  81.     struct telnet tn;
  82.  
  83.     index = sp - Sessions;
  84.     memset((char *)&tn,0,sizeof(tn));
  85.     tn.eolmode = Tn_cr_mode;
  86.     tn.session = sp;    /* Upward pointer */
  87.     sp->cb.telnet = &tn;    /* Downward pointer */
  88.     sockmode(sp->s,SOCK_ASCII);    /* Default to ascii mode */
  89.  
  90.     tprintf("Trying %s...\n",psocket((struct sockaddr *)fsocket));
  91.     if(connect(sp->s,fsocket,len) == -1){
  92.           tprintf("%s session %u failed: %s errno %d\n",
  93.          Sestypes[sp->type], index, sockerr(sp->s),errno);
  94.  
  95.         keywait(NULLCHAR,1);
  96.         freesession(sp);
  97.         return 1;
  98.     }
  99.     tprintf("%s session ",Sestypes[sp->type]);
  100.     tprintf("%u connected to %s\n",index,sp->name);
  101.     tnrecv(&tn);
  102.     return 0;
  103. }
  104.  
  105. /* Telnet input routine, common to both telnet and ttylink */
  106. void
  107. tnrecv(tn)
  108. struct telnet *tn;
  109. {
  110.     int c,s,index;
  111.     struct session *sp;
  112.     char *cp;
  113.  
  114.     sp = tn->session;
  115.     s = sp->s;
  116.  
  117.     index = sp - Sessions;
  118.  
  119.     /* Fork off the transmit process */
  120.     sp->proc1 = newproc("tel_out",1024,tel_output,0,tn,NULL,0);
  121.  
  122.     /* Process input on the connection */
  123.     while((c = recvchar(s)) != -1){
  124.         if(c != IAC){
  125.             /* Ordinary character */
  126.             if(!tn->remote[TN_TRANSMIT_BINARY])
  127.                 c &= 0x7f;
  128.  
  129.             tputc((char)c);
  130.             continue;
  131.         }
  132.         /* IAC received, get command sequence */
  133.         c = recvchar(s);
  134.         switch(c){
  135.         case WILL:
  136.             c = recvchar(s);
  137.             willopt(tn,c);
  138.             break;
  139.         case WONT:
  140.             c = recvchar(s);
  141.             wontopt(tn,c);
  142.             break;
  143.         case DO:
  144.             c = recvchar(s);
  145.             doopt(tn,c);
  146.             break;
  147.         case DONT:
  148.             c = recvchar(s);
  149.             dontopt(tn,c);
  150.             break;
  151.         case IAC:    /* Escaped IAC */
  152.             tputc(IAC);
  153.             break;
  154.         }
  155.     }
  156. quit:    /* A close was received from the remote host.
  157.      * Notify the user, kill the output task and wait for a response
  158.      * from the user before freeing the session.
  159.      */
  160.     sockmode(sp->output,SOCK_ASCII); /* Restore newline translation */
  161.     cp = sockerr(s);
  162.     tprintf("%s session %u", Sestypes[sp->type],index);
  163.     tprintf(" closed: %s\n", cp != NULLCHAR ? cp : "EOF");
  164.     killproc(sp->proc1);
  165.     sp->proc1 = NULLPROC;
  166.     close_s(sp->s);
  167.     sp->s = -1;
  168.     keywait(NULLCHAR,1);
  169.     freesession(sp);
  170. }
  171.  
  172. /* User telnet output task, started by user telnet command */
  173. void
  174. tel_output(unused,tn1,p)
  175. int unused;
  176. void *tn1;
  177. void *p;
  178. {
  179.     struct session *sp;
  180.     int c;
  181.     struct telnet *tn;
  182.  
  183.     tn = (struct telnet *)tn1;
  184.     sp = tn->session;
  185.  
  186.     /* Send whatever's typed on the terminal */
  187.     while((c = recvchar(sp->input)) != EOF){
  188.         usputc(sp->s,(char)c);
  189.         if(!tn->remote[TN_ECHO] && sp->record != NULLFILE)
  190.             putc(c,sp->record);
  191.  
  192.         /* By default, output is transparent in remote echo mode.
  193.          * If eolmode is set, turn a cr into cr-null.
  194.          * This can only happen when in remote echo (raw) mode, since
  195.          * the tty driver normally maps \r to \n in cooked mode.
  196.          */
  197.         if(c == '\r' && tn->eolmode)
  198.             usputc(sp->s,'\0');
  199.  
  200.         if(tn->remote[TN_ECHO])
  201.             usflush(sp->s);
  202.     }
  203.     /* Make sure our parent doesn't try to kill us after we exit */
  204.     sp->proc1 = NULLPROC;
  205. }
  206. int
  207. doecho(argc,argv,p)
  208. int argc;
  209. char *argv[];
  210. void *p;
  211. {
  212.     if(argc < 2){
  213.         if(Refuse_echo)
  214.             tprintf("Refuse\n");
  215.         else
  216.             tprintf("Accept\n");
  217.     } else {
  218.         if(argv[1][0] == 'r')
  219.             Refuse_echo = 1;
  220.         else if(argv[1][0] == 'a')
  221.             Refuse_echo = 0;
  222.         else
  223.             return -1;
  224.     }
  225.     return 0;
  226. }
  227. /* set for unix end of line for remote echo mode telnet */
  228. int
  229. doeol(argc,argv,p)
  230. int argc;
  231. char *argv[];
  232. void *p;
  233. {
  234.     if(argc < 2){
  235.         if(Tn_cr_mode)
  236.             tprintf("null\n");
  237.         else
  238.             tprintf("standard\n");
  239.     } else {
  240.         if(argv[1][0] == 'n')
  241.             Tn_cr_mode = 1;
  242.         else if(argv[1][0] == 's')
  243.             Tn_cr_mode = 0;
  244.         else {
  245.             tprintf("Usage: %s [standard|null]\n",argv[0]);
  246.             return -1;
  247.         }
  248.     }
  249.     return 0;
  250. }
  251.  
  252. /* The guts of the actual Telnet protocol: negotiating options */
  253. void
  254. willopt(tn,opt)
  255. struct telnet *tn;
  256. int opt;
  257. {
  258.     int ack;
  259.  
  260. #ifdef    DEBUG
  261.     printf("recv: will ");
  262.     if(uchar(opt) <= NOPTIONS)
  263.         printf("%s\n",T_options[opt]);
  264.     else
  265.         printf("%u\n",opt);
  266. #endif
  267.     
  268.     switch(uchar(opt)){
  269.     case TN_TRANSMIT_BINARY:
  270.     case TN_ECHO:
  271.     case TN_SUPPRESS_GA:
  272.         if(tn->remote[uchar(opt)] == 1)
  273.             return;        /* Already set, ignore to prevent loop */
  274.         if(uchar(opt) == TN_ECHO){
  275.             if(Refuse_echo){
  276.                 /* User doesn't want to accept */
  277.                 ack = DONT;
  278.                 break;
  279.             } else {
  280.                 /* Put tty into raw mode */
  281.                 tn->session->ttystate.edit = 0;
  282.                 tn->session->ttystate.echo = 0;
  283.                 sockmode(tn->session->s,SOCK_BINARY);
  284.                 sockmode(tn->session->input,SOCK_BINARY);
  285.                 sockmode(tn->session->output,SOCK_BINARY);
  286.                 if(tn->session->record != NULLFILE)
  287.                     filemode(tn->session->record,SOCK_BINARY);
  288.  
  289.             }
  290.         }
  291.         tn->remote[uchar(opt)] = 1;
  292.         ack = DO;            
  293.         break;
  294.     default:
  295.         ack = DONT;    /* We don't know what he's offering; refuse */
  296.     }
  297.     answer(tn,ack,opt);
  298. }
  299. void
  300. wontopt(tn,opt)
  301. struct telnet *tn;
  302. int opt;
  303. {
  304. #ifdef    DEBUG
  305.     printf("recv: wont ");
  306.     if(uchar(opt) <= NOPTIONS)
  307.         printf("%s\n",T_options[uchar(opt)]);
  308.     else
  309.         printf("%u\n",uchar(opt));
  310. #endif
  311.     if(uchar(opt) <= NOPTIONS){
  312.         if(tn->remote[uchar(opt)] == 0)
  313.             return;        /* Already clear, ignore to prevent loop */
  314.         tn->remote[uchar(opt)] = 0;
  315.         if(uchar(opt) == TN_ECHO){
  316.             /* Put tty into cooked mode */
  317.             tn->session->ttystate.edit = 1;
  318.             tn->session->ttystate.echo = 1;
  319.             sockmode(tn->session->s,SOCK_ASCII);
  320.             sockmode(tn->session->input,SOCK_ASCII);
  321.             sockmode(tn->session->output,SOCK_ASCII);
  322.             if(tn->session->record != NULLFILE)
  323.                 filemode(tn->session->record,SOCK_ASCII);
  324.         }
  325.     }
  326.     answer(tn,DONT,opt);    /* Must always accept */
  327. }
  328. void
  329. doopt(tn,opt)
  330. struct telnet *tn;
  331. int opt;
  332. {
  333.     int ack;
  334.  
  335. #ifdef    DEBUG
  336.     printf("recv: do ");
  337.     if(uchar(opt) <= NOPTIONS)
  338.         printf("%s\n",T_options[uchar(opt)]);
  339.     else
  340.         printf("%u\n",uchar(opt));
  341. #endif
  342.     switch(uchar(opt)){
  343. #ifdef    FUTURE    /* Use when local options are implemented */
  344.         if(tn->local[uchar(opt)] == 1)
  345.             return;        /* Already set, ignore to prevent loop */
  346.         tn->local[uchar(opt)] = 1;
  347.         ack = WILL;
  348.         break;
  349. #endif
  350.     default:
  351.         ack = WONT;    /* Don't know what it is */
  352.     }
  353.     answer(tn,ack,opt);
  354. }
  355. void
  356. dontopt(tn,opt)
  357. struct telnet *tn;
  358. int opt;
  359. {
  360. #ifdef    DEBUG
  361.     printf("recv: dont ");
  362.     if(uchar(opt) <= NOPTIONS)
  363.         printf("%s\n",T_options[uchar(opt)]);
  364.     else
  365.         printf("%u\n",uchar(opt));
  366. #endif
  367.     if(uchar(opt) <= NOPTIONS){
  368.         if(tn->local[uchar(opt)] == 0){
  369.             /* Already clear, ignore to prevent loop */
  370.             return;
  371.         }
  372.         tn->local[uchar(opt)] = 0;
  373.     }
  374.     answer(tn,WONT,opt);
  375. }
  376. void
  377. answer(tn,r1,r2)
  378. struct telnet *tn;
  379. int r1,r2;
  380. {
  381.     char s[3];
  382.  
  383. #ifdef    DEBUG
  384.     switch(r1){
  385.     case WILL:
  386.         printf("sent: will ");
  387.         break;
  388.     case WONT:
  389.         printf("sent: wont ");
  390.         break;
  391.     case DO:
  392.         printf("sent: do ");
  393.         break;
  394.     case DONT:
  395.         printf("sent: dont ");
  396.         break;
  397.     }
  398.     if(r2 <= NOPTIONS)
  399.         printf("%s\n",T_options[r2]);
  400.     else
  401.         printf("%u\n",r2);
  402. #endif
  403.  
  404.     s[0] = IAC;
  405.     s[1] = r1;
  406.     s[2] = r2;
  407.     send(tn->session->s,s,3,0);
  408. }
  409. #ifdef    __TURBOC__
  410. /* Set end-of-line translation mode on file */
  411. static int
  412. filemode(fp,mode)
  413. FILE *fp;
  414. int mode;
  415. {
  416.     int omode;
  417.  
  418.     if(fp == NULLFILE)
  419.         return -1;
  420.  
  421.     if(fp->flags & _F_BIN)
  422.         omode = SOCK_BINARY;
  423.     else
  424.         omode = SOCK_ASCII;
  425.  
  426.     switch(mode){
  427.     case SOCK_BINARY:
  428.         fp->flags = _F_BIN;
  429.         setmode(fileno(fp),O_BINARY);
  430.         break;
  431.     case SOCK_ASCII:
  432.         fp->flags &= ~_F_BIN;
  433.         setmode(fileno(fp),O_TEXT);
  434.         break;
  435.     }
  436.     return omode;
  437. }
  438. #else
  439. static int
  440. filemode(fp,mode)
  441. FILE *fp;
  442. int mode;
  443. {
  444.     return 0;
  445. }
  446. #endif
  447.