mirror of https://github.com/mabam/CAP.git
504 lines
12 KiB
C
504 lines
12 KiB
C
/*
|
|
* snitch.c: a mimic of the "responder" INIT for Inter-Poll from Apple.
|
|
* Advertises itself as "machinename.UNIX/CAP@*" via NBP,
|
|
* and responds to ATP system info requests.
|
|
*
|
|
* To kill this cleanly, send the process a QUIT or TERM signal.
|
|
* It will nbp_delete itself (as all nbp services should).
|
|
*
|
|
* To really make this work with Inter-Poll, you want to add the string
|
|
* "UNIX/CAP" (or your snitchtype) to the STR# that specifies the kinds of
|
|
* expected machine types. The first item in that STR# is a number that
|
|
* indicates the number of following valid entries - add one to it and then
|
|
* add "UNIX/CAP" at the end. The string list is STR# "NIP Devices".
|
|
*
|
|
* This code takes pieces from atistest.c and efsd.c from the CAP distribution.
|
|
*
|
|
* AppleTalk package for UNIX (4.2 BSD).
|
|
*
|
|
* From atistest.c:
|
|
*
|
|
* Copyright (c) 1986,1987 by The Trustees of Columbia University in the
|
|
* City of New York.
|
|
*
|
|
*
|
|
* snitch by Rob Chandhok, Computer Science Department,
|
|
* Carnegie Mellon University
|
|
*
|
|
* Edit History:
|
|
*
|
|
* March 16,1988 chandhok Created snitch.
|
|
* March 17, 1988 cckim clean up a bit
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <netdb.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <netat/appletalk.h>
|
|
|
|
#ifdef USESTRINGDOTH /* based on sysvcompat.h in appletalk.h */
|
|
# include <string.h>
|
|
#else USESTRINGDOTH
|
|
# include <strings.h>
|
|
#endif USESTRINGDOTH
|
|
|
|
#define MAXNBPSTRING 32
|
|
|
|
/* MAXSNITCHSTRING is the max string length for a packed string in
|
|
* with interpoll (127) minus some slop
|
|
*/
|
|
|
|
#define MAXSNITCHSTRING 127
|
|
#define USER_MAXSNITCHSTRING 100
|
|
private char snitchname[MAXNBPSTRING + 1] = "";
|
|
private char snitchtype[MAXNBPSTRING + 1] = "UNIX/CAP";
|
|
private int snitchdebug = 0;
|
|
|
|
private int spawn = 0; /* disassociate process? (def. no) */
|
|
|
|
private char finderstring[USER_MAXSNITCHSTRING + 1] = "";
|
|
private char lwstring[USER_MAXSNITCHSTRING + 1] = "lwsrv";
|
|
|
|
/* saved values for snitch handler */
|
|
private char real_finderstring[MAXSNITCHSTRING + 1];
|
|
private char real_lwstring[MAXSNITCHSTRING + 1];
|
|
private char real_system[MAXSNITCHSTRING + 1];
|
|
|
|
private char fullhostname[256] = "";
|
|
private int gmypid = -1;
|
|
|
|
#ifndef kipnetnumber
|
|
/* high and low part of a "kip" network number - assume number in network */
|
|
/* format */
|
|
#define nkipnetnumber(x) ntohs((x))>>8
|
|
#define nkipsubnetnumber(x) ntohs((x))&0xff
|
|
|
|
/* same but assumes in host form */
|
|
#define kipnetnumber(x) (x)>>8
|
|
#define kipsubnetnumber(x) (x)&0xff
|
|
#endif
|
|
|
|
/* forward */
|
|
void snitch_handler();
|
|
|
|
void
|
|
cleanup()
|
|
{
|
|
int err;
|
|
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"snitch: Un-Registering \"%s:%s@*\"\n",
|
|
snitchname,snitchtype);
|
|
}
|
|
err = nbp_delete(snitchname, snitchtype, "*");
|
|
if (err != noErr)
|
|
aerror("nbp delete",err);
|
|
else
|
|
fprintf(stderr,"snitch: Bye\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
usage(name)
|
|
char *name;
|
|
{
|
|
char *DBLevelOpts();
|
|
|
|
fprintf(stderr,"usage: %s [-d cap-flags] [-n name] [-t type]\n",name);
|
|
fprintf(stderr,"\t[-f finderstring] [-l laserwriterstring] [-S]\n");
|
|
fprintf(stderr,"\t-d for CAP debugging flags:\n");
|
|
fprintf(stderr,"\t l = lap, d = ddp, a = atp, n = nbp, p = pap,");
|
|
fprintf(stderr,"i = ini, s = asp\n");
|
|
fprintf(stderr,"\t-Dn for %s debugging level n (writes to stderr)\n",name);
|
|
fprintf(stderr,"\t-S to disassociate snitch process\n");
|
|
fprintf(stderr,"\nExample: %s -n 'MyCapMachine'\n",
|
|
name);
|
|
|
|
exit(1);
|
|
}
|
|
|
|
doargs(argc,argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
int c;
|
|
extern char *optarg;
|
|
extern int optind;
|
|
|
|
while ((c = getopt(argc,argv,"n:t:f:d:D:l:S")) != EOF) {
|
|
switch (c) {
|
|
case 'n':
|
|
strncpy(snitchname, optarg, MAXNBPSTRING);
|
|
break;
|
|
case 't':
|
|
strncpy(snitchtype, optarg, MAXNBPSTRING);
|
|
break;
|
|
case 'f':
|
|
strncpy(finderstring, optarg, USER_MAXSNITCHSTRING);
|
|
break;
|
|
case 'l':
|
|
strncpy(lwstring, optarg, USER_MAXSNITCHSTRING);
|
|
break;
|
|
case 'd':
|
|
dbugarg(optarg); /* '-d' is debug */
|
|
break;
|
|
case 'S':
|
|
spawn++;
|
|
break;
|
|
case 'D':
|
|
snitchdebug = atoi(optarg);
|
|
if (!snitchdebug) snitchdebug++; /* make sure -D at least sets to 1*/
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
main(argc,argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
char *cp;
|
|
AddrBlock useaddr;
|
|
int skt, err;
|
|
ABusRecord req_abr;
|
|
atpProto *ap;
|
|
char req_buf[atpMaxData]; /* max data to receive on request */
|
|
|
|
doargs(argc,argv);
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"snitch: debugging level %d\n",snitchdebug);
|
|
}
|
|
if (!snitchdebug && spawn) {
|
|
/* disassociate */
|
|
if (fork())
|
|
_exit(0); /* kill parent */
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < 20; i++) close(i); /* kill */
|
|
(void)open("/",0);
|
|
#ifndef NODUP2
|
|
(void)dup2(0,1);
|
|
(void)dup2(0,2);
|
|
#else
|
|
(void)dup(0); /* slot 1 */
|
|
(void)dup(0); /* slot 2 */
|
|
#endif
|
|
#ifdef TIOCNOTTY
|
|
if ((i = open("/dev/tty",2)) > 0) {
|
|
(void)ioctl(i, TIOCNOTTY, (caddr_t)0);
|
|
(void)close(i);
|
|
}
|
|
#endif TIOCNOTTY
|
|
#ifdef POSIX
|
|
(void) setsid();
|
|
#endif POSIX
|
|
}
|
|
}
|
|
|
|
abInit(snitchdebug);
|
|
nbpInit();
|
|
|
|
gmypid = getpid();
|
|
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"snitch: my pid is %d\n",gmypid);
|
|
}
|
|
|
|
/* Find our host name */
|
|
gethostname(fullhostname, sizeof(fullhostname));
|
|
if (! *snitchname) {
|
|
strcpy(snitchname,fullhostname);
|
|
/* strip off any domain name */
|
|
if ((cp = index(snitchname, '.')) != NULL) *cp = 0;
|
|
}
|
|
if (! *finderstring) {
|
|
struct in_addr thishost;
|
|
|
|
if (getipaddr(fullhostname, &thishost) < 0)
|
|
sprintf(finderstring,"%s @ <unknown ip address>",fullhostname);
|
|
else
|
|
sprintf(finderstring,"%s @ %s",fullhostname,inet_ntoa(thishost));
|
|
}
|
|
/* setup snitch vars */
|
|
setup_snitch();
|
|
|
|
useaddr.net = useaddr.node = useaddr.skt = 0; /* accept from anywhere */
|
|
skt = 0; /* dynamically allocate skt please */
|
|
if ((err = ATPOpenSocket(&useaddr, &skt)) < 0) {
|
|
perror("ATP Open Socket");
|
|
aerror("ATP Open Socket",err);
|
|
exit(1);
|
|
}
|
|
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"Registering \"%s:%s@*\" on socket %d\n",
|
|
snitchname,snitchtype,skt);
|
|
}
|
|
|
|
err = nbp_register(snitchname, snitchtype, "*", skt);
|
|
if (err != noErr)
|
|
aerror("nbp register",err);
|
|
else {
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"snitch ready\n");
|
|
}
|
|
}
|
|
|
|
signal(SIGQUIT, cleanup);
|
|
signal(SIGTERM, cleanup);
|
|
|
|
ap = &req_abr.proto.atp;
|
|
do {
|
|
ap->atpSocket = skt;
|
|
ap->atpReqCount = atpMaxData;
|
|
ap->atpDataPtr = req_buf;
|
|
err = ATPGetRequest(&req_abr, FALSE);
|
|
if (err != noErr)
|
|
fprintf(stderr,"ATPGetRequest error: %d\n",err);
|
|
else
|
|
snitch_handler(&req_abr);
|
|
} while (1);
|
|
|
|
}
|
|
|
|
/*
|
|
* register the specified entity
|
|
*
|
|
*/
|
|
nbp_register(sobj, stype, szone, skt)
|
|
char *sobj, *stype, *szone;
|
|
int skt;
|
|
{
|
|
EntityName en;
|
|
nbpProto nbpr; /* nbp proto */
|
|
NBPTEntry nbpt[1]; /* table of entity names */
|
|
int err;
|
|
|
|
strcpy((char *)en.objStr.s, sobj);
|
|
strcpy((char *)en.typeStr.s, stype);
|
|
strcpy((char *)en.zoneStr.s, szone);
|
|
|
|
|
|
nbpr.nbpAddress.skt = skt;
|
|
nbpr.nbpRetransmitInfo.retransInterval = 4;
|
|
nbpr.nbpRetransmitInfo.retransCount = 3;
|
|
nbpr.nbpBufPtr = nbpt;
|
|
nbpr.nbpBufSize = sizeof(nbpt);
|
|
nbpr.nbpDataField = 1; /* max entries */
|
|
nbpr.nbpEntityPtr = &en;
|
|
|
|
err = NBPRegister(&nbpr,FALSE); /* try synchronous */
|
|
return(err);
|
|
}
|
|
|
|
/*
|
|
* delete the specified entity
|
|
*
|
|
*/
|
|
nbp_delete(sobj, stype, szone)
|
|
char *sobj, *stype, *szone;
|
|
{
|
|
EntityName en;
|
|
int err;
|
|
|
|
strcpy((char *)en.objStr.s, sobj);
|
|
strcpy((char *)en.typeStr.s, stype);
|
|
strcpy((char *)en.zoneStr.s, szone);
|
|
|
|
err = NBPRemove(&en);
|
|
return(err);
|
|
}
|
|
|
|
aerror(msg, err)
|
|
char *msg;
|
|
int err;
|
|
{
|
|
fprintf(stderr,"%s error because: ",msg);
|
|
switch (err) {
|
|
case tooManySkts:
|
|
fprintf(stderr,"too many sockets open already\n");
|
|
break;
|
|
case noDataArea:
|
|
fprintf(stderr,"internal data area corruption - no room to \
|
|
create socket\n");
|
|
break;
|
|
case nbpDuplicate:
|
|
fprintf(stderr,"name already registered\n");
|
|
break;
|
|
case nbpNoConfirm:
|
|
fprintf(stderr,"couldn't register name - is atis running?\n");
|
|
break;
|
|
case nbpBuffOvr:
|
|
fprintf(stderr,"couldn't register name - too many names already \
|
|
registered\n");
|
|
break;
|
|
default:
|
|
fprintf(stderr,"error: %d\n",err);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* append to appendto as pascal string, update appendto pointer */
|
|
int packstring(appendto,cstring)
|
|
u_char **appendto;
|
|
char *cstring;
|
|
{
|
|
int len = strlen(cstring);
|
|
register u_char *app = *appendto;
|
|
|
|
if (len > MAXSNITCHSTRING) len = MAXSNITCHSTRING;
|
|
|
|
*app++ = (u_char)len;
|
|
bcopy(cstring, app, len);
|
|
*appendto += (len+1);
|
|
return(len+1);
|
|
}
|
|
|
|
/*
|
|
* setup snitch strings
|
|
*
|
|
*/
|
|
setup_snitch()
|
|
{
|
|
AddrBlock nisaddr; /* nis network and node number */
|
|
AddrBlock thisaddr; /* our network and node number */
|
|
import struct in_addr bridge_addr; /* the kbox we use */
|
|
|
|
GetNisAddr(&nisaddr);
|
|
GetMyAddr(&thisaddr);
|
|
|
|
if (thisaddr.net != nisaddr.net || thisaddr.node != nisaddr.node)
|
|
sprintf(real_system, "System: CAP: bridge %s atis: net %3d.%02d node %d",
|
|
inet_ntoa(bridge_addr), nkipnetnumber(nisaddr.net),
|
|
nkipsubnetnumber(nisaddr.net), nisaddr.node);
|
|
else
|
|
sprintf(real_system, "System: CAP: bridge %s", inet_ntoa(bridge_addr));
|
|
sprintf(real_finderstring,"Finder: %s",finderstring);
|
|
sprintf(real_lwstring,"LaserWriter: %s",lwstring);
|
|
}
|
|
|
|
struct snitch_userbytes {
|
|
byte su_code; /* snitch code */
|
|
#define SNITCH_REQUEST 0
|
|
#define SNITCH_REPLY 1
|
|
byte su_xxxx; /* unknown */
|
|
byte su_version; /* snitch version */
|
|
byte su_subversion; /* snitch subversion */
|
|
};
|
|
|
|
struct snitch_buf {
|
|
word sb_atalk_version; /* 0: atalk version: note: it may only */
|
|
/* be the second byte */
|
|
byte sb_dummy[8]; /* 2: unknown? */
|
|
#define SB_STRING_OFFSET 10
|
|
byte sb_strings[1]; /* 10: start of string area */
|
|
};
|
|
|
|
void snitch_handler(req_abr)
|
|
ABusRecord *req_abr;
|
|
{
|
|
int cnt;
|
|
atpUserDataType userData;
|
|
char buffer[atpMaxData]; /* room for translated message */
|
|
struct atpProto *ap = &req_abr->proto.atp;
|
|
struct snitch_userbytes *su;
|
|
struct snitch_buf *sb;
|
|
byte *p;
|
|
|
|
/* Check out the user data field */
|
|
su = (struct snitch_userbytes *)&ap->atpUserData;
|
|
if (snitchdebug > 1) {
|
|
u_char *p = (u_char *)&ap->atpUserData;
|
|
int i;
|
|
|
|
for ( i = 0;i < 4; i++) {
|
|
fprintf(stderr,"snitch: user data as bytes[%d]=%x\n",i,p[i]);
|
|
}
|
|
}
|
|
|
|
if ((su->su_code != SNITCH_REQUEST)) {
|
|
if (snitchdebug) {
|
|
fprintf(stderr,"snitch: bad request code = %x\n",su->su_code);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
su = (struct snitch_userbytes *)&userData;
|
|
su->su_code = SNITCH_REPLY;
|
|
su->su_xxxx = 0; /* ??? */
|
|
su->su_version = 0;
|
|
su->su_subversion = 0xCA; /* subversion (as close as I can get to CAP)*/
|
|
|
|
/* now fill the user bytes with info. */
|
|
|
|
/* atalk driver version is buffer[1] (maybe a short from 0 to 1)*/
|
|
sb = (struct snitch_buf *)buffer;
|
|
sb->sb_atalk_version = htons(2); /* random? */
|
|
|
|
/* packed strings */
|
|
p = sb->sb_strings;
|
|
cnt = SB_STRING_OFFSET;
|
|
cnt += packstring(&p,real_system); /* system name */
|
|
cnt += packstring(&p,real_finderstring); /* finder name */
|
|
cnt += packstring(&p,real_lwstring); /* laserwriter driver */
|
|
|
|
do_reply(req_abr, userData, buffer, cnt);
|
|
}
|
|
|
|
/*
|
|
* Take need information from the ATP request and turn it into
|
|
* an ATP response of the given data.
|
|
*
|
|
*
|
|
*/
|
|
do_reply(req_abr, userdata,data,datalength)
|
|
ABusRecord *req_abr;
|
|
atpUserDataType userdata;
|
|
char *data;
|
|
int datalength;
|
|
{
|
|
ABusRecord res_abr;
|
|
atpProto *ap;
|
|
BDS aBDS[1];
|
|
|
|
ap = &res_abr.proto.atp;
|
|
ap->atpAddress = req_abr->proto.atp.atpAddress;
|
|
ap->atpTransID = req_abr->proto.atp.atpTransID;
|
|
ap->atpSocket = req_abr->proto.atp.atpSocket;
|
|
ap->atpNumBufs = setup_bds(aBDS,sizeof(aBDS),atpMaxData,
|
|
data,datalength,userdata);
|
|
ap->atpRspBDSPtr = aBDS;
|
|
ap->atpBDSSize = ap->atpNumBufs; /* usually equal in an ATP response */
|
|
ap->fatpEOM = 1; /* is always EOM */
|
|
|
|
return(ATPSndRsp(&res_abr, FALSE));
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the ip address based on the hostname
|
|
*
|
|
*/
|
|
getipaddr(hostname, sin)
|
|
char *hostname;
|
|
struct in_addr *sin;
|
|
{
|
|
struct hostent *host;
|
|
|
|
if ((host = gethostbyname(hostname)) == NULL)
|
|
return(-1);
|
|
bcopy(host->h_addr, (caddr_t)sin, host->h_length);
|
|
return(0);
|
|
}
|