// // File: // // Written by: David M. Stanhope [voip@fobbit.com] // // routines to read and parse a initialization file // #include "vblast.h" // --------------------------------------------------------------------------- #define PORT_CALLS_TCP 8008 #define PORT_SERVER 8009 #define NET_HIGH_WATER 2 // how many in queue when consider to much #define NET_HIGH_COUNT 4 // no. of packet times at high-water till drop some #define DIAL_TIMEOUT_1 15 // max time from off-hook till 1st digit dialed #define DIAL_TIMEOUT_2 5 // max time between digits till auto-send #define TCP_ALIVE_TIMER 60 // max time between tcp transmissions int device_index = 0 ; int debug_level = 0 ; static int port_outgoing = PORT_CALLS_TCP ; int port_listen_tcp = PORT_CALLS_TCP ; int port_listen_udp = -1 ; // disable by default static int port_server = PORT_SERVER ; int net_high_water = NET_HIGH_WATER ; int net_high_count = NET_HIGH_COUNT ; int device_volume = 3 ; // 0 to 6, default 3 int dial_timeout_1 = DIAL_TIMEOUT_1 ; int dial_timeout_2 = DIAL_TIMEOUT_2 ; int tcp_alive_timer = TCP_ALIVE_TIMER ; static char *server_primary = NULL ; static char *server_secondary = NULL ; static char *public_address_tcp = NULL ; static char *public_address_udp = NULL ; char *public_name = "" ; // empty string char *public_location = "" ; // empty string char *public_email = "" ; // empty string int publish_to_server = 0 ; // default is NO int query_server = 0 ; // default is NO int http_server = 0 ; // default is NO int can_accept_inbound = 1 ; // default is YES int cpc_duration = 0 ; // default is OFF int force_dial = 1 ; // default is ON char *sound_ringback = "sounds/ringback.723"; char *sound_busy = "sounds/busy.723" ; char *sound_dialtone = NULL ; // default is internal char *sound_dialtone_hs = NULL ; // deafult is none struct sockaddr_in public_ip_and_port_tcp ; struct sockaddr_in public_ip_and_port_udp ; PHONEBOOK *phonebook_head = NULL; PHONEBOOK *phonebook_tail = NULL; PHONEBOOK *phonebook_current = NULL; typedef struct { char *command; int type ; // type of value void *value ; // pointer to value } CONFIG; // values for type in CONFIG struct #define T_INT 0 // integer value #define T_STR 1 // string value #define T_BOOL 2 // yes/no value #define T_END -1 // table end marker CONFIG config[] = { { "DEVICE_INDEX" , T_INT , &device_index }, { "DEBUG_LEVEL" , T_INT , &debug_level }, { "NET_HIGH_WATER" , T_INT , &net_high_water }, { "NET_HIGH_COUNT" , T_INT , &net_high_count }, { "DEVICE_VOLUME" , T_INT , &device_volume }, { "DIAL_TIMEOUT_1" , T_INT , &dial_timeout_1 }, { "DIAL_TIMEOUT_2" , T_INT , &dial_timeout_2 }, { "TCP_ALIVE_TIMER" , T_INT , &tcp_alive_timer }, { "PORT_OUTGOING" , T_INT , &port_outgoing }, { "PORT_LISTEN" , T_INT , &port_listen_tcp }, { "PORT_LISTEN_UDP" , T_INT , &port_listen_udp }, { "PORT_SERVER" , T_INT , &port_server }, { "CPC_DURATION" , T_INT , &cpc_duration }, { "FORCE_DIAL" , T_INT , &force_dial }, { "SERVER_PRIMARY" , T_STR , &server_primary }, { "SERVER_SECONDARY" , T_STR , &server_secondary }, { "PUBLIC_ADDRESS" , T_STR , &public_address_tcp}, { "PUBLIC_ADDRESS_UDP" , T_STR , &public_address_udp}, { "PUBLIC_NAME" , T_STR , &public_name }, { "PUBLIC_LOCATION" , T_STR , &public_location }, { "PUBLIC_EMAIL" , T_STR , &public_email }, { "PUBLISH_TO_SERVER" , T_BOOL, &publish_to_server }, { "QUERY_SERVER" , T_BOOL, &query_server }, { "HTTP_SERVER" , T_BOOL, &http_server }, { "CAN_ACCEPT_INBOUND" , T_BOOL, &can_accept_inbound}, { "FILE_RINGBACK" , T_STR , &sound_ringback }, { "FILE_BUSY" , T_STR , &sound_busy }, { "FILE_DIALTONE" , T_STR , &sound_dialtone }, { "FILE_DIALTONE_HEADSET", T_STR , &sound_dialtone_hs }, { NULL , T_END , NULL }, }; // --------------------------------------------------------------------------- typedef struct _dialed_ { struct _dialed_ *next ; char *dialed; char *name ; int port ; } DIALED; typedef struct _speed_ { struct _speed_ *next ; char *dialed; char *name ; } SPEED; static DIALED *head_dialed = NULL; static SPEED *head_speed = NULL; // if port present as part of the address, remove it from the address // and return the port, otherwise leave name alone and return default port int scan_address(char *address, int default_port) { char *s; if((s = strchr(address, ':')) != NULL) // scan off port if present { *s++ = '\0'; return atoi(s); } else { return default_port; } } void init_address(int port, struct sockaddr_in *pa) { memset((char *) pa, 0, sizeof(struct sockaddr_in)); #ifdef USE_SIN_LEN pa->sin_len = sizeof(struct sockaddr_in); #endif pa->sin_family = AF_INET ; pa->sin_port = htons((u_short) port) ; } // given a host name, and a port, build an address structure for it, // after calling the resolver int resolve_address(char *name, int port, struct sockaddr_in *pa) { struct hostent *hp; init_address(port, pa); if(isdigit((u_char) name[0])) { if(inet_aton(name, &(pa->sin_addr)) == 0) { ERR(("resolve_address: bogus ip(%s)\n", name)) return -1; } } else { if((hp = gethostbyname(name)) == NULL) { ERR(("resolve_address: unknown host(%s)\n", name)) return -1; } if(hp->h_addrtype != AF_INET) { ERR(("resolve_address: not an internet address(%s)\n", name)) return -1; } if(hp->h_length > sizeof(pa->sin_addr)) hp->h_length = sizeof(pa->sin_addr); memcpy(&(pa->sin_addr), hp->h_addr, hp->h_length); } MSG2(("resolve_address: (%s) is (%s)\n", name, inet_ntoa(pa->sin_addr))) return 0; } // --------------------------------------------------------------------------- // add a new entry to the local dialed-to-address table static void add_dialed(char *dialed, char *name) { DIALED *dp; if((dp = (DIALED *) malloc(sizeof(DIALED))) == NULL) { ERR(("Out of Memory: DIALED(%s)(%s)\n", dialed, name)) exit(-1); } memset(dp, 0, sizeof(DIALED)); dp->port = scan_address(name, port_outgoing); MSG2(("Adding 'Dialed' (%s)(%s)(%d)\n", dialed, name, dp->port)) dp->dialed = new_string(dialed); dp->name = new_string(name ); dp->next = head_dialed; head_dialed = dp; // add to list } static void add_speed(char *dialed, char *name) { SPEED *sp; if((sp = (SPEED *) malloc(sizeof(SPEED))) == NULL) { ERR(("Out of Memory: SPEED(%s)(%s)\n", dialed, name)) exit(-1); } memset(sp, 0, sizeof(SPEED)); MSG2(("Adding 'Speed' (%s)(%s)\n", dialed, name)) sp->dialed = new_string(dialed); sp->name = new_string(name ); sp->next = head_speed; head_speed = sp; // add to list } // --------------------------------------------------------------------------- #define MAX_PORT 32767 #define MAX_IP 255 #define BAD_C 0x0100 // illegal char value // parse and validate either an ip nybble or port number static char * check_number(char *s, int *num, int ec1, int ec2, int max_val) { char *e; long n; n = strtol(s, &e, 10); if(s == e) { return NULL; } // no valid chars seen if(((n == LONG_MAX) || (n == LONG_MIN)) && (errno == ERANGE)) { return NULL; // underflow or overflow occured } if((*e != ec1) && (*e != ec2)) { return NULL; } // didn't end correctly if(n < 0) { return NULL; } // can't happen but check anyway if(n > ((long) max_val)) { return NULL; } // too big *num = (int) n; if(ec2 == BAD_C) return ++e; // skip over term char if only 1 is legal return e; } // see if direct dialed a valid ip, look for either: // a*b*c*d or a*b*c*d*p // the a*b*c*d is a valid ip address, and the optional *p is the port to use static char * check_ip(char *s, int *port) { int a, b, c, d, p; static char ip[20]; if((s = check_number(s, &a, '*', BAD_C, MAX_IP)) == NULL) { return NULL; } if((s = check_number(s, &b, '*', BAD_C, MAX_IP)) == NULL) { return NULL; } if((s = check_number(s, &c, '*', BAD_C, MAX_IP)) == NULL) { return NULL; } if((s = check_number(s, &d, '*', '\0', MAX_IP)) == NULL) { return NULL; } sprintf(ip, "%d.%d.%d.%d", a, b, c, d); // at this point 's' either points to a '*' or '\0', if '\0' then no // port is specified if(*s == '\0') { *port = port_outgoing; // set to default return ip; } if(*s++ != '*') { return NULL; } // should never happen if(check_number(s, &p, '\0', BAD_C, MAX_PORT) == NULL) { return NULL; } *port = p; // pass pack the scanned port number return ip; } static void add_phonebook(char *name) { char *np; int port; PHONEBOOK *bp; if((np = malloc(strlen(name) + 1)) == NULL) { ERR(("add_phonebook: Out of Memory for Name (%s)\n", name)) exit(-1); } strcpy(np, name); // save original version of name if((bp = malloc(sizeof(PHONEBOOK))) == NULL) { ERR(("add_phonebook: Out of Memory for Entry (%s)\n", name)) free(np); exit(-1); } bp->next = NULL; bp->name = np ; port = scan_address(name, port_server); if(resolve_address(name, port, &(bp->address)) != 0) { free(np); free(bp); exit(-1); } // add entry to the tail of the list so first entry gets used first if((phonebook_head == NULL) || (phonebook_tail == NULL)) { phonebook_head = bp; // add first entry phonebook_current = bp; // say have at least one valid entry } else { phonebook_tail->next = bp; } phonebook_tail = bp; } // --------------------------------------------------------------------------- char * lookup_dialed(char *dialed, int *port) { DIALED *dp; SPEED *sp; char *cp; // check for ip d*d*d*d*p and the *p is optional // use 'port_outgoing' by default if(cp = check_ip(dialed, port)) { MSG(("Dialed IP(%s:%d)\n", cp, *port)) return cp; } for(dp = head_dialed; dp; dp = dp->next) { MSG2(("Checking 'Dialed' (%s)(%s)(%d)\n", dp->dialed, dp->name, dp->port)) if(strcmp(dialed, dp->dialed) != 0) continue; // doesn't match *port = dp->port; return dp->name; } if(query_server == 0) return NULL; for(sp = head_speed; sp; sp = sp->next) { MSG2(("Checking 'Speed' (%s)(%s)\n", sp->dialed, sp->name)) if(strcmp(dialed, sp->dialed) != 0) continue; // doesn't match return server_query(sp->name, port); } return server_query(dialed, port); } // --------------------------------------------------------------------------- static int get_bool(char *s) { if(strcmp(s, "YES" ) == 0) return 1; return 0; } // --------------------------------------------------------------------------- static void syntax_error(int line_cnt, char *s, char *fname) { ERR(("(%s) Error Line %d:%s\n", fname, line_cnt, s)) } #define SYNTAX_ERROR \ { syntax_error(line_cnt, ebuf, ini_name); errs++; continue; } void process_ini(char *ini_name) { FILE *fp; int i, c, port, eflag, line_cnt = 0, errs = 0; char *cp, *s, *d, buf[129], ebuf[129], cmd[129], arg1[129], arg2[129]; PMSG(("Processing <%s>\n", ini_name)) if((fp = fopen(ini_name, "r")) == NULL) { ERR(("can't open <%s>\n", ini_name)) exit(-1); } while((cp = fgets(buf, 128, fp)) != NULL) { line_cnt++; s = cp; d = ebuf; eflag = 0; // make copy for error messages and insure no '|' characters allowed while(1) { c = *s++; if((c == '\r') || (c == '\n') || (c == '\0')) { *d++ = '\0'; break; } if(c == '|') { errs++; } // can't have since used as separator *d++ = c; } if(eflag) SYNTAX_ERROR skip_whitespace(&cp); if((*cp == '#') || (*cp == '\r') || (*cp == '\n') || (*cp == '\0')) { continue; // skip comments and empty lines } if(get_token(&cp, cmd ) == NULL) SYNTAX_ERROR if(get_token(&cp, arg1) == NULL) SYNTAX_ERROR if(strcmp(cmd, "DIALED") == 0) { if(get_token(&cp, arg2) == NULL) SYNTAX_ERROR add_dialed(arg1, arg2); } else if(strcmp(cmd, "SPEED") == 0) { if(get_token(&cp, arg2) == NULL) SYNTAX_ERROR add_speed(arg1, arg2); } else if(strcmp(cmd, "PHONEBOOK_SERVER") == 0) { add_phonebook(arg1); } else { for(i = 0; config[i].command != NULL; i++) { if(strcmp(cmd, config[i].command) == 0) { MSG2(("Setting (%s) to (%s)\n", cmd, arg1)) if(config[i].type == T_INT) // int { *((int *)(config[i].value)) = atoi(arg1); } else if(config[i].type == T_STR) // string { *((char **)(config[i].value)) = new_string(arg1); } else if(config[i].type == T_BOOL) // yes|no { *((int *)(config[i].value)) = get_bool(arg1); } break; } } if(config[i].command == NULL) SYNTAX_ERROR } } fclose(fp); if(errs) { exit(-1); } // may remove since really should use PHONEBOOK_SERVER instead if(server_primary ) { add_phonebook(server_primary ); } if(server_secondary) { add_phonebook(server_secondary); } if(public_address_tcp) { port = scan_address(public_address_tcp, port_listen_tcp); if(resolve_address(public_address_tcp, port, &public_ip_and_port_tcp) != 0) { exit(-1); } } else { init_address(port_listen_tcp, &public_ip_and_port_tcp); } if(port_listen_udp >= 0) // build udp info if enabled { if(public_address_udp) { port = scan_address(public_address_udp, port_listen_udp); if(resolve_address(public_address_udp, port, &public_ip_and_port_udp) != 0) { exit(-1); } } else { init_address(port_listen_udp, &public_ip_and_port_udp); } } if((device_volume < 0) || (device_volume > 6)) { ERR(("Bogus value(%d) for 'DEVICE_VOLUME' in <%s>, default set\n", device_volume, ini_name)) device_volume = 3; } } // --------------------------------------------------------------------------- // // The End! //