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