|
|
1.1 ! root 1: /* ! 2: ** nntpxmit - transmit netnews articles across the internet with nntp ! 3: ** ! 4: ** This program is for transmitting netnews between sites that offer the ! 5: ** NNTP service, internet style. Ideally, there are two forms of ! 6: ** transmission that can be used in this environment, since the ! 7: ** communication is interactive (and relatively immediate, when compared ! 8: ** with UUCP). They are: passive poll (what have you gotten lately?) and ! 9: ** active send (I have `x', do you want it?). The USENET as a whole ! 10: ** uniformly uses active send, and where the communication is batched ! 11: ** (e.g. UUCP, or electronic mail) the software sends without even asking, ! 12: ** unless it can determine that the article has already been to some site. ! 13: ** ! 14: ** It turns out that when you implement passive poll, you have to be ! 15: ** *very* careful about what you (the server) tell the client, because ! 16: ** something that you might wish to restrict distribution of (either ! 17: ** internal newsgroups, or material posted in the international groups in ! 18: ** local distributions) will otherwise leak out. It is the case that if ! 19: ** the server doesn't tell the client that article `x' is there, the ! 20: ** client won't ask for it. If the server tells about an article which ! 21: ** would otherwise stay within some restricted distribution, the onus is ! 22: ** then on the client to figure out that the article is not appropriate to ! 23: ** post on its local system. Of course, at that point, we have already ! 24: ** wasted the network bandwidth in transferring the article... ! 25: ** ! 26: ** This is a roundabout way of saying that this program only implements ! 27: ** active send. There will have to be once-over done on the NNTP spec ! 28: ** before passive poll goes in, because of the problems that I have cited. ! 29: ** ! 30: ** Erik E. Fair <ucbvax!fair>, Oct 14, 1985 ! 31: ** ! 32: ** Changed to exit on unusual errors in opening articles, and now produces ! 33: ** CPU usage statistics to syslog. ! 34: ** ! 35: ** Erik E. Fair <ucbvax!fair>, April 26, 1986 ! 36: */ ! 37: ! 38: #include <stdio.h> ! 39: #include <errno.h> ! 40: #include <sys/types.h> ! 41: #include <sys/times.h> ! 42: #include <sys/file.h> ! 43: #include <syslog.h> ! 44: #include <sysexits.h> ! 45: #include "defs.h" ! 46: #include "header.h" ! 47: #include "response_codes.h" ! 48: #include "nntpxmit.h" ! 49: ! 50: char *Pname; ! 51: char Debug = FALSE; ! 52: char Do_Stats = TRUE; ! 53: char *USAGE = "USAGE: nntpxmit [-s] hostname:file [hostname:file ...]"; ! 54: FILE *getfp(); ! 55: ! 56: struct Stats { ! 57: u_long offered; ! 58: u_long accepted; ! 59: u_long rejected; ! 60: u_long failed; ! 61: } Stats = {0L, 0L, 0L, 0L}; ! 62: ! 63: struct tms Prev_times, Cur_times; ! 64: time_t Tbegin, Tend; ! 65: ! 66: extern int errno; ! 67: extern char *rindex(); ! 68: extern char *index(); ! 69: extern char *errmsg(); ! 70: extern char *sp_strip(); ! 71: extern int msgid_ok(); ! 72: ! 73: main(ac,av) ! 74: int ac; ! 75: char *av[]; ! 76: { ! 77: register int i; ! 78: char *host, *file; ! 79: ! 80: (void) time(&Tbegin); ! 81: ! 82: Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]); ! 83: ! 84: if (ac < 2) { ! 85: fprintf(stderr,"%s: %s\n", Pname, USAGE); ! 86: exit(EX_USAGE); ! 87: } ! 88: ! 89: /* note that 4.2 BSD openlog has only two args */ ! 90: #ifdef LOG_LOCAL7 ! 91: (void) openlog(Pname, LOG_PID, LOG_LOCAL7); ! 92: #else ! 93: (void) openlog(Pname, LOG_PID); ! 94: #endif ! 95: ! 96: for(i = 1; i < ac; i++) { ! 97: if (av[i][0] == '-') { ! 98: switch(av[i][1]) { ! 99: case 's': ! 100: Do_Stats = FALSE; ! 101: break; ! 102: case 'd': ! 103: Debug++; ! 104: break; ! 105: default: ! 106: fprintf(stderr,"%s: no such option: -%c\n", ! 107: Pname, av[i][1]); ! 108: fprintf(stderr,"%s: %s\n", Pname, USAGE); ! 109: exit(EX_USAGE); ! 110: } ! 111: continue; ! 112: } ! 113: ! 114: /* ! 115: ** OK, it wasn't an option, therefore it must be a ! 116: ** hostname, filename pair. ! 117: */ ! 118: host = av[i]; ! 119: if ((file = index(host, ':')) != (char *)NULL) { ! 120: *file++ = '\0'; ! 121: } else { ! 122: fprintf(stderr,"%s: illegal hostname:file pair: <%s>\n", ! 123: Pname, host); ! 124: continue; ! 125: } ! 126: ! 127: bzero(&Stats, sizeof(Stats)); ! 128: if (sendnews(host, file) && Do_Stats) { ! 129: struct tms delta; ! 130: time_t elapsed; ! 131: syslog(LOG_INFO, ! 132: "%s stats %lu offered %lu accepted %lu rejected %lu failed\n", ! 133: host, Stats.offered, Stats.accepted, Stats.rejected, Stats.failed); ! 134: ! 135: click(&delta, &elapsed); ! 136: ! 137: syslog(LOG_INFO, "%s xmit user %lu system %lu elapsed %lu\n", ! 138: host, delta.tms_utime, delta.tms_stime, elapsed); ! 139: } ! 140: } ! 141: exit(EX_OK); ! 142: } ! 143: ! 144: /* ! 145: ** Calculate how much time we've used. ! 146: ** ! 147: ** The HZ constant is from times(3C) man page, and is probably wrong ! 148: ** for anything other than a VAX. ! 149: ** ! 150: ** Why `click'? Well, imagine that I've got a stopwatch in my hand... ! 151: */ ! 152: #define HZ 60L ! 153: ! 154: click(cpu, elapsed) ! 155: register struct tms *cpu; ! 156: time_t *elapsed; ! 157: { ! 158: (void) times(&Cur_times); ! 159: (void) time(&Tend); ! 160: ! 161: /* delta T */ ! 162: *elapsed = Tend - Tbegin; ! 163: cpu->tms_utime = Cur_times.tms_utime - Prev_times.tms_utime; ! 164: cpu->tms_stime = Cur_times.tms_stime - Prev_times.tms_stime; ! 165: cpu->tms_cutime = Cur_times.tms_cutime - Prev_times.tms_cutime; ! 166: cpu->tms_cstime = Cur_times.tms_cstime - Prev_times.tms_cstime; ! 167: ! 168: /* reset reference point */ ! 169: Tbegin = Tend; ! 170: Prev_times = Cur_times; ! 171: ! 172: /* aggregate the children with the parent */ ! 173: cpu->tms_utime += cpu->tms_cutime; ! 174: cpu->tms_stime += cpu->tms_cstime; ! 175: ! 176: /* adjust these to seconds */ ! 177: cpu->tms_utime /= HZ; ! 178: cpu->tms_stime /= HZ; ! 179: } ! 180: ! 181: /* ! 182: ** Given a hostname to connect to, and a file of filenames (which contain ! 183: ** netnews articles), send those articles to the named host using NNTP. ! 184: */ ! 185: sendnews(host, file) ! 186: char *host, *file; ! 187: { ! 188: register int code; ! 189: register FILE *filefile = fopen(file, "r"); ! 190: register FILE *fp; ! 191: char buf[BUFSIZ]; ! 192: char article[BUFSIZ]; ! 193: ! 194: /* ! 195: ** if no news to send, return ! 196: */ ! 197: if (filefile == (FILE *)NULL) { ! 198: dprintf(stderr, "%s: %s: %s\n", Pname, file, errmsg(errno)); ! 199: return(FALSE); ! 200: } ! 201: ! 202: if (hello(host) == FAIL) { ! 203: fclose(filefile); ! 204: return(FALSE); ! 205: } ! 206: ! 207: while((fp = getfp(filefile, article, sizeof(article))) != (FILE *)NULL) { ! 208: switch(code = ihave(fp, article)) { ! 209: case CONT_XFER: ! 210: if (!sendfile(fp)) { ! 211: fprintf(stderr, "%s: %s: article transmission failed while sending %s\n", Pname, host, article); ! 212: Stats.failed++; ! 213: fclose(filefile); ! 214: fclose(fp); ! 215: goodbye(DONT_WAIT); ! 216: return(TRUE); ! 217: } ! 218: fclose(fp); ! 219: /* ! 220: ** Here I read the reply from the remote about the ! 221: ** transferred article, and I throw it away. I ! 222: ** should probably try and record the article ! 223: ** filename and append it back to the batchfile ! 224: ** again in the name of reliability, but that's ! 225: ** messy, and it's easier to assume that the guy ! 226: ** will have redundant feeds. ! 227: */ ! 228: code = readreply(buf, sizeof(buf)); ! 229: if (code != OK_XFERED) Stats.failed++; ! 230: break; ! 231: case ERR_GOTIT: ! 232: fclose(fp); ! 233: break; ! 234: default: ! 235: fprintf(stderr,"%s: %s gave an improper response to IHAVE: %d\n", Pname, host, code); ! 236: fprintf(stderr,"%s: while sending article %s\n", Pname, article); ! 237: fclose(filefile); ! 238: fclose(fp); ! 239: goodbye(DONT_WAIT); ! 240: return(TRUE); ! 241: } ! 242: } ! 243: fclose(filefile); ! 244: if (unlink(file) < 0) { ! 245: fprintf(stderr,"%s: unlink(%s): %s\n", Pname, file, errmsg(errno)); ! 246: } ! 247: goodbye(WAIT); ! 248: return(TRUE); ! 249: } ! 250: ! 251: /* ! 252: ** Read the header of a netnews article, snatch the message-id therefrom, ! 253: ** and ask the remote if they have that one already. ! 254: */ ! 255: ihave(fp, article) ! 256: FILE *fp; ! 257: char *article; ! 258: { ! 259: register int code; ! 260: struct hbuf header; ! 261: char scr[LBUFLEN]; ! 262: char buf[BUFSIZ]; ! 263: ! 264: bzero(&header, sizeof(header)); ! 265: if (!rfc822read(&header, fp, scr)) { ! 266: /* ! 267: ** something botched locally with the article ! 268: ** so we don't send it, but we don't break off ! 269: ** communications with the remote either. ! 270: */ ! 271: return(ERR_GOTIT); ! 272: } ! 273: ! 274: /* ! 275: ** If an article shows up without a message-id, ! 276: ** or with a bogus message-id, ! 277: ** we scream bloody murder. That's one in ! 278: ** the `can't ever happen' category. ! 279: */ ! 280: if (header.ident[0] == '\0') { ! 281: fprintf(stderr, "%s: %s missing message-id!\n", Pname, article); ! 282: return(ERR_GOTIT); ! 283: } else { ! 284: (void) strcpy(scr, sp_strip(header.ident)); ! 285: } ! 286: ! 287: if (!msgid_ok(scr)) { ! 288: fprintf(stderr, "%s: %s message-id syntax error!\n", Pname, article); ! 289: return(ERR_GOTIT); ! 290: } ! 291: ! 292: sprintf(buf, "IHAVE %s", scr); ! 293: Stats.offered++; ! 294: ! 295: switch(code = converse(buf, sizeof(buf))) { ! 296: case CONT_XFER: ! 297: Stats.accepted++; ! 298: rewind(fp); ! 299: return(code); ! 300: default: ! 301: Stats.rejected++; ! 302: return(code); ! 303: } ! 304: } ! 305: ! 306: /* ! 307: ** Given that fp points to an open file containing filenames, ! 308: ** open and return a file pointer to the next filename in the file. ! 309: ** Don't you love indirection? ! 310: ** ! 311: ** Returns a valid FILE pointer or NULL if end of file. ! 312: */ ! 313: ! 314: FILE * ! 315: getfp(fp, filename, fnlen) ! 316: register FILE *fp; ! 317: char *filename; ! 318: register unsigned fnlen; ! 319: { ! 320: register FILE *newfp = (FILE *)NULL; ! 321: register char *cp; ! 322: ! 323: while(newfp == (FILE *)NULL) { ! 324: if (fgets(filename, fnlen, fp) == (char *)NULL) ! 325: return((FILE *)NULL); /* EOF, tell caller */ ! 326: ! 327: filename[fnlen - 1] = '\0'; /* make sure */ ! 328: ! 329: if (*(cp = &filename[strlen(filename) - 1]) == '\n') ! 330: *cp = '\0'; ! 331: ! 332: if ((newfp = fopen(filename, "r")) == (FILE *)NULL) { ! 333: register int save = errno; ! 334: ! 335: fprintf(stderr, "%s: fopen(%s, \"r\"): %s\n", Pname, filename, errmsg(errno)); ! 336: /* ! 337: ** The only permissible error is `file non-existant' ! 338: ** anything else indicates something is seriously ! 339: ** wrong, and we should go away to let the shell ! 340: ** script clean up. ! 341: */ ! 342: if (save != ENOENT) ! 343: exit(EX_OSERR); ! 344: } ! 345: } ! 346: return(newfp); ! 347: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.