disp_name()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 2
nc 2
nop 1
1
<?php
2
3
/*
4
 * *****************************************************************************
5
 * Contributions to this work were made on behalf of the GÉANT project, a 
6
 * project that has received funding from the European Union’s Framework 
7
 * Programme 7 under Grant Agreements No. 238875 (GN3) and No. 605243 (GN3plus),
8
 * Horizon 2020 research and innovation programme under Grant Agreements No. 
9
 * 691567 (GN4-1) and No. 731122 (GN4-2).
10
 * On behalf of the aforementioned projects, GEANT Association is the sole owner
11
 * of the copyright in all material which was developed by a member of the GÉANT
12
 * project. GÉANT Vereniging (Association) is registered with the Chamber of 
13
 * Commerce in Amsterdam with registration number 40535155 and operates in the 
14
 * UK as a branch of GÉANT Vereniging.
15
 * 
16
 * Registered office: Hoekenrode 3, 1102BR Amsterdam, The Netherlands. 
17
 * UK branch address: City House, 126-130 Hills Road, Cambridge CB2 1PQ, UK
18
 *
19
 * License: see the web/copyright.inc.php file in the file structure or
20
 *          <base_url>/copyright.php after deploying the software
21
 */
22
23
require_once dirname(dirname(dirname(__FILE__)))."/config/_config.php";
24
25
$loggerInstance = new \core\common\Logging();
26
$validator = new \web\lib\common\InputValidation();
27
$languageInstance = new \core\common\Language();
28
$languageInstance->setTextDomain("diagnostics");
29
$jsonDir = dirname(dirname(dirname(__FILE__)))."/var/json_cache";
30
31
$additional_message = [
32
    \core\common\Entity::L_OK => '',
33
    \core\common\Entity::L_REMARK => _("Some properties of the connection attempt were sub-optimal; the list is below."),
34
    \core\common\Entity::L_WARN => _("Some properties of the connection attempt were sub-optimal; the list is below."),
35
    \core\common\Entity::L_ERROR => _("Some configuration errors were observed; the list is below."),
36
];
37
38
/**
39
 * returns the friendly name of an EAP type
40
 * 
41
 * @param array $eap array representation of the EAP type to be returned
42
 * @return string the friendly name
43
 */
