This is transport.c in view mode; [Download] [Up]
/* @(#)src/transport.c 1.3 18 Feb 1991 15:33:08 */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * transport.c: * process and deliver remote addresses. * * The routines in this file are used after an address has been * routed and resolved and associated with a particular transport. * This file provides some support routines for transport drivers. * * external functions: assign_transports, call_transports, * write_message, remote_from_line, local_from_line, * find_transport, find_transport_driver */ #include <stdio.h> #include <ctype.h> #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include "defs.h" #include "smail.h" #include "addr.h" #include "transport.h" #include "log.h" #include "spool.h" #include "dys.h" #include "exitcodes.h" #include "smailconf.h" #ifndef DEPEND # include "extern.h" # include "debug.h" # include "error.h" #endif #ifdef UNIX_BSD # include <sys/file.h> #endif /* variables exported from this file */ char *path_to_sender = NULL; /* uucp-style route to sender */ int cached_transports = TRUE; /* TRUE if cache_transports() called */ /* functions local to this file */ static int assign_compare(); static int write_bsmtp_prologue(); static int write_smtp_addr(); static int write_bsmtp_epilogue(); static void build_path_to_sender(); static char *transport_driv_function(); /* * assign_transports - assign instances of transports for addresses * * given a list of addresses as input, produce a list assigning a list * of remote addresses to specific instances of a transport. One * instance essentially maps onto exactly one call to the underlying * transport driver. * * NOTE: This routine breaks the forward links between addr structures. */ struct assign_transport * assign_transports(list) struct addr *list; /* list of addresses */ { extern void qsort(); register struct addr *aq; /* temp */ register struct assign_transport *asn; /* list of assigned transports */ register struct addr **aap; /* points to array for sorting */ register int i; /* index */ int ct; /* count of items in input list */ int host_ct; /* count of hosts in instance */ int addr_ct; /* count of addrs in instance */ int char_ct = 0; /* count of chars in instance */ struct transport *last_transport = NULL; /* last assigned transport */ char *last_host; /* last assigned host */ /* count the number of input items */ for (ct = 0, aq = list; aq; aq = aq->succ) { ct++; } DEBUG1(DBG_REMOTE_HI, "assign_transports called with %d addrs\n", ct); /* create an array and copy remote information to it for sorting */ aap = (struct addr **)xmalloc(ct * sizeof(*aap)); for (aq = list, i = 0; aq; aq = aq->succ, i++) { aap[i] = aq; } /* NOTE: qsort does not appear to return anything */ qsort((char *)aap, ct, sizeof(*aap), assign_compare); /* * we now have an array sorted by transport and host. * pass through that array and assign addr structures to * specific transport instances, taking into account * max_addr and max_hosts for the transports. */ for (asn = NULL, i = ct-1; i >= 0; --i) { /* possible reasons to switch to a new instance */ if ( /* we have a different transport */ aap[i]->transport != last_transport || /* maximum number of hosts per transport call exceeded */ (last_transport->max_hosts && aap[i]->next_host && last_host && !EQ(aap[i]->next_host, last_host) && last_transport->max_hosts < ++host_ct) || /* maximum number of addrs per transport call exceeded */ (last_transport->max_addrs && last_transport->max_addrs < ++addr_ct) || /* maximum number of chars of addr per call exceeded */ (last_transport->max_chars && last_transport->max_chars < (char_ct += strlen(aap[i]->next_addr) + 3))) { /* initialize for a new instance */ struct assign_transport *newasn; DEBUG1(DBG_REMOTE_HI, "new instance ... transport=<%s>\n", aap[i]->transport->name); newasn = (struct assign_transport *)xmalloc(sizeof(*newasn)); last_transport = aap[i]->transport; last_host = aap[i]->next_host; host_ct = 1; addr_ct = 1; char_ct = strlen(aap[i]->next_addr) + 3; /* fill in the first entry of the instance */ aap[i]->succ = NULL; newasn->succ = asn; asn = newasn; } else { /* add the addr to the previous instance */ aap[i]->succ = asn->addr; } asn->addr = aap[i]; DEBUG2(DBG_REMOTE_HI, "adding...host=<%s>, addr=<%s>\n", asn->addr->next_host, asn->addr->next_addr); } /* all done, free temps and return the list */ xfree((char *)aap); return asn; } /* * compare two struct addrs for transport and then hostname */ static int assign_compare(a, b) struct addr **a; struct addr **b; { register int ret = strcmp((*a)->transport->name, (*b)->transport->name); if (ret) { /* transports are unequal, return value just based on that */ return ret; } /* if next_host not defined, the addrs are equal */ if ((*a)->next_host == NULL || (*b)->next_host == NULL) { return 0; } /* otherwise, return value is based on host */ return strcmpic((*a)->next_host, (*b)->next_host); } /* * call_transports - call transport drivers for assigned transports * * given a list produced by assign_transports, call the transport drivers * to actually perform delivery to addresses. */ void call_transports(asn, defer, fail) register struct assign_transport *asn; /* list of assigned instances */ struct addr **defer; /* addrs to defer to a later time */ struct addr **fail; /* unresolvable addrs */ { struct addr *succeed; /* succeeded addrs from drivers */ struct addr *tp_fail; /* fail addrs from drivers */ struct addr *shadow_list = NULL; /* shadow delivery list */ struct assign_transport *sh_asn; /* shadow transport instances */ for (; asn; asn = asn->succ) { struct transport_driver *driver; register struct transport *tp = asn->addr->transport; /* * don't deliver to remote transports if the hop_count would * pass beyond maximum. */ if (hop_count >= max_hop_count && !(tp->flags&LOCAL_TPORT)) { /* * ERR_145 - maximum hop count exceeded * * DESCRIPTION * If a remote transport were performed, the maximum hop * count, as set by the max_hop_cound attribute in the * config file, would be exceeded. This condition is * assumed to be a result of a loop condition where two * machines are sending mail back and forth to each other. * * ACTIONS * An error message is sent back to the sender stating the * error. Note that if one of the sites back to the sender * has a shorter value for max_hop_count, then the error * message will fail to reach the sender though, hopefully, * a message will be sent back to the postmaster at the * local site. * * RESOLUTION * Determine which two sites are causing the loop condition * and remove or correct the forward or alias from one of * those two sites. If the problem is not actually a loop * condition but a long path, a shorter path should be * used. It is unlikely that paths longer than 10 or 15 * hops should ever be required. */ struct error *loop_error = note_error(ERR_NSENDER|ERR_145, "loop detection: maximum hop count exceeded"); register struct addr *cur; /* NOTE: the sequence of operations here IS correct */ for (cur = asn->addr; cur; cur = cur->succ) { cur->error = loop_error; } fail_delivery(asn->addr); insert_addr_list(asn->addr, fail, loop_error); continue; } /* lookup the driver */ driver = find_transport_driver(tp->driver); if (driver == NULL) { /* configuration error, the driver does not exist */ /* * ERR_146 - transport driver not found * * DESCRIPTION * The driver name was not in the table of transport * drivers. * * ACTIONS * Defer addresses with configuration errors. * * RESOLUTION * The postmaster must check the transport configuration * before delivery can be performed. */ insert_addr_list(asn->addr, defer, note_error(ERR_CONFERR|ERR_146, xprintf( "transport %s: driver %s not found", tp->name, tp->driver))); continue; } /* call the transport */ DEBUG2(DBG_REMOTE_LO, "transport %s uses driver %s\n", tp->name, tp->driver); succeed = NULL; tp_fail = NULL; (*driver->driver)(asn->addr, &succeed, defer, &tp_fail); /* look at successful deliveries */ if (succeed) { succeed_delivery(succeed); /* log them */ if (tp->shadow) { /* on successful delivery, call the shadow transport */ struct transport *shadow_tp = find_transport(tp->shadow); if (shadow_tp == NULL) { send_to_postmaster = TRUE; write_log(LOG_SYS|LOG_MLOG, "transport %s: shadow transport %s not found", tp->name, tp->shadow); } else { /* insert in shadow delivery list, with new transport */ register struct addr *cur; register struct addr *next; for (cur = succeed; cur; cur = next) { next = cur->succ; cur->flags |= ADDR_SHADOW; cur->transport = shadow_tp; cur->succ = shadow_list; shadow_list = cur; } } } } /* look at failed deliveries */ if (tp_fail) { fail_delivery(tp_fail); /* log them */ if (tp->error_transport == NULL) { insert_addr_list(tp_fail, fail, (struct error *)NULL); } else { /* on failed delivery, call the error transport */ struct transport *error_tp = find_transport(tp->error_transport); if (error_tp == NULL) { send_to_postmaster = TRUE; write_log(LOG_SYS|LOG_MLOG, "transport %s: error transport %s not found", tp->name, tp->error_transport); } else { /* insert in shadow delivery list, with new transport */ register struct addr *cur; register struct addr *next; for (cur = tp_fail; cur; cur = next) { next = cur->succ; cur->transport = error_tp; cur->succ = shadow_list; shadow_list = cur; } } } } } /* if there are shadow deliveries to be made, assign them and transport */ if (shadow_list == NULL) { return; /* no shadows, just return */ } /* assign shadow deliveries */ for (sh_asn = assign_transports(shadow_list); sh_asn; sh_asn = sh_asn->succ) { struct transport_driver *driver; register struct transport *tp = sh_asn->addr->transport; struct addr *dummy_defer; /* lookup the driver */ driver = find_transport_driver(tp->driver); if (driver == NULL) { /* configuration error: for shadows, send to postmaster */ send_to_postmaster = TRUE; write_log(LOG_SYS|LOG_MLOG, "shadow transport %s: driver %s not found", tp->name, tp->driver); continue; } /* call the shadow transport */ DEBUG2(DBG_REMOTE_LO, "calling driver %s from shadow transport %s\n", tp->driver, tp->name); succeed = NULL; tp_fail = NULL; (*driver->driver)(sh_asn->addr, &succeed, &dummy_defer, &tp_fail); /* log failed addresses */ if (fail) { struct addr *cur; struct addr *next; for (cur = tp_fail; cur; cur = next) { next = cur->succ; write_log(LOG_SYS, "%s ... shadow transport %s failed: %s", cur->in_addr, cur->transport->name, cur->next_addr); if ((cur->flags & ADDR_SHADOW) == 0) { /* * put failed deliveries from an error_transport on * output fail list */ cur->succ = *fail; *fail = cur; } } } } } /* * cache_transports - call cache entrypoints for all transports * * cache information used by transport drivers. This can be called * when it is determined that there will be an attempt to deliver more * than one mail message, to increase the overall efficiency of the * mailer. * * Daemons can call this periodically to recache stale data. */ void cache_transports() { struct transport *tp; /* temp for stepping thru transports */ struct transport_driver *driver; for (tp = transports; tp; tp = tp->succ) { driver = find_transport_driver(tp->driver); if (driver && driver->cache) { (*driver->cache)(tp); } } cached_transports = TRUE; } #ifdef notyet /* * finish_transports - free resources used by all directors * * free information that was cached by transports or used by * transports in the process of delivering messages. Transports can * cache data for efficiency, or can maintain state between * invocations. This function is called when transports will no * longer be needed, allowing transports to free any resources that * they were using that will no longer be needed. For example, it is * a good idea for transports to close any files that they opened, as * file descriptors are a precious resource in some machines. */ void finish_transports() { struct transports *tp; /* temp for stepping thru transports */ struct transport_driver *driver; for (tp = transports; tp; tp = tp->succ) { driver = find_transport_driver(tp->driver); if (driver->finish) { (*driver->finish)(dp); } } cached_transports = FALSE; } #endif /* * write_message - write out the current message to an open file * * this takes a transport file entry and a list of addr structures * and writes the message in the manner specified in the transport * file entry. It is intended to be called from transport drivers. * * If BSMTP_TPORT is set in the transport flags, an SMTP envelope * is placed around the message. However, result codes are not * read from the destination so this is not useful for interactive * SMTP. The list of addr structures is only used for writing the * SMTP envelope. HBSMTP_TPORT generates an SMTP envelope without * the initial HELO or the final QUIT commands. * * NOTE: Everybody still needs to call fflush after calling * write_message(). * * return READ_FAIL, WRITE_FAIL or SUCCEED */ int write_message(f, tp, addr) FILE *f; /* write on this file */ struct transport *tp; /* transport file entry */ struct addr *addr; /* list of addresses */ { int fail; /* returned failure codes */ /* * put out the initial smtp commands up to the DATA command */ if (tp->flags & (BSMTP_TPORT | HBSMTP_TPORT)) { if (write_bsmtp_prologue(f, tp, addr) == FAIL) { return WRITE_FAIL; } tp->flags |= PUT_DOTS; /* force use of SMTP dot protocol */ } /* * write out a From<space> line, if required */ if (tp->flags & PUT_FROM) { /* but what form do we need? */ if (tp->flags & LOCAL_TPORT) { if (fputs(local_from_line(), f) == EOF) { return WRITE_FAIL; } } else { if (fputs(remote_from_line(), f) == EOF) { return WRITE_FAIL; } } /* put the end of line */ if (tp->flags & PUT_CRLF) { if (putc('\r', f) == EOF) { return WRITE_FAIL; } } if (putc('\n', f) == EOF) { return WRITE_FAIL; } } /* * write out the header according to the transport flags */ if (write_header(f, tp->flags) == FAIL) { return WRITE_FAIL; } if ((tp->flags & PUT_CRLF) && putc('\r', f) == EOF) { return WRITE_FAIL; } if (putc('\n', f) == EOF) { return WRITE_FAIL; } if (tp->flags & DEBUG_TPORT) { /* if we are debugging, don't dump the body, dump interesting * debugging info instead */ extern char **save_argv; /* import this from main */ char **av; (void) fprintf(f, "|---- original flags ----|\n"); for (av = save_argv; *av; av++) { (void) fprintf(f, "%s\n", *av); } (void) fprintf(f, "|---- interesting info ----|\n"); (void) fprintf(f,"\ message_id=%s\n\ spool_dir=%s\n\ spool_fn=%s\n\ transport=%s\n\ next_host=%s\n\ msg_grade=%c\n", message_id, spool_dir, spool_fn, tp->name, addr->next_host, msg_grade); send_log(f, TRUE, "|---- per-message log ----|\n"); } else { /* * write out the body. */ if ((fail = write_body(f, tp->flags)) != SUCCEED) { return fail; } } /* * finish off the SMTP envelope */ if (tp->flags & (BSMTP_TPORT | HBSMTP_TPORT)) { if (write_bsmtp_epilogue(f, tp) == FAIL) { return WRITE_FAIL; } } return SUCCEED; /* everything went fine */ } /* * write_bsmtp_prologue - write the initial SMTP commands for BSMTP transports * * write out the HELO, MAIL FROM, RCPT TO and DATA commands. */ static int write_bsmtp_prologue(f, tp, addr) FILE *f; /* file to write commands to */ struct transport *tp; /* transport entry */ struct addr *addr; /* recipient addr structures */ { /* * how do we end a line? */ char *eol = (tp->flags & PUT_CRLF)? "\r\n": "\n"; char *error; if (! (tp->flags & HBSMTP_TPORT) && fprintf(f, "HELO %s%s", primary_name, eol) == EOF) { return FAIL; } /* * determine which sender form is appropriate */ if (error_sender) { /* if the special SMTP sender form <> was given, pass it along */ if (fprintf(f, "MAIL FROM:<>%s", eol) == EOF) { return FAIL; } } else if (tp->flags & LOCAL_TPORT) { if (fprintf(f, "MAIL FROM:<%s>%s", sender, eol) == EOF) { return FAIL; } } else if (tp->flags & UUCP_ONLY) { char *p = build_partial_uucp_route(sender, &error); if (fprintf(f, "MAIL FROM:<%s!%s>%s", uucp_name, p? p: sender, eol) == EOF) { if (p) { xfree(p); } return FAIL; } if (p) { xfree(p); } } else { if (fputs("MAIL FROM:<", f) == EOF || write_smtp_addr(f, primary_name, sender) == EOF || fprintf(f, ">%s", eol) == EOF) { return FAIL; } } /* * write out a RCPT TO: line for each recipient address */ while (addr) { if (tp->flags & LOCAL_TPORT) { if (fprintf(f, "RCPT TO:<%s>%s", addr->next_addr, eol) == EOF) { return FAIL; } } else if (tp->flags & UUCP_ONLY) { char *p = build_partial_uucp_route(addr->next_addr); if (fprintf(f, "RCPT TO:<%s!%s>%s", addr->next_host, p? p: addr->next_addr, eol) == EOF) { return FAIL; } } else { if (fputs("RCPT TO:<", f) == EOF || write_smtp_addr(f, addr->next_host, addr->next_addr) == EOF || fprintf(f, ">%s", eol) == EOF) { return FAIL; } } addr = addr->succ; } if (fprintf(f, "DATA%s", eol) == EOF) { return FAIL; } return SUCCEED; } /* * write_smtp_addr - write out an address in SMTP form * * write out an address with the destination host included, as expected * by SMTP. */ static int write_smtp_addr(f, host, addr) FILE *f; /* output */ char *host; /* destination host */ char *addr; /* address beyond host */ { register char *p; if (host == NULL) { /* trivial case */ return fputs(addr, f); } p = back_address_token(addr, addr + strlen(addr)); if (p == NULL || p <= addr + sizeof("@") || p[-1] != '@') { return fprintf(f, "%s@%s", addr, host); } if (addr[0] == '@') { return fprintf(f, "@%s,%s", host, addr); } return fprintf(f, "@%s:%s", host, addr); } /* * write_bsmtp_epilogue - write tailing commands for BSMTP transports * * finish off the data command and write out the QUIT command. */ static int write_bsmtp_epilogue(f, tp) FILE *f; struct transport *tp; { if (tp->flags & HBSMTP_TPORT) { if (fprintf(f, (tp->flags&PUT_CRLF)? ".\r\n": ".\n") == EOF) { return FAIL; } return SUCCEED; } if (tp->flags & PUT_CRLF) { if (fprintf(f, ".\r\nQUIT\r\n") == EOF) { return FAIL; } } else { if (fprintf(f, ".\nQUIT\n") == EOF) { return FAIL; } } return SUCCEED; } /* * remote_from_line - build a From_ line for remote transports */ char * remote_from_line() { static char *from_line = NULL; /* saved From_ line */ if (from_line) { return from_line; /* exists, use it */ } if (path_to_sender == NULL) { build_path_to_sender(); } from_line = xprintf("From %s %s remote from %s", path_to_sender, unix_date(), uucp_name); return from_line; } /* * local_from_line - build a From_ line for local transports */ char * local_from_line() { static char *from_line = NULL; /* saved From_ line */ if (from_line) { return from_line; /* exists, use it */ } if (path_to_sender == NULL) { build_path_to_sender(); } from_line = xprintf("From %s %s", path_to_sender, unix_date()); return from_line; } /* * build_path_to_sender - build a pure !-route version of sender address */ static void build_path_to_sender() { char *error; path_to_sender = build_uucp_route(sender, &error); if (path_to_sender == NULL) { write_log(LOG_SYS|LOG_MLOG, "error building path to sender, sender=%s, error=%s", sender, error); path_to_sender = "Postmaster"; } } /* * find_transport - given a transport's name, return the transport structure * * return NULL if no transport of that name exists. */ struct transport * find_transport(name) register char *name; /* search key */ { register struct transport *tp; /* temp for stepping thru transports */ /* loop through all the transports */ for (tp = transports; tp; tp = tp->succ) { if (EQ(tp->name, name)) { /* found the transport in question */ return tp; } } return NULL; /* transport not found */ } /* * find_transport_driver - given a driver's name, return the driver structure * * return NULL if driver does not exist. */ struct transport_driver * find_transport_driver(name) register char *name; /* search key */ { register struct transport_driver *tdp; /* pointer to table of drivers */ for (tdp = transport_drivers; tdp->name; tdp++) { if (EQ(tdp->name, name)) { return tdp; /* found the driver */ } } return NULL; /* driver not found */ } /* * read_transport_file - read transport file * * read the transport file and build the transport list describing the * entries. Return an error message or NULL. */ char * read_transport_file() { FILE *f; /* open transport file */ char *error; /* error from read_standard_file() */ struct stat statbuf; static struct attr_table transport_generic[] = { { "driver", t_string, NULL, NULL, OFFSET(transport, driver) }, { "max_addrs", t_int, NULL, NULL, OFFSET(transport, max_addrs) }, { "max_hosts", t_int, NULL, NULL, OFFSET(transport, max_hosts) }, { "max_chars", t_int, NULL, NULL, OFFSET(transport, max_chars) }, { "shadow", t_string, NULL, NULL, OFFSET(transport, shadow) }, { "error_transport", t_string, NULL, NULL, OFFSET(transport, error_transport) }, { "strict", t_boolean, NULL, NULL, STRICT_TPORT }, { "uucp", t_boolean, NULL, NULL, UUCP_ONLY }, { "received", t_boolean, NULL, NULL, PUT_RECEIVED }, { "return_path", t_boolean, NULL, NULL, PUT_RETURNPATH }, { "from", t_boolean, NULL, NULL, PUT_FROM }, { "local", t_boolean, NULL, NULL, LOCAL_TPORT }, { "crlf", t_boolean, NULL, NULL, PUT_CRLF }, { "bsmtp", t_boolean, NULL, NULL, BSMTP_TPORT }, { "hbsmtp", t_boolean, NULL, NULL, HBSMTP_TPORT }, { "dots", t_boolean, NULL, NULL, PUT_DOTS }, { "debug", t_boolean, NULL, NULL, DEBUG_TPORT }, { "unix_from_hack", t_boolean, NULL, NULL, UNIX_FROM_HACK }, { "uucp_from_hack", t_boolean, NULL, NULL, UNIX_FROM_HACK }, }; struct attr_table *end_transport_generic = ENDTABLE(transport_generic); static struct transport transport_template = { NULL, /* name */ "pipe", /* driver, a reasonable default */ NULL, /* succ will be assigned */ PUT_RECEIVED, /* flags */ 1, /* max_addrs */ 1, /* max_hosts */ 2000, /* max_chars, about half of NCARGS? */ NULL, /* shadow */ NULL, /* error_transport */ NULL, /* private */ }; /* * try to open the transport file, stat file if possible */ if (transport_file == NULL || EQ(transport_file, "-")) { return NULL; } f = fopen(transport_file, "r"); if (f == NULL) { if (require_configs) { return xprintf("%s: %s", transport_file, strerrno()); } add_config_stat(transport_file, (struct stat *)NULL); return NULL; } (void)fstat(fileno(f), &statbuf); add_config_stat(transport_file, &statbuf); /* call read_standard_file to do the real work */ error = read_standard_file(f, (char *)&transport_template, sizeof(struct transport), OFFSET(transport, name), OFFSET(transport, flags), OFFSET(transport, succ), transport_generic, end_transport_generic, transport_driv_function, (char **)&transports); /* finish up */ (void) fclose(f); /* return any error message */ if (error) { return xprintf("%s: %s", transport_file, error); } return NULL; } static char * transport_driv_function(struct_p, driver_attrs) char *struct_p; /* passed transport structure */ struct attribute *driver_attrs; /* driver-specific attributes */ { struct transport *tp = (struct transport *)struct_p; struct transport_driver *drv; if (tp->driver == NULL) { return xprintf("transport %s: no driver attribute", tp->name); } drv = find_transport_driver(tp->driver); if (drv == NULL) { return xprintf("transport %s: unknown driver: %s", tp->name, tp->driver); } if (drv->builder) { return (*drv->builder)(tp, driver_attrs); } return NULL; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.