fix for f12, gcc4.4
[iptables.git] / extensions / libipt_multiport.c
1 /* Shared library add-on to iptables to add multiple TCP port support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <iptables.h>
8 /* To ensure that iptables compiles with an old kernel */
9 #include "../include/linux/netfilter_ipv4/ipt_multiport.h"
10
11 /* Function which prints out usage message. */
12 static void
13 help(void)
14 {
15         printf(
16 "multiport v%s options:\n"
17 " --source-ports port[,port,port...]\n"
18 " --sports ...\n"
19 "                               match source port(s)\n"
20 " --destination-ports port[,port,port...]\n"
21 " --dports ...\n"
22 "                               match destination port(s)\n"
23 " --ports port[,port,port]\n"
24 "                               match both source and destination port(s)\n"
25 " NOTE: this kernel does not support port ranges in multiport.\n",
26 IPTABLES_VERSION);
27 }
28
29 static void
30 help_v1(void)
31 {
32         printf(
33 "multiport v%s options:\n"
34 " --source-ports [!] port[,port:port,port...]\n"
35 " --sports ...\n"
36 "                               match source port(s)\n"
37 " --destination-ports [!] port[,port:port,port...]\n"
38 " --dports ...\n"
39 "                               match destination port(s)\n"
40 " --ports [!] port[,port:port,port]\n"
41 "                               match both source and destination port(s)\n",
42 IPTABLES_VERSION);
43 }
44
45 static struct option opts[] = {
46         { "source-ports", 1, 0, '1' },
47         { "sports", 1, 0, '1' }, /* synonym */
48         { "destination-ports", 1, 0, '2' },
49         { "dports", 1, 0, '2' }, /* synonym */
50         { "ports", 1, 0, '3' },
51         {0}
52 };
53
54 static char *
55 proto_to_name(u_int8_t proto)
56 {
57         switch (proto) {
58         case IPPROTO_TCP:
59                 return "tcp";
60         case IPPROTO_UDP:
61                 return "udp";
62         case IPPROTO_UDPLITE:
63                 return "udplite";
64         case IPPROTO_SCTP:
65                 return "sctp";
66         case IPPROTO_DCCP:
67                 return "dccp";
68         default:
69                 return NULL;
70         }
71 }
72
73 static unsigned int
74 parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
75 {
76         char *buffer, *cp, *next;
77         unsigned int i;
78
79         buffer = strdup(portstring);
80         if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
81
82         for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
83         {
84                 next=strchr(cp, ',');
85                 if (next) *next++='\0';
86                 ports[i] = parse_port(cp, proto);
87         }
88         if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
89         free(buffer);
90         return i;
91 }
92
93 static void
94 parse_multi_ports_v1(const char *portstring, 
95                      struct ipt_multiport_v1 *multiinfo,
96                      const char *proto)
97 {
98         char *buffer, *cp, *next, *range;
99         unsigned int i;
100         u_int16_t m;
101
102         buffer = strdup(portstring);
103         if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
104
105         for (i=0; i<IPT_MULTI_PORTS; i++)
106                 multiinfo->pflags[i] = 0;
107  
108         for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
109                 next=strchr(cp, ',');
110                 if (next) *next++='\0';
111                 range = strchr(cp, ':');
112                 if (range) {
113                         if (i == IPT_MULTI_PORTS-1)
114                                 exit_error(PARAMETER_PROBLEM,
115                                            "too many ports specified");
116                         *range++ = '\0';
117                 }
118                 multiinfo->ports[i] = parse_port(cp, proto);
119                 if (range) {
120                         multiinfo->pflags[i] = 1;
121                         multiinfo->ports[++i] = parse_port(range, proto);
122                         if (multiinfo->ports[i-1] >= multiinfo->ports[i])
123                                 exit_error(PARAMETER_PROBLEM,
124                                            "invalid portrange specified");
125                         m <<= 1;
126                 }
127         }
128         multiinfo->count = i;
129         if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
130         free(buffer);
131 }
132
133 /* Initialize the match. */
134 static void
135 init(struct ipt_entry_match *m, unsigned int *nfcache)
136 {
137 }
138
139 static const char *
140 check_proto(const struct ipt_entry *entry)
141 {
142         char *proto;
143
144         if (entry->ip.invflags & IPT_INV_PROTO)
145                 exit_error(PARAMETER_PROBLEM,
146                            "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
147
148         if ((proto = proto_to_name(entry->ip.proto)) != NULL)
149                 return proto;
150         else if (!entry->ip.proto)
151                 exit_error(PARAMETER_PROBLEM,
152                            "multiport needs `-p tcp', `-p udp', `-p udplite', "
153                            "`-p sctp' or `-p dccp'");
154         else
155                 exit_error(PARAMETER_PROBLEM,
156                            "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
157 }
158
159 /* Function which parses command options; returns true if it
160    ate an option */
161 static int
162 parse(int c, char **argv, int invert, unsigned int *flags,
163       const struct ipt_entry *entry,
164       unsigned int *nfcache,
165       struct ipt_entry_match **match)
166 {
167         const char *proto;
168         struct ipt_multiport *multiinfo
169                 = (struct ipt_multiport *)(*match)->data;
170
171         switch (c) {
172         case '1':
173                 check_inverse(argv[optind-1], &invert, &optind, 0);
174                 proto = check_proto(entry);
175                 multiinfo->count = parse_multi_ports(argv[optind-1],
176                                                      multiinfo->ports, proto);
177                 multiinfo->flags = IPT_MULTIPORT_SOURCE;
178                 break;
179
180         case '2':
181                 check_inverse(argv[optind-1], &invert, &optind, 0);
182                 proto = check_proto(entry);
183                 multiinfo->count = parse_multi_ports(argv[optind-1],
184                                                      multiinfo->ports, proto);
185                 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
186                 break;
187
188         case '3':
189                 check_inverse(argv[optind-1], &invert, &optind, 0);
190                 proto = check_proto(entry);
191                 multiinfo->count = parse_multi_ports(argv[optind-1],
192                                                      multiinfo->ports, proto);
193                 multiinfo->flags = IPT_MULTIPORT_EITHER;
194                 break;
195
196         default:
197                 return 0;
198         }
199
200         if (invert)
201                 exit_error(PARAMETER_PROBLEM,
202                            "multiport does not support invert");
203
204         if (*flags)
205                 exit_error(PARAMETER_PROBLEM,
206                            "multiport can only have one option");
207         *flags = 1;
208         return 1;
209 }
210
211 static int
212 parse_v1(int c, char **argv, int invert, unsigned int *flags,
213          const struct ipt_entry *entry,
214          unsigned int *nfcache,
215          struct ipt_entry_match **match)
216 {
217         const char *proto;
218         struct ipt_multiport_v1 *multiinfo
219                 = (struct ipt_multiport_v1 *)(*match)->data;
220
221         switch (c) {
222         case '1':
223                 check_inverse(argv[optind-1], &invert, &optind, 0);
224                 proto = check_proto(entry);
225                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
226                 multiinfo->flags = IPT_MULTIPORT_SOURCE;
227                 break;
228
229         case '2':
230                 check_inverse(argv[optind-1], &invert, &optind, 0);
231                 proto = check_proto(entry);
232                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
233                 multiinfo->flags = IPT_MULTIPORT_DESTINATION;
234                 break;
235
236         case '3':
237                 check_inverse(argv[optind-1], &invert, &optind, 0);
238                 proto = check_proto(entry);
239                 parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
240                 multiinfo->flags = IPT_MULTIPORT_EITHER;
241                 break;
242
243         default:
244                 return 0;
245         }
246
247         if (invert)
248                 multiinfo->invert = 1;
249
250         if (*flags)
251                 exit_error(PARAMETER_PROBLEM,
252                            "multiport can only have one option");
253         *flags = 1;
254         return 1;
255 }
256
257 /* Final check; must specify something. */
258 static void
259 final_check(unsigned int flags)
260 {
261         if (!flags)
262                 exit_error(PARAMETER_PROBLEM, "multiport expection an option");
263 }
264
265 static char *
266 port_to_service(int port, u_int8_t proto)
267 {
268         struct servent *service;
269
270         if ((service = getservbyport(htons(port), proto_to_name(proto))))
271                 return service->s_name;
272
273         return NULL;
274 }
275
276 static void
277 print_port(u_int16_t port, u_int8_t protocol, int numeric)
278 {
279         char *service;
280
281         if (numeric || (service = port_to_service(port, protocol)) == NULL)
282                 printf("%u", port);
283         else
284                 printf("%s", service);
285 }
286
287 /* Prints out the matchinfo. */
288 static void
289 print(const struct ipt_ip *ip,
290       const struct ipt_entry_match *match,
291       int numeric)
292 {
293         const struct ipt_multiport *multiinfo
294                 = (const struct ipt_multiport *)match->data;
295         unsigned int i;
296
297         printf("multiport ");
298
299         switch (multiinfo->flags) {
300         case IPT_MULTIPORT_SOURCE:
301                 printf("sports ");
302                 break;
303
304         case IPT_MULTIPORT_DESTINATION:
305                 printf("dports ");
306                 break;
307
308         case IPT_MULTIPORT_EITHER:
309                 printf("ports ");
310                 break;
311
312         default:
313                 printf("ERROR ");
314                 break;
315         }
316
317         for (i=0; i < multiinfo->count; i++) {
318                 printf("%s", i ? "," : "");
319                 print_port(multiinfo->ports[i], ip->proto, numeric);
320         }
321         printf(" ");
322 }
323
324 static void
325 print_v1(const struct ipt_ip *ip,
326          const struct ipt_entry_match *match,
327          int numeric)
328 {
329         const struct ipt_multiport_v1 *multiinfo
330                 = (const struct ipt_multiport_v1 *)match->data;
331         unsigned int i;
332
333         printf("multiport ");
334
335         switch (multiinfo->flags) {
336         case IPT_MULTIPORT_SOURCE:
337                 printf("sports ");
338                 break;
339
340         case IPT_MULTIPORT_DESTINATION:
341                 printf("dports ");
342                 break;
343
344         case IPT_MULTIPORT_EITHER:
345                 printf("ports ");
346                 break;
347
348         default:
349                 printf("ERROR ");
350                 break;
351         }
352
353         if (multiinfo->invert)
354                 printf("! ");
355
356         for (i=0; i < multiinfo->count; i++) {
357                 printf("%s", i ? "," : "");
358                 print_port(multiinfo->ports[i], ip->proto, numeric);
359                 if (multiinfo->pflags[i]) {
360                         printf(":");
361                         print_port(multiinfo->ports[++i], ip->proto, numeric);
362                 }
363         }
364         printf(" ");
365 }
366
367 /* Saves the union ipt_matchinfo in parsable form to stdout. */
368 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
369 {
370         const struct ipt_multiport *multiinfo
371                 = (const struct ipt_multiport *)match->data;
372         unsigned int i;
373
374         switch (multiinfo->flags) {
375         case IPT_MULTIPORT_SOURCE:
376                 printf("--sports ");
377                 break;
378
379         case IPT_MULTIPORT_DESTINATION:
380                 printf("--dports ");
381                 break;
382
383         case IPT_MULTIPORT_EITHER:
384                 printf("--ports ");
385                 break;
386         }
387
388         for (i=0; i < multiinfo->count; i++) {
389                 printf("%s", i ? "," : "");
390                 print_port(multiinfo->ports[i], ip->proto, 1);
391         }
392         printf(" ");
393 }
394
395 static void save_v1(const struct ipt_ip *ip, 
396                     const struct ipt_entry_match *match)
397 {
398         const struct ipt_multiport_v1 *multiinfo
399                 = (const struct ipt_multiport_v1 *)match->data;
400         unsigned int i;
401
402         switch (multiinfo->flags) {
403         case IPT_MULTIPORT_SOURCE:
404                 printf("--sports ");
405                 break;
406
407         case IPT_MULTIPORT_DESTINATION:
408                 printf("--dports ");
409                 break;
410
411         case IPT_MULTIPORT_EITHER:
412                 printf("--ports ");
413                 break;
414         }
415
416         if (multiinfo->invert)
417                 printf("! ");
418
419         for (i=0; i < multiinfo->count; i++) {
420                 printf("%s", i ? "," : "");
421                 print_port(multiinfo->ports[i], ip->proto, 1);
422                 if (multiinfo->pflags[i]) {
423                         printf(":");
424                         print_port(multiinfo->ports[++i], ip->proto, 1);
425                 }
426         }
427         printf(" ");
428 }
429
430 static struct iptables_match multiport = { 
431         .next           = NULL,
432         .name           = "multiport",
433         .revision       = 0,
434         .version        = IPTABLES_VERSION,
435         .size           = IPT_ALIGN(sizeof(struct ipt_multiport)),
436         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport)),
437         .help           = &help,
438         .init           = &init,
439         .parse          = &parse,
440         .final_check    = &final_check,
441         .print          = &print,
442         .save           = &save,
443         .extra_opts     = opts
444 };
445
446 static struct iptables_match multiport_v1 = { 
447         .next           = NULL,
448         .name           = "multiport",
449         .version        = IPTABLES_VERSION,
450         .revision       = 1,
451         .size           = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
452         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
453         .help           = &help_v1,
454         .init           = &init,
455         .parse          = &parse_v1,
456         .final_check    = &final_check,
457         .print          = &print_v1,
458         .save           = &save_v1,
459         .extra_opts     = opts
460 };
461
462 void
463 _init(void)
464 {
465         register_match(&multiport);
466         register_match(&multiport_v1);
467 }