1 /* $OpenBSD: answer.c,v 1.11 2007/11/06 10:22:29 chl Exp $ */ 2 /* $NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $ */ 3 /* 4 * Copyright (c) 1983-2003, Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are 9 * met: 10 * 11 * + Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * + Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * + Neither the name of the University of California, San Francisco nor 17 * the names of its contributors may be used to endorse or promote 18 * products derived from this software without specific prior written 19 * permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <ctype.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <stdio.h> 40 #include <tcpd.h> 41 #include <syslog.h> 42 #include <string.h> 43 #include <sys/socket.h> 44 #include <netinet/in.h> 45 #include <arpa/inet.h> 46 47 #include "hunt.h" 48 #include "server.h" 49 #include "conf.h" 50 51 /* Exported symbols for hosts_access(): */ 52 int allow_severity = LOG_INFO; 53 int deny_severity = LOG_WARNING; 54 55 56 /* List of spawning connections: */ 57 struct spawn *Spawn = NULL; 58 59 static void stplayer(PLAYER *, int); 60 static void stmonitor(PLAYER *); 61 static IDENT * get_ident(struct sockaddr *, int, u_long, char *, char); 62 63 void 64 answer_first() 65 { 66 struct sockaddr sockstruct; 67 int newsock; 68 socklen_t socklen; 69 int flags; 70 struct request_info ri; 71 struct spawn *sp; 72 73 /* Answer the call to hunt: */ 74 socklen = sizeof sockstruct; 75 newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen); 76 if (newsock < 0) { 77 logit(LOG_ERR, "accept"); 78 return; 79 } 80 81 /* Check for access permissions: */ 82 request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, newsock, 0); 83 fromhost(&ri); 84 if (hosts_access(&ri) == 0) { 85 logx(LOG_INFO, "rejected connection from %s", eval_client(&ri)); 86 close(newsock); 87 return; 88 } 89 90 /* Remember this spawning connection: */ 91 sp = (struct spawn *)malloc(sizeof *sp); 92 if (sp == NULL) { 93 logit(LOG_ERR, "malloc"); 94 close(newsock); 95 return; 96 } 97 memset(sp, '\0', sizeof *sp); 98 99 /* Keep the calling machine's source addr for ident purposes: */ 100 memcpy(&sp->source, &sockstruct, sizeof sp->source); 101 sp->sourcelen = socklen; 102 103 /* Warn if we lose connection info: */ 104 if (socklen > sizeof Spawn->source) 105 logx(LOG_WARNING, 106 "struct sockaddr is not big enough! (%d > %zu)", 107 socklen, sizeof Spawn->source); 108 109 /* 110 * Turn off blocking I/O, so a slow or dead terminal won't stop 111 * the game. All subsequent reads check how many bytes they read. 112 */ 113 flags = fcntl(newsock, F_GETFL, 0); 114 flags |= O_NDELAY; 115 (void) fcntl(newsock, F_SETFL, flags); 116 117 /* Start listening to the spawning connection */ 118 sp->fd = newsock; 119 FD_SET(sp->fd, &Fds_mask); 120 if (sp->fd >= Num_fds) 121 Num_fds = sp->fd + 1; 122 123 sp->reading_msg = 0; 124 sp->inlen = 0; 125 126 /* Add to the spawning list */ 127 if ((sp->next = Spawn) != NULL) 128 Spawn->prevnext = &sp->next; 129 sp->prevnext = &Spawn; 130 Spawn = sp; 131 } 132 133 int 134 answer_next(sp) 135 struct spawn *sp; 136 { 137 PLAYER *pp; 138 char *cp1, *cp2; 139 u_int32_t version; 140 FILE *conn; 141 int len; 142 char teamstr[] = "[x]"; 143 144 if (sp->reading_msg) { 145 /* Receive a message from a player */ 146 len = read(sp->fd, sp->msg + sp->msglen, 147 sizeof sp->msg - sp->msglen); 148 if (len < 0) 149 goto error; 150 sp->msglen += len; 151 if (len && sp->msglen < sizeof sp->msg) 152 return FALSE; 153 154 teamstr[1] = sp->team; 155 outyx(ALL_PLAYERS, HEIGHT, 0, "%s%s: %.*s", 156 sp->name, 157 sp->team == ' ' ? "": teamstr, 158 sp->msglen, 159 sp->msg); 160 ce(ALL_PLAYERS); 161 sendcom(ALL_PLAYERS, REFRESH); 162 sendcom(ALL_PLAYERS, READY, 0); 163 flush(ALL_PLAYERS); 164 goto close_it; 165 } 166 167 /* Fill the buffer */ 168 len = read(sp->fd, sp->inbuf + sp->inlen, 169 sizeof sp->inbuf - sp->inlen); 170 if (len <= 0) 171 goto error; 172 sp->inlen += len; 173 if (sp->inlen < sizeof sp->inbuf) 174 return FALSE; 175 176 /* Extract values from the buffer */ 177 cp1 = sp->inbuf; 178 memcpy(&sp->uid, cp1, sizeof (u_int32_t)); 179 cp1+= sizeof(u_int32_t); 180 memcpy(sp->name, cp1, NAMELEN); 181 cp1+= NAMELEN; 182 memcpy(&sp->team, cp1, sizeof (u_int8_t)); 183 cp1+= sizeof(u_int8_t); 184 memcpy(&sp->enter_status, cp1, sizeof (u_int32_t)); 185 cp1+= sizeof(u_int32_t); 186 memcpy(sp->ttyname, cp1, NAMELEN); 187 cp1+= NAMELEN; 188 memcpy(&sp->mode, cp1, sizeof (u_int32_t)); 189 cp1+= sizeof(u_int32_t); 190 191 /* Convert data from network byte order: */ 192 sp->uid = ntohl(sp->uid); 193 sp->enter_status = ntohl(sp->enter_status); 194 sp->mode = ntohl(sp->mode); 195 196 /* 197 * Make sure the name contains only printable characters 198 * since we use control characters for cursor control 199 * between driver and player processes 200 */ 201 sp->name[NAMELEN] = '\0'; 202 for (cp1 = cp2 = sp->name; *cp1 != '\0'; cp1++) 203 if (isprint(*cp1) || *cp1 == ' ') 204 *cp2++ = *cp1; 205 *cp2 = '\0'; 206 207 /* Make sure team name is valid */ 208 if (sp->team < '1' || sp->team > '9') 209 sp->team = ' '; 210 211 /* Tell the other end this server's hunt driver version: */ 212 version = htonl((u_int32_t) HUNT_VERSION); 213 (void) write(sp->fd, &version, sizeof version); 214 215 if (sp->mode == C_MESSAGE) { 216 /* The clients only wants to send a message: */ 217 sp->msglen = 0; 218 sp->reading_msg = 1; 219 return FALSE; 220 } 221 222 /* Use a stdio file descriptor from now on: */ 223 conn = fdopen(sp->fd, "w"); 224 225 /* The player is a monitor: */ 226 if (sp->mode == C_MONITOR) { 227 if (conf_monitor && End_monitor < &Monitor[MAXMON]) { 228 pp = End_monitor++; 229 if (sp->team == ' ') 230 sp->team = '*'; 231 } else { 232 /* Too many monitors */ 233 fprintf(conn, "Too many monitors\n"); 234 fflush(conn); 235 logx(LOG_NOTICE, "too many monitors"); 236 goto close_it; 237 } 238 239 /* The player is a normal hunter: */ 240 } else { 241 if (End_player < &Player[MAXPL]) 242 pp = End_player++; 243 else { 244 fprintf(conn, "Too many players\n"); 245 fflush(conn); 246 /* Too many players */ 247 logx(LOG_NOTICE, "too many players"); 248 goto close_it; 249 } 250 } 251 252 /* Find the player's running scorecard */ 253 pp->p_ident = get_ident(&sp->source, sp->sourcelen, sp->uid, 254 sp->name, sp->team); 255 pp->p_output = conn; 256 pp->p_death[0] = '\0'; 257 pp->p_fd = sp->fd; 258 259 /* No idea where the player starts: */ 260 pp->p_y = 0; 261 pp->p_x = 0; 262 263 /* Mode-specific initialisation: */ 264 if (sp->mode == C_MONITOR) 265 stmonitor(pp); 266 else 267 stplayer(pp, sp->enter_status); 268 269 /* And, they're off! Caller should remove and free sp. */ 270 return TRUE; 271 272 error: 273 if (len < 0) 274 logit(LOG_WARNING, "read"); 275 else 276 logx(LOG_WARNING, "lost connection to new client"); 277 278 close_it: 279 /* Destroy the spawn */ 280 *sp->prevnext = sp->next; 281 if (sp->next) sp->next->prevnext = sp->prevnext; 282 FD_CLR(sp->fd, &Fds_mask); 283 close(sp->fd); 284 free(sp); 285 return FALSE; 286 } 287 288 /* Start a monitor: */ 289 static void 290 stmonitor(pp) 291 PLAYER *pp; 292 { 293 294 /* Monitors get to see the entire maze: */ 295 memcpy(pp->p_maze, Maze, sizeof pp->p_maze); 296 drawmaze(pp); 297 298 /* Put the monitor's name near the bottom right on all screens: */ 299 outyx(ALL_PLAYERS, 300 STAT_MON_ROW + 1 + (pp - Monitor), STAT_NAME_COL, 301 "%5.5s%c%-10.10s %c", " ", 302 stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team); 303 304 /* Ready the monitor: */ 305 sendcom(pp, REFRESH); 306 sendcom(pp, READY, 0); 307 flush(pp); 308 } 309 310 /* Start a player: */ 311 static void 312 stplayer(newpp, enter_status) 313 PLAYER *newpp; 314 int enter_status; 315 { 316 int x, y; 317 PLAYER *pp; 318 int len; 319 320 Nplayer++; 321 322 for (y = 0; y < UBOUND; y++) 323 for (x = 0; x < WIDTH; x++) 324 newpp->p_maze[y][x] = Maze[y][x]; 325 for ( ; y < DBOUND; y++) { 326 for (x = 0; x < LBOUND; x++) 327 newpp->p_maze[y][x] = Maze[y][x]; 328 for ( ; x < RBOUND; x++) 329 newpp->p_maze[y][x] = SPACE; 330 for ( ; x < WIDTH; x++) 331 newpp->p_maze[y][x] = Maze[y][x]; 332 } 333 for ( ; y < HEIGHT; y++) 334 for (x = 0; x < WIDTH; x++) 335 newpp->p_maze[y][x] = Maze[y][x]; 336 337 /* Drop the new player somewhere in the maze: */ 338 do { 339 x = rand_num(WIDTH - 1) + 1; 340 y = rand_num(HEIGHT - 1) + 1; 341 } while (Maze[y][x] != SPACE); 342 newpp->p_over = SPACE; 343 newpp->p_x = x; 344 newpp->p_y = y; 345 newpp->p_undershot = FALSE; 346 347 /* Send them flying if needed */ 348 if (enter_status == Q_FLY && conf_fly) { 349 newpp->p_flying = rand_num(conf_flytime); 350 newpp->p_flyx = 2 * rand_num(conf_flystep + 1) - conf_flystep; 351 newpp->p_flyy = 2 * rand_num(conf_flystep + 1) - conf_flystep; 352 newpp->p_face = FLYER; 353 } else { 354 newpp->p_flying = -1; 355 newpp->p_face = rand_dir(); 356 } 357 358 /* Initialize the new player's attributes: */ 359 newpp->p_damage = 0; 360 newpp->p_damcap = conf_maxdam; 361 newpp->p_nchar = 0; 362 newpp->p_ncount = 0; 363 newpp->p_nexec = 0; 364 newpp->p_ammo = conf_ishots; 365 newpp->p_nboots = 0; 366 367 /* Decide on what cloak/scan status to enter with */ 368 if (enter_status == Q_SCAN && conf_scan) { 369 newpp->p_scan = conf_scanlen * Nplayer; 370 newpp->p_cloak = 0; 371 } else if (conf_cloak) { 372 newpp->p_scan = 0; 373 newpp->p_cloak = conf_cloaklen; 374 } else { 375 newpp->p_scan = 0; 376 newpp->p_cloak = 0; 377 } 378 newpp->p_ncshot = 0; 379 380 /* 381 * For each new player, place a large mine and 382 * a small mine somewhere in the maze: 383 */ 384 do { 385 x = rand_num(WIDTH - 1) + 1; 386 y = rand_num(HEIGHT - 1) + 1; 387 } while (Maze[y][x] != SPACE); 388 Maze[y][x] = GMINE; 389 for (pp = Monitor; pp < End_monitor; pp++) 390 check(pp, y, x); 391 392 do { 393 x = rand_num(WIDTH - 1) + 1; 394 y = rand_num(HEIGHT - 1) + 1; 395 } while (Maze[y][x] != SPACE); 396 Maze[y][x] = MINE; 397 for (pp = Monitor; pp < End_monitor; pp++) 398 check(pp, y, x); 399 400 /* Create a score line for the new player: */ 401 (void) snprintf(Buf, sizeof Buf, "%5.2f%c%-10.10s %c", 402 newpp->p_ident->i_score, stat_char(newpp), 403 newpp->p_ident->i_name, newpp->p_ident->i_team); 404 len = strlen(Buf); 405 y = STAT_PLAY_ROW + 1 + (newpp - Player); 406 for (pp = Player; pp < End_player; pp++) { 407 if (pp != newpp) { 408 /* Give everyone a few more shots: */ 409 pp->p_ammo += conf_nshots; 410 newpp->p_ammo += conf_nshots; 411 outyx(pp, y, STAT_NAME_COL, Buf, len); 412 ammo_update(pp); 413 } 414 } 415 for (pp = Monitor; pp < End_monitor; pp++) 416 outyx(pp, y, STAT_NAME_COL, Buf, len); 417 418 /* Show the new player what they can see and where they are: */ 419 drawmaze(newpp); 420 drawplayer(newpp, TRUE); 421 look(newpp); 422 423 /* Make sure that the position they enter in will be erased: */ 424 if (enter_status == Q_FLY && conf_fly) 425 showexpl(newpp->p_y, newpp->p_x, FLYER); 426 427 /* Ready the new player: */ 428 sendcom(newpp, REFRESH); 429 sendcom(newpp, READY, 0); 430 flush(newpp); 431 } 432 433 /* 434 * rand_dir: 435 * Return a random direction 436 */ 437 int 438 rand_dir() 439 { 440 switch (rand_num(4)) { 441 case 0: 442 return LEFTS; 443 case 1: 444 return RIGHT; 445 case 2: 446 return BELOW; 447 case 3: 448 return ABOVE; 449 } 450 /* NOTREACHED */ 451 return(-1); 452 } 453 454 /* 455 * get_ident: 456 * Get the score structure of a player 457 */ 458 static IDENT * 459 get_ident(sa, salen, uid, name, team) 460 struct sockaddr *sa; 461 int salen; 462 u_long uid; 463 char *name; 464 char team; 465 { 466 IDENT *ip; 467 static IDENT punt; 468 u_int32_t machine; 469 470 if (sa->sa_family == AF_INET) 471 machine = ntohl((u_long)((struct sockaddr_in *)sa)->sin_addr.s_addr); 472 else 473 machine = 0; 474 475 for (ip = Scores; ip != NULL; ip = ip->i_next) 476 if (ip->i_machine == machine 477 && ip->i_uid == uid 478 /* && ip->i_team == team */ 479 && strncmp(ip->i_name, name, NAMELEN) == 0) 480 break; 481 482 if (ip != NULL) { 483 if (ip->i_team != team) { 484 logx(LOG_INFO, "player %s %s team %c", 485 name, 486 team == ' ' ? "left" : ip->i_team == ' ' ? 487 "joined" : "changed to", 488 team == ' ' ? ip->i_team : team); 489 ip->i_team = team; 490 } 491 if (ip->i_entries < conf_scoredecay) 492 ip->i_entries++; 493 else 494 ip->i_kills = (ip->i_kills * (conf_scoredecay - 1)) 495 / conf_scoredecay; 496 ip->i_score = ip->i_kills / (double) ip->i_entries; 497 } 498 else { 499 /* Alloc new entry -- it is released in clear_scores() */ 500 ip = (IDENT *) malloc(sizeof (IDENT)); 501 if (ip == NULL) { 502 logit(LOG_ERR, "malloc"); 503 /* Fourth down, time to punt */ 504 ip = &punt; 505 } 506 ip->i_machine = machine; 507 ip->i_team = team; 508 ip->i_uid = uid; 509 strlcpy(ip->i_name, name, sizeof ip->i_name); 510 ip->i_kills = 0; 511 ip->i_entries = 1; 512 ip->i_score = 0; 513 ip->i_absorbed = 0; 514 ip->i_faced = 0; 515 ip->i_shot = 0; 516 ip->i_robbed = 0; 517 ip->i_slime = 0; 518 ip->i_missed = 0; 519 ip->i_ducked = 0; 520 ip->i_gkills = ip->i_bkills = ip->i_deaths = 0; 521 ip->i_stillb = ip->i_saved = 0; 522 ip->i_next = Scores; 523 Scores = ip; 524 525 logx(LOG_INFO, "new player: %s%s%c%s", 526 name, 527 team == ' ' ? "" : " (team ", 528 team, 529 team == ' ' ? "" : ")"); 530 } 531 532 return ip; 533 } 534 535 void 536 answer_info(fp) 537 FILE *fp; 538 { 539 struct spawn *sp; 540 char buf[128]; 541 const char *bf; 542 struct sockaddr_in *sa; 543 544 if (Spawn == NULL) 545 return; 546 fprintf(fp, "\nSpawning connections:\n"); 547 for (sp = Spawn; sp; sp = sp->next) { 548 sa = (struct sockaddr_in *)&sp->source; 549 bf = inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof buf); 550 if (!bf) { 551 logit(LOG_WARNING, "inet_ntop"); 552 bf = "?"; 553 } 554 fprintf(fp, "fd %d: state %d, from %s:%d\n", 555 sp->fd, sp->inlen + (sp->reading_msg ? sp->msglen : 0), 556 bf, sa->sin_port); 557 } 558 } 559