xref: /netbsd-src/external/bsd/tcpdump/dist/netdissect.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
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
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
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
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
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 *
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
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
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
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
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
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
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