libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2009, 2011, 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, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Lesser 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/assert.h> 00020 #include <libexplain/ac/errno.h> 00021 #include <libexplain/ac/unistd.h> 00022 #include <libexplain/ac/limits.h> 00023 00024 #include <libexplain/buffer/dac.h> 00025 #include <libexplain/buffer/efault.h> 00026 #include <libexplain/buffer/einval.h> 00027 #include <libexplain/buffer/errno/generic.h> 00028 #include <libexplain/buffer/errno/sethostname.h> 00029 #include <libexplain/buffer/gettext.h> 00030 #include <libexplain/buffer/size_t.h> 00031 #include <libexplain/buffer/string_n.h> 00032 #include <libexplain/explanation.h> 00033 #include <libexplain/host_name_max.h> 00034 #include <libexplain/is_efault.h> 00035 00036 00037 static void 00038 explain_buffer_errno_sethostname_system_call(explain_string_buffer_t *sb, 00039 int errnum, const char *name, size_t name_size) 00040 { 00041 (void)errnum; 00042 explain_string_buffer_puts(sb, "sethostname(name = "); 00043 explain_buffer_string_n(sb, name, name_size); 00044 explain_string_buffer_puts(sb, ", name_size = "); 00045 explain_buffer_size_t(sb, name_size); 00046 explain_string_buffer_putc(sb, ')'); 00047 } 00048 00049 00050 static int 00051 name_is_ok(const char *name, size_t name_size) 00052 { 00053 const char *name_end; 00054 int state; 00055 00056 /* The Linux kernel permits zero-length hostname. */ 00057 if (name_size == 0) 00058 return 1; 00059 00060 /* 00061 * using RFC1035 terminology (section 2.3.1) 00062 * 00063 * $accept := <subdomain> $end 00064 * <subdomain> ::= <label> 00065 * <subdomain> ::= <subdomain> "." <label> 00066 * <label> ::= <letter> 00067 * <label> ::= <letter> <let-dig> 00068 * <label> ::= <letter> <ldh-str> <let-dig> 00069 * <ldh-str> ::= <let-dig-hyp> 00070 * <ldh-str> ::= <ldh-str> <let-dig-hyp> 00071 * <let-dig-hyp> ::= <let-dig> 00072 * <let-dig-hyp> ::= "-" 00073 * <let-dig> ::= <letter> 00074 * <let-dig> ::= <digit> 00075 */ 00076 name_end = name + name_size; 00077 state = 0; 00078 for (;;) 00079 { 00080 int c; 00081 00082 /* 00083 * Note: we don't use isdigit, isalpha, etc, because the current 00084 * locale could be different than the "C" locale. 00085 * 00086 * We distinguish end-of-string with -1, because a NUL character 00087 * would be invalid in a hostname. 00088 */ 00089 c = (name < name_end ? (unsigned char)*name++ : -1); 00090 switch (state) 00091 { 00092 case 0: 00093 /* 00094 * initial state OR after dot 00095 * 00096 * $accept := @ <subdomain> $end 00097 * <subdomain> ::= @ <subdomain> "." <label> 00098 * <subdomain> ::= @ <label> 00099 * <subdomain> ::= <subdomain> "." @ <label> 00100 * <label> ::= @ <letter> 00101 * <label> ::= @ <letter> <let-dig> 00102 * <label> ::= @ <letter> <ldh-str> <let-dig> 00103 */ 00104 switch (c) 00105 { 00106 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 00107 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 00108 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 00109 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 00110 case 'y': case 'z': 00111 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 00112 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 00113 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 00114 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 00115 case 'Y': case 'Z': 00116 state = 1; 00117 break; 00118 00119 case '0': case '1': case '2': case '3': case '4': case '5': 00120 case '6': case '7': case '8': case '9': 00121 return 0; 00122 00123 case '-': 00124 return 0; 00125 break; 00126 00127 case '.': 00128 return 0; 00129 break; 00130 00131 case -1: 00132 return 0; 00133 00134 default: 00135 return 0; 00136 } 00137 break; 00138 00139 case 1: 00140 /* 00141 * within a label 00142 * 00143 * $accept := <subdomain> @ $end 00144 * <subdomain> ::= <label> @ 00145 * <subdomain> ::= <subdomain> @ "." <label> 00146 * <label> ::= <letter> @ 00147 * <label> ::= <letter> @ <let-dig> 00148 * <label> ::= <letter> <let-dig> @ 00149 * <label> ::= <letter> @ <ldh-str> <let-dig> 00150 * <label> ::= <letter> <ldh-str> @ <let-dig> 00151 * <ldh-str> ::= @ <let-dig-hyp> 00152 * <ldh-str> ::= <let-dig-hyp> @ 00153 * <ldh-str> ::= @ <ldh-str> <let-dig-hyp> 00154 * <ldh-str> ::= <ldh-str> @ <let-dig-hyp> 00155 * <let-dig-hyp> ::= @ <let-dig> 00156 * <let-dig-hyp> ::= @ "-" 00157 * <let-dig> ::= @ <letter> 00158 * <let-dig> ::= @ <digit> 00159 */ 00160 switch (c) 00161 { 00162 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 00163 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 00164 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 00165 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 00166 case 'y': case 'z': 00167 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 00168 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 00169 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 00170 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 00171 case 'Y': case 'Z': 00172 state = 1; 00173 break; 00174 00175 case '0': case '1': case '2': case '3': case '4': case '5': 00176 case '6': case '7': case '8': case '9': 00177 state = 1; 00178 break; 00179 00180 case '-': 00181 state = 2; 00182 break; 00183 00184 case '.': 00185 state = 0; 00186 break; 00187 00188 case -1: 00189 return 1; 00190 00191 default: 00192 return 0; 00193 } 00194 break; 00195 00196 case 2: 00197 /* 00198 * within label, after hyphen 00199 * 00200 * <label> ::= <letter> <ldh-str> @ <let-dig> 00201 * <ldh-str> ::= <let-dig-hyp> @ 00202 * <ldh-str> ::= <ldh-str> @ <let-dig-hyp> 00203 * <ldh-str> ::= <ldh-str> <let-dig-hyp> @ 00204 * <let-dig-hyp> ::= "-" @ 00205 * <let-dig> ::= @ <letter> 00206 * <let-dig> ::= @ <digit> 00207 */ 00208 switch (c) 00209 { 00210 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': 00211 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': 00212 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': 00213 case 's': case 't': case 'u': case 'v': case 'w': case 'x': 00214 case 'y': case 'z': 00215 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': 00216 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': 00217 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': 00218 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': 00219 case 'Y': case 'Z': 00220 state = 1; 00221 break; 00222 00223 case '0': case '1': case '2': case '3': case '4': case '5': 00224 case '6': case '7': case '8': case '9': 00225 state = 1; 00226 break; 00227 00228 case '-': 00229 state = 2; 00230 break; 00231 00232 case '.': 00233 return 0; 00234 00235 case -1: 00236 return 0; 00237 00238 default: 00239 return 0; 00240 } 00241 break; 00242 00243 default: 00244 assert(!"unknown state"); 00245 return 0; 00246 } 00247 } 00248 } 00249 00250 00251 static void 00252 explain_buffer_errno_sethostname_explanation(explain_string_buffer_t *sb, 00253 int errnum, const char *name, size_t name_size) 00254 { 00255 size_t host_name_max; 00256 ssize_t name_ssize; 00257 00258 /* 00259 * http://www.opengroup.org/onlinepubs/009695399/functions/sethostname.html 00260 */ 00261 (void)name; 00262 name_ssize = name_size; 00263 switch (errnum) 00264 { 00265 case EFAULT: 00266 explain_buffer_efault(sb, "name"); 00267 break; 00268 00269 case EINVAL: 00270 /* A size of zero is valid. */ 00271 if (name_ssize < 0) 00272 { 00273 explain_buffer_einval_too_small(sb, "name_size", name_ssize); 00274 break; 00275 } 00276 host_name_max = explain_get_host_name_max(); 00277 if (host_name_max && name_size > host_name_max) 00278 { 00279 /* The Linux kernel checks this */ 00280 goto enametoolong; 00281 } 00282 if 00283 ( 00284 name_size 00285 && 00286 !explain_is_efault_pointer(name, name_size) 00287 && 00288 !name_is_ok(name, name_size) 00289 ) 00290 { 00291 /* 00292 * Note: the Linux kernel does not check this. 00293 */ 00294 explain_buffer_gettext 00295 ( 00296 sb, 00297 /* 00298 * xgettext: this error message is issued when a process 00299 * attempts to set the hostname, and the hostname contains 00300 * characters not in the RFC1035 spec (section 2.3.1). 00301 */ 00302 i18n("the hostname specified contains invalid characters") 00303 ); 00304 break; 00305 } 00306 /* just guessing */ 00307 goto enametoolong; 00308 00309 case ENAMETOOLONG: 00310 #ifdef EOVERFLOW 00311 case EOVERFLOW: 00312 #endif 00313 enametoolong: 00314 explain_buffer_gettext 00315 ( 00316 sb, 00317 /* 00318 * xgettext: this error message is issued when a process 00319 * attempts to set the hostname, and the name is too long. 00320 */ 00321 i18n("the hostname specified is too long") 00322 ); 00323 host_name_max = explain_get_host_name_max(); 00324 if (host_name_max && name_size > host_name_max) 00325 { 00326 explain_string_buffer_printf 00327 ( 00328 sb, 00329 " (%lu > %lu)", 00330 (unsigned long)name_size, 00331 (unsigned long)host_name_max 00332 ); 00333 } 00334 break; 00335 00336 case EPERM: 00337 explain_buffer_gettext 00338 ( 00339 sb, 00340 /* 00341 * xgettext: this error message is issued when a process 00342 * attempts to set the hostname without sufficient privilege. 00343 */ 00344 i18n("the process does not have permission to set the hostname") 00345 ); 00346 explain_buffer_dac_sys_admin(sb); 00347 break; 00348 00349 default: 00350 explain_buffer_errno_generic(sb, errnum, "sethostname"); 00351 break; 00352 } 00353 } 00354 00355 00356 void 00357 explain_buffer_errno_sethostname(explain_string_buffer_t *sb, int errnum, 00358 const char *name, size_t name_size) 00359 { 00360 explain_explanation_t exp; 00361 00362 explain_explanation_init(&exp, errnum); 00363 explain_buffer_errno_sethostname_system_call 00364 ( 00365 &exp.system_call_sb, 00366 errnum, 00367 name, 00368 name_size 00369 ); 00370 explain_buffer_errno_sethostname_explanation 00371 ( 00372 &exp.explanation_sb, 00373 errnum, 00374 name, 00375 name_size 00376 ); 00377 explain_explanation_assemble(&exp, sb); 00378 } 00379 00380 /* vim: set ts=8 sw=4 et : */