Annotation of embedaddon/mtr/packet/command.c, revision 1.1.1.1
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 2016 Matt Kimball
4:
5: This program is free software; you can redistribute it and/or modify
6: it under the terms of the GNU General Public License version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License
15: along with this program; if not, write to the Free Software
16: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: #include "command.h"
20:
21: #include <assert.h>
22: #include <errno.h>
23: #include <stdbool.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <strings.h>
28:
29: #include "cmdparse.h"
30: #include "platform.h"
31: #include "config.h"
32:
33: /*
34: Find a parameter with a particular name in a command_t structure.
35: If no such parameter exists, return NULL.
36: */
37: static
38: const char *find_parameter(
39: const struct command_t *command,
40: const char *name_request)
41: {
42: const char *name;
43: const char *value;
44: int i;
45:
46: for (i = 0; i < command->argument_count; i++) {
47: name = command->argument_name[i];
48: value = command->argument_value[i];
49:
50: if (!strcmp(name, name_request)) {
51: return value;
52: }
53: }
54:
55: return NULL;
56: }
57:
58: /* Returns a feature support string for a particular probe protocol */
59: static
60: const char *check_protocol_support(
61: struct net_state_t *net_state,
62: int protocol)
63: {
64: if (is_protocol_supported(net_state, protocol)) {
65: return "ok";
66: } else {
67: return "no";
68: }
69: }
70:
71: /* Return a feature support string for an IP protocol version */
72: static
73: const char *check_ip_version_support(
74: struct net_state_t *net_state,
75: int ip_version)
76: {
77: if (is_ip_version_supported(net_state, ip_version)) {
78: return "ok";
79: } else {
80: return "no";
81: }
82: }
83:
84: /* Given a feature name, return a string for the check-support reply */
85: static
86: const char *check_support(
87: const char *feature,
88: struct net_state_t *net_state)
89: {
90: if (!strcmp(feature, "version")) {
91: return PACKAGE_VERSION;
92: }
93:
94: if (!strcmp(feature, "ip-4")) {
95: return check_ip_version_support(net_state, 4);
96: }
97:
98: if (!strcmp(feature, "ip-6")) {
99: return check_ip_version_support(net_state, 6);
100: }
101:
102: if (!strcmp(feature, "send-probe")) {
103: return "ok";
104: }
105:
106: if (!strcmp(feature, "icmp")) {
107: return check_protocol_support(net_state, IPPROTO_ICMP);
108: }
109:
110: if (!strcmp(feature, "udp")) {
111: return check_protocol_support(net_state, IPPROTO_UDP);
112: }
113:
114: if (!strcmp(feature, "tcp")) {
115: return check_protocol_support(net_state, IPPROTO_TCP);
116: }
117: #ifdef IPPROTO_SCTP
118: if (!strcmp(feature, "sctp")) {
119: return check_protocol_support(net_state, IPPROTO_SCTP);
120: }
121: #endif
122:
123: #ifdef SO_MARK
124: if (!strcmp(feature, "mark")) {
125: return "ok";
126: }
127: #endif
128:
129: return "no";
130: }
131:
132: /* Handle a check-support request by checking for a particular feature */
133: static
134: void check_support_command(
135: const struct command_t *command,
136: struct net_state_t *net_state)
137: {
138: const char *feature;
139: const char *support;
140:
141: feature = find_parameter(command, "feature");
142: if (feature == NULL) {
143: printf("%d invalid-argument\n", command->token);
144: return;
145: }
146:
147: support = check_support(feature, net_state);
148: printf("%d feature-support support %s\n", command->token, support);
149: }
150:
151: /*
152: If a named send_probe argument is recognized, fill in the probe paramters
153: structure with the argument value.
154: */
155: static
156: bool decode_probe_argument(
157: struct probe_param_t *param,
158: const char *name,
159: const char *value)
160: {
161: char *endstr = NULL;
162:
163: /* Pass IPv4 addresses as string values */
164: if (!strcmp(name, "ip-4")) {
165: param->ip_version = 4;
166: param->remote_address = value;
167: }
168:
169: /* IPv6 address */
170: if (!strcmp(name, "ip-6")) {
171: param->ip_version = 6;
172: param->remote_address = value;
173: }
174:
175: /* IPv4 address to send from */
176: if (!strcmp(name, "local-ip-4")) {
177: param->local_address = value;
178: }
179:
180: /* IPv6 address to send from */
181: if (!strcmp(name, "local-ip-6")) {
182: param->local_address = value;
183: }
184:
185: /* Protocol for the probe */
186: if (!strcmp(name, "protocol")) {
187: if (!strcmp(value, "icmp")) {
188: param->protocol = IPPROTO_ICMP;
189: } else if (!strcmp(value, "udp")) {
190: param->protocol = IPPROTO_UDP;
191: } else if (!strcmp(value, "tcp")) {
192: param->protocol = IPPROTO_TCP;
193: #ifdef IPPROTO_SCTP
194: } else if (!strcmp(value, "sctp")) {
195: param->protocol = IPPROTO_SCTP;
196: #endif
197: } else {
198: return false;
199: }
200: }
201:
202: /* Destination port for the probe */
203: if (!strcmp(name, "port")) {
204: param->dest_port = strtol(value, &endstr, 10);
205: if (*endstr != 0) {
206: return false;
207: }
208: }
209:
210: /* The local port to send UDP probes from */
211: if (!strcmp(name, "local-port")) {
212: param->local_port = strtol(value, &endstr, 10);
213: if (*endstr != 0) {
214: return false;
215: }
216:
217: /*
218: Don't allow using a local port which requires
219: privileged binding.
220: */
221: if (param->local_port < 1024) {
222: param->local_port = 0;
223: return false;
224: }
225: }
226:
227: /* The "type of service" field for the IP header */
228: if (!strcmp(name, "tos")) {
229: param->type_of_service = strtol(value, &endstr, 10);
230: if (*endstr != 0) {
231: return false;
232: }
233: }
234:
235: /* The Linux packet mark for mark-based routing */
236: if (!strcmp(name, "mark")) {
237: param->routing_mark = strtol(value, &endstr, 10);
238: if (*endstr != 0) {
239: return false;
240: }
241: }
242:
243: /* The size of the packet (including headers) */
244: if (!strcmp(name, "size")) {
245: param->packet_size = strtol(value, &endstr, 10);
246: if (*endstr != 0) {
247: return false;
248: }
249: }
250:
251: /* The packet's bytes will be filled with this value */
252: if (!strcmp(name, "bit-pattern")) {
253: param->bit_pattern = strtol(value, &endstr, 10);
254: if (*endstr != 0) {
255: return false;
256: }
257: }
258:
259: /* Time-to-live values */
260: if (!strcmp(name, "ttl")) {
261: param->ttl = strtol(value, &endstr, 10);
262: if (*endstr != 0) {
263: return false;
264: }
265: }
266:
267: /* Number of seconds to wait for a reply */
268: if (!strcmp(name, "timeout")) {
269: param->timeout = strtol(value, &endstr, 10);
270: if (*endstr != 0) {
271: return false;
272: }
273: }
274:
275: return true;
276: }
277:
278: /*
279: Check the probe parameters against currently supported features
280: and report and error if there is a problem.
281:
282: Return true if everything is okay, false otherwise.
283: */
284: static
285: bool validate_probe_parameters(
286: struct net_state_t *net_state,
287: struct probe_param_t *param)
288: {
289: if (!is_ip_version_supported(net_state, param->ip_version)) {
290: printf("%d invalid-argument reason ip-version-not-supported\n",
291: param->command_token);
292:
293: return false;
294: }
295:
296: if (!is_protocol_supported(net_state, param->protocol)) {
297: printf("%d invalid-argument reason protocol-not-supported\n",
298: param->command_token);
299:
300: return false;
301: }
302:
303: return true;
304: }
305:
306: /* Handle "send-probe" commands */
307: static
308: void send_probe_command(
309: const struct command_t *command,
310: struct net_state_t *net_state)
311: {
312: struct probe_param_t param;
313: int i;
314: char *name;
315: char *value;
316:
317: /* We will prepare a probe_param_t for send_probe. */
318: memset(¶m, 0, sizeof(struct probe_param_t));
319: param.command_token = command->token;
320: param.protocol = IPPROTO_ICMP;
321: param.ttl = 255;
322: param.packet_size = 64;
323: param.timeout = 10;
324:
325: for (i = 0; i < command->argument_count; i++) {
326: name = command->argument_name[i];
327: value = command->argument_value[i];
328:
329: if (!decode_probe_argument(¶m, name, value)) {
330: printf("%d invalid-argument\n", command->token);
331: return;
332: }
333: }
334:
335: if (!validate_probe_parameters(net_state, ¶m)) {
336: return;
337: }
338:
339: /* Send the probe using a platform specific mechanism */
340: send_probe(net_state, ¶m);
341: }
342:
343: /*
344: Given a parsed command, dispatch to the handler for specific
345: command requests.
346: */
347: static
348: void dispatch_command(
349: const struct command_t *command,
350: struct net_state_t *net_state)
351: {
352: if (!strcmp(command->command_name, "check-support")) {
353: check_support_command(command, net_state);
354: } else if (!strcmp(command->command_name, "send-probe")) {
355: send_probe_command(command, net_state);
356: } else {
357: /* For unrecognized commands, respond with an error */
358: printf("%d unknown-command\n", command->token);
359: }
360: }
361:
362: /*
363: With newly read data in our command buffer, dispatch all completed
364: command requests.
365: */
366: void dispatch_buffer_commands(
367: struct command_buffer_t *buffer,
368: struct net_state_t *net_state)
369: {
370: struct command_t command;
371: char *end_of_command;
372: char full_command[COMMAND_BUFFER_SIZE];
373: int command_length;
374: int remaining_count;
375:
376: while (true) {
377: assert(buffer->incoming_read_position < COMMAND_BUFFER_SIZE);
378:
379: /* Terminate the buffer string */
380: buffer->incoming_buffer[buffer->incoming_read_position] = 0;
381:
382: /* Find the next newline, which terminates command requests */
383: end_of_command = index(buffer->incoming_buffer, '\n');
384: if (end_of_command == NULL) {
385: /*
386: No newlines found, so any data we've read so far is
387: not yet complete.
388: */
389: break;
390: }
391:
392: command_length = end_of_command - buffer->incoming_buffer;
393: remaining_count =
394: buffer->incoming_read_position - command_length - 1;
395:
396: /* Copy the completed command */
397: memmove(full_command, buffer->incoming_buffer, command_length);
398: full_command[command_length] = 0;
399:
400: /*
401: Free the space used by the completed command by advancing the
402: remaining requests within the buffer.
403: */
404: memmove(buffer->incoming_buffer, end_of_command + 1,
405: remaining_count);
406: buffer->incoming_read_position -= command_length + 1;
407:
408: if (parse_command(&command, full_command)) {
409: /* If the command fails to parse, respond with an error */
410: printf("0 command-parse-error\n");
411: } else {
412: dispatch_command(&command, net_state);
413: }
414: }
415:
416: if (buffer->incoming_read_position >= COMMAND_BUFFER_SIZE - 1) {
417: /*
418: If we've filled the buffer without a complete command, the
419: only thing we can do is discard what we've read and hope that
420: new data is better formatted.
421: */
422: printf("0 command-buffer-overflow\n");
423: buffer->incoming_read_position = 0;
424: }
425: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>