/* * Extract the data from b= lines and add it to the structure * @param bw - the pointer to the bandwidth structure to fill * @param sdp - the sdp body where to look for b= lines * @param start - the pointer to the starting point to look at * returns 1 on success 0 if no b= line was found */ int extract_bandwidth(bandwidth *bw,str sdp,char *start) { char *b; char *m; /*next mline*/ bw->bAS=0; bw->bRS=0; bw->bRR=0; b=find_next_sdp_line(start,(sdp.s+sdp.len),'b',NULL); while (b!=NULL) { m=find_next_sdp_line(b,(sdp.s+sdp.len),'m',NULL); if (m!=NULL && b>m) { /*this bandwidth belongs to some other media!*/ return 0; } b+=2; /*skip b and =*/ while (*b==' ') { b++; } if (*b=='A' && *(b+1)=='S') { sscanf(b,"AS:%i%*s",&bw->bAS); } else if( *b=='R') { if (*(b+1)=='S') { sscanf(b,"RS:%i%*s",&bw->bRS); } else if (*(b+1)=='R') { sscanf(b,"RR:%i%*s",&bw->bRR); } } /*find next b line*/ b=find_next_sdp_line(b,(sdp.s+sdp.len),'b',NULL); } return 1; }
/** * add the sdp component from a request or a reply, respectively an offer or an answer * @param msg - the SIP message * @param sdp_type - 0 for SDP offer and 1 for SDP answer * @param sip_uri - the SIP uri of the one that sent the SDP body * @param sdp_media_comps - the built structure * @returns 1 if ok, 0 if an error occured */ int append_sip_sdp_comp(struct sip_msg * msg, int sdp_type, str sip_uri, str user_sip_uri, sdp_media_component_list_t * sdp_media_comps){ sdp_media_component_t * sdp_media_elem = NULL; str sdp_body = {0,0}; str mline_s = {0,0}, next_mline_s= {0,0}; char * end, *media_info_end; str copy_sdp_body = {0,0}; LOG(L_DBG, "checking if the message has sdp body\n"); /*if the SIP message contains SDP body*/ if(extract_sdp_body(msg, ©_sdp_body)==-1) { return 1; } str_dup(sdp_body, copy_sdp_body, pkg); end = sdp_body.s + sdp_body.len; mline_s.s = find_sdp_line(sdp_body.s,end,'m'); for(;mline_s.s;mline_s.s = next_mline_s.s){ mline_s.len = get_line_length(mline_s.s, end); next_mline_s.s = find_next_sdp_line(mline_s.s,end,'m',NULL); mem_new(sdp_media_elem, sizeof(sdp_media_component_t), pkg); str_dup_ptr_ptr(sdp_media_elem->sdp_media_name, &mline_s, pkg); media_info_end = end; mem_new(sdp_media_elem->sdp_type, sizeof(int32_t), pkg); *(sdp_media_elem->sdp_type) = sdp_type; if(!get_media_description_list(mline_s.s+mline_s.len, end, &(sdp_media_elem->sdp_media_descriptions))) goto error; /*if(str_equal(user_sip_uri, sip_uri)) add_an_charging_id(sip_uri, sdp_media_elem);*/ WL_APPEND(sdp_media_comps, sdp_media_elem); } str_free(sdp_body,pkg); return 1; out_of_memory: LOG(L_ERR, "append_sip_sdp_comp: out of pkg memory\n"); error: WL_FREE(sdp_media_elem, sdp_media_component_list_t, pkg); return 0; }
int get_all_media_lines(char * start, char * end, char line_name, str_list_t * sdp_media_descriptions){ str sdp_par = {0,0}; str_list_slot_t * str_slot = NULL; while((sdp_par.s = find_next_sdp_line(start, end,line_name,NULL))){ sdp_par.len = get_line_length(sdp_par.s, end); mem_new(str_slot, sizeof(str_list_slot_t), pkg); str_dup(str_slot->data,sdp_par,pkg); WL_APPEND(sdp_media_descriptions,str_slot); str_slot=0; start = sdp_par.s + sdp_par.len; } return 1; out_of_memory: LOG(L_ERR, "ERR:"M_NAME":get_media_description_list: out of pkg memory\n"); if(str_slot) WL_FREE(str_slot, str_list_t,pkg); return 0; }
int extract_mclines(str sdpA,str sdpB,char **mlineA,char **clineA,char **mlineB,char **clineB,int number) { char *nclineA,*nclineB; /*next*/ char *sclineA=NULL,*sclineB=NULL; /*session*/ char *nmlineA,*nmlineB; /*next*/ int i; *clineA=find_sdp_line(sdpA.s,(sdpA.s+sdpA.len),'c'); *clineB=find_sdp_line(sdpB.s,(sdpB.s+sdpB.len),'c'); *mlineA=find_sdp_line(sdpA.s,(sdpA.s+sdpA.len),'m'); *mlineB=find_sdp_line(sdpB.s,(sdpB.s+sdpB.len),'m'); if (*clineA==NULL || *clineB==NULL || *mlineA==NULL || *mlineB==NULL) { /*missing at least one cline and mline in each SDPbody*/ LOG(L_ERR, ANSI_RED"ERR:"M_NAME" Malformed SDP body\n"); return 0; } nclineA=find_next_sdp_line(*clineA,(sdpA.s+sdpA.len),'c',NULL); nclineB=find_next_sdp_line(*clineB,(sdpB.s+sdpB.len),'c',NULL); nmlineA=find_next_sdp_line(*mlineA,(sdpA.s+sdpA.len),'m',NULL); nmlineB=find_next_sdp_line(*mlineB,(sdpB.s+sdpB.len),'m',NULL); if (*clineA < *mlineA) { sclineA=*clineA; } if (*clineB < *mlineB) { sclineB=*clineB; } if (number > 1) { for (i=1;i<number;i++) { *mlineA=nmlineA; *mlineB=nmlineB; nmlineA=find_next_sdp_line(*mlineA,(sdpA.s+sdpA.len),'m',NULL); nmlineB=find_next_sdp_line(*mlineB,(sdpB.s+sdpB.len),'m',NULL); if(nclineA >*mlineA && (nclineA < nmlineA || nmlineA== NULL)) { // if there is a c line between two m lines or after the last m line // then this c line belongs to the first one *clineA=nclineA; nclineA=find_next_sdp_line(*clineA,(sdpA.s+sdpA.len),'c',NULL); } else { // if not then the session description one is the one *clineA=sclineA; } if(nclineB >*mlineB && (nclineB < nmlineB || nmlineB== NULL)) { // if there is a c line between two m lines or after the last m line // then this c line belongs to the first one *clineB=nclineB; nclineB=find_next_sdp_line(*clineB,(sdpB.s+sdpB.len),'c',NULL); } else { // if not then the session description one is the one *clineB=sclineB; } if (*mlineA == NULL || *mlineB == NULL || *clineA == NULL || *clineB == NULL) { LOG(L_ERR,"ERR:"M_NAME":%s: Failed getting m= and c= lines in SDP\n","extract_mclines"); return 0; } } // after this we should have mlineA , mlineB , clineA, clineB // with the right values } return 1; }
AAA_AVP* Gq_create_codec_data(str sdp,int number,int direction) { char data[Gq_MAX_Char4]; char *p,*q,*r; int i=1,l; switch(direction) { case 0: sprintf(data,"uplink\noffer\n"); break; case 1: sprintf(data,"uplink\nanswer\n"); break; case 2: sprintf(data,"downlink\noffer\n"); break; case 3: sprintf(data,"downlink\nanswer\n"); break; default: break; } l=strlen(data); p=find_sdp_line(sdp.s,(sdp.s+sdp.len),'m'); while (p!= NULL && i<number) { p=find_next_sdp_line(p,(sdp.s+sdp.len),'m',NULL); i++; } if (p==NULL) { return NULL; } q=find_next_sdp_line(p,(sdp.s+sdp.len),'m',(sdp.s+sdp.len)); r=index(p,'\n'); memcpy(data+l,p,r-p+1); l+=r-p+1; /*the m= line is always copied*/ p=r+1; /* p is always the start of the line*/ r=index(r+1,'\n'); /*r always the \n char of that line*/ while(p+2<q && (l+r-p+1)<Gq_MAX_Char4) { /*what about spaces? think it closely*/ if ((strstr(p,"a=sendonly")!=NULL && strstr(p,"a=sendonly")<r) || (strstr(p,"a=recvonly")!=NULL && strstr(p,"a=recvonly")<r) || (strstr(p,"a=sendrecv")!=NULL && strstr(p,"a=sendrecv")<r) || strncmp(p,"b=RS",4)==0 || strncmp(p,"b=RR",4)==0 || strncmp(p,"b=AS",4)==0) { p=r+1; /*skip this line*/ r=index(r+1,'\n'); } else { memcpy(data+l,p,r-p+1); l+=r-p+1; p=r+1; r=index(r+1,'\n'); } } data[l-2]='\0'; l=strlen(data); return (cdpb.AAACreateAVP(AVP_IMS_Codec_Data, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,data,l, AVP_DUPLICATE_DATA)); }
inline int Gq_add_media_component_description(AAAMessage *msg,str sdpinvite,str sdp200,char *mline,int number,int tag) { str data; AAA_AVP_LIST list; AAA_AVP *media_component_number,*media_type; AAA_AVP *codec_data1,*codec_data2; AAA_AVP *media_sub_component[Gq_Media_Sub_Components]; AAA_AVP *Max_DL,*Max_UL; AAA_AVP *RR,*RS; AAA_AVP *flow_status; bandwidth bwUL,bwDL; char *ptr; char port[Gq_MAX_Char]; int type,i,n,a; char x[4]; /*needed to use AAA_AVP-LIST!!*/ list.head=0; list.tail=0; /*media-component-number*/ set_4bytes(x,number); media_component_number=cdpb.AAACreateAVP(AVP_IMS_Media_Component_Number, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); if (media_component_number!=NULL) { AAAAddAVPToAVPList(&list,media_component_number); } else { LOG(L_INFO, ANSI_RED"INF:"M_NAME"Unable to create media_component_number AVP"); return 0; } /*media-sub-component*/ n=Gq_create_add_media_subcomponents(&list,sdpinvite,sdp200,number,media_sub_component,tag); if(n==-1) { LOG(L_INFO, ANSI_RED"INF:"M_NAME"Unable to create media_sub_components list AVP"); cdpb.AAAFreeAVP(&media_component_number); list.head=0; list.tail=0; return 0; } if (n==0) { /*we don't create a Media Description for this media line*/ /*because answerer rejected the offer!*/ cdpb.AAAFreeAVP(&media_component_number); list.head=0; list.tail=0; return 1; } /*if n=-1 then its error*/ /*if n=0 is because answerer rejected this media offer*/ /*or offerer wanted it to be disabled*/ /*media-type*/ ptr=mline; ptr+=2; /*skip m=*/ if (strncmp(ptr,"audio",5)==0) { type=AVP_IMS_Media_Type_Audio; } else if(strncmp(ptr,"video",5)==0) { type=AVP_IMS_Media_Type_Video; }else if(strncmp(ptr,"data",4)==0) { type=AVP_IMS_Media_Type_Data; }else if(strncmp(ptr,"application",11)==0) { type=AVP_IMS_Media_Type_Application; }else if(strncmp(ptr,"control",7)==0) { type=AVP_IMS_Media_Type_Control; }else if(strncmp(ptr,"text",4)==0) { type=AVP_IMS_Media_Type_Text; }else if(strncmp(ptr,"message",7)==0) { type=AVP_IMS_Media_Type_Message; } else { type=AVP_IMS_Media_Type_Other; } set_4bytes(x,type); media_type=cdpb.AAACreateAVP(AVP_IMS_Media_Type, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,media_type); /*Max-Requested-Bandwidth-UL*/ /*Max-Requested-Bandwidth-DL*/ /*SDP bodies have been check by gq_create_add_media_subcomponents*/ i=1; ptr=find_sdp_line(sdp200.s,(sdp200.s+sdp200.len),'m'); while(i!=number) { ptr=find_next_sdp_line(ptr,(sdp200.s+sdp200.len),'m',NULL); i++; } if(tag==1) { /*in the invite its defined how much bandwidth * you want in the downlink!*/ extract_bandwidth(&bwDL,sdpinvite,mline); extract_bandwidth(&bwUL,sdp200,ptr); } else { extract_bandwidth(&bwDL,sdp200,ptr); extract_bandwidth(&bwUL,sdpinvite,mline); } /* * IN a SDP b=AS:x line the x is the amount of bandwidth * the sender of the SDP body is willing to RECEIVE * therefor the next code is right * * */ if (bwDL.bAS!=0) { set_4bytes(x,bwDL.bAS); Max_UL=cdpb.AAACreateAVP(AVP_IMS_Max_Requested_Bandwidth_UL, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,Max_UL); } if (bwUL.bAS!=0) { set_4bytes(x,bwUL.bAS); Max_DL=cdpb.AAACreateAVP(AVP_IMS_Max_Requested_Bandwidth_DL, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,Max_DL); } /*Flow-Status*/ /*lets follow the specs*/ if (tag==0) { extract_token(mline,port,Gq_MAX_Char,2); } else { extract_token(ptr,port,Gq_MAX_Char,2); } if(strncmp(port,"0",1)==0) { set_4bytes(x,AVP_IMS_Flow_Status_Removed); } else { if (tag==1) { a=check_atributes(sdp200,ptr); } else { a=check_atributes(sdpinvite,mline); } if (a==1) { set_4bytes(x,AVP_IMS_Flow_Status_Enabled_Uplink); } else if(a==2) { set_4bytes(x,AVP_IMS_Flow_Status_Enabled_Downlink); } else { set_4bytes(x,AVP_IMS_Flow_Status_Enabled); } } flow_status=cdpb.AAACreateAVP(AVP_IMS_Flow_Status, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,flow_status); /*RR and RS*/ x[0]=0; x[1]=0; x[2]=0; x[3]=0; if(bwUL.bRR!=0) { set_4bytes(x,bwUL.bRR); } /*else if (bwDL.bRS!=0) { set_4bytes(x,bwDL.bRS); }*/ RR=0; if (x[0]!=0 || x[1]!=0 || x[2]!=0 || x[3]!=0) { RR=cdpb.AAACreateAVP(AVP_IMS_RR_Bandwidth, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,RR); } x[0]=0; x[1]=0; x[2]=0; x[3]=0; if(bwUL.bRS!=0) { set_4bytes(x,bwUL.bRS); } /*else if(bwDL.bRR!=0){ set_4bytes(x,bwDL.bRR); }*/ RS=0; if (x[0]!=0 || x[1]!=0 || x[2]!=0 || x[3]!=0) { RS=cdpb.AAACreateAVP(AVP_IMS_RS_Bandwidth, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP,x,4, AVP_DUPLICATE_DATA); AAAAddAVPToAVPList(&list,RS); } /*codec-data*/ if (tag==0) { /*0 means uplink offer*/ codec_data1=Gq_create_codec_data(sdpinvite,number,0); AAAAddAVPToAVPList(&list,codec_data1); /*3 means downlink answer*/ codec_data2=Gq_create_codec_data(sdp200,number,3); AAAAddAVPToAVPList(&list,codec_data2); } else { /*2 means downlink offer*/ codec_data1=Gq_create_codec_data(sdpinvite,number,2); AAAAddAVPToAVPList(&list,codec_data1); /*1 means uplink answer*/ codec_data2=Gq_create_codec_data(sdp200,number,1); AAAAddAVPToAVPList(&list,codec_data2); } /*now group them in one big AVP and free them*/ data=cdpb.AAAGroupAVPS(list); Gq_add_avp(msg,data.s,data.len,AVP_IMS_Media_Component_Description, AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC, IMS_vendor_id_3GPP, AVP_DUPLICATE_DATA, __FUNCTION__); cdpb.AAAFreeAVP(&media_component_number); for(i=0;i<n;i++) { cdpb.AAAFreeAVP(&media_sub_component[i]); } if (bwUL.bAS!=0) { cdpb.AAAFreeAVP(&Max_UL); } if (bwDL.bAS!=0) { cdpb.AAAFreeAVP(&Max_DL); } cdpb.AAAFreeAVP(&flow_status); if (RS!=0) { cdpb.AAAFreeAVP(&RS); } if (RR!=0) { cdpb.AAAFreeAVP(&RR); } cdpb.AAAFreeAVP(&media_type); cdpb.AAAFreeAVP(&codec_data1); cdpb.AAAFreeAVP(&codec_data2); list.tail=0; list.head=0; return 1; }
/* Check for sendonly or recvonly modifiers in a= lines * @param sdpbody - SDP body * @param mline - pointer to beginning of m= line * returns 0 if no modifier was found, * -1 on error (malformed SDP body) * 1 if sendonly , 2 if recvonly * */ int check_atributes(str sdpbody,char *mline) { char *p,*q; int s=0; p=find_sdp_line(sdpbody.s,sdpbody.s+sdpbody.len,'a'); q=find_sdp_line(sdpbody.s,sdpbody.s+sdpbody.len,'m'); if (p==NULL) { return 0; } /*see if there is a sessionwide a= line*/ if (p<q) { p+=2; /*skip a=*/ while(*p==' ') { p++; } if(strncmp(p,"sendonly",8)==0) { s=1; } else if(strncmp(p,"recvonly",8)==0) { s=2; } } /*see if there is a mediawide a= line*/ p=find_sdp_line(mline,(sdpbody.s+sdpbody.len),'a'); if (p==NULL) { return s; } mline++; q=find_next_sdp_line(mline,(sdpbody.s+sdpbody.len),'m',NULL); if (q!=NULL && p>q) { return s; /*the a= line found belongs to some other m= line*/ } p+=2; /*skip a=*/ while(*p==' ') { p++; } if(strncmp(p,"sendonly",8)==0) { return 1; } else if(strncmp(p,"recvonly",8)==0) { return 2; } else { return s; } }
/** * @brief remove all SDP lines that begin with prefix * @return -1 - error; 0 - no lines found ; 1..N - N lines deleted */ int sdp_remove_line_by_prefix(sip_msg_t* msg, str* prefix) { str body = {NULL, 0}; if(parse_sdp(msg) < 0) { LM_ERR("Unable to parse SDP\n"); return -1; } if(msg->body == NULL) { LM_DBG("No SDP body\n"); return -1; } body.s = ((sdp_info_t*)msg->body)->raw_sdp.s; body.len = ((sdp_info_t*)msg->body)->raw_sdp.len; if (body.s==NULL) { LM_ERR("failed to get the message body\n"); return -1; } body.len = msg->len - (body.s - msg->buf); if (body.len==0) { LM_DBG("message body has zero length\n"); return -1; } char *ptr = NULL; str line = {NULL, 0}; str remove = {NULL, 0}; int found = 0; struct lump *anchor = NULL; ptr = find_sdp_line(body.s, body.s + body.len, prefix->s[0]); while (ptr) { if (sdp_locate_line(msg, ptr, &line) != 0) { LM_ERR("sdp_locate_line() failed\n"); return -1; } if (body.s + body.len < line.s + prefix->len) // check if strncmp would run too far { //LM_DBG("done searching, prefix string >%.*s< (%d) does not fit into remaining buffer space (%ld) \n", prefix->len, prefix->s, prefix->len, body.s + body.len - line.s); break; } if (strncmp(line.s, prefix->s, prefix->len ) == 0) { //LM_DBG("current remove >%.*s< (%d)\n", remove.len, remove.s, remove.len); if (!found) { //LM_DBG("first match >%.*s< (%d)\n", line.len,line.s,line.len); remove.s = line.s; remove.len = line.len; } else { //LM_DBG("cont. match >%.*s< (%d)\n", line.len,line.s,line.len); if (remove.s + remove.len == line.s) { //LM_DBG("this match is right after previous match\n"); remove.len += line.len; } else { //LM_DBG("there is gap between this and previous match, remove now\n"); anchor = del_lump(msg, remove.s - msg->buf, remove.len, HDR_OTHER_T); if (anchor==NULL) { LM_ERR("failed to remove lump\n"); return -1; } remove.s = line.s; remove.len = line.len; } } found++; //LM_DBG("updated remove >%.*s< (%d)\n", remove.len, remove.s, remove.len); } ptr = find_next_sdp_line(ptr, body.s + body.len, prefix->s[0], NULL); } if (found) { //LM_DBG("remove >%.*s< (%d)\n", remove.len, remove.s, remove.len); anchor = del_lump(msg, remove.s - msg->buf, remove.len, HDR_OTHER_T); if (anchor==NULL) { LM_ERR("failed to remove lump\n"); return -1; } return found; } LM_DBG("no match\n"); return 0; }
static int force_rtp_proxy2_f(struct sip_msg* msg, char* str1, char* str2) { str body, body1, oldport, oldip, newport, newip; str callid, from_tag, to_tag, tmp; int create, port, len, asymmetric, flookup, argc, proxied, real; int oidx, pf=0, pf1, force, node_idx; char opts[16]; char *cp, *cp1; char *cpend, *next; char **ap, *argv[10]; struct lump* anchor; struct rtpp_node *node; struct iovec v[14] = { {NULL, 0}, /* command */ {NULL, 0}, /* options */ {" ", 1}, /* separator */ {NULL, 0}, /* callid */ {" ", 1}, /* separator */ {NULL, 7}, /* newip */ {" ", 1}, /* separator */ {NULL, 1}, /* oldport */ {" ", 1}, /* separator */ {NULL, 0}, /* from_tag */ {";", 1}, /* separator */ {NULL, 0}, /* medianum */ {" ", 1}, /* separator */ {NULL, 0} /* to_tag */ }; char *v1p, *v2p, *c1p, *c2p, *m1p, *m2p, *bodylimit; char medianum_buf[20]; int medianum, media_multi; str medianum_str, tmpstr1; int c1p_altered; v[1].iov_base=opts; asymmetric = flookup = force = real = 0; oidx = 1; node_idx = -1; for (cp = str1; *cp != '\0'; cp++) { switch (*cp) { case ' ': case '\t': break; case 'a': case 'A': opts[oidx++] = 'A'; asymmetric = 1; real = 1; break; case 'i': case 'I': opts[oidx++] = 'I'; break; case 'e': case 'E': opts[oidx++] = 'E'; break; case 'l': case 'L': flookup = 1; break; case 'f': case 'F': force = 1; break; case 'r': case 'R': real = 1; break; case 'n': case 'N': cp++; for (len = 0; isdigit(cp[len]); len++) continue; if (len == 0) { LOG(L_ERR, "ERROR: force_rtp_proxy2: non-negative integer" "should follow N option\n"); return -1; } node_idx = strtoul(cp, NULL, 10); cp += len - 1; break; default: LOG(L_ERR, "ERROR: force_rtp_proxy2: unknown option `%c'\n", *cp); return -1; } } if (msg->first_line.type == SIP_REQUEST && msg->first_line.u.request.method_value == METHOD_INVITE) { create = 1; } else if (msg->first_line.type == SIP_REPLY) { create = 0; } else { return -1; } /* extract_body will also parse all the headers in the message as * a side effect => don't move get_callid/get_to_tag in front of it * -- andrei */ if (extract_body(msg, &body) == -1) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't extract body " "from the message\n"); return -1; } if (get_callid(msg, &callid) == -1 || callid.len == 0) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get Call-Id field\n"); return -1; } if (get_to_tag(msg, &to_tag) == -1) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get To tag\n"); return -1; } if (get_from_tag(msg, &from_tag) == -1 || from_tag.len == 0) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't get From tag\n"); return -1; } if (flookup != 0) { if (create == 0 || to_tag.len == 0) return -1; create = 0; tmp = from_tag; from_tag = to_tag; to_tag = tmp; } proxied = 0; for (cp = body.s; (len = body.s + body.len - cp) >= ANORTPPROXY_LEN;) { cp1 = ser_memmem(cp, ANORTPPROXY, len, ANORTPPROXY_LEN); if (cp1 == NULL) break; if (cp1[-1] == '\n' || cp1[-1] == '\r') { proxied = 1; break; } cp = cp1 + ANORTPPROXY_LEN; } if (proxied != 0 && force == 0) return -1; /* * Parsing of SDP body. * It can contain a few session descriptions (each starts with * v-line), and each session may contain a few media descriptions * (each starts with m-line). * We have to change ports in m-lines, and also change IP addresses in * c-lines which can be placed either in session header (fallback for * all medias) or media description. * Ports should be allocated for any media. IPs all should be changed * to the same value (RTP proxy IP), so we can change all c-lines * unconditionally. */ bodylimit = body.s + body.len; v1p = find_sdp_line(body.s, bodylimit, 'v'); if (v1p == NULL) { LOG(L_ERR, "ERROR: force_rtp_proxy2: no sessions in SDP\n"); return -1; } v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); media_multi = (v2p != bodylimit); v2p = v1p; medianum = 0; for(;;) { /* Per-session iteration. */ v1p = v2p; if (v1p == NULL || v1p >= bodylimit) break; /* No sessions left */ v2p = find_next_sdp_line(v1p, bodylimit, 'v', bodylimit); /* v2p is text limit for session parsing. */ m1p = find_sdp_line(v1p, v2p, 'm'); /* Have this session media description? */ if (m1p == NULL) { LOG(L_ERR, "ERROR: force_rtp_proxy2: no m= in session\n"); return -1; } /* * Find c1p only between session begin and first media. * c1p will give common c= for all medias. */ c1p = find_sdp_line(v1p, m1p, 'c'); c1p_altered = 0; /* Have session. Iterate media descriptions in session */ m2p = m1p; for (;;) { m1p = m2p; if (m1p == NULL || m1p >= v2p) break; m2p = find_next_sdp_line(m1p, v2p, 'm', v2p); /* c2p will point to per-media "c=" */ c2p = find_sdp_line(m1p, m2p, 'c'); /* Extract address and port */ tmpstr1.s = c2p ? c2p : c1p; if (tmpstr1.s == NULL) { /* No "c=" */ LOG(L_ERR, "ERROR: force_rtp_proxy2: can't" " find media IP in the message\n"); return -1; } tmpstr1.len = v2p - tmpstr1.s; /* limit is session limit text */ if (extract_mediaip(&tmpstr1, &oldip, &pf) == -1) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't" " extract media IP from the message\n"); return -1; } tmpstr1.s = m1p; tmpstr1.len = m2p - m1p; if (extract_mediaport(&tmpstr1, &oldport) == -1) { LOG(L_ERR, "ERROR: force_rtp_proxy2: can't" " extract media port from the message\n"); return -1; } ++medianum; if (asymmetric != 0 || real != 0) { newip = oldip; } else { newip.s = ip_addr2a(&msg->rcv.src_ip); newip.len = strlen(newip.s); } /* XXX must compare address families in all addresses */ if (pf == AF_INET6) { opts[oidx] = '6'; oidx++; } snprintf(medianum_buf, sizeof medianum_buf, "%d", medianum); medianum_str.s = medianum_buf; medianum_str.len = strlen(medianum_buf); opts[0] = (create == 0) ? 'L' : 'U'; v[1].iov_len = oidx; STR2IOVEC(callid, v[3]); STR2IOVEC(newip, v[5]); STR2IOVEC(oldport, v[7]); STR2IOVEC(from_tag, v[9]); if (1 || media_multi) /* XXX netch: can't choose now*/ { STR2IOVEC(medianum_str, v[11]); } else { v[10].iov_len = v[11].iov_len = 0; } STR2IOVEC(to_tag, v[13]); do { node = select_rtpp_node(callid, 1, node_idx); if (!node) { LOG(L_ERR, "ERROR: force_rtp_proxy2: no available proxies\n"); return -1; } cp = send_rtpp_command(node, v, (to_tag.len > 0) ? 14 : 12); } while (cp == NULL); /* Parse proxy reply to <argc,argv> */ argc = 0; memset(argv, 0, sizeof(argv)); cpend=cp+strlen(cp); next=eat_token_end(cp, cpend); for (ap = argv; cp<cpend; cp=next+1, next=eat_token_end(cp, cpend)){ *next=0; if (*cp != '\0') { *ap=cp; argc++; if ((char*)++ap >= ((char*)argv+sizeof(argv))) break; } } if (argc < 1) { LOG(L_ERR, "force_rtp_proxy2: no reply from rtp proxy\n"); return -1; } port = atoi(argv[0]); if (port <= 0 || port > 65535) { LOG(L_ERR, "force_rtp_proxy2: incorrect port in reply from rtp proxy\n"); return -1; } pf1 = (argc >= 3 && argv[2][0] == '6') ? AF_INET6 : AF_INET; if (isnulladdr(&oldip, pf)) { if (pf1 == AF_INET6) { newip.s = "::"; newip.len = 2; } else { newip.s = "0.0.0.0"; newip.len = 7; } } else { newip.s = (argc < 2) ? str2 : argv[1]; newip.len = strlen(newip.s); } newport.s = int2str(port, &newport.len); /* beware static buffer */ /* Alter port. */ body1.s = m1p; body1.len = bodylimit - body1.s; if (alter_mediaport(msg, &body1, &oldport, &newport, 0) == -1) return -1; /* * Alter IP. Don't alter IP common for the session * more than once. */ if (c2p != NULL || !c1p_altered) { body1.s = c2p ? c2p : c1p; body1.len = bodylimit - body1.s; if (alter_mediaip(msg, &body1, &oldip, pf, &newip, pf1, 0) == -1) return -1; if (!c2p) c1p_altered = 1; } } /* Iterate medias in session */ } /* Iterate sessions */ if (proxied == 0) { cp = pkg_malloc(ANORTPPROXY_LEN * sizeof(char)); if (cp == NULL) { LOG(L_ERR, "ERROR: force_rtp_proxy2: out of memory\n"); return -1; } anchor = anchor_lump(msg, body.s + body.len - msg->buf, 0, 0); if (anchor == NULL) { LOG(L_ERR, "ERROR: force_rtp_proxy2: anchor_lump failed\n"); pkg_free(cp); return -1; } memcpy(cp, ANORTPPROXY, ANORTPPROXY_LEN); if (insert_new_lump_after(anchor, cp, ANORTPPROXY_LEN, 0) == NULL) { LOG(L_ERR, "ERROR: force_rtp_proxy2: insert_new_lump_after failed\n"); pkg_free(cp); return -1; } } return 1; }