Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/ems.c
Line
Count
Source
1
/* ems.c
2
 *
3
 * File format support for EGNOS Message Server files
4
 * See "Multi-Band EGNOS File Format Description Document" (ESA-EGN-EPO-ICD-0031, Issue 1.4)
5
 *
6
 * Copyright (c) 2023 by Timo Warns <timo.warns@gmail.com>
7
 *
8
 * SPDX-License-Identifier: GPL-2.0-or-later
9
 */
10
11
#include "config.h"
12
0
#define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP
13
14
#include "ems.h"
15
16
#include <stdio.h>
17
18
#include "wtap_module.h"
19
#include "file_wrappers.h"
20
21
#include <wsutil/buffer.h>
22
#include <wsutil/nstime.h>
23
#include <wsutil/strtoi.h>
24
#include <wsutil/wslog.h>
25
26
static bool ems_read(wtap *wth, wtap_rec *rec, int *err, char
27
        **err_info, int64_t *data_offset);
28
static bool ems_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec,
29
        int *err, char **err_info);
30
31
0
#define MAX_EMS_LINE_LEN 1024
32
#define EMS_MSG_SIZE 40
33
34
typedef char ems_line[MAX_EMS_LINE_LEN];
35
36
typedef struct ems_msg_s {
37
    unsigned int prn;
38
    unsigned int year;
39
    unsigned int month;
40
    unsigned int day;
41
    unsigned int hour;
42
    unsigned int minute;
43
    unsigned int second;
44
    unsigned int second_frac;
45
    char band[3];
46
    unsigned int nof_bits;
47
    unsigned int mt;
48
    char sbas_msg[64];
49
} ems_msg_t;
50
51
static int ems_file_type_subtype = -1;
52
53
/**
54
 * Parses an EMS line.
55
 * Return false on error, true otherwise.
56
 */
57
0
static bool parse(ems_line *line, ems_msg_t *msg) {
58
0
    int i;
59
60
    // try to parse for L1
61
0
    i = sscanf((char *)line, "%03u %02u %02u %02u %02u %02u %02u %u %64c",
62
0
                &msg->prn,
63
0
                &msg->year,
64
0
                &msg->month,
65
0
                &msg->day,
66
0
                &msg->hour,
67
0
                &msg->minute,
68
0
                &msg->second,
69
0
                &msg->mt,
70
0
                msg->sbas_msg);
71
72
0
    if (9 != i) {
73
        // L1 parsing failed, try to parse for L5 or non-standard message encoding
74
0
        i = sscanf((char *)line, "%03u %02u %02u %02u %02u %02u %02u.%06u %2s %04x %02u",
75
0
                    &msg->prn,
76
0
                    &msg->year,
77
0
                    &msg->month,
78
0
                    &msg->day,
79
0
                    &msg->hour,
80
0
                    &msg->minute,
81
0
                    &msg->second,
82
0
                    &msg->second_frac,
83
0
                    (char *)&msg->band,
84
0
                    &msg->nof_bits,
85
0
                    &msg->mt);
86
87
0
        if (11 != i) {
88
          // L5 / non-standard message parsing failed as well. So,
89
          // return with error.
90
0
          return false;
91
0
        }
92
0
    }
93
94
    // Do some basic validation
95
    // - SBAS PRNs are limited to 128, ..., 158 by the ICAO SARPS
96
    // - basic date & time constraints
97
    // - SBAS has defined MTs up to 63 only
98
0
    if (msg->prn < 120 || msg->prn > 158 ||
99
0
            msg->year > 255 ||
100
0
            msg->month > 12 ||
101
0
            msg->day > 31 ||
102
0
            msg->hour > 23 ||
103
0
            msg->minute > 59 ||
104
0
            msg->second > 59 ||
105
0
            msg->mt > 63) {
106
0
        return false;
107
0
    }
108
109
0
    return true;
110
0
}
111
112
0
wtap_open_return_val ems_open(wtap *wth, int *err, char **err_info) {
113
0
    ems_line line;
114
0
    ems_msg_t msg;
115
116
0
    ws_debug("opening file");
117
118
    // read complete EMS line
119
0
    if (!file_gets((char *)line, MAX_EMS_LINE_LEN, wth->fh)) {
120
0
        return WTAP_OPEN_ERROR;
121
0
    }
122
123
    // Check whether the current line can be parsed.
124
0
    if (parse(&line, &msg)) {
125
        /* return to the beginning of the file */
126
0
        if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
127
0
            *err = file_error(wth->fh, err_info);
128
0
            return WTAP_OPEN_ERROR;
129
0
        }
130
131
0
        wth->file_encap = WTAP_ENCAP_EMS;
132
0
        wth->snapshot_length = 0;
133
0
        wth->file_tsprec = WTAP_TSPREC_SEC;
134
0
        wth->subtype_read = ems_read;
135
0
        wth->subtype_seek_read = ems_seek_read;
136
0
        wth->file_type_subtype = ems_file_type_subtype;
137
138
0
        return WTAP_OPEN_MINE;
139
0
    }
