From 0722a577e8e109627fb8d2e50f8c33ebc45027e8 Mon Sep 17 00:00:00 2001 From: CSRG Date: Wed, 21 Oct 1987 23:17:05 -0800 Subject: [PATCH] BSD 4_3_Tahoe development Work on file usr/src/new/nntp/server/README Work on file usr/src/new/nntp/server/SYSLOG Work on file usr/src/new/nntp/server/profile.c Work on file usr/src/new/nntp/server/time.h Work on file usr/src/new/nntp/support/Makefile Work on file usr/src/new/nntp/support/access_file Work on file usr/src/new/nntp/support/README Work on file usr/src/new/nntp/rrn/README Work on file usr/src/new/nntp/support/nntp_awk Work on file usr/src/new/nntp/rrn/MAKE_RRN_CHANGES Work on file usr/src/new/nntp/xmit/get_tcp_conn.c Work on file usr/src/new/nntp/xmit/get_tcp_conn.h Work on file usr/src/new/nntp/rrn/README_RRN Work on file usr/src/new/nntp/xmit/nntpxmit.h Work on file usr/src/new/nntp/xmit/Makefile Work on file usr/src/new/nntp/xmit/README Work on file usr/src/new/nntp/xmit/nntpxmit.c Work on file usr/src/new/nntp/xmit/remote.c Work on file usr/src/new/nntp/xmit/llist.c Work on file usr/src/new/nntp/xmit/rfc822.c Work on file usr/src/new/nntp/xmit/shlock.c Work on file usr/src/new/nntp/xmit/nntpsend Work on file usr/src/new/nntp/xmit/llist.h Work on file usr/src/new/nntp/README Work on file usr/src/new/nntp/Makefile Work on file usr/src/new/nntp/CHANGES Work on file usr/src/new/nntp/xfer/nntpxfer.c Work on file usr/src/new/nntp/xfer/README Synthesized-from: CSRG/cd2/4.3tahoe --- usr/src/new/nntp/CHANGES | 237 +++++++++ usr/src/new/nntp/Makefile | 28 ++ usr/src/new/nntp/README | 161 +++++++ usr/src/new/nntp/rrn/MAKE_RRN_CHANGES | 25 + usr/src/new/nntp/rrn/README | 9 + usr/src/new/nntp/rrn/README_RRN | 52 ++ usr/src/new/nntp/server/README | 86 ++++ usr/src/new/nntp/server/SYSLOG | 50 ++ usr/src/new/nntp/server/profile.c | 21 + usr/src/new/nntp/server/time.h | 10 + usr/src/new/nntp/support/Makefile | 36 ++ usr/src/new/nntp/support/README | 42 ++ usr/src/new/nntp/support/access_file | 22 + usr/src/new/nntp/support/nntp_awk | 403 ++++++++++++++++ usr/src/new/nntp/xfer/README | 9 + usr/src/new/nntp/xfer/nntpxfer.c | 507 +++++++++++++++++++ usr/src/new/nntp/xmit/Makefile | 12 + usr/src/new/nntp/xmit/README | 59 +++ usr/src/new/nntp/xmit/get_tcp_conn.c | 115 +++++ usr/src/new/nntp/xmit/get_tcp_conn.h | 10 + usr/src/new/nntp/xmit/llist.c | 51 ++ usr/src/new/nntp/xmit/llist.h | 14 + usr/src/new/nntp/xmit/nntpsend | 49 ++ usr/src/new/nntp/xmit/nntpxmit.c | 347 +++++++++++++ usr/src/new/nntp/xmit/nntpxmit.h | 10 + usr/src/new/nntp/xmit/remote.c | 300 ++++++++++++ usr/src/new/nntp/xmit/rfc822.c | 668 ++++++++++++++++++++++++++ usr/src/new/nntp/xmit/shlock.c | 244 ++++++++++ 28 files changed, 3577 insertions(+) create mode 100644 usr/src/new/nntp/CHANGES create mode 100644 usr/src/new/nntp/Makefile create mode 100644 usr/src/new/nntp/README create mode 100755 usr/src/new/nntp/rrn/MAKE_RRN_CHANGES create mode 100644 usr/src/new/nntp/rrn/README create mode 100644 usr/src/new/nntp/rrn/README_RRN create mode 100644 usr/src/new/nntp/server/README create mode 100644 usr/src/new/nntp/server/SYSLOG create mode 100644 usr/src/new/nntp/server/profile.c create mode 100644 usr/src/new/nntp/server/time.h create mode 100644 usr/src/new/nntp/support/Makefile create mode 100644 usr/src/new/nntp/support/README create mode 100644 usr/src/new/nntp/support/access_file create mode 100644 usr/src/new/nntp/support/nntp_awk create mode 100644 usr/src/new/nntp/xfer/README create mode 100644 usr/src/new/nntp/xfer/nntpxfer.c create mode 100644 usr/src/new/nntp/xmit/Makefile create mode 100644 usr/src/new/nntp/xmit/README create mode 100644 usr/src/new/nntp/xmit/get_tcp_conn.c create mode 100644 usr/src/new/nntp/xmit/get_tcp_conn.h create mode 100644 usr/src/new/nntp/xmit/llist.c create mode 100644 usr/src/new/nntp/xmit/llist.h create mode 100755 usr/src/new/nntp/xmit/nntpsend create mode 100644 usr/src/new/nntp/xmit/nntpxmit.c create mode 100644 usr/src/new/nntp/xmit/nntpxmit.h create mode 100644 usr/src/new/nntp/xmit/remote.c create mode 100644 usr/src/new/nntp/xmit/rfc822.c create mode 100644 usr/src/new/nntp/xmit/shlock.c diff --git a/usr/src/new/nntp/CHANGES b/usr/src/new/nntp/CHANGES new file mode 100644 index 0000000000..8bbc809b60 --- /dev/null +++ b/usr/src/new/nntp/CHANGES @@ -0,0 +1,237 @@ + + This file describes the changes which have been made in NNTP +since the initial release. Individuals who either reported or +inspired the bug/bug fix are in square brackets. + +1.4 October 15, 1987 + + Reorganized documentation directory. Thanks for the + extraction stuff, Stan. [Stan Barber, sob%%soma.uucp@rice.edu] + + Added transfer timeouts. [Steve Schoch, schoch@ames.arpa] + + Fixed a problem with IHAVE which allowed a remote machine to + repeatedly feed you articles that you expired (although all + you'd do with them is throw them away). + [Fred Avolio, avolio@decuac.dec.com] + + DECNet support (see server/dnet_access.c and common/clientlib.c). + [Matt Thomas, thomas%syrah.dec@decwrl.dec.com] + + Fixed serious joe code in distribution checks in NEWNEWS. + + NEWNEWS statistics. + + Newsgroup security. + + Performance enhancements (about 2x better for article xfers). + + xhdr command added to improve performance on subject searches. + + Compiled-in server name no longer supported. + + common/clientlib.c/getserverbyfile() now looks at the + environment variable NNTPSERVER before checking the file. + + inews/inews.c now limits .signature files to MAX_SIGNATURE lines. + + server/misc.c/spawn() now returns the error output of rnews/inews + alone with the posting failed code. This is in turn printed by + inews/inews.c so the user has some idea of why his article wasn't + accepted. + + rn patches now for patchlevel #40 + Bug fix: rrn no longer leaves droppings in /tmp + "Skipping unavailable article" problems fixed + Support for 4.3 TIOCGWINSZ ioctl [sam@okeeffe.berkeley.edu] + Configure asks for domains + Pnews/Rnmail understand hostnames with .'s in them. + Makefile fixes [harvard!lownlab!kiely] + + PYRAMID #defines removed, as it is all being done by default now. + + inews/inews.c now exits 0, since before it had a random exit + status, causing pyramids to choke. [forys@boulder.colorado.edu] + + server/server.c now logs user/system/elapsed time as floats + instead of ints. [rick@seismo.css.gov] + + server/ihave.c no longer logs every message id transfered but + instead keeps statistics which are logged at the end. + [rick@seismo.css.gov] + + server/serve.c now times out after TIMEOUT seconds of idleness. + + server/access.c converts remote hostname to lower case + when logging, in case you have a nameserver which is helping you. + + server/misc.c/getartbyid now reports message-id's when + it encounters a malformed line in the history file. + [gds@spam.istc.sri.com] + + inews/inews.c had an uninitialized variable, which + could cause trouble. [jwp%chem@sdcsvax.ucsd.edu] + + common/clientlib.c now understands 4.3 nameserver + multiple addresses, and tries them all before + giving up. + + common/clientlib.c has has 2 new functions: + "getserverbyfile" opens a given file and returns + the name of the server given in the file to use + for news. "handle_server_response" prints informative + messages based on the initial connection response code. + + server/access.c now is case insensitive when checking + for host read/xfer permissions. + + server/misc.c/spawn didn't check for a closed connection + while receiving input from client. As a result, truncated + news articles could be received. + + server/newnews.c had a printf which was missing an + argument. [louie@trantor.umd.edu] + + Added fake syslog facility to server. Code is in + server/fakesyslog.c. [mckenny@sri-unix.arpa] + + Fixed length argument to accept() in server/main.c + [mckenny@sri-unix.arpa] + + Now uses pipe to rnews so as to get rnews output for debugging. + Also chowns temporary file to POSTER's uid and gid. + [mckenny@sri-unix.arpa] + + Fixed bugs in server/netaux.c to close syslog fd. + [mckenny@sri-unix.arpa] + + Made bcopy() standard in server/misc.c [kre@munnari.OZ] + + Documentation changes to make certain things about client + installation clearer. [munnari!charlie.oz!craig] + +1.3 30 June 1986 + + rrn is no longer included as complete source, but + rather as a set of context diffs and a program to + apply them to your rn source. Many thanks go to + Gene Spafford for an outstanding job doing this. + [spaf@gatech.csnet] + + The dreaded kill/save bug is fixed; rn was passing + /bin/sh too many open file descriptors. Thanks and a tip of the + proverbial hat to Chris Maio! Change to rrn/util.c. + [chris@columbia.edu] + + Fixed a bug in rrn/artio.c which caused an assertion + failure on line 114 of artio.c; artopen was returning + Nullfp without closing the fp associated with the + bogus article. [genrad!ens@eddie.mit.edu, beorn@ic.berkeley.edu] + + Added #define PYRAMID in common/conf.h, added some + #ifdef PYRAMID code in server/misc.c to deal with + Pyramids not initializing static data to 0, as well + as an fseek problem. [spaf@gatech.CSNET] + + Another wait bug fixed in spawn() in server/misc.c. + + Added a required \r in post.c. + + Added signal(SIGCHLD, SIG_IGN) to server/serve.c, + to fix exit status problem with ALONE defined. + + Statistics logging now returns sum of the nntpd and + its children for process time. server/main.c + [fair@ucbarpa.berkeley.edu] + + Subnet support for access file finally added. + server/subnet.c added, common/conf.h now has + #defines for SUBNET, DAMAGED_NETMASK. + + inews/inews.c now generates a from line with the UUCP + name instead of always using gethostname(). common/conf.h + changed to add #defines for UUNAME, GHNAME. + [jwang@atrp.mit.edu] + + Added LIBS to Makefile. [soma!sob@rice.edu] + +1.2c 17 May 1986 + + Support for Masscomp added (#define MASSCOMP in ../common/conf.h). + [soma!sob@rice.edu] + + Syslog output now requires SYSLOG to be defined in ../common/conf.h. + This is handy on systems which, for some reason or another, + don't have syslog. [soma!sob@rice.edu] + + server/post.c had arguments reversed in a printf. [salex@rice.edu] + + rrn/common.h had PIPESAVER misdefined. [cspencer@bbncc5.arpa] + + server/group.c was missing a \r in a printf. [lear@rutgers.edu] + + xmit/nntpxmit.c is a new version. Highlights include + improved error reactions and logging info. [fair@ucbarpa.berkeley.edu] + + xmit/nntpsend is a shell script for sending news via nntp + in a sane manner, with locking. [pleasant@topaz.rutgers.edu, + fair@ucbarpa.berkeley.edu] The locking mechanism is provided + courtesy of Mr. Fair's "shlock.c", in xmit/shlock.c. + + support/nntp_awk produces weekly reports from the nntp server + logging output. [fair@ucbarpa.berkeley.edu] + + Makefile (in this directory) would do a "make install" as + the default action; it now prints a helpful message. + [arnold@cgl.ucsf.edu] + + server/Makefile and support/Makefile had needless dependencies + in them; if you didn't do a make depend, you'd have problems + on a 4.2 system. The server and support stuff now depend only + on their own .h files. [arnold@cgl.ucsf.edu] + +1.2b 13 April 1986 + + common/clientlib.c stupidly had some debugging printfs + enabled. + + rrn/{artio.c,head.c} had sprintf("... %d", foo) where "foo" + was a long. %d -> %ld. [cspencer@bbncc5.arpa] + + server/time.c had an order of evaluation problem in the + macro "twodigtoi". [fletcher@tzec.cs.utexas.edu, among others.] + + server/common.h included if DBM was defined, + caused multiply-defined NULL's. [cda@opal.berkeley.edu, + pleasant@topaz.rutgers.edu, among others.] + + server/active.c would lose because variable "i" would be + at the end of the group array if it was called on a timer + interrupt. "i" now set to zero properly. This only occurs + if FASTFORK is defined. [cda@opal.berkeley.edu] + +1.2a 20 March 1986 + + common/conf.h defined MAX_GROUPS as 300; this was too low on + some machines. Upped to 450. [solex@rice.edu, beorn@ic.berkeley.edu] + + rrn/Makefile.sh had .c instead of .o for OBJS and SRCS + respectively. Also had cc -o ../common/clientlib.o (see below). + + inews/inews.c had (char *) 0 for gets(), which made SUN's upset. + Changed to simply NULL. [brian@sdcsvax.ucsd.edu] + + inews/Makefile had cc -o ../common/clientlib.o which some + machines don't do. [brian@sdcsvax.ucsd.edu] + + common/clientlib.c has "untp" instead of "nntp". + + server/active.c made more robust about reading active file + if active file is longer than MAX_GROUPS. + + server/common.h included common/conf.h after checking for + DBM, which caused some problems. [soma!sob@rice.edu] + +1.2 15 March 1986 + + Released. diff --git a/usr/src/new/nntp/Makefile b/usr/src/new/nntp/Makefile new file mode 100644 index 0000000000..83e676c8be --- /dev/null +++ b/usr/src/new/nntp/Makefile @@ -0,0 +1,28 @@ + +# +# Makefile for NNTP intstallation +# + +install: + @ echo "If you want to install a server version or" + @ echo "a client version, type \"make install_server\"" + @ echo "or \"make install_client\" respectively." + @ echo "See README for more details." + +install_server: + cd server; make install + cd support; make install + cd doc; make install + +install_client: + cd rrn; make install + cd inews; make install + cd doc; make install + +distrib: + rm -rf mods + cd server; make distrib + cd support; make distrib + cd inews; make distrib + cd common; make distrib + cd doc; make distrib diff --git a/usr/src/new/nntp/README b/usr/src/new/nntp/README new file mode 100644 index 0000000000..1f1d1f8cb9 --- /dev/null +++ b/usr/src/new/nntp/README @@ -0,0 +1,161 @@ +NNTP README October 15, 1987 For version 1.4 NNTP package + +[See the file CHANGES to see differences between this version and +older versions.] + + This package contains everything (well, most of it, I hope) that +you'll need to implement a remote news server running the NNTP protocol. + + A brief tour of the directories and their programs: + + server Source for the NNTP news server daemon. + + rrn Patches to "rn" to allow remote news reading. + These patches courtesy of Gene Spafford. + + inews A "mini-inews" written by Steven Grady + which allows + remote posting without changing much else. + + xmit An active transmission client for transferring + news, written by Erik Fair; see note below. + + common Common stuff (response codes, configuration info, + and some client library routines) for the + the news server and the clients. The "conf.h" + file here needs to be edited to reflect + the peculiarities of your system. + + support Some support files that make the nntpd's + life considerably easier. + + doc Documentation on the server, including manual + pages. Manual pages for rrn are in rrn/. + + xfer A passive reception client which uses the + NEWNEWS command to retrieve news from a remote + server. Written by Brian Kantor, this software + is UNSUPPORTED. + + Each directory has associated with it a README file (except +for rrn -- rrn has a bunch of 'em. The one you want is README_RRN). +As you go through the system customizing things, you should read +the README for each directory to get an idea of what traps await +you in that area. You may also want to print a copy of doc/rfc977, +which describes the NNTP protocol. + + TWO IMPORTANT NOTES: + + 1. The NNTP server assumes that the history file format + is 2.11 or 2.10.3; therefore you need 2.11 news. + + 2. If you want to transfer news with NNTP, you'll be using + the "nntpxmit" program in the "xmit" directory. + This requires routines in 2.11 news source, and must + be compiled there. See xmit/README for more info. + + >>>>> Get 2.11 news if you don't have it. + + So, time for a general and cohesive Plan: + + 1. Look at common/README. This will explain the stuff which + needs to be tailored for your system in common/conf.h. + Make the necessary changes to reflect your system. + + 2. Look at server/README; there shouldn't be much to do here, + as the configuration stuff should have been taken care of + when you edited common/conf.h. + + Do a "make", and see if things work ok -- they should + (comforting, right?) + + 3. Check out support/README and learn about the support programs. + Again, there should be no configuration changes, as that's + what common/conf.h is for. You will need to edit the Makefile + here to reflect where you want your binaries to be installed, + however. + + Do a "make" here, too. + + 4. Look at inews/README. This is for the pseudo-inews which + gets installed on client news machines. If you don't want + your clients to be able to post, don't worry about this. + But I suggest you do. Again, there shouldn't be any + configuration futzing to be dealt with. + + Alas, do a make here, too. + + 5. The big one: rrn. Read rrn/README_RRN, which explains + Gene's patches and what to do, and do what it suggests. + + 6. It would be nice if you could do a "make install" at this point, + but you can't: if you compile this on the NNTP server machine, + you don't want rrn objects installed. On the other hand, + if you compile this on a client machine, you don't want + server objects installed. So, from this (nntp) directory: + + Server? Type "make install_server". This installs "server" + and "support". + + Client? Type "make install_client". This installs "rrn" + and "inews" + + >>> A full client installation of NNTP requires the following + files (suitable for rdist, assuming standard directories): + +NEWS = ( /usr/local/{Pnews,Rnmail,inews,rn,rrn,newsetup,newsgroups,lib/rn} + /usr/man/catl/{Pnews,Rnmail,rn,newsetup,newsgroups}.1 ) + + You DO NOT need any of the normal news junk (e.g., + /usr/lib/news, postnews, checknews, readnews) on CLIENT + systems. + + You DO need these on SERVER systems. + + Problems? You can get to me via electronic mail at the +following addresses: + + Internet: phil@ucbvax.berkeley.edu + UUCP: ...!ucbvax!phil + Telephone (home): (415) 848-8409 + Telephone (work): (415) 642-6792 or (415) 642-7447 + + I'm very interested in learning what hacks need to be made to +nntpd to get it to work on various systems, and certainly, if there +are outright bugs, please let me know. + + Also, please send me electronic mail if you decide to use this +package, as that way I can mail out bug reports and fixes. Be sure +to include a reply-able address if your mailer doesn't generate +one for you. + + One note, though -- as for "rrn", I'll support bugs caused +by my additions/mods to it (hopefully Larry Wall will be supporting +this soon...) but please don't send me reports about things which +were already in rn 4.3. Thanks. + + [Those who had the pleasure of dealing with the kill/save bug, +it's gone now, thanks to Chris Maio.] + + Finally, I'd like to thank the various people who both inspired +and helped to make this project a reality: Erik Fair, whose criticism +and suggestions helped mold NNTP (and who wrote the active transmission +client); Brian Kantor, who really got me motivated enough to go and +finish the thing, and whose work on the RFC was *tremendous*; Steven +Grady, who wrote the inews interface (and wasted countless hours only +to have his work dashed periodically...); Mike Meyer, who beta tested +the software and pointed out numerous problems; Bob Henry, who let me +have the resources so that it got done; Peter Yee, who repeated enough +good ideas to get me to include them; all the folks who had patience +with me and didn't go off and write this themselves (jsq, you +listening? My thanks.); Chuq von Rospach and the members of lan-news; +Gene Spafford for eliminating having to include 1 MB of source to rn +by a set of patches; Matt Thomas for adding support for DECNET; +the kind folks who beta tested version 1.4 and put up with stupid +bugs and provided helpful feedback, notably Craig Leres, Matt Thomas, +Wengyik Yeong, and Stan Barber; all the individuals who have reported +bugs or suggested improvements (see CHANGES for a list); and probably a +lot of other people I've neglected to mention. My thanks to all. + + Phil Lapsley + 15 October 1987 diff --git a/usr/src/new/nntp/rrn/MAKE_RRN_CHANGES b/usr/src/new/nntp/rrn/MAKE_RRN_CHANGES new file mode 100755 index 0000000000..73ee412636 --- /dev/null +++ b/usr/src/new/nntp/rrn/MAKE_RRN_CHANGES @@ -0,0 +1,25 @@ +#!/bin/sh + +PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin:/usr/local +export PATH + +for filename in Pnews.header addng.c artio.c common.h final.c head.c \ + init.c ng.c ngdata.c ngdata.h rcstuff.c respond.c util.c +do +patch $filename < ${filename}.rrnpatch +done + + +for filename in Makefile.SH newsetup.SH newsgroups.SH +do + patch -o ${filename}.rrn $filename < ${filename}.rrnpatch +done + +if test -r Configure.orig ; then +echo "Configure.orig exists. Save the real Configure script and then:" +echo '"mv Configure.rrn Configure"' +echo "Then run Configure and answer the questions." +else +mv Configure Configure.orig +mv Configure.rrn Configure +fi diff --git a/usr/src/new/nntp/rrn/README b/usr/src/new/nntp/rrn/README new file mode 100644 index 0000000000..5f975838d1 --- /dev/null +++ b/usr/src/new/nntp/rrn/README @@ -0,0 +1,9 @@ + +Well ... + +The patches for rrn aren't here yet. Go grab the rrn.39.tar.Z in +"pub" off of ucbvax via anonymous ftp. That's the complete deal. + +The patches should be here in a few days. + + Phil diff --git a/usr/src/new/nntp/rrn/README_RRN b/usr/src/new/nntp/rrn/README_RRN new file mode 100644 index 0000000000..1a0e23612a --- /dev/null +++ b/usr/src/new/nntp/rrn/README_RRN @@ -0,0 +1,52 @@ +Updated hacks to change rn into rrn 28 June 1986 + +Everything is now automated to install the changes and have +the source co-resident with the non-network version. These +changes also work with rn patches up to #27, and with the +Georgia Tech patch to make "rn" compatible with 2.10.3 news. + +To install, simply run the shell file MAKE_RRN_CHANGES in +the "rn" source directory (you may need to alter the file to +include the location of the "patch" command). After the file is +run, "ifdef" changes for the "rrn" version will have been included +in strategic locations, and a few extra shell files should be +in place. Running "Configure" and answering the questions should +provide you with either the local version (rn) or the network version +(rrn). + +Good luck. + +Gene Spafford, (gatech!spaf) +[Thanks, Gene! --Phil] + +---------- + +[You should ignore this, Gene's stuff is far better --Phil] + +Hacks to change rn into rrn 31 August 1985 + + The following steps should be followed to set up "rrn" on +your system. The files in this directory are somewhat modified +versions of those distributed with normal rn 4.3. However, some +things were overly-difficult to change, and you will have to +hack them in by hand later. Fortunately, that's mostly +trivial. + + 1. Run "Configure", as usual. However, make one change -- + make your news spool directory be "/tmp", instead of + whatever it would normally be. + + 2. Edit the file "config.h". Append two lines to the end of + the file: + + #define SERVER + #define SERVER_HOST "xxx" + + The first line tells the compiler that you want the + remote news reading hacks compiled in. The second + tells it that the host to query for news and to send + posted articles to is "xxx". Obviously, "xxx" should be + changed to whatever machine name is apropriate. + + 3. Type "make"... + diff --git a/usr/src/new/nntp/server/README b/usr/src/new/nntp/server/README new file mode 100644 index 0000000000..cfefd31eb7 --- /dev/null +++ b/usr/src/new/nntp/server/README @@ -0,0 +1,86 @@ + Caveat: Before compiling anything here, go look at README and conf.h +in the "common" directory. Fix conf.h up, and then come back here. + + Back already? Ok. Now following the bouncing numbers: + + 1. Create the access file with the proper entries. + This file goes wherever you said ACCESS_FILE + was supposed to be in common/conf.h. It's format is + explained in the manual entry for nntpd.8c. + A sample access file is in ../support/access_file. + If you don't care who (ab)uses your news server, + you can have the line "default read post" in your access + file, which will allow anyone on the network to + read and post news via your server. See the manual + page for a better explanation. + +Parts two and three are necessary if you're running with TCP: + + 2. Make an entry for "nntp" in /etc/services. Should + be port number 119, tcp. I.e., should look something like: + +nntp 119/tcp readnews # Network News Transfer Protocol + + Sun users running yp should yppush this file to make sure all + the clients get it. + + 3. Check ../common/conf.h to make sure you're set to do what + you want to do with inetd (i.e., #define ALONE or #undef ALONE). + If you are using inetd, + + a. Add a line to /etc/inetd.conf, or whatever your + configuration file is, to reflect the presence + of the news server. On 4.3 BSD machines this should + look like: + +nntp stream tcp nowait root /etc/nntpd nntpd + + while under Ultrix or 4.2 BSD machines: + +nntp stream tcp nowait /etc/nntpd nntpd + + On a Sun, the file is /etc/servers; the line looks like: + +nntp tcp /usr/etc/in.nntpd + + Be sure to yppush your /etc/servers file if you run + yellow pages. + + Don't forget to kill -HUP your inetd. + + If you're NOT using inetd, + + a. Edit ../common/conf.h to have the line + + #define ALONE + + to compile in code for the stand alone server. + + b. You may as well also define "FASTFORK" in + ../common/conf.h. This causes the server not to + read in the active file every time it forks, but + rather to stat it every READINTVL seconds, and if + the file has changed since the last read, to + read it in again. This makes the children run + faster, since they don't have to read the active + file every time the parent forks off a child, but + the parent server will eat more cpu, doing + stat()s every 10 minutes or so. If your server machine + is heavily loaded, you might leave this out. + + c. Change /etc/rc.local to start the server at + boot time. + +Else, if you're using decnet: + + 2. && 3. Define the NNTP object with ncp: + +ncp define object NNTP number 0 file /etc/nntpd +ncp define object NNTP default user guest type stream + +ncp set object NNTP all # just once for the running system + + 4. Compile the server by doing "make". + + 5. Cd .. and continue with the rest of the stuff; you'll + wind up doing a make install later. diff --git a/usr/src/new/nntp/server/SYSLOG b/usr/src/new/nntp/server/SYSLOG new file mode 100644 index 0000000000..81758fb001 --- /dev/null +++ b/usr/src/new/nntp/server/SYSLOG @@ -0,0 +1,50 @@ +SYSLOG INFO + +If LOG is defined, the following informational messages are +logged at LOG_INFO. All messages are preceded by +the host name executing the command. + +host connect "host" connected to the server. +host refused connection "host" tried to connect, but was denied. +host unrecognized %s "host" gave an unknown command, %s. +host group newsgroup "host" isssued GROUP to "newsgroup". +host post rejected "host" tried to POST, but was denied. +host post succeeded "host" tried to POST, inews worked. +host post failed "host" tried to POST, inews failed. +host timeout "host" didn't issue a command for TIMEOUT + seconds (#defined in common/conf.h), so + nntpd closed the connection. +host transfer_timeout "host" hasn't sent any lines of data + during article transmission for XFER_TIMEOUT + seconds, so nntpd closed the connection. +host ihave_stats accepted %d rejected %d failed %d + "host" quit, having offered news articles + to us for transfer. We accepted %d, + rejected %d (having already seem them), + and %d failed (inews exited non-zero). +host newnews %s %s %s GMT|local %s + "host" issued NEWNEWS in group %s, + from date %s time %s. Timezone was + either GMT or local. <%s> distributions. +host newnews_stats told %d took %d + "host" asked for new news (see above). + We told it about %d articles, and it + subsequently issued %d ARTICLE commands, + presumably to retrieve the asked about msgs. +host exit %d aritcles %d groups "host" quit, having read a total of %d + articles and %d groups. +host times user %f system %f elapsed %f + "host" quit, having used %f user seconds, + %f system seconds, and %f real-time elapsed + seconds. + +The following messages are logged at priority LOG_DEBUG +if IHAVE_DEBUG is #defined in common/conf.h: + +host ihave artid rejected "host" offered "artid", we already had it. +host ihave artid accepted failed "host" offered "artid", we didn't +host ihave artid accepted succeeded have it, and the rnews worked or not. + +The following error message is logged at LOG_ERR: + +host spawn: EOF before period on a line by itself diff --git a/usr/src/new/nntp/server/profile.c b/usr/src/new/nntp/server/profile.c new file mode 100644 index 0000000000..bbe7b4482a --- /dev/null +++ b/usr/src/new/nntp/server/profile.c @@ -0,0 +1,21 @@ +#include +#include + +#define MON "gmon.out" +#define DIR "/usr/tmp/nntpd.prof" + +profile() +{ + static char tmp[] = "gmon.XXXXXX"; + struct stat statbuf; + + if (chdir(DIR) < 0) + return; + + if (stat(MON, statbuf) < 0) + return; + + (void) mktemp(tmp); + + (void) rename(MON, tmp); +} diff --git a/usr/src/new/nntp/server/time.h b/usr/src/new/nntp/server/time.h new file mode 100644 index 0000000000..c3def0b2ab --- /dev/null +++ b/usr/src/new/nntp/server/time.h @@ -0,0 +1,10 @@ +/* + * Time manipulation routines. + * + * %W% (Berkeley) %G% + */ + +extern long dtol(); +extern char *ltod(); +extern long local_to_gmt(); +extern long gmt_to_local(); diff --git a/usr/src/new/nntp/support/Makefile b/usr/src/new/nntp/support/Makefile new file mode 100644 index 0000000000..80358689f7 --- /dev/null +++ b/usr/src/new/nntp/support/Makefile @@ -0,0 +1,36 @@ +# +# Makefile for NNTP server support programs +# + +OBJS = mkgrdates.o + +SRCS = mkgrdates.c + +HFILES = ../common/conf.h + +CFLAGS= -O + +# Where these support programs will live + +DESTDIR = /usr/spool/news/lib + +mkgrdates: mkgrdates.o + cc ${CFLAGS} -o mkgrdates mkgrdates.o + +install: mkgrdates + cp mkgrdates ${DESTDIR}/mkgrdates + chmod 755 ${DESTDIR}/mkgrdates + +lint: + lint mkgrdates.c + +clean: + rm -f *.o mkgrdates + +distrib: clean + rm -rf SCCS old + +tags: ${SRCS} ${HFILES} + ctags ${SRCS} ${HFILES} + +${OBJS}: ${HFILES} diff --git a/usr/src/new/nntp/support/README b/usr/src/new/nntp/support/README new file mode 100644 index 0000000000..ff37ea1d30 --- /dev/null +++ b/usr/src/new/nntp/support/README @@ -0,0 +1,42 @@ +>>>>>>>>>>>>>>>>>>>>>>>>> No software in the NNTP 1.4 package +>> NOTE NOTE NOTE NOTE >> uses the NEWGROUPS command. As a result, +>> NOTE NOTE NOTE NOTE >> mkgrdates is no longer supported, but is +>>>>>>>>>>>>>>>>>>>>>>>>> included for completeness. + + Mkgrdates is run by cron periodically (say, every 6 or 12 hours -- +it's up to you). All it does is produce a cronologically ordered list +of newsgroups in the active file, along with their dates of creation. +It tries to be intelligent, and if the active file hasn't changed since +it was last run, it simply exits. + + So, you'll need to put "mkgrdates" in /usr/lib/crontab to +be run periodically. Every day is probably good enough. + + Some things you might want to be aware of are that it creates +and updates the files STAT_FILE and NGDATE_FILE, defined in +../common/conf.h. It's up to you to define these constants to suit +your system. + + The file "access_file" is the file which tells the news server +which hosts can read, which can post, and which can transfer. +This file wants to be installed wherever ACCESS_FILE in ../common/conf.h +says it should be (you can configure this to suit you). Remember +that this should be readable by whatever uid the news server runs +as. Further, remember that the entry "default" must be first in +the table. + + >>> The access file will support subnets iff you have <<< + >>> defined SUBNET when you made the server. <<< + + Finally, edit Makefile to reflect DESTDIR -- where you want +the binary to be installed for mkgrdates. + + If you're having the nntp server log copious info, you will +probably want to run the stat package developed by Erik Fair. +Once a week you should have crontab do + + awk -f nntp_awk nntplog.old >& nntp_report + +where "nntplog.old" is the last week's nntp log file produced +by syslog. Any errors which it cannot resolve are placed +in the front of the report. diff --git a/usr/src/new/nntp/support/access_file b/usr/src/new/nntp/support/access_file new file mode 100644 index 0000000000..99844d9d7a --- /dev/null +++ b/usr/src/new/nntp/support/access_file @@ -0,0 +1,22 @@ +# +# Sample NNTP access file. "read" implies "xfer". +# Note that "default" must be the first entry in the +# table. +# +# If you defined SUBNET when you compiled the server, +# this file can have subnets as well as class A, B, C +# networks and hosts. +# +# host/net read/xfer/no post/no +# +# by default, let anyone transfer news, but not read or post +default xfer no +# hosts on the Berkeley campus can read and post news +ucb-ether read post +# bugs, a notorious undergraduate machine, is not allowed +# to read or post news at all. +bugs read no +# ic can read and post news, but users on ic cannot read +# articles in the group ucb.postgres or any of its decendents +# (e.g., ucb.postgres.core) +ic read post !ucb.postgres diff --git a/usr/src/new/nntp/support/nntp_awk b/usr/src/new/nntp/support/nntp_awk new file mode 100644 index 0000000000..6790941037 --- /dev/null +++ b/usr/src/new/nntp/support/nntp_awk @@ -0,0 +1,403 @@ +# an awk script +# an NNTP log summary report generator +# +# NOTE: for systems that are not as yet using the new 4.3 BSD syslog +# (and therefore have nntp messages lumped with everything else), it +# would be best to invoke this script thusly: +# +# egrep nntp syslog.old | awk -f nntp_awk > report_of_the_week +# +# because this script will include in the report all messages in the log +# that it does not recognize (on the assumption that they are errors to +# be dealt with by a human). +# +# Erik E. Fair +# May 17, 1986 - Norwegian Independence Day +# +# Recognize some new things - February 22, 1987 +# Erik E. Fair +# +# fix "xmt is not an array" bug - March 11, 1987 +# Change Elapsed/CPU fields to break out time values, HH:MM:SS +# Erik E. Fair +# +# Add reporting for newnews commands - August 27, 1987 +# Erik E. Fair +# +BEGIN{ + readers = 0; + transmit = 0; + receive = 0; + polled = 0; +} +### Skip stderr reports from rnews +{ + n = split($6, path, "/"); + if (path[n] == "rnews:") next; + n = split($7, path, "/"); + if (path[n] == "rnews") next; + host = $6; +} +$7 == "group" { + readers = 1; + ng[$8]++; + next; +} +$7 == "ihave" { + receive = 1; + rec[host]++; + if ($9 == "accepted") { + rec_accept[host]++; + if ($10 == "failed") rec_failed[host]++; + } else if ($9 == "rejected") rec_refuse[host]++; + next; +} +# this is from version 1.4 of nntpd +$7 == "ihave_stats" { + receive = 1; + rec[host] += $9 + $11 + $13; + rec_accept[host] += $9; + rec_refuse[host] += $11; + rec_failed[host] += $13; + next; +} +$7 == "connect" { + systems[host]++; + next; +} +$7 == "exit" { + if ($8 > 0) readers = 1; + articles[host] += $8; + groups[host] += $10; + next; +} +$7 == "xmit" { + xmt_cpu[host] += $9 + $11; + xmt_ela[host] += $13; + next; +} +$7 == "times" { + cpu[host] += $9 + $11; + ela[host] += $13; + next; +} +$7 == "stats" { + transmit = 1; + xmt[host] += $8; + xmt_accept[host] += $10; + xmt_refuse[host] += $12; + xmt_failed[host] += $14; + next; +} +# +# For the Nth time, I wish awk had two dimensional associative +# arrays. I assume that the last request is the same as all the +# others in this section of logfile. +# +$7 == "newnews" { + polled = 1; + poll[host] ++; + poll_asked[host] = $8; + next; +} +$7 == "newnews_stats" { + poll_offered[host] += $9; + poll_took[host] += $11; + next; +} +$7 == "post" { + readers = 1; + post[host]++; + next; +} +$7 == "timeout" { + timeout[host]++; + timeouts = 1; + next; +} +$7 == "unrecognized" { + unknown[host] = 1; + curious = 1; +} +### Print anything that we don't recognize in the report +{ + print; +} +END{ + printf("\n"); +############################################################################### +### Article Exchange With Peers (other servers) Statistics ### +############################################################################### + if (transmit || receive || polled) + printf("NNTP peer article transfers\n\n"); + + if (polled) for(s in poll) servers[s]++; + if (receive) for(s in rec) servers[s]++; + if (transmit) for(s in xmt) servers[s]++; + + if (receive) { + printf("Article Reception (they contact us)\n"); + printf("System Offered Took Toss Fail Toss Elapsed CPU Pct\n"); + for(s in rec) { + + nrec += rec[s]; + nrec_accept += rec_accept[s]; + nrec_refuse += rec_refuse[s]; + nrec_failed += rec_failed[s]; + nrec_cpu += cpu[s]; + nrec_ela += ela[s]; + + they_offered = rec[s]; + if (they_offered == 0) they_offered = 1; + we_toss = (rec_refuse[s] / they_offered) * 100 + 0.5; + + e_hours = ela[s] / 3600; + e_sec = ela[s] % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = cpu[s] / 3600; + c_sec = cpu[s] % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + tmp = ela[s]; + if (tmp == 0) tmp = 1; + pct = ((cpu[s] / tmp) * 100.0 + 0.5); + + printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, rec[s], rec_accept[s], rec_refuse[s], rec_failed[s], we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + + e_hours = nrec_ela / 3600; + e_sec = nrec_ela % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = nrec_cpu / 3600; + c_sec = nrec_cpu % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + they_offered = nrec; + if (they_offered == 0) they_offered = 1; + we_toss = (nrec_refuse / they_offered) * 100 + 0.5; + + if (nrec_ela == 0) nrec_ela = 1; + pct = ((nrec_cpu / nrec_ela) * 100.0 + 0.5); + + printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nrec, nrec_accept, nrec_refuse, nrec_failed, we_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + +############################################################################### + if (polled) { + printf("Article Transmission (they poll us)\n"); + printf("System Conn Offrd Took Elapsed CPU Pct Groups\n"); + npoll = 0; + npoll_offered = 0; + npoll_took = 0; + npoll_cpu = 0; + npoll_ela = 0; + + for(s in poll) { + npoll += poll[s]; + npoll_offered += poll_offered[s]; + npoll_took += poll_took[s]; + + if (rec[s]) { + printf("%-25s %5d %5d %5d (see Article Reception) %s\n", s, poll[s], poll_offered[s], poll_took[s], poll_asked[s]); + } else { + npoll_ela += ela[s]; + npoll_cpu += cpu[s]; + + e_hours = ela[s] / 3600; + e_sec = ela[s] % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = cpu[s] / 3600; + c_sec = cpu[s] % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + tmp = ela[s]; + if (tmp == 0) tmp = 1; + pct = ((cpu[s] / tmp) * 100.0 + 0.5); + + printf("%-25s %5d %5d %5d %3d:%02d:%02d %3d:%02d:%02d %3d%% %s\n", s, poll[s], poll_offered[s], poll_took[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct, poll_asked[s]); + } + } + printf("\n%-25s %5d %5d %5d", "TOTALS", npoll, npoll_offered, npoll_took); + if (npoll_ela > 0 && npoll_cpu > 0) { + + e_hours = npoll_ela / 3600; + e_sec = npoll_ela % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = npoll_cpu / 3600; + c_sec = npoll_cpu % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + tmp = npoll_ela; + if (tmp == 0) tmp = 1; + pct = ((npoll_cpu / tmp) * 100.0 + 0.5); + + printf(" %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } else + printf("\n\n"); + } + +############################################################################### + if (transmit) { + printf("Article Transmission (we contact them)\n"); + printf("System Offrd Took Toss Fail Pct Elapsed CPU Pct\n"); + for(s in xmt) { + + nxmt += xmt[s]; + nxmt_accept += xmt_accept[s]; + nxmt_refuse += xmt_refuse[s]; + nxmt_failed += xmt_failed[s]; + nxmt_ela += xmt_ela[s]; + nxmt_cpu += xmt_cpu[s]; + + we_offered = xmt[s]; + if (we_offered == 0) we_offered = 1; + they_toss = (xmt_refuse[s] / we_offered) * 100 + 0.5; + + e_hours = xmt_ela[s] / 3600; + e_sec = xmt_ela[s] % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = xmt_cpu[s] / 3600; + c_sec = xmt_cpu[s] % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + elapsed = xmt_ela[s]; + if (elapsed == 0) elapsed = 1; + pct = ((xmt_cpu[s] / elapsed) * 100.0 + 0.5); + + printf("%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, xmt[s], xmt_accept[s], xmt_refuse[s], xmt_failed[s], they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + + we_offered = nxmt; + if (we_offered == 0) we_offered = 1; + they_toss = (nxmt_refuse / we_offered) * 100 + 0.5; + + e_hours = nxmt_ela / 3600; + e_sec = nxmt_ela % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = nxmt_cpu / 3600; + c_sec = nxmt_cpu % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + if (nxmt_ela == 0) nxmt_ela = 1; + pct = ((nxmt_cpu / nxmt_ela) * 100.0 + 0.5); + + printf("\n%-25s %5d %5d %5d %5d %3d%% %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nxmt, nxmt_accept, nxmt_refuse, nxmt_failed, they_toss, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + +############################################################################### +### Article Readership Statistics ### +############################################################################### + + if (readers) { + printf("NNTP readership statistics\n"); + printf("System Conn Articles Groups Post Elapsed CPU Pct\n"); + for(s in systems) { +### +### servers are different animals; they don't belong in this part of the report +### + if (servers[s] > 0 && groups[s] == 0 && articles[s] == 0) + continue; +### +### report the curious server pokers elsewhere +### + if (groups[s] == 0 && articles[s] == 0 && post[s] == 0) { + unknown[s] += systems[s]; + curious = 1; + continue; + } + + nconn += systems[s]; + nart += articles[s]; + ngrp += groups[s]; + npost += post[s]; + ncpu += cpu[s]; + nela += ela[s]; + + e_hours = ela[s] / 3600; + e_sec = ela[s] % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = cpu[s] / 3600; + c_sec = cpu[s] % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + elapsed = ela[s]; + if (elapsed == 0) elapsed = 1; + pct = ((cpu[s] / elapsed) * 100 + 0.5); + + printf("%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n", s, systems[s], articles[s], groups[s], post[s], e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + + e_hours = nela / 3600; + e_sec = nela % 3600; + e_min = e_sec / 60; + e_sec %= 60; + + c_hours = ncpu / 3600; + c_sec = ncpu % 3600; + c_min = c_sec / 60; + c_sec %= 60; + + if (nela == 0) nela = 1; + pct = ((ncpu / nela) * 100 + 0.5); + + printf("\n%-25s %5d %8d %6d %4d %3d:%02d:%02d %3d:%02d:%02d %3d%%\n\n", "TOTALS", nconn, nart, ngrp, npost, e_hours, e_min, e_sec, c_hours, c_min, c_sec, pct); + } + +############################################################################### + if (curious) { + printf("Unknown NNTP server explorers\nSystem Conn\n"); + for(s in unknown) { + printf("%-25s %5d\n", s, unknown[s]); + } + printf("\n"); + } +############################################################################### + if (timeouts) { + printf("Server timeouts\n"); + for(s in timeout) { + printf("%-25s %5d\n", s, timeout[s]); + } + printf("\n"); + } +############################################################################### + if (readers) { + for(g in ng) { + x = length(g); + if (x > max) max = x; + + i = index(g, "."); + if (i > 0) top = substr(g, 1, i - 1); + else top = g; + category[top] += ng[g]; + } + fmt = sprintf("%%-%ds %%5d\n", max); + + printf("Newsgroup Request Counts (by category)\n"); + for(g in category) printf(fmt, g, category[g]); + + printf("\nNewsgroup Request Counts (by newsgroup)\n"); + for(g in ng) printf(fmt, g, ng[g]); + printf("\n"); + } +} diff --git a/usr/src/new/nntp/xfer/README b/usr/src/new/nntp/xfer/README new file mode 100644 index 0000000000..da4f0744dc --- /dev/null +++ b/usr/src/new/nntp/xfer/README @@ -0,0 +1,9 @@ +This is a "passive" news transmission client. It queries +other servers for news with NEWNEWS and gets it via the article +command. + +No docs, sorry. It's unsupported (like the rest of the stuff is???). +Read the source code. + +Originally written by Brian Kantor , with some +bug fixes by Ambar . diff --git a/usr/src/new/nntp/xfer/nntpxfer.c b/usr/src/new/nntp/xfer/nntpxfer.c new file mode 100644 index 0000000000..e8d28bc87b --- /dev/null +++ b/usr/src/new/nntp/xfer/nntpxfer.c @@ -0,0 +1,507 @@ +/* + * nntpxfer + * + * Connects to the specified nntp server, and transfers all new news + * since the last successful invocation. + * + * last successful invocation date and time are stored in a file at + * /usr/spool/news/nntp. as + * groups YYMMDD HHMMSS distributions\n + * in case you need to edit it. You can also override this on + * the command line in the same format, in which case the file won't + * be updated. + * + * Brian Kantor, UCSD 1986 + * (some bug fixes by ambar@athena.mit.edu) + */ + +#define DEBUG + +/* you'd think that 4096 articles at one go is enough.... */ +#define MAXARTS 4096 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define INEWS "/usr/lib/news/inews -p" +#define HIST "/usr/lib/news/history" + +char *malloc(); +char *strcpy(); +char *strcat(); +long time(); +u_long inet_addr(); + +extern int errno; +char *artlist[MAXARTS]; +int server; /* stream socket to the nntp server */ +int newart, dupart, misart; + +main(argc, argv) +int argc; +char *argv[]; + { + FILE *dtfile; /* where last xfer date/time stored */ + char buf[BUFSIZ]; + char lastdate[16]; + char distributions[BUFSIZ]; + char dtname[128]; + char newsgroups[BUFSIZ]; + char lasttime[16]; + int connected = 0; /* 1 = connected */ + int i; + int omitupdate = 0; /* 1 = don't update datetime */ + long clock; + long newdate, newtime; + struct hostent *hp; + struct servent *sp; + struct sockaddr_in sin; + struct tm *now; + + /* OPTIONS + argv[1] MUST be the host name + argv[2-4] MAY be "newsgroups YYMMDD HHMMSS" + argv[5] MAY be distributions + (otherwise use 2-4/5 from the file + "/usr/spool/news/nntp.hostname") + */ + + if (argc != 2 && argc != 5 && argc != 6) + { + (void) printf("Usage: %s host [groups YYMMDD HHMMSS []]\n", + argv[0]); + exit(1); + } + + if (argc > 2) + { + omitupdate++; + (void) strcpy(newsgroups, argv[2]); + (void) strcpy(lastdate, argv[3]); + (void) strcpy(lasttime, argv[4]); + (void) strcpy(distributions, ""); + if (argc > 5) + (void) strcpy(distributions, argv[5]); + } + else + { + (void) strcpy(dtname, "/usr/spool/news/nntp."); + (void) strcat(dtname, argv[1]); + dtfile = fopen(dtname, "r"); + if (dtfile == NULL) + { + (void) printf("%s not found; using * 860101 000000 \n", + dtname); + (void) strcpy(newsgroups, "*"); + (void) strcpy(lastdate, "860101"); + (void) strcpy(lasttime, "000000"); + (void) strcpy(distributions, ""); + } + else + { + if (fscanf(dtfile, "%s %s %s %s", + newsgroups, lastdate, lasttime, distributions) < 3) + { + (void) printf("%s invalid; using * 860101 000000\n", + dtname); + (void) strcpy(newsgroups, "*"); + (void) strcpy(lastdate, "860101"); + (void) strcpy(lasttime, "000000"); + (void) strcpy(distributions, ""); + } + (void) fclose(dtfile); + } + clock = time((long *)0); + now = gmtime(&clock); + newdate = (now->tm_year * 10000) + + ((now->tm_mon + 1) * 100) + now->tm_mday; + newtime = (now->tm_hour * 10000) + + (now->tm_min * 100) + now->tm_sec; + } + +#ifdef DEBUG + (void) printf("newsgroups = '%s'\n", newsgroups); + (void) printf("date = '%s'\n", lastdate); + (void) printf("time = '%s'\n", lasttime); + (void) printf("distributions = '%s'\n", distributions); + (void) printf("now is = %06d %06d\n", newdate, newtime); +#endif + + if (dbminit(HIST) < 0) + { + perror("couldn't open history file"); + exit(1); + } + + sin.sin_addr.s_addr = inet_addr(argv[1]); + if (sin.sin_addr.s_addr != -1) + { + sin.sin_family = AF_INET; + } + else + { + hp = gethostbyname(argv[1]); + if (hp == NULL) + { + (void) printf("%s: unknown host\n", argv[1]); + exit(1); + } + + sin.sin_family = hp->h_addrtype; +#ifdef BSD43 + bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, + hp->h_length); +#else BSD43 + bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, + hp->h_length); +#endif BSD43 + } + + sp = getservbyname("nntp", "tcp"); + if (sp == NULL) + { + perror("nntp/tcp"); + exit(1); + } + + sin.sin_port = sp->s_port; + + do { + server = socket(AF_INET, SOCK_STREAM, 0); + if (server < 0) + { + perror("nntpxfer: socket"); + exit(1); + } + + if (connect(server, (struct sockaddr *)&sin, sizeof (sin)) < 0) + { +#ifdef BSD43 + if (hp && hp->h_addr_list[1]) + { + hp->h_addr_list++; + bcopy(hp->h_addr_list[0], + (caddr_t)&sin.sin_addr, hp->h_length); + (void) close(server); + continue; + } +#endif BSD43 + perror("nntpxfer: connect"); + exit(1); + } + connected++; + } + while (connected == 0); + +#ifdef DEBUG + (void) printf("connected to nntp server at %s\n", argv[1]); +#endif + /* + * ok, at this point we're connected to the nntp daemon + * at the distant host. + */ + + /* get the greeting herald */ + (void) sockread(buf); +#ifdef DEBUG + (void) printf("%s\n", buf); +#endif + if (buf[0] != '2') /* uh-oh, something's wrong! */ + { + (void) printf("protocol error: got '%s'\n", buf); + (void) close(server); + exit(1); + } + + + /* first, tell them we're a slave process to get priority */ + sockwrite("SLAVE"); + (void) sockread(buf); +#ifdef DEBUG + (void) printf("%s\n", buf); +#endif + if (buf[0] != '2') /* uh-oh, something's wrong! */ + { + (void) printf("protocol error: got '%s'\n", buf); + (void) close(server); + exit(1); + } + + /* now, ask for a list of new articles */ + if (strlen(distributions)) + (void) sprintf(buf,"NEWNEWS %s %s %s GMT <%s>", + newsgroups, lastdate, lasttime, distributions); + else + (void) sprintf(buf,"NEWNEWS %s %s %s GMT", + newsgroups, lastdate, lasttime); + sockwrite(buf); + (void) sockread(buf); +#ifdef DEBUG + (void) printf("%s\n", buf); +#endif + if (buf[0] != '2') /* uh-oh, something's wrong! */ + { + (void) printf("protocol error: got '%s'\n", buf); + (void) close(server); + exit(1); + } + /* and here comes the list, terminated with a "." */ +#ifdef DEBUG + (void) printf("data\n"); +#endif + while (1) + { + (void) sockread(buf); + if (!strcmp(buf,".")) + break; + if (wewant(buf)) + { + if (newart > MAXARTS) + { + omitupdate++; + continue; + } + artlist[newart] = malloc((unsigned)(strlen(buf)+1)); + (void) strcpy(artlist[newart], buf); + newart++; + } + else + dupart++; + } +#ifdef DEBUG + (void) printf(".\n%d new, %d dup articles\n", newart, dupart); +#endif + + /* now that we know which articles we want, retrieve them */ + for (i=1; i < newart; i++) + (void) artfetch(artlist[i]); + +#ifdef DEBUG + (void) printf("%d missing articles\n", misart); +#endif + /* we're all done, so tell them goodbye */ + sockwrite("QUIT"); + (void) sockread(buf); +#ifdef DEBUG + (void) printf("%s\n", buf); +#endif + if (buf[0] != '2') /* uh-oh, something's wrong! */ + { + (void) printf("error: got '%s'\n", buf); + (void) close(server); + exit(1); + } + (void) close(server); + + /* do we want to update the timestamp file? */ + if (!omitupdate) + { + (void) sprintf(buf, "%s %06d %06d %s\n", + newsgroups, newdate, newtime, distributions); +#ifdef DEBUG + (void) printf("updating %s:\n\t%s\n", dtname, buf); +#endif + dtfile = fopen(dtname, "w"); + if (dtfile == NULL) + { + perror(dtname); + exit(1); + } + (void) fputs(buf,dtfile); + (void) fclose(dtfile); + } + exit(0); +} + +artfetch(articleid) +char *articleid; + { + int lines = 0; + char buf[BUFSIZ]; + FILE *inews; + + /* now, ask for the article */ + (void) sprintf(buf,"ARTICLE %s", articleid); + sockwrite(buf); + (void) sockread(buf); +#ifdef DEBUG + (void) printf("%s\n", buf); +#endif + if (buf[0] == '4') /* missing article, just skipit */ + { + misart++; + return(0); + } + + if (buf[0] != '2') /* uh-oh, something's wrong! */ + { + (void) printf("protocol error: got '%s'\n", buf); + (void) close(server); + exit(1); + } +#ifdef DEBUG + (void) printf("command: %s\n", INEWS); +#endif + if ( (inews = popen(INEWS, "w")) == NULL) + { + perror(INEWS); + exit(1); + } + + /* and here comes the article, terminated with a "." */ +#ifdef DEBUG + (void) printf("data\n"); +#endif + while (1) + { + (void) sockread(buf); + if (buf[0] == '.' && buf[1] == '\0') + break; + lines++; + (void) strcat(buf,"\n"); + (void) fputs(((buf[0] == '.') ? buf + 1 : buf), + inews); + } +#ifdef DEBUG + (void) printf(".\n%d lines\n", lines); +#endif + (void) fflush(inews); + (void) pclose(inews); + return(0); + } + +int +sockread(buf) +char *buf; + { + char c; + int j = 0; +#ifdef BSD43 + fd_set rf; +#else BSD43 + int rf; +#endif BSD43 + struct timeval tv; + int r; + char *p = buf; + + while ( 1 ) + { + tv.tv_sec = 1800; /* 15 minutes */ + tv.tv_usec = 0L; +#ifdef BSD43 + FD_ZERO(&rf); + FD_SET(server, &rf); +#else BSD43 + rf = 1 << server; +#endif BSD43 + r = select(20, (fd_set *)&rf, (fd_set *)0, (fd_set *)&rf, &tv); + + if (r < 0) + { + if (errno == EINTR) + continue; + perror("getsock select"); + exit(1); + } + if (r == 0) + { + printf("read timed out.\n"); + exit(1); + } + + if (read(server, &c, 1) <= 0) + break; + + /* mask off any chance parity bits */ + *p = c & 0x7f; + + /* look for end of line (== LF) */ + if (c == 0x0a) + { + if (j > 0 && *(p-1) == 0x0d) + *(p-1) = '\0'; + else + *p = '\0'; + return(strlen(buf)); + } + j++; p++; + } + perror("sockread"); + (void) close(server); + exit(1); + /* NOTREACHED */ + } + +sockwrite(buf) +char *buf; + { + register int sz; + char buf2[BUFSIZ]; +#ifdef DEBUG + (void) printf(">>> %s\n", buf); +#endif + (void) strcpy(buf2,buf); + (void) strcat(buf2,"\r\n"); + sz = strlen(buf2); + if (write(server,buf2,sz) != sz) + { + (void) printf("write error on server socket\n"); + (void) close(server); + exit(1); + } + } + +int +wewant(articleid) +char *articleid; + { + datum k, d; + char id[BUFSIZ]; + char *p; + + /* remove any case sensitivity */ + (void) strcpy(id, articleid); + p = id; + while (*p) + { + if (isupper(*p)) + *p = tolower(*p); + p++; + } + + k.dptr = id; + k.dsize = strlen(articleid) + 1; + + d = fetch(k); + + if (d.dptr) + { +#ifdef DEBUG + (void) printf("dup: '%s'\n", articleid); +#endif + return(0); + } + +#ifdef DEBUG + (void) printf("new: '%s'\n", articleid); +#endif + return(1); + } diff --git a/usr/src/new/nntp/xmit/Makefile b/usr/src/new/nntp/xmit/Makefile new file mode 100644 index 0000000000..2ba5afddcc --- /dev/null +++ b/usr/src/new/nntp/xmit/Makefile @@ -0,0 +1,12 @@ + +NNTPSRC = nntpxmit.c rfc822.c remote.c get_tcp_conn.c getdate.y llist.c +NNTPOBJ = nntpxmit.o rfc822.o remote.o get_tcp_conn.o getdate.o llist.o + +get_tcp_conn.o: get_tcp_conn.c get_tcp_conn.h + +remote.o: remote.c response_codes.h nntpxmit.h + +nntpxmit.o: nntpxmit.c nntpxmit.h get_tcp_conn.h header.h + +nntpxmit: $(NNTPOBJ) + $(CC) $(LDFLAGS) $(NNTPOBJ) -o nntpxmit diff --git a/usr/src/new/nntp/xmit/README b/usr/src/new/nntp/xmit/README new file mode 100644 index 0000000000..73e9eb075c --- /dev/null +++ b/usr/src/new/nntp/xmit/README @@ -0,0 +1,59 @@ +[This is a fairly old (but hardy!) version of nntpxmit. Erik Fair has +promised a newer version with more features "soon". -- Phil, 15 Oct 87] + + The program "nntpxmit" is an active trnamission client +(see the comment in nntpxmit.c for a description of the +difference between active and passive clients) written by Erik Fair +. + + nntpxmit requires support from the 2.11 news source. +Because of this, it must be compiled in the "src" directory of +the 2.11 distribution. You should: + + 1. Move the *.c and *.h files to 2.11/src + + 2. Move ../common/response_codes.h into 2.11/src + + 3. Add the lines in the Makefile in this directory to the + 2.11/src Makefile. You can just cat the Makefile + here to the Makefile in the 2.11/src directory, if + you'd like. + + 4. Then do "make nntpxmit". + + Having compiled nntpxmit like that, you need to set your news +system up to use it. Nntpxmit takes as arguments an internet host +to connect to, and a file containing a list of articles to send to +it. + + So, you might edit your news sys file to have an entry like: + +# +# NASA Ames Research Center +# +nike:ucb,uc,mod,to.nike:F:/usr/spool/news/batch/ames-titan.arpa + +This will place names of news articles in the ucb, uc, and mod newsgroups +in the file "/usr/spool/news/batch/ames-titan.arpa". This is because +we assume the output batch file will be the machine's internet name. + +Periodically, you should have crontab run "nntpsend" to transmit +the news. A good choice for "periodically" is every 10 minutes. +nntpsend depends on the program "shlock", which you'll need to +compile by hand (horrors): + + cc -O -o shlock shlock.c + +Also, nntpsend will need to be customized for your system. +Fortunately, it's pretty straightforward. + + nntpxmit has an option "-s" which *supresses* statistic +logging via syslog. Additionally, the "-d" option is availible +for debugging. + + Please forward comments/suggestions for improvement/bugs to +Erik Fair, . + + [My thanks extended to Erik for writing nntpxmit, shlock, +and the stats scripts. My thanks also to Mel Pleasant + for the nntpsend script. --Phil] diff --git a/usr/src/new/nntp/xmit/get_tcp_conn.c b/usr/src/new/nntp/xmit/get_tcp_conn.c new file mode 100644 index 0000000000..8f1b1e3bc6 --- /dev/null +++ b/usr/src/new/nntp/xmit/get_tcp_conn.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include "get_tcp_conn.h" + +extern int errno; + +/* +** Take the name of an internet host in ASCII (this may either be its +** official host name or internet number (with or without enclosing +** backets [])), and return the internet address number in 32 bit quantity. +** +** returns FAIL for failure to find the host name in the local database, +** or for a bad internet address spec. +*/ +u_long +name_to_address(host) +register char *host; +{ + if (host == (char *)NULL) + return(FAIL); + + /* + ** Is this an ASCII internet address? (either of [10.0.0.78] or + ** 10.0.0.78). + */ + if (*host == '[' || isdigit(*host)) { + u_long host_address; + char namebuf[128]; + register char *cp = namebuf; + + /* + ** strip brackets [] or anything else we don't want. + */ + while(*host && cp < &namebuf[sizeof(namebuf)]) { + if (isdigit(*host) || *host == '.') + *cp++ = *host++; + else + host++; + } + + if ((host_address = inet_addr(namebuf)) == FAIL) + return(FAIL); /* malformed internet address spec */ + return(host_address); + } else { + struct hostent *hstp = gethostbyname(host); + + if (hstp == NULL) + return(FAIL); /* no such host */ + return(*(u_long *)hstp->h_addr); /* we assume... */ + } +} + +/* +** given a host name (either name or internet address) and service name +** (or port number) (both in ASCII), give us a TCP connection to the +** requested service at the requested host (or give us FAIL). +*/ +get_tcp_conn(host,serv) +char *host; +char *serv; +{ + u_short port; + struct in_addr host_address; + + if ((host_address.s_addr = name_to_address(host)) == FAIL) { + return(NOHOST); + } + + if (isdigit(*serv)) { + port = htons((u_short)(atoi(serv))); + } else { + struct servent *srvp = getservbyname(serv, "tcp"); + + if (srvp == NULL) { + return(NOSERVICE); + } + port = (u_short)srvp->s_port; + } + + return(mkconn(&host_address, port, IPPROTO_TCP, SOCK_STREAM)); +} + +/* +** create a socket and connect it to a remote host on the specified +** port by the specified protocol. Return FAIL if something goes +** wrong somewhere. Since these are exclusively system calls, +** errno will have the correct error in it. +*/ +mkconn(host_address, port, protocol, proto_type) +struct in_addr *host_address; +u_short port; +int protocol, proto_type; +{ + register int skt; + struct sockaddr_in sadr; + + sadr.sin_family = (u_short)AF_INET; /* Only internet for now */ + sadr.sin_addr.s_addr = host_address->s_addr; + sadr.sin_port = (u_short)port; + + if ((skt = socket(AF_INET, proto_type, protocol)) < 0) + return(FAIL); + + if (connect(skt, &sadr, sizeof(sadr)) < 0) { + int save = errno; + + close(skt); + errno = save; + return(FAIL); + } + return(skt); +} diff --git a/usr/src/new/nntp/xmit/get_tcp_conn.h b/usr/src/new/nntp/xmit/get_tcp_conn.h new file mode 100644 index 0000000000..e66bf6b743 --- /dev/null +++ b/usr/src/new/nntp/xmit/get_tcp_conn.h @@ -0,0 +1,10 @@ +/* +** Return codes from get_tcp_conn(). +*/ +#define FAIL (-1) /* routine failed */ +#define NOHOST (FAIL-1) /* no such host */ +#define NOSERVICE (FAIL-2) /* no such service */ + +#ifndef NULL +#define NULL 0 +#endif diff --git a/usr/src/new/nntp/xmit/llist.c b/usr/src/new/nntp/xmit/llist.c new file mode 100644 index 0000000000..302c964463 --- /dev/null +++ b/usr/src/new/nntp/xmit/llist.c @@ -0,0 +1,51 @@ +#include "llist.h" + +extern void free(); +extern caddr_t malloc(); + +#define align(x) ((x) + ((x) % sizeof(long))) + +/* +** recursively free a linked list +*/ +void +l_free(lp) +register struct llist *lp; +{ + if (lp->l_next == NULL) + return; + l_free(lp->l_next); + free(lp->l_item); +} + +/* +** allocate a new element in a linked list, along with enough space +** at the end of the item for the next list element header. +*/ +struct llist * +l_alloc(lp, s, len) +register struct llist *lp; +caddr_t *s; +register unsigned len; +{ + if (s == NULL || lp == NULL) + return(NULL); + + lp->l_len = len; + len = align(len); + + if ((lp->l_item = malloc(len + sizeof(struct llist))) == NULL) + return(NULL); + + bcopy(s, lp->l_item, len); + lp->l_next = (struct llist *)(&lp->l_item[len]); + + /* + ** set up next list entry + */ + lp = lp->l_next; + lp->l_next = NULL; + lp->l_item = NULL; + lp->l_len = 0; + return(lp); +} diff --git a/usr/src/new/nntp/xmit/llist.h b/usr/src/new/nntp/xmit/llist.h new file mode 100644 index 0000000000..32d7678050 --- /dev/null +++ b/usr/src/new/nntp/xmit/llist.h @@ -0,0 +1,14 @@ +#include + +struct llist { + struct llist *l_next; + caddr_t l_item; + unsigned l_len; +}; + +extern void l_free(); +extern struct llist *l_alloc(); + +#ifndef NULL +#define NULL 0 +#endif diff --git a/usr/src/new/nntp/xmit/nntpsend b/usr/src/new/nntp/xmit/nntpsend new file mode 100755 index 0000000000..aba9cf1088 --- /dev/null +++ b/usr/src/new/nntp/xmit/nntpsend @@ -0,0 +1,49 @@ +#!/bin/csh -f +# +# What we have here is a csh script for sending netnews to NNTP sites. +# +set batchdir=/usr/spool/news/batch libdir=/usr/spool/news/lib +set path=( $libdir /usr/ucb /usr/bin /bin $path ) +set pname=$0 +set pname=$pname:t +echo ${pname}: "[$$]" begin `date` +# +# Go to where the action is +# +cd $batchdir +umask 022 +# +# For NNTP +# +# Here "foo", "bar", and "zot" are the Internet names of +# the machines to which to send. We make the supposition +# that the batch files will be a host's internet name. +# So, for example "nike"'s internet name is "ames-titan.arpa". +# Because of this, your sys file must have "ames-titan.arpa" +# as the batch file output for the machine "nike". +# +foreach host ( foo bar zot ) + set lock=NNTP_LOCK.${host} tmp=${host}.tmp send=${host}.nntp + shlock -p $$ -f ${lock} + if ($status == 0) then + if ( -e ${tmp} ) then + cat ${tmp} >> ${send} + rm ${tmp} + endif + if ( -e ${host} ) then +# this depends upon the atomicity of the rename(2) system call used in mv(1) + mv ${host} ${tmp} + cat ${tmp} >> ${send} + rm ${tmp} + endif + if ( -e ${send} ) then + echo ${pname}: "[$$]" begin ${host} + time nntpxmit ${host}:${send} + echo ${pname}: "[$$]" end ${host} + endif + rm -f ${lock} + else + echo ${pname}: "[$$]" ${host} locked by "[`cat ${lock}`]" + endif +end +echo ${pname}: "[$$]" end `date` diff --git a/usr/src/new/nntp/xmit/nntpxmit.c b/usr/src/new/nntp/xmit/nntpxmit.c new file mode 100644 index 0000000000..dfd1163042 --- /dev/null +++ b/usr/src/new/nntp/xmit/nntpxmit.c @@ -0,0 +1,347 @@ +/* +** nntpxmit - transmit netnews articles across the internet with nntp +** +** This program is for transmitting netnews between sites that offer the +** NNTP service, internet style. Ideally, there are two forms of +** transmission that can be used in this environment, since the +** communication is interactive (and relatively immediate, when compared +** with UUCP). They are: passive poll (what have you gotten lately?) and +** active send (I have `x', do you want it?). The USENET as a whole +** uniformly uses active send, and where the communication is batched +** (e.g. UUCP, or electronic mail) the software sends without even asking, +** unless it can determine that the article has already been to some site. +** +** It turns out that when you implement passive poll, you have to be +** *very* careful about what you (the server) tell the client, because +** something that you might wish to restrict distribution of (either +** internal newsgroups, or material posted in the international groups in +** local distributions) will otherwise leak out. It is the case that if +** the server doesn't tell the client that article `x' is there, the +** client won't ask for it. If the server tells about an article which +** would otherwise stay within some restricted distribution, the onus is +** then on the client to figure out that the article is not appropriate to +** post on its local system. Of course, at that point, we have already +** wasted the network bandwidth in transferring the article... +** +** This is a roundabout way of saying that this program only implements +** active send. There will have to be once-over done on the NNTP spec +** before passive poll goes in, because of the problems that I have cited. +** +** Erik E. Fair , Oct 14, 1985 +** +** Changed to exit on unusual errors in opening articles, and now produces +** CPU usage statistics to syslog. +** +** Erik E. Fair , April 26, 1986 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "defs.h" +#include "header.h" +#include "response_codes.h" +#include "nntpxmit.h" + +char *Pname; +char Debug = FALSE; +char Do_Stats = TRUE; +char *USAGE = "USAGE: nntpxmit [-s] hostname:file [hostname:file ...]"; +FILE *getfp(); + +struct Stats { + u_long offered; + u_long accepted; + u_long rejected; + u_long failed; +} Stats = {0L, 0L, 0L, 0L}; + +struct tms Prev_times, Cur_times; +time_t Tbegin, Tend; + +extern int errno; +extern char *rindex(); +extern char *index(); +extern char *errmsg(); +extern char *sp_strip(); +extern int msgid_ok(); + +main(ac,av) +int ac; +char *av[]; +{ + register int i; + char *host, *file; + + (void) time(&Tbegin); + + Pname = ((Pname = rindex(av[0],'/')) ? Pname + 1 : av[0]); + + if (ac < 2) { + fprintf(stderr,"%s: %s\n", Pname, USAGE); + exit(EX_USAGE); + } + + /* note that 4.2 BSD openlog has only two args */ +#ifdef LOG_LOCAL7 + (void) openlog(Pname, LOG_PID, LOG_LOCAL7); +#else + (void) openlog(Pname, LOG_PID); +#endif + + for(i = 1; i < ac; i++) { + if (av[i][0] == '-') { + switch(av[i][1]) { + case 's': + Do_Stats = FALSE; + break; + case 'd': + Debug++; + break; + default: + fprintf(stderr,"%s: no such option: -%c\n", + Pname, av[i][1]); + fprintf(stderr,"%s: %s\n", Pname, USAGE); + exit(EX_USAGE); + } + continue; + } + + /* + ** OK, it wasn't an option, therefore it must be a + ** hostname, filename pair. + */ + host = av[i]; + if ((file = index(host, ':')) != (char *)NULL) { + *file++ = '\0'; + } else { + fprintf(stderr,"%s: illegal hostname:file pair: <%s>\n", + Pname, host); + continue; + } + + bzero(&Stats, sizeof(Stats)); + if (sendnews(host, file) && Do_Stats) { + struct tms delta; + time_t elapsed; + syslog(LOG_INFO, + "%s stats %lu offered %lu accepted %lu rejected %lu failed\n", + host, Stats.offered, Stats.accepted, Stats.rejected, Stats.failed); + + click(&delta, &elapsed); + + syslog(LOG_INFO, "%s xmit user %lu system %lu elapsed %lu\n", + host, delta.tms_utime, delta.tms_stime, elapsed); + } + } + exit(EX_OK); +} + +/* +** Calculate how much time we've used. +** +** The HZ constant is from times(3C) man page, and is probably wrong +** for anything other than a VAX. +** +** Why `click'? Well, imagine that I've got a stopwatch in my hand... +*/ +#define HZ 60L + +click(cpu, elapsed) +register struct tms *cpu; +time_t *elapsed; +{ + (void) times(&Cur_times); + (void) time(&Tend); + + /* delta T */ + *elapsed = Tend - Tbegin; + cpu->tms_utime = Cur_times.tms_utime - Prev_times.tms_utime; + cpu->tms_stime = Cur_times.tms_stime - Prev_times.tms_stime; + cpu->tms_cutime = Cur_times.tms_cutime - Prev_times.tms_cutime; + cpu->tms_cstime = Cur_times.tms_cstime - Prev_times.tms_cstime; + + /* reset reference point */ + Tbegin = Tend; + Prev_times = Cur_times; + + /* aggregate the children with the parent */ + cpu->tms_utime += cpu->tms_cutime; + cpu->tms_stime += cpu->tms_cstime; + + /* adjust these to seconds */ + cpu->tms_utime /= HZ; + cpu->tms_stime /= HZ; +} + +/* +** Given a hostname to connect to, and a file of filenames (which contain +** netnews articles), send those articles to the named host using NNTP. +*/ +sendnews(host, file) +char *host, *file; +{ + register int code; + register FILE *filefile = fopen(file, "r"); + register FILE *fp; + char buf[BUFSIZ]; + char article[BUFSIZ]; + + /* + ** if no news to send, return + */ + if (filefile == (FILE *)NULL) { + dprintf(stderr, "%s: %s: %s\n", Pname, file, errmsg(errno)); + return(FALSE); + } + + if (hello(host) == FAIL) { + fclose(filefile); + return(FALSE); + } + + while((fp = getfp(filefile, article, sizeof(article))) != (FILE *)NULL) { + switch(code = ihave(fp, article)) { + case CONT_XFER: + if (!sendfile(fp)) { + fprintf(stderr, "%s: %s: article transmission failed while sending %s\n", Pname, host, article); + Stats.failed++; + fclose(filefile); + fclose(fp); + goodbye(DONT_WAIT); + return(TRUE); + } + fclose(fp); + /* + ** Here I read the reply from the remote about the + ** transferred article, and I throw it away. I + ** should probably try and record the article + ** filename and append it back to the batchfile + ** again in the name of reliability, but that's + ** messy, and it's easier to assume that the guy + ** will have redundant feeds. + */ + code = readreply(buf, sizeof(buf)); + if (code != OK_XFERED) Stats.failed++; + break; + case ERR_GOTIT: + fclose(fp); + break; + default: + fprintf(stderr,"%s: %s gave an improper response to IHAVE: %d\n", Pname, host, code); + fprintf(stderr,"%s: while sending article %s\n", Pname, article); + fclose(filefile); + fclose(fp); + goodbye(DONT_WAIT); + return(TRUE); + } + } + fclose(filefile); + if (unlink(file) < 0) { + fprintf(stderr,"%s: unlink(%s): %s\n", Pname, file, errmsg(errno)); + } + goodbye(WAIT); + return(TRUE); +} + +/* +** Read the header of a netnews article, snatch the message-id therefrom, +** and ask the remote if they have that one already. +*/ +ihave(fp, article) +FILE *fp; +char *article; +{ + register int code; + struct hbuf header; + char scr[LBUFLEN]; + char buf[BUFSIZ]; + + bzero(&header, sizeof(header)); + if (!rfc822read(&header, fp, scr)) { + /* + ** something botched locally with the article + ** so we don't send it, but we don't break off + ** communications with the remote either. + */ + return(ERR_GOTIT); + } + + /* + ** If an article shows up without a message-id, + ** or with a bogus message-id, + ** we scream bloody murder. That's one in + ** the `can't ever happen' category. + */ + if (header.ident[0] == '\0') { + fprintf(stderr, "%s: %s missing message-id!\n", Pname, article); + return(ERR_GOTIT); + } else { + (void) strcpy(scr, sp_strip(header.ident)); + } + + if (!msgid_ok(scr)) { + fprintf(stderr, "%s: %s message-id syntax error!\n", Pname, article); + return(ERR_GOTIT); + } + + sprintf(buf, "IHAVE %s", scr); + Stats.offered++; + + switch(code = converse(buf, sizeof(buf))) { + case CONT_XFER: + Stats.accepted++; + rewind(fp); + return(code); + default: + Stats.rejected++; + return(code); + } +} + +/* +** Given that fp points to an open file containing filenames, +** open and return a file pointer to the next filename in the file. +** Don't you love indirection? +** +** Returns a valid FILE pointer or NULL if end of file. +*/ + +FILE * +getfp(fp, filename, fnlen) +register FILE *fp; +char *filename; +register unsigned fnlen; +{ + register FILE *newfp = (FILE *)NULL; + register char *cp; + + while(newfp == (FILE *)NULL) { + if (fgets(filename, fnlen, fp) == (char *)NULL) + return((FILE *)NULL); /* EOF, tell caller */ + + filename[fnlen - 1] = '\0'; /* make sure */ + + if (*(cp = &filename[strlen(filename) - 1]) == '\n') + *cp = '\0'; + + if ((newfp = fopen(filename, "r")) == (FILE *)NULL) { + register int save = errno; + + fprintf(stderr, "%s: fopen(%s, \"r\"): %s\n", Pname, filename, errmsg(errno)); + /* + ** The only permissible error is `file non-existant' + ** anything else indicates something is seriously + ** wrong, and we should go away to let the shell + ** script clean up. + */ + if (save != ENOENT) + exit(EX_OSERR); + } + } + return(newfp); +} diff --git a/usr/src/new/nntp/xmit/nntpxmit.h b/usr/src/new/nntp/xmit/nntpxmit.h new file mode 100644 index 0000000000..f4b153ac82 --- /dev/null +++ b/usr/src/new/nntp/xmit/nntpxmit.h @@ -0,0 +1,10 @@ +#define dprintf if (Debug) fprintf + +#define TIMEOUT 3600 /* seconds to read timeout in sfgets */ + +/* in goodbye() wait (or not) for QUIT response */ +#define WAIT 1 +#define DONT_WAIT 0 +#ifndef FAIL +#define FAIL (-1) +#endif diff --git a/usr/src/new/nntp/xmit/remote.c b/usr/src/new/nntp/xmit/remote.c new file mode 100644 index 0000000000..f2534e51c3 --- /dev/null +++ b/usr/src/new/nntp/xmit/remote.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include "get_tcp_conn.h" +#include "response_codes.h" +#include "nntpxmit.h" + +#define TRUE 1 +#define FALSE 0 + +static jmp_buf SFGstack; +FILE *rmt_rd; +FILE *rmt_wr; +char *sfgets(); +char *rfgets(); + +extern int errno; +extern char *Pname; +extern char Debug; +extern char *errmsg(); + +/* +** send cmd to remote, terminated with a CRLF. +*/ +sendcmd(cmd) +char *cmd; +{ + 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; + + /* + ** 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 socket. +*/ +hello(host) +char *host; +{ + int socket0, socket1; /* to me (bad pun) */ + static char *service = "nntp"; + char buf[BUFSIZ]; + + switch(socket0 = get_tcp_conn(host, service)) { + case NOHOST: + fprintf(stderr,"%s: no such host <%s>\n", Pname, host); + return(FAIL); + case NOSERVICE: + fprintf(stderr,"%s: no such service <%s>\n", Pname, service); + return(FAIL); + case FAIL: + fprintf(stderr,"%s: %s: %s\n", Pname, host, errmsg(errno)); + return(FAIL); + } + + if ((socket1 = dup(socket0)) < 0) { + close(socket0); + fprintf(stderr,"%s: dup(2): %s\n", Pname, errmsg(errno)); + return(FAIL); + } + + if ((rmt_rd = fdopen(socket0, "r")) == (FILE *)NULL) { + close(socket0); + close(socket1); + fprintf(stderr,"%s: fdopen(3): %s\n", Pname, errmsg(errno)); + return(FAIL); + } + + if ((rmt_wr = fdopen(socket1, "w")) == (FILE *)NULL) { + fclose(rmt_rd); + rmt_rd = (FILE *)NULL; + close(socket1); + fprintf(stderr,"%s: fdopen(3): %s\n", Pname, errmsg(errno)); + 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') + fprintf(stderr, "%s: %s\n", Pname, buf); + goodbye(DONT_WAIT); + return(FAIL); + } + return(NULL); +} + +/* +** Say goodbye to the nice remote server. +*/ +goodbye(wait) +int wait; +{ + if (sendcmd("QUIT")) + wait = FALSE; /* override, something's wrong. */ + /* + ** I don't care what they say to me; this is just being polite. + */ + if (wait) { + char buf[BUFSIZ]; + + (void) readreply(buf, sizeof(buf)); + } + (void) fclose(rmt_rd); + rmt_rd = (FILE *)NULL; + (void) fclose(rmt_wr); + rmt_wr = (FILE *)NULL; +} + +static +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; + + if (setjmp(SFGstack)) { + alarm(0); /* reset alarm clock */ + signal(SIGALRM, SIG_DFL); /* reset SIGALRM */ + fp->_flag |= _IOERR; /* set stdio error */ + return(NULL); /* bad read, remote time out */ + } + signal(SIGALRM, to_sfgets); + alarm(TIMEOUT); + ret = fgets(buf, size, fp); + alarm(0); /* reset alarm clock */ + signal(SIGALRM, SIG_DFL); /* reset SIGALRM */ + 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; + + *cp = '\0'; + if (sfgets(buf, size, fp) == NULL) + return(NULL); + + /* => '\n' */ + if ((len = strlen(buf)) > 2 && *(cp = &buf[len - 2]) == '\r') { + *cp++ = '\n'; + *cp = '\0'; + } + + /* ".\n" => EOF */ + cp = buf; + if (*cp++ == '.' && *cp == '\n') { + return(NULL); /* EOF */ + } + + /* Dot escaping */ + if (buf[0] == '.') 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 int nl = TRUE; /* assume we start on a new line */ + + if (fp == (FILE *)NULL) + return(FALSE); + + while((c = fgetc(fp)) != EOF) { + switch(c) { + case '\n': + (void) fputc('\r', rmt_wr); /* \n -> \r\n */ + (void) fputc(c, rmt_wr); + nl = TRUE; /* for dot escaping */ + break; + case '.': + if (nl) { + (void) fputc(c, rmt_wr); /* add a dot */ + nl = FALSE; + } + (void) fputc(c, rmt_wr); + break; + default: + (void) fputc(c, rmt_wr); + nl = FALSE; + break; + } + } + if (!nl) { + (void) fputs("\r\n", rmt_wr); + } + (void) fputs(".\r\n", rmt_wr); /* . termination */ + (void) fflush(rmt_wr); + if (ferror(fp) || ferror(rmt_wr)) /* any errors? */ + return(FALSE); + return(TRUE); +} diff --git a/usr/src/new/nntp/xmit/rfc822.c b/usr/src/new/nntp/xmit/rfc822.c new file mode 100644 index 0000000000..f4d8ff5eea --- /dev/null +++ b/usr/src/new/nntp/xmit/rfc822.c @@ -0,0 +1,668 @@ +/* +** Get header info from mail-format file. +** Return non-zero on success. +*/ + +#include "defs.h" +#include +#include +#include +#include "header.h" +#include "llist.h" + +#ifndef isblank +#define isblank(c) ((c) == ' ' || (c) == '\t') +#endif + +char *hfgets(); +char *arpadate(); +char *errmsg(); +char *strpbrk(); +time_t cgtdate(); +extern char *index(); +extern unsigned strlen(); +extern time_t time(); + +#define FROM 1 +#define NEWSGROUP 2 +#define TITLE 3 +#define SUBMIT 4 +#define RECEIVE 5 +#define EXPIRE 6 +#define ARTICLEID 7 +#define MESSAGEID 8 +#define REPLYTO 9 +#define FOLLOWID 10 +#define CONTROL 11 +#define SENDER 12 +#define FOLLOWTO 13 +#define PATH 14 +#define POSTVERSION 15 +#define RELAYVERSION 16 +#define DISTRIBUTION 17 +#define ORGANIZATION 18 +#define NUMLINES 19 +#define KEYWORDS 20 +#define APPROVED 21 +#define NFID 22 +#define NFFROM 23 +#define XREF 24 +#define SUMMARY 25 +#define FULLNAME 26 +#define OTHER 99 + +/* +** This is the list of headers we recognize. +** All others get stripped before they get to inews. +*/ +struct htype { + char *hname; + int hid; +} htype[] = { + {"Approved:", APPROVED}, + {"Article-I.D.:", ARTICLEID}, + {"Control:", CONTROL}, + {"Date-Received:", RECEIVE}, + {"Date:", SUBMIT}, + {"Distribution:", DISTRIBUTION}, + {"Expires:", EXPIRE}, + {"Followup-To:", FOLLOWTO}, + {"From:", FROM}, +/* {"Full-Name:", FULLNAME}, */ + {"In-Reply-To:", FOLLOWID}, + {"Keywords:", KEYWORDS}, + {"Lines:", NUMLINES}, + {"Message-ID:", MESSAGEID}, + {"Newsgroups:", NEWSGROUP}, + {"Nf-From:", NFFROM}, + {"Nf-ID:", NFID}, + {"Organization:", ORGANIZATION}, + {"Path:", PATH}, + {"Posted:", SUBMIT}, + {"Posting-Version:", POSTVERSION}, +/* {"Received:", RECEIVE}, a bad name w.r.t. RFC822 */ + {"References:", FOLLOWID}, + {"Relay-Version:", RELAYVERSION}, + {"Reply-To:", REPLYTO}, + {"Sender:", SENDER}, + {"Subject:", TITLE}, + {"Summary:", SUMMARY}, + {"Title:", TITLE}, + {"Xref:", XREF}, + {(char *)NULL, NULL} +}; + +char *malloc(); + +rfc822read(hp, fp, bfr) +register struct hbuf *hp; +register FILE *fp; +char *bfr; +{ + register int i = type(bfr); + long curpos; + + do { + curpos = ftell(fp); + switch (i) { + case FROM: + getfield(bfr, hp->from, sizeof(hp->from)); + break; + case NEWSGROUP: + getfield(bfr, hp->nbuf, sizeof(hp->nbuf)); + break; + case TITLE: + getfield(bfr, hp->title, sizeof(hp->title)); + break; + case SUBMIT: + getfield(bfr, hp->subdate, sizeof(hp->subdate)); + break; + case EXPIRE: + getfield(bfr, hp->expdate, sizeof(hp->expdate)); + break; + case MESSAGEID: + getfield(bfr, hp->ident, sizeof(hp->ident)); + break; + case REPLYTO: + getfield(bfr, hp->replyto, sizeof(hp->replyto)); + break; + case FOLLOWID: + getfield(bfr, hp->followid, sizeof(hp->followid)); + break; + case SENDER: + getfield(bfr, hp->sender, sizeof(hp->sender)); + break; + case FOLLOWTO: + getfield(bfr, hp->followto, sizeof(hp->followto)); + break; + case CONTROL: + getfield(bfr, hp->ctlmsg, sizeof(hp->ctlmsg)); + break; + case DISTRIBUTION: + getfield(bfr, hp->distribution, sizeof(hp->distribution)); + break; + case ORGANIZATION: + getfield(bfr, hp->organization, sizeof(hp->organization)); + break; + case KEYWORDS: + getfield(bfr, hp->keywords, sizeof(hp->keywords)); + break; + case APPROVED: + getfield(bfr, hp->approved, sizeof(hp->approved)); + break; + case SUMMARY: + getfield(bfr, hp->summary, sizeof(hp->summary)); + break; + } + } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0); + + if (*bfr != '\n') + fseek(fp, curpos, 0); + return(TRUE); +} + +#define its(type) (prefix(ptr, type)) + +type(ptr) +register char *ptr; +{ + register struct htype *hp; + register char *colon, *space; + static int lasthdr = FALSE; /* for continuation headers */ + + /* + ** some consistency checks (i.e. is this really a header line?) + */ + if ((ptr == NULL) || !isascii(*ptr) || (*ptr == '\n')) + return(lasthdr = FALSE); + + if (isblank(*ptr)) /* continuation line? */ + return(lasthdr); + + colon = index(ptr, ':'); + space = index(ptr, ' '); + if (!colon || space && space < colon) + return(lasthdr = FALSE); + + for(hp = htype; hp->hname; hp++) { + if (its(hp->hname)) + return(lasthdr = hp->hid); + } + return(lasthdr = OTHER); +} + +/* +** Get the contents of the field of the header line, appending it, +** with a space delimeter if it's a continuation line. +** If there is already something in the header storage, skip this +** header line and the continuations. +*/ +getfield(src, dest, size) +register char *src, *dest; +int size; /* of dest (total bytes) */ +{ + static int skip = FALSE; /* skip the continuation lines */ + + if (src == (char *)NULL || dest == (char *)NULL) + return(FALSE); + + if (isblank(*src)) { /* continuation line? */ + if (skip) + return(TRUE); + if ((size -= strlen(dest)) <= 0) /* any space left? */ + return(FALSE); + while(*src && isblank(*src)) /* eat whitespace */ + src++; + *--src = ' '; /* backup & add one */ + (void) strncat(dest, src, size - 1); /* append to hdr */ + } else { + skip = FALSE; + if (*dest) /* already got one? */ + return(skip = TRUE); /* skip continuation */ + if ((src = index(src, ':')) == (char *)NULL) /* impossible */ + return(FALSE); + src++; /* skip colon */ + while(*src && isblank(*src)) /* eat whitespace */ + src++; + (void) strncpy(dest, src, size - 1); + } + (void) nstrip(dest); + return(TRUE); +} + +/* +** Write out an RFC822 header, paying no attention to line limits. +** Ideally, we should do continuations in here... +*/ +rfc822write(hp, fp) +register struct hbuf *hp; +register FILE *fp; +{ + time_t t; + + if (hp->path[0]) + fprintf(fp, "Path: %s\n", hp->path); + if (hp->from[0]) + fprintf(fp, "From: %s\n", hp->from); + if (hp->nbuf[0]) + fprintf(fp, "Newsgroups: %s\n", hp->nbuf); + if (hp->title[0]) + fprintf(fp, "Subject: %s\n", hp->title); + if (hp->ident[0]) + fprintf(fp, "Message-ID: %s\n", hp->ident); + if (hp->subdate[0]) + t = cgtdate(hp->subdate); + else + time(&t); + fprintf(fp, "Date: %s\n", arpadate(&t)); + if (*hp->expdate) + fprintf(fp, "Expires: %s\n", hp->expdate); + if (*hp->followid) + fprintf(fp, "References: %s\n", hp->followid); + if (*hp->ctlmsg) + fprintf(fp, "Control: %s\n", hp->ctlmsg); + if (*hp->sender) + fprintf(fp, "Sender: %s\n", hp->sender); + if (*hp->replyto) + fprintf(fp, "Reply-To: %s\n", hp->replyto); + if (*hp->followto) + fprintf(fp, "Followup-To: %s\n", hp->followto); + if (*hp->distribution) + fprintf(fp, "Distribution: %s\n", hp->distribution); + if (*hp->organization) + fprintf(fp, "Organization: %s\n", hp->organization); + if (*hp->keywords) + fprintf(fp, "Keywords: %s\n", hp->keywords); + if (*hp->summary) + fprintf(fp, "Summary: %s\n", hp->summary); + if (*hp->approved) + fprintf(fp, "Approved: %s\n", hp->approved); + putc('\n', fp); + return(!ferror(fp)); +} + +/* +** strip leading and trailing spaces +*/ +char * +sp_strip(s) +register char *s; +{ + register char *cp; + + if (s == NULL) + return(NULL); + + if (*s == '\0') + return(s); + + cp = &s[strlen(s) - 1]; + while(cp > s && isspace(*cp)) + cp--; + + *++cp = '\0'; /* zap trailing spaces */ + + for(cp = s; *cp && isspace(*cp); cp++) + continue; + + return(cp); /* return pointer to first non-space */ +} + +/* +** crack an RFC822 from header field into address and fullname. +*/ +crackfrom(addr,name,field) +char *addr, *name, *field; +{ + char commbuf[LBUFLEN]; + char addrbuf[LBUFLEN]; + register char *p; + register char *ap = addrbuf; + register char *np; + short commfound = 0, comment = 0; + short addrfound = 0, address = 0; + struct llist comm, *cp = &comm; + + *name = '\0'; /* just make sure */ + *addr = '\0'; + + if ((p = field) == NULL) + return(FALSE); + + while(*p && isspace(*p)) /* eat leading white space */ + p++; + + while(*p) { + switch(*p) { + case '(': + if (comment == 0) { + np = commbuf; + *np = '\0'; + } + comment++; + break; + case ')': + if (comment > 0 && --comment == 0) { + *np++ = *p++; /* note incr; skip `)' */ + *np++ = '\0'; + if ((cp = l_alloc(cp, commbuf, strlen(commbuf) + 1)) == NULL) { + if (commfound) + l_free(comm); + return(FALSE); + } + commfound++; + np = NULL; + continue; + } + break; + case '<': + if (address) { + if (commfound) + l_free(comm); + return(FALSE); /* AWK! Abort! */ + } + if (!comment) { + address++; + *ap = '\0'; + ap = addr; + } + break; + case '>': + if (!comment && address) { + address--; + addrfound++; + *ap = '\0'; + ap = &addrbuf[strlen(addrbuf)]; + p++; /* skip the `>' */ + } + break; + } + + if (comment) { + *np++ = *p; + } else if (address) { + if (*p != '<') + *ap++ = *p; + } else { + *ap++ = *p; + } + if (*p) + p++; + else + break; + } + + *ap++ = '\0'; + + if (addrfound) { + (void) strcpy(name, sp_strip(addrbuf)); + (void) strcpy(addr, strcpy(commbuf, sp_strip(addr))); + } else { + (void) strcpy(addr, sp_strip(addrbuf)); + *name = '\0'; + } + /* + ** Just to be sure that we got the full name, + ** we'll take all of the comments! + */ + if (commfound) { + register int len; + register int flag = (*name != '\0' ? TRUE : FALSE); + + for(cp = &comm; cp->l_item; cp = cp->l_next) { + if (flag) + (void)strcat(name, ", "); + flag = TRUE; + if (cp->l_item[cp->l_len - 2] == ')') + cp->l_item[cp->l_len - 2] = '\0'; + (void)strcat(name, sp_strip(&cp->l_item[1])); + } + l_free(comm); + } + return(TRUE); +} + +/* +** Check on the validity of an RFC822 message-id. +** Check for enclosing `<>', an `@', and a `.' after +** the `@'. Also make sure that everything is ASCII, +** and non-control characters. +*/ +msgid_ok(id) +register char *id; +{ + register atdot = FALSE; + register closure = FALSE; + + if (id == NULL) + return(FALSE); /* don't waste my time! */ + + if (*id != '<') + return(FALSE); + + /* skip the first `<', cause we check for more */ + for(id++; *id; id++) { + switch(*id) { + case '<': + return(FALSE); /* we've already got one */ + case '>': + closure = TRUE; + break; + case '.': + case '@': /* should be a domain spec */ + atdot = TRUE; + break; + default: + if (!isascii(*id) || iscntrl(*id) || isspace(*id)) + return(FALSE); /* quit immediately */ + break; + } + } + return(atdot && closure); +} + +/* +** hfgets is like fgets, but deals with continuation lines. +** It also ensures that even if a line that is too long is +** received, the remainder of the line is thrown away +** instead of treated like a second line. +*/ +char * +hfgets(buf, len, fp) +char *buf; +int len; +FILE *fp; +{ + register int c; + register int n = 0; + register char *cp; + + cp = buf; + while (n < len && (c = getc(fp)) != EOF) { + if (c == '\n') + break; + if (!iscntrl(c) || c == '\b' || c == '\t') { + *cp++ = c; + n++; + } + } + if (c == EOF && cp == buf) + return NULL; + *cp = '\0'; + + if (c != '\n') { + /* Line too long - part read didn't fit into a newline */ + while ((c = getc(fp)) != '\n' && c != EOF) + ; + } else if (cp == buf) { + /* Don't look for continuation of blank lines */ + *cp++ = '\n'; + *cp = '\0'; + return buf; + } + + while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */ + /* Continuation line. */ + if ((n += 2) < len) { + *cp++ = '\n'; + *cp++ = c; + } + while ((c = getc(fp)) != '\n' && c != EOF) + if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len) + *cp++ = c; + } + if (n >= len - 1) + cp = buf + len - 2; + *cp++ = '\n'; + *cp = '\0'; + if (c != EOF) + (void) ungetc(c, fp); /* push back first char of next header */ + return buf; +} + +/* + * arpadate is like ctime(3) except that the time is returned in + * an acceptable ARPANET time format instead of ctime format. + */ +char * +arpadate(longtime) +time_t *longtime; +{ + register char *p, *q, *ud; + register int i; + static char b[40]; + extern struct tm *gmtime(); + extern char *asctime(); + + /* Get current time. This will be used resolve the timezone. */ + ud = asctime(gmtime(longtime)); + + /* Crack the UNIX date line in a singularly unoriginal way. */ + q = b; + +#ifdef notdef +/* until every site installs the fix to getdate.y, the day + of the week can cause time warps */ + p = &ud[0]; /* Mon */ + *q++ = *p++; + *q++ = *p++; + *q++ = *p++; + *q++ = ','; *q++ = ' '; +#endif + + p = &ud[8]; /* 16 */ + if (*p == ' ') + p++; + else + *q++ = *p++; + *q++ = *p++; *q++ = ' '; + + p = &ud[4]; /* Sep */ + *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' '; + + p = &ud[22]; /* 1979 */ + *q++ = *p++; *q++ = *p++; *q++ = ' '; + + p = &ud[11]; /* 01:03:52 */ + for (i = 8; i > 0; i--) + *q++ = *p++; + + *q++ = ' '; + *q++ = 'G'; /* GMT */ + *q++ = 'M'; + *q++ = 'T'; + *q = '\0'; + + return b; +} + +time_t +cgtdate(datestr) +char *datestr; +{ + char junk[40], month[40], day[30], tod[60], year[50], buf[BUFLEN]; + static time_t lasttime; + static char lastdatestr[BUFLEN] = ""; + extern time_t getdate(); + + if (lastdatestr[0] && strcmp(datestr, lastdatestr) == 0) + return(lasttime); + lasttime = getdate(datestr, (struct timeb *)NULL); + if (lasttime < 0 && + sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) { + (void) sprintf(buf, "%s %s, %s %s", month, day, year, tod); + lasttime = getdate(buf, (struct timeb *)NULL); + } + strncpy(lastdatestr, datestr, BUFLEN); + return(lasttime); +} + +char * +errmsg(code) +int code; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + static char ebuf[6+5+1]; + + if (code > sys_nerr) { + (void) sprintf(ebuf, "Error %d", code); + return ebuf; + } else + return sys_errlist[code]; +} + +/* + * Strip trailing newlines, blanks, and tabs from 's'. + * Return TRUE if newline was found, else FALSE. + */ +nstrip(s) +register char *s; +{ + register char *p; + register int rc; + + rc = FALSE; + p = s; + while (*p) + if (*p++ == '\n') + rc = TRUE; + while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t')); + *++p = '\0'; + return rc; +} + +prefix(full, pref) +register char *full, *pref; +{ + register char fc, pc; + + while ((pc = *pref++) != '\0') { + fc = *full++; + if (isupper(fc)) + fc = tolower(fc); + if (isupper(pc)) + pc = tolower(pc); + if (fc != pc) + return FALSE; + } + return TRUE; +} + +#ifndef USG +char * +strpbrk(str, chars) +register char *str, *chars; +{ + register char *cp; + + do { + cp = chars - 1; + while (*++cp) { + if (*str == *cp) + return str; + } + } while (*str++); + return NULL; +} +#endif /* !USG */ diff --git a/usr/src/new/nntp/xmit/shlock.c b/usr/src/new/nntp/xmit/shlock.c new file mode 100644 index 0000000000..e9594182b3 --- /dev/null +++ b/usr/src/new/nntp/xmit/shlock.c @@ -0,0 +1,244 @@ +/* +** Program to produce reliable locks for shell scripts. +** Algorithmn suggested by Peter Honeyman, January 1984, in connection +** with HoneyDanBer UUCP. +** +** Erik E. Fair +*/ + +#include +#include +#include + +#define LOCK_SET 0 +#define LOCK_FAIL 1 + +#define TRUE 1 +#define FALSE 0 + +int Verbose = FALSE; +char *Pname; +char *USAGE = "%s: USAGE: shlock -f file -p pid [-v]\n"; +char *errmsg(); +char *tmpfile(); + +#define vprintf if (Verbose) printf + +extern int errno; +extern char *rindex(); +extern char *strcpy(); +extern char *strcat(); + +main(ac,av) +int ac; +char *av[]; +{ + register int x; + char *file; + int pid; + + Pname = ((Pname = rindex(av[0], '/')) ? Pname + 1 : av[0]); + + for(x = 1; x < ac; x++) { + if (av[x][0] == '-') { + switch(av[x][1]) { + case 'v': + Verbose = TRUE; + break; + case 'p': + if (strlen(av[x]) > 2) { + pid = atoi(&av[x][2]); + } else { + pid = atoi(av[x + 1]); + x++; + } + break; + case 'f': + if (strlen(av[x]) > 2) { + file = &av[x][2]; + } else { + file = av[x + 1]; + x++; + } + break; + default: + fprintf(stderr, USAGE, Pname); + exit(LOCK_FAIL); + } + } + } + if (pid == 0 || file == (char *)NULL) { + fprintf(stderr, USAGE, Pname); + exit(LOCK_FAIL); + } + if (mklock(file, pid)) + exit(LOCK_SET); + exit(LOCK_FAIL); +} + +mklock(file, pid) +char *file; +int pid; +{ + register int fd; + register int len; + register char *tmp = tmpfile(file); + char *buf[BUFSIZ]; + + vprintf("%s: attempting to get lock <%s> for process %d\n", Pname, file, pid); + sprintf(buf, "%d\n", pid); + len = strlen(buf); +loop: + if ((fd = open(tmp, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) { + switch(errno) { + case EEXIST: + vprintf("%s: temporary file %s exists already.\n", Pname, tmp); + if (unlink(tmp) < 0) { + fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno)); + return(FALSE); + } + /* + ** I hereby profane the god of structured programming + ** Edsgar Djikstra + */ + goto loop; + default: + fprintf(stderr,"%s: open(%s): %s\n", Pname, tmp, errmsg(errno)); + return(FALSE); + } + } + + /* + ** Write the PID into the temporary file before attempting to link + ** to the actual lock file. That way we have a valid lock the instant + ** the link succeeds. + */ + if (write(fd, buf, len) < 0) { + fprintf(stderr, "%s: write(%s,%d): %s\n", Pname, tmp, pid, errmsg(errno)); + close(fd); + return(FALSE); + } + close(fd); + +loop2: + if (link(tmp, file) < 0) { + switch(errno) { + case EEXIST: + vprintf("%s: lock <%s> already exists\n", Pname, file); + if (cklock(file)) { + vprintf("%s: extant lock is valid\n", Pname); + if (unlink(tmp) < 0) { + fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno)); + } + return(FALSE); + } else { + vprintf("%s: extant lock is invalid, removing\n", Pname); + if (unlink(file) < 0) { + fprintf(stderr,"%s: unlink(%s): %s\n", Pname, file, errmsg(errno)); + return(FALSE); + } + } + goto loop2; + default: + fprintf(stderr,"%s: link(%s, %s): %s\n", Pname, tmp, file, errmsg(errno)); + return(FALSE); + } + } + if (unlink(tmp) < 0) { + fprintf(stderr,"%s: unlink(%s): %s\n", Pname, tmp, errmsg(errno)); + } + vprintf("%s: got lock <%s>\n", Pname, file); + return(TRUE); +} + +/* +** Check the validity of an existing lock file. +** +** Read the PID out of the lock +** Send a null signal to determine whether that PID still exists +** Existence (or not) determines the validity of the lock. +** +** Two bigs wins to this algorithmn: +** +** o Locks do not survive crashes of either the system or the +** application by any appreciable period of time. +** +** o No clean up to do if the system or application crashes. +** +*/ + +cklock(file) +char *file; +{ + register int fd = open(file, O_RDONLY); + register int len; + char buf[BUFSIZ]; + + vprintf("%s: checking extant lock <%s>\n", Pname, file); + if (fd < 0) { + fprintf(stderr,"%s: open(%s): %s\n", Pname, file, errmsg(errno)); + return(TRUE); /* might or might not; conservatism */ + } + + if ((len = read(fd, buf, sizeof(buf))) <= 0) { + close(fd); + vprintf("%s: lock file format error\n", Pname); + return(FALSE); + } + close(fd); + buf[len + 1] = '\0'; + return(p_exists(atoi(buf))); +} + +/* +** Does the PID exist? +** Send null signal to find out. +*/ + +p_exists(pid) +int pid; +{ + vprintf("%s: locking process %d is ", Pname, pid); + if (kill(pid, 0) < 0) { + switch(errno) { + case ESRCH: + vprintf("dead\n"); + return(FALSE); /* pid does not exist */ + case EPERM: + vprintf("alive\n"); + return(TRUE); /* pid exists */ + default: + vprintf("state unknown: %s\n", errmsg(errno)); + return(TRUE); /* be conservative */ + } + } + vprintf("alive\n"); + return(TRUE); /* pid exists */ +} + +char * +errmsg(n) +register int n; +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + return((n >= 0 && n < sys_nerr) ? sys_errlist[n] : "unknown error"); +} + +char * +tmpfile(file) +char *file; +{ + register char *cp; + static char buf[BUFSIZ]; + char tempname[BUFSIZ]; + + if ((cp = rindex(strcpy(buf, file), '/')) != (char *)NULL) { + *(cp + 1) = '\0'; + } else + buf[0] = '\0'; + sprintf(tempname, "shlock%d", getpid()); + vprintf("%s: temporary filename: %s\n", Pname, tempname); + return(strcat(buf, tempname)); +} -- 2.20.1