nis-util  1.0.D108
lib/space/services.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2002, 2008, 2009, 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 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 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/rcstring/accumulator.h>
00020 #include <lib/space/protocols.h>
00021 #include <lib/space/services.h>
00022 
00023 
00024 space_services::~space_services()
00025 {
00026     discard_comments();
00027     discard_blank_lines();
00028 }
00029 
00030 
00031 space_services::space_services(const rcstring &a_filename) :
00032     space(a_filename),
00033     last_num(-1)
00034 {
00035     discard_comments();
00036     discard_blank_lines();
00037 }
00038 
00039 
00040 space_services::pointer
00041 space_services::create(const rcstring &a_filename)
00042 {
00043     return pointer(new space_services(a_filename));
00044 }
00045 
00046 
00047 space_services::record::pointer
00048 space_services::get(void)
00049 {
00050     for (;;)
00051     {
00052         source_location line_locn;
00053         line_t line;
00054         if (!read_one_line(line_locn, line))
00055         {
00056             last_num = -1;
00057             return record::pointer();
00058         }
00059         if (line.size() < 2)
00060         {
00061             error
00062             (
00063                 line_locn,
00064                 "too few fields (expected 2, given %d)",
00065                 (int)line.size()
00066             );
00067             continue;
00068         }
00069 
00070         //
00071         // The first field is the name.
00072         //
00073         rcstring rec_name = line[0];
00074         rcstring rec_name_suggest;
00075         if (!is_ok_name(rec_name, rec_name_suggest))
00076         {
00077             error
00078             (
00079                 line_locn,
00080                 "service name %s not valid, suggest %s instead",
00081                 rec_name.c_str(),
00082                 rec_name_suggest.c_str()
00083             );
00084             rec_name = rec_name_suggest;
00085         }
00086 
00087         //
00088         // The second field is the port/protocol.
00089         //
00090         std::vector<rcstring> second = line[1].break_up("/", false);
00091         if (second.size() != 2)
00092         {
00093             error
00094             (
00095                 line_locn,
00096                 "the second field %s is required to be composed of 2 parts "
00097                 "separated by a slash, not the %d part%s given",
00098                 line[1].quote_c().c_str(),
00099                 (int)second.size(),
00100                 (second.size() == 1 ? "" : "s")
00101             );
00102             if (second.size() == 0)
00103                 second[0] = "-1";
00104             if (second.size() == 1)
00105                 second[1] = "dummy";
00106         }
00107         rcstring rec_port = second[0];
00108         long rec_port_binary = -1;
00109         if (!second[0].to_long(rec_port_binary))
00110         {
00111             error
00112             (
00113                 line_locn,
00114                 "port number %s is not valid",
00115                 rec_port.quote_c().c_str()
00116             );
00117             continue;
00118         }
00119         if (rec_port_binary < 0 || rec_port_binary >= (1L << 16))
00120         {
00121             error
00122             (
00123                 line_locn,
00124                 "service number %ld out of range (1..65535)",
00125                 rec_port_binary
00126             );
00127             continue;
00128         }
00129         // canonicalise
00130         rec_port = rcstring::format("%ld", rec_port_binary);
00131 
00132         rcstring rec_protocol = second[1];
00133         {
00134             rcstring suggest;
00135             if (!space_protocols::is_ok_name(rec_protocol, false, suggest))
00136             {
00137                 error
00138                 (
00139                     line_locn,
00140                     "protocol name %s not valid, suggest %s instead",
00141                     rec_protocol.quote_c().c_str(),
00142                     suggest.quote_c().c_str()
00143                 );
00144                 rec_protocol = suggest;
00145             }
00146         }
00147 
00148         //
00149         // Check record sequencing.
00150         //
00151         if (rec_port_binary < last_num)
00152         {
00153             error
00154             (
00155                 line_locn,
00156                 "service number %ld out of order",
00157                 rec_port_binary
00158             );
00159             explain_sequence_errors();
00160         }
00161         last_num = rec_port_binary;
00162 
00163         //
00164         // The third and subsequent arguments are aliases.
00165         //
00166         record::aliases_t rec_aliases;
00167         for
00168         (
00169             line_t::const_iterator it = line.begin() + 2;
00170             it != line.end();
00171             ++it
00172         )
00173         {
00174             rcstring name = *it;
00175             rcstring suggest;
00176             if (!is_ok_name(name, suggest))
00177             {
00178                 error
00179                 (
00180                     line_locn,
00181                     "service alias %s not valid, suggest %s instead",
00182                     name.quote_c().c_str(),
00183                     suggest.quote_c().c_str()
00184                 );
00185             }
00186             rec_aliases.push_back(name);
00187         }
00188 
00189         //
00190         // Create the record
00191         //
00192         record::pointer rp =
00193             record::create
00194             (
00195                 line_locn,
00196                 rec_name,
00197                 rec_port,
00198                 rec_protocol,
00199                 rec_aliases
00200             );
00201 
00202         //
00203         // Make sure the representation fits within the size limit.
00204         //
00205         if (rp->representation().size() >= 512)
00206         {
00207             error
00208             (
00209                 rp->get_source_location(),
00210                 "line too long (NIS limits records to 511 bytes)"
00211             );
00212         }
00213 
00214         //
00215         // return the row to the caller
00216         //
00217         return rp;
00218     }
00219 }
00220 
00221 
00222 space_services::record::~record()
00223 {
00224 }
00225 
00226 
00227 space_services::record::record(
00228     const source_location &a_locn,
00229     const rcstring &a_name,
00230     const rcstring &a_port,
00231     const rcstring &a_protocol,
00232     const aliases_t &a_aliases
00233 ) :
00234     locn(a_locn),
00235     name(a_name),
00236     port(a_port),
00237     protocol(a_protocol),
00238     aliases(a_aliases)
00239 {
00240 }
00241 
00242 
00243 space_services::record::pointer
00244 space_services::record::create(const source_location &a_locn,
00245     const rcstring &a_name, const rcstring &a_port, const rcstring &a_protocol,
00246     const aliases_t &a_aliases)
00247 {
00248     return pointer(new record(a_locn, a_name, a_port, a_protocol, a_aliases));
00249 }
00250 
00251 
00252 int
00253 space_services::record::get_port_binary(void)
00254     const
00255 {
00256     long value = -1;
00257     if (!port.to_long(value))
00258         return -1;
00259     return value;
00260 }
00261 
00262 
00263 rcstring
00264 space_services::record::representation(void)
00265     const
00266 {
00267     rcstring_accumulator acc;
00268     acc.push_back(name);
00269     acc.push_back(' ');
00270     acc.push_back(port);
00271     acc.push_back('/');
00272     acc.push_back(protocol);
00273     for
00274     (
00275         aliases_t::const_iterator it = aliases.begin();
00276         it != aliases.end();
00277         ++it
00278     )
00279     {
00280         acc.push_back(' ');
00281         acc.push_back(*it);
00282     }
00283     return acc.mkstr();
00284 }
00285 
00286 
00287 bool
00288 space_services::is_ok_name(const rcstring &text, rcstring &suggest)
00289 {
00290     if (text.empty())
00291     {
00292         suggest = "a";
00293         return false;
00294     }
00295 
00296     //
00297     // According to http://www.iana.org/assignments/service-names
00298     // "A service name may be up to 40 characters taken from the set of
00299     // upper case letters, digits, and the punctuation character hyphen.
00300     // It must start with a letter, and end with a letter or digit."
00301     //
00302     // But there are problems: "whois++", "z39.50" and "krb5_pop"
00303     //
00304     // Traditionally unix service names have been mostly lowercase,
00305     // so we will continue to allow lower case names.
00306     //
00307     enum { maxlen = 40 };
00308     const char *cp = text.c_str();
00309     const char *end =
00310         cp + (text.size() < maxlen ? text.size() : (size_t)maxlen);
00311     rcstring_accumulator acc;
00312     bool all_numeric = true;
00313     while (cp < end)
00314     {
00315         unsigned char c = *cp++;
00316         switch (c)
00317         {
00318         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
00319         case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
00320         case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
00321         case 'v': case 'w': case 'x': case 'y': case 'z':
00322             all_numeric = false;
00323             break;
00324 
00325         case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
00326         case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
00327         case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
00328         case 'V': case 'W': case 'X': case 'Y': case 'Z':
00329             c = tolower(c);
00330             all_numeric = false;
00331             break;
00332 
00333         case '0': case '1': case '2': case '3': case '4':
00334         case '5': case '6': case '7': case '8': case '9':
00335             break;
00336 
00337 #ifndef STRICT_IANA_RULES
00338         //
00339         // To cope with IANA's own exceptions ("whois++", "z39.50" and
00340         // "krb5_pop") we permit a few more characters.
00341         //
00342         case '_':
00343             all_numeric = false;
00344             break;
00345 
00346         case '.':
00347         case '+':
00348 #endif
00349         case '-':
00350             break;
00351 
00352         default:
00353             c = '-';
00354             break;
00355         }
00356         acc.push_back(c);
00357     }
00358 
00359     //
00360     // starts with a letter
00361     //
00362 #ifdef STRICT_IANA_RULES
00363     if (!isalpha(acc.front()))
00364 #else
00365     if (!isalnum(acc.front()))
00366 #endif
00367     {
00368         if (acc.size() >= maxlen)
00369             acc.pop_back();
00370         acc.push_front('a');
00371         all_numeric = false;
00372     }
00373 
00374     //
00375     // To cope with the "whois++' case, we don't enforce the "ends in a
00376     // letter or digit" rule.
00377     //
00378 #ifdef STRICT_IANA_RULES
00379     if (!isalnum(acc.back()))
00380     {
00381         if (acc.size() >= maxlen)
00382             acc.pop_back();
00383         acc.push_back('a');
00384         all_numeric = false;
00385     }
00386 #endif
00387 
00388     // one final check
00389     if (all_numeric)
00390     {
00391         if (acc.size() >= maxlen)
00392             acc.pop_back();
00393         acc.push_front('a');
00394     }
00395 
00396     // build the suggestion
00397     suggest = acc.mkstr();
00398 
00399     // the name is ok if the suggestion is identical to the original text
00400     return (text == suggest);
00401 }
00402 
00403 
00404 // vim: set ts=8 sw=4 et :