44
function disp_name($eap)
45
{
46
    $displayName = \core\common\EAP::eapDisplayName($eap);
47
    return $displayName['OUTER'].($displayName['INNER'] != '' ? '-'.$displayName['INNER'] : '');
48
}
49
50
if (!isset($_REQUEST['test_type']) || !$_REQUEST['test_type']) {
51
    throw new Exception("No test type specified!");
52
}
53
54
const VALID_TEST_TYPES = ['udp_login', 'udp', 'capath', 'clients', 'openroamingcapath', 'openroamingclients'];
55
56
$test_type = 'INVALID'; // will throw Exception if not replaced with correct
57
foreach (VALID_TEST_TYPES as $index => $oneType) {
58
    if ($_REQUEST['test_type'] == $oneType) {
59
        $test_type = VALID_TEST_TYPES[$index]; // from constant -> definitely not user-tainted
60
    }
61
}
62
63
$check_realm = $validator->realm($_REQUEST['realm']);
64
// $test_type: udp / capath / clients
65
if ($check_realm === FALSE) {
66
    throw new Exception("Invalid realm was submitted!");
67
}
68
69
if (isset($_REQUEST['profile_id'])) {
70
    $my_profile = $validator->existingProfile($_REQUEST['profile_id']);
71
    if (!$my_profile instanceof \core\ProfileRADIUS) {
72
        throw new Exception("RADIUS Tests can only be performed on RADIUS Profiles (d'oh!)");
73
    }
74
    $testsuite = new \core\diag\RADIUSTests($check_realm, $my_profile->getRealmCheckOuterUsername(), $my_profile->getEapMethodsinOrderOfPreference(1), $my_profile->getCollapsedAttributes()['eap:server_name'], $my_profile->getCollapsedAttributes()['eap:ca_file']);
75
} else {
76
    $my_profile = NULL;
77
    if (isset($_REQUEST['outer_user'])) {
78
        $testsuite = new \core\diag\RADIUSTests($check_realm, $_REQUEST['outer_user'].'@'.$check_realm);
79
    } else {
80
        $testsuite = new \core\diag\RADIUSTests($check_realm, '@'.$check_realm);
81
    }
82
}
83
session_write_close();
84
85
$hostindex = $_REQUEST['hostindex'];
86
if (!is_numeric($hostindex)) {
87
    throw new Exception("The requested host index is not numeric!");    
88
}
89
90
$token = '';
91
if (isset($_REQUEST['token'])) {
92
    $token = htmlspecialchars(strip_tags(filter_input(INPUT_GET, 'token') ?? filter_input(INPUT_POST, 'token')));
93
}
94
95
$ssltest = -1;
96
if (isset($_REQUEST['ssltest'])) {
97
    $ssltest = filter_input(INPUT_GET, 'ssltest', FILTER_SANITIZE_NUMBER_INT) ?? filter_input(INPUT_POST, 'ssltest', FILTER_SANITIZE_NUMBER_INT);
98
}
99
100
$posted_host = $_REQUEST['src'];
101
if (is_numeric($posted_host)) { // UDP tests, this is an index to the test host in config
102
    $host = filter_var(\config\Diagnostics::RADIUSTESTS['UDP-hosts'][$hostindex]['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
103
    $expectedName = "IRRELEVANT-UDP";
104
} else { // dynamic discovery host, potentially unvetted user input
105
    // contains port number; needs to be redacted for filter_var to work
106
    // in any case, it's a printable string, so filter it initially
107
    $filteredHost = htmlspecialchars(strip_tags(filter_input(INPUT_GET, 'src') ?? filter_input(INPUT_POST, 'src')));
108
    $hostonly1 = preg_replace('/:[0-9]*$/', "", $filteredHost);
109
    $hostonly2 = preg_replace('/^\[/', "", $hostonly1);
110
    $hostonly3 = preg_replace('/\]$/', "", $hostonly2);
111
    $hostonly = filter_var($hostonly3, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
112
    // check if this is a valid IP address
113
    //if ($hostonly === FALSE) {
114
    //    throw new Exception("The configured test host is not a valid IP address from acceptable IP ranges!");
115
    //}
116
    // host IP address testing passed. So let's take our port number back
117
    $host = $filteredHost;
118
    $expectedName = htmlspecialchars(strip_tags(filter_input(INPUT_GET, 'expectedname') ?? filter_input(INPUT_POST, 'expectedname')));
119
}
120
if (is_null($expectedName)) {
121
    $expectedName = '';
122
}
123
$returnarray = [];
124
$timeout = \config\Diagnostics::RADIUSTESTS['UDP-hosts'][$hostindex]['timeout'];
125
$consortiumName = 'eduroam';
126
switch ($test_type) {
127
    case 'udp_login':
128
        $i = 0;
129
        $returnarray['hostindex'] = $hostindex;
130
        $eaps = $my_profile->getEapMethodsinOrderOfPreference(1);
131
        $user_name = $validator->syntaxConformUser(isset($_REQUEST['username']) && $_REQUEST['username'] ? $_REQUEST['username'] : "");
132
        $outer_user_name = $validator->syntaxConformUser(isset($_REQUEST['outer_username']) && $_REQUEST['outer_username'] ? $_REQUEST['outer_username'] : $user_name);
133
        $testsuite->setOuterIdentity($outer_user_name);
134
        $user_password = isset($_REQUEST['password']) && $_REQUEST['password'] ? $_REQUEST['password'] : ""; //!!
135
        $returnarray['result'] = [];
136
        foreach ($eaps as $eap) {
137
            if ($eap->getIntegerRep() == \core\common\EAP::INTEGER_TLS) {
138
                $run_test = TRUE;
139
                if ($_FILES['cert']['error'] == UPLOAD_ERR_OK) {
140
                    $clientcertdata = file_get_contents($_FILES['cert']['tmp_name']);
141
                    $privkey_pass = isset($_REQUEST['privkey_pass']) && $_REQUEST['privkey_pass'] ? $_REQUEST['privkey_pass'] : ""; //!!
142
                    if (isset($_REQUEST['tls_username']) && $_REQUEST['tls_username']) {
143
                        $tls_username = $validator->syntaxConformUser(htmlspecialchars(strip_tags(filter_input(INPUT_POST, 'tls_username'))));
144
                    } else {
145
                        if (openssl_pkcs12_read($clientcertdata, $certs, $privkey_pass)) {
146
                            $mydetails = openssl_x509_parse($certs['cert']);
147
                            if (isset($mydetails['subject']['CN']) && $mydetails['subject']['CN']) {
148
                                $tls_username = $mydetails['subject']['CN'];
149
                                $loggerInstance->debug(4, "PKCS12-CN=$tls_username\n");
150
                            } else {
151
                                $testresult = \core\diag\RADIUSTests::RETVAL_INCOMPLETE_DATA;
152
                                $run_test = FALSE;
153
                            }
154
                        } else {
155
                            $testresult = \core\diag\RADIUSTests::RETVAL_WRONG_PKCS12_PASSWORD;
156
                            $run_test = FALSE;
157
                        }
158
                    }
159
                } else {
160
                    $testresult = \core\diag\RADIUSTests::RETVAL_INCOMPLETE_DATA;
161
                    $run_test = FALSE;
162
                }
163
                if ($run_test) {
164
                    $loggerInstance->debug(4, "TLS-USERNAME=$tls_username\n");
165
                    $testresult = $testsuite->udpLogin($hostindex, $eap->getArrayRep(), $tls_username, $privkey_pass, TRUE, TRUE, $clientcertdata);
166
                }
167
            } else {
168
                $testresult = $testsuite->udpLogin($hostindex, $eap->getArrayRep(), $user_name, $user_password);
169
            }
170
            $returnarray['result'][$i] = $testsuite->consolidateUdpResult($hostindex);
171
            $returnarray['result'][$i]['eap'] = $eap->getPrintableRep();
172
            $returnarray['returncode'][$i] = $testresult;
173
174
175
            switch ($testresult) {
176
                case \core\diag\RADIUSTests::RETVAL_OK:
177
                    $level = $returnarray['result'][$i]['level'];
178
                    switch ($level) {
179
                        case \core\common\Entity::L_OK:
180
                            $message = _("<strong>Test successful.</strong>");
181
                            break;
182
                        case \core\common\Entity::L_REMARK:
183
                        case \core\common\Entity::L_WARN:
184
                            $message = _("<strong>Test partially successful</strong>: authentication succeeded.").' '.$additional_message[$level];
185
                            break;
186
                        case \core\common\Entity::L_ERROR:
187
                            $message = _("<strong>Test FAILED</strong>: authentication succeeded.").' '.$additional_message[$level];
188
                            break;
189
                    }
190
                    break;
191
                case \core\diag\RADIUSTests::RETVAL_CONVERSATION_REJECT:
192
                    $message = _("<strong>Test FAILED</strong>: the request was rejected. The most likely cause is that you have misspelt the Username and/or the Password.");
193
                    $level = \core\common\Entity::L_ERROR;
194
                    break;
195
                case \core\diag\RADIUSTests::RETVAL_NOTCONFIGURED:
196
                    $level = \core\common\Entity::L_ERROR;
197
                    $message = _("This method cannot be tested");
198
                    break;
199
                case \core\diag\RADIUSTests::RETVAL_IMMEDIATE_REJECT:
200
                    $level = \core\common\Entity::L_ERROR;
201
                    $message = _("<strong>Test FAILED</strong>: the request was rejected immediately, without EAP conversation. Either you have misspelt the Username or there is something seriously wrong with your server.");
202
                    unset($returnarray['result'][$i]['cert_oddities']);
203
                    $returnarray['result'][$i]['server'] = 0;
204
                    break;
205
                case \core\diag\RADIUSTests::RETVAL_NO_RESPONSE:
206
                    $level = \core\common\Entity::L_ERROR;
207
                    $message = sprintf(_("<strong>Test FAILED</strong>: no reply from the RADIUS server after %d seconds. Either the responsible server is down, or routing is broken!"), $timeout);
208
                    unset($returnarray['result'][$i]['cert_oddities']);
209
                    $returnarray['result'][$i]['server'] = 0;
210
                    break;
211
                case \core\diag\RADIUSTests::RETVAL_SERVER_UNFINISHED_COMM:
212
                    $returnarray['message'] = sprintf(_("<strong>Test FAILED</strong>: there was a bidirectional RADIUS conversation, but it did not finish after %d seconds!"), $timeout);
213
                    $returnarray['level'] = \core\common\Entity::L_ERROR;
214
                    break;
215
                default:
216
                    $level = isset($testsuite->returnCodes[$testresult]['severity']) ? $testsuite->returnCodes[$testresult]['severity'] : \core\common\Entity::L_ERROR;
217
                    $message = isset($testsuite->returnCodes[$testresult]['message']) ? $testsuite->returnCodes[$testresult]['message'] : _("<strong>Test FAILED</strong>");
218
                    $returnarray['result'][$i]['server'] = 0;
219
                    break;
220
            }
221
            $returnarray['result'][$i]['level'] = $level;
222
            $returnarray['result'][$i]['message'] = $message;
223
            $i++;
224
        }
225
        break;
226
    case 'udp':
227
        $i = 0;
228
        $returnarray['hostindex'] = $hostindex;
229
        $testresult = $testsuite->udpReachability($hostindex);
230
        $returnarray['result'][$i] = $testsuite->consolidateUdpResult($hostindex);
231
        $returnarray['result'][$i]['eap'] = 'ALL';
232
        $returnarray['returncode'][$i] = $testresult;
233
        // a failed check may not have got any certificate, be prepared for that
234
        switch ($testresult) {
235
            case \core\diag\RADIUSTests::RETVAL_CONVERSATION_REJECT:
236
                $level = $returnarray['result'][$i]['level'];
237
                if ($level > \core\common\Entity::L_OK) {
238
                    $message = _("<strong>Test partially successful</strong>: a bidirectional RADIUS conversation with multiple round-trips was carried out, and ended in an Access-Reject as planned.").' '.$additional_message[$level];
239
                } else {
240
                    $message = _("<strong>Test successful</strong>: a bidirectional RADIUS conversation with multiple round-trips was carried out, and ended in an Access-Reject as planned.");
241
                }
242
                break;
243
            case \core\diag\RADIUSTests::RETVAL_IMMEDIATE_REJECT:
244
                $message = _("<strong>Test FAILED</strong>: the request was rejected immediately, without EAP conversation. This is not necessarily an error: if the RADIUS server enforces that outer identities correspond to an existing username, then this result is expected (Note: you could configure a valid outer identity in your profile settings to get past this hurdle). In all other cases, the server appears misconfigured or it is unreachable.");
245
                $level = \core\common\Entity::L_WARN;
246
                break;
247
            case \core\diag\RADIUSTests::RETVAL_NO_RESPONSE:
248
                $returnarray['result'][$i]['server'] = 0;
249
                $message = sprintf(_("<strong>Test FAILED</strong>: no reply from the RADIUS server after %d seconds. Either the responsible server is down, or routing is broken!"), $timeout);
250
                $level = \core\common\Entity::L_ERROR;
251
                break;
252
            case \core\diag\RADIUSTests::RETVAL_SERVER_UNFINISHED_COMM:
253
                $message = sprintf(_("<strong>Test FAILED</strong>: there was a bidirectional RADIUS conversation, but it did not finish after %d seconds!"), $timeout);
254
                $level = \core\common\Entity::L_ERROR;
255
                break;
256
            default:
257
                $message = _("unhandled error");
258
                $level = \core\common\Entity::L_ERROR;
259
                break;
260
        }
261
        $loggerInstance->debug(4, "SERVER=".$returnarray['result'][$i]['server']."\n");
262
        $returnarray['result'][$i]['level'] = $level;
263
        $returnarray['result'][$i]['message'] = $message;
264
        break;
265
    case 'openroamingcapath':
266
        $consortiumName = 'openroaming';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
267
    case 'capath':
268
        $rfc6614suite = new \core\diag\RFC6614Tests([$host], $expectedName, $consortiumName);
269
        $returnarray['IP'] = $host;
270
        $returnarray['hostindex'] = $hostindex;
271
        $returnarray['consortium'] = $consortiumName;
272
        $returnarray['name'] = $expectedName;
273
        if ($ssltest) {
274
            $testresult = $rfc6614suite->cApathCheck($host);
275
            if ($testresult == \core\diag\RADIUSTests::RETVAL_INVALID) {
276
                $returnarray['result'] = $testresult;
277
                $returnarray['level'] = \core\common\Entity::L_ERROR;
278
                break;
279
            }
280
        } else {
281
            $testresult = \core\diag\RADIUSTests::RETVAL_SKIPPED;
282
            $returnarray['message'] = _("<strong>ERROR</strong>: connectivity problem!");
283
            $returnarray['level'] = \core\common\Entity::L_WARN;
284
        }
285
        
286
        // the host member of the array may not be set if RETVAL_SKIPPED was
287
        // returned (e.g. IPv6 host), be prepared for that
288
        if (!isset($rfc6614suite->TLS_CA_checks_result[$host])) {
289
            $returnarray['result'] = $testresult;
290
            break;
291
        }
292
        // we tried to contact someone, and know how long that took
293
        $returnarray['time_millisec'] = sprintf("%d", $rfc6614suite->TLS_CA_checks_result[$host]['time_millisec']);
294
        $timeDisplay = ' ('.sprintf(_("elapsed time: %d"), $rfc6614suite->TLS_CA_checks_result[$host]['time_millisec']).'&nbsp;ms)';
295
        if (isset($rfc6614suite->TLS_CA_checks_result[$host]['cert_oddity']) && ($rfc6614suite->TLS_CA_checks_result[$host]['cert_oddity'] == \core\diag\RADIUSTests::CERTPROB_UNKNOWN_CA)) {
296
            $returnarray['message'] = _("<strong>ERROR</strong>: the server presented a certificate which is from an unknown authority!").$timeDisplay;
297
            $returnarray['level'] = \core\common\Entity::L_ERROR;
298
            $returnarray['result'] = $testresult;
299
            break;
300
        }
301
        // probably all is well, but we override this default later if need be
302
        $returnarray['message'] = $rfc6614suite->returnCodes[$rfc6614suite->TLS_CA_checks_result[$host]['status']]["message"];
303
        $returnarray['level'] = \core\common\Entity::L_OK;
304
        // override if the connection was with a mismatching server name
305
        if (isset($rfc6614suite->TLS_CA_checks_result[$host]['cert_oddity']) && ($rfc6614suite->TLS_CA_checks_result[$host]['cert_oddity'] == \core\diag\RADIUSTests::CERTPROB_DYN_SERVER_NAME_MISMATCH)) {
306
            $returnarray['message'] = _("<strong>WARNING</strong>: the server name as discovered in the SRV record does not match any name in the server certificate!").$timeDisplay;
307
            $returnarray['level'] = \core\common\Entity::L_WARN;
308
        }
309
        switch ($rfc6614suite->TLS_CA_checks_result[$host]['status']) {
310
            case \core\diag\RADIUSTests::RETVAL_CONNECTION_REFUSED:
311
                $returnarray['level'] = \core\common\Entity::L_ERROR;
312
                break;
313
            case \core\diag\RADIUSTests::RETVAL_SERVER_UNFINISHED_COMM:
314
                $returnarray['level'] = \core\common\Entity::L_ERROR;
315
                break;
316
            case \core\diag\RADIUSTests::RETVAL_OK:
317
            $returnarray['certdata'] = [];
318
            $returnarray['certdata']['subject'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['subject'];
319
            $returnarray['certdata']['issuer'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['issuer'];
320
            $returnarray['certdata']['validTo'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['validTo'];
321
            $returnarray['certdata']['extensions'] = [];
322
            if (isset($rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'])) {
323
                $returnarray['certdata']['extensions']['subjectaltname'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'];
324
            }
325
            if (isset($rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['policyoid'])) {
326
                $returnarray['certdata']['extensions']['policies'] = join(' ', $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['policyoid']);
327
            }
328
            if (isset($rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['crlDistributionPoint'])) {
329
                $returnarray['certdata']['extensions']['crldistributionpoints'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['crlDistributionPoint'];
330
            }
331
            if (isset($rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['authorityInfoAccess'])) {
332
                $returnarray['certdata']['extensions']['authorityinfoaccess'] = $rfc6614suite->TLS_CA_checks_result[$host]['certdata']['extensions']['authorityInfoAccess'];
333
            }
334
            break;
335
            default:
336
                $returnarray['message'] .= $timeDisplay;
337
        }
338
        
339
        $returnarray['cert_oddities'] = [];
340
        $returnarray['result'] = $testresult;
341
        break;
342
    case 'openroamingclient':
343
        $consortiumName = 'openroaming';
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
344
    case 'clients':
345
        $rfc6614suite = new \core\diag\RFC6614Tests([$host], $expectedName, $consortiumName);
346
        if ($ssltest) {
347
            $testresult = $rfc6614suite->tlsClientSideCheck($host, $expectedName, $check_realm);
348
        } else {
349
            $testresult = \core\diag\RADIUSTests::RETVAL_SKIPPED;
350
            $returnarray['message'] = _("<strong>ERROR</strong>: connectivity problem!");
351
            $returnarray['level'] = \core\common\Entity::L_WARN;
352
        }
353
        $returnarray['IP'] = $host;
354
        $returnarray['hostindex'] = $hostindex;
355
        $returnarray['name'] = $expectedName;
356
        $returnarray['consortium'] = $consortiumName;
357
        $k = 0;
358
        // the host member of the array may not exist if RETVAL_SKIPPED came out
359
        // (e.g. no client cert to test with). Be prepared for that
360
        if (isset($rfc6614suite->TLS_clients_checks_result[$host])) {
361
            foreach ($rfc6614suite->TLS_clients_checks_result[$host]['ca'] as $type => $cli) {
362
                foreach ($cli as $key => $val) {
363
                    $returnarray['ca'][$k][$key] = $val;
364
                }
365
                $k++;
366
            }
367
        }
368
        $returnarray['result'] = $testresult;
369
        break;
370
    default:
371
        throw new Exception("Unknown test requested: default case reached!");
372
}
373
$returnarray['datetime'] = date("Y-m-d H:i:s");
374
if ($token!= '' && is_dir($jsonDir.'/'.$token)) {
375
    @mkdir($jsonDir.'/'.$token, 0777, true);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

375
    /** @scrutinizer ignore-unhandled */ @mkdir($jsonDir.'/'.$token, 0777, true);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
376
}
377
$json_data = json_encode($returnarray);
378
if ($token != '') {
379
    file_put_contents($jsonDir.'/'.$token.'/'.$test_type.'_'.$hostindex, $json_data);
380
}
381
header("Content-type: application/json; utf-8");
382
echo($json_data);
383