136285Sbrian/*- 236285Sbrian * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org> 336285Sbrian * All rights reserved. 46059Samurai * 536285Sbrian * Redistribution and use in source and binary forms, with or without 636285Sbrian * modification, are permitted provided that the following conditions 736285Sbrian * are met: 836285Sbrian * 1. Redistributions of source code must retain the above copyright 936285Sbrian * notice, this list of conditions and the following disclaimer. 1036285Sbrian * 2. Redistributions in binary form must reproduce the above copyright 1136285Sbrian * notice, this list of conditions and the following disclaimer in the 1236285Sbrian * documentation and/or other materials provided with the distribution. 136059Samurai * 1436285Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1536285Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1636285Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1736285Sbrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1836285Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1936285Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2036285Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2236285Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2336285Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2436285Sbrian * SUCH DAMAGE. 256059Samurai * 2650479Speter * $FreeBSD: releng/11.0/usr.sbin/ppp/chat.c 261900 2014-02-14 20:34:24Z brueffer $ 276059Samurai */ 2836285Sbrian 2943313Sbrian#include <sys/param.h> 3030715Sbrian#include <netinet/in.h> 3136285Sbrian#include <netinet/in_systm.h> 3236285Sbrian#include <netinet/ip.h> 3381634Sbrian#include <sys/socket.h> 3436285Sbrian#include <sys/un.h> 3530715Sbrian 3630715Sbrian#include <errno.h> 376059Samurai#include <fcntl.h> 3836285Sbrian#include <paths.h> 3930715Sbrian#include <stdio.h> 4030715Sbrian#include <stdlib.h> 4130715Sbrian#include <string.h> 426735Samurai#include <sys/wait.h> 4330715Sbrian#include <termios.h> 4430715Sbrian#include <unistd.h> 4530715Sbrian 4646686Sbrian#include "layer.h" 4730715Sbrian#include "mbuf.h" 4830715Sbrian#include "log.h" 4930715Sbrian#include "defs.h" 5030715Sbrian#include "timer.h" 5136285Sbrian#include "lqr.h" 5236285Sbrian#include "hdlc.h" 5336285Sbrian#include "throughput.h" 5436285Sbrian#include "fsm.h" 5536285Sbrian#include "lcp.h" 5636285Sbrian#include "ccp.h" 5736285Sbrian#include "link.h" 5836285Sbrian#include "async.h" 5936285Sbrian#include "descriptor.h" 6036285Sbrian#include "physical.h" 6125630Sbrian#include "chat.h" 6236285Sbrian#include "mp.h" 6336285Sbrian#include "auth.h" 6436285Sbrian#include "chap.h" 6536285Sbrian#include "slcompress.h" 6636285Sbrian#include "iplist.h" 6781634Sbrian#include "ncpaddr.h" 6836285Sbrian#include "ipcp.h" 6936285Sbrian#include "filter.h" 7038174Sbrian#include "cbcp.h" 7143919Sbrian#include "command.h" 7236285Sbrian#include "datalink.h" 7343313Sbrian#ifndef NORADIUS 7443313Sbrian#include "radius.h" 7543313Sbrian#endif 7681634Sbrian#include "ipv6cp.h" 7781634Sbrian#include "ncp.h" 7836285Sbrian#include "bundle.h" 7955253Sbrian#include "id.h" 806059Samurai 8136285Sbrian#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf)) 8230715Sbrian 8336285Sbrianstatic void ExecStr(struct physical *, char *, char *, int); 8436285Sbrianstatic char *ExpandString(struct chat *, const char *, char *, int, int); 8530715Sbrian 8636285Sbrianstatic void 8736285Sbrianchat_PauseTimer(void *v) 8836285Sbrian{ 8936285Sbrian struct chat *c = (struct chat *)v; 9036285Sbrian timer_Stop(&c->pause); 9136285Sbrian c->pause.load = 0; 9236285Sbrian} 936735Samurai 9436285Sbrianstatic void 9536285Sbrianchat_Pause(struct chat *c, u_long load) 9636285Sbrian{ 9736285Sbrian timer_Stop(&c->pause); 9836285Sbrian c->pause.load += load; 9936285Sbrian c->pause.func = chat_PauseTimer; 10036285Sbrian c->pause.name = "chat pause"; 10136285Sbrian c->pause.arg = c; 10236285Sbrian timer_Start(&c->pause); 10336285Sbrian} 1046059Samurai 10536285Sbrianstatic void 10636285Sbrianchat_TimeoutTimer(void *v) 10736285Sbrian{ 10836285Sbrian struct chat *c = (struct chat *)v; 10936285Sbrian timer_Stop(&c->timeout); 11036285Sbrian c->TimedOut = 1; 11136285Sbrian} 1126059Samurai 11336285Sbrianstatic void 11436285Sbrianchat_SetTimeout(struct chat *c) 11536285Sbrian{ 11636285Sbrian timer_Stop(&c->timeout); 11736285Sbrian if (c->TimeoutSec > 0) { 11836285Sbrian c->timeout.load = SECTICKS * c->TimeoutSec; 11936285Sbrian c->timeout.func = chat_TimeoutTimer; 12036285Sbrian c->timeout.name = "chat timeout"; 12136285Sbrian c->timeout.arg = c; 12236285Sbrian timer_Start(&c->timeout); 12336285Sbrian } 12436285Sbrian} 12536285Sbrian 1266059Samuraistatic char * 12736285Sbrianchat_NextChar(char *ptr, char ch) 12836285Sbrian{ 12936285Sbrian for (; *ptr; ptr++) 13036285Sbrian if (*ptr == ch) 13136285Sbrian return ptr; 13236285Sbrian else if (*ptr == '\\') 13336285Sbrian if (*++ptr == '\0') 13436285Sbrian return NULL; 13536285Sbrian 13636285Sbrian return NULL; 13736285Sbrian} 13836285Sbrian 13936285Sbrianstatic int 14058028Sbrianchat_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) 14136285Sbrian{ 14236285Sbrian struct chat *c = descriptor2chat(d); 14336285Sbrian int special, gotabort, gottimeout, needcr; 14436285Sbrian int TimedOut = c->TimedOut; 14537010Sbrian static char arg_term; /* An empty string */ 14636285Sbrian 14736285Sbrian if (c->pause.state == TIMER_RUNNING) 14836285Sbrian return 0; 14936285Sbrian 15036285Sbrian if (TimedOut) { 15136285Sbrian log_Printf(LogCHAT, "Expect timeout\n"); 15236285Sbrian if (c->nargptr == NULL) 15336285Sbrian c->state = CHAT_FAILED; 15436285Sbrian else { 15536285Sbrian /* c->state = CHAT_EXPECT; */ 15636285Sbrian c->argptr = &arg_term; 157261900Sbrueffer /* 158261900Sbrueffer We have to clear the input buffer, because it contains output 159261900Sbrueffer from the previous (timed out) command. 160261900Sbrueffer */ 161261900Sbrueffer c->bufstart = c->bufend; 16236285Sbrian } 16336285Sbrian c->TimedOut = 0; 16436285Sbrian } 16536285Sbrian 16636285Sbrian if (c->state != CHAT_EXPECT && c->state != CHAT_SEND) 16736285Sbrian return 0; 16836285Sbrian 16936285Sbrian gottimeout = gotabort = 0; 17036285Sbrian 17136285Sbrian if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) { 17236285Sbrian /* Go get the next string */ 17336285Sbrian if (c->arg < 0 || c->state == CHAT_SEND) 17436285Sbrian c->state = CHAT_EXPECT; 17536285Sbrian else 17636285Sbrian c->state = CHAT_SEND; 17736285Sbrian 17836285Sbrian special = 1; 17936285Sbrian while (special && (c->nargptr || c->arg < c->argc - 1)) { 18036285Sbrian if (c->arg < 0 || (!TimedOut && c->state == CHAT_SEND)) 18136285Sbrian c->nargptr = NULL; 18236285Sbrian 18336285Sbrian if (c->nargptr != NULL) { 18436285Sbrian /* We're doing expect-send-expect.... */ 18536285Sbrian c->argptr = c->nargptr; 18636285Sbrian /* Put the '-' back in case we ever want to rerun our script */ 18736285Sbrian c->nargptr[-1] = '-'; 18836285Sbrian c->nargptr = chat_NextChar(c->nargptr, '-'); 18936285Sbrian if (c->nargptr != NULL) 19036285Sbrian *c->nargptr++ = '\0'; 19136285Sbrian } else { 19236285Sbrian int minus; 19336285Sbrian 19448226Sbrian if ((c->argptr = c->argv[++c->arg]) == NULL) { 19548226Sbrian /* End of script - all ok */ 19648226Sbrian c->state = CHAT_DONE; 19748226Sbrian return 0; 19848226Sbrian } 19936285Sbrian 20036285Sbrian if (c->state == CHAT_EXPECT) { 20136285Sbrian /* Look for expect-send-expect sequence */ 20236285Sbrian c->nargptr = c->argptr; 20336285Sbrian minus = 0; 20436285Sbrian while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) { 20536285Sbrian c->nargptr++; 20636285Sbrian minus++; 20736285Sbrian } 20836285Sbrian 20936285Sbrian if (minus % 2) 21036285Sbrian log_Printf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of" 21136285Sbrian " '-' chars, all ignored\n", c->argptr); 21236285Sbrian else if (minus) { 21336285Sbrian c->nargptr = chat_NextChar(c->argptr, '-'); 21436285Sbrian *c->nargptr++ = '\0'; 21536285Sbrian } 21636285Sbrian } 21736285Sbrian } 21836285Sbrian 21936285Sbrian /* 22036285Sbrian * c->argptr now temporarily points into c->script (via c->argv) 22136285Sbrian * If it's an expect-send-expect sequence, we've just got the correct 22236285Sbrian * portion of that sequence. 22336285Sbrian */ 22436285Sbrian 22564465Sbrian needcr = c->state == CHAT_SEND && 22664465Sbrian (*c->argptr != '!' || c->argptr[1] == '!'); 22736285Sbrian 22836285Sbrian /* We leave room for a potential HDLC header in the target string */ 22936285Sbrian ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr); 23036285Sbrian 23147540Sbrian /* 23247540Sbrian * Now read our string. If it's not a special string, we unset 23347540Sbrian * ``special'' to break out of the loop. 23447540Sbrian */ 23536285Sbrian if (gotabort) { 23636285Sbrian if (c->abort.num < MAXABORTS) { 23736285Sbrian int len, i; 23836285Sbrian 23936285Sbrian len = strlen(c->exp+2); 24036285Sbrian for (i = 0; i < c->abort.num; i++) 24136285Sbrian if (len > c->abort.string[i].len) { 24236285Sbrian int last; 24336285Sbrian 24436285Sbrian for (last = c->abort.num; last > i; last--) { 24536285Sbrian c->abort.string[last].data = c->abort.string[last-1].data; 24636285Sbrian c->abort.string[last].len = c->abort.string[last-1].len; 24736285Sbrian } 24836285Sbrian break; 24936285Sbrian } 25036285Sbrian c->abort.string[i].len = len; 251136375Sbrian if ((c->abort.string[i].data = (char *)malloc(len+1)) != NULL) { 252136375Sbrian memcpy(c->abort.string[i].data, c->exp+2, len+1); 253136375Sbrian c->abort.num++; 254136375Sbrian } 25536285Sbrian } else 25636285Sbrian log_Printf(LogERROR, "chat_UpdateSet: too many abort strings\n"); 25736285Sbrian gotabort = 0; 25836285Sbrian } else if (gottimeout) { 25936285Sbrian c->TimeoutSec = atoi(c->exp + 2); 26036285Sbrian if (c->TimeoutSec <= 0) 26136285Sbrian c->TimeoutSec = 30; 26236285Sbrian gottimeout = 0; 26336285Sbrian } else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT")) 26436285Sbrian gotabort = 1; 26536285Sbrian else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT")) 26636285Sbrian gottimeout = 1; 26736285Sbrian else { 26864465Sbrian if (c->exp[2] == '!' && c->exp[3] != '!') 26964465Sbrian ExecStr(c->physical, c->exp + 3, c->exp + 3, sizeof c->exp - 3); 27036285Sbrian 27136285Sbrian if (c->exp[2] == '\0') { 27236285Sbrian /* Empty string, reparse (this may be better as a `goto start') */ 27336285Sbrian c->argptr = &arg_term; 27436285Sbrian return chat_UpdateSet(d, r, w, e, n); 27536285Sbrian } 27636285Sbrian 27736285Sbrian special = 0; 27836285Sbrian } 27936285Sbrian } 28036285Sbrian 28136285Sbrian if (special) { 28236285Sbrian if (gottimeout) 28336285Sbrian log_Printf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n"); 28436285Sbrian else if (gotabort) 28536285Sbrian log_Printf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n"); 28636285Sbrian 28736285Sbrian /* End of script - all ok */ 28836285Sbrian c->state = CHAT_DONE; 28936285Sbrian return 0; 29036285Sbrian } 29136285Sbrian 29236285Sbrian /* set c->argptr to point in the right place */ 29364465Sbrian c->argptr = c->exp + (c->exp[2] == '!' ? 3 : 2); 29436285Sbrian c->arglen = strlen(c->argptr); 29536285Sbrian 29636285Sbrian if (c->state == CHAT_EXPECT) { 29736285Sbrian /* We must check to see if the string's already been found ! */ 29836285Sbrian char *begin, *end; 29936285Sbrian 30036285Sbrian end = c->bufend - c->arglen + 1; 30136285Sbrian if (end < c->bufstart) 30236285Sbrian end = c->bufstart; 30336285Sbrian for (begin = c->bufstart; begin < end; begin++) 30436285Sbrian if (!strncmp(begin, c->argptr, c->arglen)) { 30536285Sbrian c->bufstart = begin + c->arglen; 30636285Sbrian c->argptr += c->arglen; 30736285Sbrian c->arglen = 0; 30836285Sbrian /* Continue - we've already read our expect string */ 30936285Sbrian return chat_UpdateSet(d, r, w, e, n); 31036285Sbrian } 31136285Sbrian 31236285Sbrian log_Printf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr); 31336285Sbrian chat_SetTimeout(c); 31436285Sbrian } 31536285Sbrian } 31636285Sbrian 31736285Sbrian /* 31836285Sbrian * We now have c->argptr pointing at what we want to expect/send and 31936285Sbrian * c->state saying what we want to do... we now know what to put in 32036285Sbrian * the fd_set :-) 32136285Sbrian */ 32236285Sbrian 32336285Sbrian if (c->state == CHAT_EXPECT) 32446686Sbrian return physical_doUpdateSet(&c->physical->desc, r, NULL, e, n, 1); 32536285Sbrian else 32646686Sbrian return physical_doUpdateSet(&c->physical->desc, NULL, w, e, n, 1); 32736285Sbrian} 32836285Sbrian 32936285Sbrianstatic int 33058028Sbrianchat_IsSet(struct fdescriptor *d, const fd_set *fdset) 33136285Sbrian{ 33236285Sbrian struct chat *c = descriptor2chat(d); 33348227Sbrian return c->argptr && physical_IsSet(&c->physical->desc, fdset); 33436285Sbrian} 33536285Sbrian 33636285Sbrianstatic void 33736285Sbrianchat_UpdateLog(struct chat *c, int in) 33836285Sbrian{ 33936285Sbrian if (log_IsKept(LogCHAT) || log_IsKept(LogCONNECT)) { 34036285Sbrian /* 34136285Sbrian * If a linefeed appears in the last `in' characters of `c's input 34236285Sbrian * buffer, output from there, all the way back to the last linefeed. 34336285Sbrian * This is called for every read of `in' bytes. 34436285Sbrian */ 34536285Sbrian char *ptr, *end, *stop, ch; 34636285Sbrian int level; 34736285Sbrian 34836285Sbrian level = log_IsKept(LogCHAT) ? LogCHAT : LogCONNECT; 34936285Sbrian if (in == -1) 35036285Sbrian end = ptr = c->bufend; 35136285Sbrian else { 35236285Sbrian ptr = c->bufend - in; 35336285Sbrian for (end = c->bufend - 1; end >= ptr; end--) 35436285Sbrian if (*end == '\n') 35536285Sbrian break; 35636285Sbrian } 35736285Sbrian 35836285Sbrian if (end >= ptr) { 35936285Sbrian for (ptr = c->bufend - (in == -1 ? 1 : in + 1); ptr >= c->bufstart; ptr--) 36036285Sbrian if (*ptr == '\n') 36136285Sbrian break; 36236285Sbrian ptr++; 36336285Sbrian stop = NULL; 36436285Sbrian while (stop < end) { 36536285Sbrian if ((stop = memchr(ptr, '\n', end - ptr)) == NULL) 36636285Sbrian stop = end; 36736285Sbrian ch = *stop; 36836285Sbrian *stop = '\0'; 36936285Sbrian if (level == LogCHAT || strstr(ptr, "CONNECT")) 37036285Sbrian log_Printf(level, "Received: %s\n", ptr); 37136285Sbrian *stop = ch; 37236285Sbrian ptr = stop + 1; 37336285Sbrian } 37436285Sbrian } 37536285Sbrian } 37636285Sbrian} 37736285Sbrian 37836285Sbrianstatic void 379134789Sbrianchat_Read(struct fdescriptor *d, struct bundle *bundle __unused, 380134789Sbrian const fd_set *fdset __unused) 38136285Sbrian{ 38236285Sbrian struct chat *c = descriptor2chat(d); 38336285Sbrian 38436285Sbrian if (c->state == CHAT_EXPECT) { 38536285Sbrian ssize_t in; 38636285Sbrian char *abegin, *ebegin, *begin, *aend, *eend, *end; 38736285Sbrian int n; 38836285Sbrian 38936285Sbrian /* 39036285Sbrian * XXX - should this read only 1 byte to guarantee that we don't 39136285Sbrian * swallow any ppp talk from the peer ? 39236285Sbrian */ 39336285Sbrian in = BUFLEFT(c); 394134789Sbrian if (in > (ssize_t)sizeof c->buf / 2) 39536285Sbrian in = sizeof c->buf / 2; 39636285Sbrian 39736285Sbrian in = physical_Read(c->physical, c->bufend, in); 39836285Sbrian if (in <= 0) 39936285Sbrian return; 40036285Sbrian 40136285Sbrian /* `begin' and `end' delimit where we're going to strncmp() from */ 40236285Sbrian ebegin = c->bufend - c->arglen + 1; 40336285Sbrian eend = ebegin + in; 40436285Sbrian if (ebegin < c->bufstart) 40536285Sbrian ebegin = c->bufstart; 40636285Sbrian 40736285Sbrian if (c->abort.num) { 40836285Sbrian abegin = c->bufend - c->abort.string[0].len + 1; 40936285Sbrian aend = c->bufend - c->abort.string[c->abort.num-1].len + in + 1; 41036285Sbrian if (abegin < c->bufstart) 41136285Sbrian abegin = c->bufstart; 41236285Sbrian } else { 41336285Sbrian abegin = ebegin; 41436285Sbrian aend = eend; 41536285Sbrian } 41636285Sbrian begin = abegin < ebegin ? abegin : ebegin; 41736285Sbrian end = aend < eend ? eend : aend; 41836285Sbrian 41936285Sbrian c->bufend += in; 42036285Sbrian 42136285Sbrian chat_UpdateLog(c, in); 42236285Sbrian 42336285Sbrian if (c->bufend > c->buf + sizeof c->buf / 2) { 42436285Sbrian /* Shuffle our receive buffer back a bit */ 42536285Sbrian int chop; 42636285Sbrian 42736285Sbrian for (chop = begin - c->buf; chop; chop--) 42836285Sbrian if (c->buf[chop] == '\n') 42936285Sbrian /* found some already-logged garbage to remove :-) */ 43036285Sbrian break; 43136285Sbrian 43236285Sbrian if (!chop) 43336285Sbrian chop = begin - c->buf; 43436285Sbrian 43536285Sbrian if (chop) { 43636285Sbrian char *from, *to; 43736285Sbrian 43836285Sbrian to = c->buf; 43936285Sbrian from = to + chop; 44036285Sbrian while (from < c->bufend) 44136285Sbrian *to++ = *from++; 44236285Sbrian c->bufstart -= chop; 44336285Sbrian c->bufend -= chop; 44436285Sbrian begin -= chop; 44536285Sbrian end -= chop; 44636285Sbrian abegin -= chop; 44736285Sbrian aend -= chop; 44836285Sbrian ebegin -= chop; 44936285Sbrian eend -= chop; 45036285Sbrian } 45136285Sbrian } 45236285Sbrian 45336285Sbrian for (; begin < end; begin++) 45436285Sbrian if (begin >= ebegin && begin < eend && 45536285Sbrian !strncmp(begin, c->argptr, c->arglen)) { 45636285Sbrian /* Got it ! */ 45740680Sbrian timer_Stop(&c->timeout); 45836285Sbrian if (memchr(begin + c->arglen - 1, '\n', 45998243Sbrian c->bufend - begin - c->arglen + 1) == NULL) { 46036285Sbrian /* force it into the log */ 46136285Sbrian end = c->bufend; 46236285Sbrian c->bufend = begin + c->arglen; 46336285Sbrian chat_UpdateLog(c, -1); 46436285Sbrian c->bufend = end; 46536285Sbrian } 46636285Sbrian c->bufstart = begin + c->arglen; 46736285Sbrian c->argptr += c->arglen; 46836285Sbrian c->arglen = 0; 46936285Sbrian break; 47036285Sbrian } else if (begin >= abegin && begin < aend) { 47136285Sbrian for (n = c->abort.num - 1; n >= 0; n--) { 47236285Sbrian if (begin + c->abort.string[n].len > c->bufend) 47336285Sbrian break; 47436285Sbrian if (!strncmp(begin, c->abort.string[n].data, 47536285Sbrian c->abort.string[n].len)) { 47636285Sbrian if (memchr(begin + c->abort.string[n].len - 1, '\n', 47798243Sbrian c->bufend - begin - c->abort.string[n].len + 1) == NULL) { 47836285Sbrian /* force it into the log */ 47936285Sbrian end = c->bufend; 48036285Sbrian c->bufend = begin + c->abort.string[n].len; 48136285Sbrian chat_UpdateLog(c, -1); 48236285Sbrian c->bufend = end; 48336285Sbrian } 48436285Sbrian c->bufstart = begin + c->abort.string[n].len; 48536285Sbrian c->state = CHAT_FAILED; 48636285Sbrian return; 48736285Sbrian } 48836285Sbrian } 48936285Sbrian } 49036285Sbrian } 49136285Sbrian} 49236285Sbrian 49337141Sbrianstatic int 494134789Sbrianchat_Write(struct fdescriptor *d, struct bundle *bundle __unused, 495134789Sbrian const fd_set *fdset __unused) 49636285Sbrian{ 49736285Sbrian struct chat *c = descriptor2chat(d); 49837141Sbrian int result = 0; 49936285Sbrian 50036285Sbrian if (c->state == CHAT_SEND) { 50136285Sbrian int wrote; 50236285Sbrian 50336285Sbrian if (strstr(c->argv[c->arg], "\\P")) /* Don't log the password */ 50436285Sbrian log_Printf(LogCHAT, "Send: %s\n", c->argv[c->arg]); 50536285Sbrian else { 50636285Sbrian int sz; 50736285Sbrian 50836285Sbrian sz = c->arglen - 1; 50936285Sbrian while (sz >= 0 && c->argptr[sz] == '\n') 51036285Sbrian sz--; 51136285Sbrian log_Printf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr); 51236285Sbrian } 51336285Sbrian 51436285Sbrian if (physical_IsSync(c->physical)) { 51547540Sbrian /* 51647540Sbrian * XXX: Fix me 51747540Sbrian * This data should be stuffed down through the link layers 51847540Sbrian */ 51936285Sbrian /* There's always room for the HDLC header */ 52036285Sbrian c->argptr -= 2; 52136285Sbrian c->arglen += 2; 52236285Sbrian memcpy(c->argptr, "\377\003", 2); /* Prepend HDLC header */ 52336285Sbrian } 52436285Sbrian 52536285Sbrian wrote = physical_Write(c->physical, c->argptr, c->arglen); 52693418Sbrian result = wrote > 0 ? 1 : 0; 52736285Sbrian if (wrote == -1) { 52893418Sbrian if (errno != EINTR) { 52993418Sbrian log_Printf(LogWARN, "chat_Write: %s\n", strerror(errno)); 53093418Sbrian result = -1; 53193418Sbrian } 53236285Sbrian if (physical_IsSync(c->physical)) { 53336285Sbrian c->argptr += 2; 53436285Sbrian c->arglen -= 2; 53536285Sbrian } 53636285Sbrian } else if (wrote < 2 && physical_IsSync(c->physical)) { 53736285Sbrian /* Oops - didn't even write our HDLC header ! */ 53836285Sbrian c->argptr += 2; 53936285Sbrian c->arglen -= 2; 54036285Sbrian } else { 54136285Sbrian c->argptr += wrote; 54236285Sbrian c->arglen -= wrote; 54336285Sbrian } 54436285Sbrian } 54537141Sbrian 54637141Sbrian return result; 54736285Sbrian} 54836285Sbrian 54936285Sbrianvoid 55054055Sbrianchat_Init(struct chat *c, struct physical *p) 55136285Sbrian{ 55236285Sbrian c->desc.type = CHAT_DESCRIPTOR; 55336285Sbrian c->desc.UpdateSet = chat_UpdateSet; 55436285Sbrian c->desc.IsSet = chat_IsSet; 55536285Sbrian c->desc.Read = chat_Read; 55636285Sbrian c->desc.Write = chat_Write; 55736285Sbrian c->physical = p; 55854055Sbrian *c->script = '\0'; 55954055Sbrian c->argc = 0; 56054055Sbrian c->arg = -1; 56154055Sbrian c->argptr = NULL; 56254055Sbrian c->nargptr = NULL; 56354055Sbrian c->bufstart = c->bufend = c->buf; 56436285Sbrian 56554055Sbrian memset(&c->pause, '\0', sizeof c->pause); 56654055Sbrian memset(&c->timeout, '\0', sizeof c->timeout); 56754055Sbrian} 56854055Sbrian 56954914Sbrianint 57054055Sbrianchat_Setup(struct chat *c, const char *data, const char *phone) 57154055Sbrian{ 57236285Sbrian c->state = CHAT_EXPECT; 57336285Sbrian 57436285Sbrian if (data == NULL) { 57536285Sbrian *c->script = '\0'; 57636285Sbrian c->argc = 0; 57736285Sbrian } else { 57836285Sbrian strncpy(c->script, data, sizeof c->script - 1); 57936285Sbrian c->script[sizeof c->script - 1] = '\0'; 58055145Sbrian c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv), PARSE_NOHASH); 58136285Sbrian } 58236285Sbrian 58336285Sbrian c->arg = -1; 58436285Sbrian c->argptr = NULL; 58536285Sbrian c->nargptr = NULL; 58636285Sbrian 58736285Sbrian c->TimeoutSec = 30; 58836285Sbrian c->TimedOut = 0; 58936285Sbrian c->phone = phone; 59036285Sbrian c->abort.num = 0; 59136285Sbrian 59254055Sbrian timer_Stop(&c->pause); 59354055Sbrian timer_Stop(&c->timeout); 59454914Sbrian 59554914Sbrian return c->argc >= 0; 59636285Sbrian} 59736285Sbrian 59836285Sbrianvoid 59954055Sbrianchat_Finish(struct chat *c) 60036285Sbrian{ 60136285Sbrian timer_Stop(&c->pause); 60236285Sbrian timer_Stop(&c->timeout); 60336285Sbrian while (c->abort.num) 60436285Sbrian free(c->abort.string[--c->abort.num].data); 60536285Sbrian c->abort.num = 0; 60636285Sbrian} 60736285Sbrian 60854055Sbrianvoid 60954055Sbrianchat_Destroy(struct chat *c) 61054055Sbrian{ 61154055Sbrian chat_Finish(c); 61254055Sbrian} 61354055Sbrian 6146059Samurai/* 61521488Simp * \c don't add a cr 61621488Simp * \d Sleep a little (delay 2 seconds 61721488Simp * \n Line feed character 61821488Simp * \P Auth Key password 61921488Simp * \p pause 0.25 sec 6206059Samurai * \r Carrige return character 6216059Samurai * \s Space character 62214418Sache * \T Telephone number(s) (defined via `set phone') 6236059Samurai * \t Tab character 62421488Simp * \U Auth User 6256059Samurai */ 62636285Sbrianstatic char * 62747540SbrianExpandString(struct chat *c, const char *str, char *result, int reslen, int cr) 6286059Samurai{ 62947540Sbrian int len; 6306059Samurai 63121488Simp result[--reslen] = '\0'; 63221488Simp while (*str && reslen > 0) { 6336059Samurai switch (*str) { 6346059Samurai case '\\': 6356059Samurai str++; 6366059Samurai switch (*str) { 6376059Samurai case 'c': 63847540Sbrian cr = 0; 6396059Samurai break; 6406059Samurai case 'd': /* Delay 2 seconds */ 64136285Sbrian chat_Pause(c, 2 * SECTICKS); 64228679Sbrian break; 6436059Samurai case 'p': 64436285Sbrian chat_Pause(c, SECTICKS / 4); 64547540Sbrian break; /* Delay 0.25 seconds */ 6466059Samurai case 'n': 64728679Sbrian *result++ = '\n'; 64828679Sbrian reslen--; 64928679Sbrian break; 6506059Samurai case 'r': 65128679Sbrian *result++ = '\r'; 65228679Sbrian reslen--; 65328679Sbrian break; 6546059Samurai case 's': 65528679Sbrian *result++ = ' '; 65628679Sbrian reslen--; 65728679Sbrian break; 6586059Samurai case 't': 65928679Sbrian *result++ = '\t'; 66028679Sbrian reslen--; 66128679Sbrian break; 6626059Samurai case 'P': 66336285Sbrian strncpy(result, c->physical->dl->bundle->cfg.auth.key, reslen); 66447540Sbrian len = strlen(result); 66547540Sbrian reslen -= len; 66647540Sbrian result += len; 6676059Samurai break; 6686059Samurai case 'T': 66938174Sbrian if (c->phone) { 67038174Sbrian strncpy(result, c->phone, reslen); 67147540Sbrian len = strlen(result); 67247540Sbrian reslen -= len; 67347540Sbrian result += len; 67438174Sbrian } 6756059Samurai break; 6766059Samurai case 'U': 67736285Sbrian strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen); 67847540Sbrian len = strlen(result); 67947540Sbrian reslen -= len; 68047540Sbrian result += len; 6816059Samurai break; 6826059Samurai default: 68321488Simp reslen--; 68428679Sbrian *result++ = *str; 68521488Simp break; 6866059Samurai } 68728679Sbrian if (*str) 68828679Sbrian str++; 6896059Samurai break; 6906059Samurai case '^': 6916059Samurai str++; 69221488Simp if (*str) { 6936059Samurai *result++ = *str++ & 0x1f; 69421488Simp reslen--; 69521488Simp } 6966059Samurai break; 6976059Samurai default: 6986059Samurai *result++ = *str++; 69921488Simp reslen--; 7006059Samurai break; 7016059Samurai } 7026059Samurai } 70321488Simp if (--reslen > 0) { 70447540Sbrian if (cr) 70521488Simp *result++ = '\r'; 70621488Simp } 70721488Simp if (--reslen > 0) 70821488Simp *result++ = '\0'; 70928679Sbrian return (result); 7106059Samurai} 7116059Samurai 71228679Sbrianstatic void 71336285SbrianExecStr(struct physical *physical, char *command, char *out, int olen) 71426858Sbrian{ 71532017Sbrian pid_t pid; 71632017Sbrian int fids[2]; 71743919Sbrian char *argv[MAXARGS], *vector[MAXARGS], *startout, *endout; 71849976Sbrian int stat, nb, argc, i; 71932017Sbrian 72036285Sbrian log_Printf(LogCHAT, "Exec: %s\n", command); 72155145Sbrian if ((argc = MakeArgs(command, vector, VECSIZE(vector), 72255145Sbrian PARSE_REDUCE|PARSE_NOHASH)) <= 0) { 72354914Sbrian if (argc < 0) 72454914Sbrian log_Printf(LogWARN, "Syntax error in exec command\n"); 72554914Sbrian *out = '\0'; 72654914Sbrian return; 72754914Sbrian } 72832017Sbrian 72932017Sbrian if (pipe(fids) < 0) { 73036285Sbrian log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n", 73132017Sbrian strerror(errno)); 73236285Sbrian *out = '\0'; 73336285Sbrian return; 73432017Sbrian } 73532017Sbrian if ((pid = fork()) == 0) { 73685991Sbrian command_Expand(argv, argc, (char const *const *)vector, 73785991Sbrian physical->dl->bundle, 0, getpid()); 73836452Sbrian close(fids[0]); 73936285Sbrian timer_TermService(); 74049976Sbrian if (fids[1] == STDIN_FILENO) 74149976Sbrian fids[1] = dup(fids[1]); 74246686Sbrian dup2(physical->fd, STDIN_FILENO); 74349976Sbrian dup2(fids[1], STDERR_FILENO); 74436285Sbrian dup2(STDIN_FILENO, STDOUT_FILENO); 74536285Sbrian close(3); 74649976Sbrian if (open(_PATH_TTY, O_RDWR) != 3) 74749976Sbrian open(_PATH_DEVNULL, O_RDWR); /* Leave it closed if it fails... */ 74849976Sbrian for (i = getdtablesize(); i > 3; i--) 74949976Sbrian fcntl(i, F_SETFD, 1); 75064802Sbrian#ifndef NOSUID 75155252Sbrian setuid(ID0realuid()); 75264802Sbrian#endif 75343919Sbrian execvp(argv[0], argv); 75443919Sbrian fprintf(stderr, "execvp: %s: %s\n", argv[0], strerror(errno)); 75549976Sbrian _exit(127); 75632017Sbrian } else { 75732017Sbrian char *name = strdup(vector[0]); 75832017Sbrian 75932017Sbrian close(fids[1]); 76032017Sbrian endout = out + olen - 1; 76132017Sbrian startout = out; 76232017Sbrian while (out < endout) { 76332017Sbrian nb = read(fids[0], out, 1); 76432017Sbrian if (nb <= 0) 76532017Sbrian break; 76632017Sbrian out++; 76732017Sbrian } 76832017Sbrian *out = '\0'; 76932017Sbrian close(fids[0]); 77032017Sbrian close(fids[1]); 77132017Sbrian waitpid(pid, &stat, WNOHANG); 77232017Sbrian if (WIFSIGNALED(stat)) { 77336285Sbrian log_Printf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat)); 77432017Sbrian free(name); 77536285Sbrian *out = '\0'; 77636285Sbrian return; 77732017Sbrian } else if (WIFEXITED(stat)) { 77832017Sbrian switch (WEXITSTATUS(stat)) { 77932017Sbrian case 0: 78032017Sbrian free(name); 78132017Sbrian break; 78232017Sbrian case 127: 78336285Sbrian log_Printf(LogWARN, "%s: %s\n", name, startout); 78432017Sbrian free(name); 78536285Sbrian *out = '\0'; 78636285Sbrian return; 78732017Sbrian break; 78832017Sbrian default: 78936285Sbrian log_Printf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat)); 79032017Sbrian free(name); 79136285Sbrian *out = '\0'; 79236285Sbrian return; 79332017Sbrian break; 79432017Sbrian } 79532017Sbrian } else { 79636285Sbrian log_Printf(LogWARN, "%s: Unexpected exit result\n", name); 79732017Sbrian free(name); 79836285Sbrian *out = '\0'; 79936285Sbrian return; 80032017Sbrian } 80132017Sbrian } 80232017Sbrian} 803