|
@@ -47,109 +47,6 @@ unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
|
|
|
struct nf_conntrack_expect *exp) __read_mostly;
|
|
|
EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
|
|
|
|
|
|
-static int digits_len(const struct nf_conn *, const char *, const char *, int *);
|
|
|
-static int epaddr_len(const struct nf_conn *, const char *, const char *, int *);
|
|
|
-static int skp_digits_len(const struct nf_conn *, const char *, const char *, int *);
|
|
|
-static int skp_epaddr_len(const struct nf_conn *, const char *, const char *, int *);
|
|
|
-
|
|
|
-struct sip_header_nfo {
|
|
|
- const char *lname;
|
|
|
- const char *sname;
|
|
|
- const char *ln_str;
|
|
|
- size_t lnlen;
|
|
|
- size_t snlen;
|
|
|
- size_t ln_strlen;
|
|
|
- int case_sensitive;
|
|
|
- int (*match_len)(const struct nf_conn *, const char *,
|
|
|
- const char *, int *);
|
|
|
-};
|
|
|
-
|
|
|
-static const struct sip_header_nfo ct_sip_hdrs[] = {
|
|
|
- [POS_FROM] = { /* SIP From header */
|
|
|
- .lname = "From:",
|
|
|
- .lnlen = sizeof("From:") - 1,
|
|
|
- .sname = "\r\nf:",
|
|
|
- .snlen = sizeof("\r\nf:") - 1,
|
|
|
- .ln_str = "sip:",
|
|
|
- .ln_strlen = sizeof("sip:") - 1,
|
|
|
- .match_len = skp_epaddr_len,
|
|
|
- },
|
|
|
- [POS_TO] = { /* SIP To header */
|
|
|
- .lname = "To:",
|
|
|
- .lnlen = sizeof("To:") - 1,
|
|
|
- .sname = "\r\nt:",
|
|
|
- .snlen = sizeof("\r\nt:") - 1,
|
|
|
- .ln_str = "sip:",
|
|
|
- .ln_strlen = sizeof("sip:") - 1,
|
|
|
- .match_len = skp_epaddr_len
|
|
|
- },
|
|
|
- [POS_VIA] = { /* SIP Via header */
|
|
|
- .lname = "Via:",
|
|
|
- .lnlen = sizeof("Via:") - 1,
|
|
|
- .sname = "\r\nv:",
|
|
|
- .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
|
|
|
- .ln_str = "UDP ",
|
|
|
- .ln_strlen = sizeof("UDP ") - 1,
|
|
|
- .match_len = epaddr_len,
|
|
|
- },
|
|
|
- [POS_CONTACT] = { /* SIP Contact header */
|
|
|
- .lname = "Contact:",
|
|
|
- .lnlen = sizeof("Contact:") - 1,
|
|
|
- .sname = "\r\nm:",
|
|
|
- .snlen = sizeof("\r\nm:") - 1,
|
|
|
- .ln_str = "sip:",
|
|
|
- .ln_strlen = sizeof("sip:") - 1,
|
|
|
- .match_len = skp_epaddr_len
|
|
|
- },
|
|
|
- [POS_CONTENT] = { /* SIP Content length header */
|
|
|
- .lname = "Content-Length:",
|
|
|
- .lnlen = sizeof("Content-Length:") - 1,
|
|
|
- .sname = "\r\nl:",
|
|
|
- .snlen = sizeof("\r\nl:") - 1,
|
|
|
- .ln_str = ":",
|
|
|
- .ln_strlen = sizeof(":") - 1,
|
|
|
- .match_len = skp_digits_len
|
|
|
- },
|
|
|
-};
|
|
|
-
|
|
|
-/* get line length until first CR or LF seen. */
|
|
|
-int ct_sip_lnlen(const char *line, const char *limit)
|
|
|
-{
|
|
|
- const char *k = line;
|
|
|
-
|
|
|
- while ((line < limit) && (*line == '\r' || *line == '\n'))
|
|
|
- line++;
|
|
|
-
|
|
|
- while (line < limit) {
|
|
|
- if (*line == '\r' || *line == '\n')
|
|
|
- break;
|
|
|
- line++;
|
|
|
- }
|
|
|
- return line - k;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(ct_sip_lnlen);
|
|
|
-
|
|
|
-/* Linear string search, case sensitive. */
|
|
|
-const char *ct_sip_search(const char *needle, const char *haystack,
|
|
|
- size_t needle_len, size_t haystack_len,
|
|
|
- int case_sensitive)
|
|
|
-{
|
|
|
- const char *limit = haystack + (haystack_len - needle_len);
|
|
|
-
|
|
|
- while (haystack < limit) {
|
|
|
- if (case_sensitive) {
|
|
|
- if (strncmp(haystack, needle, needle_len) == 0)
|
|
|
- return haystack;
|
|
|
- } else {
|
|
|
- if (strnicmp(haystack, needle, needle_len) == 0)
|
|
|
- return haystack;
|
|
|
- }
|
|
|
- haystack++;
|
|
|
- }
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL_GPL(ct_sip_search);
|
|
|
-
|
|
|
static int string_len(const struct nf_conn *ct, const char *dptr,
|
|
|
const char *limit, int *shift)
|
|
|
{
|
|
@@ -173,16 +70,6 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
|
|
|
return len;
|
|
|
}
|
|
|
|
|
|
-/* get digits length, skipping blank spaces. */
|
|
|
-static int skp_digits_len(const struct nf_conn *ct, const char *dptr,
|
|
|
- const char *limit, int *shift)
|
|
|
-{
|
|
|
- for (; dptr < limit && *dptr == ' '; dptr++)
|
|
|
- (*shift)++;
|
|
|
-
|
|
|
- return digits_len(ct, dptr, limit, shift);
|
|
|
-}
|
|
|
-
|
|
|
static int parse_addr(const struct nf_conn *ct, const char *cp,
|
|
|
const char **endp, union nf_inet_addr *addr,
|
|
|
const char *limit)
|
|
@@ -294,50 +181,146 @@ int ct_sip_parse_request(const struct nf_conn *ct,
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(ct_sip_parse_request);
|
|
|
|
|
|
-/* Returns 0 if not found, -1 error parsing. */
|
|
|
-int ct_sip_get_info(const struct nf_conn *ct,
|
|
|
- const char *dptr, size_t dlen,
|
|
|
- unsigned int *matchoff,
|
|
|
- unsigned int *matchlen,
|
|
|
- enum sip_header_pos pos)
|
|
|
+/* SIP header parsing: SIP headers are located at the beginning of a line, but
|
|
|
+ * may span several lines, in which case the continuation lines begin with a
|
|
|
+ * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
|
|
|
+ * CRLF, RFC 3261 allows only CRLF, we support both.
|
|
|
+ *
|
|
|
+ * Headers are followed by (optionally) whitespace, a colon, again (optionally)
|
|
|
+ * whitespace and the values. Whitespace in this context means any amount of
|
|
|
+ * tabs, spaces and continuation lines, which are treated as a single whitespace
|
|
|
+ * character.
|
|
|
+ */
|
|
|
+static const struct sip_header ct_sip_hdrs[] = {
|
|
|
+ [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
|
|
|
+ [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
|
|
|
+ [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
|
|
|
+ [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
|
|
|
+ [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
|
|
|
+};
|
|
|
+
|
|
|
+static const char *sip_follow_continuation(const char *dptr, const char *limit)
|
|
|
{
|
|
|
- const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos];
|
|
|
- const char *limit, *aux, *k = dptr;
|
|
|
- int shift = 0;
|
|
|
+ /* Walk past newline */
|
|
|
+ if (++dptr >= limit)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* Skip '\n' in CR LF */
|
|
|
+ if (*(dptr - 1) == '\r' && *dptr == '\n') {
|
|
|
+ if (++dptr >= limit)
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Continuation line? */
|
|
|
+ if (*dptr != ' ' && *dptr != '\t')
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* skip leading whitespace */
|
|
|
+ for (; dptr < limit; dptr++) {
|
|
|
+ if (*dptr != ' ' && *dptr != '\t')
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return dptr;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *sip_skip_whitespace(const char *dptr, const char *limit)
|
|
|
+{
|
|
|
+ for (; dptr < limit; dptr++) {
|
|
|
+ if (*dptr == ' ')
|
|
|
+ continue;
|
|
|
+ if (*dptr != '\r' && *dptr != '\n')
|
|
|
+ break;
|
|
|
+ dptr = sip_follow_continuation(dptr, limit);
|
|
|
+ if (dptr == NULL)
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ return dptr;
|
|
|
+}
|
|
|
|
|
|
- limit = dptr + (dlen - hnfo->lnlen);
|
|
|
+/* Search within a SIP header value, dealing with continuation lines */
|
|
|
+static const char *ct_sip_header_search(const char *dptr, const char *limit,
|
|
|
+ const char *needle, unsigned int len)
|
|
|
+{
|
|
|
+ for (limit -= len; dptr < limit; dptr++) {
|
|
|
+ if (*dptr == '\r' || *dptr == '\n') {
|
|
|
+ dptr = sip_follow_continuation(dptr, limit);
|
|
|
+ if (dptr == NULL)
|
|
|
+ break;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- while (dptr < limit) {
|
|
|
- if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
|
|
|
- (hnfo->sname == NULL ||
|
|
|
- strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
|
|
|
- dptr++;
|
|
|
+ if (strnicmp(dptr, needle, len) == 0)
|
|
|
+ return dptr;
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
|
|
|
+ unsigned int dataoff, unsigned int datalen,
|
|
|
+ enum sip_header_types type,
|
|
|
+ unsigned int *matchoff, unsigned int *matchlen)
|
|
|
+{
|
|
|
+ const struct sip_header *hdr = &ct_sip_hdrs[type];
|
|
|
+ const char *start = dptr, *limit = dptr + datalen;
|
|
|
+ int shift = 0;
|
|
|
+
|
|
|
+ for (dptr += dataoff; dptr < limit; dptr++) {
|
|
|
+ /* Find beginning of line */
|
|
|
+ if (*dptr != '\r' && *dptr != '\n')
|
|
|
continue;
|
|
|
+ if (++dptr >= limit)
|
|
|
+ break;
|
|
|
+ if (*(dptr - 1) == '\r' && *dptr == '\n') {
|
|
|
+ if (++dptr >= limit)
|
|
|
+ break;
|
|
|
}
|
|
|
- aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
|
|
|
- ct_sip_lnlen(dptr, limit),
|
|
|
- hnfo->case_sensitive);
|
|
|
- if (!aux) {
|
|
|
- pr_debug("'%s' not found in '%s'.\n", hnfo->ln_str,
|
|
|
- hnfo->lname);
|
|
|
- return -1;
|
|
|
+
|
|
|
+ /* Skip continuation lines */
|
|
|
+ if (*dptr == ' ' || *dptr == '\t')
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Find header. Compact headers must be followed by a
|
|
|
+ * non-alphabetic character to avoid mismatches. */
|
|
|
+ if (limit - dptr >= hdr->len &&
|
|
|
+ strnicmp(dptr, hdr->name, hdr->len) == 0)
|
|
|
+ dptr += hdr->len;
|
|
|
+ else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
|
|
|
+ strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
|
|
|
+ !isalpha(*(dptr + hdr->clen + 1)))
|
|
|
+ dptr += hdr->clen;
|
|
|
+ else
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* Find and skip colon */
|
|
|
+ dptr = sip_skip_whitespace(dptr, limit);
|
|
|
+ if (dptr == NULL)
|
|
|
+ break;
|
|
|
+ if (*dptr != ':' || ++dptr >= limit)
|
|
|
+ break;
|
|
|
+
|
|
|
+ /* Skip whitespace after colon */
|
|
|
+ dptr = sip_skip_whitespace(dptr, limit);
|
|
|
+ if (dptr == NULL)
|
|
|
+ break;
|
|
|
+
|
|
|
+ *matchoff = dptr - start;
|
|
|
+ if (hdr->search) {
|
|
|
+ dptr = ct_sip_header_search(dptr, limit, hdr->search,
|
|
|
+ hdr->slen);
|
|
|
+ if (!dptr)
|
|
|
+ return -1;
|
|
|
+ dptr += hdr->slen;
|
|
|
}
|
|
|
- aux += hnfo->ln_strlen;
|
|
|
|
|
|
- *matchlen = hnfo->match_len(ct, aux, limit, &shift);
|
|
|
+ *matchlen = hdr->match_len(ct, dptr, limit, &shift);
|
|
|
if (!*matchlen)
|
|
|
return -1;
|
|
|
-
|
|
|
- *matchoff = (aux - k) + shift;
|
|
|
-
|
|
|
- pr_debug("%s match succeeded! - len: %u\n", hnfo->lname,
|
|
|
- *matchlen);
|
|
|
+ *matchoff = dptr - start + shift;
|
|
|
return 1;
|
|
|
}
|
|
|
- pr_debug("%s header not found.\n", hnfo->lname);
|
|
|
return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL_GPL(ct_sip_get_info);
|
|
|
+EXPORT_SYMBOL_GPL(ct_sip_get_header);
|
|
|
|
|
|
/* SDP header parsing: a SDP session description contains an ordered set of
|
|
|
* headers, starting with a section containing general session parameters,
|