diff --git a/LibreNMS/OS/Traits/YamlOSDiscovery.php b/LibreNMS/OS/Traits/YamlOSDiscovery.php index 4375bfdbeb..55cef3999d 100644 --- a/LibreNMS/OS/Traits/YamlOSDiscovery.php +++ b/LibreNMS/OS/Traits/YamlOSDiscovery.php @@ -28,6 +28,7 @@ namespace LibreNMS\OS\Traits; use App\Models\Device; use App\Models\Location; use Illuminate\Support\Arr; +use LibreNMS\Util\StringHelpers; use Log; trait YamlOSDiscovery @@ -93,8 +94,10 @@ trait YamlOSDiscovery Log::debug('Yaml location data:', $data); + $location = $this->findFirst($data, $name, $numeric) ?? snmp_get($this->getDeviceArray(), 'SNMPv2-MIB::sysLocation.0', '-Oqv'); + return new Location([ - 'location' => $this->findFirst($data, $name, $numeric) ?? snmp_get($this->getDeviceArray(), 'SNMPv2-MIB::sysLocation.0', '-Oqv'), + 'location' => StringHelpers::inferEncoding($location), 'lat' => $this->findFirst($data, $lat, $numeric), 'lng' => $this->findFirst($data, $lng, $numeric), ]); diff --git a/LibreNMS/Util/StringHelpers.php b/LibreNMS/Util/StringHelpers.php index ea9053fa65..310197c442 100644 --- a/LibreNMS/Util/StringHelpers.php +++ b/LibreNMS/Util/StringHelpers.php @@ -93,4 +93,36 @@ class StringHelpers { return ucwords(implode(' ', preg_split('/(?=[A-Z])/', $string))); } + + /** + * Sometimes devices store strings as non-unicode strings and return them directly. + * NetSnmp parses those as UTF-8, try to convert the string if it contains non-printable ascii characters. + * + * @param string|null $string + * @return string + */ + public static function inferEncoding(?string $string): ?string + { + if (empty($string) || preg_match('//u', $string) || ! function_exists('iconv')) { + return $string; + } + + $charset = config('app.charset'); + + if (($converted = @iconv($charset, 'UTF-8', $string)) !== false) { + return (string) $converted; + } + + if ($charset !== 'Windows-1252' && ($converted = @iconv('Windows-1252', 'UTF-8', $string)) !== false) { + return (string) $converted; + } + + if ($charset !== 'CP850' && ($converted = @iconv('CP850', 'UTF-8', $string)) !== false) { + return (string) $converted; + } + + \Log::debug('Failed to convert string: ' . $string); + + return $string; + } } diff --git a/config/app.php b/config/app.php index 0c39e56059..5bfd77a693 100644 --- a/config/app.php +++ b/config/app.php @@ -247,4 +247,5 @@ return [ 'Rrd' => App\Facades\Rrd::class, ], + 'charset' => env('CHARSET', ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'), ]; diff --git a/includes/discovery/ports.inc.php b/includes/discovery/ports.inc.php index 4cac9dd845..7b57e40309 100644 --- a/includes/discovery/ports.inc.php +++ b/includes/discovery/ports.inc.php @@ -3,6 +3,7 @@ // Build SNMP Cache Array use App\Models\PortGroup; use LibreNMS\Config; +use LibreNMS\Util\StringHelpers; $port_stats = []; $port_stats = snmpwalk_cache_oid($device, 'ifDescr', $port_stats, 'IF-MIB'); @@ -60,6 +61,7 @@ $default_port_group = Config::get('default_port_group'); // New interface detection foreach ($port_stats as $ifIndex => $snmp_data) { $snmp_data['ifIndex'] = $ifIndex; // Store ifIndex in port entry + $snmp_data['ifAlias'] = StringHelpers::inferEncoding($snmp_data['ifAlias']); // Get port_id according to port_association_mode used for this device $port_id = get_port_id($ports_mapped, $snmp_data, $port_association_mode); diff --git a/includes/polling/ports.inc.php b/includes/polling/ports.inc.php index 04dcc3aadd..47d4cebf72 100644 --- a/includes/polling/ports.inc.php +++ b/includes/polling/ports.inc.php @@ -669,6 +669,8 @@ foreach ($ports as $port) { if ($oid == 'ifAlias') { if ($attribs['ifName:' . $port['ifName']]) { $this_port['ifAlias'] = $port['ifAlias']; + } else { + $this_port['ifAlias'] = \LibreNMS\Util\StringHelpers::inferEncoding($this_port['ifAlias']); } } if ($oid == 'ifSpeed') { diff --git a/tests/Unit/Util/StringHelperTest.php b/tests/Unit/Util/StringHelperTest.php new file mode 100644 index 0000000000..9bf0904c84 --- /dev/null +++ b/tests/Unit/Util/StringHelperTest.php @@ -0,0 +1,51 @@ +. + * + * @package LibreNMS + * @link http://librenms.org + * @copyright 2021 Tony Murray + * @author Tony Murray + */ + +namespace LibreNMS\Tests\Unit\Util; + +use LibreNMS\Tests\TestCase; +use LibreNMS\Util\StringHelpers; + +class StringHelperTest extends TestCase +{ + /** + * A basic feature test example. + * + * @return void + */ + public function testInferEncoding() + { + $this->assertEquals(null, StringHelpers::inferEncoding(null)); + $this->assertEquals('', StringHelpers::inferEncoding('')); + $this->assertEquals('~null', StringHelpers::inferEncoding('~null')); + $this->assertEquals('Øverbyvegen', StringHelpers::inferEncoding('Øverbyvegen')); + + $this->assertEquals('Øverbyvegen', StringHelpers::inferEncoding(base64_decode('w5h2ZXJieXZlZ2Vu'))); + $this->assertEquals('Øverbyvegen', StringHelpers::inferEncoding(base64_decode('2HZlcmJ5dmVnZW4='))); + + config(['app.charset' => 'Shift_JIS']); + $this->assertEquals('コンサート', StringHelpers::inferEncoding(base64_decode('g1KDk4NUgVuDZw=='))); + } +}