Ideally others will follow suit and contribute changes to existing functions
as well as new functions to do tasks that are required. Also, hopefully we
will be able to maintain a version of the library that is 'distribution'.
Until that time, let the source that follows, radius.h, be version .1.
--- radius.h ---
/************************************************************************\
* *
* Radius Log Proccessing Library *
* *
* Written for: Telalink Corporation <http://www.telalink.net> *
* by: James Campbell <james@telalink.net> *
* *
* Notes: *
* This library defines a class, RadiusManagement, which can be *
* used to handle the radius detail logs generated by a *
* portmaster. *
* *
* Document Conventions: *
* holdSomething *
* Variable names are long, the first letter of each *
* word is capitalized, except the first. *
* DoSomething *
* Functions are the same as variables, but with all *
* words are capitalized. *
* type_of_something *
* typedef's are all lowercase, underscored. *
* file_handle *
* file streams are lowercase, underscored. *
* Always_Be_Something *
* Consts are capitalized words, separated by underscore *
* *
* *
\************************************************************************/
#include<iostream.h>
#include<fstream.h>
#include<time.h>
#include<stdlib.h>
const short Bad_Record = 21;
const short No_More_Records = 0;
const short Good_Record = 1;
const short More_Records = 1;
#undef DEBUG
#define TIMEDEBUG
/************************************************************************\
* Definitions for the radius dictionary. If verbage changes, change *
* it here. Regretfully, we are not using this section yet. It *
* needs to be integrated. *
* *
* Running Radius version 1.6 right now. *
* *
* First, an example Radius STOP record *
Fri Nov 7 11:50:10 1997
Acct-Session-Id = "240019D3"
User-Name = "username"
NAS-IP-Address = 129.1.1.1
NAS-Port = 7
NAS-Port-Type = Async
Acct-Status-Type = Stop
Acct-Session-Time = 1075
Acct-Authentic = RADIUS
Connect-Info = "28800 LAPM/V42BIS"
Acct-Input-Octets = 6964
Acct-Output-Octets = 58425
Called-Station-Id = "1234567"
Calling-Station-Id = "1234567890"
Acct-Terminate-Cause = User-Request
Service-Type = Framed-User
Framed-Protocol = PPP
Framed-IP-Address = 129.1.1.1
Acct-Delay-Time = 0
* *
* Some const definitions are in order. Add them below as needed. *
* *
\************************************************************************/
struct radius_record {
char uid[11];
char date[21];
char duration[11];
char callingNumber[13];
char calledNumber[13];
/* These sections of the record are not used. Uncomment as needed.
char sessionID[9];
char nasIP[14];
char nasPort[4];
char nasPortType[7];
char authentic[8];
char connectInfo[30];
char inputOctets[15];
char outputOctets[15];
char terminateCause[30];
char serviceType[30];
char framedProtocol[8];
char framedIP[14];
char delayTime[5];
*/
};
typedef char expansion_record[70];
class RadiusManagement {
private:
short moreRecords;
expansion_record currentFormattedEntry;
radius_record currentEntry;
ifstream radius_file;
void FormatRecord(expansion_record output, radius_record inputRecord);
short ParseNextEntry(void);
void SyncError(char where[30], char badData[80]);
public:
RadiusManagement(const char * fileName);
void GetNextRecord(expansion_record output);
short MoreRecords(); // returns 1 if there are more entries, 0 otherwise
};
/***********************************************\
* Name: RadiusManagement (constructor) *
* Purpose: Parses the first entry and gets *
* ready to serve data. *
* *
* Input: <none> *
* *
* Output: <none> *
* *
* Notes: Parses first entry. *
* *
* A part of radius.h. *
\***********************************************/
RadiusManagement::RadiusManagement(const char * fileName) {
short worked = Bad_Record;
moreRecords = More_Records;
radius_file.open(fileName);
if (radius_file.bad()) {
cerr << "Unable to open radius file.\n";
exit(8);
}
while ((worked != Good_Record) && (moreRecords == More_Records)) {
worked = ParseNextEntry();
#ifdef DEBUG
cout << "worked = " << worked << ".\n";
#endif /* DEBUG */
}
FormatRecord(currentFormattedEntry, currentEntry);
}
/***********************************************\
* Name: ~RadiusManagement (destructor) *
* Purpose: Finishes the job. *
* *
* Input: <none> *
* *
* Output: <none> *
* *
* Notes: Closes the radius file we were *
* created to handle. *
* *
* A part of radius.h. *
\***********************************************/
RadiusManagement::~RadiusManagement() {
// all we have to do is close the radius file.
radius_file.close();
}
/***********************************************\
* Name: MoreRecords *
* Purpose: Tells the user if there are more *
* entries waiting for them *
* *
* Input: <none> *
* *
* Output: A short that is 1 if there are *
* more entries to be proccessed and *
* zero otherwise *
* *
* A part of radius.h. *
\***********************************************/
inline short RadiusManagement::MoreRecords() {
return(moreRecords);
}
/***********************************************\
* Name: GetNextRecord *
* Purpose: Returns an expansion_record for use *
* and gets the next one ready. *
* *
* Input: <none> *
* *
* Output: expansion_record (char [65]) *
* *
* Notes: First we copy the current formatted *
* record into a buffer. Then we get the *
* next record ready (replacing the value *
* in CurrentEntry and *
* CurrentFormattedEntry) and finally, *
* return the value of the initial record *
* *
* A part of radius.h. *
\***********************************************/
void RadiusManagement::GetNextRecord(expansion_record output) {
short worked=0;
strncpy(output, currentFormattedEntry, 70 );
while ((worked != Good_Record) && (moreRecords == More_Records)) {
worked = ParseNextEntry();
#ifdef DEBUG
cout << "worked = " << worked << ".\n";
#endif /* DEBUG */
}
FormatRecord(currentFormattedEntry, currentEntry);
}
/***********************************************\
* Name: FormatRecord *
* Purpose: Formats a radrecord for export into *
* the Expansion database. *
* *
* Input: struct radius_record *
* *
* Output: expansion_record (char [65]) *
* *
* A part of radius.h. *
\***********************************************/
void RadiusManagement::FormatRecord(expansion_record output, radius_record inputRecord) {
expansion_record inProgress;
double durationFloat; // duration converted to hours
char buffer[11];
strncpy(inProgress, "", 70);
strncpy(inProgress, "\"", 1); // first, build the username
strncat(inProgress, inputRecord.uid, 10);
strncat(inProgress, "\",\"", 3); // next, we have the type
strncat(inProgress, "001", 3); // type is a constant
strncat(inProgress, "\",\"", 3); // now date
strncat(inProgress, inputRecord.date, 19);
strncat(inProgress, "\",\"", 3); // next, duration
/* convert duration to hours. */
durationFloat = (atof(inputRecord.duration)) / 3600.0;
sprintf(buffer, "%10.2f", durationFloat);
strncat(inProgress, buffer, 10);
strncat(inProgress, "\",\"", 3); // finally, resourceID
strncat(inProgress, "123456789012", 12); // resourceID is a constant
strncat(inProgress, "\"", 1); // now top it all off...
// we have just written 64 characters which leaves one for the null
// until I standardize the usernames to six characters, I am allowing 70.
// DEBUG ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
strncpy(output, inProgress, 65);
}
/***********************************************\
* Name: ParseNextEntry *
* Purpose: Parses an entry from the detail *
* file "radius_file" *
* *
* Input: <none> *
* *
* Output: short worked -- whether we got a good *
* record or not. *
* *
* Notes: Parses an entry. *
* *
* A part of radius.h. *
\***********************************************/
short RadiusManagement::ParseNextEntry(void) {
char lineBuffer[80];
int lineBufferSize;
char *beginStringIsolator; // used to isolate strings
char *endStringIsolator;
struct tm timeStruct; // holds the converted date
time_t timeSecs; // Number of seconds
int cnt;
lineBufferSize = sizeof(lineBuffer);
radius_file.getline(lineBuffer, lineBufferSize); // Time, date
while (strcasecmp(lineBuffer, "") == 0) { // check for extra blanks
radius_file.getline(lineBuffer, lineBufferSize);
if (cnt > 10) { // more than ten times through...
moreRecords = No_More_Records; // give up. No more in this file.
return(No_More_Records);
}
++cnt;
}
#ifdef DEBUG
cout << "We think we've found the time in this line:\n" << lineBuffer << "\n";
#endif /* DEBUG */
strptime(lineBuffer," %a %B %d %T %Y ", &timeStruct);
timeSecs = mktime(&timeStruct); // timeSecs = num of secs since 1970
while (1) { // Looking for username
#ifdef DEBUG
cout << "We're looking for username in this line:\n" << lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { return (Bad_Record); }// record end
beginStringIsolator = (lineBuffer + 1);
endStringIsolator = (beginStringIsolator + 9);
*endStringIsolator = '\0';
if (strcasecmp(beginStringIsolator, "User-Name") == 0) {
#ifdef DEBUG
cout << "We think we've found the username in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
beginStringIsolator += 13;
endStringIsolator = strchr(beginStringIsolator, '\"');
if (endStringIsolator == NULL) {
SyncError("Username", lineBuffer);
} else {
*endStringIsolator = '\0';
}
strncpy(currentEntry.uid,beginStringIsolator, 10);
break; // of loop looking for username
} // if it's not username, keep trying
} // of while
while (1) { // Looking for Record Type
#ifdef DEBUG
cout << "We're looking for type in this line:\n" << lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { return (Bad_Record); }//record end
beginStringIsolator = (lineBuffer + 1);
endStringIsolator = (beginStringIsolator + 7);
*endStringIsolator = '\0';
if (strcasecmp(beginStringIsolator, "Acct-St") == 0) { // it's the type
#ifdef DEBUG
cout << "We think we've found the type in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
beginStringIsolator += 19;
endStringIsolator = (beginStringIsolator + 4);
*endStringIsolator = '\0';
if ( strcasecmp(beginStringIsolator, "Stop") != 0 ) {
/* We don't care about start records. Eat them */
while (1) {
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { break; }
}
#ifdef DEBUG
cout << "We are about to return b/c of start record:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
return(Bad_Record); // Tell them it's a start record
} // end of the case that this is a start record
break; // from the loop of finding the type
} // of the case that we found the line on which the type is located
} // of the loop to find the line containing the type
/* This will be executed only if it is a stop record */
while (1) { // looking for duration
#ifdef DEBUG
cout << "We're looking for duration in this line:\n" << lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { return (Bad_Record); }//record end
beginStringIsolator = (lineBuffer + 1);
endStringIsolator = (beginStringIsolator + 7);
*endStringIsolator = '\0';
if (strcasecmp(beginStringIsolator, "Acct-Se") == 0) { // this is duration
#ifdef DEBUG
cout << "We think we've found the duration in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
beginStringIsolator += 20;
#ifdef TIMEDEBUG
cout << "We think that the duration will be "
<< beginStringIsolator << ".\n";
#endif /* TIMEDEBUG */
strncpy(currentEntry.duration, beginStringIsolator, 11);
break; // from while of getting duration
} // of getting duration
} // of while for finding duration
while (1) { // looking for called-num
#ifdef DEBUG
cout << "We're looking for called-num in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { return (Bad_Record); }//record end
beginStringIsolator = (lineBuffer + 1);
endStringIsolator = (beginStringIsolator + 5);
*endStringIsolator = '\0';
if (strcasecmp(beginStringIsolator, "Calle") == 0) { // this is called-num
#ifdef DEBUG
cout << "We think we've found the called-num in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
beginStringIsolator += 21;
endStringIsolator = strchr(beginStringIsolator, '\"');
if (endStringIsolator == NULL) {
SyncError("Called-num", lineBuffer);
} else {
*endStringIsolator = '\0';
}
strncpy(currentEntry.calledNumber, beginStringIsolator, 13);
break; // from loop of finding called number
} // of case that we found called-num
} // of loop for finding line that has called-num
while (1) { // looking for calling-num
#ifdef DEBUG
cout << "We're looking for calling-num in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { return (Bad_Record); }//record end
beginStringIsolator = (lineBuffer + 1);
endStringIsolator = (beginStringIsolator + 5);
*endStringIsolator = '\0';
if (strcasecmp(beginStringIsolator, "Calli") == 0) { // this is calling-num
#ifdef DEBUG
cout << "We think we've found the calling-num in this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
beginStringIsolator += 22;
endStringIsolator = strchr(beginStringIsolator, '\"');
if (endStringIsolator == NULL) {
SyncError("Calling-num", lineBuffer);
} else {
*endStringIsolator = '\0';
}
strncpy(currentEntry.callingNumber, beginStringIsolator, 13);
break; // from look looking for calling-num
} // of the case that we found calling-num
} // of while looking for calling-num
while (1) { // looking for end of record
#ifdef DEBUG
cout << "We're looking for the end in this line:\n" << lineBuffer << "\n";
#endif /* DEBUG */
radius_file.getline(lineBuffer, lineBufferSize);
if (strcasecmp(lineBuffer, "") == 0) { // it's blank, we're done
break; // from looking for end of record
}
} // of while that looks for end of record
#ifdef TIMEDEBUG
char timeTestBuffer[30];
cout << timeSecs << " - timeSecs (When we think it was logged)\n";
cout << atol(currentEntry.duration) << " - what we think the duration is.\n";
cftime(timeTestBuffer, "%m-%d-%Y:%T" , &timeSecs);
cout << timeTestBuffer << " - what we think the logging time was.\n";
timeSecs -= atol(currentEntry.duration);
cftime(timeTestBuffer, "%m-%d-%Y:%T" , &timeSecs);
cout << timeTestBuffer << " - what we think the starting time was.\n";
#endif /* TIMEDEBUG */
timeSecs -= atol(currentEntry.duration);
cftime(currentEntry.date, "%m-%d-%Y:%T" , &timeSecs);// fills in the date
#ifdef DEBUG
cout << "We think we've finshed one record at this line:\n"
<< lineBuffer << "\n";
#endif /* DEBUG */
moreRecords = 1; // we just made one good record
return(1); // This means that we got a record!
} // of ParseNextEntry
/***********************************************\
* Name: SyncError *
* Purpose: Report a reading sync error and *
* abort. *
* *
* Input: char where[30] where the error was *
* char badData[80] the line of bad data *
* *
* Output: <none> *
* *
* Notes: Exit at the end so we don't produce *
* bad accounting data. *
* *
* A part of radius.h. *
\***********************************************/
void RadiusManagement::SyncError(char where[30], char badData[80]) {
cerr << "Out of sync in " << where << " read. Aborting.\n";
cerr << " The bad data was: " << badData << ".\n";
exit(8);
}
--- James Campbell james@telalink.net- To unsubscribe, email 'majordomo@livingston.com' with 'unsubscribe portmaster-users' in the body of the message.