1 /*
2 * net_tele.c - telegrams for cleint/server communication
3 *
4 * $Id: net_tele.c,v 1.11 2006/02/09 21:21:24 fzago Exp $
5 *
6 * Program XBLAST
7 * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2; or (at your option)
12 * any later version
13 *
14 * This program is distributed in the hope that it will be entertaining,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "xblast.h"
25
26 /*
27 * local macros
28 */
29 #define MAX_HEADER_SIZE 5
30 #define MAX_DATA_SIZE 255
31 #define MAX_TAIL_SIZE 1
32 #define MAX_TOTAL_SIZE (MAX_HEADER_SIZE + MAX_DATA_SIZE + MAX_TAIL_SIZE)
33
34 #define START_BYTE 0x68
35 #define STOP_BYTE 0x16
36
37 /*
38 * local types
39 */
40 typedef int (*WriteFunc) (const XBSocket *, const void *, size_t);
41 typedef int (*ReadFunc) (const XBSocket *, void *, size_t);
42
43 /* details the parse state of a telegram */
44 typedef enum
45 {
46 PS_Start,
47 PS_Len,
48 PS_COT,
49 PS_ID,
50 PS_IOB,
51 PS_Data,
52 PS_Stop
53 } ParseState;
54
55 /* extracted telegram information */
56 struct _xb_telegram
57 {
58 XBTeleCOT cot;
59 XBTeleID id;
60 unsigned char iob;
61 void *data;
62 size_t len;
63 XBTelegram *next;
64 };
65
66 /* telegram queue */
67 typedef struct _xb_any_queue
68 {
69 XBTelegram *first;
70 XBTelegram *last;
71 } XBAnyQueue;
72
73 /* for sending */
74 struct _xb_snd_queue
75 {
76 XBAnyQueue any;
77 size_t wIndex; /* next byte to write */
78 size_t wLen; /* bytes to write */
79 char wBuf[MAX_TOTAL_SIZE]; /* data to write */
80 };
81
82 /* for receiving */
83 struct _xb_rcv_queue
84 {
85 XBAnyQueue any;
86 size_t rIndex; /* next byte to read */
87 size_t rLen; /* length to be read */
88 XBBool rFlag; /* flag to read more data */
89 ParseState rState; /* current state of reading */
90 size_t dCount; /* length of read data block */
91 char rBuf[MAX_TOTAL_SIZE]; /* buffer read from socket */
92 char tBuf[MAX_TOTAL_SIZE]; /* extracted telegram */
93 };
94
95 /*------------------------------------------------------------------------*
96 *
97 * Debugoutput
98 *
99 *------------------------------------------------------------------------*/
100
101 #ifdef DEBUG_TELE
102
103 /*
104 * translate COT codes
105 */
106 const char *
StringCOT(XBTeleCOT cot)107 StringCOT (XBTeleCOT cot)
108 {
109 switch (cot) {
110 case XBT_COT_Activate:
111 return "act";
112 case XBT_COT_Spontaneous:
113 return "spont";
114 case XBT_COT_SendData:
115 return "snd_dat";
116 case XBT_COT_RequestData:
117 return "req_dat";
118 case XBT_COT_DataNotAvailable:
119 return "dat_not";
120 case XBT_COT_DataAvailable:
121 return "dat_ava";
122 default:
123 return "???";
124 }
125 } /* StringCOT */
126
127 /*
128 * translate ID codes
129 */
130 const char *
StringID(XBTeleID id)131 StringID (XBTeleID id)
132 {
133 switch (id) {
134 case XBT_ID_GameConfig:
135 return "gam_cfg";
136 case XBT_ID_PlayerConfig:
137 return "plr_cfg";
138 case XBT_ID_RequestDisconnect:
139 return "req_dis";
140 case XBT_ID_HostDisconnected:
141 return "hst_dis";
142 case XBT_ID_StartGame:
143 return "sta_gam";
144 case XBT_ID_RandomSeed:
145 return "rnd_sed";
146 case XBT_ID_LevelConfig:
147 return "lvl_cfg";
148 case XBT_ID_DgramPort:
149 return "dgm_prt";
150 case XBT_ID_Sync:
151 return "sync";
152 case XBT_ID_HostIsIn:
153 return "hst_in";
154 case XBT_ID_HostIsOut:
155 return "hst_out";
156 case XBT_ID_TeamChange:
157 return "tm_cng";
158 case XBT_ID_GameStat:
159 return "gm_stat";
160 case XBT_ID_PID:
161 , return "pid";
162 case XBT_ID_WinnerTeam:
163 , return "win_tm";
164 case XBT_ID_Async:
165 return "async";
166 case XBT_ID_Chat:
167 return "chat";
168 case XBT_ID_HostChange:
169 return "hst_chg";
170 case XBT_ID_HostChangeReq:
171 return "hst_req";
172 case XBT_ID_TeamChangeReq:
173 return "tm_req";
174 default:
175 return "???";
176 }
177 } /* StringID */
178
179 /*
180 * output telegram
181 */
182 static void
DebugTelegram(FILE * fout,const XBTelegram * tele)183 DebugTelegram (FILE * fout, const XBTelegram * tele)
184 {
185 fprintf (fout, "%s %s %03u (%03u Bytes)",
186 StringID (tele->id), StringCOT (tele->cot), (unsigned)tele->iob, (unsigned)tele->len);
187 if (tele->len > 0) {
188 size_t i;
189 const char *s = tele->data;
190 fputs (" \"", fout);
191 for (i = 0; i < tele->len; i++) {
192 if (isprint (s[i])) {
193 fputc (s[i], fout);
194 }
195 else {
196 fprintf (fout, "\\%03o", (unsigned)s[i]);
197 }
198 }
199 fputc ('\"', fout);
200 }
201 } /* DebugTelegram */
202
203 #endif
204
205 /*------------------------------------------------------------------------*
206 *
207 * XBTelegram
208 *
209 *------------------------------------------------------------------------*/
210
211 /*
212 * create a new telegram from given data
213 */
214 XBTelegram *
Net_CreateTelegram(XBTeleCOT cot,XBTeleID id,XBTeleIOB iob,const void * buf,size_t len)215 Net_CreateTelegram (XBTeleCOT cot, XBTeleID id, XBTeleIOB iob, const void *buf, size_t len)
216 {
217 XBTelegram *tele;
218 assert (len < MAX_DATA_SIZE);
219 tele = calloc (1, sizeof (XBTelegram));
220 assert (tele != NULL);
221 tele->cot = cot;
222 tele->id = id;
223 tele->iob = iob;
224 /* copy buffer if needed */
225 if (buf != NULL && len != 0) {
226 tele->len = len;
227 tele->data = malloc (len);
228 assert (tele->data != NULL);
229 memcpy (tele->data, buf, len);
230 }
231 return tele;
232 } /* Net_CreateTelegram */
233
234 /*
235 * delete a telegram
236 */
237 void
Net_DeleteTelegram(XBTelegram * tele)238 Net_DeleteTelegram (XBTelegram * tele)
239 {
240 assert (tele != NULL);
241 if (NULL != tele->data) {
242 free (tele->data);
243 }
244 free (tele);
245 } /* Net_DeleteTelegram */
246
247 /*
248 * write telegram to a given buffer, return size
249 */
250 static size_t
WriteTelegram(const XBTelegram * tele,char * buf)251 WriteTelegram (const XBTelegram * tele, char *buf)
252 {
253 char *ptr = buf;
254 /* write header */
255 *ptr++ = START_BYTE;
256 *ptr++ = tele->len;
257 *ptr++ = (char)tele->cot;
258 *ptr++ = (char)tele->id;
259 *ptr++ = (char)tele->iob;
260 /* write data */
261 assert (tele->len < MAX_DATA_SIZE);
262 if (tele->len > 0) {
263 assert (tele->data != NULL);
264 memcpy (ptr, tele->data, tele->len);
265 ptr += tele->len;
266 }
267 /* write tail */
268 *ptr++ = STOP_BYTE;
269 /* return len */
270 return ptr - buf;
271 } /* WriteTelegram */
272
273 /*------------------------------------------------------------------------*
274 *
275 * XBAnyQueue
276 *
277 *------------------------------------------------------------------------*/
278
279 /*
280 * delete a queue completely
281 */
282 static void
DeleteAnyQueue(XBAnyQueue * list)283 DeleteAnyQueue (XBAnyQueue * list)
284 {
285 XBTelegram *teleNext;
286 for (; list->first != NULL; list->first = teleNext) {
287 teleNext = list->first->next;
288 Net_DeleteTelegram (list->first);
289 }
290 } /* DeleteAnyQueue */
291
292 /*
293 * add telegram to to a queue
294 */
295 static void
AddTelegram(XBAnyQueue * list,XBTelegram * tele)296 AddTelegram (XBAnyQueue * list, XBTelegram * tele)
297 {
298 assert (list != NULL);
299 assert (tele != NULL);
300 if (NULL == list->last) {
301 list->first = tele;
302 }
303 else {
304 list->last->next = tele;
305 }
306 list->last = tele;
307 #ifdef DEBUG_TELE
308 fputs ("add tele:", stderr);
309 DebugTelegram (stderr, tele);
310 fputc ('\n', stderr);
311 #endif
312 } /* AddTelegram */
313
314 /*
315 * get first telegram from queue
316 */
317 static XBTelegram *
GetTelegram(XBAnyQueue * list)318 GetTelegram (XBAnyQueue * list)
319 {
320 XBTelegram *tele;
321 assert (list != NULL);
322 /* first element from list */
323 tele = list->first;
324 /* update list if needed */
325 if (list->first != NULL) {
326 list->first = list->first->next;
327 }
328 if (list->first == NULL) {
329 list->last = NULL;
330 }
331 return tele;
332 } /* GetTelegram */
333
334 /*------------------------------------------------------------------------*
335 *
336 * XBSndQueue
337 *
338 *------------------------------------------------------------------------*/
339
340 /*
341 * create a snd queue
342 */
343 XBSndQueue *
Net_CreateSndQueue(XBBool server)344 Net_CreateSndQueue (XBBool server)
345 {
346 XBSndQueue *list = calloc (1, sizeof (XBSndQueue));
347 assert (list != NULL);
348 return list;
349 } /* Net_CreateSndQueue */
350
351 /*
352 * delete a sndqueue
353 */
354 void
Net_DeleteSndQueue(XBSndQueue * list)355 Net_DeleteSndQueue (XBSndQueue * list)
356 {
357 DeleteAnyQueue (&list->any);
358 free (list);
359 } /* Net_DeleteSndQueue */
360
361 /*
362 * write a first telegram fro queue to socket
363 */
364 XBTeleResult
Net_Send(XBSndQueue * list,const XBSocket * pSocket)365 Net_Send (XBSndQueue * list, const XBSocket * pSocket)
366 {
367 int result;
368 XBTelegram *tele;
369 assert (list != NULL);
370 /* check if new telegram must be written to buffer */
371 if (0 == list->wLen && NULL != (tele = GetTelegram (&list->any))) {
372 #ifdef DEBUG_TELE
373 Dbg_Out ("wrt tele %d (%03u bytes)\n", Socket_Fd (pSocket), tele->len);
374 #endif
375 list->wLen = WriteTelegram (tele, list->wBuf);
376 list->wIndex = 0;
377 Net_DeleteTelegram (tele);
378 }
379 /* check if any unwritten bytes are left in the buffer */
380 if (list->wIndex < list->wLen) {
381 result = Socket_Send (pSocket, list->wBuf + list->wIndex, list->wLen - list->wIndex);
382 switch (result) {
383 case XB_SOCKET_ERROR:
384 Dbg_Out ("ERROR while writing\n");
385 return XBT_R_IOError;
386 case XB_SOCKET_END_OF_FILE:
387 Dbg_Out ("END_OF_FILE while writing\n");
388 return XBT_R_IOError;
389 case XB_SOCKET_WOULD_BLOCK:
390 return XBT_R_Continue;
391 default:
392 break;
393 }
394 list->wIndex += result;
395 /* mark as complete send */
396 if (list->wIndex == list->wLen) {
397 list->wLen = list->wIndex = 0;
398 }
399 }
400 /* check if we need to call write again */
401 if (list->wLen > 0 || list->any.first != NULL) {
402 return XBT_R_Continue;
403 }
404 else {
405 return XBT_R_Complete;
406 }
407 } /* Net_Send */
408
409 /*
410 * add a telegram to the send queue
411 */
412 void
Net_SendTelegram(XBSndQueue * list,XBTelegram * tele)413 Net_SendTelegram (XBSndQueue * list, XBTelegram * tele)
414 {
415 #ifdef DEBUG_TELE
416 Dbg_Out (" > snd ");
417 #endif
418 AddTelegram (&list->any, tele);
419 } /* Net_SendTelegram */
420
421 /*------------------------------------------------------------------------*
422 *
423 * XBRcvQueue
424 *
425 *------------------------------------------------------------------------*/
426
427 /*
428 * create rcv queue
429 */
430 XBRcvQueue *
Net_CreateRcvQueue(XBBool server)431 Net_CreateRcvQueue (XBBool server)
432 {
433 XBRcvQueue *list = calloc (1, sizeof (XBRcvQueue));
434 assert (list != NULL);
435 list->rState = PS_Start;
436 list->rFlag = XBTrue;
437 return list;
438 } /* Net_CreateRcvQueue */
439
440 /*
441 * delete rcv queue
442 */
443 void
Net_DeleteRcvQueue(XBRcvQueue * list)444 Net_DeleteRcvQueue (XBRcvQueue * list)
445 {
446 DeleteAnyQueue (&list->any);
447 free (list);
448 } /* Net_DeleteRcvQueue */
449
450 /*
451 * fill read buffer from socket
452 */
453 static XBTeleResult
ReadBuffer(XBRcvQueue * list,const XBSocket * pSocket)454 ReadBuffer (XBRcvQueue * list, const XBSocket * pSocket)
455 {
456 #ifdef W32
457 long result;
458 #else
459 ssize_t result;
460 #endif
461 result = Socket_Receive (pSocket, list->rBuf, MAX_TOTAL_SIZE);
462 switch (result) {
463 case XB_SOCKET_ERROR:
464 return XBT_R_IOError;
465 case XB_SOCKET_END_OF_FILE:
466 return XBT_R_EndOfFile;
467 case XB_SOCKET_WOULD_BLOCK:
468 result = 0;
469 break;
470 }
471 list->rLen = result;
472 list->rIndex = 0;
473 return XBT_R_Continue;
474 } /* ReadBuffer */
475
476 /*
477 * fetch byte from internal read buffer
478 */
479 static XBBool
GetByte(XBRcvQueue * list,size_t tIndex)480 GetByte (XBRcvQueue * list, size_t tIndex)
481 {
482 if (list->rIndex < list->rLen) {
483 list->tBuf[tIndex] = list->rBuf[list->rIndex++];
484 return XBTrue;
485 }
486 else {
487 return XBFalse;
488 }
489 } /* GetByte */
490
491 /*
492 * read telegram data from socket
493 */
494 XBTeleResult
Net_Receive(XBRcvQueue * list,const XBSocket * pSocket)495 Net_Receive (XBRcvQueue * list, const XBSocket * pSocket)
496 {
497 size_t i, len;
498 XBTelegram *tele;
499 assert (list != NULL);
500 /* check if more input is needed */
501 if (list->rFlag) {
502 XBTeleResult result;
503 /* try tp read from socket into buffer */
504 if (XBT_R_Continue != (result = ReadBuffer (list, pSocket))) {
505 return result;
506 }
507 /* assume we will complete the telegram */
508 list->rFlag = XBFalse;
509 }
510 /* parse read buffer until empty or parse error */
511 while (1) {
512 switch (list->rState) {
513 case PS_Start: /* we need a start byte */
514
515 do {
516 /* try to get first start byte in buffer */
517 if (!GetByte (list, 0)) {
518 /* failed, mark for input and return */
519 list->rFlag = XBTrue;
520 return XBT_R_Continue;
521 }
522 #ifdef DEBUG_TELE
523 fputc ('{', stderr);
524 #endif
525 } while (list->tBuf[0] != START_BYTE);
526 /* restart loop needing length byte */
527 list->rState = PS_Len;
528 break;
529 case PS_Len: /* we need a length byte */
530 /* try to get len byte */
531 if (!GetByte (list, 1)) {
532 /* failed, mark for more input and return */
533 list->rFlag = XBTrue;
534 return XBT_R_Continue;
535 }
536 #ifdef DEBUG_TELE
537 fputc ('L', stderr);
538 #endif
539 /* restart loop needing COT */
540 list->rState = PS_COT;
541 break;
542 case PS_COT: /* we need COT */
543 /* try to read COT */
544 if (!GetByte (list, 2)) {
545 /* failed, mark for more input and return */
546 list->rFlag = XBTrue;
547 return XBT_R_Continue;
548 }
549 #ifdef DEBUG_TELE
550 fputc ('C', stderr);
551 #endif
552 /* need signal id now */
553 list->rState = PS_ID;
554 break;
555 case PS_ID: /* need signal id */
556 /* try to read id byte */
557 if (!GetByte (list, 3)) {
558 /* failed, mark for more input and return */
559 list->rFlag = XBTrue;
560 return XBT_R_Continue;
561 }
562 #ifdef DEBUG_TELE
563 fputc ('I', stderr);
564 #endif
565 /* need IOB now */
566 list->rState = PS_IOB;
567 break;
568 case PS_IOB: /* need IOB */
569 /* try to get IOB byte */
570 if (!GetByte (list, 4)) {
571 /* failed, mark for more input and return */
572 list->rFlag = XBTrue;
573 return XBT_R_Continue;
574 }
575 #ifdef DEBUG_TELE
576 fputc ('i', stderr);
577 #endif
578 /* need data now, no data so far */
579 list->rState = PS_Data;
580 list->dCount = 0;
581 break;
582 case PS_Data: /* need data */
583 /* data length needed */
584 len = list->tBuf[1];
585 /* try to read data byte by byte */
586 for (i = list->dCount; i < len; i++) {
587 /* try to get it */
588 if (!GetByte (list, MAX_HEADER_SIZE + i)) {
589 /* failed, mark for more input, update data count so far and return */
590 list->rFlag = XBTrue;
591 list->dCount = i;
592 return XBT_R_Continue;
593 }
594 #ifdef DEBUG_TELE
595 fputc ('.', stderr);
596 #endif
597 }
598 #ifdef DEBUG_TELE
599 fputc ('D', stderr);
600 #endif
601 /* need stop now */
602 list->rState = PS_Stop;
603 break;
604 case PS_Stop: /* need stop */
605 /* try to read stop byte */
606 len = list->tBuf[1];
607 if (!GetByte (list, MAX_HEADER_SIZE + len)) {
608 /* failed, mark for more input and return */
609 list->rFlag = XBTrue;
610 return XBT_R_Continue;
611 }
612 /* we will need start byte next, in any case */
613 list->rState = PS_Start;
614 /* check stop byte */
615 if (STOP_BYTE != list->tBuf[MAX_HEADER_SIZE + len]) {
616 /* wrong byte, return parse error */
617 return XBT_R_TeleError;
618 }
619 #ifdef DEBUG_TELE
620 Dbg_Out ("}\n");
621 #endif
622 /* telegram is now complete, create the structurre */
623 tele =
624 Net_CreateTelegram (list->tBuf[2], list->tBuf[3], list->tBuf[4],
625 list->tBuf + MAX_HEADER_SIZE, len);
626 assert (NULL != tele);
627 #ifdef DEBUG_TELE
628 Dbg_Out (" < rcv ");
629 #endif
630 /* add it to the queue */
631 AddTelegram (&list->any, tele);
632 /* continue to parse buffer for more telegrams */
633 }
634 }
635 return XBT_R_TeleError;
636 } /* Net_Receive */
637
638 /*
639 * get first telegram in receive queue
640 */
641 XBTelegram *
Net_ReceiveTelegram(XBRcvQueue * list)642 Net_ReceiveTelegram (XBRcvQueue * list)
643 {
644 return GetTelegram (&list->any);
645 } /* Net_ReceiveTelegram */
646
647 /*
648 * get cause of telegram transmission
649 */
650 XBTeleCOT
Net_TeleCOT(const XBTelegram * tele)651 Net_TeleCOT (const XBTelegram * tele)
652 {
653 assert (tele != NULL);
654 return tele->cot;
655 } /* Net_TeleCOT */
656
657 /*
658 * get id of telegram
659 */
660 XBTeleID
Net_TeleID(const XBTelegram * tele)661 Net_TeleID (const XBTelegram * tele)
662 {
663 assert (tele != NULL);
664 return tele->id;
665 } /* Net_TeleID */
666
667 /*
668 * get iob of telegram
669 */
670 XBTeleIOB
Net_TeleIOB(const XBTelegram * tele)671 Net_TeleIOB (const XBTelegram * tele)
672 {
673 assert (tele != NULL);
674 return tele->iob;
675 } /* Net_TeleIOB */
676
677 /*
678 * get telegram data
679 */
680 const void *
Net_TeleData(const XBTelegram * tele,size_t * len)681 Net_TeleData (const XBTelegram * tele, size_t * len)
682 {
683 assert (tele != NULL);
684 assert (len != NULL);
685 *len = tele->len;
686 return tele->data;
687 } /* Net_TeleData */
688
689 /*
690 * end of file net_tele.c
691 */
692