1 /*
2 ** Copyright (C) 2014-2021 Cisco and/or its affiliates. All rights reserved.
3 ** Copyright (C) 2005-2013 Sourcefire, Inc.
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License Version 2 as
7 ** published by the Free Software Foundation.  You may not use, modify or
8 ** distribute this program under any other version of the GNU General
9 ** Public License.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20 
21 
22 #include <ctype.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 
29 #include "flow.h"
30 #include "service_api.h"
31 
32 #define LPR_COUNT_THRESHOLD 5
33 
34 typedef enum
35 {
36     LPR_STATE_COMMAND,
37     LPR_STATE_RECEIVE,
38     LPR_STATE_REPLY1,
39     LPR_STATE_REPLY,
40     LPR_STATE_IGNORE
41 } LPRState;
42 
43 typedef enum
44 {
45     LPR_CMD_PRINT = 1,
46     LPR_CMD_RECEIVE,
47     LPR_CMD_SHORT_STATE,
48     LPR_CMD_LONG_STATE,
49     LPR_CMD_REMOVE
50 } LPRCommand;
51 
52 typedef enum
53 {
54     LPR_SUBCMD_ABORT = 1,
55     LPR_SUBCMD_CONTROL,
56     LPR_SUBCMD_DATA
57 } LPRSubCommand;
58 
59 typedef struct _SERVICE_LPR_DATA
60 {
61     LPRState state;
62     unsigned no_data_count;
63     unsigned count;
64 } ServiceLPRData;
65 
66 static int lpr_init(const InitServiceAPI * const init_api);
67 static int lpr_validate(ServiceValidationArgs* args);
68 
69 static tRNAServiceElement svc_element =
70 {
71     .next = NULL,
72     .validate = &lpr_validate,
73     .detectorType = DETECTOR_TYPE_DECODER,
74     .name = "lpr",
75     .ref_count = 1,
76     .current_ref_count = 1,
77 };
78 
79 static RNAServiceValidationPort pp[] =
80 {
81     {&lpr_validate, 515, IPPROTO_TCP},
82     {NULL, 0, 0}
83 };
84 
85 tRNAServiceValidationModule lpr_service_mod =
86 {
87     "LPR",
88     &lpr_init,
89     pp
90 };
91 
92 static tAppRegistryEntry appIdRegistry[] = {{APP_ID_PRINTSRV, 0}};
93 
lpr_init(const InitServiceAPI * const init_api)94 static int lpr_init(const InitServiceAPI * const init_api)
95 {
96 	unsigned i;
97 	for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++)
98 	{
99 		_dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId);
100 		init_api->RegisterAppId(&lpr_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig);
101 	}
102 
103     return 0;
104 }
105 
lpr_validate(ServiceValidationArgs * args)106 static int lpr_validate(ServiceValidationArgs* args)
107 {
108     ServiceLPRData *ld;
109     int i;
110     tAppIdData *flowp = args->flowp;
111     const uint8_t *data = args->data;
112     const int dir = args->dir;
113     uint16_t size = args->size;
114 
115     if (!size) goto inprocess;
116 
117     ld = lpr_service_mod.api->data_get(flowp, lpr_service_mod.flow_data_index);
118     if (!ld)
119     {
120         ld = calloc(1, sizeof(*ld));
121         if (!ld)
122             return SERVICE_ENOMEM;
123         if (lpr_service_mod.api->data_add(flowp, ld, lpr_service_mod.flow_data_index, &free))
124         {
125             free(ld);
126             return SERVICE_ENOMEM;
127         }
128         ld->state = LPR_STATE_COMMAND;
129     }
130 
131     switch (ld->state)
132     {
133     case LPR_STATE_COMMAND:
134         if (dir != APP_ID_FROM_INITIATOR)
135             goto bail;
136         if (size < 3) goto bail;
137         switch (*data)
138         {
139         case LPR_CMD_RECEIVE:
140             if (data[size-1] != 0x0A) goto bail;
141             size--;
142             for (i=1; i<size; i++)
143                 if (!isprint(data[i]) || isspace(data[i])) goto bail;
144             ld->state = LPR_STATE_REPLY;
145             break;
146         case LPR_CMD_PRINT:
147             ld->state = LPR_STATE_IGNORE;
148             break;
149         case LPR_CMD_SHORT_STATE:
150             ld->state = LPR_STATE_IGNORE;
151             break;
152         case LPR_CMD_LONG_STATE:
153             ld->state = LPR_STATE_IGNORE;
154             break;
155         case LPR_CMD_REMOVE:
156             ld->state = LPR_STATE_IGNORE;
157             break;
158         default:
159             goto bail;
160         }
161         break;
162     case LPR_STATE_RECEIVE:
163         if (dir != APP_ID_FROM_INITIATOR) goto inprocess;
164         if (size < 2) goto bail;
165         switch (*data)
166         {
167         case LPR_SUBCMD_ABORT:
168             if (size != 2) goto bail;
169             if (data[1] != 0x0A) goto bail;
170             ld->state = LPR_STATE_REPLY;
171             break;
172         case LPR_SUBCMD_CONTROL:
173         case LPR_SUBCMD_DATA:
174             if (size < 5) goto bail;
175             if (data[size-1] != 0x0A) goto bail;
176             if (!isdigit(data[1])) goto bail;
177             for (i=2; i<size; i++)
178             {
179                 if (data[i] == 0x0A) goto bail;
180                 else if (isspace(data[i])) break;
181                 if (!isdigit(data[i])) goto bail;
182             }
183             i++;
184             if (i >= size) goto bail;
185             for (; i<size-1; i++)
186                 if (!isprint(data[i]) || isspace(data[i])) goto bail;
187             ld->state = LPR_STATE_REPLY1;
188             break;
189         default:
190             goto bail;
191         }
192         break;
193     case LPR_STATE_REPLY1:
194         if (dir != APP_ID_FROM_RESPONDER) goto inprocess;
195         if (size != 1) goto fail;
196         ld->count++;
197         if (ld->count >= LPR_COUNT_THRESHOLD)
198         {
199             ld->state = LPR_STATE_IGNORE;
200             goto success;
201         }
202         ld->state = LPR_STATE_REPLY;
203         break;
204     case LPR_STATE_REPLY:
205         if (dir != APP_ID_FROM_RESPONDER) goto inprocess;
206         if (size != 1) goto fail;
207         ld->count++;
208         if (ld->count >= LPR_COUNT_THRESHOLD)
209         {
210             ld->state = LPR_STATE_IGNORE;
211             goto success;
212         }
213         ld->state = LPR_STATE_RECEIVE;
214         break;
215     case LPR_STATE_IGNORE:
216         break;
217     default:
218         goto bail;
219     }
220 inprocess:
221     lpr_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL);
222     return SERVICE_INPROCESS;
223 
224 success:
225     lpr_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element,
226                                      APP_ID_PRINTSRV, NULL, NULL, NULL, NULL);
227     return SERVICE_SUCCESS;
228 
229 fail:
230     lpr_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element,
231                                       lpr_service_mod.flow_data_index, args->pConfig, NULL);
232     return SERVICE_NOMATCH;
233 
234 bail:
235     lpr_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element,
236                                            lpr_service_mod.flow_data_index, args->pConfig, NULL);
237     return SERVICE_NOT_COMPATIBLE;
238 }
239 
240