Issues

ZF-2900: Issue on Zend_Validate_Hostname

Description

Exception

fopen(Zend/Validate/Hostname/Net.php): failed to open stream: No such file or directory on Loader.php, Line 160

0 /www/security.playxp.com/includes/Zend/Loader.php(160): Handler::Error(2, 'fopen(Zend/Vali...', '/www/security.p...', 160, Array)

1 /www/security.playxp.com/includes/Zend/Validate/Hostname.php(327): Zend_Loader::isReadable('Zend/Validate/H...', 'r', true)

2 /www/security.playxp.com/includes/Zend/Uri/Http.php(354): Zend_Validate_Hostname->isValid('Zend/Validate/H...')

3 /www/security.playxp.com/includes/Zend/Uri/Http.php(185): Zend_Uri_Http->validateHost('openid.daum.net')

4 /www/security.playxp.com/includes/Zend/Uri/Http.php(96): Zend_Uri_Http->valid()

5 /www/security.playxp.com/includes/Zend/Uri.php(117): Zend_Uri_Http->__construct()

6 /www/security.playxp.com/includes/Zend/Http/Client.php(223): Zend_Uri::factory('http', '//openid.daum.n...')

7 /www/security.playxp.com/includes/Zend/Http/Client.php(209): Zend_Http_Client->setUri('http://openid.d...')

8 /www/security.playxp.com/includes/Zend/OpenId/Consumer.php(406): Zend_Http_Client->__construct('http://openid.d...')

9 /www/security.playxp.com/includes/Zend/OpenId/Consumer.php(607): Zend_OpenId_Consumer->_httpRequest('http://openid.d...', Array)

10 /www/security.playxp.com/includes/Zend/OpenId/Consumer.php(694): Zend_OpenId_Consumer->_discovery('http://openid.d...')

11 /www/security.playxp.com/includes/Zend/OpenId/Consumer.php(131): Zend_OpenId_Consumer->_checkId('http://openid.d...', NULL, NULL)

12 /www/security.playxp.com/htdocs/login.php(23): Zend_OpenId_Consumer->login(false, 'openid.daum.net...', 'http://security...', 'http://*.playxp...', NULL, NULL)

13 /www/security.playxp.com/htdocs/processor.php(18): require('/www/security.p...')

14 {main}

Frontend(login.php) source-code are below...

