Skip to content

Commit

Permalink
Fix API ports using ifName with slashes
Browse files Browse the repository at this point in the history
hand parse the path for the ports graph endpoints
this way we can respect the original way of handling slashes (%2F)
  • Loading branch information
murrant committed Aug 12, 2019
1 parent 0084ab8 commit d11e9bd
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 30 deletions.
29 changes: 12 additions & 17 deletions doc/API/Devices.md
Expand Up @@ -469,16 +469,13 @@ Output:
"count": 3,
"ports": [
{
"ifName": "lo",
"port_id": 1
"ifName": "lo"
},
{
"ifName": "eth0",
"port_id": 2
"ifName": "eth0"
},
{
"ifName": "eth1",
"port_id": 14
"ifName": "eth1"
}
]
}
Expand Down Expand Up @@ -750,11 +747,10 @@ Get information about a particular port for a device.
Route: `/api/v0/devices/:hostname/ports/:ifname`

- hostname can be either the device hostname or id
- port_id or ifname of any of the interfaces of the device which can be
obtained using [`get_port_graphs`](#function-get_port_graphs).
> If ifName contains `/`, you must pass it as the ifName GET variable
and put a dummy value in ifname. For example:
`/api/v0/devices/1/ports/none?ifName=Gi0/1/0`
- ifname can be any of the interface names for the device which can be
obtained using
[`get_port_graphs`](#function-get_port_graphs). Please ensure that
the ifname is urlencoded if it needs to be (i.e Gi0/1/0 would need to be urlencoded.

Input:

Expand Down Expand Up @@ -788,12 +784,11 @@ Get a graph of a port for a particular device.
Route: `/api/v0/devices/:hostname/ports/:ifname/:type`

- hostname can be either the device hostname or id
- port_id or ifname of any of the ports of the device which can be
obtained using [`get_port_graphs`](#function-get_port_graphs).
> If ifName contains `/`, you must pass it as the ifName GET variable
and put a dummy value in ifname. For example:
`/api/v0/devices/1/ports/none/port_bits?ifName=Gi0/1/0`

- ifname can be any of the interface names for the device which can be
obtained using
[`get_port_graphs`](#function-get_port_graphs). Please ensure that
the ifname is urlencoded if it needs to be (i.e Gi0/1/0 would need
to be urlencoded.
- type is the port type you want the graph for, you can request a list
of ports for a device with [`get_port_graphs`](#function-get_port graphs).

Expand Down
36 changes: 27 additions & 9 deletions includes/html/api_functions.inc.php
Expand Up @@ -74,6 +74,12 @@ function api_get_graph(array $vars)
$auth = '1';
$base64_output = '';

// prevent ugly error for undefined graphs from being passed to the user
list($type, $subtype) = extract_graph_type($vars['type']);
if (!is_file(base_path("includes/html/graphs/$type/auth.inc.php"))) {
return api_error(400, 'Invalid graph type');
}

ob_start();

rrdtool_initialize(false);
Expand Down Expand Up @@ -117,14 +123,14 @@ function check_port_permission($port_id, $device_id, $callback)
return $callback($port_id);
}

function get_graph_by_port_hostname(\Illuminate\Http\Request $request)
function get_graph_by_port_hostname(\Illuminate\Http\Request $request, $ifname = null, $type = 'port_bits')
{
// This will return a graph for a given port by the ifName
$hostname = $request->route('hostname');
$device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
$vars = [
'port' => $request->get('ifName', $request->route('ifname')),
'type' => $request->route('type', 'port_bits'),
'port' => $ifname ?: $request->route('ifname'),
'type' => $request->route('type', $type),
'output' => $request->get('output', 'display'),
'width' => $request->get('width', 1075),
'height' => $request->get('height', 300),
Expand All @@ -138,8 +144,8 @@ function get_graph_by_port_hostname(\Illuminate\Http\Request $request)
$vars['to'] = $request->get('to');
}

$id = (ctype_digit($vars['port']) && !$request->has('ifName')) ? 'port_id' : ($request->get('ifDescr') ? 'ifDescr' : 'ifName');
$vars['id'] = dbFetchCell('SELECT `P`.`port_id` FROM `ports` AS `P` JOIN `devices` AS `D` ON `P`.`device_id` = `D`.`device_id` WHERE `D`.`device_id`=? AND `P`.`' . $id . '`=? AND `deleted` = 0 LIMIT 1', [$device_id, $vars['port']]);
$port = $request->get('ifDescr') ? 'ifDescr' : 'ifName';
$vars['id'] = dbFetchCell("SELECT `P`.`port_id` FROM `ports` AS `P` JOIN `devices` AS `D` ON `P`.`device_id` = `D`.`device_id` WHERE `D`.`device_id`=? AND `P`.`$port`=? AND `deleted` = 0 LIMIT 1", [$device_id, $vars['port']]);

return check_port_permission($vars['id'], $device_id, function () use ($vars) {
return api_get_graph($vars);
Expand All @@ -149,12 +155,24 @@ function get_graph_by_port_hostname(\Illuminate\Http\Request $request)

function get_port_stats_by_port_hostname(\Illuminate\Http\Request $request)
{
$ifName = $request->route('ifname');

// handle %2f in paths and pass to get_graph_by_port_hostname if needed
if (str_contains($ifName, '/')) {
$parts = explode('/', $request->path());

if (isset($parts[5])) {
$ifName = urldecode($parts[5]);
if (isset($parts[6])) {
return get_graph_by_port_hostname($request, $ifName, $parts[6]);
}
}
}

// This will return port stats based on a devices hostname and ifName
$hostname = $request->route('hostname');
$device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
$ifName = $request->get('ifName', $request->route('ifname'));
$id = (ctype_digit($ifName) && !$request->has('ifName')) ? 'port_id' : 'ifName';
$port = dbFetchRow('SELECT * FROM `ports` WHERE `device_id`=? AND `' . $id . '`=? AND `deleted` = 0', [$device_id, $ifName]);
$port = dbFetchRow('SELECT * FROM `ports` WHERE `device_id`=? AND `ifName`=? AND `deleted` = 0', [$device_id, $ifName]);

return check_port_permission($port['port_id'], $device_id, function () use ($request, $port) {
$in_rate = $port['ifInOctets_rate'] * 8;
Expand Down Expand Up @@ -796,7 +814,7 @@ function list_available_wireless_graphs(\Illuminate\Http\Request $request)
function get_port_graphs(\Illuminate\Http\Request $request)
{
$hostname = $request->route('hostname');
$columns = $request->get('columns', 'ifName,port_id');
$columns = $request->get('columns', 'ifName');

if ($validate = validate_column_list($columns, 'ports') !== true) {
return $validate;
Expand Down
12 changes: 12 additions & 0 deletions includes/html/functions.inc.php
Expand Up @@ -455,6 +455,18 @@ function generate_entity_link($type, $entity, $text = null, $graph_type = null)
return ($link);
}//end generate_entity_link()

/**
* Extract type and subtype from a complex graph type, also makes sure variables are file name safe.
* @param string $type
* @return array [type, subtype]
*/
function extract_graph_type($type): array
{
preg_match('/^(?P<type>[A-Za-z0-9]+)_(?P<subtype>.+)/', $type, $graphtype);
$type = basename($graphtype['type']);
$subtype = basename($graphtype['subtype']);
return [$type, $subtype];
}

function generate_port_link($port, $text = null, $type = null, $overlib = 1, $single_graph = 0)
{
Expand Down
4 changes: 1 addition & 3 deletions includes/html/graphs/graph.inc.php
Expand Up @@ -7,9 +7,7 @@
$vars[$name] = $value;
}

preg_match('/^(?P<type>[A-Za-z0-9]+)_(?P<subtype>.+)/', $vars['type'], $graphtype);
$type = basename($graphtype['type']);
$subtype = basename($graphtype['subtype']);
list($type, $subtype) = extract_graph_type($vars['type']);

if (is_numeric($vars['device'])) {
$device = device_by_id_cache($vars['device']);
Expand Down
3 changes: 2 additions & 1 deletion routes/api.php
Expand Up @@ -91,7 +91,8 @@
Route::get('{hostname}/port_stack', 'LegacyApiController@get_port_stack')->name('get_port_stack');
Route::get('{hostname}/components', 'LegacyApiController@get_components')->name('get_components');
Route::get('{hostname}/groups', 'LegacyApiController@get_device_groups')->name('get_device_groups');
Route::get('{hostname}/ports/{ifname}', 'LegacyApiController@get_port_stats_by_port_hostname')->name('get_port_stats_by_port_hostname');
// consumes the route below, but passes to it when detected
Route::get('{hostname}/ports/{ifname}', 'LegacyApiController@get_port_stats_by_port_hostname')->name('get_port_stats_by_port_hostname')->where('ifname', '.*');
Route::get('{hostname}/ports/{ifname}/{type}', 'LegacyApiController@get_graph_by_port_hostname')->name('get_graph_by_port_hostname');

Route::get('{hostname}/{type}', 'LegacyApiController@get_graph_generic_by_hostname')->name('get_graph_generic_by_hostname');
Expand Down

0 comments on commit d11e9bd

Please sign in to comment.