File: | tport_logging.c |
Warning: | line 835, column 12 Potential memory leak |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * This file is part of the Sofia-SIP package | |||
3 | * | |||
4 | * Copyright (C) 2006 Nokia Corporation. | |||
5 | * | |||
6 | * Contact: Pekka Pessi <pekka.pessi@nokia.com> | |||
7 | * | |||
8 | * This library is free software; you can redistribute it and/or | |||
9 | * modify it under the terms of the GNU Lesser General Public License | |||
10 | * as published by the Free Software Foundation; either version 2.1 of | |||
11 | * the License, or (at your option) any later version. | |||
12 | * | |||
13 | * This library is distributed in the hope that it will be useful, but | |||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
16 | * Lesser General Public License for more details. | |||
17 | * | |||
18 | * You should have received a copy of the GNU Lesser General Public | |||
19 | * License along with this library; if not, write to the Free Software | |||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||
21 | * 02110-1301 USA | |||
22 | * | |||
23 | */ | |||
24 | ||||
25 | /**@CFILE tport_logging.c Logging transported messages. | |||
26 | * | |||
27 | * See tport.docs for more detailed description of tport interface. | |||
28 | * | |||
29 | * @author Pekka Pessi <Pekka.Pessi@nokia.com> | |||
30 | * @author Martti Mela <Martti.Mela@nokia.com> | |||
31 | * | |||
32 | * @date Created: Fri Mar 24 08:45:49 EET 2006 ppessi | |||
33 | */ | |||
34 | ||||
35 | #include "config.h" | |||
36 | #include "msg_internal.h" | |||
37 | ||||
38 | #include "tport_internal.h" | |||
39 | ||||
40 | #include <sofia-sip/su.h> | |||
41 | #include <sofia-sip/su_string.h> | |||
42 | #include <stdlib.h> | |||
43 | #include <time.h> | |||
44 | #include <assert.h> | |||
45 | #include <errno(*__errno_location ()).h> | |||
46 | #include <limits.h> | |||
47 | ||||
48 | #define TPORT_STAMP_SIZE144 144 | |||
49 | ||||
50 | /**@var TPORT_LOG | |||
51 | * | |||
52 | * Environment variable determining if parsed message contents are logged. | |||
53 | * | |||
54 | * If the TPORT_LOG environment variable is set, the tport module logs the | |||
55 | * contents of parsed messages. This eases debugging the signaling greatly. | |||
56 | * | |||
57 | * @sa TPORT_DUMP, TPORT_DEBUG, tport_log | |||
58 | */ | |||
59 | #ifdef DOXYGEN | |||
60 | extern char const TPORT_LOG[]; /* dummy declaration for Doxygen */ | |||
61 | #endif | |||
62 | ||||
63 | /**@var TPORT_DUMP | |||
64 | * | |||
65 | * Environment variable for transport data dump. | |||
66 | * | |||
67 | * The received and sent data is dumped to the file specified by TPORT_DUMP | |||
68 | * environment variable. This can be used to save message traces and help | |||
69 | * hairy debugging tasks. | |||
70 | * | |||
71 | * @sa TPORT_LOG, TPORT_DEBUG, tport_log | |||
72 | */ | |||
73 | #ifdef DOXYGEN | |||
74 | extern char const TPORT_DUMP[]; /* dummy declaration for Doxygen */ | |||
75 | #endif | |||
76 | ||||
77 | /**@var TPORT_CAPT | |||
78 | * | |||
79 | * Environment variable for transport data capturing. | |||
80 | * | |||
81 | * The received and sent data is dumped to the capture server specified by TPORT_CAPT | |||
82 | * environment variable. This can be used to save message traces into database and help | |||
83 | * hairy debugging tasks. | |||
84 | * | |||
85 | * @sa TPORT_LOG, TPORT_DEBUG, TPORT_CAPT, TPORT_CAPT_SRC, tport_log | |||
86 | */ | |||
87 | #ifdef DOXYGEN | |||
88 | extern char const TPORT_CAPT[]; /* dummy declaration for Doxygen */ | |||
89 | #endif | |||
90 | ||||
91 | /**@var TPORT_DEBUG | |||
92 | * | |||
93 | * Environment variable determining the debug log level for @b tport module. | |||
94 | * | |||
95 | * The TPORT_DEBUG environment variable is used to determine the debug logging | |||
96 | * level for @b tport module. The default level is 3. | |||
97 | * | |||
98 | * @sa <sofia-sip/su_debug.h>, tport_log, SOFIA_DEBUG | |||
99 | */ | |||
100 | #ifdef DOXYGEN | |||
101 | extern char const TPORT_DEBUG[]; /* dummy declaration for Doxygen */ | |||
102 | #endif | |||
103 | ||||
104 | /**Debug log for @b tport module. | |||
105 | * | |||
106 | * The tport_log is the log object used by @b tport module. The level of | |||
107 | * #tport_log is set using #TPORT_DEBUG environment variable. | |||
108 | */ | |||
109 | su_log_t tport_log[] = { | |||
110 | SU_LOG_INIT("tport", "TPORT_DEBUG", SU_DEBUG){ sizeof(su_log_t), "tport", "TPORT_DEBUG", 0, SU_LOG_MAX, 0, ((void*)0), ((void*)0), } | |||
111 | }; | |||
112 | ||||
113 | ||||
114 | ||||
115 | /** Initialize logging. */ | |||
116 | int tport_open_log(tport_master_t *mr, tagi_t *tags) | |||
117 | { | |||
118 | int n; | |||
119 | int log_msg = mr->mr_log != 0; | |||
120 | char const *dump = NULL((void*)0); | |||
121 | char const *capt = NULL((void*)0);; | |||
122 | char const *capt_src = NULL((void*)0); | |||
123 | ||||
124 | if(mr->mr_capt_name) capt = mr->mr_capt_name; | |||
125 | ||||
126 | n = tl_gets(tags, | |||
127 | TPTAG_LOG_REF(log_msg)tptag_log_ref, tag_bool_vr(&(log_msg)), | |||
128 | TPTAG_DUMP_REF(dump)tptag_dump_ref, tag_str_vr(&(dump)), | |||
129 | TPTAG_CAPT_REF(capt)tptag_capt_ref, tag_str_vr(&(capt)), | |||
130 | TPTAG_CAPT_SRC_REF(capt_src)tptag_capt_src_ref, tag_str_vr(&(capt_src)), | |||
131 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||
132 | ||||
133 | if (getenv("MSG_STREAM_LOG") != NULL((void*)0) || getenv("TPORT_LOG") != NULL((void*)0)) | |||
134 | log_msg = 1; | |||
135 | mr->mr_log = log_msg ? MSG_DO_EXTRACT_COPYMSG_FLG_EXTRACT_COPY : 0; | |||
136 | ||||
137 | if (getenv("TPORT_CAPT")) | |||
138 | capt = getenv("TPORT_CAPT"); | |||
139 | if (getenv("MSG_DUMP")) | |||
140 | dump = getenv("MSG_DUMP"); | |||
141 | if (getenv("TPORT_DUMP")) | |||
142 | dump = getenv("TPORT_DUMP"); | |||
143 | ||||
144 | /* Overriding src address for HEP */ | |||
145 | if (capt_src && !mr->mr_capt_src_addr) { | |||
146 | su_addrinfo_t *ai = NULL((void*)0), hints[1] = {{ 0 }}; | |||
147 | hints->ai_flags = AI_NUMERICSERV0x0400; | |||
148 | hints->ai_family = AF_UNSPEC0; | |||
149 | if (su_getaddrinfo(capt_src, "", hints, &ai)) { | |||
150 | su_perror("capture_src: su_getaddrinfo()"); | |||
151 | } else { | |||
152 | mr->mr_capt_src_addr = ai; | |||
153 | } | |||
154 | } | |||
155 | ||||
156 | if(capt) { | |||
157 | ||||
158 | char *captname, *p, *host_s; | |||
159 | char port[10]; | |||
160 | su_addrinfo_t *ai = NULL((void*)0), hints[1] = {{ 0 }}; | |||
161 | unsigned len =0, iport = 0; | |||
162 | ||||
163 | ||||
164 | ||||
165 | if (mr->mr_capt_name && mr->mr_capt_sock && strcmp(capt, mr->mr_capt_name) == 0) | |||
166 | return n; | |||
167 | ||||
168 | captname = su_strdup(mr->mr_homemr_master->tp_home, capt); | |||
169 | if (captname == NULL((void*)0)) | |||
170 | return n; | |||
171 | ||||
172 | if(strncmp(captname, "udp:",4) != 0) { | |||
173 | su_log("tport_open_log: capturing. Only udp protocol supported [%s]\n", captname); | |||
174 | return n; | |||
175 | } | |||
176 | ||||
177 | /* separate proto and host */ | |||
178 | p = captname+4; | |||
179 | if( (*(p)) == '\0') { | |||
180 | su_log("malformed ip address\n"); | |||
181 | return n; | |||
182 | } | |||
183 | host_s = p; | |||
184 | ||||
185 | if( (p = strrchr(p+1, ':')) == 0 ) { | |||
186 | su_log("no host or port specified\n"); | |||
187 | return n; | |||
188 | } | |||
189 | ||||
190 | /*the address contains a port number*/ | |||
191 | *p = '\0'; | |||
192 | p++; | |||
193 | ||||
194 | iport = atoi(p); | |||
195 | ||||
196 | if (iport <1024 || iport >65536) | |||
197 | { | |||
198 | su_log("invalid port number; must be in [1024,65536]\n"); | |||
199 | return n; | |||
200 | } | |||
201 | ||||
202 | snprintf(port, sizeof(port), "%d", iport); | |||
203 | ||||
204 | /* default values for capture protocol and agent id */ | |||
205 | mr->mr_prot_ver = 3; | |||
206 | mr->mr_agent_id = 200; | |||
207 | mr->mr_agent_nodename = "default"; | |||
208 | /* get all params */ | |||
209 | while(p) | |||
210 | { | |||
211 | /* check ; in the URL */ | |||
212 | if( (p = strchr(p+1, ';')) == 0 ) { | |||
213 | break; | |||
214 | } | |||
215 | ||||
216 | *p = '\0'; | |||
217 | p++; | |||
218 | ||||
219 | SU_DEBUG_7(("events HEP RRR DATA [%s]\n", p))(((tport_log != ((void*)0) && tport_log->log_init) == 0 ? 9 : ((tport_log != ((void*)0) && tport_log-> log_init > 1) ? tport_log->log_level : su_log_default-> log_level)) >= 7 ? (_su_llog(tport_log, 7, "tport_logging.c" , (const char *)__func__, 219, "events HEP RRR DATA [%s]\n", p )) : (void)0); | |||
220 | ||||
221 | if(strncmp(p, "hep=",4) == 0) { | |||
222 | p+=4; | |||
223 | mr->mr_prot_ver = atoi(p); | |||
224 | /* hepv3 come later */ | |||
225 | if (mr->mr_prot_ver < 1 || mr->mr_prot_ver > 3) | |||
226 | { | |||
227 | su_log("invalid hep version number; must be in [1-3]\n"); | |||
228 | mr->mr_prot_ver = 3; | |||
229 | return n; | |||
230 | } | |||
231 | } | |||
232 | else if(strncmp(p, "capture_id=", 11) == 0) { | |||
233 | p+=11; | |||
234 | if((mr->mr_agent_id = atoi(p)) == 0) | |||
235 | { | |||
236 | mr->mr_agent_id = 200; | |||
237 | su_log("invalid capture id number; must be uint32 \n"); | |||
238 | return n; | |||
239 | } | |||
240 | } | |||
241 | else if(strncmp(p, "capture_nodename=", 17) == 0) { | |||
242 | p+=17; | |||
243 | mr->mr_agent_nodename = p; | |||
244 | } | |||
245 | else { | |||
246 | su_log("unsupported capture param\n"); | |||
247 | return n; | |||
248 | } | |||
249 | } | |||
250 | ||||
251 | /* check if we have [] */ | |||
252 | if (host_s[0] == '[') { | |||
253 | len = strlen(host_s + 1) - 1; | |||
254 | if(host_s[len+1] != ']') { | |||
255 | su_log("bracket not closed\n"); | |||
256 | return n; | |||
257 | } | |||
258 | memmove(host_s, host_s + 1, len); | |||
259 | host_s[len] = '\0'; | |||
260 | } | |||
261 | ||||
262 | /* and again */ | |||
263 | captname = su_strdup(mr->mr_homemr_master->tp_home, capt); | |||
264 | if (captname == NULL((void*)0)) return n; | |||
265 | ||||
266 | su_free(mr->mr_homemr_master->tp_home, mr->mr_capt_name); | |||
267 | mr->mr_capt_name = captname; | |||
268 | ||||
269 | if (mr->mr_capt_sock) | |||
270 | su_close(mr->mr_capt_sock), mr->mr_capt_sock = 0; | |||
271 | ||||
272 | /* HINTS && getaddrinfo */ | |||
273 | hints->ai_flags = AI_NUMERICSERV0x0400; | |||
274 | hints->ai_family = AF_UNSPEC0; | |||
275 | hints->ai_socktype = SOCK_DGRAMSOCK_DGRAM; | |||
276 | hints->ai_protocol = IPPROTO_UDPIPPROTO_UDP; | |||
277 | ||||
278 | if (su_getaddrinfo(host_s, port, hints, &ai)) { | |||
279 | su_perror("capture: su_getaddrinfo()"); | |||
280 | return n; | |||
281 | } | |||
282 | ||||
283 | mr->mr_capt_sock = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | |||
284 | if (mr->mr_capt_sock == INVALID_SOCKET((su_socket_t)INVALID_SOCKET)) { | |||
285 | su_perror("capture: invalid socket"); | |||
286 | return n; | |||
287 | } | |||
288 | ||||
289 | su_setblocking(mr->mr_capt_sock, 0); /* Don't block */ | |||
290 | ||||
291 | if (connect(mr->mr_capt_sock, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == -1) { | |||
292 | if (errno(*__errno_location ()) != EINPROGRESS115) { | |||
293 | su_perror("capture: socket connect"); | |||
294 | return n; | |||
295 | } | |||
296 | } | |||
297 | ||||
298 | su_freeaddrinfo(ai); | |||
299 | } | |||
300 | else if(mr->mr_capt_sock) { | |||
301 | /* close capture server*/ | |||
302 | su_close(mr->mr_capt_sock); | |||
303 | mr->mr_capt_sock = 0; | |||
304 | } | |||
305 | ||||
306 | if (dump) { | |||
307 | time_t now; | |||
308 | char *dumpname; | |||
309 | ||||
310 | if (mr->mr_dump && strcmp(dump, mr->mr_dump) == 0) | |||
311 | return n; | |||
312 | dumpname = su_strdup(mr->mr_homemr_master->tp_home, dump); | |||
313 | if (dumpname == NULL((void*)0)) | |||
314 | return n; | |||
315 | su_free(mr->mr_homemr_master->tp_home, mr->mr_dump); | |||
316 | mr->mr_dump = dumpname; | |||
317 | ||||
318 | if (mr->mr_dump_file && mr->mr_dump_file != stdoutstdout) | |||
319 | fclose(mr->mr_dump_file), mr->mr_dump_file = NULL((void*)0); | |||
320 | ||||
321 | if (strcmp(dumpname, "-")) | |||
322 | mr->mr_dump_file = fopen(dumpname, "ab"); /* XXX */ | |||
323 | else | |||
324 | mr->mr_dump_file = stdoutstdout; | |||
325 | ||||
326 | if (mr->mr_dump_file) { | |||
327 | time(&now); | |||
328 | fprintf(mr->mr_dump_file, "dump started at %s\n\n", ctime(&now)); | |||
329 | } | |||
330 | } | |||
331 | ||||
332 | return n; | |||
333 | } | |||
334 | ||||
335 | /** Create log stamp */ | |||
336 | void tport_stamp(tport_t const *self, msg_t *msg, | |||
337 | char *stamp, char const *what, | |||
338 | size_t n, char const *via, | |||
339 | su_time_t now) | |||
340 | { | |||
341 | char label[24] = ""; | |||
342 | char *comp = ""; | |||
343 | char name[SU_ADDRSIZE(48)] = ""; | |||
344 | su_sockaddr_t const *su; | |||
345 | unsigned short second, minute, hour; | |||
346 | /* should check for ifdef HAVE_LOCALTIME_R instead -_- */ | |||
347 | #if defined(HAVE_GETTIMEOFDAY1) || defined(HAVE_CLOCK_MONOTONIC1) | |||
348 | struct tm nowtm = { 0 }; | |||
349 | time_t nowtime = (now.tv_sec - SU_TIME_EPOCH2208988800UL); /* see su_time0.c 'now' is not really 'now', so we decrease it by SU_TIME_EPOCH */ | |||
350 | #endif | |||
351 | ||||
352 | assert(self)((void) sizeof ((self) ? 1 : 0), __extension__ ({ if (self) ; else __assert_fail ("self", "tport_logging.c", 352, __extension__ __PRETTY_FUNCTION__); })); assert(msg)((void) sizeof ((msg) ? 1 : 0), __extension__ ({ if (msg) ; else __assert_fail ("msg", "tport_logging.c", 352, __extension__ __PRETTY_FUNCTION__ ); })); | |||
353 | ||||
354 | #if defined(HAVE_GETTIMEOFDAY1) || defined(HAVE_CLOCK_MONOTONIC1) | |||
355 | localtime_r(&nowtime, &nowtm); | |||
356 | second = nowtm.tm_sec; | |||
357 | minute = nowtm.tm_min; | |||
358 | hour = nowtm.tm_hour; | |||
359 | #else | |||
360 | second = (unsigned short)(now.tv_sec % 60); | |||
361 | minute = (unsigned short)((now.tv_sec / 60) % 60); | |||
362 | hour = (unsigned short)((now.tv_sec / 3600) % 24); | |||
363 | #endif | |||
364 | ||||
365 | su = msg_addr(msg); | |||
366 | ||||
367 | #if SU_HAVE_IN61 | |||
368 | if (su->su_familysu_sa.sa_family == AF_INET610) { | |||
369 | if (su->su_sin6.sin6_flowinfo) | |||
370 | snprintf(label, sizeof(label), "/%u", ntohl(su->su_sin6.sin6_flowinfo)); | |||
371 | } | |||
372 | #endif | |||
373 | ||||
374 | if (msg_addrinfo(msg)->ai_flags & TP_AI_COMPRESSED0x01000) | |||
375 | comp = ";comp=sigcomp"; | |||
376 | ||||
377 | su_inet_ntopinet_ntop(su->su_familysu_sa.sa_family, SU_ADDR(su)((su)->su_sa.sa_family == 2 ? (void *)&(su)->su_sin .sin_addr : ((su)->su_sa.sa_family == 10 ? (void *)&(su )->su_sin6.sin6_addr : (void *)&(su)->su_sa.sa_data )), name, sizeof(name)); | |||
378 | ||||
379 | snprintf(stamp, TPORT_STAMP_SIZE144, | |||
380 | "%s "MOD_ZU"%zu"" bytes %s %s/[%s]:%u%s%s at %02u:%02u:%02u.%06lu:\n", | |||
381 | what, (size_t)n, via, self->tp_name->tpn_proto, | |||
382 | name, ntohs(su->su_portsu_sin.sin_port), label[0] ? label : "", comp, | |||
383 | hour, minute, second, now.tv_usec); | |||
384 | } | |||
385 | ||||
386 | /** Dump the data from the iovec */ | |||
387 | void tport_dump_iovec(tport_t const *self, msg_t *msg, | |||
388 | size_t n, su_iovec_t const iov[], size_t iovused, | |||
389 | char const *what, char const *how) | |||
390 | { | |||
391 | tport_master_t *mr; | |||
392 | char stamp[TPORT_STAMP_SIZE144]; | |||
393 | size_t i; | |||
394 | ||||
395 | assert(self)((void) sizeof ((self) ? 1 : 0), __extension__ ({ if (self) ; else __assert_fail ("self", "tport_logging.c", 395, __extension__ __PRETTY_FUNCTION__); })); assert(msg)((void) sizeof ((msg) ? 1 : 0), __extension__ ({ if (msg) ; else __assert_fail ("msg", "tport_logging.c", 395, __extension__ __PRETTY_FUNCTION__ ); })); | |||
396 | ||||
397 | mr = self->tp_master; | |||
398 | if (!mr->mr_dump_file) | |||
399 | return; | |||
400 | ||||
401 | tport_stamp(self, msg, stamp, what, n, how, su_now()); | |||
402 | fputs(stamp, mr->mr_dump_file); | |||
403 | ||||
404 | for (i = 0; i < iovused && n > 0; i++) { | |||
405 | size_t len = iov[i].mv_lensiv_len; | |||
406 | if (len > n) | |||
407 | len = n; | |||
408 | if (fwrite(iov[i].mv_basesiv_base, len, 1, mr->mr_dump_file) != 1) | |||
409 | break; | |||
410 | n -= len; | |||
411 | } | |||
412 | ||||
413 | fputs("\v\n", mr->mr_dump_file); | |||
414 | fflush(mr->mr_dump_file); | |||
415 | } | |||
416 | ||||
417 | /** Capture the data from the iovec */ | |||
418 | void tport_capt_msg(tport_t const *self, msg_t *msg, size_t n, | |||
419 | su_iovec_t const iov[], size_t iovused, char const *what) | |||
420 | { | |||
421 | ||||
422 | int buflen = 0; | |||
423 | char* buffer = NULL((void*)0); | |||
424 | tport_master_t *mr; | |||
425 | ||||
426 | assert(self)((void) sizeof ((self) ? 1 : 0), __extension__ ({ if (self) ; else __assert_fail ("self", "tport_logging.c", 426, __extension__ __PRETTY_FUNCTION__); })); | |||
| ||||
427 | ||||
428 | mr = self->tp_master; | |||
429 | ||||
430 | /* If we don't have socket, go out */ | |||
431 | if (!mr->mr_capt_sock) { | |||
432 | su_log("error: capture socket is not open\n"); | |||
433 | return; | |||
434 | } | |||
435 | ||||
436 | switch(mr->mr_prot_ver) | |||
437 | { | |||
438 | ||||
439 | case 3: | |||
440 | buflen = tport_capt_msg_hepv3(self, msg, n, iov, iovused, what, &buffer); | |||
441 | break; | |||
442 | ||||
443 | case 2: | |||
444 | case 1: | |||
445 | buflen = tport_capt_msg_hepv2(self, msg, n, iov, iovused, what, &buffer); | |||
446 | break; | |||
447 | ||||
448 | default: | |||
449 | su_log("error: unsupported hep version\n"); | |||
450 | break; | |||
451 | } | |||
452 | ||||
453 | if(buflen > 0) { | |||
454 | /* check if we have error i.e. capture server is down */ | |||
455 | if (su_soerror(mr->mr_capt_sock)) { | |||
456 | su_perror("error: tport_logging: capture socket error"); | |||
457 | goto done; | |||
458 | } | |||
459 | ||||
460 | su_send(mr->mr_capt_sock, buffer, buflen, 0)send((mr->mr_capt_sock),(buffer),(buflen),(0)); | |||
461 | } | |||
462 | ||||
463 | ||||
464 | done: | |||
465 | /* Now we release it */ | |||
466 | if(buffer) free(buffer); | |||
467 | return; | |||
468 | } | |||
469 | ||||
470 | /** Capture the data from the iovec */ | |||
471 | int tport_capt_msg_hepv2 (tport_t const *self, msg_t *msg, size_t n, | |||
472 | su_iovec_t const iov[], size_t iovused, char const *what, char **buffer) | |||
473 | { | |||
474 | ||||
475 | int buflen = 0; | |||
476 | su_sockaddr_t const *su, *su_self, *su_self_ext; | |||
477 | struct hep_hdr hep_header; | |||
478 | struct hep_timehdr hep_time = {0}; | |||
479 | su_time_t now; | |||
480 | #if __sun__ | |||
481 | struct hep_iphdr hep_ipheader = {{{{0}}}}; | |||
482 | #else | |||
483 | struct hep_iphdr hep_ipheader = {{0}}; | |||
484 | #endif | |||
485 | #if SU_HAVE_IN61 | |||
486 | struct hep_ip6hdr hep_ip6header = {{{{0}}}}; | |||
487 | #endif | |||
488 | int eth_frame_len = 16000; | |||
489 | size_t i, dst = 1; | |||
490 | tport_master_t *mr; | |||
491 | ||||
492 | assert(self)((void) sizeof ((self) ? 1 : 0), __extension__ ({ if (self) ; else __assert_fail ("self", "tport_logging.c", 492, __extension__ __PRETTY_FUNCTION__); })); assert(msg)((void) sizeof ((msg) ? 1 : 0), __extension__ ({ if (msg) ; else __assert_fail ("msg", "tport_logging.c", 492, __extension__ __PRETTY_FUNCTION__ ); })); | |||
493 | ||||
494 | su = msg_addr(msg); | |||
495 | su_self = self->tp_pri->pri_primary->tp_addr; | |||
496 | ||||
497 | mr = self->tp_master; | |||
498 | if (mr->mr_capt_src_addr) { | |||
499 | /* override SRC address with configuration */ | |||
500 | su_self_ext = (void *)mr->mr_capt_src_addr->ai_addr; | |||
501 | } else { | |||
502 | /* use the SRC address from the socket */ | |||
503 | su_self_ext = su_self; | |||
504 | } | |||
505 | ||||
506 | /* If we don't have socket, go out */ | |||
507 | if (!mr->mr_capt_sock) { | |||
508 | su_log("error: capture socket is not open\n"); | |||
509 | return 0; | |||
510 | } | |||
511 | ||||
512 | /*buffer for ethernet frame*/ | |||
513 | *buffer = (void*)malloc(eth_frame_len); | |||
514 | ||||
515 | /* VOIP Header */ | |||
516 | hep_header.hp_v = mr->mr_prot_ver; | |||
517 | hep_header.hp_f = su->su_familysu_sa.sa_family; | |||
518 | /* Header Length */ | |||
519 | hep_header.hp_l = sizeof(struct hep_hdr); | |||
520 | ||||
521 | /* PROTOCOL */ | |||
522 | if(strcmp(self->tp_name->tpn_proto, "tcp") == 0) hep_header.hp_p = IPPROTO_TCPIPPROTO_TCP; | |||
523 | else if(strcmp(self->tp_name->tpn_proto, "tls") == 0) hep_header.hp_p = IPPROTO_IDPIPPROTO_IDP; /* FAKE*/ | |||
524 | else if(strcmp(self->tp_name->tpn_proto, "sctp") == 0) hep_header.hp_p = IPPROTO_SCTPIPPROTO_SCTP; | |||
525 | else if(strcmp(self->tp_name->tpn_proto, "ws") == 0) hep_header.hp_p = IPPROTO_TCPIPPROTO_TCP; | |||
526 | else if(strcmp(self->tp_name->tpn_proto, "wss") == 0) hep_header.hp_p = IPPROTO_TCPIPPROTO_TCP; | |||
527 | else hep_header.hp_p = IPPROTO_UDPIPPROTO_UDP; /* DEFAULT UDP */ | |||
528 | ||||
529 | /* Check destination */ | |||
530 | if(strncmp("sent", what, 4) == 0) dst = 0; | |||
531 | ||||
532 | /* copy destination and source IPs*/ | |||
533 | if(su->su_familysu_sa.sa_family == AF_INET2) { | |||
534 | ||||
535 | memcpy(dst ? &hep_ipheader.hp_src : &hep_ipheader.hp_dst, &su->su_sin.sin_addr.s_addr, sizeof(su->su_sin.sin_addr.s_addr)); | |||
536 | memcpy(dst ? &hep_ipheader.hp_dst : &hep_ipheader.hp_src, &su_self_ext->su_sin.sin_addr.s_addr, sizeof(su_self_ext->su_sin.sin_addr.s_addr)); | |||
537 | hep_header.hp_l += sizeof(struct hep_iphdr); | |||
538 | } | |||
539 | #if SU_HAVE_IN61 | |||
540 | else { | |||
541 | memcpy(dst ? &hep_ip6header.hp6_src : &hep_ip6header.hp6_dst, &su->su_sin.sin_addr.s_addr, sizeof(su->su_sin.sin_addr.s_addr)); | |||
542 | memcpy(dst ? &hep_ip6header.hp6_dst : &hep_ip6header.hp6_src, &su_self_ext->su_sin.sin_addr.s_addr, sizeof(su_self_ext->su_sin.sin_addr.s_addr)); | |||
543 | hep_header.hp_l += sizeof(struct hep_ip6hdr); | |||
544 | } | |||
545 | #endif | |||
546 | ||||
547 | hep_header.hp_dport = dst ? su_self->su_portsu_sin.sin_port : su->su_portsu_sin.sin_port; | |||
548 | hep_header.hp_sport = dst ? su->su_portsu_sin.sin_port : su_self->su_portsu_sin.sin_port; | |||
549 | ||||
550 | if (hep_header.hp_v == 2){ | |||
551 | hep_header.hp_l += sizeof(struct hep_timehdr); | |||
552 | } | |||
553 | ||||
554 | /* Copy hepheader */ | |||
555 | memset(*buffer, '\0', eth_frame_len); | |||
556 | memcpy(*buffer, &hep_header, sizeof(struct hep_hdr)); | |||
557 | buflen = sizeof(struct hep_hdr); | |||
558 | ||||
559 | if(su->su_familysu_sa.sa_family == AF_INET2) { | |||
560 | memcpy(*buffer + buflen, &hep_ipheader, sizeof(struct hep_iphdr)); | |||
561 | buflen += sizeof(struct hep_iphdr); | |||
562 | } | |||
563 | #if SU_HAVE_IN61 | |||
564 | else if(su->su_familysu_sa.sa_family == AF_INET610) { | |||
565 | memcpy(*buffer+buflen, &hep_ip6header, sizeof(struct hep_ip6hdr)); | |||
566 | buflen += sizeof(struct hep_ip6hdr); | |||
567 | } | |||
568 | #endif | |||
569 | else { | |||
570 | su_perror("error: tport_logging: capture: unsupported protocol family"); | |||
571 | goto done; | |||
572 | } | |||
573 | ||||
574 | /* copy time header */ | |||
575 | if (hep_header.hp_v == 2) { | |||
576 | /* now */ | |||
577 | now = su_now(); | |||
578 | /* should check for ifdef HAVE_LOCALTIME_R instead -_- */ | |||
579 | #if defined(HAVE_GETTIMEOFDAY1) || defined(HAVE_CLOCK_MONOTONIC1) | |||
580 | hep_time.tv_sec = (now.tv_sec - SU_TIME_EPOCH2208988800UL); /* see su_time0.c 'now' is not really 'now', so we decrease it by SU_TIME_EPOCH */ | |||
581 | #else | |||
582 | hep_time.tv_sec = now.tv_sec; | |||
583 | #endif | |||
584 | hep_time.tv_usec = now.tv_usec; | |||
585 | ||||
586 | hep_time.captid = mr->mr_agent_id; | |||
587 | memcpy((char*)*buffer+buflen, &hep_time, sizeof(struct hep_timehdr)); | |||
588 | buflen += sizeof(struct hep_timehdr); | |||
589 | } | |||
590 | ||||
591 | for (i = 0; i < iovused && n > 0; i++) { | |||
592 | size_t len = iov[i].mv_lensiv_len; | |||
593 | if (len > n) | |||
594 | len = n; | |||
595 | /* if the packet too big for us */ | |||
596 | if((buflen + len) > eth_frame_len) | |||
597 | break; | |||
598 | ||||
599 | memcpy(*buffer + buflen , (void*)iov[i].mv_basesiv_base, len); | |||
600 | buflen +=len; | |||
601 | n -= len; | |||
602 | } | |||
603 | ||||
604 | return buflen; | |||
605 | ||||
606 | done: | |||
607 | /* Now we release it */ | |||
608 | if(*buffer) { | |||
609 | free(*buffer); | |||
610 | *buffer = NULL((void*)0); | |||
611 | } | |||
612 | return 0; | |||
613 | } | |||
614 | ||||
615 | ||||
616 | /** Capture the data from the iovec */ | |||
617 | int tport_capt_msg_hepv3 (tport_t const *self, msg_t *msg, size_t n, | |||
618 | su_iovec_t const iov[], size_t iovused, char const *what, char **buffer) | |||
619 | { | |||
620 | ||||
621 | su_sockaddr_t const *su, *su_self, *su_self_ext; | |||
622 | struct hep_generic *hg=NULL((void*)0); | |||
623 | unsigned int buflen=0, iplen=0,tlen=0, payload_len = 0; | |||
624 | su_time_t now; | |||
625 | hep_chunk_ip4_t src_ip4 = {{0}}, dst_ip4 = {{0}}; | |||
626 | hep_chunk_t payload_chunk; | |||
627 | hep_chunk_t nodename_chunk; | |||
628 | int orig_n = 0; | |||
629 | ||||
630 | #if SU_HAVE_IN61 | |||
631 | hep_chunk_ip6_t src_ip6 = {{0}}, dst_ip6 = {{0}}; | |||
632 | #endif | |||
633 | ||||
634 | int eth_frame_len = 16000; | |||
635 | size_t i, dst = 1; | |||
636 | tport_master_t *mr; | |||
637 | ||||
638 | assert(self)((void) sizeof ((self) ? 1 : 0), __extension__ ({ if (self) ; else __assert_fail ("self", "tport_logging.c", 638, __extension__ __PRETTY_FUNCTION__); })); assert(msg)((void) sizeof ((msg) ? 1 : 0), __extension__ ({ if (msg) ; else __assert_fail ("msg", "tport_logging.c", 638, __extension__ __PRETTY_FUNCTION__ ); })); | |||
639 | ||||
640 | su = msg_addr(msg); | |||
641 | su_self = self->tp_pri->pri_primary->tp_addr; | |||
642 | ||||
643 | mr = self->tp_master; | |||
644 | if (mr->mr_capt_src_addr) { | |||
645 | /* override SRC address with configuration */ | |||
646 | su_self_ext = (void *)mr->mr_capt_src_addr->ai_addr; | |||
647 | } else { | |||
648 | /* use the SRC address from the socket */ | |||
649 | su_self_ext = su_self; | |||
650 | } | |||
651 | ||||
652 | /* If we don't have socket, go out */ | |||
653 | if (!mr->mr_capt_sock
| |||
654 | su_log("error: capture socket is not open\n"); | |||
655 | return 0; | |||
656 | } | |||
657 | ||||
658 | /*buffer for ethernet frame*/ | |||
659 | ||||
660 | hg = malloc(sizeof(struct hep_generic)); | |||
661 | memset(hg, 0, sizeof(struct hep_generic)); | |||
662 | ||||
663 | /* header set */ | |||
664 | memcpy(hg->header.id, "\x48\x45\x50\x33", 4); | |||
665 | ||||
666 | /* IP proto */ | |||
667 | hg->ip_family.chunk.vendor_id = htons(0x0000); | |||
668 | hg->ip_family.chunk.type_id = htons(0x0001); | |||
669 | hg->ip_family.data = su->su_familysu_sa.sa_family; | |||
670 | hg->ip_family.chunk.length = htons(sizeof(hg->ip_family)); | |||
671 | ||||
672 | /* PROTOCOL */ | |||
673 | if(strcmp(self->tp_name->tpn_proto, "tcp") == 0) hg->ip_proto.data = IPPROTO_TCPIPPROTO_TCP; | |||
674 | else if(strcmp(self->tp_name->tpn_proto, "tls") == 0) hg->ip_proto.data = IPPROTO_IDPIPPROTO_IDP; /* FAKE*/ | |||
675 | else if(strcmp(self->tp_name->tpn_proto, "sctp") == 0) hg->ip_proto.data = IPPROTO_SCTPIPPROTO_SCTP; | |||
676 | else if(strcmp(self->tp_name->tpn_proto, "ws") == 0) hg->ip_proto.data = IPPROTO_TCPIPPROTO_TCP; | |||
677 | else if(strcmp(self->tp_name->tpn_proto, "wss") == 0) hg->ip_proto.data = IPPROTO_TCPIPPROTO_TCP; | |||
678 | else hg->ip_proto.data = IPPROTO_UDPIPPROTO_UDP; /* DEFAULT UDP */ | |||
679 | ||||
680 | /* Proto ID */ | |||
681 | hg->ip_proto.chunk.vendor_id = htons(0x0000); | |||
682 | hg->ip_proto.chunk.type_id = htons(0x0002); | |||
683 | hg->ip_proto.chunk.length = htons(sizeof(hg->ip_proto)); | |||
684 | ||||
685 | /* Check destination */ | |||
686 | if(strncmp("sent", what, 4) == 0) dst = 0; | |||
687 | ||||
688 | /* copy destination and source IPs*/ | |||
689 | if(su->su_familysu_sa.sa_family == AF_INET2) { | |||
690 | ||||
691 | /* SRC IP */ | |||
692 | src_ip4.chunk.vendor_id = htons(0x0000); | |||
693 | src_ip4.chunk.type_id = htons(0x0003); | |||
694 | memcpy(dst
| |||
695 | src_ip4.chunk.length = htons(sizeof(src_ip4)); | |||
696 | ||||
697 | /* DST IP */ | |||
698 | dst_ip4.chunk.vendor_id = htons(0x0000); | |||
699 | dst_ip4.chunk.type_id = htons(0x0004); | |||
700 | memcpy(dst
| |||
701 | dst_ip4.chunk.length = htons(sizeof(dst_ip4)); | |||
702 | ||||
703 | iplen = sizeof(dst_ip4) + sizeof(src_ip4); | |||
704 | } | |||
705 | #if SU_HAVE_IN61 | |||
706 | else if(su->su_familysu_sa.sa_family == AF_INET610) { | |||
707 | ||||
708 | /* SRC IPv6 */ | |||
709 | src_ip6.chunk.vendor_id = htons(0x0000); | |||
710 | src_ip6.chunk.type_id = htons(0x0005); | |||
711 | memcpy(dst ? &src_ip6.data : &dst_ip6.data, &su->su_sin.sin_addr.s_addr, sizeof(su->su_sin.sin_addr.s_addr)); | |||
712 | src_ip6.chunk.length = htons(sizeof(src_ip6)); | |||
713 | ||||
714 | /* DST IPv6 */ | |||
715 | dst_ip6.chunk.vendor_id = htons(0x0000); | |||
716 | dst_ip6.chunk.type_id = htons(0x0006); | |||
717 | memcpy(dst ? &dst_ip6.data : &src_ip6.data, &su_self_ext->su_sin.sin_addr.s_addr, sizeof(su_self_ext->su_sin.sin_addr.s_addr)); | |||
718 | dst_ip6.chunk.length = htons(sizeof(dst_ip6)); | |||
719 | ||||
720 | iplen = sizeof(dst_ip6) + sizeof(src_ip6); | |||
721 | } | |||
722 | #endif | |||
723 | else { | |||
724 | su_perror("error: tport_logging hepv3: capture: unsupported protocol family"); | |||
725 | goto done; | |||
726 | } | |||
727 | ||||
728 | /* SRC PORT */ | |||
729 | hg->src_port.chunk.vendor_id = htons(0x0000); | |||
730 | hg->src_port.chunk.type_id = htons(0x0007); | |||
731 | hg->src_port.data = dst
| |||
732 | hg->src_port.chunk.length = htons(sizeof(hg->src_port)); | |||
733 | ||||
734 | /* DST PORT */ | |||
735 | hg->dst_port.chunk.vendor_id = htons(0x0000); | |||
736 | hg->dst_port.chunk.type_id = htons(0x0008); | |||
737 | hg->dst_port.data = dst
| |||
738 | hg->dst_port.chunk.length = htons(sizeof(hg->dst_port)); | |||
739 | ||||
740 | ||||
741 | /* TIMESTAMP SEC */ | |||
742 | hg->time_sec.chunk.vendor_id = htons(0x0000); | |||
743 | hg->time_sec.chunk.type_id = htons(0x0009); | |||
744 | hg->time_sec.chunk.length = htons(sizeof(hg->time_sec)); | |||
745 | ||||
746 | now = su_now(); | |||
747 | /* should check for ifdef HAVE_LOCALTIME_R instead -_- */ | |||
748 | #if defined(HAVE_GETTIMEOFDAY1) || defined(HAVE_CLOCK_MONOTONIC1) | |||
749 | hg->time_sec.data = htonl(now.tv_sec - SU_TIME_EPOCH2208988800UL); /* see su_time0.c 'now' is not really 'now', so we decrease it by SU_TIME_EPOCH */ | |||
750 | #else | |||
751 | hg->time_sec.data = htonl(now.tv_sec); | |||
752 | #endif | |||
753 | ||||
754 | /* TIMESTAMP USEC */ | |||
755 | hg->time_usec.chunk.vendor_id = htons(0x0000); | |||
756 | hg->time_usec.chunk.type_id = htons(0x000a); | |||
757 | hg->time_usec.data = htonl(now.tv_usec); | |||
758 | hg->time_usec.chunk.length = htons(sizeof(hg->time_usec)); | |||
759 | ||||
760 | /* Protocol TYPE */ | |||
761 | hg->proto_t.chunk.vendor_id = htons(0x0000); | |||
762 | hg->proto_t.chunk.type_id = htons(0x000b); | |||
763 | hg->proto_t.data = 0x001; //SIP | |||
764 | hg->proto_t.chunk.length = htons(sizeof(hg->proto_t)); | |||
765 | ||||
766 | /* Capture ID */ | |||
767 | hg->capt_id.chunk.vendor_id = htons(0x0000); | |||
768 | hg->capt_id.chunk.type_id = htons(0x000c); | |||
769 | hg->capt_id.data = htonl(mr->mr_agent_id); | |||
770 | hg->capt_id.chunk.length = htons(sizeof(hg->capt_id)); | |||
771 | ||||
772 | ||||
773 | /* Payload caclulation */ | |||
774 | orig_n = n; | |||
775 | for (i = 0; i < iovused && n > 0; i++) { | |||
776 | size_t len = iov[i].mv_lensiv_len; | |||
777 | if (len > n) len = n; | |||
778 | if((payload_len + len) > eth_frame_len) break; | |||
779 | payload_len +=len; | |||
780 | n -= len; | |||
781 | } | |||
782 | /* restore n */ | |||
783 | n = orig_n; | |||
784 | ||||
785 | /* Payload */ | |||
786 | payload_chunk.vendor_id = htons(0x0000); | |||
787 | payload_chunk.type_id = htons(0x000f); | |||
788 | payload_chunk.length = htons(sizeof(payload_chunk) + payload_len); | |||
789 | ||||
790 | tlen = sizeof(struct hep_generic) + payload_len + iplen + sizeof(hep_chunk_t); | |||
791 | ||||
792 | /* NODE NAME */ | |||
793 | tlen += sizeof(hep_chunk_t); | |||
794 | nodename_chunk.vendor_id = htons(0x0000); | |||
795 | nodename_chunk.type_id = htons(0x0013); | |||
796 | nodename_chunk.length = htons(sizeof(nodename_chunk) + strlen(mr->mr_agent_nodename)); | |||
797 | tlen += strlen(mr->mr_agent_nodename); | |||
798 | ||||
799 | /* total */ | |||
800 | hg->header.length = htons(tlen); | |||
801 | ||||
802 | *buffer = (void*)malloc(tlen); | |||
803 | ||||
804 | if (*buffer==NULL((void*)0)){ | |||
805 | su_perror("error: tport_logging hepv3: no memory for buffer"); | |||
806 | goto done; | |||
807 | } | |||
808 | ||||
809 | memcpy((void*) *buffer, hg, sizeof(struct hep_generic)); | |||
810 | buflen = sizeof(struct hep_generic); | |||
811 | ||||
812 | /* IPv4 */ | |||
813 | if(su->su_familysu_sa.sa_family == AF_INET2) { | |||
814 | /* SRC IP */ | |||
815 | memcpy((char*) *buffer+buflen, &src_ip4, sizeof(struct hep_chunk_ip4)); | |||
816 | buflen += sizeof(struct hep_chunk_ip4); | |||
817 | ||||
818 | memcpy((char*) *buffer+buflen, &dst_ip4, sizeof(struct hep_chunk_ip4)); | |||
819 | buflen += sizeof(struct hep_chunk_ip4); | |||
820 | } | |||
821 | #if SU_HAVE_IN61 | |||
822 | /* IPv6 */ | |||
823 | else if(su->su_familysu_sa.sa_family == AF_INET610) { | |||
824 | /* SRC IPv6 */ | |||
825 | memcpy((char*) *buffer+buflen, &src_ip6, sizeof(struct hep_chunk_ip6)); | |||
826 | buflen += sizeof(struct hep_chunk_ip6); | |||
827 | ||||
828 | memcpy((char*) *buffer+buflen, &dst_ip6, sizeof(struct hep_chunk_ip6)); | |||
829 | buflen += sizeof(struct hep_chunk_ip6); | |||
830 | } | |||
831 | #endif | |||
832 | ||||
833 | /* NODE NAME */ | |||
834 | memcpy((void*) buffer + buflen, &nodename_chunk, sizeof(struct hep_chunk)); | |||
835 | buflen += sizeof(struct hep_chunk); | |||
| ||||
836 | /* Now copying payload self */ | |||
837 | memcpy((void*) buffer + buflen, mr->mr_agent_nodename, strlen(mr->mr_agent_nodename)); | |||
838 | buflen += strlen(mr->mr_agent_nodename); | |||
839 | ||||
840 | /* PAYLOAD CHUNK */ | |||
841 | memcpy((char*) *buffer+buflen, &payload_chunk, sizeof(struct hep_chunk)); | |||
842 | buflen += sizeof(struct hep_chunk); | |||
843 | ||||
844 | /* PAYLOAD */ | |||
845 | for (i = 0; i < iovused && n > 0; i++) { | |||
846 | size_t len = iov[i].mv_lensiv_len; | |||
847 | if (len > n) len = n; | |||
848 | /* if the packet too big for us */ | |||
849 | if((buflen + len) > eth_frame_len) | |||
850 | break; | |||
851 | ||||
852 | memcpy(*buffer + buflen , (void*)iov[i].mv_basesiv_base, len); | |||
853 | buflen +=len; | |||
854 | n -= len; | |||
855 | } | |||
856 | ||||
857 | free(hg); | |||
858 | return buflen; | |||
859 | ||||
860 | done: | |||
861 | /* Now we release it */ | |||
862 | if(hg) free(hg); | |||
863 | return 0; | |||
864 | } | |||
865 | ||||
866 | ||||
867 | /** Log the message. */ | |||
868 | void tport_log_msg(tport_t *self, msg_t *msg, | |||
869 | char const *what, char const *via, | |||
870 | su_time_t now) | |||
871 | { | |||
872 | msg_iovec_t iov[80]; | |||
873 | size_t i, iovlen = msg_iovec(msg, iov, 80); | |||
874 | size_t n; | |||
875 | int skip_lf = 0; | |||
876 | char *buffer = NULL((void*)0); | |||
877 | size_t buffer_size = 0; | |||
878 | size_t buffer_pos = 0; | |||
879 | size_t bytes_written = 0; | |||
880 | ||||
881 | #define MSG_SEPARATOR"------------------------------------------------------------------------\n" \ | |||
882 | "------------------------------------------------------------------------\n" | |||
883 | ||||
884 | for (i = n = 0; i < iovlen && i < 80; i++) | |||
885 | n += iov[i].mv_lensiv_len; | |||
886 | ||||
887 | buffer_size = sizeof(char) * n + 1 + TPORT_STAMP_SIZE144 + sizeof(MSG_SEPARATOR"------------------------------------------------------------------------\n"); | |||
888 | if (buffer_size > 16000) { | |||
889 | buffer_size = 16000; | |||
890 | } | |||
891 | ||||
892 | buffer = malloc(buffer_size); | |||
893 | buffer[0] = '\0'; | |||
894 | ||||
895 | tport_stamp(self, msg, buffer, what, n, via, now); | |||
896 | buffer_pos = strlen(buffer); | |||
897 | if (buffer_pos < buffer_size) { | |||
898 | bytes_written = snprintf(buffer + buffer_pos, buffer_size - buffer_pos, "%s", MSG_SEPARATOR"------------------------------------------------------------------------\n"); | |||
899 | if (bytes_written > 0) { | |||
900 | buffer_pos += bytes_written; | |||
901 | } | |||
902 | } | |||
903 | ||||
904 | for (i = 0; buffer_pos < buffer_size && i < iovlen && i < 80; i++) { | |||
905 | char *s = iov[i].mv_basesiv_base, *end = s + iov[i].mv_lensiv_len; | |||
906 | ||||
907 | if (skip_lf && s < end && s[0] == '\n') { s++; skip_lf = 0; } | |||
908 | ||||
909 | while (s < end) { | |||
910 | if (s[0] == '\0') { | |||
911 | break; | |||
912 | } | |||
913 | ||||
914 | n = su_strncspn(s, end - s, "\r\n"); | |||
915 | if (buffer_pos > buffer_size) { | |||
916 | break; | |||
917 | } | |||
918 | bytes_written = snprintf(buffer + buffer_pos, buffer_size - buffer_pos, "%.*s", (int)n, s); | |||
919 | if (bytes_written > 0) { | |||
920 | buffer_pos += bytes_written; | |||
921 | } | |||
922 | ||||
923 | s += n; | |||
924 | ||||
925 | if (s == end) | |||
926 | break; | |||
927 | ||||
928 | if (buffer_pos < buffer_size) { | |||
929 | buffer[buffer_pos++] = '\n'; | |||
930 | } | |||
931 | /* Skip eol */ | |||
932 | if (s[0] == '\r') { | |||
933 | s++; | |||
934 | if (s == end) { | |||
935 | skip_lf = 1; | |||
936 | continue; | |||
937 | } | |||
938 | } | |||
939 | ||||
940 | if (s[0] == '\n') { | |||
941 | s++; | |||
942 | } | |||
943 | } | |||
944 | } | |||
945 | ||||
946 | if (buffer_pos >= buffer_size) { | |||
947 | buffer_pos = buffer_size - 1; | |||
948 | } | |||
949 | buffer[buffer_pos] = '\0'; | |||
950 | su_log("%s\n", buffer); | |||
951 | free(buffer); | |||
952 | } |