1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 #ifdef USERDB 11 static char sccsid [] = "@(#)udb.c 5.14 (Berkeley) 12/14/91 (with USERDB)"; 12 #else 13 static char sccsid [] = "@(#)udb.c 5.14 (Berkeley) 12/14/91 (without USERDB)"; 14 #endif 15 #endif 16 17 #include "sendmail.h" 18 19 #ifdef USERDB 20 21 #include <sys/file.h> 22 #include <sys/time.h> 23 #include <errno.h> 24 #include <fcntl.h> 25 #include <netdb.h> 26 #include <db.h> 27 28 /* 29 ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. 30 ** 31 ** This depends on the 4.4BSD db package. 32 */ 33 34 35 struct udbent 36 { 37 char *udb_spec; /* string version of spec */ 38 int udb_type; /* type of entry */ 39 union 40 { 41 /* type UE_REMOTE -- do remote call for lookup */ 42 struct 43 { 44 struct sockaddr_in _udb_addr; /* address */ 45 int _udb_timeout; /* timeout */ 46 } udb_remote; 47 #define udb_addr udb_u.udb_remote._udb_addr 48 #define udb_timeout udb_u.udb_remote._udb_timeout 49 50 /* type UE_FORWARD -- forward message to remote */ 51 struct 52 { 53 char *_udb_fwdhost; /* name of forward host */ 54 } udb_forward; 55 #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 56 57 /* type UE_LOOKUP -- lookup in local database */ 58 struct 59 { 60 char *_udb_dbname; /* pathname of database */ 61 DB *_udb_dbp; /* open database ptr */ 62 } udb_lookup; 63 #define udb_dbname udb_u.udb_lookup._udb_dbname 64 #define udb_dbp udb_u.udb_lookup._udb_dbp 65 } udb_u; 66 }; 67 68 #define UDB_EOLIST 0 /* end of list */ 69 #define UDB_SKIP 1 /* skip this entry */ 70 #define UDB_REMOTE 2 /* look up in remote database */ 71 #define UDB_LOOKUP 3 /* look up in local database */ 72 #define UDB_FORWARD 4 /* forward to remote host */ 73 74 #define MAXUDBENT 10 /* maximum number of UDB entries */ 75 76 77 struct option 78 { 79 char *name; 80 char *val; 81 }; 82 /* 83 ** UDBEXPAND -- look up user in database and expand 84 ** 85 ** Parameters: 86 ** a -- address to expand. 87 ** sendq -- pointer to head of sendq to put the expansions in. 88 ** 89 ** Returns: 90 ** EX_TEMPFAIL -- if something "odd" happened -- probably due 91 ** to accessing a file on an NFS server that is down. 92 ** EX_OK -- otherwise. 93 ** 94 ** Side Effects: 95 ** Modifies sendq. 96 */ 97 98 int UdbPort = 1616; 99 int UdbTimeout = 10; 100 101 struct udbent UdbEnts[MAXUDBENT + 1]; 102 int UdbSock = -1; 103 104 int 105 udbexpand(a, sendq) 106 register ADDRESS *a; 107 ADDRESS **sendq; 108 { 109 int i; 110 register char *p; 111 DBT key; 112 DBT info; 113 static bool firstcall = TRUE; 114 bool breakout; 115 register struct udbent *up; 116 int keylen; 117 char keybuf[128]; 118 char buf[8192]; 119 120 if (tTd(28, 1)) 121 printf("expand(%s)\n", a->q_paddr); 122 123 /* make certain we are supposed to send to this address */ 124 if (bitset(QDONTSEND, a->q_flags)) 125 return EX_OK; 126 CurEnv->e_to = a->q_paddr; 127 128 /* on first call, locate the database */ 129 if (firstcall) 130 { 131 extern int _udbx_init(); 132 133 if (_udbx_init() == EX_TEMPFAIL) 134 return EX_TEMPFAIL; 135 firstcall = FALSE; 136 } 137 138 /* short circuit the process if no chance of a match */ 139 if (UdbSpec == NULL || UdbSpec[0] == '\0') 140 return EX_OK; 141 142 /* if name is too long, assume it won't match */ 143 if (strlen(a->q_user) > sizeof keybuf - 12) 144 return EX_OK; 145 146 /* if name begins with a colon, it indicates our metadata */ 147 if (a->q_user[0] == ':') 148 return EX_OK; 149 150 /* build actual database key */ 151 (void) strcpy(keybuf, a->q_user); 152 (void) strcat(keybuf, ":maildrop"); 153 keylen = strlen(keybuf); 154 155 breakout = FALSE; 156 for (up = UdbEnts; !breakout; up++) 157 { 158 char *user; 159 struct timeval timeout; 160 fd_set fdset; 161 162 /* 163 ** Select action based on entry type. 164 ** 165 ** On dropping out of this switch, "class" should 166 ** explain the type of the data, and "user" should 167 ** contain the user information. 168 */ 169 170 switch (up->udb_type) 171 { 172 case UDB_LOOKUP: 173 key.data = keybuf; 174 key.size = keylen; 175 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 176 if (i > 0 || info.size <= 0) 177 { 178 if (tTd(28, 2)) 179 printf("expand: no match on %s\n", keybuf); 180 continue; 181 } 182 183 while (i == 0 && key.size == keylen && 184 bcmp(key.data, keybuf, keylen) == 0) 185 { 186 breakout = TRUE; 187 if (info.size < sizeof buf) 188 user = buf; 189 else 190 user = xalloc(info.size + 1); 191 bcopy(info.data, user, info.size); 192 user[info.size] = '\0'; 193 194 message(Arpa_Info, "expanded to %s", user); 195 AliasLevel++; 196 sendtolist(user, a, sendq); 197 AliasLevel--; 198 199 if (user != buf) 200 free(user); 201 202 /* get the next record */ 203 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 204 } 205 if (i < 0) 206 { 207 syserr("udbexpand: db-get stat %s"); 208 return EX_TEMPFAIL; 209 } 210 break; 211 212 case UDB_REMOTE: 213 /* not yet implemented */ 214 continue; 215 216 case UDB_FORWARD: 217 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 218 if (i < sizeof buf) 219 user = buf; 220 else 221 user = xalloc(i + 1); 222 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 223 message(Arpa_Info, "expanded to %s", user); 224 AliasLevel++; 225 sendtolist(user, a, sendq); 226 AliasLevel--; 227 if (user != buf) 228 free(user); 229 breakout = TRUE; 230 break; 231 232 case UDB_EOLIST: 233 breakout = TRUE; 234 continue; 235 236 default: 237 /* unknown entry type */ 238 continue; 239 } 240 } 241 return EX_OK; 242 } 243 244 #define MAXUDBOPTS 27 245 246 int 247 _udbx_init() 248 { 249 register char *p; 250 int i; 251 register struct udbent *up; 252 char buf[8192]; 253 254 # ifdef UDB_DEFAULT_SPEC 255 if (UdbSpec == NULL) 256 UdbSpec = UDB_DEFAULT_SPEC; 257 # endif 258 259 p = UdbSpec; 260 up = UdbEnts; 261 while (p != NULL) 262 { 263 char *spec; 264 auto int rcode; 265 int nopts; 266 int nmx; 267 register struct hostent *h; 268 char *mxhosts[MAXMXHOSTS + 1]; 269 struct option opts[MAXUDBOPTS + 1]; 270 271 while (*p == ' ' || *p == '\t' || *p == ',') 272 p++; 273 if (*p == '\0') 274 break; 275 spec = p; 276 p = index(p, ','); 277 if (p != NULL) 278 *p++ = '\0'; 279 280 /* extract options */ 281 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 282 283 /* 284 ** Decode database specification. 285 ** 286 ** In the sendmail tradition, the leading character 287 ** defines the semantics of the rest of the entry. 288 ** 289 ** +hostname -- send a datagram to the udb server 290 ** on host "hostname" asking for the 291 ** home mail server for this user. 292 ** *hostname -- similar to +hostname, except that the 293 ** hostname is searched as an MX record; 294 ** resulting hosts are searched as for 295 ** +mxhostname. If no MX host is found, 296 ** this is the same as +hostname. 297 ** @hostname -- forward email to the indicated host. 298 ** This should be the last in the list, 299 ** since it always matches the input. 300 ** /dbname -- search the named database on the local 301 ** host using the Berkeley db package. 302 */ 303 304 switch (*spec) 305 { 306 case '+': /* search remote database */ 307 case '*': /* search remote database (expand MX) */ 308 if (*spec == '*') 309 { 310 nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 311 if (tTd(28, 16)) 312 { 313 int i; 314 315 printf("getmxrr(%s): %d", spec + 1, nmx); 316 for (i = 0; i <= nmx; i++) 317 printf(" %s", mxhosts[i]); 318 printf("\n"); 319 } 320 } 321 else 322 { 323 nmx = 1; 324 mxhosts[0] = spec + 1; 325 } 326 327 for (i = 0; i < nmx; i++) 328 { 329 h = gethostbyname(mxhosts[i]); 330 if (h == NULL) 331 continue; 332 up->udb_type = UDB_REMOTE; 333 up->udb_addr.sin_family = h->h_addrtype; 334 bcopy(h->h_addr_list[0], 335 (char *) &up->udb_addr.sin_addr, 336 h->h_length); 337 up->udb_addr.sin_port = UdbPort; 338 up->udb_timeout = UdbTimeout; 339 up++; 340 } 341 342 /* set up a datagram socket */ 343 if (UdbSock < 0) 344 { 345 UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 346 (void) fcntl(UdbSock, F_SETFD, 1); 347 } 348 break; 349 350 case '@': /* forward to remote host */ 351 up->udb_type = UDB_FORWARD; 352 up->udb_fwdhost = spec + 1; 353 up++; 354 break; 355 356 case '/': /* look up remote name */ 357 up->udb_dbname = spec; 358 errno = 0; 359 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 360 if (up->udb_dbp == NULL) 361 { 362 if (errno != ENOENT && errno != EACCES) 363 return EX_TEMPFAIL; 364 break; 365 } 366 up->udb_type = UDB_LOOKUP; 367 up++; 368 break; 369 } 370 } 371 up->udb_type = UDB_EOLIST; 372 373 if (tTd(28, 4)) 374 { 375 for (up = UdbEnts; ; up++) 376 { 377 switch (up->udb_type) 378 { 379 case UDB_EOLIST: 380 return EX_OK; 381 382 case UDB_REMOTE: 383 printf("REMOTE: addr %s, timeo %d\n", 384 inet_ntoa(up->udb_addr.sin_addr), 385 up->udb_timeout); 386 break; 387 388 case UDB_LOOKUP: 389 printf("LOOKUP: file %s\n", 390 up->udb_dbname); 391 break; 392 393 case UDB_FORWARD: 394 printf("FORWARD: host %s\n", 395 up->udb_fwdhost); 396 break; 397 398 default: 399 printf("UNKNOWN\n"); 400 break; 401 } 402 } 403 } 404 } 405 406 int 407 _udb_parsespec(udbspec, opt, maxopts) 408 char *udbspec; 409 struct option opt[]; 410 int maxopts; 411 { 412 register char *spec; 413 register char *spec_end; 414 register int optnum; 415 416 spec_end = index(udbspec, ':'); 417 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 418 { 419 register char *p; 420 421 while (isspace(*spec)) 422 spec++; 423 spec_end = index(spec, ':'); 424 if (spec_end != NULL) 425 *spec_end++ = '\0'; 426 427 opt[optnum].name = spec; 428 opt[optnum].val = NULL; 429 p = index(spec, '='); 430 if (p != NULL) 431 opt[optnum].val = ++p; 432 } 433 return optnum; 434 } 435 436 #else /* not USERDB */ 437 438 int 439 udbexpand(a, sendq) 440 ADDRESS *a; 441 ADDRESS **sendq; 442 { 443 return EX_OK; 444 } 445 446 #endif /* USERDB */ 447