home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / N / TCPIP / NNTP-1.000 / NNTP-1 / nntp.1.5.11t / xmit / remote.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-17  |  10.4 KB  |  478 lines

  1. #ifndef lint
  2. static char * rcsid = "@(#)$Header: remote.c,v 1.4 91/01/25 21:47:52 sob Exp $";
  3. #endif
  4. /*
  5. ** remote communication routines for NNTP/SMTP style communication.
  6. **
  7. ************
  8. ** This version has been modified to support mmap()'ing of article files
  9. ** on systems that support it.
  10. **
  11. ** David Robinson (david@elroy.jpl.nasa.gov) and
  12. ** Steve Groom (stevo@elroy.jpl.nasa.gov), June 30, 1989.
  13. **
  14. ************
  15. **
  16. **    sendcmd        - return TRUE on error.
  17. **
  18. **    readreply    - return reply code or FAIL for error;
  19. **                modifies buffer passed to it.
  20. **
  21. **    converse    - sendcmd() & readreply();
  22. **                return reply code or FAIL for error;
  23. **                modifies buffer passed to it.
  24. **
  25. **    hello        - establish connection with remote;
  26. **                check greeting code.
  27. **
  28. **    goodbye        - give QUIT command, and shut down connection.
  29. **
  30. **    sfgets        - safe fgets(); does fgets with TIMEOUT.
  31. **              (N.B.: possibly unportable stdio macro ref in here)
  32. **
  33. **    rfgets        - remote fgets() (calls sfgets());
  34. **                does SMTP dot escaping and
  35. **                \r\n -> \n conversion.
  36. **
  37. **    sendfile    - send a file with SMTP dot escaping and
  38. **                \n -> \r\n conversion.
  39. **
  40. ** Erik E. Fair <fair@ucbarpa.berkeley.edu>
  41. */
  42. #include "../common/conf.h"
  43. #include "nntpxmit.h"
  44. #include <sys/types.h>
  45. #include <sys/socket.h>
  46. #include <errno.h>
  47. #include <stdio.h>
  48. #include <ctype.h>
  49. #include <setjmp.h>
  50. #include <signal.h>
  51. #ifdef dgux
  52. #define _IOERR  _IO_ERR
  53. #endif
  54. #ifdef SYSLOG
  55. #ifdef FAKESYSLOG
  56. #include "../server/fakesyslog.h"
  57. #else
  58. #include <syslog.h>
  59. #endif
  60. #endif
  61. #include "get_tcp_conn.h"
  62. #include "../common/nntp.h"
  63.  
  64. static    jmp_buf    SFGstack;
  65. FILE    *rmt_rd;
  66. FILE    *rmt_wr;
  67. char    *sfgets();
  68. char    *rfgets();
  69.  
  70. extern    int    errno;
  71. extern    char    Debug;
  72. extern    char    *errmsg();
  73. extern    char    *strcpy();
  74. extern    void    log();
  75.  
  76. /*
  77. ** send cmd to remote, terminated with a CRLF.
  78. */
  79. sendcmd(cmd)
  80. char    *cmd;
  81. {
  82.     if (cmd == (char *)NULL)
  83.         return(TRUE);    /* error */
  84.     dprintf(stderr, ">>> %s\n", cmd);    /* DEBUG */
  85.     (void) fprintf(rmt_wr, "%s\r\n", cmd);
  86.     (void) fflush(rmt_wr);
  87.     return(ferror(rmt_wr));
  88. }
  89.  
  90. /*
  91. ** read a reply line from the remote server and return the code number
  92. ** as an integer, and the message in a buffer supplied by the caller.
  93. ** Returns FAIL if something went wrong.
  94. */
  95. readreply(buf, size)
  96. register char    *buf;
  97. int    size;
  98. {
  99.     register char    *cp;
  100.     register int    len;
  101.  
  102.     if (buf == (char *)NULL || size <= 0)
  103.         return(FAIL);
  104.  
  105.     /*
  106.     ** make sure it's invalid, unless we say otherwise
  107.     */
  108.     buf[0] = '\0';
  109.  
  110.     /*
  111.     ** read one line from the remote
  112.     */
  113.     if (sfgets(buf, size, rmt_rd) == NULL)
  114.         return(FAIL);    /* error reading from remote */
  115.  
  116.     /*
  117.     ** Make sure that what the remote sent us had a CRLF at the end
  118.     ** of the line, and then null it out.
  119.     */
  120.     if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r' &&
  121.         *(cp + 1) == '\n')
  122.     {
  123.         *cp = '\0';
  124.     } else
  125.         return(FAIL);    /* error reading from remote */
  126.  
  127.     dprintf(stderr, "%s\n", buf);    /* DEBUG */
  128.     /*
  129.     ** Skip any non-digits leading the response code 
  130.     ** and then convert the code from ascii to integer for
  131.     ** return from this routine.
  132.     */
  133.     cp = buf;
  134.     while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
  135.         cp++;    /* skip anything leading */
  136.  
  137.     if (*cp == '\0' || !isascii(*cp))
  138.         return(FAIL);    /* error reading from remote */
  139.  
  140.     return(atoi(cp));
  141. }
  142.  
  143. /*
  144. ** send a command to the remote, and wait for a response
  145. ** returns the response code, and the message in the buffer
  146. */
  147. converse(buf, size)
  148. char    *buf;
  149. int    size;
  150. {
  151.     register int    resp;
  152.  
  153.     if (sendcmd(buf))
  154.         return(FAIL);    /* Ooops! Something went wrong in xmit */
  155.     /*
  156.     ** Skip the silly 100 series messages, since they're not the
  157.     ** final response we can expect
  158.     */
  159.     while((resp = readreply(buf, size)) >= 100 && resp < 200)
  160.         continue;
  161.     return(resp);
  162. }
  163.  
  164. /*
  165. ** Contact the remote server and set up the two global FILE pointers
  166. ** to that descriptor.
  167. **
  168. ** I can see the day when this routine will have 8 args:  one for
  169. ** hostname, and one for each of the seven ISO Reference Model layers
  170. ** for networking. A curse upon those involved with the ISO protocol
  171. ** effort: may they be forced to use the network that they will create,
  172. ** as opposed to something that works (like the Internet).
  173. */
  174. hello(host, transport)
  175. char    *host;
  176. int    transport;
  177. { char    *service;
  178.     char    *rmode = "r";
  179.     char    *wmode = "w";
  180.     char    *e_fdopen = "fdopen(%d, \"%s\"): %s";
  181.     int    socket0, socket1;    /* to me (bad pun) */
  182.     char    buf[BUFSIZ];
  183.  
  184.     switch(transport) {
  185.     case T_IP_TCP:
  186.         service = "nntp";
  187.         socket0 = get_tcp_conn(host, service);
  188.         break;
  189.     case T_DECNET:
  190. #ifdef DECNET
  191.         (void) signal(SIGPIPE, SIG_IGN);
  192.         service = "NNTP";
  193.         socket0 = dnet_conn(host, service, 0, 0, 0, 0, 0);
  194.         if (socket0 < 0) {
  195.             switch(errno) {
  196.             case EADDRNOTAVAIL:
  197.                 socket0 = NOHOST;
  198.                 break;
  199.             case ESRCH:
  200.                 socket0 = NOSERVICE;
  201.                 break;
  202.             }
  203.         }
  204.         break;
  205. #else
  206.         log(L_WARNING, "no DECNET support compiled in");
  207.         return(FAIL);
  208. #endif
  209.     case T_FD:
  210.         service = "with a smile";
  211.         socket0 = atoi(host);
  212.         break;
  213.     }
  214.  
  215.     if (socket0 < 0) {
  216.         switch(socket0) {
  217.         case NOHOST:
  218.             sprintf(buf, "%s host unknown", host);
  219.             log(L_WARNING, buf);
  220.             return(FAIL);
  221.         case NOSERVICE:
  222.             sprintf(buf, "%s service unknown: %s", host, service);
  223.             log(L_WARNING, buf);
  224.             return(FAIL);
  225.         case FAIL:
  226.             sprintf(buf, "%s hello: %s", host, errmsg(errno));
  227.             log(L_NOTICE, buf);
  228.             return(FAIL);
  229.         }
  230.     }
  231.  
  232.     if ((socket1 = dup(socket0)) < 0) {
  233.         sprintf(buf, "dup(%d): %s", socket0, errmsg(errno));
  234.         log(L_WARNING, buf);
  235.         (void) close(socket0);
  236.         return(FAIL);
  237.     }
  238.  
  239.     if ((rmt_rd = fdopen(socket0, rmode)) == (FILE *)NULL) {
  240.         sprintf(buf, e_fdopen, socket0, rmode);
  241.         log(L_WARNING, buf);
  242.         (void) close(socket0);
  243.         (void) close(socket1);
  244.         return(FAIL);
  245.     }
  246.  
  247.     if ((rmt_wr = fdopen(socket1, wmode)) == (FILE *)NULL) {
  248.         sprintf(buf, e_fdopen, socket1, wmode);
  249.         log(L_WARNING, buf);
  250.         (void) fclose(rmt_rd);
  251.         rmt_rd = (FILE *)NULL;
  252.         (void) close(socket1);
  253.         return(FAIL);
  254.     }
  255.  
  256.     switch(readreply(buf, sizeof(buf))) {
  257.     case OK_CANPOST:
  258.     case OK_NOPOST:
  259.         if (ferror(rmt_rd)) {
  260.             goodbye(DONT_WAIT);
  261.             return(FAIL);
  262.         }
  263.         break;
  264.     default:
  265.         if (buf[0] != '\0') {
  266.             char    err[BUFSIZ];
  267.  
  268.             sprintf(err, "%s greeted us with %s", host, buf);
  269.             log(L_NOTICE, err);
  270.         }
  271.         goodbye(DONT_WAIT);
  272.         return(FAIL);
  273.     }
  274.     return(NULL);
  275. }
  276.  
  277. /*
  278. ** Say goodbye to the nice remote server.
  279. **
  280. ** We trap SIGPIPE because the socket might already be gone.
  281. */
  282. goodbye(wait_for_reply)
  283. int    wait_for_reply;
  284. {
  285.     register ifunp    pstate = signal(SIGPIPE, SIG_IGN);
  286.  
  287.     if (sendcmd("QUIT"))
  288.         wait_for_reply = FALSE;    /* override, something's wrong. */
  289.     /*
  290.     ** I don't care what they say to me; this is just being polite.
  291.     */
  292.     if (wait_for_reply) {
  293.         char    buf[BUFSIZ];
  294.  
  295.         (void) readreply(buf, sizeof(buf));
  296.     }
  297.     (void) fclose(rmt_rd);
  298.     rmt_rd = (FILE *)NULL;
  299.     (void) fclose(rmt_wr);
  300.     rmt_wr = (FILE *)NULL;
  301.     if (pstate != (ifunp)(-1))
  302.         (void) signal(SIGPIPE, pstate);
  303. }
  304.  
  305. static SIGRET
  306. to_sfgets()
  307. {
  308.     longjmp(SFGstack, 1);
  309. }
  310.  
  311. /*
  312. ** `Safe' fgets, ala sendmail. This fgets will timeout after some
  313. ** period of time, on the assumption that if the remote did not
  314. ** return, they're gone.
  315. ** WARNING: contains a possibly unportable reference to stdio
  316. ** error macros.
  317. */
  318. char *
  319. sfgets(buf, size, fp)
  320. char    *buf;
  321. int    size;
  322. FILE    *fp;
  323. {
  324.     register char    *ret;
  325.     int    esave;
  326.  
  327.     if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
  328.         return((char *)NULL);
  329.     if (setjmp(SFGstack)) {
  330.         (void) alarm(0);        /* reset alarm clock */
  331.         (void) signal(SIGALRM, SIG_DFL);
  332. #ifdef apollo
  333.         fp->_flag |= _SIERR;
  334. #else
  335. /*        fp->_flag |= _IOERR;        /* set stdio error */
  336. #endif
  337. #ifndef ETIMEDOUT
  338.         errno = EPIPE;            /* USG doesn't have ETIMEDOUT*/
  339. #else
  340.         errno = ETIMEDOUT;        /* connection timed out */
  341. #endif
  342.         return((char *)NULL);        /* bad read, remote time out */
  343.     }
  344.     (void) signal(SIGALRM, to_sfgets);
  345.     (void) alarm(TIMEOUT);
  346.     ret = fgets(buf, size, fp);
  347.     esave = errno;
  348.     (void) alarm(0);            /* reset alarm clock */
  349.     (void) signal(SIGALRM, SIG_DFL);    /* reset SIGALRM */
  350.     errno = esave;
  351.     return(ret);
  352. }
  353.  
  354. /*
  355. ** Remote fgets - converts CRLF to \n, and returns NULL on `.' EOF from
  356. ** the remote. Otherwise it returns its first argument, like fgets(3).
  357. */
  358. char *
  359. rfgets(buf, size, fp)
  360. char    *buf;
  361. int    size;
  362. FILE    *fp;
  363. {
  364.     register char    *cp = buf;
  365.     register int    len;
  366.  
  367.     if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
  368.         return((char *)NULL);
  369.     *cp = '\0';
  370.     if (sfgets(buf, size, fp) == (char *)NULL)
  371.         return((char *)NULL);
  372.  
  373.     /* <CRLF> => '\n' */
  374.     if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') {
  375.         *cp++ = '\n';
  376.         *cp = '\0';
  377.     }
  378.  
  379.     /* ".\n" => EOF */
  380.     cp = buf;
  381.     if (*cp++ == '.' && *cp == '\n') {
  382.         return((char *)NULL);    /* EOF */
  383.     }
  384.  
  385.     /* Dot escaping */
  386.     if (buf[0] == '.')
  387.         (void) strcpy(&buf[0], &buf[1]);
  388.     return(buf);
  389. }
  390.  
  391. /*
  392. ** send the contents of an open file descriptor to the remote,
  393. ** with appropriate RFC822 filtering (e.g. CRLF line termination,
  394. ** and dot escaping). Return FALSE if something went wrong.
  395. */
  396. sendfile(fp)
  397. FILE    *fp;
  398. {
  399.     register int    c;
  400.     register FILE    *remote = rmt_wr;
  401.     register int    nl = TRUE;    /* assume we start on a new line */
  402.  
  403. #ifdef MMAP
  404.     register char *mbufr,*mptr;
  405.         long msize;
  406.         long offset;
  407.         struct stat sbuf;
  408.     char buf[BUFSIZ];
  409. #endif MMAP
  410.  
  411. /*
  412. ** I'm using putc() instead of fputc();
  413. ** why do a subroutine call when you don't have to?
  414. ** Besides, this ought to give the C preprocessor a work-out.
  415. */
  416. #ifdef MMAP
  417. #define    PUTC(c)    if (putc(c, remote) == EOF) {\
  418.     (void) munmap (mbufr, sbuf.st_size);\
  419.     return(FALSE); }
  420. #else !MMAP
  421. #define    PUTC(c)    if (putc(c, remote) == EOF) return(FALSE)
  422. #endif !MMAP
  423.  
  424.     if (fp == (FILE *)NULL)
  425.         return(FALSE);
  426.  
  427. #ifdef MMAP
  428.     /* map the article into memory */
  429.     (void) fstat(fileno(fp), &sbuf);
  430.         mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
  431.         if(mbufr == (char *) -1){
  432.         sprintf(buf, "sendfile: mmap failed: %s", errmsg(errno));
  433.         log(L_NOTICE, buf);
  434.                 return(FALSE);
  435.         }
  436.  
  437.         mptr = mbufr;        /* start of article in memory */
  438.         msize = sbuf.st_size;    /* size of article (bytes) */
  439.     while(msize-- > 0) {
  440.         c = *mptr++;
  441. #else !MMAP
  442.     /*
  443.     ** the second test makes no sense to me,
  444.     ** but System V apparently needed it...
  445.     */
  446.     while((c = fgetc(fp)) != EOF && !feof(fp)) {
  447. #endif !MMAP
  448.         switch(c) {
  449.         case '\0177':            /* skip deletes... */
  450.             break;
  451.         case '\n':
  452.             PUTC('\r');        /* \n -> \r\n */
  453.             PUTC(c);
  454.             nl = TRUE;        /* for dot escaping */
  455.             break;
  456.         case '.':
  457.             if (nl) {
  458.                 PUTC(c);    /* add a dot */
  459.                 nl = FALSE;
  460.             }
  461.             PUTC(c);
  462.             break;
  463.         default:
  464.             PUTC(c);
  465.             nl = FALSE;
  466.             break;
  467.         }
  468.     }
  469.     if (!nl) {
  470.         PUTC('\r');
  471.         PUTC('\n');
  472.     }
  473. #ifdef MMAP
  474.     (void) munmap (mbufr, sbuf.st_size);
  475. #endif MMAP
  476.     return( !(sendcmd(".") || ferror(fp)) );
  477. }
  478.