ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/lowlevel.C
Revision: 1.29
Committed: Thu Dec 21 23:37:06 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.28: +7 -4 lines
Log Message:
- made state a per-client variable
  (that does not magically make state a per-client thing!)
- rename player->socket to player->ns. its not a good name for "client",
  but it is an historical artifact, and better than "socket".

File Contents

# User Rev Content
1 elmex 1.1 /*
2     CrossFire, A Multiplayer game for X-windows
3    
4     Copyright (C) 1992 Frank Tore Johansen
5    
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10    
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     GNU General Public License for more details.
15    
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19    
20     The author can be reached via e-mail to mark@pyramid.com
21     */
22    
23     /**
24     * \file
25     * Low-level socket-related functions.
26     *
27     * \date 2003-12-02
28     *
29     * Contains some base functions that both the client and server
30     * can use. As such, depending what we are being compiled for will
31     * determine what we can include. the client is designed have
32     * CFCLIENT defined as part of its compile flags.
33     */
34    
35     using namespace std;
36    
37     #include <global.h>
38     #include <sproto.h>
39 root 1.11 #include <cstdarg>
40 elmex 1.1
41     #ifdef __linux__
42     # include <sys/types.h>
43     # include <sys/socket.h>
44     # include <netinet/in.h>
45     # include <netinet/tcp.h>
46     #endif
47    
48     // use a really low timeout, as it doesn't cost any bandwidth, and you can
49     // easily die in 20 seconds...
50 root 1.26 #define SOCKET_TIMEOUT1 10 * 1000
51     #define SOCKET_TIMEOUT2 20 * 1000
52 elmex 1.1
53 root 1.5 void
54 root 1.22 client::flush ()
55 elmex 1.1 {
56 root 1.29 if (destroyed ())
57     return;
58    
59 elmex 1.1 #ifdef __linux__
60     // check time of last ack, and, if too old, kill connection
61     struct tcp_info tcpi;
62     socklen_t len = sizeof (tcpi);
63    
64 root 1.20 if (!getsockopt (fd, IPPROTO_TCP, TCP_INFO, &tcpi, &len) && len == sizeof (tcpi))
65 elmex 1.1 {
66     unsigned int diff = tcpi.tcpi_last_ack_recv - tcpi.tcpi_last_data_sent;
67    
68 root 1.21 rtt = tcpi.tcpi_rtt;
69     rttvar = tcpi.tcpi_rttvar;
70    
71 root 1.26 if (tcpi.tcpi_unacked && SOCKET_TIMEOUT1 < diff && diff < 0x80000000UL // ack delayed for 20s
72     && SOCKET_TIMEOUT2 < tcpi.tcpi_last_data_sent) // no data sent for 10s
73 elmex 1.1 {
74 root 1.20 LOG (llevDebug, "Connection on fd %d closed due to ack timeout (%u/%u/%u)\n", fd,
75 root 1.5 (unsigned) tcpi.tcpi_last_ack_recv, (unsigned) tcpi.tcpi_last_data_sent, (unsigned) tcpi.tcpi_unacked);
76 root 1.23 destroy ();
77 elmex 1.1 }
78     }
79 root 1.20 #endif
80    
81     /**
82     * Writes data to socket.
83     *
84     * When the socket is clear to write, and we have backlogged data, this
85     * is called to write it out.
86     */
87    
88     if (!outputbuffer.len || socket_ev.poll () & PE_W)
89     return;
90    
91     write_outputbuffer ();
92     }
93 elmex 1.1
94 root 1.20 void
95 root 1.22 client::write_outputbuffer ()
96 root 1.20 {
97     while (outputbuffer.len)
98     {
99     int res = write (fd, outputbuffer.data + outputbuffer.start,
100     min (outputbuffer.len, SOCKETBUFSIZE - outputbuffer.start));
101 root 1.5
102 root 1.20 if (res > 0)
103     {
104     outputbuffer.start += res;
105     /* wrap back to start of buffer */
106     if (outputbuffer.start == SOCKETBUFSIZE)
107     outputbuffer.start = 0;
108    
109     outputbuffer.len -= res;
110     #ifdef CS_LOGSTATS
111     cst_tot.obytes += res;
112     cst_lst.obytes += res;
113 elmex 1.1 #endif
114 root 1.20 }
115     else if (res == 0)
116     {
117     LOG (llevError, "socket write failed, connection closed.\n");
118 root 1.23 destroy ();
119 root 1.20 return;
120     }
121     else if (errno == EINTR)
122     {
123     // just retry
124     }
125     else if (errno == EAGAIN)
126     {
127     // delay till ready
128     socket_ev.poll (socket_ev.poll () | PE_W);
129     socket_ev.start ();
130     return;
131     }
132     else
133     {
134     LOG (llevError, "socket write failed: %s\n", strerror (errno));
135 root 1.23 destroy ();
136 root 1.20 return;
137     }
138     }
139    
140     socket_ev.poll (socket_ev.poll () & ~PE_W);
141 elmex 1.1 }
142    
143 root 1.23 /******************************************************************************
144 elmex 1.1 *
145 root 1.23 * Start of read routines.
146 elmex 1.1 *
147 root 1.23 ******************************************************************************/
148 elmex 1.1
149 root 1.23 int
150     client::next_packet ()
151 elmex 1.1 {
152 root 1.23 if (inbuf_len >= 2)
153 root 1.11 {
154 root 1.23 int pkt_len = (inbuf [0] << 8) | inbuf [1];
155    
156     if (inbuf_len >= 2 + pkt_len)
157     return 2 + pkt_len;
158    
159     if (inbuf_len == sizeof (inbuf))
160 root 1.15 {
161 root 1.23 send_packet_printf ("drawinfo %d input buffer overflow - closing connection.", NDI_RED);
162     destroy ();
163     return -1;
164 root 1.15 }
165 root 1.11 }
166 root 1.9
167 root 1.23 return 0;
168 root 1.11 }
169 root 1.9
170 root 1.23 void
171     client::skip_packet (int len)
172 root 1.11 {
173 root 1.23 inbuf_len -= len;
174     memmove (inbuf, inbuf + len, inbuf_len);
175 elmex 1.1 }
176    
177 root 1.23 /*****************************************************************************
178     * Start of command dispatch area.
179     * The commands here are protocol commands.
180     ****************************************************************************/
181    
182     // SocketCommand, PlayingCommand, should not exist with those ugly casts
183     #define SC(cb) (void *)static_cast<void (*)(char *, int, client *)>(cb),
184     #define PC(cb) (void *)static_cast<void (*)(char *, int, player *)>(cb), PF_PLAYER |
185    
186     /**
187     * Dispatch table for the server.
188     */
189     static struct packet_type packets[] = {
190     {"ncom", PC(NewPlayerCmd) PF_PLAYING },
191     {"command", PC(PlayerCmd) PF_PLAYING },
192    
193     {"examine", PC(ExamineCmd) PF_PLAYING },
194     {"apply", PC(ApplyCmd) PF_PLAYING },
195     {"lookat", PC(LookAt) PF_PLAYING },
196     {"lock", PC(LockItem) PF_PLAYING },
197     {"mark", PC(MarkItem) PF_PLAYING },
198     {"move", PC(MoveCmd) PF_PLAYING },
199     {"ext", PC(ExtCmd) 0 }, /* CF+ */
200 root 1.26 {"mapredraw", PC(MapRedrawCmd) 0 }, /* Added: phil */
201 root 1.23 {"mapinfo", PC(MapInfoCmd) 0 }, /* CF+ */
202    
203 root 1.28 {"reply", SC(ReplyCmd) 0 },
204 root 1.26 {"exti", SC(ExtiCmd) 0 }, /* CF+ */
205     {"addme", SC(AddMeCmd) 0 },
206     {"askface", SC(SendFaceCmd) 0 }, /* Added: phil */
207     {"requestinfo", SC(RequestInfo) 0 },
208     {"setfacemode", SC(SetFaceMode) 0 },
209     {"setsound", SC(SetSound) 0 },
210     {"setup", SC(SetUp) 0 },
211     {"version", SC(VersionCmd) 0 },
212     {"toggleextendedinfos", SC(ToggleExtendedInfos) 0 }, /*Added: tchize */
213     {"toggleextendedtext", SC(ToggleExtendedText) 0 }, /*Added: tchize */
214     {"asksmooth", SC(AskSmooth) 0 }, /*Added: tchize (smoothing technologies) */
215 root 1.23 };
216    
217     bool
218     client::may_execute (const packet_type *pkt) const
219 elmex 1.1 {
220 root 1.23 return (!(pkt->flags & PF_PLAYER) || pl)
221 root 1.29 && (!(pkt->flags & PF_PLAYING) || state == ST_PLAYING);
222 root 1.11 }
223 root 1.9
224 root 1.23 void
225     client::execute (const packet_type *pkt, char *data, int datalen)
226 root 1.11 {
227 root 1.23 if (may_execute (pkt))
228     {
229     //TODO: only one format
230     if (pkt->flags & PF_PLAYER)
231     ((void (*)(char *, int, player *))pkt->cb)((char *)data, datalen, pl);
232     else
233     ((void (*)(char *, int, client *))pkt->cb)((char *)data, datalen, this);
234     }
235     else
236     send_packet_printf ("drawinfo %d ERROR: you cannot execute '%s' now.", NDI_RED, pkt->name);
237 elmex 1.1 }
238    
239 root 1.23 bool
240     client::handle_packet ()
241 root 1.10 {
242 root 1.23 int pkt_len = next_packet ();
243    
244     if (!pkt_len)
245     return false;
246     else if (pkt_len < 0)
247     {
248     LOG (llevError, "read error on player %s\n",
249     pl && pl->ob ? &pl->ob->name : "[anonymous]");
250     destroy ();
251     return false;
252     }
253    
254     inbuf [pkt_len] = 0; /* Terminate buffer - useful for string data */
255    
256     /* First, break out beginning word. There are at least
257     * a few commands that do not have any paremeters. If
258     * we get such a command, don't worry about trying
259     * to break it up.
260     */
261     int datalen;
262     char *data = strchr ((char *)inbuf + 2, ' ');
263    
264     if (data)
265     {
266     *data++ = 0;
267     datalen = pkt_len - (data - (char *)inbuf);
268     }
269     else
270     {
271     data = (char *)inbuf + 2; // better read garbage than segfault
272     datalen = 0;
273     }
274 root 1.15
275 root 1.23 for (packet_type *pkt = packets; pkt < packets + (sizeof (packets) / sizeof (packets[0])); ++pkt)
276     if (!strcmp ((char *)inbuf + 2, pkt->name))
277     {
278 root 1.26 if (pkt->flags & PF_PLAYER)
279     queue_command (pkt, data, datalen);
280     else
281 root 1.23 execute (pkt, data, datalen);
282    
283     goto next_packet;
284     }
285    
286 root 1.26 // If we get here, we didn't find a valid command.
287 root 1.23 send_packet_printf ("drawinfo %d ERROR: command '%s' not supported.", NDI_RED, (char *)inbuf + 2);
288     next_packet:
289     skip_packet (pkt_len);
290 root 1.11
291 root 1.23 // input buffer has space again
292     socket_ev.poll (socket_ev.poll () | PE_R);
293 root 1.10
294 root 1.23 return true;
295 root 1.10 }
296    
297 root 1.23 // callback called when socket is either readable or writable
298     void
299     client::socket_cb (iow &w, int got)
300     {
301     //TODO remove when we have better socket cleanup logic
302 root 1.29 if (destroyed ())
303 root 1.23 {
304     socket_ev.poll (0);
305     return;
306     }
307 elmex 1.1
308 root 1.23 if (got & PE_W)
309 root 1.5 {
310 root 1.23 write_outputbuffer ();
311    
312     if (!outputbuffer.len)
313     socket_ev.poll (socket_ev.poll () & ~PE_W);
314     }
315 root 1.12
316 root 1.23 if (got & PE_R)
317     {
318     //TODO: rate-limit tcp connection in better ways, important
319 pippijn 1.8
320 root 1.12 int amount = sizeof (inbuf) - inbuf_len;
321    
322 root 1.23 if (!amount)
323 root 1.5 {
324 root 1.23 // input buffer full
325     socket_ev.poll (socket_ev.poll () & ~PE_R);
326     return;
327 root 1.3 }
328 elmex 1.1
329 root 1.12 amount = read (fd, inbuf + inbuf_len, amount);
330    
331     if (!amount)
332 root 1.5 {
333 root 1.23 destroy ();
334     return;
335 root 1.5 }
336 root 1.12 else if (amount < 0)
337 root 1.5 {
338 root 1.12 if (errno != EAGAIN && errno != EINTR)
339 root 1.5 {
340 root 1.20 LOG (llevError, "read error: %s\n", strerror (errno));
341 root 1.23 destroy ();
342     return;
343 root 1.3 }
344 root 1.12
345 root 1.23 // should not be here, normally
346 root 1.3 }
347 root 1.23 else
348     {
349     inbuf_len += amount;
350 root 1.12
351 root 1.23 cst_tot.ibytes += amount;
352     cst_lst.ibytes += amount;
353 root 1.12
354 root 1.23 cmd_ev.start ();
355     }
356 root 1.5 }
357 root 1.12 }
358    
359 root 1.23 // called whenever we have additional commands to process
360 root 1.12 void
361 root 1.23 client::cmd_cb (iw &w)
362 root 1.12 {
363 root 1.26 if (handle_packet ())
364 root 1.23 w.start ();
365     else
366     flush ();
367 elmex 1.1 }
368    
369     /*******************************************************************************
370     *
371     * Start of write related routines.
372     *
373     ******************************************************************************/
374    
375     /**
376     * Adds data to a socket buffer for whatever reason.
377     *
378     * ns is the socket we are adding the data to, buf is the start of the
379     * data, and len is the number of bytes to add.
380     */
381 root 1.20 void
382 root 1.22 client::send (void *buf_, int len)
383 root 1.20 {
384     char *buf = (char *)buf_;
385     char *pos = buf;
386     int amt = 0;
387 elmex 1.1
388 root 1.29 if (destroyed () || !buf)
389 root 1.23 return;
390 elmex 1.1
391 root 1.20 if ((len + outputbuffer.len) > SOCKETBUFSIZE)
392 root 1.5 {
393 root 1.23 LOG (llevDebug, "socket on fd %d has overrun internal buffer - marking as dead\n", fd);
394     destroy ();
395 root 1.5 return;
396     }
397    
398 root 1.20 int avail, end;
399    
400 root 1.5 /* data + end is where we start putting the new data. The last byte
401     * currently in use is actually data + end -1
402     */
403 root 1.20 end = outputbuffer.start + outputbuffer.len;
404 root 1.5 /* The buffer is already in a wrapped state, so adjust end */
405     if (end >= SOCKETBUFSIZE)
406     end -= SOCKETBUFSIZE;
407 root 1.7
408 root 1.5 avail = SOCKETBUFSIZE - end;
409 elmex 1.1
410 root 1.5 /* We can all fit it behind the current data without wrapping */
411     if (avail >= len)
412 root 1.20 memcpy (outputbuffer.data + end, buf, len);
413 root 1.5 else
414     {
415 root 1.20 memcpy (outputbuffer.data + end, buf, avail);
416     memcpy (outputbuffer.data, buf + avail, len - avail);
417 elmex 1.1 }
418 root 1.7
419 root 1.20 outputbuffer.len += len;
420 elmex 1.1 }
421    
422     /**
423     * Takes a string of data, and writes it out to the socket. A very handy
424     * shortcut function.
425     */
426 root 1.13 void
427 root 1.22 client::send_packet (packet &sl)
428 root 1.13 {
429 root 1.29 if (destroyed ())
430 root 1.19 return;
431    
432     if (sl.length () >= MAXSOCKBUF)
433     {
434     LOG (llevError, "Trying to send a buffer beyond properly size, len =%d\n", sl.length ());
435     /* Almost certainly we've overflowed a buffer, so quit now to make
436     * it easier to debug.
437     */
438     abort ();
439     }
440    
441     if (!sl.length ())
442     return;
443    
444     assert (sl.hdrlen == 2);
445    
446     sl.buf_ [0] = sl.length () >> 8;
447     sl.buf_ [1] = sl.length () ;
448    
449     send (sl.buf_, sl.length () + sl.hdrlen);
450 root 1.13 }
451    
452 root 1.5 void
453 root 1.22 client::send_packet (const char *buf, int len)
454 elmex 1.1 {
455 root 1.14 packet sl;
456 elmex 1.1
457 root 1.13 sl << data (buf, len);
458     send_packet (sl);
459 elmex 1.1 }
460    
461 root 1.13 void
462 root 1.22 client::send_packet (const char *buf)
463 root 1.13 {
464     send_packet (buf, strlen (buf));
465     }
466 elmex 1.1
467 root 1.23 void
468     client::send_packet_printf (const char *format, ...)
469     {
470     packet sl;
471    
472     va_list ap;
473     va_start (ap, format);
474     sl.vprintf (format, ap);
475     va_end (ap);
476    
477     send_packet (sl);
478     }
479    
480     /***********************************************************************
481     *
482     * packet functions/utilities
483     *
484     **********************************************************************/
485    
486 root 1.27 packet::packet (const char *name)
487     {
488     reset ();
489    
490     int len = strlen (name);
491     memcpy (cur, name, len); cur += len;
492     *cur++ = ' ';
493     }
494    
495 root 1.23 packet &packet::operator <<(const data &v)
496     {
497     if (room () < v.len)
498     reset ();
499     else
500     {
501     if (v.len)
502     {
503     memcpy (cur, v.ptr, v.len);
504     cur += v.len;
505     }
506     }
507    
508     return *this;
509     }
510    
511     packet &packet::operator <<(const data8 &v)
512     {
513     unsigned int len = min (v.len, 0x00FF);
514     return *this << uint8 (len) << data (v.ptr, len);
515     }
516    
517     packet &packet::operator <<(const data16 &v)
518     {
519     unsigned int len = min (v.len, 0xFFFF);
520     return *this << uint16 (len) << data (v.ptr, len);
521     }
522    
523     packet &packet::operator <<(const char *v)
524     {
525     return *this << data (v, strlen (v ? v : 0));
526     }
527    
528     void
529     packet::vprintf (const char *format, va_list ap)
530     {
531     int size = room ();
532    
533     int len = vsnprintf ((char *)cur, size, format, ap);
534    
535     if (len >= size)
536     return reset ();
537    
538     cur += len;
539     }
540    
541 elmex 1.1 /******************************************************************************
542     *
543     * statistics logging functions.
544     *
545     ******************************************************************************/
546    
547     #ifdef CS_LOGSTATS
548 root 1.5
549 elmex 1.1 /* cst_tot is for the life of the server, cst_last is for the last series of
550     * stats
551     */
552     CS_Stats cst_tot, cst_lst;
553    
554     /**
555     * Writes out the gathered stats. We clear cst_lst.
556     */
557 root 1.5 void
558     write_cs_stats (void)
559 elmex 1.1 {
560 root 1.5 time_t now = time (NULL);
561 elmex 1.1
562 root 1.5 /* If no connections recently, don't both to log anything */
563     if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
564     return;
565    
566     /* CSSTAT is put in so scripts can easily find the line */
567     LOG (llevInfo, "CSSTAT: %.16s tot %d %d %d %d inc %d %d %d %d\n",
568     ctime (&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
569     now - cst_tot.time_start, cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, now - cst_lst.time_start);
570     cst_lst.ibytes = 0;
571     cst_lst.obytes = 0;
572     cst_lst.time_start = now;
573 elmex 1.1 }
574     #endif
575 root 1.9