1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdlib.h> 29 #include <limits.h> 30 #include <string.h> 31 #include <ctype.h> 32 33 #define __NSS_PRIVATE_INTERFACE 34 #include "nsswitch_priv.h" 35 #undef __NSS_PRIVATE_INTERFACE 36 37 #define islabel(c) (isalnum(c) || (c) == '_') 38 39 /* 40 * The _nsw_getoneconfig_v1() in this file parses the switch policy 41 * configuration for a switch database, e.g., 42 * 43 * hosts: nis [NOTFOUND=return] files 44 * or 45 * printers: user files nis 46 */ 47 48 /* 49 * Local routines 50 */ 51 static char *skip(char **, char); 52 static char *labelskip(char *); 53 static char *spaceskip(char *); 54 static void freeconf_v1(struct __nsw_switchconfig_v1 *); 55 static int alldigits(char *); 56 57 /* 58 * 59 * With the "lookup control" feature, the default criteria for NIS, NIS+, 60 * and any new services (e.g. ldap) will be: 61 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever] 62 * 63 * For backward compat, NIS via NIS server in DNS forwarding mode will be: 64 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] 65 * 66 * And also for backward compat, the default criteria for DNS will be: 67 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue] 68 */ 69 70 71 72 /* 73 * The BIND resolver normally will retry several times on server non-response. 74 * But now with the "lookup control" feature, we don't want the resolver doing 75 * many retries, rather we want it to return control (reasonably) quickly back 76 * to the switch engine. However, when TRYAGAIN=N or TRYAGAIN=forever is 77 * not explicitly set by the admin in the conf file, we want the old "resolver 78 * retry a few times" rather than no retries at all. 79 */ 80 static int dns_tryagain_retry = 3; 81 82 /* 83 * For backward compat (pre "lookup control"), the dns default behavior is 84 * soft lookup. 85 */ 86 static void 87 set_dns_default_lkp(struct __nsw_lookup_v1 *lkp) 88 { 89 if (strcasecmp(lkp->service_name, "dns") == 0) { 90 lkp->actions[__NSW_TRYAGAIN] = 91 __NSW_TRYAGAIN_NTIMES; 92 lkp->max_retries = dns_tryagain_retry; 93 } 94 } 95 96 static void 97 freeconf_v1(struct __nsw_switchconfig_v1 *cfp) 98 { 99 if (cfp) { 100 if (cfp->dbase) 101 free(cfp->dbase); 102 if (cfp->lookups) { 103 struct __nsw_lookup_v1 *nex, *cur; 104 for (cur = cfp->lookups; cur; cur = nex) { 105 free(cur->service_name); 106 nex = cur->next; 107 free(cur); 108 } 109 } 110 free(cfp); 111 } 112 } 113 114 /* give the next non-alpha character */ 115 static char * 116 labelskip(char *cur) 117 { 118 char *p = cur; 119 while (islabel(*p)) 120 ++p; 121 return (p); 122 } 123 124 /* give the next non-space character */ 125 static char * 126 spaceskip(char *cur) 127 { 128 char *p = cur; 129 while (*p == ' ' || *p == '\t') 130 ++p; 131 return (p); 132 } 133 134 /* 135 * terminate the *cur pointed string by null only if it is 136 * followed by "key" surrounded by zero or more spaces and 137 * return value is the same as the original *cur pointer and 138 * *cur pointer is advanced to the first non {space, key} char 139 * followed by the key. Otherwise, return NULL and keep 140 * *cur unchanged. 141 */ 142 static char * 143 skip(char **cur, char key) 144 { 145 char *p, *tmp; 146 char *q = *cur; 147 int found, tmpfound; 148 149 tmp = labelskip(*cur); 150 p = tmp; 151 found = (*p == key); 152 if (found) { 153 *p++ = '\0'; /* overwrite the key */ 154 p = spaceskip(p); 155 } else { 156 while (*p == ' ' || *p == '\t') { 157 tmpfound = (*++p == key); 158 if (tmpfound) { 159 found = tmpfound; 160 /* null terminate the return token */ 161 *tmp = '\0'; 162 p++; /* skip the key */ 163 } 164 } 165 } 166 if (!found) 167 return (NULL); /* *cur unchanged */ 168 *cur = p; 169 return (q); 170 } 171 172 /* Return 1 if the string contains all digits, else return 0. */ 173 static int 174 alldigits(char *s) 175 { 176 for (; *s; s++) 177 if (!isdigit(*s)) 178 return (0); 179 return (1); 180 } 181 182 struct __nsw_switchconfig_v1 * 183 _nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp) 184 /* linep Nota Bene: not const char * */ 185 /* errp Meanings are abused a bit */ 186 { 187 struct __nsw_switchconfig_v1 *cfp; 188 struct __nsw_lookup_v1 *lkp, **lkq; 189 int end_crit; 190 action_t act; 191 char *p, *tokenp; 192 193 *errp = __NSW_CONF_PARSE_SUCCESS; 194 195 if ((cfp = calloc(1, sizeof (struct __nsw_switchconfig_v1))) 196 == NULL) { 197 *errp = __NSW_CONF_PARSE_SYSERR; 198 return (NULL); 199 } 200 cfp->dbase = strdup(name); 201 lkq = &cfp->lookups; 202 203 /* linep points to a naming service name */ 204 for (;;) { 205 int i; 206 207 /* white space following the last service */ 208 if (*linep == '\0' || *linep == '\n') { 209 return (cfp); 210 } 211 if ((lkp = calloc(1, sizeof (struct __nsw_lookup_v1))) 212 == NULL) { 213 *errp = __NSW_CONF_PARSE_SYSERR; 214 freeconf_v1(cfp); 215 return (NULL); 216 } 217 218 *lkq = lkp; 219 lkq = &lkp->next; 220 221 for (i = 0; i < __NSW_STD_ERRS_V1; i++) 222 if (i == __NSW_SUCCESS) 223 lkp->actions[i] = __NSW_RETURN; 224 else if (i == __NSW_TRYAGAIN) 225 lkp->actions[i] = __NSW_TRYAGAIN_FOREVER; 226 else 227 lkp->actions[i] = __NSW_CONTINUE; 228 229 /* get criteria for the naming service */ 230 if (tokenp = skip(&linep, '[')) { /* got criteria */ 231 232 /* premature end, illegal char following [ */ 233 if (!islabel(*linep)) 234 goto barf_line; 235 lkp->service_name = strdup(tokenp); 236 cfp->num_lookups++; 237 238 set_dns_default_lkp(lkp); 239 240 end_crit = 0; 241 242 /* linep points to a switch_err */ 243 for (;;) { 244 int ntimes = 0; /* try again max N times */ 245 int dns_continue = 0; 246 247 if ((tokenp = skip(&linep, '=')) == NULL) { 248 goto barf_line; 249 } 250 251 /* premature end, ill char following = */ 252 if (!islabel(*linep)) 253 goto barf_line; 254 255 /* linep points to the string following '=' */ 256 p = labelskip(linep); 257 if (*p == ']') 258 end_crit = 1; 259 else if (*p != ' ' && *p != '\t') 260 goto barf_line; 261 *p++ = '\0'; /* null terminate linep */ 262 p = spaceskip(p); 263 if (!end_crit) { 264 if (*p == ']') { 265 end_crit = 1; 266 *p++ = '\0'; 267 } else if (*p == '\0' || *p == '\n') { 268 return (cfp); 269 } else if (!islabel(*p)) 270 /* p better be the next switch_err */ 271 goto barf_line; 272 } 273 if (strcasecmp(linep, __NSW_STR_RETURN) == 0) 274 act = __NSW_RETURN; 275 else if (strcasecmp(linep, 276 __NSW_STR_CONTINUE) == 0) { 277 if (strcasecmp(lkp->service_name, 278 "dns") == 0 && 279 strcasecmp(tokenp, 280 __NSW_STR_TRYAGAIN) 281 == 0) { 282 /* 283 * Add one more condition 284 * so it retries only if it's 285 * "dns [TRYAGAIN=continue]" 286 */ 287 dns_continue = 1; 288 act = __NSW_TRYAGAIN_NTIMES; 289 } else 290 act = __NSW_CONTINUE; 291 } else if (strcasecmp(linep, 292 __NSW_STR_FOREVER) == 0) 293 act = __NSW_TRYAGAIN_FOREVER; 294 else if (alldigits(linep)) { 295 act = __NSW_TRYAGAIN_NTIMES; 296 ntimes = atoi(linep); 297 if (ntimes < 0 || ntimes > INT_MAX) 298 ntimes = 0; 299 } 300 else 301 goto barf_line; 302 303 if (__NSW_SUCCESS_ACTION(act) && 304 strcasecmp(tokenp, 305 __NSW_STR_SUCCESS) == 0) { 306 lkp->actions[__NSW_SUCCESS] = act; 307 } else if (__NSW_NOTFOUND_ACTION(act) && 308 strcasecmp(tokenp, 309 __NSW_STR_NOTFOUND) == 0) { 310 lkp->actions[__NSW_NOTFOUND] = act; 311 } else if (__NSW_UNAVAIL_ACTION(act) && 312 strcasecmp(tokenp, 313 __NSW_STR_UNAVAIL) == 0) { 314 lkp->actions[__NSW_UNAVAIL] = act; 315 } else if (__NSW_TRYAGAIN_ACTION(act) && 316 strcasecmp(tokenp, 317 __NSW_STR_TRYAGAIN) == 0) { 318 lkp->actions[__NSW_TRYAGAIN] = act; 319 if (strcasecmp(lkp->service_name, 320 "nis") == 0) 321 lkp->actions[ 322 __NSW_NISSERVDNS_TRYAGAIN] 323 = act; 324 if (act == __NSW_TRYAGAIN_NTIMES) 325 lkp->max_retries = 326 dns_continue ? 327 dns_tryagain_retry : ntimes; 328 } else { 329 /*EMPTY*/ 330 /* 331 * convert string tokenp to integer 332 * and put in long_errs 333 */ 334 } 335 if (end_crit) { 336 linep = spaceskip(p); 337 if (*linep == '\0' || *linep == '\n') 338 return (cfp); 339 break; /* process next naming service */ 340 } 341 linep = p; 342 } /* end of while loop for a name service's criteria */ 343 } else { 344 /* 345 * no criteria for this naming service. 346 * linep points to name service, but not null 347 * terminated. 348 */ 349 p = labelskip(linep); 350 if (*p == '\0' || *p == '\n') { 351 *p = '\0'; 352 lkp->service_name = strdup(linep); 353 set_dns_default_lkp(lkp); 354 cfp->num_lookups++; 355 return (cfp); 356 } 357 if (*p != ' ' && *p != '\t') 358 goto barf_line; 359 *p++ = '\0'; 360 lkp->service_name = strdup(linep); 361 set_dns_default_lkp(lkp); 362 cfp->num_lookups++; 363 linep = spaceskip(p); 364 } 365 } /* end of while(1) loop for a name service */ 366 367 barf_line: 368 freeconf_v1(cfp); 369 *errp = __NSW_CONF_PARSE_NOPOLICY; 370 return (NULL); 371 } 372 373 int 374 __nsw_freeconfig_v1( 375 struct __nsw_switchconfig_v1 *conf) 376 { 377 freeconf_v1(conf); 378 return (0); 379 } 380