]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
62782921c610b32ace36588676da652d2cab09e5
[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, unsigned int* choosen_local_tag, 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         /* Look for a local enabled crypto algo that matches one of the proposed by remote */
139                 for(j=0; j<SAL_CRYPTO_ALGO_MAX; j++) {
140                         if (remote[i].algo == local[j].algo) {
141                                 result->algo = remote[i].algo;
142             /* We're answering an SDP offer. Supply our master key, associated with the remote supplied tag */
143                                 if (use_local_key) {
144                                         strncpy(result->master_key, local[j].master_key, 41);
145                                         result->tag = remote[i].tag;
146                     *choosen_local_tag = local[j].tag;
147                                 }
148                                 /* We received an answer to our SDP crypto proposal. Copy matching algo remote master key to result, and memorize local tag */
149             else {
150                                         strncpy(result->master_key, remote[i].master_key, 41);
151                                         result->tag = local[j].tag;
152                     *choosen_local_tag = local[j].tag;
153                                 }
154                                 result->master_key[40] = '\0';
155                                 return TRUE;
156                         }
157                 }
158         }
159         return FALSE;
160 }
161
162
163
164 static SalStreamDir compute_dir_outgoing(SalStreamDir local, SalStreamDir answered){
165         SalStreamDir res=local;
166         if (local==SalStreamSendRecv){
167                 if (answered==SalStreamRecvOnly){
168                         res=SalStreamSendOnly;
169                 }else if (answered==SalStreamSendOnly){
170                         res=SalStreamRecvOnly;
171                 }
172         }
173         if (answered==SalStreamInactive){
174                 res=SalStreamInactive;
175         }
176         return res;
177 }
178
179 static SalStreamDir compute_dir_incoming(SalStreamDir local, SalStreamDir offered){
180         SalStreamDir res=SalStreamSendRecv;
181         if (local==SalStreamSendRecv){
182                 if (offered==SalStreamSendOnly)
183                         res=SalStreamRecvOnly;
184                 else if (offered==SalStreamRecvOnly)
185                         res=SalStreamSendOnly;
186                 else if (offered==SalStreamInactive)
187                         res=SalStreamInactive;
188                 else
189                         res=SalStreamSendRecv;
190         }else if (local==SalStreamSendOnly){
191                 if (offered==SalStreamRecvOnly || offered==SalStreamSendRecv)
192                         res=SalStreamSendOnly;
193                 else res=SalStreamInactive;
194         }else if (local==SalStreamRecvOnly){
195                 if (offered==SalStreamSendOnly || offered==SalStreamSendRecv)
196                         res=SalStreamRecvOnly;
197                 else
198                         res=SalStreamInactive;
199         }else res=SalStreamInactive;
200         return res;
201 }
202
203 static void initiate_outgoing(const SalStreamDescription *local_offer,
204                                         const SalStreamDescription *remote_answer,
205                                         SalStreamDescription *result){
206         if (remote_answer->rtp_port!=0)
207                 result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads,TRUE,FALSE);
208         result->proto=remote_answer->proto;
209         result->type=local_offer->type;
210         result->dir=compute_dir_outgoing(local_offer->dir,remote_answer->dir);
211
212         if (result->payloads && !only_telephone_event(result->payloads)){
213                 strcpy(result->rtp_addr,remote_answer->rtp_addr);
214                 result->rtp_port=remote_answer->rtp_port;
215                 result->bandwidth=remote_answer->bandwidth;
216                 result->ptime=remote_answer->ptime;
217         }else{
218                 result->rtp_port=0;
219         }
220         if (result->proto == SalProtoRtpSavp) {
221                 /* verify crypto algo */
222                 memset(result->crypto, 0, sizeof(result->crypto));
223                 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
224                         result->rtp_port = 0;
225         }
226 }
227
228
229 static void initiate_incoming(const SalStreamDescription *local_cap,
230                                         const SalStreamDescription *remote_offer,
231                                         SalStreamDescription *result, bool_t one_matching_codec){
232         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
233         result->proto=remote_offer->proto;
234         result->type=local_cap->type;
235         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
236         if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){
237                 strcpy(result->rtp_addr,local_cap->rtp_addr);
238                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
239                 result->rtp_port=local_cap->rtp_port;
240                 result->bandwidth=local_cap->bandwidth;
241                 result->ptime=local_cap->ptime;
242         }else{
243                 result->rtp_port=0;
244         }
245         if (result->proto == SalProtoRtpSavp) {
246                 /* select crypto algo */
247                 memset(result->crypto, 0, sizeof(result->crypto));
248                 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
249                         result->rtp_port = 0;
250                 
251         }
252 }
253
254 /**
255  * Returns a media description to run the streams with, based on a local offer
256  * and the returned response (remote).
257 **/
258 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
259                                                                         const SalMediaDescription *remote_answer,
260                                                         SalMediaDescription *result){
261         int i,j;
262     
263         const SalStreamDescription *ls,*rs;
264         for(i=0,j=0;i<local_offer->nstreams;++i){
265                 ms_message("Processing for stream %i",i);
266                 ls=&local_offer->streams[i];
267                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
268         if (rs) {
269                         initiate_outgoing(ls,rs,&result->streams[j]);
270                         ++j;
271                 }
272                 else ms_warning("No matching stream for %i",i);
273         }
274         result->nstreams=j;
275         result->bandwidth=remote_answer->bandwidth;
276         strcpy(result->addr,remote_answer->addr);
277         return 0;
278 }
279
280 /**
281  * Returns a media description to run the streams with, based on the local capabilities and
282  * and the received offer.
283  * The returned media description is an answer and should be sent to the offerer.
284 **/
285 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
286                                                 const SalMediaDescription *remote_offer,
287                                         SalMediaDescription *result, bool_t one_matching_codec){
288         int i;
289         const SalStreamDescription *ls=NULL,*rs;
290                                                         
291         for(i=0;i<remote_offer->nstreams;++i){
292                 rs=&remote_offer->streams[i];
293                 if (rs->proto!=SalProtoUnknown){
294                         ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
295                         /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
296                         if (!ls && rs->proto == SalProtoRtpAvp) {
297                                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
298                         }
299                 }else ms_warning("Unknown protocol for mline %i, declining",i);
300                 if (ls){
301                         initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
302                 }
303                 else {
304                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
305                         result->streams[i].dir=SalStreamInactive;
306                         result->streams[i].rtp_port=0;
307                         result->streams[i].type=rs->type;
308                         result->streams[i].proto=rs->proto;
309                         if (rs->type==SalOther){
310                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
311                         }
312                 }
313         }
314         result->nstreams=i;
315         strcpy(result->username, local_capabilities->username);
316         strcpy(result->addr,local_capabilities->addr);
317         result->bandwidth=local_capabilities->bandwidth;
318         result->session_ver=local_capabilities->session_ver;
319         result->session_id=local_capabilities->session_id;
320         return 0;
321 }