Showing posts with label will need again. Show all posts
Showing posts with label will need again. Show all posts

Saturday, March 31, 2012

"Connection Reset" errors, MTU, DHCP, and Time Warner Cable

So long, AT&T DSL

Not too long ago, I made the move from AT&T DSL to Time Warner Cable for my family's home Internet connection. AT&T's pricing was no longer competitive, and their terms of service were nothing to be proud of.

Hopefully most readers have heard about the recent AT&T policies regarding 150 GB data caps for DSL connections. While most people have recently been complaining about similar data caps for mobile data plans - these caps are something I largely agree with, but only for wireless. There is only so much wireless spectrum available to share for everyone within a given area - and the laws of physics don't make these limits the easiest to overcome, at least without having towers on every street corner. (This is what we have Wi-Fi for.) While it is great that the wireless industry is marketing their mobile video capabilities, etc. - they need to ensure that they are offering the actual network services to match, instead of balancing on the edge of false advertising. However, for wired connections, there should be few excuses - as a wire can always be upgraded, or another wire (or fiber!) can always be added. Especially as my family is watching more Netflix and other video content online - combined with regular remote sessions to work and other computer-related activities, hitting 150 GB would not be too difficult. Personally, I also don't expect to be able to watch Netflix videos on mobile - I don't even have a mobile data plan. There is no place for data caps on wired Internet connections.

The other concern that I had was a provision that allowed AT&T to forcefully upgrade us from our DSL account to a higher-priced U-verse account at their discretion. I agree that U-verse is cool, but I'd really like to see it stand up to competition by also having something like Version FiOS available to the same customers. Verizon FiOS is not currently available in my area. I had also expressed some related thoughts in a previous post, when Appleton was considering a bid for Google Fiber. In that post, I had also stated some concerns with Time Warner Cable (TWC) - but they seem to have cleaned-up their act a bit since then - including with a new, impressive local retail presence.

"Connection Reset" errors, MTU, and DHCP with Time Warner Cable

So we switched to TWC's Road Runner service for our Internet service. Of course, it would be too easy if this was without issue. Without any other changes in my computer or network configurations, I noticed "The Connection was reset" errors the very first night with the service. Unfortunately, these issues were very difficult to troubleshoot, as the errors were quite sporadic, and I wasn't able to reproduce on-demand. The issues also were more prevalent on some devices than others - even though all devices worked just fine when connected to other networks. Interestingly, Google sites and services also seemed to be affected more than others. I'm guessing this had to do with most of Google's services being accessed over https:// - which increased the packet sizes and likely led to some of the issues. Having the issues happen the most often with encrypted connections also made troubleshooting with Wireshark, etc., quite difficult.

