Answering several points at once:
* RFC2181 clarifies what the underlying protocol supports. It didn't change anything.
* DNS has always been this open; it's up to the application layer to impose restrictions. If you don't put in the restrictions yourself, you have no restrictions, so don't forget them. Then remember that common usage doesn't always match specifications -- eg, URLs often do end up with underscores in them.
* There isn't actually a generic specification for hostnames that I've ever found. There's old rules which applied to HOSTS.TXT; there are rules for mail domains; there are rules for hostnames as encountered in URLs. That's about it.
* Escaping: with modern Bind, you might need to export IDN_DISABLE=t into the environment before running dig(1) and friends, to avoid the IDN errors that might get thrown for parse issues. With that in mind, there is actually a quoting mechanism in common use, derived from RFC1035 section 5.1. RFC4343 is the one which extends this to be used more generally.
* If you validate hostnames from user input, don't forget to consider hostnames which you get from reverse DNS or from CNAME lookups, or NAPTR. You might leave it unmolested for pass-back to the DNS layer, but if you're passing it up, consider escaping.
* Whatever beliefs people have about what's allowed in a hostname: fine, now just make sure your code enforces it. :)