/* * Copyright (C) 2013 Intel Corporation. * * Author: Christophe Guiraud, * Jussi Kukkonen * * This file is part of Rygel. * * Rygel is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Rygel is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ using GLib; internal class Rygel.BasicManagementTestNSLookup : BasicManagementTest { private const string HEADER = "\n" + "\n"; private const string FOOTER = "\n"; private enum ProcessState { INIT, SERVER, NAME, } private enum GenericStatus { SUCCESS, ERROR_DNS_SERVER_NOT_RESOLVED, ERROR_INTERNAL, ERROR_OTHER; public string to_string () { switch (this) { case SUCCESS: return "Success"; case ERROR_DNS_SERVER_NOT_RESOLVED: return "Error_DNSServerNotResolved"; case ERROR_INTERNAL: return "Error_Internal"; case ERROR_OTHER: return "Error_Other"; default: assert_not_reached (); } } } private enum ResultStatus { SUCCESS, ERROR_DNS_SERVER_NOT_AVAILABLE, ERROR_HOSTNAME_NOT_RESOLVED, ERROR_TIMEOUT, ERROR_OTHER; public string to_string () { switch (this) { case SUCCESS: return "Success"; case ERROR_DNS_SERVER_NOT_AVAILABLE: return "Error_DNSServerNotAvailable"; case ERROR_HOSTNAME_NOT_RESOLVED: return "Error_HostNameNotResolved"; case ERROR_TIMEOUT: return "Error_Timeout"; case ERROR_OTHER: return "Error_Other"; default: assert_not_reached (); } } } private enum AnswerType { NONE, AUTHORITATIVE, NON_AUTHORITATIVE; public string to_string () { switch (this) { case NONE: return "None"; case AUTHORITATIVE: return "Authoritative"; case NON_AUTHORITATIVE: return "NonAuthoritative"; default: assert_not_reached (); } } } private const uint MAX_REPETITIONS = 100; private const uint DEFAULT_REPETITIONS = 1; private const uint MIN_INTERVAL_TIMEOUT = 1000; private const uint MAX_INTERVAL_TIMEOUT = 30000; private const uint DEFAULT_INTERVAL_TIMEOUT = 1000; private struct Result { public ProcessState state; public string name_server_address; public string returned_host_name; public string[] addresses; public ResultStatus status; public AnswerType answer_type; uint execution_time; private string get_addresses_csv () { var builder = new StringBuilder (""); foreach (var address in this.addresses) { if (builder.len != 0) { builder.append (","); } builder.append (address); } return builder.str; } public string to_xml_fragment () { return ("\n" + "%s\n" + "%s\n" + "%s\n" + "%s\n" + "%s\n" + "%u\n" + "\n").printf (this.status.to_string (), this.answer_type.to_string (), this.returned_host_name, this.get_addresses_csv (), this.name_server_address, this.execution_time); } } public string host_name { construct; private get; default = ""; } public string? name_server { construct; private get; default = null; } private uint _interval_time_out = DEFAULT_INTERVAL_TIMEOUT; public uint interval_time_out { construct { this._interval_time_out = value; if (this._interval_time_out == 0) this._interval_time_out = DEFAULT_INTERVAL_TIMEOUT; } private get { return this._interval_time_out; } } private uint _repetitions = DEFAULT_REPETITIONS; public uint repetitions { construct { this.iterations = 1; this._repetitions = value; if (this._repetitions == 0) { this._repetitions = DEFAULT_REPETITIONS; } } private get { return this._repetitions; } } private Result[] results; private GenericStatus generic_status; private string additional_info; private Timer timer = new Timer (); public override string method_type { get { return "NSLookup"; } } public override string results_type { get { return "GetNSLookupResult"; } } public BasicManagementTestNSLookup (string host_name, string? name_server, uint repetitions, uint32 interval_time_out) { Object (host_name: host_name, name_server: name_server, repetitions: repetitions, interval_time_out: interval_time_out); } public override void constructed () { base.constructed (); this.generic_status = GenericStatus.ERROR_INTERNAL; this.additional_info = ""; this.results = {}; this.command = { "nslookup", "-timeout=%u".printf (this.interval_time_out/1000), "-retry=%u".printf (this.repetitions), host_name }; if (name_server != null && name_server.length > 0) { this.command += name_server; } /* Fail early if internal parameter limits are violated */ if (this.repetitions > MAX_REPETITIONS) { init_state = InitState.INVALID_PARAMETER; var msg = "NumberOfRepetitions %u is not in allowed range [0, %u]"; this.additional_info = msg.printf (this.repetitions, MAX_REPETITIONS); } else if (this.interval_time_out < MIN_INTERVAL_TIMEOUT || this.interval_time_out > MAX_INTERVAL_TIMEOUT) { init_state = InitState.INVALID_PARAMETER; var msg = "Timeout %u is not in allowed range [%u, %u]"; this.additional_info = msg.printf (this.interval_time_out, MIN_INTERVAL_TIMEOUT, MAX_INTERVAL_TIMEOUT); } } protected override void init_iteration () { base.init_iteration (); var result = Result () { state = ProcessState.INIT, name_server_address = "", returned_host_name = "", addresses = {}, status = ResultStatus.ERROR_OTHER, answer_type = AnswerType.NONE, execution_time = 0 }; this.results += result; this.timer.start (); } protected override bool finish_iteration () { switch (this.init_state) { case InitState.SPAWN_FAILED: /* quitting early */ this.generic_status = GenericStatus.ERROR_INTERNAL; this.additional_info = "Failed to spawn nslookup"; this.results[results.length - 1].status = ResultStatus.ERROR_OTHER; break; case InitState.INVALID_PARAMETER: /* quitting early */ /* constructed () has set info already */ this.generic_status = GenericStatus.ERROR_OTHER; this.results[results.length - 1].status = ResultStatus.ERROR_OTHER; break; default: var elapsed_msec = this.timer.elapsed (null) * 1000; var exec_time = (uint)Math.round (elapsed_msec); this.results[results.length - 1].execution_time = exec_time; break; } return base.finish_iteration (); } protected override void handle_error (string line) { unowned Result* result = &this.results[results.length - 1]; if (line.contains ("couldn't get address for")) { this.generic_status = GenericStatus.ERROR_DNS_SERVER_NOT_RESOLVED; this.execution_state = ExecutionState.COMPLETED; result.status = ResultStatus.ERROR_DNS_SERVER_NOT_AVAILABLE; } } protected override void handle_output (string line) { unowned Result* result = &this.results[results.length - 1]; line.strip (); if (line.has_prefix ("Server:")) { if (result.state != ProcessState.INIT) { debug ("nslookup parser: Unexpected 'Server:' line.\n"); } result.state = ProcessState.SERVER; } else if (line.has_prefix ("Name:")) { if (result.state == ProcessState.INIT) { debug ("nslookup parser: Unexpected 'Name:' line"); } else if (result.state == ProcessState.SERVER) { var name = line.substring ("Name:".length).strip (); result.returned_host_name = name; } result.state = ProcessState.NAME; } else if (line.has_prefix ("Address:")) { if (result.state == ProcessState.SERVER) { var address = line.substring ("Address:".length).strip (); result.name_server_address = address.split ("#", 2)[0]; this.generic_status = GenericStatus.SUCCESS; } else if (result.state == ProcessState.NAME) { result.addresses += line.substring ("Address:".length).strip (); result.status = ResultStatus.SUCCESS; if (result.answer_type == AnswerType.NONE) { result.answer_type = AnswerType.AUTHORITATIVE; } } else { debug ("nslookup parser: Unexpected 'Address:' line"); } } else if (line.has_prefix ("Non-authoritative answer:")) { result.answer_type = AnswerType.NON_AUTHORITATIVE; } else if (line.contains ("server can't find")) { result.status = ResultStatus.ERROR_HOSTNAME_NOT_RESOLVED; } else if (line.contains ("couldn't get address for")) { this.generic_status = GenericStatus.ERROR_DNS_SERVER_NOT_RESOLVED; result.status = ResultStatus.ERROR_DNS_SERVER_NOT_AVAILABLE; this.execution_state = ExecutionState.COMPLETED; } else if (line.contains ("no servers could be reached")) { result.status = ResultStatus.ERROR_DNS_SERVER_NOT_AVAILABLE; } } public void get_results (out string status, out string additional_info, out uint success_count, out string result_string) { success_count = 0; StringBuilder builder = new StringBuilder (HEADER); foreach (var result in this.results) { builder.append (result.to_xml_fragment ()); if (result.status == ResultStatus.SUCCESS) { success_count++; } } builder.append (FOOTER); result_string = builder.str; status = this.generic_status.to_string (); additional_info = this.additional_info; } }