/* qi.c - UIUC CCSO nameserver query interpreter */
/* Bruce Tanner - Cerritos College */
/* Version history: */
/* 1.0 1993/08/15 Initial version */
/* 1.1 1993/08/25 Add field instance attribute, conditionalize options */
/* 1.2 1993/09/08 Soundex is now an indexed explicit field; exact match mode */
/* 1.3 1993/09/15 Added ID to index key to remove duplicate index records */
/* 2.0 1993/09/16 Add login mode (login, answer, clear, logout) */
/* 2.1 1993/09/20 Interactive mode */
#include stdio
#include string
#include ctype
#include ssdef
#include descrip
#include time
#include stdarg
#include rms
#include dvidef
#include dcdef
#include "qi.h"
Fields fields[MAX_FIELD]; /* field attributes, global */
int mode = DEFAULT_MODE; /* global mode flags */
extern int db_status; /* status of database */
char login_alias[KEYWORD_SIZE + 1]; /* current login */
char login_challenge[CHALLENGE_SIZE + 1];
int login_mode = MODE_ANONYMOUS;
void db_open();
void db_close();
int read_fields(char *);
int fields_cmd(char *, int);
char *new_string(char *);
char *getlogical(char *);
void qilog(int, char *, ...);
extern int query(char *, int);
extern char *get_value(int, char*, char*, char *);
int quit(char *, int);
Arg *parse_cmd(char *, int);
Arg *make_arg(char *, int, char *, int);
void free_args(Arg *);
void swrite(int, char *, ...);
void writestring(int, char *);
void qiabort(int, char *);
int id_cmd(char *, int);
int stat_cmd(char *, int);
int set_cmd(char *, int);
int site_cmd(char *, int);
int login_cmd(char *, int);
int answer_cmd(char *, int);
int clear_cmd(char *, int);
int logout_cmd(char *, int);
char *challenge(int size);
struct verb_struct {
char *name;
int mode; /* requires login, etc */
int (*proc)(char *, int);
} verbs[] = {{"quit", MODE_ANONYMOUS | MODE_LOGIN, quit},
{"stop", MODE_ANONYMOUS | MODE_LOGIN, quit},
{"exit", MODE_ANONYMOUS | MODE_LOGIN, quit},
{"fields", MODE_ANONYMOUS | MODE_LOGIN, fields_cmd},
{"query", MODE_ANONYMOUS | MODE_LOGIN, query},
{"ph", MODE_ANONYMOUS | MODE_LOGIN, query},
{"status", MODE_ANONYMOUS | MODE_LOGIN, stat_cmd},
{"id", MODE_ANONYMOUS | MODE_LOGIN, id_cmd},
{"set", MODE_ANONYMOUS | MODE_LOGIN, set_cmd},
{"siteinfo", MODE_ANONYMOUS | MODE_LOGIN, site_cmd},
{"login", MODE_ANONYMOUS, login_cmd},
{"answer", MODE_PASSWORD, answer_cmd},
{"clear", MODE_PASSWORD, clear_cmd},
{"logout", MODE_LOGIN, logout_cmd}
};
#define MAX_VERBS (sizeof(verbs) / sizeof(struct verb_struct))
/* This program is designed to run as an 'inetd' detached process */
main()
{
int status, sock, class;
unsigned short chan;
$DESCRIPTOR(sysin_dsc, "SYS$INPUT");
/* open a channel to an INET device */
status = sys$assign(&sysin_dsc, &chan, 0, 0, 0);
if ((status & 1) != SS$_NORMAL) {
qilog(-1, "Open fail status = %d", status);
exit(status);
}
sock = (int) chan;
status = lib$getdvi(&DVI$_DEVCLASS, &chan, 0, &class, 0, 0);
/* load fields */
if (read_fields(getlogical(CONFIG_NAME)) == False)
exit(4);
/* initialize randomness */
srand((int) time((time_t *) NULL));
db_open();
while (process(sock, class == DC$_TERM));
closenet(sock);
db_close();
}
/* process a command stream */
int process(int sock, int interactive)
{
int status, ind, length;
static int bad_cmd = 0;
char *cp, inputline[MAX_INPUT], *vp, verb[MAX_INPUT];
strncpy(inputline, "", MAX_INPUT);
if (interactive) {
printf("qi> ");
fgets(inputline, MAX_INPUT, stdin);
length = strlen(inputline);
}
else
length = readline(sock, inputline, MAX_INPUT); /** Get the line **/
ZapCRLF(inputline);
qilog(sock, "Cmd: %s", inputline);
if (length <= 0) {
qilog(sock, "Remote end shutdown");
return False;
}
if (strlen(inputline) == 0) /* ignore blank lines */
return (++bad_cmd < MAX_BAD); /* return False if too many null cmds */
/* put first word of input in verb as lowercase */
strncpy(verb, "", sizeof(verb));
vp = verb;
for (cp = inputline; *cp; cp++)
if (*cp == ' ')
break; /* break on space */
else
*vp++ = _tolower(*cp); /* copy lower case char to verb */
for (ind = 0; ind < MAX_VERBS; ind++)
if (strcmp(verb, verbs[ind].name) == 0)
break;
if (ind == MAX_VERBS) {
qilog(sock, "Unknown command: /%s/%s/", verb, inputline);
writestring(sock, "514:Unknown command.\r\n");
return (++bad_cmd < MAX_BAD); /* return False if too many bad cmds */
}
if (((verbs[ind].mode & login_mode) == 0) && (login_mode == MODE_ANONYMOUS)) {
qilog(sock, "Not logged in: %s", inputline);
writestring(sock, "506:Request refused; must be logged in to execute.\r\n");
return (++bad_cmd < MAX_BAD); /* return False if too many bad cmds */
}
if (((verbs[ind].mode & login_mode) == 0) && (login_mode == MODE_PASSWORD)) {
qilog(sock, "Not answer or clear: %s", inputline);
writestring(sock, "523:Expecting 'answer' or 'clear'\r\n");
return (++bad_cmd < MAX_BAD); /* return False if too many bad cmds */
}
if ((ind < MAX_VERBS) && (verbs[ind].mode & login_mode))
status = (*verbs[ind].proc)(inputline, sock);
bad_cmd = 0; /* reset bad command count */
return status;
}
int quit(char *cmd, int sock)
{
writestring(sock, "200:Bye!\r\n");
return False;
}
char * get_field(char *ptr, char *field, int lower)
{
int ind;
for (ind= 0; *ptr != '\0' && *ptr != ':'; ptr++, ind++)
field[ind] = lower ? _tolower(*ptr) : *ptr;
field[ind] = '\0';
if (*ptr == ':') ptr++; /* skip over terminating ":" */
return ptr;
}
int read_fields(char *file)
{
FILE *cnf;
char *ptr, line[256], field[128];
int ind, field_idx, count = 0;
for (ind = 0; ind < MAX_FIELD; ind++) {
fields[ind].number = NULL;
fields[ind].name = NULL;
fields[ind].desc = NULL;
fields[ind].attrib = 0;
}
cnf = fopen(file, "r", "shr=get");
if (cnf == NULL)
return (False);
while (fgets(line, sizeof(line), cnf)) {
ZapCRLF(line);
ptr = line;
if ((*ptr == '#') || (*ptr == '\0')) /* comment or blank? */
continue; /* yes, skip line */
count++;
ptr = get_field(ptr, field, False); /* field number */
field_idx = atoi(field);
fields[field_idx].number = new_string(field);
ptr = get_field(ptr, field, True); /* field name */
fields[field_idx].name = new_string(field);
ptr = get_field(ptr, field, False); /* field size (ignore) */
ptr = get_field(ptr, field, False); /* field description */
fields[field_idx].desc = new_string(field);
ptr = get_field(ptr, field, False); /* field option (ignore) */
for (;;) {
ptr = get_field(ptr, field, True); /* get attribute */
if (strlen(field) == 0)
break; /* no more attributes */
fields[field_idx].attrib |= field_attrib(field);
}
if (fields[field_idx].number < 1)
qilog(-1, "Field \"%s\" has illegal field number",
fields[field_idx].name);
}
fclose(cnf);
return True;
}
int write_afield(int field_num, int sock)
{
char line[128];
int aidx;
if (fields[field_num].name == NULL)
return False;
sprintf(line, "-200:%d:%s:max %d",
field_num, fields[field_num].name, DATA_SIZE);
for (aidx = 0; aidx < MAX_ATTRIBUTES; aidx++)
if (fields[field_num].attrib & attributes[aidx].value) {
strcat(line, " ");
strcat(line, attributes[aidx].name);
}
strcat(line, "\r\n");
writestring(sock, line);
swrite(sock, "-200:%d:%s:%s\r\n",
field_num, fields[field_num].name, fields[field_num].desc);
return True;
}
int fields_cmd(char *cmd, int sock)
{
int fidx, aidx, count = 0;
char line[256];
Arg *list, *listp;
list = listp = parse_cmd(cmd, sock);
if (list == NULL) /* null arg list means all fields */
for (fidx = 0; fidx < MAX_FIELD; fidx++)
count += write_afield(fidx, sock);
else
for (; listp; listp = listp->next)
if (listp->field > -1)
count += write_afield(listp->field, sock);
else
writestring(sock, "507:Field does not exist.\r\n");
free_args(list);
writestring(sock, "200:Ok.\r\n");
if (DEBUG) qilog(sock, "Sent %d field definitions", count);
return True;
}
id_cmd(char *cmd, int sock)
{
writestring(sock, "200:Thanks, but we don't use ids here.\r\n");
return True;
}
stat_cmd(char *cmd, int sock)
{
if ((db_status & 1) == SS$_NORMAL)
writestring(sock, "200:Database ready.\r\n");
else
writestring(sock, "475:Database unavailable; try later.\r\n");
return True;
}
/* set global mode flags on/off */
int set_cmd(char *cmd, int sock)
{
Arg *list, *listp;
int index;
list = listp = parse_cmd(cmd, sock);
for (; listp; listp = listp->next) {
for (index = 0; index < MAX_MODES; index++)
if (listp->name && strcmp(listp->name, modes[index].name) == 0) {
switch (listp->type & TYPE_MASK) {
case TYPE_ON:
mode |= modes[index].value;
writestring(sock, "200:Done.\r\n");
break;
case TYPE_OFF:
mode &= ~modes[index].value;
writestring(sock, "200:Done.\r\n");
break;
default:
writestring(sock, "513:Option must be ON or OFF.\r\n");
break;
}
break;
}
if (index == MAX_MODES)
swrite(sock, "513:Unknown mode %s\r\n",
listp && listp->name ? listp->name : "");
}
free_args(list);
return True;
}
/* return some arbitrary site info */
int site_cmd(char *cmd, int sock)
{
FILE *fd;
char line[128];
swrite(sock, "-200:0:version:%s\r\n", VERSION);
if ((fd = fopen(getlogical(SITEINFO_NAME), "r", "shr=get")) == NULL)
swrite(sock, "525:No siteinfo available.\r\n");
else {
while (fgets(line, sizeof(line), fd)) {
ZapCRLF(line);
swrite(sock, "-200:%s\r\n", line);
}
swrite(sock, "200:Ok.\r\n");
fclose(fd);
}
return True;
}
/* set current alias */
int login_cmd(char *cmd, int sock)
{
Arg *list;
char *ap;
list = parse_cmd(cmd, sock);
if ((list == NULL) || (list->next) || (list->type != TYPE_VALUE)) {
free_args(list);
writestring(sock, "599:Syntax error\r\n");
return True;
}
if ((ap = get_value(sock, list->value, ALIAS_FIELD, ALIAS_FIELD)) == NULL) {
free_args(list);
swrite(sock, "500:Alias does not exist\r\n");
return True;
}
free_args(list);
strcpy(login_alias, ap);
if (get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD) == NULL) {
swrite(sock, "500:Password does not exist\r\n");
return True;
}
strcpy(login_challenge, challenge(CHALLENGE_SIZE));
swrite(sock, "301:%s\r\n", login_challenge);
login_mode = MODE_PASSWORD;
return True;
}
int clear_cmd(char *cmd, int sock)
{
char *cp, *pw;
cp = strchr(cmd, ' '); /* skip verb */
for (;cp && *cp && (*cp == ' '); cp++); /* skip spaces after verb */
if (cp == NULL) {
writestring(sock, "599:Syntax error\r\n");
login_mode = MODE_ANONYMOUS;
return True;
}
if ((pw = get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD)) == NULL) {
swrite(sock, "500:Password does not exist\r\n");
login_mode = MODE_ANONYMOUS;
return True;
}
if (strcmp(cp, pw) == 0) {
swrite(sock, "200:%s:Password accepted\r\n", login_alias);
login_mode = MODE_LOGIN;
}
else {
swrite(sock, "500:Login failed\r\n", login_alias);
login_mode = MODE_ANONYMOUS;
}
return True;
}
int answer_cmd(char *cmd, int sock)
{
char *cp, decrypted[128];
cp = strchr(cmd, ' '); /* skip verb */
for (;cp && *cp && (*cp == ' '); cp++); /* skip spaces after verb */
if (cp == NULL) {
writestring(sock, "599:Syntax error\r\n");
login_mode = MODE_ANONYMOUS;
return True;
}
crypt_start(get_value(sock, login_alias, ALIAS_FIELD, PASSWORD_FIELD));
decrypt(decrypted, cp);
if ((cp = strchr(decrypted, '\r')) || (cp = strchr(decrypted, '\n')))
*cp = '\0'; /* truncate at cr or lf */
if (DEBUG)
swrite(sock, "%s decrypted into\r\n%s compared with\r\n%s\r\n",
cp, decrypted, login_challenge);
if (strcmp(decrypted, login_challenge) == 0) {
swrite(sock, "200:%s:Password accepted\r\n", login_alias);
login_mode = MODE_LOGIN;
}
else {
swrite(sock, "500:Login failed\r\n");
login_mode = MODE_ANONYMOUS;
}
return True;
}
int logout_cmd(char *cmd, int sock)
{
strncpy(login_alias, "", sizeof(login_alias));
login_mode = MODE_ANONYMOUS;
swrite(sock, "200:Done.\r\n");
return True;
}
char *challenge(int size)
{
char *ptr, *base;
base = calloc(size + 1, sizeof(char));
for (ptr = base; size; ptr++, size--)
*ptr = (rand() & 0x3f) + 0x21;
return (base);
}
/* return the attribute value for the given field name */
int field_attrib(char *str)
{
int ind;
for (ind = 0; ind < MAX_ATTRIBUTES; ind++)
if (*str == _tolower(*attributes[ind].name)) /* check only first char */
return (attributes[ind].value);
return (0); /* no match = no bits */
}
/* return the field_number for the given field name */
int field_number(char *str)
{
int ind;
for (ind = 0; ind < MAX_FIELD; ind++)
if (fields[ind].name && (strcmp(str, fields[ind].name) == 0))
return (atoi(fields[ind].number));
return (-1); /* no field number */
}
/* get a token as part of the 'field=value' clause */
/* return pointer to terminator */
char *get_token(char *cp, char *dp)
{
int in_quote = False;
if (*cp) {
while (isspace(*cp)) cp++; /* skip space */
while (*cp && (in_quote || ((*cp != ' ') && (*cp != '='))))
if (*cp == '"') {
in_quote = !in_quote;
cp++;
}
else
*dp++ = in_quote ? *cp++ : tolower(*cp++);
}
*dp = '\0';
return cp;
}
/* cmd = 'verb [field=]value ...' */
Arg *parse_cmd(char *cmd, int sock)
{
int index;
char *cp, token[128];
Arg *start = NULL, *end = NULL;
cp = strchr(cmd, ' '); /* skip verb */
while (cp) {
cp = get_token(++cp, token);
if (strlen(token) == 0)
return start;
if (start == NULL)
start = end = make_arg(NULL, -1, NULL, 0);
else {
end->next = make_arg(NULL, -1, NULL, 0);
end = end->next;
}
if (*cp == '=') {
end->name = new_string(token);
end->field = field_number(token);
end->type |= TYPE_NAME | TYPE_EQUAL;
cp = get_token(++cp, token);
}
if (strlen(token)) {
end->value = new_string(token);
end->type |= TYPE_VALUE;
}
if (strcmp(token, "return") == 0) /* check for special names */
end->type |= TYPE_RETURN;
else if (strcmp(token, "on") == 0)
end->type |= TYPE_ON;
else if (strcmp(token, "off") == 0)
end->type |= TYPE_OFF;
if (end->field == -1) /* if there were no field name given */
end->field = field_number(token); /* try the field value as a field name */
if (DEBUG)
swrite(sock, "-100: Parse >> %s (field %d) = %s\r\n",
end->name ? end->name : "", end->field,
end->value ? end->value : "");
}
return start; /* should only get here on null list */
}
Arg *make_arg(char * name, int field, char *value, int type)
{
Arg *ptr;
ptr = malloc(sizeof (Arg));
ptr->type = type;
ptr->name = name;
ptr->field = field;
ptr->value = value;
ptr->next = (Arg *) 0;
return ptr;
}
void free_args(Arg *ptr)
{
Arg *next;
while (ptr) {
next = ptr->next;
free(ptr);
ptr = next;
}
}
/* copy string into malloc'ed space */
char *new_string(char *str)
{
char *ptr;
ptr = (char *) malloc(strlen(str) + 1);
strcpy(ptr, str);
return (ptr);
}
void swrite(int sock, char *fmt, ...)
{
char buf[512];
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsprintf(buf, fmt, arg_ptr);
va_end(arg_ptr);
writestring(sock, buf);
}
void qilog(int sock, char *fmt, ...)
{
FILE *logfd;
char host_name[256];
time_t Now;
char NowBuf[26];
char *cp;
char buf[512];
va_list arg_ptr;
va_start(arg_ptr, fmt);
vsprintf(buf, fmt, arg_ptr);
va_end(arg_ptr);
host_name[0] = '\0';
if (sock > -1)
inet_netnames(sock, host_name);
time(&Now);
cp = (char *) ctime(&Now);
ZapCRLF(cp);
cp = strcpy(NowBuf, cp);
if ((logfd = fopen(getlogical(LOG_NAME), "a",
"dna=qi.log", "shr=put")) != NULL) {
if (strlen(fmt)) {
fprintf(logfd, "%s %s : %s\n", cp, host_name, buf);
fflush(logfd);
}
fclose(logfd);
}
}
void qiabort(int sock, char *str)
{
writestring(sock, str);
ZapCRLF(str);
qilog(sock, "Abort: %s", str);
exit(2);
}
void *my_realloc(void *mem, int size)
{
if ((mem == (void *) 0))
return ((void *) malloc(size));
else
return ((void *) realloc(mem, size));
}
/* translate an exec mode logical name */
char *getlogical(char *name)
{
#include psldef
#include lnmdef
typedef struct {
short length;
short item_code;
char *bufadr;
short *ret_len_addr;
} item_desc;
struct {
item_desc string;
int terminator;
} trnlst;
static char result[128]; /* the object returned */
short ret_len = 0;
int acmode = PSL$C_EXEC;
$DESCRIPTOR(table_dsc, "LNM$SYSTEM_TABLE");
struct dsc$descriptor_s log_dsc = { strlen(name), DSC$K_DTYPE_T,
DSC$K_CLASS_S, name };
trnlst.string.bufadr = result;
trnlst.string.length = sizeof(result);
trnlst.string.item_code = LNM$_STRING;
trnlst.string.ret_len_addr = &ret_len;
trnlst.terminator = 0;
sys$trnlnm(0, &table_dsc, &log_dsc, &acmode, &trnlst);
result[ret_len] = '\0';
return result;
}
.
Response:
text/plain