X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/462182534289f0484d05688616f39943abd2f9a4..38a0a95ff69327042421b9ee6982e386175f141b:/src/src/expand.c diff --git a/src/src/expand.c b/src/src/expand.c index 3b5036345..2e39f2638 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/expand.c,v 1.57 2006/03/08 11:13:07 ph10 Exp $ */ +/* $Cambridge: exim/src/src/expand.c,v 1.61 2006/09/19 11:28:45 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -300,6 +300,8 @@ enum { /* This table must be kept in alphabetical order. */ static var_entry var_table[] = { + /* WARNING: Do not invent variables whose names start acl_c or acl_m because + they will be confused with user-creatable ACL variables. */ { "acl_verify_message", vtype_stringptr, &acl_verify_message }, { "address_data", vtype_stringptr, &deliver_address_data }, { "address_file", vtype_stringptr, &address_file }, @@ -1231,37 +1233,25 @@ find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize) int first = 0; int last = var_table_size; -/* Handle ACL variables, which are not in the table because their number may -vary depending on a build-time setting. If the variable's name is not of the -form acl_mddd or acl_cddd, where the d's are digits, fall through to look for -other names that start with acl_. */ +/* Handle ACL variables, whose names are of the form acl_cxxx or acl_mxxx. +Originally, xxx had to be a number in the range 0-9 (later 0-19), but from +release 4.64 onwards arbitrary names are permitted, as long as the first 5 +characters are acl_c or acl_m (this gave backwards compatibility at the +changeover). There may be built-in variables whose names start acl_ but they +should never start acl_c or acl_m. This slightly messy specification is a +consequence of the history, needless to say. -if (Ustrncmp(name, "acl_", 4) == 0) - { - uschar *endptr; - int offset = -1; - int max = 0; - - if (name[4] == 'm') - { - offset = ACL_CVARS; - max = ACL_MVARS; - } - else if (name[4] == 'c') - { - offset = 0; - max = ACL_CVARS; - } +If an ACL variable does not exist, treat it as empty, unless strict_acl_vars is +set, in which case give an error. */ - if (offset >= 0) - { - int n = Ustrtoul(name + 5, &endptr, 10); - if (*endptr == 0 && n < max) - return (acl_var[offset + n] == NULL)? US"" : acl_var[offset + n]; - } +if (Ustrncmp(name, "acl_c", 5) == 0 || Ustrncmp(name, "acl_m", 5) == 0) + { + tree_node *node = + tree_search((name[4] == 'c')? acl_var_c : acl_var_m, name + 4); + return (node == NULL)? (strict_acl_vars? NULL : US"") : node->data.ptr; } -/* Similarly for $auth variables. */ +/* Handle $auth variables. */ if (Ustrncmp(name, "auth", 4) == 0) { @@ -1681,6 +1671,13 @@ switch(cond_type) expand_string_message = (name[0] == 0)? string_sprintf("variable name omitted after \"def:\"") : string_sprintf("unknown variable \"%s\" after \"def:\"", name); + + if (strict_acl_vars && + Ustrncmp(name, "acl_", 4) == 0 && + (name[4] == 'c' || name[4] == 'm')) + expand_string_message = string_sprintf("%s (strict_acl_vars is set)", + expand_string_message); + return NULL; } if (yield != NULL) *yield = (value[0] != 0) == testfor; @@ -1893,25 +1890,8 @@ switch(cond_type) if (!isalpha(name[0])) { - uschar *endptr; - num[i] = (int)Ustrtol((const uschar *)sub[i], &endptr, 10); - if (tolower(*endptr) == 'k') - { - num[i] *= 1024; - endptr++; - } - else if (tolower(*endptr) == 'm') - { - num[i] *= 1024*1024; - endptr++; - } - while (isspace(*endptr)) endptr++; - if (*endptr != 0) - { - expand_string_message = string_sprintf("\"%s\" is not a number", - sub[i]); - return NULL; - } + num[i] = expand_string_integer(sub[i], FALSE); + if (expand_string_message != NULL) return NULL; } } @@ -2976,6 +2956,13 @@ while (*s != 0) { expand_string_message = string_sprintf("unknown variable name \"%s\"", name); + + if (strict_acl_vars && + Ustrncmp(name, "acl_", 4) == 0 && + (name[4] == 'c' || name[4] == 'm')) + expand_string_message = string_sprintf("%s (strict_acl_vars is set)", + expand_string_message); + goto EXPAND_FAILED; } } @@ -3655,28 +3642,148 @@ while (*s != 0) } else sub_arg[3] = NULL; /* No eol if no timeout */ - /* If skipping, we don't actually do anything */ + /* If skipping, we don't actually do anything. Otherwise, arrange to + connect to either an IP or a Unix socket. */ if (!skipping) { - /* Make a connection to the socket */ + /* Handle an IP (internet) domain */ - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + if (Ustrncmp(sub_arg[0], "inet:", 5) == 0) { - expand_string_message = string_sprintf("failed to create socket: %s", - strerror(errno)); - goto SOCK_FAIL; + BOOL connected = FALSE; + int namelen, port; + host_item shost; + host_item *h; + uschar *server_name = sub_arg[0] + 5; + uschar *port_name = Ustrrchr(server_name, ':'); + + /* Sort out the port */ + + if (port_name == NULL) + { + expand_string_message = + string_sprintf("missing port for readsocket %s", sub_arg[0]); + goto EXPAND_FAILED; + } + *port_name++ = 0; /* Terminate server name */ + + if (isdigit(*port_name)) + { + uschar *end; + port = Ustrtol(port_name, &end, 0); + if (end != port_name + Ustrlen(port_name)) + { + expand_string_message = + string_sprintf("invalid port number %s", port_name); + goto EXPAND_FAILED; + } + } + else + { + struct servent *service_info = getservbyname(CS port_name, "tcp"); + if (service_info == NULL) + { + expand_string_message = string_sprintf("unknown port \"%s\"", + port_name); + goto EXPAND_FAILED; + } + port = ntohs(service_info->s_port); + } + + /* Sort out the server. */ + + shost.next = NULL; + shost.address = NULL; + shost.port = port; + shost.mx = -1; + + namelen = Ustrlen(server_name); + + /* Anything enclosed in [] must be an IP address. */ + + if (server_name[0] == '[' && + server_name[namelen - 1] == ']') + { + server_name[namelen - 1] = 0; + server_name++; + if (string_is_ip_address(server_name, NULL) == 0) + { + expand_string_message = + string_sprintf("malformed IP address \"%s\"", server_name); + goto EXPAND_FAILED; + } + shost.name = shost.address = server_name; + } + + /* Otherwise check for an unadorned IP address */ + + else if (string_is_ip_address(server_name, NULL) != 0) + shost.name = shost.address = server_name; + + /* Otherwise lookup IP address(es) from the name */ + + else + { + shost.name = server_name; + if (host_find_byname(&shost, NULL, NULL, FALSE) != HOST_FOUND) + { + expand_string_message = + string_sprintf("no IP address found for host %s", shost.name); + goto EXPAND_FAILED; + } + } + + /* Try to connect to the server - test each IP till one works */ + + for (h = &shost; h != NULL; h = h->next) + { + int af = (Ustrchr(h->address, ':') != 0)? AF_INET6 : AF_INET; + if ((fd = ip_socket(SOCK_STREAM, af)) == -1) + { + expand_string_message = string_sprintf("failed to create socket: " + "%s", strerror(errno)); + goto SOCK_FAIL; + } + + if (ip_connect(fd, af, h->address, port, timeout) == 0) + { + connected = TRUE; + break; + } + } + + if (!connected) + { + expand_string_message = string_sprintf("failed to connect to " + "socket %s: couldn't connect to any host", sub_arg[0], + strerror(errno)); + goto SOCK_FAIL; + } } - sockun.sun_family = AF_UNIX; - sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), - sub_arg[0]); - if(connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun)) == -1) + /* Handle a Unix domain socket */ + + else { - expand_string_message = string_sprintf("failed to connect to socket " - "%s: %s", sub_arg[0], strerror(errno)); - goto SOCK_FAIL; + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + { + expand_string_message = string_sprintf("failed to create socket: %s", + strerror(errno)); + goto SOCK_FAIL; + } + + sockun.sun_family = AF_UNIX; + sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1), + sub_arg[0]); + if(connect(fd, (struct sockaddr *)(&sockun), sizeof(sockun)) == -1) + { + expand_string_message = string_sprintf("failed to connect to socket " + "%s: %s", sub_arg[0], strerror(errno)); + goto SOCK_FAIL; + } } + DEBUG(D_expand) debug_printf("connected to socket %s\n", sub_arg[0]); /* Write the request string, if not empty */ @@ -3710,7 +3817,7 @@ while (*s != 0) if (sigalrm_seen) { ptr = save_ptr; - expand_string_message = US"socket read timed out"; + expand_string_message = US "socket read timed out"; goto SOCK_FAIL; } } @@ -5015,6 +5122,13 @@ while (*s != 0) { expand_string_message = string_sprintf("unknown variable in \"${%s}\"", name); + + if (strict_acl_vars && + Ustrncmp(name, "acl_", 4) == 0 && + (name[4] == 'c' || name[4] == 'm')) + expand_string_message = string_sprintf("%s (strict_acl_vars is set)", + expand_string_message); + goto EXPAND_FAILED; } len = Ustrlen(value); @@ -5140,22 +5254,26 @@ return yield; /* Expand a string, and convert the result into an integer. -Argument: the string to be expanded +Arguments: + string the string to be expanded + isplus TRUE if a non-negative number is expected Returns: the integer value, or -1 for an expansion error ) in both cases, message in -2 for an integer interpretation error ) expand_string_message - + expand_string_message is set NULL for an OK integer */ int -expand_string_integer(uschar *string) +expand_string_integer(uschar *string, BOOL isplus) { long int value; uschar *s = expand_string(string); uschar *msg = US"invalid integer \"%s\""; uschar *endptr; +/* If expansion failed, expand_string_message will be set. */ + if (s == NULL) return -1; /* On an overflow, strtol() returns LONG_MAX or LONG_MIN, and sets errno @@ -5163,12 +5281,17 @@ to ERANGE. When there isn't an overflow, errno is not changed, at least on some systems, so we set it zero ourselves. */ errno = 0; +expand_string_message = NULL; /* Indicates no error */ value = strtol(CS s, CSS &endptr, 0); if (endptr == s) { msg = US"integer expected but \"%s\" found"; } +else if (value < 0 && isplus) + { + msg = US"non-negative integer expected but \"%s\" found"; + } else { /* Ensure we can cast this down to an int */