libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2013 Peter Miller 00004 * 00005 * This program is free software; you can redistribute it and/or modify it 00006 * under the terms of the GNU Lesser General Public License as published by 00007 * the Free Software Foundation; either version 3 of the License, or (at 00008 * your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, but 00011 * WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 00013 * General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Lesser General Public License 00016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00017 */ 00018 00019 #include <libexplain/ac/errno.h> 00020 #include <libexplain/ac/string.h> 00021 00022 #include <libexplain/buffer/errno/generic.h> 00023 #include <libexplain/buffer/errno/gethostbyname.h> 00024 #include <libexplain/buffer/gettext.h> 00025 #include <libexplain/explanation.h> 00026 00027 00028 static void 00029 explain_buffer_errno_gethostbyname_system_call(explain_string_buffer_t *sb, 00030 int errnum, const char *name) 00031 { 00032 (void)errnum; 00033 explain_string_buffer_puts(sb, "gethostbyname(name = "); 00034 explain_string_buffer_puts_quoted(sb, name); 00035 explain_string_buffer_putc(sb, ')'); 00036 } 00037 00038 00039 /* RFC 2181 */ 00040 #define ALLOW_UNDERSCORE 1 00041 #define ALLOW_LEADING_UNDERSCORE 1 00042 00043 /* RFC 1123 */ 00044 #define ALLOW_LEADING_HYPHEN 1 00045 #define ALLOW_LEADING_DIGIT 1 00046 #define ALLOW_ALL_NUMERIC 1 00047 00048 00049 typedef struct label label; 00050 struct label 00051 { 00052 const char *begin; 00053 size_t size; 00054 const char *end; 00055 }; 00056 00057 00067 static int 00068 valid_label(const label *lp) 00069 { 00070 const char *name; 00071 size_t size; 00072 size_t seen_alpha; 00073 size_t seen_digit; 00074 size_t seen_punct; 00075 00076 /* 00077 * Each label must be between 1 and 63 characters long. 00078 */ 00079 size = lp->size; 00080 if (size < 1 || size > 63) 00081 return 0; 00082 00083 /* 00084 * The original specification of hostnames in RFC 952, mandated that 00085 * labels could not start with a digit or with a hyphen, and must 00086 * not end with a hyphen. However, a subsequent specification (RFC 00087 * 1123) permitted hostname labels to start with digits. 00088 * 00089 * That means they still can't start with a hyphen. 00090 */ 00091 name = lp->begin; 00092 switch (name[0]) 00093 { 00094 #ifndef ALLOW_LEADING_DIGIT 00095 case '0': case '1': case '2': case '3': case '4': 00096 case '5': case '6': case '7': case '8': case '9': 00097 return 0; 00098 #endif 00099 00100 #ifndef ALLOW_LEADING_HYPHEN 00101 case '-': 00102 return 0; 00103 #endif 00104 00105 #ifdef ALLOW_UNDERSCORE 00106 #ifndef ALLOW_LEADING_UNDERSCORE 00107 case '_': 00108 /* no *leading* underscore */ 00109 return 0; 00110 #endif 00111 #endif 00112 00113 default: 00114 break; 00115 } 00116 00117 /* 00118 * Check each character. 00119 */ 00120 seen_alpha = 0; 00121 seen_digit = 0; 00122 seen_punct = 0; 00123 while (size-- > 0) 00124 { 00125 unsigned char c = *name++; 00126 switch (c) 00127 { 00128 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': 00129 case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': 00130 case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': 00131 case 'V': case 'W': case 'X': case 'Y': case 'Z': 00132 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': 00133 case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': 00134 case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': 00135 case 'v': case 'w': case 'x': case 'y': case 'z': 00136 /* 00137 * Hostname labels may contain only the ASCII letters 'a' through 00138 * 'z' (in a case-insensitive manner), the digits '0' through '9', 00139 * and the hyphen ('-'). 00140 */ 00141 ++seen_alpha; 00142 break; 00143 00144 case '0': case '1': case '2': case '3': case '4': 00145 case '5': case '6': case '7': case '8': case '9': 00146 /* 00147 * The original specification of hostnames in RFC 952, mandated that 00148 * labels could not start with a digit or with a hyphen, and must 00149 * not end with a hyphen. However, a subsequent specification (RFC 00150 * 1123) permitted hostname labels to start with digits. 00151 */ 00152 ++seen_digit; 00153 break; 00154 00155 case '-': 00156 if (size == 0) 00157 { 00158 /* must not end with a hyphen */ 00159 return 0; 00160 } 00161 ++seen_punct; 00162 break; 00163 00164 #ifdef ALLOW_UNDERSCORE 00165 case '_': 00166 if (size == 0) 00167 { 00168 /* must not end with an underscore */ 00169 return 0; 00170 } 00171 ++seen_punct; 00172 break; 00173 #endif 00174 00175 default: 00176 /* 00177 * No other symbols, punctuation characters, or white space are 00178 * permitted. 00179 */ 00180 return 0; 00181 } 00182 } 00183 #ifndef ALLOW_ALL_NUMERIC 00184 if (seen_digit == lp->size) 00185 return 0; 00186 #endif 00187 00188 /* 00189 * Found nothing wrong. 00190 */ 00191 return 1; 00192 } 00193 00194 00203 static label * 00204 validate_hostname(const char *name) 00205 { 00206 static label labels[(255 + 1) / 2]; 00207 size_t nlabels; 00208 size_t len; 00209 const char *name_end; 00210 label *lp; 00211 size_t j; 00212 00213 /* 00214 * Hostnames are composed of series of labels concatenated with 00215 * dots, as are all domain names. For example, "en.wikipedia.org" 00216 * is a hostname. Each label must be between 1 and 63 characters 00217 * long, and the entire hostname (including the delimiting dots) 00218 * has a maximum of 255 characters. 00219 * 00220 * The Internet standards (Request for Comments) for protocols 00221 * mandate that component hostname labels may contain only the 00222 * ASCII letters 'a' through 'z' (in a case-insensitive manner), 00223 * the digits '0' through '9', and the hyphen ('-'). The original 00224 * specification of hostnames in RFC 952, mandated that labels 00225 * could not start with a digit or with a hyphen, and must not end 00226 * with a hyphen. However, a subsequent specification (RFC 1123) 00227 * permitted hostname labels to start with digits. No other symbols, 00228 * punctuation characters, or white space are permitted. 00229 * 00230 * [http://en.wikipedia.org/wiki/Hostname] 00231 */ 00232 len = strlen(name); 00233 if (len < 1 || len > 255) 00234 return (label *)(-1); 00235 name_end = name + len; 00236 00237 nlabels = 0; 00238 for (;;) 00239 { 00240 const char *dot; 00241 00242 dot = memchr(name, '.', name_end - name); 00243 if (!dot) 00244 { 00245 lp = &labels[nlabels++]; 00246 lp->begin = name; 00247 lp->size = name_end - name; 00248 lp->end = name_end; 00249 break; 00250 } 00251 lp = &labels[nlabels++]; 00252 lp->begin = name; 00253 lp->size = dot - name; 00254 lp->end = dot; 00255 name = dot + 1; 00256 } 00257 00258 /* 00259 * If a trailing dot was used, the last "label" will be empty, so toss it. 00260 * No other labels are permitted to be empty. 00261 */ 00262 if (nlabels > 0 && labels[nlabels - 1].size == 0) 00263 --nlabels; 00264 if (nlabels == 0) 00265 return (label *)(-1); 00266 00267 /* 00268 * Check each label for validity. 00269 */ 00270 for (j = 0; j < nlabels; ++j) 00271 { 00272 lp = &labels[j]; 00273 if (!valid_label(lp)) 00274 return lp; 00275 } 00276 00277 /* 00278 * Couldn't find anything wrong. 00279 */ 00280 return 0; 00281 } 00282 00283 00284 static int 00285 check_valid_hostname(explain_string_buffer_t *sb, const char *name) 00286 { 00287 label *lp; 00288 explain_string_buffer_t sb2; 00289 char buffer[1000]; 00290 00291 lp = validate_hostname(name); 00292 if (!lp) 00293 return 0; 00294 if (sb == sb->footnotes) 00295 explain_string_buffer_puts(sb, "; "); 00296 if (lp == (label *)(-1)) 00297 { 00298 explain_buffer_gettext 00299 ( 00300 sb, 00301 i18n 00302 ( 00303 /* 00304 * xgettext: this error message is used to explain a 00305 * NO_RECOVERY error returned by the gethostbyname system 00306 * call, in the case where the host name is not a valid 00307 * length. 00308 */ 00309 "the host name is not a valid length" 00310 ) 00311 ); 00312 return 1; 00313 } 00314 00315 if (lp->size == 0) 00316 { 00317 explain_buffer_gettext 00318 ( 00319 sb, 00320 00321 /* 00322 * xgettext: this error message is used to explain a 00323 * NO_RECOVERY error returned by the gethostbyname 00324 * system call, in the case where the host name contains 00325 * at least one empty label. 00326 */ 00327 i18n("host names may not have empty parts") 00328 ); 00329 return 1; 00330 } 00331 00332 explain_string_buffer_init(&sb2, buffer, sizeof(buffer)); 00333 explain_string_buffer_puts_quoted_n(&sb2, lp->begin, lp->size); 00334 explain_string_buffer_printf_gettext 00335 ( 00336 sb, 00337 i18n 00338 ( 00339 /* 00340 * xgettext: this error message is used to explain a NO_RECOVERY 00341 * error returned by the gethostbyname system call, in the case 00342 * where the host name is not properly formed. 00343 * 00344 * %1$s => text of the label (the text between the dots) 00345 */ 00346 "the host name (at %s) is not properly formed" 00347 ), 00348 buffer 00349 ); 00350 return 1; 00351 } 00352 00353 00354 void 00355 explain_buffer_errno_gethostbyname_explanation(explain_string_buffer_t *sb, 00356 int errnum, const char *syscall_name, const char *name) 00357 { 00358 /* 00359 * http://www.opengroup.org/onlinepubs/009695399/functions/gethostbyname.html 00360 * 00361 * In this case, errnum has the h_error value, not the errno value. 00362 */ 00363 switch (errnum) 00364 { 00365 case HOST_NOT_FOUND: 00366 /* 00367 * FIXME: what about if it is shorter, e.g. www.example.com vs 00368 * example.com. We need to let the user know what part of the name is 00369 * not found. 00370 */ 00371 explain_buffer_gettext 00372 ( 00373 sb, 00374 i18n 00375 ( 00376 /* 00377 * xgettext: this error message is used to explain 00378 * HOST_NOT_FOUND errors returned by the gethostbyname system 00379 * call. Authoritative Answer Host not found. The specified 00380 * host is unknown. 00381 */ 00382 "an authoritative DNS server was reached and the given host " 00383 "name does not exist" 00384 ) 00385 ); 00386 check_valid_hostname(sb->footnotes, name); 00387 break; 00388 00389 case TRY_AGAIN: 00390 /* 00391 * FIXME: what about if it is shorter, e.g. work1.fixed.example.com 00392 * vs fixed.example.com. vs example.com. We need to let the user know 00393 * what part of the name is not found. 00394 */ 00395 explain_buffer_gettext 00396 ( 00397 sb, 00398 i18n 00399 ( 00400 /* 00401 * xgettext: this error message is used to explain TRY_AGAIN 00402 * errors returned by the gethostbyname system call. 00403 * Authoritative Answer Host not found, or SERVERFAIL. A 00404 * temporary error occurred on an authoritative name server. 00405 * The specified host is unknown. 00406 */ 00407 "an authoritative DNS server could not be reached and so the " 00408 "given host name does not appear to exist" 00409 ) 00410 ); 00411 check_valid_hostname(sb->footnotes, name); 00412 /* FIXME: i18n */ 00413 explain_string_buffer_puts(sb->footnotes, "try again later"); 00414 break; 00415 00416 case NO_RECOVERY: 00417 /* Non recoverable error: FORMERR */ 00418 if (check_valid_hostname(sb, name)) 00419 break; 00420 00421 /* FIXME: which? */ 00422 explain_buffer_gettext 00423 ( 00424 sb, 00425 i18n 00426 ( 00427 /* 00428 * xgettext: this error message is used to explain NO_RECOVERY 00429 * errors returned by the gethostbyname system call. 00430 * Non recoverable errors, FORMERR, REFUSED, NOTIMP. 00431 */ 00432 "the operation was refused, or " 00433 "the operation is not implemented on this system" 00434 ) 00435 ); 00436 break; 00437 00438 case NO_DATA: 00439 explain_buffer_gettext 00440 ( 00441 sb, 00442 i18n 00443 ( 00444 /* 00445 * xgettext: this error message is used to explain NO_DATA 00446 * errors returned by the gethostbyname system call. 00447 * Valid name, no data record of requested type. 00448 * The requested name is valid but does not have an IP address. 00449 */ 00450 "the host name does not have any DNS data" 00451 ) 00452 ); 00453 check_valid_hostname(sb->footnotes, name); 00454 break; 00455 00456 #if defined(NO_ADDRESS) && defined(NO_DATA) && NO_ADDRESS != NO_DATA 00457 case NO_ADDRESS: 00458 explain_buffer_gettext 00459 ( 00460 sb, 00461 i18n 00462 ( 00463 /* 00464 * xgettext: this error message is used to explain NO_ADDRESS 00465 * errors returned by the gethostbyname system call. 00466 * No address, look for MX record. 00467 */ 00468 "the host name has DNS data but does not have an IP address" 00469 ) 00470 ); 00471 explain_buffer_puts(sb->footnote, "look for an MX record"); 00472 check_value_hostname(sb->footnotes, name); 00473 break; 00474 #endif 00475 00476 #ifdef NETDB_INTERNAL 00477 case NETDB_INTERNAL: 00478 #endif 00479 default: 00480 explain_buffer_errno_generic(sb, errno, syscall_name); 00481 break; 00482 } 00483 } 00484 00485 00486 void 00487 explain_buffer_errno_gethostbyname(explain_string_buffer_t *sb, int errnum, 00488 const char *name) 00489 { 00490 explain_explanation_t exp; 00491 int hold_errno; 00492 00493 hold_errno = errno; 00494 explain_explanation_init(&exp, errnum); 00495 explain_buffer_errno_gethostbyname_system_call 00496 ( 00497 &exp.system_call_sb, 00498 errnum, 00499 name 00500 ); 00501 explain_buffer_errno_gethostbyname_explanation 00502 ( 00503 &exp.explanation_sb, 00504 errnum, 00505 "gethostbyname", 00506 name 00507 ); 00508 errno = hold_errno; /* for NETDB_INTERNAL error case */ 00509 explain_explanation_assemble_netdb(&exp, sb); 00510 } 00511 00512 00513 /* vim: set ts=8 sw=4 et : */