-
Notifications
You must be signed in to change notification settings - Fork 0
/
ngx_stream_minecraft_forward_module_utils.c
177 lines (134 loc) · 4.45 KB
/
ngx_stream_minecraft_forward_module_utils.c
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
173
174
175
176
177
#include "ngx_stream_minecraft_forward_module_utils.h"
#include "ngx_stream_minecraft_forward_module.h"
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_regex.h>
#include <ngx_stream.h>
/*
Reference: https://wiki.vg/index.php?title=Protocol&oldid=7617#VarInt_and_VarLong
*/
/*
Varint is used a lot in Minecraft packet.
This function tries to parse varint and return actual integer value.
Result should be non-negative. In case anything, it would return -1 indicating failure.
\param *buf Nginx buffer pointer.
\param *byte_len A `size_t` pointer to which the length of varint bytes will be stored. Optional, pass `NULL` if no need.
\returns Actual int value represented by the varint. If read failure, -1.
*/
ngx_int_t read_minecraft_varint(u_char *buf, size_t *byte_len) {
if (!buf) {
return -1;
}
ngx_int_t value;
ngx_int_t position;
u_char byte;
u_char *pos;
value = 0;
position = 0;
pos = buf;
for (;;) {
byte = *pos;
value |= (byte & 0x7F) << position;
if ((byte & 0x80) == 0) {
break;
}
position += 7;
if (position >= 32) {
value = -1;
break;
}
++pos;
}
if (value < 0) {
return -1;
}
if (byte_len != NULL) {
*byte_len = pos - buf + 1;
}
return value;
}
/*
Modern Minecraft Java protocol packet has a preceding varint that indicates the whole packet's length (not including varint bytes themselves, which's often confusing).
This function parses varint and retrieve actual packet length, to instruct the proxy to expect a fully transmitted packet before prereading and filtering.
\param *s Nginx stream session object pointer.
\param *bufpos Nginx buffer pointer.
\param *packet_len A `size_t` pointer to which the parsed value will be stored. Required.
\param *varint_byte_len A `size_t` pointer to which the length of varint bytes will be stored. Optional, pass `NULL` if no need.
\returns Nginx buffer pointer that passes over the parsed varint bytes. If failure, NULL.
*/
u_char *parse_packet_length(ngx_stream_session_t *s, u_char *bufpos, size_t *packet_len, size_t *varint_byte_len) {
size_t vl;
size_t res;
ngx_stream_minecraft_forward_ctx_t *ctx;
if (s == NULL) {
return NULL;
}
ctx = ngx_stream_get_module_ctx(s, ngx_stream_minecraft_forward_module);
if (ctx == NULL || bufpos == NULL) {
return NULL;
}
res = read_minecraft_varint(bufpos, &vl);
if (res <= 0) {
return NULL;
}
if (packet_len == NULL) {
return NULL;
}
*packet_len = res;
if (varint_byte_len != NULL) {
*varint_byte_len = vl;
}
return bufpos + vl;
}
/*
Convert an integer value to varint bytes and store in an unsigned char array.
The char array's memory is allocated from the nginx connection object's memory pool.
\param *pool Nginx memory pool pointer.
\param value Integer to be converted. Must be non-negative.
\param *byte_len A `size_t` pointer to which the length of varint bytes will be stored. Optional, pass `NULL` if no need.
\returns Pointer to an unsigned char array that stores varint. If failure, NULL.
*/
u_char *create_minecraft_varint(ngx_pool_t *pool, ngx_int_t value, size_t *byte_len) {
if (pool == NULL || value < 0) {
return NULL;
}
u_char *varint;
ngx_uint_t v;
ngx_uint_t count;
v = value;
varint = ngx_pcalloc(pool, sizeof(u_char) * _MC_VARINT_MAX_BYTE_LEN_);
if (varint == NULL) {
return NULL;
}
count = 0;
for (;;) {
if ((v & ~0x7F) == 0) {
varint[count++] = v;
break;
}
varint[count++] = ((v & 0x7F) | 0x80);
v >>= 7;
}
if (byte_len != NULL) {
*byte_len = count;
}
return varint;
}
ngx_int_t ngx_stream_minecraft_forward_module_srv_conf_validate_hostname(ngx_str_t *str) {
if (!str) {
return NGX_ERROR;
}
if (!str->data || str->len > 253 || str->len <= 0) {
return NGX_ERROR;
}
#if (NGX_PCRE)
if (!ngx_stream_minecraft_forward_module_srv_hostname_check_regex) {
return NGX_OK;
}
return ngx_regex_exec(ngx_stream_minecraft_forward_module_srv_hostname_check_regex, str, NULL, 0) >= 0
? NGX_OK
: NGX_ERROR;
#else
return NGX_OK;
#endif
}