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