HardenedBSD/usr.sbin/named/db_dump.c
Peter Wemm ac8318a368 Merge in delta from 4.9.3.b9 -> 4.9.3.b24.
Much of the "-j" merge was clean, except for $Id$ and other trivial changes.
1995-08-20 21:19:18 +00:00

908 lines
23 KiB
C

#if !defined(lint) && !defined(SABER)
static char sccsid[] = "@(#)db_dump.c 4.33 (Berkeley) 3/3/91";
static char rcsid[] = "$Id: db_dump.c,v 1.3 1995/05/30 03:48:35 rgrimes Exp $";
#endif /* not lint */
/*
* ++Copyright++ 1986, 1988, 1990
* -
* Copyright (c) 1986, 1988, 1990
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
* -
* --Copyright--
*/
#include <sys/param.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netiso/iso.h>
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <resolv.h>
#include <errno.h>
#include "named.h"
static int scan_root __P((struct hashbuf *));
static const char *MkCredStr __P((int));
#ifdef ALLOW_T_UNSPEC
static void putbyte __P((int, char **));
#endif
/*
* Dump current cache in a format similar to RFC 883.
*
* We try to be careful and determine whether the operation succeeded
* so that the new cache file can be installed.
*/
void
doachkpt()
{
FILE *fp;
char tmpcheckfile[256];
/* nowhere to checkpoint cache... */
if (cache_file == NULL) {
dprintf(3, (ddt, "skipping doachkpt (cache_file == NULL)\n"));
return;
}
dprintf(3, (ddt, "doachkpt()\n"));
(void) sprintf(tmpcheckfile, "%s.chk", cache_file);
if ((fp = fopen(tmpcheckfile, "w")) == NULL) {
dprintf(3, (ddt,
"doachkpt(can't open %s for write)\n", tmpcheckfile));
return;
}
(void) gettime(&tt);
fprintf(fp, "; Dumped at %s", ctimel(tt.tv_sec));
fflush(fp);
if (ferror(fp)) {
dprintf(3, (ddt, "doachkpt(write to checkpoint file failed)\n"));
return;
}
if (fcachetab != NULL) {
int n = scan_root(hashtab);
if (n < MINROOTS) {
syslog(LOG_NOTICE, "%d root hints... (too low)", n);
fprintf(fp, "; ---- Root hint cache dump ----\n");
(void) db_dump(fcachetab, fp, DB_Z_CACHE, "");
}
}
if (hashtab != NULL) {
fprintf(fp, "; ---- Cache dump ----\n");
if (db_dump(hashtab, fp, DB_Z_CACHE, "") == NODBFILE) {
dprintf(3, (ddt, "doachkpt(checkpoint failed)\n"));
(void) my_fclose(fp);
return;
}
}
if (my_fclose(fp) == EOF) {
return;
}
if (rename(tmpcheckfile, cache_file)) {
dprintf(3, (ddt, "doachkpt(install %s to %s failed, %d)\n",
tmpcheckfile, cache_file, errno));
}
}
/*
* What we do is scan the root hint cache to make sure there are at least
* MINROOTS root pointers with non-0 TTL's so that the checkpoint will not
* lose the root. Failing this, all pointers are written out w/ TTL ~0
* (root pointers timed out and prime_cache() not done or failed).
*/
static int
scan_root(htp)
struct hashbuf *htp;
{
register struct databuf *dp;
register struct namebuf *np;
struct timeval soon;
int roots = 0;
dprintf(1, (ddt, "scan_root(0x%lx)\n", (u_long)htp));
/* metric by which we determine whether a root NS pointer is still */
/* valid (will be written out if we do a dump). we also add some */
/* time buffer for safety... */
(void) gettime(&soon);
soon.tv_sec += TIMBUF;
for (np = htp->h_tab[0]; np != NULL; np = np->n_next) {
if (np->n_dname[0] == '\0') {
dp = np->n_data;
while (dp != NULL) {
if (dp->d_type == T_NS &&
dp->d_ttl > soon.tv_sec) {
roots++;
if (roots >= MINROOTS)
return (roots);
}
dp = dp->d_next;
}
}
}
return (roots);
}
#ifdef notdef
mark_cache(htp, ttl)
struct hashbuf *htp;
int ttl;
{
register struct databuf *dp;
register struct namebuf *np;
struct namebuf **npp, **nppend;
struct timeval soon;
dprintf(1, (ddt, "mark_cache()\n"));
(void) gettime(&soon);
soon.tv_sec += TIMBUF;
npp = htp->h_tab;
nppend = npp + htp->h_size;
while (npp < nppend) {
for (np = *npp++; np != NULL; np = np->n_next) {
if (np->n_data == NULL)
continue;
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
if (dp->d_ttl < soon.tv_sec)
dp->d_ttl = ttl;
}
}
}
npp = htp->h_tab;
nppend = npp + htp->h_size;
while (npp < nppend) {
for (np = *npp++; np != NULL; np = np->n_next) {
if (np->n_hash == NULL)
continue;
mark_cache(np->n_hash, ttl);
}
}
}
#endif /* notdef */
/*
* Dump current data base in a format similar to RFC 883.
*/
void
doadump()
{
FILE *fp;
dprintf(3, (ddt, "doadump()\n"));
syslog(LOG_NOTICE, "dumping nameserver data\n");
if ((fp = fopen(dumpfile, "w")) == NULL)
return;
gettime(&tt);
fprintf(fp, "; Dumped at %s", ctimel(tt.tv_sec));
if (zones && nzones)
zt_dump(fp);
fputs(
"; Note: Cr=(auth,answer,addtnl,cache) tag only shown for non-auth RR's\n",
fp);
fputs(
"; Note: NT=milliseconds for any A RR which we've used as a nameserver\n",
fp);
fprintf(fp, "; --- Cache & Data ---\n");
if (hashtab != NULL)
(void) db_dump(hashtab, fp, DB_Z_ALL, "");
fprintf(fp, "; --- Hints ---\n");
if (fcachetab != NULL)
(void) db_dump(fcachetab, fp, DB_Z_ALL, "");
(void) my_fclose(fp);
syslog(LOG_NOTICE, "finished dumping nameserver data\n");
}
#ifdef ALLOW_UPDATES
/* Create a disk database to back up zones
*/
void
zonedump(zp)
register struct zoneinfo *zp;
{
FILE *fp;
char *fname;
struct hashbuf *htp;
char *op;
struct stat st;
/* Only dump zone if there is a cache specified */
if (zp->z_source && *(zp->z_source)) {
dprintf(1, (ddt, "zonedump(%s)\n", zp->z_source));
if ((fp = fopen(zp->z_source, "w")) == NULL)
return;
if (op = strchr(zp->z_origin, '.'))
op++;
gettime(&tt);
htp = hashtab;
if (nlookup(zp->z_origin, &htp, &fname, 0) != NULL) {
db_dump(htp, fp, zp-zones, (op == NULL ? "" : op));
zp->z_flags &= ~Z_CHANGED; /* Checkpointed */
}
(void) my_fclose(fp);
if (stat(zp->z_source, &st) == 0)
zp->z_ftime = st.st_mtime;
} else {
dprintf(1, (ddt, "zonedump: no zone to dump\n"));
}
}
#endif
int
zt_dump(fp)
FILE *fp;
{
register struct zoneinfo *zp;
fprintf(fp, ";; ++zone table++\n");
for (zp = &zones[1]; zp < &zones[nzones]; zp++) {
char *pre, buf[64];
u_int cnt;
fprintf(fp, "; %s (type %d, class %d, source %s)\n",
zp->z_origin, zp->z_type, zp->z_class,
zp->z_source ? zp->z_source : "Nil");
fprintf(fp, ";\ttime=%ld, lastupdate=%ld, serial=%u,\n",
zp->z_time, zp->z_lastupdate, zp->z_serial);
fprintf(fp, ";\trefresh=%u, retry=%u, expire=%u, minimum=%u\n",
zp->z_refresh, zp->z_retry,
zp->z_expire, zp->z_minimum);
fprintf(fp, ";\tftime=%ld, xaddr=[%s], state=%04x, pid=%d\n",
zp->z_ftime, inet_ntoa(zp->z_xaddr),
zp->z_flags, (int)zp->z_xferpid);
sprintf(buf, ";\tz_addr[%d]: ", zp->z_addrcnt);
pre = buf;
for (cnt = 0; cnt < zp->z_addrcnt; cnt++) {
fprintf(fp, "%s[%s]", pre, inet_ntoa(zp->z_addr[cnt]));
pre = ", ";
}
if (zp->z_addrcnt)
fputc('\n', fp);
#ifdef BIND_NOTIFY
if (zp->z_notifylist) {
register struct notify *ap;
for (ap = zp->z_notifylist; ap; ap = ap->next)
fprintf(fp, ";\tNotify [%s] %s",
inet_ntoa(ap->addr),
ctime(&ap->last));
}
#endif
}
fprintf(fp, ";; --zone table--\n");
return (0);
}
int
db_dump(htp, fp, zone, origin)
struct hashbuf *htp;
FILE *fp;
int zone;
char *origin;
{
register struct databuf *dp = NULL;
register struct namebuf *np;
struct namebuf **npp, **nppend;
char dname[MAXDNAME];
u_int32_t n;
u_int32_t addr;
int j, i;
struct iso_addr isoa;
register u_char *cp;
u_char *end;
char *proto, *sep;
int found_data = 0, tab, printed_origin = 0;
npp = htp->h_tab;
nppend = npp + htp->h_size;
while (npp < nppend) {
for (np = *npp++; np != NULL; np = np->n_next) {
if (np->n_data == NULL)
continue;
/* Blecch - can't tell if there is data here for the
* right zone, so can't print name yet
*/
found_data = 0;
/* we want a snapshot in time... */
for (dp = np->n_data; dp != NULL; dp = dp->d_next) {
/* Is the data for this zone? */
if (zone != DB_Z_ALL && dp->d_zone != zone)
continue;
if (dp->d_zone == DB_Z_CACHE &&
dp->d_ttl <= tt.tv_sec &&
(dp->d_flags & DB_F_HINT) == 0)
continue;
if (!printed_origin) {
fprintf(fp, "$ORIGIN %s.\n", origin);
printed_origin++;
}
tab = 0;
#ifdef NCACHE
if (dp->d_rcode == NXDOMAIN ||
dp->d_rcode == NOERROR_NODATA) {
fputc(';', fp);
} else if (found_data == 0 || found_data == 1) {
found_data = 2;
}
#endif /*NCACHE*/
if (found_data == 0 || found_data == 2) {
if (np->n_dname[0] == 0) {
if (origin[0] == 0)
fprintf(fp, ".\t");
else
fprintf(fp, ".%s.\t", origin); /* ??? */
} else
fprintf(fp, "%s\t", np->n_dname);
if (strlen(np->n_dname) < (size_t)8)
tab = 1;
found_data++;
} else {
(void) putc('\t', fp);
tab = 1;
}
if (dp->d_zone == DB_Z_CACHE) {
if (dp->d_flags & DB_F_HINT
&& (int32_t)(dp->d_ttl - tt.tv_sec)
< DB_ROOT_TIMBUF)
fprintf(fp, "%d\t", DB_ROOT_TIMBUF);
else
fprintf(fp, "%d\t",
(int)(dp->d_ttl - tt.tv_sec));
} else if (dp->d_ttl != 0 &&
dp->d_ttl != zones[dp->d_zone].z_minimum)
fprintf(fp, "%d\t", (int)dp->d_ttl);
else if (tab)
(void) putc('\t', fp);
fprintf(fp, "%s\t%s\t",
p_class(dp->d_class),
p_type(dp->d_type));
cp = (u_char *)dp->d_data;
sep = "\t;";
#ifdef NCACHE
#ifdef RETURNSOA
if (dp->d_rcode == NOERROR_NODATA) {
fprintf(fp, "NODATA%s-$", sep);
goto eoln;
}
#else
if (dp->d_rcode == NXDOMAIN ||
dp->d_rcode == NOERROR_NODATA) {
fprintf(fp, "%s%s-$",
(dp->d_rcode == NXDOMAIN)
?"NXDOMAIN" :"NODATA",
sep);
goto eoln;
}
#endif
#endif
/*
* Print type specific data
*/
switch (dp->d_type) {
case T_A:
switch (dp->d_class) {
case C_IN:
case C_HS:
GETLONG(n, cp);
n = htonl(n);
fputs(inet_ntoa(*(struct in_addr *)&n),
fp);
break;
}
if (dp->d_nstime) {
fprintf(fp, "%sNT=%d",
sep, dp->d_nstime);
sep = " ";
}
break;
case T_CNAME:
case T_MB:
case T_MG:
case T_MR:
case T_PTR:
fprintf(fp, "%s.", cp);
break;
case T_NS:
cp = (u_char *)dp->d_data;
if (cp[0] == '\0')
fprintf(fp, ".\t");
else
fprintf(fp, "%s.", cp);
break;
case T_HINFO:
case T_ISDN:
if (n = *cp++) {
fprintf(fp, "\"%.*s\"", (int)n, cp);
cp += n;
} else
fprintf(fp, "\"\"");
if (n = *cp++)
fprintf(fp, " \"%.*s\"", (int)n, cp);
else
fprintf(fp, " \"\"");
break;
case T_SOA:
fprintf(fp, "%s.", cp);
cp += strlen((char *)cp) + 1;
fprintf(fp, " %s. (\n", cp);
#if defined(RETURNSOA) && defined(NCACHE)
if (dp->d_rcode == NXDOMAIN)
fputs(";", fp);
#endif
cp += strlen((char *)cp) + 1;
GETLONG(n, cp);
fprintf(fp, "\t\t%lu", (u_long)n);
GETLONG(n, cp);
fprintf(fp, " %lu", (u_long)n);
GETLONG(n, cp);
fprintf(fp, " %lu", (u_long)n);
GETLONG(n, cp);
fprintf(fp, " %lu", (u_long)n);
GETLONG(n, cp);
fprintf(fp, " %lu )", (u_long)n);
#if defined(RETURNSOA) && defined(NCACHE)
if (dp->d_rcode == NXDOMAIN) {
fprintf(fp,";%s.;NXDOMAIN%s-$",cp,sep);
}
#endif
break;
case T_MX:
case T_AFSDB:
case T_RT:
GETSHORT(n, cp);
fprintf(fp, "%lu", (u_long)n);
fprintf(fp, " %s.", cp);
break;
case T_PX:
GETSHORT(n, cp);
fprintf(fp, "%lu", (u_long)n);
fprintf(fp, " %s.", cp);
cp += strlen((char *)cp) + 1;
fprintf(fp, " %s.", cp);
break;
case T_TXT:
case T_X25:
end = (u_char *)dp->d_data + dp->d_size;
(void) putc('"', fp);
while (cp < end) {
if (n = *cp++) {
for (j = n ; j > 0 && cp < end ; j--)
if (*cp == '\n') {
(void) putc('\\', fp);
(void) putc(*cp++, fp);
} else
(void) putc(*cp++, fp);
}
}
(void) fputs("\"", fp);
break;
case T_NSAP:
isoa.isoa_len = dp->d_size;
if (isoa.isoa_len > sizeof(isoa.isoa_genaddr))
isoa.isoa_len = sizeof(isoa.isoa_genaddr);
bcopy(dp->d_data, isoa.isoa_genaddr,
isoa.isoa_len);
(void) fputs(iso_ntoa(&isoa), fp);
break;
#ifdef LOC_RR
case T_LOC:
(void) fputs(loc_ntoa(dp->d_data, NULL), fp);
break;
#endif /* LOC_RR */
case T_UINFO:
fprintf(fp, "\"%s\"", cp);
break;
case T_UID:
case T_GID:
if (dp->d_size == INT32SZ) {
GETLONG(n, cp);
} else {
n = -2; /* XXX - hack */
}
fprintf(fp, "%u", n);
break;
case T_WKS:
GETLONG(addr, cp);
addr = htonl(addr);
fputs(inet_ntoa(*(struct in_addr *)&addr), fp);
proto = protocolname(*cp);
cp += sizeof(char);
fprintf(fp, "%s ", proto);
i = 0;
while(cp < (u_char *)dp->d_data + dp->d_size) {
j = *cp++;
do {
if (j & 0200)
fprintf(fp, " %s",
servicename(i, proto));
j <<= 1;
} while (++i & 07);
}
break;
case T_MINFO:
case T_RP:
fprintf(fp, "%s.", cp);
cp += strlen((char *)cp) + 1;
fprintf(fp, " %s.", cp);
break;
#ifdef ALLOW_T_UNSPEC
case T_UNSPEC:
/* Dump binary data out in an ASCII-encoded
format */
{
/* Allocate more than enough space:
* actually need 5/4 size + 20 or so
*/
int TmpSize = 2 * dp->d_size + 30;
char *TmpBuf = (char *) malloc(TmpSize);
if (TmpBuf == NULL) {
TmpBuf = "BAD_MALLOC";
}
if (btoa(cp, dp->d_size, TmpBuf, TmpSize)
== CONV_OVERFLOW) {
TmpBuf = "OVERFLOW";
}
fprintf(fp, "%s", TmpBuf);
}
break;
#endif /* ALLOW_T_UNSPEC */
default:
fprintf(fp, "%s?d_type=%d?",
sep, dp->d_type);
sep = " ";
}
if (dp->d_cred < DB_C_ZONE) {
fprintf(fp, "%sCr=%s",
sep, MkCredStr(dp->d_cred));
sep = " ";
} else {
fprintf(fp, "%sCl=%d",
sep, dp->d_clev);
sep = " ";
}
eoln:
#ifdef STATS
if (dp->d_ns) {
fprintf(fp, "%s[%s]",
sep, inet_ntoa(dp->d_ns->addr));
sep = " ";
}
#endif
putc('\n', fp);
}
}
}
if (ferror(fp))
return(NODBFILE);
npp = htp->h_tab;
nppend = npp + htp->h_size;
while (npp < nppend) {
for (np = *npp++; np != NULL; np = np->n_next) {
if (np->n_hash == NULL)
continue;
getname(np, dname, sizeof(dname));
if (db_dump(np->n_hash, fp, zone, dname) == NODBFILE)
return(NODBFILE);
}
}
return(OK);
}
static const char *
MkCredStr(cred)
int cred;
{
static char badness[20];
switch (cred) {
case DB_C_ZONE: return "zone";
case DB_C_AUTH: return "auth";
case DB_C_ANSWER: return "answer";
case DB_C_ADDITIONAL: return "addtnl";
case DB_C_CACHE: return "cache";
default: break;
}
sprintf(badness, "?%d?", cred);
return (badness);
}
#ifdef ALLOW_T_UNSPEC
/*
* Subroutines to convert between 8 bit binary bytes and printable ASCII.
* Computes the number of bytes, and three kinds of simple checksums.
* Incoming bytes are collected into 32-bit words, then printed in base 85:
* exp(85,5) > exp(2,32)
* The ASCII characters used are between '!' and 'u';
* 'z' encodes 32-bit zero; 'x' is used to mark the end of encoded data.
*
* Originally by Paul Rutter (philabs!per) and Joe Orost (petsd!joe) for
* the atob/btoa programs, released with the compress program, in mod.sources.
* Modified by Mike Schwartz 8/19/86 for use in BIND.
*/
/* Make sure global variable names are unique */
#define Ceor T_UNSPEC_Ceor
#define Csum T_UNSPEC_Csum
#define Crot T_UNSPEC_Crot
#define word T_UNSPEC_word
#define bcount T_UNSPEC_bcount
static int32_t Ceor, Csum, Crot, word, bcount;
#define EN(c) ((int) ((c) + '!'))
#define DE(c) ((c) - '!')
#define AddToBuf(bufp, c) **bufp = c; (*bufp)++;
#define times85(x) ((((((x<<2)+x)<<2)+x)<<2)+x)
/* Decode ASCII-encoded byte c into binary representation and
* place into *bufp, advancing bufp
*/
static int
byte_atob(c, bufp)
register c;
char **bufp;
{
if (c == 'z') {
if (bcount != 0)
return(CONV_BADFMT);
else {
putbyte(0, bufp);
putbyte(0, bufp);
putbyte(0, bufp);
putbyte(0, bufp);
}
} else if ((c >= '!') && (c < ('!' + 85))) {
if (bcount == 0) {
word = DE(c);
++bcount;
} else if (bcount < 4) {
word = times85(word);
word += DE(c);
++bcount;
} else {
word = times85(word) + DE(c);
putbyte((int)((word >> 24) & 255), bufp);
putbyte((int)((word >> 16) & 255), bufp);
putbyte((int)((word >> 8) & 255), bufp);
putbyte((int)(word & 255), bufp);
word = 0;
bcount = 0;
}
} else
return(CONV_BADFMT);
return(CONV_SUCCESS);
}
/* Compute checksum info and place c into *bufp, advancing bufp */
static void
putbyte(c, bufp)
register c;
char **bufp;
{
Ceor ^= c;
Csum += c;
Csum += 1;
if ((Crot & 0x80000000)) {
Crot <<= 1;
Crot += 1;
} else {
Crot <<= 1;
}
Crot += c;
AddToBuf(bufp, c);
}
/* Read the ASCII-encoded data from inbuf, of length inbuflen, and convert
it into T_UNSPEC (binary data) in outbuf, not to exceed outbuflen bytes;
outbuflen must be divisible by 4. (Note: this is because outbuf is filled
in 4 bytes at a time. If the actual data doesn't end on an even 4-byte
boundary, there will be no problem...it will be padded with 0 bytes, and
numbytes will indicate the correct number of bytes. The main point is
that since the buffer is filled in 4 bytes at a time, even if there is
not a full 4 bytes of data at the end, there has to be room to 0-pad the
data, so the buffer must be of size divisible by 4). Place the number of
output bytes in numbytes, and return a failure/success status */
int
atob(inbuf, inbuflen, outbuf, outbuflen, numbytes)
char *inbuf;
int inbuflen;
char *outbuf;
int outbuflen;
int *numbytes;
{
int inc, nb;
int32_t oeor, osum, orot;
char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen];
if ( (outbuflen % 4) != 0)
return(CONV_BADBUFLEN);
Ceor = Csum = Crot = word = bcount = 0;
for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) {
if (outp > endoutp)
return(CONV_OVERFLOW);
if (*inp == 'x') {
inp +=2;
break;
} else {
if (byte_atob(*inp, &outp) == CONV_BADFMT)
return(CONV_BADFMT);
}
}
/* Get byte count and checksum information from end of buffer */
if(sscanf(inp, "%ld %lx %lx %lx", numbytes, &oeor, &osum, &orot) != 4)
return(CONV_BADFMT);
if ((oeor != Ceor) || (osum != Csum) || (orot != Crot))
return(CONV_BADCKSUM);
return(CONV_SUCCESS);
}
/* Encode binary byte c into ASCII representation and place into *bufp,
advancing bufp */
static void
byte_btoa(c, bufp)
register c;
char **bufp;
{
Ceor ^= c;
Csum += c;
Csum += 1;
if ((Crot & 0x80000000)) {
Crot <<= 1;
Crot += 1;
} else {
Crot <<= 1;
}
Crot += c;
word <<= 8;
word |= c;
if (bcount == 3) {
if (word == 0) {
AddToBuf(bufp, 'z');
} else {
register int tmp = 0;
register int32_t tmpword = word;
if (tmpword < 0) {
/* Because some don't support unsigned long */
tmp = 32;
tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32);
}
if (tmpword < 0) {
tmp = 64;
tmpword -= (int32_t)(85 * 85 * 85 * 85 * 32);
}
AddToBuf(bufp,
EN((tmpword / (int32_t)(85 * 85 * 85 * 85)) + tmp));
tmpword %= (int32_t)(85 * 85 * 85 * 85);
AddToBuf(bufp, EN(tmpword / (85 * 85 * 85)));
tmpword %= (85 * 85 * 85);
AddToBuf(bufp, EN(tmpword / (85 * 85)));
tmpword %= (85 * 85);
AddToBuf(bufp, EN(tmpword / 85));
tmpword %= 85;
AddToBuf(bufp, EN(tmpword));
}
bcount = 0;
} else {
bcount += 1;
}
}
/*
* Encode the binary data from inbuf, of length inbuflen, into a
* null-terminated ASCII representation in outbuf, not to exceed outbuflen
* bytes. Return success/failure status
*/
static int
btoa(inbuf, inbuflen, outbuf, outbuflen)
char *inbuf;
int inbuflen;
char *outbuf;
int outbuflen;
{
int32_t inc, nb;
int32_t oeor, osum, orot;
char *inp, *outp = outbuf, *endoutp = &outbuf[outbuflen -1];
Ceor = Csum = Crot = word = bcount = 0;
for (inp = inbuf, inc = 0; inc < inbuflen; inp++, inc++) {
byte_btoa((unsigned char) (*inp), &outp);
if (outp >= endoutp)
return(CONV_OVERFLOW);
}
while (bcount != 0) {
byte_btoa(0, &outp);
if (outp >= endoutp)
return(CONV_OVERFLOW);
}
/* Put byte count and checksum information at end of buffer, delimited
by 'x' */
(void) sprintf(outp, "x %ld %lx %lx %lx", inbuflen, Ceor, Csum, Crot);
if (&outp[strlen(outp) - 1] >= endoutp)
return(CONV_OVERFLOW);
else
return(CONV_SUCCESS);
}
#endif /* ALLOW_T_UNSPEC */