pkg://Eznet-1.11.1-1.src.rpm:28948/Eznet-1.11.1.tar.gz
info downloads
Eznet-1.11.1/ 40755 0 0 0 6706444444 10656 5 ustar root root Eznet-1.11.1/eznet.c 100644 0 0 171473 6706436236 12321 0 ustar root root /*
** 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";
argv[argc++] = zService;
for(i=0; i<10; i++){
sprintf(zLabel,"pppopt%d",i);
z = Lookup(iRec,zLabel,0);
if( z==0 ) break;
argv[argc++] = z;
}
argv[argc] = 0;
WriteArgvToTranscript(iRec,argv);
execv(argv[0],argv);
exit(0);
}else{
/* The parent process */
char zPppFile[sizeof(DB_DIR)+20];
if( piChild ) *piChild = rc;
sprintf(zPppFile,"%s/ppp.%d", DB_DIR, iRec);
WriteToFile(zPppFile,0644,"%d\n",rc);
while( access(zPidFile,0)==0 ){
int status;
if( waitpid(rc,&status,WNOHANG)>0 ){
isup = 0;
break;
}
sleep(1);
}
sprintf(zChatStat,"%s/chatstat.%d", DB_DIR, iRec);
if( !isup ){
char *z = ReadFile(zChatStat);
if( z ){
WriteStatus(iRec,z);
}else{
WriteStatus(iRec,"pppd failure at");
}
unlink(zDeviceFile);
unlink(zPppFile);
}
unlink(zPidFile);
unlink(zChatStat);
MakeSessionLog(startTime, rc,iRec);
PrintStatus(iRec);
}
return !isup;
}
/* This routine is called in order to active the diald daemon.
*/
static int StartDiald(int iRec){
char *zPpp;
char *zDiald;
char *zPw;
char *zUser;
char *zService;
char *z;
int pidPpp;
int pidDiald;
int rc;
int i;
FILE *out;
char zConfFile[sizeof(DB_DIR)+100];
char zPidFile[sizeof(DB_DIR)+20];
char zDialdFile[sizeof(DB_DIR)+100];
char zServiceBuf[50];
/* Check to see if diald is already running.
*/
sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
zDiald = ReadFile(zDialdFile);
if( zDiald && (pidDiald=atoi(zDiald))>0 && kill(pidDiald,0)==0 ){
return 0;
}
/* Check to see if we are already running directly. If so, then
** do a hangup before continuing.
*/
sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
zPpp = ReadFile(zPidFile);
if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
Hangup(iRec);
}
/* Initialize the chap-secrets and pap-secrets files.
*/
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);
}
/* Create the diald configuration script.
*/
sprintf(zConfFile, "%s/diald.conf.%d", DB_DIR, iRec);
out = fopen(zConfFile,"w");
if( out==0 ){
WriteStatus(iRec, "configuration failed");
return 1;
}
fprintf(out,"mode ppp\n");
fprintf(out,"device %s\n", Lookup(iRec,"tty","/dev/modem"));
fprintf(out,"connect '" SELF " chat %d'\n", iRec);
fprintf(out,"speed %s\n", Lookup(iRec,"baud","115200"));
z = Lookup(iRec,"defaultroute","y");
if( *z=='y' ){
fprintf(out,"defaultroute\n");
}
fprintf(out,"local %s\n", Lookup(iRec,"local","127.0.0.100"));
fprintf(out,"remote %s\n", Lookup(iRec,"remote","127.0.0.101"));
fprintf(out,
"lock\n"
"modem\n"
"crtscts\n"
"-reroute\n"
"dynamic\n"
"-daemon\n"
);
fclose(out);
/* Fire up the diald process
*/
rc = fork();
if( rc<0 ){
WriteStatus(iRec,"fork failed");
return 1;
}
if( rc==0 ){
/* The child process */
int argc = 0;
char *argv[30];
argv[argc++] = Lookup(iRec, "diald", DIALD_EXE);
argv[argc++] = "-f";
argv[argc++] = zConfFile;
argv[argc++] = "--";
argv[argc++] = "remotename";
argv[argc++] = zService;
argv[argc++] = "user";
argv[argc++] = zUser;
for(i=0; i<10; i++){
char zLabel[100];
sprintf(zLabel,"pppopt%d",i);
z = Lookup(iRec,zLabel,0);
if( z==0 ) break;
argv[argc++] = z;
}
argv[argc] = 0;
WriteArgvToTranscript(iRec,argv);
close(0);
close(1);
close(2);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
open("/dev/null",O_RDWR);
execv(argv[0],argv);
exit(0);
}
/* The parent process */
WriteToFile(zDialdFile,0644,"%d\n",rc);
sleep(1);
if( kill(rc,0) ){
WriteStatus(iRec,"diald startup failed at");
rc = 1;
}else{
WriteStatus(iRec,"diald started at");
rc = 0;
}
return rc;
}
/*
** Given a configuration file entry, return a "safe" value for
** this entry. If the entry is the password and the real user
** is anything other than root, return "*********".
*/
static char *SafeValue(ConfEntry *p){
char *zVal;
if( strcmp(p->zLabel,"password")==0 && getuid()!=0 ){
zVal = "**********";
}else{
zVal = p->zValue;
}
return zVal;
}
/* Print a usage comment and die. A call to this
** routine never returns.
*/
static void usage(char *argv0){
fprintf(stderr,
"User commands:\n"
" %s add ORDINAL FIELD=VALUE...\n"
" %s change ORDINAL|SERVICE ?FIELD=VALUE?...\n"
" %s delete ORDINAL|SERVICE|IP\n"
" %s dialdoff ORDINAL|IP|DEVICE\n"
" %s dialdon ORDINAL|IP|DEVICE\n"
" %s down ORDINAL|IP|DEVICE|all\n"
" %s list ?ORDINAL|SERVICE?\n"
" %s log\n"
" %s move ORDINAL ORDINAL\n"
" %s names\n"
" %s status\n"
" %s up SERVICE|IP|ORDINAL\n"
" %s wait SERVICE|IP|ORDINAL\n"
"\n"
"Commands for internal use only:\n"
" %s chat ORDINAL\n"
" %s ipdown INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
" %s ipup INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
"\n"
"Field names:\n"
" phone = phone number for modem to dial\n"
" user = user login name\n"
" password = login password\n"
" tty = serial port for the modem\n"
" baud = baud rate\n"
" service = name of ISP\n"
" chat = \"no\" to ignore \"login:\" prompts, etc.\n"
" autostart = \"no\" to prevent starting pppd if no \"login:\" seen\n"
" pppversion = what version of pppd is being used\n"
" debug = \"yes\" to turn on pppd debugging\n"
" expectN = Nth extra expect string (N between 1 and 9)\n"
" replyN = Reply to issue when expectN is seen\n"
" initN = Nth modem initialization string (N between 1 and 9)\n"
" diald = name of diald executable\n"
" pppd = name of pppd executable\n"
" local = local IP address for diald slip link\n"
" remote = remote IP address for diald slip link\n"
" idle = number of seconds for idle timeout\n"
" persist = \"yes\" for a permanent connection\n"
" defaultroute = \"no\" to prevent creating a default route\n"
" pppoptN = Nth extra parameter to pppd\n"
" routeN = Add a route to the specified network when pppd comes up\n"
" ip = network that this service gives access to\n"
" netmask = netmask for the ip= network\n"
" dialtimeout = time allowed for the modem to connect\n"
" chattimeout = time allowed for chat responses\n"
" errordelay = \"wait\" command waits this long after an error\n",
argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0,
argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0
);
exit(1);
}
int main(int argc, char **argv){
int iRec;
int rc = 0;
if( argc<2 ) usage(argv[0]);
if( access(DB_DIR,0) ){
mkdir(DB_DIR,0755);
chown(DB_DIR,0,0);
if( access(DB_DIR,0) ){
fprintf(stderr,"%s: missing directory \"%s\"\n", argv[0], DB_DIR);
exit(1);
}
}
ReadDb();
if( (argc==2 || argc==3) && strcmp(argv[1],"list")==0 ){
int iRec = FindRecord(argv[2]);
Unprivileged();
if( iRec<0 || iRec>=nRecord ){
int i;
for(i=0; i<nRecord; i++){
ConfEntry *p = aRecord[i] = sort(aRecord[i]);
if( p==0 ) continue;
printf("Record %d:\n",i);
for(; p; p=p->next){
printf(" %s = %s\n",p->zLabel, SafeValue(p));
}
}
}else{
ConfEntry *p = aRecord[iRec] = sort(aRecord[iRec]);
for(; p; p=p->next){
printf(" %s = %s\n",p->zLabel, SafeValue(p));
}
}
}else if( argc==2 && strcmp(argv[1],"log")==0 ){
char *z;
Unprivileged();
z = ReadFile(SESSIONLOG);
if( z ){
printf("%s",z);
}
}else if( (argc==2 || argc==3 ) && strcmp(argv[1],"tcllist")==0 ){
int i;
Unprivileged();
for(i=0; i<nRecord; i++){
ConfEntry *p = aRecord[i] = sort(aRecord[i]);
if( p==0 ) continue;
for(; p; p=p->next){
printf("set eznet(%d:%s) ",i,p->zLabel);
WriteTcl(stdout,SafeValue(p));
printf("\n");
}
}
}else if( argc==2 && strcmp(argv[1],"names")==0 ){
int i;
Unprivileged();
for(i=0; i<nRecord; i++){
char *z = Lookup(i,"service",0);
if( z ){
printf("%s\n",z);
}else{
printf("%d\n",i);
}
}
}else if( argc>=3 && strcmp(argv[1],"add")==0 ){
int iRec;
Unprivileged();
iRec = FindRecord(argv[2]);
if( iRec==FIND_ERROR ){
AddEntries(nRecord, argc-2, argv+2);
}else if( iRec>=nRecord ){
AddEntries(nRecord, argc-3, argv+3);
}else{
AddEntries(nRecord, argc-3, argv+3);
MoveRecord(nRecord, iRec);
}
WriteDb();
}else if( argc==3 && strcmp(argv[1],"delete")==0 ){
int iRec = FindRecord(argv[2]);
Unprivileged();
if( iRec==FIND_ALL ){
nRecord = 0;
}else if( iRec==FIND_ERROR ){
usage(argv[0]);
}else{
MoveRecord(iRec,nRecord-1);
aRecord[nRecord-1] = 0;
}
WriteDb();
}else if( argc>=4 && strcmp(argv[1],"change")==0 ){
int iRec;
Unprivileged();
iRec = FindRecord(argv[2]);
if( iRec==FIND_ALL ){
usage(argv[0]);
}else{
if( iRec==FIND_ERROR ){
iRec = nRecord==1 ? 0 : nRecord;
}
AddEntries(iRec, argc-3, argv+3);
}
WriteDb();
}else if( argc==4 && strcmp(argv[1],"move")==0 ){
int from, to;
Unprivileged();
if( argc!=4 ) usage(argv[0]);
from = GetInt(argv[2]);
to = GetInt(argv[3]);
if( from>=0 && to>=0 ) MoveRecord(from,to);
WriteDb();
}else if( argc==3 && strcmp(argv[1],"chat")==0 ){
iRec = FindRecord(argv[2]);
if( iRec<0 ) usage(argv[0]);
rc = Chat(iRec);
}else if( (argc==2 || argc==3) && strcmp(argv[1],"status")==0 ){
iRec = FindRecord(argv[2]);
Unprivileged();
if( iRec<0 ){
for(iRec=0; iRec<nRecord; iRec++){
PrintStatus(iRec);
}
}else{
PrintStatus(iRec);
}
}else if( (argc==2 || argc==3) && strcmp(argv[1],"down")==0 ){
iRec = FindRecord(argv[2]);
/* Unprivileged(); --- Yeah. Let anybody do a hangup... */
if( iRec==FIND_ALL ){
system("kill -TERM `pidof pppd` 2>/dev/null");
system("kill -TERM `pidof diald` 2>/dev/null");
for(iRec=0; iRec<nRecord; iRec++){
Hangup(iRec);
}
}else{
Hangup(iRec);
}
}else if( argc==7 && strcmp(argv[1],"ipup")==0 ){
Unprivileged();
IpUp(&argv[2]);
}else if( argc==7 && strcmp(argv[1],"ipdown")==0 ){
Unprivileged();
IpDown(&argv[2]);
}else if( (argc==2 || argc==3) && strcmp(argv[1],"up")==0 ){
if( access(SELF,X_OK)!=0 ){
fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
exit(1);
}
iRec = FindRecord(argv[2]);
if( iRec<0 ){ usage(argv[0]); }
rc = Dial(iRec, 0);
}else if( (argc==2 || argc==3) && strcmp(argv[1],"wait")==0 ){
int child;
if( access(SELF,X_OK)!=0 ){
fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
exit(1);
}
iRec = FindRecord(argv[2]);
if( iRec<0 ){ usage(argv[0]); }
rc = Dial(iRec, &child);
if( rc==0 ){
int status;
waitpid(child, &status, 0);
}else{
int delay = atoi(Lookup(iRec,"errordelay","0"));
if( delay>0 ) sleep(delay);
}
}else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdon")==0 ){
if( access(SELF,X_OK)!=0 ){
fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
exit(1);
}
iRec = FindRecord(argv[2]);
if( iRec<0 ){ usage(argv[0]); }
rc = StartDiald(iRec);
}else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdoff")==0 ){
iRec = FindRecord(argv[2]);
if( iRec<0 ){ usage(argv[0]); }
rc = StopDiald(iRec);
}else if( argc==3 && strcmp(argv[1],"delete")==0 ){
Unprivileged();
iRec = FindRecord(argv[2]);
if( iRec>=0 && iRec<nRecord ){
MoveRecord(iRec, nRecord-1);
nRecord--;
WriteDb();
}
}else{
Unprivileged();
usage(argv[0]);
}
return rc;
}
Eznet-1.11.1/ezsetup 100755 0 0 10037 6706436236 12421 0 ustar root root #!/bin/sh
MSG() {
cat <<EOF
# ezsetup
#
# Setup PPP connection with eznet: (2-24-98) Kent Robotti
#
# This script will ask you a few questions to setup a PPP connection
# to your service provider using 'eznet'.
#
# ezsetup -s <Setup eznet>
# ezsetup -h <This message>
EOF
}
if [ "$1" = "" -o "$1" = "-h" ]; then
MSG
exit
fi
if [ ! "$UID" = "0" ]; then
echo "You need to be 'root' to run this script."
exit
fi
if [ "$1" = "-s" ]; then
if [ ! -x /usr/bin/eznet ]; then
echo "You don't have '/usr/bin/eznet' installed, i can't continue."
exit
fi
if [ -s /etc/ppp/eznet/eznet.conf ]; then
echo
echo "You have a /etc/ppp/eznet/eznet.conf file, do you"
echo "want to remove it or add a new entry to it?"
echo -n "Should i remove it? (N/y) : "
read ans
if [ "$ans" = "y" -o "$ans" = "Y" ]; then
rm -f /etc/ppp/eznet/* 2>/dev/null
else
ADD="YES"
fi
fi
echo
echo "Example: 1234567"
echo -n "What is your service providers Phone Number? : "
read PN
if [ "$PN" = "" ]; then
echo "Cancelled."
exit
fi
echo
echo "Examples: something.com something.net something.edu"
echo -n "What is your service providers Domain Name? : "
read DN
if [ "$DN" = "" ]; then
echo "Cancelled."
exit
fi
echo
echo "What is the IP address of your service providers nameserver?"
echo "If you have a IP address in /etc/resolv.conf press [Enter] and skip this."
echo "Example: 205.252.116.61"
echo -n "IP address? : "
read IP
echo
echo "Example: jerry"
echo -n "What is your Username? : "
read UN
if [ "$UN" = "" ]; then
echo "Cancelled."
exit
fi
echo
echo "Example: Xpo4Gkh9"
echo -n "What is your Password? : "
read PW
if [ "$PW" = "" ]; then
echo "Cancelled."
exit
fi
echo
echo "Examples: ttyS0 = com1 ttyS1 = com2 ttyS2 = com3 ttyS3 = com4"
echo "Don't include the /dev/ part in your answer."
echo -n "Where is your modem? ttyS? : "
read MOD
if [ "$MOD" = "" ]; then
echo "Cancelled."
exit
fi
echo
echo "You probably have PPP 2.2.something or the more recent 2.3.something."
echo "If not sure or NO press [Enter] otherwise put: y"
echo -n "Do you have version 2.2 of PPP? (N/y) : "
read VER
if [ "$VER" = "y" -o "$VER" = "Y" ]; then
ver=2.2
fi
if [ ! -d /etc/ppp ]; then
mkdir -p /etc/ppp
fi
if [ -f /etc/ppp/pap-secrets -o -f /etc/ppp/chap-secrets ]; then
rm -f /etc/ppp/pap-secrets
rm -f /etc/ppp/chap-secrets
fi
if [ -f /etc/ppp/ip-up -o -f /etc/ppp/ip-down ]; then
rm -f /etc/ppp/ip-up
rm -f /etc/ppp/ip-down
fi
if [ -s /etc/ppp/options ]; then
mv /etc/ppp/options /etc/ppp/options.off
fi
if [ ! "$IP" = "" ]; then
echo "nameserver $IP" > /etc/resolv.conf
fi
if [ -c "/dev/$MOD" ]; then
ln -sf /dev/$MOD /dev/modem 2>/dev/null
else
DEV="NO"
fi
if ! type -all pidof >/dev/null 2>&1 ; then
if [ -x /sbin/killall5 ]; then
ln -sf /sbin/killall5 /sbin/pidof 2>/dev/null
elif [ -x /bin/killall5 ]; then
ln -sf /bin/killall5 /bin/pidof 2>/dev/null
elif [ -x /usr/sbin/killall5 ]; then
ln -sf /usr/sbin/killall5 /usr/sbin/pidof 2>/dev/null
elif [ -x /usr/bin/killall5 ]; then
ln -sf /usr/bin/killall5 /usr/bin/pidof 2>/dev/null
elif [ -x /sbin/killall ]; then
ln -sf /sbin/killall /sbin/pidof 2>/dev/null
elif [ -x /bin/killall ]; then
ln -sf /bin/killall /bin/pidof 2>/dev/null
elif [ -x /usr/sbin/killall ]; then
ln -sf /usr/sbin/killall /usr/sbin/pidof 2>/dev/null
elif [ -x /usr/bin/killall ]; then
ln -sf /usr/bin/killall /usr/bin/pidof 2>/dev/null
fi
fi
chown root.root /usr/bin/eznet 2>/dev/null
chmod 4755 /usr/bin/eznet 2>/dev/null
/usr/bin/eznet add service=$DN phone=$PN user=$UN password=$PW
if [ "$ver" = "2.2" ]; then
/usr/bin/eznet change $DN pppversion=2.2
fi
if [ "$ADD" = "YES" ]; then
N="`tail -n 1 /etc/ppp/eznet/eznet.conf | cut -b-1 2>/dev/null`" || N="number?"
OR="or eznet up $DN"
fi
echo
echo "Done ..."
echo "To make the PPP connection do this."
echo "eznet up $N $OR"
echo "To end the PPP connection do this."
echo "eznet down"
echo "Your config file is in /etc/ppp/eznet/eznet.conf."
if [ "$DEV" = "NO" ]; then
echo
echo "WARNING: The modem device you chose '/dev/$MOD' doesn't exist."
echo "Create '/dev/MAKEDEV $MOD' and link 'ln -sf /dev/$MOD /dev/modem'."
echo
fi
echo "Done ..."
else
MSG
fi
Eznet-1.11.1/eznet.txt 100600 0 0 41651 6706436236 12660 0 ustar root root ============================================================================
There's a compiled libc6 eznet in the bin directory.
To compile eznet do this.
eznet-1.11.1# gcc -O2 -o eznet eznet.c
chown root.root /usr/bin/eznet
chmod 04755 /usr/bin/eznet
Put eznet and ezsetup in the /usr/bin directory.
You can use the shell script 'ezsetup' to setup 'eznet' to make the PPP
connection, it will ask you a few questions i.e. username, password,
and phone number.
Made these changes to eznet.c 1.11
Changed DB_DIR to /etc/ppp/eznet
Added 'asyncmap 0' to pppd options
Changed sesion.html to sesion.log
Changed idle 300 to 600
Removed mtu and mru 552
Added new line for Up since
Changed /bin/kill -TERM `/sbin/pidof pppd` to kill -TERM `pidof pppd`
Changed /bin/kill -TERM `/sbin/pidof diald` to kill -TERM `pidof diald`
2-24-98 Kent Robotti
The text below is by the author of eznet taken from his home page.
============================================================================
Notice: This page is incomplete. I've tried to provide enough data to
get started, but I don't have the time right now to write a complete
tutorial, or even a decent man-page. I hope that this will not prevent
you from trying out eznet and sending me your comments. If it does,
please check back in a few weeks to see what improvements have been
made. Thanks...
Eznet: A Simpler Way To Do PPP
Let's face it: setting up PPP under Linux can be intimidating,
especially for newbies. Between chat scripts and a zillion options to
pppd, setting up PPP can be a hassle for experienced users. It's a
wonder beginners are able to do it at all.
``eznet'' is a new program designed to make it much easier to get PPP
running under Linux. Basically all you have to do is supply a name for
your ISP, a login name, password and phone number and eznet will do
the rest. There are not configuration files to edit, no chat scripts
to design and no need to figure out what a "chap-secret" is. Assuming
you have pppd and eznet correctly installed (which should happen
automatically from your CD-ROM, right?) the following command is all
it takes to set up PPP:
eznet add service=NAME user=ABC password=XYZ phone=5551234
The setup command has to be entered as root. But after the setup is
done, any user can bring up the PPP connection by simply typing:
eznet up NAME
If everything works right, you should have a PPP connection in 30
seconds or so. The ``eznet up'' command will not return until either
the connection has been established, or the connection attempt fails.
The command will return a non-zero result code if the connection
attempt fails, making is suitable for use within shell-scripts.
``eznet up'' prints a status message just before it returns so that
interactive users can also see what happened.
The connection established by eznet will automatically go down after 5
minutes of inactivity. Or you can bring it down manually by typing:
eznet down NAME
To see the current status of a connection, type:
eznet status
Eznet also supports demand dialing. If you have Eric Schenk's
``Diald'' package installed, you can start it up by typing:
eznet dialdon NAME
And turn diald back off using:
eznet dialdoff NAME
If you prefer to use the request-route feature of kerneld, then just
write the following script into the file ``/sbin/request-route'':
#!/bin/sh
eznet up NAME
I prefer to use diald, since it seems to work better and because
kerneld will not be supported in the 2.2.X kernels. But either method
works.
Viewing And Changing The Configuration
Eznet has a configuration file (in /var/eznet/eznet.conf) but you
never need to read or edit it. Instead, use eznet itself to keep the
configuration up-to-date. To see the current configuration type:
eznet list
This command can be run as an unprivileged user, but if it is, the
passwords will not be displayed. Be careful when using the list
command as root because the passwords will appear in plain text on
your screen -- make sure nobody is looking over your shoulder!
To change the configuration, use a command like the following:
eznet change NAME ARG1=VALUE1 ARG2=VALUE2...
Only root is allowed to change configuration values. The four basic
configuration values are shown in the sample command above: "service"
"user", "password" and "phone". But there are many others. A complete
list is shown below. Hopefully, most users will never need to deal
with more than the basic four.
Multiple Service Providers
Eznet is able to manage connections to multiple service providers.
(The only purpose of the "service" option is to give a symbolic name
to each service provider.) Suppose, for example, that you have a
personal dialup account with AT&T WorldNet and a separate dialup
account where you work. You could setup two eznet configurations like
this:
eznet add service=work user=slave5 'password=yes sir!' phone=5551234
eznet add service=worldnet user=123456@worldnet.att.net \
password=xyzzy phone=5554321
Now eznet knows about both services. To log into work, just type
eznet up work
And to log into your personal WorldNet account, type
eznet up worldnet
Assuming you have two modems, you can even log into both serices at
the same time!
Some eznet commands require that you specify a particular service. The
"up" command is like this. Both other commands will apply to all
services if no particular service name is given. For example, if you
type
eznet down
then eznet immediately terminates all connections. The best why to
know whether or not a particular eznet command will work globally is
to try it and see. Note that if you only have a single service
provider defined, then there is no need to mention its name with any
command -- eznet will always know which service you mean.
Optimizing and Trouble Shooting
In some situations, eznet may require some additional hints in order
to work optimally, or to work at all. If you are having trouble
getting eznet to work, or wonder if you can make it work better, this
section is for you.
If eznet is not working for you, the first step is to figure out why
not. If eznet fails for any reason, run the command
eznet log
to get a diagnostic log of what it did on the most recent connection
attempt. This diagnostic log will often immediately show what the
trouble is. Then all you have to do is fix the problem. Usually adding
a few additional configuration options to your eznet setup will do the
trick.
Eznet makes very few assumptions about your modem. Basically, it
assumes that the modem supports the single Hayes modem command "atd"
for dialing, and that lowering the DTR line on your serial port will
cause the modem to hang up and reset. If your particular modem
requires some additional setup, you can cause eznet to issue extra
initialization commands to the modem using configuration parameters.
For example, to dial one of my local ISPs (named "VNet") using a
SupraFaxModem, I have to configure eznet as follows:
eznet change vnet init0=atz 'init1=at&d3'
The "initN" options (where N is between 0 and 9) each contain an
additional modem initialization that eznet issues prior to dialing the
modem. In this example, the first string resets the modem, and the
second string tells the modem to hangup and reset whenever the DTR
line goes low. Some folks like to turn the speaker on their modem off.
The following command will do that:
eznet change vnet init2=atm0
There are some minor differences between version 2.2 and 2.3 of pppd
that eznet needs to know about. Eznet assumes by default that you are
using version 2.3 or later of pppd. If you get an error message like
this:
idle: unrecognized command
pppd version 2.2 patch level 0
then configure eznet to use version 2.2 of pppd as follows:
eznet change vnet pppversion=2.2
It is possible to recompile eznet to assume version 2.2 of pppd (by
changing the DFLT_PPP_VERSION #define in the source code) but then it
will not work right for versions 2.3 of pppd and later.
Eznet does a good job of negotiating the login name and password of
most ISPs on its on, but with some ISPs it may require some additional
hints. If eznet fails to connect, you can view a transcript of the
connection attempt in the file "/var/eznet/transcript.*". This
transcript should help you figure out what is going wrong. Beginning
with version 1.8 of eznet, this transcript is a part of the "eznet
log" output.
With some service providers it may be necessary to respond to unusual
login prompts. Eznet allows you to code around this problem using
"expectN" and "replyN" options. For example, suppose VNet prompted for
the login name like this:
Enter your name:
instead of the more traditional "login:". By default, eznet would not
recognize this string and would not know how to respond. But you can
tell it would to do like this:
eznet change vnet expect0=name: reply0=drh
The "expect0" and "reply0" option pair tells eznet that it should
respond with the value of reply0 whenever it sees the text contained
in expect0. You can have up to 10 such expect and reply pairs numbered
0 through 9. The order is not important.
Two particular values of the replyN string have a special meaning. A
reply of "FAIL" will cause eznet to immediately abandon the connection
attempt when the corresponding expectN string is encountered.
Similarly, a reply of "ACCEPT" will cause eznet to immediately attempt
to start pppd.
If you have to use expectN and replyN parameters to get eznet to
work, please send e-mail to drh@acm.org. Include the output of
"eznet log" if possible. Hopefully, the information you provide
will enable me to upgrade eznet to work on your ISP without the
need for additional expectN and replyN configuration options.
A Summary of Eznet Configuration Options
The following is a summary of all of the configuration options that
eznet currently understands:
service This is a symbolic name for the ISP to which you will connect.
This name is used in the output of the status command. If you
configure eznet with data for two or more ISPs, then you can use this
name to select a particular ISP to dial.
phone This is the phone number used to dial the modem. The text
associated with this value will be appended to the string ``atd'' and
sent to the modem verbatim.
user The username or login for your ISP account. If your ISP is
running some kind of Unix, then this is the name that is entered at
the ``Login:'' prompt. If your ISP runs NT, then this is the name used
for PAP or CHAP authentification.
password This is the password for your ISP account. The password will
be entered at the ``Password:'' prompt during login or used during PAP
or CHAP authentification or both.
baud The is the baud rate of the serial connection between your
computer and the modem. The default value is 115200. If your modem or
serial port can't handle that speed, you might need to lower this
number.
debug If the value of the option is ``y'' or ``yes'' then pppd will be
launched in debugging mode. This will cause lots of extra information
to be sent to syslog, and can sometime be helpful in fixing problems.
tty By default, eznet attempt to setup the PPP connection on the
device named /dev/modem. You can choose a different serial device with
this option.
defaultroute Eznet normally sets up a default route to connections it
arranges. You can disable this feature by setting the defaultroute
attribute to ``no'' or ``n''.
idle This parameter records the number of seconds of inactivity that
will cause the PPP connection to shutdown. The default value is 300 (5
minutes). If you set it to 0, then no idle timeout will occur.
chat The default value of this option is "yes". If you set it to "no",
then eznet will try to set up a PPP link as soon as the modem
connects, ignoring any "login:" or similar prompts from the remote
machine.
pppversion Specifies what version of pppd is being used. (There are
changes in the command line syntax between 2.2 and 2.3 that eznet has
to deal with.) The default value is determined by the #define
"DFLT_PPP_VERSION" in the source code. If eznet causes pppd to fail
with a long error message the first time you try to use it, you may
want to set this value manually.
pppoptN You can pass up to 10 additional options to pppd by setting
the values of parameters pppopt0, pppopt1, ..., pppopt9 as
appropriate. The values specified are placed at the end of the
parameter list.
ip & netmask These parameters can be used to select from two or more
ISPs in eznet's database. If you type:
eznet up 192.18.135.11
for example, eznet will search through its list of ISPs looking for
one whose ``ip'' value matches 192.18.135.11 for every bit that is set
in the ``netmask''. It will then dial the first match it finds. This
feature is useful when eznet is started from /sbin/request-route and
you need to dial different ISPs depending on the IP number of the
machine you want to talk to.
initN You can have up to 10 extra modem initialization strings named
init0, init1, ...,init9. A common option is:
eznet change 0 init0=atz
expectN & replyN For more complex login procedures, you may need to
specify additional prompt strings and replies. You can enter up to 10
additional prompt strings that will respond with the corresponding
reply. For example:
eznet change 0 expect0=SERVICE: reply0=PPP
A carriage return is automatically added to every reply string.
pppd If your pppd is installed someplace other than /usr/sbin/pppd
then you can enter an alternative name here.
diald If you have diald installed as something other than
/usr/sbin/diald then enter its name using this parameter.
local This option can be used to specify the local IP number required
by diald. The default value is 127.0.0.100.
remote This option can be used to specify the remote IP number
required by diald. The default value is 127.0.0.101.
mtu This option sets the maximum length of a transmitted packet over
the PPP link. Longer packets are fragmented. The default is 552.
mru This option sets the maximum length of a received packet over the
PPP link. Longer packets are fragmented. The default is 552.
autostart If this option is "yes" (its default) then PPP will
automatically begin its protocol negotiation with the other end if
there is no response over the serial link for 8 seconds. If this
option is "no", then PPP will not start on the local end until PPP
packets are seen coming from the remote end.
routeN Where N is any digit between 0 and 9, this option causes a
route to be setup to the specified network after PPPD connects.
dialtimeout This is the number of seconds that eznet will wait for a
CONNECT message from the modem after issuing the dialing commands. The
default is now 60 seconds.
chattimeout This is the number of seconds that eznet will wait for
replies to commands while doing the login chat sequence. The default
is 3 seconds.
Installation
The eznet program is completely contained in a single C source file
named ``eznet.c''. You can compile and install it as follows:
gcc -o /usr/bin/eznet -O eznet.c
chown root.root /usr/bin/eznet
chmod 04755 /usr/bin/eznet
The eznet executable must reside in the directory /usr/bin. It won't
work if you install it someplace else. If you need to install eznet
elsewhere, change the value of the SELF #define near the top of the
source file and recompile.
Eznet does not need to be suid. But if it isn't, then only root will
be able to initiate a PPP connection. Eznet takes care to drop its
root privileges as soon as possible so it should be fairly safe as a
suid program. It is certainly easier to use that way.
Copyright And License
Eznet 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.
Eznet 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.
Author Contact Information
Eznet is written and maintained by D. Richard Hipp. If you have
questions or comments, send him e-mail at drh@acm.org. The eznet home
page is http://www.hwaci.com/sw/eznet/.
Eznet-1.11.1/eznet-1.11.1.lsm 100644 0 0 1676 6706436236 13344 0 ustar root root Begin3
Title: eznet
Version: 1.11.1
Entered-date: 25FEB99
Description: EZNET is a easy-to-use, user-friendly way to set up your
PPP daemon. Get it if you're having trouble creating a chat
script to connect to your service provider with pppd & chat.
Source and compiled libc6 included.
Added frontend 'ezsetup' script and made a few changes
to eznet.c.
The changes to eznet.c 1.11 are listed in eznet.txt which
is included in this package.
This is eznet.c 1.11 with a few changes by me kent robotti.
Keywords: ppp pppd setup isp internet dialup
Author: krobot@erols.com (Kent Robotti)
Maintained-by:
Primary-site: http://www.hwaci.com/sw/eznet <Home site of original eznet>
http://www.hwaci.com/drh
Alternate-site: ftp://sunsite.unc.edu/pub/Linux/system/network/serial/ppp
Platforms: Unix system with pppd.
Copying-policy: GPL
End
Eznet-1.11.1/Makefile 100664 0 0 765 6706436400 12376 0 ustar root root #
# woz's quick'n'dirty makefile... (hack at will!)
#
#
# install dir
#
INSTALL_BIN = /usr/bin
#
# program
#
EXEC = eznet
SRC = eznet.c
#
# options/commands
#
CC = gcc
CC_OPTS = -O2
CP = cp -f
CHOWN = chown
CHMOD = chmod
RM = rm -f
#
# make targets
#
all: build
build:
$(CC) $(CC_OPTS) -o $(EXEC) $(SRC)
install:
$(CP) $(EXEC) $(INSTALL_BIN)
$(CHOWN) root.root $(INSTALL_BIN)/$(EXEC)
$(CHMOD) 04755 $(INSTALL_BIN)/$(EXEC)
uninstall:
$(RM) $(INSTALL_BIN)/$(EXEC)
clean:
$(RM) $(EXEC) *.o