From f0416b327688746ab560b2f55651af20d8d98424 Mon Sep 17 00:00:00 2001 From: Eric Allman Date: Sat, 13 May 1995 22:06:16 -0800 Subject: [PATCH] put getcanonname into service switch SCCS-vsn: usr.sbin/sendmail/src/pathnames.h 8.3 SCCS-vsn: usr.sbin/sendmail/src/conf.h 8.156 SCCS-vsn: usr.sbin/sendmail/src/domain.c 8.37 SCCS-vsn: usr.sbin/sendmail/src/sendmail.h 8.132 SCCS-vsn: usr.sbin/sendmail/src/util.c 8.66 SCCS-vsn: usr.sbin/sendmail/src/readcf.c 8.88 SCCS-vsn: usr.sbin/sendmail/src/conf.c 8.159 SCCS-vsn: usr.sbin/sendmail/src/map.c 8.58 --- usr/src/usr.sbin/sendmail/src/conf.c | 12 +- usr/src/usr.sbin/sendmail/src/conf.h | 5 +- usr/src/usr.sbin/sendmail/src/domain.c | 59 +-- usr/src/usr.sbin/sendmail/src/map.c | 423 +++++++++++++++++++++- usr/src/usr.sbin/sendmail/src/pathnames.h | 6 +- usr/src/usr.sbin/sendmail/src/readcf.c | 8 +- usr/src/usr.sbin/sendmail/src/sendmail.h | 5 +- usr/src/usr.sbin/sendmail/src/util.c | 39 +- 8 files changed, 508 insertions(+), 49 deletions(-) diff --git a/usr/src/usr.sbin/sendmail/src/conf.c b/usr/src/usr.sbin/sendmail/src/conf.c index 871754f4ae..e2014d6fe3 100644 --- a/usr/src/usr.sbin/sendmail/src/conf.c +++ b/usr/src/usr.sbin/sendmail/src/conf.c @@ -7,7 +7,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)conf.c 8.158 (Berkeley) %G%"; +static char sccsid[] = "@(#)conf.c 8.159 (Berkeley) %G%"; #endif /* not lint */ # include "sendmail.h" @@ -191,6 +191,7 @@ setdefaults(e) TimeOuts.to_q_warning[i] = 0; /* option T */ } ServiceSwitchFile = "/etc/service.switch"; + HostsFile = _PATH_HOSTS; setdefuser(); setupmaps(); setupmailers(); @@ -629,6 +630,9 @@ switch_map_find(service, maptype, mapreturn) struct svcinfo *svcinfo; int svc; + for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) + mapreturn[svcno] = 0; + svcinfo = getsvc(); if (svcinfo == NULL) goto punt; @@ -674,6 +678,9 @@ switch_map_find(service, maptype, mapreturn) ** Fall-back mechanism. */ + for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) + mapreturn[svcno] = 0; + svcno = 0; fp = fopen(ServiceSwitchFile, "r"); if (fp != NULL) @@ -712,6 +719,9 @@ switch_map_find(service, maptype, mapreturn) /* if the service file doesn't work, use an absolute fallback */ punt: + for (svcno = 0; svcno < MAXMAPACTIONS; svcno++) + mapreturn[svcno] = 0; + svcno = 0; if (strcmp(service, "aliases") == 0) { maptype[svcno++] = "files"; diff --git a/usr/src/usr.sbin/sendmail/src/conf.h b/usr/src/usr.sbin/sendmail/src/conf.h index 190fbcaadd..9e3e19c0ae 100644 --- a/usr/src/usr.sbin/sendmail/src/conf.h +++ b/usr/src/usr.sbin/sendmail/src/conf.h @@ -5,7 +5,7 @@ * * %sccs.include.redist.c% * - * @(#)conf.h 8.155 (Berkeley) %G% + * @(#)conf.h 8.156 (Berkeley) %G% */ /* @@ -253,6 +253,9 @@ extern int syslog(int, char *, ...); # ifndef _PATH_SENDMAILPID # define _PATH_SENDMAILPID "/etc/mail/sendmail.pid" # endif +# ifndef _PATH_HOSTS +# define _PATH_HOSTS "/etc/inet/hosts" +# endif # ifndef SYSLOG_BUFSIZE # define SYSLOG_BUFSIZE 1024 /* allow full size syslog buffer */ # endif diff --git a/usr/src/usr.sbin/sendmail/src/domain.c b/usr/src/usr.sbin/sendmail/src/domain.c index a0ce4ba639..cfc2296d32 100644 --- a/usr/src/usr.sbin/sendmail/src/domain.c +++ b/usr/src/usr.sbin/sendmail/src/domain.c @@ -10,9 +10,9 @@ #ifndef lint #if NAMED_BIND -static char sccsid[] = "@(#)domain.c 8.36 (Berkeley) %G% (with name server)"; +static char sccsid[] = "@(#)domain.c 8.37 (Berkeley) %G% (with name server)"; #else -static char sccsid[] = "@(#)domain.c 8.36 (Berkeley) %G% (without name server)"; +static char sccsid[] = "@(#)domain.c 8.37 (Berkeley) %G% (without name server)"; #endif #endif /* not lint */ @@ -384,7 +384,7 @@ mxrand(host) return hfunc; } /* -** GETCANONNAME -- get the canonical name for named host +** DNS_GETCANONNAME -- get the canonical name for named host using DNS ** ** This algorithm tries to be smart about wildcard MX records. ** This is hard to do because DNS doesn't tell is if we matched @@ -406,6 +406,7 @@ mxrand(host) ** This is a value-result parameter. ** hbsize -- the size of the host buffer. ** trymx -- if set, try MX records as well as A and CNAME. +** statp -- pointer to place to store status. ** ** Returns: ** TRUE -- if the host matched. @@ -413,10 +414,11 @@ mxrand(host) */ bool -getcanonname(host, hbsize, trymx) +dns_getcanonname(host, hbsize, trymx, statp) char *host; int hbsize; bool trymx; + int *statp; { extern int h_errno; register u_char *eom, *ap; @@ -443,7 +445,10 @@ getcanonname(host, hbsize, trymx) printf("getcanonname(%s)\n", host); if ((_res.options & RES_INIT) == 0 && res_init() == -1) - return (FALSE); + { + *statp = EX_UNAVAILABLE; + return FALSE; + } /* ** Initialize domain search list. If there is at least one @@ -520,6 +525,7 @@ cnameloop: { /* the name server seems to be down */ h_errno = TRY_AGAIN; + *statp = EX_TEMPFAIL; return FALSE; } @@ -570,6 +576,7 @@ cnameloop: if (tTd(8, 20)) printf("qdcount failure (%d)\n", ntohs(hp->qdcount)); + *statp = EX_SOFTWARE; return FALSE; /* ???XXX??? */ } } @@ -622,6 +629,7 @@ cnameloop: CurEnv->e_message = newstr(ebuf); } h_errno = NO_RECOVERY; + *statp = EX_CONFIG; return FALSE; } @@ -671,7 +679,10 @@ cnameloop: } if (mxmatch == NULL) + { + *statp = EX_NOHOST; return FALSE; + } /* create matching name and return */ (void) sprintf(nbuf, "%.*s%s%.*s", MAXDNAME, host, @@ -679,6 +690,7 @@ cnameloop: MAXDNAME, mxmatch); strncpy(host, nbuf, hbsize); host[hbsize - 1] = '\0'; + *statp = EX_OK; return TRUE; } @@ -855,39 +867,4 @@ retry: #endif /* DNS_MAILB */ - -#else /* not NAMED_BIND */ - -bool -getcanonname(host, hbsize, trymx) - char *host; - int hbsize; - bool trymx; -{ - struct hostent *hp; - char *p; - - hp = sm_gethostbyname(host); - if (hp == NULL) - return (FALSE); - p = hp->h_name; - if (strchr(p, '.') == NULL) - { - /* first word is a short name -- try to find a long one */ - char **ap; - - for (ap = hp->h_aliases; *ap != NULL; ap++) - if (strchr(*ap, '.') != NULL) - break; - if (*ap != NULL) - p = *ap; - } - - if (strlen(p) >= hbsize) - return (FALSE); - - (void) strcpy(host, p); - return (TRUE); -} - -#endif /* not NAMED_BIND */ +#endif /* NAMED_BIND */ diff --git a/usr/src/usr.sbin/sendmail/src/map.c b/usr/src/usr.sbin/sendmail/src/map.c index 50aaa9d92c..939cf79472 100644 --- a/usr/src/usr.sbin/sendmail/src/map.c +++ b/usr/src/usr.sbin/sendmail/src/map.c @@ -7,7 +7,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)map.c 8.57 (Berkeley) %G%"; +static char sccsid[] = "@(#)map.c 8.58 (Berkeley) %G%"; #endif /* not lint */ #include "sendmail.h" @@ -57,6 +57,10 @@ static char sccsid[] = "@(#)map.c 8.57 (Berkeley) %G%"; ** ** void map_close(MAP *map) ** Close the map. +** +** This file also includes the implementation for getcanonname. +** It is currently implemented in a pretty ad-hoc manner; it ought +** to be more properly integrated into the map structure. */ #define DBMMODE 0644 @@ -1168,6 +1172,93 @@ nis_map_lookup(map, name, av, statp) return map_rewrite(map, vp, vsize, av); } + +/* +** NIS_GETCANONNAME -- look up canonical name in NIS +*/ + +bool +nis_getcanonname(name, hbsize, statp) + char *name; + int hbsize; + int *statp; +{ + char *vp; + auto int vsize; + int keylen; + int yperr; + static bool try0null = TRUE; + static bool try1null = TRUE; + static char *yp_domain = NULL; + char *domain, *p; + char host_record[MAXLINE]; + char buf[MAXNAME]; + char *cname; + extern char *get_column(); + + if (tTd(38, 20)) + printf("nis_getcanonname(%s)\n", name); + + shorten_hostname(name); + + /* we only accept single token search key */ + if (strchr(name, '.')) + { + *statp = EX_NOHOST; + return FALSE; + } + + keylen = strlen(name); + + if (yp_domain == NULL) + yp_get_default_domain(&yp_domain); + makelower(name); + yperr = YPERR_KEY; + if (try0null) + { + yperr = yp_match(yp_domain, "hosts.byname", name, keylen, + &vp, &vsize); + if (yperr == 0) + try1null = FALSE; + } + if (yperr == YPERR_KEY && try1null) + { + keylen++; + yperr = yp_match(yp_domain, "hosts.byname", name, keylen, + &vp, &vsize); + if (yperr == 0) + try0null = FALSE; + } + if (yperr != 0) + { + if (yperr == YPERR_KEY) + *statp = EX_NOHOST; + else if (yperr == YPERR_BUSY) + *statp = EX_TEMPFAIL; + else + *statp = EX_UNAVAILABLE; + return FALSE; + } + strncpy(host_record, vp, vsize); + host_record[vsize] = '\0'; + cname = get_column(host_record, 1, '\t', buf); + if (cname == NULL) + { + /* this should not happen, but.... */ + *statp = EX_NOHOST; + return FALSE; + } + + if (hbsize >= strlen(cname)) + { + strcpy(name, cname); + *statp = EX_OK; + return TRUE; + } + *statp = EX_UNAVAILABLE; + return FALSE; +} + #endif /* ** NISPLUS Modules @@ -1435,6 +1526,139 @@ nisplus_map_lookup(map, name, av, statp) } + +/* +** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ +*/ + +bool +nisplus_getcanonname(name, hbsize, statp) + char *name; + int hbsize; + int *statp; +{ + char *vp; + auto int vsize; + int buflen; + char buf1[MAXLINE + NIS_MAXNAMELEN]; + static char *nis_path = NULL; + static char nis_path_buf[MAXLINE]; + nis_result *result; + char *p; + int len; + + if (nis_path == NULL) + { + char *ptr; + char pathbuf[MAXLINE]; + + strcpy(buf1, macvalue('m', CurEnv)); + strcat(buf1, "."); + /* try the host tabe in $m */ + if (hosts_table_ok(buf1)) + strcpy(pathbuf, buf1); + else + strcpy(pathbuf, "$"); + + nis_path = nis_path_buf; + sprintf(nis_path, "NIS_PATH=%s", pathbuf); + } + + if (nis_path[0] != '\0') + putenv(nis_path); + else + syslog(LOG_WARNING, "no NIS+ path defined"); + + shorten_hostname(name); + + p = strchr(name, '.'); + if (p == NULL) + { + /* single token */ + sprintf(buf1, "[name=%s],hosts.org_dir", name); + } + else if (p[1] != '\0') + { + /* multi token -- take only first token in name buf */ + *p = '\0'; + sprintf(buf1, "[name=%s],hosts.org_dir.%s", name, &p[1]); + } + else + { + *statp = EX_NOHOST; + return FALSE; + } + + if (tTd(38, 20)) + printf("\nnisplus_getcanoname(%s), qbuf=%s\n%s\n", + name, buf1, nis_path); + + result = nis_list(buf1, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, + NULL, NULL); + + /* unset NIS_PATH, just in case */ + unsetenv("NIS_PATH"); + + if (result->status == NIS_SUCCESS) + { + int count; + char *str; + char *domain; + + if ((count = NIS_RES_NUMOBJ(result)) != 1) + { +#ifdef LOG + if (LogLevel > 10) + syslog(LOG_WARNING, + "nisplus_getcanonname: Lookup error, expected 1 entry, got (%d)", + count); +#endif + + /* ignore second entry */ + if (tTd(38, 20)) + printf("nisplus_getcanoname(%s), got %d entries, addtional entries ignores\n", name); + } + + if (tTd(38, 20)) + printf("nisplus_getcanoname(%s), found in directory \"%s\"\n", + name, (NIS_RES_OBJECT(result))->zo_domain); + + + vp = ((NIS_RES_OBJECT(result))->EN_col(0)); + vsize = strlen(vp); + if (tTd(38, 20)) + printf("nisplus_getcanonname(%s), found %s\n", + name, vp); + domain = macvalue('m', CurEnv); + if (hbsize > (vsize + ((int) strlen(domain)))) + { + sprintf(name, "%s.%s", vp, domain); + *statp = EX_OK; + } + else + *statp = EX_NOHOST; + nis_freeresult(result); + return TRUE; + } + else + { + if (result->status == NIS_NOTFOUND) + *statp = EX_NOHOST; + else if (result->status == NIS_TRYAGAIN) + *statp = EX_TEMPFAIL; + else + { + *statp = EX_UNAVAILABLE; + } + } + if (tTd(38, 20)) + printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", + name, result->status, *statp); + nis_freeresult(result); + return FALSE; +} + + char * nisplus_default_domain() { @@ -1743,6 +1967,92 @@ text_map_lookup(map, name, av, statp) else return map_rewrite(map, vp, vsize, av); } + + +/* +** TEXT_GETCANONNAME -- look up canonical name in hosts file +*/ + +bool +text_getcanonname(name, hbsize, statp) + char *name; + int hbsize; + int *statp; +{ + int buflen; + char delim; + int key_idx; + char *cname; + bool found; + char *domain; + FILE *f; + char linebuf[MAXLINE]; + char cbuf[MAXNAME + 1]; + char buf[MAXNAME + 1]; + extern char *get_column(); + + shorten_hostname(name); + + /* we only accept single token search key */ + if (strchr(name, '.') != NULL) + { + *statp = EX_NOHOST; + return FALSE; + } + + found = FALSE; + + f = fopen(HostsFile, "r"); + if (f == NULL) + { +#ifdef MAP_EXIT_STAT + *statp = EX_UNAVAILABLE; +#endif + return FALSE; + } + delim = '\t'; + while (!found && fgets(linebuf, MAXLINE, f) != NULL) + { + char *p; + + if (linebuf[0] == '#') + continue; + if ((p = strchr(linebuf, '\n')) != NULL) + *p = '\0'; + cname = get_column(linebuf, 1, delim, cbuf); + if (cname != NULL && strcasecmp(name, cname) == 0) + { + found = TRUE; + break; + } + + key_idx = 2; + while ((p = get_column(linebuf, key_idx, delim, buf)) != NULL) + { + if (strcasecmp(name, p) == 0) + { + found = TRUE; + break; + } + key_idx++; + } + } + fclose(f); + if (!found) + { + *statp = EX_NOHOST; + return FALSE; + } + + if (hbsize >= strlen(cname)) + { + strcpy(name, cname); + *statp = EX_OK; + return TRUE; + } + *statp = EX_UNAVAILABLE; + return FALSE; +} /* ** STAB (Symbol Table) Modules */ @@ -2353,6 +2663,117 @@ seq_map_store(map, key, val) map->map_mname, key, val); } /* +** GETCANONNAME -- look up name using service switch +** +** Parameters: +** host -- the host name to look up. +** hbsize -- the size of the host buffer. +** trymx -- if set, try MX records. +** +** Returns: +** TRUE -- if the host was found. +** FALSE -- otherwise. +*/ + +bool +getcanonname(host, hbsize, trymx) + char *host; + int hbsize; + bool trymx; +{ + int nmaps; + int mapno; + bool found = FALSE; + auto int stat; + char *maptype[MAXMAPSTACK]; + short mapreturn[MAXMAPACTIONS]; + extern int h_errno; + + nmaps = switch_map_find("hosts", maptype, mapreturn); + for (mapno = 0; mapno < nmaps; mapno++) + { + int i; + + if (tTd(38, 20)) + printf("getcanonname(%s), trying %s\n", + host, maptype[mapno]); + if (strcmp("files", maptype[mapno]) == 0) + found = text_getcanonname(host, hbsize, &stat); +#ifdef NIS + else if (strcmp("nis", maptype[mapno]) == 0) + found = nis_getcanonname(host, hbsize, &stat); +#endif +#ifdef NISPLUS + else if (strcmp("nisplus", maptype[mapno]) == 0) + found = nisplus_getcanonname(host, hbsize, &stat); +#endif +#if NAMED_BIND + else if (strcmp("dns", maptype[mapno]) == 0) + found = dns_getcanonname(host, hbsize, trymx, &stat); +#endif + else + { + found = FALSE; + stat = EX_UNAVAILABLE; + } + if (found) + break; + + /* see if we should continue */ + if (stat == EX_TEMPFAIL) + i = MA_TRYAGAIN; + else if (stat == EX_NOHOST) + i = MA_NOTFOUND; + else + i = MA_UNAVAIL; + if (bitset(1 << mapno, mapreturn[i])) + break; + } + + if (found) + { + char *d; + + if (tTd(38, 20)) + printf("getcanonname(%s), found\n", host); + + /* + ** If returned name is still single token, compensate + ** by tagging on $m. This is because some sites set + ** up their DNS or NIS databases wrong. + */ + + if ((d = strchr(host, '.')) == NULL || d[1] == '\0') + { + d = macvalue('m', CurEnv); + if (d != NULL && + hbsize > (int) (strlen(host) + strlen(d) + 1)) + { + if (host[strlen(host) - 1] != '.') + strcat(host, "."); + strcat(host, d); + } + else + { + return FALSE; + } + } + return TRUE; + } + + if (tTd(38, 20)) + printf("getcanonname(%s), failed, stat=%d\n", host, stat); + +#if NAMED_BIND + if (stat == EX_NOHOST) + h_errno = HOST_NOT_FOUND; + else + h_errno = TRY_AGAIN; +#endif + + return FALSE; +} + /* ** NULL stubs */ diff --git a/usr/src/usr.sbin/sendmail/src/pathnames.h b/usr/src/usr.sbin/sendmail/src/pathnames.h index 2e0ac10fd2..52419ddf75 100644 --- a/usr/src/usr.sbin/sendmail/src/pathnames.h +++ b/usr/src/usr.sbin/sendmail/src/pathnames.h @@ -4,7 +4,7 @@ * * %sccs.include.redist.c% * - * @(#)pathnames.h 8.2 (Berkeley) %G% + * @(#)pathnames.h 8.3 (Berkeley) %G% */ #ifndef _PATH_SENDMAILCF @@ -18,3 +18,7 @@ # define _PATH_SENDMAILPID "/etc/sendmail.pid" # endif #endif + +#ifndef _PATH_HOSTS +# define _PATH_HOSTS "/etc/hosts" +#endif diff --git a/usr/src/usr.sbin/sendmail/src/readcf.c b/usr/src/usr.sbin/sendmail/src/readcf.c index 00cc6d7d10..7a25a51b56 100644 --- a/usr/src/usr.sbin/sendmail/src/readcf.c +++ b/usr/src/usr.sbin/sendmail/src/readcf.c @@ -7,7 +7,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)readcf.c 8.87 (Berkeley) %G%"; +static char sccsid[] = "@(#)readcf.c 8.88 (Berkeley) %G%"; #endif /* not lint */ # include "sendmail.h" @@ -1310,6 +1310,8 @@ struct optioninfo "RetryFactor", 'Z', FALSE, #define O_QUEUESORTORD 0x81 "QueueSortOrder", O_QUEUESORTORD, TRUE, +#define O_HOSTSFILE 0x82 + "HostsFile", O_HOSTSFILE, FALSE, #define O_MQA 0x83 "MinQueueAge", O_MQA, TRUE, #define O_MHSA 0x84 @@ -1908,6 +1910,10 @@ setoption(opt, val, sticky) } break; + case O_HOSTSFILE: /* pathname of /etc/hosts file */ + HostsFile = newstr(val); + break; + case O_MQA: /* minimum queue age between deliveries */ MinQueueAge = convtime(val, 'm'); break; diff --git a/usr/src/usr.sbin/sendmail/src/sendmail.h b/usr/src/usr.sbin/sendmail/src/sendmail.h index fd4487cc18..b63fcbc69e 100644 --- a/usr/src/usr.sbin/sendmail/src/sendmail.h +++ b/usr/src/usr.sbin/sendmail/src/sendmail.h @@ -5,7 +5,7 @@ * * %sccs.include.redist.c% * - * @(#)sendmail.h 8.131 (Berkeley) %G% + * @(#)sendmail.h 8.132 (Berkeley) %G% */ /* @@ -15,7 +15,7 @@ # ifdef _DEFINE # define EXTERN # ifndef lint -static char SmailSccsId[] = "@(#)sendmail.h 8.131 %G%"; +static char SmailSccsId[] = "@(#)sendmail.h 8.132 %G%"; # endif # else /* _DEFINE */ # define EXTERN extern @@ -983,6 +983,7 @@ EXTERN time_t MaxHostStatAge; /* max age of cached host status info */ EXTERN time_t MinQueueAge; /* min delivery interval */ EXTERN time_t DialDelay; /* delay between dial-on-demand tries */ EXTERN char *SafeFileEnv; /* chroot location for file delivery */ +EXTERN char *HostsFile; /* path to /etc/hosts file */ EXTERN char *ServiceSwitchFile; /* backup service switch */ EXTERN char *DefaultCharSet; /* default character set for MIME */ EXTERN int DeliveryNiceness; /* how nice to be during delivery */ diff --git a/usr/src/usr.sbin/sendmail/src/util.c b/usr/src/usr.sbin/sendmail/src/util.c index 56f0d06b34..5af40fa722 100644 --- a/usr/src/usr.sbin/sendmail/src/util.c +++ b/usr/src/usr.sbin/sendmail/src/util.c @@ -7,7 +7,7 @@ */ #ifndef lint -static char sccsid[] = "@(#)util.c 8.65 (Berkeley) %G%"; +static char sccsid[] = "@(#)util.c 8.66 (Berkeley) %G%"; #endif /* not lint */ # include "sendmail.h" @@ -1664,6 +1664,43 @@ shortenstring(s, m) return buf; } /* +** SHORTEN_HOSTNAME -- strip local domain information off of hostname. +** +** Parameters: +** host -- the host to shorten (stripped in place). +** +** Returns: +** none. +*/ + +void +shorten_hostname(host) + char host[]; +{ + register char *p; + char *mydom; + int i; + + /* strip off final dot */ + p = &host[strlen(host) - 1]; + if (*p == '.') + *p = '\0'; + + /* see if there is any domain at all -- if not, we are done */ + p = strchr(host, '.'); + if (p == NULL) + return; + + /* yes, we have a domain -- see if it looks like us */ + mydom = macvalue('m', CurEnv); + if (mydom == NULL) + mydom = ""; + i = strlen(++p); + if (strncasecmp(p, mydom, i) == 0 && + (mydom[i] == '.' || mydom[i] == '\0')) + *--p = '\0'; +} + /* ** GET_COLUMN -- look up a Column in a line buffer ** ** Parameters: -- 2.20.1