int process_contact(udomain_t * _d, int expires, str contact_uri, int contact_state) { char bufport[5], *rest, *sep, *val, *port, *trans; pcontact_t* pcontact; struct pcontact_info ci; struct sip_uri puri, uri; unsigned int received_proto, received_port_len; int local_time_now, rest_len, val_len, has_alias; int ret = RESULT_CONTACTS_FOUND; pcscf_act_time(); local_time_now = time_now; has_alias = 0; //get contact //if does not exist then add it //if it does exist check if state it terminated - if so delete it, if not update it memset(&ci, 0, sizeof (struct pcontact_info)); ci.num_public_ids = 0; ci.num_service_routes = 0; LM_DBG("Processing contact using contact from NOTIFY [%.*s]\n", contact_uri.len, contact_uri.s); if (parse_uri(contact_uri.s, contact_uri.len, &puri) < 0) { LM_DBG("Error parsing Contact URI <%.*s>\n", contact_uri.len, contact_uri.s); return RESULT_ERROR; } expires = local_time_now + expires; //turn expires into correct time since epoch format LM_DBG("Changed expires to format time since the epoch: %d", expires); ci.expires = expires; ci.reg_state = PCONTACT_REGISTERED; ul.lock_udomain(_d, &puri.host, puri.port_no, puri.proto); ci.aor = contact_uri; ci.via_host = puri.host; ci.via_prot = puri.proto; ci.via_port = puri.port_no; /* parse the uri in the NOTIFY */ if (parse_uri(contact_uri.s, contact_uri.len, &uri) != 0) { LM_ERR("Unable to parse contact in SIP notify [%.*s]\n", contact_uri.len, contact_uri.s); return RESULT_ERROR; } /*check for alias - NAT */ rest = uri.sip_params.s; rest_len = uri.sip_params.len; while (rest_len >= ALIAS_LEN) { if (strncmp(rest, ALIAS, ALIAS_LEN) == 0) { has_alias = 1; break; } sep = memchr(rest, 59 /* ; */, rest_len); if (sep == NULL) { LM_DBG("no alias param\n"); break; } else { rest_len = rest_len - (sep - rest + 1); rest = sep + 1; } } if (has_alias) { val = rest + ALIAS_LEN; val_len = rest_len - ALIAS_LEN; port = memchr(val, 126 /* ~ */, val_len); if (port == NULL) { LM_ERR("no '~' in alias param value\n"); return RESULT_ERROR; } port++; // received_port = atoi(port); trans = memchr(port, 126 /* ~ */, val_len - (port - val)); if (trans == NULL) { LM_ERR("no second '~' in alias param value\n"); return RESULT_ERROR; } received_port_len = trans - port; trans = trans + 1; received_proto = *trans - 48 /* char 0 */; memcpy(bufport, port, received_port_len); bufport[received_port_len]=0; ci.received_host.s = val; ci.received_host.len = port - val - 1; LM_DBG("Setting received host in search to [%.*s]\n", ci.received_host.len, ci.received_host.s); ci.received_port = atoi(bufport); LM_DBG("Setting received port in search to %d\n", ci.received_port); ci.received_proto = received_proto; LM_DBG("Setting received proto in search to %d\n", ci.received_proto); ci.searchflag = SEARCH_RECEIVED; } else { LM_DBG("Contact in NOTIFY does not have an alias....\n"); } if (ul.get_pcontact(_d, &ci, &pcontact) != 0) { //contact does not exist if (contact_state == STATE_TERMINATED) { LM_DBG("This contact: <%.*s> is in state terminated and is not in usrloc, ignore\n", contact_uri.len, contact_uri.s); ret = RESULT_CONTACTS_FOUND; goto done; } //LM_WARN("This contact: <%.*s> is in state active and is not in usrloc - must be another contact on a different P so going to ignore\n", contact_uri.len, contact_uri.s); LM_DBG("This contact: <%.*s> is in state active and is not in usrloc so adding it to usrloc, expires: %d which is in %d seconds\n", contact_uri.len, contact_uri.s, expires, expires-local_time_now); if (ul.insert_pcontact(_d, &contact_uri, &ci, &pcontact) != 0) { LM_ERR("Failed inserting new pcontact\n"); ret = RESULT_ERROR; goto done; } else { //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change LM_DBG("registering for UL callback\n"); ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); } } else {//contact exists if (contact_state == STATE_TERMINATED) { //delete contact LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); if (ul.delete_pcontact(_d, pcontact) != 0) { LM_DBG("failed to delete pcscf contact <%.*s> - not a problem this may have been removed by de registration", contact_uri.len, contact_uri.s); } /*TODO_LATEST - put this back */ } else {//state is active //update this contact LM_DBG("This contact: <%.*s> is in state active and is in usrloc so just updating - old expires: %li, new expires: %i which is in %i seconds\n", contact_uri.len, contact_uri.s, pcontact->expires, expires, expires - local_time_now); if (ul.update_pcontact(_d, &ci, pcontact) != 0) { LM_ERR("failed to update pcscf contact\n"); ret = RESULT_ERROR; goto done; } pcontact->expires = expires; } } done: ul.unlock_udomain(_d, &puri.host, puri.port_no, puri.proto); return ret; }
/** * Updates the registrar with the new values * @param req - the REGISTER request - to extract NAT info * @param rpl - the REGISTER reply - to extract contact info * @param is_star - whether this was a STAR contact header * @param expires_hdr - value of the Expires header * @param public_id - array of public identities attached to this contact * @param public_id_cnt - size of the public_id array * @param service_route - array of Service-Routes * @param service_route_cnt - size of the service_route array * @param requires_nat - if to create pinholes * @returns the maximum expiration time, -1 on error */ static inline int update_contacts(struct sip_msg *req,struct sip_msg *rpl, udomain_t* _d, unsigned char is_star,int expires_hdr, str *public_id,int public_id_cnt,str *service_route,int service_route_cnt, int requires_nat) { int local_time_now, expires=0; struct hdr_field* h; contact_t* c; struct sip_uri puri; struct sip_uri parsed_received; struct pcontact_info ci; pcontact_t* pcontact; char srcip[50]; int_str val; pcscf_act_time(); local_time_now = time_now; if (is_star) { /* first of all, we shouldn't get here... * then, we will update on NOTIFY */ return 0; } // Set the structure to "0", to make sure it's properly initialized memset(&ci, 0, sizeof(struct pcontact_info)); for (h = rpl->contact; h; h = h->next) { if (h->type == HDR_CONTACT_T && h->parsed) for (c = ((contact_body_t*) h->parsed)->contacts; c; c = c->next) { expires = calc_contact_expires(c, expires_hdr, local_time_now); if (parse_uri(c->uri.s, c->uri.len, &puri) < 0) { LM_DBG("Error parsing Contact URI <%.*s>\n", c->uri.len, c->uri.s); continue; } //build contact info ci.expires = expires; ci.public_ids = public_id; ci.num_public_ids = public_id_cnt; ci.service_routes = service_route; ci.num_service_routes = service_route_cnt; ci.reg_state = PCONTACT_REGISTERED; ci.received_host.len = 0; ci.received_host.s = 0; ci.received_port = 0; ci.received_proto = 0; // Received Info: First try AVP, otherwise simply take the source of the request: memset(&val, 0, sizeof(int_str)); if (rcv_avp_name.n!=0 && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0) && val.s.len > 0) { if (val.s.len>RECEIVED_MAX_SIZE) { LM_ERR("received too long\n"); goto error; } if (parse_uri(val.s.s, val.s.len, &parsed_received) < 0) { LM_DBG("Error parsing Received URI <%.*s>\n", val.s.len, val.s.s); continue; } ci.received_host = parsed_received.host; ci.received_port = parsed_received.port_no; ci.received_proto = parsed_received.proto; } else { ci.received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, sizeof(srcip)); ci.received_host.s = srcip; ci.received_port = req->rcv.src_port; ci.received_proto = req->rcv.proto; } // Set to default, if not set: if (ci.received_port == 0) ci.received_port = 5060; ul.lock_udomain(_d, &c->uri); if (ul.get_pcontact(_d, &c->uri, &pcontact) != 0) { //need to insert new contact if ((expires-local_time_now)<=0) { //remove contact - de-register LM_DBG("This is a de-registration for contact <%.*s> but contact is not in usrloc - ignore\n", c->uri.len, c->uri.s); goto next_contact; } LM_DBG("Adding pcontact: <%.*s>, expires: %d which is in %d seconds\n", c->uri.len, c->uri.s, expires, expires-local_time_now); if (ul.insert_pcontact(_d, &c->uri, &ci, &pcontact) != 0) { LM_ERR("Failed inserting new pcontact\n"); } else { //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change LM_DBG("registering for UL callback\n"); ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); //we also need to subscribe to reg event of this contact at SCSCF } } else { //contact already exists - update LM_DBG("contact already exists and is in state (%d) : [%s]\n",pcontact->reg_state, reg_state_to_string(pcontact->reg_state)); if ((expires-local_time_now)<=0) { //remove contact - de-register LM_DBG("This is a de-registration for contact <%.*s>\n", c->uri.len, c->uri.s); if (ul.delete_pcontact(_d, &c->uri, pcontact) != 0) { LM_ERR("failed to delete pcscf contact <%.*s>\n", c->uri.len, c->uri.s); } } else { //update contact LM_DBG("Updating contact: <%.*s>, old expires: %li, new expires: %i which is in %i seconds\n", c->uri.len, c->uri.s, pcontact->expires-local_time_now, expires, expires-local_time_now); if (ul.update_pcontact(_d, &ci, pcontact) != 0) { LM_ERR("failed to update pcscf contact\n"); } pcontact->expires = expires; } } next_contact: ul.unlock_udomain(_d, &c->uri); } } return 1; error: return 0; }
/** * Save contact based on REGISTER request. this will be a pending save, until we receive response * from SCSCF. If no response after pending_timeout seconds, the contacts is removed. Can only be used from REQUEST ROUTE */ int save_pending(struct sip_msg* _m, udomain_t* _d) { contact_body_t* cb = 0; int cexpires = 0; pcontact_t* pcontact; contact_t* c; struct pcontact_info ci; struct via_body* vb; unsigned short port, proto; int_str val; struct sip_uri parsed_received; char srcip[50]; memset(&ci, 0, sizeof(struct pcontact_info)); vb = cscf_get_ue_via(_m); port = vb->port?vb->port:5060; proto = vb->proto; cb = cscf_parse_contacts(_m); if (!cb || (!cb->contacts)) { LM_ERR("No contact headers\n"); goto error; } c = cb->contacts; //TODO: need support for multiple contacts - currently assume one contact //make sure this is not a de-registration int expires_hdr = cscf_get_expires_hdr(_m, 0); if (expires_hdr < 0) { //no global header we have to check the contact expiry if (c && c->expires && c->expires->body.len) { str2int(&(c->expires->body), (unsigned int*) &cexpires); } if (!cexpires){ //assume de-registration LM_DBG("not doing pending reg on de-registration\n"); return 1; } } pcscf_act_time(); int local_time_now = time_now; int expires = calc_contact_expires(c, expires_hdr, local_time_now); if (expires <= 0) { LM_DBG("not doing pending reg on de-registration\n"); return 1; } LM_DBG("Save Pending"); LM_DBG("contact requesting to expire in %d seconds\n", expires-local_time_now); /*populate CI with bare minimum*/ ci.via_host = vb->host; ci.via_port = port; ci.via_prot = proto; ci.aor = c->uri; ci.num_public_ids=0; ci.num_service_routes=0; ci.expires=local_time_now + pending_reg_expires; ci.reg_state=PCONTACT_ANY; ci.searchflag=SEARCH_RECEIVED; //we want to make sure we are very specific with this search to make sure we get the correct contact to put into reg_pending. // Received Info: First try AVP, otherwise simply take the source of the request: memset(&val, 0, sizeof(int_str)); if (rcv_avp_name.n != 0 && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0) && val.s.len > 0) { if (val.s.len > RECEIVED_MAX_SIZE) { LM_ERR("received too long\n"); goto error; } if (parse_uri(val.s.s, val.s.len, &parsed_received) < 0) { LM_DBG("Error parsing Received URI <%.*s>\n", val.s.len, val.s.s); goto error; } ci.received_host = parsed_received.host; ci.received_port = parsed_received.port_no; ci.received_proto = parsed_received.proto; } else { ci.received_host.len = ip_addr2sbuf(&_m->rcv.src_ip, srcip, sizeof(srcip)); ci.received_host.s = srcip; ci.received_port = _m->rcv.src_port; ci.received_proto = _m->rcv.proto; } // Set to default, if not set: if (ci.received_port == 0) ci.received_port = 5060; ul.lock_udomain(_d, &ci.via_host, ci.via_port, ci.via_prot); if (ul.get_pcontact(_d, &ci, &pcontact) != 0) { //need to insert new contact LM_DBG("Adding pending pcontact: <%.*s>\n", c->uri.len, c->uri.s); ci.reg_state=PCONTACT_REG_PENDING; if (ul.insert_pcontact(_d, &c->uri, &ci, &pcontact) != 0) { LM_ERR("Failed inserting new pcontact\n"); } else { LM_DBG("registering for UL callback\n"); ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE | PCSCF_CONTACT_UPDATE, callback_pcscf_contact_cb, NULL); } } else { //contact already exists - update LM_DBG("Contact already exists - not doing anything for now\n"); } ul.unlock_udomain(_d, &ci.via_host, ci.via_port, ci.via_prot); return 1; error: LM_DBG("Error saving pending contact\n"); return -1; }
int process_contact(udomain_t * _d, int expires, str contact_uri, int contact_state) { pcontact_t* pcontact; struct pcontact_info ci; int local_time_now; int ret = RESULT_CONTACTS_FOUND; pcscf_act_time(); local_time_now = time_now; //get contact //if does not exist then add it //if it does exist check if state it terminated - if so delete it, if not update it memset(&ci, 0, sizeof(struct pcontact_info)); ci.num_public_ids=0; ci.num_service_routes=0; expires = local_time_now + expires; //turn expires into correct time since epoch format LM_DBG("Changed expires to format time since the epoch: %d", expires); ci.expires=expires; ci.reg_state = PCONTACT_REGISTERED; ul.lock_udomain(_d, &contact_uri); if (ul.get_pcontact(_d, &contact_uri, &pcontact) != 0) { //contact does not exist if (contact_state == STATE_TERMINATED) { LM_DBG("This contact: <%.*s> is in state terminated and is not in usrloc, ignore\n", contact_uri.len, contact_uri.s); ret = RESULT_CONTACTS_FOUND; goto done; } LM_DBG("This contact: <%.*s> is in state active and is not in usrloc so adding it to usrloc, expires: %d which is in %d seconds\n", contact_uri.len, contact_uri.s, expires, expires-local_time_now); if (ul.insert_pcontact(_d, &contact_uri, &ci, &pcontact) != 0) { LM_ERR("Failed inserting new pcontact\n"); ret = RESULT_ERROR; goto done; } else { //register for callbacks on this contact so we can send PUBLISH to SCSCF should status change LM_DBG("registering for UL callback\n"); ul.register_ulcb(pcontact, PCSCF_CONTACT_DELETE | PCSCF_CONTACT_EXPIRE, callback_pcscf_contact_cb, NULL); } } else {//contact exists if (contact_state == STATE_TERMINATED) { //delete contact LM_DBG("This contact <%.*s> is in state terminated and is in usrloc so removing it from usrloc\n", contact_uri.len, contact_uri.s); if (ul.delete_pcontact(_d, &contact_uri, pcontact) != 0) { LM_DBG("failed to delete pcscf contact <%.*s> - not a problem this may have been removed by de registration", contact_uri.len, contact_uri.s); } } else {//state is active //update this contact LM_DBG("This contact: <%.*s> is in state active and is in usrloc so just updating - old expires: %li, new expires: %i which is in %i seconds\n", contact_uri.len, contact_uri.s, pcontact->expires, expires, expires-local_time_now); if (ul.update_pcontact(_d, &ci, pcontact) != 0) { LM_ERR("failed to update pcscf contact\n"); ret = RESULT_ERROR; goto done; } pcontact->expires = expires; } } done: ul.unlock_udomain(_d, &contact_uri); return ret; }