I had called TWC on this, but got the typical run-around. (Obligatory technical support comic: http://xkcd.com/806/.) They don't see any issues with their service - simply saying that it had to be an issue with my computer or router. They were somewhat correct - but only because my router was following TWC's direction.

eth1      Link encap:Ethernet  HWaddr **:**:**:**:**:**
          inet addr:75.87.***.***  Bcast:255.255.255.255  Mask:255.255.240.0
          UP BROADCAST RUNNING MULTICAST  MTU:576  Metric:1
          RX packets:807 errors:0 dropped:0 overruns:0 frame:0
          TX packets:482 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:85701 (85.7 KB)  TX bytes:77492 (77.4 KB)

See the issue? Frustratingly, it had taken me at least another week to spot it. Per TWC's own Road Runner help pages, the Ethernet standard for the Maximum Transmission Unit (MTU) is Ethernet standard of 1,500 bytes. So why was my router being configured for an MTU of only 576 bytes? Here was the latest from my /var/lib/dhcp3/dhclient.eth1.leases file after reproducing the issue:

lease {
  interface "eth1";
  fixed-address 75.87.***.***;
  option subnet-mask 255.255.240.0;
  option routers 75.87.192.1;
  option dhcp-lease-time 43200;
  option dhcp-message-type 5;
  option dhcp-server-identifier 10.65.64.1;
  option interface-mtu 576;
  option broadcast-address 255.255.255.255;
  option host-name "********";
  renew 0 2012/04/01 05:31:32;
  rebind 0 2012/04/01 10:41:37;
  expire 0 2012/04/01 12:11:37;
}

As I mentioned, my router was only doing "as it was told". I'm not exactly sure what is acting as the DHCP server or where it is located, but as the 10.65.64.1 address is not resolvable nor does it appear active, my guess is that this is being served from the cable modem itself, and a function of firmware loaded and controlled by TWC's Road Runner's servers. I'm also guessing that this option is not requested by or respected by most other devices (including SOHO routers and Microsoft Windows, etc.) - otherwise I'm sure TWC would have recognized this and corrected it by now. Fortunately, dhclient under Linux provides an easy work-around - with "work-around" probably being too drastic of a name, as it is really just a simple configuration change. Here is a portion of the default /etc/dhcp/dhclient.conf on my distribution, with the critical detail highlighted:

request subnet-mask, broadcast-address, time-offset, routers,
  domain-name, domain-name-servers, domain-search, host-name,
  netbios-name-servers, netbios-scope, interface-mtu,
  rfc3442-classless-static-routes, ntp-servers,
  dhcp6.domain-search, dhcp6.fqdn,
  dhcp6.name-servers, dhcp6.sntp-servers;

I simply commented-out this field, and my Internet connection has been stable since.

I called back TWC to report my findings, in an effort to hopefully help others having the same issue. Realizing that they refused to recognize that anything was wrong, I also wrote-up a summary of the issue and hand-delivered it to the local TWC retail office - including an invite to contact me with any needed requests for further details - hoping that it would be directed to someone who would be empowered to fix this. Needless to say, 4 months later, this issue still has not been addressed or resolved by TWC.

Sunday, October 30, 2011

Ubuntu Linux 11.10 Install Notes

This past week, I finally got around to installing the latest version of Ubuntu Linux on my home server - 11.10, "Oneiric Ocelot" (released 2011-10-13). There shouldn't be anything too significant here - these are mainly notes for myself, but posted here in case something is useful to others. I always do a full, clean re-installation - so several of the notes listed here won't be of concern for an in-place upgrade.

For some additional reference, this is the same server that I have setup as part of my Ubuntu Linux Router project, is setup with LDAP, and is the subject of several other Linux-related posts.

As I am using and now dependent on VLANs on this server for my network setup, I need to have a few necessary related packages readily available for the upgraded OS before reinstalling. These are not currently included on either the CD or DVD installation images, and I need them to be able to regain Internet connectivity - so simply using "apt-get" to re-install them from the Internet is clearly not an option. For the 11.10 upgrade, I downloaded the following amd64 *.deb packages:

After the fresh installation, the server fails to boot. This isn't anything new to to 11.10, but has been something to remember since I converted to a GUID Partition Table (GPT) from the legacy MBR partitioning scheme. I already have the special BIOS boot partition, as detailed under "GPT" at http://www.gnu.org/software/grub/manual/grub.html#BIOS-installation, which doesn't need to be re-configured with an OS upgrade. However, after the Ubuntu installation finishes, I do need to reset this partition as "active" in the legacy MBR partition table using fdisk - as I apparently have one of the Intel motherboards with a "buggy BIOS" that otherwise shouldn't require this. Additional details are at http://ubuntuforums.org/showpost.php?p=9787109&postcount=22 and http://www.rodsbooks.com/gdisk/bios.html.

All of my posts for the Ubuntu Linux Router project, while written for 8.04, are still almost completely valid and applicable for all releases since - including 11.10. One edit for Configuring Persistent PPP is that instead of attempting to disable NetworkManager, it is easier to just remove it: "apt-get remove network-manager". Install the networking *.deb packages downloaded above, then configure the DSL. Uncomment the "usepeerdns" line from the /etc/ppp/peers/dsl-provider restored from backup until BIND can be setup.

Once online, download and install some additional packages:

apt-get install vim build-essential ssh gnome-panel nautilus-open-terminal dkms linux-server
apt-get upgrade

After getting LDAP re-configured, 11.10 has some problems with graphical / xorg logins, as detailed at https://bugs.launchpad.net/ubuntu/+source/at-spi2-core/+bug/870874, and somewhat, "Problems with LightDm on Ubuntu 11.10 Oneiric? Here's how to solve" (2011-10-16, linux-software-news-tutorials.blogspot.com). Unfortunately, in this case, both LightDm and gdm exhibit the same issue - so my current work-around is to disable the problem script, /etc/X11/Xsession.d/90qt-a11y, by prefixing its name with a non-numeric character.

Finally, I don't need the extra help that Ubuntu provides at every login, so I disable it:

$ sudo chmod -x /etc/update-motd.d/10-help-text

Saturday, October 1, 2011

HP LaserJet PCL Errors and Driver Availability

My primary printer for the past 10 years has been a trusted Hewlett-Packard (HP) LaserJet 2200D, with an added network adapter making it the equivalent of a 2200DN. 12,600 pages later, it is still working like new (though recognizing in a shared office environment, this same print volume would probably be used within a fraction of a year.)

After recently completing a Windows 7 x64 reinstall, I installed the latest driver linked to from the product support page, which is the Universal Print Driver (UPD), version 5.3.1. Interestingly, the "Previous version" column on the download page is empty - and I was unable to find any archive of driver downloads on HP's site.

This wouldn't have been a problem - except for having the following printed in place of the expected output whenever I attempted to print:

PCL XL error

        Subsystem:  KERNEL

        Error:      UnsupportedProtocol

        Operator:   0x0

        Position:   0

Fortunately, I still had the last known-working version of the driver saved, 5.0.3.37 from December 2009. This worked, but now I was curious as to if this was an issue new to the 5.3.1 drivers and if there were still other updates I could be benefiting from.

Kudos to HP for still releasing drivers for a 10-year-old product, and for the concept of a universal driver. However, these versions shouldn't replace existing ones on the product support page - especially if there isn't at least minimal testing with the related product to ensure that it still works.

From HP's Readme for the HP Universal Print Driver v5.3.1.10527:

Each release is a full release of the product for all printer description languages for both 32bit and 64bit platform. Version history of UPD builds released on CD-ROM or posted to www.hp.com/go/upd.

Unfortunately, there is nothing visible at www.hp.com/go/upd for previous versions of the drivers. Even after searching HP's FTP site, I couldn't find any "official" archive of the UPD driver for versions prior to 5.3.1. However, I did find copies elsewhere on the Internet, and here are some details of each that should be useful to anyone else trying to find the same versions. Note that these are for Windows x64, PCL 6 only:

VersionFile NameSize (bytes)SHA-1 Checksum
5.0.3.37 / December 2009 upd-503-pcl664.exe 16,289,336 733cd725b962b99a25b9b7c94cdd5aa3a06a7116
5.1.1.8232 / August 2010 upd-5_1_1_8232-pcl6_winxp-vista-x64.exe 16,329,016 2ead57774607bec316e55ad707b841fe8dfe6cfa
5.2.6.9321 / February 2011 upd-PCL6-X64-5_2_6_9321.exe 16,633,400 bcb70eb684d1f5b684bcc2873a30df72293416b0
5.3.1.10527 / July 2011 upd-pcl6-x64-5.3.1.10527.exe 17,794,616 a789f2bab5bf9d48897c8dd99dbae9a46bd7031f

None of the drivers newer than 5.0.3.37 printed anything other than the "UnsupportedProtocol" error shown above on my 2200. This seems to correlate to HP's version history, which shows that Unidrv*.dll - which was at version 6.0.6001.22127 from 4.7.0.30 in November 2008 through 5.0.3.37, was updated to 6.1.7600.16385. It seems probable that the upgrade of Unidrv*.dll caused an incompatibility with the printer.

I've emailed this to HP support (incident # 14612070), and beyond receiving the automated "One of our Technical Support Specialists will be responding to your inquiry" message, I'm skeptical that I'll receive a response, or that this issue will be addressed in future versions of the UPD driver.

I did receive a few responses:

  • According to your product serial number, the warranty on your HP LaserJet 2300d printer has expired.
  • Thank you for contacting Hewlett-Packard's Commercial Solutions Center.

    Mark, unfortunately, Hewlett-Packard does not offer support via e-mail for your product.

    We are sorry for the inconvenience caused to you in this regard.

    The issue seems to be with the printer driver you are using. Hence, to resolve the issue, please install the UPD driver which mmight resolve the issue.

    http://h20000.www2.hp.com/bizsupport/TechSupport/SoftwareDescription.jsp?lang=en&cc=us&prodTypeId=18972&prodSeriesId=28861&prodNameId=28862&swEnvOID=4063&swLang=8&mode=2&taskId=135&swItem=lj-95991-2

    We hope that the above information provides a quick solution to your inquiry.

    Thank you again for contacting HP e-Solutions.

    • (Apparently, HP didn't comprehend the part about the problem being with latest version of the UPD driver - and that no previous versions were available for download from HP.)
    • My response:

      I understand that my previous email was not even read, beyond the fact that yes, I'm using an out-of-warranty LaserJet 2200D. However, I'm not looking for support - I just want you to update your web site to reflect correct information.

      The problem that I'm attempting to bring to your attention is that I'm not the only one having this issue, and that the product page for this product that HP is still hosting should either be updated to reflect this, or remove the page entirely. I already found the solution - using the 5.0 version of the UPD instead of using the 5.1.3 one. Simply, please update the page at http://h20000.www2.hp.com/bizsupport/TechSupport/SoftwareIndex.jsp?lang=en&cc=us&prodNameId=28862&prodTypeId=18972&prodSeriesId=28861&swLang=8&taskId=135&swEnvOID=4063 to reflect this, and to assist other users who may be having the same problem - or remove the page as to not promote dis-information.

  • Thank you for contacting HP eSolutions.

    We really apologize for the inconveniance caused to you in regards to this issue.

    Thank you for updating on this. We will escalate this to the concerned team.

    If you have any other queries, please feel free to get back to us and we would be glad to be of assistance.

    Once again, thank you for contacting HP eSolutions.

    • (Received 2011-09-19. No communications since.)

Monday, September 7, 2009

"networking restart" issues, VLANs under Ubuntu

For this post, I'm using Ubuntu Linux 9.04 / "Jaunty Jackalope". This is somewhat a follow-up to my Ubuntu Linux Router Upgrade Project.

Errors during "/etc/init.d/networking restart"

First, assuming a statically-configured LAN for server use, with NetworkManager disabled:

/etc/network/interfaces:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0
$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...                                   [ OK ]

The networking simply restarts, without showing any errors or warnings. However, this quickly changes once an additional network adapter is configured. This could be an additional physical adapter, but for my purposes, I had added a virtual LAN (VLAN) to work with my Dell PowerConnect 2716. For Ubuntu, this simply requires installing the "vlan" package, and defining the virtual LAN with an additional entry in /etc/network/interfaces. Note that this is done using a "<base name>.<VLAN ID>" naming scheme. As shown below, I'm adding an interface for VLAN 2:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0

auto eth0.2
iface eth0.2 inet static
  address 192.168.2.1
  netmask 255.255.255.0

Please add a comment if you can find any official documentation that documents this functionality of the interfaces file, as I can't. However, this appears to be driven by "/etc/network/if-pre-up.d/vlan" and "/etc/network/if-post-down.d/vlan".

This also requires the "8021q" module, but at least in Jaunty, it is already available by default, as shown by "lsmod | grep 8021q".

This is where I started running into errors:

$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...
RTNETLINK answers: No such process
Removed VLAN -:eth0.2:-
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
                                                                         [ OK ]

This doesn't actually cause any issues, but it isn't right and should be fixed. I don't see any way to make "networking restart" give more verbose output. Additionally, nothing helpful appears in the logs. However, most of what the networking script does is call "ifdown -a --exclude=lo" and "ifup -a", where "-a" is "affect all interfaces marked auto". Fortunately, repeating this with adding "-v" for verbose mode yields some details:

$ sudo ifdown -av --exclude=lo && sudo ifup -av
Configuring interface eth0=eth0 (inet)
run-parts --verbose /etc/network/if-down.d
run-parts: executing /etc/network/if-down.d/avahi-autoipd
run-parts: executing /etc/network/if-down.d/wpasupplicant

ifconfig eth0 down
run-parts --verbose /etc/network/if-post-down.d
run-parts: executing /etc/network/if-post-down.d/avahi-daemon
run-parts: executing /etc/network/if-post-down.d/bridge
run-parts: executing /etc/network/if-post-down.d/vlan
run-parts: executing /etc/network/if-post-down.d/wireless-tools
run-parts: executing /etc/network/if-post-down.d/wpasupplicant
Configuring interface eth0.2=eth0.2 (inet)
run-parts --verbose /etc/network/if-down.d
run-parts: executing /etc/network/if-down.d/avahi-autoipd
RTNETLINK answers: No such process
run-parts: executing /etc/network/if-down.d/wpasupplicant

ifconfig eth0.2 down
run-parts --verbose /etc/network/if-post-down.d
run-parts: executing /etc/network/if-post-down.d/avahi-daemon
run-parts: executing /etc/network/if-post-down.d/bridge
run-parts: executing /etc/network/if-post-down.d/vlan
Removed VLAN -:eth0.2:-
run-parts: executing /etc/network/if-post-down.d/wireless-tools
run-parts: executing /etc/network/if-post-down.d/wpasupplicant
Configuring interface eth0=eth0 (inet)
run-parts --verbose /etc/network/if-pre-up.d
run-parts: executing /etc/network/if-pre-up.d/bridge
run-parts: executing /etc/network/if-pre-up.d/dhclient3-apparmor
run-parts: executing /etc/network/if-pre-up.d/vlan
run-parts: executing /etc/network/if-pre-up.d/wireless-tools
run-parts: executing /etc/network/if-pre-up.d/wpasupplicant

ifconfig eth0 192.168.1.1 netmask 255.255.255.0       up

run-parts --verbose /etc/network/if-up.d
run-parts: executing /etc/network/if-up.d/avahi-autoipd
run-parts: executing /etc/network/if-up.d/avahi-daemon
run-parts: executing /etc/network/if-up.d/ip
run-parts: executing /etc/network/if-up.d/mountnfs
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
run-parts: executing /etc/network/if-up.d/ntpdate
run-parts: executing /etc/network/if-up.d/wpasupplicant
Configuring interface eth0.2=eth0.2 (inet)
run-parts --verbose /etc/network/if-pre-up.d
run-parts: executing /etc/network/if-pre-up.d/bridge
run-parts: executing /etc/network/if-pre-up.d/dhclient3-apparmor
run-parts: executing /etc/network/if-pre-up.d/vlan
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
run-parts: executing /etc/network/if-pre-up.d/wireless-tools
run-parts: executing /etc/network/if-pre-up.d/wpasupplicant

ifconfig eth0.2 192.168.2.1 netmask 255.255.255.0       up

run-parts --verbose /etc/network/if-up.d
run-parts: executing /etc/network/if-up.d/avahi-autoipd
run-parts: executing /etc/network/if-up.d/avahi-daemon
run-parts: executing /etc/network/if-up.d/ip
run-parts: executing /etc/network/if-up.d/mountnfs
run-parts: executing /etc/network/if-up.d/ntpdate
run-parts: executing /etc/network/if-up.d/wpasupplicant

So the error is apparently coming from "/etc/network/if-down.d/avahi-autoipd". (Avahi is a free Zero configuration networking (zeroconf) implementation.) I temporarily edited the "avahi-autoipd" script to add "-x" to enable debugging by changing the first line to "#!/bin/sh -ex". What is happening is that this script first checks for the existence of a route matching "169.254.0.0/16", but doesn't check for what interface it is on. If it finds a matching route, even on another interface, it makes a call to delete the route, but only for the current interface. When this combination doesn't exist, the "RTNETLINK answers: No such process" message is given.

Resolution: The scripts could just be fixed to include the interface in the search. I opened a bug report to Ubuntu on this: #425854. However, a "server"-type system shouldn't be handling these automatic types of network routings, so I just disabled these scripts:

sudo chmod -x /etc/network/if-up.d/avahi-autoipd
sudo chmod -x /etc/network/if-down.d/avahi-autoipd

Shown fixed:

$ sudo /etc/init.d/networking restart
 * Reconfiguring network interfaces...
Removed VLAN -:eth0.2:-
 * if-up.d/mountnfs[eth0]: waiting for interface eth0.2 before doing NFS mounts
Set name-type for VLAN subsystem. Should be visible in /proc/net/vlan/config
Added VLAN with VID == 2 to IF -:eth0:-
                                                                         [ OK ]

DHCP server binding issues

Another issue I noticed with running "/etc/init.d/networking restart" is that the DHCP server ("dhcpd" / "dhcp3-server") quit responding to requests. (Previous posting on dhcp3-server.) Using Wireshark, I noticed that DHCP requests were still being received, but no responses were being sent. Nothing applicable is shown in any of the logs, other than that dhcpd isn't even acknowledging the requests after the interface is brought back up.

Tools such as "lsof" "netstat" show that dhcpd is still properly bound to the interfaces. Other applications such as the SSH server (sshd) don't have this issue, and continue accepting connections even after the interface goes down and comes back up, so I'm not sure why this is an issue for the DHCP server. However, I can't get things to resume working properly short of restarting the DHCP server using "sudo /etc/init.d/dhcp3-server restart" As I'm starting to work with IPv6, I noticed the exact same issue with radvd.

I wasn't exactly sure where the best place was to automate these restarts. I could probably add a script to "/etc/network/if-up.d/", but would then have to include a check that I wasn't unnecessarily restarting the servers for every interface, as they should only be concerned with the interface they are serving requests on, e.g. eth0. For now, I just added these commands to "/etc/network/interfaces":

# …

auto eth0
iface eth0 inet static
  address 192.168.1.1
  netmask 255.255.255.0
  post-up /etc/init.d/dhcp3-server restart
  post-up /etc/init.d/radvd restart

# …

Wednesday, May 27, 2009

PuTTY and SSH port forwarding corruption

PuTTY seems to have some serious issues with SSH port forwarding. http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/portfwd-corrupt.html seems to document exactly the same issues I've been having.

I was doing a simple read of XML data in Java from a remote HTTP server, using a SSH tunnel created by PuTTY. Commonly but sporadically, the HttpURLConnection appeared to close prematurely, resulting in only partially-received content. Switching between "dynamic" and "static" (Local) tunnel destinations did not have any effect.

I've also been having issues with PuTTY crashing on unclean closes.

The latest development snapshot (2009-05-27:r8577) does not appear to help. The "portfwd-corrupt" bug was filed in mid-2003, and there hasn't been a new release in over 2 years. (0.60 was released on 2007-04-29.) All of the PuTTY forks and variations I tried have the same issue. (See PuTTY on Wikipedia.) TeraTerm (Wikipedia) does not suffer the same issue, but does not appear to support dynamic tunneling using SOCKS.

My working solution: Use OpenSSH (Wikipedia), which also works on Windows through the use of Cygwin (Wikipedia). Just be aware that this is a command-line solution. While there are probably various front-ends available, I'd be surprised if there was one that didn't limit the available options that ssh has to offer. Ironically, I am using PuTTYcyg to run ssh, as it is much better than the Windows' command prompt. By entering ssh with the desired arguments as the command instead of the default "-" for the login shell, it saves a bash process from running.

Sunday, January 11, 2009

XML and XSLT Tips and Tricks for Java

1. Get the latest versions

Starting with Java 1.4, the Java runtime has included a default XML parser and transformer implementation as part of the Java API for XML Processing (JAXP).

However, the included versions aren't up-to-date - not even to the latest versions available when each Java version was released.

As of this writing, the latest Apache Xerces2-J version is 2.9.1 (2.11.0 as of November 2010), and the latest Apache Xalan-J version is 2.7.1. I strongly recommend using the latest versions, as the versions built-in to Java are both somewhat limited and buggy. Xalan's FAQ page gives some strict notes concerning using a newer version under Java 1.4, due to the Endorsed Standards Override Mechanism. However, these instructions make it rather difficult - if not impossible - to use an updated library for a particular application on a shared JRE. Fortunately, these steps appear to be no longer required starting with Java 1.5 / 5.0. In these later versions, Sun has repackaged the Apache libraries into rt.jar as com.sun.org.apache.*, and properly load any desired implementation based on the "META-INF/services/javax.xml.*" files found on the classpath. Implementations including these files, including Apache Xerces2-J and Xalan-J, will automatically be used by default if included on the classpath.

2. Use Templates to reuse Transformations

Most of the JAXP interfaces are not thread-safe, including the factories and the instances obtained from them. I.E., neither instance of DocumentBuilderFactory nor DocumentBuilder should be stored statically or in another such way where they could be accessed by multiple threads.

The same applies to a Transformer. While it can be used repeatedly within a given thread, it is not thread-safe for use across multiple threads.

The solution is to use a Templates object, which can be thought of as a compiled-form of a stylesheet. Per the JavaDoc, "Templates must be threadsafe for a given instance over multiple threads running concurrently, and may be used multiple times in a given session." Additionally, use of Templates for repeated transformations will probably provide a performance improvement, as the transformation source (usually an XSLT) doesn't need to be re-read, re-parsed, and re-compiled.

Here is some simple, typical code of performing a transformation without a Templates object:

TransformerFactory tf = TransformerFactory.newInstance();
StreamSource myStylesheetSrc = new StreamSource(
  getClass().getResourceAsStream("MyStylesheet.xslt"));
Transformer t = tf.newTransformer(myStylesheetSrc);
t.transform(new StreamSource(System.in), new StreamResult(System.out));

Here is the improved code, which makes use of a reusable Templates object:

TransformerFactory tf = TransformerFactory.newInstance();
if(!tf.getFeature(SAXTransformerFactory.FEATURE)){
  throw new RuntimeException(
    "Did not find a SAX-compatible TransformerFactory.");
}
SAXTransformerFactory stf = (SAXTransformerFactory)tf;
StreamSource myStylesheetSrc = new StreamSource(
  getClass().getResourceAsStream("MyStylesheet.xslt"));
Templates templates = stf.newTemplates(myStylesheetSrc);

// templates can now be stored and re-used from practically anywhere.

Transformer t = templates.newTransformer();
t.transform(new StreamSource(System.in), new StreamResult(System.out));

3. Chaining Transformations

When multiple, successive transformations are required to the same XML document, be sure to avoid unnecessary parsing operations. I frequently run into code that transforms a String to another String, then transforms that String to yet another String. Not only is this slow, but it can consume a significant amount of memory as well, especially if the intermediate Strings aren't allowed to be garbage collected.

Most transformations are based on a series of SAX events. A SAX parser will typically parse an InputStream or another InputSource into SAX events, which can then be fed to a Transformer. Rather than having the Transformer output to a File, String, or another such Result, a SAXResult can be used instead. A SAXResult accepts a ContentHandler, which can pass these SAX events directly to another Transformer, etc.

Here is one approach, and the one I usually prefer as it provides more flexibility for various input and output sources. It also makes it fairly easy to create a transformation chain dynamically and with a variable number of transformations.

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();

// These templates objects could be reused and obtained from elsewhere.
Templates templates1 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet1.xslt")));
Templates templates2 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet2.xslt")));

TransformerHandler th1 = stf.newTransformerHandler(templates1);
TransformerHandler th2 = stf.newTransformerHandler(templates2);

th1.setResult(new SAXResult(th2));
th2.setResult(new StreamResult(System.out));

Transformer t = stf.newTransformer();
t.transform(new StreamSource(System.in), new SAXResult(th1));

// th1 feeds th2, which in turn feeds System.out.

Here is another approach, which makes use of XMLFilter's. This approach is also documented in Sun's J2EE 1.4 Tutorial.

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();

// These templates objects could be reused and obtained from elsewhere.
Templates templates1 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet1.xslt")));
Templates templates2 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet2.xslt")));

SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();

XMLFilter filter1 = stf.newXMLFilter(templates1);
XMLFilter filter2 = stf.newXMLFilter(templates2);

filter1.setParent(reader);
filter2.setParent(filter1);

Transformer t = stf.newTransformer();
t.transform(
  new SAXSource(filter2, new InputSource(System.in)),
  new StreamResult(System.out));

Note how in this later approach, the filter is applied at the source instead of the result.

4. Input Validation

Prior to Java 1.5 / 5.0, the only way to control validation through the JAXP API was to set custom attributes. This is described quite well in Sun's J2EE 1.4 Tutorial in "Validating with XML Schema", so I won't repeat it all here. However, do pay attention to the end of the page, which explains that schemas can be loaded from several different sources, including InputStreams and other InputSources - not just local Files or URLs, which many developers seem to overlook.

Starting with Java 1.5 / 5.0, the function of setValidating on DocumentBuilderFactory seems to have changed slightly. It now essentially controls only DTD validation, not modern schema validation e.g. W3C XML Schema or RELAX NG. Instead, a setSchema method is available, which accepts a compiled Schema object. Like the Templates object above, this is one of the few JAXP classes that is thread-safe and is meant for reuse.

One advantage with the DocumentBuilderFactory's setSchema method is that a document can be checked not only for well-formedness and for validity against a schema, but also for validity against a particular, pre-defined schema. Additionally, by default, the parsing process will follow URLs out to the Internet to resolve schemas, etc. Passing in a Schema object built from locally-kept files can improve performance, and eliminate the need for accessing the Internet. However, if there are additional references to be resolved, further attempts may still be made. These can be intercepted by registering an EntityResolver to the DocumentBuilder.

For ensuring that a particular DTD is used, use the extended EntityResolver2. I've found that if the DOCTYPE is missing, getExternalSubset is called. To use a particular DOCTYPE by default, this method could call and return the result from resolveEntity, after passing in the desired publicId and/or systemId. If the XML to be validated already includes a DOCTYPE, then resolveEntity will be called directly. This can be written to either throw an exception or silently return the desired entity when an unexpected entity is received.

5. XML Creation using XSLT

XSLT is a well-known method for transforming XML, but it can also be used for XML generation. The easiest way it to use XSLT as a transformation, similar to the above methods, but with an empty input source. This is additionally noted in the Transformer.transform(…) JavaDoc.

Using XSLT for XML generation works particularly well when the XML is rather static, or when the XSLT can be used as a template. The Transformer's setParameter(…) can be used to pass parameters into the transformation which can then be used as variables. To avoid possible naming collisions, especially when using larger or 3rd-party XSLTs, I strongly recommend using the namespace prefixes.

Below is a sample XSLT with namespaced parameters, then populated by Java code. It generates a valid XHTML document, with the document title and a message in the body passed-in as parameters:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:z="http://namespaces.ziesemer.com/example"
    exclude-result-prefixes="z">
    
  <xsl:output
    method="html"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  
  <xsl:param name="z:title"/>
  <xsl:param name="z:message"/>
  
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
      <head>
        <title><xsl:value-of select="$z:title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="$z:title"/></h1>
        <p><xsl:value-of select="$z:message"/></p>
      </body>
    </html>
  </xsl:template>
  
</xsl:stylesheet>
final String NAMESPACE_PREFIX = "{http://namespaces.ziesemer.com/example}";

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();
Templates templates = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("XHTMLMessage.xslt")));

