aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/netfilter/nf_conntrack_sip.h30
-rw-r--r--net/ipv4/netfilter/nf_nat_sip.c18
-rw-r--r--net/netfilter/nf_conntrack_sip.c271
3 files changed, 151 insertions, 168 deletions
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index 480b26f40ce4..ccc701422963 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -5,14 +5,6 @@
5#define SIP_PORT 5060 5#define SIP_PORT 5060
6#define SIP_TIMEOUT 3600 6#define SIP_TIMEOUT 3600
7 7
8enum sip_header_pos {
9 POS_FROM,
10 POS_TO,
11 POS_VIA,
12 POS_CONTACT,
13 POS_CONTENT,
14};
15
16struct sip_header { 8struct sip_header {
17 const char *name; 9 const char *name;
18 const char *cname; 10 const char *cname;
@@ -36,9 +28,20 @@ struct sip_header {
36 .match_len = (__match), \ 28 .match_len = (__match), \
37} 29}
38 30
31#define SIP_HDR(__name, __cname, __search, __match) \
32 __SIP_HDR(__name, __cname, __search, __match)
33
39#define SDP_HDR(__name, __search, __match) \ 34#define SDP_HDR(__name, __search, __match) \
40 __SIP_HDR(__name, NULL, __search, __match) 35 __SIP_HDR(__name, NULL, __search, __match)
41 36
37enum sip_header_types {
38 SIP_HDR_FROM,
39 SIP_HDR_TO,
40 SIP_HDR_CONTACT,
41 SIP_HDR_VIA,
42 SIP_HDR_CONTENT_LENGTH,
43};
44
42enum sdp_header_types { 45enum sdp_header_types {
43 SDP_HDR_UNSPEC, 46 SDP_HDR_UNSPEC,
44 SDP_HDR_VERSION, 47 SDP_HDR_VERSION,
@@ -60,13 +63,10 @@ extern unsigned int (*nf_nat_sdp_hook)(struct sk_buff *skb,
60extern int ct_sip_parse_request(const struct nf_conn *ct, 63extern int ct_sip_parse_request(const struct nf_conn *ct,
61 const char *dptr, unsigned int datalen, 64 const char *dptr, unsigned int datalen,
62 unsigned int *matchoff, unsigned int *matchlen); 65 unsigned int *matchoff, unsigned int *matchlen);
63extern int ct_sip_get_info(const struct nf_conn *ct, const char *dptr, 66extern int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
64 size_t dlen, unsigned int *matchoff, 67 unsigned int dataoff, unsigned int datalen,
65 unsigned int *matchlen, enum sip_header_pos pos); 68 enum sip_header_types type,
66extern int ct_sip_lnlen(const char *line, const char *limit); 69 unsigned int *matchoff, unsigned int *matchlen);
67extern const char *ct_sip_search(const char *needle, const char *haystack,
68 size_t needle_len, size_t haystack_len,
69 int case_sensitive);
70 70
71extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, 71extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
72 unsigned int dataoff, unsigned int datalen, 72 unsigned int dataoff, unsigned int datalen,
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index 60151b5901a5..c13e43862361 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -108,14 +108,14 @@ static int map_addr(struct sk_buff *skb,
108 108
109static int map_sip_addr(struct sk_buff *skb, 109static int map_sip_addr(struct sk_buff *skb,
110 const char **dptr, unsigned int *datalen, 110 const char **dptr, unsigned int *datalen,
111 enum sip_header_pos pos, struct addr_map *map) 111 enum sip_header_types type, struct addr_map *map)
112{ 112{
113 enum ip_conntrack_info ctinfo; 113 enum ip_conntrack_info ctinfo;
114 struct nf_conn *ct = nf_ct_get(skb, &ctinfo); 114 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
115 unsigned int matchlen, matchoff; 115 unsigned int matchlen, matchoff;
116 116
117 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen, 117 if (ct_sip_get_header(ct, *dptr, 0, *datalen, type,
118 pos) <= 0) 118 &matchoff, &matchlen) <= 0)
119 return 1; 119 return 1;
120 return map_addr(skb, dptr, datalen, matchoff, matchlen, map); 120 return map_addr(skb, dptr, datalen, matchoff, matchlen, map);
121} 121}
@@ -141,10 +141,10 @@ static unsigned int ip_nat_sip(struct sk_buff *skb,
141 return NF_DROP; 141 return NF_DROP;
142 } 142 }
143 143
144 if (!map_sip_addr(skb, dptr, datalen, POS_FROM, &map) || 144 if (!map_sip_addr(skb, dptr, datalen, SIP_HDR_FROM, &map) ||
145 !map_sip_addr(skb, dptr, datalen, POS_TO, &map) || 145 !map_sip_addr(skb, dptr, datalen, SIP_HDR_TO, &map) ||
146 !map_sip_addr(skb, dptr, datalen, POS_VIA, &map) || 146 !map_sip_addr(skb, dptr, datalen, SIP_HDR_VIA, &map) ||
147 !map_sip_addr(skb, dptr, datalen, POS_CONTACT, &map)) 147 !map_sip_addr(skb, dptr, datalen, SIP_HDR_CONTACT, &map))
148 return NF_DROP; 148 return NF_DROP;
149 return NF_ACCEPT; 149 return NF_ACCEPT;
150} 150}
@@ -166,8 +166,8 @@ static int mangle_content_len(struct sk_buff *skb,
166 c_len = *datalen - matchoff + strlen("v="); 166 c_len = *datalen - matchoff + strlen("v=");
167 167
168 /* Now, update SDP length */ 168 /* Now, update SDP length */
169 if (ct_sip_get_info(ct, *dptr, *datalen, &matchoff, &matchlen, 169 if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CONTENT_LENGTH,
170 POS_CONTENT) <= 0) 170 &matchoff, &matchlen) <= 0)
171 return 0; 171 return 0;
172 172
173 buflen = sprintf(buffer, "%u", c_len); 173 buflen = sprintf(buffer, "%u", c_len);
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,