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