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