]> sjero.net Git - linphone/blob - coreapi/offeranswer.c
118f2420a165ce6cd0b01365f5d3912c98913f88
[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                 strcpy(result->rtcp_addr,remote_answer->rtcp_addr);
215                 result->rtp_port=remote_answer->rtp_port;
216                 result->rtcp_port=remote_answer->rtcp_port;
217                 result->bandwidth=remote_answer->bandwidth;
218                 result->ptime=remote_answer->ptime;
219         }else{
220                 result->rtp_port=0;
221         }
222         if (result->proto == SalProtoRtpSavp) {
223                 /* verify crypto algo */
224                 memset(result->crypto, 0, sizeof(result->crypto));
225                 if (!match_crypto_algo(local_offer->crypto, remote_answer->crypto, &result->crypto[0], &result->crypto_local_tag, FALSE))
226                         result->rtp_port = 0;
227         }
228 }
229
230
231 static void initiate_incoming(const SalStreamDescription *local_cap,
232                                         const SalStreamDescription *remote_offer,
233                                         SalStreamDescription *result, bool_t one_matching_codec){
234         result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads, FALSE, one_matching_codec);
235         result->proto=remote_offer->proto;
236         result->type=local_cap->type;
237         result->dir=compute_dir_incoming(local_cap->dir,remote_offer->dir);
238         if (result->payloads && !only_telephone_event(result->payloads) && (remote_offer->rtp_port!=0 || remote_offer->rtp_port==SalStreamSendOnly)){
239                 strcpy(result->rtp_addr,local_cap->rtp_addr);
240                 strcpy(result->rtcp_addr,local_cap->rtcp_addr);
241                 memcpy(result->candidates,local_cap->candidates,sizeof(result->candidates));
242                 result->rtp_port=local_cap->rtp_port;
243                 result->rtcp_port=local_cap->rtcp_port;
244                 result->bandwidth=local_cap->bandwidth;
245                 result->ptime=local_cap->ptime;
246         }else{
247                 result->rtp_port=0;
248         }
249         if (result->proto == SalProtoRtpSavp) {
250                 /* select crypto algo */
251                 memset(result->crypto, 0, sizeof(result->crypto));
252                 if (!match_crypto_algo(local_cap->crypto, remote_offer->crypto, &result->crypto[0], &result->crypto_local_tag, TRUE))
253                         result->rtp_port = 0;
254                 
255         }
256 }
257
258 /**
259  * Returns a media description to run the streams with, based on a local offer
260  * and the returned response (remote).
261 **/
262 int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer,
263                                                                         const SalMediaDescription *remote_answer,
264                                                         SalMediaDescription *result){
265         int i,j;
266     
267         const SalStreamDescription *ls,*rs;
268         for(i=0,j=0;i<local_offer->nstreams;++i){
269                 ms_message("Processing for stream %i",i);
270                 ls=&local_offer->streams[i];
271                 rs=sal_media_description_find_stream((SalMediaDescription*)remote_answer,ls->proto,ls->type);
272         if (rs) {
273                         initiate_outgoing(ls,rs,&result->streams[j]);
274                         ++j;
275                 }
276                 else ms_warning("No matching stream for %i",i);
277         }
278         result->nstreams=j;
279         result->bandwidth=remote_answer->bandwidth;
280         strcpy(result->addr,remote_answer->addr);
281         return 0;
282 }
283
284 /**
285  * Returns a media description to run the streams with, based on the local capabilities and
286  * and the received offer.
287  * The returned media description is an answer and should be sent to the offerer.
288 **/
289 int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities,
290                                                 const SalMediaDescription *remote_offer,
291                                         SalMediaDescription *result, bool_t one_matching_codec){
292         int i;
293         const SalStreamDescription *ls=NULL,*rs;
294                                                         
295         for(i=0;i<remote_offer->nstreams;++i){
296                 rs=&remote_offer->streams[i];
297                 if (rs->proto!=SalProtoUnknown){
298                         ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,rs->proto,rs->type);
299                         /* if matching failed, and remote proposes Avp only, ask for local Savp streams */ 
300                         if (!ls && rs->proto == SalProtoRtpAvp) {
301                                 ls=sal_media_description_find_stream((SalMediaDescription*)local_capabilities,SalProtoRtpSavp,rs->type);
302                         }
303                 }else ms_warning("Unknown protocol for mline %i, declining",i);
304                 if (ls){
305                         initiate_incoming(ls,rs,&result->streams[i],one_matching_codec);
306                 }
307                 else {
308                         /* create an inactive stream for the answer, as there where no matching stream a local capability */
309                         result->streams[i].dir=SalStreamInactive;
310                         result->streams[i].rtp_port=0;
311                         result->streams[i].type=rs->type;
312                         result->streams[i].proto=rs->proto;
313                         if (rs->type==SalOther){
314                                 strncpy(result->streams[i].typeother,rs->typeother,sizeof(rs->typeother)-1);
315                         }
316                 }
317         }
318         result->nstreams=i;
319         strcpy(result->username, local_capabilities->username);
320         strcpy(result->addr,local_capabilities->addr);
321         result->bandwidth=local_capabilities->bandwidth;
322         result->session_ver=local_capabilities->session_ver;
323         result->session_id=local_capabilities->session_id;
324         return 0;
325 }