// templates can now be stored and re-used from practically anywhere.

Transformer t = templates.newTransformer();
t.setParameter(NAMESPACE_PREFIX + "title",
  "Example Title");
t.setParameter(NAMESPACE_PREFIX + "message",
  "Example Message");

t.transform(new DOMSource(), new StreamResult(System.out));

This approach has a number of advantages. It is fairly easy to see what is happening, and it is easy to make changes or additions to the output. It guarantees valid XML output, as an exception will be thrown if the XSLT is invalid. It also performs quite well.

6. XSLT Inheritance

Just as common functionality can be factored out of Java classes into shared parent classes, XSLT can also make similar use of inheritance by using Stylesheet Imports. Here is an example split into a parent and child:

XHTMLTemplate.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:z="http://namespaces.ziesemer.com/example"
    exclude-result-prefixes="z">
    
  <xsl:output
    method="html"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  
  <xsl:param name="z:title"/>
  
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
      <head>
        <title><xsl:value-of select="$z:title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="$z:title"/></h1>
        <xsl:call-template name="z:Message"/>
      </body>
    </html>
  </xsl:template>
  
  <!-- This should be overridden by child stylesheets. -->
  <xsl:template name="z:Message"/>
  
</xsl:stylesheet>

XHTMLMessage.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:z="http://namespaces.ziesemer.com/example"
    exclude-result-prefixes="z">
  
  <xsl:import href="XHTMLTemplate.xslt"/>
  
  <xsl:param name="z:message"/>
  
  <xsl:template name="z:Message">
    <p xmlns="http://www.w3.org/1999/xhtml">
      <xsl:value-of select="$z:message"/>
    </p>
  </xsl:template>
  
