/* * _________________________________________________________________________ * | | * | This source code software is the property of Quintum Technologies, Inc.| * | Possession, use, modification, copy, sale, distribution, disclosure, | * | or the performance of any act or any other use involving this software | * | without a license explicitly granted in writing by Quintum, or in | * | violation of such license granted by Acrotron, is strictly prohibited | * | and may result in civil and/or criminal liability. | * |_________________________________________________________________________| * * $Id$ * $Source$ */ /* * CDRSrv.c * * CDR Server code for Multithreading Applications in Win32 * This server can only be run on Windows NT, * version 3.51 or later. The client will be a Gateway * which will be configured with cdrserverport as 9002 and * cdrserverip as the CDR Server PC's IP Address. * For each Gateway, it will create a yymmdd_xxx_xxx_xxx_xxx.cdr file. * where yymmdd is the year, month, and day, xxx_xxx_xxx_xxx generated * from the CDR Server PC ip address by replacing '.' with '_'. * Before running this program, a Gateway administrator has to * config the Gateway by the following steps: * * 1. Telnet * 2. > Password: * Thank you. Type ? for help * 3 con sys cdrpassword * 4. con sys cdrserverip 1 * 5. con sys cdrserverport 1 9002 * 6. con sub * 7. exit * 8. You should create a cdrserver.cfg text file using notepad or textpad * to contain the following threelines: * cdr_password * max_chassis_no * max_cdr_per_file * 9. Enter cdrsrv <-f cfgfile_path\cdrserver.cfg> <-o cdrfile_path> <-p cdrserverport> <-v ?> * * 10. Output: all the cdr files will be in \ directory * */ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include //#include "MtVerify.h" // Pick a port number that seems to be away from all others #define MAX_CONNECTION_NUM 100 #define MAX_REC_LEN 512 #define DATE_LEN 7 #define IPADDR_LEN 16 #define PORT_LEN 7 #define VERSION_LEN 11 #define MAX_NAME_LEN 32 #define MAX_CHASSIS_NUM 100 #define MAX_FILE_NAME_LEN 128 #define MAX_PATH_LEN 256 #define MAX_ESQN_LEN 10 #define MAX_PASSWORD_LEN 16 #define MAX_CDR_NUM 10000 #define MAX_RESETNUM_LEN 11 #define MAX_FILE_INDEX_LEN 4 // States of the CDR Server #define AVAIL -1 #define START 0 #define WELCOMED 1 #define CONNECTED 2 #define PASSWORDSENT 3 #define PASSWORDOK 4 #define GOTGWIP 5 #define GOTUNITNAME 6 #define GOTLASTSEQNO 7 #define READY 8 // // Structure definition // // The context key keeps track of how the I/O // is progressing for each individual file handle. struct ContextKey { SOCKET sock; // Input char InBuffer[MAX_REC_LEN]; OVERLAPPED ovIn; // Output int nCDRBuffIndex; int nOutBuffIndex; char CDRBuffer[MAX_REC_LEN]; char OutBuffer[MAX_REC_LEN]; OVERLAPPED ovOut; DWORD dwWritten; int sid; }; // // Global variables // struct ContextKey *pCntx[MAX_CONNECTION_NUM]; HANDLE ghFile; HANDLE ghCompletionPort[MAX_CONNECTION_NUM]; HANDLE hFile[MAX_CONNECTION_NUM]; OVERLAPPED overlap[MAX_CONNECTION_NUM]; char cdrfile_path[MAX_PATH_LEN]; char cdrpassword[MAX_PASSWORD_LEN]; char cdrport[PORT_LEN]; char cfgfile_path[MAX_PATH_LEN]; char version[VERSION_LEN]; int cid; //Current Connection Identifier int state[MAX_CONNECTION_NUM]; int maxchano = MAX_CHASSIS_NUM; int maxcdrno = MAX_CDR_NUM; short cdrserverport; typedef struct chassis { short avail; char IpAddr[IPADDR_LEN]; char unitname[MAX_NAME_LEN]; char resetnum[MAX_RESETNUM_LEN]; long resetno; long curr_resetno; } CHASSIS; CHASSIS cha[MAX_CHASSIS_NUM]; // // Functions // void CreateWorkerThreads(int sid); DWORD WINAPI ThreadFunc(LPVOID sid); int IssueRead(struct ContextKey *pCntx); int convert_cdr(struct ContextKey *pCntx); void CheckOsVersion(); void FatalError(char *s); void GetConfigInfo(char *pswd, int *maxchano, int *maxcdrno); /////////////////////////////////////////////////////// int main(int argc, char *argv[]) { SOCKET listener; SOCKET AcceptSocket; WSADATA WsaData; struct sockaddr_in serverAddress; struct sockaddr_in clientAddress; int clientAddressLength; int err; int i = 0; struct ContextKey *pKey[MAX_CONNECTION_NUM]; char *curr_path; char ver[VERSION_LEN]; // Initialize cdrip to 9002 as the default value // the cfg file path and cdr file path to the working directory path strcpy(version, "07.22.2002_V1.0.3"); strcpy(cdrport, "9002"); curr_path = _getcwd(NULL, MAX_PATH_LEN); strcpy(cfgfile_path, curr_path); strcat(cfgfile_path, "\\cdrserver.cfg"); strcpy(cdrfile_path, curr_path); strcat(cdrfile_path, "\\"); strcpy(cdrpassword, "quintum"); printf("cfgfile_path = %s, cdrfile_path = %s\n", cfgfile_path, cdrfile_path); free(curr_path); // // parse the command options // if (argc%2 != 1) { fprintf(stdout, "Usage: %s -f \\cdrserver.cfg -o -p <-v ?>\n", argv[0]); return -1; } else { for (i = 1; i < argc;i=i+2) { // // Get cdrserver.cfg path // if (strcmpi(argv[i], "-f") == 0) { strcpy (cfgfile_path, argv[i+1]); // fprintf(stdout, "CFG FILE PATH = %s\n", cfgfile_path); } else if (strcmpi(argv[i], "-o") == 0) { strcpy (cdrfile_path, argv[i+1]); strcat (cdrfile_path, "\\"); // fprintf(stdout, "CDR FILE PATH = %s\n", cdrfile_path); } else if (strcmpi(argv[i], "-p") == 0) { strcpy (cdrport, argv[i+1]); // fprintf(stdout, "CDR PORT = %s\n", cdrport); } else if (strcmpi(argv[i], "-v") == 0) { strncpy (ver, argv[i+1], VERSION_LEN-1); strcat(ver, "\0"); if (strcmpi(ver, "?") == 0) { fprintf(stdout, "cdr server version = %s\n", version); } else { fprintf(stdout, "cdr server version = %s\n", version); } } else fprintf(stdout, "Usage: %s -f \\cdrserver.cfg\n -o -p <-v ?>\n", argv[0]); } } cdrserverport = atoi(cdrport); // convert cdr tcp ip port number into integer // fprintf(stdout, "TCP IP PORT = %d\n", cdrserverport); // // Now we are ready to open the cdrserver.cfg file to get the cdr password, // the maximum number of gateways that it will collect CDRs from, // the maximum records that each cdr file can contain. // ghFile = CreateFile( cfgfile_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL ); if (ghFile == INVALID_HANDLE_VALUE) { FatalError("Open Cdrserver Configuration File Failed\n"); } else { GetConfigInfo(&(cdrpassword[0]), &maxchano, &maxcdrno); } fprintf(stdout, "Maximal Number of CDRs per file = %d\n", maxcdrno); /* Initialize the Chassis Units */ for (i=0; i < MAX_CHASSIS_NUM; i++) { strcpy(cha[i].IpAddr, "0.0.0.0\0"); strcpy(cha[i].unitname, "unit1\0"); strcpy(cha[i].resetnum, "-1\0"); cha[i].curr_resetno = -1; cha[i].resetno = -1; cha[i].avail = 1; } CheckOsVersion(); err = WSAStartup (0x0101, &WsaData); if (err == SOCKET_ERROR) { FatalError("WSAStartup Failed"); return EXIT_FAILURE; } /* * Open a TCP socket connection to the server * By default, a socket is always opened * for overlapped I/O. Do NOT attach this * socket (listener) to the I/O completion * port! */ listener = socket(AF_INET, SOCK_STREAM, 0); if (listener < 0) { FatalError("socket() failed"); return EXIT_FAILURE; } // set socket option to so_keepalive /* * Bind our local address */ memset(&serverAddress, 0, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); serverAddress.sin_port = htons(cdrserverport); err = bind(listener, (struct sockaddr *)&serverAddress, sizeof(serverAddress) ); if (err < 0) { FatalError("bind() failed"); } else { int keepalivestat = 1; setsockopt(listener, IPPROTO_TCP, SO_KEEPALIVE, (char *)&keepalivestat, sizeof(int)); // sendto(listener, // (char *)&SAPData, // sizeof(SAPData), // MSG_DONTROUTE, // (struct sockaddr *)&serverAddress, // sizeof(serverAddress)); // Set timer to remind us to send SAP packet every 60 seconds. // SetTimer(hwnd, SAPTIMER, 60000, NULL); } // // Create a IO completion port for each incoming gateway connection // for (i = 0; i < MAX_CONNECTION_NUM; i++) { ghCompletionPort[i] = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, // No prior port 0, // No key 0 // Use default # of threads ); if (ghCompletionPort[i] == NULL) FatalError("CreateIoCompletionPort() failed"); CreateWorkerThreads(i); state[i] = AVAIL; } listen(listener, 10); fprintf(stderr, "CDR Server with I/O Completion Ports\n"); fprintf(stderr, "Running on TCP port %d\n", cdrserverport); fprintf(stderr, "\nPress Ctrl+C to stop the server\n"); // // Loop forever accepting requests new connections // and starting reading from them. // cid = -1; for (;;) { clientAddressLength = sizeof(clientAddress); AcceptSocket = accept(listener, (struct sockaddr *)&clientAddress, &clientAddressLength); if (AcceptSocket < 0) { FatalError("accept() Failed"); return EXIT_FAILURE; } for (i=0; i < MAX_CONNECTION_NUM; i++) { if (state [i] == AVAIL) { cid = i; printf("AVAIL CONN ID = %d\n", cid); break; } } if (cid == -1) FatalError("NO AVAILABLE TCP/IP Connections"); pKey[cid] = calloc(1, sizeof(struct ContextKey)); pKey[cid]->sock = AcceptSocket; pKey[cid]->ovOut.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // Set the event for writing so that packets // will not be sent to the completion port when // a write finishes. pKey[cid]->ovOut.hEvent = (HANDLE)((DWORD)pKey[cid]->ovOut.hEvent | 0x1); pKey[cid]->sid = cid; // Associate the socket with the completion port CreateIoCompletionPort( (HANDLE)AcceptSocket, ghCompletionPort[cid], (DWORD)pKey[cid], // No key 0 // Use default # of threads ); // Kick off the first read state[cid] = START; if (IssueRead(pKey[cid]) < 0) { free(pKey[cid]); state[cid] = AVAIL; fprintf(stderr, "CONNID = %d, IssueRead Failed\n", cid); } } return 0; } void CreateWorkerThreads(int sid) { SYSTEM_INFO sysinfo; DWORD dwThreadId; DWORD dwThreads; DWORD i; GetSystemInfo(&sysinfo); dwThreads = sysinfo.dwNumberOfProcessors * 2 + 2; for (i=0; isock); free(pCntx[k]); state[k] = AVAIL; // set the connection to be available again fprintf(stderr,"ThreadFunc - I/O operation failed, CONN ID = %d\n", k); } else if (dwNumRead == 0) { closesocket(pCntx[k]->sock); free(pCntx[k]); CloseHandle(hFile[k]); // close the cdr record file too state[k] = AVAIL; // set the connection to be available again fprintf(stderr, "ThreadFunc - End of file. CONN ID = %d\n", k); } // // Save each incoming record in the CDRbuffer // Process the incoming CDR according to it current // state // Each thread is running a finite state machine // There are totally 9 different states // Index k represent the current connection session id. // else { // Figure out where in the buffer to save the character char *pch = &(pCntx[k]->CDRBuffer[pCntx[k]->nCDRBuffIndex++]); *pch++ = pCntx[k]->InBuffer[0]; *pch = '\0'; // For debugging, WriteFile doesn't care //fprintf(stdout, "InBuffer = %s, state[%d] = %d\n", pCntx[k]->InBuffer, k, state[k]); switch (state[k]) { case START: if (pCntx[k]->InBuffer[0] == '\n') { // Get Welcome string from the Gateway // Break and goback to reac the incoming data block c = pCntx[k]->CDRBuffer[0]; if ((c == 'T') || (c == 'H') || (c == 'W')) { fprintf(stdout, "GET WELCOME STRING: CDRBuffer = %s\n", pCntx[k]->CDRBuffer); state[k] = WELCOMED; // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } } break; case WELCOMED: if (pCntx[k]->InBuffer[0] == '\n') { if (pCntx[k]->CDRBuffer[0] == 'C') { // Get Connect from IpAddr/Port# CDRSVRIpAddr/CDRPort to a gateway port message // Break and go back to read next incoming data block state[k] = CONNECTED; fprintf(stdout, "CONNECTED: CDRBuffer = %s\n", pCntx[k]->CDRBuffer); // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } } break; case CONNECTED: // If the last character read is a ':', then the data block is a // "Password:" prompter if (pCntx[k]->InBuffer[0] == ':') { //copy the CDR password to the CDRBuffer and sent it back to the Gateway // fprintf(stdout, "Get Password Prompt: CDRBuffer = %s\n", pCntx[k]->CDRBuffer); //strncpy(&pCntx[k]->CDRBuffer[0], "ming\r\n",6); for (i=0; i < MAX_PASSWORD_LEN; i++) { if ( cdrpassword[i] != '\0') pCntx[k]->CDRBuffer[i] = cdrpassword[i]; else break; } pCntx[k]->CDRBuffer[i++] = '\r'; pCntx[k]->CDRBuffer[i++] = '\n'; pCntx[k]->nCDRBuffIndex = i; fprintf(stdout, "CONNECTED: Send Password %s back to Gateway, nOutBuffIndex = %d\n", pCntx[k]->CDRBuffer, pCntx[k]->nCDRBuffIndex); WriteFile ( (HANDLE)(pCntx[k]->sock), pCntx[k]->CDRBuffer, pCntx[k]->nCDRBuffIndex, &pCntx[k]->dwWritten, &pCntx[k]->ovOut ); state[k] = PASSWORDSENT; // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } break; case PASSWORDSENT: // // If the last character read is a '\n', then // it means that Gateway accepts the password that we just sent // if ((pCntx[k]->InBuffer[0] == '\n') || (pCntx[k]->InBuffer[0] == '\r') || (pCntx[k]->InBuffer[0] == '\0')) { fprintf(stdout, "PASSWORDSENT: state = PASSWORD OK \n"); state[k] = PASSWORDOK; } if ( (pCntx[k]->InBuffer[0] == 'P') || (pCntx[k]->InBuffer[0] == 'S')) { fprintf(stdout, "PASSWORDSENT: CDRSRV Password Failure, Check CDR PASSWORD set in the Gateway\n"); // we will retry it again state[k] = CONNECTED; } for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; case PASSWORDOK: // // If Password is OK, The Gateway will send its own IP address and end with a ',' // // fprintf(stdout, "PASSWORDOK: CDRBuffer = %s, pCntx[%d]->nCDRBuffIndex = %d\n", // pCntx[k]->CDRBuffer, k, pCntx[k]->nCDRBuffIndex); if ( (pCntx[k]->InBuffer[0] == 'P') || (pCntx[k]->InBuffer[0] == 'S')) { fprintf(stdout, "PASSWORDOK? CDRSRV Password Failure, Check CDR PASSWORD set in the Gateway\n"); // we will retry it again state[k] = CONNECTED; for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } //fprintf(stdout, "PASSWORDOK: pCntx[k]->InBuffer[0] = %c\n", pCntx[k]->InBuffer[0]); if(pCntx[k]->InBuffer[0] == ',') { // fprintf(stdout, "PASSWORDOK: CDRBuffer = %s, pCntx[%d]->nCDRBuffIndex = %d\n", // pCntx[k]->CDRBuffer, k, pCntx[k]->nCDRBuffIndex); j = 0; for (i=0; i < pCntx[k]->nCDRBuffIndex; i++) { c = pCntx[k]->CDRBuffer[i]; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': ipaddr[j++] = c; break; default: break; } } ipaddr[j] = '\0'; fprintf(stdout, "PASSWORDOK: Connecting to the Chassis with ipaddr = %s\n", ipaddr); cha_idx = -1; for (i= 0; i < MAX_CHASSIS_NUM; i++) { if (strcmp(ipaddr, cha[i].IpAddr) == 0) { cha_idx = i; fprintf(stdout, "PASSWORDOK: ipaddr = %s, cha[%d].IpAddr = %s\n", ipaddr, i, cha[i].IpAddr); break; } } // if it cannot find a chassis with the ip address, then // find the first available chassis slot to store the chassis information // fprintf(stdout, "PASSWORDOK: cha_idx = %d\n", cha_idx); if (cha_idx == -1) { for (i= 0; i < MAX_CHASSIS_NUM; i++) { if ( cha[i].avail == 1) { cha_idx = i; strcpy(cha[cha_idx].IpAddr, ipaddr); cha[cha_idx].avail = 0; // fprintf(stdout, "PASSWORDOK: ipaddr = %s, Got %dth Chassis IP %s\n", ipaddr, cha_idx, cha[cha_idx].IpAddr); break; } } } if (cha_idx == -1) { fprintf(stdout, "ERROR: Too Many Chassises, Capacity Overflow\n"); return -1; } fprintf(stdout, "PASSWORDOK: GET %dth Chassis IP %s\n",cha_idx, cha[cha_idx].IpAddr); state[k] = GOTGWIP; // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } break; case GOTGWIP: uendidx = 0; // // After getting the Gateway IP Address, it sends its own unit name and followed by a newline char // // fprintf(stdout, "GOTGWIP: INBuffer = %c, state[k] = %d\n", pCntx[k]->InBuffer[0], state[k]); if ((pCntx[k]->InBuffer[0] == '\n') || (pCntx[k]->InBuffer[0] == '\r')) { // fprintf(stdout, "GOTGWIP: CDRBuffer = %s, state[%d] = %d\n", pCntx[k]->CDRBuffer, k, state[k]); j = 0; for (i=0; i < pCntx[k]->nCDRBuffIndex; i++) { c = pCntx[k]->CDRBuffer[i]; if ((c != ',') && (c != '\r') && (c != '\n')) { if (uendidx == 0) cha[cha_idx].unitname[j++] = c; else cha[cha_idx].resetnum[j++] = c; } else if ( c == ',') { cha[cha_idx].unitname[j] = '\0'; uendidx = j; j = 0; } else if (( c == '\r') || (c == '\n')) { if (uendidx == 0) cha[cha_idx].unitname[j] = '\0'; cha[cha_idx].resetnum[j] = '\0'; cha[cha_idx].curr_resetno = atol(cha[cha_idx].resetnum); break; } } fprintf(stdout, "GOTGWIP: GOT %dth Chassis Unit Name = %s, resetno = %d, curr_resetno = %d\n", cha_idx, cha[cha_idx].unitname, cha[cha_idx].resetno, cha[cha_idx].curr_resetno); state[k] = GOTUNITNAME; // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; break; } break; case GOTUNITNAME: // // After getting the Unit Name of the Gateway, // we are ready to get or create a GW CDR file name based on the // year, month, day, and Gateway IpAddress. // The CDR file name format is: // // yymmdd.cdr.nnn // // where yy - last two digits of the year, mm- monith 01-12, dd - 01-31, nnn- 1 to 999 // The cdr file shall be placed in ./xxx_xxx_xxx_xxx sub-directory under // the current directory. // // If the file exists, then the system will open it. // Otherwise, it will create the file. Everyday, it will create one // cdr file per Gateway. // // First the server will get the today's date and create the date string // // fprintf(stdout, "GOTUNITNAME: InBuffer = %c, state[k] = %d\n", pCntx[k]->InBuffer[0], state[k]); // fprintf(stdout, "GOTUNITNAME: CDRBuffer[%d] = %s\n", k, pCntx[k]->CDRBuffer); time(&timer); today = localtime(&timer); sprintf(currdate, "%.2d%.2d%.2d", ((today->tm_year)+1900)%100, ((today->tm_mon)+1), today->tm_mday ); currdate[DATE_LEN] = '\0'; strcpy(cdrfilename, currdate); cdrfilename[6] = '.'; cdrfilename[7] = 'c'; cdrfilename[8] = 'd'; cdrfilename[9] = 'r'; cdrfilename[10] = '\0'; i = 0; // // Start to translate xxx.xxx.xxx.xxx address into xxx_xxx_xxx_xxx // for (i=0;i < IPADDR_LEN; i++) { c = cha[cha_idx].IpAddr[i]; // fprintf(stdout, "GOUNITNAME: IP Address Charactor %c\n",IpAddr[i]); switch(c) { case '\0': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': cdrdir[i] = cha[cha_idx].IpAddr[i]; break; case '.': cdrdir[i] = '_'; default: break; } if (c == '\0') break; } fprintf(stdout, "GOTUNITNAME:CONN ID = %d, CDR directory = %s, CDR File Name = %s\n", k, cdrdir, cdrfilename); // // Create the total file path // strcpy(path, cdrfile_path); strcat(path, cdrdir); strcat(path, "\\"); if (_chdir(path)) { if ((fret = _mkdir (path)) == 0) { strcat(path, cdrfilename); } else { fprintf(stderr, "Cannot Make directory = %s, error code = %d\n", path, fret); return -1; } } else { // change back to the original cdr file path _chdir (cdrfile_path); strcat(path, cdrfilename); if (dailyfilecount > 0) { itoa (dailyfilecount, dailyfileindex, 10); strcat(path, "."); strcat(path, dailyfileindex); } } fprintf(stdout, "GOTUNITNAME:CONN ID = %d CDR File Name = %s\n", k, path); // // Open or create the cdr file // for (i=0; i < retry; i++ ) { hFile[k] = CreateFile( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL ); // //Initialize overlap structure // The system will read the cdr from the begginng // i.e., Offset is set to 0 // memset(&overlap[k], 0, sizeof (overlap[k])); overlap[k].Offset = 0; if (hFile[k] == INVALID_HANDLE_VALUE) { fprintf(stdout, "GOTUNITNAME: create file failed, CDR File Name = %s\n", path); Sleep(50); } else break; } // // Initialize a cdr record // for (i=0; i < MAX_REC_LEN; i++) cdrecord[i] = '\0'; // fprintf(stdout, "GOTUNITNAME: CONN ID= %d, hFile[%d] = %d, %s open for appended\n", k, k, hFile[k], cdrfilename); i=0; lsqno = 0; recno = 0; csqno = 0; // // Start to get the expected sequence number that the CDR Server // will send to the Gateway to ask for the next CDR record with the // expected sequence number. // To do this, one has to read one character at a time from the CDR file // It uses the separated, a newline char, to get a cdr record from the file // For each cdr record in the file, it extracts its sequence number. // It loops through each record and keep the highest sequence number. // After getting the highest sequence number of all the cdr records in the file // The expected sequence number is the highest sequence number plus one. // while (ReadFile( hFile[k], record, 1, &numread, &overlap[k] )) { //fprintf(stdout, "GOTUNITNAME:CONN ID = %d, recno# = %d, %dth char = %c\n", k, recno, i, record[0]); switch(record[0]) { case '\n': case '\r': case '\0': // get the highest sequence number of all the records in the cdr file // the last cdr record may not have the highest sequence no for (j = 0; cdrecord[j]!= ',' ; j++) { seqno[j] = cdrecord[j]; } seqno[j] = '\0'; csqno = atoi(seqno); //lsqno = ((lsqno > csqno) ? lsqno: csqno); // CH always send back sequence in ascending order until it starts to recycle lsqno = csqno; cdrecord[i] = '\0'; break; default: cdrecord [i] = record[0]; break; } if (cdrecord[i] == '\0') { recno++; // fprintf(stdout, "GOTUNITNAME:CONN ID = %d, %dth cdrecord = %s, lenght = %d\n", k, recno, cdrecord, i); i = 0; } else i++; overlap[k].Offset++; } if ((recno == 0) || (cha[cha_idx].curr_resetno != cha[cha_idx].resetno)) { esqno = 1; lastseqno[0] = '0'; lastseqno[1] = '\0'; fprintf(stdout, "GOTUNITNAME:CONN ID = %d, # of records = %d LastSeqNo = %s, ExpectedSeqNo = %d\n", k, recno, lastseqno, esqno); } else { esqno = lsqno + 1; //fprintf(stdout, "GOTUNITNAME:CONN ID = %d, LastRec# = %d lsqno = %d, esqno = %d\n", // k, recno, lsqno, esqno); ltoa (lsqno, lastseqno, MAX_ESQN_LEN); fprintf(stdout, "GOTUNITNAME:CONN ID = %d, # of records = %d, LastSeqNo = %s ExpectedSeqNo = %d\n", k, recno, lastseqno, esqno); } cha[cha_idx].resetno = cha[cha_idx].curr_resetno; state[k] = GOTLASTSEQNO; case GOTLASTSEQNO: // // Now we are ready to send back the expected sequence number to the Gateway. // pCntx[k]->nCDRBuffIndex = sizeof(lastseqno); strncpy(pCntx[k]->CDRBuffer, lastseqno, pCntx[k]->nCDRBuffIndex); pCntx[k]->CDRBuffer[pCntx[k]->nCDRBuffIndex++] = '\r'; pCntx[k]->CDRBuffer[pCntx[k]->nCDRBuffIndex++] = '\n'; fprintf(stdout, "GOTLASTSEQNO:Send LastSeqNo %s back to Gateway\n", pCntx[k]->CDRBuffer); WriteFile ( (HANDLE)(pCntx[k]->sock), pCntx[k]->CDRBuffer, pCntx[k]->nCDRBuffIndex, &pCntx[k]->dwWritten, &pCntx[k]->ovOut ); // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; //memset (pCntx[k]->CDRBuffer, 0, MAX_REC_LEN); pCntx[k]->nCDRBuffIndex = 0; state[k] = READY; break; case READY: // // Now the CDR Server is ready to receive CDR records from the Gateway. // The Gateway may send some junk or special characters to the Gateway at this time // The server will drop those junks and continue to read the next records. // Notice that all CDRs are starting with digital charactors. // if (pCntx[k]->InBuffer[0] == '\n') { fprintf(stdout, "READY: CONNID = %d, Incoming String = %s\n", k, pCntx[k]->CDRBuffer); switch (pCntx[k]->CDRBuffer[0] ) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': //It must be a CDR record now //write it into the cdrfile // Check the current date to see if it is time // to create another file due to the new date // time(&timer); today = localtime(&timer); sprintf(currdate, "%.2d%.2d%.2d", ((today->tm_year)+1900)%100, ((today->tm_mon)+1), today->tm_mday ); currdate[DATE_LEN] = '\0'; // fprintf(stdout, "READY: Current CDR File Name = %s and the current date = %s\n", cdrfilename, currdate); if ((strncmp(currdate, cdrfilename, DATE_LEN-1) != 0) || (recno >= maxcdrno)) { CloseHandle(hFile[k]); for(i=0; i 0) { itoa (dailyfilecount, dailyfileindex, 10); strcat(path, "."); strcat(path, dailyfileindex); } for (i=0; i < retry; i++) { hFile[k] = CreateFile( path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, NULL ); if (hFile[k] == INVALID_HANDLE_VALUE) { fprintf(stdout, "READY: open file failed, CDR File Name = %s\n", path); Sleep(50); } else { recno = 0; if (strncmp(currdate, cdrfilename, DATE_LEN-1) != 0) { dailyfilecount = 0; } else { dailyfilecount++; if (dailyfilecount >= 999) dailyfilecount = 0; } break; } } overlap[k].Offset = 0; // fprintf(stdout, "READY: Created CDR File Name = %s and the current date = %s\n", cdrfilename, currdate); } pCntx[k]->CDRBuffer[pCntx[k]->nCDRBuffIndex] = '\0'; // get the expected sequence from the CDR record i = 0; while (pCntx[k]->CDRBuffer[i] != ',') seqno[i] = pCntx[k]->CDRBuffer[i++]; seqno[i] = '\0'; csqno = atoi(seqno); //fprintf(stdout, "READY: incoming record seq no = %d and expected seq no = %d\n", csqno, esqno); for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->OutBuffer[i] = '\0'; pCntx[k]->nOutBuffIndex = 0; fret = convert_cdr(pCntx[k]); if ((fret != 24) && (fret != 20) && (fret != 28)) { fprintf(stderr, "CONN ID = %d, WARNING: the incoming CDR record may have missing fields, Number of fields = %d\n",k, fret); } for (i = 1; i < retry; i++) { rc = WriteFile( hFile[k], pCntx[k]->OutBuffer, pCntx[k]->nOutBuffIndex, &pCntx[k]->dwWritten, &overlap[k]); if (rc) { overlap[k].Offset += pCntx[k]->dwWritten; fprintf(stdout, "READY: CONN ID = %d:Write CDR = %s\n", k, pCntx[k]->OutBuffer); for (i=0; i < MAX_ESQN_LEN; i++) lastseqno[i] = '\0'; ltoa (csqno, lastseqno, MAX_ESQN_LEN); recno++; break; } else { fprintf(stdout, "READY: CONN ID = %d:FileWrite Failed %dth time, file Handle = %d, rc = %d\n", k, i, hFile[k], rc); Sleep (50); } } // WARNING: if the incoming CDR sequence number is greater than the expected sequence if (csqno > esqno) { fprintf(stdout, "WARNING: CONN ID = %d: Some CDR records may be lost!\n"); fprintf(stdout, "WARNING: CONN ID = %d: Receiving CDR Sequence Number > Expected Sequence Number\n"); } // set next expected sequence no for incoming record esqno = csqno + 1; //state[k] = GOTEXPECTEDSEQNO; state[k] = READY; // clear the CDRBuffer and reset index for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; pCntx[k]->dwWritten=0; break; default: // fprintf(stderr, "Get some charater %2x\n", pCntx[k]->CDRBuffer[0]); for (i=0; i < MAX_REC_LEN; i++) pCntx[k]->CDRBuffer[i] = '\0'; pCntx[k]->nCDRBuffIndex = 0; // get some junk break; } } default: break; } // Start a new read if (IssueRead(pCntx[k]) < 0) { free(pCntx[k]); CloseHandle(hFile[k]); state[k] = AVAIL; fprintf(stderr, "CONNID = %d, IssueRead Failed\n", k); return 0; } } } free(pCntx[k]); CloseHandle(hFile[k]); state[k] = AVAIL; return 0; } /* * Call ReadFile to start an overlapped request * on a socket. Make sure we handle errors * that are recoverable. */ int IssueRead(struct ContextKey *pCntx) { int i = 0; BOOL bResult; int err; int numRead; while (++i) { // Request a single character bResult = ReadFile( (HANDLE)pCntx->sock, pCntx->InBuffer, 1, &numRead, &pCntx->ovIn ); // It succeeded immediately, but do not process it // here, wait for the completion packet. if (bResult) return 1; err = GetLastError(); // This is what we want to happen, it's not an error if (err == ERROR_IO_PENDING) return 1; // Handle recoverable error if ( err == ERROR_INVALID_USER_BUFFER || err == ERROR_NOT_ENOUGH_QUOTA || err == ERROR_NOT_ENOUGH_MEMORY || err == ERROR_WORKING_SET_QUOTA) { if ((i >=1) && (i <= 5))// I just picked a number { fprintf(stdout, "ISSUE_READ: CONN ID= %d, Error = %d, Sleep 200 mseconds to retry\n", pCntx->sid, err); Sleep(25); // Wait around and try later continue; } fprintf(stderr, "IssueRead - System ran out of non-paged space"); return -err; } break; } fprintf(stderr, "IssueRead - ReadFile failed.Error Code = %d\n", err); return -err; } // // Make sure we are running under the right versions // of Windows NT (3.51, 4.0, or later) // void CheckOsVersion() { OSVERSIONINFO ver; BOOL bResult; ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); bResult = GetVersionEx((LPOSVERSIONINFO) &ver); if ( (!bResult) || (ver.dwPlatformId != VER_PLATFORM_WIN32_NT) ) { FatalError("CDRSRV requires Windows NT 3.51 or later."); } } // // Error handler // void FatalError(char *s) { fprintf(stdout, "%s\n", s); exit(EXIT_FAILURE); } void GetConfigInfo(char *pswd, int *maxchano, int *maxcdrno) { int i, j; int numread; char inbuf[4]; char buffer[MAX_REC_LEN]; char c; OVERLAPPED overlap; // initialize overlap structure memset(&overlap, 0, sizeof (overlap)); overlap.Offset = 0; // parsing the cdr server configuration file //fprintf(stdout, "GetConfigInfo Read CDRSERVER Configuration File Handle = %d\n", ghFile); i = 0; while (ReadFile( ghFile, inbuf, 1, &numread, &overlap )) { switch(inbuf[0]) { case '\n': case '\r': case '\0': buffer[i] = '\0'; // get a line if (strncmp (buffer, "//", 2) == 0) { //skip the comment line //fprintf(stdout, "GetConfigInfo Get a comment line = %s\n", buffer); // clear the buffer to get the next line } else if (strncmp(buffer, "cdr_password", 12) == 0) { // fprintf(stdout, "GetConfigInfo Get Password = %s\n", buffer); // get the cdrpassword j = 12; c=buffer[j]; while (isspace(c)) c=buffer[j++]; // skip the blanks while (isprint(c)) { *(pswd) = c; // fprintf(stdout, "GetConfigInfo pswd = %c\n", *pswd); c = buffer[j++]; pswd++; } *pswd = '\0'; } else if (strncmp(buffer, "MAX_CHASSIS_NUM", 13) == 0){ //fprintf(stdout, "GetConfigInfo Get MAX GW NUM LINE = %s\n", buffer); j = 13; c = buffer[j]; while (isspace(c)) c=buffer[j++]; // skip the blanks *maxchano = atoi(&(buffer[j-1])); } else if (strncmp(buffer, "max_cdr_per_file", 16) == 0) { //fprintf(stdout, "GetConfigInfo Get MAX CDR PER FILE LINE = %s\n", buffer); j = 16; while (isspace(buffer[j++])); //skip the blanks *maxcdrno = atoi(&(buffer[j-1])); } // clear the buffer to get another line for(i=0; i < MAX_REC_LEN; i++) buffer[i] = '\0'; i = 0; break; default: buffer[i++] = inbuf[0]; break; } overlap.Offset++; } } int convert_cdr(struct ContextKey *pCntx) { int i, field_cnt; char c; field_cnt = 0; pCntx->nOutBuffIndex = 0; for (i=0; i < pCntx->nCDRBuffIndex; i++) { c = pCntx->CDRBuffer[i]; if (c == ',') { field_cnt++; if ((field_cnt > 1) && (pCntx->CDRBuffer[i-1] == ',')) pCntx->OutBuffer[pCntx->nOutBuffIndex++] = '-'; pCntx->OutBuffer[pCntx->nOutBuffIndex++] = c; } else if (c == '\r') { if (pCntx->CDRBuffer[i-1] == ',') pCntx->OutBuffer[pCntx->nOutBuffIndex++] = '-'; pCntx->OutBuffer[pCntx->nOutBuffIndex++] = ','; pCntx->OutBuffer[pCntx->nOutBuffIndex++] = c; field_cnt++; } else pCntx->OutBuffer[pCntx->nOutBuffIndex++] = c; } return field_cnt; }