/* * $Id: from.c 798 2006-04-10 12:53:47Z bogdan_iancu $ * * Copyright (C) 2005 Voice Sistem SRL * * This file is part of openser, a free SIP server. * * UAC OpenSER-module is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * UAC OpenSER-module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * History: * --------- * 2005-01-31 first version (ramona) * 2005-08-12 encoded old FROM URI stored in RR hdr and not in FROM anymore; * some TM callbacks replaced with RR callback - more efficient; * XOR used to mix together old and new URI * (bogdan) * 2006-03-03 new display name is added even if there is no previous one * (bogdan) * 2006-03-03 the RR parameter is encrypted via XOR with a password * (bogdan) */ #include #include "../../parser/parse_from.h" #include "../../mem/mem.h" #include "../../data_lump.h" #include "../tm/h_table.h" #include "../tm/tm_load.h" #include "../rr/api.h" #include "from.h" extern str rr_param; extern str uac_passwd; extern int from_restore_mode; extern struct tm_binds uac_tmb; extern struct rr_binds uac_rrb; static char enc_table64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789+/"; static int dec_table64[256]; static void restore_from_reply(struct cell* t, int type, struct tmcb_params *p); static void restore_to_reply(struct cell* t, int type, struct tmcb_params *p); #define text3B64_len(_l) ( ( ((_l)+2)/3 ) << 2 ) void init_from_replacer() { int i; for( i=0 ; i<256 ; i++) dec_table64[i] = -1; for ( i=0 ; i<64; i++) dec_table64[(unsigned char)enc_table64[i]] = i; } static inline int encode_from( str *src, str *dst ) { static char buf[text3B64_len(MAX_URI_SIZE)]; int idx; int left; int block; int i,r; char *p; dst->len = text3B64_len( src->len ); dst->s = buf; if (dst->len>text3B64_len(MAX_URI_SIZE)) { LOG(L_ERR,"ERROR:uac:encode_from: uri too long\n"); return -1; } for ( idx=0, p=buf ; idxlen ; idx+=3) { left = src->len - idx -1 ; left = (left>1? 2 : left); /* Collect 1 to 3 bytes to encode */ block = 0; for ( i=0,r= 16 ; i<=left ; i++,r-=8 ) { block += ((unsigned char)src->s[idx+i]) << r; } /* Encode into 2-4 chars appending '=' if not enough data left.*/ *(p++) = enc_table64[(block >> 18) & 0x3f]; *(p++) = enc_table64[(block >> 12) & 0x3f]; *(p++) = left > 0 ? enc_table64[(block >> 6) & 0x3f] : '-'; *(p++) = left > 1 ? enc_table64[block & 0x3f] : '-'; } return 0; } static inline int decode_from( str *src , str *dst) { static char buf[MAX_URI_SIZE]; int block; int n; int idx; int end; int i,j; char c; /* Count '-' at end and disregard them */ for( n=0,i=src->len-1; src->s[i]=='-'; i--) n++; dst->len = ((src->len * 6) >> 3) - n; dst->s = buf; if (dst->len>MAX_URI_SIZE) { LOG(L_ERR,"ERROR:uac:decode_from: uri too long\n"); return -1; } end = src->len - n; for ( i=0,idx=0 ; is[i++]]; if ( c<0 ) { LOG(L_ERR,"ERROR:uac:decode_from: invalid base64 string " "\"%.*s\"\n",src->len,src->s); return -1; } block += c << (18 - 6*j); } /* Add the bytes */ for ( j=0,n=16 ; j<3 && idx+j< dst->len; j++,n-=8 ) buf[idx+j] = (char) ((block >> n) & 0xff); } return 0; } static inline struct lump* get_fdisplay_anchor(struct sip_msg *msg, struct to_body *from, str *dsp) { struct lump* l; char *p1; char *p2; /* is URI quoted or not? */ p1 = msg->from->name.s + msg->from->name.len; for( p2=from->uri.s-1 ; p2>=p1 && *p2!='<' ; p2--); if (*p2=='<') { /* is quoted */ l = anchor_lump( msg, p2 - msg->buf, 0, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:get_fdisplay_anchor: unable to build lump " "anchor\n"); return 0; } dsp->s[dsp->len++] = ' '; return l; } /* not quoted - more complicated....must place the closing bracket */ l = anchor_lump( msg, (from->uri.s+from->uri.len) - msg->buf, 0, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:get_fdisplay_anchor: unable to build lump " "anchor\n"); return 0; } p1 = (char*)pkg_malloc(1); if (p1==0) { LOG(L_ERR,"ERROR:uac:get_fdisplay_anchor: no more pkg mem \n"); return 0; } *p1 = '>'; if (insert_new_lump_after( l, p1, 1, 0)==0) { LOG(L_ERR,"ERROR:uac:get_fdisplay_anchor: insert lump failed\n"); pkg_free(p1); return 0; } /* build anchor for display */ l = anchor_lump( msg, from->uri.s - msg->buf, 0, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:get_fdisplay_anchor: unable to build lump " "anchor\n"); return 0; } dsp->s[dsp->len++] = ' '; dsp->s[dsp->len++] = '<'; return l; } /* * relace from uri and/or from display name */ int replace_from( struct sip_msg *msg, str *from_dsp, str *from_uri) { static char buf_s[MAX_URI_SIZE]; struct to_body *from; struct lump* l; str replace; char *p; str param; str buf; int i; /* consistency check! in AUTO mode, do NOT allow FROM changing * in sequential request */ if (from_restore_mode==FROM_AUTO_RESTORE && from_uri && from_uri->len) { if ( msg->to==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) { LOG(L_ERR,"ERROR:uac:replace_from: failed to parse TO hdr\n"); goto error; } if (get_to(msg)->tag_value.len!=0) { LOG(L_ERR,"ERROR:uac:replace_from: decline FROM replacing in " "sequential request in auto mode (has TO tag)\n"); goto error; } } /* parse original from hdr */ if (parse_from_header(msg)<0 ) { LOG(L_ERR,"ERROR:uac:replace_from: failed to find/parse FROM hdr\n"); goto error; } from = (struct to_body*)msg->from->parsed; /* some validity checks */ if (from->param_lst==0) { LOG(L_ERR,"ERROR:uac:replace_from: broken FROM hdr; no tag param\n"); goto error; } /* first deal with display name */ if (from_dsp) { /* must be replaced/ removed */ l = 0; /* first remove the existing display */ if ( from->display.len) { DBG("DEBUG:uac:replace_from: removing display [%.*s]\n", from->display.len,from->display.s); /* build del lump */ l = del_lump( msg, from->display.s-msg->buf, from->display.len, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:replace_from: display del lump failed\n"); goto error; } } /* some new display to set? */ if (from_dsp->len) { /* add the new display exactly over the deleted one */ buf.s = pkg_malloc( from_dsp->len + 2 ); if (buf.s==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } memcpy( buf.s, from_dsp->s, from_dsp->len); buf.len = from_dsp->len; if (l==0 && (l=get_fdisplay_anchor(msg,from,&buf))==0) { LOG(L_ERR,"ERROR:uac:replace_from: failed to insert anchor\n"); goto error; } if (insert_new_lump_after( l, buf.s, buf.len, 0)==0) { LOG(L_ERR,"ERROR:uac:replace_from: insert new " "display lump failed\n"); pkg_free(buf.s); goto error; } } } /* now handle the URI */ if (from_uri==0 || from_uri->len==0 ) /* do not touch URI part */ return 0; DBG("DEBUG:uac:replace_from: uri to replace [%.*s]\n", from->uri.len, from->uri.s); DBG("DEBUG:uac:replace_from: replacement uri is [%.*s]\n", from_uri->len, from_uri->s); /* build del/add lumps */ if ((l=del_lump( msg, from->uri.s-msg->buf, from->uri.len, 0))==0) { LOG(L_ERR,"ERROR:uac:replace_from: del lump failed\n"); goto error; } p = pkg_malloc( from_uri->len); if (p==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } memcpy( p, from_uri->s, from_uri->len); if (insert_new_lump_after( l, p, from_uri->len, 0)==0) { LOG(L_ERR,"ERROR:uac:replace_from: insert new lump failed\n"); pkg_free(p); goto error; } if (from_restore_mode==FROM_NO_RESTORE) return 0; /* build RR parameter */ buf.s = buf_s; if ( from->uri.len>from_uri->len ) { if (from->uri.len>MAX_URI_SIZE) { LOG(L_ERR,"ERROR:uac:replace_from: old from uri to long\n"); goto error; } memcpy( buf.s, from->uri.s, from->uri.len); for( i=0 ; ilen ; i++ ) buf.s[i] ^=from_uri->s[i]; buf.len = from->uri.len; } else { if (from_uri->len>MAX_URI_SIZE) { LOG(L_ERR,"ERROR:uac:replace_from: new from uri to long\n"); goto error; } memcpy( buf.s, from_uri->s, from_uri->len); for( i=0 ; iuri.len ; i++ ) buf.s[i] ^=from->uri.s[i]; buf.len = from_uri->len; } /* encrypt parameter ;) */ if (uac_passwd.len) for( i=0 ; i len=%d\n", replace.len,replace.s,replace.len); /* add RR parameter */ param.len = 1+rr_param.len+1+replace.len; param.s = (char*)pkg_malloc(param.len); if (param.s==0) { LOG(L_ERR,"ERROR:uac:replace_from: no more pkg mem\n"); goto error; } p = param.s; *(p++) = ';'; memcpy( p, rr_param.s, rr_param.len); p += rr_param.len; *(p++) = '='; memcpy( p, replace.s, replace.len); p += replace.len; if (uac_rrb.add_rr_param( msg, ¶m)!=0) { LOG(L_ERR,"ERROR:uac:replace_from: add_RR_param failed\n"); goto error1; } msg->msg_flags |= FL_USE_UAC_FROM; /* add TM callback to restore the FROM hdr in reply */ if (uac_tmb.register_tmcb(msg,0,TMCB_RESPONSE_IN,restore_from_reply,0)!=1) { LOG(L_ERR,"ERROR:uac:replace_from: failed to install TM callback\n"); goto error1; } pkg_free(param.s); return 0; error1: pkg_free(param.s); error: return -1; } /* * return 0 - restored * -1 - not restored or error */ int restore_from( struct sip_msg *msg, int *is_from ) { struct lump* l; str param_val; str old_uri; str new_uri; char *p; int i; int flag; /* we should process only sequntial request, but since we are looking * for Route param, the test is not really required -bogdan */ DBG("DEBUG:uac:restore_from: getting '%.*s' Route param\n", rr_param.len,rr_param.s); /* is there something to restore ? */ if (uac_rrb.get_route_param( msg, &rr_param, ¶m_val)!=0) { DBG("DEBUG:uac:restore_from: Route param '%.*s' not found\n", rr_param.len,rr_param.s); goto failed; } DBG("DEBUG:uac:restore_from: Route param is '%.*s' (len=%d)\n", param_val.len,param_val.s,param_val.len); /* decode the parameter val to a URI */ if (decode_from( ¶m_val, &new_uri)<0 ) { LOG(L_ERR,"ERROR:uac:restore_from: failed to decode uri\n"); goto failed; } /* dencrypt parameter ;) */ if (uac_passwd.len) for( i=0 ; ito==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) { LOG(L_ERR,"ERROR:uac:restore_from: failed to parse TO hdr\n"); goto failed; } old_uri = ((struct to_body*)msg->to->parsed)->uri; flag = FL_USE_UAC_TO; if (is_from) *is_from = 0; } else { /* replace the FROM URI */ if ( parse_from_header(msg)<0 ) { LOG(L_ERR,"ERROR:uac:restore_from: failed to find/parse " "FROM hdr\n"); goto failed; } old_uri = ((struct to_body*)msg->from->parsed)->uri; flag = FL_USE_UAC_FROM; if (is_from) *is_from = 1; } /* get new uri */ if ( new_uri.lenbuf, old_uri.len, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:restore_from: del lump failed\n"); goto failed1; } if (insert_new_lump_after( l, new_uri.s, new_uri.len, 0)==0) { LOG(L_ERR,"ERROR:uac:restore_from: insert new lump failed\n"); goto failed1; } msg->msg_flags |= flag; return 0; failed1: pkg_free(new_uri.s); failed: return -1; } /************************** RRCB functions ******************************/ void rr_checker(struct sip_msg *msg, str *r_param, void *cb_param) { int is_from; is_from = 0; /* check if the request contains the route param */ if ( restore_from( msg, &is_from)==0 ) { /* restore in req performed -> replace in reply */ /* in callback we need TO/FROM to be parsed- it's already done * by restore_from() function */ if ( uac_tmb.register_tmcb( msg, 0, TMCB_RESPONSE_IN, is_from?restore_from_reply:restore_to_reply, 0)!=1 ) { LOG(L_ERR,"ERROR:uac:rr_checker: failed to install TM callback\n"); return; } } } /************************** TMCB functions ******************************/ /* replace the entire from HDR with the original FROM request */ void restore_from_reply(struct cell* t, int type, struct tmcb_params *p) { struct lump* l; struct sip_msg *req; struct sip_msg *rpl; str new_val; if ( !t || !t->uas.request || !p->rpl ) return; req = t->uas.request; rpl = p->rpl; /* parse FROM in reply */ if (parse_from_header( p->rpl )<0 ) { LOG(L_ERR,"ERROR:uac:restore_from_reply: failed to find/parse " "FROM hdr\n"); return; } /* duplicate the new from value */ new_val.s = pkg_malloc( req->from->len ); if (p==0) { LOG(L_ERR,"ERROR:uac:restore_from_reply: no more pkg mem\n"); return; } memcpy( new_val.s, req->from->name.s, req->from->len); new_val.len = req->from->len; DBG("DBG:uac::restore_from_reply: removing <%.*s>\n", rpl->from->len,rpl->from->name.s); l = del_lump( rpl, rpl->from->name.s-rpl->buf, rpl->from->len, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:restore_from_reply: del lump failed\n"); return; } DBG("DBG:uac::restore_from_reply: inserting <%.*s>\n", new_val.len,new_val.s); if (insert_new_lump_after( l, new_val.s, new_val.len, 0)==0) { LOG(L_ERR,"ERROR:uac:restore_from_reply: insert new lump failed\n"); return; } } /* replace the entire from TO with the original TO request */ void restore_to_reply(struct cell* t, int type, struct tmcb_params *p) { struct lump* l; struct sip_msg *req; struct sip_msg *rpl; str new_val; if ( !t || !t->uas.request || !p->rpl ) return; req = t->uas.request; rpl = p->rpl; /* parse TO in reply */ if ( rpl->to==0 && (parse_headers(rpl,HDR_TO_F,0)!=0 || rpl->to==0) ) { LOG(L_ERR,"ERROR:uac:restore_to_reply: failed to parse TO hdr\n"); return; } /* duplicate the new from value */ new_val.s = pkg_malloc( req->to->len ); if (p==0) { LOG(L_ERR,"ERROR:uac:restore_from_reply: no more pkg mem\n"); return; } memcpy( new_val.s, req->to->name.s, req->to->len); new_val.len = req->to->len; DBG("DBG:uac::restore_to_reply: removing <%.*s>\n", rpl->to->len,rpl->to->name.s); l = del_lump( rpl, rpl->to->name.s-rpl->buf, rpl->to->len, 0); if (l==0) { LOG(L_ERR,"ERROR:uac:restore_to_reply: del lump failed\n"); return; } DBG("DBG:uac::restore_to_reply: inserting <%.*s>\n", new_val.len, new_val.s); if (insert_new_lump_after( l, new_val.s, new_val.len, 0)==0) { LOG(L_ERR,"ERROR:uac:restore_to_reply: insert new lump failed\n"); return; } }