/src/rtpproxy/src/rtpp_record.c
Line | Count | Source (jump to first uncovered line) |
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 | | #if defined(LINUX_XXX) |
30 | | #undef _GNU_SOURCE |
31 | | #define __FAVOR_BSD |
32 | | #endif |
33 | | |
34 | | #include <sys/types.h> |
35 | | #include <sys/socket.h> |
36 | | #include <sys/stat.h> |
37 | | #include <sys/uio.h> |
38 | | #include <netinet/in.h> |
39 | | #include <netinet/ip.h> |
40 | | #include <netinet/ip6.h> |
41 | | #include <netinet/udp.h> |
42 | | #include <fcntl.h> |
43 | | #include <limits.h> |
44 | | #include <netdb.h> |
45 | | #include <pthread.h> |
46 | | #include <stddef.h> |
47 | | #include <stdio.h> |
48 | | #include <stdlib.h> |
49 | | #include <stdint.h> |
50 | | #include <string.h> |
51 | | #include <unistd.h> |
52 | | |
53 | | #include "config.h" |
54 | | |
55 | | #include "rtp.h" |
56 | | #include "rtpp_time.h" |
57 | | #include "rtp_packet.h" |
58 | | #include "rtpp_log.h" |
59 | | #include "rtpp_cfg.h" |
60 | | #include "rtpp_ip_chksum.h" |
61 | | #include "rtpp_debug.h" |
62 | | #include "rtpp_defines.h" |
63 | | #include "rtpp_types.h" |
64 | | #include "rtpp_refcnt.h" |
65 | | #include "rtpp_log_obj.h" |
66 | | #include "rtpp_mallocs.h" |
67 | | #include "rtpp_network.h" |
68 | | #include "rtpp_record.h" |
69 | | #include "rtpp_record_fin.h" |
70 | | #include "rtpp_record_adhoc.h" |
71 | | #include "rtpp_record_private.h" |
72 | | #include "rtpp_session.h" |
73 | | #include "rtpp_stream.h" |
74 | | #include "rtpp_time.h" |
75 | | #include "rtpp_util.h" |
76 | | #include "rtpp_pipe.h" |
77 | | #include "rtpp_netaddr.h" |
78 | | #include "advanced/pproc_manager.h" |
79 | | |
80 | | enum record_mode {MODE_LOCAL_PKT, MODE_REMOTE_RTP, MODE_LOCAL_PCAP}; /* MODE_LOCAL_RTP/MODE_REMOTE_PKT? */ |
81 | | |
82 | | struct rtpp_record_channel { |
83 | | struct rtpp_record pub; |
84 | | pthread_mutex_t lock; |
85 | | char spath[PATH_MAX + 1]; |
86 | | char rpath[PATH_MAX + 1]; |
87 | | int fd; |
88 | | int needspool; |
89 | | char rbuf[4096]; |
90 | | int rbuf_len; |
91 | | enum record_mode mode; |
92 | | int record_single_file; |
93 | | const char *proto; |
94 | | struct rtpp_log *log; |
95 | | struct rtpp_timestamp epoch; |
96 | | }; |
97 | | |
98 | | static void rtpp_record_write(struct rtpp_record *, const struct pkt_proc_ctx *); |
99 | | static void rtpp_record_close(struct rtpp_record_channel *); |
100 | | static int get_hdr_size(const struct sockaddr *); |
101 | | |
102 | | #if HAVE_SO_TS_CLOCK |
103 | | #define ARRIVAL_TIME(rp, pp) (rp->epoch.wall + (pp->rtime.mono - rp->epoch.mono)) |
104 | | #else |
105 | 0 | #define ARRIVAL_TIME(rp, pp) (pp->rtime.wall) |
106 | | #endif |
107 | | |
108 | | DEFINE_SMETHODS(rtpp_record, |
109 | | .pktwrite = &rtpp_record_write |
110 | | ); |
111 | | |
112 | | static int |
113 | | ropen_remote_ctor_pa(struct rtpp_record_channel *rrc, struct rtpp_log *log, |
114 | | const char *rname, int is_rtcp) |
115 | 0 | { |
116 | 0 | char *cp, *tmp; |
117 | 0 | int n, port; |
118 | 0 | struct sockaddr_storage raddr; |
119 | |
|
120 | 0 | tmp = strdup(rname + 4); |
121 | 0 | if (tmp == NULL) { |
122 | 0 | RTPP_ELOG(log, RTPP_LOG_ERR, "can't allocate memory"); |
123 | 0 | goto e0; |
124 | 0 | } |
125 | 0 | rrc->mode = MODE_REMOTE_RTP; |
126 | 0 | rrc->needspool = 0; |
127 | 0 | cp = strrchr(tmp, ':'); |
128 | 0 | if (cp == NULL) { |
129 | 0 | RTPP_LOG(log, RTPP_LOG_ERR, "remote recording target specification should include port number"); |
130 | 0 | goto e1; |
131 | 0 | } |
132 | 0 | *cp = '\0'; |
133 | 0 | cp++; |
134 | |
|
135 | 0 | if (is_rtcp) { |
136 | | /* Handle RTCP (increase target port by 1) */ |
137 | 0 | port = atoi(cp); |
138 | 0 | if (port <= 0 || port > 65534) { |
139 | 0 | RTPP_LOG(log, RTPP_LOG_ERR, "invalid port in the remote recording target specification"); |
140 | 0 | goto e1; |
141 | 0 | } |
142 | 0 | sprintf(cp, "%d", port + 1); |
143 | 0 | } |
144 | | |
145 | 0 | n = resolve(sstosa(&raddr), AF_INET, tmp, cp, AI_PASSIVE); |
146 | 0 | if (n != 0) { |
147 | 0 | RTPP_LOG(log, RTPP_LOG_ERR, "ropen: getaddrinfo: %s", gai_strerror(n)); |
148 | 0 | goto e1; |
149 | 0 | } |
150 | 0 | rrc->fd = socket(AF_INET, SOCK_DGRAM, 0); |
151 | 0 | if (rrc->fd == -1) { |
152 | 0 | RTPP_ELOG(log, RTPP_LOG_ERR, "ropen: can't create socket"); |
153 | 0 | goto e1; |
154 | 0 | } |
155 | 0 | if (connect(rrc->fd, sstosa(&raddr), SA_LEN(sstosa(&raddr))) == -1) { |
156 | 0 | RTPP_ELOG(log, RTPP_LOG_ERR, "ropen: can't connect socket"); |
157 | 0 | goto e2; |
158 | 0 | } |
159 | 0 | free(tmp); |
160 | 0 | return (0); |
161 | | |
162 | 0 | e2: |
163 | 0 | close(rrc->fd); |
164 | 0 | e1: |
165 | 0 | free(tmp); |
166 | 0 | e0: |
167 | 0 | return (-1); |
168 | 0 | } |
169 | | |
170 | | struct rtpp_record * |
171 | | rtpp_record_ctor(const struct rtpp_cfg *cfsp, struct rtpp_session *sp, |
172 | | const char *rname, int orig, int record_type) |
173 | 0 | { |
174 | 0 | struct rtpp_record_channel *rrc; |
175 | 0 | const char *sdir, *suffix1, *suffix2; |
176 | 0 | int rval, remote; |
177 | 0 | pcap_hdr_t pcap_hdr; |
178 | |
|
179 | 0 | remote = (rname != NULL && strncmp("udp:", rname, 4) == 0) ? 1 : 0; |
180 | |
|
181 | 0 | rrc = rtpp_rzmalloc(sizeof(*rrc), PVT_RCOFFS(rrc)); |
182 | 0 | if (rrc == NULL) { |
183 | 0 | RTPP_ELOG(sp->log, RTPP_LOG_ERR, "can't allocate memory"); |
184 | 0 | goto e0; |
185 | 0 | } |
186 | 0 | if (pthread_mutex_init(&rrc->lock, NULL) != 0) |
187 | 0 | goto e1; |
188 | | |
189 | 0 | rrc->record_single_file = (record_type == RECORD_BOTH) ? 1 : 0; |
190 | 0 | if (rrc->record_single_file != 0) { |
191 | 0 | rrc->proto = "RTP/RTCP"; |
192 | 0 | } else { |
193 | 0 | rrc->proto = (record_type == RECORD_RTP) ? "RTP" : "RTCP"; |
194 | 0 | } |
195 | 0 | rrc->log = sp->log; |
196 | 0 | RTPP_OBJ_INCREF(sp->log); |
197 | | #if defined(RTPP_DEBUG) |
198 | | rrc->pub.smethods = rtpp_record_smethods; |
199 | | #endif |
200 | 0 | if (remote) { |
201 | 0 | rval = ropen_remote_ctor_pa(rrc, sp->log, rname, (record_type == RECORD_RTCP)); |
202 | 0 | if (rval < 0) { |
203 | 0 | goto e2; |
204 | 0 | } |
205 | 0 | CALL_SMETHOD(rrc->pub.rcnt, attach, (rtpp_refcnt_dtor_t)&rtpp_record_close, |
206 | 0 | rrc); |
207 | 0 | return (&rrc->pub); |
208 | 0 | } |
209 | | |
210 | 0 | if (cfsp->rdir == NULL) { |
211 | 0 | RTPP_LOG(sp->log, RTPP_LOG_ERR, "directory for saving local recordings is not configured"); |
212 | 0 | goto e2; |
213 | 0 | } |
214 | | |
215 | 0 | if (cfsp->record_pcap != 0) { |
216 | 0 | rrc->mode = MODE_LOCAL_PCAP; |
217 | 0 | } else { |
218 | 0 | rrc->mode = MODE_LOCAL_PKT; |
219 | 0 | } |
220 | |
|
221 | 0 | if (rrc->record_single_file != 0) { |
222 | 0 | suffix1 = suffix2 = ""; |
223 | 0 | if (rrc->mode == MODE_LOCAL_PCAP && rname == NULL) { |
224 | 0 | suffix2 = ".pcap"; |
225 | 0 | } |
226 | 0 | } else { |
227 | 0 | suffix1 = (orig != 0) ? ".o" : ".a"; |
228 | 0 | suffix2 = (record_type == RECORD_RTP) ? ".rtp" : ".rtcp"; |
229 | 0 | } |
230 | 0 | if (cfsp->sdir == NULL) { |
231 | 0 | sdir = cfsp->rdir; |
232 | 0 | rrc->needspool = 0; |
233 | 0 | } else { |
234 | 0 | sdir = cfsp->sdir; |
235 | 0 | rrc->needspool = 1; |
236 | 0 | if (rname == NULL) { |
237 | 0 | sprintf(rrc->rpath, "%s/%.*s=%.*s%s%s", cfsp->rdir, (int)sp->call_id->len, |
238 | 0 | sp->call_id->s, (int)sp->from_tag_nmn->len, sp->from_tag_nmn->s, |
239 | 0 | suffix1, suffix2); |
240 | 0 | } else { |
241 | 0 | sprintf(rrc->rpath, "%s/%s%s", cfsp->rdir, rname, suffix2); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | if (rname == NULL) { |
245 | 0 | sprintf(rrc->spath, "%s/%.*s=%.*s%s%s", sdir, (int)sp->call_id->len, |
246 | 0 | sp->call_id->s, (int)sp->from_tag_nmn->len, sp->from_tag_nmn->s, |
247 | 0 | suffix1, suffix2); |
248 | 0 | } else { |
249 | 0 | sprintf(rrc->spath, "%s/%s%s", sdir, rname, suffix2); |
250 | 0 | } |
251 | 0 | rrc->fd = open(rrc->spath, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); |
252 | 0 | if (rrc->fd == -1) { |
253 | 0 | RTPP_ELOG(sp->log, RTPP_LOG_ERR, "can't open file %s for writing", |
254 | 0 | rrc->spath); |
255 | 0 | goto e2; |
256 | 0 | } |
257 | | |
258 | 0 | if (rrc->mode == MODE_LOCAL_PCAP) { |
259 | 0 | pcap_hdr.magic_number = PCAP_MAGIC; |
260 | 0 | pcap_hdr.version_major = PCAP_VER_MAJR; |
261 | 0 | pcap_hdr.version_minor = PCAP_VER_MINR; |
262 | 0 | pcap_hdr.thiszone = 0; |
263 | 0 | pcap_hdr.sigfigs = 0; |
264 | 0 | pcap_hdr.snaplen = 65535; |
265 | 0 | pcap_hdr.network = PCAP_FORMAT; |
266 | 0 | rval = write(rrc->fd, &pcap_hdr, sizeof(pcap_hdr)); |
267 | 0 | if (rval == -1) { |
268 | 0 | RTPP_ELOG(sp->log, RTPP_LOG_ERR, "%s: error writing header", |
269 | 0 | rrc->spath); |
270 | 0 | goto e3; |
271 | 0 | } |
272 | 0 | if (rval < sizeof(pcap_hdr)) { |
273 | 0 | RTPP_LOG(sp->log, RTPP_LOG_ERR, "%s: short write writing header", |
274 | 0 | rrc->spath); |
275 | 0 | goto e3; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | 0 | CALL_SMETHOD(rrc->pub.rcnt, attach, (rtpp_refcnt_dtor_t)&rtpp_record_close, |
280 | 0 | rrc); |
281 | 0 | return (&rrc->pub); |
282 | | |
283 | 0 | e3: |
284 | 0 | close(rrc->fd); |
285 | 0 | e2: |
286 | 0 | RTPP_OBJ_DECREF(rrc->log); |
287 | 0 | RTPP_OBJ_DECREF(&(rrc->pub)); |
288 | 0 | pthread_mutex_destroy(&rrc->lock); |
289 | 0 | e1: |
290 | 0 | free(rrc); |
291 | 0 | e0: |
292 | 0 | return NULL; |
293 | 0 | } |
294 | | |
295 | | static int |
296 | | flush_rbuf(struct rtpp_record_channel *rrc) |
297 | 0 | { |
298 | 0 | int rval; |
299 | |
|
300 | 0 | rval = write(rrc->fd, rrc->rbuf, rrc->rbuf_len); |
301 | 0 | if (rval != -1) { |
302 | 0 | rrc->rbuf_len = 0; |
303 | 0 | return 0; |
304 | 0 | } |
305 | | |
306 | 0 | RTPP_ELOG(rrc->log, RTPP_LOG_ERR, "error while recording session (%s)", |
307 | 0 | rrc->proto); |
308 | | /* Prevent futher writing if error happens */ |
309 | 0 | close(rrc->fd); |
310 | 0 | rrc->fd = -1; |
311 | 0 | return -1; |
312 | 0 | } |
313 | | |
314 | | union anyhdr { |
315 | | union pkt_hdr_pcap pcap; |
316 | | struct pkt_hdr_adhoc adhoc; |
317 | | }; |
318 | | |
319 | | struct prepare_pkt_hdr_args { |
320 | | const struct rtp_packet *packet; |
321 | | union anyhdr *hdrp; |
322 | | const struct sockaddr *daddr; |
323 | | const struct sockaddr *ldaddr; |
324 | | int ldport; |
325 | | int face; |
326 | | double atime_wall; |
327 | | }; |
328 | | |
329 | | DEFINE_RAW_METHOD(prepare_pkt_hdr, int, const struct prepare_pkt_hdr_args *); |
330 | | |
331 | | static int |
332 | | prepare_pkt_hdr_adhoc(const struct prepare_pkt_hdr_args *phap) |
333 | 0 | { |
334 | 0 | struct pkt_hdr_adhoc *ap; |
335 | |
|
336 | 0 | ap = &(phap->hdrp->adhoc); |
337 | 0 | memset(ap, 0, sizeof(*ap)); |
338 | 0 | ap->time = phap->atime_wall; |
339 | 0 | switch (sstosa(&phap->packet->raddr)->sa_family) { |
340 | 0 | case AF_INET: |
341 | 0 | ap->addr.in4.sin_family = sstosa(&phap->packet->raddr)->sa_family; |
342 | 0 | ap->addr.in4.sin_port = satosin(&phap->packet->raddr)->sin_port; |
343 | 0 | ap->addr.in4.sin_addr = satosin(&phap->packet->raddr)->sin_addr; |
344 | 0 | break; |
345 | | |
346 | 0 | case AF_INET6: |
347 | 0 | ap->addr.in6.sin_family = sstosa(&phap->packet->raddr)->sa_family; |
348 | 0 | ap->addr.in6.sin_port = satosin6(&phap->packet->raddr)->sin6_port; |
349 | 0 | ap->addr.in6.sin_addr = satosin6(&phap->packet->raddr)->sin6_addr; |
350 | 0 | break; |
351 | | |
352 | 0 | default: |
353 | 0 | abort(); |
354 | 0 | } |
355 | | |
356 | 0 | ap->plen = phap->packet->size; |
357 | 0 | return 0; |
358 | 0 | } |
359 | | |
360 | | static uint16_t ip_id = 0; |
361 | | |
362 | | #if (PCAP_FORMAT != DLT_NULL) |
363 | | static void |
364 | | fake_ether_addr(const struct sockaddr *addr, uint8_t *eaddr) |
365 | 0 | { |
366 | 0 | uint8_t *ra; |
367 | 0 | int i; |
368 | |
|
369 | 0 | switch (addr->sa_family) { |
370 | 0 | case AF_INET: |
371 | 0 | eaddr[0] = eaddr[1] = 0; |
372 | 0 | memcpy(eaddr + 2, &(satosin(addr)->sin_addr), 4); |
373 | 0 | return; |
374 | | |
375 | 0 | case AF_INET6: |
376 | 0 | ra = &satosin6(addr)->sin6_addr.s6_addr[0]; |
377 | 0 | memcpy(eaddr, ra, 6); |
378 | 0 | for (i = 6; i < sizeof(struct in6_addr); i++) { |
379 | 0 | eaddr[i % 6] ^= ra[i]; |
380 | 0 | } |
381 | 0 | return; |
382 | | |
383 | 0 | default: |
384 | 0 | break; |
385 | 0 | } |
386 | 0 | abort(); |
387 | 0 | } |
388 | | #endif |
389 | | |
390 | | static int |
391 | | prepare_pkt_hdr_pcap(const struct prepare_pkt_hdr_args *phap) |
392 | 0 | { |
393 | 0 | const struct sockaddr *src_addr, *dst_addr; |
394 | 0 | uint16_t src_port, dst_port; |
395 | 0 | pcaprec_hdr_t phd; |
396 | 0 | union { |
397 | 0 | struct ip6_hdr *v6; |
398 | 0 | struct ip *v4; |
399 | 0 | } ipp; |
400 | 0 | struct udphdr *udp; |
401 | 0 | int pcap_size; |
402 | 0 | struct timeval rtimeval; |
403 | 0 | #if (PCAP_FORMAT != DLT_NULL) |
404 | 0 | struct sockaddr_storage tmp_addr; |
405 | 0 | struct layer2_hdr *ether; |
406 | 0 | #endif |
407 | |
|
408 | 0 | if (phap->face == 0) { |
409 | 0 | src_addr = sstosa(&(phap->packet->raddr)); |
410 | 0 | src_port = getnport(src_addr); |
411 | 0 | dst_addr = phap->packet->laddr; |
412 | 0 | dst_port = htons(phap->packet->lport); |
413 | 0 | } else { |
414 | 0 | src_addr = phap->ldaddr; |
415 | 0 | src_port = htons(phap->ldport); |
416 | 0 | dst_addr = phap->daddr; |
417 | 0 | dst_port = getnport(dst_addr); |
418 | 0 | } |
419 | |
|
420 | | #if 0 |
421 | | if (src_addr->sa_family != AF_INET) { |
422 | | RTPP_ELOG(phap->log, RTPP_LOG_ERR, "only AF_INET pcap format is supported"); |
423 | | return -1; |
424 | | } |
425 | | #endif |
426 | |
|
427 | 0 | union pkt_hdr_pcap *pcp = &(phap->hdrp->pcap); |
428 | 0 | memset(pcp, 0, get_hdr_size(src_addr)); |
429 | 0 | memset(&phd, 0, sizeof(phd)); |
430 | |
|
431 | | #if (PCAP_FORMAT == DLT_NULL) |
432 | | pcap_size = (src_addr->sa_family == AF_INET) ? sizeof(pcp->null) : |
433 | | sizeof(pcp->null_v6); |
434 | | #else |
435 | 0 | pcap_size = (src_addr->sa_family == AF_INET) ? sizeof(pcp->en10t) : |
436 | 0 | sizeof(pcp->en10t_v6); |
437 | 0 | #endif |
438 | |
|
439 | 0 | dtime2timeval(phap->atime_wall, &rtimeval); |
440 | |
|
441 | 0 | RTPP_DBG_ASSERT(SEC(&rtimeval) > 0 && SEC(&rtimeval) <= UINT32_MAX); |
442 | 0 | RTPP_DBG_ASSERT(USEC(&rtimeval) < USEC_MAX); |
443 | |
|
444 | 0 | phd.ts_sec = SEC(&rtimeval); |
445 | 0 | phd.ts_usec = USEC(&rtimeval); |
446 | 0 | phd.orig_len = phd.incl_len = pcap_size - |
447 | 0 | sizeof(phd) + phap->packet->size; |
448 | |
|
449 | | #if (PCAP_FORMAT == DLT_NULL) |
450 | | if (src_addr->sa_family == AF_INET) { |
451 | | memcpy(&pcp->null.pcaprec_hdr, &phd, sizeof(phd)); |
452 | | pcp->null.family = src_addr->sa_family; |
453 | | ipp.v4 = &(pcp->null.udpip.iphdr); |
454 | | udp = &(pcp->null.udpip.udphdr); |
455 | | } else { |
456 | | memcpy(&pcp->null_v6.pcaprec_hdr, &phd, sizeof(phd)); |
457 | | pcp->null_v6.family = src_addr->sa_family; |
458 | | ipp.v6 = &(pcp->null_v6.udpip6.iphdr); |
459 | | udp = &(pcp->null_v6.udpip6.udphdr); |
460 | | } |
461 | | #else |
462 | | /* Prepare fake ethernet header */ |
463 | 0 | if (src_addr->sa_family == AF_INET) { |
464 | 0 | memcpy(&pcp->en10t.pcaprec_hdr, &phd, sizeof(phd)); |
465 | 0 | ether = &pcp->en10t.ether; |
466 | 0 | ether->type = ETHERTYPE_INET; |
467 | 0 | udp = &(pcp->en10t.udpip.udphdr); |
468 | 0 | ipp.v4 = &(pcp->en10t.udpip.iphdr); |
469 | 0 | } else { |
470 | 0 | memcpy(&pcp->en10t_v6.pcaprec_hdr, &phd, sizeof(phd)); |
471 | 0 | ether = &pcp->en10t_v6.ether; |
472 | 0 | ether->type = ETHERTYPE_INET6; |
473 | 0 | udp = &(pcp->en10t_v6.udpip6.udphdr); |
474 | 0 | ipp.v6 = &(pcp->en10t_v6.udpip6.iphdr); |
475 | 0 | } |
476 | 0 | if (phap->face == 0 && ishostnull(dst_addr) && !ishostnull(src_addr)) { |
477 | 0 | if (local4remote(src_addr, &tmp_addr) == 0) { |
478 | 0 | dst_addr = sstosa(&tmp_addr); |
479 | 0 | } else { |
480 | 0 | return -1; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | fake_ether_addr(dst_addr, ether->dhost); |
484 | 0 | if (phap->face != 0 && ishostnull(src_addr) && !ishostnull(dst_addr)) { |
485 | 0 | if (local4remote(dst_addr, &tmp_addr) == 0) { |
486 | 0 | src_addr = sstosa(&tmp_addr); |
487 | 0 | } else { |
488 | 0 | return -1; |
489 | 0 | } |
490 | 0 | } |
491 | 0 | fake_ether_addr(src_addr, ether->shost); |
492 | 0 | #endif |
493 | | |
494 | | /* Prepare fake IP header */ |
495 | 0 | if (src_addr->sa_family == AF_INET) { |
496 | 0 | ipp.v4->ip_v = 4; |
497 | 0 | ipp.v4->ip_hl = sizeof(*ipp.v4) >> 2; |
498 | 0 | ipp.v4->ip_len = htons(sizeof(*ipp.v4) + sizeof(*udp) + phap->packet->size); |
499 | 0 | ipp.v4->ip_src = satosin(src_addr)->sin_addr; |
500 | 0 | ipp.v4->ip_dst = satosin(dst_addr)->sin_addr; |
501 | 0 | ipp.v4->ip_p = IPPROTO_UDP; |
502 | 0 | ipp.v4->ip_id = htons(ip_id++); |
503 | 0 | ipp.v4->ip_ttl = 127; |
504 | 0 | ipp.v4->ip_sum = rtpp_in_cksum(ipp.v4, sizeof(*ipp.v4)); |
505 | 0 | } else { |
506 | 0 | ipp.v6->ip6_vfc |= IPV6_VERSION; |
507 | 0 | ipp.v6->ip6_hlim = IPV6_DEFHLIM; |
508 | 0 | ipp.v6->ip6_nxt = IPPROTO_UDP; |
509 | 0 | ipp.v6->ip6_src = satosin6(src_addr)->sin6_addr; |
510 | 0 | ipp.v6->ip6_dst = satosin6(dst_addr)->sin6_addr; |
511 | 0 | ipp.v6->ip6_plen = htons(sizeof(*udp) + phap->packet->size); |
512 | 0 | } |
513 | | |
514 | | /* Prepare fake UDP header */ |
515 | 0 | udp->uh_sport = src_port; |
516 | 0 | udp->uh_dport = dst_port; |
517 | 0 | udp->uh_ulen = htons(sizeof(*udp) + phap->packet->size); |
518 | |
|
519 | 0 | rtpp_ip_chksum_start(); |
520 | 0 | if (src_addr->sa_family == AF_INET) { |
521 | 0 | rtpp_ip_chksum_update(&(ipp.v4->ip_src), sizeof(ipp.v4->ip_src)); |
522 | 0 | rtpp_ip_chksum_update(&(ipp.v4->ip_dst), sizeof(ipp.v4->ip_dst)); |
523 | 0 | rtpp_ip_chksum_pad_v4(); |
524 | 0 | rtpp_ip_chksum_update(&(udp->uh_ulen), sizeof(udp->uh_ulen)); |
525 | 0 | } else { |
526 | 0 | uint32_t ulen32 = htonl(sizeof(*udp) + phap->packet->size); |
527 | |
|
528 | 0 | rtpp_ip_chksum_update(&ipp.v6->ip6_src, sizeof(ipp.v6->ip6_src)); |
529 | 0 | rtpp_ip_chksum_update(&ipp.v6->ip6_dst, sizeof(ipp.v6->ip6_dst)); |
530 | 0 | rtpp_ip_chksum_update(&ulen32, sizeof(ulen32)); |
531 | 0 | rtpp_ip_chksum_pad_v6(); |
532 | 0 | } |
533 | 0 | rtpp_ip_chksum_update(&(udp->uh_sport), sizeof(udp->uh_sport)); |
534 | 0 | rtpp_ip_chksum_update(&(udp->uh_dport), sizeof(udp->uh_dport)); |
535 | 0 | rtpp_ip_chksum_update(&(udp->uh_ulen), sizeof(udp->uh_ulen)); |
536 | 0 | rtpp_ip_chksum_update_data(phap->packet->data.buf, phap->packet->size); |
537 | 0 | rtpp_ip_chksum_fin(udp->uh_sum); |
538 | |
|
539 | 0 | return 0; |
540 | 0 | } |
541 | | |
542 | | static int |
543 | | get_hdr_size(const struct sockaddr *raddr) |
544 | 0 | { |
545 | 0 | int hdr_size; |
546 | |
|
547 | | #if (PCAP_FORMAT == DLT_NULL) |
548 | | if (raddr->sa_family == AF_INET) { |
549 | | hdr_size = sizeof(struct pkt_hdr_pcap_null); |
550 | | } else { |
551 | | hdr_size = sizeof(struct pkt_hdr_pcap_null_v6); |
552 | | } |
553 | | #else |
554 | 0 | if (raddr->sa_family == AF_INET) { |
555 | 0 | hdr_size = sizeof(struct pkt_hdr_pcap_en10t); |
556 | 0 | } else { |
557 | 0 | hdr_size = sizeof(struct pkt_hdr_pcap_en10t_v6); |
558 | 0 | } |
559 | 0 | #endif |
560 | 0 | return (hdr_size); |
561 | 0 | } |
562 | | |
563 | | static void |
564 | | rtpp_record_write_locked(struct rtpp_record_channel *rrc, const struct pkt_proc_ctx *pktxp) |
565 | 0 | { |
566 | 0 | struct iovec v[2]; |
567 | 0 | int rval, hdr_size; |
568 | 0 | prepare_pkt_hdr_t prepare_pkt_hdr; |
569 | 0 | const char *proto; |
570 | 0 | struct sockaddr_storage daddr; |
571 | 0 | struct rtpp_netaddr *rem_addr; |
572 | 0 | struct rtp_packet *packet = pktxp->pktp; |
573 | 0 | struct rtpp_stream *stp = pktxp->strmp_out; |
574 | |
|
575 | 0 | if (rrc->fd == -1) |
576 | 0 | return; |
577 | | |
578 | 0 | rem_addr = CALL_SMETHOD(stp, get_rem_addr, 0); |
579 | 0 | if (rem_addr == NULL) { |
580 | 0 | return; |
581 | 0 | } |
582 | 0 | CALL_SMETHOD(rem_addr, get, sstosa(&daddr), sizeof(daddr)); |
583 | 0 | RTPP_OBJ_DECREF(rem_addr); |
584 | |
|
585 | 0 | if (packet->rtime.wall == -1) { |
586 | 0 | RTPP_ELOG(stp->log, RTPP_LOG_ERR, "can't get current time"); |
587 | 0 | } |
588 | |
|
589 | 0 | if (rrc->epoch.wall == 0) { |
590 | 0 | rrc->epoch = packet->rtime; |
591 | 0 | } |
592 | |
|
593 | 0 | switch (rrc->mode) { |
594 | 0 | case MODE_REMOTE_RTP: |
595 | 0 | send(rrc->fd, packet->data.buf, packet->size, 0); |
596 | 0 | return; |
597 | | |
598 | 0 | case MODE_LOCAL_PKT: |
599 | 0 | hdr_size = sizeof(struct pkt_hdr_adhoc); |
600 | 0 | prepare_pkt_hdr = &prepare_pkt_hdr_adhoc; |
601 | 0 | break; |
602 | | |
603 | 0 | case MODE_LOCAL_PCAP: |
604 | 0 | hdr_size = get_hdr_size(sstosa(&packet->raddr)); |
605 | 0 | prepare_pkt_hdr = &prepare_pkt_hdr_pcap; |
606 | 0 | break; |
607 | | |
608 | 0 | default: |
609 | | /* Should not happen */ |
610 | 0 | abort(); |
611 | 0 | } |
612 | | |
613 | | /* Check if the write buffer has necessary space, and flush if not */ |
614 | 0 | if ((rrc->rbuf_len + hdr_size + packet->size > sizeof(rrc->rbuf)) && rrc->rbuf_len > 0) |
615 | 0 | if (flush_rbuf(rrc) != 0) |
616 | 0 | return; |
617 | | |
618 | 0 | struct prepare_pkt_hdr_args pargs = { |
619 | 0 | .packet = packet, |
620 | 0 | .ldaddr = stp->laddr, |
621 | 0 | .ldport = stp->port, |
622 | 0 | .daddr = sstosa(&daddr), |
623 | 0 | .face = (rrc->record_single_file == 0) ? 0 : (stp->pipe_type != PIPE_RTP), |
624 | 0 | .atime_wall = ARRIVAL_TIME(rrc, packet) |
625 | 0 | }; |
626 | | |
627 | | /* Check if received packet doesn't fit into the buffer, do synchronous write if so */ |
628 | 0 | if (rrc->rbuf_len + hdr_size + packet->size > sizeof(rrc->rbuf)) { |
629 | 0 | union anyhdr hdr; |
630 | 0 | pargs.hdrp = &hdr; |
631 | |
|
632 | 0 | if (prepare_pkt_hdr(&pargs) != 0) |
633 | 0 | return; |
634 | | |
635 | 0 | v[0].iov_base = (void *)&hdr; |
636 | 0 | v[0].iov_len = hdr_size; |
637 | 0 | v[1].iov_base = packet->data.buf; |
638 | 0 | v[1].iov_len = packet->size; |
639 | |
|
640 | 0 | rval = writev(rrc->fd, v, 2); |
641 | 0 | if (rval != -1) |
642 | 0 | return; |
643 | | |
644 | 0 | proto = CALL_SMETHOD(stp, get_proto); |
645 | 0 | RTPP_ELOG(stp->log, RTPP_LOG_ERR, "error while recording session (%s)", |
646 | 0 | proto); |
647 | | /* Prevent futher writing if error happens */ |
648 | 0 | close(rrc->fd); |
649 | 0 | rrc->fd = -1; |
650 | 0 | return; |
651 | 0 | } |
652 | 0 | pargs.hdrp = (void *)rrc->rbuf + rrc->rbuf_len; |
653 | 0 | if (prepare_pkt_hdr(&pargs) != 0) |
654 | 0 | return; |
655 | 0 | rrc->rbuf_len += hdr_size; |
656 | 0 | memcpy(rrc->rbuf + rrc->rbuf_len, packet->data.buf, packet->size); |
657 | 0 | rrc->rbuf_len += packet->size; |
658 | 0 | } |
659 | | |
660 | | static void |
661 | | rtpp_record_write(struct rtpp_record *self, const struct pkt_proc_ctx *pktxp) |
662 | 0 | { |
663 | 0 | struct rtpp_record_channel *rrc; |
664 | |
|
665 | 0 | PUB2PVT(self, rrc); |
666 | 0 | pthread_mutex_lock(&rrc->lock); |
667 | 0 | rtpp_record_write_locked(rrc, pktxp); |
668 | 0 | pthread_mutex_unlock(&rrc->lock); |
669 | 0 | } |
670 | | |
671 | | static void |
672 | | rtpp_record_close(struct rtpp_record_channel *rrc) |
673 | 0 | { |
674 | 0 | static int keep = 1; |
675 | |
|
676 | 0 | rtpp_record_fin(&rrc->pub); |
677 | 0 | if (rrc->mode != MODE_REMOTE_RTP && rrc->rbuf_len > 0) |
678 | 0 | flush_rbuf(rrc); |
679 | |
|
680 | 0 | if (rrc->fd != -1) |
681 | 0 | close(rrc->fd); |
682 | |
|
683 | 0 | if (rrc->mode == MODE_REMOTE_RTP) |
684 | 0 | goto done; |
685 | | |
686 | 0 | if (keep == 0) { |
687 | 0 | if (unlink(rrc->spath) == -1) |
688 | 0 | RTPP_ELOG(rrc->log, RTPP_LOG_ERR, "can't remove " |
689 | 0 | "session record %s", rrc->spath); |
690 | 0 | } else if (rrc->needspool == 1) { |
691 | 0 | if (rename(rrc->spath, rrc->rpath) == -1) |
692 | 0 | RTPP_ELOG(rrc->log, RTPP_LOG_ERR, "can't move " |
693 | 0 | "session record from spool into permanent storage"); |
694 | 0 | } |
695 | 0 | done: |
696 | 0 | RTPP_OBJ_DECREF(rrc->log); |
697 | 0 | pthread_mutex_destroy(&rrc->lock); |
698 | 0 | free(rrc); |
699 | 0 | } |