1 /* 2 * Copyright (c) 1989 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)utility.c 5.3 (Berkeley) 06/01/90"; 10 #endif /* not lint */ 11 12 13 #include "telnetd.h" 14 15 /* 16 * utility functions performing io related tasks 17 */ 18 19 /* 20 * ttloop 21 * 22 * A small subroutine to flush the network output buffer, get some data 23 * from the network, and pass it through the telnet state machine. We 24 * also flush the pty input buffer (by dropping its data) if it becomes 25 * too full. 26 */ 27 28 void 29 ttloop() 30 { 31 void netflush(); 32 33 if (nfrontp-nbackp) { 34 netflush(); 35 } 36 ncc = read(net, netibuf, sizeof netibuf); 37 if (ncc < 0) { 38 syslog(LOG_INFO, "ttloop: read: %m\n"); 39 exit(1); 40 } else if (ncc == 0) { 41 syslog(LOG_INFO, "ttloop: peer died: %m\n"); 42 exit(1); 43 } 44 netip = netibuf; 45 telrcv(); /* state machine */ 46 if (ncc > 0) { 47 pfrontp = pbackp = ptyobuf; 48 telrcv(); 49 } 50 } /* end of ttloop */ 51 52 /* 53 * Check a descriptor to see if out of band data exists on it. 54 */ 55 stilloob(s) 56 int s; /* socket number */ 57 { 58 static struct timeval timeout = { 0 }; 59 fd_set excepts; 60 int value; 61 62 do { 63 FD_ZERO(&excepts); 64 FD_SET(s, &excepts); 65 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 66 } while ((value == -1) && (errno == EINTR)); 67 68 if (value < 0) { 69 fatalperror(pty, "select"); 70 } 71 if (FD_ISSET(s, &excepts)) { 72 return 1; 73 } else { 74 return 0; 75 } 76 } 77 78 ptyflush() 79 { 80 int n; 81 82 if ((n = pfrontp - pbackp) > 0) 83 n = write(pty, pbackp, n); 84 if (n < 0) 85 return; 86 pbackp += n; 87 if (pbackp == pfrontp) 88 pbackp = pfrontp = ptyobuf; 89 } 90 91 /* 92 * nextitem() 93 * 94 * Return the address of the next "item" in the TELNET data 95 * stream. This will be the address of the next character if 96 * the current address is a user data character, or it will 97 * be the address of the character following the TELNET command 98 * if the current address is a TELNET IAC ("I Am a Command") 99 * character. 100 */ 101 char * 102 nextitem(current) 103 char *current; 104 { 105 if ((*current&0xff) != IAC) { 106 return current+1; 107 } 108 switch (*(current+1)&0xff) { 109 case DO: 110 case DONT: 111 case WILL: 112 case WONT: 113 return current+3; 114 case SB: /* loop forever looking for the SE */ 115 { 116 register char *look = current+2; 117 118 for (;;) { 119 if ((*look++&0xff) == IAC) { 120 if ((*look++&0xff) == SE) { 121 return look; 122 } 123 } 124 } 125 } 126 default: 127 return current+2; 128 } 129 } /* end of nextitem */ 130 131 132 /* 133 * netclear() 134 * 135 * We are about to do a TELNET SYNCH operation. Clear 136 * the path to the network. 137 * 138 * Things are a bit tricky since we may have sent the first 139 * byte or so of a previous TELNET command into the network. 140 * So, we have to scan the network buffer from the beginning 141 * until we are up to where we want to be. 142 * 143 * A side effect of what we do, just to keep things 144 * simple, is to clear the urgent data pointer. The principal 145 * caller should be setting the urgent data pointer AFTER calling 146 * us in any case. 147 */ 148 netclear() 149 { 150 register char *thisitem, *next; 151 char *good; 152 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 153 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 154 155 thisitem = netobuf; 156 157 while ((next = nextitem(thisitem)) <= nbackp) { 158 thisitem = next; 159 } 160 161 /* Now, thisitem is first before/at boundary. */ 162 163 good = netobuf; /* where the good bytes go */ 164 165 while (nfrontp > thisitem) { 166 if (wewant(thisitem)) { 167 int length; 168 169 next = thisitem; 170 do { 171 next = nextitem(next); 172 } while (wewant(next) && (nfrontp > next)); 173 length = next-thisitem; 174 bcopy(thisitem, good, length); 175 good += length; 176 thisitem = next; 177 } else { 178 thisitem = nextitem(thisitem); 179 } 180 } 181 182 nbackp = netobuf; 183 nfrontp = good; /* next byte to be sent */ 184 neturg = 0; 185 } /* end of netclear */ 186 187 /* 188 * netflush 189 * Send as much data as possible to the network, 190 * handling requests for urgent data. 191 */ 192 void 193 netflush() 194 { 195 int n; 196 extern int not42; 197 198 if ((n = nfrontp - nbackp) > 0) { 199 /* 200 * if no urgent data, or if the other side appears to be an 201 * old 4.2 client (and thus unable to survive TCP urgent data), 202 * write the entire buffer in non-OOB mode. 203 */ 204 if ((neturg == 0) || (not42 == 0)) { 205 n = write(net, nbackp, n); /* normal write */ 206 } else { 207 n = neturg - nbackp; 208 /* 209 * In 4.2 (and 4.3) systems, there is some question about 210 * what byte in a sendOOB operation is the "OOB" data. 211 * To make ourselves compatible, we only send ONE byte 212 * out of band, the one WE THINK should be OOB (though 213 * we really have more the TCP philosophy of urgent data 214 * rather than the Unix philosophy of OOB data). 215 */ 216 if (n > 1) { 217 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 218 } else { 219 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 220 } 221 } 222 } 223 if (n < 0) { 224 if (errno == EWOULDBLOCK || errno == EINTR) 225 return; 226 cleanup(); 227 } 228 nbackp += n; 229 if (nbackp >= neturg) { 230 neturg = 0; 231 } 232 if (nbackp == nfrontp) { 233 nbackp = nfrontp = netobuf; 234 } 235 return; 236 } /* end of netflush */ 237 238 239 /* 240 * writenet 241 * 242 * Just a handy little function to write a bit of raw data to the net. 243 * It will force a transmit of the buffer if necessary 244 * 245 * arguments 246 * ptr - A pointer to a character string to write 247 * len - How many bytes to write 248 */ 249 writenet(ptr, len) 250 register char *ptr; 251 register int len; 252 { 253 /* flush buffer if no room for new data) */ 254 if ((&netobuf[BUFSIZ] - nfrontp) < len) { 255 /* if this fails, don't worry, buffer is a little big */ 256 netflush(); 257 } 258 259 bcopy(ptr, nfrontp, len); 260 nfrontp += len; 261 262 } /* end of writenet */ 263 264 265 /* 266 * miscellaneous functions doing a variety of little jobs follow ... 267 */ 268 269 270 fatal(f, msg) 271 int f; 272 char *msg; 273 { 274 char buf[BUFSIZ]; 275 276 (void) sprintf(buf, "telnetd: %s.\r\n", msg); 277 (void) write(f, buf, (int)strlen(buf)); 278 sleep(1); /*XXX*/ 279 exit(1); 280 } 281 282 fatalperror(f, msg) 283 int f; 284 char *msg; 285 { 286 char buf[BUFSIZ], *strerror(); 287 288 (void) sprintf(buf, "%s: %s\r\n", msg, strerror(errno)); 289 fatal(f, buf); 290 } 291 292 char editedhost[32]; 293 294 edithost(pat, host) 295 register char *pat; 296 register char *host; 297 { 298 register char *res = editedhost; 299 char *strncpy(); 300 301 if (!pat) 302 pat = ""; 303 while (*pat) { 304 switch (*pat) { 305 306 case '#': 307 if (*host) 308 host++; 309 break; 310 311 case '@': 312 if (*host) 313 *res++ = *host++; 314 break; 315 316 default: 317 *res++ = *pat; 318 break; 319 } 320 if (res == &editedhost[sizeof editedhost - 1]) { 321 *res = '\0'; 322 return; 323 } 324 pat++; 325 } 326 if (*host) 327 (void) strncpy(res, host, 328 sizeof editedhost - (res - editedhost) -1); 329 else 330 *res = '\0'; 331 editedhost[sizeof editedhost - 1] = '\0'; 332 } 333 334 static char *putlocation; 335 336 putstr(s) 337 register char *s; 338 { 339 340 while (*s) 341 putchr(*s++); 342 } 343 344 putchr(cc) 345 { 346 *putlocation++ = cc; 347 } 348 349 putf(cp, where) 350 register char *cp; 351 char *where; 352 { 353 char *slash; 354 #ifndef NO_GETTYTAB 355 char datebuffer[60]; 356 #endif /* NO_GETTYTAB */ 357 extern char *rindex(); 358 359 putlocation = where; 360 361 while (*cp) { 362 if (*cp != '%') { 363 putchr(*cp++); 364 continue; 365 } 366 switch (*++cp) { 367 368 case 't': 369 slash = rindex(line, '/'); 370 if (slash == (char *) 0) 371 putstr(line); 372 else 373 putstr(&slash[1]); 374 break; 375 376 case 'h': 377 putstr(editedhost); 378 break; 379 380 #ifndef NO_GETTYTAB 381 case 'd': 382 get_date(datebuffer); 383 putstr(datebuffer); 384 break; 385 #endif /* NO_GETTYTAB */ 386 387 case '%': 388 putchr('%'); 389 break; 390 } 391 cp++; 392 } 393 } 394 395 /*ARGSUSED*/ 396 #ifdef NO_GETTYTAB 397 getent(cp, name) 398 char *cp, *name; 399 { 400 return(0); 401 } 402 403 /*ARGSUSED*/ 404 char * 405 getstr(cp, cpp) 406 char *cp, **cpp; 407 { 408 return(0); 409 } 410 #endif /* NO_GETTYTAB */ 411