/src/kamailio/src/core/parser/msg_parser.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * sip msg. header proxy parser |
3 | | * |
4 | | * Copyright (C) 2001-2003 FhG Fokus |
5 | | * |
6 | | * This file is part of Kamailio, a free SIP server. |
7 | | * |
8 | | * Kamailio is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version |
12 | | * |
13 | | * Kamailio is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, write to the Free Software |
20 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | | * |
22 | | */ |
23 | | |
24 | | /** Parser :: SIP Message header proxy parser. |
25 | | * @file |
26 | | * @ingroup parser |
27 | | */ |
28 | | |
29 | | /*! \defgroup parser SIP-router SIP message parser |
30 | | * |
31 | | * The SIP message parser |
32 | | * |
33 | | */ |
34 | | |
35 | | |
36 | | #include <string.h> |
37 | | #include <stdlib.h> |
38 | | #include <sys/time.h> |
39 | | |
40 | | #include "../comp_defs.h" |
41 | | #include "msg_parser.h" |
42 | | #include "parser_f.h" |
43 | | #include "../ut.h" |
44 | | #include "../error.h" |
45 | | #include "../dprint.h" |
46 | | #include "../data_lump_rpl.h" |
47 | | #include "../mem/mem.h" |
48 | | #include "../core_stats.h" |
49 | | #include "../globals.h" |
50 | | #include "parse_hname2.h" |
51 | | #include "parse_uri.h" |
52 | | #include "parse_content.h" |
53 | | #include "parse_to.h" |
54 | | #include "../compiler_opt.h" |
55 | | |
56 | | #ifdef DEBUG_DMALLOC |
57 | | #include <mem/dmalloc.h> |
58 | | #endif |
59 | | |
60 | | |
61 | 232k | #define parse_hname(_b, _e, _h) parse_hname2((_b), (_e), (_h)) |
62 | | |
63 | | /* number of via's encountered */ |
64 | | int via_cnt; |
65 | | /* global request flags */ |
66 | | msg_flags_t global_req_flags = 0; |
67 | | |
68 | | int ksr_sip_parser_mode = KSR_SIP_PARSER_MODE_STRICT; |
69 | | |
70 | | /* returns pointer to next header line, and fill hdr_f ; |
71 | | * if at end of header returns pointer to the last crlf (always buf)*/ |
72 | | char *get_hdr_field( |
73 | | char *const buf, char *const end, struct hdr_field *const hdr) |
74 | 235k | { |
75 | | |
76 | 235k | char *tmp = 0; |
77 | 235k | char *match; |
78 | 235k | struct via_body *vb; |
79 | 235k | struct cseq_body *cseq_b; |
80 | 235k | struct to_body *to_b; |
81 | 235k | int integer, err; |
82 | 235k | unsigned uval; |
83 | | |
84 | 235k | if(!buf) { |
85 | 0 | DBG("null buffer pointer\n"); |
86 | 0 | goto error; |
87 | 0 | } |
88 | | |
89 | 235k | if((*buf) == '\n' || (*buf) == '\r') { |
90 | | /* double crlf or lflf or crcr */ |
91 | 2.92k | DBG("found end of header\n"); |
92 | 2.92k | hdr->type = HDR_EOH_T; |
93 | 2.92k | return buf; |
94 | 2.92k | } |
95 | | |
96 | 232k | tmp = parse_hname(buf, end, hdr); |
97 | 232k | if(hdr->type == HDR_ERROR_T) { |
98 | 14.1k | ERR("bad header\n"); |
99 | 14.1k | goto error; |
100 | 14.1k | } |
101 | | |
102 | | /* eliminate leading whitespace */ |
103 | 218k | tmp = eat_lws_end(tmp, end); |
104 | 218k | if(tmp >= end) { |
105 | 540 | ERR("HF empty\n"); |
106 | 540 | goto error; |
107 | 540 | } |
108 | | |
109 | | /* if header-field well-known, parse it, find its end otherwise ; |
110 | | * after leaving the hdr->type switch, tmp should be set to the |
111 | | * next header field |
112 | | */ |
113 | 218k | switch(hdr->type) { |
114 | 75.0k | case HDR_VIA_T: |
115 | | /* keep number of vias parsed -- we want to report it in |
116 | | replies for diagnostic purposes */ |
117 | 75.0k | via_cnt++; |
118 | 75.0k | vb = pkg_malloc(sizeof(struct via_body)); |
119 | 75.0k | if(vb == 0) { |
120 | 0 | PKG_MEM_ERROR; |
121 | 0 | goto error; |
122 | 0 | } |
123 | 75.0k | memset(vb, 0, sizeof(struct via_body)); |
124 | 75.0k | hdr->body.s = tmp; |
125 | 75.0k | tmp = parse_via(tmp, end, vb); |
126 | 75.0k | if(vb->error == PARSE_ERROR) { |
127 | 32.1k | ERR("bad via\n"); |
128 | 32.1k | free_via_list(vb); |
129 | 32.1k | goto error; |
130 | 32.1k | } |
131 | 42.8k | hdr->parsed = vb; |
132 | 42.8k | vb->hdr.s = hdr->name.s; |
133 | 42.8k | vb->hdr.len = hdr->name.len; |
134 | 42.8k | hdr->body.len = tmp - hdr->body.s; |
135 | 42.8k | break; |
136 | 16.6k | case HDR_CSEQ_T: |
137 | 16.6k | cseq_b = pkg_malloc(sizeof(struct cseq_body)); |
138 | 16.6k | if(cseq_b == 0) { |
139 | 0 | PKG_MEM_ERROR; |
140 | 0 | goto error; |
141 | 0 | } |
142 | 16.6k | memset(cseq_b, 0, sizeof(struct cseq_body)); |
143 | 16.6k | hdr->body.s = tmp; |
144 | 16.6k | tmp = parse_cseq(tmp, end, cseq_b); |
145 | 16.6k | if(cseq_b->error == PARSE_ERROR) { |
146 | 3.06k | ERR("bad cseq\n"); |
147 | 3.06k | free_cseq(cseq_b); |
148 | 3.06k | goto error; |
149 | 3.06k | } |
150 | 13.5k | hdr->parsed = cseq_b; |
151 | 13.5k | hdr->body.len = tmp - hdr->body.s; |
152 | 13.5k | DBG("cseq <%.*s>: <%.*s> <%.*s>\n", hdr->name.len, ZSW(hdr->name.s), |
153 | 0 | cseq_b->number.len, ZSW(cseq_b->number.s), |
154 | 0 | cseq_b->method.len, cseq_b->method.s); |
155 | 13.5k | break; |
156 | 21.6k | case HDR_TO_T: |
157 | 21.6k | to_b = pkg_malloc(sizeof(struct to_body)); |
158 | 21.6k | if(to_b == 0) { |
159 | 0 | PKG_MEM_ERROR; |
160 | 0 | goto error; |
161 | 0 | } |
162 | 21.6k | memset(to_b, 0, sizeof(struct to_body)); |
163 | 21.6k | hdr->body.s = tmp; |
164 | 21.6k | tmp = parse_to(tmp, end, to_b); |
165 | 21.6k | if(to_b->error == PARSE_ERROR) { |
166 | 6.09k | ERR("bad to header\n"); |
167 | 6.09k | free_to(to_b); |
168 | 6.09k | goto error; |
169 | 6.09k | } |
170 | 15.5k | hdr->parsed = to_b; |
171 | 15.5k | hdr->body.len = tmp - hdr->body.s; |
172 | 15.5k | DBG("<%.*s> [%d]; uri=[%.*s]\n", hdr->name.len, ZSW(hdr->name.s), |
173 | 0 | hdr->body.len, to_b->uri.len, ZSW(to_b->uri.s)); |
174 | 15.5k | DBG("to body (%d)[%.*s], to tag (%d)[%.*s]\n", to_b->body.len, |
175 | 0 | to_b->body.len, ZSW(to_b->body.s), to_b->tag_value.len, |
176 | 0 | to_b->tag_value.len, ZSW(to_b->tag_value.s)); |
177 | 15.5k | break; |
178 | 1.54k | case HDR_CONTENTLENGTH_T: |
179 | 1.54k | hdr->body.s = tmp; |
180 | 1.54k | tmp = parse_content_length(tmp, end, &integer); |
181 | 1.54k | if(tmp == 0) { |
182 | 547 | ERR("bad content_length header\n"); |
183 | 547 | goto error; |
184 | 547 | } |
185 | 1.00k | hdr->parsed = (void *)(long)integer; |
186 | 1.00k | hdr->body.len = tmp - hdr->body.s; |
187 | 1.00k | DBG("content_length=%d\n", (int)(long)hdr->parsed); |
188 | 1.00k | break; |
189 | 2.21k | case HDR_RETRY_AFTER_T: |
190 | 2.21k | hdr->body.s = tmp; |
191 | 2.21k | tmp = parse_retry_after(tmp, end, &uval, &err); |
192 | 2.21k | if(err) { |
193 | 721 | ERR("bad retry_after header\n"); |
194 | 721 | goto error; |
195 | 721 | } |
196 | 1.49k | hdr->parsed = (void *)(unsigned long)uval; |
197 | 1.49k | hdr->body.len = tmp - hdr->body.s; |
198 | 1.49k | DBG("retry_after=%d\n", (unsigned)(long)hdr->parsed); |
199 | 1.49k | break; |
200 | 793 | case HDR_IDENTITY_T: |
201 | 1.09k | case HDR_DATE_T: |
202 | 3.55k | case HDR_IDENTITY_INFO_T: |
203 | 3.96k | case HDR_SUPPORTED_T: |
204 | 4.28k | case HDR_REQUIRE_T: |
205 | 9.25k | case HDR_CONTENTTYPE_T: |
206 | 14.3k | case HDR_FROM_T: |
207 | 15.3k | case HDR_CALLID_T: |
208 | 23.8k | case HDR_CONTACT_T: |
209 | 25.5k | case HDR_ROUTE_T: |
210 | 26.1k | case HDR_RECORDROUTE_T: |
211 | 27.0k | case HDR_MAXFORWARDS_T: |
212 | 27.3k | case HDR_AUTHORIZATION_T: |
213 | 27.6k | case HDR_EXPIRES_T: |
214 | 27.9k | case HDR_MIN_EXPIRES_T: |
215 | 28.6k | case HDR_PROXYAUTH_T: |
216 | 28.9k | case HDR_PROXYREQUIRE_T: |
217 | 29.2k | case HDR_UNSUPPORTED_T: |
218 | 29.5k | case HDR_ALLOW_T: |
219 | 30.2k | case HDR_EVENT_T: |
220 | 30.5k | case HDR_ACCEPT_T: |
221 | 30.7k | case HDR_ACCEPTLANGUAGE_T: |
222 | 31.0k | case HDR_ORGANIZATION_T: |
223 | 31.2k | case HDR_PRIORITY_T: |
224 | 31.8k | case HDR_SUBJECT_T: |
225 | 32.1k | case HDR_USERAGENT_T: |
226 | 32.4k | case HDR_SERVER_T: |
227 | 33.1k | case HDR_CONTENTDISPOSITION_T: |
228 | 43.1k | case HDR_DIVERSION_T: |
229 | 43.3k | case HDR_RPID_T: |
230 | 43.8k | case HDR_SIPIFMATCH_T: |
231 | 46.6k | case HDR_REFER_TO_T: |
232 | 47.1k | case HDR_SESSIONEXPIRES_T: |
233 | 47.4k | case HDR_MIN_SE_T: |
234 | 47.7k | case HDR_SUBSCRIPTION_STATE_T: |
235 | 48.7k | case HDR_ACCEPTCONTACT_T: |
236 | 49.3k | case HDR_ALLOWEVENTS_T: |
237 | 49.8k | case HDR_CONTENTENCODING_T: |
238 | 50.1k | case HDR_REFERREDBY_T: |
239 | 50.6k | case HDR_REJECTCONTACT_T: |
240 | 51.4k | case HDR_REQUESTDISPOSITION_T: |
241 | 51.7k | case HDR_WWW_AUTHENTICATE_T: |
242 | 52.0k | case HDR_PROXY_AUTHENTICATE_T: |
243 | 52.3k | case HDR_PATH_T: |
244 | 54.3k | case HDR_PRIVACY_T: |
245 | 90.3k | case HDR_PAI_T: |
246 | 90.6k | case HDR_PPI_T: |
247 | 90.8k | case HDR_REASON_T: |
248 | 91.1k | case HDR_CALLINFO_T: |
249 | 101k | case HDR_OTHER_T: |
250 | | /* just skip over it */ |
251 | 101k | hdr->body.s = tmp; |
252 | | /* find end of header */ |
253 | | /* find lf */ |
254 | 123k | do { |
255 | 123k | match = q_memchr(tmp, '\n', end - tmp); |
256 | 123k | if(match) { |
257 | 122k | match++; |
258 | 122k | } else { |
259 | 1.72k | ERR("no eol - bad body for <%.*s> (hdr type: %d) [%.*s]\n", |
260 | 1.72k | hdr->name.len, hdr->name.s, hdr->type, |
261 | 1.72k | ((end - tmp) > 128) ? 128 : (int)(end - tmp), tmp); |
262 | | /* abort(); */ |
263 | 1.72k | tmp = end; |
264 | 1.72k | goto error; |
265 | 1.72k | } |
266 | 122k | tmp = match; |
267 | 122k | } while(match < end && ((*match == ' ') || (*match == '\t'))); |
268 | 99.4k | tmp = match; |
269 | 99.4k | hdr->body.len = match - hdr->body.s; |
270 | 99.4k | break; |
271 | 0 | default: |
272 | 0 | BUG("unknown header type %d [%.*s]\n", hdr->type, |
273 | 0 | ((end - buf) > 128) ? 128 : (int)(end - buf), buf); |
274 | 0 | goto error; |
275 | 218k | } |
276 | | /* jku: if \r covered by current length, shrink it */ |
277 | 173k | trim_r(hdr->body); |
278 | 173k | hdr->len = tmp - hdr->name.s; |
279 | 173k | return tmp; |
280 | 59.0k | error: |
281 | 59.0k | DBG("error exit\n"); |
282 | 59.0k | STATS_BAD_MSG_HDR(); |
283 | 59.0k | hdr->type = HDR_ERROR_T; |
284 | 59.0k | hdr->len = tmp - hdr->name.s; |
285 | 59.0k | return tmp; |
286 | 218k | } |
287 | | |
288 | | |
289 | | /* parse the headers and adds them to msg->headers and msg->to, from etc. |
290 | | * It stops when all the headers requested in flags were parsed, on error |
291 | | * (bad header) or end of headers |
292 | | * WARNING: parse_headers was changed to use hdr_flags_t (the flags are now |
293 | | * different from the header types). Don't call it with a header type |
294 | | * (HDR_xxx_T), only with header flags (HDR_xxx_F)!*/ |
295 | | /* note: it continues where it previously stopped and goes ahead until |
296 | | end is encountered or desired HFs are found; if you call it twice |
297 | | for the same HF which is present only once, it will fail the second |
298 | | time; if you call it twice and the HF is found on second time too, |
299 | | it's not replaced in the well-known HF pointer but just added to |
300 | | header list; if you want to use a dumb convenience function which will |
301 | | give you the first occurrence of a header you are interested in, |
302 | | look at check_transaction_quadruple |
303 | | */ |
304 | | int parse_headers( |
305 | | struct sip_msg *const msg, const hdr_flags_t flags, const int next) |
306 | 225k | { |
307 | 225k | struct hdr_field *hf; |
308 | 225k | char *tmp; |
309 | 225k | char *rest; |
310 | 225k | char *end; |
311 | 225k | hdr_flags_t orig_flag; |
312 | | |
313 | 225k | end = msg->buf + msg->len; |
314 | 225k | tmp = msg->unparsed; |
315 | | |
316 | 225k | if(unlikely(next)) { |
317 | 9.00k | orig_flag = msg->parsed_flag; |
318 | 9.00k | msg->parsed_flag &= ~flags; |
319 | 216k | } else { |
320 | 216k | orig_flag = 0; |
321 | 216k | } |
322 | | |
323 | | #ifdef EXTRA_DEBUG |
324 | | DBG("flags=%llx\n", (unsigned long long)flags); |
325 | | #endif |
326 | 398k | while(tmp < end && (flags & msg->parsed_flag) != flags) { |
327 | 235k | prefetch_loc_r(tmp + 64, 1); |
328 | 235k | hf = pkg_malloc(sizeof(struct hdr_field)); |
329 | 235k | if(unlikely(hf == 0)) { |
330 | 0 | PKG_MEM_ERROR; |
331 | 0 | ser_error = E_OUT_OF_MEM; |
332 | 0 | goto error; |
333 | 0 | } |
334 | 235k | memset(hf, 0, sizeof(struct hdr_field)); |
335 | 235k | hf->type = HDR_ERROR_T; |
336 | 235k | rest = get_hdr_field(tmp, end, hf); |
337 | 235k | switch(hf->type) { |
338 | 59.0k | case HDR_ERROR_T: |
339 | 59.0k | ERR("bad header field [%.*s]\n", |
340 | 59.0k | (end - tmp > 100) ? 100 : (int)(end - tmp), tmp); |
341 | 59.0k | goto error; |
342 | 2.92k | case HDR_EOH_T: |
343 | 2.92k | msg->eoh = tmp; /* or rest?*/ |
344 | 2.92k | msg->parsed_flag |= HDR_EOH_F; |
345 | 2.92k | pkg_free(hf); |
346 | 2.92k | goto skip; |
347 | 972 | case HDR_ACCEPTCONTACT_T: |
348 | 1.53k | case HDR_ALLOWEVENTS_T: |
349 | 1.91k | case HDR_CONTENTENCODING_T: |
350 | 2.24k | case HDR_REFERREDBY_T: |
351 | 2.71k | case HDR_REJECTCONTACT_T: |
352 | 3.43k | case HDR_REQUESTDISPOSITION_T: |
353 | 3.74k | case HDR_WWW_AUTHENTICATE_T: |
354 | 4.03k | case HDR_PROXY_AUTHENTICATE_T: |
355 | 5.52k | case HDR_RETRY_AFTER_T: |
356 | 15.3k | case HDR_OTHER_T: /* mark the type as found/parsed*/ |
357 | 15.3k | msg->parsed_flag |= HDR_T2F(hf->type); |
358 | 15.3k | break; |
359 | 909 | case HDR_CALLID_T: |
360 | 909 | if(msg->callid == 0) { |
361 | 68 | msg->callid = hf; |
362 | 841 | } else if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
363 | 841 | if(IS_SIP(msg)) { |
364 | 148 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
365 | 148 | "duplicate Call-ID header field [%.*s]\n", |
366 | 148 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
367 | 148 | tmp); |
368 | 148 | goto error; |
369 | 148 | } |
370 | 841 | } |
371 | 761 | msg->parsed_flag |= HDR_CALLID_F; |
372 | 761 | break; |
373 | 405 | case HDR_SIPIFMATCH_T: |
374 | 405 | if(msg->sipifmatch == 0) |
375 | 12 | msg->sipifmatch = hf; |
376 | 405 | msg->parsed_flag |= HDR_SIPIFMATCH_F; |
377 | 405 | break; |
378 | 15.5k | case HDR_TO_T: |
379 | 15.5k | if(msg->to == 0) { |
380 | 4.06k | msg->to = hf; |
381 | 11.4k | } else if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
382 | 11.4k | if(IS_SIP(msg)) { |
383 | 108 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
384 | 108 | "duplicate To header field [%.*s]\n", |
385 | 108 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
386 | 108 | tmp); |
387 | 108 | goto error; |
388 | 108 | } |
389 | 11.4k | } |
390 | 15.4k | msg->parsed_flag |= HDR_TO_F; |
391 | 15.4k | break; |
392 | 13.5k | case HDR_CSEQ_T: |
393 | 13.5k | if(msg->cseq == 0) { |
394 | 968 | msg->cseq = hf; |
395 | 12.5k | } else if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
396 | 12.5k | if(IS_SIP(msg)) { |
397 | 96 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
398 | 96 | "duplicate CSeq header field [%.*s]\n", |
399 | 96 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
400 | 96 | tmp); |
401 | 96 | goto error; |
402 | 96 | } |
403 | 12.5k | } |
404 | 13.4k | msg->parsed_flag |= HDR_CSEQ_F; |
405 | 13.4k | break; |
406 | 5.02k | case HDR_FROM_T: |
407 | 5.02k | if(msg->from == 0) { |
408 | 1.74k | msg->from = hf; |
409 | 3.28k | } else if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
410 | 3.28k | if(IS_SIP(msg)) { |
411 | 73 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
412 | 73 | "duplicate From header field [%.*s]\n", |
413 | 73 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
414 | 73 | tmp); |
415 | 73 | goto error; |
416 | 73 | } |
417 | 3.28k | } |
418 | 4.94k | msg->parsed_flag |= HDR_FROM_F; |
419 | 4.94k | break; |
420 | 8.40k | case HDR_CONTACT_T: |
421 | 8.40k | if(msg->contact == 0) |
422 | 1.75k | msg->contact = hf; |
423 | 8.40k | msg->parsed_flag |= HDR_CONTACT_F; |
424 | 8.40k | break; |
425 | 777 | case HDR_MAXFORWARDS_T: |
426 | 777 | if(msg->maxforwards == 0) { |
427 | 37 | msg->maxforwards = hf; |
428 | 740 | } else { |
429 | 740 | if(IS_SIP(msg)) { |
430 | 158 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
431 | 158 | "duplicate Max-Forwards header field [%.*s]\n", |
432 | 158 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
433 | 158 | tmp); |
434 | 158 | goto error; |
435 | 158 | } |
436 | 740 | } |
437 | 619 | msg->parsed_flag |= HDR_MAXFORWARDS_F; |
438 | 619 | break; |
439 | 1.69k | case HDR_ROUTE_T: |
440 | 1.69k | if(msg->route == 0) |
441 | 470 | msg->route = hf; |
442 | 1.69k | msg->parsed_flag |= HDR_ROUTE_F; |
443 | 1.69k | break; |
444 | 622 | case HDR_RECORDROUTE_T: |
445 | 622 | if(msg->record_route == 0) |
446 | 47 | msg->record_route = hf; |
447 | 622 | msg->parsed_flag |= HDR_RECORDROUTE_F; |
448 | 622 | break; |
449 | 4.86k | case HDR_CONTENTTYPE_T: |
450 | 4.86k | if(msg->content_type == 0) |
451 | 1.67k | msg->content_type = hf; |
452 | 4.86k | msg->parsed_flag |= HDR_CONTENTTYPE_F; |
453 | 4.86k | break; |
454 | 1.00k | case HDR_CONTENTLENGTH_T: |
455 | 1.00k | if(msg->content_length == 0) { |
456 | 84 | msg->content_length = hf; |
457 | 918 | } else if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
458 | 918 | if(IS_SIP(msg)) { |
459 | 158 | LOG(cfg_get(core, core_cfg, sip_parser_log), |
460 | 158 | "duplicate Content-Length header field " |
461 | 158 | "[%.*s]\n", |
462 | 158 | (end - tmp > 100) ? 100 : (int)(end - tmp), |
463 | 158 | tmp); |
464 | 158 | goto error; |
465 | 158 | } |
466 | 918 | } |
467 | 844 | msg->parsed_flag |= HDR_CONTENTLENGTH_F; |
468 | 844 | break; |
469 | 314 | case HDR_AUTHORIZATION_T: |
470 | 314 | if(msg->authorization == 0) |
471 | 23 | msg->authorization = hf; |
472 | 314 | msg->parsed_flag |= HDR_AUTHORIZATION_F; |
473 | 314 | break; |
474 | 313 | case HDR_EXPIRES_T: |
475 | 313 | if(msg->expires == 0) |
476 | 22 | msg->expires = hf; |
477 | 313 | msg->parsed_flag |= HDR_EXPIRES_F; |
478 | 313 | break; |
479 | 268 | case HDR_MIN_EXPIRES_T: |
480 | 268 | if(msg->min_expires == 0) |
481 | 13 | msg->min_expires = hf; |
482 | 268 | msg->parsed_flag |= HDR_MIN_EXPIRES_F; |
483 | 268 | break; |
484 | 675 | case HDR_PROXYAUTH_T: |
485 | 675 | if(msg->proxy_auth == 0) |
486 | 15 | msg->proxy_auth = hf; |
487 | 675 | msg->parsed_flag |= HDR_PROXYAUTH_F; |
488 | 675 | break; |
489 | 261 | case HDR_PROXYREQUIRE_T: |
490 | 261 | if(msg->proxy_require == 0) |
491 | 12 | msg->proxy_require = hf; |
492 | 261 | msg->parsed_flag |= HDR_PROXYREQUIRE_F; |
493 | 261 | break; |
494 | 350 | case HDR_SUPPORTED_T: |
495 | 350 | if(msg->supported == 0) |
496 | 22 | msg->supported = hf; |
497 | 350 | msg->parsed_flag |= HDR_SUPPORTED_F; |
498 | 350 | break; |
499 | 302 | case HDR_REQUIRE_T: |
500 | 302 | if(msg->require == 0) |
501 | 18 | msg->require = hf; |
502 | 302 | msg->parsed_flag |= HDR_REQUIRE_F; |
503 | 302 | break; |
504 | 305 | case HDR_UNSUPPORTED_T: |
505 | 305 | if(msg->unsupported == 0) |
506 | 15 | msg->unsupported = hf; |
507 | 305 | msg->parsed_flag |= HDR_UNSUPPORTED_F; |
508 | 305 | break; |
509 | 265 | case HDR_ALLOW_T: |
510 | 265 | if(msg->allow == 0) |
511 | 19 | msg->allow = hf; |
512 | 265 | msg->parsed_flag |= HDR_ALLOW_F; |
513 | 265 | break; |
514 | 702 | case HDR_EVENT_T: |
515 | 702 | if(msg->event == 0) |
516 | 39 | msg->event = hf; |
517 | 702 | msg->parsed_flag |= HDR_EVENT_F; |
518 | 702 | break; |
519 | 240 | case HDR_ACCEPT_T: |
520 | 240 | if(msg->accept == 0) |
521 | 9 | msg->accept = hf; |
522 | 240 | msg->parsed_flag |= HDR_ACCEPT_F; |
523 | 240 | break; |
524 | 240 | case HDR_ACCEPTLANGUAGE_T: |
525 | 240 | if(msg->accept_language == 0) |
526 | 9 | msg->accept_language = hf; |
527 | 240 | msg->parsed_flag |= HDR_ACCEPTLANGUAGE_F; |
528 | 240 | break; |
529 | 243 | case HDR_ORGANIZATION_T: |
530 | 243 | if(msg->organization == 0) |
531 | 9 | msg->organization = hf; |
532 | 243 | msg->parsed_flag |= HDR_ORGANIZATION_F; |
533 | 243 | break; |
534 | 271 | case HDR_PRIORITY_T: |
535 | 271 | if(msg->priority == 0) |
536 | 16 | msg->priority = hf; |
537 | 271 | msg->parsed_flag |= HDR_PRIORITY_F; |
538 | 271 | break; |
539 | 537 | case HDR_SUBJECT_T: |
540 | 537 | if(msg->subject == 0) |
541 | 29 | msg->subject = hf; |
542 | 537 | msg->parsed_flag |= HDR_SUBJECT_F; |
543 | 537 | break; |
544 | 283 | case HDR_USERAGENT_T: |
545 | 283 | if(msg->user_agent == 0) |
546 | 14 | msg->user_agent = hf; |
547 | 283 | msg->parsed_flag |= HDR_USERAGENT_F; |
548 | 283 | break; |
549 | 294 | case HDR_SERVER_T: |
550 | 294 | if(msg->server == 0) |
551 | 19 | msg->server = hf; |
552 | 294 | msg->parsed_flag |= HDR_SERVER_F; |
553 | 294 | break; |
554 | 689 | case HDR_CONTENTDISPOSITION_T: |
555 | 689 | if(msg->content_disposition == 0) |
556 | 434 | msg->content_disposition = hf; |
557 | 689 | msg->parsed_flag |= HDR_CONTENTDISPOSITION_F; |
558 | 689 | break; |
559 | 9.91k | case HDR_DIVERSION_T: |
560 | 9.91k | if(msg->diversion == 0) |
561 | 671 | msg->diversion = hf; |
562 | 9.91k | msg->parsed_flag |= HDR_DIVERSION_F; |
563 | 9.91k | break; |
564 | 275 | case HDR_RPID_T: |
565 | 275 | if(msg->rpid == 0) |
566 | 18 | msg->rpid = hf; |
567 | 275 | msg->parsed_flag |= HDR_RPID_F; |
568 | 275 | break; |
569 | 2.74k | case HDR_REFER_TO_T: |
570 | 2.74k | if(msg->refer_to == 0) |
571 | 423 | msg->refer_to = hf; |
572 | 2.74k | msg->parsed_flag |= HDR_REFER_TO_F; |
573 | 2.74k | break; |
574 | 454 | case HDR_SESSIONEXPIRES_T: |
575 | 454 | if(msg->session_expires == 0) |
576 | 28 | msg->session_expires = hf; |
577 | 454 | msg->parsed_flag |= HDR_SESSIONEXPIRES_F; |
578 | 454 | break; |
579 | 292 | case HDR_MIN_SE_T: |
580 | 292 | if(msg->min_se == 0) |
581 | 15 | msg->min_se = hf; |
582 | 292 | msg->parsed_flag |= HDR_MIN_SE_F; |
583 | 292 | break; |
584 | 271 | case HDR_SUBSCRIPTION_STATE_T: |
585 | 271 | if(msg->subscription_state == 0) |
586 | 13 | msg->subscription_state = hf; |
587 | 271 | msg->parsed_flag |= HDR_SUBSCRIPTION_STATE_F; |
588 | 271 | break; |
589 | 42.8k | case HDR_VIA_T: |
590 | 42.8k | msg->parsed_flag |= HDR_VIA_F; |
591 | 42.8k | DBG("Via found, flags=%llx\n", (unsigned long long)flags); |
592 | 42.8k | if(msg->via1 == 0) { |
593 | 4.30k | DBG("this is the first via\n"); |
594 | 4.30k | msg->h_via1 = hf; |
595 | 4.30k | msg->via1 = hf->parsed; |
596 | 4.30k | if(msg->via1->next) { |
597 | 76 | msg->via2 = msg->via1->next; |
598 | 76 | msg->parsed_flag |= HDR_VIA2_F; |
599 | 76 | } |
600 | 38.5k | } else if(msg->via2 == 0) { |
601 | 758 | msg->h_via2 = hf; |
602 | 758 | msg->via2 = hf->parsed; |
603 | 758 | msg->parsed_flag |= HDR_VIA2_F; |
604 | 758 | DBG("this is the second via\n"); |
605 | 758 | } |
606 | 42.8k | break; |
607 | 284 | case HDR_DATE_T: |
608 | 284 | if(msg->date == 0) |
609 | 15 | msg->date = hf; |
610 | 284 | msg->parsed_flag |= HDR_DATE_F; |
611 | 284 | break; |
612 | 748 | case HDR_IDENTITY_T: |
613 | 748 | if(msg->identity == 0) |
614 | 47 | msg->identity = hf; |
615 | 748 | msg->parsed_flag |= HDR_IDENTITY_F; |
616 | 748 | break; |
617 | 2.43k | case HDR_IDENTITY_INFO_T: |
618 | 2.43k | if(msg->identity_info == 0) |
619 | 741 | msg->identity_info = hf; |
620 | 2.43k | msg->parsed_flag |= HDR_IDENTITY_INFO_F; |
621 | 2.43k | break; |
622 | 281 | case HDR_PATH_T: |
623 | 281 | if(msg->path == 0) |
624 | 18 | msg->path = hf; |
625 | 281 | msg->parsed_flag |= HDR_PATH_F; |
626 | 281 | break; |
627 | 1.97k | case HDR_PRIVACY_T: |
628 | 1.97k | if(msg->privacy == 0) |
629 | 996 | msg->privacy = hf; |
630 | 1.97k | msg->parsed_flag |= HDR_PRIVACY_F; |
631 | 1.97k | break; |
632 | 35.9k | case HDR_PAI_T: |
633 | 35.9k | if(msg->pai == 0) |
634 | 320 | msg->pai = hf; |
635 | 35.9k | msg->parsed_flag |= HDR_PAI_F; |
636 | 35.9k | break; |
637 | 281 | case HDR_PPI_T: |
638 | 281 | if(msg->ppi == 0) |
639 | 16 | msg->ppi = hf; |
640 | 281 | msg->parsed_flag |= HDR_PPI_F; |
641 | 281 | break; |
642 | 280 | case HDR_REASON_T: |
643 | 280 | msg->parsed_flag |= HDR_REASON_F; |
644 | 280 | break; |
645 | 268 | case HDR_CALLINFO_T: |
646 | 268 | msg->parsed_flag |= HDR_CALLINFO_F; |
647 | 268 | break; |
648 | 0 | default: |
649 | 0 | BUG("unknown header type %d\n", hf->type); |
650 | 0 | goto error; |
651 | 235k | } |
652 | | /* add the header to the list*/ |
653 | 173k | if(msg->last_header == 0) { |
654 | 14.8k | msg->headers = hf; |
655 | 14.8k | msg->last_header = hf; |
656 | 158k | } else { |
657 | 158k | msg->last_header->next = hf; |
658 | 158k | msg->last_header = hf; |
659 | 158k | } |
660 | | #ifdef EXTRA_DEBUG |
661 | | DBG("header field type %d, name=<%.*s>, body=<%.*s>\n", hf->type, |
662 | | hf->name.len, ZSW(hf->name.s), hf->body.len, ZSW(hf->body.s)); |
663 | | #endif |
664 | 173k | tmp = rest; |
665 | 173k | } |
666 | | |
667 | 165k | skip: |
668 | 165k | msg->unparsed = tmp; |
669 | 165k | if(ksr_sip_parser_mode & KSR_SIP_PARSER_MODE_STRICT) { |
670 | 165k | if(msg->headers == NULL) { |
671 | | /* nothing parsed - invalid input sip message */ |
672 | 79 | goto error1; |
673 | 79 | } |
674 | 165k | } |
675 | | /* restore original flags */ |
676 | 165k | msg->parsed_flag |= orig_flag; |
677 | 165k | return 0; |
678 | | |
679 | 59.7k | error: |
680 | 59.7k | if(hf) { |
681 | 59.7k | clean_hdr_field(hf); |
682 | 59.7k | pkg_free(hf); |
683 | 59.7k | } |
684 | | |
685 | 59.8k | error1: |
686 | 59.8k | ser_error = E_BAD_REQ; |
687 | | /* restore original flags */ |
688 | 59.8k | msg->parsed_flag |= orig_flag; |
689 | 59.8k | return -1; |
690 | 59.7k | } |
691 | | |
692 | | |
693 | | /* returns 0 if ok, -1 for errors */ |
694 | | int parse_msg( |
695 | | char *const buf, const unsigned int len, struct sip_msg *const msg) |
696 | 18.7k | { |
697 | | |
698 | 18.7k | char *tmp; |
699 | 18.7k | char *rest; |
700 | 18.7k | struct msg_start *fl; |
701 | 18.7k | int offset; |
702 | 18.7k | hdr_flags_t flags; |
703 | | |
704 | | /* eat crlf & whitespaces from the beginning */ |
705 | 280k | for(tmp = buf; (tmp - buf < len) |
706 | 280k | && (*tmp == '\n' || *tmp == '\r' || *tmp == '\0' |
707 | 280k | || *tmp == '\t' || *tmp == ' '); |
708 | 261k | tmp++) |
709 | 261k | ; |
710 | 18.7k | offset = tmp - buf; |
711 | 18.7k | fl = &(msg->first_line); |
712 | 18.7k | rest = parse_first_line(tmp, len - offset, fl); |
713 | 18.7k | offset += rest - tmp; |
714 | 18.7k | tmp = rest; |
715 | 18.7k | switch(fl->type) { |
716 | 1.01k | case SIP_INVALID: |
717 | 1.01k | DBG("invalid message\n"); |
718 | 1.01k | goto error; |
719 | 0 | break; |
720 | 17.7k | case SIP_REQUEST: |
721 | 17.7k | DBG("SIP Request:\n"); |
722 | 17.7k | DBG(" method: <%.*s>\n", fl->u.request.method.len, |
723 | 0 | ZSW(fl->u.request.method.s)); |
724 | 17.7k | DBG(" uri: <%.*s>\n", fl->u.request.uri.len, |
725 | 0 | ZSW(fl->u.request.uri.s)); |
726 | 17.7k | DBG(" version: <%.*s>\n", fl->u.request.version.len, |
727 | 0 | ZSW(fl->u.request.version.s)); |
728 | 17.7k | flags = HDR_VIA_F; |
729 | 17.7k | break; |
730 | 52 | case SIP_REPLY: |
731 | 52 | DBG("SIP Reply (status):\n"); |
732 | 52 | DBG(" version: <%.*s>\n", fl->u.reply.version.len, |
733 | 0 | ZSW(fl->u.reply.version.s)); |
734 | 52 | DBG(" status: <%.*s>\n", fl->u.reply.status.len, |
735 | 0 | ZSW(fl->u.reply.status.s)); |
736 | 52 | DBG(" reason: <%.*s>\n", fl->u.reply.reason.len, |
737 | 0 | ZSW(fl->u.reply.reason.s)); |
738 | | /* flags=HDR_VIA | HDR_VIA2; */ |
739 | | /* we don't try to parse VIA2 for local messages; -Jiri */ |
740 | 52 | flags = HDR_VIA_F; |
741 | 52 | break; |
742 | 0 | default: |
743 | 0 | DBG("unknown type %d\n", fl->type); |
744 | 0 | goto error; |
745 | 18.7k | } |
746 | 17.7k | msg->unparsed = tmp; |
747 | | /*find first Via: */ |
748 | 17.7k | if(parse_headers(msg, flags, 0) == -1) |
749 | 3.51k | goto error; |
750 | | |
751 | | #ifdef EXTRA_DEBUG |
752 | | /* dump parsed data */ |
753 | | if(msg->via1) { |
754 | | DBG("first via: <%.*s/%.*s/%.*s> <%.*s:%.*s(%d)>", msg->via1->name.len, |
755 | | ZSW(msg->via1->name.s), msg->via1->version.len, |
756 | | ZSW(msg->via1->version.s), msg->via1->transport.len, |
757 | | ZSW(msg->via1->transport.s), msg->via1->host.len, |
758 | | ZSW(msg->via1->host.s), msg->via1->port_str.len, |
759 | | ZSW(msg->via1->port_str.s), msg->via1->port); |
760 | | if(msg->via1->params.s) |
761 | | DBG(";<%.*s>", msg->via1->params.len, ZSW(msg->via1->params.s)); |
762 | | if(msg->via1->comment.s) |
763 | | DBG(" <%.*s>", msg->via1->comment.len, ZSW(msg->via1->comment.s)); |
764 | | DBG("\n"); |
765 | | } |
766 | | if(msg->via2) { |
767 | | DBG("second via: <%.*s/%.*s/%.*s> <%.*s:%.*s(%d)>", msg->via2->name.len, |
768 | | ZSW(msg->via2->name.s), msg->via2->version.len, |
769 | | ZSW(msg->via2->version.s), msg->via2->transport.len, |
770 | | ZSW(msg->via2->transport.s), msg->via2->host.len, |
771 | | ZSW(msg->via2->host.s), msg->via2->port_str.len, |
772 | | ZSW(msg->via2->port_str.s), msg->via2->port); |
773 | | if(msg->via2->params.s) |
774 | | DBG(";<%.*s>", msg->via2->params.len, ZSW(msg->via2->params.s)); |
775 | | if(msg->via2->comment.s) |
776 | | DBG(" <%.*s>", msg->via2->comment.len, ZSW(msg->via2->comment.s)); |
777 | | DBG("\n"); |
778 | | } |
779 | | #endif |
780 | | |
781 | | |
782 | | #ifdef EXTRA_DEBUG |
783 | | DBG("exiting parse_msg\n"); |
784 | | #endif |
785 | | |
786 | 14.2k | return 0; |
787 | | |
788 | 4.52k | error: |
789 | | /* more debugging, msg->orig is/should be null terminated*/ |
790 | 4.52k | LOG(cfg_get(core, core_cfg, sip_parser_log), |
791 | 4.52k | "ERROR: parse_msg: message=<%.*s>\n", (int)msg->len, |
792 | 4.52k | ZSW(ksr_buf_oneline(msg->buf, (int)msg->len))); |
793 | 4.52k | return -1; |
794 | 17.7k | } |
795 | | |
796 | | |
797 | | void free_reply_lump(struct lump_rpl *lump) |
798 | 0 | { |
799 | 0 | struct lump_rpl *foo, *bar; |
800 | 0 | for(foo = lump; foo;) { |
801 | 0 | bar = foo->next; |
802 | 0 | free_lump_rpl(foo); |
803 | 0 | foo = bar; |
804 | 0 | } |
805 | 0 | } |
806 | | |
807 | | |
808 | | /*only the content*/ |
809 | | void free_sip_msg(struct sip_msg *const msg) |
810 | 18.7k | { |
811 | 18.7k | reset_new_uri(msg); |
812 | 18.7k | reset_dst_uri(msg); |
813 | 18.7k | reset_path_vector(msg); |
814 | 18.7k | reset_instance(msg); |
815 | 18.7k | reset_ruid(msg); |
816 | 18.7k | reset_ua(msg); |
817 | 18.7k | if(msg->headers) |
818 | 14.8k | free_hdr_field_lst(msg->headers); |
819 | 18.7k | if(msg->body && msg->body->free) |
820 | 256 | msg->body->free(&msg->body); |
821 | 18.7k | if(msg->add_rm) |
822 | 0 | free_lump_list(msg->add_rm); |
823 | 18.7k | if(msg->body_lumps) |
824 | 0 | free_lump_list(msg->body_lumps); |
825 | 18.7k | if(msg->reply_lump) |
826 | 0 | free_reply_lump(msg->reply_lump); |
827 | 18.7k | msg_ldata_reset(msg); |
828 | | /* no free of msg->buf -- a pointer to a static buffer */ |
829 | 18.7k | } |
830 | | |
831 | | /** |
832 | | * reset new uri value |
833 | | */ |
834 | | void reset_new_uri(struct sip_msg *const msg) |
835 | 18.7k | { |
836 | 18.7k | if(msg->new_uri.s != 0) { |
837 | 0 | pkg_free(msg->new_uri.s); |
838 | 0 | } |
839 | 18.7k | msg->new_uri.s = 0; |
840 | 18.7k | msg->new_uri.len = 0; |
841 | 18.7k | msg->parsed_uri_ok = 0; |
842 | 18.7k | } |
843 | | |
844 | | |
845 | | /* |
846 | | * Make a private copy of the string and assign it to dst_uri |
847 | | */ |
848 | | int set_dst_uri(struct sip_msg *const msg, const str *const uri) |
849 | 0 | { |
850 | 0 | char *ptr; |
851 | |
|
852 | 0 | if(unlikely(!msg || !uri)) { |
853 | 0 | ERR("Invalid parameter value\n"); |
854 | 0 | return -1; |
855 | 0 | } |
856 | | |
857 | 0 | if(unlikely(uri->len == 0)) { |
858 | 0 | reset_dst_uri(msg); |
859 | 0 | } else if(msg->dst_uri.s && (msg->dst_uri.len >= uri->len)) { |
860 | 0 | memcpy(msg->dst_uri.s, uri->s, uri->len); |
861 | 0 | msg->dst_uri.len = uri->len; |
862 | 0 | } else { |
863 | 0 | ptr = (char *)pkg_malloc(uri->len + 1); |
864 | 0 | if(!ptr) { |
865 | 0 | PKG_MEM_ERROR; |
866 | 0 | return -1; |
867 | 0 | } |
868 | | |
869 | 0 | memcpy(ptr, uri->s, uri->len); |
870 | 0 | if(msg->dst_uri.s) |
871 | 0 | pkg_free(msg->dst_uri.s); |
872 | 0 | msg->dst_uri.s = ptr; |
873 | 0 | msg->dst_uri.len = uri->len; |
874 | 0 | msg->dst_uri.s[msg->dst_uri.len] = '\0'; |
875 | 0 | } |
876 | 0 | return 0; |
877 | 0 | } |
878 | | |
879 | | |
880 | | void reset_dst_uri(struct sip_msg *const msg) |
881 | 18.7k | { |
882 | 18.7k | if(msg->dst_uri.s != 0) { |
883 | 0 | pkg_free(msg->dst_uri.s); |
884 | 0 | } |
885 | 18.7k | msg->dst_uri.s = 0; |
886 | 18.7k | msg->dst_uri.len = 0; |
887 | 18.7k | } |
888 | | |
889 | | int set_path_vector(struct sip_msg *msg, str *path) |
890 | 0 | { |
891 | 0 | char *ptr; |
892 | |
|
893 | 0 | if(unlikely(!msg || !path)) { |
894 | 0 | ERR("invalid parameter value\n"); |
895 | 0 | return -1; |
896 | 0 | } |
897 | | |
898 | 0 | if(unlikely(path->len == 0)) { |
899 | 0 | reset_path_vector(msg); |
900 | 0 | } else if(msg->path_vec.s && (msg->path_vec.len >= path->len)) { |
901 | 0 | memcpy(msg->path_vec.s, path->s, path->len); |
902 | 0 | msg->path_vec.len = path->len; |
903 | 0 | } else { |
904 | 0 | ptr = (char *)pkg_malloc(path->len); |
905 | 0 | if(!ptr) { |
906 | 0 | PKG_MEM_ERROR; |
907 | 0 | return -1; |
908 | 0 | } |
909 | | |
910 | 0 | memcpy(ptr, path->s, path->len); |
911 | 0 | if(msg->path_vec.s) |
912 | 0 | pkg_free(msg->path_vec.s); |
913 | 0 | msg->path_vec.s = ptr; |
914 | 0 | msg->path_vec.len = path->len; |
915 | 0 | } |
916 | 0 | return 0; |
917 | 0 | } |
918 | | |
919 | | |
920 | | void reset_path_vector(struct sip_msg *const msg) |
921 | 18.7k | { |
922 | 18.7k | if(!shm_address_in(msg->path_vec.s)) { |
923 | 18.7k | if(msg->path_vec.s) |
924 | 0 | pkg_free(msg->path_vec.s); |
925 | 18.7k | msg->path_vec.s = 0; |
926 | 18.7k | msg->path_vec.len = 0; |
927 | 18.7k | } else { |
928 | 0 | LM_WARN("Found path_vec that is not in pkg mem!\n"); |
929 | 0 | } |
930 | 18.7k | } |
931 | | |
932 | | |
933 | | int set_instance(struct sip_msg *msg, str *instance) |
934 | 0 | { |
935 | 0 | char *ptr; |
936 | |
|
937 | 0 | if(unlikely(!msg || !instance)) { |
938 | 0 | ERR("invalid instance parameter value\n"); |
939 | 0 | return -1; |
940 | 0 | } |
941 | | |
942 | 0 | if(unlikely(instance->len == 0)) { |
943 | 0 | reset_instance(msg); |
944 | 0 | } else if(msg->instance.s && (msg->instance.len >= instance->len)) { |
945 | 0 | memcpy(msg->instance.s, instance->s, instance->len); |
946 | 0 | msg->instance.len = instance->len; |
947 | 0 | } else { |
948 | 0 | ptr = (char *)pkg_malloc(instance->len); |
949 | 0 | if(!ptr) { |
950 | 0 | PKG_MEM_ERROR; |
951 | 0 | return -1; |
952 | 0 | } |
953 | 0 | memcpy(ptr, instance->s, instance->len); |
954 | 0 | if(msg->instance.s) |
955 | 0 | pkg_free(msg->instance.s); |
956 | 0 | msg->instance.s = ptr; |
957 | 0 | msg->instance.len = instance->len; |
958 | 0 | } |
959 | 0 | return 0; |
960 | 0 | } |
961 | | |
962 | | |
963 | | void reset_instance(struct sip_msg *const msg) |
964 | 18.7k | { |
965 | 18.7k | if(msg->instance.s != 0) { |
966 | 0 | pkg_free(msg->instance.s); |
967 | 0 | } |
968 | 18.7k | msg->instance.s = 0; |
969 | 18.7k | msg->instance.len = 0; |
970 | 18.7k | } |
971 | | |
972 | | |
973 | | int set_ruid(struct sip_msg *msg, str *ruid) |
974 | 0 | { |
975 | 0 | char *ptr; |
976 | |
|
977 | 0 | if(unlikely(!msg || !ruid)) { |
978 | 0 | ERR("invalid ruid parameter value\n"); |
979 | 0 | return -1; |
980 | 0 | } |
981 | | |
982 | 0 | if(unlikely(ruid->len == 0)) { |
983 | 0 | reset_ruid(msg); |
984 | 0 | } else if(msg->ruid.s && (msg->ruid.len >= ruid->len)) { |
985 | 0 | memcpy(msg->ruid.s, ruid->s, ruid->len); |
986 | 0 | msg->ruid.len = ruid->len; |
987 | 0 | } else { |
988 | 0 | ptr = (char *)pkg_malloc(ruid->len); |
989 | 0 | if(!ptr) { |
990 | 0 | PKG_MEM_ERROR; |
991 | 0 | return -1; |
992 | 0 | } |
993 | 0 | memcpy(ptr, ruid->s, ruid->len); |
994 | 0 | if(msg->ruid.s) |
995 | 0 | pkg_free(msg->ruid.s); |
996 | 0 | msg->ruid.s = ptr; |
997 | 0 | msg->ruid.len = ruid->len; |
998 | 0 | } |
999 | 0 | return 0; |
1000 | 0 | } |
1001 | | |
1002 | | |
1003 | | void reset_ruid(struct sip_msg *const msg) |
1004 | 18.7k | { |
1005 | 18.7k | if(msg->ruid.s != 0) { |
1006 | 0 | pkg_free(msg->ruid.s); |
1007 | 0 | } |
1008 | 18.7k | msg->ruid.s = 0; |
1009 | 18.7k | msg->ruid.len = 0; |
1010 | 18.7k | } |
1011 | | |
1012 | | |
1013 | | int set_ua(struct sip_msg *msg, str *location_ua) |
1014 | 0 | { |
1015 | 0 | char *ptr; |
1016 | |
|
1017 | 0 | if(unlikely(!msg || !location_ua)) { |
1018 | 0 | ERR("invalid location_ua parameter value\n"); |
1019 | 0 | return -1; |
1020 | 0 | } |
1021 | | |
1022 | 0 | if(unlikely(location_ua->len == 0)) { |
1023 | 0 | reset_ua(msg); |
1024 | 0 | } else if(msg->location_ua.s |
1025 | 0 | && (msg->location_ua.len >= location_ua->len)) { |
1026 | 0 | memcpy(msg->location_ua.s, location_ua->s, location_ua->len); |
1027 | 0 | msg->location_ua.len = location_ua->len; |
1028 | 0 | } else { |
1029 | 0 | ptr = (char *)pkg_malloc(location_ua->len); |
1030 | 0 | if(!ptr) { |
1031 | 0 | PKG_MEM_ERROR; |
1032 | 0 | return -1; |
1033 | 0 | } |
1034 | 0 | memcpy(ptr, location_ua->s, location_ua->len); |
1035 | 0 | if(msg->location_ua.s) |
1036 | 0 | pkg_free(msg->location_ua.s); |
1037 | 0 | msg->location_ua.s = ptr; |
1038 | 0 | msg->location_ua.len = location_ua->len; |
1039 | 0 | } |
1040 | 0 | return 0; |
1041 | 0 | } |
1042 | | |
1043 | | |
1044 | | void reset_ua(struct sip_msg *const msg) |
1045 | 18.7k | { |
1046 | 18.7k | if(msg->location_ua.s != 0) { |
1047 | 0 | pkg_free(msg->location_ua.s); |
1048 | 0 | } |
1049 | 18.7k | msg->location_ua.s = 0; |
1050 | 18.7k | msg->location_ua.len = 0; |
1051 | 18.7k | } |
1052 | | |
1053 | | /** |
1054 | | * reset content of msg->ldv (msg_ldata_t structure) |
1055 | | */ |
1056 | | void msg_ldata_reset(sip_msg_t *msg) |
1057 | 18.7k | { |
1058 | 18.7k | if(msg == NULL) |
1059 | 0 | return; |
1060 | 18.7k | memset(&msg->ldv, 0, sizeof(msg_ldata_t)); |
1061 | 18.7k | } |
1062 | | |
1063 | | |
1064 | | hdr_field_t *get_hdr(const sip_msg_t *const msg, const enum _hdr_types_t ht) |
1065 | 0 | { |
1066 | 0 | hdr_field_t *hdr; |
1067 | |
|
1068 | 0 | if(ht == HDR_ERROR_T || ht == HDR_EOH_T) { |
1069 | 0 | return NULL; |
1070 | 0 | } |
1071 | 0 | if(msg->parsed_flag & HDR_T2F(ht)) { |
1072 | 0 | for(hdr = msg->headers; hdr; hdr = hdr->next) { |
1073 | 0 | if(hdr->type == ht) |
1074 | 0 | return hdr; |
1075 | 0 | } |
1076 | 0 | } |
1077 | 0 | return NULL; |
1078 | 0 | } |
1079 | | |
1080 | | |
1081 | | hdr_field_t *next_sibling_hdr(const hdr_field_t *const hf) |
1082 | 9.82k | { |
1083 | 9.82k | hdr_field_t *hdr; |
1084 | | |
1085 | 11.4k | for(hdr = hf->next; hdr; hdr = hdr->next) { |
1086 | 11.2k | if(hdr->type == hf->type) |
1087 | 9.57k | return hdr; |
1088 | 11.2k | } |
1089 | 249 | return NULL; |
1090 | 9.82k | } |
1091 | | |
1092 | | hdr_field_t *get_hdr_by_name( |
1093 | | const sip_msg_t *const msg, const char *const name, const int name_len) |
1094 | 0 | { |
1095 | 0 | hdr_field_t *hdr; |
1096 | |
|
1097 | 0 | for(hdr = msg->headers; hdr; hdr = hdr->next) { |
1098 | 0 | if(hdr->name.len == name_len && *hdr->name.s == *name |
1099 | 0 | && strncasecmp(hdr->name.s, name, name_len) == 0) |
1100 | 0 | return hdr; |
1101 | 0 | } |
1102 | 0 | return NULL; |
1103 | 0 | } |
1104 | | |
1105 | | /** not used yet */ |
1106 | | hdr_field_t *next_sibling_hdr_by_name(const hdr_field_t *const hf) |
1107 | 0 | { |
1108 | 0 | hdr_field_t *hdr; |
1109 | |
|
1110 | 0 | for(hdr = hf->next; hdr; hdr = hdr->next) { |
1111 | 0 | if(hdr->name.len == hf->name.len && *hdr->name.s == *hf->name.s |
1112 | 0 | && strncasecmp(hdr->name.s, hf->name.s, hf->name.len) == 0) |
1113 | 0 | return hdr; |
1114 | 0 | } |
1115 | 0 | return NULL; |
1116 | 0 | } |
1117 | | |
1118 | | /** |
1119 | | * set msg context id |
1120 | | * - return: -1 on error; 0 - on set |
1121 | | */ |
1122 | | int msg_ctx_id_set(const sip_msg_t *const msg, msg_ctx_id_t *const mid) |
1123 | 0 | { |
1124 | 0 | if(msg == NULL || mid == NULL) |
1125 | 0 | return -1; |
1126 | 0 | mid->msgid = msg->id; |
1127 | 0 | mid->pid = msg->pid; |
1128 | 0 | return 0; |
1129 | 0 | } |
1130 | | |
1131 | | /** |
1132 | | * check msg context id |
1133 | | * - return: -1 on error; 0 - on no match; 1 - on match |
1134 | | */ |
1135 | | int msg_ctx_id_match(const sip_msg_t *const msg, const msg_ctx_id_t *const mid) |
1136 | 0 | { |
1137 | 0 | if(msg == NULL || mid == NULL) |
1138 | 0 | return -1; |
1139 | 0 | if(msg->id != mid->msgid || msg->pid != mid->pid) |
1140 | 0 | return 0; |
1141 | 0 | return 1; |
1142 | 0 | } |
1143 | | |
1144 | | /** |
1145 | | * set msg time value |
1146 | | */ |
1147 | | int msg_set_time(sip_msg_t *const msg) |
1148 | 0 | { |
1149 | 0 | if(unlikely(msg == NULL)) |
1150 | 0 | return -2; |
1151 | 0 | if(msg->tval.tv_sec != 0) |
1152 | 0 | return 0; |
1153 | 0 | return gettimeofday(&msg->tval, NULL); |
1154 | 0 | } |
1155 | | |
1156 | | /** |
1157 | | * replace \r\n with . and space |
1158 | | */ |
1159 | | char *ksr_buf_oneline(char *inbuf, int inlen) |
1160 | 84.8k | { |
1161 | 84.8k | static char outbuf[BUF_SIZE]; |
1162 | 84.8k | int outlen; |
1163 | 84.8k | int i = 0; |
1164 | | |
1165 | 84.8k | if(cfg_get(core, core_cfg, sip_parser_log_oneline) == 0) { |
1166 | 84.8k | return inbuf; |
1167 | 84.8k | } |
1168 | | |
1169 | 0 | if(inbuf == NULL) { |
1170 | 0 | outbuf[0] = '\0'; |
1171 | 0 | return outbuf; |
1172 | 0 | } |
1173 | | |
1174 | 0 | outlen = (inlen < BUF_SIZE) ? inlen : BUF_SIZE - 1; |
1175 | 0 | memcpy(outbuf, inbuf, outlen); |
1176 | 0 | outbuf[outlen] = '\0'; |
1177 | |
|
1178 | 0 | for(i = 0; i < outlen; i++) { |
1179 | 0 | if(outbuf[i] == '\r') { |
1180 | 0 | outbuf[i] = '.'; |
1181 | 0 | } else if(outbuf[i] == '\n') { |
1182 | 0 | outbuf[i] = ' '; |
1183 | 0 | } |
1184 | 0 | } |
1185 | |
|
1186 | 0 | return outbuf; |
1187 | 0 | } |
1188 | | |
1189 | | /** |
1190 | | * get source ip, port and protocol in SIP URI format |
1191 | | * - tmode - 0: short format (transport=udp is not added, being default) |
1192 | | */ |
1193 | | int get_src_uri(sip_msg_t *m, int tmode, str *uri) |
1194 | 14.2k | { |
1195 | 14.2k | static char buf[MAX_URI_SIZE]; |
1196 | 14.2k | char *p; |
1197 | 14.2k | str ip, port; |
1198 | 14.2k | int len; |
1199 | 14.2k | str proto; |
1200 | | |
1201 | 14.2k | if(!uri || !m) { |
1202 | 0 | ERR("invalid parameter value\n"); |
1203 | 0 | return -1; |
1204 | 0 | } |
1205 | | |
1206 | 14.2k | if(tmode == 0) { |
1207 | 14.2k | switch(m->rcv.proto) { |
1208 | 14.2k | case PROTO_NONE: |
1209 | 14.2k | case PROTO_UDP: |
1210 | 14.2k | proto.s = |
1211 | 14.2k | 0; /* Do not add transport parameter, UDP is default */ |
1212 | 14.2k | proto.len = 0; |
1213 | 14.2k | break; |
1214 | 0 | default: |
1215 | 0 | if(get_valid_proto_string(m->rcv.proto, 1, 0, &proto) < 0) { |
1216 | 0 | ERR("unknown transport protocol\n"); |
1217 | 0 | return -1; |
1218 | 0 | } |
1219 | 14.2k | } |
1220 | 14.2k | } else { |
1221 | 0 | if(get_valid_proto_string(m->rcv.proto, 1, 0, &proto) < 0) { |
1222 | 0 | ERR("unknown transport protocol\n"); |
1223 | 0 | return -1; |
1224 | 0 | } |
1225 | 0 | } |
1226 | | |
1227 | 14.2k | ip.s = ip_addr2a(&m->rcv.src_ip); |
1228 | 14.2k | ip.len = strlen(ip.s); |
1229 | | |
1230 | 14.2k | port.s = int2str(m->rcv.src_port, &port.len); |
1231 | | |
1232 | 14.2k | len = 4 + ip.len + 2 * (m->rcv.src_ip.af == AF_INET6) + 1 + port.len; |
1233 | 14.2k | if(proto.s) { |
1234 | 0 | len += TRANSPORT_PARAM_LEN; |
1235 | 0 | len += proto.len; |
1236 | 0 | } |
1237 | | |
1238 | 14.2k | if(len > MAX_URI_SIZE) { |
1239 | 0 | ERR("buffer too small\n"); |
1240 | 0 | return -1; |
1241 | 0 | } |
1242 | | |
1243 | 14.2k | p = buf; |
1244 | 14.2k | memcpy(p, "sip:", 4); |
1245 | 14.2k | p += 4; |
1246 | | |
1247 | 14.2k | if(m->rcv.src_ip.af == AF_INET6) |
1248 | 0 | *p++ = '['; |
1249 | 14.2k | memcpy(p, ip.s, ip.len); |
1250 | 14.2k | p += ip.len; |
1251 | 14.2k | if(m->rcv.src_ip.af == AF_INET6) |
1252 | 0 | *p++ = ']'; |
1253 | | |
1254 | 14.2k | *p++ = ':'; |
1255 | | |
1256 | 14.2k | memcpy(p, port.s, port.len); |
1257 | 14.2k | p += port.len; |
1258 | | |
1259 | 14.2k | if(proto.s) { |
1260 | 0 | memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); |
1261 | 0 | p += TRANSPORT_PARAM_LEN; |
1262 | |
|
1263 | 0 | memcpy(p, proto.s, proto.len); |
1264 | 0 | p += proto.len; |
1265 | 0 | } |
1266 | | |
1267 | 14.2k | uri->s = buf; |
1268 | 14.2k | uri->len = len; |
1269 | | |
1270 | 14.2k | return 0; |
1271 | 14.2k | } |
1272 | | |
1273 | | /** |
1274 | | * get source proto:ip:port (socket address format) |
1275 | | */ |
1276 | | int get_src_address_socket(sip_msg_t *m, str *ssock) |
1277 | 14.2k | { |
1278 | 14.2k | static char buf[MAX_URI_SIZE]; |
1279 | 14.2k | char *p; |
1280 | 14.2k | str ip, port; |
1281 | 14.2k | int len; |
1282 | 14.2k | str proto; |
1283 | | |
1284 | 14.2k | if(!ssock || !m) { |
1285 | 0 | ERR("invalid parameter value\n"); |
1286 | 0 | return -1; |
1287 | 0 | } |
1288 | | |
1289 | 14.2k | if(get_valid_proto_string(m->rcv.proto, 1, 0, &proto) < 0) { |
1290 | 14.2k | ERR("unknown transport protocol\n"); |
1291 | 14.2k | return -1; |
1292 | 14.2k | } |
1293 | | |
1294 | 0 | ip.s = ip_addr2a(&m->rcv.src_ip); |
1295 | 0 | ip.len = strlen(ip.s); |
1296 | |
|
1297 | 0 | port.s = int2str(m->rcv.src_port, &port.len); |
1298 | |
|
1299 | 0 | len = proto.len + 1 + ip.len + 2 * (m->rcv.src_ip.af == AF_INET6) + 1 |
1300 | 0 | + port.len; |
1301 | |
|
1302 | 0 | if(len + 1 >= MAX_URI_SIZE) { |
1303 | 0 | ERR("buffer too small\n"); |
1304 | 0 | return -1; |
1305 | 0 | } |
1306 | | |
1307 | 0 | p = buf; |
1308 | |
|
1309 | 0 | memcpy(p, proto.s, proto.len); |
1310 | 0 | p += proto.len; |
1311 | |
|
1312 | 0 | *p++ = ':'; |
1313 | |
|
1314 | 0 | if(m->rcv.src_ip.af == AF_INET6) |
1315 | 0 | *p++ = '['; |
1316 | 0 | memcpy(p, ip.s, ip.len); |
1317 | 0 | p += ip.len; |
1318 | 0 | if(m->rcv.src_ip.af == AF_INET6) |
1319 | 0 | *p++ = ']'; |
1320 | |
|
1321 | 0 | *p++ = ':'; |
1322 | |
|
1323 | 0 | memcpy(p, port.s, port.len); |
1324 | 0 | p += port.len; |
1325 | 0 | *p = '\0'; |
1326 | |
|
1327 | 0 | ssock->s = buf; |
1328 | 0 | ssock->len = len; |
1329 | |
|
1330 | 0 | return 0; |
1331 | 0 | } |
1332 | | |
1333 | | /** |
1334 | | * get received-on-socket ip, port and protocol in SIP URI format |
1335 | | * - tmode - 0: short format (transport=udp is not added, being default) |
1336 | | * - atype - 0: listen address; 1: advertised address |
1337 | | */ |
1338 | | int get_rcv_socket_uri(sip_msg_t *m, int tmode, str *uri, int atype) |
1339 | 0 | { |
1340 | 0 | static char buf[MAX_URI_SIZE]; |
1341 | 0 | char *p; |
1342 | 0 | str ip, port; |
1343 | 0 | int len; |
1344 | 0 | int af; |
1345 | 0 | str proto = STR_NULL; |
1346 | |
|
1347 | 0 | if(!uri || !m || !m->rcv.bind_address) { |
1348 | 0 | ERR("invalid parameter value\n"); |
1349 | 0 | return -1; |
1350 | 0 | } |
1351 | | |
1352 | 0 | if((tmode == 0) |
1353 | 0 | && (m->rcv.proto == PROTO_NONE || m->rcv.proto == PROTO_UDP)) { |
1354 | | /* do not add transport parameter, UDP is default */ |
1355 | 0 | proto.s = NULL; |
1356 | 0 | proto.len = 0; |
1357 | 0 | } else { |
1358 | 0 | if(atype == 0 || m->rcv.bind_address->useinfo.address_str.len <= 0 |
1359 | 0 | || m->rcv.bind_address->useinfo.proto == PROTO_NONE) { |
1360 | 0 | if(get_valid_proto_string(m->rcv.proto, 1, 0, &proto) < 0) { |
1361 | 0 | ERR("unknown transport protocol\n"); |
1362 | 0 | return -1; |
1363 | 0 | } |
1364 | 0 | } else { |
1365 | 0 | if(get_valid_proto_string( |
1366 | 0 | m->rcv.bind_address->useinfo.proto, 1, 0, &proto) |
1367 | 0 | < 0) { |
1368 | 0 | ERR("unknown transport protocol\n"); |
1369 | 0 | return -1; |
1370 | 0 | } |
1371 | 0 | } |
1372 | 0 | } |
1373 | | |
1374 | 0 | if(atype == 0 || m->rcv.bind_address->useinfo.address_str.len <= 0) { |
1375 | 0 | ip.s = m->rcv.bind_address->address_str.s; |
1376 | 0 | ip.len = m->rcv.bind_address->address_str.len; |
1377 | 0 | } else { |
1378 | 0 | ip.s = m->rcv.bind_address->useinfo.address_str.s; |
1379 | 0 | ip.len = m->rcv.bind_address->useinfo.address_str.len; |
1380 | 0 | } |
1381 | |
|
1382 | 0 | if(atype == 0 || m->rcv.bind_address->useinfo.port_no_str.len <= 0) { |
1383 | 0 | port.s = m->rcv.bind_address->port_no_str.s; |
1384 | 0 | port.len = m->rcv.bind_address->port_no_str.len; |
1385 | 0 | } else { |
1386 | 0 | port.s = m->rcv.bind_address->useinfo.port_no_str.s; |
1387 | 0 | port.len = m->rcv.bind_address->useinfo.port_no_str.len; |
1388 | 0 | } |
1389 | 0 | if(atype == 1 && m->rcv.bind_address->useinfo.address_str.len > 0) { |
1390 | 0 | af = m->rcv.bind_address->useinfo.af; |
1391 | 0 | } else { |
1392 | 0 | af = m->rcv.src_ip.af; |
1393 | 0 | } |
1394 | |
|
1395 | 0 | len = 4 + ip.len + 2 * (af == AF_INET6) + 1 + port.len; |
1396 | 0 | if(proto.s) { |
1397 | 0 | len += TRANSPORT_PARAM_LEN; |
1398 | 0 | len += proto.len; |
1399 | 0 | } |
1400 | |
|
1401 | 0 | if(len > MAX_URI_SIZE) { |
1402 | 0 | ERR("buffer too small\n"); |
1403 | 0 | return -1; |
1404 | 0 | } |
1405 | | |
1406 | 0 | p = buf; |
1407 | 0 | memcpy(p, "sip:", 4); |
1408 | 0 | p += 4; |
1409 | |
|
1410 | 0 | if(af == AF_INET6) |
1411 | 0 | *p++ = '['; |
1412 | 0 | memcpy(p, ip.s, ip.len); |
1413 | 0 | p += ip.len; |
1414 | 0 | if(af == AF_INET6) |
1415 | 0 | *p++ = ']'; |
1416 | |
|
1417 | 0 | *p++ = ':'; |
1418 | |
|
1419 | 0 | memcpy(p, port.s, port.len); |
1420 | 0 | p += port.len; |
1421 | |
|
1422 | 0 | if(proto.s) { |
1423 | 0 | memcpy(p, TRANSPORT_PARAM, TRANSPORT_PARAM_LEN); |
1424 | 0 | p += TRANSPORT_PARAM_LEN; |
1425 | |
|
1426 | 0 | memcpy(p, proto.s, proto.len); |
1427 | 0 | p += proto.len; |
1428 | 0 | } |
1429 | |
|
1430 | 0 | uri->s = buf; |
1431 | 0 | uri->len = len; |
1432 | |
|
1433 | 0 | return 0; |
1434 | 0 | } |
1435 | | |
1436 | | |
1437 | | /*! \brief returns a pointer to the beginning of the msg's body |
1438 | | */ |
1439 | | char *get_body(sip_msg_t *const msg) |
1440 | 14.2k | { |
1441 | 14.2k | int offset; |
1442 | 14.2k | unsigned int len; |
1443 | | |
1444 | 14.2k | if(parse_headers(msg, HDR_EOH_F, 0) == -1) { |
1445 | 3.98k | LM_ERR("failed to parse to end of headers\n"); |
1446 | 3.98k | return 0; |
1447 | 3.98k | } |
1448 | | |
1449 | 10.2k | if(msg->unparsed) { |
1450 | 10.2k | len = (unsigned int)(msg->unparsed - msg->buf); |
1451 | 10.2k | } else { |
1452 | 0 | LM_ERR("unparsed hook for end of headers is not set\n"); |
1453 | 0 | return 0; |
1454 | 0 | } |
1455 | | |
1456 | 10.2k | if((len + 2 <= msg->len) && (strncmp(CRLF, msg->unparsed, CRLF_LEN) == 0)) { |
1457 | 10 | offset = CRLF_LEN; |
1458 | 10.2k | } else if((len + 1 <= msg->len) |
1459 | 10.2k | && (*(msg->unparsed) == '\n' || *(msg->unparsed) == '\r')) { |
1460 | 1.74k | offset = 1; |
1461 | 8.50k | } else { |
1462 | 8.50k | LM_ERR("failed to locate end of headers (%p %p - %d %d [%.*s])\n", |
1463 | 8.50k | msg->buf, msg->unparsed, msg->len, len, |
1464 | 8.50k | (len < msg->len) ? (msg->len - len) : 0, |
1465 | 8.50k | (len < msg->len) ? msg->unparsed : ""); |
1466 | 8.50k | return 0; |
1467 | 8.50k | } |
1468 | | |
1469 | 1.75k | return msg->unparsed + offset; |
1470 | 10.2k | } |
1471 | | |
1472 | | /*! \brief make sure all HFs needed for transaction identification have been |
1473 | | * parsed; return 0 if those HFs can't be found |
1474 | | */ |
1475 | | int check_transaction_quadruple(sip_msg_t *const msg) |
1476 | 0 | { |
1477 | 0 | if(parse_headers(msg, HDR_FROM_F | HDR_TO_F | HDR_CALLID_F | HDR_CSEQ_F, 0) |
1478 | 0 | != -1 |
1479 | 0 | && msg->from && msg->to && msg->callid && msg->cseq) { |
1480 | 0 | return 1; |
1481 | 0 | } else { |
1482 | 0 | ser_error = E_BAD_TUPEL; |
1483 | 0 | return 0; |
1484 | 0 | } |
1485 | 0 | } |