/** * Processes a notification and updates the registrar info. * @param n - the notification * @param expires - the Subscription-Status expires parameter * @returns 1 on success, 0 on error */ int r_notification_process(r_notification *n,int expires) { r_registration *r; r_regcontact *rc; r_contact *c; struct sip_uri puri; enum Reg_States reg_state; int expires2; r_subscription *s=0; r_notification_print(n); if (!n) return 0; r_act_time(); r = n->registration; while(r){ rc = r->contact; while(rc){ if (parse_uri(rc->uri.s,rc->uri.len,&puri)<0){ LOG(L_ERR,"ERR:"M_NAME":r_notification_process: Error parsing Contact URI <%.*s>\n", rc->uri.len,rc->uri.s); goto next; } // LOG(L_CRIT,"DBG:"M_NAME":r_notification_process: refreshing contacts <%.*s> [%d]\n",rc->uri.len,rc->uri.s,rc->expires); if (rc->state==IMS_REGINFO_TERMINATED){ reg_state = DEREGISTERED; expires2 = time_now+30; c = update_r_contact(puri.host,puri.port_no,puri.proto, 0,®_state,&expires2,0,0,0); if (c) { LOG(L_DBG,"DBG:"M_NAME":r_notification_process: expired contact <%.*s>\n", c->uri.len,c->uri.s); r_unlock(c->hash); } }else{ reg_state = REGISTERED; expires2 = rc->expires+time_now; c = update_r_contact(puri.host,puri.port_no,puri.proto, 0,®_state,&expires2,0,0,0); if (c) { LOG(L_DBG,"DBG:"M_NAME":r_notification_process: refreshing contact <%.*s> [%d]\n", c->uri.len,c->uri.s,rc->expires); r_unlock(c->hash); } } next: rc = rc->next; } s = get_r_subscription(r->aor); if (s){ update_r_subscription(s,expires); subs_unlock(s->hash); } r = r->next; } return 1; }
/** * Response callback for subscribe */ void r_subscribe_response(struct cell *t,int type,struct tmcb_params *ps) { str req_uri; int expires; r_subscription *s=0; LOG(L_DBG,"DBG:"M_NAME":r_subscribe_response: code %d\n",ps->code); if (!ps->rpl) { LOG(L_ERR,"INF:"M_NAME":r_subscribe_response: No reply\n"); return; } req_uri = *((str*) *(ps->param)); s = get_r_subscription(req_uri); if (!s){ LOG(L_ERR,"INF:"M_NAME":r_subscribe_response: received a SUBSCRIBE response but no subscription for <%.*s>\n", req_uri.len,req_uri.s); return; } if (ps->code>=200 && ps->code<300){ expires = cscf_get_expires_hdr(ps->rpl); update_r_subscription(s,expires); tmb.dlg_response_uac(s->dialog, ps->rpl, IS_TARGET_REFRESH); }else if (ps->code==404){ update_r_subscription(s,0); //tmb.dlg_response_uac(s->dialog, ps->rpl, IS_TARGET_REFRESH); }else{ LOG(L_INFO,"INF:"M_NAME":r_subscribe_response: SUBSCRIRE response code %d ignored\n",ps->code); } if (s) subs_unlock(s->hash); }
void print_subs(int log_level) { r_subscription *s; int i; #ifdef SER_MOD_INTERFACE if (!is_printable(log_level)) #else if (debug<log_level) #endif return; /* to avoid useless calls when nothing will be printed */ LOG(log_level,ANSI_GREEN"INF:"M_NAME":---------- Subscription list begin ---------\n"); for(i=0;i<subscriptions_hash_size;i++){ subs_lock(i); s = subscriptions[i].head; r_act_time(); while(s){ LOG(log_level,ANSI_GREEN"INF:"M_NAME":[%4u]\tP: <"ANSI_BLUE"%.*s"ANSI_GREEN"> D:["ANSI_CYAN"%5d"ANSI_GREEN"] E:["ANSI_MAGENTA"%5d"ANSI_GREEN"] Att:[%2d]\n", s->hash,s->req_uri.len,s->req_uri.s,s->duration,(int)(s->expires-time_now),s->attempts_left); s = s->next; } subs_unlock(i); } LOG(log_level,ANSI_GREEN"INF:"M_NAME":---------- Subscription list end -----------\n"); }
/** * Finds out if a subscription exists * @param aor - AOR to look for * @returns 1 if found, 0 if not */ int is_r_subscription(str aor) { r_subscription *s; unsigned int hash = get_subscription_hash(aor); subs_lock(hash); s = subscriptions[hash].head; while(s){ if (s->req_uri.len == aor.len && strncasecmp(s->req_uri.s,aor.s,aor.len)==0) { subs_unlock(hash); return 1; } s = s->next; } subs_unlock(hash); return 0; }
/** * The Subscription timer looks for almost expired subscriptions and subscribes again. * @param ticks - the current time * @param param - the generic parameter */ void subscription_timer(unsigned int ticks, void* param) { r_subscription *s,*ns; int i; #ifdef WITH_IMS_PM int subs_cnt=0; #endif for(i=0;i<subscriptions_hash_size;i++){ subs_lock(i); s = subscriptions[i].head; r_act_time(); while(s){ ns = s->next; if (s->attempts_left > 0 ){ /* attempt to send a subscribe */ if (!r_send_subscribe(s,s->duration)){ LOG(L_ERR,"ERR:"M_NAME":subscription_timer: Error on SUBSCRIBE (%d times)... droping\n", pcscf_subscribe_retries); del_r_subscription_nolock(s); }else{ s->attempts_left--; #ifdef WITH_IMS_PM subs_cnt++; #endif } }else if (s->attempts_left==0) { /* we failed to many times, drop the subscription */ LOG(L_ERR,"ERR:"M_NAME":subscription_timer: Error on SUBSCRIBE for %d times... aborting\n",pcscf_subscribe_retries); del_r_subscription_nolock(s); }else{ /* we are subscribed already */ /* if expired, drop it */ if (s->expires<time_now) del_r_subscription_nolock(s); #ifdef WITH_IMS_PM else subs_cnt++; #endif /* if not expired, check for renewal */ // Commented as the S-CSCF should adjust the subscription time accordingly // if ((s->duration<1200 && s->expires-time_now<s->duration/2)|| // (s->duration>=1200 && s->expires-time_now<600)) // { // /* if we need a resubscribe, we mark it as such and try to subscribe again */ // s->attempts_left = pcscf_subscribe_retries; // ns = s; // } } s = ns; } subs_unlock(i); } print_subs(L_INFO); #ifdef WITH_IMS_PM IMS_PM_LOG01(RD_NbrSubs,subs_cnt); #endif }
/** * Updates the expiration time of a subscription. * \todo Maybe we should use a hash here to index it as this is called for every notification * @param aor - aor to look for * @param expires - new expiration time * @returns 1 if found, 0 if not */ int update_r_subscription(r_subscription *s,int expires) { LOG(L_DBG,"DBG:"M_NAME":update_r_subscription: refreshing subscription for <%.*s> [%d]\n", s->req_uri.len,s->req_uri.s,expires); s->attempts_left = -1; if (expires == 0) del_r_subscription_nolock(s); else s->expires = expires+time_now;; subs_unlock(s->hash); return 1; }
/** * Deletes a subscription from the list of subscriptions * @param s - the subscription to be deleted */ void del_r_subscription(r_subscription *s) { if (!s) return; subs_lock(s->hash); if (subscriptions[s->hash].head == s) subscriptions[s->hash].head = s->next; else s->prev->next = s->next; if (subscriptions[s->hash].tail == s) subscriptions[s->hash].tail = s->prev; else s->next->prev = s->prev; subs_unlock(s->hash); free_r_subscription(s); }
/** * Adds a subscription to the list of subscriptions at the end (FIFO). * @param s - the subscription to be added */ void add_r_subscription(r_subscription *s) { if (!s) return; s->hash = get_subscription_hash(s->req_uri); subs_lock(s->hash); s->next = 0; s->prev = subscriptions[s->hash].tail; if (subscriptions[s->hash].tail) subscriptions[s->hash].tail->next = s; subscriptions[s->hash].tail = s; if (!subscriptions[s->hash].head) subscriptions[s->hash].head = s; subs_unlock(s->hash); }
void print_subs(int log_level) { r_subscription *s; int i; LOG(log_level,ANSI_GREEN"INF:"M_NAME":---------- Subscription list begin ---------\n"); for(i=0;i<subscriptions_hash_size;i++){ subs_lock(i); s = subscriptions[i].head; r_act_time(); while(s){ LOG(log_level,ANSI_GREEN"INF:"M_NAME":[%4u]\tP: <"ANSI_BLUE"%.*s"ANSI_GREEN"> D:["ANSI_CYAN"%5d"ANSI_GREEN"] E:["ANSI_MAGENTA"%5d"ANSI_GREEN"] Att:[%2d]\n", s->hash,s->req_uri.len,s->req_uri.s,s->duration,(int)(s->expires-time_now),s->attempts_left); s = s->next; } subs_unlock(i); } LOG(log_level,ANSI_GREEN"INF:"M_NAME":---------- Subscription list end -----------\n"); }
/** * Creates a subcription and starts the timer resubscription for the given contact. * @param uri - the contact to subscribe to (actually to its default public id) * @param duration - SUBCRIBE expires * @returns 1 on success, 0 on failure */ int r_subscribe(str uri,int duration) { r_subscription *s; /* first we try to update. if not found, add it */ s = get_r_subscription(uri); if (s){ s->duration = duration; s->attempts_left=pcscf_subscribe_retries; subs_unlock(s->hash); }else{ s = new_r_subscription(uri,duration); if (!s){ LOG(L_ERR,"ERR:"M_NAME":r_subscribe: Error creating new subscription\n"); return 0; } add_r_subscription(s); s->attempts_left=pcscf_subscribe_retries; } return 1; }
/** * Loads the subscriptions data from the last snapshot. * @returns 1 on success or 0 on failure */ int load_snapshot_subscriptions() { bin_data x; r_subscription *s; int k,max; FILE *f; switch (pcscf_persistency_mode){ case NO_PERSISTENCY: k=0; case WITH_FILES: f = bin_load_from_file_open(pcscf_persistency_location,"psubscriptions"); if (!f) return 0; bin_alloc(&x,128*1024); k=bin_load_from_file_read(f,&x); max = x.max; x.max=0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_subscriptions: max %d len %d\n",x.max,x.len); while(x.max<x.len){ s = bin_decode_r_subscription(&x); if (!s) return 0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_subscriptions: Loaded r_subscription for <%.*s>\n",s->req_uri.len,s->req_uri.s); subs_lock(s->hash); s->prev = subscriptions[s->hash].tail; s->next = 0; if (subscriptions[s->hash].tail) subscriptions[s->hash].tail->next = s; subscriptions[s->hash].tail = s; if (!subscriptions[s->hash].head) subscriptions[s->hash].head = s; subs_unlock(s->hash); memmove(x.s,x.s+x.max,x.len-x.max); x.len = x.max; x.max = max; k=bin_load_from_file_read(f,&x); max = x.max; x.max = 0; } bin_free(&x); bin_load_from_file_close(f); k = 1; break; case WITH_DATABASE_BULK: k=bin_load_from_db(&x, P_SUBSCRIPTIONS); x.max=0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_subscriptions: max %d len %d\n",x.max,x.len); while(x.max<x.len){ s = bin_decode_r_subscription(&x); if (!s) return 0; LOG(L_INFO,"INFO:"M_NAME":load_snapshot_subscriptions: Loaded r_subscription for <%.*s>\n",s->req_uri.len,s->req_uri.s); subs_lock(s->hash); s->prev = subscriptions[s->hash].tail; s->next = 0; if (subscriptions[s->hash].tail) subscriptions[s->hash].tail->next = s; subscriptions[s->hash].tail = s; if (!subscriptions[s->hash].head) subscriptions[s->hash].head = s; subs_unlock(s->hash); } bin_free(&x); break; case WITH_DATABASE_CACHE: k=bin_load_from_db(NULL, P_SUBSCRIPTIONS); //ignore x, x is empty break; default: LOG(L_ERR,"ERR:"M_NAME":load_snapshot_subscriptions: Can't resume because no such mode %d\n",pcscf_persistency_mode); k=0; } if (!k) goto error; return 1; error: return 0; }
/** * Creates a snapshots of the subscriptions and then calls the dumping function. * @returns 1 on success or 0 on failure */ int make_snapshot_subscriptions() { bin_data x={0,0,0}; r_subscription *s; int i,k; time_t unique = time(0); FILE *f; switch (pcscf_persistency_mode) { case NO_PERSISTENCY: return 0; case WITH_FILES: f = bin_dump_to_file_create(pcscf_persistency_location,"psubscriptions",unique); if (!f) return 0; for(i=0;i<subscriptions_hash_size;i++){ if (!bin_alloc(&x,1024)) goto error; subs_lock(i); s = subscriptions[i].head; if (s) { while(s){ if (!bin_encode_r_subscription(&x,s)) goto error; s = s->next; } subs_unlock(i); k = bind_dump_to_file_append(f,&x); if (k!=x.len) { LOG(L_ERR,"ERR:"M_NAME":make_snapshot_registrar: error while dumping to file - only wrote %d bytes of %d \n",k,x.len); subs_unlock(i); bin_free(&x); return 0; } } else subs_unlock(i); bin_free(&x); } return bind_dump_to_file_close(f,pcscf_persistency_location,"psubscriptions",unique); break; case WITH_DATABASE_BULK: if (!bin_alloc(&x,1024)) goto error; for(i=0;i<subscriptions_hash_size;i++){ subs_lock(i); s = subscriptions[i].head; while(s){ if (!bin_encode_r_subscription(&x,s)) goto error; s = s->next; } subs_unlock(i); } return bin_dump_to_db(&x, P_SUBSCRIPTIONS); case WITH_DATABASE_CACHE: return bin_dump_to_db(NULL, P_SUBSCRIPTIONS); //ignore x, x is empty default: LOG(L_ERR,"ERR:"M_NAME":make_snapshot_registrar: Snapshot done but no such mode %d\n",pcscf_persistency_mode); return 0; } error: if (x.s) bin_free(&x); return 0; }