home *** CD-ROM | disk | FTP | other *** search
/ ftp.qualcomm.com / 2014.06.ftp.qualcomm.com.tar / ftp.qualcomm.com / eudora / servers / unix / srialpop.shar / srialpop.c < prev   
C/C++ Source or Header  |  1997-03-26  |  14KB  |  532 lines

  1. /*
  2.  
  3.     S R I A L P O P  (POP over serial lines)
  4.  
  5.     Author: Rudi van Houten <R.vanHouten@cc.ruu.nl>
  6.         Ac.Comp.Centr.Utrecht (Netherlands)
  7.     With thanks to:
  8.         Steve Dorner <sdorner@qualcomm.com>
  9.         Scott Hannahs <sth@slipknot.mit.edu>
  10.         Mogen Trab Damsgaard <trab@kubism.ku.dk>
  11.         Richard J. Johnson <johnsonr@Colorado.EDU>
  12.  
  13.     see the file README for an introduction and hints for installation
  14.  
  15. ========================== COPYRIGHT NOTICE =============================
  16. This package srialpop is written by and copyright Rudi van Houten
  17. <R.vanHouten@cc.ruu.nl>, it is tested and used now at ACCU (Ac.Comp.
  18. Centr.Utrecht, the Netherlands) for several years.
  19.  
  20. The package consists of four files:
  21.     srialpop.c  the C source of the program
  22.     Makefile    to build and eventually install the package
  23.     README      an old documentation describing the package
  24.     INSTALL     a newer document with concise installation instructions
  25.  
  26. I grant everybody the right to use the package and eventually
  27. adapt it to local requirements. Please document your changes in the
  28. program source with comments explicitely stating that a modification
  29. to the original package has been made. I don't want to be blamed for
  30. your errors nor get the praise for your inventions.
  31.  
  32. Also everybody has the right to bundle this package with other software
  33. (e.g. a POP client) on distribution media, but THEN THE UNCHANGED VERSION
  34. MUST BE DISTRIBUTED, and no extra fee may be charged for the presence
  35. of srialpop in the bundle. If there is a need to modify srialpop for
  36. special purposes these modification should be made available as a separate
  37. file (e.g. a diff file to be applied with Larry Wall's patch program).
  38.  
  39. I present this package as is, feeling it can be of use. But no
  40. guarantees are given about its proper working or behaviour. It has
  41. been used at ACCU for some years now without troubles, so I think it may
  42. be bug free. But I cannot be held responsible for any damage or other
  43. misery resulting from using srialpop.
  44. ===========================================================================
  45.  
  46. */
  47. static char sccsid[]= "@(#)srialpop.c    2.3";
  48.  
  49. /*
  50.     this is the time to wait (seconds) after a logout command before
  51.     actually breaking the connection
  52. */
  53. #define LOGOUTGRACE 3
  54.  
  55. /*
  56.     define some symbols for the logging facility
  57. */
  58. #define ONFILE 0
  59. #define LOGBSD42 1
  60. #define LOGBSD43 2
  61. /*
  62.     define the include file for the termio/sgtty structs
  63. */
  64. #define SGTTY   0
  65. #define TERMIO  1
  66. #define TERMIOS 2
  67.  
  68. #include <stdio.h>
  69. #include "hdrs.h"
  70. #include <fcntl.h>
  71. #include <signal.h>
  72. #include <ctype.h>
  73. #include <sys/types.h>
  74. #include <sys/socket.h>
  75. #include <netinet/in.h>
  76. #include <netdb.h>
  77. #include <errno.h>
  78. #include <string.h>
  79. #if TERM_H==TERMIOS
  80. # include <termios.h>
  81. #elif TERM_H==TERMIO
  82. # include <termio.h>
  83. #else
  84. # include <sgtty.h>
  85. #endif
  86. #ifdef __STDC__
  87. # include <stdarg.h>
  88. #else
  89. # include <varargs.h>
  90. #endif
  91. #include <time.h>
  92. #if LOGGING > ONFILE
  93. # include <syslog.h>
  94. #endif
  95.  
  96. #ifndef TIMEOUT
  97. # define TIMEOUT 300
  98. #endif
  99.  
  100. #ifdef LOGGING
  101. # if LOGGING==ONFILE
  102. #  define LOG_NOTICE 0
  103. #  define LOG_INFO 1
  104. #  undef LOGLEVEL
  105. #  define LOGLEVEL LOG_INFO
  106. # else
  107. #  if LOGGING==LOGBSD43
  108. #   ifndef LOGFACILITY
  109. #    define LOGFACILITY LOG_LOCAL0
  110. #   endif
  111. #  endif
  112. #  ifndef LOGLEVEL
  113. #   define LOGLEVEL LOG_INFO
  114. #  endif
  115. # endif
  116. #endif
  117.  
  118. #define CRLF "\r\n"
  119. #define warning(msg) write(2,msg,strlen(msg))
  120.  
  121. #define iBuflen 512
  122. static char sBuf[iBuflen];
  123.  
  124. static short iValidPort[]=
  125.         {105    /* PH server */
  126.         ,106    /* password server */
  127.         ,109    /* POP2 */
  128.         ,110    /* POP3 */
  129. #ifndef NO_SMTP
  130.         ,25     /* SMTP */
  131. #endif
  132.         };
  133. #define iValidPortsCount sizeof(iValidPort)/sizeof(short)
  134.  
  135. #if TERM_H==TERMIOS
  136. static struct termios sTermHold, sTermCurr;
  137. #elif TERM_H==TERMIO
  138. static struct termio sTermHold, sTermCurr;
  139. #else
  140. static struct sgttyb sTermHold, sTermCurr;
  141. #endif
  142.  
  143. #if LOGGING==ONFILE
  144. static char *tMonth[]=
  145.     {"Jan","Feb","Mar","Apr","May","Jun"
  146.     ,"Jul","Aug","Sep","Oct","Nov","Dec"
  147.     };
  148. #endif
  149.  
  150. #if TERM_H > SGTTY
  151. # if defined(sgi) || defined(__sgi)
  152. #  define CRTSCTS CNEW_RTSCTS
  153. # elif !defined(CRTSCTS) && defined(FLOWRTSCTS)
  154.    !! This machine seems unable to set rts/cts flowcontrol
  155.    !! disable/change that definition in the Makefile in DEFS=
  156. # endif
  157. #endif
  158.  
  159. extern int errno, sys_nerr;
  160. extern char *sys_errlist[];
  161.  
  162. #ifdef __STDC__
  163. static void errorexit(const char*, ... );
  164. static void timetrap(int);
  165. static void timtrap2(int);
  166. # ifdef EXTRATTYFLAGS
  167. static void extrattyflags(void);
  168. # endif
  169. # ifdef LOGGING
  170. static void report(const int,const char*, ...);
  171. # endif
  172. void main(void);
  173. #endif
  174.  
  175. #ifdef EXTRATTYFLAGS
  176. static void extrattyflags()
  177. {
  178. /*
  179.  * This routine can be enabled by defining EXTRATTYFLAGS
  180.  * in the Makefile.
  181.  * Here code can be placed to add/clear extra flags and
  182.  * attributes in the tty discipline descriptions if you feel
  183.  * the need and don't want to change the main routine.
  184.  */
  185. return;
  186. } /* void extrattyflags */
  187. #endif
  188.  
  189. #ifdef LOGGING
  190. # ifdef __STDC__
  191. static void report(const int level,const char *sFormat, ...)
  192. {
  193. # else
  194. static void report(va_alist) va_dcl
  195. {
  196.     int level;
  197.     char *sFormat;
  198. # endif
  199.     char sLogBuf[128];
  200.     va_list ap;
  201. # if LOGGING==ONFILE
  202.     time_t iNow;
  203.     struct tm *sNow;
  204.     register FILE *logfile;
  205. # endif
  206. # ifdef __STDC__
  207.     va_start(ap,sFormat);
  208. # else
  209.     va_start(ap);
  210.     level= va_arg(ap,int);
  211.     sFormat= va_arg(ap,char*);
  212. # endif
  213.     vsprintf(sLogBuf,sFormat,ap);
  214.     va_end(ap);
  215. # if LOGGING==ONFILE
  216.     iNow= time(NULL);
  217.     sNow= localtime(&iNow);
  218.     if ((logfile= fopen("srialpop.log","a+")) == NULL)
  219.         warning("Couldn't open logfile, notify system manager\r\n");
  220.     else
  221.     {
  222.         fprintf(logfile,"%2d %s %2d %2d:%2d:%2d [%d]%s: %s\n"
  223.             ,sNow->tm_mday,tMonth[sNow->tm_mon],sNow->tm_year
  224.             ,sNow->tm_hour,sNow->tm_min,sNow->tm_sec
  225.             ,getpid(),level == LOGLEVEL ? "" : " NOTICE",sLogBuf
  226.             );
  227.         (void)fclose(logfile);
  228.     }
  229. # else
  230.     (void)syslog(level,"%s",sLogBuf);
  231. # endif
  232. } /* void report() */
  233. #endif
  234.  
  235. #ifdef __STDC__
  236. static void errorexit(const char *sFormat, ...)
  237. {
  238. #else
  239. static void errorexit(va_alist) va_dcl
  240. {
  241.     char *sFormat;
  242. #endif
  243.     char sMsgBuf[128];
  244.     va_list ap;
  245.     write(2,CRLF,2);
  246. #ifdef __STDC__
  247.     va_start(ap,sFormat);
  248. #else
  249.     va_start(ap);
  250.     sFormat= va_arg(ap,char*);
  251. #endif
  252.     vsprintf(sMsgBuf,sFormat,ap);
  253.     va_end(ap);
  254.     warning(sMsgBuf);
  255.     write(2,CRLF,2);
  256. #ifdef LOGGING
  257.     report(errno != 0 ? LOG_NOTICE : LOGLEVEL,sMsgBuf);
  258. #endif
  259. #if TERM_H==TERMIOS
  260.     tcsetattr(0,TCSANOW,&sTermHold);
  261. #elif TERM_H==TERMIO
  262.     ioctl(0,TCSETAF,&sTermHold);
  263. #else
  264.     ioctl(0,TIOCSETP,&sTermHold);
  265. #endif
  266.     sleep(1); exit(1);
  267. } /* errorexit */
  268.  
  269. static void timetrap(iSign)
  270. int iSign;
  271. {
  272.     errorexit("Timeout on %d seconds",TIMEOUT);
  273. } /* timetrap */
  274.  
  275. static void timtrap2(iSign)
  276. int iSign;
  277. {
  278.     exit(1);
  279. } /* timtrap2 */
  280.  
  281. void main()
  282. {
  283. int iSock, iPort, iChild;
  284. register int i, l;
  285. struct in_addr sInAddr;
  286. union CombineSock
  287.     {
  288.         struct sockaddr_in sServer;
  289.         struct sockaddr addr;
  290.     } uServer;
  291. struct hostent *psHost, *gethostbyname(), *gethostbyaddr();
  292. char *psCommand, *psName, *psNumber;
  293.  
  294. #ifdef LOGGING
  295. # if LOGGING==LOGBSD42
  296.     openlog("srialpop",LOG_PID);
  297. # elif LOGGING==LOGBSD43
  298.     openlog("srialpop",LOG_PID,LOGFACILITY);
  299. # endif
  300. #endif
  301.  
  302.     /* get stty structure sTermHold */
  303. #if TERM_H==TERMIOS
  304. (void)tcgetattr(0,&sTermHold);
  305. #elif TERM_H==TERMIO
  306. ioctl(0,TCGETA,&sTermHold);
  307. #else
  308. ioctl(0,TIOCGETP,&sTermHold);
  309. #endif
  310. memcpy(&sTermCurr,&sTermHold,sizeof(sTermCurr));
  311.  
  312.     /* and here prepare the line characteristics */
  313.  
  314. #if TERM_H > SGTTY
  315.  
  316.     /* don't translate CR or LF, neither ignore CR */
  317. sTermCurr.c_iflag  &= ~(ICRNL | INLCR | IGNCR);
  318.  
  319. # ifndef _POSIX_SOURCE
  320. sTermCurr.c_iflag &= ~IXANY;
  321. # endif
  322.  
  323. # ifdef FLOWXONXOFF
  324.     /* enable software flow control (XON/XOFF, ^S/^Q) */
  325. sTermCurr.c_iflag |= (IXON | IXOFF);
  326. # endif
  327.  
  328.     /* no output post processing */
  329. sTermCurr.c_oflag  &= ~OPOST;
  330.  
  331. # ifdef FLOWRTSCTS
  332.     /* enable hardware flow control (CTS/RTS) */
  333. sTermCurr.c_cflag |= CRTSCTS;
  334. # endif
  335.  
  336.     /* disable echo, erase/kill processing and special
  337.      * signal handling (INTR, SUP, QUIT */
  338. sTermCurr.c_lflag  &= ~(ECHO | ICANON | ISIG );
  339.  
  340.     /* read will wait endlessly till a character is received,
  341.      * but a timeout is set with alarm */
  342. sTermCurr.c_cc[VMIN] = 1;
  343. sTermCurr.c_cc[VTIME] = 0;
  344.  
  345. #else
  346.  
  347.     /* disable echo, CR translation, tab expanding */
  348. sTermCurr.sg_flags &= ~(ECHO | CRMOD | XTABS);
  349.     /* set half-cooked mode */
  350. sTermCurr.sg_flags |= CBREAK;
  351.     /* get rid of funny old AT&T defaults for erase and kill */
  352. sTermCurr.sg_erase = 0xff; sTermCurr.sg_kill = 0x15;
  353.  
  354. # ifdef FLOWRTSCTS
  355.    !! hardware flow control not supported with sgtty.h
  356.    !! disable/change that definition in the Makefile in DEFS=
  357. # endif
  358. # ifdef FLOWXONXOFF
  359.     /* set software flow control (XON/XOFF, ^S/^Q) */
  360. sTermCurr.sg_flags |= TANDEM;
  361. # endif
  362.  
  363. #endif
  364.  
  365. #ifdef EXTRATTYFLAGS
  366. extrattyflags();
  367. #endif
  368.         /* set line characteristics to NOECHO, NOCRLF etc. */
  369. #if TERM_H==TERMIOS
  370. tcsetattr(0,TCSANOW,&sTermCurr);
  371. #elif TERM_H==TERMIO
  372. ioctl(0,TCSETAF,&sTermCurr);
  373. #else
  374. ioctl(0,TIOCSETP,&sTermCurr);
  375. #endif
  376.  
  377.  
  378.  
  379. /* first set the timeout trap (primitive signal call) */
  380. signal(SIGALRM,timetrap);
  381. #ifdef LOGGING
  382. report(LOGLEVEL,"srialpop starts processing");
  383. #endif
  384.  
  385. /* this is the main loop, it reads the telnet or exit commandline */
  386. for (;;)
  387. {
  388.     write(1,"%",1); l= 0;
  389.     while(alarm(TIMEOUT)
  390.            ,read(0,&sBuf[l],1) == 1 && sBuf[l] != '\n' && sBuf[l] != '\r'
  391.          )
  392.     {
  393.         alarm(0);
  394.         write(1,&sBuf[l],1);
  395.         if (++l >= iBuflen) errorexit("Too long commandline");
  396.     }
  397.     if (sBuf[l] == '\n' || sBuf[l] == '\r') sBuf[l]= '\0'; else sBuf[++l]= '\0';
  398.     if (l == 0) continue;
  399.     write(1,CRLF,2);
  400.  
  401.     if (strcmp(sBuf,"logout") == 0
  402.          || strcmp(sBuf,"exit") == 0
  403.          || strcmp(sBuf,"quit") == 0
  404.         )
  405.         break; /* finished, logout now */
  406.  
  407.             /* next loop splits the commandline in its constituents */
  408.     i= 0; psCommand= psName= psNumber= NULL;
  409.     while (i < l && psNumber == NULL)
  410.     {
  411.         while (isspace(sBuf[i])) i++;
  412.         if (i >= l) break;
  413.         if (psCommand == NULL) psCommand= &sBuf[i]; /* command found */
  414.         else
  415.         if (psName == NULL) psName= &sBuf[i];       /* argument 1 */
  416.         else psNumber= &sBuf[i];                    /* argument 2 */
  417.         do { i++; } while (!isspace(sBuf[i]) && i < l);
  418.         sBuf[i++]= '\0';        /* terminator after last argument */
  419.     }
  420.     /* now we have a command (telnet) with two
  421.        arguments (1=host 2=portnumber) */
  422.  
  423.     if (strcmp(psCommand,"telnet"))
  424.         errorexit("Sorry, don't know '%s'\n",psCommand);
  425.                     /* telnet is the only known command */
  426.  
  427.     if (psName == NULL || psNumber == NULL)
  428.         errorexit("Sorry, hostname or portnumber missing\n");
  429.                         /* and both host and port are required */
  430.  
  431.     if (l= strlen(psNumber))
  432.     {
  433.         for (i= 0; i < l; i++)
  434.             if (!isdigit(psNumber[i]))
  435.                 errorexit("Portnumber (%s) not numeric\n",psNumber);
  436.         iPort= atoi(psNumber);
  437.     }
  438.     else iPort= 0;
  439.  
  440. #ifdef LOGGING
  441.     report(LOGLEVEL,"connect to host %s on port %d",psName,iPort);
  442. #endif
  443.  
  444.                         /* check validity of portnumber */
  445.     for (i=0; iPort != iValidPort[i]; i++)
  446.         if (i == iValidPortsCount)
  447.             errorexit("Sorry, %d is not a valid port\n",iPort);
  448.  
  449.         /*
  450.            (unsigned long)(-1) = 0xffffffff, returnvalue
  451.            from inet_addr() if illegal address passed through.
  452.            I assume then it is a domain name.
  453.            Thanks to Edwin Kremer (edwin@cs.ruu.nl) for the trick
  454.         */
  455.     if ((sInAddr.s_addr= inet_addr(psName)) == 0xffffffff)
  456.         psHost= gethostbyname(psName);
  457.     else
  458.         psHost= gethostbyaddr((char*)&sInAddr,sizeof(struct in_addr),AF_INET);
  459.     if (psHost == 0)
  460.         errorexit("Cannot find server %s (host unknown)\n",psName);
  461.     
  462.         /* create socket */
  463.     if ((iSock= socket(AF_INET,SOCK_STREAM,0)) < 0)
  464.         errorexit("Cannot open IP socket: %s"
  465.                   , errno > 0 && errno < sys_nerr ? sys_errlist[errno] : ""
  466.                   );
  467.  
  468.         /* fill socket structure .....*/
  469.     uServer.sServer.sin_family= AF_INET;
  470.     memcpy(&(uServer.sServer.sin_addr),psHost->h_addr,psHost->h_length);
  471.     uServer.sServer.sin_port= htons(iPort);
  472.     
  473.         /*........and connect */
  474.     if (connect(iSock,&(uServer.addr),sizeof(uServer.addr)) < 0)
  475.         errorexit("connecting stream socket: %s"
  476.                   , errno > 0 && errno < sys_nerr ? sys_errlist[errno] : ""
  477.                   );
  478.  
  479.         /* now fork a child to copy stdin to socket */
  480.     iChild= fork();
  481.     if (iChild < 0) 
  482.         errorexit("Cannot fork: %s"
  483.                   , errno > 0 && errno < sys_nerr ? sys_errlist[errno] : ""
  484.                   );
  485.     if (iChild == 0)
  486.     { /* the child copies stdin to the socket */
  487.         close(1); close(2); /* now close stdout and stderr */
  488.         signal(SIGALRM,timtrap2); /* reset to even more primitve routine */
  489.         if (alarm(TIMEOUT), read(0,sBuf,1) == 1)
  490.         {
  491.             if (sBuf[0] != '\r' && sBuf[0] != '\n') write(iSock,sBuf,1);
  492.             while ((alarm(TIMEOUT), l= read(0,sBuf,iBuflen)) > 0)
  493.                 write(iSock,sBuf,l);
  494.             alarm(0);
  495.         }
  496.         close(0); exit(0);
  497.     } /* if (iChild == 0) */
  498.     /* no else part, child does exit
  499.        the parent copies the socket to stdout
  500.     */
  501.     while ((alarm(TIMEOUT), l= read(iSock,sBuf,iBuflen)) > 0) write(1,sBuf,l);
  502.     alarm(0);
  503.     close(iSock);
  504.  
  505.     kill(iChild,SIGTERM); /* superfluous */
  506. #ifdef LOGGING
  507.     report(LOGLEVEL,"close TCP connection on port %d",iPort);
  508. #endif
  509.  
  510. } /* main loop */
  511.  
  512. #ifdef LOGGING
  513. report(LOGLEVEL,"srialpop session ending normally");
  514. #endif
  515.  
  516.         /* reset line characteristics */
  517. #if TERM_H==TERMIOS
  518. tcsetattr(0,TCSANOW,&sTermHold);
  519. #elif TERM_H==TERMIO
  520. ioctl(0,TCSETAF,&sTermHold);
  521. #else
  522. ioctl(0,TIOCSETP,&sTermHold);
  523. #endif
  524.  
  525. sleep(LOGOUTGRACE); /* wait to satisfy Mac's Hayes Modem Tool */
  526. close(0); close(1); close(2);
  527. #if LOGGING==LOGBSD42 || LOGGING==LOGBSD43
  528. closelog();
  529. #endif
  530. exit(0);
  531. } /* main */
  532.