508eb90b1eae53836e9b92ba7d5a50a466f48ee6
[iptables.git] / extensions / libipt_string.c
1 /* Shared library add-on to iptables to add string matching support. 
2  * 
3  * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
4  *
5  * ChangeLog
6  *     29.12.2003: Michael Rash <mbr@cipherdyne.org>
7  *             Fixed iptables save/restore for ascii strings
8  *             that contain space chars, and hex strings that
9  *             contain embedded NULL chars.  Updated to print
10  *             strings in hex mode if any non-printable char
11  *             is contained within the string.
12  *
13  *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
14  *             Changed --tos to --string in save(). Also
15  *             updated to work with slightly modified
16  *             ipt_string_info.
17  */
18 #include <stdio.h>
19 #include <netdb.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <getopt.h>
23 #include <ctype.h>
24
25 #include <iptables.h>
26 #include <linux/netfilter_ipv4/ipt_string.h>
27
28
29 /* Function which prints out usage message. */
30 static void
31 help(void)
32 {
33         printf(
34 "STRING match v%s options:\n"
35 "--string [!] string          Match a string in a packet\n"
36 "--hex-string [!] string      Match a hex string in a packet\n",
37 IPTABLES_VERSION);
38 }
39
40
41 static struct option opts[] = {
42         { .name = "string",     .has_arg = 1, .flag = 0, .val = '1' },
43         { .name = "hex-string", .has_arg = 1, .flag = 0, .val = '2' },
44         { .name = 0 }
45 };
46
47 static void
48 parse_string(const unsigned char *s, struct ipt_string_info *info)
49 {       
50         if (strlen(s) <= BM_MAX_NLEN) strcpy(info->string, s);
51         else exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
52 }
53
54
55 static void
56 parse_hex_string(const unsigned char *s, struct ipt_string_info *info)
57 {
58         int i=0, slen, sindex=0, schar;
59         short hex_f = 0, literal_f = 0;
60         char hextmp[3];
61
62         slen = strlen(s);
63
64         if (slen == 0) {
65                 exit_error(PARAMETER_PROBLEM,
66                         "STRING must contain at least one char");
67         }
68
69         while (i < slen) {
70                 if (s[i] == '\\' && !hex_f) {
71                         literal_f = 1;
72                 } else if (s[i] == '\\') {
73                         exit_error(PARAMETER_PROBLEM,
74                                 "Cannot include literals in hex data");
75                 } else if (s[i] == '|') {
76                         if (hex_f)
77                                 hex_f = 0;
78                         else {
79                                 hex_f = 1;
80                                 /* get past any initial whitespace just after the '|' */
81                                 while (s[i+1] == ' ')
82                                         i++;
83                         }
84                         if (i+1 >= slen)
85                                 break;
86                         else
87                                 i++;  /* advance to the next character */
88                 }
89
90                 if (literal_f) {
91                         if (i+1 >= slen) {
92                                 exit_error(PARAMETER_PROBLEM,
93                                         "Bad literal placement at end of string");
94                         }
95                         info->string[sindex] = s[i+1];
96                         i += 2;  /* skip over literal char */
97                         literal_f = 0;
98                 } else if (hex_f) {
99                         if (i+1 >= slen) {
100                                 exit_error(PARAMETER_PROBLEM,
101                                         "Odd number of hex digits");
102                         }
103                         if (i+2 >= slen) {
104                                 /* must end with a "|" */
105                                 exit_error(PARAMETER_PROBLEM, "Invalid hex block");
106                         }
107                         if (! isxdigit(s[i])) /* check for valid hex char */
108                                 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
109                         if (! isxdigit(s[i+1])) /* check for valid hex char */
110                                 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
111                         hextmp[0] = s[i];
112                         hextmp[1] = s[i+1];
113                         hextmp[2] = '\0';
114                         if (! sscanf(hextmp, "%x", &schar))
115                                 exit_error(PARAMETER_PROBLEM,
116                                         "Invalid hex char `%c'", s[i]);
117                         info->string[sindex] = (char) schar;
118                         if (s[i+2] == ' ')
119                                 i += 3;  /* spaces included in the hex block */
120                         else
121                                 i += 2;
122                 } else {  /* the char is not part of hex data, so just copy */
123                         info->string[sindex] = s[i];
124                         i++;
125                 }
126                 if (sindex > BM_MAX_NLEN)
127                         exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
128                 sindex++;
129         }
130         info->len = sindex;
131 }
132
133
134 /* Function which parses command options; returns true if it
135    ate an option */
136 static int
137 parse(int c, char **argv, int invert, unsigned int *flags,
138       const struct ipt_entry *entry,
139       unsigned int *nfcache,
140       struct ipt_entry_match **match)
141 {
142         struct ipt_string_info *stringinfo = (struct ipt_string_info *)(*match)->data;
143
144         switch (c) {
145         case '1':
146                 if (*flags)
147                         exit_error(PARAMETER_PROBLEM,
148                                    "Can't specify multiple strings");
149
150                 check_inverse(optarg, &invert, &optind, 0);
151                 parse_string(argv[optind-1], stringinfo);
152                 if (invert)
153                         stringinfo->invert = 1;
154                 stringinfo->len=strlen((char *)&stringinfo->string);
155                 *flags = 1;
156                 break;
157
158         case '2':
159                 if (*flags)
160                         exit_error(PARAMETER_PROBLEM,
161                                    "Can't specify multiple strings");
162
163                 check_inverse(optarg, &invert, &optind, 0);
164                 parse_hex_string(argv[optind-1], stringinfo);  /* sets length */
165                 if (invert)
166                         stringinfo->invert = 1;
167                 *flags = 1;
168                 break;
169
170         default:
171                 return 0;
172         }
173         return 1;
174 }
175
176
177 /* Final check; must have specified --string. */
178 static void
179 final_check(unsigned int flags)
180 {
181         if (!flags)
182                 exit_error(PARAMETER_PROBLEM,
183                            "STRING match: You must specify `--string' or `--hex-string'");
184 }
185
186 /* Test to see if the string contains non-printable chars or quotes */
187 static unsigned short int
188 is_hex_string(const char *str, const unsigned short int len)
189 {
190         unsigned int i;
191         for (i=0; i < len; i++)
192                 if (! isprint(str[i]))
193                         return 1;  /* string contains at least one non-printable char */
194         /* use hex output if the last char is a "\" */
195         if ((unsigned char) str[len-1] == 0x5c)
196                 return 1;
197         return 0;
198 }
199
200 /* Print string with "|" chars included as one would pass to --hex-string */
201 static void
202 print_hex_string(const char *str, const unsigned short int len)
203 {
204         unsigned int i;
205         /* start hex block */
206         printf("\"|");
207         for (i=0; i < len; i++) {
208                 /* see if we need to prepend a zero */
209                 if ((unsigned char) str[i] <= 0x0F)
210                         printf("0%x", (unsigned char) str[i]);
211                 else
212                         printf("%x", (unsigned char) str[i]);
213         }
214         /* close hex block */
215         printf("|\" ");
216 }
217
218 static void
219 print_string(const char *str, const unsigned short int len)
220 {
221         unsigned int i;
222         printf("\"");
223         for (i=0; i < len; i++) {
224                 if ((unsigned char) str[i] == 0x22)  /* escape any embedded quotes */
225                         printf("%c", 0x5c);
226                 printf("%c", (unsigned char) str[i]);
227         }
228         printf("\" ");  /* closing space and quote */
229 }
230
231 /* Prints out the matchinfo. */
232 static void
233 print(const struct ipt_ip *ip,
234       const struct ipt_entry_match *match,
235       int numeric)
236 {
237         const struct ipt_string_info *info =
238             (const struct ipt_string_info*) match->data;
239
240         if (is_hex_string(info->string, info->len)) {
241                 printf("STRING match %s", (info->invert) ? "!" : "");
242                 print_hex_string(info->string, info->len);
243         } else {
244                 printf("STRING match %s", (info->invert) ? "!" : "");
245                 print_string(info->string, info->len);
246         }
247 }
248
249
250 /* Saves the union ipt_matchinfo in parseable form to stdout. */
251 static void
252 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
253 {
254         const struct ipt_string_info *info =
255             (const struct ipt_string_info*) match->data;
256
257         if (is_hex_string(info->string, info->len)) {
258                 printf("--hex-string %s", (info->invert) ? "! ": "");
259                 print_hex_string(info->string, info->len);
260         } else {
261                 printf("--string %s", (info->invert) ? "! ": "");
262                 print_string(info->string, info->len);
263         }
264 }
265
266
267 static struct iptables_match string = {
268     .name          = "string",
269     .version       = IPTABLES_VERSION,
270     .size          = IPT_ALIGN(sizeof(struct ipt_string_info)),
271     .userspacesize = IPT_ALIGN(sizeof(struct ipt_string_info)),
272     .help          = &help,
273     .parse         = &parse,
274     .final_check   = &final_check,
275     .print         = &print,
276     .save          = &save,
277     .extra_opts    = opts
278 };
279
280
281 void _init(void)
282 {
283         register_match(&string);
284 }