aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2008-03-25 23:18:57 -0400
committerDavid S. Miller <davem@davemloft.net>2008-03-25 23:18:57 -0400
commitea45f12a2766dae54e5426a23e8f4bafdbe2782e (patch)
treeeb3096bee1e0053c545424f9cea5d0c91b114fe4 /net/netfilter
parentac3677406d4e36e86b1eb5a453997a3b3e0c089a (diff)
[NETFILTER]: nf_conntrack_sip: parse SIP headers properly
Introduce new function for SIP header parsing that properly deals with continuation lines and whitespace in headers and use it. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/nf_conntrack_sip.c271
1 files changed, 127 insertions, 144 deletions
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index bb4396155681..cbc91598acee 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -47,109 +47,6 @@ unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
47 struct nf_conntrack_expect *exp) __read_mostly; 47 struct nf_conntrack_expect *exp) __read_mostly;
48EXPORT_SYMBOL_GPL(nf_nat_sdp_hook); 48EXPORT_SYMBOL_GPL(nf_nat_sdp_hook);
49 49
50static int digits_len(const struct nf_conn *, const char *, const char *, int *);
51static int epaddr_len(const struct nf_conn *, const char *, const char *, int *);
52static int skp_digits_len(const struct nf_conn *, const char *, const char *, int *);
53static int skp_epaddr_len(const struct nf_conn *, const char *, const char *, int *);
54
55struct sip_header_nfo {
56 const char *lname;
57 const char *sname;
58 const char *ln_str;
59 size_t lnlen;
60 size_t snlen;
61 size_t ln_strlen;
62 int case_sensitive;
63 int (*match_len)(const struct nf_conn *, const char *,
64 const char *, int *);
65};
66
67static const struct sip_header_nfo ct_sip_hdrs[] = {
68 [POS_FROM] = { /* SIP From header */
69 .lname = "From:",
70 .lnlen = sizeof("From:") - 1,
71 .sname = "\r\nf:",
72 .snlen = sizeof("\r\nf:") - 1,
73 .ln_str = "sip:",
74 .ln_strlen = sizeof("sip:") - 1,
75 .match_len = skp_epaddr_len,
76 },
77 [POS_TO] = { /* SIP To header */
78 .lname = "To:",
79 .lnlen = sizeof("To:") - 1,
80 .sname = "\r\nt:",
81 .snlen = sizeof("\r\nt:") - 1,
82 .ln_str = "sip:",
83 .ln_strlen = sizeof("sip:") - 1,
84 .match_len = skp_epaddr_len
85 },
86 [POS_VIA] = { /* SIP Via header */
87 .lname = "Via:",
88 .lnlen = sizeof("Via:") - 1,
89 .sname = "\r\nv:",
90 .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
91 .ln_str = "UDP ",
92 .ln_strlen = sizeof("UDP ") - 1,
93 .match_len = epaddr_len,
94 },
95 [POS_CONTACT] = { /* SIP Contact header */
96 .lname = "Contact:",
97 .lnlen = sizeof("Contact:") - 1,
98 .sname = "\r\nm:",
99 .snlen = sizeof("\r\nm:") - 1,
100 .ln_str = "sip:",
101 .ln_strlen = sizeof("sip:") - 1,
102 .match_len = skp_epaddr_len
103 },
104 [POS_CONTENT] = { /* SIP Content length header */
105 .lname = "Content-Length:",
106 .lnlen = sizeof("Content-Length:") - 1,
107 .sname = "\r\nl:",
108 .snlen = sizeof("\r\nl:") - 1,
109 .ln_str = ":",
110 .ln_strlen = sizeof(":") - 1,
111 .match_len = skp_digits_len
112 },
113};
114
115/* get line length until first CR or LF seen. */
116int ct_sip_lnlen(const char *line, const char *limit)
117{
118 const char *k = line;
119
120 while ((line < limit) && (*line == '\r' || *line == '\n'))
121 line++;
122
123 while (line < limit) {
124 if (*line == '\r' || *line == '\n')
125 break;
126 line++;
127 }
128 return line - k;
129}
130EXPORT_SYMBOL_GPL(ct_sip_lnlen);
131
132/* Linear string search, case sensitive. */
133const char *ct_sip_search(const char *needle, const char *haystack,
134 size_t needle_len, size_t haystack_len,
135 int case_sensitive)
136{
137 const char *limit = haystack + (haystack_len - needle_len);
138
139 while (haystack < limit) {
140 if (case_sensitive) {
141 if (strncmp(haystack, needle, needle_len) == 0)
142 return haystack;
143 } else {
144 if (strnicmp(haystack, needle, needle_len) == 0)
145 return haystack;
146 }
147 haystack++;
148 }
149 return NULL;
150}
151EXPORT_SYMBOL_GPL(ct_sip_search);
152
153static int string_len(const struct nf_conn *ct, const char *dptr, 50static int string_len(const struct nf_conn *ct, const char *dptr,
154 const char *limit, int *shift) 51 const char *limit, int *shift)
155{ 52{
@@ -173,16 +70,6 @@ static int digits_len(const struct nf_conn *ct, const char *dptr,
173 return len; 70 return len;
174} 71}
175 72
176/* get digits length, skipping blank spaces. */
177static int skp_digits_len(const struct nf_conn *ct, const char *dptr,
178 const char *limit, int *shift)
179{
180 for (; dptr < limit && *dptr == ' '; dptr++)
181 (*shift)++;
182
183 return digits_len(ct, dptr, limit, shift);
184}
185
186static int parse_addr(const struct nf_conn *ct, const char *cp, 73static int parse_addr(const struct nf_conn *ct, const char *cp,
187 const char **endp, union nf_inet_addr *addr, 74 const char **endp, union nf_inet_addr *addr,
188 const char *limit) 75 const char *limit)
@@ -294,50 +181,146 @@ int ct_sip_parse_request(const struct nf_conn *ct,
294} 181}
295EXPORT_SYMBOL_GPL(ct_sip_parse_request); 182EXPORT_SYMBOL_GPL(ct_sip_parse_request);
296 183
297/* Returns 0 if not found, -1 error parsing. */ 184/* SIP header parsing: SIP headers are located at the beginning of a line, but
298int ct_sip_get_info(const struct nf_conn *ct, 185 * may span several lines, in which case the continuation lines begin with a
299 const char *dptr, size_t dlen, 186 * whitespace character. RFC 2543 allows lines to be terminated with CR, LF or
300 unsigned int *matchoff, 187 * CRLF, RFC 3261 allows only CRLF, we support both.
301 unsigned int *matchlen, 188 *
302 enum sip_header_pos pos) 189 * Headers are followed by (optionally) whitespace, a colon, again (optionally)
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
192 * character.
193 */
194static const struct sip_header ct_sip_hdrs[] = {
195 [SIP_HDR_FROM] = SIP_HDR("From", "f", "sip:", skp_epaddr_len),
196 [SIP_HDR_TO] = SIP_HDR("To", "t", "sip:", skp_epaddr_len),
197 [SIP_HDR_CONTACT] = SIP_HDR("Contact", "m", "sip:", skp_epaddr_len),
198 [SIP_HDR_VIA] = SIP_HDR("Via", "v", "UDP ", epaddr_len),
199 [SIP_HDR_CONTENT_LENGTH] = SIP_HDR("Content-Length", "l", NULL, digits_len),
200};
201
202static const char *sip_follow_continuation(const char *dptr, const char *limit)
303{ 203{
304 const struct sip_header_nfo *hnfo = &ct_sip_hdrs[pos]; 204 /* Walk past newline */
305 const char *limit, *aux, *k = dptr; 205 if (++dptr >= limit)
306 int shift = 0; 206 return NULL;
207
208 /* Skip '\n' in CR LF */
209 if (*(dptr - 1) == '\r' && *dptr == '\n') {
210 if (++dptr >= limit)
211 return NULL;
212 }
213
214 /* Continuation line? */
215 if (*dptr != ' ' && *dptr != '\t')
216 return NULL;
217
218 /* skip leading whitespace */
219 for (; dptr < limit; dptr++) {
220 if (*dptr != ' ' && *dptr != '\t')
221 break;
222 }
223 return dptr;
224}
225
226static const char *sip_skip_whitespace(const char *dptr, const char *limit)
227{
228 for (; dptr < limit; dptr++) {
229 if (*dptr == ' ')
230 continue;
231 if (*dptr != '\r' && *dptr != '\n')
232 break;
233 dptr = sip_follow_continuation(dptr, limit);
234 if (dptr == NULL)
235 return NULL;
236 }
237 return dptr;
238}
307 239
308 limit = dptr + (dlen - hnfo->lnlen); 240/* Search within a SIP header value, dealing with continuation lines */
241static const char *ct_sip_header_search(const char *dptr, const char *limit,
242 const char *needle, unsigned int len)
243{
244 for (limit -= len; dptr < limit; dptr++) {
245 if (*dptr == '\r' || *dptr == '\n') {
246 dptr = sip_follow_continuation(dptr, limit);
247 if (dptr == NULL)
248 break;
249 continue;
250 }
309 251
310 while (dptr < limit) { 252 if (strnicmp(dptr, needle, len) == 0)
311 if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && 253 return dptr;
312 (hnfo->sname == NULL || 254 }
313 strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { 255 return NULL;
314 dptr++; 256}
257
258int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
259 unsigned int dataoff, unsigned int datalen,
260 enum sip_header_types type,
261 unsigned int *matchoff, unsigned int *matchlen)
262{
263 const struct sip_header *hdr = &ct_sip_hdrs[type];
264 const char *start = dptr, *limit = dptr + datalen;
265 int shift = 0;
266
267 for (dptr += dataoff; dptr < limit; dptr++) {
268 /* Find beginning of line */
269 if (*dptr != '\r' && *dptr != '\n')
315 continue; 270 continue;
271 if (++dptr >= limit)
272 break;
273 if (*(dptr - 1) == '\r' && *dptr == '\n') {
274 if (++dptr >= limit)
275 break;
316 } 276 }
317 aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, 277
318 ct_sip_lnlen(dptr, limit), 278 /* Skip continuation lines */
319 hnfo->case_sensitive); 279 if (*dptr == ' ' || *dptr == '\t')
320 if (!aux) { 280 continue;
321 pr_debug("'%s' not found in '%s'.\n", hnfo->ln_str, 281
322 hnfo->lname); 282 /* Find header. Compact headers must be followed by a
323 return -1; 283 * non-alphabetic character to avoid mismatches. */
284 if (limit - dptr >= hdr->len &&
285 strnicmp(dptr, hdr->name, hdr->len) == 0)
286 dptr += hdr->len;
287 else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
288 strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
289 !isalpha(*(dptr + hdr->clen + 1)))
290 dptr += hdr->clen;
291 else
292 continue;
293
294 /* Find and skip colon */
295 dptr = sip_skip_whitespace(dptr, limit);
296 if (dptr == NULL)
297 break;
298 if (*dptr != ':' || ++dptr >= limit)
299 break;
300
301 /* Skip whitespace after colon */
302 dptr = sip_skip_whitespace(dptr, limit);
303 if (dptr == NULL)
304 break;
305
306 *matchoff = dptr - start;
307 if (hdr->search) {
308 dptr = ct_sip_header_search(dptr, limit, hdr->search,
309 hdr->slen);
310 if (!dptr)
311 return -1;
312 dptr += hdr->slen;
324 } 313 }
325 aux += hnfo->ln_strlen;
326 314
327 *matchlen = hnfo->match_len(ct, aux, limit, &shift); 315 *matchlen = hdr->match_len(ct, dptr, limit, &shift);
328 if (!*matchlen) 316 if (!*matchlen)
329 return -1; 317 return -1;
330 318 *matchoff = dptr - start + shift;
331 *matchoff = (aux - k) + shift;
332
333 pr_debug("%s match succeeded! - len: %u\n", hnfo->lname,
334 *matchlen);
335 return 1; 319 return 1;
336 } 320 }
337 pr_debug("%s header not found.\n", hnfo->lname);
338 return 0; 321 return 0;
339} 322}
340EXPORT_SYMBOL_GPL(ct_sip_get_info); 323EXPORT_SYMBOL_GPL(ct_sip_get_header);
341 324
342/* SDP header parsing: a SDP session description contains an ordered set of 325/* SDP header parsing: a SDP session description contains an ordered set of
343 * headers, starting with a section containing general session parameters, 326 * headers, starting with a section containing general session parameters,