aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/netfilter/nf_conntrack_sip.h5
-rw-r--r--net/netfilter/nf_conntrack_sip.c107
2 files changed, 112 insertions, 0 deletions
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index ccc701422963..87bc6f79efc4 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -67,6 +67,11 @@ extern int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
67 unsigned int dataoff, unsigned int datalen, 67 unsigned int dataoff, unsigned int datalen,
68 enum sip_header_types type, 68 enum sip_header_types type,
69 unsigned int *matchoff, unsigned int *matchlen); 69 unsigned int *matchoff, unsigned int *matchlen);
70extern int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
71 unsigned int *dataoff, unsigned int datalen,
72 enum sip_header_types type, int *in_header,
73 unsigned int *matchoff, unsigned int *matchlen,
74 union nf_inet_addr *addr, __be16 *port);
70 75
71extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 76extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
72 unsigned int dataoff, unsigned int datalen, 77 unsigned int dataoff, unsigned int datalen,
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index cbc91598acee..a74d76a97312 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -190,6 +190,9 @@ EXPORT_SYMBOL_GPL(ct_sip_parse_request);
190 * whitespace and the values. Whitespace in this context means any amount of 190 * whitespace and the values. Whitespace in this context means any amount of
191 * tabs, spaces and continuation lines, which are treated as a single whitespace 191 * tabs, spaces and continuation lines, which are treated as a single whitespace
192 * character. 192 * character.
193 *
194 * Some headers may appear multiple times. A comma seperated list of values is
195 * equivalent to multiple headers.
193 */ 196 */
194static const struct sip_header ct_sip_hdrs[] = { 197static const struct sip_header ct_sip_hdrs[] = {
195 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len), 198 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
@@ -322,6 +325,110 @@ int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
322} 325}
323EXPORT_SYMBOL_GPL(ct_sip_get_header); 326EXPORT_SYMBOL_GPL(ct_sip_get_header);
324 327
328/* Get next header field in a list of comma seperated values */
329static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
330 unsigned int dataoff, unsigned int datalen,
331 enum sip_header_types type,
332 unsigned int *matchoff, unsigned int *matchlen)
333{
334 const struct sip_header *hdr = &ct_sip_hdrs[type];
335 const char *start = dptr, *limit = dptr + datalen;
336 int shift = 0;
337
338 dptr += dataoff;
339
340 dptr = ct_sip_header_search(dptr, limit, ",", strlen(","));
341 if (!dptr)
342 return 0;
343
344 dptr = ct_sip_header_search(dptr, limit, hdr->search, hdr->slen);
345 if (!dptr)
346 return 0;
347 dptr += hdr->slen;
348
349 *matchoff = dptr - start;
350 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
351 if (!*matchlen)
352 return -1;
353 *matchoff += shift;
354 return 1;
355}
356
357/* Walk through headers until a parsable one is found or no header of the
358 * given type is left. */
359static int ct_sip_walk_headers(const struct nf_conn *ct, const char *dptr,
360 unsigned int dataoff, unsigned int datalen,
361 enum sip_header_types type, int *in_header,
362 unsigned int *matchoff, unsigned int *matchlen)
363{
364 int ret;
365
366 if (in_header && *in_header) {
367 while (1) {
368 ret = ct_sip_next_header(ct, dptr, dataoff, datalen,
369 type, matchoff, matchlen);
370 if (ret > 0)
371 return ret;
372 if (ret == 0)
373 break;
374 dataoff += *matchoff;
375 }
376 *in_header = 0;
377 }
378
379 while (1) {
380 ret = ct_sip_get_header(ct, dptr, dataoff, datalen,
381 type, matchoff, matchlen);
382 if (ret > 0)
383 break;
384 if (ret == 0)
385 return ret;
386 dataoff += *matchoff;
387 }
388
389 if (in_header)
390 *in_header = 1;
391 return 1;
392}
393
394/* Locate a SIP header, parse the URI and return the offset and length of
395 * the address as well as the address and port themselves. A stream of
396 * headers can be parsed by handing in a non-NULL datalen and in_header
397 * pointer.
398 */
399int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
400 unsigned int *dataoff, unsigned int datalen,
401 enum sip_header_types type, int *in_header,
402 unsigned int *matchoff, unsigned int *matchlen,
403 union nf_inet_addr *addr, __be16 *port)
404{
405 const char *c, *limit = dptr + datalen;
406 unsigned int p;
407 int ret;
408
409 ret = ct_sip_walk_headers(ct, dptr, dataoff ? *dataoff : 0, datalen,
410 type, in_header, matchoff, matchlen);
411 WARN_ON(ret < 0);
412 if (ret == 0)
413 return ret;
414
415 if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
416 return -1;
417 if (*c == ':') {
418 c++;
419 p = simple_strtoul(c, (char **)&c, 10);
420 if (p < 1024 || p > 65535)
421 return -1;
422 *port = htons(p);
423 } else
424 *port = htons(SIP_PORT);
425
426 if (dataoff)
427 *dataoff = c - dptr;
428 return 1;
429}
430EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
431
325/* SDP header parsing: a SDP session description contains an ordered set of 432/* SDP header parsing: a SDP session description contains an ordered set of
326 * headers, starting with a section containing general session parameters, 433 * headers, starting with a section containing general session parameters,
327 * optionally followed by multiple media descriptions. 434 * optionally followed by multiple media descriptions.