1 /*-
2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: pppctl.c,v 1.18 2005/07/11 22:32:28 brad Exp $
27 */
28
29 #include <sys/types.h>
30
31 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 #include <sys/un.h>
35 #include <netdb.h>
36
37 #include <sys/time.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <histedit.h>
41 #ifdef __FreeBSD__
42 #include <libutil.h>
43 #endif
44 #include <setjmp.h>
45 #include <signal.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <time.h>
50 #include <poll.h>
51 #include <unistd.h>
52
53 #define LINELEN 2048
54 static char Buffer[LINELEN], Command[LINELEN];
55
56 static int
usage()57 usage()
58 {
59 fprintf(stderr, "usage: pppctl [-v] [-p passwd] [-t n] "
60 "Port|LocalSock [command[;command]...]\n");
61 fprintf(stderr, " -p passwd specifies your password\n");
62 fprintf(stderr, " -t n specifies a timeout of n"
63 " seconds when connecting (default 2)\n");
64 fprintf(stderr, " -v tells pppctl to output all"
65 " conversation\n");
66 exit(1);
67 }
68
69 static int TimedOut = 0;
70 static void
Timeout(int Sig)71 Timeout(int Sig)
72 {
73 TimedOut = 1;
74 }
75
76 #define REC_PASSWD (1)
77 #define REC_SHOW (2)
78 #define REC_VERBOSE (4)
79
80 static char *passwd;
81 static char *prompt;
82
83 static char *
GetPrompt(EditLine * e)84 GetPrompt(EditLine *e)
85 {
86 if (prompt == NULL)
87 prompt = "";
88 return prompt;
89 }
90
91 static int
Receive(int fd,int display)92 Receive(int fd, int display)
93 {
94 int Result;
95 int len;
96 char *last;
97
98 prompt = Buffer;
99 len = 0;
100 while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
101 if (Result == 0 && errno != EINTR) {
102 Result = -1;
103 break;
104 }
105 len += Result;
106 Buffer[len] = '\0';
107 if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
108 prompt = strrchr(Buffer, '\n');
109 if (display & (REC_SHOW|REC_VERBOSE)) {
110 if (display & REC_VERBOSE)
111 last = Buffer+len-1;
112 else
113 last = prompt;
114 if (last) {
115 last++;
116 write(1, Buffer, last-Buffer);
117 }
118 }
119 prompt = prompt == NULL ? Buffer : prompt+1;
120 for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
121 ;
122 if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
123 /* a password is required! */
124 if (display & REC_PASSWD) {
125 /* password time */
126 if (!passwd)
127 if ((passwd = getpass("Password: ")) == NULL)
128 err(1, "getpass");
129 snprintf(Buffer, sizeof Buffer, "passwd %s\n", passwd);
130 memset(passwd, '\0', strlen(passwd));
131 if (display & REC_VERBOSE)
132 write(1, Buffer, strlen(Buffer));
133 write(fd, Buffer, strlen(Buffer));
134 memset(Buffer, '\0', strlen(Buffer));
135 return Receive(fd, display & ~REC_PASSWD);
136 }
137 Result = 1;
138 } else
139 Result = 0;
140 break;
141 }
142 if (len == sizeof Buffer - 1) {
143 int flush;
144 if ((last = strrchr(Buffer, '\n')) == NULL)
145 /* Yeuch - this is one mother of a line ! */
146 flush = sizeof Buffer / 2;
147 else
148 flush = last - Buffer + 1;
149 write(1, Buffer, flush);
150 strlcpy(Buffer, Buffer + flush, sizeof Buffer);
151 len -= flush;
152 }
153 }
154
155 return Result;
156 }
157
158 static int data = -1;
159 static jmp_buf pppdead;
160
161 static void
check_fd(int sig)162 check_fd(int sig)
163 {
164 if (data != -1) {
165 struct pollfd pfd[1];
166 static char buf[LINELEN];
167 int len;
168
169 pfd[0].fd = data;
170 pfd[0].events = POLLIN;
171 if (poll(pfd, 1, 0) > 0) {
172 len = read(data, buf, sizeof buf);
173 if (len > 0)
174 write(1, buf, len);
175 else
176 longjmp(pppdead, -1);
177 }
178 }
179 }
180
181 static const char *
smartgets(EditLine * e,int * count,int fd)182 smartgets(EditLine *e, int *count, int fd)
183 {
184 const char *result;
185
186 data = fd;
187 signal(SIGALRM, check_fd);
188 ualarm(500000, 500000);
189 result = setjmp(pppdead) ? NULL : el_gets(e, count);
190 ualarm(0,0);
191 signal(SIGALRM, SIG_DFL);
192 data = -1;
193
194 return result;
195 }
196
197 static const char *
sockaddr_ntop(const struct sockaddr * sa)198 sockaddr_ntop(const struct sockaddr *sa)
199 {
200 const void *addr;
201 static char addrbuf[INET6_ADDRSTRLEN];
202
203 switch (sa->sa_family) {
204 case AF_INET:
205 addr = &((const struct sockaddr_in *)sa)->sin_addr;
206 break;
207 case AF_UNIX:
208 addr = &((const struct sockaddr_un *)sa)->sun_path;
209 break;
210 case AF_INET6:
211 addr = &((const struct sockaddr_in6 *)sa)->sin6_addr;
212 break;
213 default:
214 return NULL;
215 }
216 inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
217 return addrbuf;
218 }
219
220 /*
221 * Connect to ppp using either a local domain socket or a tcp socket.
222 */
223 int
main(int argc,char ** argv)224 main(int argc, char **argv)
225 {
226 struct sockaddr_un ifsun;
227 int n, arg, fd = -1, len, verbose, save_errno, hide1, hide1off, hide2;
228 unsigned TimeoutVal;
229 char *DoneWord = "x", *next, *start;
230 struct sigaction act, oact;
231
232 verbose = 0;
233 TimeoutVal = 2;
234 hide1 = hide1off = hide2 = 0;
235
236 for (arg = 1; arg < argc; arg++)
237 if (*argv[arg] == '-') {
238 for (start = argv[arg] + 1; *start; start++)
239 switch (*start) {
240 case 't':
241 TimeoutVal = (unsigned)atoi
242 (start[1] ? start + 1 : argv[++arg]);
243 start = DoneWord;
244 break;
245
246 case 'v':
247 verbose = REC_VERBOSE;
248 break;
249
250 case 'p':
251 if (start[1]) {
252 hide1 = arg;
253 hide1off = start - argv[arg];
254 passwd = start + 1;
255 } else {
256 hide1 = arg;
257 hide1off = start - argv[arg];
258 passwd = argv[++arg];
259 hide2 = arg;
260 }
261 start = DoneWord;
262 break;
263
264 default:
265 usage();
266 }
267 }
268 else
269 break;
270
271
272 if (argc < arg + 1)
273 usage();
274
275 if (hide1) {
276 char title[1024];
277 int pos, harg;
278
279 for (harg = pos = 0; harg < argc; harg++)
280 if (harg == 0 || harg != hide2) {
281 if (harg == 0 || harg != hide1)
282 n = snprintf(title + pos, sizeof title - pos, "%s%s",
283 harg ? " " : "", argv[harg]);
284 else if (hide1off > 1)
285 n = snprintf(title + pos, sizeof title - pos, " %.*s",
286 hide1off, argv[harg]);
287 else
288 n = 0;
289 if (n < 0 || n >= sizeof title - pos)
290 break;
291 pos += n;
292 }
293 #ifdef __FreeBSD__
294 setproctitle("-%s", title);
295 #else
296 setproctitle("%s", title);
297 #endif
298 }
299
300 if (*argv[arg] == '/') {
301 memset(&ifsun, '\0', sizeof ifsun);
302 ifsun.sun_len = strlen(argv[arg]);
303 if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
304 warnx("%s: path too long", argv[arg]);
305 return 1;
306 }
307 ifsun.sun_family = AF_LOCAL;
308 strlcpy(ifsun.sun_path, argv[arg], sizeof ifsun.sun_path);
309
310 if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
311 warnx("cannot create local domain socket");
312 return 2;
313 }
314 if (connect(fd, (struct sockaddr *)&ifsun, sizeof(ifsun)) < 0) {
315 if (errno)
316 warn("cannot connect to socket %s", argv[arg]);
317 else
318 warnx("cannot connect to socket %s", argv[arg]);
319 close(fd);
320 return 3;
321 }
322 } else {
323 char *addr, *p, *port;
324 const char *caddr;
325 struct addrinfo hints, *res, *pai;
326 int gai;
327 char local[] = "localhost";
328
329 addr = argv[arg];
330 if (addr[strspn(addr, "0123456789")] == '\0') {
331 /* port on local machine */
332 port = addr;
333 addr = local;
334 } else if (*addr == '[') {
335 /* [addr]:port */
336 if ((p = strchr(addr, ']')) == NULL) {
337 warnx("%s: mismatched '['", addr);
338 return 1;
339 }
340 addr++;
341 *p++ = '\0';
342 if (*p != ':') {
343 warnx("%s: missing port", addr);
344 return 1;
345 }
346 port = ++p;
347 } else {
348 /* addr:port */
349 p = addr + strcspn(addr, ":");
350 if (*p != ':') {
351 warnx("%s: missing port", addr);
352 return 1;
353 }
354 *p++ = '\0';
355 port = p;
356 }
357 memset(&hints, 0, sizeof(hints));
358 hints.ai_socktype = SOCK_STREAM;
359 gai = getaddrinfo(addr, port, &hints, &res);
360 if (gai != 0) {
361 warnx("%s: %s", addr, gai_strerror(gai));
362 return 1;
363 }
364 for (pai = res; pai != NULL; pai = pai->ai_next) {
365 if (fd = socket(pai->ai_family, pai->ai_socktype,
366 pai->ai_protocol), fd < 0) {
367 warnx("cannot create socket");
368 continue;
369 }
370 TimedOut = 0;
371 if (TimeoutVal) {
372 act.sa_handler = Timeout;
373 sigemptyset(&act.sa_mask);
374 act.sa_flags = 0;
375 sigaction(SIGALRM, &act, &oact);
376 alarm(TimeoutVal);
377 }
378 if (connect(fd, pai->ai_addr, pai->ai_addrlen) == 0)
379 break;
380 if (TimeoutVal) {
381 save_errno = errno;
382 alarm(0);
383 sigaction(SIGALRM, &oact, 0);
384 errno = save_errno;
385 }
386 caddr = sockaddr_ntop(pai->ai_addr);
387 if (caddr == NULL)
388 caddr = argv[arg];
389 if (TimedOut)
390 warnx("timeout: cannot connect to %s", caddr);
391 else {
392 if (errno)
393 warn("cannot connect to %s", caddr);
394 else
395 warnx("cannot connect to %s", caddr);
396 }
397 close(fd);
398 }
399 freeaddrinfo(res);
400 if (pai == NULL)
401 return 1;
402 if (TimeoutVal) {
403 alarm(0);
404 sigaction(SIGALRM, &oact, 0);
405 }
406 }
407
408 len = 0;
409 Command[sizeof(Command)-1] = '\0';
410 for (arg++; arg < argc; arg++) {
411 if (len && len < sizeof(Command)-1) {
412 strlcpy(Command+len, " ", sizeof Command - len);
413 len++;
414 }
415 strncpy(Command+len, argv[arg], sizeof(Command)-len-1);
416 len += strlen(Command+len);
417 }
418
419 switch (Receive(fd, verbose | REC_PASSWD))
420 {
421 case 1:
422 fprintf(stderr, "Password incorrect\n");
423 break;
424
425 case 0:
426 if (len == 0) {
427 EditLine *edit;
428 History *hist;
429 const char *l, *env;
430 int size;
431 HistEvent hev = { 0, "" };
432
433 hist = history_init();
434 if ((env = getenv("EL_SIZE"))) {
435 size = atoi(env);
436 if (size < 0)
437 size = 20;
438 } else
439 size = 20;
440 history(hist, &hev, H_SETSIZE, size);
441 edit = el_init("pppctl", stdin, stdout, stderr);
442 el_source(edit, NULL);
443 el_set(edit, EL_PROMPT, GetPrompt);
444 if ((env = getenv("EL_EDITOR"))) {
445 if (!strcmp(env, "vi"))
446 el_set(edit, EL_EDITOR, "vi");
447 else if (!strcmp(env, "emacs"))
448 el_set(edit, EL_EDITOR, "emacs");
449 }
450 el_set(edit, EL_SIGNAL, 1);
451 el_set(edit, EL_HIST, history, (const char *)hist);
452 while ((l = smartgets(edit, &len, fd))) {
453 if (len > 1)
454 history(hist, &hev, H_ENTER, l);
455 write(fd, l, len);
456 if (Receive(fd, REC_SHOW) != 0)
457 break;
458 }
459 fprintf(stderr, "Connection closed\n");
460 el_end(edit);
461 history_end(hist);
462 } else {
463 start = Command;
464 do {
465 next = strchr(start, ';');
466 while (*start == ' ' || *start == '\t')
467 start++;
468 if (next)
469 *next = '\0';
470 strlcpy(Buffer, start, sizeof Buffer);
471 Buffer[sizeof(Buffer)-2] = '\0';
472 strlcat(Buffer, "\n", sizeof Buffer);
473 if (verbose)
474 write(1, Buffer, strlen(Buffer));
475 write(fd, Buffer, strlen(Buffer));
476 if (Receive(fd, verbose | REC_SHOW) != 0) {
477 fprintf(stderr, "Connection closed\n");
478 break;
479 }
480 if (next)
481 start = ++next;
482 } while (next && *next);
483 if (verbose)
484 puts("");
485 }
486 break;
487
488 default:
489 warnx("ppp is not responding");
490 break;
491 }
492
493 close(fd);
494
495 return 0;
496 }
497