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