1 /*
2  * Copyright (c) 1988-1997
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * Copyright (c) 1998-2012  Michael Richardson <mcr@tcpdump.org>
6  *      The TCPDUMP project
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that: (1) source code distributions
10  * retain the above copyright notice and this paragraph in its entirety, (2)
11  * distributions including binary code include the above copyright notice and
12  * this paragraph in its entirety in the documentation or other materials
13  * provided with the distribution, and (3) all advertising materials mentioning
14  * features or use of this software display the following acknowledgement:
15  * ``This product includes software developed by the University of California,
16  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
17  * the University nor the names of its contributors may be used to endorse
18  * or promote products derived from this software without specific prior
19  * written permission.
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
21  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23  */
24 
25 #include <sys/cdefs.h>
26 #ifndef lint
27 __RCSID("$NetBSD: netdissect.c,v 1.4 2024/09/02 16:15:30 christos Exp $");
28 #endif
29 
30 #include <config.h>
31 
32 #include "netdissect-stdinc.h"
33 #include "netdissect.h"
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 
38 #ifdef USE_LIBSMI
39 #include <smi.h>
40 #endif
41 
42 /*
43  * Initialize anything that must be initialized before dissecting
44  * packets.
45  *
46  * This should be called at the beginning of the program; it does
47  * not need to be called, and should not be called, for every
48  * netdissect_options structure.
49  */
50 int
nd_init(char * errbuf,size_t errbuf_size)51 nd_init(char *errbuf, size_t errbuf_size)
52 {
53 #ifdef _WIN32
54           WORD wVersionRequested;
55           WSADATA wsaData;
56           int err;
57 
58           /*
59            * Request Winsock 2.2; we expect Winsock 2.
60            */
61           wVersionRequested = MAKEWORD(2, 2);
62           err = WSAStartup(wVersionRequested, &wsaData);
63           if (err != 0) {
64                     strlcpy(errbuf, "Attempting to initialize Winsock failed",
65                         errbuf_size);
66                     return (-1);
67           }
68 #endif /* _WIN32 */
69 
70 #ifdef USE_LIBSMI
71           /*
72            * XXX - should we just fail if this fails?  Some of the
73            * libsmi calls may fail.
74            */
75           smiInit("tcpdump");
76 #endif
77 
78           /*
79            * Clears the error buffer, and uses it so we don't get
80            * "unused argument" warnings at compile time.
81            */
82           strlcpy(errbuf, "", errbuf_size);
83           return (0);
84 }
85 
86 /*
87  * Clean up anything that ndo_init() did.
88  */
89 void
nd_cleanup(void)90 nd_cleanup(void)
91 {
92 #ifdef USE_LIBSMI
93           /*
94            * This appears, in libsmi 0.4.8, to do nothing if smiInit()
95            * wasn't done or failed, so we call it unconditionally.
96            */
97           smiExit();
98 #endif
99 
100 #ifdef _WIN32
101           /*
102            * Undo the WSAStartup() call above.
103            */
104           WSACleanup();
105 #endif
106 }
107 
108 int
nd_have_smi_support(void)109 nd_have_smi_support(void)
110 {
111 #ifdef USE_LIBSMI
112           return (1);
113 #else
114           return (0);
115 #endif
116 }
117 
118 /*
119  * Indicates whether an SMI module has been loaded, so that we can use
120  * libsmi to translate OIDs.
121  */
122 int nd_smi_module_loaded;
123 
124 int
nd_load_smi_module(const char * module,char * errbuf,size_t errbuf_size)125 nd_load_smi_module(const char *module, char *errbuf, size_t errbuf_size)
126 {
127 #ifdef USE_LIBSMI
128           if (smiLoadModule(module) == 0) {
129                     snprintf(errbuf, errbuf_size, "could not load MIB module %s",
130                         module);
131                     return (-1);
132           }
133           nd_smi_module_loaded = 1;
134           return (0);
135 #else
136           snprintf(errbuf, errbuf_size, "MIB module %s not loaded: no libsmi support",
137               module);
138           return (-1);
139 #endif
140 }
141 
142 const char *
nd_smi_version_string(void)143 nd_smi_version_string(void)
144 {
145 #ifdef USE_LIBSMI
146           return (smi_version_string);
147 #else
148           return (NULL);
149 #endif
150 }
151 
152 
153 int
nd_push_buffer(netdissect_options * ndo,u_char * new_buffer,const u_char * new_packetp,const u_int newlen)154 nd_push_buffer(netdissect_options *ndo, u_char *new_buffer,
155                  const u_char *new_packetp, const u_int newlen)
156 {
157           struct netdissect_saved_packet_info *ndspi;
158 
159           ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
160           if (ndspi == NULL)
161                     return (0);         /* fail */
162           ndspi->ndspi_buffer = new_buffer;
163           ndspi->ndspi_packetp = ndo->ndo_packetp;
164           ndspi->ndspi_snapend = ndo->ndo_snapend;
165           ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
166 
167           ndo->ndo_packetp = new_packetp;
168           ndo->ndo_snapend = new_packetp + newlen;
169           ndo->ndo_packet_info_stack = ndspi;
170 
171           return (1);         /* success */
172 }
173 
174 
175 /*
176  * In a given netdissect_options structure:
177  *
178  *    push the current packet information onto the packet information
179  *    stack;
180  *
181  *    given a pointer into the packet and a length past that point in
182  *    the packet, calculate a new snapshot end that's at the lower
183  *    of the current snapshot end and that point in the packet;
184  *
185  *    set the snapshot end to that new value.
186  */
187 int
nd_push_snaplen(netdissect_options * ndo,const u_char * bp,const u_int newlen)188 nd_push_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
189 {
190           struct netdissect_saved_packet_info *ndspi;
191           u_int snaplen_remaining;
192 
193           ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
194           if (ndspi == NULL)
195                     return (0);         /* fail */
196           ndspi->ndspi_buffer = NULL;   /* no new buffer */
197           ndspi->ndspi_packetp = ndo->ndo_packetp;
198           ndspi->ndspi_snapend = ndo->ndo_snapend;
199           ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
200 
201           /*
202            * Push the saved previous data onto the stack.
203            */
204           ndo->ndo_packet_info_stack = ndspi;
205 
206           /*
207            * Find out how many bytes remain after the current snapend.
208            *
209            * We're restricted to packets with at most UINT_MAX bytes;
210            * cast the result to u_int, so that we don't get truncation
211            * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
212            * signed and we want an unsigned difference; the pointer
213            * should at most be equal to snapend, and must *never*
214            * be past snapend.)
215            */
216           snaplen_remaining = (u_int)(ndo->ndo_snapend - bp);
217 
218           /*
219            * If the new snapend is smaller than the one calculated
220            * above, set the snapend to that value, otherwise leave
221            * it unchanged.
222            */
223           if (newlen <= snaplen_remaining) {
224                     /* Snapend isn't past the previous snapend */
225                     ndo->ndo_snapend = bp + newlen;
226           }
227 
228           return (1);         /* success */
229 }
230 
231 /*
232  * In a given netdissect_options structure:
233  *
234  *    given a pointer into the packet and a length past that point in
235  *    the packet, calculate a new snapshot end that's at the lower
236  *    of the previous snapshot end - or, if there is no previous
237  *    snapshot end, the current snapshot end - and that point in the
238  *    packet;
239  *
240  *    set the snapshot end to that new value.
241  *
242  * This is to change the current snapshot end.  This may increase the
243  * snapshot end, as it may be used, for example, for a Jumbo Payload
244  * option in IPv6.  It must not increase it past the snapshot length
245  * atop which the current one was pushed, however.
246  */
247 void
nd_change_snaplen(netdissect_options * ndo,const u_char * bp,const u_int newlen)248 nd_change_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
249 {
250           struct netdissect_saved_packet_info *ndspi;
251           const u_char *previous_snapend;
252           u_int snaplen_remaining;
253 
254           ndspi = ndo->ndo_packet_info_stack;
255           if (ndspi->ndspi_prev != NULL)
256                     previous_snapend = ndspi->ndspi_prev->ndspi_snapend;
257           else
258                     previous_snapend = ndo->ndo_snapend;
259 
260           /*
261            * Find out how many bytes remain after the previous
262            * snapend - or, if there is no previous snapend, after
263            * the current snapend.
264            *
265            * We're restricted to packets with at most UINT_MAX bytes;
266            * cast the result to u_int, so that we don't get truncation
267            * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
268            * signed and we want an unsigned difference; the pointer
269            * should at most be equal to snapend, and must *never*
270            * be past snapend.)
271            */
272           snaplen_remaining = (u_int)(previous_snapend - bp);
273 
274           /*
275            * If the new snapend is smaller than the one calculated
276            * above, set the snapend to that value, otherwise leave
277            * it unchanged.
278            */
279           if (newlen <= snaplen_remaining) {
280                     /* Snapend isn't past the previous snapend */
281                     ndo->ndo_snapend = bp + newlen;
282           }
283 }
284 
285 void
nd_pop_packet_info(netdissect_options * ndo)286 nd_pop_packet_info(netdissect_options *ndo)
287 {
288           struct netdissect_saved_packet_info *ndspi;
289 
290           ndspi = ndo->ndo_packet_info_stack;
291           ndo->ndo_packetp = ndspi->ndspi_packetp;
292           ndo->ndo_snapend = ndspi->ndspi_snapend;
293           ndo->ndo_packet_info_stack = ndspi->ndspi_prev;
294 
295           free(ndspi->ndspi_buffer);
296           free(ndspi);
297 }
298 
299 void
nd_pop_all_packet_info(netdissect_options * ndo)300 nd_pop_all_packet_info(netdissect_options *ndo)
301 {
302           while (ndo->ndo_packet_info_stack != NULL)
303                     nd_pop_packet_info(ndo);
304 }
305 
306 NORETURN void
nd_trunc_longjmp(netdissect_options * ndo)307 nd_trunc_longjmp(netdissect_options *ndo)
308 {
309           longjmp(ndo->ndo_early_end, ND_TRUNCATED);
310 #ifdef _AIX
311           /*
312            * In AIX <setjmp.h> decorates longjmp() with "#pragma leaves", which tells
313            * XL C that the function is noreturn, but GCC remains unaware of that and
314            * yields a "'noreturn' function does return" warning.
315            */
316           ND_UNREACHABLE
317 #endif /* _AIX */
318 }
319