$consumer = new Zend_OpenId_Consumer(); if ($consumer->login($strIdentifier, 'http://security.playxp.com/openid', 'http://*.playxp.com') === false) {  $status = "OpenID login failed.
"; }

include_path = .:/www/security.playxp.com/includes/:/www/security.playxp.com/templates/

The Zend is installed on /www/security.playxp.com/includes/Zend. Tested $strIdentifier are "openid.daum.net/nios" and "nios.myid.net".

Comments

I think there is problem in isValid($value) function in Zend/Validate/Hostname.php .

If you use a top level domain name that are not in /Zend/Validate/Hostname directory but exists in $_validTlds array , this problem will happen.

I think default php file should exists in Zend/Validate/Hostname directory.

The default php file should work if there is not php file to match the top level domain name.

Can someone please confirm whether this is an active problem or not? The linked case ZF-4110 which appears to be the same issue is marked as closed.

Line 327 in Validate/Hostname.php is supposed to use Zend_Loader::isReadable() to ensure the class file exists before loading it. This report appears to indicate this isn't working and its trying to load the class file anyway. Which obviously isn't good!

I added a test locally to replicate how Zend_Uri_Http::validateHost() accesses Validate_Hostname and I couldn't replicate this issue at present

As a note it may be worth wrapping Zend_Loader::loadClass on line 331 of Validate/Hostname.php within a try/catch to capture this kind of error. If anyone thinks this is a good idea I can do so.

I meant the linked case ZF-4410

Hello, Simon. I do not mean this issue is fixed.

Thanks Satoru, I see now case ZF-4410 was closed since it is a duplicate. I'll try to replicate this using your code example.

Solved in SVN r12211. If the file is not found in Zend/Validate/Hostname directory, Zend/Validate/Hostname.php does not load additional characters.

Thanks for solving this Satoru,

Firstly, forgive my possible misunderstanding but I couldn't replicate this issue and I don't understand how enclosing Zend_Loader::isReadable() in a try/catch statement solves this.

From what I can see Zend_Loader::isReadable() doesn't throw any exceptions, it supresses any PHP errors in fopen() and returns a boolean which states whether the file can be opened with fopen() or not. It sounds as if this wasn't working for you and an exception was thrown by Zend_Loader. Is this the case? If so, I wonder if this affects other parts of ZF. I'd suggest we look to fix this in Zend_Loader::isReadable() if that is where the error lies.

Finally, would it be wise to add a unit test to HostnameTest.php to check that this issue is solved for any future changes?

best wishes, Simon

Hello, Simon.

Thank you. I forgot fopen() happens no exception but warning.

I propose SVN r12214 that I change Zend_Loader::isReadable() to file_exists() in it.

About unit test, we should prepare test against Zend/Validate/Hostname/Xxxx.php in HostnameTest.php.

Best Regards, Satoru

Firstly, I'll re-open this issue at present since it seems as if there is some uncertainty as to whether this is fixed or where the problem lies. Feel free to re-close if you think its right to do so :-)

I'm happy to look into this over the weekend since I wrote the hostname validator so I should be able to fix this (I hope!). I can also see other related/duplicated issues so I can look into them for further testing.

Anyway..

Thanks for the response Satoru, that's what I thought and since the warning is being supressed with @ the function simply returns false on an error.

I believe fopen() is used because it also searches on the include path. file_exists() does not, so this functionality would need to be replicated if this was changed.

However, I have struggled a little to replicate this issue so it's a little difficult to suggest how to fix it.

From my tests Zend_Loader::isReadable() doesn't raise an exception and works as expected. I haven't tried your code exactly yet (I know I should) but if I trace the code in Zend_OpenID_Consumer it calls Zend_Uri_Http which calls Zend_Validate_Hostname with the ALLOW_ALL option.

if I add test code to test the two domains you mention then these pass the unit test and do not report an error. For example:


    public function testFromZendUriHttp ()
    {
        $valuesExpected = array(
            array(Zend_Validate_Hostname::ALLOW_ALL, true, array('openid.daum.net', 'nios.myid.net'))
        );
        foreach ($valuesExpected as $element) {
            $validator = new Zend_Validate_Hostname($element[0]);
            foreach ($element[2] as $input) {
                $this->assertEquals($element[1], $validator->isValid($input), implode("\n", $validator->getMessages()));
            }
        }
    }

I'll try to get your test code working, but I'll have to change it to use a public OpenID provider such as www.chi.mp since I obviously don't have an acount at playxp.com

best wishes, Simon

Satoru,

Can you please confirm whether you have custom error handling in your test script. If so, I think I know the source of your problem.

If not, can you post your complete test script since I've not been able to replicate this issue.

If custom error handling is used then all errors, even those suppressed with an @, are sent to your custom error handler. Since Zend_Loader uses fopen with errors suppressed to check whether a file exists, this is most likely causing the issue in your script. I assume you have custom error handling since Zend_Loader doesn't throw an exception on that line, just a suppressed PHP error.

The PHP manual states you should check the value of error_reporting(), if this is zero then the error was suppressed with @ and you should take appropriate action (i.e. ignore it).

From the manual -

{quote} ..you are still able to read the current value of error_reporting and act appropriately. Of particular note is that this value will be 0 if the statement that caused the error was prepended by the @ error-control operator

http://uk3.php.net/set_error_handler {quote}

The @ error supression operator appears to be widely used throughout Zend Framework so I don't recommend the Zend_Loader functionality is changed.

The solution is to supress error code zero (0) errors from any custom error handling. It may also be wise to add a note in the documentation or a Submit a Bug FAQ page (if there is one) about this since it seems to be tripping people up.

As far as I can see this affects the following issues: * ZF-3020 * ZF-3829 * ZF-3989 * ZF-4267 * ZF-4410

I can close all these off if people are happy this is the explanation!

Finally, I recommend the changes made to Zend/Validate/Hostname.php in revision 12211 are undone since there the code that was changed seems to be unnecessary since the bug is due to the use of custom error handling, not Zend_Hostname or Zend_Loader

best wishes, Simon

Hi, Simon. Thank you for your research and explanation.

I wondered why fopen() with at mark happens in ver. 1.6.1 that is used with ZF-4410. Because I did not find any warning in my tests with ver.1.6.1 and 1.6.2

I understand what causes the warning.

I agree that SVN r12211 is unnecessary and I just change back the Zend/Validate/Hostname.php to same as SVN r10523 .

If you want, you can close this issue.

Best Wishes Satoru Yoshida.

Hi, Simon. I just closed the duplicated issues.

And ZF-2985. Because I think ZF-2985 also duplicates this issue.

Hi, Simon. I wrote some note in phpDoc of Zend_Loader::isReadable in SVN r12399.

I hope if users meet warning message, they could find the note.

Changing issues in preparation for the 1.7.0 release.

I am still seeing this issue in version 1.7.2. Another reporter has a duplicate open reporting it for 1.7.5. It crops up in many places where hostname validation is performed, e.g. Zend_Validate_EmailAddress, Zend_XmlRpc_Client, ...

To be clear the issue is that warnings are being generated from the library code and suppressing error display is the wrong solution to fixing this.

Instead loading a non-existent class should be prevented in the first place. The same for sending a hostname (instead of an IP) to inet_pton etc.

We have a zero-notice policy and this throws many false positives e.g. in our unit tests. So currently we have to use a custom solution to validate emails, send xmlrpc, use Gdata etc.

Sample code: <?php error_reporting(E_ALL ^ E_STRICT ^ E_NOTICE); ini_set('display_errors', 1); $service = new Zend_XmlRpc_Client('http://www.sikids.com/services/xmlrpc'); $service->call('methodDoesNotExist',array()); ?>

Zend/Validate/Ip.php (Line 62): [E_WARNING(2)] inet_pton() [function.inet-pton]: Unrecognized address www.sikids.com Zend/Loader.php (Line 165): [E_WARNING(2)] fopen(Zend/Validate/Hostname/Com.php) [function.fopen]: failed to open stream: No such file or directory [filename] => Zend/Validate/Hostname/Com.php

Please note that COM is supported with 1.8 and not with 1.7.

It looks like Thomas has done some good work to remove the dependency on loading class files that may not exist in the Validate/Hostname/ folder in the 1.8 version of Zend_Hostname.

When I run the sample code in 1.8.1 I get the expected error:

Zend_XmlRpc_Client_FaultException making GET request to / -32601 Server error. Requested method methodDoesNotExist not specified.

I suggest this issue (and any duplicates) are closed marked as fixed in 1.8

Closing issue with corrected fix version.

Regarding #ZF-2985, I've found that using fopen with error suppression is slower than checking if file exists in include_path. I'm using a custom error handler and know how to suppress the error, but I want to log all errors, including suppressed, and I don't want other other errors than my own to appear in it.

Since the current method of checking whether a file exists in include_path (Zend_Loader::isReadable()) is both slower and generates unnecessary errors in my full log, could someone please take a look at my comments in #ZF-2985, where I describe how to fix this? http://framework.zend.com/issues/browse/…

I'm posting here since no-one replied in the other issue, which is marked as duplicate of this (and I can't find how to re-open it).


