pppctl.c revision 31119
1#include <sys/types.h>
2
3#include <sys/socket.h>
4#include <netinet/in.h>
5#include <arpa/inet.h>
6#include <sys/un.h>
7#include <netdb.h>
8
9#include <sys/time.h>
10#include <histedit.h>
11#include <signal.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <time.h>
16#include <unistd.h>
17
18#define LINELEN 2048
19static char Buffer[LINELEN], Command[LINELEN];
20
21static int
22Usage()
23{
24    fprintf(stderr, "Usage: pppctl [-v] [ -t n ] [ -p passwd ] "
25            "Port|LocalSock [command[;command]...]\n");
26    fprintf(stderr, "              -v tells pppctl to output all"
27            " conversation\n");
28    fprintf(stderr, "              -t n specifies a timeout of n"
29            " seconds (default 2)\n");
30    fprintf(stderr, "              -p passwd specifies your password\n");
31    return 1;
32}
33
34static int TimedOut = 0;
35static void
36Timeout(int Sig)
37{
38    TimedOut = 1;
39}
40
41#define REC_PASSWD  (1)
42#define REC_SHOW    (2)
43#define REC_VERBOSE (4)
44
45static char *passwd;
46static char *prompt;
47
48static char *
49GetPrompt(EditLine *e)
50{
51    if (prompt == NULL)
52        prompt = "";
53    return prompt;
54}
55
56static int
57Receive(int fd, unsigned TimeoutVal, int display)
58{
59    int Result;
60    struct sigaction act, oact;
61    int len;
62    char *last;
63
64    TimedOut = 0;
65    if (TimeoutVal) {
66        act.sa_handler = Timeout;
67        sigemptyset(&act.sa_mask);
68        act.sa_flags = 0;
69        sigaction(SIGALRM, &act, &oact);
70        alarm(TimeoutVal);
71    }
72
73    prompt = Buffer;
74    len = 0;
75    while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
76        len += Result;
77        Buffer[len] = '\0';
78        if (TimedOut) {
79            if (display & REC_VERBOSE)
80                write(1,Buffer,len);
81            Result = -1;
82            break;
83        } else if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
84            prompt = strrchr(Buffer, '\n');
85            if (display & (REC_SHOW|REC_VERBOSE)) {
86                if (display & REC_VERBOSE)
87                    last = Buffer+len-1;
88                else
89                    last = prompt;
90                if (last) {
91                    last++;
92                    write(1, Buffer, last-Buffer);
93                }
94            }
95            prompt = prompt == NULL ? Buffer : prompt+1;
96            for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
97                ;
98            if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
99                 /* a password is required ! */
100                 if (display & REC_PASSWD) {
101                    if (TimeoutVal) {
102                        alarm(0);
103                        sigaction(SIGALRM, &oact, 0);
104                    }
105                    /* password time */
106                    if (!passwd)
107                        passwd = getpass("Password: ");
108                    sprintf(Buffer, "passwd %s\n", passwd);
109                    memset(passwd, '\0', strlen(passwd));
110                    if (display & REC_VERBOSE)
111                        write(1, Buffer, strlen(Buffer));
112                    write(fd, Buffer, strlen(Buffer));
113                    memset(Buffer, '\0', strlen(Buffer));
114                    return Receive(fd, TimeoutVal, display & ~REC_PASSWD);
115                }
116                Result = 1;
117            } else
118                Result = 0;
119            break;
120        }
121    }
122
123    if (TimedOut)
124        Result = -1;
125
126    if (TimeoutVal) {
127        alarm(0);
128        sigaction(SIGALRM, &oact, 0);
129    }
130    return Result;
131}
132
133static int data = -1;
134
135static void
136check_fd(int sig)
137{
138  if (data != -1) {
139    struct timeval t;
140    fd_set f;
141    static char buf[LINELEN];
142
143    FD_ZERO(&f);
144    FD_SET(data, &f);
145    t.tv_sec = t.tv_usec = 0;
146    if (select(data+1, &f, NULL, NULL, &t) > 0)
147      write(1, buf, read(data, buf, sizeof buf));
148  }
149}
150
151static const char *
152smartgets(EditLine *e, int *count, int fd)
153{
154  const char *result;
155  /* struct itimerval it; */
156
157  data = fd;
158  signal(SIGALRM, check_fd);
159  ualarm(500000, 500000);
160  result = el_gets(e, count);
161  ualarm(0,0);
162  signal(SIGALRM, SIG_DFL);
163  data = -1;
164
165  return result;
166}
167
168int
169main(int argc, char **argv)
170{
171    struct servent *s;
172    struct hostent *h;
173    struct sockaddr *sock;
174    struct sockaddr_in ifsin;
175    struct sockaddr_un ifsun;
176    int socksz, arg, fd, len, verbose;
177    unsigned TimeoutVal;
178    char *DoneWord = "x", *next, *start;
179    struct sigaction act, oact;
180
181    verbose = 0;
182    TimeoutVal = 2;
183
184    for (arg = 1; arg < argc; arg++)
185        if (*argv[arg] == '-') {
186            for (start = argv[arg] + 1; *start; start++)
187                switch (*start) {
188                    case 't':
189                        TimeoutVal = (unsigned)atoi
190                            (start[1] ? start + 1 : argv[++arg]);
191                        start = DoneWord;
192                        break;
193
194                    case 'v':
195                        verbose = REC_VERBOSE;
196                        break;
197
198                    case 'p':
199                        passwd = (start[1] ? start + 1 : argv[++arg]);
200                        start = DoneWord;
201                        break;
202
203                    default:
204                        return Usage();
205                }
206        }
207        else
208            break;
209
210
211    if (argc < arg + 1)
212        return Usage();
213
214    if (*argv[arg] == '/') {
215        sock = (struct sockaddr *)&ifsun;
216        socksz = sizeof ifsun;
217
218        ifsun.sun_len = strlen(argv[arg]);
219        if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
220            fprintf(stderr, "%s: Path too long\n", argv[arg]);
221            return 1;
222        }
223        ifsun.sun_family = AF_LOCAL;
224        strcpy(ifsun.sun_path, argv[arg]);
225
226        if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
227            fprintf(stderr, "Cannot create local domain socket\n");
228            return 2;
229        }
230    } else {
231        char *port, *host, *colon;
232        int hlen;
233
234        colon = strchr(argv[arg], ':');
235        if (colon) {
236            port = colon + 1;
237            *colon = '\0';
238            host = argv[arg];
239        } else {
240            port = argv[arg];
241            host = "127.0.0.1";
242        }
243        sock = (struct sockaddr *)&ifsin;
244        socksz = sizeof ifsin;
245        hlen = strlen(host);
246
247        if (strspn(host, "0123456789.") == hlen) {
248            if (!inet_aton(host, (struct in_addr *)&ifsin.sin_addr.s_addr)) {
249                fprintf(stderr, "Cannot translate %s\n", host);
250                return 1;
251            }
252        } else if ((h = gethostbyname(host)) == 0) {
253            fprintf(stderr, "Cannot resolve %s\n", host);
254            return 1;
255        }
256        else
257            ifsin.sin_addr.s_addr = *(u_long *)h->h_addr_list[0];
258
259        if (colon)
260            *colon = ':';
261
262        if (strspn(port, "0123456789") == strlen(port))
263            ifsin.sin_port = htons(atoi(port));
264        else if (s = getservbyname(port, "tcp"), !s) {
265            fprintf(stderr, "%s isn't a valid port or service!\n", port);
266            return Usage();
267        }
268        else
269            ifsin.sin_port = s->s_port;
270
271        ifsin.sin_len = sizeof(ifsin);
272        ifsin.sin_family = AF_INET;
273
274        if (fd = socket(AF_INET, SOCK_STREAM, 0), fd < 0) {
275            fprintf(stderr, "Cannot create internet socket\n");
276            return 2;
277        }
278    }
279
280    TimedOut = 0;
281    if (TimeoutVal) {
282        act.sa_handler = Timeout;
283        sigemptyset(&act.sa_mask);
284        act.sa_flags = 0;
285        sigaction(SIGALRM, &act, &oact);
286        alarm(TimeoutVal);
287    }
288
289    if (connect(fd, sock, socksz) < 0) {
290        if (TimeoutVal) {
291            alarm(0);
292            sigaction(SIGALRM, &oact, 0);
293        }
294        if (TimedOut)
295            fputs("Timeout: ", stderr);
296        fprintf(stderr, "Cannot connect to socket %s\n", argv[arg]);
297        close(fd);
298        return 3;
299    }
300
301    if (TimeoutVal) {
302        alarm(0);
303        sigaction(SIGALRM, &oact, 0);
304    }
305
306    len = 0;
307    Command[sizeof(Command)-1] = '\0';
308    for (arg++; arg < argc; arg++) {
309        if (len && len < sizeof(Command)-1)
310            strcpy(Command+len++, " ");
311        strncpy(Command+len, argv[arg], sizeof(Command)-len-1);
312        len += strlen(Command+len);
313    }
314
315    switch (Receive(fd, TimeoutVal, verbose | REC_PASSWD))
316    {
317        case 1:
318            fprintf(stderr, "Password incorrect\n");
319            break;
320
321        case 0:
322            if (len == 0) {
323                EditLine *edit;
324                History *hist;
325                const char *l, *env;
326                int size;
327
328                hist = history_init();
329                if ((env = getenv("EL_SIZE"))) {
330                    size = atoi(env);
331                    if (size < 0)
332                      size = 20;
333                } else
334                    size = 20;
335                history(hist, H_EVENT, size);
336
337                edit = el_init("pppctl", stdin, stdout);
338                el_source(edit, NULL);
339                el_set(edit, EL_PROMPT, GetPrompt);
340                if ((env = getenv("EL_EDITOR")))
341                    if (!strcmp(env, "vi"))
342                        el_set(edit, EL_EDITOR, "vi");
343                    else if (!strcmp(env, "emacs"))
344                        el_set(edit, EL_EDITOR, "emacs");
345                el_set(edit, EL_SIGNAL, 1);
346                el_set(edit, EL_HIST, history, (const char *)hist);
347                while ((l = smartgets(edit, &len, fd))) {
348                    if (len > 1)
349                        history(hist, H_ENTER, l);
350                    write(fd, l, len);
351                    if (!strcasecmp(l, "quit\n") ||
352                        !strcasecmp(l, "bye\n"))   /* ok, we're cheating */
353                        break;
354                    if (Receive(fd, TimeoutVal, REC_SHOW) != 0) {
355                        fprintf(stderr, "Connection closed\n");
356                        break;
357                    }
358                }
359                el_end(edit);
360                history_end(hist);
361            } else {
362                start = Command;
363                do {
364                    next = strchr(start, ';');
365                    while (*start == ' ' || *start == '\t')
366                        start++;
367                    if (next)
368                        *next = '\0';
369                    strcpy(Buffer, start);
370                    Buffer[sizeof(Buffer)-2] = '\0';
371                    strcat(Buffer, "\n");
372                    if (verbose)
373                        write(1, Buffer, strlen(Buffer));
374                    write(fd, Buffer, strlen(Buffer));
375                    if (Receive(fd, TimeoutVal, verbose | REC_SHOW) != 0) {
376                        fprintf(stderr, "No reply from ppp\n");
377                        break;
378                    }
379                    if (next)
380                        start = ++next;
381                } while (next && *next);
382                if (verbose)
383                    puts("");
384            }
385            break;
386
387        default:
388            fprintf(stderr, "ppp is not responding\n");
389            break;
390    }
391
392    close(fd);
393
394    return 0;
395}
396