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