home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char * rcsid = "@(#)$Header: remote.c,v 1.4 91/01/25 21:47:52 sob Exp $";
- #endif
- /*
- ** remote communication routines for NNTP/SMTP style communication.
- **
- ************
- ** This version has been modified to support mmap()'ing of article files
- ** on systems that support it.
- **
- ** David Robinson (david@elroy.jpl.nasa.gov) and
- ** Steve Groom (stevo@elroy.jpl.nasa.gov), June 30, 1989.
- **
- ************
- **
- ** sendcmd - return TRUE on error.
- **
- ** readreply - return reply code or FAIL for error;
- ** modifies buffer passed to it.
- **
- ** converse - sendcmd() & readreply();
- ** return reply code or FAIL for error;
- ** modifies buffer passed to it.
- **
- ** hello - establish connection with remote;
- ** check greeting code.
- **
- ** goodbye - give QUIT command, and shut down connection.
- **
- ** sfgets - safe fgets(); does fgets with TIMEOUT.
- ** (N.B.: possibly unportable stdio macro ref in here)
- **
- ** rfgets - remote fgets() (calls sfgets());
- ** does SMTP dot escaping and
- ** \r\n -> \n conversion.
- **
- ** sendfile - send a file with SMTP dot escaping and
- ** \n -> \r\n conversion.
- **
- ** Erik E. Fair <fair@ucbarpa.berkeley.edu>
- */
- #include "../common/conf.h"
- #include "nntpxmit.h"
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <errno.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <setjmp.h>
- #include <signal.h>
- #ifdef dgux
- #define _IOERR _IO_ERR
- #endif
- #ifdef SYSLOG
- #ifdef FAKESYSLOG
- #include "../server/fakesyslog.h"
- #else
- #include <syslog.h>
- #endif
- #endif
- #include "get_tcp_conn.h"
- #include "../common/nntp.h"
-
- static jmp_buf SFGstack;
- FILE *rmt_rd;
- FILE *rmt_wr;
- char *sfgets();
- char *rfgets();
-
- extern int errno;
- extern char Debug;
- extern char *errmsg();
- extern char *strcpy();
- extern void log();
-
- /*
- ** send cmd to remote, terminated with a CRLF.
- */
- sendcmd(cmd)
- char *cmd;
- {
- if (cmd == (char *)NULL)
- return(TRUE); /* error */
- dprintf(stderr, ">>> %s\n", cmd); /* DEBUG */
- (void) fprintf(rmt_wr, "%s\r\n", cmd);
- (void) fflush(rmt_wr);
- return(ferror(rmt_wr));
- }
-
- /*
- ** read a reply line from the remote server and return the code number
- ** as an integer, and the message in a buffer supplied by the caller.
- ** Returns FAIL if something went wrong.
- */
- readreply(buf, size)
- register char *buf;
- int size;
- {
- register char *cp;
- register int len;
-
- if (buf == (char *)NULL || size <= 0)
- return(FAIL);
-
- /*
- ** make sure it's invalid, unless we say otherwise
- */
- buf[0] = '\0';
-
- /*
- ** read one line from the remote
- */
- if (sfgets(buf, size, rmt_rd) == NULL)
- return(FAIL); /* error reading from remote */
-
- /*
- ** Make sure that what the remote sent us had a CRLF at the end
- ** of the line, and then null it out.
- */
- if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r' &&
- *(cp + 1) == '\n')
- {
- *cp = '\0';
- } else
- return(FAIL); /* error reading from remote */
-
- dprintf(stderr, "%s\n", buf); /* DEBUG */
- /*
- ** Skip any non-digits leading the response code
- ** and then convert the code from ascii to integer for
- ** return from this routine.
- */
- cp = buf;
- while(*cp != '\0' && isascii(*cp) && !isdigit(*cp))
- cp++; /* skip anything leading */
-
- if (*cp == '\0' || !isascii(*cp))
- return(FAIL); /* error reading from remote */
-
- return(atoi(cp));
- }
-
- /*
- ** send a command to the remote, and wait for a response
- ** returns the response code, and the message in the buffer
- */
- converse(buf, size)
- char *buf;
- int size;
- {
- register int resp;
-
- if (sendcmd(buf))
- return(FAIL); /* Ooops! Something went wrong in xmit */
- /*
- ** Skip the silly 100 series messages, since they're not the
- ** final response we can expect
- */
- while((resp = readreply(buf, size)) >= 100 && resp < 200)
- continue;
- return(resp);
- }
-
- /*
- ** Contact the remote server and set up the two global FILE pointers
- ** to that descriptor.
- **
- ** I can see the day when this routine will have 8 args: one for
- ** hostname, and one for each of the seven ISO Reference Model layers
- ** for networking. A curse upon those involved with the ISO protocol
- ** effort: may they be forced to use the network that they will create,
- ** as opposed to something that works (like the Internet).
- */
- hello(host, transport)
- char *host;
- int transport;
- { char *service;
- char *rmode = "r";
- char *wmode = "w";
- char *e_fdopen = "fdopen(%d, \"%s\"): %s";
- int socket0, socket1; /* to me (bad pun) */
- char buf[BUFSIZ];
-
- switch(transport) {
- case T_IP_TCP:
- service = "nntp";
- socket0 = get_tcp_conn(host, service);
- break;
- case T_DECNET:
- #ifdef DECNET
- (void) signal(SIGPIPE, SIG_IGN);
- service = "NNTP";
- socket0 = dnet_conn(host, service, 0, 0, 0, 0, 0);
- if (socket0 < 0) {
- switch(errno) {
- case EADDRNOTAVAIL:
- socket0 = NOHOST;
- break;
- case ESRCH:
- socket0 = NOSERVICE;
- break;
- }
- }
- break;
- #else
- log(L_WARNING, "no DECNET support compiled in");
- return(FAIL);
- #endif
- case T_FD:
- service = "with a smile";
- socket0 = atoi(host);
- break;
- }
-
- if (socket0 < 0) {
- switch(socket0) {
- case NOHOST:
- sprintf(buf, "%s host unknown", host);
- log(L_WARNING, buf);
- return(FAIL);
- case NOSERVICE:
- sprintf(buf, "%s service unknown: %s", host, service);
- log(L_WARNING, buf);
- return(FAIL);
- case FAIL:
- sprintf(buf, "%s hello: %s", host, errmsg(errno));
- log(L_NOTICE, buf);
- return(FAIL);
- }
- }
-
- if ((socket1 = dup(socket0)) < 0) {
- sprintf(buf, "dup(%d): %s", socket0, errmsg(errno));
- log(L_WARNING, buf);
- (void) close(socket0);
- return(FAIL);
- }
-
- if ((rmt_rd = fdopen(socket0, rmode)) == (FILE *)NULL) {
- sprintf(buf, e_fdopen, socket0, rmode);
- log(L_WARNING, buf);
- (void) close(socket0);
- (void) close(socket1);
- return(FAIL);
- }
-
- if ((rmt_wr = fdopen(socket1, wmode)) == (FILE *)NULL) {
- sprintf(buf, e_fdopen, socket1, wmode);
- log(L_WARNING, buf);
- (void) fclose(rmt_rd);
- rmt_rd = (FILE *)NULL;
- (void) close(socket1);
- return(FAIL);
- }
-
- switch(readreply(buf, sizeof(buf))) {
- case OK_CANPOST:
- case OK_NOPOST:
- if (ferror(rmt_rd)) {
- goodbye(DONT_WAIT);
- return(FAIL);
- }
- break;
- default:
- if (buf[0] != '\0') {
- char err[BUFSIZ];
-
- sprintf(err, "%s greeted us with %s", host, buf);
- log(L_NOTICE, err);
- }
- goodbye(DONT_WAIT);
- return(FAIL);
- }
- return(NULL);
- }
-
- /*
- ** Say goodbye to the nice remote server.
- **
- ** We trap SIGPIPE because the socket might already be gone.
- */
- goodbye(wait_for_reply)
- int wait_for_reply;
- {
- register ifunp pstate = signal(SIGPIPE, SIG_IGN);
-
- if (sendcmd("QUIT"))
- wait_for_reply = FALSE; /* override, something's wrong. */
- /*
- ** I don't care what they say to me; this is just being polite.
- */
- if (wait_for_reply) {
- char buf[BUFSIZ];
-
- (void) readreply(buf, sizeof(buf));
- }
- (void) fclose(rmt_rd);
- rmt_rd = (FILE *)NULL;
- (void) fclose(rmt_wr);
- rmt_wr = (FILE *)NULL;
- if (pstate != (ifunp)(-1))
- (void) signal(SIGPIPE, pstate);
- }
-
- static SIGRET
- to_sfgets()
- {
- longjmp(SFGstack, 1);
- }
-
- /*
- ** `Safe' fgets, ala sendmail. This fgets will timeout after some
- ** period of time, on the assumption that if the remote did not
- ** return, they're gone.
- ** WARNING: contains a possibly unportable reference to stdio
- ** error macros.
- */
- char *
- sfgets(buf, size, fp)
- char *buf;
- int size;
- FILE *fp;
- {
- register char *ret;
- int esave;
-
- if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
- return((char *)NULL);
- if (setjmp(SFGstack)) {
- (void) alarm(0); /* reset alarm clock */
- (void) signal(SIGALRM, SIG_DFL);
- #ifdef apollo
- fp->_flag |= _SIERR;
- #else
- /* fp->_flag |= _IOERR; /* set stdio error */
- #endif
- #ifndef ETIMEDOUT
- errno = EPIPE; /* USG doesn't have ETIMEDOUT*/
- #else
- errno = ETIMEDOUT; /* connection timed out */
- #endif
- return((char *)NULL); /* bad read, remote time out */
- }
- (void) signal(SIGALRM, to_sfgets);
- (void) alarm(TIMEOUT);
- ret = fgets(buf, size, fp);
- esave = errno;
- (void) alarm(0); /* reset alarm clock */
- (void) signal(SIGALRM, SIG_DFL); /* reset SIGALRM */
- errno = esave;
- return(ret);
- }
-
- /*
- ** Remote fgets - converts CRLF to \n, and returns NULL on `.' EOF from
- ** the remote. Otherwise it returns its first argument, like fgets(3).
- */
- char *
- rfgets(buf, size, fp)
- char *buf;
- int size;
- FILE *fp;
- {
- register char *cp = buf;
- register int len;
-
- if (buf == (char *)NULL || size <= 0 || fp == (FILE *)NULL)
- return((char *)NULL);
- *cp = '\0';
- if (sfgets(buf, size, fp) == (char *)NULL)
- return((char *)NULL);
-
- /* <CRLF> => '\n' */
- if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') {
- *cp++ = '\n';
- *cp = '\0';
- }
-
- /* ".\n" => EOF */
- cp = buf;
- if (*cp++ == '.' && *cp == '\n') {
- return((char *)NULL); /* EOF */
- }
-
- /* Dot escaping */
- if (buf[0] == '.')
- (void) strcpy(&buf[0], &buf[1]);
- return(buf);
- }
-
- /*
- ** send the contents of an open file descriptor to the remote,
- ** with appropriate RFC822 filtering (e.g. CRLF line termination,
- ** and dot escaping). Return FALSE if something went wrong.
- */
- sendfile(fp)
- FILE *fp;
- {
- register int c;
- register FILE *remote = rmt_wr;
- register int nl = TRUE; /* assume we start on a new line */
-
- #ifdef MMAP
- register char *mbufr,*mptr;
- long msize;
- long offset;
- struct stat sbuf;
- char buf[BUFSIZ];
- #endif MMAP
-
- /*
- ** I'm using putc() instead of fputc();
- ** why do a subroutine call when you don't have to?
- ** Besides, this ought to give the C preprocessor a work-out.
- */
- #ifdef MMAP
- #define PUTC(c) if (putc(c, remote) == EOF) {\
- (void) munmap (mbufr, sbuf.st_size);\
- return(FALSE); }
- #else !MMAP
- #define PUTC(c) if (putc(c, remote) == EOF) return(FALSE)
- #endif !MMAP
-
- if (fp == (FILE *)NULL)
- return(FALSE);
-
- #ifdef MMAP
- /* map the article into memory */
- (void) fstat(fileno(fp), &sbuf);
- mbufr = mmap (0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(fp), 0);
- if(mbufr == (char *) -1){
- sprintf(buf, "sendfile: mmap failed: %s", errmsg(errno));
- log(L_NOTICE, buf);
- return(FALSE);
- }
-
- mptr = mbufr; /* start of article in memory */
- msize = sbuf.st_size; /* size of article (bytes) */
- while(msize-- > 0) {
- c = *mptr++;
- #else !MMAP
- /*
- ** the second test makes no sense to me,
- ** but System V apparently needed it...
- */
- while((c = fgetc(fp)) != EOF && !feof(fp)) {
- #endif !MMAP
- switch(c) {
- case '\0177': /* skip deletes... */
- break;
- case '\n':
- PUTC('\r'); /* \n -> \r\n */
- PUTC(c);
- nl = TRUE; /* for dot escaping */
- break;
- case '.':
- if (nl) {
- PUTC(c); /* add a dot */
- nl = FALSE;
- }
- PUTC(c);
- break;
- default:
- PUTC(c);
- nl = FALSE;
- break;
- }
- }
- if (!nl) {
- PUTC('\r');
- PUTC('\n');
- }
- #ifdef MMAP
- (void) munmap (mbufr, sbuf.st_size);
- #endif MMAP
- return( !(sendcmd(".") || ferror(fp)) );
- }
-