pppctl.c revision 64703
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 * $FreeBSD: head/usr.sbin/pppctl/pppctl.c 64703 2000-08-16 12:01:17Z brian $ 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 <unistd.h> 51 52#define LINELEN 2048 53static char Buffer[LINELEN], Command[LINELEN]; 54 55static int 56usage() 57{ 58 fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] " 59 "Port|LocalSock [command[;command]...]\n"); 60 fprintf(stderr, " -v tells pppctl to output all" 61 " conversation\n"); 62 fprintf(stderr, " -t n specifies a timeout of n" 63 " seconds when connecting (default 2)\n"); 64 fprintf(stderr, " -p passwd specifies your password\n"); 65 exit(1); 66} 67 68static int TimedOut = 0; 69static void 70Timeout(int Sig) 71{ 72 TimedOut = 1; 73} 74 75#define REC_PASSWD (1) 76#define REC_SHOW (2) 77#define REC_VERBOSE (4) 78 79static char *passwd; 80static char *prompt; 81 82static char * 83GetPrompt(EditLine *e) 84{ 85 if (prompt == NULL) 86 prompt = ""; 87 return prompt; 88} 89 90static int 91Receive(int fd, int display) 92{ 93 int Result; 94 int len; 95 char *last; 96 97 prompt = Buffer; 98 len = 0; 99 while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) { 100 if (Result == 0 && errno != EINTR) { 101 Result = -1; 102 break; 103 } 104 len += Result; 105 Buffer[len] = '\0'; 106 if (len > 2 && !strcmp(Buffer+len-2, "> ")) { 107 prompt = strrchr(Buffer, '\n'); 108 if (display & (REC_SHOW|REC_VERBOSE)) { 109 if (display & REC_VERBOSE) 110 last = Buffer+len-1; 111 else 112 last = prompt; 113 if (last) { 114 last++; 115 write(1, Buffer, last-Buffer); 116 } 117 } 118 prompt = prompt == NULL ? Buffer : prompt+1; 119 for (last = Buffer+len-2; last > Buffer && *last != ' '; last--) 120 ; 121 if (last > Buffer+3 && !strncmp(last-3, " on", 3)) { 122 /* a password is required ! */ 123 if (display & REC_PASSWD) { 124 /* password time */ 125 if (!passwd) 126 passwd = getpass("Password: "); 127 sprintf(Buffer, "passwd %s\n", passwd); 128 memset(passwd, '\0', strlen(passwd)); 129 if (display & REC_VERBOSE) 130 write(1, Buffer, strlen(Buffer)); 131 write(fd, Buffer, strlen(Buffer)); 132 memset(Buffer, '\0', strlen(Buffer)); 133 return Receive(fd, display & ~REC_PASSWD); 134 } 135 Result = 1; 136 } else 137 Result = 0; 138 break; 139 } 140 if (len == sizeof Buffer - 1) { 141 int flush; 142 if ((last = strrchr(Buffer, '\n')) == NULL) 143 /* Yeuch - this is one mother of a line ! */ 144 flush = sizeof Buffer / 2; 145 else 146 flush = last - Buffer + 1; 147 write(1, Buffer, flush); 148 strcpy(Buffer, Buffer + flush); 149 len -= flush; 150 } 151 } 152 153 return Result; 154} 155 156static int data = -1; 157static jmp_buf pppdead; 158 159static void 160check_fd(int sig) 161{ 162 if (data != -1) { 163 struct timeval t; 164 fd_set f; 165 static char buf[LINELEN]; 166 int len; 167 168 FD_ZERO(&f); 169 FD_SET(data, &f); 170 t.tv_sec = t.tv_usec = 0; 171 if (select(data+1, &f, NULL, NULL, &t) > 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 181static const char * 182smartgets(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 197int 198main(int argc, char **argv) 199{ 200 struct servent *s; 201 struct hostent *h; 202 struct sockaddr *sock; 203 struct sockaddr_in ifsin; 204 struct sockaddr_un ifsun; 205 int socksz, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2; 206 unsigned TimeoutVal; 207 char *DoneWord = "x", *next, *start; 208 struct sigaction act, oact; 209 210 verbose = 0; 211 TimeoutVal = 2; 212 hide1 = hide1off = hide2 = 0; 213 214 for (arg = 1; arg < argc; arg++) 215 if (*argv[arg] == '-') { 216 for (start = argv[arg] + 1; *start; start++) 217 switch (*start) { 218 case 't': 219 TimeoutVal = (unsigned)atoi 220 (start[1] ? start + 1 : argv[++arg]); 221 start = DoneWord; 222 break; 223 224 case 'v': 225 verbose = REC_VERBOSE; 226 break; 227 228 case 'p': 229 if (start[1]) { 230 hide1 = arg; 231 hide1off = start - argv[arg]; 232 passwd = start + 1; 233 } else { 234 hide1 = arg; 235 hide1off = start - argv[arg]; 236 passwd = argv[++arg]; 237 hide2 = arg; 238 } 239 start = DoneWord; 240 break; 241 242 default: 243 usage(); 244 } 245 } 246 else 247 break; 248 249 250 if (argc < arg + 1) 251 usage(); 252 253 if (hide1) { 254 char title[1024]; 255 int pos, harg; 256 257 for (harg = pos = 0; harg < argc; harg++) 258 if (harg == 0 || harg != hide2) { 259 if (harg == 0 || harg != hide1) 260 pos += snprintf(title + pos, sizeof title - pos, "%s%s", 261 harg ? " " : "", argv[harg]); 262 else if (hide1off > 1) 263 pos += snprintf(title + pos, sizeof title - pos, " %.*s", 264 hide1off, argv[harg]); 265 } 266#ifdef __FreeBSD__ 267 setproctitle("-%s", title); 268#else 269 setproctitle("%s", title); 270#endif 271 } 272 273 if (*argv[arg] == '/') { 274 sock = (struct sockaddr *)&ifsun; 275 socksz = sizeof ifsun; 276 277 memset(&ifsun, '\0', sizeof ifsun); 278 ifsun.sun_len = strlen(argv[arg]); 279 if (ifsun.sun_len > sizeof ifsun.sun_path - 1) { 280 warnx("%s: path too long", argv[arg]); 281 return 1; 282 } 283 ifsun.sun_family = AF_LOCAL; 284 strcpy(ifsun.sun_path, argv[arg]); 285 286 if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) { 287 warnx("cannot create local domain socket"); 288 return 2; 289 } 290 } else { 291 char *port, *host, *colon; 292 int hlen; 293 294 colon = strchr(argv[arg], ':'); 295 if (colon) { 296 port = colon + 1; 297 *colon = '\0'; 298 host = argv[arg]; 299 } else { 300 port = argv[arg]; 301 host = "127.0.0.1"; 302 } 303 sock = (struct sockaddr *)&ifsin; 304 socksz = sizeof ifsin; 305 hlen = strlen(host); 306 307 memset(&ifsin, '\0', sizeof ifsin); 308 if (strspn(host, "0123456789.") == hlen) { 309 if (!inet_aton(host, &ifsin.sin_addr)) { 310 warnx("cannot translate %s", host); 311 return 1; 312 } 313 } else if ((h = gethostbyname(host)) == 0) { 314 warnx("cannot resolve %s", host); 315 return 1; 316 } 317 else 318 ifsin.sin_addr.s_addr = *(u_long *)h->h_addr_list[0]; 319 320 if (colon) 321 *colon = ':'; 322 323 if (strspn(port, "0123456789") == strlen(port)) 324 ifsin.sin_port = htons(atoi(port)); 325 else if (s = getservbyname(port, "tcp"), !s) { 326 warnx("%s isn't a valid port or service!", port); 327 usage(); 328 } 329 else 330 ifsin.sin_port = s->s_port; 331 332 ifsin.sin_len = sizeof(ifsin); 333 ifsin.sin_family = AF_INET; 334 335 if (fd = socket(AF_INET, SOCK_STREAM, 0), fd < 0) { 336 warnx("cannot create internet socket"); 337 return 2; 338 } 339 } 340 341 TimedOut = 0; 342 if (TimeoutVal) { 343 act.sa_handler = Timeout; 344 sigemptyset(&act.sa_mask); 345 act.sa_flags = 0; 346 sigaction(SIGALRM, &act, &oact); 347 alarm(TimeoutVal); 348 } 349 350 if (connect(fd, sock, socksz) < 0) { 351 if (TimeoutVal) { 352 save_errno = errno; 353 alarm(0); 354 sigaction(SIGALRM, &oact, 0); 355 errno = save_errno; 356 } 357 if (TimedOut) 358 warnx("timeout: cannot connect to socket %s", argv[arg]); 359 else { 360 if (errno) 361 warn("cannot connect to socket %s", argv[arg]); 362 else 363 warnx("cannot connect to socket %s", argv[arg]); 364 } 365 close(fd); 366 return 3; 367 } 368 369 if (TimeoutVal) { 370 alarm(0); 371 sigaction(SIGALRM, &oact, 0); 372 } 373 374 len = 0; 375 Command[sizeof(Command)-1] = '\0'; 376 for (arg++; arg < argc; arg++) { 377 if (len && len < sizeof(Command)-1) 378 strcpy(Command+len++, " "); 379 strncpy(Command+len, argv[arg], sizeof(Command)-len-1); 380 len += strlen(Command+len); 381 } 382 383 switch (Receive(fd, verbose | REC_PASSWD)) 384 { 385 case 1: 386 fprintf(stderr, "Password incorrect\n"); 387 break; 388 389 case 0: 390 if (len == 0) { 391 EditLine *edit; 392 History *hist; 393 const char *l, *env; 394 int size; 395 396 hist = history_init(); 397 if ((env = getenv("EL_SIZE"))) { 398 size = atoi(env); 399 if (size < 0) 400 size = 20; 401 } else 402 size = 20; 403#ifdef __NetBSD__ 404 history(hist, NULL, H_SETSIZE, size); 405 edit = el_init("pppctl", stdin, stdout, stderr); 406#else 407 history(hist, H_EVENT, size); 408 edit = el_init("pppctl", stdin, stdout); 409#endif 410 el_source(edit, NULL); 411 el_set(edit, EL_PROMPT, GetPrompt); 412 if ((env = getenv("EL_EDITOR"))) { 413 if (!strcmp(env, "vi")) 414 el_set(edit, EL_EDITOR, "vi"); 415 else if (!strcmp(env, "emacs")) 416 el_set(edit, EL_EDITOR, "emacs"); 417 } 418 el_set(edit, EL_SIGNAL, 1); 419 el_set(edit, EL_HIST, history, (const char *)hist); 420 while ((l = smartgets(edit, &len, fd))) { 421 if (len > 1) 422#ifdef __NetBSD__ 423 history(hist, NULL, H_ENTER, l); 424#else 425 history(hist, H_ENTER, l); 426#endif 427 write(fd, l, len); 428 if (Receive(fd, REC_SHOW) != 0) 429 break; 430 } 431 fprintf(stderr, "Connection closed\n"); 432 el_end(edit); 433 history_end(hist); 434 } else { 435 start = Command; 436 do { 437 next = strchr(start, ';'); 438 while (*start == ' ' || *start == '\t') 439 start++; 440 if (next) 441 *next = '\0'; 442 strcpy(Buffer, start); 443 Buffer[sizeof(Buffer)-2] = '\0'; 444 strcat(Buffer, "\n"); 445 if (verbose) 446 write(1, Buffer, strlen(Buffer)); 447 write(fd, Buffer, strlen(Buffer)); 448 if (Receive(fd, verbose | REC_SHOW) != 0) { 449 fprintf(stderr, "Connection closed\n"); 450 break; 451 } 452 if (next) 453 start = ++next; 454 } while (next && *next); 455 if (verbose) 456 puts(""); 457 } 458 break; 459 460 default: 461 warnx("ppp is not responding"); 462 break; 463 } 464 465 close(fd); 466 467 return 0; 468} 469