MFC: An off-by-one malloc size was corrupting the installer's memory,
[dragonfly.git] / contrib / ipfilter / ipfs.c
blobffbd71bd64889b74c18af75a962a2e346008c44f
1 /*
2 * Copyright (C) 1999-2001 by Darren Reed.
4 * See the IPFILTER.LICENCE file for details on licencing.
5 */
6 #ifdef __FreeBSD__
7 # ifndef __FreeBSD_cc_version
8 # include <osreldate.h>
9 # else
10 # if __FreeBSD_cc_version < 430000
11 # include <osreldate.h>
12 # endif
13 # endif
14 #endif
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <errno.h>
20 #if !defined(__SVR4) && !defined(__GNUC__)
21 #include <strings.h>
22 #endif
23 #include <sys/types.h>
24 #include <sys/param.h>
25 #include <sys/file.h>
26 #include <stdlib.h>
27 #include <stddef.h>
28 #include <sys/socket.h>
29 #include <sys/ioctl.h>
30 #include <netinet/in.h>
31 #include <netinet/in_systm.h>
32 #include <sys/time.h>
33 #include <net/if.h>
34 #if __FreeBSD_version >= 300000
35 # include <net/if_var.h>
36 #endif
37 #include <netinet/ip.h>
38 #include <netdb.h>
39 #include <arpa/nameser.h>
40 #include <resolv.h>
41 #include "ip_compat.h"
42 #include "ip_fil.h"
43 #include "ip_nat.h"
44 #include "ip_state.h"
45 #include "ipf.h"
47 #if !defined(lint)
48 static const char rcsid[] = "@(#)$Id: ipfs.c,v 2.6.2.15 2003/05/31 02:12:21 darrenr Exp $";
49 #endif
51 #ifndef IPF_SAVEDIR
52 # define IPF_SAVEDIR "/var/db/ipf"
53 #endif
54 #ifndef IPF_NATFILE
55 # define IPF_NATFILE "ipnat.ipf"
56 #endif
57 #ifndef IPF_STATEFILE
58 # define IPF_STATEFILE "ipstate.ipf"
59 #endif
61 #if !defined(__SVR4) && defined(__GNUC__)
62 extern char *index __P((const char *, int));
63 #endif
65 extern char *optarg;
66 extern int optind;
68 int main __P((int, char *[]));
69 void usage __P((void));
70 int changestateif __P((char *, char *));
71 int changenatif __P((char *, char *));
72 int readstate __P((int, char *));
73 int readnat __P((int, char *));
74 int writestate __P((int, char *));
75 int opendevice __P((char *));
76 void closedevice __P((int));
77 int setlock __P((int, int));
78 int writeall __P((char *));
79 int readall __P((char *));
80 int writenat __P((int, char *));
81 char *concat __P((char *, char *));
83 int opts = 0;
84 char *progname;
87 void usage()
89 fprintf(stderr, "\
90 usage: %s [-nv] -l\n\
91 usage: %s [-nv] -u\n\
92 usage: %s [-nv] [-d <dir>] -R\n\
93 usage: %s [-nv] [-d <dir>] -W\n\
94 usage: %s [-nv] -N [-f <file> | -d <dir>] -r\n\
95 usage: %s [-nv] -S [-f <file> | -d <dir>] -r\n\
96 usage: %s [-nv] -N [-f <file> | -d <dir>] -w\n\
97 usage: %s [-nv] -S [-f <file> | -d <dir>] -w\n\
98 usage: %s [-nv] -N [-f <filename> | -d <dir> ] -i <if1>,<if2>\n\
99 usage: %s [-nv] -S [-f <filename> | -d <dir> ] -i <if1>,<if2>\n\
100 ", progname, progname, progname, progname, progname, progname,
101 progname, progname, progname, progname);
102 exit(1);
107 * Change interface names in state information saved out to disk.
109 int changestateif(ifs, fname)
110 char *ifs, *fname;
112 int fd, olen, nlen, rw;
113 ipstate_save_t ips;
114 off_t pos;
115 char *s;
117 s = strchr(ifs, ',');
118 if (!s)
119 usage();
120 *s++ = '\0';
121 nlen = strlen(s);
122 olen = strlen(ifs);
123 if (nlen >= sizeof(ips.ips_is.is_ifname) ||
124 olen >= sizeof(ips.ips_is.is_ifname))
125 usage();
127 fd = open(fname, O_RDWR);
128 if (fd == -1) {
129 perror("open");
130 exit(1);
133 for (pos = 0; read(fd, &ips, sizeof(ips)) == sizeof(ips); ) {
134 rw = 0;
135 if (!strncmp(ips.ips_is.is_ifname[0], ifs, olen + 1)) {
136 strcpy(ips.ips_is.is_ifname[0], s);
137 rw = 1;
139 if (!strncmp(ips.ips_is.is_ifname[1], ifs, olen + 1)) {
140 strcpy(ips.ips_is.is_ifname[1], s);
141 rw = 1;
143 if (rw == 1) {
144 if (lseek(fd, pos, SEEK_SET) != pos) {
145 perror("lseek");
146 exit(1);
148 if (write(fd, &ips, sizeof(ips)) != sizeof(ips)) {
149 perror("write");
150 exit(1);
153 pos = lseek(fd, 0, SEEK_CUR);
155 close(fd);
157 return 0;
162 * Change interface names in NAT information saved out to disk.
164 int changenatif(ifs, fname)
165 char *ifs, *fname;
167 int fd, olen, nlen, rw;
168 nat_save_t ipn;
169 nat_t *nat;
170 off_t pos;
171 char *s;
173 s = strchr(ifs, ',');
174 if (!s)
175 usage();
176 *s++ = '\0';
177 nlen = strlen(s);
178 olen = strlen(ifs);
179 nat = &ipn.ipn_nat;
180 if (nlen >= sizeof(nat->nat_ifname) || olen >= sizeof(nat->nat_ifname))
181 usage();
183 fd = open(fname, O_RDWR);
184 if (fd == -1) {
185 perror("open");
186 exit(1);
189 for (pos = 0; read(fd, &ipn, sizeof(ipn)) == sizeof(ipn); ) {
190 rw = 0;
191 if (!strncmp(nat->nat_ifname, ifs, olen + 1)) {
192 strcpy(nat->nat_ifname, s);
193 rw = 1;
195 if (rw == 1) {
196 if (lseek(fd, pos, SEEK_SET) != pos) {
197 perror("lseek");
198 exit(1);
200 if (write(fd, &ipn, sizeof(ipn)) != sizeof(ipn)) {
201 perror("write");
202 exit(1);
205 pos = lseek(fd, 0, SEEK_CUR);
207 close(fd);
209 return 0;
213 int main(argc,argv)
214 int argc;
215 char *argv[];
217 int c, lock = -1, devfd = -1, err = 0, rw = -1, ns = -1, set = 0;
218 char *dirname = NULL, *filename = NULL, *ifs = NULL;
220 progname = argv[0];
222 while ((c = getopt(argc, argv, "d:f:i:lNnSRruvWw")) != -1)
223 switch (c)
225 case 'd' :
226 if ((set == 0) && !dirname && !filename)
227 dirname = optarg;
228 else
229 usage();
230 break;
231 case 'f' :
232 if ((set == 1) && !dirname && !filename && !(rw & 2))
233 filename = optarg;
234 else
235 usage();
236 break;
237 case 'i' :
238 ifs = optarg;
239 set = 1;
240 break;
241 case 'l' :
242 if (filename || dirname || set)
243 usage();
244 lock = 1;
245 set = 1;
246 break;
247 case 'n' :
248 opts |= OPT_DONOTHING;
249 break;
250 case 'N' :
251 if ((ns >= 0) || dirname || (rw != -1) || set)
252 usage();
253 ns = 0;
254 set = 1;
255 break;
256 case 'r' :
257 if (dirname || (rw != -1) || (ns == -1))
258 usage();
259 rw = 0;
260 set = 1;
261 break;
262 case 'R' :
263 if (filename || (ns != -1))
264 usage();
265 rw = 2;
266 set = 1;
267 break;
268 case 'S' :
269 if ((ns >= 0) || dirname || (rw != -1) || set)
270 usage();
271 ns = 1;
272 set = 1;
273 break;
274 case 'u' :
275 if (filename || dirname || set)
276 usage();
277 lock = 0;
278 set = 1;
279 break;
280 case 'v' :
281 opts |= OPT_VERBOSE;
282 break;
283 case 'w' :
284 if (dirname || (rw != -1) || (ns == -1))
285 usage();
286 rw = 1;
287 set = 1;
288 break;
289 case 'W' :
290 if (filename || (ns != -1))
291 usage();
292 rw = 3;
293 set = 1;
294 break;
295 case '?' :
296 default :
297 usage();
300 if (optind < 2)
301 usage();
303 if (filename == NULL) {
304 if (ns == 0) {
305 if (dirname == NULL)
306 dirname = IPF_SAVEDIR;
307 if (dirname[strlen(dirname) - 1] != '/')
308 dirname = concat(dirname, "/");
309 filename = concat(dirname, IPF_NATFILE);
310 } else if (ns == 1) {
311 if (dirname == NULL)
312 dirname = IPF_SAVEDIR;
313 if (dirname[strlen(dirname) - 1] != '/')
314 dirname = concat(dirname, "/");
315 filename = concat(dirname, IPF_STATEFILE);
319 if (ifs) {
320 if (!filename || ns < 0)
321 usage();
322 if (ns == 0)
323 return changenatif(ifs, filename);
324 else
325 return changestateif(ifs, filename);
328 if ((ns >= 0) || (lock >= 0)) {
329 if (lock >= 0)
330 devfd = opendevice(NULL);
331 else if (ns >= 0) {
332 if (ns == 1)
333 devfd = opendevice(IPL_STATE);
334 else if (ns == 0)
335 devfd = opendevice(IPL_NAT);
337 if (devfd == -1)
338 exit(1);
341 if (lock >= 0)
342 err = setlock(devfd, lock);
343 else if (rw >= 0) {
344 if (rw & 1) { /* WRITE */
345 if (rw & 2)
346 err = writeall(dirname);
347 else {
348 if (ns == 0)
349 err = writenat(devfd, filename);
350 else if (ns == 1)
351 err = writestate(devfd, filename);
353 } else {
354 if (rw & 2)
355 err = readall(dirname);
356 else {
357 if (ns == 0)
358 err = readnat(devfd, filename);
359 else if (ns == 1)
360 err = readstate(devfd, filename);
364 return err;
368 char *concat(base, append)
369 char *base, *append;
371 char *str;
373 str = malloc(strlen(base) + strlen(append) + 1);
374 if (str != NULL) {
375 strcpy(str, base);
376 strcat(str, append);
378 return str;
382 int opendevice(ipfdev)
383 char *ipfdev;
385 int fd = -1;
387 if (opts & OPT_DONOTHING)
388 return -2;
390 if (!ipfdev)
391 ipfdev = IPL_NAME;
393 if ((fd = open(ipfdev, O_RDWR)) == -1)
394 if ((fd = open(ipfdev, O_RDONLY)) == -1)
395 perror("open device");
396 return fd;
400 void closedevice(fd)
401 int fd;
403 close(fd);
407 int setlock(fd, lock)
408 int fd, lock;
410 if (opts & OPT_VERBOSE)
411 printf("Turn lock %s\n", lock ? "on" : "off");
412 if (!(opts & OPT_DONOTHING)) {
413 if (ioctl(fd, SIOCSTLCK, &lock) == -1) {
414 perror("SIOCSTLCK");
415 return 1;
417 if (opts & OPT_VERBOSE)
418 printf("Lock now %s\n", lock ? "on" : "off");
420 return 0;
424 int writestate(fd, file)
425 int fd;
426 char *file;
428 ipstate_save_t ips, *ipsp;
429 int wfd = -1;
431 if (!file)
432 file = IPF_STATEFILE;
434 wfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
435 if (wfd == -1) {
436 fprintf(stderr, "%s ", file);
437 perror("state:open");
438 return 1;
441 ipsp = &ips;
442 bzero((char *)ipsp, sizeof(ips));
444 do {
445 if (opts & OPT_VERBOSE)
446 printf("Getting state from addr %p\n", ips.ips_next);
447 if (ioctl(fd, SIOCSTGET, &ipsp)) {
448 if (errno == ENOENT)
449 break;
450 perror("state:SIOCSTGET");
451 close(wfd);
452 return 1;
454 if (opts & OPT_VERBOSE)
455 printf("Got state next %p\n", ips.ips_next);
456 if (write(wfd, ipsp, sizeof(ips)) != sizeof(ips)) {
457 perror("state:write");
458 close(wfd);
459 return 1;
461 } while (ips.ips_next != NULL);
462 close(wfd);
464 return 0;
468 int readstate(fd, file)
469 int fd;
470 char *file;
472 ipstate_save_t ips, *is, *ipshead = NULL, *is1, *ipstail = NULL;
473 int sfd = -1, i;
475 if (!file)
476 file = IPF_STATEFILE;
478 sfd = open(file, O_RDONLY, 0600);
479 if (sfd == -1) {
480 fprintf(stderr, "%s ", file);
481 perror("open");
482 return 1;
485 bzero((char *)&ips, sizeof(ips));
488 * 1. Read all state information in.
490 do {
491 i = read(sfd, &ips, sizeof(ips));
492 if (i == -1) {
493 perror("read");
494 close(sfd);
495 return 1;
497 if (i == 0)
498 break;
499 if (i != sizeof(ips)) {
500 fprintf(stderr, "incomplete read: %d != %d\n", i,
501 (int)sizeof(ips));
502 close(sfd);
503 return 1;
505 is = (ipstate_save_t *)malloc(sizeof(*is));
506 if(!is) {
507 fprintf(stderr, "malloc failed\n");
508 return 1;
511 bcopy((char *)&ips, (char *)is, sizeof(ips));
514 * Check to see if this is the first state entry that will
515 * reference a particular rule and if so, flag it as such
516 * else just adjust the rule pointer to become a pointer to
517 * the other. We do this so we have a means later for tracking
518 * who is referencing us when we get back the real pointer
519 * in is_rule after doing the ioctl.
521 for (is1 = ipshead; is1 != NULL; is1 = is1->ips_next)
522 if (is1->ips_rule == is->ips_rule)
523 break;
524 if (is1 == NULL)
525 is->ips_is.is_flags |= FI_NEWFR;
526 else
527 is->ips_rule = (void *)&is1->ips_rule;
530 * Use a tail-queue type list (add things to the end)..
532 is->ips_next = NULL;
533 if (!ipshead)
534 ipshead = is;
535 if (ipstail)
536 ipstail->ips_next = is;
537 ipstail = is;
538 } while (1);
540 close(sfd);
542 for (is = ipshead; is; is = is->ips_next) {
543 if (opts & OPT_VERBOSE)
544 printf("Loading new state table entry\n");
545 if (is->ips_is.is_flags & FI_NEWFR) {
546 if (opts & OPT_VERBOSE)
547 printf("Loading new filter rule\n");
549 if (!(opts & OPT_DONOTHING))
550 if (ioctl(fd, SIOCSTPUT, &is)) {
551 perror("SIOCSTPUT");
552 return 1;
555 if (is->ips_is.is_flags & FI_NEWFR) {
556 if (opts & OPT_VERBOSE)
557 printf("Real rule addr %p\n", is->ips_rule);
558 for (is1 = is->ips_next; is1; is1 = is1->ips_next)
559 if (is1->ips_rule == (frentry_t *)&is->ips_rule)
560 is1->ips_rule = is->ips_rule;
564 return 0;
568 int readnat(fd, file)
569 int fd;
570 char *file;
572 nat_save_t ipn, *in, *ipnhead = NULL, *in1, *ipntail = NULL;
573 int nfd = -1, i;
574 nat_t *nat;
575 char *s;
576 int n;
578 if (!file)
579 file = IPF_NATFILE;
581 nfd = open(file, O_RDONLY);
582 if (nfd == -1) {
583 fprintf(stderr, "%s ", file);
584 perror("nat:open");
585 return 1;
588 bzero((char *)&ipn, sizeof(ipn));
591 * 1. Read all state information in.
593 do {
594 i = read(nfd, &ipn, sizeof(ipn));
595 if (i == -1) {
596 perror("read");
597 close(nfd);
598 return 1;
600 if (i == 0)
601 break;
602 if (i != sizeof(ipn)) {
603 fprintf(stderr, "incomplete read: %d != %d\n", i,
604 (int)sizeof(ipn));
605 close(nfd);
606 return 1;
609 if (ipn.ipn_dsize > 0) {
610 n = ipn.ipn_dsize;
612 if (n > sizeof(ipn.ipn_data))
613 n -= sizeof(ipn.ipn_data);
614 else
615 n = 0;
616 in = malloc(sizeof(*in) + n);
617 if (!in)
618 break;
620 if (n > 0) {
621 s = in->ipn_data + sizeof(in->ipn_data);
622 i = read(nfd, s, n);
623 if (i == 0)
624 break;
625 if (i != n) {
626 fprintf(stderr,
627 "incomplete read: %d != %d\n",
628 i, n);
629 close(nfd);
630 return 1;
633 } else
634 in = (nat_save_t *)malloc(sizeof(*in));
635 bcopy((char *)&ipn, (char *)in, sizeof(ipn));
638 * Check to see if this is the first NAT entry that will
639 * reference a particular rule and if so, flag it as such
640 * else just adjust the rule pointer to become a pointer to
641 * the other. We do this so we have a means later for tracking
642 * who is referencing us when we get back the real pointer
643 * in is_rule after doing the ioctl.
645 nat = &in->ipn_nat;
646 if (nat->nat_fr != NULL) {
647 for (in1 = ipnhead; in1 != NULL; in1 = in1->ipn_next)
648 if (in1->ipn_rule == nat->nat_fr)
649 break;
650 if (in1 == NULL)
651 nat->nat_flags |= FI_NEWFR;
652 else
653 nat->nat_fr = &in1->ipn_fr;
657 * Use a tail-queue type list (add things to the end)..
659 in->ipn_next = NULL;
660 if (!ipnhead)
661 ipnhead = in;
662 if (ipntail)
663 ipntail->ipn_next = in;
664 ipntail = in;
665 } while (1);
667 close(nfd);
668 nfd = -1;
670 for (in = ipnhead; in; in = in->ipn_next) {
671 if (opts & OPT_VERBOSE)
672 printf("Loading new NAT table entry\n");
673 nat = &in->ipn_nat;
674 if (nat->nat_flags & FI_NEWFR) {
675 if (opts & OPT_VERBOSE)
676 printf("Loading new filter rule\n");
678 if (!(opts & OPT_DONOTHING))
679 if (ioctl(fd, SIOCSTPUT, &in)) {
680 perror("SIOCSTPUT");
681 return 1;
684 if (nat->nat_flags & FI_NEWFR) {
685 if (opts & OPT_VERBOSE)
686 printf("Real rule addr %p\n", nat->nat_fr);
687 for (in1 = in->ipn_next; in1; in1 = in1->ipn_next)
688 if (in1->ipn_rule == &in->ipn_fr)
689 in1->ipn_rule = nat->nat_fr;
693 return 0;
697 int writenat(fd, file)
698 int fd;
699 char *file;
701 nat_save_t *ipnp = NULL, *next = NULL;
702 int nfd = -1;
703 natget_t ng;
705 if (!file)
706 file = IPF_NATFILE;
708 nfd = open(file, O_WRONLY|O_TRUNC|O_CREAT, 0600);
709 if (nfd == -1) {
710 fprintf(stderr, "%s ", file);
711 perror("nat:open");
712 return 1;
716 do {
717 if (opts & OPT_VERBOSE)
718 printf("Getting nat from addr %p\n", ipnp);
719 ng.ng_ptr = next;
720 ng.ng_sz = 0;
721 if (ioctl(fd, SIOCSTGSZ, &ng)) {
722 perror("nat:SIOCSTGSZ");
723 close(nfd);
724 return 1;
727 if (opts & OPT_VERBOSE)
728 printf("NAT size %d from %p\n", ng.ng_sz, ng.ng_ptr);
730 if (ng.ng_sz == 0)
731 break;
733 if (!ipnp)
734 ipnp = malloc(ng.ng_sz);
735 else
736 ipnp = realloc((char *)ipnp, ng.ng_sz);
737 if (!ipnp) {
738 fprintf(stderr,
739 "malloc for %d bytes failed\n", ng.ng_sz);
740 break;
743 bzero((char *)ipnp, ng.ng_sz);
744 ipnp->ipn_next = next;
745 if (ioctl(fd, SIOCSTGET, &ipnp)) {
746 if (errno == ENOENT)
747 break;
748 perror("nat:SIOCSTGET");
749 close(nfd);
750 return 1;
753 if (opts & OPT_VERBOSE)
754 printf("Got nat next %p\n", ipnp->ipn_next);
755 if (write(nfd, ipnp, ng.ng_sz) != ng.ng_sz) {
756 perror("nat:write");
757 close(nfd);
758 return 1;
760 next = ipnp->ipn_next;
761 } while (ipnp && next);
762 close(nfd);
764 return 0;
768 int writeall(dirname)
769 char *dirname;
771 int fd, devfd;
773 if (!dirname)
774 dirname = IPF_SAVEDIR;
776 if (chdir(dirname)) {
777 fprintf(stderr, "IPF_SAVEDIR=%s: ", dirname);
778 perror("chdir(IPF_SAVEDIR)");
779 return 1;
782 fd = opendevice(NULL);
783 if (fd == -1)
784 return 1;
785 if (setlock(fd, 1)) {
786 close(fd);
787 return 1;
790 devfd = opendevice(IPL_STATE);
791 if (devfd == -1)
792 goto bad;
793 if (writestate(devfd, NULL))
794 goto bad;
795 close(devfd);
797 devfd = opendevice(IPL_NAT);
798 if (devfd == -1)
799 goto bad;
800 if (writenat(devfd, NULL))
801 goto bad;
802 close(devfd);
804 if (setlock(fd, 0)) {
805 close(fd);
806 return 1;
809 return 0;
811 bad:
812 setlock(fd, 0);
813 close(fd);
814 return 1;
818 int readall(dirname)
819 char *dirname;
821 int fd, devfd;
823 if (!dirname)
824 dirname = IPF_SAVEDIR;
826 if (chdir(dirname)) {
827 perror("chdir(IPF_SAVEDIR)");
828 return 1;
831 fd = opendevice(NULL);
832 if (fd == -1)
833 return 1;
834 if (setlock(fd, 1)) {
835 close(fd);
836 return 1;
839 devfd = opendevice(IPL_STATE);
840 if (devfd == -1)
841 return 1;
842 if (readstate(devfd, NULL))
843 return 1;
844 close(devfd);
846 devfd = opendevice(IPL_NAT);
847 if (devfd == -1)
848 return 1;
849 if (readnat(devfd, NULL))
850 return 1;
851 close(devfd);
853 if (setlock(fd, 0)) {
854 close(fd);
855 return 1;
858 return 0;