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