ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.49
Committed: Sun Jul 1 05:00:20 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.48: +11 -12 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 1992,2007 Frank Tore Johansen
6 *
7 * Crossfire TRT is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * The authors can be reached via e-mail to <crossfire@schmorp.de>
21 */
22
23 using namespace std;
24
25 #include <global.h>
26 #include <sproto.h>
27 #include <cstdarg>
28
29 #if HAVE_TCP_INFO
30 # include <sys/types.h>
31 # include <sys/socket.h>
32 # include <netinet/in.h>
33 # include <netinet/tcp.h>
34 #endif
35
36 // disconnect a socket after this many seconds without an ack
37 #define SOCKET_TIMEOUT 8.
38
39 // force a packet when idle for more than this many seconds,
40 // forcing an ack regularly.
41 #define IDLE_PING 2.
42
43 void
44 client::flush ()
45 {
46 if (destroyed ())
47 return;
48
49 #if HAVE_TCP_INFO
50 // check about once per second, spread evenly over all clients
51 // do this only when player is active
52 if (!((pticks + fd) & 7) && pl && pl->active)
53 {
54 // check time of last ack, and, if too old, kill connection
55 struct tcp_info tcpi;
56 socklen_t len = sizeof (tcpi);
57
58 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
59 {
60 if (tcpi.tcpi_snd_mss)
61 mss = tcpi.tcpi_snd_mss;
62
63 rtt = tcpi.tcpi_rtt;
64 rttvar = tcpi.tcpi_rttvar;
65
66 if (tcpi.tcpi_last_ack_recv > int (SOCKET_TIMEOUT * 1000))
67 {
68 send_msg (NDI_RED, "connection-timeout", "safety disconnect due to tcp/ip timeout (no packets received)");
69 write_outputbuffer ();
70
71 LOG (llevDebug, "connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
72 (unsigned)tcpi.tcpi_last_ack_recv, (unsigned)tcpi.tcpi_last_data_sent, (unsigned)tcpi.tcpi_unacked);
73 destroy ();
74 }
75 }
76 }
77 #endif
78
79 /**
80 * Writes data to socket.
81 *
82 * When the socket is clear to write, and we have backlogged data, this
83 * is called to write it out.
84 */
85
86 // write a nop to the socket at least every IDLE_NOP seconds.
87 if (!outputbuffer.len)
88 {
89 if (last_send + IDLE_PING <= NOW && pl && pl->active)
90 {
91 // this is a bit ugly, but map1/map1a seem to be the only
92 // nop'able commands and they are quite small.
93 packet sl (mapmode == Map1Cmd ? "map1" : "map1a");
94 send_packet (sl);
95 }
96 else
97 return;
98 }
99
100 if (socket_ev.poll () & PE_W)
101 return;
102
103 last_send = NOW;
104 write_outputbuffer ();
105 }
106
107 void
108 client::write_outputbuffer ()
109 {
110 while (outputbuffer.len)
111 {
112 int res = write (fd, outputbuffer.data + outputbuffer.start,
113 min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
114
115 if (res > 0)
116 {
117 outputbuffer.start += res;
118 /* wrap back to start of buffer */
119 if (outputbuffer.start == SOCKETBUFSIZE)
120 outputbuffer.start = 0;
121
122 outputbuffer.len -= res;
123 }
124 else if (res == 0)
125 {
126 LOG (llevError, "socket write failed, connection closed.\n");
127 destroy ();
128 return;
129 }
130 else if (errno == EINTR)
131 {
132 // just retry
133 }
134 else if (errno == EAGAIN)
135 {
136 // delay till ready
137 socket_ev.poll (socket_ev.poll () | PE_W);
138 socket_ev.start ();
139 return;
140 }
141 else
142 {
143 LOG (llevError, "socket write failed: %s\n", strerror (errno));
144 destroy ();
145 return;
146 }
147 }
148
149 socket_ev.poll (socket_ev.poll () & ~PE_W);
150 }
151
152 /******************************************************************************
153 *
154 * Start of read routines.
155 *
156 ******************************************************************************/
157
158 int
159 client::next_packet ()
160 {
161 if (inbuf_len >= 2)
162 {
163 int pkt_len = (inbuf [0] << 8) | inbuf [1];
164
165 if (inbuf_len >= 2 + pkt_len)
166 return 2 + pkt_len;
167
168 if (inbuf_len == sizeof (inbuf))
169 {
170 send_packet_printf ("drawinfo %d input buffer overflow - closing connection.", NDI_RED);
171 destroy ();
172 return -1;
173 }
174 }
175
176 return 0;
177 }
178
179 void
180 client::skip_packet (int len)
181 {
182 inbuf_len -= len;
183 memmove (inbuf, inbuf + len, inbuf_len);
184 }
185
186 /*****************************************************************************
187 * Start of command dispatch area.
188 * The commands here are protocol commands.
189 ****************************************************************************/
190
191 // SocketCommand, PlayingCommand, should not exist with those ugly casts
192 #define SC(cb) (void *)static_cast<void (*)(char *, int, client *)>(cb),
193 #define PC(cb) (void *)static_cast<void (*)(char *, int, player *)>(cb), PF_PLAYER |
194
195 /**
196 * Dispatch table for the server.
197 */
198 static struct packet_type packets[] = {
199 {"ncom", PC(NewPlayerCmd) PF_PLAYING | PF_COMMAND6 },
200 {"command", PC(PlayerCmd) PF_PLAYING | PF_COMMAND0 },
201
202 {"examine", PC(ExamineCmd) PF_PLAYING },
203 {"ex", PC(ExCmd) PF_PLAYING },
204 {"apply", PC(ApplyCmd) PF_PLAYING },
205 {"lookat", PC(LookAt) PF_PLAYING },
206 {"lock", PC(LockItem) PF_PLAYING },
207 {"mark", PC(MarkItem) PF_PLAYING },
208 {"move", PC(MoveCmd) PF_PLAYING },
209 {"ext", PC(ExtCmd) 0 }, // CF+
210 {"mapredraw", PC(MapRedrawCmd) 0 },
211 {"mapinfo", PC(MapInfoCmd) 0 }, // CF+
212
213 {"reply", SC(ReplyCmd) 0 },
214 {"exti", SC(ExtiCmd) 0 }, // CF+
215 {"addme", SC(AddMeCmd) 0 },
216 {"askface", SC(AskFaceCmd) 0 },
217 {"requestinfo", SC(RequestInfo) 0 },
218 {"setfacemode", SC(SetFaceMode) 0 },
219 {"setsound", SC(SetSound) 0 },
220 {"setup", SC(SetUp) 0 },
221 {"version", SC(VersionCmd) 0 },
222 {"toggleextendedinfos", SC(ToggleExtendedInfos) 0 }, /*Added: tchize */
223 {"toggleextendedtext", SC(ToggleExtendedText) 0 }, /*Added: tchize */
224 {"asksmooth", SC(AskSmooth) 0 }, /*Added: tchize (smoothing technologies) */
225 };
226
227 bool
228 client::may_execute (const packet_type *pkt) const
229 {
230 return (!(pkt->flags & PF_PLAYER) || pl)
231 && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING);
232 }
233
234 // HACK: some commands currently should be executed
235 // even when the player is frozen. this hack detects
236 // those commands. it should be folded into may_execute,
237 // but kept seperate to emphasise the hack aspect, i.e.
238 // do it better, then remove.
239 static bool
240 always_immediate (const client *ns, const packet_type *pkt, const char *data, int len)
241 {
242 if (!(pkt->flags & (PF_COMMAND0 | PF_COMMAND6)))
243 return false;
244
245 if (!ns->pl || !ns->pl->ob || !ns->pl->ob->map)
246 return false;
247
248 if (pkt->flags & PF_COMMAND6)
249 {
250 data += 6;
251 len -= 6;
252 }
253
254 if (len > 4 && !strncmp (data, "say " , 4))
255 return true;
256 if (len > 5 && !strncmp (data, "chat ", 5))
257 return true;
258
259 return false;
260 }
261
262 void
263 client::execute (const packet_type *pkt, char *data, int datalen)
264 {
265 if (may_execute (pkt) || always_immediate (this, pkt, data, datalen))
266 {
267 //TODO: only one format
268 if (pkt->flags & PF_PLAYER)
269 ((void (*)(char *, int, player *))pkt->cb)((char *)data, datalen, pl);
270 else
271 ((void (*)(char *, int, client *))pkt->cb)((char *)data, datalen, this);
272 }
273 else
274 send_packet_printf ("drawinfo %d ERROR: you cannot execute '%s' now.", NDI_RED, pkt->name);
275 }
276
277 bool
278 client::handle_packet ()
279 {
280 int pkt_len = next_packet ();
281
282 if (!pkt_len)
283 return false;
284 else if (pkt_len < 0)
285 {
286 LOG (llevError, "read error on player %s\n",
287 pl && pl->ob ? &pl->ob->name : "[anonymous]");
288 destroy ();
289 return false;
290 }
291
292 inbuf [pkt_len] = 0; /* Terminate buffer - useful for string data */
293
294 /* First, break out beginning word. There are at least
295 * a few commands that do not have any paremeters. If
296 * we get such a command, don't worry about trying
297 * to break it up.
298 */
299 int datalen;
300 char *data = strchr ((char *)inbuf + 2, ' ');
301
302 if (data)
303 {
304 *data++ = 0;
305 datalen = pkt_len - (data - (char *)inbuf);
306 }
307 else
308 {
309 data = (char *)inbuf + 2; // better read garbage than segfault
310 datalen = 0;
311 }
312
313 for (packet_type *pkt = packets; pkt < packets + (sizeof (packets) / sizeof (packets[0])); ++pkt)
314 if (!strcmp ((char *)inbuf + 2, pkt->name))
315 {
316 if (pkt->flags & PF_PLAYER && !always_immediate (this, pkt, data, datalen))
317 queue_command (pkt, data, datalen);
318 else
319 execute (pkt, data, datalen);
320
321 goto next_packet;
322 }
323
324 // If we get here, we didn't find a valid command.
325 send_packet_printf ("drawinfo %d ERROR: command '%s' not supported.", NDI_RED, (char *)inbuf + 2);
326 next_packet:
327 skip_packet (pkt_len);
328
329 // input buffer has space again
330 socket_ev.poll (socket_ev.poll () | PE_R);
331
332 return true;
333 }
334
335 // callback called when socket is either readable or writable
336 void
337 client::socket_cb (iow &w, int got)
338 {
339 //TODO remove when we have better socket cleanup logic
340 if (destroyed ())
341 {
342 socket_ev.poll (0);
343 return;
344 }
345
346 if (got & PE_W)
347 {
348 write_outputbuffer ();
349
350 if (!outputbuffer.len)
351 socket_ev.poll (socket_ev.poll () & ~PE_W);
352 }
353
354 if (got & PE_R)
355 {
356 //TODO: rate-limit tcp connection in better ways, important
357
358 int amount = sizeof (inbuf) - inbuf_len;
359
360 if (!amount)
361 {
362 // input buffer full
363 socket_ev.poll (socket_ev.poll () & ~PE_R);
364 return;
365 }
366
367 amount = read (fd, inbuf + inbuf_len, amount);
368
369 if (!amount)
370 {
371 destroy ();
372 return;
373 }
374 else if (amount < 0)
375 {
376 if (errno != EAGAIN && errno != EINTR)
377 {
378 LOG (llevError, "read error: %s\n", strerror (errno));
379 destroy ();
380 return;
381 }
382
383 // should not be here, normally
384 }
385 else
386 {
387 inbuf_len += amount;
388
389 cmd_ev.start ();
390 }
391 }
392 }
393
394 // called whenever we have additional commands to process
395 void
396 client::cmd_cb (iw &w)
397 {
398 if (handle_packet ())
399 w.start ();
400 else
401 flush ();
402 }
403
404 /*******************************************************************************
405 *
406 * Start of write related routines.
407 *
408 ******************************************************************************/
409
410 /**
411 * Adds data to a socket buffer for whatever reason.
412 *
413 * ns is the socket we are adding the data to, buf is the start of the
414 * data, and len is the number of bytes to add.
415 */
416 void
417 client::send (void *buf_, int len)
418 {
419 char *buf = (char *)buf_;
420
421 if (destroyed () || !buf)
422 return;
423
424 if (len + outputbuffer.len > SOCKETBUFSIZE)
425 {
426 LOG (llevDebug, "socket on fd %d has overrun internal buffer - marking as dead\n", fd);
427 // shutdown the socket, this is safer than destroying it immediately
428 // as lots of code in the callchain might still access the map etc.
429 shutdown (fd, SHUT_RDWR);
430 return;
431 }
432
433 int avail, end;
434
435 /* data + end is where we start putting the new data. The last byte
436 * currently in use is actually data + end -1
437 */
438 end = outputbuffer.start + outputbuffer.len;
439 /* The buffer is already in a wrapped state, so adjust end */
440 if (end >= SOCKETBUFSIZE)
441 end -= SOCKETBUFSIZE;
442
443 avail = SOCKETBUFSIZE - end;
444
445 /* We can all fit it behind the current data without wrapping */
446 if (avail >= len)
447 memcpy (outputbuffer.data + end, buf, len);
448 else
449 {
450 memcpy (outputbuffer.data + end, buf, avail);
451 memcpy (outputbuffer.data, buf + avail, len - avail);
452 }
453
454 outputbuffer.len += len;
455 }
456
457 /**
458 * Takes a string of data, and writes it out to the socket. A very handy
459 * shortcut function.
460 */
461 void
462 client::send_packet (packet &sl)
463 {
464 if (destroyed ())
465 return;
466
467 if (sl.length () >= MAXSOCKBUF)
468 {
469 LOG (llevError, "Trying to send a buffer beyond properly size, len =%d\n", sl.length ());
470 /* Almost certainly we've overflowed a buffer, so quit now to make
471 * it easier to debug.
472 */
473 abort ();
474 }
475
476 if (!sl.length ())
477 return;
478
479 assert (sl.hdrlen == 2);
480
481 sl.buf_ [0] = sl.length () >> 8;
482 sl.buf_ [1] = sl.length () ;
483
484 send (sl.buf_, sl.length () + sl.hdrlen);
485 }
486
487 void
488 client::send_packet (const char *buf, int len)
489 {
490 packet sl;
491
492 sl << data (buf, len);
493 send_packet (sl);
494 }
495
496 void
497 client::send_packet (const char *buf)
498 {
499 send_packet (buf, strlen (buf));
500 }
501
502 void
503 client::send_packet_printf (const char *format, ...)
504 {
505 packet sl;
506
507 va_list ap;
508 va_start (ap, format);
509 sl.vprintf (format, ap);
510 va_end (ap);
511
512 send_packet (sl);
513 }
514
515 // returns true when the message needs special (read: perl) treatment
516 static bool
517 msg_is_special (const char *msg)
518 {
519 return msg [strcspn (msg, "<[&\n")];
520 }
521
522 void
523 client::send_msg (int color, const char *type, const char *msg)
524 {
525 if (msg_is_special (msg))
526 cfperl_send_msg (this, color, type, msg);
527 else if (can_msg)
528 send_packet_printf ("msg %d %s %s", color, type, msg);
529 else if (color < 0)
530 return; // client cannot handle this
531 else
532 send_packet_printf ("drawinfo %d %s", color, msg);
533 }
534
535 void
536 client::send_drawinfo (const char *msg, int flags)
537 {
538 send_msg (flags, "log", msg);
539 }
540
541 /***********************************************************************
542 *
543 * packet functions/utilities
544 *
545 **********************************************************************/
546
547 packet::packet (const char *name)
548 {
549 reset ();
550
551 int len = strlen (name);
552 memcpy (cur, name, len); cur += len;
553 *cur++ = ' ';
554 }
555
556 packet &packet::operator <<(const ber32 v)
557 {
558 enum { maxlen = 32 / 7 + 1};
559 uint8 buf[maxlen];
560 uint8 *p = buf + maxlen;
561 uint32 val = v.val;
562
563 *--p = val & 0x7F;
564
565 while (val > 0x7F)
566 {
567 val >>= 7;
568 *--p = (val & 0x7F) | 0x80;
569 }
570
571 return *this << data (p, buf + maxlen - p);
572 }
573
574 packet &packet::operator <<(const data &v)
575 {
576 if (room () < v.len)
577 reset ();
578 else
579 {
580 if (v.len)
581 {
582 memcpy (cur, v.ptr, v.len);
583 cur += v.len;
584 }
585 }
586
587 return *this;
588 }
589
590 packet &packet::operator <<(const data8 &v)
591 {
592 unsigned int len = min (v.len, 0x00FF);
593 return *this << uint8 (len) << data (v.ptr, len);
594 }
595
596 packet &packet::operator <<(const data16 &v)
597 {
598 unsigned int len = min (v.len, 0xFFFF);
599 return *this << uint16 (len) << data (v.ptr, len);
600 }
601
602 packet &packet::operator <<(const char *v)
603 {
604 return *this << data (v, strlen (v ? v : 0));
605 }
606
607 void
608 packet::vprintf (const char *format, va_list ap)
609 {
610 int size = room ();
611
612 int len = vsnprintf ((char *)cur, size, format, ap);
613
614 if (len >= size)
615 return reset ();
616
617 cur += len;
618 }
619