// // File: // // Directory server program for the fobbit voip-blaster program // // Written by: David M. Stanhope // // TODO: // catch signal (INTR) to dump just before exit // add flag is memory data-base changed, then do the dump on exit // allow multiple remote servers // // WHAT ABOUT RECEIVE_PORT, WHY NOT USING IT? // (NOT REALLY USEFULL IN TCP SINCE WILL BE DIFFERENT WITH LISTEN PORT) // MIRROR 'last_alert' and 'last_query'?? // #define CURRENT_VERSION_MAJOR 0 #define CURRENT_VERSION_MINOR 55 #define MINIMUM_VERSION_MINOR 53 #define SUPPORT_VERSION_MINOR 50 #define ALERT_MSG0 \ "|ALERT=[SOFTWARE VERSION %d.%d AVAILABLE FOR DOWNLOAD AT www.fobbit.com]\n"\ "[Upgrade required to insure compatibility with future releases!]\n"\ "[Just replace 'vb.exe' & check 'vb.ini' for any changes needed!]\n" #define ALERT_MSG1 \ "|ALERT=[SOFTWARE VERSION %d.%d AVAILABLE FOR DOWNLOAD AT www.fobbit.com]\n"\ "[Upgrade strongly recommened, several bug-fixes & new features!]\n"\ "[Just replace 'vb.exe' & check 'vb.ini' for any changes needed!]\n" #define ALERT_MSG2 \ "|ALERT=[SOFTWARE VERSION %d.%d AVAILABLE FOR DOWNLOAD AT www.fobbit.com]\n"\ "[This release features some bug-fixes and performance upgrades!]\n"\ "[Just replace 'vb.exe' & check 'vb.ini' for any changes needed!]\n" #include #include #include #include #include #include #include #include #include #include #include #include "timestamp.h" #define MSG(x) { printf x ; fflush(stdout); } #define ERR(x) { printf x ; fflush(stdout); } #define MSG0(x) MSG(x) #define MSG1(x) // nothing // flag bits #define FLAGS_STARTUP 0x80 #define FLAGS_HEADSET 0x40 #define FLAGS_INBOUND 0x01 typedef unsigned long long U64; typedef int SOCKET; #define SOCKET_ERROR (-1) #define INVALID_SOCKET (-1) #define Socket_Close(fd) close(fd) #define Socket_Error() Show_Error() #include "vars.inc" #define ONE_MINUTE (60) // seconds #define ONE_HOUR (60 * ONE_MINUTE) #define ONE_DAY (24 * ONE_HOUR ) #define ONE_WEEK ( 7 * ONE_DAY ) static int port_listen = 8009 ; // tcp port static int port_mirror = 8011 ; // udp port static int interval_select = 30 ; // in seconds static int interval_dump = (5 * ONE_MINUTE); // in seconds static int interval_register = (5 * ONE_MINUTE); // in seconds static int interval_alert = (6 * ONE_HOUR ); // in seconds static int connect_timeout = 30 ; // in seconds static int debug_level = 1 ; // so dump will work static int have_changes = 0 ; // no changes yet static char *mirror_name = NULL; // no mirrors by default static SOCKET fd_mirror = INVALID_SOCKET; // no mirrors by default static struct sockaddr_in mirror_address; #define BSIZE 4096 typedef struct _connection_ { struct _connection_ *next ; SOCKET fd ; u_short port ; struct in_addr sin_addr ; char name[64 ]; char ibuf[BSIZE]; int have ; time_t start ; // when connection made } CONNECTION; static CONNECTION *connection_head = NULL; typedef struct _phonebook_ { struct _phonebook_ *next ; struct _phonebook_ *prev ; char serial_number [32]; char receive_ip [16]; // xxx.xxx.xxx.xxx\0 char listen_ip [16]; // xxx.xxx.xxx.xxx\0 char listen_port [ 6]; // 65535\0 char remote_name [64]; char remote_location[64]; char remote_email [64]; char remote_flags [ 8]; char remote_version [ 8]; char remote_os [32]; time_t created ; // the time record created time_t updated ; // last time record updated time_t received ; // last time record received time_t last_alert ; // last time alert sent time_t last_query ; // last time query received/sent } PHONEBOOK; static PHONEBOOK *phonebook_head = NULL; // most active static PHONEBOOK *phonebook_tail = NULL; // least active void pb_add_head(PHONEBOOK *pb) { pb->next = NULL; if(phonebook_head == NULL) // is empty { pb->prev = NULL ; phonebook_tail = pb ; } else { pb->prev = phonebook_head; phonebook_head->next = pb ; } phonebook_head = pb; } void pb_add_tail(PHONEBOOK *pb) { pb->prev = NULL; if(phonebook_tail == NULL) // is empty { pb->next = NULL ; phonebook_head = pb ; } else { pb->next = phonebook_tail; phonebook_tail->prev = pb ; } phonebook_tail = pb; } void pb_unlink(PHONEBOOK *pb) { if((pb->next == NULL) && (pb->prev == NULL)) // must be only entry on list { phonebook_head = NULL ; phonebook_tail = NULL ; } else if(pb->next == NULL) // must be at head with at least one prev entry { phonebook_head = pb->prev; phonebook_head->next = NULL ; } else if(pb->prev == NULL) // must be at tail with at least one next entry { phonebook_tail = pb->next; phonebook_tail->prev = NULL ; } else // must have an entry on both sides { pb->next->prev = pb->prev; pb->prev->next = pb->next; } } // --------------------------------------------------------------------------- static char * Show_Error(void) { static char buf[BSIZE]; sprintf(buf, "%d:%s", errno, strerror(errno)); return buf; } // --------------------------------------------------------------------------- int hex_char(int c) { c &= 0x0f; if(c < 10) return c + '0'; else return (c - 10) + 'a'; } // --------------------------------------------------------------------------- // dump data at given address with given length in hex and ascii // --------------------------------------------------------------------------- #define DW 16 void dump(char *msg, u_char *sp, int len) { int i, n, c, offset; char *dp, *cp, dbuf[(DW * 3) + 1], cbuf[DW + 1]; if(debug_level && (len > 0)) { offset = 0; MSG(("%s(%02d)\n", msg, len)) while(len > 0) { dp = dbuf; cp = cbuf; if(len > DW) { n = DW; } else { n = len; } for(i = 0; i < DW; i++) { if(i < n) { c = *sp++; *dp++ = hex_char(c >> 4); *dp++ = hex_char(c ); *cp++ = ((c >= ' ') && (c <= '~')) ? c : '.'; } else { *dp++ = 'x'; *dp++ = 'x'; *cp++ = 'x'; } *dp++ = ' '; } *dp++ = '\0'; *cp++ = '\0'; MSG(("%08lx: %s %s\n", offset, dbuf, cbuf)) offset += DW; len -= DW; } } } // --------------------------------------------------------------------------- char * scan_to(char **s, int t) { int c; char *cp, *bp; bp = *s; cp = bp; while(c = *cp) { if(c == t) { *cp++ = '\0'; *s = cp; return bp; } cp++; } if(t == '\0') { *s = cp; return bp; } ERR(("scan_to: end-of-string on (%s)\n", bp)) exit(-1); } static int update_check(char *src, char *dst, int len) { len--; // insure room for the '\0'; if(strlen(src) > len) { src[len] = '\0'; } if(strcmp(src, dst) == 0) return 0; // no change strcpy(dst, src); return 1; } // --------------------------------------------------------------------------- static char *name_phonebook = "phonebook.db"; static char *name_stats = "stats.log" ; static PHONEBOOK * lookup_phonebook(char *serial_number) { PHONEBOOK *pb; for(pb = phonebook_head; pb; pb = pb->prev) { if(strcmp(pb->serial_number, serial_number) == 0) { MSG1(("FOUND Serial-Number: %s\n" " IP Receive : %s\n" " IP Listen : %s\n" " Port : %s\n" " Name : %s\n" " Location : %s\n" " Email : %s\n" " Flags : %s\n" " Version : %s\n" " OS : %s\n", pb->serial_number , pb->receive_ip , pb->listen_ip , pb->listen_port , pb->remote_name , pb->remote_location, pb->remote_email , pb->remote_flags , pb->remote_version , pb->remote_os )) return pb; } } MSG1(("NOT FOUND\n")) return NULL; } #define M_S (60 ) #define H_S (60 * 60 ) #define D_S (60 * 60 * 24) static char * elasped(time_t last) { static char buf[64]; long d, h, m, s; s = RIGHT_NOW - last; d = s / D_S; s -= (d * D_S); h = s / H_S; s -= (h * H_S); m = s / M_S; s -= (m * M_S); sprintf(buf, "%ld:%02ld:%02ld:%02ld", d, h, m, s); return buf; } static void log_stats(char type, char *info) { FILE *fp; if((fp = fopen(name_stats, "a")) == NULL) { ERR(("Can't open <%s> for append\n", name_stats)) return; } fprintf(fp, "%c %ld %s\n", type, RIGHT_NOW, info); fclose(fp); } static void phonebook_dump(void) { FILE *fp; PHONEBOOK *pb; time_t t; char lbuf[80]; int count_records = 0, count_active = 0, count_day = 0, count_week = 0; have_changes = 0; // do first so if aborts won't do again if((fp = fopen(name_phonebook, "w")) == NULL) { ERR(("Can't open <%s> for write\n", name_phonebook)) return; } for(pb = phonebook_head; pb; pb = pb->prev) { fprintf(fp,"%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%ld|%ld|%ld|%ld|%ld|%s\n", pb->serial_number , pb->receive_ip , pb->listen_ip , pb->listen_port , pb->remote_name , pb->remote_location , pb->remote_email , pb->remote_flags , pb->remote_version , pb->remote_os , pb->created , pb->updated , pb->received , pb->last_alert , pb->last_query , elasped(pb->received)); count_records++; t = RIGHT_NOW - pb->received; if(t <= interval_register) { count_active++; } if(t <= ONE_DAY ) { count_day++ ; } if(t <= ONE_WEEK ) { count_week++ ; } } fclose(fp); MSG(("Phonebook Dump of (%d,%d,%d,%d) records (%s)\n", count_records, count_week, count_day, count_active, timestamp())) // LOG STATISTICS sprintf(lbuf, "%d %d %d %d", count_records, count_week , count_day , count_active); log_stats('D', lbuf); } static void phonebook_load(void) { FILE *fp; PHONEBOOK *pb; char *s, buf[BSIZE]; int n, count = 0; char *serial_number ; char *receive_ip ; char *listen_ip ; char *listen_port ; char *remote_name ; char *remote_location; char *remote_email ; char *remote_flags ; char *remote_version ; char *remote_os ; char *time_created ; char *time_updated ; char *time_received ; char *time_alert ; char *time_query ; char *minutes_since ; MSG(("Loading Phonebook\n")) if((fp = fopen(name_phonebook, "r")) == NULL) { ERR(("Can't open <%s> for reading\n", name_phonebook)) exit(-1); } while(fgets(buf, BSIZE, fp)) { if(((n = strlen(buf)) > 0) && (buf[n - 1] == '\n')) { buf[n - 1] = '\0'; } MSG1(("LOAD(%s)\n", buf)) // parse out fields s = buf; serial_number = scan_to(&s, '|'); receive_ip = scan_to(&s, '|'); listen_ip = scan_to(&s, '|'); listen_port = scan_to(&s, '|'); remote_name = scan_to(&s, '|'); remote_location = scan_to(&s, '|'); remote_email = scan_to(&s, '|'); remote_flags = scan_to(&s, '|'); remote_version = scan_to(&s, '|'); remote_os = scan_to(&s, '|'); time_created = scan_to(&s, '|'); time_updated = scan_to(&s, '|'); time_received = scan_to(&s, '|'); time_alert = scan_to(&s, '|'); time_query = scan_to(&s, '|'); minutes_since = scan_to(&s, '\0'); // not used if((pb = lookup_phonebook(serial_number)) != NULL) { ERR(("load_phonebook: skipping duplicate entry (%s)\n", serial_number)) continue; } if((pb = (PHONEBOOK *) malloc(sizeof(PHONEBOOK))) == NULL) { ERR(("load_phonebook: malloc failed for (%s)!\n", serial_number)) exit(-1); } memset(pb, 0, sizeof(PHONEBOOK)); #define LOAD_DATA(x) update_check(x, pb->x, sizeof(pb->x)) LOAD_DATA(serial_number ); LOAD_DATA(receive_ip ); LOAD_DATA(listen_ip ); LOAD_DATA(listen_port ); LOAD_DATA(remote_name ); LOAD_DATA(remote_location); LOAD_DATA(remote_email ); LOAD_DATA(remote_flags ); LOAD_DATA(remote_version ); LOAD_DATA(remote_os ); //#undef LOAD_DATA(x) #undef LOAD_DATA pb->created = atol(time_created ); pb->updated = atol(time_updated ); pb->received = atol(time_received); pb->last_alert = atol(time_alert ); pb->last_query = atol(time_query ); pb_add_tail(pb); // file goes most active to least active count++; } fclose(fp); MSG(("Loaded (%d) Records\n", count)) } // --------------------------------------------------------------------------- static void init_connection(CONNECTION *cp, SOCKET fd, struct sockaddr_in *pa) { cp->have = 0; cp->start = RIGHT_NOW; cp->port = ntohs(pa->sin_port); memcpy(&(cp->sin_addr), &(pa->sin_addr), sizeof(struct in_addr)); sprintf(cp->name, "%s:%d:%d", inet_ntoa(cp->sin_addr), cp->port, fd); cp->fd = fd; cp->next = NULL; } static void new_connection(SOCKET fd, struct sockaddr_in *pa) { CONNECTION *cp; if((cp = (CONNECTION *) malloc(sizeof(CONNECTION))) == NULL) { ERR(("new_conneciton: malloc failed!\n")) exit(-1); } init_connection(cp, fd, pa); cp->next = connection_head; connection_head = cp; // add to list MSG1(("%s Connect\n", cp->name)) } // --------------------------------------------------------------------------- void respond(CONNECTION *cp, char *msg) { write(cp->fd, msg, strlen(msg) + 1); } // --------------------------------------------------------------------------- static char * check_update(char *remote_version) { char *cp; int version_major, version_minor; if((cp = strchr(remote_version, '.')) == NULL) { return NULL; // no update needed, really an error! } version_major = atoi(remote_version); version_minor = atoi(cp + 1 ); MSG1(("VERSION(%d,%d)\n", version_major, version_minor)) if(version_major != CURRENT_VERSION_MAJOR) // !!!FOR NOW!!! { return NULL; // no update needed, really an error! } if(version_minor < SUPPORT_VERSION_MINOR) { return ALERT_MSG0; // send update alert } if(version_minor < MINIMUM_VERSION_MINOR) { return ALERT_MSG1; // send update alert } if(version_minor < CURRENT_VERSION_MINOR) { return ALERT_MSG2; // send update alert } return NULL; // no update needed } // --------------------------------------------------------------------------- // if message from mirror, then 'cp' will be NULL int check_register(CONNECTION *cp, char *msg, int mirror_flag) { PHONEBOOK *pb; char *s, buf[BSIZE]; int created, updated; time_t now, tt, flags; char *serial_number ; char *receive_ip ; char *listen_ip ; char *listen_port ; char *remote_name ; char *remote_location; char *remote_email ; char *remote_flags ; char *remote_version ; char *remote_os ; char *rtime = NULL; char *cmd = NULL; char *udp_ip = NULL; char *udp_port = NULL; char *peer_ip = NULL; char *peer_port = NULL; MSG1(("%s (%s)\n", cp->name, msg)) receive_ip = inet_ntoa(cp->sin_addr); // parse out fields if((serial_number = var0_chk("register" )) == NULL) return 0; if((listen_ip = var_find("ip" )) == NULL) return 0; if((listen_port = var_find("port" )) == NULL) return 0; if((remote_name = var_find("name" )) == NULL) return 0; if((remote_location = var_find("location" )) == NULL) return 0; if((remote_email = var_find("email" )) == NULL) return 0; if((remote_flags = var_find("flags" )) == NULL) return 0; if((remote_version = var_find("version" )) == NULL) return 0; if((remote_os = var_find("os" )) == NULL) return 0; rtime = var_find("rtime" ); cmd = var_find("cmd" ); udp_ip = var_find("udp_ip" ); udp_port = var_find("udp_port" ); peer_ip = var_find("peer_ip" ); peer_port = var_find("peer_port"); flags = atoi(remote_flags); MSG0(("REGISTER from (%s) (%s) (%d)\n" " Serial-Number: %s\n" " IP Receive : %s\n" " IP Listen : %s\n" " Port : %s\n" " Name : %s\n" " Location : %s\n" " Email : %s\n" " Flags : 0x%02x\n" " Version : %s\n" " OS : %s\n", cp->name , timestamp() , mirror_flag , serial_number , receive_ip , listen_ip , listen_port , remote_name , remote_location, remote_email , flags , remote_version , remote_os )) if((rtime) && ((tt = atol(rtime)) != 0)) { MSG0((" Rtime : %s\n", time_string_time_t(tt))) } if(cmd ) { MSG0((" Cmt : %s\n", cmd )) } if(udp_ip ) { MSG0((" Udp_Ip : %s\n", udp_ip )) } if(udp_port ) { MSG0((" Udp_Port : %s\n", udp_port )) } if(peer_ip ) { MSG0((" Peer_IP : %s\n", peer_ip )) } if(peer_port) { MSG0((" Peer_Port : %s\n", peer_port)) } now = RIGHT_NOW; created = 0; updated = 0; // if got it replace peer ip with value from intermediate client if(peer_ip) { receive_ip = peer_ip; } if((pb = lookup_phonebook(serial_number)) == NULL) { if((pb = (PHONEBOOK *) malloc(sizeof(PHONEBOOK))) == NULL) { ERR(("new_phonebook: malloc failed for (%s)!\n", serial_number)) exit(-1); } memset(pb, 0, sizeof(PHONEBOOK)); pb->created = now; MSG((" NEW\n")) created = 1; } else { pb_unlink(pb); // so can move it to the head if(pb->last_query) { MSG0((" Last Query : %s\n", elasped(pb->last_query))) } } pb_add_head(pb); // mark it as most active pb->received = now; #define CHANGE_CHECK(x) updated += update_check(x, pb->x, sizeof(pb->x)) CHANGE_CHECK(serial_number ); CHANGE_CHECK(receive_ip ); CHANGE_CHECK(listen_ip ); CHANGE_CHECK(listen_port ); CHANGE_CHECK(remote_name ); CHANGE_CHECK(remote_location); CHANGE_CHECK(remote_email ); CHANGE_CHECK(remote_flags ); CHANGE_CHECK(remote_version ); CHANGE_CHECK(remote_os ); //#undef CHANGE_CHECK(x) #undef CHANGE_CHECK if(updated) { pb->updated = now; if(created == 0) { MSG((" CHANGED\n")) } } // get ready to send the response sprintf(buf, "rtimer=%d", interval_register); //#define FORCE_ALERT "104747227" //#define FORCE_ALERT "108508867" #ifdef FORCE_ALERT if(strcmp(serial_number, FORCE_ALERT) == 0) { pb->last_alert = 0; remote_version = "0.42"; } #endif // see if need to bug them about new software, do every so often // and always on software startup if((now > (pb->last_alert + interval_alert)) || (flags & FLAGS_STARTUP)) { if((s = check_update(remote_version)) != NULL) { sprintf(buf + strlen(buf), s, CURRENT_VERSION_MAJOR, CURRENT_VERSION_MINOR); MSG((" ALERT (%d)\n", mirror_flag)) pb->last_alert = now; } } if(mirror_flag) { return 1; // all done if a mirror message, say handled here } respond(cp, buf); if(mirror_name) { // if sending message to mirror, may need to add peer info if(peer_ip == NULL) // add peer_ip if not there { sprintf(msg + strlen(msg), "|peer_ip=%s", receive_ip); } if(peer_port == NULL) // add peer_port if not there { sprintf(msg + strlen(msg), "|peer_port=%d", cp->port); } MSG1(("RSEND(%s)\n", msg)) sendto(fd_mirror, msg, strlen(msg) + 1, 0, (struct sockaddr *) &mirror_address, sizeof(struct sockaddr_in)); } return 1; // say handled here } // --------------------------------------------------------------------------- int check_query(CONNECTION *cp, char *msg, int mirror_flag) { PHONEBOOK *caller_pb, *dialed_pb; char *response, *caller, *dialed, *listen_ip, buf[BSIZE]; // parse out fields if((caller = var0_chk("query" )) == NULL) return 0; if((dialed = var_find("lookup")) == NULL) return 0; MSG1(("%s (%s)\n", cp->name, msg)) // FIXME: more validation of message caller_pb = lookup_phonebook(caller); dialed_pb = lookup_phonebook(dialed); MSG0(("QUERY from (%s) (%s) (%d)\n" " Serial-Number: %s (%s)\n" " Destination : %s (%s) (%s)\n", cp->name, timestamp(), mirror_flag, caller, caller_pb ? caller_pb->remote_email : "UNKNOWN", dialed, dialed_pb ? dialed_pb->remote_email : "UNKNOWN", dialed_pb ? elasped(dialed_pb->received) : "UNKNOWN")) if(caller_pb != NULL) // TODO: should caller/dialed be separate values? { caller_pb->last_query = RIGHT_NOW; } // lookup in database, and reply if found or not // TODO: ALSO RETURN NOT_FOUND IF dialed_pb->received WAS TOO LONG AGO! if(dialed_pb == NULL) { response = "serial=UNKNOWN"; } else { // if ip is 0, use recv-from, this should be the normal case if(strcmp(dialed_pb->listen_ip, "0.0.0.0") == 0) { listen_ip = dialed_pb->receive_ip; } else { listen_ip = dialed_pb->listen_ip; } sprintf(buf, "serial=%s|ip=%s|port=%s|name=%s|location=%s|" "email=%s|flags=%s|version=%s|os=%s|" "ctime=%ld|utime=%ld|rtime=%ld", dialed_pb->serial_number , listen_ip , dialed_pb->listen_port , dialed_pb->remote_name , dialed_pb->remote_location, dialed_pb->remote_email , dialed_pb->remote_flags , dialed_pb->remote_version , dialed_pb->remote_os , dialed_pb->created , dialed_pb->updated , dialed_pb->received ); response = buf; dialed_pb->last_query = RIGHT_NOW; } if(mirror_flag) { return 1; // all done if a mirror message, say handled here } respond(cp, response); if(mirror_name) { MSG1(("QSEND(%s)\n", msg)) sendto(fd_mirror, msg, strlen(msg) + 1, 0, (struct sockaddr *) &mirror_address, sizeof(struct sockaddr_in)); } return 1; // say handled here } int check_stats(CONNECTION *cp, char *msg, int mirror_flag) { PHONEBOOK *pb; char *source, *dir, *peer, *tcp_tx, *udp_tx, *tcp_rx, *udp_rx, *drop, *queue, *secs, *src_email, *src_ver, *peer_email, *peer_ver, *reason; if((source = var0_chk("stats" )) == NULL) return 0; if((dir = var_find("dir" )) == NULL) return 0; if((peer = var_find("peer" )) == NULL) return 0; if((tcp_tx = var_find("tcp_tx")) == NULL) return 0; if((udp_tx = var_find("udp_tx")) == NULL) return 0; if((tcp_rx = var_find("tcp_rx")) == NULL) return 0; if((udp_rx = var_find("udp_rx")) == NULL) return 0; if((drop = var_find("drop" )) == NULL) return 0; if((queue = var_find("queue" )) == NULL) return 0; if((secs = var_find("secs" )) == NULL) return 0; reason = var_find("reason"); if(pb = lookup_phonebook(source)) { src_email = pb->remote_email ; src_ver = pb->remote_version; } else { src_email = "UNKNOWN"; src_ver = "UNKN" ; } if(pb = lookup_phonebook(peer)) { peer_email = pb->remote_email ; peer_ver = pb->remote_version; } else { peer_email = "UNKNOWN"; peer_ver = "UNKN" ; } MSG0(("STATS from (%s) (%s) (%d)\n" " Serial-Number: %s (%s) %s %s (%s)\n" " Email : (%s) %s (%s)\n" " TX (TCP/UDP) : %s/%s\n" " RX (TCP/UDP) : %s/%s\n" " Drop/Queue : %s/%s\n" " Seconds : %s\n", cp->name , timestamp(), mirror_flag, source , src_ver , dir , peer , peer_ver , src_email , dir , peer_email , tcp_tx , udp_tx , tcp_rx , udp_rx , drop , queue , secs )) // 1 (tcp socket closed) // 2 (went on-hook) // 3 (tcp socket-write failed) // 4 (udp socket-write failed) // 5 (udp socket-write length mismatch) if(reason) { char *rs; switch(atoi(reason)) { case 1: rs = "tcp closed"; break; case 2: rs = "on-hook" ; break; case 3: rs = "tcp write" ; break; case 4: rs = "udp write" ; break; case 5: rs = "udp length"; break; default: rs = "unknown" ; break; } MSG0((" Hangup-Reason: %s (%s)\n", reason, rs)) } if(mirror_flag == 0) { respond(cp, "rx=ok"); if(mirror_name) { MSG1(("SSEND(%s)\n", msg)) sendto(fd_mirror, msg, strlen(msg) + 1, 0, (struct sockaddr *) &mirror_address, sizeof(struct sockaddr_in)); } } // LOG STATISTICS log_stats(((strcmp(dir, "to") == 0) ? 'T' : 'F'), secs); return 1; // say handled here } // --------------------------------------------------------------------------- static void set_signals(void (*handler)(int)) { signal(SIGHUP , handler); signal(SIGINT , handler); signal(SIGQUIT, handler); signal(SIGABRT, handler); signal(SIGFPE , handler); signal(SIGSEGV, handler); signal(SIGPIPE, handler); signal(SIGALRM, handler); signal(SIGTERM, handler); signal(SIGCHLD, handler); signal(SIGBUS , handler); } static void catch_signals(int n) { set_signals(SIG_IGN); // if any new changes since lost dump, then dump again if(have_changes) { MSG0(("Signal (%d) Caught, Changes (%d)\n", n, have_changes)) phonebook_dump(); MSG0(("Exiting\n")) } else { MSG0(("Signal (%d) Caught, Exiting\n", n)) } exit(1); } // --------------------------------------------------------------------------- static SOCKET open_socket(char *msg, int type, int port) { int fd, n; struct sockaddr_in address; if((fd = socket(PF_INET, type, 0)) == INVALID_SOCKET) { ERR(("socket-%s: error(%s)\n", msg, Socket_Error())) exit(-1); } #if 1 n = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)); #ifdef SO_REUSEPORT n = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &n, sizeof(n)); #endif #endif memset((char *) &address, 0, sizeof(address)); #ifndef __linux__ address.sin_len = sizeof(struct sockaddr_in); #endif address.sin_family = AF_INET ; address.sin_port = htons((u_short) port); address.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(fd, (struct sockaddr *) &address, sizeof(address)) == SOCKET_ERROR) { ERR(("bind-%s: error(%s)\n", msg, Socket_Error())) exit(-1); } return fd; } void init_address(int port, struct sockaddr_in *pa) { memset((char *) pa, 0, sizeof(struct sockaddr_in)); #ifndef __linux__ 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); } MSG1(("resolve_address: (%s) is (%s)\n", name, inet_ntoa(pa->sin_addr))) return 0; } main(int argc, char *argv[]) { int i, r, n, err, fromlen; fd_set imask; struct timeval timer ; struct sockaddr peer_addr; struct sockaddr_in from ; SOCKET fd_accept, fd_peer, fd_max; CONNECTION *cp, *np, tc; time_t dump_timer; char buf[BSIZE]; MSG(("VSERVER Started (%s)\n", timestamp())) for(err = 0, i = 1; i < argc; i++) { if(strcmp(argv[i], "interval_register") == 0) { if(++i >= argc) { ERR(("no value given for 'interval_register'!\n")) err = 1; break; } interval_register = atoi(argv[i]); // set new value if(interval_register < 10) interval_register = 10; continue; } if(strcmp(argv[i], "interval_dump") == 0) { if(++i >= argc) { ERR(("no value given for 'interval_dump'!\n")) err = 1; break; } interval_dump = atoi(argv[i]); // set new value if(interval_dump < 10) interval_dump = 10; continue; } if(strcmp(argv[i], "interval_alert") == 0) { if(++i >= argc) { ERR(("no value given for 'interval_alert'!\n")) err = 1; break; } interval_alert = atoi(argv[i]); // set new value if(interval_alert < 10) interval_alert = 10; continue; } if(strcmp(argv[i], "interval_select") == 0) { if(++i >= argc) { ERR(("no value given for 'interval_select'!\n")) err = 1; break; } interval_select = atoi(argv[i]); // set new value if(interval_select < 1) interval_select = 1; continue; } if(strcmp(argv[i], "connect_timeout") == 0) { if(++i >= argc) { ERR(("no value given for 'connect_timeout'!\n")) err = 1; break; } connect_timeout = atoi(argv[i]); // set new value if(connect_timeout < 1) connect_timeout = 1; continue; } if(strcmp(argv[i], "mirror_name") == 0) { if(++i >= argc) { ERR(("no name given for 'mirror_name'!\n")) err = 1; break; } mirror_name = argv[i]; // set new value continue; } ERR(("invalid argument (%s) given!\n", argv[i])) err = 1; break; } if(err) { ERR(("Usage: vserver [interval_register (value)]\n" " [interval_dump (value)]\n" " [interval_alert (value)]\n" " [interval_select (value)]\n" " [connect_timeout (value)]\n" " [peer_name (name )]\n")) ERR(("Exiting!\n")) exit(-1); } MSG(("Setting \'interval_register\' to (%d)\n", interval_register)) MSG(("Setting \'interval_dump\' to (%d)\n", interval_dump )) MSG(("Setting \'interval_alert\' to (%d)\n", interval_alert )) MSG(("Setting \'interval_select\' to (%d)\n", interval_select )) MSG(("Setting \'connect_timeout\' to (%d)\n", connect_timeout )) if(mirror_name) { MSG(("Setting \'mirror_name\' to (%s)\n", mirror_name )) } // ----------------------------------------------------------------------- phonebook_load(); // ----------------------------------------------------------------------- // build socket to listen for connections on // ----------------------------------------------------------------------- fd_accept = open_socket("accept", SOCK_STREAM, port_listen); if(listen(fd_accept, 5) == SOCKET_ERROR) { ERR(("listen: error(%s)\n", Socket_Error())) exit(-1); } if(mirror_name) { if(resolve_address(mirror_name, port_mirror, &mirror_address) < 0) { exit(-1); } } // always be ready to listen fd_mirror = open_socket("mirror", SOCK_DGRAM, port_mirror); dump_timer = RIGHT_NOW + interval_dump; MSG(("Waiting for Connections (%s)\n", timestamp())) set_signals(catch_signals); while(1) { if(RIGHT_NOW >= dump_timer) { set_signals(SIG_IGN); // let dump complete phonebook_dump(); set_signals(catch_signals); dump_timer = RIGHT_NOW + interval_dump; } fd_max = 0; FD_ZERO(&imask); FD_SET(fd_accept, &imask); if(fd_accept > fd_max) { fd_max = fd_accept; } FD_SET(fd_mirror, &imask); if(fd_mirror > fd_max) { fd_max = fd_mirror; } for(cp = connection_head; cp; cp = cp->next) { FD_SET(cp->fd, &imask); if(cp->fd > fd_max) { fd_max = cp->fd; } } timer.tv_sec = interval_select; timer.tv_usec = 0; r = select(fd_max + 1, &imask, NULL, NULL, &timer); if(r < 0) { ERR(("select: r(%d) error(%s)\n", r, Socket_Error())) exit(-1); } if(r == 0) { continue; // go check if need to do a dump } if(FD_ISSET(fd_accept, &imask)) // any connections { n = sizeof(struct sockaddr); if((fd_peer = accept(fd_accept, &peer_addr, &n)) == INVALID_SOCKET) { ERR(("socket-accept: error(%s)\n", Socket_Error())) exit(-1); } new_connection(fd_peer, (struct sockaddr_in *) &peer_addr); } // as scan the list, also rebuild it, dropping any that closed for(cp = connection_head, connection_head = NULL; cp; cp = np) { np = cp->next; if(RIGHT_NOW > (cp->start + connect_timeout)) { MSG(("%s Timeout\n", cp->name)) if(cp->have > 0) { dump(cp->name, cp->ibuf, cp->have); } Socket_Close(cp->fd); free(cp); continue; // drop from list by not relinking it } if(FD_ISSET(cp->fd, &imask)) // any data { // TODO: need buffer per conneciton and need to wait some few seconds // for the EOS if((n = read(cp->fd,cp->ibuf + cp->have,BSIZE - cp->have))<=0) { MSG1(("%s Close, Read Got (%d)\n", cp->name, n)) Socket_Close(cp->fd); free(cp); continue; // drop from list by not relinking it } // got something cp->have += n; if(cp->ibuf[cp->have - 1] == '\0') { MSG1(("%s ?(%s)(%d,%d)\n", cp->name, cp->ibuf, n, strlen(cp->ibuf))) var_parse(cp->ibuf); // parse out variables if((check_register(cp, cp->ibuf, 0) != 1) && (check_query (cp, cp->ibuf, 0) != 1) && (check_stats (cp, cp->ibuf, 0) != 1)) { MSG0(("%s U(%s)\n", cp->name, cp->ibuf)) // TODO: send something but probably better to // have a timeout in the client? } else { have_changes++; // something was updated } cp->have = 0; } } // keep this connection by relinking it to the list cp->next = connection_head; connection_head = cp; } if(FD_ISSET(fd_mirror, &imask)) // any mirror messages { fromlen = sizeof(struct sockaddr_in); n = recvfrom(fd_mirror, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if(n < 0) { MSG(("Mirror Recv Got(%d)(%s)\n", n, Show_Error())) } else if(n == 0) { MSG(("Mirror Recv Got(0)\n")) } else if(buf[n - 1] != '\0') { MSG(("Mirror Recv No EOS\n")) dump("Mirror", buf, n); } else { MSG1(("Mirror Recv Got(%d)(%s)\n", n, buf)) init_connection(&tc, fd_mirror, &from); var_parse(buf); // parse out variables if((check_register(&tc, buf, 1) != 1) && (check_query (&tc, buf, 1) != 1) && (check_stats (&tc, buf, 1) != 1)) { MSG0(("%s MU(%s)\n", cp->name, buf)) } else { have_changes++; // something was updated } } } } } // // The End! //