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