2 * Copyright (c) 1983-2003, Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
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 $
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
53 /* Exported symbols for hosts_access(): */
54 int allow_severity
= LOG_INFO
;
55 int deny_severity
= LOG_WARNING
;
58 /* List of spawning connections: */
59 struct spawn
*Spawn
= NULL
;
61 static void stplayer(PLAYER
*, int);
62 static void stmonitor(PLAYER
*);
63 static IDENT
* get_ident(struct sockaddr
*, int, uid_t
, char *, char);
68 struct sockaddr sockstruct
;
72 struct request_info ri
;
75 /* Answer the call to hunt: */
76 socklen
= sizeof sockstruct
;
77 newsock
= accept(Socket
, (struct sockaddr
*) &sockstruct
, &socklen
);
79 logit(LOG_ERR
, "accept");
83 /* Check for access permissions: */
84 request_init(&ri
, RQ_DAEMON
, "huntd", RQ_FILE
, newsock
, 0);
86 if (hosts_access(&ri
) == 0) {
87 logx(LOG_INFO
, "rejected connection from %s", eval_client(&ri
));
92 /* Remember this spawning connection: */
93 sp
= (struct spawn
*)malloc(sizeof *sp
);
95 logit(LOG_ERR
, "malloc");
99 memset(sp
, '\0', sizeof *sp
);
101 /* Keep the calling machine's source addr for ident purposes: */
102 memcpy(&sp
->source
, &sockstruct
, sizeof sp
->source
);
103 sp
->sourcelen
= socklen
;
105 /* Warn if we lose connection info: */
106 if (socklen
> sizeof Spawn
->source
)
108 "struct sockaddr is not big enough! (%d > %zu)",
109 socklen
, sizeof Spawn
->source
);
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.
115 flags
= fcntl(newsock
, F_GETFL
, 0);
117 fcntl(newsock
, F_SETFL
, flags
);
119 /* Start listening to the spawning connection */
121 FD_SET(sp
->fd
, &Fds_mask
);
122 if (sp
->fd
>= Num_fds
)
123 Num_fds
= sp
->fd
+ 1;
128 /* Add to the spawning list */
129 if ((sp
->next
= Spawn
) != NULL
)
130 Spawn
->prevnext
= &sp
->next
;
131 sp
->prevnext
= &Spawn
;
136 answer_next(struct spawn
*sp
)
143 char teamstr
[] = "[x]";
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
);
152 if (len
&& sp
->msglen
< (int)(sizeof sp
->msg
))
155 teamstr
[1] = sp
->team
;
156 outyx(ALL_PLAYERS
, HEIGHT
, 0, "%s%s: %.*s",
158 sp
->team
== ' ' ? "": teamstr
,
162 sendcom(ALL_PLAYERS
, REFRESH
);
163 sendcom(ALL_PLAYERS
, READY
, 0);
168 /* Fill the buffer */
169 len
= read(sp
->fd
, sp
->inbuf
+ sp
->inlen
,
170 sizeof sp
->inbuf
- sp
->inlen
);
174 if (sp
->inlen
< (int)(sizeof sp
->inbuf
))
177 /* Extract values from the buffer */
179 memcpy(&sp
->uid
, cp1
, sizeof (u_int32_t
));
180 cp1
+= sizeof(u_int32_t
);
181 memcpy(sp
->name
, 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
);
189 memcpy(&sp
->mode
, cp1
, sizeof (u_int32_t
));
190 cp1
+= sizeof(u_int32_t
);
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
);
198 * Make sure the name contains only printable characters
199 * since we use control characters for cursor control
200 * between driver and player processes
202 sp
->name
[NAMELEN
] = '\0';
203 for (cp1
= cp2
= sp
->name
; *cp1
!= '\0'; cp1
++)
204 if (isprint(*cp1
) || *cp1
== ' ')
208 /* Make sure team name is valid */
209 if (sp
->team
< '1' || sp
->team
> '9')
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
);
216 if (sp
->mode
== C_MESSAGE
) {
217 /* The clients only wants to send a message: */
223 /* Use a stdio file descriptor from now on: */
224 conn
= fdopen(sp
->fd
, "w");
226 /* The player is a monitor: */
227 if (sp
->mode
== C_MONITOR
) {
228 if (conf_monitor
&& End_monitor
< &Monitor
[MAXMON
]) {
233 /* Too many monitors */
234 fprintf(conn
, "Too many monitors\n");
236 logx(LOG_NOTICE
, "too many monitors");
240 /* The player is a normal hunter: */
242 if (End_player
< &Player
[MAXPL
])
245 fprintf(conn
, "Too many players\n");
247 /* Too many players */
248 logx(LOG_NOTICE
, "too many players");
253 /* Find the player's running scorecard */
254 pp
->p_ident
= get_ident(&sp
->source
, sp
->sourcelen
, sp
->uid
,
257 pp
->p_death
[0] = '\0';
260 /* No idea where the player starts: */
264 /* Mode-specific initialisation: */
265 if (sp
->mode
== C_MONITOR
)
268 stplayer(pp
, sp
->enter_status
);
270 /* And, they're off! Caller should remove and free sp. */
275 logit(LOG_WARNING
, "read");
277 logx(LOG_WARNING
, "lost connection to new client");
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
);
289 /* Start a monitor: */
291 stmonitor(PLAYER
*pp
)
294 /* Monitors get to see the entire maze: */
295 memcpy(pp
->p_maze
, Maze
, sizeof pp
->p_maze
);
298 /* Put the monitor's name near the bottom right on all screens: */
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
);
304 /* Ready the monitor: */
305 sendcom(pp
, REFRESH
);
306 sendcom(pp
, READY
, 0);
310 /* Start a player: */
312 stplayer(PLAYER
*newpp
, int enter_status
)
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
];
331 for ( ; y
< HEIGHT
; y
++)
332 for (x
= 0; x
< WIDTH
; x
++)
333 newpp
->p_maze
[y
][x
] = Maze
[y
][x
];
335 /* Drop the new player somewhere in the maze: */
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
;
343 newpp
->p_undershot
= FALSE
;
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
;
352 newpp
->p_flying
= -1;
353 newpp
->p_face
= rand_dir();
356 /* Initialize the new player's attributes: */
358 newpp
->p_damcap
= conf_maxdam
;
362 newpp
->p_ammo
= conf_ishots
;
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
;
369 } else if (conf_cloak
) {
371 newpp
->p_cloak
= conf_cloaklen
;
379 * For each new player, place a large mine and
380 * a small mine somewhere in the maze:
383 x
= rand_num(WIDTH
- 1) + 1;
384 y
= rand_num(HEIGHT
- 1) + 1;
385 } while (Maze
[y
][x
] != SPACE
);
387 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
391 x
= rand_num(WIDTH
- 1) + 1;
392 y
= rand_num(HEIGHT
- 1) + 1;
393 } while (Maze
[y
][x
] != SPACE
);
395 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
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
);
403 y
= STAT_PLAY_ROW
+ 1 + (newpp
- Player
);
404 for (pp
= Player
; pp
< End_player
; pp
++) {
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
);
413 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
414 outyx(pp
, y
, STAT_NAME_COL
, Buf
, len
);
416 /* Show the new player what they can see and where they are: */
418 drawplayer(newpp
, TRUE
);
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
);
425 /* Ready the new player: */
426 sendcom(newpp
, REFRESH
);
427 sendcom(newpp
, READY
, 0);
433 * Return a random direction
438 switch (rand_num(4)) {
454 * Get the score structure of a player
457 get_ident(struct sockaddr
*sa
, int salen __unused
, uid_t uid
, char *name
,
464 if (sa
->sa_family
== AF_INET
)
465 machine
= ntohl((u_long
)((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
);
469 for (ip
= Scores
; ip
!= NULL
; ip
= ip
->i_next
)
470 if (ip
->i_machine
== machine
472 /* && ip->i_team == team */
473 && strncmp(ip
->i_name
, name
, NAMELEN
) == 0)
477 if (ip
->i_team
!= team
) {
478 logx(LOG_INFO
, "player %s %s team %c",
480 team
== ' ' ? "left" : ip
->i_team
== ' ' ?
481 "joined" : "changed to",
482 team
== ' ' ? ip
->i_team
: team
);
485 if (ip
->i_entries
< conf_scoredecay
)
488 ip
->i_kills
= (ip
->i_kills
* (conf_scoredecay
- 1))
490 ip
->i_score
= ip
->i_kills
/ (double) ip
->i_entries
;
493 /* Alloc new entry -- it is released in clear_scores() */
494 ip
= (IDENT
*) malloc(sizeof (IDENT
));
496 logit(LOG_ERR
, "malloc");
497 /* Fourth down, time to punt */
500 ip
->i_machine
= machine
;
503 strlcpy(ip
->i_name
, name
, sizeof ip
->i_name
);
514 ip
->i_gkills
= ip
->i_bkills
= ip
->i_deaths
= 0;
515 ip
->i_stillb
= ip
->i_saved
= 0;
519 logx(LOG_INFO
, "new player: %s%s%c%s",
521 team
== ' ' ? "" : " (team ",
523 team
== ' ' ? "" : ")");
530 answer_info(FILE *fp
)
535 struct sockaddr_in
*sa
;
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
);
544 logit(LOG_WARNING
, "inet_ntop");
547 fprintf(fp
, "fd %d: state %d, from %s:%d\n",
548 sp
->fd
, sp
->inlen
+ (sp
->reading_msg
? sp
->msglen
: 0),