</xsl:stylesheet>

Depending upon the type of location the dependent files, a custom URIResolver will probably be needed to properly resolve the resources. In my example, I'm reading from the Java classpath. Other possibilities could include the local file system, or HTTP URLs. Only the necessary changes to the above Java code are shown below:

URIResolver resolver = new URIResolver(){
  @Override
  public Source resolve(String href, String base) throws TransformerException{
    return new StreamSource(getClass().getResourceAsStream(href));
  }};

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();
stf.setURIResolver(resolver);
Templates templates = stf.newTemplates(resolver.resolve("XHTMLMessage.xslt", null));

7. XSLT Extensions

Using parameters is a start, but the limitations are quickly visible. However, when combined with extension mechanisms, XSLT generation should be able to solve almost any requirement. Reading http://xml.apache.org/xalan-j/extensions.html is an excellent starting point. When properly used, extensions can feed into the transformation process and keep the memory footprint to a minimum.

Following is an example that uses an XSLT extension to output a variable number of messages. Additionally, this method allows for the properties to be calculated dynamically on each iteration, rather than pre-processing and storing the formatted messages - which can save memory.

XHTMLMessage.xslt:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:z="http://namespaces.ziesemer.com/example"
    xmlns:zMessageGenExt="com.ziesemer.example.MessageGenerator"
    xmlns:zMessageExt="com.ziesemer.example.Message"
    extension-element-prefixes="zMessageGenExt zMessageExt"
    exclude-result-prefixes="z zMessageGenExt zMessageExt">
    
  <xsl:output
    method="html"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  
  <xsl:param name="z:title"/>
  <xsl:param name="z:ext"/>
  
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
      <head>
        <title><xsl:value-of select="$z:title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="$z:title"/></h1>
        <xsl:call-template name="z:Messages"/>
      </body>
    </html>
  </xsl:template>
  
  <xsl:template name="z:Messages">
    <xsl:variable name="z:message" select="zMessageGenExt:getNextMessage($z:ext)"/>
    <xsl:if test="string($z:message)">
      <p xmlns="http://www.w3.org/1999/xhtml">
        <b>
          <xsl:value-of select="zMessageExt:getTitle($z:message)"/>
        </b><xsl:text>: </xsl:text>
        <xsl:value-of select="zMessageExt:getDescription($z:message)"/>
      </p>
      <xsl:call-template name="z:Messages"/>
    </xsl:if>
  </xsl:template>
  
