1 #include <getopt.h>
2 #include <libgen.h>
3 #include <netdb.h>
4 #include <stdbool.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/socket.h>
10 #include <sys/un.h>
11 #include <unistd.h>
12 #include <xtables.h>
13 #include "xshared.h"
14 
15 #define XT_SOCKET_NAME "xtables"
16 #define XT_SOCKET_LEN 8
17 
18 /*
19  * Print out any special helps. A user might like to be able to add a --help
20  * to the commandline, and see expected results. So we call help for all
21  * specified matches and targets.
22  */
print_extension_helps(const struct xtables_target * t,const struct xtables_rule_match * m)23 void print_extension_helps(const struct xtables_target *t,
24     const struct xtables_rule_match *m)
25 {
26 	for (; t != NULL; t = t->next) {
27 		if (t->used) {
28 			printf("\n");
29 			if (t->help == NULL)
30 				printf("%s does not take any options\n",
31 				       t->name);
32 			else
33 				t->help();
34 		}
35 	}
36 	for (; m != NULL; m = m->next) {
37 		printf("\n");
38 		if (m->match->help == NULL)
39 			printf("%s does not take any options\n",
40 			       m->match->name);
41 		else
42 			m->match->help();
43 	}
44 }
45 
46 const char *
proto_to_name(uint8_t proto,int nolookup)47 proto_to_name(uint8_t proto, int nolookup)
48 {
49 	unsigned int i;
50 
51 	if (proto && !nolookup) {
52 		struct protoent *pent = getprotobynumber(proto);
53 		if (pent)
54 			return pent->p_name;
55 	}
56 
57 	for (i = 0; xtables_chain_protos[i].name != NULL; ++i)
58 		if (xtables_chain_protos[i].num == proto)
59 			return xtables_chain_protos[i].name;
60 
61 	return NULL;
62 }
63 
64 static struct xtables_match *
find_proto(const char * pname,enum xtables_tryload tryload,int nolookup,struct xtables_rule_match ** matches)65 find_proto(const char *pname, enum xtables_tryload tryload,
66 	   int nolookup, struct xtables_rule_match **matches)
67 {
68 	unsigned int proto;
69 
70 	if (xtables_strtoui(pname, NULL, &proto, 0, UINT8_MAX)) {
71 		const char *protoname = proto_to_name(proto, nolookup);
72 
73 		if (protoname)
74 			return xtables_find_match(protoname, tryload, matches);
75 	} else
76 		return xtables_find_match(pname, tryload, matches);
77 
78 	return NULL;
79 }
80 
81 /*
82  * Some explanations (after four different bugs in 3 different releases): If
83  * we encounter a parameter, that has not been parsed yet, it's not an option
84  * of an explicitly loaded match or a target. However, we support implicit
85  * loading of the protocol match extension. '-p tcp' means 'l4 proto 6' and at
86  * the same time 'load tcp protocol match on demand if we specify --dport'.
87  *
88  * To make this work, we need to make sure:
89  * - the parameter has not been parsed by a match (m above)
90  * - a protocol has been specified
91  * - the protocol extension has not been loaded yet, or is loaded and unused
92  *   [think of ip6tables-restore!]
93  * - the protocol extension can be successively loaded
94  */
should_load_proto(struct iptables_command_state * cs)95 static bool should_load_proto(struct iptables_command_state *cs)
96 {
97 	if (cs->protocol == NULL)
98 		return false;
99 	if (find_proto(cs->protocol, XTF_DONT_LOAD,
100 	    cs->options & OPT_NUMERIC, NULL) == NULL)
101 		return true;
102 	return !cs->proto_used;
103 }
104 
load_proto(struct iptables_command_state * cs)105 struct xtables_match *load_proto(struct iptables_command_state *cs)
106 {
107 	if (!should_load_proto(cs))
108 		return NULL;
109 	return find_proto(cs->protocol, XTF_TRY_LOAD,
110 			  cs->options & OPT_NUMERIC, &cs->matches);
111 }
112 
command_default(struct iptables_command_state * cs,struct xtables_globals * gl)113 int command_default(struct iptables_command_state *cs,
114 		    struct xtables_globals *gl)
115 {
116 	struct xtables_rule_match *matchp;
117 	struct xtables_match *m;
118 
119 	if (cs->target != NULL &&
120 	    (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
121 	    cs->c >= cs->target->option_offset &&
122 	    cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
123 		xtables_option_tpcall(cs->c, cs->argv, cs->invert,
124 				      cs->target, &cs->fw);
125 		return 0;
126 	}
127 
128 	for (matchp = cs->matches; matchp; matchp = matchp->next) {
129 		m = matchp->match;
130 
131 		if (matchp->completed ||
132 		    (m->x6_parse == NULL && m->parse == NULL))
133 			continue;
134 		if (cs->c < matchp->match->option_offset ||
135 		    cs->c >= matchp->match->option_offset + XT_OPTION_OFFSET_SCALE)
136 			continue;
137 		xtables_option_mpcall(cs->c, cs->argv, cs->invert, m, &cs->fw);
138 		return 0;
139 	}
140 
141 	/* Try loading protocol */
142 	m = load_proto(cs);
143 	if (m != NULL) {
144 		size_t size;
145 
146 		cs->proto_used = 1;
147 
148 		size = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size;
149 
150 		m->m = xtables_calloc(1, size);
151 		m->m->u.match_size = size;
152 		strcpy(m->m->u.user.name, m->name);
153 		m->m->u.user.revision = m->revision;
154 		xs_init_match(m);
155 
156 		if (m->x6_options != NULL)
157 			gl->opts = xtables_options_xfrm(gl->orig_opts,
158 							gl->opts,
159 							m->x6_options,
160 							&m->option_offset);
161 		else
162 			gl->opts = xtables_merge_options(gl->orig_opts,
163 							 gl->opts,
164 							 m->extra_opts,
165 							 &m->option_offset);
166 		if (gl->opts == NULL)
167 			xtables_error(OTHER_PROBLEM, "can't alloc memory!");
168 		optind--;
169 		/* Indicate to rerun getopt *immediately* */
170  		return 1;
171 	}
172 
173 	if (cs->c == ':')
174 		xtables_error(PARAMETER_PROBLEM, "option \"%s\" "
175 		              "requires an argument", cs->argv[optind-1]);
176 	if (cs->c == '?')
177 		xtables_error(PARAMETER_PROBLEM, "unknown option "
178 			      "\"%s\"", cs->argv[optind-1]);
179 	xtables_error(PARAMETER_PROBLEM, "Unknown arg \"%s\"", optarg);
180 	return 0;
181 }
182 
subcmd_get(const char * cmd,const struct subcommand * cb)183 static mainfunc_t subcmd_get(const char *cmd, const struct subcommand *cb)
184 {
185 	for (; cb->name != NULL; ++cb)
186 		if (strcmp(cb->name, cmd) == 0)
187 			return cb->main;
188 	return NULL;
189 }
190 
subcmd_main(int argc,char ** argv,const struct subcommand * cb)191 int subcmd_main(int argc, char **argv, const struct subcommand *cb)
192 {
193 	const char *cmd = basename(*argv);
194 	mainfunc_t f = subcmd_get(cmd, cb);
195 
196 	if (f == NULL && argc > 1) {
197 		/*
198 		 * Unable to find a main method for our command name?
199 		 * Let's try again with the first argument!
200 		 */
201 		++argv;
202 		--argc;
203 		f = subcmd_get(*argv, cb);
204 	}
205 
206 	/* now we should have a valid function pointer */
207 	if (f != NULL)
208 		return f(argc, argv);
209 
210 	fprintf(stderr, "ERROR: No valid subcommand given.\nValid subcommands:\n");
211 	for (; cb->name != NULL; ++cb)
212 		fprintf(stderr, " * %s\n", cb->name);
213 	exit(EXIT_FAILURE);
214 }
215 
xs_init_target(struct xtables_target * target)216 void xs_init_target(struct xtables_target *target)
217 {
218 	if (target->udata_size != 0) {
219 		free(target->udata);
220 		target->udata = calloc(1, target->udata_size);
221 		if (target->udata == NULL)
222 			xtables_error(RESOURCE_PROBLEM, "malloc");
223 	}
224 	if (target->init != NULL)
225 		target->init(target->t);
226 }
227 
xs_init_match(struct xtables_match * match)228 void xs_init_match(struct xtables_match *match)
229 {
230 	if (match->udata_size != 0) {
231 		/*
232 		 * As soon as a subsequent instance of the same match
233 		 * is used, e.g. "-m time -m time", the first instance
234 		 * is no longer reachable anyway, so we can free udata.
235 		 * Same goes for target.
236 		 */
237 		free(match->udata);
238 		match->udata = calloc(1, match->udata_size);
239 		if (match->udata == NULL)
240 			xtables_error(RESOURCE_PROBLEM, "malloc");
241 	}
242 	if (match->init != NULL)
243 		match->init(match->m);
244 }
245 
xtables_lock(bool wait)246 bool xtables_lock(bool wait)
247 {
248 	int i = 0, ret, xt_socket;
249 	struct sockaddr_un xt_addr;
250 
251 	memset(&xt_addr, 0, sizeof(xt_addr));
252 	xt_addr.sun_family = AF_UNIX;
253 	strcpy(xt_addr.sun_path+1, XT_SOCKET_NAME);
254 	xt_socket = socket(AF_UNIX, SOCK_STREAM, 0);
255 	/* If we can't even create a socket, fall back to prior (lockless) behavior */
256 	if (xt_socket < 0)
257 		return true;
258 
259 	while (1) {
260 		ret = bind(xt_socket, (struct sockaddr*)&xt_addr,
261 			   offsetof(struct sockaddr_un, sun_path)+XT_SOCKET_LEN);
262 		if (ret == 0)
263 			return true;
264 		else if (wait == false)
265 			return false;
266 		if (++i % 2 == 0)
267 			fprintf(stderr, "Another app is currently holding the xtables lock; "
268 				"waiting for it to exit...\n");
269 		sleep(1);
270 	}
271 }
272