1 /*
2 * SpanDSP - a series of DSP components for telephony
3 *
4 * t38_core.c - Encode and decode the ASN.1 of a T.38 IFP message
5 *
6 * Written by Steve Underwood <steveu@coppice.org>
7 *
8 * Copyright (C) 2005, 2006 Steve Underwood
9 *
10 * All rights reserved.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 2.1,
14 * as published by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26 /*! \file */
27
28 #if defined(HAVE_CONFIG_H)
29 #include "config.h"
30 #endif
31
32 #include <inttypes.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <time.h>
37 #include <string.h>
38 #if defined(HAVE_TGMATH_H)
39 #include <tgmath.h>
40 #endif
41 #if defined(HAVE_MATH_H)
42 #include <math.h>
43 #endif
44 #if defined(HAVE_STDBOOL_H)
45 #include <stdbool.h>
46 #else
47 #include "spandsp/stdbool.h"
48 #endif
49 #include "floating_fudge.h"
50 #include <assert.h>
51 #include <memory.h>
52 #include <tiffio.h>
53
54 #include "spandsp/telephony.h"
55 #include "spandsp/alloc.h"
56 #include "spandsp/logging.h"
57 #include "spandsp/bit_operations.h"
58 #include "spandsp/t38_core.h"
59
60 #include "spandsp/private/logging.h"
61 #include "spandsp/private/t38_core.h"
62
63 #define ACCEPTABLE_SEQ_NO_OFFSET 2000
64
65 /* The times for training, the optional TEP, and the HDLC preamble, for all the modem options, in ms.
66 Note that the preamble for V.21 is 1s+-15%, and for the other modems is 200ms+100ms. */
67 static const struct
68 {
69 int tep;
70 int training;
71 int flags;
72 } modem_startup_time[] =
73 {
74 { 0, 75000, 0}, /* T38_IND_NO_SIGNAL */
75 { 0, 0, 0}, /* T38_IND_CNG */
76 { 0, 3000000, 0}, /* T38_IND_CED */
77 { 0, 0, 1000000}, /* T38_IND_V21_PREAMBLE */ /* TODO: 850ms should be OK for this, but it causes trouble with some ATAs. Why? */
78 { 215000, 943000, 200000}, /* T38_IND_V27TER_2400_TRAINING */
79 { 215000, 708000, 200000}, /* T38_IND_V27TER_4800_TRAINING */
80 { 215000, 234000, 200000}, /* T38_IND_V29_7200_TRAINING */
81 { 215000, 234000, 200000}, /* T38_IND_V29_9600_TRAINING */
82 { 215000, 142000, 200000}, /* T38_IND_V17_7200_SHORT_TRAINING */
83 { 215000, 1393000, 200000}, /* T38_IND_V17_7200_LONG_TRAINING */
84 { 215000, 142000, 200000}, /* T38_IND_V17_9600_SHORT_TRAINING */
85 { 215000, 1393000, 200000}, /* T38_IND_V17_9600_LONG_TRAINING */
86 { 215000, 142000, 200000}, /* T38_IND_V17_12000_SHORT_TRAINING */
87 { 215000, 1393000, 200000}, /* T38_IND_V17_12000_LONG_TRAINING */
88 { 215000, 142000, 200000}, /* T38_IND_V17_14400_SHORT_TRAINING */
89 { 215000, 1393000, 200000}, /* T38_IND_V17_14400_LONG_TRAINING */
90 { 0, 0, 0}, /* T38_IND_V8_ANSAM */
91 { 0, 0, 0}, /* T38_IND_V8_SIGNAL */
92 { 0, 0, 200000}, /* T38_IND_V34_CNTL_CHANNEL_1200 */
93 { 0, 0, 200000}, /* T38_IND_V34_PRI_CHANNEL */
94 { 0, 0, 0}, /* T38_IND_V34_CC_RETRAIN */
95 { 215000, 0, 200000}, /* T38_IND_V33_12000_TRAINING */
96 { 215000, 0, 200000} /* T38_IND_V33_14400_TRAINING */
97 };
98
t38_indicator_to_str(int indicator)99 SPAN_DECLARE(const char *) t38_indicator_to_str(int indicator)
100 {
101 switch (indicator)
102 {
103 case T38_IND_NO_SIGNAL:
104 return "no-signal";
105 case T38_IND_CNG:
106 return "cng";
107 case T38_IND_CED:
108 return "ced";
109 case T38_IND_V21_PREAMBLE:
110 return "v21-preamble";
111 case T38_IND_V27TER_2400_TRAINING:
112 return "v27-2400-training";
113 case T38_IND_V27TER_4800_TRAINING:
114 return "v27-4800-training";
115 case T38_IND_V29_7200_TRAINING:
116 return "v29-7200-training";
117 case T38_IND_V29_9600_TRAINING:
118 return "v29-9600-training";
119 case T38_IND_V17_7200_SHORT_TRAINING:
120 return "v17-7200-short-training";
121 case T38_IND_V17_7200_LONG_TRAINING:
122 return "v17-7200-long-training";
123 case T38_IND_V17_9600_SHORT_TRAINING:
124 return "v17-9600-short-training";
125 case T38_IND_V17_9600_LONG_TRAINING:
126 return "v17-9600-long-training";
127 case T38_IND_V17_12000_SHORT_TRAINING:
128 return "v17-12000-short-training";
129 case T38_IND_V17_12000_LONG_TRAINING:
130 return "v17-12000-long-training";
131 case T38_IND_V17_14400_SHORT_TRAINING:
132 return "v17-14400-short-training";
133 case T38_IND_V17_14400_LONG_TRAINING:
134 return "v17-14400-long-training";
135 case T38_IND_V8_ANSAM:
136 return "v8-ansam";
137 case T38_IND_V8_SIGNAL:
138 return "v8-signal";
139 case T38_IND_V34_CNTL_CHANNEL_1200:
140 return "v34-cntl-channel-1200";
141 case T38_IND_V34_PRI_CHANNEL:
142 return "v34-pri-channel";
143 case T38_IND_V34_CC_RETRAIN:
144 return "v34-CC-retrain";
145 case T38_IND_V33_12000_TRAINING:
146 return "v33-12000-training";
147 case T38_IND_V33_14400_TRAINING:
148 return "v33-14400-training";
149 }
150 /*endswitch*/
151 return "???";
152 }
153 /*- End of function --------------------------------------------------------*/
154
t38_data_type_to_str(int data_type)155 SPAN_DECLARE(const char *) t38_data_type_to_str(int data_type)
156 {
157 switch (data_type)
158 {
159 case T38_DATA_V21:
160 return "v21";
161 case T38_DATA_V27TER_2400:
162 return "v27-2400";
163 case T38_DATA_V27TER_4800:
164 return "v27-4800";
165 case T38_DATA_V29_7200:
166 return "v29-7200";
167 case T38_DATA_V29_9600:
168 return "v29-9600";
169 case T38_DATA_V17_7200:
170 return "v17-7200";
171 case T38_DATA_V17_9600:
172 return "v17-9600";
173 case T38_DATA_V17_12000:
174 return "v17-12000";
175 case T38_DATA_V17_14400:
176 return "v17-14400";
177 case T38_DATA_V8:
178 return "v8";
179 case T38_DATA_V34_PRI_RATE:
180 return "v34-pri-rate";
181 case T38_DATA_V34_CC_1200:
182 return "v34-CC-1200";
183 case T38_DATA_V34_PRI_CH:
184 return "v34-pri-ch";
185 case T38_DATA_V33_12000:
186 return "v33-12000";
187 case T38_DATA_V33_14400:
188 return "v33-14400";
189 }
190 /*endswitch*/
191 return "???";
192 }
193 /*- End of function --------------------------------------------------------*/
194
t38_field_type_to_str(int field_type)195 SPAN_DECLARE(const char *) t38_field_type_to_str(int field_type)
196 {
197 switch (field_type)
198 {
199 case T38_FIELD_HDLC_DATA:
200 return "hdlc-data";
201 case T38_FIELD_HDLC_SIG_END:
202 return "hdlc-sig-end";
203 case T38_FIELD_HDLC_FCS_OK:
204 return "hdlc-fcs-OK";
205 case T38_FIELD_HDLC_FCS_BAD:
206 return "hdlc-fcs-BAD";
207 case T38_FIELD_HDLC_FCS_OK_SIG_END:
208 return "hdlc-fcs-OK-sig-end";
209 case T38_FIELD_HDLC_FCS_BAD_SIG_END:
210 return "hdlc-fcs-BAD-sig-end";
211 case T38_FIELD_T4_NON_ECM_DATA:
212 return "t4-non-ecm-data";
213 case T38_FIELD_T4_NON_ECM_SIG_END:
214 return "t4-non-ecm-sig-end";
215 case T38_FIELD_CM_MESSAGE:
216 return "cm-message";
217 case T38_FIELD_JM_MESSAGE:
218 return "jm-message";
219 case T38_FIELD_CI_MESSAGE:
220 return "ci-message";
221 case T38_FIELD_V34RATE:
222 return "v34rate";
223 }
224 /*endswitch*/
225 return "???";
226 }
227 /*- End of function --------------------------------------------------------*/
228
t38_cm_profile_to_str(int profile)229 SPAN_DECLARE(const char *) t38_cm_profile_to_str(int profile)
230 {
231 switch (profile)
232 {
233 case '1':
234 return "G3 FAX sending terminal";
235 case '2':
236 return "G3 FAX receiving terminal";
237 case '3':
238 return "V.34 HDX and G3 FAX sending terminal";
239 case '4':
240 return "V.34 HDX and G3 FAX receiving terminal";
241 case '5':
242 return "V.34 HDX-only FAX sending terminal";
243 case '6':
244 return "V.34 HDX-only FAX receiving terminal";
245 }
246 /*endswitch*/
247 return "???";
248 }
249 /*- End of function --------------------------------------------------------*/
250
t38_jm_to_str(const uint8_t * data,int len)251 SPAN_DECLARE(const char *) t38_jm_to_str(const uint8_t *data, int len)
252 {
253 if (len < 2)
254 return "???";
255 /*endif*/
256 switch (data[0])
257 {
258 case 'A':
259 switch (data[1])
260 {
261 case '0':
262 return "ACK";
263 }
264 /*endswitch*/
265 break;
266 case 'N':
267 switch (data[1])
268 {
269 case '0':
270 return "NACK: No compatible mode available";
271 case '1':
272 /* Response for profiles 1 and 2 */
273 return "NACK: No V.34 FAX, use G3 FAX";
274 case '2':
275 /* Response for profiles 5 and 6 */
276 return "NACK: V.34 only FAX.";
277 }
278 /*endswitch*/
279 break;
280 }
281 /*endswitch*/
282 return "???";
283 }
284 /*- End of function --------------------------------------------------------*/
285
t38_v34rate_to_bps(const uint8_t * data,int len)286 SPAN_DECLARE(int) t38_v34rate_to_bps(const uint8_t *data, int len)
287 {
288 int i;
289 int rate;
290
291 if (len < 3)
292 return -1;
293 /*endif*/
294 for (i = 0, rate = 0; i < 3; i++)
295 {
296 if (data[i] < '0' || data[i] > '9')
297 return -1;
298 /*endif*/
299 rate = rate*10 + data[i] - '0';
300 }
301 /*endfor*/
302 return rate*100;
303 }
304 /*- End of function --------------------------------------------------------*/
305
classify_seq_no_offset(int expected,int actual)306 static __inline__ int classify_seq_no_offset(int expected, int actual)
307 {
308 /* Classify the mismatch between expected and actual sequence numbers
309 according to whether the actual is a little in the past (late), a
310 little in the future (some packets have been lost), or a large jump
311 that represents the sequence being lost (possibly when some RTP
312 gets dumped to a UDPTL port). */
313 /* This assumes they are not equal */
314 if (expected > actual)
315 {
316 if (expected > actual + 0x10000 - ACCEPTABLE_SEQ_NO_OFFSET)
317 {
318 /* In the near future */
319 return 1;
320 }
321 /*endif*/
322 if (expected < actual + ACCEPTABLE_SEQ_NO_OFFSET)
323 {
324 /* In the recent past */
325 return -1;
326 }
327 /*endif*/
328 }
329 else
330 {
331 if (expected + ACCEPTABLE_SEQ_NO_OFFSET > actual)
332 {
333 /* In the near future */
334 return 1;
335 }
336 /*endif*/
337 if (expected + 0x10000 - ACCEPTABLE_SEQ_NO_OFFSET < actual)
338 {
339 /* In the recent past */
340 return -1;
341 }
342 /*endif*/
343 }
344 /*endif*/
345 /* There has been a huge step in the sequence */
346 return 0;
347 }
348 /*- End of function --------------------------------------------------------*/
349
t38_core_rx_ifp_stream(t38_core_state_t * s,const uint8_t * buf,int len,uint16_t log_seq_no)350 SPAN_DECLARE(int) t38_core_rx_ifp_stream(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t log_seq_no)
351 {
352 int i;
353 int t30_indicator;
354 int t30_data;
355 int prev_ptr;
356 int ptr;
357 int other_half;
358 int numocts;
359 int pkt_len;
360 int ret;
361 const uint8_t *msg;
362 unsigned int count;
363 unsigned int t30_field_type;
364 uint8_t type;
365 uint8_t data_field_present;
366 uint8_t field_data_present;
367 char tag[20];
368
369 if (span_log_test(&s->logging, SPAN_LOG_FLOW))
370 {
371 sprintf(tag, "Rx %5d: IFP", log_seq_no);
372 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, buf, len);
373 }
374 /*endif*/
375 ptr = 0;
376 pkt_len = len;
377 switch (s->data_transport_protocol)
378 {
379 case T38_TRANSPORT_TCP:
380 /* We don't know the actual packet length, so treat everythign we have as the packet */
381 ret = 0;
382 break;
383 case T38_TRANSPORT_TCP_TPKT:
384 if (len >= 4)
385 {
386 /* Version */
387 if (buf[0] != 3)
388 return -1;
389 /*endif*/
390 /* Reserved */
391 if (buf[1] != 0)
392 return -1;
393 /*endif*/
394 /* Packet length - this includes the length of the header itself */
395 pkt_len = (buf[2] << 8) | buf[3];
396 if (len < pkt_len)
397 return 0;
398 /*endif*/
399 ptr = 4;
400 }
401 /*endif*/
402 ret = -1;
403 break;
404 default:
405 /* We know the actual packet length, and its the exact length of what we were passed. */
406 ret = -1;
407 break;
408 }
409 /*endswitch*/
410 if ((ptr + 1) > pkt_len)
411 return ret;
412 /*endif*/
413 data_field_present = buf[ptr] & 0x80;
414 type = (buf[ptr] >> 6) & 1;
415 switch (type)
416 {
417 case T38_TYPE_OF_MSG_T30_INDICATOR:
418 /* Indicators should never have a data field */
419 if (data_field_present)
420 {
421 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Data field with indicator\n", log_seq_no);
422 return -1;
423 }
424 /*endif*/
425 /* Any received indicator should mean we no longer have a valid concept of "last received data/field type". */
426 s->current_rx_data_type = -1;
427 s->current_rx_field_type = -1;
428 if ((buf[ptr] & 0x20))
429 {
430 /* Extension */
431 if ((ptr + 2) > pkt_len)
432 return ret;
433 /*endif*/
434 t30_indicator = T38_IND_V8_ANSAM + (((buf[ptr] << 2) & 0x3C) | ((buf[ptr + 1] >> 6) & 0x3));
435 if (t30_indicator > T38_IND_V33_14400_TRAINING)
436 {
437 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown indicator - %d\n", log_seq_no, t30_indicator);
438 return -1;
439 }
440 /*endif*/
441 ptr += 2;
442 }
443 else
444 {
445 t30_indicator = (buf[ptr] >> 1) & 0xF;
446 ptr += 1;
447 }
448 /*endif*/
449 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: indicator %s\n", log_seq_no, t38_indicator_to_str(t30_indicator));
450 s->rx_indicator_handler(s, s->rx_user_data, t30_indicator);
451 /* This must come after the indicator handler, so the handler routine sees the existing state of the
452 indicator. */
453 s->current_rx_indicator = t30_indicator;
454 break;
455 case T38_TYPE_OF_MSG_T30_DATA:
456 if ((buf[ptr] & 0x20))
457 {
458 /* Extension */
459 if ((ptr + 2) > pkt_len)
460 return ret;
461 /*endif*/
462 t30_data = T38_DATA_V8 + (((buf[ptr] << 2) & 0x3C) | ((buf[ptr + 1] >> 6) & 0x3));
463 if (t30_data > T38_DATA_V33_14400)
464 {
465 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown data type - %d\n", log_seq_no, t30_data);
466 return -1;
467 }
468 /*endif*/
469 ptr += 2;
470 }
471 else
472 {
473 t30_data = (buf[ptr] >> 1) & 0xF;
474 if (t30_data > T38_DATA_V17_14400)
475 {
476 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown data type - %d\n", log_seq_no, t30_data);
477 return -1;
478 }
479 /*endif*/
480 ptr += 1;
481 }
482 /*endif*/
483 if (!data_field_present)
484 {
485 /* This is kinda weird, but I guess if the length checks out we accept it. */
486 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Data type with no data field\n", log_seq_no);
487 break;
488 }
489 /*endif*/
490 if (ptr >= pkt_len)
491 return ret;
492 /*endif*/
493 count = buf[ptr++];
494 //printf("Count is %d\n", count);
495 /* Do a dummy run through the fields to check we have a complete and uncorrupted packet. */
496 prev_ptr = ptr;
497 other_half = false;
498 for (i = 0; i < (int) count; i++)
499 {
500 if (ptr >= pkt_len)
501 return ret;
502 /*endif*/
503 if (s->t38_version == 0)
504 {
505 /* The original version of T.38 with a typo in the ASN.1 spec. */
506 if (other_half)
507 {
508 /* The lack of a data field in the previous message means
509 we are currently in the middle of an octet. */
510 field_data_present = (buf[ptr] >> 3) & 1;
511 /* Decode field_type */
512 t30_field_type = buf[ptr] & 0x7;
513 ptr++;
514 other_half = false;
515 }
516 else
517 {
518 field_data_present = (buf[ptr] >> 7) & 1;
519 /* Decode field_type */
520 t30_field_type = (buf[ptr] >> 4) & 0x7;
521 if (field_data_present)
522 ptr++;
523 else
524 other_half = true;
525 /*endif*/
526 }
527 /*endif*/
528 if (t30_field_type > T38_FIELD_T4_NON_ECM_SIG_END)
529 {
530 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown field type - %d\n", log_seq_no, t30_field_type);
531 return -1;
532 }
533 /*endif*/
534 }
535 else
536 {
537 field_data_present = (buf[ptr] >> 7) & 1;
538 /* Decode field_type */
539 if ((buf[ptr] & 0x40))
540 {
541 if ((ptr + 2) > pkt_len)
542 return ret;
543 /*endif*/
544 t30_field_type = T38_FIELD_CM_MESSAGE + (((buf[ptr] << 2) & 0x3C) | ((buf[ptr + 1] >> 6) & 0x3));
545 if (t30_field_type > T38_FIELD_V34RATE)
546 {
547 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Unknown field type - %d\n", log_seq_no, t30_field_type);
548 return -1;
549 }
550 /*endif*/
551 ptr += 2;
552 }
553 else
554 {
555 ptr++;
556 }
557 /*endif*/
558 }
559 /*endif*/
560 /* Decode field_data */
561 if (field_data_present)
562 {
563 if ((ptr + 2) > pkt_len)
564 return ret;
565 /*endif*/
566 numocts = ((buf[ptr] << 8) | buf[ptr + 1]) + 1;
567 ptr += numocts + 2;
568 }
569 /*endif*/
570 if (ptr > pkt_len)
571 return ret;
572 /*endif*/
573 }
574 /*endfor*/
575 /* Check if we finished mid byte in a version 0 packet. */
576 if (other_half)
577 ptr++;
578 /*endif*/
579 if (ptr > pkt_len)
580 return ret;
581 /*endif*/
582
583 /* Things look alright in the data, so lets run through the fields again, actually processing them.
584 There is no need to do all the error checking along the way on this pass. */
585 ptr = prev_ptr;
586 other_half = false;
587 for (i = 0; i < (int) count; i++)
588 {
589 if (s->t38_version == 0)
590 {
591 /* The original version of T.38 with a typo in the ASN.1 spec. */
592 if (other_half)
593 {
594 /* The lack of a data field in the previous message means
595 we are currently in the middle of an octet. */
596 field_data_present = (buf[ptr] >> 3) & 1;
597 /* Decode field_type */
598 t30_field_type = buf[ptr] & 0x7;
599 ptr++;
600 other_half = false;
601 }
602 else
603 {
604 field_data_present = (buf[ptr] >> 7) & 1;
605 /* Decode field_type */
606 t30_field_type = (buf[ptr] >> 4) & 0x7;
607 if (field_data_present)
608 ptr++;
609 else
610 other_half = true;
611 /*endif*/
612 }
613 /*endif*/
614 }
615 else
616 {
617 field_data_present = (buf[ptr] >> 7) & 1;
618 /* Decode field_type */
619 if ((buf[ptr] & 0x40))
620 {
621 t30_field_type = T38_FIELD_CM_MESSAGE + (((buf[ptr] << 2) & 0x3C) | ((buf[ptr + 1] >> 6) & 0x3));
622 ptr += 2;
623 }
624 else
625 {
626 t30_field_type = (buf[ptr++] >> 3) & 0x7;
627 }
628 /*endif*/
629 }
630 /*endif*/
631 /* Decode field_data */
632 if (field_data_present)
633 {
634 numocts = ((buf[ptr] << 8) | buf[ptr + 1]) + 1;
635 msg = buf + ptr + 2;
636 ptr += numocts + 2;
637 }
638 else
639 {
640 numocts = 0;
641 msg = NULL;
642 }
643 /*endif*/
644 span_log(&s->logging,
645 SPAN_LOG_FLOW,
646 "Rx %5d: (%d) data %s/%s + %d byte(s)\n",
647 log_seq_no,
648 i,
649 t38_data_type_to_str(t30_data),
650 t38_field_type_to_str(t30_field_type),
651 numocts);
652 s->rx_data_handler(s, s->rx_user_data, t30_data, t30_field_type, msg, numocts);
653 s->current_rx_data_type = t30_data;
654 s->current_rx_field_type = t30_field_type;
655 }
656 /*endfor*/
657 /* Check if we finished mid byte in a version 0 packet. */
658 if (other_half)
659 ptr++;
660 /*endif*/
661 break;
662 }
663 /*endswitch*/
664 if (ptr > pkt_len)
665 return ret;
666 /*endif*/
667 return ptr;
668 }
669 /*- End of function --------------------------------------------------------*/
670
t38_core_rx_ifp_packet(t38_core_state_t * s,const uint8_t * buf,int len,uint16_t seq_no)671 SPAN_DECLARE(int) t38_core_rx_ifp_packet(t38_core_state_t *s, const uint8_t *buf, int len, uint16_t seq_no)
672 {
673 int log_seq_no;
674 int ptr;
675
676 log_seq_no = (s->check_sequence_numbers) ? seq_no : s->rx_expected_seq_no;
677
678 if (s->check_sequence_numbers)
679 {
680 seq_no &= 0xFFFF;
681 if (seq_no != s->rx_expected_seq_no)
682 {
683 /* An expected value of -1 indicates this is the first received packet, and will accept
684 anything for that. We can't assume they will start from zero, even though they should. */
685 if (s->rx_expected_seq_no != -1)
686 {
687 /* We have a packet with a serial number that is not in sequence. The cause could be:
688 - 1. a repeat copy of a recent packet. Many T.38 implementations can preduce quite a lot of these.
689 - 2. a late packet, whose point in the sequence we have already passed.
690 - 3. the result of a hop in the sequence numbers cause by something weird from the other
691 end. Stream switching might cause this
692 - 4. missing packets.
693
694 In cases 1 and 2 we need to drop this packet. In case 2 it might make sense to try to do
695 something with it in the terminal case. Currently we don't. For gateway operation it will be
696 too late to do anything useful.
697 */
698 if (((seq_no + 1) & 0xFFFF) == s->rx_expected_seq_no)
699 {
700 /* Assume this is truly a repeat packet, and don't bother checking its contents. */
701 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Repeat packet number\n", log_seq_no);
702 return 0;
703 }
704 /*endif*/
705 /* Distinguish between a little bit out of sequence, and a huge hop. */
706 switch (classify_seq_no_offset(s->rx_expected_seq_no, seq_no))
707 {
708 case -1:
709 /* This packet is in the near past, so its late. */
710 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Late packet - expected %d\n", log_seq_no, s->rx_expected_seq_no);
711 return 0;
712 case 1:
713 /* This packet is in the near future, so some packets have been lost */
714 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Missing from %d\n", log_seq_no, s->rx_expected_seq_no);
715 s->rx_missing_handler(s, s->rx_user_data, s->rx_expected_seq_no, seq_no);
716 s->missing_packets += (seq_no - s->rx_expected_seq_no);
717 break;
718 default:
719 /* The sequence has jumped wildly */
720 span_log(&s->logging, SPAN_LOG_FLOW, "Rx %5d: Sequence restart\n", log_seq_no);
721 s->rx_missing_handler(s, s->rx_user_data, -1, -1);
722 s->missing_packets++;
723 break;
724 }
725 /*endswitch*/
726 }
727 /*endif*/
728 s->rx_expected_seq_no = seq_no;
729 }
730 /*endif*/
731 }
732 /*endif*/
733 if (len < 1)
734 {
735 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Bad packet length - %d\n", log_seq_no, len);
736 return -1;
737 }
738 /*endif*/
739 /* The sequence numbering is defined as rolling from 0xFFFF to 0x0000. Some implementations
740 of T.38 roll from 0xFFFF to 0x0001. Isn't standardisation a wonderful thing? The T.38
741 document specifies only a small fraction of what it should, yet then they actually nail
742 something properly, people ignore it. Developers in this industry truly deserves the ****
743 **** **** **** **** **** documents they have to live with. Anyway, when the far end has a
744 broken rollover behaviour we will get a hiccup at the rollover point. Don't worry too
745 much. We will just treat the message in progress as one with some missing data. With any
746 luck a retry will ride over the problem. Rollovers don't occur that often. It takes quite
747 a few FAX pages to reach rollover. */
748 s->rx_expected_seq_no = (s->rx_expected_seq_no + 1) & 0xFFFF;
749
750 ptr = t38_core_rx_ifp_stream(s, buf, len, seq_no);
751 if (ptr != len)
752 {
753 if (ptr >= 0)
754 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Rx %5d: Invalid length for packet - %d %d\n", log_seq_no, ptr, len);
755 /*endif*/
756 return -1;
757 }
758 /*endif*/
759 return 0;
760 }
761 /*- End of function --------------------------------------------------------*/
762
t38_encode_indicator(t38_core_state_t * s,uint8_t buf[],int indicator)763 static int t38_encode_indicator(t38_core_state_t *s, uint8_t buf[], int indicator)
764 {
765 int len;
766
767 /* Build the IFP packet */
768 len = 0;
769 if (s->data_transport_protocol == T38_TRANSPORT_TCP_TPKT)
770 len = 4;
771 /*endif*/
772
773 /* Data field not present */
774 /* Indicator packet */
775 /* Type of indicator */
776 if (indicator <= T38_IND_V17_14400_LONG_TRAINING)
777 {
778 buf[len++] = (uint8_t) (indicator << 1);
779 }
780 else if (s->t38_version != 0 && indicator <= T38_IND_V33_14400_TRAINING)
781 {
782 buf[len++] = (uint8_t) (0x20 | (((indicator - T38_IND_V8_ANSAM) & 0xF) >> 2));
783 buf[len++] = (uint8_t) (((indicator - T38_IND_V8_ANSAM) << 6) & 0xFF);
784 }
785 else
786 {
787 len = -1;
788 }
789 /*endif*/
790 if (s->data_transport_protocol == T38_TRANSPORT_TCP_TPKT)
791 {
792 /* Fill in the TPKT header (see RFC1006) */
793 /* Version */
794 buf[0] = 3;
795 /* Reserved */
796 buf[1] = 0;
797 /* Packet length - this includes the length of the header itself */
798 buf[2] = (len >> 8) & 0xFF;
799 buf[3] = len & 0xFF;
800 }
801 /*endif*/
802 return len;
803 }
804 /*- End of function --------------------------------------------------------*/
805
t38_encode_data(t38_core_state_t * s,uint8_t buf[],int data_type,const t38_data_field_t field[],int fields)806 static int t38_encode_data(t38_core_state_t *s, uint8_t buf[], int data_type, const t38_data_field_t field[], int fields)
807 {
808 int len;
809 int i;
810 int enclen;
811 int multiplier;
812 int data_field_no;
813 const t38_data_field_t *q;
814 unsigned int encoded_len;
815 unsigned int fragment_len;
816 unsigned int value;
817 uint8_t data_field_present;
818 uint8_t field_data_present;
819 char tag[20];
820
821 /* Build the IFP packet */
822 len = 0;
823 if (s->data_transport_protocol == T38_TRANSPORT_TCP_TPKT)
824 len = 4;
825 /*endif*/
826
827 /* There seems no valid reason why a packet would ever be generated without a data field present */
828 data_field_present = (fields > 0) ? 0x80 : 0x00;
829
830 /* Data field present */
831 /* Data packet */
832 /* Type of data */
833 if (data_type <= T38_DATA_V17_14400)
834 {
835 buf[len++] = (uint8_t) (data_field_present | 0x40 | (data_type << 1));
836 }
837 else if (s->t38_version != 0 && data_type <= T38_DATA_V33_14400)
838 {
839 buf[len++] = (uint8_t) (data_field_present | 0x60 | (((data_type - T38_DATA_V8) & 0xF) >> 2));
840 buf[len++] = (uint8_t) (((data_type - T38_DATA_V8) << 6) & 0xFF);
841 }
842 else
843 {
844 return -1;
845 }
846 /*endif*/
847
848 if (data_field_present)
849 {
850 encoded_len = 0;
851 data_field_no = 0;
852 do
853 {
854 value = fields - encoded_len;
855 if (value < 0x80)
856 {
857 /* 1 octet case */
858 buf[len++] = (uint8_t) value;
859 enclen = value;
860 }
861 else if (value < 0x4000)
862 {
863 /* 2 octet case */
864 buf[len++] = (uint8_t) (0x80 | ((value >> 8) & 0xFF));
865 buf[len++] = (uint8_t) (value & 0xFF);
866 enclen = value;
867 }
868 else
869 {
870 /* Fragmentation case */
871 multiplier = (value/0x4000 < 4) ? value/0x4000 : 4;
872 buf[len++] = (uint8_t) (0xC0 | multiplier);
873 enclen = 0x4000*multiplier;
874 }
875 /*endif*/
876
877 fragment_len = enclen;
878 encoded_len += fragment_len;
879 /* Encode the elements */
880 for (i = 0; i < (int) encoded_len; i++)
881 {
882 q = &field[data_field_no];
883 field_data_present = (uint8_t) (q->field_len > 0);
884 /* Encode field_type */
885 if (s->t38_version == 0)
886 {
887 /* Original version of T.38 with a typo */
888 if (q->field_type > T38_FIELD_T4_NON_ECM_SIG_END)
889 return -1;
890 /*endif*/
891 buf[len++] = (uint8_t) ((field_data_present << 7) | (q->field_type << 4));
892 }
893 else
894 {
895 if (q->field_type <= T38_FIELD_T4_NON_ECM_SIG_END)
896 {
897 buf[len++] = (uint8_t) ((field_data_present << 7) | (q->field_type << 3));
898 }
899 else if (q->field_type <= T38_FIELD_V34RATE)
900 {
901 buf[len++] = (uint8_t) ((field_data_present << 7) | 0x40 | ((q->field_type - T38_FIELD_CM_MESSAGE) >> 2));
902 buf[len++] = (uint8_t) (((q->field_type - T38_FIELD_CM_MESSAGE) << 6) & 0xC0);
903 }
904 else
905 {
906 return -1;
907 }
908 /*endif*/
909 }
910 /*endif*/
911 /* Encode field_data */
912 if (field_data_present)
913 {
914 if (q->field_len < 1 || q->field_len > 65535)
915 return -1;
916 /*endif*/
917 buf[len++] = (uint8_t) (((q->field_len - 1) >> 8) & 0xFF);
918 buf[len++] = (uint8_t) ((q->field_len - 1) & 0xFF);
919 memcpy(&buf[len], q->field, q->field_len);
920 len += q->field_len;
921 }
922 /*endif*/
923 data_field_no++;
924 }
925 /*endfor*/
926 }
927 while ((int) encoded_len != fields || fragment_len >= 16384);
928 }
929 /*endif*/
930
931 for (data_field_no = 0; data_field_no < fields; data_field_no++)
932 {
933 span_log(&s->logging,
934 SPAN_LOG_FLOW,
935 "Tx %5d: (%d) data %s/%s + %d byte(s)\n",
936 s->tx_seq_no,
937 data_field_no,
938 t38_data_type_to_str(data_type),
939 t38_field_type_to_str(field[data_field_no].field_type),
940 field[data_field_no].field_len);
941 }
942 /*endfor*/
943
944 if (s->data_transport_protocol == T38_TRANSPORT_TCP_TPKT)
945 {
946 /* Fill in the TPKT header (see RFC1006) */
947 /* Version */
948 buf[0] = 3;
949 /* Reserved */
950 buf[1] = 0;
951 /* Packet length - this includes the length of the header itself */
952 buf[2] = (len >> 8) & 0xFF;
953 buf[3] = len & 0xFF;
954 }
955 /*endif*/
956
957 if (span_log_test(&s->logging, SPAN_LOG_FLOW))
958 {
959 sprintf(tag, "Tx %5d: IFP", s->tx_seq_no);
960 span_log_buf(&s->logging, SPAN_LOG_FLOW, tag, buf, len);
961 }
962 /*endif*/
963 return len;
964 }
965 /*- End of function --------------------------------------------------------*/
966
t38_core_send_indicator(t38_core_state_t * s,int indicator)967 SPAN_DECLARE(int) t38_core_send_indicator(t38_core_state_t *s, int indicator)
968 {
969 uint8_t buf[100];
970 int len;
971 int delay;
972 int transmissions;
973
974 delay = 0;
975 /* Only send an indicator if it represents a change of state. */
976 /* If the 0x100 bit is set in indicator it will bypass this test, and force transmission */
977 if (s->current_tx_indicator != indicator)
978 {
979 /* Zero is a valid count, to suppress the transmission of indicators when the
980 transport means they are not needed - e.g. TPKT/TCP. */
981 transmissions = (indicator & 0x100) ? 1 : s->category_control[T38_PACKET_CATEGORY_INDICATOR];
982 indicator &= 0xFF;
983 if (s->category_control[T38_PACKET_CATEGORY_INDICATOR])
984 {
985 if ((len = t38_encode_indicator(s, buf, indicator)) < 0)
986 {
987 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 indicator len is %d\n", len);
988 return len;
989 }
990 /*endif*/
991 span_log(&s->logging, SPAN_LOG_FLOW, "Tx %5d: indicator %s\n", s->tx_seq_no, t38_indicator_to_str(indicator));
992 if (s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, transmissions) < 0)
993 {
994 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Tx packet handler failure\n");
995 return -1;
996 }
997 /*endif*/
998 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF;
999 if (s->pace_transmission)
1000 {
1001 delay = modem_startup_time[indicator].training;
1002 if (s->allow_for_tep)
1003 delay += modem_startup_time[indicator].tep;
1004 /*endif*/
1005 }
1006 /*endif*/
1007 }
1008 /*endif*/
1009 s->current_tx_indicator = indicator;
1010 }
1011 /*endif*/
1012 return delay;
1013 }
1014 /*- End of function --------------------------------------------------------*/
1015
t38_core_send_flags_delay(t38_core_state_t * s,int indicator)1016 SPAN_DECLARE(int) t38_core_send_flags_delay(t38_core_state_t *s, int indicator)
1017 {
1018 if (s->pace_transmission)
1019 return modem_startup_time[indicator].flags;
1020 /*endif*/
1021 return 0;
1022 }
1023 /*- End of function --------------------------------------------------------*/
1024
t38_core_send_training_delay(t38_core_state_t * s,int indicator)1025 SPAN_DECLARE(int) t38_core_send_training_delay(t38_core_state_t *s, int indicator)
1026 {
1027 if (s->pace_transmission)
1028 return modem_startup_time[indicator].training;
1029 /*endif*/
1030 return 0;
1031 }
1032 /*- End of function --------------------------------------------------------*/
1033
t38_core_send_data(t38_core_state_t * s,int data_type,int field_type,const uint8_t field[],int field_len,int category)1034 SPAN_DECLARE(int) t38_core_send_data(t38_core_state_t *s, int data_type, int field_type, const uint8_t field[], int field_len, int category)
1035 {
1036 t38_data_field_t field0;
1037 uint8_t buf[1000];
1038 int len;
1039
1040 field0.field_type = field_type;
1041 field0.field = field;
1042 field0.field_len = field_len;
1043 if ((len = t38_encode_data(s, buf, data_type, &field0, 1)) < 0)
1044 {
1045 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 data len is %d\n", len);
1046 return len;
1047 }
1048 /*endif*/
1049 if (s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, s->category_control[category]) < 0)
1050 {
1051 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Tx packet handler failure\n");
1052 return -1;
1053 }
1054 /*endif*/
1055 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF;
1056 return 0;
1057 }
1058 /*- End of function --------------------------------------------------------*/
1059
t38_core_send_data_multi_field(t38_core_state_t * s,int data_type,const t38_data_field_t field[],int fields,int category)1060 SPAN_DECLARE(int) t38_core_send_data_multi_field(t38_core_state_t *s, int data_type, const t38_data_field_t field[], int fields, int category)
1061 {
1062 uint8_t buf[1000];
1063 int len;
1064
1065 if ((len = t38_encode_data(s, buf, data_type, field, fields)) < 0)
1066 {
1067 span_log(&s->logging, SPAN_LOG_FLOW, "T.38 data len is %d\n", len);
1068 return len;
1069 }
1070 /*endif*/
1071 if (s->tx_packet_handler(s, s->tx_packet_user_data, buf, len, s->category_control[category]) < 0)
1072 {
1073 span_log(&s->logging, SPAN_LOG_PROTOCOL_WARNING, "Tx packet handler failure\n");
1074 return -1;
1075 }
1076 /*endif*/
1077 s->tx_seq_no = (s->tx_seq_no + 1) & 0xFFFF;
1078 return 0;
1079 }
1080 /*- End of function --------------------------------------------------------*/
1081
t38_set_data_rate_management_method(t38_core_state_t * s,int method)1082 SPAN_DECLARE(void) t38_set_data_rate_management_method(t38_core_state_t *s, int method)
1083 {
1084 s->data_rate_management_method = method;
1085 }
1086 /*- End of function --------------------------------------------------------*/
1087
t38_set_data_transport_protocol(t38_core_state_t * s,int data_transport_protocol)1088 SPAN_DECLARE(void) t38_set_data_transport_protocol(t38_core_state_t *s, int data_transport_protocol)
1089 {
1090 s->data_transport_protocol = data_transport_protocol;
1091 }
1092 /*- End of function --------------------------------------------------------*/
1093
t38_set_fill_bit_removal(t38_core_state_t * s,bool fill_bit_removal)1094 SPAN_DECLARE(void) t38_set_fill_bit_removal(t38_core_state_t *s, bool fill_bit_removal)
1095 {
1096 s->fill_bit_removal = fill_bit_removal;
1097 }
1098 /*- End of function --------------------------------------------------------*/
1099
t38_set_mmr_transcoding(t38_core_state_t * s,bool mmr_transcoding)1100 SPAN_DECLARE(void) t38_set_mmr_transcoding(t38_core_state_t *s, bool mmr_transcoding)
1101 {
1102 s->mmr_transcoding = mmr_transcoding;
1103 }
1104 /*- End of function --------------------------------------------------------*/
1105
t38_set_jbig_transcoding(t38_core_state_t * s,bool jbig_transcoding)1106 SPAN_DECLARE(void) t38_set_jbig_transcoding(t38_core_state_t *s, bool jbig_transcoding)
1107 {
1108 s->jbig_transcoding = jbig_transcoding;
1109 }
1110 /*- End of function --------------------------------------------------------*/
1111
t38_set_max_buffer_size(t38_core_state_t * s,int max_buffer_size)1112 SPAN_DECLARE(void) t38_set_max_buffer_size(t38_core_state_t *s, int max_buffer_size)
1113 {
1114 s->max_buffer_size = max_buffer_size;
1115 }
1116 /*- End of function --------------------------------------------------------*/
1117
t38_set_max_datagram_size(t38_core_state_t * s,int max_datagram_size)1118 SPAN_DECLARE(void) t38_set_max_datagram_size(t38_core_state_t *s, int max_datagram_size)
1119 {
1120 s->max_datagram_size = max_datagram_size;
1121 }
1122 /*- End of function --------------------------------------------------------*/
1123
t38_set_t38_version(t38_core_state_t * s,int t38_version)1124 SPAN_DECLARE(void) t38_set_t38_version(t38_core_state_t *s, int t38_version)
1125 {
1126 s->t38_version = t38_version;
1127 }
1128 /*- End of function --------------------------------------------------------*/
1129
t38_set_sequence_number_handling(t38_core_state_t * s,bool check)1130 SPAN_DECLARE(void) t38_set_sequence_number_handling(t38_core_state_t *s, bool check)
1131 {
1132 s->check_sequence_numbers = check;
1133 }
1134 /*- End of function --------------------------------------------------------*/
1135
t38_set_pace_transmission(t38_core_state_t * s,int pace_transmission)1136 SPAN_DECLARE(void) t38_set_pace_transmission(t38_core_state_t *s, int pace_transmission)
1137 {
1138 s->pace_transmission = pace_transmission;
1139 }
1140 /*- End of function --------------------------------------------------------*/
1141
t38_set_tep_handling(t38_core_state_t * s,bool allow_for_tep)1142 SPAN_DECLARE(void) t38_set_tep_handling(t38_core_state_t *s, bool allow_for_tep)
1143 {
1144 s->allow_for_tep = allow_for_tep;
1145 }
1146 /*- End of function --------------------------------------------------------*/
1147
t38_set_redundancy_control(t38_core_state_t * s,int category,int setting)1148 SPAN_DECLARE(void) t38_set_redundancy_control(t38_core_state_t *s, int category, int setting)
1149 {
1150 s->category_control[category] = setting;
1151 }
1152 /*- End of function --------------------------------------------------------*/
1153
t38_set_fastest_image_data_rate(t38_core_state_t * s,int max_rate)1154 SPAN_DECLARE(void) t38_set_fastest_image_data_rate(t38_core_state_t *s, int max_rate)
1155 {
1156 s->fastest_image_data_rate = max_rate;
1157 }
1158 /*- End of function --------------------------------------------------------*/
1159
t38_get_fastest_image_data_rate(t38_core_state_t * s)1160 SPAN_DECLARE(int) t38_get_fastest_image_data_rate(t38_core_state_t *s)
1161 {
1162 return s->fastest_image_data_rate;
1163 }
1164 /*- End of function --------------------------------------------------------*/
1165
t38_core_get_logging_state(t38_core_state_t * s)1166 SPAN_DECLARE(logging_state_t *) t38_core_get_logging_state(t38_core_state_t *s)
1167 {
1168 return &s->logging;
1169 }
1170 /*- End of function --------------------------------------------------------*/
1171
t38_core_restart(t38_core_state_t * s)1172 SPAN_DECLARE(int) t38_core_restart(t38_core_state_t *s)
1173 {
1174 /* Set the initial current receive states to something invalid, so the
1175 first data received is seen as a change of state. */
1176 s->current_rx_indicator = -1;
1177 s->current_rx_data_type = -1;
1178 s->current_rx_field_type = -1;
1179
1180 /* Set the initial current indicator state to something invalid, so the
1181 first attempt to send an indicator will work. */
1182 s->current_tx_indicator = -1;
1183
1184 /* We have no initial expectation of the received packet sequence number.
1185 They most often start at 0 or 1 for a UDPTL transport, but random
1186 starting numbers are possible. */
1187 s->rx_expected_seq_no = -1;
1188 return 0;
1189 }
1190 /*- End of function --------------------------------------------------------*/
1191
t38_core_init(t38_core_state_t * s,t38_rx_indicator_handler_t rx_indicator_handler,t38_rx_data_handler_t rx_data_handler,t38_rx_missing_handler_t rx_missing_handler,void * rx_user_data,t38_tx_packet_handler_t tx_packet_handler,void * tx_packet_user_data)1192 SPAN_DECLARE(t38_core_state_t *) t38_core_init(t38_core_state_t *s,
1193 t38_rx_indicator_handler_t rx_indicator_handler,
1194 t38_rx_data_handler_t rx_data_handler,
1195 t38_rx_missing_handler_t rx_missing_handler,
1196 void *rx_user_data,
1197 t38_tx_packet_handler_t tx_packet_handler,
1198 void *tx_packet_user_data)
1199 {
1200 if (s == NULL)
1201 {
1202 if ((s = (t38_core_state_t *) span_alloc(sizeof(*s))) == NULL)
1203 return NULL;
1204 /*endif*/
1205 }
1206 /*endif*/
1207 memset(s, 0, sizeof(*s));
1208 span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
1209 span_log_set_protocol(&s->logging, "T.38");
1210
1211 /* Set some defaults for the parameters configurable from outside the
1212 T.38 domain - e.g. from SDP data. */
1213 s->data_rate_management_method = T38_DATA_RATE_MANAGEMENT_TRANSFERRED_TCF;
1214 s->data_transport_protocol = T38_TRANSPORT_UDPTL;
1215 s->fill_bit_removal = false;
1216 s->mmr_transcoding = false;
1217 s->jbig_transcoding = false;
1218 s->max_buffer_size = 400;
1219 s->max_datagram_size = 100;
1220 s->t38_version = 0;
1221 s->check_sequence_numbers = true;
1222 s->pace_transmission = true;
1223
1224 /* Set some defaults */
1225 s->category_control[T38_PACKET_CATEGORY_INDICATOR] = 1;
1226 s->category_control[T38_PACKET_CATEGORY_CONTROL_DATA] = 1;
1227 s->category_control[T38_PACKET_CATEGORY_CONTROL_DATA_END] = 1;
1228 s->category_control[T38_PACKET_CATEGORY_IMAGE_DATA] = 1;
1229 s->category_control[T38_PACKET_CATEGORY_IMAGE_DATA_END] = 1;
1230
1231 s->rx_indicator_handler = rx_indicator_handler;
1232 s->rx_data_handler = rx_data_handler;
1233 s->rx_missing_handler = rx_missing_handler;
1234 s->rx_user_data = rx_user_data;
1235 s->tx_packet_handler = tx_packet_handler;
1236 s->tx_packet_user_data = tx_packet_user_data;
1237
1238 t38_core_restart(s);
1239 return s;
1240 }
1241 /*- End of function --------------------------------------------------------*/
1242
t38_core_release(t38_core_state_t * s)1243 SPAN_DECLARE(int) t38_core_release(t38_core_state_t *s)
1244 {
1245 return 0;
1246 }
1247 /*- End of function --------------------------------------------------------*/
1248
t38_core_free(t38_core_state_t * s)1249 SPAN_DECLARE(int) t38_core_free(t38_core_state_t *s)
1250 {
1251 if (s)
1252 span_free(s);
1253 return 0;
1254 }
1255 /*- End of function --------------------------------------------------------*/
1256 /*- End of file ------------------------------------------------------------*/
1257