|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
mtr -- a network diagnostic tool
|
|
Packit |
b802ec |
Copyright (C) 2016 Matt Kimball
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
b802ec |
it under the terms of the GNU General Public License version 2 as
|
|
Packit |
b802ec |
published by the Free Software Foundation.
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
This program is distributed in the hope that it will be useful,
|
|
Packit |
b802ec |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b802ec |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
b802ec |
GNU General Public License for more details.
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
You should have received a copy of the GNU General Public License
|
|
Packit |
b802ec |
along with this program; if not, write to the Free Software
|
|
Packit |
b802ec |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "command.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include <assert.h>
|
|
Packit |
b802ec |
#include <errno.h>
|
|
Packit |
b802ec |
#include <stdbool.h>
|
|
Packit |
b802ec |
#include <stdio.h>
|
|
Packit |
b802ec |
#include <stdlib.h>
|
|
Packit |
b802ec |
#include <string.h>
|
|
Packit |
b802ec |
#include <strings.h>
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "cmdparse.h"
|
|
Packit |
b802ec |
#include "platform.h"
|
|
Packit |
b802ec |
#include "config.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Find a parameter with a particular name in a command_t structure.
|
|
Packit |
b802ec |
If no such parameter exists, return NULL.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
const char *find_parameter(
|
|
Packit |
b802ec |
const struct command_t *command,
|
|
Packit |
b802ec |
const char *name_request)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const char *name;
|
|
Packit |
b802ec |
const char *value;
|
|
Packit |
b802ec |
int i;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
for (i = 0; i < command->argument_count; i++) {
|
|
Packit |
b802ec |
name = command->argument_name[i];
|
|
Packit |
b802ec |
value = command->argument_value[i];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(name, name_request)) {
|
|
Packit |
b802ec |
return value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return NULL;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Returns a feature support string for a particular probe protocol */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
const char *check_protocol_support(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
int protocol)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
if (is_protocol_supported(net_state, protocol)) {
|
|
Packit |
b802ec |
return "ok";
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
return "no";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Return a feature support string for an IP protocol version */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
const char *check_ip_version_support(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
int ip_version)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
if (is_ip_version_supported(net_state, ip_version)) {
|
|
Packit |
b802ec |
return "ok";
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
return "no";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Given a feature name, return a string for the check-support reply */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
const char *check_support(
|
|
Packit |
b802ec |
const char *feature,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
if (!strcmp(feature, "version")) {
|
|
Packit |
b802ec |
return PACKAGE_VERSION;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "ip-4")) {
|
|
Packit |
b802ec |
return check_ip_version_support(net_state, 4);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "ip-6")) {
|
|
Packit |
b802ec |
return check_ip_version_support(net_state, 6);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "send-probe")) {
|
|
Packit |
b802ec |
return "ok";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "icmp")) {
|
|
Packit |
b802ec |
return check_protocol_support(net_state, IPPROTO_ICMP);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "udp")) {
|
|
Packit |
b802ec |
return check_protocol_support(net_state, IPPROTO_UDP);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(feature, "tcp")) {
|
|
Packit |
b802ec |
return check_protocol_support(net_state, IPPROTO_TCP);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
if (!strcmp(feature, "sctp")) {
|
|
Packit |
b802ec |
return check_protocol_support(net_state, IPPROTO_SCTP);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#ifdef SO_MARK
|
|
Packit |
b802ec |
if (!strcmp(feature, "mark")) {
|
|
Packit |
b802ec |
return "ok";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return "no";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Handle a check-support request by checking for a particular feature */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void check_support_command(
|
|
Packit |
b802ec |
const struct command_t *command,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const char *feature;
|
|
Packit |
b802ec |
const char *support;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
feature = find_parameter(command, "feature");
|
|
Packit |
b802ec |
if (feature == NULL) {
|
|
Packit |
b802ec |
printf("%d invalid-argument\n", command->token);
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
support = check_support(feature, net_state);
|
|
Packit |
b802ec |
printf("%d feature-support support %s\n", command->token, support);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If a named send_probe argument is recognized, fill in the probe paramters
|
|
Packit |
b802ec |
structure with the argument value.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
bool decode_probe_argument(
|
|
Packit |
b802ec |
struct probe_param_t *param,
|
|
Packit |
b802ec |
const char *name,
|
|
Packit |
b802ec |
const char *value)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char *endstr = NULL;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Pass IPv4 addresses as string values */
|
|
Packit |
b802ec |
if (!strcmp(name, "ip-4")) {
|
|
Packit |
b802ec |
param->ip_version = 4;
|
|
Packit |
b802ec |
param->remote_address = value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* IPv6 address */
|
|
Packit |
b802ec |
if (!strcmp(name, "ip-6")) {
|
|
Packit |
b802ec |
param->ip_version = 6;
|
|
Packit |
b802ec |
param->remote_address = value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* IPv4 address to send from */
|
|
Packit |
b802ec |
if (!strcmp(name, "local-ip-4")) {
|
|
Packit |
b802ec |
param->local_address = value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* IPv6 address to send from */
|
|
Packit |
b802ec |
if (!strcmp(name, "local-ip-6")) {
|
|
Packit |
b802ec |
param->local_address = value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Protocol for the probe */
|
|
Packit |
b802ec |
if (!strcmp(name, "protocol")) {
|
|
Packit |
b802ec |
if (!strcmp(value, "icmp")) {
|
|
Packit |
b802ec |
param->protocol = IPPROTO_ICMP;
|
|
Packit |
b802ec |
} else if (!strcmp(value, "udp")) {
|
|
Packit |
b802ec |
param->protocol = IPPROTO_UDP;
|
|
Packit |
b802ec |
} else if (!strcmp(value, "tcp")) {
|
|
Packit |
b802ec |
param->protocol = IPPROTO_TCP;
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
} else if (!strcmp(value, "sctp")) {
|
|
Packit |
b802ec |
param->protocol = IPPROTO_SCTP;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Destination port for the probe */
|
|
Packit |
b802ec |
if (!strcmp(name, "port")) {
|
|
Packit |
b802ec |
param->dest_port = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The local port to send UDP probes from */
|
|
Packit |
b802ec |
if (!strcmp(name, "local-port")) {
|
|
Packit |
b802ec |
param->local_port = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Don't allow using a local port which requires
|
|
Packit |
b802ec |
privileged binding.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (param->local_port < 1024) {
|
|
Packit |
b802ec |
param->local_port = 0;
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The "type of service" field for the IP header */
|
|
Packit |
b802ec |
if (!strcmp(name, "tos")) {
|
|
Packit |
b802ec |
param->type_of_service = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The Linux packet mark for mark-based routing */
|
|
Packit |
b802ec |
if (!strcmp(name, "mark")) {
|
|
Packit |
b802ec |
param->routing_mark = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The size of the packet (including headers) */
|
|
Packit |
b802ec |
if (!strcmp(name, "size")) {
|
|
Packit |
b802ec |
param->packet_size = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The packet's bytes will be filled with this value */
|
|
Packit |
b802ec |
if (!strcmp(name, "bit-pattern")) {
|
|
Packit |
b802ec |
param->bit_pattern = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Time-to-live values */
|
|
Packit |
b802ec |
if (!strcmp(name, "ttl")) {
|
|
Packit |
b802ec |
param->ttl = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Number of seconds to wait for a reply */
|
|
Packit |
b802ec |
if (!strcmp(name, "timeout")) {
|
|
Packit |
b802ec |
param->timeout = strtol(value, &endstr, 10);
|
|
Packit |
b802ec |
if (*endstr != 0) {
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return true;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Check the probe parameters against currently supported features
|
|
Packit |
b802ec |
and report and error if there is a problem.
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
Return true if everything is okay, false otherwise.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
bool validate_probe_parameters(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
struct probe_param_t *param)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
if (!is_ip_version_supported(net_state, param->ip_version)) {
|
|
Packit |
b802ec |
printf("%d invalid-argument reason ip-version-not-supported\n",
|
|
Packit |
b802ec |
param->command_token);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!is_protocol_supported(net_state, param->protocol)) {
|
|
Packit |
b802ec |
printf("%d invalid-argument reason protocol-not-supported\n",
|
|
Packit |
b802ec |
param->command_token);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return false;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return true;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Handle "send-probe" commands */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void send_probe_command(
|
|
Packit |
b802ec |
const struct command_t *command,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
struct probe_param_t param;
|
|
Packit |
b802ec |
int i;
|
|
Packit |
b802ec |
char *name;
|
|
Packit |
b802ec |
char *value;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* We will prepare a probe_param_t for send_probe. */
|
|
Packit |
b802ec |
memset(¶m, 0, sizeof(struct probe_param_t));
|
|
Packit |
b802ec |
param.command_token = command->token;
|
|
Packit |
b802ec |
param.protocol = IPPROTO_ICMP;
|
|
Packit |
b802ec |
param.ttl = 255;
|
|
Packit |
b802ec |
param.packet_size = 64;
|
|
Packit |
b802ec |
param.timeout = 10;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
for (i = 0; i < command->argument_count; i++) {
|
|
Packit |
b802ec |
name = command->argument_name[i];
|
|
Packit |
b802ec |
value = command->argument_value[i];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!decode_probe_argument(¶m, name, value)) {
|
|
Packit |
b802ec |
printf("%d invalid-argument\n", command->token);
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!validate_probe_parameters(net_state, ¶m)) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Send the probe using a platform specific mechanism */
|
|
Packit |
b802ec |
send_probe(net_state, ¶m;;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Given a parsed command, dispatch to the handler for specific
|
|
Packit |
b802ec |
command requests.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void dispatch_command(
|
|
Packit |
b802ec |
const struct command_t *command,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
if (!strcmp(command->command_name, "check-support")) {
|
|
Packit |
b802ec |
check_support_command(command, net_state);
|
|
Packit |
b802ec |
} else if (!strcmp(command->command_name, "send-probe")) {
|
|
Packit |
b802ec |
send_probe_command(command, net_state);
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
/* For unrecognized commands, respond with an error */
|
|
Packit |
b802ec |
printf("%d unknown-command\n", command->token);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
With newly read data in our command buffer, dispatch all completed
|
|
Packit |
b802ec |
command requests.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
void dispatch_buffer_commands(
|
|
Packit |
b802ec |
struct command_buffer_t *buffer,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
struct command_t command;
|
|
Packit |
b802ec |
char *end_of_command;
|
|
Packit |
b802ec |
char full_command[COMMAND_BUFFER_SIZE];
|
|
Packit |
b802ec |
int command_length;
|
|
Packit |
b802ec |
int remaining_count;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
while (true) {
|
|
Packit |
b802ec |
assert(buffer->incoming_read_position < COMMAND_BUFFER_SIZE);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Terminate the buffer string */
|
|
Packit |
b802ec |
buffer->incoming_buffer[buffer->incoming_read_position] = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Find the next newline, which terminates command requests */
|
|
Packit |
b802ec |
end_of_command = index(buffer->incoming_buffer, '\n');
|
|
Packit |
b802ec |
if (end_of_command == NULL) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
No newlines found, so any data we've read so far is
|
|
Packit |
b802ec |
not yet complete.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
command_length = end_of_command - buffer->incoming_buffer;
|
|
Packit |
b802ec |
remaining_count =
|
|
Packit |
b802ec |
buffer->incoming_read_position - command_length - 1;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Copy the completed command */
|
|
Packit |
b802ec |
memmove(full_command, buffer->incoming_buffer, command_length);
|
|
Packit |
b802ec |
full_command[command_length] = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Free the space used by the completed command by advancing the
|
|
Packit |
b802ec |
remaining requests within the buffer.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
memmove(buffer->incoming_buffer, end_of_command + 1,
|
|
Packit |
b802ec |
remaining_count);
|
|
Packit |
b802ec |
buffer->incoming_read_position -= command_length + 1;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (parse_command(&command, full_command)) {
|
|
Packit |
b802ec |
/* If the command fails to parse, respond with an error */
|
|
Packit |
b802ec |
printf("0 command-parse-error\n");
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
dispatch_command(&command, net_state);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (buffer->incoming_read_position >= COMMAND_BUFFER_SIZE - 1) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If we've filled the buffer without a complete command, the
|
|
Packit |
b802ec |
only thing we can do is discard what we've read and hope that
|
|
Packit |
b802ec |
new data is better formatted.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
printf("0 command-buffer-overflow\n");
|
|
Packit |
b802ec |
buffer->incoming_read_position = 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|