Coverage Report

Created: 2026-01-02 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-lpd.c
Line
Count
Source
1
/* packet-lpd.c
2
 * Routines for LPR and LPRng packet disassembly
3
 * Gilbert Ramirez <gram@alumni.rice.edu>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
16
void proto_register_lpd(void);
17
void proto_reg_handoff_lpd(void);
18
19
static dissector_handle_t lpd_handle;
20
21
14
#define TCP_PORT_PRINTER    515
22
23
static int proto_lpd;
24
static int hf_lpd_response;
25
static int hf_lpd_request;
26
static int hf_lpd_client_code;
27
static int hf_lpd_printer_option;
28
static int hf_lpd_response_code;
29
30
static int ett_lpd;
31
32
enum lpr_type { request, response, unknown };
33
34
static int find_printer_string(tvbuff_t *tvb, int offset);
35
36
/* This information comes from the LPRng HOWTO, which also describes
37
  RFC 1179. http://www.astart.com/lprng/LPRng-HOWTO.html */
38
static const value_string lpd_client_code[] = {
39
  { 1, "LPC: start print / jobcmd: abort" },
40
  { 2, "LPR: transfer a printer job / jobcmd: receive control file" },
41
  { 3, "LPQ: print short form of queue status / jobcmd: receive data file" },
42
  { 4, "LPQ: print long form of queue status" },
43
  { 5, "LPRM: remove jobs" },
44
  { 6, "LPRng lpc: do control operation" },
45
  { 7, "LPRng lpr: transfer a block format print job" },
46
  { 8, "LPRng lpc: secure command transfer" },
47
  { 9, "LPRng lpq: verbose status information" },
48
  { 0, NULL }
49
};
50
static const value_string lpd_server_code[] = {
51
  { 0, "Success: accepted, proceed" },
52
  { 1, "Queue not accepting jobs" },
53
  { 2, "Queue temporarily full, retry later" },
54
  { 3, "Bad job format, do not retry" },
55
  { 0, NULL }
56
};
57
58
static int
59
dissect_lpd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
60
12
{
61
12
  proto_tree  *lpd_tree;
62
12
  proto_item  *ti, *hidden_item;
63
12
  enum lpr_type lpr_packet_type;
64
12
  uint8_t   code;
65
12
  int   printer_len;
66
67
12
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "LPD");
68
12
  col_clear(pinfo->cinfo, COL_INFO);
69
70
  /* rfc1179 states that all responses are 1 byte long */
71
12
  code = tvb_get_uint8(tvb, 0);
72
12
  if (tvb_reported_length(tvb) == 1) {
73
1
    lpr_packet_type = response;
74
1
  }
75
11
  else if (code <= 9) {
76
4
    lpr_packet_type = request;
77
4
  }
78
7
  else {
79
7
    lpr_packet_type = unknown;
80
7
  }
81
82
12
  if (lpr_packet_type == request && code !=0) {
83
1
    col_add_str(pinfo->cinfo, COL_INFO, val_to_str(pinfo->pool, code, lpd_client_code, "Unknown client code: %u"));
84
1
  }
85
11
  else if (lpr_packet_type == response) {
86
1
    col_set_str(pinfo->cinfo, COL_INFO, "LPD response");
87
1
  }
88
10
  else {
89
10
    col_set_str(pinfo->cinfo, COL_INFO, "LPD continuation");
90
10
  }
91
92
12
    ti = proto_tree_add_item(tree, proto_lpd, tvb, 0, -1, ENC_NA);
93
12
    lpd_tree = proto_item_add_subtree(ti, ett_lpd);
94
95
12
    if (lpr_packet_type == response) {
96
1
      hidden_item = proto_tree_add_boolean(lpd_tree, hf_lpd_response,
97
1
            tvb, 0, 0, true);
98
11
    } else {
99
11
      hidden_item = proto_tree_add_boolean(lpd_tree, hf_lpd_request,
100
11
            tvb, 0, 0, true);
101
11
    }
102
12
    proto_item_set_hidden(hidden_item);
