Writing Apache Modules with Perl and C by Lincoln Stein & Doug MacEachern Unconfirmed error reports are from readers. They have not yet been approved or disproved by the author or editor and represent solely the opinion of the reader. This page was updated March 20, 2003. Here's a key to the markup: [page-number]: serious technical mistake {page-number}: minor technical mistake : important language/formatting problem (page-number): language change or minor formatting problem ?page-number?: reader question or request for clarification UNCONFIRMED errors and comments from readers: [23] 5th paragraph: This is a serious error (I think!). For poeple wanting to install Apache with mod_perl into a specified final destination, you use the APACHE_PREFIX option. This forces the final 'make install' command to automatically copy the final httpd executeable (with embedded perl interpreter) and it's support files to the specified destination. The only problem is that this switch only works correctly if specified with the USE_APACI=1 option enabled. This option is omitted from page 23,24,25 but Appendix B page 645 states (correctly) that you need the USE_APACI option for APACHE_PREFIX to work. [26] Win32Installation; I assume this book is a little old now, but I just purchased it. Anyway, on CPAN under author Jeffrey_Baker there is no Win32 distribution file for mod_perl? I searched hi and low, and it does not exist. I cannot load the mod_perl from page 26 and 27 on my NT box. There must be a update to this, or something. [29] 4th full paragraph; "We'll need to tell Apache to load and run the startup file at launch time. Open perl.conf (actually, any of the configuration files will do) ...." should read (I think) "We'll need to tell Apache to load and run the startup file at launch time. Open httpd.conf(actually, any of the configuration files will do) ...." [36] Apache configuration settings following second paragraph; My system is Debian 3.0r1: root@d3020g:~/d3020g/etc/apache# apache -v Server version: Apache/1.3.26 (Unix) Debian GNU/Linux Server built: Oct 26 2002 09:15:15 The SetHandler directive given in the Eagle book, p. 36: SetHandler hello-handler Generates a error message in the Apache error.log: [Tue Feb 24 22:26:18 2004] [error] [client 192.168.1.2] File does not exist: Per the comments given in mod_hello.c generated by: apxs -n hello -g The SetHandler directive needs to be: SetHandler hello {80} line 9: "Malcom" should read "Malcolm". {90} 1st code example; It says "-e $r->finfo" when it should say "-e $file". And, a few lines later it says "unless (-r _)" when it should say "unless -r $file". {107} line -12: ucfirst(...) should probably ucfirst(lc(...)) if you're really "tidying them up" and "canonicalizing" them as the text claims. {108} line 26: "rarely seen >&= notation" Really, the "&=" is the rarely seen part, ">" is just an ordinary open-for-output marker. {115} code following 4th paragraph: The line } continue { has no effect and should be deleted. {121} In the code snippet, the second line: $r-.set_last_modified; should read $r->set_last_modified; The third line, my $rc = $r-> meets_conditions should read my $rc = $r->meets_conditions; {123} line 19: "unless" should read "if". {123} In Example 4-7: return OK unless $r->header_only; should read return OK if $r->header_only; This is similar to the change you made for the 6/99 printing on page 146. (144) line 2; The Apache ALIAS directive has two arguments, which should agree on the last char being a slash or not. The last word of the first sentence should say "/perl/" instead of "/perl". The actual directive in the preceding paragraph is correct. {150 & 156} the code; Where the script parses param('action') in the CASE: statement, the line that reads: /^view/i and do { view_guestbook(1); last CASE; }; the parameter of '1' causes an error under the 'use strict' pragma: [error] Can't use string ("1") as a symbol ref while "strict refs" in use at /path/to/guestbook_lean.pl line 109. {152} locking not really needed if you use atomicity of append {214} MAC by concatenating and crypto-hashing: OK for what you're doing there, but change it only slightly (as readers are likely to do) and there's a security hole like one that affected PGP. If you keep "bit boundaries" outside the hash then you can change the "protected" information. Roughly, PGP concatenated the bits of n and e and "protected" them. But the "number of bits in n" was kept outside. An attacker could construct a key by moving the boundary (one bit less/more for n; one bit more/less for e) and they'd hash the same. Security is always difficult to explain but it might be worth at least telling readers to include "boundaries" or "counts" before concatenation and hashing. {219} CBC: I haven't read through this in great detail but key reuse or IV (of the crypto kind, not the perl kind :-) reuse can totally remove any crypto protection when non-crypto kinds (like most readers) try to roll their own chaining/streaming crypto. {219} Footnote: France has now legalised crypto, if I recall. {226} line 22: "peak" should read "peek". {230} line 11: At least add PostgreSQL and Informix to the initial list :-) {230} Footnote: MySQL licence change {240} line 3: split(':', ...) should read split(/:/, ...) (and similarly anywhere else). {246} Example 5-7: The use of split and join causes trailing slashes in URIs to be silently dropped. This can cause extra internal redirects in the case where the URI refers to a directory, with the dubious side-effect of causing the browser to effectively lose the session information. I have found that the equivalent of adding $new_uri .= '/' if substr($r->uri, -1, 1) eq '/'; before the $r->uri( $new_uri ); line solves this problem. {280} last line: Delete "(hits)" and append it to the previous (comment) line. {281} Line 4 is a duplicate comment. {286} line -2: regexp should allow "-" in domain name (and arguably should disallow "_" in domain name since \w matches underscore but it's an illegal character in domains which well-run places now enforce). {291} Footnote: Actually, the salt is mainly to make it much less likely that two people who pick the same password on a non-shadowed system will end up with the same crypted password (and hence can use their own password to log into the other's account). (297) line -1: split(/\s+/, ...) should read split(' ', ...). (Even if you are guaranteed that there's no leading whitespace, the "turn leading whitespace into a null first field" behaviour of /\s+/ is more confusing then the simple split(' ', ...) which Does The Right Thing. [331] 2nd paragraph; In my module-writing adventures, I noticed (after a very long debugging session!) that the child init handler is not called for each virtual server. Rather, it's called only once, with the "main" server as the first argument. The same seems to hold true for the child exit handler. This, at least for C modules -- mod_perl modules may behave differently. A clarification on this point would have saved me some hair, hopefully it can help someone else. Of course, everyone's writing 2.0 modules these days... {334} 4th Paragraph Apache::DefaultTrans handler: The TransHandler is supposed to return BAD_REQUEST if the uri does not have a '/' as the first character, or if there is a '*' anywhere in it. The code given is: if ($uri !~ m:^/: or index($uri, '*')) { however, index returns -1 if there is no '*', not zero (0). Therefore the code should be: if ($uri !~ m:^/: or (index($uri, '*') >= 0)) { {334} Translation Handler code sample: This line: $r->log->error("Invalid URI in request ", $r->the_request); should be $r->log_error("Invalid URI in request ", $r->the_request); (This is in the 03/1999 printing, I have not seen it listed in errata for later versions either...) (347) line -12: >&= should be just &= (see note for p108). (347) line -7: Big nasty security hole if put together with an upload facility that allows users to pick their own filename (such as the usual "; rm -rf /"). {468} The second example on p. 128 of the Eagle book sets the content type and sends the HTTP headers itself before running a subrequest. However, on p. 468, the documentation for the run() method says in part: When you invoke the subrequest's response handler in this way, it will do everything a response handler is supposed to, including sending the HTTP headers and the document body. ... If you are invoking the subrequest uri() method from within your own content handler, you must not set the HTTP headers and document body yourself ... These seem to contradict each other. From testing, however, it seems as though the example on p. 128 is correct and the documentation on p. 468 isn't. {514} end of "int allowed" description: I believe r->allowed = M_GET | M_HEAD; should be r->allowed = (1 << M_GET) | (1 << M_HEAD); {515} The first line reads: int length; where it should read int clength; as the variable clength holds the request (C)ontent (Length). {515} top of page: The request_rec description on page 515 states that "long length" holds the outgoing Content-length. It should be "long clength." {528} description of ap_pstrndup() near bottom of page: The description states "This is a version of ap_pstrdup(), but it only allocates and copies n bytes." According to the source (and common sense) it actually allocates n+1 bytes, copies n bytes and NUL-terminates the new string. {547} Example 10-4: I suggest that "int rc" should be initialed as DECLINED or otherelse. Because "if(ap_should_client_bolck(r))" maybe return 'false', it results in return uninitialized "rc". {562} APLOG_WARN is actually called APLOG_WARNING. {569} bottom half of page; In the example for the ap_satisfies function, you have: " if (!(r->satisfies == SATISFY_ANY && ap_some_auth_required(r))) " However, in the declaration for the request_rec, I cannot find a "satisfies" member. Don't you instead mean: " if (!(ap_satisfies(r)==SATISFY_ANY && ap_some_auth_required(r))) " {628} There's a bug in Example 11-6 if compiled on a WIN32 platform. The WIN32 version returns back to the caller, but r->filename and r->args have been overwritten with values for the ap_call_exec function. In UNIX, this is not a problem because the code exits after the exec. But in Windows, the exec returns and code continues with these bogus values. {649} bottom of page: Do s/\.makepl_args\.mod_perl/.makepl_args.mod_perl/ (??)