1/*
2 This file is part of Mtproto-proxy Library.
3
4 Mtproto-proxy Library is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 Mtproto-proxy Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
16
17 Copyright 2014 Telegram Messenger Inc
18 2014 Vitaly Valtman
19*/
20
21#include <assert.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <unistd.h>
25#include <stdarg.h>
26#include <string.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30
31#include "md5.h"
32#include "common/parse-config.h"
33#include "resolver.h"
34#include "kprintf.h"
35
36#define MAX_CONFIG_SIZE (16 << 20)
37
38static char *config_buff;
39char *config_name, *cfg_start, *cfg_end, *cfg_cur;
40int config_bytes, cfg_lno, cfg_lex = -1;
41
42int cfg_skipspc (void) {
43 while (*cfg_cur == ' ' || *cfg_cur == 9 || *cfg_cur == 13 || *cfg_cur == 10 || *cfg_cur == '#') {
44 if (*cfg_cur == '#') {
45 do cfg_cur++; while (*cfg_cur && *cfg_cur != 10);
46 continue;
47 }
48 if (*cfg_cur == 10) {
49 cfg_lno++;
50 }
51 cfg_cur++;
52 }
53 return (unsigned char) *cfg_cur;
54}
55
56int cfg_skspc (void) {
57 while (*cfg_cur == ' ' || *cfg_cur == 9) {
58 cfg_cur++;
59 }
60 return (unsigned char) *cfg_cur;
61}
62
63int cfg_getlex (void) {
64 switch (cfg_skipspc()) {
65 case ';':
66 case ':':
67 case '{':
68 case '}':
69 return cfg_lex = *cfg_cur++;
70 case 0:
71 return cfg_lex = 0;
72 }
73 return cfg_lex = -1;
74}
75
76int cfg_getword (void) {
77 cfg_skspc();
78 char *s = cfg_cur;
79 if (*s != '[') {
80 while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '.' || *s == '-' || *s == '_') {
81 s++;
82 }
83 } else {
84 s++;
85 while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '.' || *s == '-' || *s == '_' || *s == ':') {
86 s++;
87 }
88 if (*s == ']') {
89 s++;
90 }
91 }
92 return s - cfg_cur;
93}
94
95int cfg_getstr (void) {
96 cfg_skspc();
97 char *s = cfg_cur;
98 if (*s == '"') { return 1; } // fix later
99 while (*s > ' ' && *s != ';') {
100 s++;
101 }
102 return s - cfg_cur;
103}
104
105long long cfg_getint (void) {
106 cfg_skspc ();
107 char *s = cfg_cur;
108 long long x = 0;
109 while (*s >= '0' && *s <= '9') {
110 x = x * 10 + *(s ++) - '0';
111 }
112 cfg_cur = s;
113 return x;
114}
115
116long long cfg_getint_zero (void) {
117 cfg_skspc ();
118 char *s = cfg_cur;
119 long long x = 0;
120 while (*s >= '0' && *s <= '9') {
121 x = x * 10 + *(s ++) - '0';
122 }
123 if (cfg_cur == s) {
124 return -1;
125 } else {
126 cfg_cur = s;
127 return x;
128 }
129}
130
131long long cfg_getint_signed_zero (void) {
132 cfg_skspc ();
133 char *s = cfg_cur;
134 long long x = 0;
135 int sgn = 1;
136 if (*s == '-') {
137 sgn = -1;
138 ++s;
139 }
140 while (*s >= '0' && *s <= '9') {
141 x = x * 10 + sgn * (*(s++) - '0');
142 }
143 if (s == cfg_cur + (sgn < 0)) {
144 return (-1LL << 63);
145 } else {
146 cfg_cur = s;
147 return x;
148 }
149}
150
151void syntax (const char *msg, ...) {
152 if (!msg) {
153 msg = "syntax error";
154 }
155 if (cfg_lno) {
156 fprintf (stderr, "%s:%d: ", config_name, cfg_lno);
157 }
158 fprintf (stderr, "fatal: ");
159 va_list args;
160 va_start (args, msg);
161 vfprintf (stderr, msg, args);
162 va_end (args);
163 int len = 0;
164 while (cfg_cur[len] && cfg_cur[len] != 13 && cfg_cur[len] != 10 && len < 20) {
165 len++;
166 }
167 fprintf (stderr, " near %.*s%s\n", len, cfg_cur, len >= 20 ? " ..." : "");
168}
169
170void syntax_warning (const char *msg, ...) {
171 va_list args;
172 if (cfg_lno) {
173 fprintf (stderr, "%s:%d: ", config_name, cfg_lno);
174 }
175 fputs ("warning: ", stderr);
176 va_start (args, msg);
177 vfprintf (stderr, msg, args);
178 va_end (args);
179 int len = 0;
180 while (cfg_cur[len] && cfg_cur[len] != 13 && cfg_cur[len] != 10 && len < 20) {
181 len++;
182 }
183 fprintf (stderr, " near %.*s%s\n", len, cfg_cur, len >= 20 ? " ..." : "");
184}
185
186int expect_lexem (int lexem) {
187 if (cfg_lex != lexem) {
188 syntax ("%c expected", lexem);
189 return -1;
190 } else {
191 return 0;
192 }
193}
194
195int expect_word (const char *name, int len) {
196 int l = cfg_getword ();
197 if (len != l || memcmp (name, cfg_cur, len)) {
198 syntax ("Expected %.*s", len, name);
199 return -1;
200 }
201 cfg_cur += l;
202 return 0;
203}
204
205struct hostent *cfg_gethost_ex (int verb) {
206 struct hostent *h;
207 int l = cfg_getword ();
208 if (!l || l > 63) {
209 syntax ("hostname expected");
210 return 0;
211 }
212 char c = cfg_cur[l];
213 //hostname = cfg_cur;
214 cfg_cur[l] = 0;
215
216 if (!(h = kdb_gethostbyname (cfg_cur)) || !h->h_addr_list || !h->h_addr) {
217 if (verbosity >= verb) {
218 syntax ("cannot resolve '%s'\n", cfg_cur);
219 }
220 *(cfg_cur += l) = c;
221 return 0;
222 }
223 *(cfg_cur += l) = c;
224 return h;
225}
226
227struct hostent *cfg_gethost (void) {
228 return cfg_gethost_ex (0);
229}
230
231void reset_config (void) {
232 assert (config_buff);
233 cfg_cur = cfg_start = config_buff;
234 cfg_end = cfg_start + config_bytes;
235 *cfg_end = 0;
236 cfg_lno = 0;
237}
238
239int load_config (const char *file, int fd) {
240 if (!config_buff) {
241 config_buff = malloc (MAX_CONFIG_SIZE+4);
242 assert (config_buff);
243 }
244 if (fd < 0) {
245 fd = open (file, O_RDONLY);
246 if (fd < 0) {
247 fprintf (stderr, "Can not open file %s: %m\n", file);
248 return -1;
249 }
250 }
251 int r;
252 config_bytes = r = read (fd, config_buff, MAX_CONFIG_SIZE + 1);
253 if (r < 0) {
254 fprintf (stderr, "error reading configuration file %s: %m\n", config_name);
255 return -2;
256 }
257 if (r > MAX_CONFIG_SIZE) {
258 fprintf (stderr, "configuration file %s too long (max %d bytes)\n", config_name, MAX_CONFIG_SIZE);
259 return -2;
260 }
261 if (config_name) {
262 free (config_name);
263 }
264 if (file) {
265 config_name = strdup (file);
266 }
267
268 reset_config ();
269 return fd;
270}
271
272void md5_hex_config (char *out) {
273 assert (config_buff);
274 md5_hex (config_buff, config_bytes, out);
275}
276
277void close_config (int *fd) {
278 if (config_buff) {
279 free (config_buff);
280 config_buff = NULL;
281 }
282 if (config_name) {
283 free (config_name);
284 config_name = NULL;
285 }
286 config_bytes = 0;
287 cfg_cur = cfg_start = cfg_end = NULL;
288 if (fd) {
289 if (*fd >= 0) {
290 assert (!close (*fd));
291 *fd = -1;
292 }
293 }
294}
295