]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
fix usage of non default srtp key
[linphone] / coreapi / offeranswer.c
1 /*
2 linphone
3 Copyright (C) 2010  Simon MORLAT (simon.morlat@free.fr)
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "sal.h"
21 #include "offeranswer.h"
22
23 static bool_t only_telephone_event(const MSList *l){
24         PayloadType *p=(PayloadType*)l->data;
25         if (strcasecmp(p->mime_type,"telephone-event")!=0){
26                 return FALSE;
27         }
28         return TRUE;
29 }
30
31 static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){
32         PayloadType *pt;
33         char value[10];
34         const MSList *elem;
35         PayloadType *candidate=NULL;
36
37         for (elem=l;elem!=NULL;elem=elem->next){
38                 pt=(PayloadType*)elem->data;
39                 /* the compare between G729 and G729A is for some stupid uncompliant phone*/
40                 if ( (strcasecmp(pt->mime_type,refpt->mime_type)==0  ||
41                     (strcasecmp(pt->mime_type, "G729") == 0 && strcasecmp(refpt->mime_type, "G729A") == 0 ))
42                         && pt->clock_rate==refpt->clock_rate){
43                         candidate=pt;
44                         /*good candidate, check fmtp for H264 */
45                         if (strcasecmp(pt->mime_type,"H264")==0){
46                                 if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){
47                                         int mode1=0,mode2=0;
48                                         if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){
49                                                 mode1=atoi(value);
50                                         }
51                                         if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){
52                                                 mode2=atoi(value);
53                                         }
54                                         if (mode1==mode2)
55                                             break; /*exact match */
56                                 }
57                         }else break;
58                 }
59         }
60         return candidate;
61 }
62
63 static MSList *match_payloads(const MSList *local, const MSList *remote, bool_t reading_response, bool_t one_matching_codec){
64         const MSList *e2,*e1;
65         MSList *res=NULL;
66         PayloadType *matched;
67         bool_t found_codec=FALSE;
68         
69         for(e2=remote;e2!=NULL;e2=e2->next){
70                 PayloadType *p2=(PayloadType*)e2->data;
71                 matched=find_payload_type_best_match(local,p2);
72                 if (matched){
73                         PayloadType *newp;
74                         int local_number=payload_type_get_number(matched);
75                         int remote_number=payload_type_get_number(p2);
76
77                         if (one_matching_codec){
78                                 if (strcasecmp(matched->mime_type,"telephone-event")!=0){
79                                         if (found_codec){/* we have found a real codec already*/
80                                                 continue; /*this codec won't be added*/
81                                         }else found_codec=TRUE;
82                                 }
83                         }
84                         
85                         newp=payload_type_clone(matched);
86                         if (p2->send_fmtp)
87                                 payload_type_set_send_fmtp(newp,p2->send_fmtp);
88                         newp->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV|PAYLOAD_TYPE_FLAG_CAN_SEND;
89                         res=ms_list_append(res,newp);
90                         /* we should use the remote numbering even when parsing a response */
91                         payload_type_set_number(newp,remote_number);
92                         if (reading_response && remote_number!=local_number){
93                                 ms_warning("For payload type %s, proposed number was %i but the remote phone answered %i",
94                                           newp->mime_type, local_number, remote_number);
95                                 /*
96                                  We must add this payload type with our local numbering in order to be able to receive it.
97                                  Indeed despite we must sent with the remote numbering, we must be able to receive with
98                                  our local one.
99                                 */
100                                 newp=payload_type_clone(newp);
101                                 payload_type_set_number(newp,local_number);
102                                 res=ms_list_append(res,newp);
103                         }
104                 }else{
105                         ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate);
106                 }
107         }
108         if (reading_response){
109                 /* add remaning local payload as CAN_RECV only so that if we are in front of a non-compliant equipment we are still able to decode the RTP stream*/
110                 for(e1=local;e1!=NULL;e1=e1->next){
111                         PayloadType *p1=(PayloadType*)e1->data;
112                         bool_t found=FALSE;
113                         for(e2=res;e2!=NULL;e2=e2->next){
114                                 PayloadType *p2=(PayloadType*)e2->data;
115                                 if (payload_type_get_number(p2)==payload_type_get_number(p1)){
116                                         found=TRUE;
117                                         break;
118                                 }
119                         }
120                         if (!found){
121                                 ms_message("Adding %s/%i for compatibility, just in case.",p1->mime_type,p1->clock_rate);
122                                 p1=payload_type_clone(p1);
123                                 p1->flags|=PAYLOAD_TYPE_FLAG_CAN_RECV;
124                                 res=ms_list_append(res,p1);
125                         }
126                 }
127         }
128         return res;
129 }
130
131 static bool_t match_crypto_algo(const SalSrtpCryptoAlgo* local, const SalSrtpCryptoAlgo* remote, 
132         SalSrtpCryptoAlgo* result, bool_t use_local_key) {
133         int i,j;
134         for(i=0; i<SAL_CRYPTO_ALGO_MAX; i++) {
135                 if (remote[i].algo == 0)
136                         break;
137                         
138                 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
139                         if (remote[i].algo == local[j].algo) {
140                                 result->algo = remote[i].algo;
141                                 if (use_local_key) {
142                                         strncpy(result->master_key, local[j].master_key, 41);
143                                         result->tag = local[j].tag;
144                                 } else {
145                                         strncpy(result->master_key, remote[i].master_key, 41);
146                                         result->tag = remote[i].tag;
147                                 }
148                                 result->master_key[40] = '\0';
149                                 return TRUE;
150                         }
151                 }
152         }
153         return FALSE;
154 }
155
156
157
158 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
159         SalStreamDir res=local;
160         if (local==SalStreamSendRecv){
161                 if (answered==SalStreamRecvOnly){
162                         res=SalStreamSendOnly;
163                 }else if (answered==SalStreamSendOnly){
164                         res=SalStreamRecvOnly;
165                 }
166         }
167         if (answered==SalStreamInactive){
168                 res=SalStreamInactive;
169         }
170         return res;
171 }
172
173 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
174         SalStreamDir res=SalStreamSendRecv;
175         if (local==SalStreamSendRecv){
176                 if (offered==SalStreamSendOnly)
177                         res=SalStreamRecvOnly;
178                 else if (offered==SalStreamRecvOnly)
179                         res=SalStreamSendOnly;
180                 else if (offered==SalStreamInactive)
181                         res=SalStreamInactive;
182                 else
183                         res=SalStreamSendRecv;
184         }else if (local==SalStreamSendOnly){
185                 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
186                         res=SalStreamSendOnly;
187                 else res=SalStreamInactive;
188         }else if (local==SalStreamRecvOnly){
189                 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
190                         res=SalStreamRecvOnly;
191                 else
192                         res=SalStreamInactive;
193         }else res=SalStreamInactive;
194         return res;
195 }
196
197 static void initiate_outgoing(const SalStreamDescription *local_offer,
198                                         const SalStreamDescription *remote_answer,
199                                         SalStreamDescription *result){
200         if (remote_answer->port!=0)
201                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
202         result->proto=remote_answer->proto;
203         result->type=local_offer->type;
204         result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
205
206         if (result->payloads && !only_telephone_event(result->payloads)){
207                 strcpy(result->addr,remote_answer->addr);
208                 result->port=remote_answer->port;
209                 result->bandwidth=remote_answer->bandwidth;
210                 result->ptime=remote_answer->ptime;
211         }else{
212                 result->port=0;
213         }
214         if (result->proto == SalProtoRtpSavp) {
215                 /* verify crypto algo */
216                 memset(result->crypto, 0, sizeof(result->crypto));
217                 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], FALSE))
218                         result->port = 0;
219         }
220 }
221
222
223 static void initiate_incoming(const SalStreamDescription *local_cap,
224                                         const SalStreamDescription *remote_offer,
225                                         SalStreamDescription *result, bool_t one_matching_codec){
226         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
227         result->proto=remote_offer->proto;
228         result->type=local_cap->type;
229         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
230         if (result->payloads && !only_telephone_event(result->payloads)){
231                 strcpy(result->addr,local_cap->addr);
232                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
233                 result->port=local_cap->port;
234                 result->bandwidth=local_cap->bandwidth;
235                 result->ptime=local_cap->ptime; 
236         }else{
237                 result->port=0;
238         }
239         if (result->proto == SalProtoRtpSavp) {
240                 /* select crypto algo */
241                 memset(result->crypto, 0, sizeof(result->crypto));
242                 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], TRUE))
243                         result->port = 0; 
244                 
245         }
246 }
247
248 /**
249  * Returns a media description to run the streams with, based on a local offer
250  * and the returned response (remote).
251 **/
252 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
253                                                                         const SalMediaDescription *remote_answer,
254                                                         SalMediaDescription *result){
255     int i,j;
256     
257         const SalStreamDescription *ls,*rs;
258     for(i=0,j=0;i<local_offer->nstreams;++i){
259                 ms_message("Processing for stream %i",i);
260                 ls=&local_offer->streams[i];
261                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
262         if (rs) {
263                         initiate_outgoing(ls,rs,&result->streams[j]);
264                         ++j;
265                 }
266                 else ms_warning("No matching stream for %i",i);
267     }
268         result->nstreams=j;
269         result->bandwidth=remote_answer->bandwidth;
270         strcpy(result->addr,remote_answer->addr);
271         return 0;
272 }
273
274 /**
275  * Returns a media description to run the streams with, based on the local capabilities and
276  * and the received offer.
277  * The returned media description is an answer and should be sent to the offerer.
278 **/
279 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
280                                                 const SalMediaDescription *remote_offer,
281                                         SalMediaDescription *result, bool_t one_matching_codec){
282     int i;
283         const SalStreamDescription *ls,*rs;
284                                                         
285     for(i=0;i<remote_offer->nstreams;++i){
286                 rs=&remote_offer->streams[i];
287                 ms_message("Processing for stream %i",i);
288                 
289                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
290                 ms_message("remote proto: %s => %p", (rs->proto == SalProtoRtpAvp)?"AVP":"SAVP", ls);
291                 /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
292                 if (!ls && rs->proto == SalProtoRtpAvp) {
293                         ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
294                         ms_message("retry with AVP => %p", ls);
295                 }
296                 if (ls){
297                 initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
298                 }
299                 else {
300                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
301                         result->streams[i].dir=SalStreamInactive;
302                         result->streams[i].port=0;
303                         result->streams[i].type=rs->type;
304                         result->streams[i].proto=rs->proto;
305                         if (rs->type==SalOther){
306                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
307                         }
308                 }
309     }
310         result->nstreams=i;
311         strcpy(result->username, local_capabilities->username);
312         strcpy(result->addr,local_capabilities->addr);
313         result->bandwidth=local_capabilities->bandwidth;
314         result->session_ver=local_capabilities->session_ver;
315         result->session_id=local_capabilities->session_id;
316         return 0;
317 }