diff --git a/src/Makefile b/src/Makefile index 61fa03c..44f09dd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,10 +5,15 @@ CFLAGS = -g -O2 PERL = perl INSTALL = install +whois_OBJECTS := whois.o utils.o +mkpasswd_OBJECTS := mkpasswd.o utils.o + +############################################################################## # Solaris -#whois_LDADD += -lnsl -lsocket +#whois_LDADD += -lnsl -lsocket -liconv # FreeBSD +#whois_LDADD += -liconv #LIBS += -L/usr/local/lib -lintl #INCLUDES += -I/usr/local/include @@ -16,6 +21,9 @@ INSTALL = install #whois_LDADD += -lsocket #LDFLAGS += -Zexe -Dstrncasecmp=strnicmp +# OS X +#whois_LDADD += -liconv + ifdef CONFIG_FILE DEFS += -DCONFIG_FILE=\"$(CONFIG_FILE)\" endif @@ -29,6 +37,11 @@ whois_LDADD += -lidn DEFS += -DHAVE_LIBIDN endif +ifdef HAVE_ICONV +whois_OBJECTS += simple_recode.o +DEFS += -DHAVE_ICONV +endif + ifdef HAVE_XCRYPT mkpasswd_LDADD += -lxcrypt DEFS += -DHAVE_XCRYPT @@ -36,11 +49,9 @@ else mkpasswd_LDADD += -lcrypt endif +############################################################################## all: Makefile.depend whois mkpasswd #pos -whois_OBJECTS := whois.o utils.o -mkpasswd_OBJECTS := mkpasswd.o utils.o - ############################################################################## %.o: %.c $(CC) $(DEFS) $(INCLUDES) $(CFLAGS) -c $< @@ -53,19 +64,22 @@ mkpasswd: $(mkpasswd_OBJECTS) ############################################################################## as_del.h: as_del_list make_as_del.pl - $(PERL) -w make_as_del.pl < as_del_list > $@ + $(PERL) -w make_as_del.pl < $< > $@ as32_del.h: as32_del_list make_as32_del.pl - $(PERL) -w make_as32_del.pl < as32_del_list > $@ + $(PERL) -w make_as32_del.pl < $< > $@ ip_del.h: ip_del_list make_ip_del.pl - $(PERL) -w make_ip_del.pl < ip_del_list > $@ + $(PERL) -w make_ip_del.pl < $< > $@ ip6_del.h: ip6_del_list make_ip6_del.pl - $(PERL) -w make_ip6_del.pl < ip6_del_list > $@ + $(PERL) -w make_ip6_del.pl < $< > $@ tld_serv.h: tld_serv_list make_tld_serv.pl - $(PERL) -w make_tld_serv.pl < tld_serv_list > $@ + $(PERL) -w make_tld_serv.pl < $< > $@ + +servers_charset.h: servers_charset_list make_servers_charset.pl + $(PERL) -w make_servers_charset.pl < $< > $@ ############################################################################## install: install-whois install-mkpasswd #install-pos diff --git a/src/config.h b/src/config.h index 2b88b45..7d7f415 100644 --- a/src/config.h +++ b/src/config.h @@ -1,5 +1,5 @@ /* Program version */ -#define VERSION "4.7.37" +#define VERSION "5.0.0" /* Configurable features */ diff --git a/src/data.h b/src/data.h index e20d396..89984fa 100644 --- a/src/data.h +++ b/src/data.h @@ -21,8 +21,10 @@ const char *ripe_servers[] = { "whois.arnes.si", "www.registry.co.ug", "whois.nic.ir", + "whois.pandi.or.id", "whois.nic.ck", "whois.ra.net", + "whois.bgpmon.net", NULL }; @@ -74,12 +76,6 @@ const char *nic_handles[] = { "poem-", "whois.ripe.net", "form-", "whois.ripe.net", "pgpkey-", "whois.ripe.net", -#if 0 - // commented until somebody will explain the query format for these - "coco-", "whois.corenic.net", - "coho-", "whois.corenic.net", - "core-", "whois.corenic.net", -#endif "denic-", "whois.denic.de", /* RPSL objects */ "as-", "whois.ripe.net", @@ -139,3 +135,16 @@ const char *tld_serv[] = { NULL, NULL }; +#ifdef HAVE_ICONV +struct server_charset { + const char *name; + const char *charset; + const char *options; +}; + +const struct server_charset servers_charset[] = { +#include "servers_charset.h" + { NULL, NULL, NULL } +}; +#endif + diff --git a/src/debian/changelog b/src/debian/changelog index bf5fa59..c19264e 100644 --- a/src/debian/changelog +++ b/src/debian/changelog @@ -1,3 +1,16 @@ +whois (5.0.0) unstable; urgency=low + + * Added optional support for automatically transcoding the output of + servers. (Closes: #363366, #402356) + * Automatically add --show-handles to queries for .dk domains. + * Normalize the querystring and convert it to punycode even if a server + is specified on the command line. + * Updated the .id, .is, .my, .sb and .tj TLD servers. + * Removed the .mm, .pw, .sr and .tp TLD servers. + * Cleaned up the horrible strings manipulation code. (Closes: #131924) + + -- Marco d'Itri Sun, 20 Dec 2009 03:01:40 +0100 + whois (4.7.37) unstable; urgency=medium * Added new ASN allocations. diff --git a/src/debian/rules b/src/debian/rules index 09fceb8..558b921 100755 --- a/src/debian/rules +++ b/src/debian/rules @@ -9,7 +9,7 @@ VERSION := $(shell dpkg-parsechangelog | sed -n 's/\+.*$$//; /^Version/s/.* //p' build: dh_testdir - $(MAKE) CONFIG_FILE="/etc/whois.conf" HAVE_LIBIDN=1 + $(MAKE) CONFIG_FILE="/etc/whois.conf" HAVE_LIBIDN=1 HAVE_ICONV=1 cd po && $(MAKE) whois.pot touch $@ diff --git a/src/make_servers_charset.pl b/src/make_servers_charset.pl new file mode 100755 index 0000000..8517b75 --- /dev/null +++ b/src/make_servers_charset.pl @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +use warnings; +use strict; + +while (<>) { + chomp; + s/#.*$//; + s/^\s+//; s/\s+$//; + next if /^$/; + + die "format error: $_" unless + (my ($a, $b, $c) = /^([a-z0-9.-]+)\s+([a-z0-9-]+)(?:\s+(.+))?$/); + + if ($c) { + print qq( { "$a",\t"$b",\t"$c" },\n); + } else { + print qq( { "$a",\t"$b",\tNULL },\n); + } +} + diff --git a/src/servers_charset.h b/src/servers_charset.h new file mode 100644 index 0000000..d8539ee --- /dev/null +++ b/src/servers_charset.h @@ -0,0 +1,34 @@ + { "whois.corenic.net", "utf-8", "-C UTF-8" }, + { "whois.cat", "utf-8", "-C UTF-8" }, + { "whois.museum", "utf-8", "-C UTF-8" }, + { "whois.nic.br", "iso-8859-1", NULL }, + { "whois.cira.ca", "iso-8859-1", NULL }, + { "whois.nic.ch", "utf-8", NULL }, + { "whois.nic.cl", "iso-8859-1", NULL }, + { "whois.cnnic.net.cn", "utf-8", NULL }, + { "whois.nic.cz", "utf-8", NULL }, + { "whois.denic.de", "utf-8", NULL }, + { "whois.enum.denic.de", "utf-8", NULL }, + { "whois.dk-hostmaster.dk", "utf-8", "--charset=utf-8" }, + { "whois.eenet.ee", "iso-8859-1", NULL }, + { "whois.eu", "utf-8", NULL }, + { "whois.ficora.fi", "iso-8859-1", NULL }, + { "whois.nic.fr", "iso-8859-1", NULL }, + { "whois.hkdnr.net.hk", "utf-8", NULL }, + { "whois.nic.hu", "iso-8859-1", NULL }, + { "whois.isnic.is", "iso-8859-1", NULL }, + { "whois.jprs.jp", "iso-2022-jp", NULL }, + { "whois.nic.ad.jp", "iso-2022-jp", NULL }, + { "whois.nic.or.kr", "euc-kr", NULL }, + { "whois.nic.li", "utf-8", NULL }, + { "whois.domreg.lt", "utf-8", NULL }, + { "whois.dns.lu", "iso-8859-1", NULL }, + { "whois.nic.mu", "utf-8", NULL }, + { "whois.norid.no", "iso-8859-1", NULL }, + { "whois.nic.nu", "utf-8", NULL }, + { "whois.dns.pt", "iso-8859-1", NULL }, + { "whois.nic-se.se", "utf-8", NULL }, + { "whois.nic.tr", "utf-8", NULL }, + { "whois.twnic.net", "utf-8", NULL }, + { "whois.net.ua", "koi8-u", NULL }, + { "whois.nic.org.uy", "utf-8", NULL }, diff --git a/src/servers_charset_list b/src/servers_charset_list new file mode 100644 index 0000000..d2b6e77 --- /dev/null +++ b/src/servers_charset_list @@ -0,0 +1,37 @@ +# server name charset optional parameters +whois.corenic.net utf-8 -C UTF-8 +whois.cat utf-8 -C UTF-8 +whois.museum utf-8 -C UTF-8 + +whois.nic.br iso-8859-1 +whois.cira.ca iso-8859-1 +whois.nic.ch utf-8 +whois.nic.cl iso-8859-1 +whois.cnnic.net.cn utf-8 +whois.nic.cz utf-8 +whois.denic.de utf-8 +whois.enum.denic.de utf-8 +whois.dk-hostmaster.dk utf-8 --charset=utf-8 +whois.eenet.ee iso-8859-1 +whois.eu utf-8 +whois.ficora.fi iso-8859-1 +whois.nic.fr iso-8859-1 +whois.hkdnr.net.hk utf-8 +whois.nic.hu iso-8859-1 +whois.isnic.is iso-8859-1 +whois.jprs.jp iso-2022-jp +whois.nic.ad.jp iso-2022-jp +whois.nic.or.kr euc-kr +whois.nic.li utf-8 +whois.domreg.lt utf-8 +whois.dns.lu iso-8859-1 +whois.nic.mu utf-8 +whois.norid.no iso-8859-1 +whois.nic.nu utf-8 +whois.dns.pt iso-8859-1 +whois.nic-se.se utf-8 +whois.nic.tr utf-8 +whois.twnic.net utf-8 +whois.net.ua koi8-u +whois.nic.org.uy utf-8 + diff --git a/src/simple_recode.c b/src/simple_recode.c new file mode 100644 index 0000000..e7d9bb5 --- /dev/null +++ b/src/simple_recode.c @@ -0,0 +1,176 @@ +/* + * Copyright 2009 by Marco d'Itri . + * + * simple_recode was inspired by a similar function found in Simon + * Josefsson's libidn. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +#include "simple_recode.h" + +/* Global variables */ +iconv_t simple_recode_iconv_handle; +const char *simple_recode_input_charset; + +/* + * These value should be tuned to an acceptable compromise between memory + * usage and calling iconv(3) as few times as possible. + */ +#define SIMPLE_RECODE_BUFFER_SIZE_1 256 +#define SIMPLE_RECODE_BUFFER_SIZE_2 1024 +#define SIMPLE_RECODE_BUFFER_INCREMENT 1 + +/* + * Convert a NULL-terminated string accordingly to the provided iconv(3) + * handle. The returned string is allocated using malloc(3) and needs to be + * deallocated by the caller. + * Incomplete, invalid and impossible to recode sequences are copied as-is. + * On failure, NULL is returned and errno is set. + */ +char *simple_recode(const iconv_t handle, const char *str) +{ + char *inp = (char *) str; + char *outp, *result; + size_t inbytes_remaining, outbytes_remaining, outbuf_size; + + inbytes_remaining = strlen(inp); + if (inbytes_remaining + 1 <= SIMPLE_RECODE_BUFFER_SIZE_1 + - (SIMPLE_RECODE_BUFFER_SIZE_1 >> SIMPLE_RECODE_BUFFER_INCREMENT)) + outbuf_size = SIMPLE_RECODE_BUFFER_SIZE_1; + else + outbuf_size = inbytes_remaining + 1 + + (inbytes_remaining >> SIMPLE_RECODE_BUFFER_INCREMENT); + + outp = result = malloc(outbuf_size); + if (!result) + return NULL; + outbytes_remaining = outbuf_size - 1; + + do { + size_t err = iconv(handle, &inp, &inbytes_remaining, &outp, + &outbytes_remaining); + + if (err != -1) + break; /* success */ + + switch (errno) { + case EINVAL: /* incomplete multibyte sequence */ + case EILSEQ: /* invalid multibyte sequence */ +#ifdef SIMPLE_RECODE_SKIP_INVALID_SEQUENCES + /* recover from invalid input by replacing it with a '?' */ + inp++; + *outp++ = '?'; /* use U+FFFD for unicode output? how? */ +#else + /* garbage in, garbage out */ + *outp++ = *inp++; +#endif + inbytes_remaining--; + outbytes_remaining--; + continue; + + case E2BIG: + { + size_t used = outp - result; + size_t newsize; + + if (outbuf_size < SIMPLE_RECODE_BUFFER_SIZE_2) + newsize = SIMPLE_RECODE_BUFFER_SIZE_2; + else + newsize = outbuf_size + + (outbuf_size >> SIMPLE_RECODE_BUFFER_INCREMENT); + + /* check if the newsize variable has overflowed */ + if (newsize <= outbuf_size) { + errno = ENOMEM; + return NULL; + } + outbuf_size = newsize; + result = realloc(result, outbuf_size); + if (!result) + return NULL; + + /* update the position in the new output stream */ + outp = result + used; + outbytes_remaining = outbuf_size - used - 1; + + continue; + } + + default: + return NULL; + } + } while (inbytes_remaining > 0); + + *outp = '\0'; + + return result; +} + +/* + * Like fputs(3), but transparently recodes s using the global variable + * simple_recode_input_charset as the input charset and the current locale + * as the output charset. + * If simple_recode_input_charset is NULL it just calls fputs(3). + * Exits with an error if iconv(3) or iconv_open(3) fail. + * + * Assumes that setlocale(3) has already been called. + * + * If appropriate, the iconv object referenced by the global variable + * simple_recode_iconv_handle should be deallocated with iconv_close(3). + */ +int recode_fputs(const char *s, FILE *stream) +{ + char *out; + int result; + + if (simple_recode_input_charset == NULL) /* no conversion is needed */ + return fputs(s, stream); + + if (simple_recode_iconv_handle == NULL) { + simple_recode_iconv_handle = iconv_open(nl_langinfo(CODESET), + simple_recode_input_charset); + if (simple_recode_iconv_handle == (iconv_t) - 1) + err_sys("iconv_open"); + } + + out = simple_recode(simple_recode_iconv_handle, s); + if (!out) + err_sys("iconv"); + result = fputs(out, stream); + free(out); + + return result; +} + +void simple_recode_iconv_close(void) +{ + if (simple_recode_iconv_handle == NULL) + return; + + iconv_close(simple_recode_iconv_handle); + simple_recode_iconv_handle = NULL; + simple_recode_input_charset = NULL; +} + diff --git a/src/simple_recode.h b/src/simple_recode.h new file mode 100644 index 0000000..5fc95b0 --- /dev/null +++ b/src/simple_recode.h @@ -0,0 +1,14 @@ +#ifndef SIMPLE_RECODE_H +#define SIMPLE_RECODE_H + +#include +#include + +extern iconv_t simple_recode_iconv_handle; +extern const char *simple_recode_input_charset; + +char *simple_recode(const iconv_t handle, const char *str); +int recode_fputs(const char *s, FILE* stream); +void simple_recode_iconv_close(void); + +#endif diff --git a/src/tld_serv_list b/src/tld_serv_list index 81231fe..5cc6e80 100644 --- a/src/tld_serv_list +++ b/src/tld_serv_list @@ -80,6 +80,7 @@ .bh NONE # www.inet.com.bh .bi WEB http://whois.nic.bi/register/whois.hei # whois.nic.bi wrong referral? .bj whois.nic.bj +#.bl .bm WEB http://207.228.133.14/cgi-bin/lansaweb?procfun+BMWHO+BMWHO2+WHO .bn NONE # www.brunet.bn .bo WEB http://www.nic.bo/ @@ -156,7 +157,7 @@ .hr WEB http://www.dns.hr/pretrazivanje.html .ht whois.nic.ht .hu whois.nic.hu -.id whois.idnic.net.id +.id whois.pandi.or.id .ie whois.domainregistry.ie .il whois.isoc.org.il .im whois.nic.im @@ -164,7 +165,7 @@ .io whois.nic.io .iq NONE # http://www.cmc.iq/english/iq/iqregister1.htm .ir whois.nic.ir -.is whois.isnet.is +.is whois.isnic.is .it whois.nic.it .je whois.je .jm NONE # NIC? uwimona.edu.jm http://nic.jm @@ -196,11 +197,12 @@ .mc whois.ripe.net .md WEB http://www.dns.md/wh1.php # whois.nic.md is restricted .me whois.meregistry.net # afilias +#.mf .mg whois.nic.mg .mh NONE # www.nic.net.mh .mk WEB http://dns.marnet.net.mk/registar.php .ml NONE # NIC? www.sotelma.ml -.mm whois.nic.mm +.mm NONE # NO NIC .mn AFILIAS .mo WEB http://www.monic.net.mo/ .mp NONE # get.mp @@ -212,7 +214,7 @@ .mv NONE # NIC? www.dhiraagu.com.mv .mw WEB http://www.registrar.mw/ .mx whois.nic.mx -.my whois.mynic.net.my +.my whois.domainregistry.my .mz NONE # NIC? www.uem.mz .na whois.na-nic.com.na .nc whois.cctld.nc @@ -240,7 +242,7 @@ .pr whois.nic.pr .ps WEB http://www.nic.ps/whois/whois.html .pt whois.dns.pt -.pw whois.nic.pw +.pw NONE # NO NIC .py WEB http://www.nic.py/consultas.html .qa NONE # http://www.qtel.com.qa/InternetFeatures.do .re whois.nic.fr @@ -250,7 +252,7 @@ .ru whois.ripn.net .rw WEB http://www.nic.rw/cgi-bin/whoisrw.pl .sa saudinic.net.sa -.sb whois.nic.net.sb +.sb whois.coccaregistry.net .sc AFILIAS # www.nic.sc .sd NONE # http://isoc.sd/ (CHECK LATER) .se whois.nic-se.se @@ -263,7 +265,7 @@ .sm whois.ripe.net .sn whois.nic.sn .so NONE # www.nic.so (CHECK LATER, recently delegated) -.sr whois.register.sr +.sr NONE # www.register.sr .st whois.nic.st .su whois.ripn.net .sv WEB http://www.uca.edu.sv/dns/ # http://www.svnet.org.sv/ @@ -274,13 +276,13 @@ .tf whois.nic.tf .tg WEB http://www.nic.tg/ .th whois.thnic.net -.tj whois.nic.tj +.tj WEB http://www.nic.tj/whois.html .tk whois.dot.tk .tl whois.nic.tl .tm whois.nic.tm .tn WEB http://whois.ati.tn/ .to whois.tonic.to -.tp whois.nic.tp +.tp NONE # phase out in progress .tr whois.nic.tr .tt WEB http://www.nic.tt/cgi-bin/search.pl .tv VERISIGN whois.nic.tv @@ -300,6 +302,7 @@ .parliament.uk NONE .police.uk NONE .uk whois.nic.uk +#.um .fed.us whois.nic.gov .us whois.nic.us .com.uy WEB https://nic.anteldata.com.uy/dns/ @@ -312,10 +315,10 @@ .vi WEB http://www.nic.vi/whoisform.htm .vn WEB http://www.vnnic.vn/english/ .vu WEB http://www.vunic.vu/whois.html -.wf whois.nic.wf +.wf whois.nic.fr .ws whois.samoanic.ws .ye NONE # NIC? www.nominet.org.ye http://www.y.net.ye/services/domain_name.htm -.yt whois.nic.yt +.yt whois.nic.fr .yu NONE # www.nic.yu - phase out date: 30 March 2009 .ac.za whois.ac.za .co.za whois.coza.net.za @@ -340,7 +343,7 @@ -cz whois.nic.cz -dk whois.dk-hostmaster.dk -il whois.isoc.org.il --is whois.isnet.is +-is whois.isnic.is -kg whois.domain.kg -ti whois.telstra.net -tw whois.twnic.net diff --git a/src/whois-4.6.6-gentoo-security.patch b/src/whois-4.6.6-gentoo-security.patch deleted file mode 100644 index d933b91..0000000 --- a/src/whois-4.6.6-gentoo-security.patch +++ /dev/null @@ -1,90 +0,0 @@ -The gentoo people submitted this patch. I'm not applying it as it makes -the code harder to understand with no major gain and without being a -fully correct fix. - -diff -Nru whois-4.6.6.orig/whois.c whois-4.6.6/whois.c ---- whois-4.6.6.orig/whois.c 2003-06-15 12:36:52.000000000 -0400 -+++ whois-4.6.6/whois.c 2003-08-11 02:15:30.000000000 -0400 -@@ -73,12 +73,12 @@ - /* RIPE flags */ - if (strchr(ripeflags, ch)) { - for (p = fstring; *p; p++); -- sprintf(p--, "-%c ", ch); -+ snprintf(p--, sizeof(fstring), "-%c ", ch); - continue; - } - if (strchr(ripeflagsp, ch)) { - for (p = fstring; *p; p++); -- sprintf(p--, "-%c %s ", ch, optarg); -+ snprintf(p--, sizeof(fstring), "-%c %s ", ch, optarg); - if (ch == 't' || ch == 'v' || ch == 'q') - nopar = 1; - continue; -@@ -132,10 +132,10 @@ - while (1) { - qslen += strlen(*argv) + 1 + 1; - qstring = realloc(qstring, qslen); -- strcat(qstring, *argv++); -+ strncat(qstring, *argv++, qslen-1); - if (argc == 1) - break; -- strcat(qstring, " "); -+ strncat(qstring, " ", qslen); - argc--; - } - } -@@ -401,10 +401,13 @@ - { - char *buf; - int i, isripe = 0; -+ int buflen = 0; - - /* +10 for CORE; +2 for \r\n; +1 for NULL */ -- buf = malloc(strlen(flags) + strlen(query) + strlen(client_tag) + 4 -- + 10 + 2 + 1); -+ buflen = (strlen(flags) + strlen(query) + strlen(client_tag) + 4 + 10 + 2 + 1); -+ -+ buf = malloc(buflen); -+ - *buf = '\0'; - for (i = 0; ripe_servers[i]; i++) - if (strcmp(server, ripe_servers[i]) == 0) { -@@ -426,23 +429,23 @@ - if (*flags) { - if (!isripe && strcmp(server, "whois.corenic.net") != 0) - puts(_("Warning: RIPE flags used with a traditional server.")); -- strcat(buf, flags); -+ strncat(buf, flags, buflen); - } - /* FIXME: /e is not applied to .JP ASN */ - if (!isripe && (strcmp(server, "whois.nic.mil") == 0 || - strcmp(server, "whois.nic.ad.jp") == 0) && - strncasecmp(query, "AS", 2) == 0 && isasciidigit(query[2])) -- sprintf(buf, "AS %s", query + 2); /* fix query for DDN */ -+ snprintf(buf, buflen, "AS %s", query + 2); /* fix query for DDN */ - else if (!isripe && strcmp(server, "whois.corenic.net") == 0) -- sprintf(buf, "--machine %s", query); /* machine readable output */ -+ snprintf(buf, buflen, "--machine %s", query); /* machine readable output */ - else if (!isripe && strcmp(server, "whois.nic.ad.jp") == 0) { - char *lang = getenv("LANG"); /* not a perfect check, but... */ - if (!lang || (strncmp(lang, "ja", 2) != 0)) -- sprintf(buf, "%s/e", query); /* ask for english text */ -+ snprintf(buf, buflen, "%s/e", query); /* ask for english text */ - else -- strcat(buf, query); -+ strncat(buf, query, buflen); - } else -- strcat(buf, query); -+ strncat(buf, query, buflen); - return buf; - } - -@@ -485,7 +488,7 @@ - - if (verb) - printf(_("Detected referral to %s on %s.\n"), nq, nh); -- strcat(nq, "\r\n"); -+ strncat(nq, "\r\n", sizeof(nq)); - fd = openconn(nh, np); - do_query(fd, nq); - continue; diff --git a/src/whois.1 b/src/whois.1 index 62cb204..5607789 100644 --- a/src/whois.1 +++ b/src/whois.1 @@ -1,4 +1,4 @@ -.TH "WHOIS" "1" "4 March 2005" "Marco d'Itri" "Debian GNU/Linux" +.TH "WHOIS" "1" "20 December 2009" "Marco d'Itri" "Debian GNU/Linux" .SH "NAME" whois \- client for the whois directory service .SH "SYNOPSIS" @@ -52,17 +52,20 @@ only search in the domains database. If you want to search for NIC handles you have to prepend a \fI!\fP character. When you do this, the default server becomes \fIwhois.networksolutions.com\fP. .P -When querying \fIwhois.nic.mil\fP for AS numbers, the program will +When querying \fIwhois.arin.net\fP for IPv4 or IPv6 networks, the CIDR +netmask length will be automatically removed from the query string. +.P +When querying \fIwhois.nic.ad.jp\fP for AS numbers, the program will automatically convert the request in the appropriate format, inserting a space after the string \fIAS\fP. .P -If the program is compiled with IDN support, when querying -\fIwhois.denic.de\fP for domain names it will automatically add the flags -\fI\-T dn,ace \-C US\-ASCII\fP if no flags have been specified by the user. -The domain name will \fBalways\fP be IDN-encoded. +When querying \fIwhois.denic.de\fP for domain names and no other +flags have been specified, the program will automatically add the flag +\fI\-T dn\fP. .P -When querying \fIwhois.corenic.net\fP, the program will automatically -request machine-readable output. +When querying \fIwhois.dk-hostmaster.dk\fP for domain names and no other +flags have been specified, the program will automatically add the flag +\fI\-\-show\-handles\fP. .P RIPE-specific command line options are ignored when querying non-RIPE servers. This may or may not be the behaviour intended by the user. @@ -75,6 +78,12 @@ to find a server before applying the normal rules. Each line of the file should contain a regular expression to be matched against the query text and the whois server to use, separated by white space. .P +The whois protocol does not specify an encoding for characters which +cannot be represented by ASCII and implementations vary wildly. +If the program knows that a specific server uses a certain encoding, +if needed it will transcode the server output to the encoding specified +by the current system locale. +.P Command line arguments will always be interpreted accordingly to the current system locale and converted to the IDN ASCII Compatible Encoding. .SH "Files" @@ -82,8 +91,8 @@ current system locale and converted to the IDN ASCII Compatible Encoding. .SH "ENVIRONMENT" .IP LANG When querying \fIwhois.nic.ad.jp\fP and \fIwhois.jprs.jp\fP english text -is requested unless the -\fILANG\fP environment variable specifies a Japanese locale. +is requested unless the \fILANG\fP or \fILC_MESSAGES\fP environment +variables specify a Japanese locale. .IP "WHOIS_OPTIONS" A list of options which will be evalued before the ones specified on the command line. @@ -101,9 +110,9 @@ the help file which can be obtained with the command: .IP .B whois \-h whois.ripe.net HELP .SH "BUGS" -The program has many buffer overflows when parsing the command line -parameters: be sure to not pass untrusted data to it. -It will be rewritten to use a dynamic strings library. +The program may have buffer overflows in the command line parser: +be sure to not pass untrusted data to it. +It should be rewritten to use a dynamics strings library. .SH "HISTORY" This program closely tracks the user interface of the whois client developed at RIPE by Ambrose Magee and others on the base of the diff --git a/src/whois.c b/src/whois.c index c6f36a5..0be8d30 100644 --- a/src/whois.c +++ b/src/whois.c @@ -40,6 +40,12 @@ #include "whois.h" #include "utils.h" +#ifdef HAVE_ICONV +#include "simple_recode.h" +#else +#define recode_fputs(a, b) fputs(a, b) +#endif + /* hack */ #define malloc(s) NOFAIL(malloc(s)) #define realloc(p, s) NOFAIL(realloc(p, s)) @@ -48,12 +54,12 @@ int sockfd, verb = 0; #ifdef ALWAYS_HIDE_DISCL -int hide_discl = HIDE_UNSTARTED; +int hide_discl = HIDE_NOT_STARTED; #else int hide_discl = HIDE_DISABLED; #endif -const char *client_tag = (char *)IDSTRING; +const char *client_tag = IDSTRING; #ifdef HAVE_GETOPT_LONG static const struct option longopts[] = { @@ -72,9 +78,9 @@ extern int optind; int main(int argc, char *argv[]) { - int ch, nopar = 0; + int ch, nopar = 0, fstringlen = 64; const char *server = NULL, *port = NULL; - char *p, *qstring, fstring[64] = "\0"; + char *qstring, *fstring; #ifdef ENABLE_NLS setlocale(LC_ALL, ""); @@ -82,6 +88,9 @@ int main(int argc, char *argv[]) textdomain(NLS_CAT_NAME); #endif + fstring = malloc(fstringlen + 1); + *fstring = '\0'; + /* prepend options from environment */ argv = merge_args(getenv("WHOIS_OPTIONS"), argv, &argc); @@ -89,13 +98,20 @@ int main(int argc, char *argv[]) "abBcdFg:Gh:Hi:KlLmMp:q:rRs:St:T:v:V:x", longopts, 0)) > 0) { /* RIPE flags */ if (strchr(ripeflags, ch)) { - for (p = fstring; *p; p++); - sprintf(p--, "-%c ", ch); + if (strlen(fstring) + 3 > fstringlen) { + fstringlen += 3; + fstring = realloc(fstring, fstringlen + 1); + } + sprintf(fstring + strlen(fstring), "-%c ", ch); continue; } if (strchr(ripeflagsp, ch)) { - for (p = fstring; *p; p++); - snprintf(p--, sizeof(fstring), "-%c %s ", ch, optarg); + int flaglen = 3 + strlen(optarg) + 1; + if (strlen(fstring) + flaglen > fstringlen) { + fstringlen += flaglen; + fstring = realloc(fstring, fstringlen + 1); + } + sprintf(fstring + strlen(fstring), "-%c %s ", ch, optarg); if (ch == 't' || ch == 'v' || ch == 'q') nopar = 1; continue; @@ -108,7 +124,7 @@ int main(int argc, char *argv[]) case 'V': client_tag = optarg; case 'H': - hide_discl = HIDE_UNSTARTED; /* enable disclaimers hiding */ + hide_discl = HIDE_NOT_STARTED; /* enable disclaimers hiding */ break; case 'p': port = strdup(optarg); @@ -131,16 +147,17 @@ int main(int argc, char *argv[]) usage(); /* On some systems realloc only works on non-NULL buffers */ + /* I wish I could remember which ones they are... */ qstring = malloc(64); *qstring = '\0'; /* parse other parameters, if any */ if (!nopar) { - int qslen = 0; + int qstringlen = 0; while (1) { - qslen += strlen(*argv) + 1 + 1; - qstring = realloc(qstring, qslen); + qstringlen += strlen(*argv) + 1; + qstring = realloc(qstring, qstringlen + 1); strcat(qstring, *argv++); if (argc == 1) break; @@ -154,12 +171,18 @@ int main(int argc, char *argv[]) signal(SIGALRM, alarm_handler); if (getenv("WHOIS_HIDE")) - hide_discl = HIDE_UNSTARTED; + hide_discl = HIDE_NOT_STARTED; /* -v or -t has been used */ if (!server && !*qstring) server = strdup("whois.ripe.net"); + if (*qstring) { + char *tmp = normalize_domain(qstring); + free(qstring); + qstring = tmp; + } + #ifdef CONFIG_FILE if (!server) { server = match_config_file(qstring); @@ -168,29 +191,24 @@ int main(int argc, char *argv[]) } #endif - if (!server) { - char *tmp; - - tmp = normalize_domain(qstring); - free(qstring); - qstring = tmp; - server = whichwhois(qstring); - } + if (!server) + server = guess_server(qstring); handle_query(server, port, qstring, fstring); exit(0); } -/* server may be a server name from the command line, a server name got - * from whichwhois or an encoded command/message from whichwhois. - * server and port are allocated with malloc. +/* + * Server may be a server name from the command line, a server name got + * from guess_server or an encoded command/message from guess_server. + * This function has multiple memory leaks. */ -const char *handle_query(const char *hserver, const char *hport, - const char *qstring, const char *fstring) +void handle_query(const char *hserver, const char *hport, + const char *query, const char *flags) { const char *server = NULL, *port = NULL; - char *p; + char *p, *query_string; if (hport) { server = strdup(hserver); @@ -200,6 +218,7 @@ const char *handle_query(const char *hserver, const char *hport, else split_server_port(hserver, &server, &port); + retry: switch (server[0]) { case 0: if (!(server = getenv("WHOIS_SERVER"))) @@ -209,78 +228,77 @@ const char *handle_query(const char *hserver, const char *hport, puts(_("This TLD has no whois server, but you can access the " "whois database at")); puts(server + 1); - return NULL; + return; case 3: puts(_("This TLD has no whois server.")); - return NULL; + return; case 5: puts(_("No whois server is known for this kind of object.")); - return NULL; + return; case 6: puts(_("Unknown AS number or IP network. Please upgrade this program.")); - return NULL; + return; case 4: if (verb) printf(_("Using server %s.\n"), server + 1); sockfd = openconn(server + 1, NULL); - server = query_crsnic(sockfd, qstring); + server = query_crsnic(sockfd, query); break; case 7: if (verb) printf(_("Using server %s.\n"), "whois.publicinterestregistry.net"); sockfd = openconn("whois.publicinterestregistry.net", NULL); - server = query_pir(sockfd, qstring); + server = query_pir(sockfd, query); break; case 8: if (verb) printf(_("Using server %s.\n"), "whois.afilias-grs.info"); sockfd = openconn("whois.afilias-grs.info", NULL); - server = query_afilias(sockfd, qstring); + server = query_afilias(sockfd, query); break; case 0x0A: - p = convert_6to4(qstring); - /* XXX should fail if p = 0.0.0.0 */ + p = convert_6to4(query); printf(_("\nQuerying for the IPv4 endpoint %s of a 6to4 IPv6 address.\n\n"), p); - server = whichwhois(p); - /* XXX should fail if server[0] < ' ' */ - qstring = p; /* XXX leak */ - break; + server = guess_server(p); + query = p; + goto retry; case 0x0B: - p = convert_teredo(qstring); + p = convert_teredo(query); printf(_("\nQuerying for the IPv4 endpoint %s of a Teredo IPv6 address.\n\n"), p); - server = whichwhois(p); - qstring = p ; - break; + server = guess_server(p); + query = p; + goto retry; case 0x0C: - p = convert_inaddr(qstring); - server = whichwhois(p); - break; + p = convert_inaddr(query); + server = guess_server(p); + free(p); + goto retry; default: break; } if (!server) - return NULL; + return; - p = queryformat(server, fstring, qstring); + query_string = queryformat(server, flags, query); if (verb) { printf(_("Using server %s.\n"), server); - printf(_("Query string: \"%s\"\n\n"), p); + printf(_("Query string: \"%s\"\n\n"), query_string); } sockfd = openconn(server, port); - strcat(p, "\r\n"); - server = do_query(sockfd, p); + server = do_query(sockfd, query_string); + free(query_string); /* recursion is fun */ if (server) { printf(_("\n\nFound a referral to %s.\n\n"), server); - handle_query(server, NULL, qstring, fstring); + handle_query(server, NULL, query, flags); } - return NULL; + return; } #ifdef CONFIG_FILE @@ -304,9 +322,8 @@ const char *match_config_file(const char *s) regex_t re; #endif - for (p = buf; *p; p++) - if (*p == '\n') - *p = '\0'; + if ((p = strpbrk(buf, "\r\n"))) + *p = '\0'; p = buf; while (*p == ' ' || *p == '\t') /* eat leading blanks */ @@ -359,7 +376,7 @@ const char *match_config_file(const char *s) /* Parses an user-supplied string and tries to guess the right whois server. * Returns a statically allocated buffer. */ -const char *whichwhois(const char *s) +const char *guess_server(const char *s) { unsigned long ip, as32; unsigned int i; @@ -463,6 +480,11 @@ const char *whereas(const unsigned long asn) return "\x06"; } +/* + * Construct the query string. + * Determines the server character set as a side effect. + * Returns a malloc'ed string which needs to be freed by the caller. + */ char *queryformat(const char *server, const char *flags, const char *query) { char *buf, *p; @@ -471,59 +493,75 @@ char *queryformat(const char *server, const char *flags, const char *query) /* 64 bytes reserved for server-specific flags added later */ buf = malloc(strlen(flags) + strlen(query) + strlen(client_tag) + 64); *buf = '\0'; + for (i = 0; ripe_servers[i]; i++) if (streq(server, ripe_servers[i])) { - strcat(buf, "-V "); - strcat(buf, client_tag); - strcat(buf, " "); + sprintf(buf + strlen(buf), "-V %s ", client_tag); isripe = 1; break; } + if (*flags) { - if (!isripe && !streq(server, "whois.corenic.net")) + if (!isripe) puts(_("Warning: RIPE flags used with a traditional server.")); strcat(buf, flags); } +#ifdef HAVE_ICONV + simple_recode_iconv_close(); + for (i = 0; servers_charset[i].name; i++) + if (streq(server, servers_charset[i].name)) { + simple_recode_input_charset = servers_charset[i].charset; + if (servers_charset[i].options) { + strcat(buf, servers_charset[i].options); + strcat(buf, " "); + } + break; + } +#endif + #ifdef HAVE_LIBIDN - /* why, oh why DENIC had to make whois "user friendly"? - * Do this only if the user did not use any flag. - */ - if (streq(server, "whois.denic.de") && domcmp(query, ".de") - && !strchr(query, ' ') && !*flags) - sprintf(buf, "-T dn,ace -C US-ASCII %s", query); - else - /* here we have another registrar who could not make things simple - * -C sets the language for both input and output - */ - if (!isripe && streq(server, "whois.cat") && domcmp(query, ".cat") - && !strchr(query, ' ')) - sprintf(buf, "-C US-ASCII ace %s", query); - else +# define DENIC_PARAM_ACE ",ace" +#else +# define DENIC_PARAM_ACE "" #endif - if (!isripe && (streq(server, "whois.nic.mil") || - streq(server, "whois.nic.ad.jp")) && - strncaseeq(query, "AS", 2) && isasciidigit(query[2])) - /* FIXME: /e is not applied to .JP ASN */ - sprintf(buf, "AS %s", query + 2); /* fix query for DDN */ - else if (!isripe && (streq(server, "whois.nic.ad.jp") || - streq(server, "whois.jprs.jp"))) { - char *lang = getenv("LANG"); /* not a perfect check, but... */ - if (!lang || !strneq(lang, "ja", 2)) - sprintf(buf, "%s/e", query); /* ask for english text */ - else - strcat(buf, query); - } else if (!isripe && streq(server, "whois.arin.net") && - (p = strrchr(query, '/'))) { - strncat(buf, query, p - query); /* strip CIDR */ - } else +#ifdef HAVE_ICONV +# define DENIC_PARAM_CHARSET "" +#else +# define DENIC_PARAM_CHARSET " -C US-ASCII" +#endif + + /* add useful default flags if there are no flags or multiple arguments */ + if (isripe) { } + else if (strchr(query, ' ') || *flags) { } + else if (streq(server, "whois.denic.de") && domcmp(query, ".de")) + strcat(buf, "-T dn" DENIC_PARAM_ACE DENIC_PARAM_CHARSET " "); + else if (streq(server, "whois.dk-hostmaster.dk") && domcmp(query, ".dk")) + strcat(buf, "--show-handles "); + + /* mangle and add the query string */ + if (!isripe && streq(server, "whois.nic.ad.jp") && + strncaseeq(query, "AS", 2) && isasciidigit(query[2])) { + strcat(buf, "AS "); + strcat(buf, query + 2); + } + else if (!isripe && streq(server, "whois.arin.net") && + (p = strrchr(query, '/'))) + strncat(buf, query, p - query); /* strip the mask length */ + else strcat(buf, query); + + /* ask for english text */ + if (!isripe && (streq(server, "whois.nic.ad.jp") || + streq(server, "whois.jprs.jp")) && japanese_locale()) + strcat(buf, "/e"); + return buf; } /* the first parameter contains the state of this simple state machine: * HIDE_DISABLED: hidden text finished - * HIDE_UNSTARTED: hidden text not seen yet + * HIDE_NOT_STARTED: hidden text not seen yet * >= 0: currently hiding message hide_strings[*hiding] */ int hide_line(int *hiding, const char *const line) @@ -532,7 +570,7 @@ int hide_line(int *hiding, const char *const line) if (*hiding == HIDE_DISABLED) { return 0; - } else if (*hiding == HIDE_UNSTARTED) { /* looking for smtng to hide */ + } else if (*hiding == HIDE_NOT_STARTED) { /* looking for smtng to hide */ for (i = 0; hide_strings[i] != NULL; i += 2) { if (strneq(line, hide_strings[i], strlen(hide_strings[i]))) { *hiding = i; /* start hiding */ @@ -540,7 +578,7 @@ int hide_line(int *hiding, const char *const line) } } return 0; /* don't hide this line */ - } else if (*hiding > HIDE_UNSTARTED) { /* hiding something */ + } else if (*hiding > HIDE_NOT_STARTED) { /* hiding something */ if (*hide_strings[*hiding + 1] == '\0') { /*look for a blank line?*/ if (*line == '\n' || *line == '\r' || *line == '\0') { *hiding = HIDE_DISABLED; /* stop hiding */ @@ -561,18 +599,19 @@ int hide_line(int *hiding, const char *const line) /* returns a string which should be freed by the caller, or NULL */ const char *do_query(const int sock, const char *query) { - char buf[2000], *p; + char *temp, *p, buf[2000]; FILE *fi; int hide = hide_discl; char *referral_server = NULL; + temp = malloc(strlen(query) + 2 + 1); + strcpy(temp, query); + strcat(temp, "\r\n"); + fi = fdopen(sock, "r"); - if (write(sock, query, strlen(query)) < 0) + if (write(sock, temp, strlen(temp)) < 0) err_sys("write"); -/* Using shutdown used to break the buggy RIPE server. Would this work now? - if (shutdown(sock, 1) < 0) - err_sys("shutdown"); -*/ + free(temp); while (fgets(buf, sizeof(buf), fi)) { /* 6bone-style referral: @@ -583,7 +622,7 @@ const char *do_query(const int sock, const char *query) if (sscanf(buf, REFERTO_FORMAT, nh, np, nq) == 3) { /* XXX we are ignoring the new query string */ - referral_server = malloc(300); + referral_server = malloc(strlen(nh) + 1 + strlen(np) + 1); sprintf(referral_server, "%s:%s", nh, np); } } @@ -593,32 +632,28 @@ const char *do_query(const int sock, const char *query) * ReferralServer: whois://whois.ripe.net */ if (!referral_server && strneq(buf, "ReferralServer:", 15)) { - char *q; - - q = strstr(buf, "rwhois://"); - if ((q = strstr(buf, "rwhois://"))) - referral_server = strdup(q + 9); - else if ((q = strstr(buf, "whois://"))) - referral_server = strdup(q + 8); - if (referral_server) { - if ((q = strchr(referral_server, '/')) - || (q = strchr(referral_server, '\n'))) - *q = '\0'; - } + if ((p = strstr(buf, "rwhois://"))) + referral_server = strdup(p + 9); + else if ((p = strstr(buf, "whois://"))) + referral_server = strdup(p + 8); + if (referral_server && (p = strpbrk(referral_server, "/\r\n"))) + *p = '\0'; } if (hide_line(&hide, buf)) continue; - for (p = buf; *p && *p != '\r' && *p != '\n'; p++); - *p = '\0'; - fprintf(stdout, "%s\n", buf); + if ((p = strpbrk(buf, "\r\n"))) + *p = '\0'; + recode_fputs(buf, stdout); + fputc('\n', stdout); } + if (ferror(fi)) err_sys("fgets"); fclose(fi); - if (hide > HIDE_UNSTARTED) + if (hide > HIDE_NOT_STARTED) err_quit(_("Catastrophic error: disclaimer text has been changed.\n" "Please upgrade this program.\n")); @@ -627,9 +662,10 @@ const char *do_query(const int sock, const char *query) const char *query_crsnic(const int sock, const char *query) { - char *temp, buf[2000], *ret = NULL; + char *temp, *p, buf[2000]; FILE *fi; int hide = hide_discl; + char *referral_server = NULL; int state = 0; temp = malloc(strlen(query) + 1 + 2 + 1); @@ -640,39 +676,46 @@ const char *query_crsnic(const int sock, const char *query) fi = fdopen(sock, "r"); if (write(sock, temp, strlen(temp)) < 0) err_sys("write"); + free(temp); + while (fgets(buf, sizeof(buf), fi)) { /* If there are multiple matches only the server of the first record is queried */ if (state == 0 && strneq(buf, " Domain Name:", 15)) state = 1; if (state == 1 && strneq(buf, " Whois Server:", 16)) { - char *p, *q; - for (p = buf; *p != ':'; p++); /* skip until colon */ for (p++; *p == ' '; p++); /* skip colon and spaces */ - ret = malloc(strlen(p) + 1); - for (q = ret; *p != '\n' && *p != '\r' && *p != ' '; *q++ = *p++) - ; /*copy data*/ - *q = '\0'; + referral_server = strdup(p); + if ((p = strpbrk(referral_server, "\r\n "))) + *p = '\0'; state = 2; } + /* the output must not be hidden or no data will be shown for host records and not-existing domains */ - if (!hide_line(&hide, buf)) - fputs(buf, stdout); + if (hide_line(&hide, buf)) + continue; + + if ((p = strpbrk(buf, "\r\n"))) + *p = '\0'; + recode_fputs(buf, stdout); + fputc('\n', stdout); } + if (ferror(fi)) err_sys("fgets"); + fclose(fi); - free(temp); - return ret; + return referral_server; } const char *query_pir(const int sock, const char *query) { - char *temp, buf[2000], *ret = NULL; + char *temp, *p, buf[2000]; FILE *fi; int hide = hide_discl; + char *referral_server = NULL; int state = 0; temp = malloc(strlen(query) + 5 + 2 + 1); @@ -683,6 +726,8 @@ const char *query_pir(const int sock, const char *query) fi = fdopen(sock, "r"); if (write(sock, temp, strlen(temp)) < 0) err_sys("write"); + free(temp); + while (fgets(buf, sizeof(buf), fi)) { /* If there are multiple matches only the server of the first record is queried */ @@ -691,31 +736,37 @@ const char *query_pir(const int sock, const char *query) state = 1; if (state == 1 && strneq(buf, "Registrant Street1:Whois Server:", 32)) { - char *p, *q; - for (p = buf; *p != ':'; p++); /* skip until colon */ for (p++; *p != ':'; p++); /* skip until 2nd colon */ for (p++; *p == ' '; p++); /* skip colon and spaces */ - ret = malloc(strlen(p) + 1); - for (q = ret; *p != '\n' && *p != '\r'; *q++ = *p++); /*copy data*/ - *q = '\0'; + referral_server = strdup(p); + if ((p = strpbrk(referral_server, "\r\n"))) + *p = '\0'; state = 2; } - if (!hide_line(&hide, buf)) - fputs(buf, stdout); + + if (hide_line(&hide, buf)) + continue; + + if ((p = strpbrk(buf, "\r\n"))) + *p = '\0'; + recode_fputs(buf, stdout); + fputc('\n', stdout); } + if (ferror(fi)) err_sys("fgets"); + fclose(fi); - free(temp); - return ret; + return referral_server; } const char *query_afilias(const int sock, const char *query) { - char *temp, buf[2000], *ret = NULL; + char *temp, *p, buf[2000]; FILE *fi; int hide = hide_discl; + char *referral_server = NULL; int state = 0; temp = malloc(strlen(query) + 2 + 1); @@ -725,39 +776,37 @@ const char *query_afilias(const int sock, const char *query) fi = fdopen(sock, "r"); if (write(sock, temp, strlen(temp)) < 0) err_sys("write"); + free(temp); while (fgets(buf, sizeof(buf), fi)) { if (state == 0 && strneq(buf, "Domain Name:", 12)) state = 1; if (state == 1 && strneq(buf, "Whois Server:", 13)) { - char *p, *q; - for (p = buf; *p != ':'; p++); /* skip until colon */ for (p++; *p == ' '; p++); /* skip colon and spaces */ - ret = malloc(strlen(p) + 1); - for (q = ret; *p != '\n' && *p != '\r' && *p != ' '; *q++ = *p++) - ; /*copy data*/ - *q = '\0'; + referral_server = strdup(p); + if ((p = strpbrk(referral_server, "\r\n "))) + *p = '\0'; } - if (!hide_line(&hide, buf)) { - char *p; + if (hide_line(&hide, buf)) + continue; - for (p = buf; *p && *p != '\r' && *p != '\n'; p++) - ; + if ((p = strpbrk(buf, "\r\n"))) *p = '\0'; - fprintf(stdout, "%s\n", buf); - } + recode_fputs(buf, stdout); + fputc('\n', stdout); } + if (ferror(fi)) err_sys("fgets"); fclose(fi); - if (hide > HIDE_UNSTARTED) + if (hide > HIDE_NOT_STARTED) err_quit(_("Catastrophic error: disclaimer text has been changed.\n" "Please upgrade this program.\n")); - return ret; + return referral_server; } int openconn(const char *server, const char *port) @@ -831,9 +880,9 @@ int connect_with_timeout(int fd, const struct sockaddr *addr, socklen_t addrlen, int timeout) { int savedflags, rc, connect_errno, opt; + unsigned int len; fd_set fd_w; struct timeval tv; - size_t len; if (timeout <= 0) return (connect(fd, addr, addrlen)); @@ -881,7 +930,7 @@ int connect_with_timeout(int fd, const struct sockaddr *addr, /* and report them */ if (opt != 0) { - errno = (int) &opt; + errno = opt; return -1; } @@ -900,6 +949,22 @@ void sighandler(int signum) err_quit(_("Interrupted by signal %d..."), signum); } +int japanese_locale(void) { + char *lang; + + lang = getenv("LC_MESSAGE"); + if (lang) { + if (strneq(lang, "ja", 2)) + return 0; + return 1; + } + + lang = getenv("LANG"); + if (lang && strneq(lang, "ja", 2)) + return 0; + return 1; +} + /* check if dom ends with tld */ int domcmp(const char *dom, const char *tld) { @@ -915,14 +980,20 @@ int domcmp(const char *dom, const char *tld) return 0; } +/* + * Attempt to normalize a query by removing trailing dots and whitespace, + * then convert the domain to punycode. + * The function assumes that the domain is the last token of they query. + * Returns a malloc'ed string which needs to be freed by the caller. + */ char *normalize_domain(const char *dom) { char *p, *ret; char *domain_start = NULL; ret = strdup(dom); - for (p = ret; *p; p++); p--; /* move to the last char */ /* eat trailing dots and blanks */ + p = ret + strlen(ret); for (; *p == '.' || *p == ' ' || *p == '\t' || p == ret; p--) *p = '\0'; @@ -1004,61 +1075,61 @@ void split_server_port(const char *const input, char *convert_6to4(const char *s) { - char *new = malloc(sizeof("255.255.255.255")); + char *new; unsigned int a, b; if (sscanf(s, "2002:%x:%x:", &a, &b) != 2) - return (char *) "0.0.0.0"; + return strdup("0.0.0.0"); + new = malloc(sizeof("255.255.255.255")); sprintf(new, "%d.%d.%d.%d", a >> 8, a & 0xff, b >> 8, b & 0xff); return new; } char *convert_teredo(const char *s) { - char *new = malloc(sizeof("255.255.255.255")); + char *new; unsigned int a, b; if (sscanf(s, "2001:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%*[^:]:%x:%x", &a, &b) != 2) - return (char *) "0.0.0.0"; + return strdup("0.0.0.0"); a ^= 0xffff; b ^= 0xffff; + new = malloc(sizeof("255.255.255.255")); sprintf(new, "%d.%d.%d.%d", a >> 8, a & 0xff, b >> 8, b & 0xff); return new; } char *convert_inaddr(const char *s) { - char *new = malloc(sizeof("255.255.255.255")); + char *new; char *endptr; - unsigned int a, b, c; + unsigned int a, b = 0, c = 0; errno = 0; a = strtol(s, &endptr, 10); if (errno || a < 0 || a > 255 || *endptr != '.') - return (char *) "0.0.0.0"; + return strdup("0.0.0.0"); if (domcmp(endptr + 1, ".in-addr.arpa")) { b = strtol(endptr + 1, &endptr, 10); /* 1.2. */ if (errno || b < 0 || b > 255 || *endptr != '.') - return (char *) "0.0.0.0"; + return strdup("0.0.0.0"); if (domcmp(endptr + 1, ".in-addr.arpa")) { c = strtol(endptr + 1, &endptr, 10); /* 1.2.3. */ if (errno || c < 0 || c > 255 || *endptr != '.') - return (char *) "0.0.0.0"; + return strdup("0.0.0.0"); if (domcmp(endptr + 1, ".in-addr.arpa")) - return (char *) "0.0.0.0"; - - sprintf(new, "%d.%d.%d.0", c, b, a); - } else - sprintf(new, "%d.%d.0.0", b, a); - } else - sprintf(new, "%d.0.0.0", a); + return strdup("0.0.0.0"); + } + } + new = malloc(sizeof("255.255.255.255")); + sprintf(new, "%d.%d.%d.0", c, b, a); return new; } diff --git a/src/whois.h b/src/whois.h index ccd87e7..e274138 100644 --- a/src/whois.h +++ b/src/whois.h @@ -3,13 +3,13 @@ /* String sent to RIPE servers - MUST NOT BE LONGER THAN FIVE CHARACTERS! */ /* Do *NOT* change it if you don't know what you are doing! */ -#define IDSTRING "Md4.7" +#define IDSTRING "Md5.0" #define HIDE_DISABLED -2 -#define HIDE_UNSTARTED -1 +#define HIDE_NOT_STARTED -1 /* prototypes */ -const char *whichwhois(const char *); +const char *guess_server(const char *); const char *match_config_file(const char *); const char *whereas(const unsigned long); const char *whereas32(const unsigned long); @@ -24,6 +24,7 @@ int connect_with_timeout(int, const struct sockaddr *, socklen_t, int); void usage(void); void alarm_handler(int); void sighandler(int); +int japanese_locale(void); unsigned long myinet_aton(const char *); unsigned long asn32_to_long(const char *); int isasciidigit(const char); @@ -33,7 +34,7 @@ char *normalize_domain(const char *); char *convert_6to4(const char *); char *convert_teredo(const char *); char *convert_inaddr(const char *); -const char *handle_query(const char *server, const char *port, +void handle_query(const char *server, const char *port, const char *qstring, const char *fstring); void split_server_port(const char *const input, const char **server, const char **port); diff --git a/src/whois.spec b/src/whois.spec index b8c7e29..0179366 100644 --- a/src/whois.spec +++ b/src/whois.spec @@ -1,6 +1,6 @@ Summary: Enhanced WHOIS client Name: whois -Version: 4.7.37 +Version: 5.0.0 Release: 1 License: GPL Vendor: Marco d'Itri @@ -20,7 +20,7 @@ server for most queries. %setup %build -make OPTS="$RPM_OPT_FLAGS" +make CFLAGS="$RPM_OPT_FLAGS" HAVE_LIBIDN=1 HAVE_ICONV=1 %install rm -rf ${RPM_BUILD_ROOT}