</xsl:stylesheet>

XHTMLExample.java:

t.setParameter(NAMESPACE_PREFIX + "ext",
  new MessageGenerator());

IMessage.java:

package com.ziesemer.example;

public interface IMessage{
  String getTitle();
  String getDescription();
}

MesssageGenerator.java

package com.ziesemer.example;

public class MessageGenerator{
  
  protected int index = 0;
  protected int size = 5;
  
  public IMessage getNextMessage(){
    if(index < size){
      IMessage msg = new TestMessage();
      index++;
      return msg;
    }
    return null;
  }
  
  protected class TestMessage implements IMessage{
    @Override
    public String getTitle(){
      return String.format("This is title %d.", index);
    }
    
    @Override
    public String getDescription(){
      return String.format("This is description %d.", index);
    }
  }
}

Output:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<title>Example Title</title>
</head>
<body>
<h1>Example Title</h1>
<p>
<b>This is title 1.</b>: This is description 1.</p>
<p>
<b>This is title 2.</b>: This is description 2.</p>
<p>
<b>This is title 3.</b>: This is description 3.</p>
<p>
<b>This is title 4.</b>: This is description 4.</p>
<p>
<b>This is title 5.</b>: This is description 5.</p>
</body>
</html>

XSLT is more of a functional language than procedural, and the demonstrated use of recursion is really the only way to implement a loop. Unfortunately, this can lead to stack overflow errors within the Java implementation. This can be mitigated by increasing the stack size. While this can be set globally for the JVM, it is certainly not the best option. There is a Thread constructor that allows for the thread's stack size to be specified, but it is platform-depdendent, and is still only a mitigation. There is a good article on IBM developerWorks, "Use recursion effectively in XSL" (Jared Jackson, 2002-10-01), that specifically addresses this stack overflow issue with XSL recursion. Unfortunately, the examples provided require either a pre-known list size, and/or only calculate within the loop rather than producing output.

Here are some modifications to my above XSLT that recurses down a tree, splitting into 2 children at each level, and making the maximum necessary depth log2n. (A traditional divide & conquer algorithm.) However, by supporting a loop of an unknown size, the depth cannot be calculated to the appropriate minimum level in advance. In my approach, a pre-defined depth of a "sufficient size" is used. I chose 32, as 2^32 = 4,294,967,296, and would be equal to Java's Integer if it were non-signed. (As Integers are signed in Java, the maximum value of an int is 2,147,483,647.) Also, my trials have shown that the default stack size supports over 1,000 recursions before overflowing, so 32 should be a more than safe value. The tree will be filled "depth-first", and the tree will then continue to grow in "width". Some additional work is done to test if each call still resulted in output. If not, an xsl-if prevents further recursion, otherwise the entire tree would still be built and traversed. At 2 billion+ potential nodes in the tree, completing the recursion would require an unacceptable amount of time.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:z="http://namespaces.ziesemer.com/example"
    xmlns:zMessageGenExt="com.ziesemer.example.MessageGenerator"
    xmlns:zMessageExt="com.ziesemer.example.Message"
    extension-element-prefixes="zMessageGenExt zMessageExt"
    exclude-result-prefixes="z zMessageGenExt zMessageExt">
    
  <xsl:output
    method="html"
    doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
  
  <xsl:param name="z:title"/>
  <xsl:param name="z:ext"/>
  
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
      <head>
        <title><xsl:value-of select="$z:title"/></title>
      </head>
      <body>
        <h1><xsl:value-of select="$z:title"/></h1>
        <xsl:call-template name="z:MessagesRecursive">
          <xsl:with-param name="z:depth" select="0"/>
        </xsl:call-template>
      </body>
    </html>
  </xsl:template>
  
  <xsl:template name="z:MessagesRecursive">
    <xsl:param name="z:depth"/>
    <xsl:variable name="x">
      <xsl:call-template name="z:Messages"/>
    </xsl:variable>
    <xsl:if test="string($x) and $z:depth &lt; 32">
      <xsl:copy-of select="$x"/>
      <xsl:call-template name="z:MessagesRecursive">
        <xsl:with-param name="z:depth" select="$z:depth + 1"/>
      </xsl:call-template>
      <xsl:call-template name="z:MessagesRecursive">
        <xsl:with-param name="z:depth" select="$z:depth + 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
  
  <xsl:template name="z:Messages">
    <xsl:variable name="z:message" select="zMessageGenExt:getNextMessage($z:ext)"/>
    <xsl:if test="string($z:message)">
      <p xmlns="http://www.w3.org/1999/xhtml">
        <b>
          <xsl:value-of select="zMessageExt:getTitle($z:message)"/>
        </b><xsl:text>: </xsl:text>
        <xsl:value-of select="zMessageExt:getDescription($z:message)"/>
      </p>
    </xsl:if>
  </xsl:template>
  
</xsl:stylesheet>

8. Beware of classloaders

One frustrating issue I recently dealt with was related to multiple classloaders, where extension classes and methods were being reported as "not found", as mentioned in the Xalan-J FAQ. Fortunately, the Xalan implementation appears to handle multiple classloaders in a quite robust fashion, by using the context ClassLoader. In the environment where I was working, the Xalan classes were in a parent classloader from the extension classes; however, this never posed to be a problem for me previously. The actual error in my particular case was that the servlet engine was old and buggy, and was not setting the context ClassLoader on new threads. I worked around this by calling setContextClassLoader(…) in my servlet's service(…) method, before calling super.service(…).

9. Using DocumentFragments

This is really the first use I found of DocumentFragment where it seemed appropriate. Even when using the XSLT approach, there may be instances where it is necessary or easier to build and include a section of XML from within Java rather than XSLT. If used excessively, fragments are counter-productive to the advantages of using XSLT. Understand that while XSLT can stream the content in a pipelined-fashion as it is prodcued, each DocumentFragment must be completely built and returned before it can be streamed, which will increase memory requirements with the size of the fragments.

As doocumented on the Xalan Extensions page, DocumentFragments are a valid return type from an extension. They are also far easier to produce than the other Node-Set types. Here is the best method I found to make use of this functionality:

Java extension method:

public DocumentFragment fill(Node n){
  Document doc = (Document)n;
  DocumentFragment df = doc.createDocumentFragment();
  
  // Append any number of children and/or sub-children...
  Element e = doc.createElement("Example");
  df.appendChild(e);
  
  return df;
}

XSLT:

<xsl:copy-of select="extensionPrefix:fill($instanceVariable, .)"/>

10. XSLT vs. JAXB and JibX, Castor, etc.

While several people I know are big fans of XML data binding frameworks, I try my best to avoid them. For almost any use that I've seen of these frameworks, I would contend that XSLT and/or one of the XML generation techniques I previously described would be a better fit. In general, these frameworks introduce additional complexities and dependencies, along with usually artificial limitations. I've seen several performance comparisons and presentations between such frameworks, but none that dare to include XSLT and the other direct approaches.

Wednesday, December 3, 2008

Resizing the Blogger Edit Box

If you compose in Blogger at all, you may be frustrated with the relatively small size of the text area provided for writing. It defaults to only about 15 lines!

Here's a fix.

I started out by using Firebug's Inspect feature to find what I wanted to resize, and to initially tweak the size on-demand. However, this is a bit tedious, and requires further tweaking if the window is resized.

