pkg://eznet-1.11.1.tar.gz:39454/
eznet-1.11.1/eznet.c
downloads
/*
** Copyright (c) 1998 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this program; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@acm.org
** http://www.hwaci.com/drh/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
/*
** Names of files and executables and other hard-coded
** strings.
*/
#define DB_DIR "/etc/ppp/eznet"
#define DB_FILE DB_DIR "/eznet.conf"
#define PPP_DIR "/etc/ppp"
#define PPP_EXE "/usr/sbin/pppd"
#define SELF "/usr/bin/eznet"
#define DIALD_EXE "/usr/sbin/diald"
#define SESSIONLOG DB_DIR "/session.log"
/*
** The following define determines what version of PPP
** this program expects by default. You can override this
** value using the pppversion= parameter.
*/
#define DFLT_PPP_VERSION "2.3"
/*
** Each entry in the database is represented in memory by an instance
** of the following structure.
*/
typedef struct ConfEntry ConfEntry;
struct ConfEntry {
char *zLabel; /* A label for this entry */
ConfEntry *next; /* Next entry in the same record */
char *zValue; /* Value for this entry */
};
/*
** These variables record the whole database. There are nRecord
** records, and each record consists of a linked list of ConfEntry
** structures where aRecord[N] points to the head of the list for
** record number N.
*/
static int nRecord = 0; /* Number of records */
static ConfEntry **aRecord = 0; /* List of ConfEntries for each record */
/*
** A transcript of the CHAT session is written to this file, if
** it is open.
*/
static FILE *transcript = 0;
/*
** If this is a set-uid or set-gid process, then release all
** privileges.
*/
static void Unprivileged(void){
int uid = getuid();
int gid = getgid();
setregid(gid,gid);
setreuid(uid,uid);
}
/*
** Read the entire contents of a file into memory. Space to
** hold the file is obtained from malloc. NULL is returned if
** we can't read the file for any reason.
*/
static char *ReadFile(const char *zFilename){
int fd;
FILE *in;
int toread;
int read;
int n;
char *zBuf;
struct stat sbuf;
in = fopen(zFilename,"r");
if( in==0 ) return 0;
fd = fileno(in);
if( fstat(fd, &sbuf)!=0 ){
fclose(in);
return 0;
}
toread = sbuf.st_size;
read = 0;
zBuf = malloc( toread+1 );
if( zBuf==0 ){
fclose(in);
return 0;
}
while( toread && (n=fread(&zBuf[read],1,toread,in))>0 ){
toread -= n;
read += n;
}
zBuf[read] = 0;
fclose(in);
return zBuf;
}
/* Change the status for a record. The status is recorded in
** a file named
**
** /var/eznet/status.NNN
**
** where NNN is the record number.
*/
static void WriteStatus(int iRec, char *zStatus, ...){
FILE *out;
va_list ap;
char zFilename[sizeof(DB_DIR)+20];
sprintf(zFilename,"%s/status.%d",DB_DIR,iRec);
out = fopen(zFilename,"w");
if( out ){
time_t now;
struct tm *p;
char zBuf[200];
fchown(fileno(out),0,0);
fchmod(fileno(out),0644);
va_start(ap, zStatus);
vfprintf(out,zStatus,ap);
va_end(ap);
time(&now);
p = localtime(&now);
strftime(zBuf,sizeof(zBuf)-1," %I:%M%p %a %b %d, %Y\n", p);
fprintf(out,zBuf);
fclose(out);
}
}
/* Write a status report from Chat. This is written to a file
** named
**
** /var/eznet/chatstat.NNN
**
** where NNN is the record number.
*/
static void WriteChatStat(int iRec, char *zStatus, ...){
FILE *out;
va_list ap;
char zFilename[sizeof(DB_DIR)+20];
sprintf(zFilename,"%s/chatstat.%d",DB_DIR,iRec);
out = fopen(zFilename,"w");
if( out ){
va_start(ap, zStatus);
vfprintf(out,zStatus,ap);
va_end(ap);
fclose(out);
}
}
/*
** Write text into a file. zFile is the name of the file into
** which the text is written. zFormat is a printf-like format
** string that defines the text. The owner of the file is converted
** to root and the permissions are set to "mode".
*/
static void WriteToFile(char *zFile, int mode, char *zFormat, ...){
FILE *out;
va_list ap;
out = fopen(zFile,"w");
if( out==0 ) return;
fchmod(fileno(out),mode);
fchown(fileno(out),0,0);
va_start(ap,zFormat);
vfprintf(out,zFormat,ap);
va_end(ap);
fclose(out);
}
/* Make sure /etc/ppp/ip-up and /etc/ppp/ip-down invoke eznet
** with either the "ipup" or "ipdown" argument (as approprite.)
** This is needed so that we can adjust the status when the link
** goes up or down.
*/
static void SetIpUpDown(char *zUpDown){
char *zText;
int i;
int lineLen = 0;
int patLen;
char zPat[100];
char zFile[sizeof(PPP_DIR)+20];
sprintf(zFile,"%s/ip-%s",PPP_DIR,zUpDown);
zText = ReadFile(zFile);
if( zText==0 ){
WriteToFile(zFile, 0744,
"#!/bin/sh\n" SELF " ip%s $* &\n", zUpDown);
return;
}
for(i=0; zText[i] && zText[i]!='\n'; i++){}
if( zText[i]=='\n' ){
lineLen = i;
}
sprintf(zPat,SELF " ip%s $* &",zUpDown);
patLen = strlen(zPat);
while( zText[i] && (zText[i]!='/' || strncmp(&zText[i], zPat, patLen)) ){
i++;
}
if( zText[i]==0 ){
WriteToFile(zFile, 0744, "#!/bin/sh\n%s\n%s",
zPat, &zText[lineLen]);
}
}
/*
** Parse a single line of the pap-secrets or chap-secrets file whose
** text begins as *pzText. The elements of the line are place in
** azArgs[0] through azArgs[3].
**
** After parsing, *pzText is left pointing to the first character
** after then end of the parsed text. At end of file, *pzText points
** to 0.
**
** All of azArgs[0] through azArgs[3] are always filled in. Empty
** strings are inserted if necessary. A comment line is placed in
** azArgs[0] and the others are empty strings.
**
** Quotes and escapes are removed from azArgs[0] through azArgs[2],
** except for comment lines which are passed through unaltered.
*/
static void GetSecret(char **pzText, char **azArgs){
int i;
char *z = *pzText;
while( isspace(*z) ) z++;
azArgs[0] = azArgs[1] = azArgs[2] = azArgs[3] = "";
if( *z=='#' ){
azArgs[0] = z;
while( *z && *z!='\n' ) z++;
if( *z=='\n' ){
*z = 0;
z++;
}
*pzText = z;
return;
}
for(i=0; i<=2; i++){
int hasEscape = 0;
azArgs[i] = z;
if( *z=='"' ){
azArgs[i]++;
z++;
while( *z && *z!='"' ){
if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
z++;
}
if( *z=='"' ){
*z = 0;
z++;
}
}else{
while( *z && !isspace(*z) ){
if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
z++;
}
if( *z ){
*z = 0;
z++;
}
}
while( isspace(*z) && *z!='\n' ){ z++; }
if( hasEscape ){
int j = 0, k = 0;
char *zStr = azArgs[i];
while( zStr[j] ){
if( zStr[j]=='\\' && zStr[j+1] ){ j++; }
zStr[k++] = zStr[j++];
}
}
}
azArgs[i] = z;
while( *z && *z!='\n' ){
if( *z=='\\' && z[1] ){ z++; }
z++;
}
if( *z=='\n' ){
*z = 0;
z++;
}
*pzText = z;
}
/*
** The elements azArgs[0] through azArgs[3] contain the text of
** a secret. This secret is written to *pzBuf. *pzBuf is updated
** to point to the null terminator at the end of the written text.
**
** Quotes and escapes are inserted on azArgs[0] through azArgs[2]
** if needed. Except if the first character of azArgs[0] is "#"
** then no quotes or escapes are inserted.
*/
static void AppendSecret(char **pzBuf, char **azArgs){
char *z = *pzBuf;
char *zSrc;
int i;
if( azArgs[0][0]=='#' ){
zSrc = azArgs[0];
while( *zSrc ){ *(z++) = *(zSrc++); }
*(z++) = '\n';
*z = 0;
*pzBuf = z;
return;
}
for(i=0; i<=2; i++){
zSrc = azArgs[i];
if( *zSrc==0 ) continue;
while( *zSrc ){
switch( *zSrc ){
case ' ':
case '\t':
case '\n':
case '"':
case '#':
case '\'':
case '\\':
*(z++) = '\\';
/* Fall thru */
default:
*(z++) = *(zSrc++);
break;
}
}
*(z++) = ' ';
}
while( *zSrc ){ *(z++) = *(zSrc++); }
*(z++) = '\n';
*z = 0;
*pzBuf = z;
}
/* Make sure the correct password for the service and user is found in the
** secrets file. Add it if is not. Preserve any commands or
** unrelated secrets that are previously found in the file.
**
** Needless to say, we have to have root privileges to run
** this function successfully. The secrets files contain information
** which should not be publicly viewable.
*/
static void SetSecrets(
char *zBase, /* Either "pap-secrets" or "chap-secrets" */
char *zService, /* Name of the service provider we are calling */
char *zUser, /* Our login name */
char *zPassword /* Our password */
){
char *zOld, *zOldOrig;
char *z;
char *zNew;
int extra;
int seenServiceUser = 0;
int seenUserService = 0;
char *azArgs[4];
char zFile[sizeof(PPP_DIR)+20];
if( zPassword==0 || zUser==0 || zService==0 || zFile==0 ) return;
sprintf(zFile,"%s/%.15s",PPP_DIR,zBase);
zOldOrig = zOld = ReadFile(zFile);
extra = (strlen(zService) + strlen(zPassword) + strlen(zUser) + 10)*4;
if( zOld==0 ){
WriteToFile(zFile, 0600, "#\n%s %s %s\n%s %s %s\n",
zService, zUser, zPassword,
zUser, zService, zPassword
);
return;
}
z = zNew = malloc( strlen(zOld) + extra + 100 );
if( z==0 ) return;
while( zOld && *zOld ){
GetSecret(&zOld, azArgs);
if( azArgs[0][0]!='#' ){
if( strcmp(azArgs[0],zService)==0 && strcmp(azArgs[1],zUser)==0 ){
seenServiceUser = 1;
azArgs[2] = zPassword;
azArgs[3] = "";
}else if( strcmp(azArgs[0],zUser)==0 && strcmp(azArgs[1],zService)==0 ){
seenUserService = 1;
azArgs[2] = zPassword;
azArgs[3] = "";
}
}
AppendSecret(&z, azArgs);
}
if( !seenServiceUser ){
azArgs[0] = zService;
azArgs[1] = zUser;
azArgs[2] = zPassword;
azArgs[3] = "";
AppendSecret(&z, azArgs);
}
if( !seenUserService ){
azArgs[0] = zUser;
azArgs[1] = zService;
azArgs[2] = zPassword;
azArgs[3] = "";
AppendSecret(&z, azArgs);
}
WriteToFile(zFile, 0600, "%s", zNew);
free(zOldOrig);
free(zNew);
}
/* zFile[*pI] is the first character in a line of text. Return
** a pointer to the first non-whitespace character in this line.
** Also convert the \n into a \0 and make *pI be the index of
** the first character on the next line.
**
** If zFile[*pI] is 0, then we've reached the end of input.
** Return NULL.
*/
static char *GetLine(char *zFile, int *pI){
int i = *pI;
int iStart;
if( zFile[i]==0 ) return 0;
while( isspace(zFile[i]) ){ i++; }
iStart = i;
while( zFile[i] && zFile[i]!='\n' ){ i++; }
if( zFile[i] ){
zFile[i] = 0;
*pI = i+1;
}else{
*pI = i;
}
return &zFile[iStart];
}
/* This array maps hexadecimal digit values into their numeric
** value.
*/
static int hexval[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/* Return a pointer to the first space-separated token past
** the character zLine[*pI]. Return NULL if there are no more
** tokens. *pI is left pointing to the first character after
** the token.
**
** The HTTP-style encoding is removed from the token and the
** token is null-terminated.
*/
static char *GetToken(char *zLine, int *pI){
int i = *pI;
int iStart;
int j;
while( isspace(zLine[i]) ){ i++; }
if( zLine[i]==0 ) return 0;
iStart = i;
while( zLine[i] && !isspace(zLine[i]) ){ i++; }
if( zLine[i] ){
*pI = i + 1;
zLine[i] = 0;
}else{
*pI = i;
}
for(i=j=iStart; zLine[i]; i++){
switch( zLine[i] ){
case '+':
zLine[j++] = ' ';
break;
case '%':
if( zLine[i+1] && zLine[i+2] ){
zLine[j++] = hexval[0x7f & zLine[i+1]]*16 + hexval[0x7f & zLine[i+2]];
i += 2;
}else{
zLine[j++] = '%';
}
break;
default:
zLine[j++] = zLine[i];
break;
}
}
zLine[j] = 0;
return &zLine[iStart];
}
/*
** This function inserts a new entry into the database (or replaces
** the value of an existing entry). iRec is the record number.
** New records are created as necessary. zField is the entry label,
** and zValue is its value. Both are plain text.
**
** If zValue==0 or *zValue==0, then the entry is deleted.
**
** Memory to hold the new entry is obtained from malloc.
*/
static void InsertEntry(int iRec, char *zLabel, char *zValue){
ConfEntry *p;
if( zValue==0 || *zValue==0 ){
if( iRec>=0 && iRec<nRecord ){
ConfEntry **pp = &aRecord[iRec];
while( (p=*pp)!=0 ){
if( strcmp(p->zLabel,zLabel)==0 ) break;
pp = &p->next;
}
if( p ){
*pp = p->next;
free( p->zValue );
free( p );
}
}
return;
}
if( iRec>=nRecord ){
if( nRecord==0 ){
aRecord = malloc( sizeof(ConfEntry*)*(iRec+1) );
}else{
aRecord = realloc( aRecord, sizeof(ConfEntry*)*(iRec+1) );
}
if( aRecord==0 ){ nRecord = 0; return; }
while( nRecord<=iRec ){
aRecord[nRecord++] = 0;
}
}
for(p=aRecord[iRec]; p; p=p->next){
if( strcmp(p->zLabel,zLabel)==0 ) break;
}
if( p==0 ){
p = malloc( sizeof(ConfEntry) + strlen(zLabel) + 1 );
if( p==0 ) return;
p->zLabel = (char*)&p[1];
strcpy(p->zLabel, zLabel);
p->next = aRecord[iRec];
p->zValue = 0;
aRecord[iRec] = p;
}
if( p ){
if( p->zValue ) free(p->zValue);
p->zValue = malloc( strlen(zValue) + 1 );
if( p->zValue ) strcpy(p->zValue, zValue);
}
}
/*
** The set of characters that need to be excaped are coded as "1".
** Safe characters are 0.
*/
static unsigned char NeedEsc[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
/* Do an HTTP-style encoding of zToken and write it on the
** output file given.
*/
static void WriteToken(FILE *out, char *zToken){
int i = 0;
while( zToken[i] ){
if( !NeedEsc[(unsigned)zToken[i]&0xff] ){
i++;
}else{
if( i>0 ){
fwrite(zToken,1,i,out);
}
if( zToken[i]==' ' ){
fwrite("+",1,1,out);
}else{
char zBuf[4];
zBuf[0] = '%';
zBuf[1] = "0123456789ABCDEF"[(zToken[i]>>4)&0xf];
zBuf[2] = "0123456789ABCDEF"[zToken[i]&0xf];
zBuf[3] = 0;
fwrite(zBuf,1,3,out);
}
zToken += i + 1;
i = 0;
}
}
if( i>0 ){
fwrite(zToken,1,i,out);
}
}
/*
** The set of characters that need to be excaped like "\012" are
** are coded as 1. Safe characters are 0. Characters that need
** a backslash in front are coded with 2. Another code generates
** a symbolic escape by putting a backslash in front of the value.
*/
static unsigned char NeedTclEsc[] = {
1, 1, 1, 1, 1, 1, 1, 'a', 'b','t','n', 1, 'f','r', 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 0, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
/* Write a string as a single TCL token.
*/
static void WriteTcl(FILE *out, char *z){
register c;
while( (c=*(z++))!=0 ){
if( NeedTclEsc[c] ){
int e = NeedTclEsc[c];
if( e==1 ){
fprintf(out,"\\%03o",0xff&c);
}else if( e==2 ){
fprintf(out,"\\%c",c);
}else{
fprintf(out,"\\%c",e);
}
}else{
putc(c,out);
}
}
}
/*
** The set of characters that need to be excaped like "\012" are
** are coded as 1. Safe characters are 0. Characters that need
** a backslash in front are coded with 2. Another code generates
** a symbolic escape by putting a backslash in front of the value.
*/
static unsigned char NeedHumanEsc[] = {
1, 1, 1, 1, 1, 1, 1, 'a', 'b','t','n', 1, 'f','r', 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
/* Write a string as a single TCL token.
*/
static void WriteHumanReadable(FILE *out, char *z){
register c;
while( (c=*(z++))!=0 ){
if( NeedHumanEsc[c] ){
int e = NeedHumanEsc[c];
if( e==1 ){
fprintf(out,"\\%03o",0xff&c);
}else if( e==2 ){
fprintf(out,"\\%c",c);
}else{
fprintf(out,"\\%c",e);
}
}else{
putc(c,out);
}
}
}
/*
** Convert a non-negative number from its ASCII form into a binary
** form. Return -1 if the input string is not a non-negative number.
*/
static int GetInt(char *z){
int v = 0;
while( *z ){
if( *z<'0' || *z>'9' ) return -1;
v = v*10 + *(z++) - '0';
}
return v;
}
/*
** Read the whole database into memory.
*/
static void ReadDb(void){
char *zDb;
int i,j;
char *zLine;
char *zToken;
int iRec;
char *zField;
char *zValue;
zDb = ReadFile(DB_FILE);
if( zDb==0 ) return;
i = 0;
while( (zLine=GetLine(zDb,&i))!=0 ){
if( *zLine=='#' ) continue;
j = 0;
zToken = GetToken(zLine,&j);
if( zToken==0 ) continue;
iRec = GetInt(zToken);
if( iRec<0 ) continue;
zField = GetToken(zLine,&j);
zValue = GetToken(zLine,&j);
if( zValue==0 ) continue;
InsertEntry(iRec,zField,zValue);
}
free( zDb );
}
/* Given two lists of ConfEntry structures both of which are
** sorted and either or both of which can be empty, combine the
** two lists into a single sorted list. Return a pointer to
** the head of the new list.
**
** This is part of the merge-sort algorithm.
*/
static ConfEntry *merge(ConfEntry *left, ConfEntry *right){
ConfEntry *head = 0, *tail = 0;
while( left && right ){
int c = strcmp(left->zLabel, right->zLabel);
if( c<0 ){
if( tail ){
tail->next = left;
tail = tail->next;
}else{
head = tail = left;
}
left = left->next;
tail->next = 0;
}else{
if( tail ){
tail->next = right;
tail = tail->next;
}else{
head = tail = right;
}
right = right->next;
tail->next = 0;
}
}
if( left ){
if( tail ){
tail->next = left;
}else{
head = tail = left;
}
}else if( right ){
if( tail ){
tail->next = right;
}else{
head = tail = right;
}
}
return head;
}
/* Sort a list of ConfEntry structures. Return a pointer to
** the sorted list.
**
** The algorithm is merge-sort.
*/
static ConfEntry *sort(ConfEntry *p){
int i;
ConfEntry *pNext;
ConfEntry *a[32];
for(i=0; i<sizeof(a)/sizeof(a[0]); i++){ a[i] = 0; }
while( p ){
pNext = p->next;
p->next = 0;
for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
p = merge(a[i],p);
a[i] = 0;
}
a[i] = p;
p = pNext;
}
for(i=0; i<sizeof(a)/sizeof(a[0]); i++){
p = merge(p,a[i]);
}
return p;
}
/*
** Write the entire database back to the disk. Make sure the
** database file is owned by root and has 0600 permissions so
** that unprivileged users can't see the passwords.
*/
static void WriteDb(void){
FILE *out;
int i;
ConfEntry *p;
int fd;
char zFilename[sizeof(DB_FILE)+100];
sprintf(zFilename,"%s.%d",DB_FILE,getpid());
out = fopen(zFilename,"w");
if( out==0 ) return;
fd = fileno(out);
fchmod(fd,0600);
fchown(fd,0,0);
for(i=0; i<nRecord; i++){
aRecord[i] = p = sort(aRecord[i]);
for(p=aRecord[i]; p; p=p->next){
fprintf(out,"%d %s ",i,p->zLabel);
WriteToken(out,p->zValue);
fprintf(out,"\n");
}
}
fclose(out);
unlink(DB_FILE);
link(zFilename,DB_FILE);
unlink(zFilename);
}
/*
** Given a sequence of strings of the form
**
** LABEL=VALUE
**
** parse the strings up into LABEL and VALUE and add entries
** for each string to the database as record iRec.
*/
static void AddEntries(int iRec, int argc, char **argv){
int i, j;
char *zLabel;
char *zValue;
for(i=0; i<argc; i++){
zLabel = argv[i];
for(j=1; zLabel[j] && zLabel[j]!='='; j++){}
if( zLabel[j]!='=' ) continue;
zLabel[j] = 0;
zValue = &zLabel[j+1];
InsertEntry(iRec, zLabel, zValue);
}
}
/*
** Rearrange the order of records so that the Mth record
** becomes the Nth record.
*/
static void MoveRecord(int M, int N){
int i;
ConfEntry *pTemp;
if( nRecord<0 ) return;
if( M>=nRecord ) M = nRecord-1;
if( M<0 ) M = 0;
if( N>=nRecord ) N = nRecord-1;
if( N<0 ) N = 0;
if( M==N ) return;
if( M<N ){
pTemp = aRecord[M];
for(i=M; i<N; i++){
aRecord[i] = aRecord[i+1];
}
aRecord[N] = pTemp;
}else{
pTemp = aRecord[M];
for(i=M; i>N; i--){
aRecord[i] = aRecord[i-1];
}
aRecord[N] = pTemp;
}
}
/*
** Return TRUE if the given string is an IP address composed of
** 4 numbers between 0 and 255 separated by dots. If the string
** is an IP address, return the value in *pIp.
*/
static int IsIpAddr(char *z, int *pIp){
int a1, a2, a3, a4, rc;
if( sscanf(z,"%d.%d.%d.%d",&a1,&a2,&a3,&a4)==4
&& a1>=0 && a1<=255
&& a2>=0 && a2<=255
&& a3>=0 && a3<=255
&& a4>=0 && a4<=255
){
*pIp = (a1<<24) | (a2<<16) | (a3<<8) | a4;
rc = 1;
}else{
rc = 0;
}
return rc;
}
/*
** Look up a specific field in a specific record and return its
** value. Return the given default value if it is not found.
*/
static char *Lookup(int iRec, char *zLabel, char *zDflt){
ConfEntry *p;
if( iRec<0 || iRec>=nRecord ) return zDflt;
for(p=aRecord[iRec]; p; p=p->next){
if( strcmp(zLabel,p->zLabel)==0 ) return p->zValue;
}
return zDflt;
}
/*
** Given a command-line parameter that might be a record number,
** or an IP address, or a modem tty name, or the keyword "all",
** return a record number. The special code FIND_ERROR is returned
** if we can't find a match. FIND_ALL is returned if the keyword
** "all" appears or for a NULL string.
*/
#define FIND_ALL -1
#define FIND_ERROR -2
static int FindRecord(char *z){
int i;
int ip;
if( z==0 ){
if( nRecord==1 ) return 0;
return FIND_ALL;
}
if( strcmp(z,"all")==0 ){
return FIND_ALL;
}
i = GetInt(z);
if( i>=0 ){
if( i>=nRecord ) i = nRecord-1;
if( i<0 ) i = 0;
return i;
}
if( IsIpAddr(z,&ip) ){
int t, mask, iRec;
char *zVal;
for(iRec=0; iRec<nRecord; iRec++){
zVal = Lookup(iRec,"ip","0.0.0.0");
if( !IsIpAddr(zVal,&t) ) continue;
zVal = Lookup(iRec,"netmask","0.0.0.0");
if( !IsIpAddr(zVal,&mask) ) continue;
if( (t&mask)==(ip&mask) ) return iRec;
}
}
if( z[0]=='/' ){
int iRec;
char *zVal;
for(iRec=0; iRec<nRecord; iRec++){
zVal = Lookup(iRec,"tty","/dev/modem");
if( strcmp(zVal,z)==0 ) return iRec;
}
}
for(i=0; i<nRecord; i++){
char *zVal = Lookup(i,"service",0);
if( zVal && strcmp(zVal,z)==0 ) return i;
}
return FIND_ERROR;
}
/*
** Write a timestamp on the transcript.
*/
static void WriteTimestamp(void){
struct timeval stv;
struct timezone stz;
if( transcript==0 ) return;
gettimeofday(&stv,&stz);
fprintf(transcript,"%02ld:%02ld.%03ld", (long)(stv.tv_sec/60)%60,
(long)stv.tv_sec % 60, (long)stv.tv_usec/1000);
}
/*
** Write to a file descriptor, and also to the transcript.
*/
static void ChatWrite(char *z, int shroud){
int n, w;
if( transcript ){
if( shroud>0 ){
int i = strlen(z);
while( i>0 ){
fprintf(transcript,"%.*s",i,"**********" "**********" "***********");
i -= 30;
}
}else{
WriteHumanReadable(transcript,z);
}
}
n = strlen(z);
while( n ){
w = write(1, z, n);
if( w>0 ){
n -= w;
z += w;
}
}
}
/*
** Send output to the modem.
**
** Delay for "delay" microseconds before actually doing the write.
** This is for modems that get fouled up by input that follows to
** closely after their own output.
**
** The transmitted text is normally written to the transcript file.
** But if "shroud" is one, then "*" characters are subsitituted.
** This is used when writting the password, to keep it from view.
*/
static void ChatSend(int delay, int shroud, char *z, ...){
va_list ap;
if( z==0 ) return;
if( delay>0 ){
usleep(delay);
}
if( transcript ){
WriteTimestamp();
fprintf(transcript," Send: \"");
}
va_start(ap, z);
ChatWrite(z, shroud);
while( (z = va_arg(ap,char*))!=0 ){
ChatWrite(z, shroud);
shroud--;
}
if( transcript ){
fprintf(transcript,"\"\n");
fflush(transcript);
}
}
/*
** Read text from the input and put it into zBuf. Reading continues
** until:
**
** * No characters are received after waiting for "timeout"
** seconds, or
**
** * Some characters have been received but no characters have
** appeared for 500 milliseconds or more.
**
** Only characters since the last new-line are returned in zBuf.
** But all received characters are written to the transcript.
**
** Characters in zBuf[] when this routine is called are not erased
** or overwritten. Newly read characters are appended. Unless
** a new-line or carriage routine is seen, when all characters are
** deleted.
**
** The function returns the number of characters that were received
** during the current call to this function. This number will be
** zero if a timeout occurs.
*/
static int ChatRecv(char *zBuf, int nBuf, int timeout){
int n; /* Number of bytes returned from a single read() */
int i; /* Loop counter */
int idle = 0; /* Number of 1/10ths of second since last read() */
int got; /* How many characters previously read */
int nChar = 0; /* Total number of characters read */
time_t start;
time_t now;
/* Remember when we began looking... */
time(&start);
/* Figure out how much text is already in zBuf[]. New
** text will be appended.
*/
for(got=0; zBuf[got]; got++){}
if( got>0 && (zBuf[got-1]=='\n' || zBuf[got-1]=='\r') ){ got = 0; }
zBuf[got] = 0;
/* Start reading input...
*/
while( got<nBuf-1 && (nChar==0 || idle<5) ){
time(&now);
if( start+timeout < now ) break;
n = read(0, &zBuf[got], nBuf-1-got);
if( n<0 ) n = 0;
nChar += n;
if( n==0 ){
usleep(100000);
idle++;
continue;
}
zBuf[got+n] = 0;
idle = 0;
got += n;
/* Remove any characters that are followed by a '\n'. Except,
** don't remove characters if the '\n' is followed by nothing
** unless '\n' is on a line by itself or preceded by a single
** '\r'.
*/
for(i=0; i<got; i++){
if( zBuf[i]=='\n' && (zBuf[i+1]!=0 || i==0 || (i==1 && zBuf[0]=='\r')) ){
if( transcript ){
int c;
WriteTimestamp();
fprintf(transcript," Skip: \"");
c = zBuf[i+1];
zBuf[i+1] = 0;
WriteHumanReadable(transcript,zBuf);
zBuf[i+1] = c;
fprintf(transcript,"\"\n");
fflush(transcript);
}
strcpy(zBuf, &zBuf[i+1]);
got -= i+1;
i = -1;
}
}
}
if( transcript && zBuf[0] ){
WriteTimestamp();
fprintf(transcript," Recv: \"");
WriteHumanReadable(transcript,zBuf);
fprintf(transcript,"\"\n");
fflush(transcript);
}
return nChar;
}
/*
** Return true if zPattern occurs anywhere in zBuf
*/
static int Match(char *zBuf, char *zPattern){
int len = strlen(zPattern);
while( *zBuf ){
if( *zBuf==*zPattern && strncmp(zBuf,zPattern,len)==0 ){
if( transcript ){
WriteTimestamp();
fprintf(transcript," Match: \"");
WriteHumanReadable(transcript,zPattern);
fprintf(transcript,"\"\n");
fflush(transcript);
}
return 1;
}
zBuf++;
}
return 0;
}
/*
** The following array maps 8-bit characters into 7-bit characters
** and upper-case into lower-case.
*/
static unsigned char SevenBit[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
128, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
};
/*
** Compare two strings. In the first string, use only the lower
** 7 bits and convert all upper case to lower case letters.
*/
static int StrNCmp7(char *zBuf, char *zPattern, int n){
while( n-- ){
int a = SevenBit[(int)*((unsigned char*)zBuf++)];
int b = SevenBit[(int)*((unsigned char*)zPattern++)];
if( a==0 || b==0 || a!=b ){ return a - b; }
}
return 0;
}
/*
** Like Match(), but only uses the lower seven 7 bits of zBuf
** and treats all characters in zBuf as lower-case.
*/
static int Match7(char *zBuf, char *zPattern){
int len = strlen(zPattern);
while( *zBuf ){
if( SevenBit[*(unsigned char*)zBuf]==SevenBit[*(unsigned char*)zPattern]
&& StrNCmp7(zBuf,zPattern,len)==0 ){
if( transcript ){
WriteTimestamp();
fprintf(transcript," Match: \"");
WriteHumanReadable(transcript,zPattern);
fprintf(transcript,"\"\n");
fflush(transcript);
}
return 1;
}
zBuf++;
}
return 0;
}
/*
** Return TRUE if the input line looks like a prompt of some kind.
** A "prompt" is a seqence of words followed by a colon, or perhaps
** something like "->".
*/
static int IsPrompt(char *zLine){
int i;
for(i=0; zLine[i]!=0 && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
while( i>0 && isspace(zLine[i-1]) ){ i--; }
i--;
if( i<=0 ) return 0;
if( zLine[i]==':' ) return 1;
if( zLine[i]=='?' ) return 1;
if( zLine[i]=='>' ) return 1;
if( zLine[i]=='$' ) return 1;
return 0;
}
#define MAX_TIME 70 /* Maximum time to connect */
#define DIAL_TIMEOUT "60" /* How long to wait for dialing to complete */
#define CHAT_TIMEOUT "3" /* How long to wait for responses after dialing */
/* This routine is pretty much the whole point. Assuming a
** modem is connected on file descriptors 0 and 1, initialize
** the modem, dial the phone number, supply user ID and password
** (if requested). Return successfully (0) if we see the start of
** PPP communication and return an error (1) if anything goes
** wrong.
**
** This routine runs as root so that it will have permission
** to open the output file associated with WriteChatStat().
*/
static int Chat(int iRec){
struct termios tio_0_saved;
struct termios tio_1_saved;
struct termios tio_new;
int flags;
int i;
char *zVal;
int timeout;
int dial_timeout;
int nTimeout = 0;
time_t start, now;
int rc = 4;
int chatDelay = 0;
char *zChat;
char *zPhone;
char *zExpect[10];
char *zReply[10];
char zLine[1000];
tcgetattr(0,&tio_0_saved);
tcgetattr(1,&tio_1_saved);
tio_new = tio_0_saved;
cfmakeraw(&tio_new);
tcsetattr(0,TCSANOW,&tio_new);
tio_new = tio_1_saved;
cfmakeraw(&tio_new);
tcsetattr(1,TCSANOW,&tio_new);
flags = fcntl(0, F_GETFL);
fcntl(0, F_SETFL, O_NONBLOCK);
sprintf(zLine,"%s/transcript.%d",DB_DIR,iRec);
transcript = fopen(zLine,"a");
if( transcript ){
fchmod(fileno(transcript),0644);
fchown(fileno(transcript),0,0);
}
zChat = Lookup(iRec,"chat","yes");
chatDelay = atoi(Lookup(iRec,"delay","0"))*1000;
for(i=0; i<=9; i++){
static char *dfltInit[10] = { "atz", "at&d3", };
sprintf(zLine,"init%d",i);
zVal = Lookup(iRec,zLine,dfltInit[i]);
if( zVal ){
ChatSend(chatDelay,0,zVal,"\r",0);
zLine[0] = 0;
ChatRecv(zLine,sizeof(zLine),1000);
if( !Match(zLine,"OK") ){
ChatRecv(zLine,sizeof(zLine),1000);
}
}
sprintf(zLine,"expect%d",i);
zExpect[i] = Lookup(iRec,zLine,0);
sprintf(zLine,"reply%d",i);
zReply[i] = Lookup(iRec,zLine,0);
}
zPhone = Lookup(iRec,"phone",0);
if( zPhone==0 ){
WriteChatStat(iRec,"No phone number specified");
return 1;
}
ChatSend(chatDelay,0,"atd", zPhone, "\r", 0);
timeout = dial_timeout = atoi(Lookup(iRec,"dialtimeout",DIAL_TIMEOUT));
time(&start);
zLine[0] = 0;
nTimeout = 0;
while( 1 ){
int nChar = ChatRecv(zLine,sizeof(zLine),timeout);
time(&now);
if( now-start > MAX_TIME ){
WriteChatStat(iRec,"Login timeout at");
break;
}
if( zLine[0]=='a' && strncmp(zLine,"atd",3)==0
&& zLine[3]==zPhone[0] && strncmp(&zLine[3],zPhone,strlen(zPhone))==0 ){
/* Skip the echo of the dial command to the modem */
zLine[0] = 0;
continue;
}
if( nChar==0 ){
nTimeout++;
if( timeout==dial_timeout ){
WriteChatStat(iRec,"Modem could not connect at");
break;
}else if( (nTimeout>2 && *Lookup(iRec,"autostart","yes")!='n')
|| zChat[0]=='n' ){
rc = 0;
break;
}else{
ChatSend(chatDelay,0,"\r",0);
continue;
}
}
timeout = atoi(Lookup(iRec,"chattimeout",CHAT_TIMEOUT));
nTimeout = 0;
for(i=0; i<=9; i++){
if( zExpect[i]==0 || zReply[i]==0 ) continue;
if( Match7(zLine,zExpect[i]) ){
zLine[0] = 0;
if( strcmp(zReply[i],"ACCEPT")==0 ){
rc = 0;
goto chat_done;
}else if( strcmp(zReply[i],"FAIL")==0 ){
goto chat_done;
}else{
ChatSend(chatDelay,0,zReply[i],"\r",0);
nTimeout = 0;
timeout = 3;
break;
}
}
}
if( Match(zLine,"BUSY") ){
WriteChatStat(iRec,"Busy signal at");
break;
}else if( Match(zLine,"NO CARRIER") ){
WriteChatStat(iRec,"No carrier at");
break;
}else if( Match(zLine,"NO DIALTONE") ){
WriteChatStat(iRec,"No dialtone at");
break;
}else if( Match(zLine,"NO ANSWER") ){
WriteChatStat(iRec,"Ringing but no answer at");
break;
}else if( Match7(zLine,"ogin:") || Match7(zLine,"rname:")
|| Match7(zLine,"-on:") || Match7(zLine,"serid:") ){
zVal = Lookup(iRec,"user","anonymous");
ChatSend(chatDelay,0,zVal,"\r",0);
zLine[0] = 0;
}else if( Match7(zLine,"ssword:") ){
zVal = Lookup(iRec,"password","");
ChatSend(chatDelay,1,zVal,"\r",0);
zLine[0] = 0;
}else if( Match(zLine,"~\377}#") || Match(zLine,"\300!}!") ){
rc = 0;
break;
}else if( Match(zLine,"CONNECT") && zChat[0]=='n' ){
rc = 0;
break;
#if 0
}else if( Match7(zLine,"destination:") || Match7(zLine,"response:") ){
ChatSend(chatDelay,0,"ppp\r",0);
zLine[0] = 0;
#endif
}else if( IsPrompt(zLine) ){
ChatSend(chatDelay,0,"ppp\r",0);
zLine[0] = 0;
}else{
/* Ignore it */
}
}
chat_done:
if( transcript ){
WriteTimestamp();
fprintf(transcript, rc ? " Fail\n" : " Accept\n");
fclose(transcript);
}
fcntl(0,F_SETFL,flags);
tcsetattr(0,TCSANOW,&tio_0_saved);
tcsetattr(1,TCSANOW,&tio_1_saved);
return rc;
}
/* Print status information for a given service.
*/
static void PrintStatus(int iRec){
char *zService;
char *zStatus;
char *zDiald;
int pidDiald;
char zBuf[30];
char zFile[sizeof(DB_DIR)+100];
if( iRec<0 || iRec>=nRecord ) return;
sprintf(zFile,"%s/diald.%d",DB_DIR,iRec);
zDiald = ReadFile(zFile);
if( zDiald==0 || (pidDiald=atoi(zDiald))<=0
|| kill(pidDiald,0)!=0 ){
zDiald = "";
}else{
zDiald = "(diald running) ";
}
sprintf(zBuf,"service #%d",iRec);
zService = Lookup(iRec,"service",zBuf);
sprintf(zFile,"%s/status.%d",DB_DIR,iRec);
zStatus = ReadFile(zFile);
if( zStatus==0 ){
printf("%s: %sStatus Unknown\n",zService, zDiald);
}else{
printf("%s: %s%s",zService, zDiald, zStatus);
free( zStatus );
}
}
/*
** Return true if the given string contains a space or $ character
** and therefore needs to be quoted for the shell.
*/
static int NeedQuote(char *z){
while( *z ){
if( *z==' ' || *z=='$' || *z=='"' ) return 1;
z++;
}
return 0;
}
/*
** Open the transcript for record N and write the given argv[] string
** to it.
*/
static void WriteArgvToTranscript(int iRec, char **argv){
int size;
int i;
char *zStr, *z;
char zFile[sizeof(DB_DIR)+20];
size = 0;
for(i=0; argv[i]; i++){
size += strlen(argv[i]) + 3;
}
zStr = z = malloc( size );
if( zStr ){
for(i=0; argv[i]; i++){
if( NeedQuote(argv[i]) ){
*z++ = '\'';
strcpy(z,argv[i]);
z += strlen(z);
*z++ = '\'';
}else{
strcpy(z,argv[i]);
z += strlen(z);
}
if( argv[i+1] ){
*z++ = ' ';
}
}
*z = 0;
sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
WriteToFile(zFile, 0600, "%s\n", zStr);
}
}
/* Check to see if diald is already running. Return a process ID
** for diald if it is running. Or return 0 if there is no diald
** running.
*/
static int DialdPid(int iRec){
char *zDiald;
int pidDiald;
char zDialdFile[sizeof(DB_DIR)+100];
sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
zDiald = ReadFile(zDialdFile);
if( zDiald==0 ) return 0;
pidDiald = atoi(zDiald);
free(zDiald);
if( pidDiald==0 ){
unlink(zDialdFile);
return 0;
}
if( kill(pidDiald,0) ){
unlink(zDialdFile);
return 0;
}
return pidDiald;
}
/*
** Shutdown the diald process
*/
static int StopDiald(int iRec){
int pidDiald;
if( (pidDiald = DialdPid(iRec))!=0 ){
kill(pidDiald,SIGTERM);
}
return 0;
}
/* Attempt to hang up the given port.
*/
static void Hangup(int iRec){
char *zDev;
char *zPpp;
int pidPpp = -1;
int pidDiald = -1;
int fd;
int tries;
struct termios ios;
char zFile[sizeof(DB_DIR)+20];
WriteStatus(iRec,"hangup at");
/* Send a SIGINT to any diald process that is running.
** This is suppose to make diald hangup the line.
*/
pidDiald = DialdPid(iRec);
if( pidDiald ){
kill(pidDiald,SIGINT);
return;
}
/* We only reach this point if diald is not running.
** Send a SIGTERM to any running ppp process. This is suppose
** to make ppp hang up the line and exit
*/
sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
zPpp = ReadFile(zFile);
if( zPpp && (pidPpp=atoi(zPpp))>0 ){
kill(pidPpp,SIGTERM);
sleep(1);
}
/* Try to open the TTY. Try as many as three times waiting for
** one second between each attempt. After the second failed attempt,
** send a SIGKILL to any diald or ppp process out there.
*/
zDev = Lookup(iRec,"tty","/dev/modem");
tries = 0;
while( (fd=open(zDev,O_RDWR|O_NONBLOCK))<0 && tries<2 ){
if( tries==1 ){
if( pidPpp>0 ){
kill(pidPpp, SIGKILL);
}else if( pidDiald>0 ){
kill(pidDiald, SIGKILL);
}
}
tries++;
sleep(1);
}
/* Change the buad rate of the TTY to 0. This is suppose to make
** the DTR line go low, which should make the modem hangup.
*/
if( fd>=0 ){
tcgetattr(fd, &ios);
cfsetospeed(&ios, B0);
tcsetattr(fd, TCSANOW, &ios);
close(fd);
}
}
/*
** Write into the given buffer the name of a file that will contain
** the PID of the PPP process that is calling on serial device zDev.
** Assume the size of the buffer into which we write is at least
** sizeof(DB_DIR)+100.
**
** The zDev probably contains '/' characters. These must be converted
** to something different.
*/
static void MakeDeviceFile(char *zFile, char *zDev){
int i;
sprintf(zFile,"%s/device%.80s", DB_DIR, zDev);
i = strlen(DB_DIR) + 5;
while( zFile[i] ){
if( zFile[i]=='/' ){ zFile[i] = '.'; }
i++;
}
}
/*
** Given a modem device name, find the service number.
*/
static int DeviceToRec(char *zDev){
char *z;
int iRec;
char zFile[sizeof(DB_DIR)+100];
MakeDeviceFile(zFile, zDev);
z = ReadFile(zFile);
if( z==0 || (iRec=atoi(z))<0 || iRec>=nRecord ){
for(iRec=0; iRec<nRecord; iRec++){
z = Lookup(iRec,"tty","/dev/modem");
if( strcmp(z,zDev)==0 ) break;
}
if( iRec>nRecord ) iRec = FIND_ERROR;
}
return iRec;
}
/* This routine is called when "pppd" executes "ip-up".
*/
static void IpUp(char **argv){
int iRec;
int i;
char zFile[sizeof(DB_DIR)+20];
iRec = DeviceToRec(argv[1]);
if( iRec<0 ) return;
WriteStatus(iRec,"%s %s to %s\nUp since",argv[0],argv[3],argv[4]);
sprintf(zFile,"%s/pid.%d", DB_DIR, iRec);
unlink(zFile);
for(i=0; i<=9; i++){
char *zNet;
char zRoute[1000];
char zLabel[20];
sprintf(zLabel,"route%d",i);
zNet = Lookup(iRec,zLabel,0);
if( zNet==0 ) continue;
sprintf(zRoute,"/sbin/route add -net %.100s %.100s", zNet, argv[0]);
system(zRoute);
}
}
/* This routine is called when "pppd" executes "ip-down".
*/
static void IpDown(char **argv){
int iRec;
char zFile[sizeof(DB_DIR)+80];
iRec = DeviceToRec(argv[1]);
if( iRec<0 ) return;
WriteStatus(iRec,"down since");
MakeDeviceFile(zFile, argv[1]);
unlink(zFile);
sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
unlink(zFile);
}
/*
** This routine creates a session log for the most recent login
** attempt. The session log consists of two data sources:
**
** 1. The modem dialing and logon transcript.
**
** 2. All messages written to the /var/log/messages file by pppd.
**
** The session log is overwritten after every logon attempt.
*/
static void MakeSessionLog(time_t startTime, int pid, int iRec){
FILE *out;
char *z;
struct tm *pTm;
char zFile[sizeof(DB_DIR)+100];
char zCmd[sizeof(DB_DIR)+100];
out = fopen(SESSIONLOG,"w");
if( out==0 ) return;
fchmod(fileno(out),0644);
fprintf(out,"This is a diagnostic log of the most recent login\n"
"attempt by eznet.\n\n");
z = Lookup(iRec,"service",0);
if( z ){
fprintf(out, " ISP Name: \"%s\"\n", z);
}
pTm = localtime(&startTime);
fprintf(out," Call Initiated: %s\n", asctime(pTm));
sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
z = ReadFile(zFile);
if( z ){
int i;
for(i=0; z[i] && z[i]!='\n'; i++){}
fprintf(out,
"The UNIX command used to initiate the connection attempt was: \n\n"
"%.*s\n\n",
i, z);
if( z[i] ){ i++; }
fprintf(out,"The interaction with the modem was as follows:\n\n"
"%s\n", &z[i]);
free(z);
}else{
fprintf(out,"The modem transcript could not be located!\n\n");
}
fprintf(out,
"The following messages were written to the system log\n"
"(in /var/log/messages) by pppd as it attempted to negotiate\n"
"a PPP connection.");
z = Lookup(iRec,"debug","n");
if( z && *z=='y' ){
fprintf(out," Details may be omitted by clearing the\n"
"\"debug=%s\" property.\n\n", z);
}else{
fprintf(out," To see more detail, set the \"debug=yes\"\n"
"property and reattempt the logon.\n\n");
}
fclose(out);
sprintf(zCmd,"tail -200 /var/log/messages | grep 'pppd.%d.' >>%s",
pid, SESSIONLOG);
system(zCmd);
}
/* This routine is called in order to attempt to establish
** a connection. Return 0 if successful and non-zero if we fail.
**
** The process ID of the PPP process is written to *piChild if piChild!=0.
*/
static int Dial(int iRec, int *piChild){
char *zPw;
char *zDevice;
char *zUser;
char *zPpp;
char *zService;
char *zIdle;
int pidPpp;
int pidDiald;
int rc;
int isup = 1;
time_t startTime;
char zDeviceFile[sizeof(DB_DIR)+100];
char zChatStat[sizeof(DB_DIR)+100];
char zPidFile[sizeof(DB_DIR)+100];
char zConnect[200];
char zServiceBuf[50];
/* Check to see if we are already running directly.
*/
startTime = time(0);
sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
zPpp = ReadFile(zPidFile);
if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
return 0;
}
/* Check to see if diald is running. If it is, then
** just send it a SIGUSR1 to bring the link up.
*/
if( (pidDiald = DialdPid(iRec))>0 ){
kill(pidDiald,SIGUSR1);
return 0;
}
/* Initialize the ipup, ipdown, chap-secrets and pap-secrets files.
*/
SetIpUpDown("up");
SetIpUpDown("down");
sprintf(zServiceBuf,"x%d",iRec);
zService = Lookup(iRec,"service",zServiceBuf);
zUser = Lookup(iRec,"user","anonymous");
zPw = Lookup(iRec, "password", "*");
if( zPw ){
SetSecrets("pap-secrets",zService,zUser,zPw);
SetSecrets("chap-secrets",zService,zUser,zPw);
}
zDevice = Lookup(iRec, "tty", "/dev/modem");
/* Set the device file to the current ordinal number. This is needed
** by the ipup and ipdown scripts.
*/
MakeDeviceFile(zDeviceFile, zDevice);
WriteToFile(zDeviceFile, 0644, "%d\n", iRec);
WriteToFile(zPidFile, 0644, "%d\n", getpid());
/* Set the status to dialing.
*/
WriteStatus(iRec, "dialing since");
/* The child does the work. The parent waits for it to finish.
*/
rc = fork();
if( rc<0 ){
WriteStatus(iRec,"fork failed");
return 1;
}
if( rc==0 ){
/* The child process */
int argc = 0;
char *z;
int i;
char zLabel[100];
char *argv[200];
argv[argc++] = Lookup(iRec, "pppd", PPP_EXE);
argv[argc++] = zDevice;
argv[argc++] = Lookup(iRec,"baud","115200");
argv[argc++] = "connect";
sprintf(zConnect,SELF " chat %d",iRec);
argv[argc++] = zConnect;
z = Lookup(iRec, "defaultroute", "y");
if( *z=='y' ){
argv[argc++] = "defaultroute";
}
argv[argc++] = "lock";
argv[argc++] = "modem";
argv[argc++] = "asyncmap";
argv[argc++] = Lookup(iRec,"asyncmap","0");
z = Lookup(iRec,"debug",0);
if( z && *z=='y' ){
argv[argc++] = "debug";
}
argv[argc++] = "crtscts";
argv[argc++] = "-detach";
z = Lookup(iRec,"persist","no");
if( *z=='y' ){
argv[argc++] = "persist";
}else{
zIdle = Lookup(iRec,"idle","600");
if( atoi(zIdle)>0 ){
z = Lookup(iRec,"pppversion",DFLT_PPP_VERSION);
argv[argc++] = strcmp(z,"2.3")<0 ? "idle-disconnect" : "idle";
argv[argc++] = zIdle;
}
}
argv[argc++] = "user";
argv[argc++] = zUser;
argv[argc++] = "remotename";