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, 11 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

# User Rev Content
1 elmex 1.1 /*
2 root 1.49 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 pippijn 1.32 *
4 root 1.42 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5     * Copyright (©) 1992,2007 Frank Tore Johansen
6     *
7 root 1.49 * 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 root 1.42 *
12 root 1.49 * 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 root 1.42 *
17 root 1.49 * 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 pippijn 1.32 *
20 root 1.42 * The authors can be reached via e-mail to <crossfire@schmorp.de>
21 pippijn 1.32 */
22 root 1.42
23 elmex 1.1 using namespace std;
24    
25     #include <global.h>
26     #include <sproto.h>
27 root 1.11 #include <cstdarg>
28 elmex 1.1
29 root 1.45 #if HAVE_TCP_INFO
30 elmex 1.1 # include <sys/types.h>
31     # include <sys/socket.h>
32     # include <netinet/in.h>
33     # include <netinet/tcp.h>
34     #endif
35    
36 root 1.43 // 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 elmex 1.1
43 root 1.5 void
44 root 1.22 client::flush ()
45 elmex 1.1 {
46 root 1.29 if (destroyed ())
47     return;
48    
49 root 1.45 #if HAVE_TCP_INFO
50 root 1.43 // check about once per second, spread evenly over all clients
51 root 1.47 // do this only when player is active
52     if (!((pticks + fd) & 7) && pl && pl->active)
53 root 1.43 {
54     // check time of last ack, and, if too old, kill connection
55     struct tcp_info tcpi;
56     socklen_t len = sizeof (tcpi);
57 elmex 1.1
58 root 1.43 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
59     {
60 root 1.46 if (tcpi.tcpi_snd_mss)
61     mss = tcpi.tcpi_snd_mss;
62    
63 root 1.43 rtt = tcpi.tcpi_rtt;
64     rttvar = tcpi.tcpi_rttvar;
65 elmex 1.1
66 root 1.43 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 root 1.21
71 root 1.43 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 elmex 1.1 }
76     }
77 root 1.20 #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 root 1.43 // write a nop to the socket at least every IDLE_NOP seconds.
87     if (!outputbuffer.len)
88     {
89 root 1.47 if (last_send + IDLE_PING <= NOW && pl && pl->active)
90 root 1.43 {
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 root 1.20 return;
102    
103 root 1.43 last_send = NOW;
104 root 1.20 write_outputbuffer ();
105     }
106 elmex 1.1
107 root 1.20 void
108 root 1.22 client::write_outputbuffer ()
109 root 1.20 {
110     while (outputbuffer.len)
111     {
112     int res = write (fd, outputbuffer.data + outputbuffer.start,
113     min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
114 root 1.5
115 root 1.20 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 root 1.23 destroy ();
128 root 1.20 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 root 1.23 destroy ();
145 root 1.20 return;
146     }
147     }
148    
149     socket_ev.poll (socket_ev.poll () & ~PE_W);
150 elmex 1.1 }
151    
152 root 1.23 /******************************************************************************
153 elmex 1.1 *
154 root 1.23 * Start of read routines.
155 elmex 1.1 *
156 root 1.23 ******************************************************************************/
157 elmex 1.1
158 root 1.23 int
159     client::next_packet ()
160 elmex 1.1 {
161 root 1.23 if (inbuf_len >= 2)
162 root 1.11 {
163 root 1.23 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 root 1.15 {
170 root 1.23 send_packet_printf ("drawinfo %d input buffer overflow - closing connection.", NDI_RED);
171     destroy ();
172     return -1;
173 root 1.15 }
174 root 1.11 }
175 root 1.9
176 root 1.23 return 0;
177 root 1.11 }
178 root 1.9
179 root 1.23 void
180     client::skip_packet (int len)
181 root 1.11 {
182 root 1.23 inbuf_len -= len;
183     memmove (inbuf, inbuf + len, inbuf_len);
184 elmex 1.1 }
185    
186 root 1.23 /*****************************************************************************
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 root 1.33 {"ncom", PC(NewPlayerCmd) PF_PLAYING | PF_COMMAND6 },
200     {"command", PC(PlayerCmd) PF_PLAYING | PF_COMMAND0 },
201 root 1.23
202     {"examine", PC(ExamineCmd) PF_PLAYING },
203 root 1.39 {"ex", PC(ExCmd) PF_PLAYING },
204 root 1.23 {"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 root 1.39 {"ext", PC(ExtCmd) 0 }, // CF+
210     {"mapredraw", PC(MapRedrawCmd) 0 },
211     {"mapinfo", PC(MapInfoCmd) 0 }, // CF+
212 root 1.23
213 root 1.28 {"reply", SC(ReplyCmd) 0 },
214 root 1.39 {"exti", SC(ExtiCmd) 0 }, // CF+
215 root 1.26 {"addme", SC(AddMeCmd) 0 },
216 root 1.36 {"askface", SC(AskFaceCmd) 0 },
217 root 1.26 {"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 root 1.23 };
226    
227     bool
228     client::may_execute (const packet_type *pkt) const
229 elmex 1.1 {
230 root 1.23 return (!(pkt->flags & PF_PLAYER) || pl)
231 root 1.29 && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING);
232 root 1.11 }
233 root 1.9
234 root 1.33 // 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 root 1.23 void
263     client::execute (const packet_type *pkt, char *data, int datalen)
264 root 1.11 {
265 root 1.33 if (may_execute (pkt) || always_immediate (this, pkt, data, datalen))
266 root 1.23 {
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 elmex 1.1 }
276    
277 root 1.23 bool
278     client::handle_packet ()
279 root 1.10 {
280 root 1.23 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 root 1.15
313 root 1.23 for (packet_type *pkt = packets; pkt < packets + (sizeof (packets) / sizeof (packets[0])); ++pkt)
314     if (!strcmp ((char *)inbuf + 2, pkt->name))
315     {
316 root 1.33 if (pkt->flags & PF_PLAYER && !always_immediate (this, pkt, data, datalen))
317 root 1.26 queue_command (pkt, data, datalen);
318     else
319 root 1.23 execute (pkt, data, datalen);
320    
321     goto next_packet;
322     }
323    
324 root 1.26 // If we get here, we didn't find a valid command.
325 root 1.23 send_packet_printf ("drawinfo %d ERROR: command '%s' not supported.", NDI_RED, (char *)inbuf + 2);
326     next_packet:
327     skip_packet (pkt_len);
328 root 1.11
329 root 1.23 // input buffer has space again
330     socket_ev.poll (socket_ev.poll () | PE_R);
331 root 1.10
332 root 1.23 return true;
333 root 1.10 }
334    
335 root 1.23 // 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 root 1.29 if (destroyed ())
341 root 1.23 {
342     socket_ev.poll (0);
343     return;
344     }
345 elmex 1.1
346 root 1.23 if (got & PE_W)
347 root 1.5 {
348 root 1.23 write_outputbuffer ();
349    
350     if (!outputbuffer.len)
351     socket_ev.poll (socket_ev.poll () & ~PE_W);
352     }
353 root 1.12
354 root 1.23 if (got & PE_R)
355     {
356     //TODO: rate-limit tcp connection in better ways, important
357 pippijn 1.8
358 root 1.12 int amount = sizeof (inbuf) - inbuf_len;
359    
360 root 1.23 if (!amount)
361 root 1.5 {
362 root 1.23 // input buffer full
363     socket_ev.poll (socket_ev.poll () & ~PE_R);
364     return;
365 root 1.3 }
366 elmex 1.1
367 root 1.12 amount = read (fd, inbuf + inbuf_len, amount);
368    
369     if (!amount)
370 root 1.5 {
371 root 1.23 destroy ();
372     return;
373 root 1.5 }
374 root 1.12 else if (amount < 0)
375 root 1.5 {
376 root 1.12 if (errno != EAGAIN && errno != EINTR)
377 root 1.5 {
378 root 1.20 LOG (llevError, "read error: %s\n", strerror (errno));
379 root 1.23 destroy ();
380     return;
381 root 1.3 }
382 root 1.12
383 root 1.23 // should not be here, normally
384 root 1.3 }
385 root 1.23 else
386     {
387     inbuf_len += amount;
388 root 1.12
389 root 1.23 cmd_ev.start ();
390     }
391 root 1.5 }
392 root 1.12 }
393    
394 root 1.23 // called whenever we have additional commands to process
395 root 1.12 void
396 root 1.23 client::cmd_cb (iw &w)
397 root 1.12 {
398 root 1.26 if (handle_packet ())
399 root 1.23 w.start ();
400     else
401     flush ();
402 elmex 1.1 }
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 root 1.20 void
417 root 1.22 client::send (void *buf_, int len)
418 root 1.20 {
419     char *buf = (char *)buf_;
420 elmex 1.1
421 root 1.29 if (destroyed () || !buf)
422 root 1.23 return;
423 elmex 1.1
424 root 1.31 if (len + outputbuffer.len > SOCKETBUFSIZE)
425 root 1.5 {
426 root 1.23 LOG (llevDebug, "socket on fd %d has overrun internal buffer - marking as dead\n", fd);
427 root 1.36 // 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 root 1.5 return;
431     }
432    
433 root 1.20 int avail, end;
434    
435 root 1.5 /* data + end is where we start putting the new data. The last byte
436     * currently in use is actually data + end -1
437     */
438 root 1.20 end = outputbuffer.start + outputbuffer.len;
439 root 1.5 /* The buffer is already in a wrapped state, so adjust end */
440     if (end >= SOCKETBUFSIZE)
441     end -= SOCKETBUFSIZE;
442 root 1.7
443 root 1.5 avail = SOCKETBUFSIZE - end;
444 elmex 1.1
445 root 1.5 /* We can all fit it behind the current data without wrapping */
446     if (avail >= len)
447 root 1.20 memcpy (outputbuffer.data + end, buf, len);
448 root 1.5 else
449     {
450 root 1.20 memcpy (outputbuffer.data + end, buf, avail);
451     memcpy (outputbuffer.data, buf + avail, len - avail);
452 elmex 1.1 }
453 root 1.7
454 root 1.20 outputbuffer.len += len;
455 elmex 1.1 }
456    
457     /**
458     * Takes a string of data, and writes it out to the socket. A very handy
459     * shortcut function.
460     */
461 root 1.13 void
462 root 1.22 client::send_packet (packet &sl)
463 root 1.13 {
464 root 1.29 if (destroyed ())
465 root 1.19 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 root 1.13 }
486    
487 root 1.5 void
488 root 1.22 client::send_packet (const char *buf, int len)
489 elmex 1.1 {
490 root 1.14 packet sl;
491 elmex 1.1
492 root 1.13 sl << data (buf, len);
493     send_packet (sl);
494 elmex 1.1 }
495    
496 root 1.13 void
497 root 1.22 client::send_packet (const char *buf)
498 root 1.13 {
499     send_packet (buf, strlen (buf));
500     }
501 elmex 1.1
502 root 1.23 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 root 1.48 // 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 root 1.35 }
521    
522 root 1.40 void
523     client::send_msg (int color, const char *type, const char *msg)
524     {
525 root 1.48 if (msg_is_special (msg))
526     cfperl_send_msg (this, color, type, msg);
527     else if (can_msg)
528 root 1.40 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 root 1.48 void
536     client::send_drawinfo (const char *msg, int flags)
537     {
538     send_msg (flags, "log", msg);
539     }
540    
541 root 1.23 /***********************************************************************
542     *
543     * packet functions/utilities
544     *
545     **********************************************************************/
546    
547 root 1.27 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 root 1.37 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 root 1.38 *--p = val & 0x7F;
564    
565 root 1.37 while (val > 0x7F)
566     {
567 root 1.38 val >>= 7;
568 root 1.37 *--p = (val & 0x7F) | 0x80;
569     }
570    
571     return *this << data (p, buf + maxlen - p);
572     }
573    
574 root 1.23 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