103
104
12
    if (lpr_packet_type == request) {
105
4
      printer_len = find_printer_string(tvb, 1);
106
107
4
      if (code <= 9 && printer_len != -1) {
108
4
        proto_tree_add_uint_format(lpd_tree, hf_lpd_client_code, tvb, 0, 1, code,
109
4
          "%s", val_to_str(pinfo->pool, code, lpd_client_code, "Unknown client code: %u"));
110
4
        proto_tree_add_item(lpd_tree, hf_lpd_printer_option, tvb, 1, printer_len, ENC_ASCII);
111
4
      }
112
0
      else {
113
0
        call_data_dissector(tvb, pinfo, lpd_tree);
114
0
      }
115
4
    }
116
8
    else if (lpr_packet_type == response) {
117
1
      if (code <= 3) {
118
1
        proto_tree_add_item(lpd_tree, hf_lpd_response_code, tvb, 0, 1, ENC_BIG_ENDIAN);
119
1
      }
120
0
      else {
121
0
        call_data_dissector(tvb, pinfo, lpd_tree);
122
0
      }
123
1
    }
124
7
    else {
125
7
      call_data_dissector(tvb, pinfo, lpd_tree);
126
7
    }
127
128
12
  return tvb_captured_length(tvb);
129
12
}
130
131
132
static int
133
find_printer_string(tvbuff_t *tvb, int offset)
134
4
{
135
4
  int i;
136
137
  /* try to find end of string, either '\n' or '\0' */
138
4
  i = tvb_find_uint8(tvb, offset, -1, '\0');
139
4
  if (i == -1)
140
0
    i = tvb_find_uint8(tvb, offset, -1, '\n');
141
4
  if (i == -1)
142
0
    return -1;
143
4
  return i - offset; /* length of string */
144
4
}
145
146
147
void
148
proto_register_lpd(void)
149
14
{
150
14
  static hf_register_info hf[] = {
151
14
    { &hf_lpd_response,
152
14
      { "Response",           "lpd.response",
153
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x0,
154
14
        "true if LPD response", HFILL }},
155
156
14
    { &hf_lpd_request,
157
14
      { "Request",            "lpd.request",
158
14
        FT_BOOLEAN, BASE_NONE, NULL, 0x0,
159
14
        "true if LPD request", HFILL }},
160
161
14
    { &hf_lpd_client_code,
162
14
      { "Client code",            "lpd.client_code",
163
14
        FT_UINT8, BASE_DEC, VALS(lpd_client_code), 0x0,
164
14
        NULL, HFILL }},
165
166
14
    { &hf_lpd_printer_option,
167
14
      { "Printer/options",            "lpd.printer_option",
168
14
        FT_STRING, BASE_NONE, NULL, 0x0,
169
14
        NULL, HFILL }},
170
171
14
    { &hf_lpd_response_code,
172
14
      { "Response",            "lpd.response_code",
173
14
        FT_UINT8, BASE_DEC, VALS(lpd_server_code), 0x0,
174
14
        NULL, HFILL }},
175
14
  };
176
14
  static int *ett[] = {
177
14
    &ett_lpd,
178
14
  };
179
180
14
  proto_lpd = proto_register_protocol("Line Printer Daemon Protocol", "LPD", "lpd");
181
14
  lpd_handle = register_dissector("lpd", dissect_lpd, proto_lpd);
182
14
  proto_register_field_array(proto_lpd, hf, array_length(hf));
183
14
  proto_register_subtree_array(ett, array_length(ett));
184
14
}
185
186
void
187
proto_reg_handoff_lpd(void)
188
14
{
189
14
  dissector_add_uint_with_preference("tcp.port", TCP_PORT_PRINTER, lpd_handle);
190
14
}
191
192
/*
193
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
194
 *
195
 * Local variables:
196
 * c-basic-offset: 8
197
 * tab-width: 8
198
 * indent-tabs-mode: t
199
 * End:
200
 *
201
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
202
 * :indentSize=8:tabSize=8:noTabs=false:
203
 */