140
141
0
    return WTAP_OPEN_NOT_MINE;
142
0
}
143
144
static bool ems_read_message(wtap *wth, FILE_T fh, wtap_rec *rec,
145
0
        int *err, char **err_info) {
146
147
0
    ems_line line;
148
0
    ems_msg_t msg = {.second_frac = 0};
149
0
    char *end;
150
151
    // get complete EMS line
152
0
    end = file_getsp((char *)line, MAX_EMS_LINE_LEN, fh);
153
0
    if (!end) {
154
0
        *err = file_error(fh, err_info);
155
0
        return false;
156
0
    }
157
158
    // Check whether the current line can be parsed.
159
0
    if (parse(&line, &msg)) {
160
0
        char ts[NSTIME_ISO8601_BUFSIZE + 1];
161
162
0
        ws_buffer_append(&rec->data, (const uint8_t*)line, end - line);
163
164
0
        wtap_setup_packet_rec(rec, wth->file_encap);
165
0
        rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
166
0
        rec->presence_flags = WTAP_HAS_TS;
167
0
        rec->rec_header.packet_header.len = (uint32_t) (end - line);
168
0
        rec->rec_header.packet_header.caplen = (uint32_t) (end - line);
169
170
        // use EMS timestamp as packet timestamp
171
0
        snprintf(ts, sizeof(ts), "%04u-%02u-%02uT%02u:%02u:%02u.%06uZ",
172
0
                 msg.year + 2000, msg.month, msg.day, msg.hour, msg.minute,
173
0
                 msg.second, msg.second_frac);
174
0
        iso8601_to_nstime(&rec->ts, ts, ISO8601_DATETIME);
175
176
0
        return true;
177
0
    }
178
179
0
    return false;
180
0
}
181
182
static bool ems_read(wtap *wth, wtap_rec *rec, int *err, char **err_info,
183
0
    int64_t *offset) {
184
185
0
    *offset = file_tell(wth->fh);
186
0
    ws_debug("reading at offset %" PRId64, *offset);
187
188
0
    if (!ems_read_message(wth, wth->fh, rec, err, err_info)) {
189
0
        return false;
190
0
    }
191
192
0
    return true;
193
0
}
194
195
static bool ems_seek_read(wtap *wth, int64_t offset, wtap_rec *rec,
196
0
        int *err, char **err_info) {
197
198
0
    if (file_seek(wth->random_fh, offset, SEEK_SET, err) == -1) {
199
0
        *err = file_error(wth->random_fh, err_info);
200
0
        return false;
201
0
    }
202
203
0
    if (!ems_read_message(wth, wth->random_fh, rec, err, err_info)) {
204
0
        return false;
205
0
    }
206
207
0
    return true;
208
0
}
209
210
static const struct supported_block_type ems_blocks_supported[] = {
211
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
212
};
213
214
static const struct file_type_subtype_info ems_info = {
215
    "EGNOS Message Server File Format", "ems", "ems", "ems",
216
    false, BLOCKS_SUPPORTED(ems_blocks_supported),
217
    NULL, NULL, NULL
218
};
219
220
void register_ems(void)
221
16
{
222
16
    ems_file_type_subtype = wtap_register_file_type_subtype(&ems_info);
223
224
16
    wtap_register_backwards_compatibility_lua_name("EMS",
225
16
            ems_file_type_subtype);
226
16
}