<net/dlt.h>: Sync with libpcap 1.9.1
[dragonfly.git] / games / hunt / huntd / answer.c
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 }