forked from iagox86/hash_extender
-
Notifications
You must be signed in to change notification settings - Fork 0
/
buffer.h
172 lines (141 loc) · 8.31 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
172
/* 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 <stdint.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. */
uint64_t position;
/* The maximum length of the buffer that "buffer" is pointing to. When
* space in this runs out, it's expanded */
uint64_t max_length;
/* The current length of the buffer. */
uint64_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) */
uint8_t 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 uint64_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. */
uint64_t buffer_get_length(buffer_t *buffer);
/* Get the current location in the buffer. */
uint64_t buffer_get_current_offset(buffer_t *buffer);
/* Set the current location in the buffer. */
void buffer_set_current_offset(buffer_t *buffer, uint64_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, uint64_t align);
void buffer_write_align(buffer_t *buffer, uint64_t align);
/* Consume (discard) bytes. */
void buffer_consume(buffer_t *buffer, uint64_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, uint64_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, uint64_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 uint64_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 uint64_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, uint64_t offset);
buffer_t *buffer_add_int16_at(buffer_t *buffer, const uint16_t data, uint64_t offset);
buffer_t *buffer_add_int32_at(buffer_t *buffer, const uint64_t data, uint64_t offset);
buffer_t *buffer_add_ntstring_at(buffer_t *buffer, const char *data, uint64_t offset);
buffer_t *buffer_add_string_at(buffer_t *buffer, const char *data, uint64_t offset);
buffer_t *buffer_add_unicode_at(buffer_t *buffer, const char *data, uint64_t offset);
buffer_t *buffer_add_bytes_at(buffer_t *buffer, const void *data, const uint64_t length, uint64_t offset);
buffer_t *buffer_add_buffer_at(buffer_t *buffer, const buffer_t *source, uint64_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);
uint64_t buffer_read_next_int32(buffer_t *buffer);
char *buffer_read_next_ntstring(buffer_t *buffer, char *data_ret, uint64_t max_length);
char *buffer_read_next_unicode(buffer_t *buffer, char *data_ret, uint64_t max_length);
char *buffer_read_next_unicode_data(buffer_t *buffer, char *data_ret, uint64_t length);
void *buffer_read_next_bytes(buffer_t *buffer, void *data, uint64_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);
uint64_t buffer_peek_next_int32(buffer_t *buffer);
char *buffer_peek_next_ntstring(buffer_t *buffer, char *data_ret, uint64_t max_length);
char *buffer_peek_next_unicode(buffer_t *buffer, char *data_ret, uint64_t max_length);
void *buffer_peek_next_bytes(buffer_t *buffer, void *data, uint64_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, uint64_t offset);
uint16_t buffer_read_int16_at(buffer_t *buffer, uint64_t offset);
uint64_t buffer_read_int32_at(buffer_t *buffer, uint64_t offset);
char *buffer_read_ntstring_at(buffer_t *buffer, uint64_t offset, char *data_ret, uint64_t max_length);
char *buffer_read_unicode_at(buffer_t *buffer, uint64_t offset, char *data_ret, uint64_t max_length);
char *buffer_read_unicode_data_at(buffer_t *buffer, uint64_t offset, char *data_ret, uint64_t length);
void *buffer_read_bytes_at(buffer_t *buffer, uint64_t offset, void *data, uint64_t length);
/* These uint8_t 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. */
uint8_t buffer_can_read_int8(buffer_t *buffer);
uint8_t buffer_can_read_int16(buffer_t *buffer);
uint8_t buffer_can_read_int32(buffer_t *buffer);
uint8_t buffer_can_read_ntstring(buffer_t *buffer);
uint8_t buffer_can_read_unicode(buffer_t *buffer);
uint8_t buffer_can_read_bytes(buffer_t *buffer, uint64_t length);
/* These functions check if there are enough bytes in the buffer at the specified location. */
uint8_t buffer_can_read_int8_at(buffer_t *buffer, uint64_t offset);
uint8_t buffer_can_read_int16_at(buffer_t *buffer, uint64_t offset);
uint8_t buffer_can_read_int32_at(buffer_t *buffer, uint64_t offset);
uint8_t buffer_can_read_ntstring_at(buffer_t *buffer, uint64_t offset, uint64_t max_length);
uint8_t buffer_can_read_unicode_at(buffer_t *buffer, uint64_t offset, uint64_t max_length);
uint8_t buffer_can_read_bytes_at(buffer_t *buffer, uint64_t offset, uint64_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, uint64_t *length);
#endif