libexplain  1.4.D001
libexplain/buffer/errno/sethostname.c
Go to the documentation of this file.
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 : */