b4710f9738edae0775919cb7be1950120a6bdcaa
[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  *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
7  *             Changed --tos to --string in save(). Also
8  *             updated to work with slightly modified
9  *             ipt_string_info.
10  */
11 #include <stdio.h>
12 #include <netdb.h>
13 #include <string.h>
14 #include <stdlib.h>
15 #include <getopt.h>
16 #include <ctype.h>
17
18 #include <iptables.h>
19 #include <linux/netfilter_ipv4/ipt_string.h>
20
21
22 /* Function which prints out usage message. */
23 static void
24 help(void)
25 {
26         printf(
27 "STRING match v%s options:\n"
28 "--string [!] string          Match a string in a packet\n"
29 "--hex-string [!] string      Match a hex string in a packet\n",
30 IPTABLES_VERSION);
31 }
32
33
34 static struct option opts[] = {
35         { .name = "string",     .has_arg = 1, .flag = 0, .val = '1' },
36         { .name = "hex-string", .has_arg = 1, .flag = 0, .val = '2' },
37         { .name = 0 }
38 };
39
40
41 /* Initialize the match. */
42 static void
43 init(struct ipt_entry_match *m, unsigned int *nfcache)
44 {
45         *nfcache |= NFC_UNKNOWN;
46 }
47
48
49 static void
50 parse_string(const unsigned char *s, struct ipt_string_info *info)
51 {       
52         if (strlen(s) <= BM_MAX_NLEN) strcpy(info->string, s);
53         else exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
54 }
55
56
57 static void
58 parse_hex_string(const unsigned char *s, struct ipt_string_info *info)
59 {
60         int i=0, slen, sindex=0, schar;
61         short hex_f = 0, literal_f = 0;
62         char hextmp[3];
63
64         slen = strlen(s);
65
66         if (slen == 0) {
67                 exit_error(PARAMETER_PROBLEM,
68                         "STRING must contain at least one char");
69         }
70
71         while (i < slen) {
72                 if (s[i] == '\\' && !hex_f) {
73                         literal_f = 1;
74                 } else if (s[i] == '\\') {
75                         exit_error(PARAMETER_PROBLEM,
76                                 "Cannot include literals in hex data");
77                 } else if (s[i] == '|') {
78                         if (hex_f)
79                                 hex_f = 0;
80                         else
81                                 hex_f = 1;
82                         if (i+1 >= slen)
83                                 break;
84                         else
85                                 i++;  /* advance to the next character */
86                 }
87
88                 if (literal_f) {
89                         if (i+1 >= slen) {
90                                 exit_error(PARAMETER_PROBLEM,
91                                         "Bad literal placement at end of string");
92                         }
93                         info->string[sindex] = s[i+1];
94                         i += 2;  /* skip over literal char */
95                         literal_f = 0;
96                 } else if (hex_f) {
97                         if (i+1 >= slen) {
98                                 exit_error(PARAMETER_PROBLEM,
99                                         "Odd number of hex digits");
100                         }
101                         if (i+2 >= slen) {
102                                 /* must end with a "|" */
103                                 exit_error(PARAMETER_PROBLEM, "Invalid hex block");
104                         }
105                         if (! isxdigit(s[i])) /* check for valid hex char */
106                                 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
107                         if (! isxdigit(s[i+1])) /* check for valid hex char */
108                                 exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
109                         hextmp[0] = s[i];
110                         hextmp[1] = s[i+1];
111                         hextmp[2] = '\0';
112                         if (! sscanf(hextmp, "%x", &schar))
113                                 exit_error(PARAMETER_PROBLEM,
114                                         "Invalid hex char `%c'", s[i]);
115                         info->string[sindex] = (char) schar;
116                         if (s[i+2] == ' ')
117                                 i += 3;  /* spaces included in the hex block */
118                         else
119                                 i += 2;
120                 } else {  /* the char is not part of hex data, so just copy */
121                         info->string[sindex] = s[i];
122                         i++;
123                 }
124                 if (sindex > BM_MAX_NLEN)
125                         exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
126                 sindex++;
127         }
128         info->len = sindex;
129 }
130
131
132 /* Function which parses command options; returns true if it
133    ate an option */
134 static int
135 parse(int c, char **argv, int invert, unsigned int *flags,
136       const struct ipt_entry *entry,
137       unsigned int *nfcache,
138       struct ipt_entry_match **match)
139 {
140         struct ipt_string_info *stringinfo = (struct ipt_string_info *)(*match)->data;
141
142         switch (c) {
143         case '1':
144                 if (*flags)
145                         exit_error(PARAMETER_PROBLEM,
146                                    "Can't specify multiple strings");
147
148                 check_inverse(optarg, &invert, &optind, 0);
149                 parse_string(argv[optind-1], stringinfo);
150                 if (invert)
151                         stringinfo->invert = 1;
152                 stringinfo->len=strlen((char *)&stringinfo->string);
153                 *flags = 1;
154                 break;
155
156         case '2':
157                 if (*flags)
158                         exit_error(PARAMETER_PROBLEM,
159                                    "Can't specify multiple strings");
160
161                 check_inverse(optarg, &invert, &optind, 0);
162                 parse_hex_string(argv[optind-1], stringinfo);  /* sets length */
163                 if (invert)
164                         stringinfo->invert = 1;
165                 *flags = 1;
166                 break;
167
168         default:
169                 return 0;
170         }
171         return 1;
172 }
173
174
175 /* Final check; must have specified --string. */
176 static void
177 final_check(unsigned int flags)
178 {
179         if (!flags)
180                 exit_error(PARAMETER_PROBLEM,
181                            "STRING match: You must specify `--string'");
182 }
183
184
185 /* Prints out the matchinfo. */
186 static void
187 print(const struct ipt_ip *ip,
188       const struct ipt_entry_match *match,
189       int numeric)
190 {
191         const struct ipt_string_info *info =
192             (const struct ipt_string_info*) match->data;
193
194         printf("STRING match %s%s ", (info->invert) ? "!" : "", info->string);
195 }
196
197
198 /* Saves the union ipt_matchinfo in parseable form to stdout. */
199 static void
200 save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
201 {
202         const struct ipt_string_info *info =
203             (const struct ipt_string_info*) match->data;
204
205         printf("--string %s%s ", (info->invert) ? "! ": "", info->string);
206 }
207
208
209 static struct iptables_match string = {
210     .name          = "string",
211     .version       = IPTABLES_VERSION,
212     .size          = IPT_ALIGN(sizeof(struct ipt_string_info)),
213     .userspacesize = IPT_ALIGN(sizeof(struct ipt_string_info)),
214     .help          = &help,
215     .init          = &init,
216     .parse         = &parse,
217     .final_check   = &final_check,
218     .print         = &print,
219     .save          = &save,
220     .extra_opts    = opts
221 };
222
223
224 void _init(void)
225 {
226         register_match(&string);
227 }