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