public static function isReadable($filename)
{
    if ($filename === null || $filename === false || $filename === '' || !is_scalar($filename)) return false;
    if (is_readable($filename)) return true;
    foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
    {
        if (is_readable($path . (in_array(substr($path, -1), array('\\', '/')) ? $filename : DIRECTORY_SEPARATOR . $filename))) return true;
    }
    return false;
}

I hate it when I post something and just when I read my own comment I discover something that could be improved.. ;-)

I only changed the first if statement. Input validation is simpler than what I did previously: it must be string, but not empty string. Now it's also more accurate compared to is_readable-behaviour for non-string input.


public static function isReadable($filename)
{
    if (($filename = (string)$filename) === '') return false;
    if (is_readable($filename)) return true;
    foreach (explode(PATH_SEPARATOR, get_include_path()) as $path)
    {
        if (is_readable($path . (in_array(substr($path, -1), array('\\', '/')) ? $filename : DIRECTORY_SEPARATOR . $filename))) return true;
    }
    return false;
}

Zend_Validate_Hostname does not call Zend_Loader, fopen or isReadable. Non of the methods you mentioned is used within this validator.

Can you clearify what your comments have to do with Zend_Validate_Hostname ?

Nothing that I know of, but #ZF-2985 is for some reason marked as duplicate of this issue, that's why I'm posting here.

Please read the issue and it's description.

You may note that this issue describes a problem of Zend_Validate_Hostname, which has been fixed and does not occur anymore. Even the codelines you refer to do no longer exist. I can't integrate a isReadable method within Zend_Validate_Hostname as it would not solve any problems.

Please write eigther to the correct issue or, when you don't find one, write a new issue.

I get all that, but my problem is that the issue I should be posting to (2985) is marked as duplicate of this one. I don't have the rights to un-duplicate it or even re-open it. I could create a new one, but that would actually be a duplicate of 2985, which again leads here. Or perhaps I could try #ZF-3249, which "depends" on 2985. sigh What a mess. Fine, I'll just post a new bug report and hope that noone will bash me for it.

Oh, and the codelines I wrote above are not references to existing code, they were my suggested fix. Not for Zend_Validate_Hostname, but for Zend_Loader. Sorry if that wasn't clear.

In any case, could someone un-duplicate #ZF-2985? Satoru Yoshida apparently seemed to think that it was a duplicate of this, but I guess not.

Reopened ZF-2900...

I don't think re-opening this issue is relevant, at least for me it's not. I posted a new bug report for my issue: http://framework.zend.com/issues/browse/ZF-6833.