nis-util
1.0.D108
|
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 :