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