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