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