Removed some more "statement not reached" warnings.
[obnox/wireshark/wip.git] / epan / dissectors / packet-uts.c
1 /* packet-uts.c
2  * Routines for UTS WAN protocol dissection
3  * Copyright 2007, Fulko Hew, SITA INC Canada, Inc.
4  *
5  * $Id$
6  *
7  * Copied from packet-ipars.c
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 /* Use tabstops = 4 */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <glib.h>
35 #include <string.h>
36 #include <epan/packet.h>
37 #include <epan/emem.h>
38
39 #define SOH             (0x01)
40 #define STX             (0x02)
41 #define ETX             (0x03)
42 #define EOT             (0x04)
43 #define ENQ             (0x05)
44 #define BEL             (0x07)
45 #define NAK             (0x15)
46 #define DLE             (0x10)
47
48 #define GRID    (0x20)
49 #define GSID    (0x50)
50 #define GDID    (0x70)
51
52 #define MAX_POLL_TYPE_MSG_SIZE  (50)
53
54 static int              proto_uts                       = -1;
55 static gint             ett_uts                         = -1;
56 static gint             ett_header_uts          = -1;
57 static gint             ett_trailer_uts         = -1;
58 static int              hf_rid                          = -1;
59 static int              hf_sid                          = -1;
60 static int              hf_did                          = -1;
61 static int              hf_retxrequest          = -1;
62 static int              hf_ack                          = -1;
63 static int              hf_replyrequest         = -1;
64 static int              hf_busy                         = -1;
65 static int              hf_notbusy                      = -1;
66 static int              hf_msgwaiting           = -1;
67 static int              hf_function                     = -1;
68 static int              hf_data                         = -1;
69
70 #define MATCH   (1)
71 #define FETCH   (2)
72
73 #define SRC             (1)
74 #define DST             (2)
75
76 static int testchar(tvbuff_t *tvb, packet_info *pinfo _U_, int offset, int op, gchar match, gchar *storage)
77 {
78         gchar temp;
79
80         if (tvb_length_remaining(tvb, offset)) {
81                 temp = tvb_get_guint8(tvb, offset) & 0x7f;
82                 if (op == FETCH || (op == MATCH && temp == match)) {
83                         if (storage != NULL) *storage = temp;
84                         return 1;
85                 } else {
86                         return 0;
87                 }
88         } else {
89                 if (check_col(pinfo->cinfo, COL_INFO))
90                         col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown Message Format");
91                 return 0;       
92         }
93 }
94
95 static void
96 set_addr(packet_info *pinfo _U_ , int field, gchar rid, gchar sid, gchar did) 
97 {
98         if (field == SRC) {
99                 if (check_col(pinfo->cinfo, COL_DEF_SRC)) col_append_fstr(pinfo->cinfo, COL_DEF_SRC, " %2.2X:%2.2X:%2.2X", rid, sid, did);
100         } else {
101                 if (check_col(pinfo->cinfo, COL_DEF_DST)) col_append_fstr(pinfo->cinfo, COL_DEF_DST, " %2.2X:%2.2X:%2.2X", rid, sid, did);
102         }
103 }
104
105 static void
106 dissect_uts(tvbuff_t *tvb, packet_info *pinfo _U_ , proto_tree *tree)
107 {
108         proto_tree      *uts_tree                       = NULL;
109         proto_tree      *uts_header_tree        = NULL;
110         proto_tree      *uts_trailer_tree       = NULL;
111         proto_item      *ti;
112         int                     length;
113         gchar           rid, sid, did;
114         gchar           *msg_msg;
115         int                     offset                          = 0;
116         int                     header_length           = -1;
117         int                     ack_start                       = 0;
118         int                     busy_start                      = 0;
119         int                     notbusy_start           = 0;
120         int                     replyrequest_start      = 0;
121         int                     function_start          = 0;
122         int                     msgwaiting_start        = 0;
123         int                     nak_start                       = 0;
124         int                     etx_start                       = 0;
125         int                     bcc_start                       = 0;
126         int                     stx_start                       = 0;
127         gchar           function_code;
128         guint8          *data_ptr;
129
130         enum    { NOTRAFFIC, OTHER }    msg_type = OTHER;
131
132         msg_msg         = ep_alloc(MAX_POLL_TYPE_MSG_SIZE);
133         msg_msg[0]      = 0;
134
135         if (check_col(pinfo->cinfo, COL_PROTOCOL))                      /* set the protocol column on summary display */
136                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "UTS");
137
138         if (testchar(tvb, pinfo, 0, MATCH, EOT, NULL)   &&
139                 testchar(tvb, pinfo, 1, MATCH, EOT, NULL)       &&
140                 testchar(tvb, pinfo, 2, MATCH, ETX, NULL)) {
141                 msg_type = NOTRAFFIC;
142                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "No Traffic");
143         } else {
144                 if (testchar(tvb, pinfo, 0, MATCH, SOH, NULL)   &&
145                         testchar(tvb, pinfo, 1, FETCH, 0, (gchar *)&rid)        &&
146                         testchar(tvb, pinfo, 2, FETCH, 0, (gchar *)&sid)        &&
147                         testchar(tvb, pinfo, 3, FETCH, 0, (gchar *)&did)) {
148                                 offset = 4;
149                                 if (testchar(tvb, pinfo, offset, MATCH, ETX, NULL)) {
150                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "General Poll");
151                                         set_addr(pinfo, DST, rid, sid, did);
152                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)       &&
153                                                         testchar(tvb, pinfo, offset+1, MATCH, '1', NULL)        &&
154                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)) {
155                                                                 ack_start = offset;
156                                                                 if (sid == GSID && did == GDID) {
157                                                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "General Poll + ACK");
158                                                                         set_addr(pinfo, DST, rid, sid, did);
159                                                                 } else if (sid != GSID && did == GDID) {
160                                                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Specific Poll + ACK");
161                                                                         set_addr(pinfo, DST, rid, sid, did);
162                                                                 } else if (sid != GSID && did != GDID) {
163                                                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "No Traffic + ACK");
164                                                                         set_addr(pinfo, SRC, rid, sid, did);
165                                                                 } else {
166                                                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Unknown Message Format");
167                                                                         if ((pinfo->pseudo_header->sita.flags & SITA_FRAME_DIR) == SITA_FRAME_DIR_TXED) {
168                                                                                 set_addr(pinfo, DST, rid, sid, did);    /* if the ACN sent it, the address is of the destination... the terminal */
169                                                                         } else {
170                                                                                 set_addr(pinfo, SRC, rid, sid, did);    /* if the ACN received it, the address if of the source... the terminal */
171                                                                         }
172                                                                 }
173                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
174                                                         testchar(tvb, pinfo, offset+1, MATCH, NAK, NULL)        &&
175                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)        &&
176                                                         sid != GSID && did == GDID) {
177                                                                 nak_start = offset;
178                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Retransmit Request");
179                                                                 set_addr(pinfo, DST, rid, sid, did);
180                                 } else if (testchar(tvb, pinfo, offset, MATCH, BEL, NULL)               &&
181                                                         testchar(tvb, pinfo, offset+1, MATCH, STX, NULL)        &&
182                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)) {
183                                                                 header_length = offset+2;
184                                                                 msgwaiting_start = offset;
185                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Message Waiting");
186                                                                 set_addr(pinfo, DST, rid, sid, did);
187                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
188                                                         testchar(tvb, pinfo, offset+1, MATCH, '1', NULL)        &&
189                                                         testchar(tvb, pinfo, offset+2, MATCH, STX, NULL)) {
190                                                                 ack_start = offset;
191                                                                 header_length = offset+3;               
192                                                                 stx_start = offset+2;
193                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Text + ACK");
194                                                                 set_addr(pinfo, SRC, rid, sid, did);
195                                 } else if (testchar(tvb, pinfo, offset, MATCH, STX, NULL)) {
196                                         header_length = offset+1;
197                                         stx_start = offset;
198                                         g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Text");
199                                         if ((pinfo->pseudo_header->sita.flags & SITA_FRAME_DIR) == SITA_FRAME_DIR_TXED) {
200                                                 set_addr(pinfo, DST, rid, sid, did);            /* if the ACN sent it, the address is of the destination... the terminal */
201                                         } else {
202                                                 set_addr(pinfo, SRC, rid, sid, did);            /* if the ACN received it, the address if of the source... the terminal */
203                                         }
204                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
205                                                         testchar(tvb, pinfo, offset+1, MATCH, ENQ, NULL)        &&
206                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)) {
207                                                                 replyrequest_start = offset;
208                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Reply Request");
209                                                                 set_addr(pinfo, SRC, rid, sid, did);
210                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
211                                                         testchar(tvb, pinfo, offset+1, MATCH, '?', NULL)        &&
212                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)) {
213                                                                 busy_start = offset;
214                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Busy");
215                                                                 set_addr(pinfo, SRC, rid, sid, did);
216                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
217                                                         testchar(tvb, pinfo, offset+1, MATCH, ';', NULL)        &&
218                                                         testchar(tvb, pinfo, offset+2, MATCH, ETX, NULL)) {
219                                                                 notbusy_start = offset;
220                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Not Busy");
221                                                                 set_addr(pinfo, SRC, rid, sid, did);
222                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)               &&
223                                                         testchar(tvb, pinfo, offset+1, MATCH, '1', NULL)        &&
224                                                         testchar(tvb, pinfo, offset+2, MATCH, DLE, NULL)        &&
225                                                         testchar(tvb, pinfo, offset+3, MATCH, ';', NULL)        &&
226                                                         testchar(tvb, pinfo, offset+4, MATCH, ETX, NULL)) {
227                                                                 notbusy_start = offset+2;
228                                                                 ack_start = offset;
229                                                                 g_snprintf(msg_msg, MAX_POLL_TYPE_MSG_SIZE, "Not Busy + ACK");
230                                                                 set_addr(pinfo, SRC, rid, sid, did);
231                                 } else if (testchar(tvb, pinfo, offset, MATCH, DLE, NULL)                               &&
232                                                         testchar(tvb, pinfo, offset+1, MATCH, '1', NULL)                        &&
233                                                         testchar(tvb, pinfo, offset+2, FETCH, 0, &function_code)        &&
234                                                         testchar(tvb, pinfo, offset+3, MATCH, ETX, NULL)) {
235                                                                 ack_start = offset;
236                                                                 function_start = offset + 2;
237                                                                 if (check_col(pinfo->cinfo, COL_INFO))
238                                                                         col_add_fstr(pinfo->cinfo, COL_INFO, "Function Message '%c' + ACK", function_code);
239                                                                 set_addr(pinfo, SRC, rid, sid, did);
240                                 } else if (testchar(tvb, pinfo, offset, FETCH, 0, &function_code)               &&
241                                                         testchar(tvb, pinfo, offset+1, MATCH, ETX, NULL)) {
242                                                                 function_start = offset;
243                                                                 if (check_col(pinfo->cinfo, COL_INFO))
244                                                                         col_add_fstr(pinfo->cinfo, COL_INFO, "Function Message '%c'", function_code);
245                                                                 set_addr(pinfo, SRC, rid, sid, did);
246                                 }
247                         }
248         }
249         if ((check_col(pinfo->cinfo, COL_INFO)) && strlen(msg_msg))
250                 col_add_str(pinfo->cinfo, COL_INFO, msg_msg);
251
252         while (tvb_length_remaining(tvb, offset)) {                                     /* now look for the ETX */
253                 if ((tvb_get_guint8(tvb, offset) & 0x7f) == ETX) {
254                         if (header_length == -1) header_length = offset;        /* the header ends at an STX, or if not found, the ETX */
255                         etx_start = offset;
256                         offset++;
257                         break;
258                 }
259                 offset++;
260         }
261         if (tvb_length_remaining(tvb, offset))                                          /* if there is anything left, it could be the BCC and pads */
262                 bcc_start = offset;
263
264         if (tree) {
265                 ti = proto_tree_add_protocol_format(tree, proto_uts, tvb, 0, -1, "UTS");
266                 uts_tree = proto_item_add_subtree(ti, ett_uts);
267
268                 if (msg_type == NOTRAFFIC) {
269                         proto_tree_add_protocol_format(uts_tree, proto_uts, tvb, 0, 2, "No Traffic");
270                         proto_tree_add_protocol_format(uts_tree, proto_uts, tvb, 2, -1, "ETX + padding");
271                 } else {
272                         ti = proto_tree_add_text(uts_tree, tvb, 0, header_length, "Header");
273                         uts_header_tree = proto_item_add_subtree(ti, ett_header_uts);
274
275                         proto_tree_add_protocol_format(uts_header_tree, proto_uts, tvb, 0, 1, "SOH");
276
277                         if (rid == GRID)                proto_tree_add_uint_format(uts_header_tree, hf_rid, tvb, 1, 1, rid, "RID (%02X) (General)",     rid     );
278                         else                                    proto_tree_add_uint_format(uts_header_tree, hf_rid, tvb, 1, 1, rid, "RID (%02X)",                       rid     );
279                         if (sid == GSID)                proto_tree_add_uint_format(uts_header_tree, hf_sid, tvb, 2, 1, sid, "SID (%02X) (General)",     sid     );
280                         else                                    proto_tree_add_uint_format(uts_header_tree, hf_sid, tvb, 2, 1, sid, "SID (%02X)",                       sid     );
281                         if (sid == GDID)                proto_tree_add_uint_format(uts_header_tree, hf_did, tvb, 3, 1, did, "DID (%02X) (General)",     did     );
282                         else                                    proto_tree_add_uint_format(uts_header_tree, hf_did, tvb, 3, 1, did, "DID (%02X)",                       did     );
283
284                         if (nak_start)                  proto_tree_add_boolean_format(uts_header_tree, hf_retxrequest,  tvb, nak_start,                         2, 1, "Re-transmit Request");
285                         if (ack_start)                  proto_tree_add_boolean_format(uts_header_tree, hf_ack,                  tvb, ack_start,                         2, 1, "Ack");
286                         if (replyrequest_start) proto_tree_add_boolean_format(uts_header_tree, hf_replyrequest, tvb, replyrequest_start,        2, 1, "Reply Request");
287                         if (busy_start)                 proto_tree_add_boolean_format(uts_header_tree, hf_busy,                 tvb, busy_start,                        2, 1, "Busy");
288                         if (notbusy_start)              proto_tree_add_boolean_format(uts_header_tree, hf_notbusy,              tvb, notbusy_start,                     2, 1, "Not Busy");
289                         if (msgwaiting_start)   proto_tree_add_boolean_format(uts_header_tree, hf_msgwaiting,   tvb, msgwaiting_start,          1, 1, "Message Waiting");
290                         if (function_start)
291                                 proto_tree_add_uint_format(uts_header_tree, hf_function, tvb, function_start, 1, function_code, "Function '%c'", function_code  );
292
293                         if (stx_start) {
294                                 proto_tree_add_protocol_format(uts_header_tree, proto_uts, tvb, stx_start,                      1, "Start of Text"                      );
295                                 length = tvb_length_remaining(tvb, stx_start+1);                                        /* find out how much message remains */
296                                 if (etx_start) length = (etx_start - stx_start - 1);                            /* and the data part is the rest... whatever preceeds the ETX if it exists */
297                                 data_ptr = tvb_get_ephemeral_string(tvb, stx_start+1, length);  /* copy the string for dissecting */
298                                 proto_tree_add_string_format(uts_tree, hf_data, tvb, stx_start + 1, length, data_ptr, "Text (%d byte%s)", length, plurality(length, "", "s"));
299                         }
300                         if (etx_start) {
301                                 ti = proto_tree_add_text(uts_tree, tvb, etx_start, -1, "Trailer");
302                                 uts_trailer_tree = proto_item_add_subtree(ti, ett_trailer_uts);
303
304                                 if (etx_start)  proto_tree_add_protocol_format(uts_trailer_tree, proto_uts, tvb, etx_start,     1, "ETX"                                );
305                                 if (bcc_start)  proto_tree_add_protocol_format(uts_trailer_tree, proto_uts, tvb, bcc_start,     -1, "CCC + padding"             );
306                         }
307                 }
308         }
309 }
310
311 void
312 proto_register_uts(void)
313 {
314    static hf_register_info hf[] = {
315         { &hf_rid,                      { "RID",                        "uts.rid",                      FT_UINT8,       BASE_HEX,       NULL, 0, "Remote Identifier address",   HFILL }}, 
316         { &hf_sid,                      { "SID",                        "uts.sid",                      FT_UINT8,       BASE_HEX,       NULL, 0, "Site Identifier address",             HFILL }}, 
317         { &hf_did,                      { "DID",                        "uts.did",                      FT_UINT8,       BASE_HEX,       NULL, 0, "Device Identifier address",   HFILL }}, 
318                 { &hf_retxrequest,      { "ReTxRequst",         "uts.retxrequst",       FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Re-transmit Request", HFILL }}, 
319                 { &hf_ack,                      { "Ack",                        "uts.ack",                      FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Ack",                                 HFILL }}, 
320                 { &hf_replyrequest,     { "ReplyRequst",        "uts.replyrequest",     FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Reply Request",               HFILL }},
321                 { &hf_busy,                     { "Busy",                       "uts.busy",                     FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Busy",                                HFILL }},
322                 { &hf_notbusy,          { "NotBusy",            "uts.notbusy",          FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Not Busy",                    HFILL }},
323                 { &hf_msgwaiting,       { "MsgWaiting",         "uts.msgwaiting",       FT_BOOLEAN,     BASE_NONE,      NULL, 0, "TRUE if Message Waiting",             HFILL }},
324         { &hf_function,         { "Function",           "uts.function",         FT_UINT8,       BASE_HEX,       NULL, 0, "Function Code value",                 HFILL }},
325                 { &hf_data,                     { "Data",                       "uts.data",                     FT_STRING,      BASE_NONE,      NULL, 0, "User Data Message",                   HFILL }},
326     };
327
328         static gint *ett[] = {
329                 &ett_uts,
330                 &ett_header_uts,
331                 &ett_trailer_uts,
332         };
333
334         proto_uts = proto_register_protocol("Unisys Transmittal System", "UTS", "uts");         /* name, short name, abbrev */
335         proto_register_field_array(proto_uts, hf, array_length(hf));
336         proto_register_subtree_array(ett, array_length(ett));
337         register_dissector("uts", dissect_uts, proto_uts);
338 }