/* * 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 void do_work(void) { char *curip = NULL; struct in_addr inr; log_line("updating to interface: [%s]", ifname); while (1) { free(curip); if (update_from_remote == 0) { curip = get_interface_ip(ifname); } else { curip = query_curip(); } if (!curip) goto sleep; if (inet_aton(curip, &inr) == 0) { log_line("%s has ip: [%s], which is invalid. Sleeping.", ifname, curip); goto sleep; } dd_work(curip); nc_work(curip); he_dns_work(curip); he_tun_work(curip); sleep: do_sleep(); } }
/* * 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; }
char* get_local_ip(int ip_source, void* check_parameter) { char* ip = NULL; if(ip_source == INTERFACE) { ip = get_interface_ip((char*)check_parameter); } else { char** urls = (char**)check_parameter; char* next_url; char first_url[MAX_LOOKUP_URL_LENGTH]; int is_first_lookup; initialize_default_ip_lookup_urls(); if(urls != NULL) { is_first_lookup=1; next_url = get_next_url_and_rotate(urls); strcpy(first_url, next_url); while(ip == NULL && (strcmp(first_url, next_url) != 0 || is_first_lookup == 1)) { ip = get_ip_from_url(next_url); //syslog(LOG_INFO, "\t\t%s local ip from url: %s\n", (ip == NULL ? "Could not determine" : "Successfully retrieved"), next_url); printf("\t\t%s local IP from URL: %s\n", (ip == NULL ? "Could not determine" : "Successfully retrieved"), next_url); if(ip == NULL) { next_url = get_next_url_and_rotate(urls); } is_first_lookup = 0; } } if(ip == NULL) { is_first_lookup=1; next_url = get_next_url_and_rotate(default_ip_lookup_urls); strcpy(first_url, next_url); while(ip == NULL && (strcmp(first_url, next_url) != 0 || is_first_lookup == 1)) { ip = get_ip_from_url(next_url); //syslog(LOG_INFO, "\t\t%s local ip from url: %s\n", (ip == NULL ? "Could not determine" : "Successfully retrieved"), next_url); printf("\t\t%s local IP from URL: %s\n", (ip == NULL ? "Could not determine" : "Successfully retrieved"), next_url); if(ip == NULL) { next_url = get_next_url_and_rotate(default_ip_lookup_urls); } is_first_lookup = 0; } } } return ip; }
/* * 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; }
/* * 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; }
/* * PROXY_REWRITE_INVITATION_BODY * * rewrites the outgoing INVITATION request or response packet * * RETURNS * STS_SUCCESS on success * STS_FAILURE on error */ int proxy_rewrite_invitation_body(osip_message_t *mymsg, int direction){ osip_body_t *body; sdp_message_t *sdp; struct in_addr map_addr, addr_sess, addr_media, outside_addr, inside_addr; int sts; char *bodybuff; int bodybuflen; char clen[8]; /* content length: probably never more than 7 digits !*/ int map_port, msg_port; int media_stream_no; sdp_connection_t *sdp_conn; sdp_media_t *sdp_med; int rtp_direction=0; int have_c_media=0; if (configuration.rtp_proxy_enable == 0) return STS_SUCCESS; /* * get SDP structure */ sts = osip_message_get_body(mymsg, 0, &body); if (sts != 0) { #if 0 if ((MSG_IS_RESPONSE_FOR(mymsg,"INVITE")) && (MSG_IS_STATUS_1XX(mymsg))) { /* 1xx responses *MAY* contain SDP data */ DEBUGC(DBCLASS_PROXY, "rewrite_invitation_body: " "no body found in message"); return STS_SUCCESS; } else { /* INVITE request and 200 response *MUST* contain SDP data */ ERROR("rewrite_invitation_body: no body found in message"); return STS_FAILURE; } #else DEBUGC(DBCLASS_PROXY, "rewrite_invitation_body: " "no body found in message"); return STS_SUCCESS; #endif } sts = sip_body_to_str(body, &bodybuff, &bodybuflen); if (sts != 0) { ERROR("rewrite_invitation_body: unable to sip_body_to_str"); } sts = sdp_message_init(&sdp); sts = sdp_message_parse (sdp, bodybuff); if (sts != 0) { ERROR("rewrite_invitation_body: unable to sdp_message_parse body"); DUMP_BUFFER(-1, bodybuff, bodybuflen); osip_free(bodybuff); sdp_message_free(sdp); return STS_FAILURE; } osip_free(bodybuff); if (configuration.debuglevel) { /* just dump the buffer */ char *tmp, *tmp2; int tmplen; sts = osip_message_get_body(mymsg, 0, &body); sts = sip_body_to_str(body, &tmp, &tmplen); osip_content_length_to_str(mymsg->content_length, &tmp2); DEBUG("Body before rewrite (clen=%s, strlen=%i):\n%s\n----", tmp2, tmplen, tmp); osip_free(tmp); osip_free(tmp2); } /* * RTP proxy: get ready and start forwarding * start forwarding for each media stream ('m=' item in SIP message) */ /* get outbound address */ if (get_interface_ip(IF_OUTBOUND, &outside_addr) != STS_SUCCESS) { sdp_message_free(sdp); return STS_FAILURE; } /* get inbound address */ if (get_interface_ip(IF_INBOUND, &inside_addr) != STS_SUCCESS) { sdp_message_free(sdp); return STS_FAILURE; } /* figure out what address to use for RTP masquerading */ if (MSG_IS_REQUEST(mymsg)) { if (direction == DIR_INCOMING) { memcpy(&map_addr, &inside_addr, sizeof (map_addr)); rtp_direction = DIR_OUTGOING; } else { memcpy(&map_addr, &outside_addr, sizeof (map_addr)); rtp_direction = DIR_INCOMING; } } else /* MSG_IS_REPONSE(mymsg) */ { if (direction == DIR_INCOMING) { memcpy(&map_addr, &inside_addr, sizeof (map_addr)); rtp_direction = DIR_OUTGOING; } else { memcpy(&map_addr, &outside_addr, sizeof (map_addr)); rtp_direction = DIR_INCOMING; } } DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: SIP[%s %s] RTP[%s %s]", MSG_IS_REQUEST(mymsg)? "RQ" : "RS", (direction==DIR_INCOMING)? "IN" : "OUT", (rtp_direction==DIR_INCOMING)? "IN" : "OUT", utils_inet_ntoa(map_addr)); /* * first, check presence of a 'c=' item on session level */ if (sdp->c_connection==NULL || sdp->c_connection->c_addr==NULL) { /* * No 'c=' on session level, search on media level now * * According to RFC2327, ALL media description must * include a 'c=' item now: */ media_stream_no=0; while (!sdp_message_endof_media(sdp, media_stream_no)) { /* check if n'th media stream is present */ if (sdp_message_c_addr_get(sdp, media_stream_no, 0) == NULL) { ERROR("SDP: have no 'c=' on session level and neither " "on media level (media=%i)",media_stream_no); sdp_message_free(sdp); return STS_FAILURE; } media_stream_no++; } /* while */ } /* Required 'c=' items ARE present */ /* * rewrite 'c=' item on session level if present and not yet done. * remember the original address in addr_sess */ memset(&addr_sess, 0, sizeof(addr_sess)); if (sdp->c_connection && sdp->c_connection->c_addr) { sts = get_ip_by_host(sdp->c_connection->c_addr, &addr_sess); if (sts == STS_FAILURE) { ERROR("SDP: cannot resolve session 'c=' host [%s]", sdp->c_connection->c_addr); sdp_message_free(sdp); return STS_FAILURE; } /* * Rewrite * an IP address of 0.0.0.0 means *MUTE*, don't rewrite such */ if (strcmp(sdp->c_connection->c_addr, "0.0.0.0") != 0) { osip_free(sdp->c_connection->c_addr); sdp->c_connection->c_addr=osip_malloc(HOSTNAME_SIZE); sprintf(sdp->c_connection->c_addr, "%s", utils_inet_ntoa(map_addr)); } else { /* 0.0.0.0 - don't rewrite */ DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: " "got a MUTE c= record (on session level - legal?)"); } } /* * rewrite 'o=' item (originator) on session level if present. */ if (sdp->o_addrtype && sdp->o_addr) { if (strcmp(sdp->o_addrtype, "IP4") != 0) { ERROR("got IP6 in SDP originator - not yet suported by siproxd"); sdp_message_free(sdp); return STS_FAILURE; } osip_free(sdp->o_addr); sdp->o_addr=osip_malloc(HOSTNAME_SIZE); sprintf(sdp->o_addr, "%s", utils_inet_ntoa(map_addr)); } /* * loop through all media descritions, * start RTP proxy and rewrite them */ for (media_stream_no=0;;media_stream_no++) { /* check if n'th media stream is present */ if (sdp_message_m_port_get(sdp, media_stream_no) == NULL) break; /* * check if a 'c=' item is present in this media description, * if so -> rewrite it */ memset(&addr_media, 0, sizeof(addr_media)); have_c_media=0; sdp_conn=sdp_message_connection_get(sdp, media_stream_no, 0); if (sdp_conn && sdp_conn->c_addr) { if (strcmp(sdp_conn->c_addr, "0.0.0.0") != 0) { sts = get_ip_by_host(sdp_conn->c_addr, &addr_media); have_c_media=1; /* have a valid address */ osip_free(sdp_conn->c_addr); sdp_conn->c_addr=osip_malloc(HOSTNAME_SIZE); sprintf(sdp_conn->c_addr, "%s", utils_inet_ntoa(map_addr)); } else { /* 0.0.0.0 - don't rewrite */ DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: got a " "MUTE c= record (media level)"); } } /* start an RTP proxying stream */ if (sdp_message_m_port_get(sdp, media_stream_no)) { msg_port=atoi(sdp_message_m_port_get(sdp, media_stream_no)); if (msg_port > 0) { osip_uri_t *cont_url = NULL; char *client_id=NULL; /* try to get some additional UA specific unique ID. * Try: * 1) User part of Contact header * 2) Host part of Contact header (will be different * between internal UA and external UA) */ if (!osip_list_eol(mymsg->contacts, 0)) cont_url = ((osip_contact_t*)(mymsg->contacts->node->element))->url; if (cont_url) { client_id=cont_url->username; if (client_id == NULL) client_id=cont_url->host; } /* * do we have a 'c=' item on media level? * if not, use the same as on session level */ if (have_c_media == 0) { memcpy(&addr_media, &addr_sess, sizeof(addr_sess)); } /* * Am I running in front of the routing device? Then I cannot * use the external IP to bind a listen socket to, so force * the use of my inbound IP for listening. */ if ((rtp_direction == DIR_INCOMING) && (configuration.outbound_host) && (strcmp(configuration.outbound_host, "")!=0)) { DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: " "in-front-of-NAT-Router"); memcpy(&map_addr, &inside_addr, sizeof (map_addr)); } sts = rtp_start_fwd(osip_message_get_call_id(mymsg), client_id, rtp_direction, media_stream_no, map_addr, &map_port, addr_media, msg_port); if (sts == STS_SUCCESS) { /* and rewrite the port */ sdp_med=osip_list_get(sdp->m_medias, media_stream_no); if (sdp_med && sdp_med->m_port) { osip_free(sdp_med->m_port); sdp_med->m_port=osip_malloc(8); /* 5 digits, \0 + align */ sprintf(sdp_med->m_port, "%i", map_port); DEBUGC(DBCLASS_PROXY, "proxy_rewrite_invitation_body: " "m= rewrote port to [%i]",map_port); } else { ERROR("rewriting port in m= failed sdp_med=%p, " "m_number_of_port=%p", sdp_med, sdp_med->m_port); } } /* sts == success */ } /* if msg_port > 0 */ } else { /* no port defined - skip entry */ WARN("no port defined in m=(media) stream_no=%i", media_stream_no); continue; } } /* for media_stream_no */ /* remove old body */ sts = osip_list_remove(mymsg->bodies, 0); osip_body_free(body); /* dump new body */ sdp_message_to_str(sdp, &bodybuff); bodybuflen=strlen(bodybuff); /* free sdp structure */ sdp_message_free(sdp); /* include new body */ sip_message_set_body(mymsg, bodybuff, bodybuflen); if (sts != 0) { ERROR("rewrite_invitation_body: unable to sip_message_set_body body"); } /* free content length resource and include new one*/ osip_content_length_free(mymsg->content_length); mymsg->content_length=NULL; sprintf(clen,"%i",bodybuflen); sts = osip_message_set_content_length(mymsg, clen); /* free old body */ osip_free(bodybuff); if (configuration.debuglevel) { /* just dump the buffer */ char *tmp, *tmp2; int tmplen; sts = osip_message_get_body(mymsg, 0, &body); sts = sip_body_to_str(body, &tmp, &tmplen); osip_content_length_to_str(mymsg->content_length, &tmp2); DEBUG("Body after rewrite (clen=%s, strlen=%i):\n%s\n----", tmp2, tmplen, tmp); osip_free(tmp); osip_free(tmp2); } return STS_SUCCESS; }
int main (int argc, char *argv[]) { int sts; int i; int buflen; int access; char buff [BUFFER_SIZE]; sip_ticket_t ticket; extern char *optarg; /* Defined in libc getopt and unistd.h */ int ch1; char configfile[64]="siproxd"; /* basename of configfile */ int config_search=1; /* search the config file */ int cmdline_debuglevel=0; char *pidfilename=NULL; struct sigaction act; log_set_stderr(1); /* * setup signal handlers */ act.sa_handler=sighandler; sigemptyset(&act.sa_mask); act.sa_flags=SA_RESTART; if (sigaction(SIGTERM, &act, NULL)) { ERROR("Failed to install SIGTERM handler"); } if (sigaction(SIGINT, &act, NULL)) { ERROR("Failed to install SIGINT handler"); } if (sigaction(SIGUSR2, &act, NULL)) { ERROR("Failed to install SIGUSR2 handler"); } /* * prepare default configuration */ make_default_config(); log_set_pattern(configuration.debuglevel); /* * parse command line */ { #ifdef HAVE_GETOPT_LONG int option_index = 0; static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"config", required_argument, NULL, 'c'}, {"debug", required_argument, NULL, 'd'}, {"pid-file", required_argument, NULL,'p'}, {0,0,0,0} }; while ((ch1 = getopt_long(argc, argv, "hc:d:p:", long_options, &option_index)) != -1) { #else /* ! HAVE_GETOPT_LONG */ while ((ch1 = getopt(argc, argv, "hc:d:p:")) != -1) { #endif switch (ch1) { case 'h': /* help */ DEBUGC(DBCLASS_CONFIG,"option: help"); fprintf(stderr,str_helpmsg); exit(0); break; case 'c': /* load config file */ DEBUGC(DBCLASS_CONFIG,"option: config file=%s",optarg); i=sizeof(configfile)-1; strncpy(configfile,optarg,i-1); configfile[i]='\0'; config_search=0; break; case 'd': /* set debug level */ DEBUGC(DBCLASS_CONFIG,"option: set debug level: %s",optarg); cmdline_debuglevel=atoi(optarg); log_set_pattern(cmdline_debuglevel); break; case 'p': pidfilename = optarg; break; default: DEBUGC(DBCLASS_CONFIG,"no command line options"); break; } } } /* * Init stuff */ INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" starting up"); /* read the config file */ if (read_config(configfile, config_search) == STS_FAILURE) exit(1); /* if a debug level > 0 has been given on the commandline use its value and not what is in the config file */ if (cmdline_debuglevel != 0) { configuration.debuglevel=cmdline_debuglevel; } /* * open a the pwfile instance, so we still have access after * we possibly have chroot()ed to somewhere. */ if (configuration.proxy_auth_pwfile) { siproxd_passwordfile = fopen(configuration.proxy_auth_pwfile, "r"); } else { siproxd_passwordfile = NULL; } /* set debug level as desired */ log_set_pattern(configuration.debuglevel); log_set_listen_port(configuration.debugport); /* change user and group IDs */ secure_enviroment(); /* daemonize if requested to */ if (configuration.daemonize) { DEBUGC(DBCLASS_CONFIG,"daemonizing"); if (fork()!=0) exit(0); setsid(); if (fork()!=0) exit(0); log_set_stderr(0); INFO("daemonized, pid=%i", getpid()); } /* write PID file of main thread */ if (pidfilename == NULL) pidfilename = configuration.pid_file; if (pidfilename) { FILE *pidfile; DEBUGC(DBCLASS_CONFIG,"creating PID file [%s]", pidfilename); sts=unlink(configuration.pid_file); if ((sts==0) ||(errno == ENOENT)) { if ((pidfile=fopen(pidfilename, "w"))) { fprintf(pidfile,"%i\n",(int)getpid()); fclose(pidfile); } else { WARN("couldn't create new PID file: %s", strerror(errno)); } } else { WARN("couldn't delete old PID file: %s", strerror(errno)); } } /* initialize the RTP proxy */ sts=rtpproxy_init(); if (sts != STS_SUCCESS) { ERROR("unable to initialize RTP proxy - aborting"); exit(1); } /* init the oSIP parser */ parser_init(); /* listen for incoming messages */ sts=sipsock_listen(); if (sts == STS_FAILURE) { /* failure to allocate SIP socket... */ ERROR("unable to bind to SIP listening socket - aborting"); exit(1); } /* initialize the registration facility */ register_init(); /* * silence the log - if so required... */ log_set_silence(configuration.silence_log); INFO(PACKAGE"-"VERSION"-"BUILDSTR" "UNAME" started"); /***************************** * Main loop *****************************/ while (!exit_program) { DEBUGC(DBCLASS_BABBLE,"going into sipsock_wait\n"); while (sipsock_wait()<=0) { /* got no input, here by timeout. do aging */ register_agemap(); /* TCP log: check for a connection */ log_tcp_connect(); /* dump memory stats if requested to do so */ if (dmalloc_dump) { dmalloc_dump=0; #ifdef DMALLOC INFO("SIGUSR2 - DMALLOC statistics is dumped"); dmalloc_log_stats(); dmalloc_log_unfreed(); #else INFO("SIGUSR2 - DMALLOC support is not compiled in"); #endif } if (exit_program) goto exit_prg; } /* * got input, process */ DEBUGC(DBCLASS_BABBLE,"back from sipsock_wait"); ticket.direction=0; buflen=sipsock_read(&buff, sizeof(buff)-1, &ticket.from, &ticket.protocol); buff[buflen]='\0'; /* * evaluate the access lists (IP based filter) */ access=accesslist_check(ticket.from); if (access == 0) { DEBUGC(DBCLASS_ACCESS,"access for this packet was denied"); continue; /* there are no resources to free */ } /* * integrity checks */ sts=security_check_raw(buff, buflen); if (sts != STS_SUCCESS) { DEBUGC(DBCLASS_SIP,"security check (raw) failed"); continue; /* there are no resources to free */ } /* * init sip_msg */ sts=osip_message_init(&ticket.sipmsg); ticket.sipmsg->message=NULL; if (sts != 0) { ERROR("osip_message_init() failed... this is not good"); continue; /* skip, there are no resources to free */ } /* * RFC 3261, Section 16.3 step 1 * Proxy Behavior - Request Validation - Reasonable Syntax * (parse the received message) */ sts=sip_message_parse(ticket.sipmsg, buff, buflen); if (sts != 0) { ERROR("sip_message_parse() failed... this is not good"); DUMP_BUFFER(-1, buff, buflen); goto end_loop; /* skip and free resources */ } /* * integrity checks - parsed buffer */ sts=security_check_sip(&ticket); if (sts != STS_SUCCESS) { ERROR("security_check_sip() failed... this is not good"); DUMP_BUFFER(-1, buff, buflen); goto end_loop; /* skip and free resources */ } /* * RFC 3261, Section 16.3 step 2 * Proxy Behavior - Request Validation - URI scheme * (check request URI and refuse with 416 if not understood) */ /* NOT IMPLEMENTED */ /* * RFC 3261, Section 16.3 step 3 * Proxy Behavior - Request Validation - Max-Forwards check * (check Max-Forwards header and refuse with 483 if too many hops) */ { osip_header_t *max_forwards; int forwards_count = DEFAULT_MAXFWD; osip_message_get_max_forwards(ticket.sipmsg, 0, &max_forwards); if (max_forwards && max_forwards->hvalue) { forwards_count = atoi(max_forwards->hvalue); } DEBUGC(DBCLASS_PROXY,"checking Max-Forwards (=%i)",forwards_count); if (forwards_count <= 0) { DEBUGC(DBCLASS_SIP, "Forward count reached 0 -> 483 response"); sip_gen_response(&ticket, 483 /*Too many hops*/); goto end_loop; /* skip and free resources */ } } /* * RFC 3261, Section 16.3 step 4 * Proxy Behavior - Request Validation - Loop Detection check * (check for loop and return 482 if a loop is detected) */ if (check_vialoop(&ticket) == STS_TRUE) { /* make sure we don't end up in endless loop when detecting * an loop in an "loop detected" message - brrr */ if (MSG_IS_RESPONSE(ticket.sipmsg) && MSG_TEST_CODE(ticket.sipmsg, 482)) { DEBUGC(DBCLASS_SIP,"loop in loop-response detected, ignoring"); } else { DEBUGC(DBCLASS_SIP,"via loop detected, ignoring request"); sip_gen_response(&ticket, 482 /*Loop detected*/); } goto end_loop; /* skip and free resources */ } /* * RFC 3261, Section 16.3 step 5 * Proxy Behavior - Request Validation - Proxy-Require check * (check Proxy-Require header and return 420 if unsupported option) */ /* NOT IMPLEMENTED */ /* * RFC 3261, Section 16.5 * Proxy Behavior - Determining Request Targets */ /* NOT IMPLEMENTED */ DEBUGC(DBCLASS_SIP,"received SIP type %s:%s", (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES", (MSG_IS_REQUEST(ticket.sipmsg) ? ((ticket.sipmsg->sip_method)? ticket.sipmsg->sip_method : "NULL") : ((ticket.sipmsg->reason_phrase) ? ticket.sipmsg->reason_phrase : "NULL"))); /********************************* * The message did pass all the * tests above and is now ready * to be proxied. * Before we do so, we apply some * additional preprocessing *********************************/ /* Dial shortcuts */ if (configuration.pi_shortdial) { sts = plugin_shortdial(&ticket); if (sts == STS_SIP_SENT) goto end_loop; } /********************************* * finally proxy the message. * This includes the masquerading * of the local UA and starting/ * stopping the RTP proxy for this * call *********************************/ /* * if a REQ REGISTER, check if it is directed to myself, * or am I just the outbound proxy but no registrar. * - If I'm the registrar, register & generate answer * - If I'm just the outbound proxy, register, rewrite & forward */ if (MSG_IS_REGISTER(ticket.sipmsg) && MSG_IS_REQUEST(ticket.sipmsg)) { if (access & ACCESSCTL_REG) { osip_uri_t *url; struct in_addr addr1, addr2, addr3; int dest_port; url = osip_message_get_uri(ticket.sipmsg); dest_port= (url->port)?atoi(url->port):SIP_PORT; if ( (get_ip_by_host(url->host, &addr1) == STS_SUCCESS) && (get_interface_ip(IF_INBOUND,&addr2) == STS_SUCCESS) && (get_interface_ip(IF_OUTBOUND,&addr3) == STS_SUCCESS)) { if ((configuration.sip_listen_port == dest_port) && ((memcmp(&addr1, &addr2, sizeof(addr1)) == 0) || (memcmp(&addr1, &addr3, sizeof(addr1)) == 0))) { /* I'm the registrar, send response myself */ sts = register_client(&ticket, 0); sts = register_response(&ticket, sts); } else { /* I'm just the outbound proxy */ DEBUGC(DBCLASS_SIP,"proxying REGISTER request to:%s", url->host); sts = register_client(&ticket, 1); if (sts == STS_SUCCESS) { sts = proxy_request(&ticket); } } } else { sip_gen_response(&ticket, 408 /*request timeout*/); } } else { WARN("non-authorized registration attempt from %s", utils_inet_ntoa(ticket.from.sin_addr)); } /* * check if outbound interface is UP. * If not, send back error to UA and * skip any proxying attempt */ } else if (get_interface_ip(IF_OUTBOUND,NULL) != STS_SUCCESS) { DEBUGC(DBCLASS_SIP, "got a %s to proxy, but outbound interface " "is down", (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES"); if (MSG_IS_REQUEST(ticket.sipmsg)) sip_gen_response(&ticket, 408 /*request timeout*/); /* * MSG is a request, add current via entry, * do a lookup in the URLMAP table and * send to the final destination */ } else if (MSG_IS_REQUEST(ticket.sipmsg)) { if (access & ACCESSCTL_SIP) { sts = proxy_request(&ticket); } else { INFO("non-authorized request received from %s", utils_inet_ntoa(ticket.from.sin_addr)); } /* * MSG is a response, remove current via and * send to the next VIA in chain */ } else if (MSG_IS_RESPONSE(ticket.sipmsg)) { if (access & ACCESSCTL_SIP) { sts = proxy_response(&ticket); } else { INFO("non-authorized response received from %s", utils_inet_ntoa(ticket.from.sin_addr)); } /* * unsupported message */ } else { ERROR("received unsupported SIP type %s %s", (MSG_IS_REQUEST(ticket.sipmsg))? "REQ" : "RES", ticket.sipmsg->sip_method); } /********************************* * Done with proxying. Message * has been sent to its destination. *********************************/ /* * free the SIP message buffers */ end_loop: osip_message_free(ticket.sipmsg); } /* while TRUE */ exit_prg: /* save current known SIP registrations */ register_save(); INFO("properly terminating siproxd"); /* remove PID file */ if (pidfilename) { DEBUGC(DBCLASS_CONFIG,"deleting PID file [%s]", pidfilename); sts=unlink(pidfilename); if (sts != 0) { WARN("couldn't delete old PID file: %s", strerror(errno)); } } /* END */ return 0; } /* main */ /* * Signal handler * * this one is called asynchronously whevener a registered * signal is applied. Just set a flag and don't do any funny * things here. */ static void sighandler(int sig) { if (sig==SIGTERM) exit_program=1; if (sig==SIGINT) exit_program=1; if (sig==SIGUSR2) dmalloc_dump=1; return; }
int bindport(const char *hostname, int port) { unsigned long inetaddr =1; int shandle; int one = 1; struct sockaddr_in sin; unsigned long host_ip; memset((void*)&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; inetaddr = inet_addr(hostname); if (inetaddr == (unsigned long int) UINT_MAX) { /* see if HOSTNAME is an interface */ if (get_interface_ip(hostname, &sin) == 0) { /* found it */ /* sin.sin_addr is already set */ sin.sin_family = AF_INET; } else { /* HOSTNAME was probably a name since inet_addr * returned an error. * Look up the name to get the IP */ host_ip = hostent_get_ip(&hostcache, hostname); if (host_ip == (unsigned long int) UINT_MAX) { jlog(1, "Could not resolve %s: %s", hostname, strerror(errno)); perror("Could not resolve the hostname"); return -1; } sin.sin_addr.s_addr = host_ip; } } else { /* okay, HOSTNAME was a valid dot-notation IP */ sin.sin_addr.s_addr = inetaddr; } sin.sin_port = htons(port); /* become root again - use our function instead of plain * setuid() for the logging */ if (changeid(PRIV, UID, "Changing ID to root (socket(), bind())")) { return -1; } shandle = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (shandle < 0) { jlog(1, "Error creating socket to bind: %s", strerror(errno)); perror("Error creating socket to bind to"); return -1; } if (setsockopt(shandle, SOL_SOCKET, SO_REUSEADDR, (void*) &one, sizeof(one)) < 0) { jlog(3, "Error setting socket to SO_REUSEADDR"); } if (bind(shandle, (struct sockaddr *) &sin, sizeof(sin)) < 0) { jlog(1, "Error binding: %s", strerror(errno)); perror("Error binding"); return -1; } if (listen(shandle, 5) < 0) { jlog(1, "Error listening on the socket: %s", strerror(errno)); perror("Error listening on a bound socket"); return -1; } jlog(6, "Listening on %s, port %d, fd %d", hostname, port, shandle); if (changeid(UNPRIV, EUID, "Changing id back (socket(), bind())") < 0) { return -1; } return shandle; }