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