|
|
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.