/* * cyclically called to do the aging of the URL mapping table entries * and throw out expired entries. * Also we do the cyclic saving here - if required. */ void register_agemap(void) { int i; time_t t; /* expire old entries */ time(&t); DEBUGC(DBCLASS_BABBLE,"sip_agemap, t=%i",(int)t); for (i=0; i<URLMAP_SIZE; i++) { if ((urlmap[i].active == 1) && (urlmap[i].expires < t)) { DEBUGC(DBCLASS_REG,"cleaned entry:%i %s@%s", i, urlmap[i].masq_url->username, urlmap[i].masq_url->host); urlmap[i].active=0; osip_uri_free(urlmap[i].true_url); osip_uri_free(urlmap[i].masq_url); osip_uri_free(urlmap[i].reg_url); } } /* auto-save of registration table */ if ((configuration.autosave_registrations > 0) && ((last_save + configuration.autosave_registrations) < t)) { register_save(); last_save = t; } return; }
/* * Processing. * */ int PLUGIN_PROCESS(int stage, sip_ticket_t *ticket){ /* stage contains the PLUGIN_* value - the stage of SIP processing. */ int type; osip_via_t *via; struct sockaddr_in from; type = ticket->direction; /* Incoming SIP message? */ DEBUGC(DBCLASS_PLUGIN, "plugin_fix_bogus_via: type=%i", type); if (type == REQTYP_INCOMING) { if((via = osip_list_get(&(ticket->sipmsg->vias), 0)) == NULL) { WARN("no Via header found in incoming SIP message"); return STS_SUCCESS; } get_ip_by_host(via->host, &(from.sin_addr)); /* check for Via IP in configured range */ if ((plugin_cfg.networks != NULL) && (strcmp(plugin_cfg.networks, "") !=0) && (process_aclist(plugin_cfg.networks, from) == STS_SUCCESS)) { /* is in list, patch Via header with received from IP */ DEBUGC(DBCLASS_PLUGIN, "plugin_fix_bogus_via: replacing a bogus via"); if (sip_patch_topvia(ticket) == STS_FAILURE) { ERROR("patching inbound Via failed!"); } } } return STS_SUCCESS; }
/* * check if a given request (outbound -> inbound) shall its * request URI get rewritten based upon our UA knowledge * * RETURNS * STS_TRUE if to be rewritten * STS_FALSE otherwise */ int check_rewrite_rq_uri (osip_message_t *sip) { int i, j, sts; int dflidx; osip_header_t *ua_hdr; /* get index of default entry */ dflidx=(sizeof(RQ_rewrite)/sizeof(RQ_rewrite[0])) - 1; /* check fort existence of method */ if ((sip==NULL) || (sip->sip_method==NULL)) { ERROR("check_rewrite_rq_uri: got NULL method"); return STS_FALSE; } /* extract UA string */ osip_message_get_user_agent (sip, 0, &ua_hdr); if ((ua_hdr==NULL) || (ua_hdr->hvalue==NULL)) { DEBUGC(DBCLASS_SIP, "check_rewrite_rq_uri: NULL UA in Header, " "using default"); i=dflidx; } else { /* loop through the knowledge base */ for (i=0; RQ_rewrite[i].UAstring; i++) { if (strncmp(RQ_rewrite[i].UAstring, ua_hdr->hvalue, sizeof(RQ_rewrite[i].UAstring))==0) { DEBUGC(DBCLASS_SIP, "got knowledge entry for [%s]", ua_hdr->hvalue); break; } } /* for i */ } /* if ua_hdr */ for (j=0; RQ_method[j].name; j++) { if (strncmp(RQ_method[j].name, sip->sip_method, RQ_method[j].size)==0) { if (RQ_rewrite[i].action[j] >= 0) { sts = (RQ_rewrite[i].action[j])? STS_TRUE: STS_FALSE; } else { sts = (RQ_rewrite[dflidx].action[j])? STS_TRUE: STS_FALSE; } DEBUGC(DBCLASS_SIP, "check_rewrite_rq_uri: [%s:%s, i=%i, j=%i] " "got action %s", (sip && sip->sip_method) ? sip->sip_method : "*NULL*", (ua_hdr && ua_hdr->hvalue)? ua_hdr->hvalue:"*NULL*", i, j, (sts==STS_TRUE)? "rewrite":"norewrite"); return sts; } } /* for j */ WARN("check_rewrite_rq_uri: didn't get a hit of the method [%s]", sip->sip_method); return STS_FALSE; }
/* * SIP_GEN_RESPONSE * * send an proxy generated response back to the client. * Only errors are reported from the proxy itself. * code = SIP result code to deliver * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int sip_gen_response(sip_ticket_t *ticket, int code) { osip_message_t *response; int sts; osip_via_t *via; int port; char *buffer; size_t buflen; struct in_addr addr; /* create the response template */ if ((response=msg_make_template_reply(ticket, code))==NULL) { ERROR("sip_gen_response: error in msg_make_template_reply"); return STS_FAILURE; } /* we must check if first via has x.x.x.x address. If not, we must resolve it */ osip_message_get_via (response, 0, &via); if (via == NULL) { ERROR("sip_gen_response: Cannot send response - no via field"); return STS_FAILURE; } /* name resolution */ if (utils_inet_aton(via->host, &addr) == 0) { /* need name resolution */ DEBUGC(DBCLASS_DNS,"resolving name:%s",via->host); sts = get_ip_by_host(via->host, &addr); if (sts == STS_FAILURE) { DEBUGC(DBCLASS_PROXY, "sip_gen_response: cannot resolve via [%s]", via->host); return STS_FAILURE; } } sts = sip_message_to_str(response, &buffer, &buflen); if (sts != 0) { ERROR("sip_gen_response: msg_2char failed"); return STS_FAILURE; } if (via->port) { port=atoi(via->port); } else { port=SIP_PORT; } /* send to destination */ sipsock_send(addr, port, ticket->protocol, buffer, buflen); /* free the resources */ osip_message_free(response); osip_free(buffer); return STS_SUCCESS; }
/* * check if a given request is addressed to local. I.e. it is addressed * to the proxy itself (IP of my inbound or outbound interface, same port) * * RETURNS * STS_TRUE if the request is addressed local * STS_FALSE otherwise */ int is_sipuri_local (sip_ticket_t *ticket) { osip_message_t *sip=ticket->sipmsg; int found; struct in_addr addr_uri, addr_myself; int port; int i; if (sip==NULL) { ERROR("called is_sipuri_local with NULL sip"); return STS_FALSE; } if (!sip || !sip->req_uri) { ERROR("is_sipuri_local: no request URI present"); return STS_FALSE; } DEBUGC(DBCLASS_DNS,"check for local SIP URI %s:%s", sip->req_uri->host? sip->req_uri->host : "*NULL*", sip->req_uri->port? sip->req_uri->port : "*NULL*"); if (utils_inet_aton(sip->req_uri->host, &addr_uri) == 0) { /* need name resolution */ get_ip_by_host(sip->req_uri->host, &addr_uri); } found=0; for (i=0; i<2; i++) { /* * search my in/outbound interfaces */ DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s", (i==IF_INBOUND)? "inbound":"outbound"); if (get_interface_ip(i, &addr_myself) != STS_SUCCESS) { continue; } /* check the extracted HOST against my own host addresses */ if (sip->req_uri->port) { port=atoi(sip->req_uri->port); } else { port=SIP_PORT; } if ( (memcmp(&addr_myself, &addr_uri, sizeof(addr_myself))==0) && (port == configuration.sip_listen_port) ) { DEBUG("address match [%s]", utils_inet_ntoa(addr_uri)); found=1; break; } } DEBUGC(DBCLASS_DNS, "SIP URI is %slocal", found? "":"not "); return (found)? STS_TRUE : STS_FALSE; }
/* * compares two Call IDs * (by now, only hostname and username are compared) * * RETURNS * STS_SUCCESS if equal * STS_FAILURE if non equal or error */ int compare_callid(osip_call_id_t *cid1, osip_call_id_t *cid2) { if ((cid1==0) || (cid2==0)) { ERROR("compare_callid: NULL ptr: cid1=0x%p, cid2=0x%p",cid1, cid2); return STS_FAILURE; } /* * Check number part: if present must be equal, * if not present, must be not present in both cids */ if (cid1->number && cid2->number) { /* have both numbers */ if (strcmp(cid1->number, cid2->number) != 0) goto mismatch; } else { /* at least one number missing, make sure that both are empty */ if ( (cid1->number && (cid1->number[0]!='\0')) || (cid2->number && (cid2->number[0]!='\0'))) { goto mismatch; } } /* * Check host part: if present must be equal, * if not present, must be not present in both cids */ if (cid1->host && cid2->host) { /* have both hosts */ if (strcmp(cid1->host, cid2->host) != 0) goto mismatch; } else { /* at least one host missing, make sure that both are empty */ if ( (cid1->host && (cid1->host[0]!='\0')) || (cid2->host && (cid2->host[0]!='\0'))) { goto mismatch; } } DEBUGC(DBCLASS_BABBLE, "comparing callid - matched: " "%s@%s <-> %s@%s", cid1->number, cid1->host, cid2->number, cid2->host); return STS_SUCCESS; mismatch: DEBUGC(DBCLASS_BABBLE, "comparing callid - mismatch: " "%s@%s <-> %s@%s", cid1->number, cid1->host, cid2->number, cid2->host); return STS_FAILURE; }
/* * SIP_ADD_MYVIA * * interface == IF_OUTBOUND, IF_INBOUND * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int sip_add_myvia (sip_ticket_t *ticket, int interface) { struct in_addr addr; char tmp[URL_STRING_SIZE]; osip_via_t *via; int sts; char branch_id[VIA_BRANCH_SIZE]; char *myaddr; if (get_interface_ip(interface, &addr) != STS_SUCCESS) { return STS_FAILURE; } sts = sip_calculate_branch_id(ticket, branch_id); myaddr=utils_inet_ntoa(addr); sprintf(tmp, "SIP/2.0/UDP %s:%i;branch=%s", myaddr, configuration.sip_listen_port, branch_id); DEBUGC(DBCLASS_BABBLE,"adding VIA:%s",tmp); sts = osip_via_init(&via); if (sts!=0) return STS_FAILURE; /* allocation failed */ sts = osip_via_parse(via, tmp); if (sts!=0) return STS_FAILURE; osip_list_add(ticket->sipmsg->vias,via,0); return STS_SUCCESS; }
/*--------------------------------------------------------------------*/ static int sip_patch_topvia(sip_ticket_t *ticket) { osip_via_t *via; if((via = osip_list_get(&(ticket->sipmsg->vias), 0)) != NULL) { /* clone Via header */ /* set new IP (from IP) */ osip_free(via->host); via->host=osip_malloc(IPSTRING_SIZE); snprintf(via->host, IPSTRING_SIZE, "%s", utils_inet_ntoa(ticket->from.sin_addr)); via->host[IPSTRING_SIZE-1] ='\0'; /* set new port number */ osip_free(via->port); via->port=osip_malloc(6); /* 5 digits + \0 */ snprintf(via->port, 5, "%u", ntohs(ticket->from.sin_port)); via->port[5-1] ='\0'; DEBUGC(DBCLASS_PLUGIN, "plugin_fix_bogus_via: -> %s:%s", via->host, via->port); } return STS_SUCCESS; }
/* * kills the rtp_proxy thread * * RETURNS * - */ void rtpproxy_kill( void ) { void *thread_status; osip_call_id_t cid; int i, sts; /* stop any active RTP stream */ for (i=0;i<RTPPROXY_SIZE;i++) { if (rtp_proxytable[i].rtp_rx_sock != 0) { cid.number = rtp_proxytable[i].callid_number; cid.host = rtp_proxytable[i].callid_host; sts = rtp_relay_stop_fwd(&cid, rtp_proxytable[i].direction, 0); } } /* kill the thread */ if (rtpproxy_tid) { pthread_cancel(rtpproxy_tid); pthread_kill(rtpproxy_tid, SIGALRM); pthread_join(rtpproxy_tid, &thread_status); } DEBUGC(DBCLASS_RTP,"killed RTP proxy thread"); return; }
/* * check if a given osip_via_t is local. I.e. its address is owned * by my inbound or outbound interface * * RETURNS * STS_TRUE if the given VIA is one of my interfaces * STS_FALSE otherwise */ int is_via_local (osip_via_t *via) { int sts, found; struct in_addr addr_via, addr_myself; int port; int i; if (via==NULL) { ERROR("called is_via_local with NULL via"); return STS_FALSE; } DEBUGC(DBCLASS_BABBLE,"via name %s",via->host); if (utils_inet_aton(via->host,&addr_via) == 0) { /* need name resolution */ sts=get_ip_by_host(via->host, &addr_via); if (sts == STS_FAILURE) { DEBUGC(DBCLASS_DNS, "is_via_local: cannot resolve VIA [%s]", via->host); return STS_FAILURE; } } found=0; for (i=0; i<2; i++) { /* * search my in/outbound interfaces */ DEBUGC(DBCLASS_BABBLE,"resolving IP of interface %s", (i==IF_INBOUND)? "inbound":"outbound"); if (get_interface_ip(i, &addr_myself) != STS_SUCCESS) { continue; } /* check the extracted VIA against my own host addresses */ if (via->port) port=atoi(via->port); else port=SIP_PORT; if ( (memcmp(&addr_myself, &addr_via, sizeof(addr_myself))==0) && (port == configuration.sip_listen_port) ) { DEBUG("got address match [%s]", utils_inet_ntoa(addr_via)); found=1; break; } } return (found)? STS_TRUE : STS_FALSE; }
/* * sends an UDP datagram to the specified destination * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int sipsock_send(struct in_addr addr, int port, int protocol, char *buffer, int size) { struct sockaddr_in dst_addr; int sts; /* first time: allocate a socket for sending */ if (sip_udp_socket == 0) { ERROR("SIP socket not allocated"); return STS_FAILURE; } if (buffer == NULL) { ERROR("sipsock_send got NULL buffer"); return STS_FAILURE; } if (protocol != PROTO_UDP) { ERROR("sipsock_send: only UDP supported by now"); return STS_FAILURE; } dst_addr.sin_family = AF_INET; memcpy(&dst_addr.sin_addr.s_addr, &addr, sizeof(struct in_addr)); dst_addr.sin_port= htons(port); DEBUGC(DBCLASS_NET,"send UDP packet to %s: %i", utils_inet_ntoa(addr),port); DUMP_BUFFER(DBCLASS_NETTRAF, buffer, size); sts = sendto(sip_udp_socket, buffer, size, 0, (const struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr)); if (sts == -1) { if (errno != ECONNREFUSED) { ERROR("sendto() [%s:%i size=%i] call failed: %s", utils_inet_ntoa(addr), port, size, strerror(errno)); return STS_FAILURE; } DEBUGC(DBCLASS_BABBLE,"sendto() [%s:%i] call failed: %s", utils_inet_ntoa(addr), port, strerror(errno)); } return STS_SUCCESS; }
/* * Secure enviroment: * If running as root, put myself into a chroot jail and * change UID/GID to user as requested in config file */ void secure_enviroment (void) { int sts; struct passwd *passwd=NULL; DEBUGC(DBCLASS_CONFIG,"running w/uid=%i, euid=%i, gid=%i, egid=%i", (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid()); if ((getuid()==0) || (geteuid()==0)) { /* * preparation - after chrooting there will be NOTHING more around */ if (configuration.user) passwd=getpwnam(configuration.user); /* * change root directory into chroot jail */ if (configuration.chrootjail) { /* !!! * Before chrooting I must at least once trigger the resolver * as it loads some dynamic libraries. Once chrootet * these libraries will *not* be found and gethostbyname() * calls will simply fail (return NULL pointer and h_errno=0). * Also (at least for FreeBSD) syslog() needs to be called * before chroot()ing - this is done in main() by an INFO(). * Took me a while to figure THIS one out */ struct in_addr dummy; get_ip_by_host("localhost", &dummy); DEBUGC(DBCLASS_CONFIG,"chrooting to %s", configuration.chrootjail); sts = chroot(configuration.chrootjail); if (sts != 0) DEBUGC(DBCLASS_CONFIG,"chroot(%s) failed: %s", configuration.chrootjail, strerror(errno)); sts=chdir("/"); } /* * change user ID and group ID */ if (passwd) { DEBUGC(DBCLASS_CONFIG,"changing uid/gid to %s", configuration.user); sts = setgid(passwd->pw_gid); DEBUGC(DBCLASS_CONFIG,"changed gid to %i - %s", (int)passwd->pw_gid, (sts==0)?"Ok":"Failed"); sts = setegid(passwd->pw_gid); DEBUGC(DBCLASS_CONFIG,"changed egid to %i - %s", (int)passwd->pw_gid, (sts==0)?"Ok":"Failed"); sts = seteuid(passwd->pw_uid); DEBUGC(DBCLASS_CONFIG,"changed euid to %i - %s", (int)passwd->pw_uid, (sts==0)?"Ok":"Failed"); } } }
/* * verifies the from address agains the access lists * defined in the configuration file. * * returns a bitmask with ACCESSCTL_SIP, ACCESSCTL_REG */ int accesslist_check (struct sockaddr_in from) { int access = 0; DEBUGC(DBCLASS_ACCESS,"deny list (SIP):%s", configuration.hosts_deny_sip? configuration.hosts_deny_sip : "*NULL*"); DEBUGC(DBCLASS_ACCESS,"allow list (SIP):%s", configuration.hosts_allow_sip? configuration.hosts_allow_sip : "*NULL*"); DEBUGC(DBCLASS_ACCESS,"allow list (REG):%s", configuration.hosts_allow_reg? configuration.hosts_allow_reg : "*NULL*"); /* * check DENY list */ if ( (configuration.hosts_deny_sip !=NULL) && (strcmp(configuration.hosts_deny_sip,"")!=0) ) { /* non-empty list -> check agains it */ if (process_aclist(configuration.hosts_deny_sip, from)== STS_SUCCESS) { /* yup - this one is blacklisted */ DEBUGC(DBCLASS_ACCESS,"caught by deny list"); return 0; } } /* * check SIP allow list */ if ( (configuration.hosts_allow_sip !=NULL) && (strcmp(configuration.hosts_allow_sip,"")!=0) ) { /* non-empty list -> check agains it */ if (process_aclist(configuration.hosts_allow_sip, from)==STS_SUCCESS) { /* SIP access granted */ DEBUGC(DBCLASS_ACCESS,"granted SIP access"); access |= ACCESSCTL_SIP; } } else { access |= ACCESSCTL_SIP; } /* * check SIP registration allow list */ if ( (configuration.hosts_allow_reg !=NULL) && (strcmp(configuration.hosts_allow_reg,"")!=0) ) { /* non-empty list -> check agains it */ if (process_aclist(configuration.hosts_allow_reg, from)==STS_SUCCESS) { /* SIP registration access granted */ DEBUGC(DBCLASS_ACCESS,"granted REG/SIP access"); access |= ACCESSCTL_REG | ACCESSCTL_SIP; } } else { access |= ACCESSCTL_REG; } DEBUGC(DBCLASS_ACCESS,"access check =%i", access); return access; }
/* * module-local functions */ static int stun_validate_response(char *buffer, int len, char *tid){ /* expect min. STUN header + 1 attribute */ if (len < 24) { DEBUGC(DBCLASS_BABBLE,"stun_validate_response: no STUN response (too short)"); return STS_FAILURE; } if (ntohs(*((int*)&buffer[0]) & 0x0000ffff) != 0x0101) { DEBUGC(DBCLASS_BABBLE,"stun_validate_response: no STUN response (type)"); return STS_FAILURE; } if (memcmp(&buffer[4], tid, STUN_TID_SIZE) != 0) { DEBUGC(DBCLASS_BABBLE,"stun_validate_response: wrong STUN response (TID)"); return STS_FAILURE; } DEBUGC(DBCLASS_BABBLE,"valid STUN response"); return STS_SUCCESS; }
static int stun_send_request(char *tid){ struct in_addr addr; int sts; char stun_rq[28]; /*&&& testing */ size_t size=28; /* name resolution */ if (utils_inet_aton(plugin_cfg.server, &addr) == 0) { /* need name resolution */ DEBUGC(DBCLASS_DNS,"resolving name:%s", plugin_cfg.server); sts = get_ip_by_host(plugin_cfg.server, &addr); if (sts == STS_FAILURE) { DEBUGC(DBCLASS_DNS, "stun_send_request: cannot resolve STUN server [%s]", plugin_cfg.server); return STS_FAILURE; } } /* compose STUN BIND request - poor mans way */ stun_rq[0]=0x00; // type stun_rq[1]=0x01; stun_rq[2]=0x00; // length stun_rq[3]=0x08; memcpy (&stun_rq[4], tid, STUN_TID_SIZE); stun_rq[20]=0x00; // ATTR change request stun_rq[21]=0x03; stun_rq[22]=0x00; // ATTR len 4 stun_rq[23]=0x04; stun_rq[24]=0x00; // Change IP not set, Change port not set stun_rq[25]=0x00; stun_rq[26]=0x00; stun_rq[27]=0x00; /* and send via the SIP UDP port */ sts = sipsock_send(addr, plugin_cfg.port, PROTO_UDP, stun_rq, size); return STS_SUCCESS; }
/* * PROXY_REWRITE_REQUEST_URI * * rewrites the incoming Request URI * * RETURNS * STS_SUCCESS on success */ int proxy_rewrite_request_uri(osip_message_t *mymsg, int idx){ char *host; char *port; osip_uri_t *url; if ((idx >= URLMAP_SIZE) || (idx < 0)) { WARN("proxy_rewrite_request_uri: called with invalid index"); return STS_FAILURE; } DEBUGC(DBCLASS_PROXY,"rewriting incoming Request URI"); url=osip_message_get_uri(mymsg); /* set the true host */ if (url->host) osip_free(url->host);url->host=NULL; if (urlmap[idx].true_url->host) { DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: host=%s", urlmap[idx].true_url->host); host = (char *)malloc(strlen(urlmap[idx].true_url->host)+1); memcpy(host, urlmap[idx].true_url->host, strlen(urlmap[idx].true_url->host)); host[strlen(urlmap[idx].true_url->host)]='\0'; osip_uri_set_host(url, host); } /* set the true port */ if (url->port) osip_free(url->port);url->port=NULL; if (urlmap[idx].true_url->port) { DEBUGC(DBCLASS_BABBLE,"proxy_rewrite_request_uri: port=%s", urlmap[idx].true_url->port); port = (char *)malloc(strlen(urlmap[idx].true_url->port)+1); memcpy(port, urlmap[idx].true_url->port, strlen(urlmap[idx].true_url->port)); port[strlen(urlmap[idx].true_url->port)]='\0'; osip_uri_set_port(url, port); } return STS_SUCCESS; }
/* * compare_client_id: * Compares two client_id_t structures. If both have the Contact item * defined (not NULL), then compare it and return. * If one (or both) do NOT have the contact item defined, then * fall back on comparing the from_ip (IP address). * * returns: * STS_SUCCESS on match * STS_FAILURE on no match */ int compare_client_id(client_id_t cid1, client_id_t cid2) { /* Prio 1: Contact - if present in both structures */ if ((cid1.idstring[0] != '\0') && (cid2.idstring[0] != '\0')) { if (strncmp(cid1.idstring, cid2.idstring, CLIENT_ID_SIZE) == 0) { DEBUGC(DBCLASS_BABBLE, "compare_client_id: contact match [%s]", cid1.idstring); return STS_SUCCESS; } DEBUGC(DBCLASS_BABBLE, "compare_client_id: contact NO match [%s<->%s]", cid1.idstring, cid2.idstring); return STS_FAILURE; } /* Prio 2: IP (always present) - fallback, if no ID string present. */ if (memcmp(&cid1.from_ip, &cid2.from_ip, sizeof(struct in_addr)) == 0) { DEBUGC(DBCLASS_BABBLE, "compare_client_id: IP match [%s]", utils_inet_ntoa(cid1.from_ip)); return STS_SUCCESS; } DEBUGC(DBCLASS_BABBLE, "compare_client_id: no match"); return STS_FAILURE; }
/* * Create the PID file */ int createpidfile(char *pidfilename) { FILE *f = NULL; int sts; DEBUGC(DBCLASS_CONFIG,"creating PID file [%s]", pidfilename); sts=unlink(pidfilename); if ((sts==0) || (errno == ENOENT)) { if ((f=fopen(pidfilename, "w"))) { fprintf(f,"%i\n",(int)getpid()); fclose(f); } else { WARN("couldn't create new PID file: %s", strerror(errno)); return STS_FAILURE; } } else { WARN("couldn't delete old PID file: %s", strerror(errno)); return STS_FAILURE; } return STS_SUCCESS; }
/* * shut down the URL mapping table */ void register_save(void) { int i; FILE *stream; if (configuration.registrationfile) { DEBUGC(DBCLASS_REG,"saving registration table"); /* write urlmap back to file */ stream = fopen(configuration.registrationfile, "w+"); if (!stream) { /* try to unlink it and open again */ unlink(configuration.registrationfile); stream = fopen(configuration.registrationfile, "w+"); /* open file for write failed, complain */ if (!stream) { ERROR("unable to write registration file"); return; } } for (i=0;i < URLMAP_SIZE; i++) { fprintf(stream, "***:%i:%i\n", urlmap[i].active, urlmap[i].expires); if (urlmap[i].active) { #define W(X) fprintf(stream, "%s\n", (X)? X:""); W(urlmap[i].true_url->scheme); W(urlmap[i].true_url->username); W(urlmap[i].true_url->host); W(urlmap[i].true_url->port); W(urlmap[i].masq_url->scheme); W(urlmap[i].masq_url->username); W(urlmap[i].masq_url->host); W(urlmap[i].masq_url->port); W(urlmap[i].reg_url->scheme); W(urlmap[i].reg_url->username); W(urlmap[i].reg_url->host); W(urlmap[i].reg_url->port); } } fclose(stream); } return; }
/* * get_interface_ip: * fetches own IP address by interface INBOUND/OUTBOUND * takes into account a possible outbound_host setting. * * STS_SUCCESS on returning a valid IP and interface is UP * STS_FAILURE if interface is DOWN or other problem */ int get_interface_ip(int interface, struct in_addr *retaddr) { int sts=STS_FAILURE; if ((interface == IF_OUTBOUND) && (configuration.outbound_host) && (strcmp(configuration.outbound_host, "")!=0)) { DEBUGC(DBCLASS_DNS, "fetching outbound IP by HOSTNAME"); if (retaddr) { sts = get_ip_by_host(configuration.outbound_host, retaddr); } else { sts = STS_SUCCESS; } } else { sts = get_interface_real_ip(interface, retaddr); } return sts; }
/* * SIP_ADD_MYVIA * * interface == IF_OUTBOUND, IF_INBOUND * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int sip_add_myvia (sip_ticket_t *ticket, int interface, struct in_addr *local_ip) { struct in_addr addr; char tmp[URL_STRING_SIZE] = {0}; osip_via_t *via; int sts; char branch_id[VIA_BRANCH_SIZE]; if (local_ip == NULL) { if (interface == IF_OUTBOUND) { if (get_ip_by_ifname(configuration.outbound_if, &addr) != STS_SUCCESS) { ERROR("can't find interface %s - configuration error?", configuration.outbound_if); return STS_FAILURE; } } else { if (get_ip_by_ifname(configuration.inbound_if, &addr) != STS_SUCCESS) { ERROR("can't find inbound interface %s - configuration error?", configuration.inbound_if); return STS_FAILURE; } } } else { addr = *local_ip; } sts = sip_calculate_branch_id(ticket, branch_id); snprintf(tmp,URL_STRING_SIZE, "SIP/2.0/UDP %s:%i;branch=%s;", utils_inet_ntoa(addr), configuration.sip_listen_port, branch_id); DEBUGC(DBCLASS_BABBLE,"adding VIA:%s",tmp); sts = osip_via_init(&via); if (sts!=0) return STS_FAILURE; /* allocation failed */ sts = osip_via_parse(via, tmp); if (sts!=0) return STS_FAILURE; osip_list_add(ticket->sipmsg->vias,via,0); return STS_SUCCESS; }
/* * read a message from SIP listen socket (UDP datagram) * * RETURNS number of bytes read * from is modified to return the sockaddr_in of the sender */ int sipsock_read(int fd, void *buf, size_t bufsize, struct sockaddr_in *from, int *protocol) { int count; socklen_t fromlen; /* start of AU4D00875 by d00107688 to support bind 2008-10-15*/ if (fd < 0) { count = 0; return count; } /* end of AU4D00875 by d00107688 to support bind 2008-10-15*/ fromlen=sizeof(struct sockaddr_in); *protocol = PROTO_UDP; /* up to now, unly UDP */ #if 0 count=recvfrom(sip_udp_socket, buf, bufsize, 0, (struct sockaddr *)from, &fromlen); #else /*BEGIN 3092906251 s00201037 2013-10-15 added*/ count=recvfrom(fd, buf, bufsize, 0, from, &fromlen); // count=recvfrom(fd, buf, bufsize, 0, // (struct sockaddr *)from, &fromlen); /*END 3092906251 s00201037 2013-10-15 added*/ #endif if (count<0) { WARN("recvfrom() returned error [%s]",strerror(errno)); *protocol = PROTO_UNKN; } DEBUGC(DBCLASS_NET,"received UDP packet from %s, count=%i", utils_inet_ntoa(from->sin_addr), count); DUMP_BUFFER(DBCLASS_NETTRAF, buf, count); /*BEGIN 3092906251 s00201037 2013-10-15 added*/ lansrcport = 0; lansrcport = ntohs(from->sin_port); lansrcip = htonl(inet_addr(utils_inet_ntoa(from->sin_addr))); /*END 3092906251 s00201037 2013-10-15 added*/ return count; }
/* * SIP_DEL_MYVIA * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int sip_del_myvia (sip_ticket_t *ticket, struct in_addr *local_ip) { osip_via_t *via; int sts; DEBUGC(DBCLASS_PROXY,"deleting topmost VIA"); via = osip_list_get (ticket->sipmsg->vias, 0); if ( via == NULL ) { ERROR("Got empty VIA list - is your UA configured properly?"); return STS_FAILURE; } if ( is_via_local(via, local_ip) == STS_FALSE ) { ERROR("I'm trying to delete a VIA but it's not mine! host=%s",via->host); return STS_FAILURE; } sts = osip_list_remove(ticket->sipmsg->vias, 0); osip_via_free (via); return STS_SUCCESS; }
/* * get_interface_real_ip: * fetches the real IP address of my interface INBOUND/OUTBOUND * * STS_SUCCESS on returning a valid IP and interface is UP * STS_FAILURE if interface is DOWN or other problem */ int get_interface_real_ip(int interface, struct in_addr *retaddr) { int sts=STS_FAILURE; char *tmp=NULL; if (interface == IF_INBOUND) { tmp = configuration.inbound_if; } else if (interface == IF_OUTBOUND) { tmp = configuration.outbound_if; } if (tmp && (strcmp(tmp, "")!=0)) { DEBUGC(DBCLASS_DNS, "fetching interface IP by INTERFACE [%i]", interface); sts = get_ip_by_ifname(tmp, retaddr); if (sts != STS_SUCCESS) { ERROR("can't find interface %s - configuration error?", tmp); } } else { ERROR("Don't know what interface to look for - configuration error?"); } return sts; }
/* * resolve a hostname and return in_addr * handles its own little DNS cache. * * RETURNS * STS_SUCCESS on success * STS_FAILURE on failure */ int get_ip_by_host(char *hostname, struct in_addr *addr) { int i, j, k, idx; time_t t1, t2; struct hostent *hostentry; #if defined(HAVE_GETHOSTBYNAME_R) struct hostent result_buffer; char tmp[GETHOSTBYNAME_BUFLEN]; #endif int error; static struct { time_t expires_timestamp; /* time of expiration */ struct in_addr addr; /* IP address or 0.0.0.0 if a bad entry */ char error_count; /* counts failed resolution attempts */ char bad_entry; /* != 0 if resolving failed */ char hostname[HOSTNAME_SIZE+1]; } dns_cache[DNS_CACHE_SIZE]; static int cache_initialized=0; if (hostname == NULL) { ERROR("get_ip_by_host: NULL hostname requested"); return STS_FAILURE; } if (addr == NULL) { ERROR("get_ip_by_host: NULL in_addr passed"); return STS_FAILURE; } /* first time: initialize DNS cache */ if (cache_initialized == 0) { DEBUGC(DBCLASS_DNS, "initializing DNS cache (%i entries)", DNS_CACHE_SIZE); memset(dns_cache, 0, sizeof(dns_cache)); cache_initialized=1; } time(&t1); /* clean expired entries */ for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') continue; if ( (dns_cache[i].expires_timestamp) < t1 ) { DEBUGC(DBCLASS_DNS, "cleaning DNS cache (entry %i)", i); memset (&dns_cache[i], 0, sizeof(dns_cache[0])); } } /* * search requested entry in cache */ idx=0; for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') continue; /* empty */ if (strcasecmp(hostname, dns_cache[i].hostname) == 0) { /* match */ memcpy(addr, &dns_cache[i].addr, sizeof(struct in_addr)); if (dns_cache[i].bad_entry) { DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisted from cache: %s", hostname); return STS_FAILURE; } if (dns_cache[i].error_count > 0) { DEBUGC(DBCLASS_DNS, "DNS lookup - previous resolution failed: %s" ", attempt %i", hostname, dns_cache[i].error_count); idx=i; break; } DEBUGC(DBCLASS_DNS, "DNS lookup - from cache: %s -> %s", hostname, utils_inet_ntoa(*addr)); return STS_SUCCESS; } } /* I did not find it in cache, so I have to resolve it */ error = 0; /* need to deal with reentrant versions of gethostbyname_r() * as we may use threads... */ #if defined(HAVE_GETHOSTBYNAME_R) /* gethostbyname_r() with 3 arguments (e.g. osf/1) */ #if defined(HAVE_FUNC_GETHOSTBYNAME_R_3) gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ &hostentry ); if (hostentry == NULL) error = h_errno; /* gethostbyname_r() with 5 arguments (e.g. solaris, linux libc5) */ #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_5) hostentry = gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ tmp, GETHOSTBYNAME_BUFLEN, &error); /* gethostbyname_r() with 6 arguments (e.g. linux glibc) */ #elif defined(HAVE_FUNC_GETHOSTBYNAME_R_6) gethostbyname_r(hostname, /* the FQDN */ &result_buffer, /* the result buffer */ tmp, GETHOSTBYNAME_BUFLEN, &hostentry, &error); #else #error "gethostbyname_r() with 3, 5 or 6 arguments supported only" #endif #elif defined(HAVE_GETHOSTBYNAME) hostentry=gethostbyname(hostname); if (hostentry == NULL) error = h_errno; #else #error "need gethostbyname() or gethostbyname_r()" #endif /* Here I have 'hostentry' and 'error' */ if (hostentry==NULL) { /* * Some errors just tell us that there was no IP resolvable. * From the manpage: * HOST_NOT_FOUND * The specified host is unknown. * NO_ADDRESS or NO_DATA * The requested name is valid but does not have an IP * address. */ if ((error == HOST_NOT_FOUND) || (error == NO_ADDRESS) || (error == NO_DATA)) { #ifdef HAVE_HSTRERROR DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i [%s]", hostname, h_errno, hstrerror(error)); #else DEBUGC(DBCLASS_DNS, "gethostbyname(%s) failed: h_errno=%i", hostname, error); #endif } else { #ifdef HAVE_HSTRERROR ERROR("gethostbyname(%s) failed: h_errno=%i [%s]", hostname, h_errno, hstrerror(h_errno)); #else ERROR("gethostbyname(%s) failed: h_errno=%i",hostname, h_errno); #endif } } if (hostentry) { memcpy(addr, hostentry->h_addr, sizeof(struct in_addr)); DEBUGC(DBCLASS_DNS, "DNS lookup - resolved: %s -> %s", hostname, utils_inet_ntoa(*addr)); } /* if we already have the entry, skip finding a new empty one */ if (idx == 0) { /* * find an empty slot in the cache */ j=0; k=0; t1=INT_MAX; t2=INT_MAX; for (i=0; i<DNS_CACHE_SIZE; i++) { if (dns_cache[i].hostname[0]=='\0') break; if ((dns_cache[i].expires_timestamp < t1) && (dns_cache[i].bad_entry == 0)) { /* remember oldest good entry */ t1=dns_cache[i].expires_timestamp; j=i; } else if (dns_cache[i].expires_timestamp < t2) { /* remember oldest bad entry */ t2=dns_cache[i].expires_timestamp; k=i; } } /* if no empty slot found, victimize oldest one. * Give preference to the oldest "bad" entry if * one exists */ if (i >= DNS_CACHE_SIZE) { if (k > 0) i=k; else i=j; } idx=i; memset(&dns_cache[idx], 0, sizeof(dns_cache[0])); } /* * store the result in the cache */ DEBUGC(DBCLASS_DNS, "DNS lookup - store into cache, entry %i)", idx); strncpy(dns_cache[idx].hostname, hostname, HOSTNAME_SIZE); dns_cache[idx].expires_timestamp = time(NULL) + DNS_GOOD_AGE; if (hostentry) { memcpy(&dns_cache[idx].addr, addr, sizeof(struct in_addr)); dns_cache[idx].error_count = 0; dns_cache[idx].bad_entry = 0; } else { dns_cache[idx].error_count++; DEBUGC(DBCLASS_DNS, "DNS lookup - errcnt=%i", dns_cache[idx].error_count); if (dns_cache[idx].error_count >= DNS_ATTEMPTS) { DEBUGC(DBCLASS_DNS, "DNS lookup - blacklisting entry"); dns_cache[idx].expires_timestamp = time(NULL) + DNS_BAD_AGE; dns_cache[idx].bad_entry = 1; } return STS_FAILURE; } return STS_SUCCESS; }
/* * handles register requests and updates the URL mapping table * * RETURNS: * STS_SUCCESS : successfully registered * STS_FAILURE : registration failed * STS_NEED_AUTH : authentication needed */ int register_client(sip_ticket_t *ticket, int force_lcl_masq) { int i, j, n, sts; int expires; time_t time_now; osip_contact_t *contact; osip_uri_t *url1_to, *url1_contact=NULL; osip_uri_t *url2_to; osip_header_t *expires_hdr; osip_uri_param_t *expires_param=NULL; /* * Authorization - do only if I'm not just acting as outbound proxy * but am ment to be the registrar */ if (force_lcl_masq == 0) { /* * RFC 3261, Section 16.3 step 6 * Proxy Behavior - Request Validation - Proxy-Authorization */ sts = authenticate_proxy(ticket->sipmsg); if (sts == STS_FAILURE) { /* failed */ WARN("proxy authentication failed for %s@%s", (ticket->sipmsg->to->url->username)? ticket->sipmsg->to->url->username : "******", ticket->sipmsg->to->url->host); return STS_FAILURE; } else if (sts == STS_NEED_AUTH) { /* needed */ DEBUGC(DBCLASS_REG,"proxy authentication needed for %s@%s", ticket->sipmsg->to->url->username, ticket->sipmsg->to->url->host); return STS_NEED_AUTH; } } /* fetch 1st Via entry and remember this address. Incoming requests for the registered address have to be passed on to that host. To: -> address to be registered Contact: -> host is reachable there Note: in case of un-REGISTER, the contact header may contain '*' only - which means "all registrations made by this UA" => Mapping is To: <1--n> Contact */ time(&time_now); DEBUGC(DBCLASS_BABBLE,"sip_register:"); /* * First make sure, we have a proper Contact header: * - url * - url -> hostname * * Libosip parses an: * "Contact: *" * the following way (Note: Display name!! and URL is NULL) * (gdb) p *((osip_contact_t*)(sip->contacts.node->element)) * $5 = {displayname = 0x8af8848 "*", url = 0x0, gen_params = 0x8af8838} */ osip_message_get_contact(ticket->sipmsg, 0, &contact); if ((contact == NULL) || (contact->url == NULL) || (contact->url->host == NULL)) { /* Don't have required Contact fields. This may be a Registration query or unregistering all registered records for this UA. We should simply forward this request to its destination. However, if this is an unregistration from a client that is not registered (Grandstream "unregister at startup" option) -> How do I handle this one? Right now we do a direction lookup and if this fails we generate an OK message by ourself (fake) */ DEBUGC(DBCLASS_REG, "empty Contact header - " "seems to be a registration query"); sts = sip_find_direction(ticket, NULL); if (sts != STS_SUCCESS) { /* answer the request myself. Most likely this is an UNREGISTER * request when the client just booted */ sts = register_response(ticket, STS_SUCCESS); return STS_SIP_SENT; } return STS_SUCCESS; } url1_contact=contact->url; /* evaluate Expires Header field */ osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr); /* * look for an Contact expires parameter - in case of REGISTER * these two are equal. The Contact expires has higher priority! */ if (ticket->sipmsg->contacts.node && ticket->sipmsg->contacts.node->element) { osip_contact_param_get_byname( (osip_contact_t*) ticket->sipmsg->contacts.node->element, EXPIRES, &expires_param); } if (expires_param && expires_param->gvalue) { /* get expires from contact Header */ expires=atoi(expires_param->gvalue); if ((expires < 0) || (expires >= UINT_MAX )) expires=configuration.default_expires; } else if (expires_hdr && expires_hdr->hvalue) { /* get expires from expires Header */ expires=atoi(expires_hdr->hvalue); if ((expires < 0) || (expires >= UINT_MAX )) expires=configuration.default_expires; } else { char tmp[16]; /* it seems, the expires field is not present everywhere... */ DEBUGC(DBCLASS_REG,"no 'expires' header found - set time to %i sec", configuration.default_expires); expires=configuration.default_expires; sprintf(tmp,"%i",expires); osip_message_set_expires(ticket->sipmsg, tmp); } url1_to=ticket->sipmsg->to->url; /* * REGISTER */ if (expires > 0) { DEBUGC(DBCLASS_REG,"register: %s@%s expires=%i seconds", (url1_contact->username) ? url1_contact->username : "******", (url1_contact->host) ? url1_contact->host : "*NULL*", expires); /* * Update registration. There are two possibilities: * - already registered, then update the existing record * - not registered, then create a new record */ j=-1; for (i=0; i<URLMAP_SIZE; i++) { if (urlmap[i].active == 0) { if (j < 0) j=i; /* remember first hole */ continue; } url2_to=urlmap[i].reg_url; /* check address-of-record ("public address" of user) */ if (compare_url(url1_to, url2_to)==STS_SUCCESS) { DEBUGC(DBCLASS_REG, "found entry for %s@%s <-> %s@%s at " "slot=%i, exp=%li", (url1_contact->username) ? url1_contact->username : "******", (url1_contact->host) ? url1_contact->host : "*NULL*", (url2_to->username) ? url2_to->username : "******", (url2_to->host) ? url2_to->host : "*NULL*", i, (long)urlmap[i].expires-time_now); break; } } if ( (j < 0) && (i >= URLMAP_SIZE) ) { /* oops, no free entries left... */ ERROR("URLMAP is full - registration failed"); return STS_FAILURE; } if (i >= URLMAP_SIZE) { /* entry not existing, create new one */ i=j; /* write entry */ urlmap[i].active=1; /* Contact: field */ osip_uri_clone( ((osip_contact_t*) (ticket->sipmsg->contacts.node->element))->url, &urlmap[i].true_url); /* To: field */ osip_uri_clone( ticket->sipmsg->to->url, &urlmap[i].reg_url); DEBUGC(DBCLASS_REG,"create new entry for %s@%s <-> %s@%s at slot=%i", (url1_contact->username) ? url1_contact->username : "******", (url1_contact->host) ? url1_contact->host : "*NULL*", (urlmap[i].reg_url->username) ? urlmap[i].reg_url->username : "******", (urlmap[i].reg_url->host) ? urlmap[i].reg_url->host : "*NULL*", i); /* * try to figure out if we ought to do some masquerading */ osip_uri_clone( ticket->sipmsg->to->url, &urlmap[i].masq_url); n=configuration.mask_host.used; if (n != configuration.masked_host.used) { ERROR("# of mask_host is not equal to # of masked_host in config!"); n=0; } DEBUG("%i entries in MASK config table", n); for (j=0; j<n; j++) { DEBUG("compare [%s] <-> [%s]",configuration.mask_host.string[j], ticket->sipmsg->to->url->host); if (strcmp(configuration.mask_host.string[j], ticket->sipmsg->to->url->host)==0) break; } if (j<n) { /* we are masquerading this UA, replace the host part of the url */ DEBUGC(DBCLASS_REG,"masquerading UA %s@%s as %s@%s", (url1_contact->username) ? url1_contact->username : "******", (url1_contact->host) ? url1_contact->host : "*NULL*", (url1_contact->username) ? url1_contact->username : "******", configuration.masked_host.string[j]); urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, strlen(configuration.masked_host.string[j])+1); strcpy(urlmap[i].masq_url->host, configuration.masked_host.string[j]); } } else { /* if new entry */ /* This is an existing entry */ /* * Some phones (like BudgeTones *may* dynamically grab a SIP port * so we might want to update the true_url and reg_url each time * we get an REGISTER */ /* Contact: field (true_url) */ osip_uri_free(urlmap[i].true_url); osip_uri_clone( ((osip_contact_t*) (ticket->sipmsg->contacts.node->element))->url, &urlmap[i].true_url); /* To: field (reg_url) */ osip_uri_free(urlmap[i].reg_url); osip_uri_clone( ticket->sipmsg->to->url, &urlmap[i].reg_url); } /* * for proxying: force device to be masqueraded * as with the outbound IP (masq_url) */ if (force_lcl_masq) { struct in_addr addr; char *addrstr; if (get_interface_ip(IF_OUTBOUND, &addr) != STS_SUCCESS) { return STS_FAILURE; } /* host part */ addrstr = utils_inet_ntoa(addr); DEBUGC(DBCLASS_REG,"masquerading UA %s@%s local %s@%s", (url1_contact->username) ? url1_contact->username : "******", (url1_contact->host) ? url1_contact->host : "*NULL*", (url1_contact->username) ? url1_contact->username : "******", addrstr); urlmap[i].masq_url->host=realloc(urlmap[i].masq_url->host, strlen(addrstr)+1); strcpy(urlmap[i].masq_url->host, addrstr); /* port number if required */ if (configuration.sip_listen_port != SIP_PORT) { urlmap[i].masq_url->port=realloc(urlmap[i].masq_url->port, 16); sprintf(urlmap[i].masq_url->port, "%i", configuration.sip_listen_port); } } /* give some safety margin for the next update */ if (expires > 0) expires+=30; /* update registration timeout */ urlmap[i].expires=time_now+expires; /* * un-REGISTER */ } else { /* expires > 0 */ /* * Remove registration * Siproxd will ALWAYS remove ALL bindings for a given * address-of-record */ for (i=0; i<URLMAP_SIZE; i++) { if (urlmap[i].active == 0) continue; url2_to=urlmap[i].reg_url; if (compare_url(url1_to, url2_to)==STS_SUCCESS) { DEBUGC(DBCLASS_REG, "removing registration for %s@%s at slot=%i", (url2_to->username) ? url2_to->username : "******", (url2_to->host) ? url2_to->host : "*NULL*", i); urlmap[i].expires=0; break; } } } return STS_SUCCESS; }
/* * get_ip_by_ifname: * fetches own IP address by its interface name * * STS_SUCCESS on returning a valid IP and interface is UP * STS_FAILURE if interface is DOWN or other problem */ int get_ip_by_ifname(char *ifname, struct in_addr *retaddr) { struct in_addr ifaddr; /* resulting IP */ int i, j; int ifflags=0, isup=0; time_t t; static struct { time_t timestamp; struct in_addr ifaddr; /* IP */ int isup; /* interface is UP */ char ifname[IFNAME_SIZE+1]; } ifaddr_cache[IFADR_CACHE_SIZE]; static int cache_initialized=0; #ifdef HAVE_GETIFADDRS struct ifaddrs *ifa; struct ifaddrs *ifa_list; #else struct ifreq ifr; struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; int sockfd; #endif if (ifname == NULL) { WARN("get_ip_by_ifname: got NULL ifname passed - please check config" "file ('if_inbound' and 'if_outbound')"); return STS_FAILURE; } /* first time: initialize ifaddr cache */ if (cache_initialized == 0) { DEBUGC(DBCLASS_DNS, "initializing ifaddr cache (%i entries)", IFADR_CACHE_SIZE); memset(ifaddr_cache, 0, sizeof(ifaddr_cache)); cache_initialized=1; } if (retaddr) memset(retaddr, 0, sizeof(struct in_addr)); time(&t); /* clean expired entries */ for (i=0; i<IFADR_CACHE_SIZE; i++) { if (ifaddr_cache[i].ifname[0]=='\0') continue; if ( (ifaddr_cache[i].timestamp+IFADR_MAX_AGE) < t ) { DEBUGC(DBCLASS_DNS, "cleaning ifaddr cache (entry %i)", i); memset (&ifaddr_cache[i], 0, sizeof(ifaddr_cache[0])); } } /* * search requested entry in cache */ for (i=0; i<IFADR_CACHE_SIZE; i++) { if (ifaddr_cache[i].ifname[0]=='\0') continue; if (strcmp(ifname, ifaddr_cache[i].ifname) == 0) { /* match */ if (retaddr) memcpy(retaddr, &ifaddr_cache[i].ifaddr, sizeof(struct in_addr)); DEBUGC(DBCLASS_DNS, "ifaddr lookup - from cache: %s -> %s %s", ifname, utils_inet_ntoa(ifaddr_cache[i].ifaddr), (ifaddr_cache[i].isup)? "UP":"DOWN"); return (ifaddr_cache[i].isup)? STS_SUCCESS: STS_FAILURE; } /* if */ } /* for i */ /* not found in cache, go and get it */ #ifdef HAVE_GETIFADDRS if (getifaddrs(&ifa_list)) { ERROR("Error in getifaddrs: %s",strerror(errno)); return STS_FAILURE; } i=0; /* use "found" marker */ for (ifa = ifa_list; ifa != NULL; ifa = ifa->ifa_next) { DEBUGC(DBCLASS_BABBLE,"getifaddrs - %s / %s, ifa_addr=%p, addrfamily=%i", ifname, ifa->ifa_name,ifa->ifa_addr, (ifa->ifa_addr)?ifa->ifa_addr->sa_family:-1); if (ifa && ifa->ifa_name && ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET && strcmp(ifa->ifa_name, ifname) == 0) { /* found the entry */ i=1; memcpy(&ifaddr, &((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, sizeof(struct in_addr)); ifflags=ifa->ifa_flags; DEBUGC(DBCLASS_BABBLE,"getifaddrs - MATCH, sin_addr=%s", utils_inet_ntoa(ifaddr)); break; } } freeifaddrs(ifa_list); if (i==0) { DEBUGC(DBCLASS_DNS,"Interface %s not found.", ifname); return STS_FAILURE; } #else memset(&ifr, 0, sizeof(ifr)); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ERROR("Error in socket: %s",strerror(errno)); return STS_FAILURE; } /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&6 ifname=0x%p",ifname); strcpy(ifr.ifr_name, ifname); sin->sin_family = AF_INET; /* get interface flags */ if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) != 0) { ERROR("Error in ioctl SIOCGIFFLAGS: %s [%s]", strerror(errno), ifname); close(sockfd); return STS_FAILURE; } ifflags=ifr.ifr_flags; /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&7 ifname=0x%p",ifname); /* get address */ if(ioctl(sockfd, SIOCGIFADDR, &ifr) != 0) { ERROR("Error in ioctl SIOCGIFADDR: %s (interface %s)", strerror(errno), ifname); close(sockfd); return STS_FAILURE; } /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&8 ifname=0x%p",ifname); memcpy(&ifaddr, &sin->sin_addr, sizeof(struct in_addr)); /*&&&*/DEBUGC(DBCLASS_BABBLE,"&&&9 ifname=0x%p",ifname); close(sockfd); #endif if (ifflags & IFF_UP) isup=1; else isup=0; DEBUGC(DBCLASS_DNS, "get_ip_by_ifname: if %s has IP:%s (flags=%x) %s", ifname, utils_inet_ntoa(ifaddr), ifflags, (isup)? "UP":"DOWN"); /* *find an empty slot in the cache */ j=0; for (i=0; i<IFADR_CACHE_SIZE; i++) { if (ifaddr_cache[i].ifname[0]=='\0') break; if (ifaddr_cache[i].timestamp < t) { /* remember oldest entry */ t=ifaddr_cache[i].timestamp; j=i; } } /* if no empty slot found, take oldest one */ if (i >= IFADR_CACHE_SIZE) i=j; /* * store the result in the cache */ DEBUGC(DBCLASS_DNS, "ifname lookup - store into cache, entry %i)", i); memset(&ifaddr_cache[i], 0, sizeof(ifaddr_cache[0])); strncpy(ifaddr_cache[i].ifname, ifname, IFNAME_SIZE); ifaddr_cache[i].timestamp=t; memcpy(&ifaddr_cache[i].ifaddr, &ifaddr, sizeof(struct in_addr)); ifaddr_cache[i].isup=isup; if (retaddr) memcpy(retaddr, &ifaddr, sizeof(struct in_addr)); return (isup)? STS_SUCCESS : STS_FAILURE; }
/* * send answer to a registration request. * flag = STS_SUCCESS -> positive answer (200) * flag = STS_FAILURE -> negative answer (503) * flag = STS_NEED_AUTH -> proxy authentication needed (407) * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int register_response(sip_ticket_t *ticket, int flag) { osip_message_t *response; int code; int sts; osip_via_t *via; int port; char *buffer; size_t buflen; struct in_addr addr; osip_header_t *expires_hdr; /* ok -> 200, fail -> 503 */ switch (flag) { case STS_SUCCESS: code = 200; /* OK */ break; case STS_FAILURE: code = 503; /* failed */ break; case STS_NEED_AUTH: code = 407; /* proxy authentication needed */ break; default: code = 503; /* failed */ break; } /* create the response template */ if ((response=msg_make_template_reply(ticket, code))==NULL) { ERROR("register_response: error in msg_make_template_reply"); return STS_FAILURE; } /* insert the expiration header */ osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr); if (expires_hdr) { osip_message_set_expires(response, expires_hdr->hvalue); } /* if we send back an proxy authentication needed, include the Proxy-Authenticate field */ if (code == 407) { auth_include_authrq(response); } /* get the IP address from existing VIA header */ osip_message_get_via (response, 0, &via); if (via == NULL) { ERROR("register_response: Cannot send response - no via field"); return STS_FAILURE; } /* name resolution needed? */ if (utils_inet_aton(via->host,&addr) == 0) { /* yes, get IP address */ sts = get_ip_by_host(via->host, &addr); if (sts == STS_FAILURE) { DEBUGC(DBCLASS_REG, "register_response: cannot resolve VIA [%s]", via->host); return STS_FAILURE; } } sts = sip_message_to_str(response, &buffer, &buflen); if (sts != 0) { ERROR("register_response: msg_2char failed"); return STS_FAILURE; } /* send answer back */ if (via->port) { port=atoi(via->port); if ((port<=0) || (port>65535)) port=SIP_PORT; } else { port=configuration.sip_listen_port; } sipsock_send(addr, port, ticket->protocol, buffer, buflen); /* free the resources */ osip_message_free(response); free(buffer); return STS_SUCCESS; }
/* * set expiration timeout as received with SIP response * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int register_set_expire(sip_ticket_t *ticket) { int i, j; int expires=-1; osip_contact_t *contact=NULL; time_t time_now; osip_header_t *expires_hdr=NULL; osip_uri_param_t *expires_param=NULL; if (ticket->direction != RESTYP_INCOMING) { WARN("register_set_expire called with != incoming response"); return STS_FAILURE; } time(&time_now); DEBUGC(DBCLASS_REG,"REGISTER response, looking for 'Expire' information"); /* evaluate Expires Header field */ osip_message_get_expires(ticket->sipmsg, 0, &expires_hdr); /* loop for all existing contact headers in message */ for (j=0; (contact != NULL) || (j==0); j++) { osip_message_get_contact(ticket->sipmsg, j, &contact); /* * look for an Contact expires parameter - in case of REGISTER * these two are equal. The Contact expires has higher priority! */ if (contact==NULL) continue; osip_contact_param_get_byname(contact, EXPIRES, &expires_param); if (expires_param && expires_param->gvalue) { /* get expires from contact Header */ expires=atoi(expires_param->gvalue); if ((expires < 0) || (expires >= UINT_MAX )) expires=configuration.default_expires; } else if (expires_hdr && expires_hdr->hvalue) { /* get expires from expires Header */ expires=atoi(expires_hdr->hvalue); if ((expires < 0) || (expires >= UINT_MAX )) expires=configuration.default_expires; } DEBUGC(DBCLASS_REG,"Expires=%i, expires_param=%p, expires_hdr=%p", expires, expires_param, expires_hdr); if (expires > 0) { /* search for an entry */ for (i=0;i<URLMAP_SIZE;i++){ if (urlmap[i].active == 0) continue; if ((compare_url(contact->url, urlmap[i].masq_url)==STS_SUCCESS)) break; } /* found a mapping entry */ if (i<URLMAP_SIZE) { /* update registration timeout */ DEBUGC(DBCLASS_REG,"changing registration timeout to %i" " entry [%i]", expires, i); urlmap[i].expires=time_now+expires; } else { DEBUGC(DBCLASS_REG,"no urlmap entry found"); } } } /* for j */ return STS_SUCCESS; }
int main(int argc, char** argv) { s32 opt; u32 loop_cnt = 0, purge_age = 0, seed; u8 sig_loaded = 0, show_once = 0, no_statistics = 0, display_mode = 0, has_fake = 0; s32 oindex = 0; u8 *wordlist = NULL, *output_dir = NULL; u8 *sig_list_strg = NULL; u8 *gtimeout_str = NULL; u32 gtimeout = 0; struct termios term; struct timeval tv; u64 st_time, en_time; signal(SIGINT, ctrlc_handler); signal(SIGWINCH, resize_handler); signal(SIGPIPE, SIG_IGN); SSL_library_init(); /* Options, options, and options */ static struct option long_options[] = { {"auth", required_argument, 0, 'A' }, {"host", required_argument, 0, 'F' }, {"cookie", required_argument, 0, 'C' }, {"reject-cookies", required_argument, 0, 'N' }, {"header", required_argument, 0, 'H' }, {"user-agent", required_argument, 0, 'b' }, #ifdef PROXY_SUPPORT {"proxy", required_argument, 0, 'J' }, #endif /* PROXY_SUPPORT */ {"max-depth", required_argument, 0, 'd' }, {"max-child", required_argument, 0, 'c' }, {"max-descendants", required_argument, 0, 'x' }, {"max-requests", required_argument, 0, 'r' }, {"max-rate", required_argument, 0, 'l'}, {"probability", required_argument, 0, 'p' }, {"seed", required_argument, 0, 'q' }, {"include", required_argument, 0, 'I' }, {"exclude", required_argument, 0, 'X' }, {"skip-param", required_argument, 0, 'K' }, {"skip-forms", no_argument, 0, 'O' }, {"include-domain", required_argument, 0, 'D' }, {"ignore-links", no_argument, 0, 'P' }, {"no-ext-fuzzing", no_argument, 0, 'Y' }, {"log-mixed-content", no_argument, 0, 'M' }, {"skip-error-pages", no_argument, 0, 'Z' }, {"log-external-urls", no_argument, 0, 'U' }, {"log-cache-mismatches", no_argument, 0, 'E' }, {"form-value", no_argument, 0, 'T' }, {"rw-wordlist", required_argument, 0, 'W' }, {"no-keyword-learning", no_argument, 0, 'L' }, {"mode", required_argument, 0, 'J' }, {"wordlist", required_argument, 0, 'S'}, {"trust-domain", required_argument, 0, 'B' }, {"max-connections", required_argument, 0, 'g' }, {"max-host-connections", required_argument, 0, 'm' }, {"max-fail", required_argument, 0, 'f' }, {"request-timeout", required_argument, 0, 't' }, {"network-timeout", required_argument, 0, 'w' }, {"idle-timeout", required_argument, 0, 'i' }, {"response-size", required_argument, 0, 's' }, {"discard-binary", required_argument, 0, 'e' }, {"output", required_argument, 0, 'o' }, {"help", no_argument, 0, 'h' }, {"quiet", no_argument, 0, 'u' }, {"verbose", no_argument, 0, 'v' }, {"scan-timeout", required_argument, 0, 'k'}, {"signatures", required_argument, 0, 'z'}, {"checks", no_argument, 0, 0}, {"checks-toggle", required_argument, 0, 0}, {"no-checks", no_argument, 0, 0}, {"fast", no_argument, 0, 0}, {"auth-form", required_argument, 0, 0}, {"auth-form-target", required_argument, 0, 0}, {"auth-user", required_argument, 0, 0}, {"auth-user-field", required_argument, 0, 0}, {"auth-pass", required_argument, 0, 0}, {"auth-pass-field", required_argument, 0, 0}, {"auth-verify-url", required_argument, 0, 0}, {0, 0, 0, 0 } }; /* Come up with a quasi-decent random seed. */ gettimeofday(&tv, NULL); seed = tv.tv_usec ^ (tv.tv_sec << 16) ^ getpid(); SAY("skipfish version " VERSION " by <*****@*****.**>\n"); while ((opt = getopt_long(argc, argv, "+A:B:C:D:EF:G:H:I:J:K:LMNOPQR:S:T:UW:X:YZ" "b:c:d:ef:g:hi:k:l:m:o:p:q:r:s:t:uvw:x:z:", long_options, &oindex)) >= 0) switch (opt) { case 'A': { u8* x = (u8*)strchr(optarg, ':'); if (!x) FATAL("Credentials must be in 'user:pass' form."); *(x++) = 0; auth_user = (u8*)optarg; auth_pass = x; auth_type = AUTH_BASIC; break; } #ifdef PROXY_SUPPORT case 'J': { u8* x = (u8*)strchr(optarg, ':'); if (!x) FATAL("Proxy data must be in 'host:port' form."); *(x++) = 0; use_proxy = (u8*)optarg; use_proxy_port = atoi((char*)x); if (!use_proxy_port) FATAL("Incorrect proxy port number."); break; } #endif /* PROXY_SUPPORT */ case 'F': { u8* x = (u8*)strchr(optarg, '='); u32 fake_addr; if (!x) FATAL("Fake mappings must be in 'host=IP' form."); *x = 0; fake_addr = inet_addr((char*)x + 1); if (fake_addr == (u32)-1) FATAL("Could not parse IP address '%s'.", x + 1); fake_host((u8*)optarg, fake_addr); has_fake = 1; break; } case 'H': { u8* x = (u8*)strchr(optarg, '='); if (!x) FATAL("Extra headers must be in 'name=value' form."); *x = 0; if (!strcasecmp(optarg, "Cookie")) FATAL("Do not use -H to set cookies (try -C instead)."); SET_HDR((u8*)optarg, x + 1, &global_http_par); break; } case 'C': { u8* x = (u8*)strchr(optarg, '='); if (!x) FATAL("Cookies must be in 'name=value' form."); if (strchr(optarg, ';')) FATAL("Split multiple cookies into separate -C options."); *x = 0; SET_CK((u8*)optarg, x + 1, &global_http_par); break; } case 'D': if (*optarg == '*') optarg++; APPEND_FILTER(allow_domains, num_allow_domains, optarg); break; case 'K': APPEND_FILTER(skip_params, num_skip_params, optarg); break; case 'B': if (*optarg == '*') optarg++; APPEND_FILTER(trust_domains, num_trust_domains, optarg); break; case 'I': if (*optarg == '*') optarg++; APPEND_FILTER(allow_urls, num_allow_urls, optarg); break; case 'X': if (*optarg == '*') optarg++; APPEND_FILTER(deny_urls, num_deny_urls, optarg); break; case 'T': { u8* x = (u8*)strchr(optarg, '='); if (!x) FATAL("Rules must be in 'name=value' form."); *x = 0; add_form_hint((u8*)optarg, x + 1); break; } case 'N': ignore_cookies = 1; break; case 'Y': no_fuzz_ext = 1; break; case 'q': if (sscanf(optarg, "0x%08x", &seed) != 1) FATAL("Invalid seed format."); srandom(seed); break; case 'Q': suppress_dupes = 1; break; case 'P': no_parse = 1; break; case 'M': warn_mixed = 1; break; case 'U': log_ext_urls = 1; break; case 'L': dont_add_words = 1; break; case 'E': pedantic_cache = 1; break; case 'O': no_forms = 1; break; case 'R': purge_age = atoi(optarg); if (purge_age < 3) FATAL("Purge age invalid or too low (min 3)."); break; case 'd': max_depth = atoi(optarg); if (max_depth < 2) FATAL("Invalid value '%s'.", optarg); break; case 'c': max_children = atoi(optarg); if (!max_children) FATAL("Invalid value '%s'.", optarg); break; case 'x': max_descendants = atoi(optarg); if (!max_descendants) FATAL("Invalid value '%s'.", optarg); break; case 'p': crawl_prob = atoi(optarg); if (!crawl_prob) FATAL("Invalid value '%s'.", optarg); break; case 'W': if (wordlist) FATAL("Only one -W parameter permitted (use -S to load supplemental dictionaries)."); if (!strcmp(optarg, "-")) wordlist = (u8*)"/dev/null"; else wordlist = (u8*)optarg; break; case 'S': load_keywords((u8*)optarg, 1, 0); break; case 'z': load_signatures((u8*)optarg); sig_loaded = 1; break; case 'b': if (optarg[0] == 'i') browser_type = BROWSER_MSIE; else if (optarg[0] == 'f') browser_type = BROWSER_FFOX; else if (optarg[0] == 'p') browser_type = BROWSER_PHONE; else usage(argv[0]); break; case 'g': max_connections = atoi(optarg); if (!max_connections) FATAL("Invalid value '%s'.", optarg); break; case 'm': max_conn_host = atoi(optarg); if (!max_conn_host) FATAL("Invalid value '%s'.", optarg); break; case 'G': max_guesses = atoi(optarg); if (!max_guesses) FATAL("Invalid value '%s'.", optarg); break; case 'r': max_requests = atoi(optarg); if (!max_requests) FATAL("Invalid value '%s'.", optarg); break; case 'l': max_requests_sec = atof(optarg); if (!max_requests_sec) FATAL("Invalid value '%s'.", optarg); break; case 'f': max_fail = atoi(optarg); if (!max_fail) FATAL("Invalid value '%s'.", optarg); break; case 't': resp_tmout = atoi(optarg); if (!resp_tmout) FATAL("Invalid value '%s'.", optarg); break; case 'w': rw_tmout = atoi(optarg); if (!rw_tmout) FATAL("Invalid value '%s'.", optarg); break; case 'i': idle_tmout = atoi(optarg); if (!idle_tmout) FATAL("Invalid value '%s'.", optarg); break; case 's': size_limit = atoi(optarg); if (!size_limit) FATAL("Invalid value '%s'.", optarg); break; case 'o': if (output_dir) FATAL("Multiple -o options not allowed."); output_dir = (u8*)optarg; rmdir(optarg); if (mkdir(optarg, 0755)) PFATAL("Unable to create '%s'.", output_dir); break; case 'u': no_statistics = 1; break; case 'v': verbosity++; break; case 'e': delete_bin = 1; break; case 'k': if (gtimeout_str) FATAL("Multiple -k options not allowed."); gtimeout_str = (u8*)optarg; break; case 'Z': no_500_dir = 1; break; case 0: if (!strcmp("checks", long_options[oindex].name )) display_injection_checks(); if (!strcmp("checks-toggle", long_options[oindex].name )) toggle_injection_checks((u8*)optarg, 1, 1); if (!strcmp("no-checks", long_options[oindex].name )) no_checks = 1; if (!strcmp("signatures", long_options[oindex].name )) load_signatures((u8*)optarg); if(!strcmp("fast", long_options[oindex].name )) toggle_injection_checks((u8*)"2,4,5,13,14,15,16", 0, 0); if (!strcmp("auth-form", long_options[oindex].name )) auth_form = (u8*)optarg; if (!strcmp("auth-user", long_options[oindex].name )) auth_user = (u8*)optarg; if (!strcmp("auth-pass", long_options[oindex].name )) auth_pass = (u8*)optarg; if (!strcmp("auth-pass-field", long_options[oindex].name )) auth_pass_field = (u8*)optarg; if (!strcmp("auth-user-field", long_options[oindex].name )) auth_user_field = (u8*)optarg; if (!strcmp("auth-form-target", long_options[oindex].name )) auth_form_target = (u8*)optarg; if (!strcmp("auth-verify-url", long_options[oindex].name )) auth_verify_url = (u8*)optarg; break; default: usage(argv[0]); } #ifdef PROXY_SUPPORT if (has_fake && use_proxy) FATAL("-F and -J should not be used together."); #endif /* PROXY_SUPPORT */ if (access(ASSETS_DIR "/index.html", R_OK)) PFATAL("Unable to access '%s/index.html' - wrong directory?", ASSETS_DIR); srandom(seed); if (optind == argc) FATAL("Scan target not specified (try -h for help)."); if (!output_dir) FATAL("Output directory not specified (try -h for help)."); if(verbosity && !no_statistics && isatty(2)) FATAL("Please use -v in combination with the -u flag or, " "run skipfish while redirecting stderr to a file. "); if (resp_tmout < rw_tmout) resp_tmout = rw_tmout; if (max_connections < max_conn_host) max_connections = max_conn_host; /* Parse the timeout string - format h:m:s */ if (gtimeout_str) { int i = 0; int m[3] = { 1, 60, 3600 }; u8* tok = (u8*)strtok((char*)gtimeout_str, ":"); while(tok && i <= 2) { gtimeout += atoi((char*)tok) * m[i]; tok = (u8*)strtok(NULL, ":"); i++; } if(!gtimeout) FATAL("Wrong timeout format, please use h:m:s (hours, minutes, seconds)"); DEBUG("* Scan timeout is set to %d seconds\n", gtimeout); } if (!wordlist) { wordlist = (u8*)"/dev/null"; DEBUG("* No wordlist specified with -W: defaulting to /dev/null\n"); } /* If no signature files have been specified via command-line: load the default file */ if (!sig_loaded) load_signatures((u8*)SIG_FILE); load_keywords(wordlist, 0, purge_age); /* Load the signatures list for the matching */ if (sig_list_strg) load_signatures(sig_list_strg); /* Try to authenticate when the auth_user and auth_pass fields are set. */ if (auth_user && auth_pass) { authenticate(); while (next_from_queue()) { usleep(1000); } switch (auth_state) { case ASTATE_DONE: DEBUGC(L1, "*- Authentication succeeded!\n"); break; default: DEBUG("Auth state: %d\n", auth_state); FATAL("Authentication failed (use -uv for more info)\n"); break; } } /* Schedule all URLs in the command line for scanning. */ while (optind < argc) { struct http_request *req; /* Support @ notation for reading URL lists from files. */ if (argv[optind][0] == '@') { read_urls((u8*)argv[optind++] + 1); continue; } req = ck_alloc(sizeof(struct http_request)); if (parse_url((u8*)argv[optind], req, NULL)) FATAL("Scan target '%s' is not a valid absolute URL.", argv[optind]); if (!url_allowed_host(req)) APPEND_FILTER(allow_domains, num_allow_domains, __DFL_ck_strdup(req->host)); if (!url_allowed(req)) FATAL("URL '%s' explicitly excluded by -I / -X rules.", argv[optind]); maybe_add_pivot(req, NULL, 2); destroy_request(req); optind++; } /* Char-by char stdin. */ tcgetattr(0, &term); term.c_lflag &= ~ICANON; tcsetattr(0, TCSANOW, &term); fcntl(0, F_SETFL, O_NONBLOCK); gettimeofday(&tv, NULL); st_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; #ifdef SHOW_SPLASH if (!no_statistics) splash_screen(); #endif /* SHOW_SPLASH */ if (!no_statistics) SAY("\x1b[H\x1b[J"); else SAY(cLGN "[*] " cBRI "Scan in progress, please stay tuned...\n"); u64 refresh_time = 0; /* Enter the crawler loop */ while ((next_from_queue() && !stop_soon) || (!show_once++)) { u8 keybuf[8]; u64 end_time; u64 run_time; struct timeval tv_tmp; gettimeofday(&tv_tmp, NULL); end_time = tv_tmp.tv_sec * 1000LL + tv_tmp.tv_usec / 1000; run_time = end_time - st_time; if (gtimeout > 0 && run_time && run_time/1000 > gtimeout) { DEBUG("* Stopping scan due to timeout\n"); stop_soon = 1; } req_sec = (req_count - queue_cur / 1.15) * 1000 / (run_time + 1); if (no_statistics || ((loop_cnt++ % 100) && !show_once && idle == 0)) continue; if (end_time > refresh_time) { refresh_time = (end_time + 10); if (clear_screen) { SAY("\x1b[H\x1b[2J"); clear_screen = 0; } SAY(cYEL "\x1b[H" "skipfish version " VERSION " by [email protected]\n\n" cBRI " -" cPIN " %s " cBRI "-\n\n" cNOR, allow_domains[0]); if (!display_mode) { http_stats(st_time); SAY("\n"); database_stats(); } else { http_req_list(); } SAY(" \r"); } if (fread(keybuf, 1, sizeof(keybuf), stdin) > 0) { display_mode ^= 1; clear_screen = 1; } } gettimeofday(&tv, NULL); en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; SAY("\n"); if (stop_soon) SAY(cYEL "[!] " cBRI "Scan aborted by user, bailing out!" cNOR "\n"); term.c_lflag |= ICANON; tcsetattr(0, TCSANOW, &term); fcntl(0, F_SETFL, O_SYNC); save_keywords((u8*)wordlist); write_report(output_dir, en_time - st_time, seed); #ifdef LOG_STDERR SAY("\n== PIVOT DEBUG ==\n"); dump_pivots(0, 0); SAY("\n== END OF DUMP ==\n\n"); #endif /* LOG_STDERR */ SAY(cLGN "[+] " cBRI "This was a great day for science!" cRST "\n\n"); #ifdef DEBUG_ALLOCATOR if (!stop_soon) { destroy_database(); destroy_signature_lists(); destroy_http(); destroy_signatures(); __TRK_report(); } #endif /* DEBUG_ALLOCATOR */ fflush(0); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); return 0; }