I then wrote a JavaScript Bookmarklet to automate the task. When run, it automatically resizes the Blogger edit boxes for both the "Edit Html" and "Compose" tabs to just a bit smaller than the current size of the browser window, leaving just enough margin for the scrollbars and other editor components, etc. Additionally, it also listens for window resize events, and resizes the edit boxes along with the browser window.

Here is the human-friendly source code:

// Mark A. Ziesemer, www.ziesemer.com.
javascript:(
  function(){
    // http://www.quirksmode.org./js/events_advanced.html
    var attachEvent = function(obj, evType, handler){
      if(obj.addEventListener){
        obj.addEventListener(evType, handler, true);
        return true;
      }else if(obj.attachEvent){
        return obj.attachEvent("on" + evType, handler);
      }else{
        return false;
      }
    },
    getDim = function(dim){
      // http://www.howtocreate.co.uk./tutorials/javascript/browserwindow
      return window["inner" + dim] || document.documentElement["client" + dim] || document.body["client" + dim];
    },
    getH = function(){
      return getDim("Height");
    },
    getW = function(){
      return getDim("Width");
    },
    editSize = function(dim, size){
      document.getElementById("textarea").style[dim]
        = document.getElementById("richeditorframe").style[dim]
        = size + "px";
    },
    lastH, lastW,
    resize = function(){
      var newH = getH(), newW = getW();
      // http://webbugtrack.blogspot.com./2007/10/bug-104-resize-event-firing-errors-in.html
      if(newH != lastH || newW != lastW){
        editSize("height", newH - 250);
        editSize("width", newW - 100);
        lastH = getH();
        lastW = getW();
      }
    };
    resize();
    attachEvent(window, "resize", resize);
  }
)();

This is cross-browser compatible, and was tested against Mozilla Firefox 3 and Microsoft Internet Explorer 7. Not surprisingly, it could be much shorter if it didn't need to work-around a few IE bugs, as commented above:

  1. Supporting a non-standards event registration model.
  2. Difficulties in obtaining the size of the browser window, including differences between "strict" and "quirks" modes.
  3. The resize event being called repeatedly, which usually resulted in infinite loops and hanging IE.

Here's the same code, condensed as a usable bookmarklet:

javascript:(function(){var%20attachEvent=function(obj,evType,handler){if(obj.addEventListener){obj.addEventListener(evType,handler,true);return%20true;}else%20if(obj.attachEvent){return%20obj.attachEvent('on'+evType,handler);}else{return%20false;}},getDim=function(dim){return%20window['inner'+dim]||document.documentElement['client'+dim]||document.body['client'+dim];},getH=function(){return%20getDim('Height');},getW=function(){return%20getDim('Width');},editSize=function(dim,size){document.getElementById('textarea').style[dim]=document.getElementById('richeditorframe').style[dim]=size+'px';},lastH,lastW,resize=function(){var%20newH=getH(),newW=getW();if(newH!=lastH||newW!=lastW){editSize('height',newH-250);editSize('width',newW-100);lastH=getH();lastW=getW();}};resize();attachEvent(window,'resize',resize);})();

(Bookmarklet - Either right-click and choose to bookmark, or drag to your bookmarks menu or links toolbar.)

I've not yet found a decent tool to compress JavaScript into bookmarklets. The best I've found is at http://chris.zarate.org./projects/bookmarkleter/. However, one issue it has in particular is failure to properly handle comments. While it is the tool I used to condense to the above, I manually removed the comments first and converted the double-quotes to single-quotes.

This script could easily be modified for similar functionality on other sites. Additionally, the need to initially enable the resizing by manually activating the bookmarklet could be avoided by converting this to a Greasemonkey script.

Update (2009-08-29):

Now also provided as a Greasemonkey script: http://userscripts.org/scripts/show/56651

Update (2009-01-26):

As discussed in the comments below, I'm avoiding Blogger's "updated editor" for now, due to many other unresolved issues with that editor itself. My scripts are only designed to work with the "old editor". Some details about the different editors are available from Google's Blogger Help.

If you have an issue with the bookmarklet, please post a comment here. If you have an issue with the Greasemonkey script, please report it at http://userscripts.org/scripts/issues/56651. In either case, be sure to include which browser and version is being used. Please also include any other possible factors that are visible, such as JavaScript errors or warnings in the browser.

Friday, October 31, 2008

Configuring LAN DCHP and Dynamic DNS under Ubuntu Linux

(This post is the 6th part of my Ubuntu Linux Router Upgrade Project.)

After configuring routing and NAT in my last post, while the Internet access may be shared, other clients won't know about it unless manually configured, requiring at least an IP address and subnet, local gateway, and one or more DNS servers for proper access.

DHCP

As DHCP is used to retrieve IP configuration information for the router from the Internet, it can also be served from the router to the LAN, and is probably the easiest way to configure networking within a LAN.

In OpenWrt, Dnsmasq was used for this purpose. However, it is relatively simple, and part of this project was to learn other Linux standards. For this project, I chose ISC's DHCP Distribution (a.k.a. "dhcpd", "dhcp3-server"), which seems to be the most popular and full-featured choice for a DHCP server under Linux. (See also dhcp3-server on help.ubuntu.com.)

ISC's DHCP Distribution is available as an Ubuntu-maintained package, "dhcp3-server". See "InstallingSoftware" on help.ubuntu.com for notes on installing software. A good write-up on installing and configuring dhcp3-server in particular is available at http://www.howtoforge.com/dhcp_server_linux_debian_sarge.

One of the main prerequisites is to have a static IP address on the interface that dhcp3-server will be bound to and serving IP addresses on. For this project, this was already done as part disabling NetworkManager in Configuring Persistent PPP.

The server then needs to be configured, using "/etc/dhcp3/dhcpd.conf". Shown below is mine:

# option definitions common to all supported networks...
option domain-name "lan.ziesemer.com";

default-lease-time 691200; #600;
max-lease-time 691200; #7200;

# If this DHCP server is the official DHCP server for the local
# network, the authoritative directive should be uncommented.
authoritative;

# Use this to send dhcp log messages to a different log file (you also
# have to hack syslog.conf to complete the redirection).
log-facility local7;

subnet 192.168.###.0 netmask 255.255.255.0 {
 range 192.168.###.### 192.168.###.###;
 option routers 192.168.###.1;
 option domain-name-servers 192.168.###.1; 
 ddns-domainname "lan.ziesemer.com.";
}

host static-host-name {
 hardware ethernet xx:xx:xx:xx:xx:xx;
 fixed-address 192.168.###.###;
}

DNS

In order for clients to resolve domain names to IP addresses, DNS Servers need to be defined. For many ISP consumers, DNS servers provided by the ISP are used. Unfortunately, more and more ISPs, including Charter Communications, feel obliged to break standard Internet protocol (RFC 2308) and hijack these requests, posing security and other implications. (See also "DNS hijacking" on Wikipedia.) As such, running a local resolver becomes rather desirable, which can also offer performance advantages, and the ability to offer DNS services for the local domain.

