1 /*
2  * Copyright (c) 2004-2006 Maxim Sobolev <sobomax@FreeBSD.org>
3  * Copyright (c) 2006-2007 Sippy Software, Inc., http://www.sippysoft.com
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include "rtpp_debug.h"
36 #include "rtpp_types.h"
37 #include "rtpp_log.h"
38 #include "rtpp_log_obj.h"
39 #include "rtpp_cfg_stable.h"
40 #include "rtpp_defines.h"
41 #include "rtpp_acct_pipe.h"
42 #include "rtpp_acct.h"
43 #include "rtpp_analyzer.h"
44 #include "rtpp_command_private.h"
45 #include "rtpp_genuid_singlet.h"
46 #include "rtpp_hash_table.h"
47 #include "rtpp_mallocs.h"
48 #include "rtpp_module_if.h"
49 #include "rtpp_pipe.h"
50 #include "rtpp_socket.h"
51 #include "rtpp_stream.h"
52 #include "rtpp_session.h"
53 #include "rtpp_sessinfo.h"
54 #include "rtpp_stats.h"
55 #include "rtpp_time.h"
56 #include "rtpp_ttl.h"
57 #include "rtpp_refcnt.h"
58 
59 struct rtpp_session_priv
60 {
61     struct rtpp_session pub;
62     struct rtpp_sessinfo *sessinfo;
63     struct rtpp_module_if *modules_cf;
64     struct rtpp_acct *acct;
65 };
66 
67 #define PUB2PVT(pubp) \
68   ((struct rtpp_session_priv *)((char *)(pubp) - offsetof(struct rtpp_session_priv, pub)))
69 
70 static void rtpp_session_dtor(struct rtpp_session_priv *);
71 
72 struct rtpp_session *
rtpp_session_ctor(struct rtpp_cfg_stable * cfs,struct common_cmd_args * ccap,double dtime,struct sockaddr ** lia,int weak,int lport,struct rtpp_socket ** fds)73 rtpp_session_ctor(struct rtpp_cfg_stable *cfs, struct common_cmd_args *ccap,
74   double dtime, struct sockaddr **lia, int weak, int lport,
75   struct rtpp_socket **fds)
76 {
77     struct rtpp_session_priv *pvt;
78     struct rtpp_session *pub;
79     struct rtpp_log *log;
80     struct rtpp_refcnt *rcnt;
81     int i;
82     char *cp;
83 
84     pvt = rtpp_rzmalloc(sizeof(struct rtpp_session_priv), &rcnt);
85     if (pvt == NULL) {
86         goto e0;
87     }
88 
89     pub = &(pvt->pub);
90     pub->rcnt = rcnt;
91     rtpp_gen_uid(&pub->seuid);
92 
93     log = rtpp_log_ctor(cfs, "rtpproxy", ccap->call_id, 0);
94     if (log == NULL) {
95         goto e1;
96     }
97     CALL_METHOD(log, setlevel, cfs->log_level);
98     pub->rtp = rtpp_pipe_ctor(pub->seuid, cfs->rtp_streams_wrt,
99       cfs->servers_wrt, log, cfs->rtpp_stats, PIPE_RTP);
100     if (pub->rtp == NULL) {
101         goto e2;
102     }
103     /* spb is RTCP twin session for this one. */
104     pub->rtcp = rtpp_pipe_ctor(pub->seuid, cfs->rtcp_streams_wrt,
105       cfs->servers_wrt, log, cfs->rtpp_stats, PIPE_RTCP);
106     if (pub->rtcp == NULL) {
107         goto e3;
108     }
109     pvt->acct = rtpp_acct_ctor(pub->seuid);
110     if (pvt->acct == NULL) {
111         goto e4;
112     }
113     pvt->acct->init_ts = dtime;
114     pub->call_id = strdup(ccap->call_id);
115     if (pub->call_id == NULL) {
116         goto e5;
117     }
118     pub->tag = strdup(ccap->from_tag);
119     if (pub->tag == NULL) {
120         goto e6;
121     }
122     pub->tag_nomedianum = strdup(ccap->from_tag);
123     if (pub->tag_nomedianum == NULL) {
124         goto e7;
125     }
126     cp = strrchr(pub->tag_nomedianum, ';');
127     if (cp != NULL)
128         *cp = '\0';
129     for (i = 0; i < 2; i++) {
130         pub->rtp->stream[i]->laddr = lia[i];
131         pub->rtcp->stream[i]->laddr = lia[i];
132     }
133     if (weak) {
134         pub->rtp->stream[0]->weak = 1;
135     } else {
136         pub->strong = 1;
137     }
138 
139     pub->rtp->stream[0]->port = lport;
140     pub->rtcp->stream[0]->port = lport + 1;
141     for (i = 0; i < 2; i++) {
142         if (i == 0 || cfs->ttl_mode == TTL_INDEPENDENT) {
143             pub->rtp->stream[i]->ttl = rtpp_ttl_ctor(cfs->max_setup_ttl);
144             if (pub->rtp->stream[i]->ttl == NULL) {
145                 goto e8;
146             }
147         } else {
148             pub->rtp->stream[i]->ttl = pub->rtp->stream[0]->ttl;
149             CALL_SMETHOD(pub->rtp->stream[0]->ttl->rcnt, incref);
150         }
151         /* RTCP shares the same TTL */
152         pub->rtcp->stream[i]->ttl = pub->rtp->stream[i]->ttl;
153         CALL_SMETHOD(pub->rtp->stream[i]->ttl->rcnt, incref);
154     }
155     for (i = 0; i < 2; i++) {
156         pub->rtp->stream[i]->stuid_rtcp = pub->rtcp->stream[i]->stuid;
157         pub->rtcp->stream[i]->stuid_rtp = pub->rtp->stream[i]->stuid;
158     }
159 
160     pvt->pub.rtpp_stats = cfs->rtpp_stats;
161     pvt->pub.log = log;
162     pvt->sessinfo = cfs->sessinfo;
163     if (cfs->modules_cf != NULL) {
164         CALL_SMETHOD(cfs->modules_cf->rcnt, incref);
165         pvt->modules_cf = cfs->modules_cf;
166     }
167 
168     CALL_METHOD(cfs->sessinfo, append, pub, 0, fds);
169 
170     CALL_SMETHOD(pub->rcnt, attach, (rtpp_refcnt_dtor_t)&rtpp_session_dtor,
171       pvt);
172     return (&pvt->pub);
173 
174 e8:
175     free(pub->tag_nomedianum);
176 e7:
177     free(pub->tag);
178 e6:
179     free(pub->call_id);
180 e5:
181     CALL_SMETHOD(pvt->acct->rcnt, decref);
182 e4:
183     CALL_SMETHOD(pub->rtcp->rcnt, decref);
184 e3:
185     CALL_SMETHOD(pub->rtp->rcnt, decref);
186 e2:
187     CALL_SMETHOD(log->rcnt, decref);
188 e1:
189     CALL_SMETHOD(pub->rcnt, decref);
190     free(pvt);
191 e0:
192     return (NULL);
193 }
194 
195 #define MT2RT_NZ(mt) ((mt) == 0.0 ? 0.0 : dtime2rtime(mt))
196 #define DRTN_NZ(bmt, emt) ((emt) == 0.0 || (bmt) == 0.0 ? 0.0 : ((emt) - (bmt)))
197 
198 static void
rtpp_session_dtor(struct rtpp_session_priv * pvt)199 rtpp_session_dtor(struct rtpp_session_priv *pvt)
200 {
201     int i;
202     double session_time;
203     struct rtpp_session *pub;
204 
205     pub = &(pvt->pub);
206     pvt->acct->destroy_ts = getdtime();
207     session_time = pvt->acct->destroy_ts - pvt->acct->init_ts;
208 
209     CALL_METHOD(pub->rtp, get_stats, &pvt->acct->rtp);
210     CALL_METHOD(pub->rtcp, get_stats, &pvt->acct->rtcp);
211     if (pub->complete != 0) {
212         CALL_METHOD(pub->rtp, upd_cntrs, &pvt->acct->rtp);
213         CALL_METHOD(pub->rtcp, upd_cntrs, &pvt->acct->rtcp);
214     }
215     RTPP_LOG(pub->log, RTPP_LOG_INFO, "session on ports %d/%d is cleaned up",
216       pub->rtp->stream[0]->port, pub->rtp->stream[1]->port);
217     for (i = 0; i < 2; i++) {
218         CALL_METHOD(pvt->sessinfo, remove, pub, i);
219     }
220     CALL_METHOD(pub->rtpp_stats, updatebyname, "nsess_destroyed", 1);
221     CALL_METHOD(pub->rtpp_stats, updatebyname_d, "total_duration",
222       session_time);
223     if (pvt->modules_cf != NULL) {
224         pvt->acct->call_id = pvt->pub.call_id;
225         pvt->pub.call_id = NULL;
226         pvt->acct->from_tag = pvt->pub.tag;
227         pvt->pub.tag = NULL;
228         CALL_METHOD(pub->rtp->stream[0]->analyzer, get_stats, \
229           pvt->acct->rasto);
230         CALL_METHOD(pub->rtp->stream[1]->analyzer, get_stats, \
231           pvt->acct->rasta);
232         CALL_METHOD(pub->rtp->stream[0]->analyzer, get_jstats, \
233           pvt->acct->jrasto);
234         CALL_METHOD(pub->rtp->stream[1]->analyzer, get_jstats, \
235           pvt->acct->jrasta);
236 
237         CALL_METHOD(pvt->modules_cf, do_acct, pvt->acct);
238         CALL_SMETHOD(pvt->modules_cf->rcnt, decref);
239     }
240     CALL_SMETHOD(pvt->acct->rcnt, decref);
241 
242     CALL_SMETHOD(pvt->pub.log->rcnt, decref);
243     if (pvt->pub.timeout_data.notify_tag != NULL)
244         free(pvt->pub.timeout_data.notify_tag);
245     if (pvt->pub.call_id != NULL)
246         free(pvt->pub.call_id);
247     if (pvt->pub.tag != NULL)
248         free(pvt->pub.tag);
249     if (pvt->pub.tag_nomedianum != NULL)
250         free(pvt->pub.tag_nomedianum);
251 
252     CALL_SMETHOD(pvt->pub.rtcp->rcnt, decref);
253     CALL_SMETHOD(pvt->pub.rtp->rcnt, decref);
254     free(pvt);
255 }
256 
257 int
compare_session_tags(const char * tag1,const char * tag0,unsigned * medianum_p)258 compare_session_tags(const char *tag1, const char *tag0, unsigned *medianum_p)
259 {
260     size_t len0 = strlen(tag0);
261 
262     if (!strncmp(tag1, tag0, len0)) {
263 	if (tag1[len0] == ';') {
264 	    if (medianum_p != NULL)
265 		*medianum_p = strtoul(tag1 + len0 + 1, NULL, 10);
266 	    return 2;
267 	}
268 	if (tag1[len0] == '\0')
269 	    return 1;
270 	return 0;
271     }
272     return 0;
273 }
274 
275 struct session_match_args {
276     const char *from_tag;
277     const char *to_tag;
278     struct rtpp_session *sp;
279     int rval;
280 };
281 
282 static int
rtpp_session_ematch(void * dp,void * ap)283 rtpp_session_ematch(void *dp, void *ap)
284 {
285     struct rtpp_session *rsp;
286     struct session_match_args *map;
287     const char *cp1, *cp2;
288 
289     rsp = (struct rtpp_session *)dp;
290     map = (struct session_match_args *)ap;
291 
292     if (strcmp(rsp->tag, map->from_tag) == 0) {
293         map->rval = 0;
294         goto found;
295     }
296     if (map->to_tag != NULL) {
297         switch (compare_session_tags(rsp->tag, map->to_tag, NULL)) {
298         case 1:
299             /* Exact tag match */
300             map->rval = 1;
301             goto found;
302 
303         case 2:
304             /*
305              * Reverse tag match without medianum. Medianum is always
306              * applied to the from tag, verify that.
307              */
308             cp1 = strrchr(rsp->tag, ';');
309             cp2 = strrchr(map->from_tag, ';');
310             if (cp2 != NULL && strcmp(cp1, cp2) == 0) {
311                 map->rval = 1;
312                 goto found;
313             }
314             break;
315 
316         default:
317             break;
318         }
319     }
320     return (RTPP_HT_MATCH_CONT);
321 
322 found:
323     CALL_SMETHOD(rsp->rcnt, incref);
324     RTPP_DBG_ASSERT(map->sp == NULL);
325     map->sp = rsp;
326     return (RTPP_HT_MATCH_BRK);
327 }
328 
329 int
find_stream(struct cfg * cf,const char * call_id,const char * from_tag,const char * to_tag,struct rtpp_session ** spp)330 find_stream(struct cfg *cf, const char *call_id, const char *from_tag,
331   const char *to_tag, struct rtpp_session **spp)
332 {
333     struct session_match_args ma;
334 
335     memset(&ma, '\0', sizeof(ma));
336     ma.from_tag = from_tag;
337     ma.to_tag = to_tag;
338     ma.rval = -1;
339 
340     CALL_METHOD(cf->stable->sessions_ht, foreach_key, call_id,
341       rtpp_session_ematch, &ma);
342     if (ma.rval != -1) {
343         *spp = ma.sp;
344     }
345     return ma.rval;
346 }
347