-
Notifications
You must be signed in to change notification settings - Fork 29
/
buffer.h
171 lines (140 loc) · 8.29 KB
/
buffer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* buffer.h
* By Ron
* Created August, 2008
*
* (See LICENSE.txt)
*
* This is a generic class for buffering (marshalling) data that in many ways dates back
* to my days as a Battle.net developers. It gives programmers an easy way to prepare
* data to be sent on the network, as well as a simplified way to build strings in a
* language where string building isn't always straight forward. In many ways, it's like
* the pack()/unpack() functions from Perl.
*
* I've strived to keep this implementation the same on every platform. You'll notice
* that there's no Windows-specific code here, and also that all calculations will work
* on both a little- or big-endian system. The only time you won't get the same results
* is when you create a buffer with the type BO_HOST, which always uses the host's byte
* ordering. I don't recommend that.
*/
#ifndef __PACKET_BUFFER_H__
#define __PACKET_BUFFER_H__
#include "types.h"
typedef enum
{
BO_HOST, /* Use the byte order of the host (bad idea -- changes on systems. */
BO_NETWORK, /* Use network byte order which, as it turns out, is big endian. */
BO_LITTLE_ENDIAN, /* Use big endian byte ordering (0x12345678 => 78 56 34 12). */
BO_BIG_ENDIAN, /* Use big endian byte ordering (0x12345678 => 12 34 56 78). */
} BYTE_ORDER_t;
/* This struct shouldn't be accessed directly */
typedef struct
{
/* Byte order to use */
BYTE_ORDER_t byte_order;
/* The current position in the string, used when reading it. */
uint32_t position;
/* The maximum length of the buffer that "buffer" is pointing to. When
* space in this runs out, it's expanded */
uint32_t max_length;
/* The current length of the buffer. */
uint32_t current_length;
/* The current buffer. Will always point to a string of length max_length */
uint8_t *data;
/* Set to FALSE when the packet is destroyed, to make sure I don't accidentally
* re-use it (again) */
NBBOOL valid;
} buffer_t;
/* Create a new packet buffer */
buffer_t *buffer_create(BYTE_ORDER_t byte_order);
/* Create a new packet buffer, with data. */
buffer_t *buffer_create_with_data(BYTE_ORDER_t byte_order, const void *data, const uint32_t length);
/* Destroy the buffer and free resources. If this isn't used, memory will leak. */
void buffer_destroy(buffer_t *buffer);
/* Makes a copy of the buffer. */
buffer_t *buffer_duplicate(buffer_t *base);
/* Get the length of the buffer. */
uint32_t buffer_get_length(buffer_t *buffer);
/* Get the current location in the buffer. */
uint32_t buffer_get_current_offset(buffer_t *buffer);
/* Set the current location in the buffer. */
void buffer_set_current_offset(buffer_t *buffer, uint32_t position);
/* Clear out the buffer. Memory is kept, but the contents are blanked out and the pointer is returned
* to the beginning. */
void buffer_clear(buffer_t *buffer);
/* Align the buffer to even multiples. */
void buffer_read_align(buffer_t *buffer, uint32_t align);
void buffer_write_align(buffer_t *buffer, uint32_t align);
/* Consume (discard) bytes. */
void buffer_consume(buffer_t *buffer, uint32_t count);
/* Return the contents of the buffer in a newly allocated string. Fill in the length, if a pointer
* is given. Note that this allocates memory that has to be freed! */
uint8_t *buffer_create_string(buffer_t *buffer, uint32_t *length);
/* Does the same thing as above, but also frees up the buffer (good for a function return). */
uint8_t *buffer_create_string_and_destroy(buffer_t *buffer, uint32_t *length);
/* Add data to the end of the buffer */
buffer_t *buffer_add_int8(buffer_t *buffer, const uint8_t data);
buffer_t *buffer_add_int16(buffer_t *buffer, const uint16_t data);
buffer_t *buffer_add_int32(buffer_t *buffer, const uint32_t data);
buffer_t *buffer_add_ntstring(buffer_t *buffer, const char *data);
buffer_t *buffer_add_string(buffer_t *buffer, const char *data);
/* Note: UNICODE support is a hack -- it adds every second character as a NULL, but is otherwise ASCII. */
buffer_t *buffer_add_unicode(buffer_t *buffer, const char *data);
buffer_t *buffer_add_bytes(buffer_t *buffer, const void *data, const uint32_t length);
buffer_t *buffer_add_buffer(buffer_t *buffer, const buffer_t *source);
/* Add data to the middle of a buffer. These functions won't write past the end of the buffer, so it's
* up to the programmer to be careful. */
buffer_t *buffer_add_int8_at(buffer_t *buffer, const uint8_t data, uint32_t offset);
buffer_t *buffer_add_int16_at(buffer_t *buffer, const uint16_t data, uint32_t offset);
buffer_t *buffer_add_int32_at(buffer_t *buffer, const uint32_t data, uint32_t offset);
buffer_t *buffer_add_ntstring_at(buffer_t *buffer, const char *data, uint32_t offset);
buffer_t *buffer_add_string_at(buffer_t *buffer, const char *data, uint32_t offset);
buffer_t *buffer_add_unicode_at(buffer_t *buffer, const char *data, uint32_t offset);
buffer_t *buffer_add_bytes_at(buffer_t *buffer, const void *data, const uint32_t length, uint32_t offset);
buffer_t *buffer_add_buffer_at(buffer_t *buffer, const buffer_t *source, uint32_t offset);
/* Read the next data from the buffer. The first read will be at the beginning.
* An assertion will fail and the program will end if read off
* the end of the buffer; it's probably a good idea to verify that enough data can be removed
* before actually attempting to remove it; otherwise, a DoS condition can occur */
uint8_t buffer_read_next_int8(buffer_t *buffer);
uint16_t buffer_read_next_int16(buffer_t *buffer);
uint32_t buffer_read_next_int32(buffer_t *buffer);
char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, uint32_t max_length);
char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, uint32_t max_length);
char *buffer_read_next_unicode_data(buffer_t *buffer, char *data_ret, uint32_t length);
void *buffer_read_next_bytes(buffer_t *buffer, void *data, uint32_t length);
/* Read the next data, without incrementing the current pointer. */
uint8_t buffer_peek_next_int8(buffer_t *buffer);
uint16_t buffer_peek_next_int16(buffer_t *buffer);
uint32_t buffer_peek_next_int32(buffer_t *buffer);
char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, uint32_t max_length);
char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, uint32_t max_length);
void *buffer_peek_next_bytes(buffer_t *buffer, void *data, uint32_t length);
/* Read data at the specified location in the buffer (counting the first byte as 0). */
uint8_t buffer_read_int8_at(buffer_t *buffer, uint32_t offset);
uint16_t buffer_read_int16_at(buffer_t *buffer, uint32_t offset);
uint32_t buffer_read_int32_at(buffer_t *buffer, uint32_t offset);
char *buffer_read_ntstring_at(buffer_t *buffer, uint32_t offset, char *data_ret, uint32_t max_length);
char *buffer_read_unicode_at(buffer_t *buffer, uint32_t offset, char *data_ret, uint32_t max_length);
char *buffer_read_unicode_data_at(buffer_t *buffer, uint32_t offset, char *data_ret, uint32_t length);
void *buffer_read_bytes_at(buffer_t *buffer, uint32_t offset, void *data, uint32_t length);
/* These NBBOOL functions check if there are enough bytes left in the buffer to remove
* specified data. These should always be used on the server side to verify valid
* packets for a critical service. */
NBBOOL buffer_can_read_int8(buffer_t *buffer);
NBBOOL buffer_can_read_int16(buffer_t *buffer);
NBBOOL buffer_can_read_int32(buffer_t *buffer);
NBBOOL buffer_can_read_ntstring(buffer_t *buffer);
NBBOOL buffer_can_read_unicode(buffer_t *buffer);
NBBOOL buffer_can_read_bytes(buffer_t *buffer, uint32_t length);
/* These functions check if there are enough bytes in the buffer at the specified location. */
NBBOOL buffer_can_read_int8_at(buffer_t *buffer, uint32_t offset);
NBBOOL buffer_can_read_int16_at(buffer_t *buffer, uint32_t offset);
NBBOOL buffer_can_read_int32_at(buffer_t *buffer, uint32_t offset);
NBBOOL buffer_can_read_ntstring_at(buffer_t *buffer, uint32_t offset, uint32_t max_length);
NBBOOL buffer_can_read_unicode_at(buffer_t *buffer, uint32_t offset, uint32_t max_length);
NBBOOL buffer_can_read_bytes_at(buffer_t *buffer, uint32_t offset, uint32_t length);
/* Print out the buffer in a nice format -- useful for debugging. */
void buffer_print(buffer_t *buffer);
/* Returns a pointer to the actual buffer (I don't recommend using this). */
uint8_t *buffer_get(buffer_t *buffer, uint32_t *length);
#endif