As with DHCP, in OpenWrt, Dnsmasq was also used for this purpose. Again, I chose something a bit more "heavy-weight" and popular, BIND (a.k.a. "named"). (See also: http://en.wikipedia.org./wiki/BIND and https://help.ubuntu.com/community/BIND9ServerHowto.) Again, "bind9" will have to be installed.

There are 3 main files for configuring BIND: "/etc/bind/named.conf", "/etc/bind/named.conf.options", and "/etc/bind/named.conf.local", the later two of which are both included by the first.

There is really nothing needed for a basic configuration. However, adding the RFC 1918 zones into named.conf.local as commented in the file is recommended. However, as I'm using 192.168.###.###, I didn't want to uncomment the line to include the entire file. Instead, I did a "literal" include by copying in the contents of the "/etc/bind/zones.rfc1918" file, except for the last line that contained zone "168.192.in-addr.arpa".

Making this DNS server known to the LAN was already accomplished in the dhcpd configuration above by "option domain-name-servers".

Local DNS and Dynamic Updates

Now for the advanced part - configuring a local domain with dynamic updates. Two other posts I reviewed were http://www.semicomplete.com/articles/dynamic-dns-with-dhcp/ and http://www.debian-administration.org/articles/343, neither of which seemed completely correct, comprehensive, or up-to-date - though this post isn't exactly meant to be completely comprehensive, either.

The first thing is to generate a secure key used to allow dhcpd to send updates to named. "/usr/sbin/rndc-confgen" can be used to generate this key context. Running it without any arguments will output 2 sections that are meant to be copied and pasted into the named files, "rndc.conf" and "named.conf". Alternatively, running it with the "-a" argument will generate just the "key" clause, and write it to the default key file ("(/etc/bind/rndc.key", run with sudo). It seems that I needed all 3 parts, but running both versions would result in separate keys. I used the "-a" argument, then copied out the key for manually updating the other 2 files. This assumes that both dhcpd and named are running on the same server, which isn't necessary.

Assume the following was output to "/etc/bind/rndc.key" using "sudo /usr/sbin/rndc-confgen -a":

key "rndc-key" {
 algorithm hmac-md5;
 secret "hp83n+fUOC2LdoDF9S12wg==";
};

The following should then be added to "/etc/bind/named.conf" (at no particular location):

include "/etc/bind/rndc.key";
controls {
 inet 127.0.0.1 allow {
  localhost;
 }
 keys {
  rndc-key;
 };
};

Next, the following should then be added to "/etc/dhcp3/dhcpd.conf" (at no particular location):

ddns-update-style interim;

key "rndc-key" {
 algorithm hmac-md5;
 secret hp83n+fUOC2LdoDF9S12wg==;
};

Note that quotes around the secret are not allowed here. Including quotes will produce errors.

At this point, both dhcpd and named now share a "secret" key used to authenticate dhcpd to named for updating DNS entries.

Now, we need to define a DNS zone in "/etc/bind/named.conf.local" for the LAN:

zone "lan.ziesemer.com" {
 type master;
 file "/var/lib/bind/lan.ziesemer.com.zone";
 allow-update {
  key "rndc-key";
 };
 notify yes;
};

zone "##.168.192.in-addr.arpa" {
 type master;
 file "/var/lib/bind/##.168.192.in-addr.arpa.zone";
 allow-update {
  key "rndc-key";
 };
 notify yes;
};

The "allow-update" sections with the "key" are references to the key defined in "/etc/bind/rndc.key" (and included from "/etc/bind/named.conf") that is allowed to make updates for the zones.

Note the location of the zone files. Originally, I had these in "/etc/bind" as well, but ran into "permission denied" errors whenever updates were attempted, even after editing permissions on the files. It seems that even with opened permissions, the files are protected by AppArmor. This is further described in the "bind / dhcp jnl failure - permission denied" thread on ubuntuforums.org.

The referenced zone files then need to be created, the process of which is outside the scope of this post. Refer to the above linked articles for further reference. For the purpose of dynamically updated zones, these files should be chmod'd to 755, with both the user and group set to "bind". (I don't recall doing this, so they may be automatically set at some point?)

Finally, the following should be included in the subnet block declared in "/etc/dhcp3/dhcpd.conf" (above):

subnet 192.168.###.0 netmask 255.255.255.0 {
 zone lan.ziesemer.com. {
  primary 127.0.0.1;
  key "rndc-key";
 }

 zone ##.168.192.in-addr.arpa. {
  primary 127.0.0.1;
  key "rndc-key";
 }
}

Restart the services for a fully functional LAN:

sudo /etc/init.d/dhcp3-server restart
sudo /etc/init.d/bind9 restart

Enabling routing and NAT with iptables

(This post is the 5th part of my Ubuntu Linux Router Upgrade Project.)

After my last post, I now have a persistent Internet connection configured through PPP. Now it's time to make the connection available to the rest of my Local Area Network.

Enabling Routing

IPv4 (and also IPv6) routing is already built-in to the Linux kernel (at least under Ubuntu Hardy Heron), and simply needs to be enabled. The easiest way to do this is to edit "/etc/sysctl.conf", which like many of the other configuration changes made in this project, needs to be edited as root. (Use sudo.)

At least under Ubuntu Hardy Heron, a sysctl.conf file already exists with a few default options, and many other commented and documented options. The option that needs to be enabled is "net.ipv4.ip_forward=1", either by removing the prefixed "#" comment character, or by adding the line somewhere in the file.

This setting can then be immediately placed into effect without rebooting, etc., by executing "/sbin/sysctl -p".

Note that there are many related pages on the web that refer to echoing a "1" into "/proc/sys/net/ipv4/ip_forward". While this may work temporarily, it will most likely be set during reboot, so the above method should almost always be used instead.

The case for NAT

At this point, assuming there is both a public / WAN (Internet) interface and a private / LAN interface, Linux will route between them. However, assuming the LAN is configured with RFC1918 non-routable addresses e.g. 192.168.*.* (see also Private network on Wikipedia), these addresses aren't recognized on the Internet, and Linux shouldn't (and probably doesn't) even try to forward traffic from them. Even if this wasn't the case and traffic from the LAN destined for the Internet was forwarded, no other router on the Internet would know where to send the responses back to, without having configured routing rules back to the local router.

Most ISP consumers only receive one public IP address, which is typically dynamic and changes regularly. NAT, or Network Address Translation is the most commonly used method to allow multiple computers / devices on a LAN to share this one address. (This is the same method that is used by almost all SOHO routers, e.g. Linksys, Netgear, and D-Link.)

Configuring iptables and NAT

NAT is commonly implemented in Linux within iptables. At least under Ubuntu Hardy Heron, iptables is already installed and running, just using a default set of rules that effectively allows everything. (This is not necessarily an issue for a new installation as there are no services installed and running to connect to that can be compromised.)

iptables is typically configured using the "iptables" command (which is actually located at "/sbin/iptables"). Usually several steps are needed for a desired configuration, and are typically entered as separate calls to iptables. These are typically consolidated into a shell script.

Here is the simplest configuration using iptables necessary to enable sharing from a WAN to a LAN:

iptables -t nat -A POSTROUTING -o ppp0 -j MASQUERADE

Again, since this alters the configuration of the system, the commands or script containing the commands need to be run with sudo. If forgotten, an error containing "Permission denied (you must be root)" should be displayed.

At least under the default iptables configuration in Ubuntu Hardy Heron, this is all that is needed as the default policy for everything is ACCEPT. This command basically changes the source address of any outgoing packets (in this case, on ppp0) to the outgoing interface's address, so return packets are sent to the router's public / WAN address. This is really the only valid option, as the original private / LAN addresses are invalid, as described above. These masqueraded connections are then tracked, such that when a response is received, the process can be reversed, and the response is sent to the original requesting device.

Here is a more comprehensive example, one that I saved as "/etc/iptables.conf":

#!/bin/sh

logger -i $0 Starting...
iptables -F
iptables -F -t nat

#LAN_IF=eth0
WAN_IF=ppp0

if [ -d "/proc/sys/net/ipv4/conf/$WAN_IF" ]; then

 #WAN_IP=$(ifconfig $WAN_IF | sed -n 's/ *inet addr:\([0-9.]*\).*/\1/p')

 iptables -t nat -A POSTROUTING -o $WAN_IF -j MASQUERADE

fi

logger -i $0 Complete.
  • This version first flushes all existing rules ("iptables -F"), ensuring a consistent configuration.
  • The WAN-dependent portions run only if the WAN interface exists.
  • It also logs to the system logger using logger, for future diagnosis.
  • Variables e.g. "WAN_IF" are used for maintainability, making it easier to update when the configuration changes. "WAN_IP" and "LAN_IF" are currently not used, but shown for example.

This is not the most secure configuration by any means, but it's a simple solution for solving the immediate need that can be extended upon. Some additional details are available in the "Masquerading Made Simple HOWTO".

Making it Automatic

So far, everything configured with iptables will be lost on reboot. The best way I found to handle this is by placing a link to the above "/etc/iptables.conf" in "/etc/network/if-up.d". Use "ln -s" to do this, or write a new script that calls the above. The same may be desirable for capturing when the interface goes down, using "/etc/network/if-down.d". As documented in run-parts, ensure that any desired files are marked executable, and follow the proper naming (consist entirely of upper and lower case letters, digits, underscores, and hyphens - no periods!).

For connections that utilize IP leases using DHCP, and if utilizing a DHCP IP address within iptables, it is probably also necessary to make sure that the rules are updated by re-running iptables when a lease is renewed. Similar to the above, scripts can be put in or linked to from the "dhclient-enter-hooks.d" and/or "dhclient-exit-hooks.d" directories, which executed by the default "/sbin/dhclient-script" script when using dhclient.

As I'm currently using a PPP connection which doesn't utilize DHCP, this wasn't necessary for me. Instead, I tied into the PPP connection process by linking to "/etc/iptables.conf" in "/etc/ppp/ip-up.d", same as described above.

To be continued...

Next up: Configuring LAN DHCP and Dynamic DNS.

Sunday, October 19, 2008

Configuring Persistent PPP under Ubuntu

(This post is the 4th part of my Ubuntu Linux Router Upgrade Project.)

After my last post, Alltel UM175AL USB EVDO under Ubuntu Hardy Heron, I now have a usable serial device to connect to with PPP.

Initial Trials

At this point, Jason Costomiris's instructions at HOWTO: Verizon UM175 USB EVDO Card under Ubuntu Hardy work perfectly. The only difference is that being on Alltel instead of Verizon, the username is "<Modem'sPhoneNumber>@alltel.net", and the password is "alltel".

Disabling NetworkManager

The next improvement is to upgrade the NetworkManager utility that is part of the ubuntu-desktop package, in order for it to recognize and work with the CDMA connection. This is detailed in Jason's next post, Using NetworkManager with Pantech UM175 under Ubuntu Hardy, and allows NetworkManager to respond with the proper connection status when queried by other applications. This also makes it easier to establish the connection automatically.

Unfortunately, NetworkManager does not play well with the goals of a server or router configuration. As listed on its package page at http://packages.ubuntu.com/hardy/base/network-manager:

NetworkManager attempts to keep an active network connection available at all times. It is intended only for the desktop use-case, and is not intended for usage on servers. The point of NetworkManager is to make networking configuration and setup as painless and automatic as possible. If using DHCP, NetworkManager is _intended_ to replace default routes, obtain IP addresses from a DHCP server, and change nameservers whenever it sees fit.

As suggested, I disabled NetworkManager. There are instructions at https://help.ubuntu.com/community/NetworkManager#Disabling%20NetworkManager. However, at least under 8.10 (Intrepid Ibex), placing the "exit" scripts in "/etc/default" didn't have any effect. Instead, I just disabled the startup scripts, by calling "update-rc.d -f NetworkManager remove" (again, using sudo).

The next step is to manually configure the network settings in "/etc/network/interfaces". (See "man interfaces" for details.) Primarily, ensure that the desired interfaces are brought up automatically on startup (using the "auto" stanza), and for the purposes of a router, assigning the internal interface a static IP address:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.###.1
netmask 255.255.255.0

pppd Configuration

This is fully documented in the pppd man page. You could even use pppconfig to help with the initial setup, but I opted to work with the actual configuration files directly.

Here is what I saved as "/etc/ppp/peers/Alltel":

/dev/ttyACM0
lock

persist
#debug
hide-password

noauth
user ##########@alltel.net

defaultroute
#usepeerdns #Using bind9 instead.

init "/usr/bin/logger -i /etc/ppp/peers/Alltel Calling..."
connect "/usr/sbin/chat -Vf /etc/chatscripts/Alltel 2>/var/log/Alltel.log"

Here is what I saved as "/etc/chatscripts/Alltel":

ABORT 'BUSY'
ABORT 'NO CARRIER'
ABORT 'ERROR'
'' 'AT'
'OK' 'ATQ0V1E0'
'OK' 'ATZ'
'OK' 'AT&F'
'OK' 'AT+CSQ'
'OK' 'ATDT#777'
CONNECT CLIENT

This and other chatscripts are used by and documented in chat.

Finally, the password for the user identified in the peers file needs to be added to "/etc/ppp/pap-secrets":

"##########@alltel.net" Alltel "alltel"

The connection can then manually be initiated using "pppd call <name>", where "<name>" is one of the scripts configured under "/etc/ppp/peers". Note that the "nodetach debug" options are only to diagnose the initial connection, and the "nodetach" option will prevent the call from returning. (Closing the terminal window will disconnect the connection.) Note also that this needs to be run with "sudo" otherwise pppd won't have access to the above configured scripts, which should also be saved with restricted permissions to "root" (or another restricted user account).

$ sudo pppd call Alltel nodetach debug
Serial port initialized.
Serial connection established.
using channel 34
Using interface ppp0
Connect: ppp0 <--> /dev/ttyACM0
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x63dc86e0> <pcomp> <accomp>]
sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x63dc86e0> <pcomp> <accomp>]
rcvd [LCP ConfReq id=0x1 <asyncmap 0x0> <auth chap MD5> <magic 0xfd80aab9> <pcomp> <accomp>]
sent [LCP ConfNak id=0x1 <auth pap>]
rcvd [LCP ConfAck id=0x1 <asyncmap 0x0> <magic 0x63dc86e0> <pcomp> <accomp>]
rcvd [LCP ConfReq id=0x2 <asyncmap 0x0> <auth pap> <magic 0xfd80aab9> <pcomp> <accomp>]
sent [LCP ConfAck id=0x2 <asyncmap 0x0> <auth pap> <magic 0xfd80aab9> <pcomp> <accomp>]
sent [LCP EchoReq id=0x0 magic=0x63dc86e0]
sent [PAP AuthReq id=0x1 user="##########@alltel.net" password=<hidden>]
rcvd [LCP EchoRep id=0x0 magic=0xfd80aab9]
rcvd [PAP AuthAck id=0x1 ""]
PAP authentication succeeded
sent [CCP ConfReq id=0x1 <deflate 15> <deflate(old#) 15> <bsd v1 15>]
sent [IPCP ConfReq id=0x1 <compress VJ 0f 01> <addr 0.0.0.0>]
rcvd [IPCP ConfReq id=0x1 <addr ###.###.###.###>]
sent [IPCP ConfAck id=0x1 <addr ###.###.###.###>]
rcvd [LCP ProtRej id=0x4 80 fd 01 01 00 0f 1a 04 78 00 18 04 78 00 15 03 2f]
Protocol-Reject for 'Compression Control Protocol' (0x80fd) received
rcvd [IPCP ConfRej id=0x1 <compress VJ 0f 01>]
sent [IPCP ConfReq id=0x2 <addr 0.0.0.0>]
rcvd [IPCP ConfNak id=0x2 <addr ###.###.###.###>]
sent [IPCP ConfReq id=0x3 <addr ###.###.###.###>]
rcvd [IPCP ConfAck id=0x3 <addr ###.###.###.###>]
Cannot determine ethernet address for proxy ARP
local  IP address ###.###.###.###
remote IP address ###.###.###.###
Script /etc/ppp/ip-up started (pid 12340)
Script /etc/ppp/ip-up finished (pid 12340), status = 0x0

Once the connection is successfully established without "nodetach", there doesn't appear to be a "hangup" option to pair with "call". It seems that using "kill" to kill the associated pppd process is the most common approach. The "pon" and "poff" scripts may also be used, but internally, "poff" just calls "kill" as well.

Integrating with /etc/network/interfaces

By adding a reference to the above pppd configuration in "/etc/network/interfaces", the connection can be easily managed like any of the other interfaces with commands like "ifup" and "ifdown" (both of which are actually the same program, but called with different names).

auto ppp0
iface ppp0 inet ppp
 provider Alltel

Bringing up the connection then yields an error:

$ sudo ifup ppp0
ppp0: ERROR while getting interface flags: No such device

While this doesn't look good, the connection is actually successfully established. This can be verified by seeing a connection for "ppp0" using "ifconfig". Calling ifup with the "-v" flag for verbose mode shows some detail as to where the error is occurring:

$ sudo ifup -v ppp0
Configuring interface ppp0=ppp0 (inet)
run-parts --verbose /etc/network/if-pre-up.d
run-parts: executing /etc/network/if-pre-up.d/vlan
run-parts: executing /etc/network/if-pre-up.d/wireless-tools
ppp0: ERROR while getting interface flags: No such device
run-parts: executing /etc/network/if-pre-up.d/wpasupplicant
pon Alltel
run-parts --verbose /etc/network/if-up.d
run-parts: executing /etc/network/if-up.d/avahi-autoipd
run-parts: executing /etc/network/if-up.d/avahi-daemon
run-parts: executing /etc/network/if-up.d/ip
run-parts: executing /etc/network/if-up.d/mountnfs
run-parts: executing /etc/network/if-up.d/wpasupplicant

It is actually due to the "wireless-tools" script, apparently on line #11 where it calls "$IFCONFIG "$IFACE"" up". It seems that "$IFACE" resolves to "" at this point, as the "/etc/network/if-*up.d/" scripts are called before pppd actually brings up the connection's interface. The entire script could probably be selectively disabled, but it isn't hurting anything for now.

At another point, I added a link to an iptables script in "/etc/network/if-up.d/". Similar to the above situation, "ppp0" isn't yet available at this point. The script called ifconfig to determine ppp0's IP address, which resulted in the following error:

ppp0: error fetching interface information: Device not found

That's it. At this point, I have a functional, persistent Internet connection over PPP for the local computer.

To be continued...

Next up: Enabling routing and NAT with iptables.