0 /* See LICENSE file for copyright and license details. */

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <string.h>

4 #include <errno.h>

5 #include <unistd.h>

6 #include <stdint.h>

7 #include <fcntl.h>

8 #include <time.h>

9 #include <pthread.h>

10 #include "strlcpy.h"

11 #include "util.h"

12 #include "server.h"

13 #include "conf.h"

14 #include "log.h"

15 #include "murmur3.h"

16

17 #define MAX_RETRY 2048

18 #define DEFAULT_CERT_DIR "./clients"

19

20 struct conf conf = {0};

21 struct server *servers = NULL;

22 int hashtable_seed = 0;

23 uint32_t hashtable_size = 0;

24 uint32_t hashtable_bits = 0;

25

26 enum {

27 READING_NONE,

28 READING_LEFT,

29 READING_RIGHT,

30 READING_COMMENT

31 };

32

33 enum {

34 SP_NAME,

35 SP_CERTIFICATE,

36 SP_KEY,

37 SP_RELAY_TO,

38 SP_PLAIN,

39 SP_FORWARD_IP,

40 SP_CLIENT_CERTIFICATES

41 };

42

43 enum {

44 P_CLIENT_CERTIFICATES_FOLDER,

45 P_THREADS,

46 P_LOG

47 };

48

49 enum {

50 VALUE_INVALID,

51 VALUE_STRING,

52 VALUE_BOOL,

53 VALUE_INT

54 };

55

56 const char *server_parameters[] = {

57 "name",

58 "certificate",

59 "key",

60 "relay-to",

61 "plain",

62 "forward-ip",

63 "client-certificates"

64 };

65

66

67 const char *parameters[] = {

68 "certificates-directory",

69 "threads",

70 "log"

71 };

72

73 struct server_node {

74 struct server server;

75 struct server_node *next;

76 };

77

78 int conf_parse_value(const char *value,

79 char* string, size_t len, bool *b, int *i) {

80 if (string && *value == '"') {

81 char *end = string + len;

82 value++;

83 while (*value && string < end) {

84 if (*value == '"') {

85 if (value[1]) return VALUE_INVALID;

86 *string = '\0';

87 return VALUE_STRING;

88 }

89 *string++ = *value++;

90 }

91 return VALUE_INVALID;

92 }

93 if (b) {

94 if (!strcmp(value, "true") || !strcmp(value, "True")) {

95 *b = true;

96 return VALUE_BOOL;

97 }

98 if (!strcmp(value, "false") || !strcmp(value, "False")) {

99 *b = false;

100 return VALUE_BOOL;

101 }

102 }

103 if (i) {

104 int v;

105 if (!strcmp(value, "0")) {

106 *i = 0;

107 return VALUE_INT;

108 }

109 v = atoi(value);

110 if (!v)

111 return VALUE_INVALID;

112 *i = v;

113 return VALUE_INT;

114 }

115 return VALUE_INVALID;

116 }

117

118

119 int conf_parse(const char *word) {

120

121 size_t i;

122 int type;

123 int integer;

124 bool found, boolean;

125 char string[WORD_SIZE], *value;

126

127 value = strchr(word, ':');

128 if (!value) return -1;

129 *value = '\0';

130 value++;

131

132 found = false;

133 for (i = 0; i < SIZEOF(parameters); i++) {

134 if (!strcmp(parameters[i], word)) {

135 found = true;

136 break;

137 }

138 }

139 if (!found) return -1;

140

141 type = conf_parse_value(value, string, sizeof(string),

142 &boolean, &integer);

143

144 switch (i) {

145 case P_CLIENT_CERTIFICATES_FOLDER:

146 case P_LOG:

147 if (type != VALUE_STRING) return -1;

148 #ifdef DEBUG

149 log_debug("%s = %s", word, string);

150 #endif

151 break;

152 case P_THREADS:

153 if (type != VALUE_INT) return -1;

154 #ifdef DEBUG

155 log_debug("%s = %d", word, integer);

156 #endif

157 break;

158 default:

159 return -1;

160 }

161

162 switch (i) {

163 case P_LOG:

164 {

165 int fd = open(string, O_WRONLY | O_CREAT | O_APPEND,

166 0600);

167 if (fd < 0) {

168 log_error("unable to open %s: %s",

169 string, strerror(errno));

170 return -2;

171 }

172 close(fileno(stdout));

173 if (dup(fd) == -1) {

174 log_error("dub: %s", strerror(errno));

175 return -2;

176 }

177 close(fd);

178 }

179 break;

180 case P_CLIENT_CERTIFICATES_FOLDER:

181 {

182 int fd = open(string, O_DIRECTORY);

183 if (fd < 0) {

184 log_error("directory not found: %s", string);

185 return -2;

186 }

187 close(fd);

188 }

189 STRCPY(conf.clients, string);

190 break;

191 case P_THREADS:

192 conf.threads = integer;

193 break;

194 default:

195 return -1;

196 }

197

198 return 0;

199 }

200

201 int conf_parse_server(struct server *server, const char *word) {

202

203 size_t i;

204 int type;

205 bool found, boolean;

206 char string[WORD_SIZE], *value;

207

208 value = strchr(word, ':');

209 if (!value) return -1;

210 *value = '\0';

211 value++;

212

213 found = false;

214 for (i = 0; i < SIZEOF(server_parameters); i++) {

215 if (!strcmp(server_parameters[i], word)) {

216 found = true;

217 break;

218 }

219 }

220 if (!found) return -1;

221

222 type = conf_parse_value(value, string, sizeof(string), &boolean, NULL);

223

224 switch (i) {

225 case SP_NAME:

226 case SP_CERTIFICATE:

227 case SP_KEY:

228 case SP_RELAY_TO:

229 if (type != VALUE_STRING) return -1;

230 #ifdef DEBUG

231 log_debug("%s = %s", word, string);

232 #endif

233 break;

234 case SP_PLAIN:

235 case SP_FORWARD_IP:

236 case SP_CLIENT_CERTIFICATES:

237 if (type != VALUE_BOOL) return -1;

238 #ifdef DEBUG

239 log_debug("%s = %s", word, boolean ? "true" : "false");

240 #endif

241 break;

242 default:

243 return -1;

244 }

245

246 switch (i) {

247 case SP_NAME:

248 STRCPY(server->name, string);

249 break;

250 case SP_CERTIFICATE:

251 STRCPY(server->certificate, string);

252 break;

253 case SP_KEY:

254 STRCPY(server->key, string);

255 break;

256 case SP_RELAY_TO:

257 STRCPY(server->relayto, string);

258 {

259 char *ptr = strchr(server->relayto, ':');

260 if (ptr) {

261 *ptr++ = '\0';

262 if (atoi(ptr) == 0) return -1;

263 STRCPY(server->relayto_port, ptr);

264 } else {

265 STRCPY(server->relayto_port, "1965");

266 }

267 }

268 break;

269 case SP_PLAIN:

270 server->plain = boolean;

271 break;

272 case SP_FORWARD_IP:

273 server->forward = boolean;

274 break;

275 case SP_CLIENT_CERTIFICATES:

276 server->client = boolean;

277 break;

278 default:

279 return -1;

280 }

281

282 return 0;

283 }

284

285 int conf_load(const char *path) {

286

287 size_t length, line, count;

288 FILE *f;

289 char *data, *ptr, *end;

290 bool in, failure, string, reading;

291 struct server_node *nodes = NULL;

292 struct server server = {0};

293 char word[WORD_SIZE] = {0};

294 char *word_ptr = word, *word_end = word + sizeof(word);

295

296 STRCPY(conf.clients, DEFAULT_CERT_DIR);

297

298 f = fopen(path, "r");

299 if (!f) {

300 log_error("Failed to open %s: %s", path, strerror(errno));

301 return -1;

302 }

303

304 fseek(f, 0, SEEK_END);

305 length = ftell(f);

306 fseek(f, 0, SEEK_SET);

307

308 data = malloc(length);

309 if (!data) {

310 log_error("Memory allocation failure: %s", strerror(errno));

311 fclose(f);

312 return -1;

313 }

314

315 if (fread(data, 1, length, f) != length) {

316 log_error("Failed to read file: %s", strerror(errno));

317 fclose(f);

318 free(data);

319 return -1;

320 }

321

322 fclose(f);

323

324 ptr = data;

325 end = data + length;

326 reading = string = failure = in = false;

327 line = 1;

328 count = 0;

329 while (ptr < end) {

330 switch (*ptr) {

331 case '\n':

332 if (string) {

333 failure = true;

334 break;

335 }

336 if (word_ptr > word) {

337 int ret;

338 *word_ptr = '\0';

339 word_ptr = word;

340 ret = in ? conf_parse_server(&server, word) :

341 conf_parse(word);

342 if (ret) {

343 failure = ret == -1 ? true : ret;

344 break;

345 }

346 }

347 reading = READING_NONE;

348 line++;

349 /* fallthrough */

350 case ' ':

351 case '\t':

352 if (string) goto in_string;

353 break;

354 case '{':

355 if (reading == READING_COMMENT) break;

356 if (in) {

357 failure = true;

358 break;

359 }

360 in = true;

361 break;

362 case '}':

363 if (reading == READING_COMMENT) break;

364 if (!in || word != word_ptr) {

365 missing:

366 failure = true;

367 break;

368 }

369 if (!server.certificate[0]) {

370 log_error("missing certificate parameter");

371 goto missing;

372 }

373 if (!server.key[0]) {

374 log_error("missing key parameter");

375 goto missing;

376 }

377 if (!server.relayto[0]) {

378 log_error("missing relay-to parameter");

379 goto missing;

380 }

381 if (!server.name[0]) {

382 log_error("missing name parameter");

383 goto missing;

384 }

385 in = false;

386 {

387 struct server_node *node =

388 malloc(sizeof(struct server_node));

389 if (!node) {

390 log_error("memory allocation failure");

391 exit(-1);

392 }

393 node->server = server;

394 node->next = nodes;

395 nodes = node;

396 memset(&server, 0, sizeof(server));

397 count++;

398 }

399 break;

400 default:

401 if (reading == READING_COMMENT ||

402 (*ptr == '#' && !string)) {

403 reading = READING_COMMENT;

404 string = false;

405 break;

406 }

407 if (*ptr == '"') {

408 if (reading == READING_LEFT) {

409 failure = true;

410 break;

411 }

412 string = !string;

413 }

414 in_string:

415 if (word_ptr == word)

416 reading = READING_LEFT;

417 if (!string && *ptr == ':') reading = READING_RIGHT;

418 *word_ptr = *ptr;

419 word_ptr++;

420 if (word_ptr >= word_end) {

421 log_error("line is too long");

422 failure = true;

423 break;

424 }

425 break;

426 }

427 if (failure) break;

428 ptr++;

429 }

430

431 free(data);

432

433 if (failure) {

434 if (failure == true)

435 log_error("invalid syntax: line %ld", line);

436 return -1;

437 }

438

439 if (in) {

440 log_error("invalid syntax: non-closing '{'");

441 return -1;

442 }

443

444 {

445

446 struct server_node *node;

447 uint32_t size = count;

448 int seed, try;

449

450 /* find the nearest power of 2 so we can use & instead of % */

451 size--;

452 size |= size >> 1;

453 size |= size >> 2;

454 size |= size >> 4;

455 size |= size >> 8;

456 size |= size >> 16;

457 size++;

458

459 servers = malloc(sizeof(server) * (size + 1));

460 if (!servers) {

461 log_error("memory allocation failure");

462 return -1;

463 }

464

465 try = 0;

466 retry:

467 node = nodes;

468 seed = rand();

469 memset(servers, 0, size * sizeof(server));

470 while (node) {

471 size_t len = strnlen(node->server.name,

472 sizeof(node->server.name));

473 uint32_t hash = murmur3(node->server.name, len, seed)

474 & (size - 1);

475 if (servers[hash].exist) {

476 if (!strcmp(node->server.name,

477 servers[hash].name)) {

478 log_error("multiple instances of %s",

479 node->server.name);

480 return -1;

481 }

482 try++;

483 if (try < MAX_RETRY) goto retry;

484 log_warning("resizing hash table");

485 size *= 2;

486 servers = realloc(servers,

487 sizeof(server) * (size + 1));

488 if (!servers) {

489 log_error("memory allocation failure");

490 return -1;

491 }

492 try = 0;

493 goto retry;

494 }

495 servers[hash] = node->server;

496 servers[hash].exist = true;

497 node = node->next;

498 }

499 servers[size].exist = END_OF_ARRAY;

500 hashtable_seed = seed;

501 hashtable_size = size;

502 hashtable_bits = size - 1;

503

504 #ifdef DEBUG

505 log_debug("hash table of size %d created in %d %s", size,

506 try + 1, try ? "tries" : "try");

507 #endif

508 }

509

510 while (nodes) {

511 struct server_node *next = nodes->next;

512 free(nodes);

513 nodes = next;

514 }

515

516 if (!count) {

517 log_error("The configuration file does "

518 "not contain any virtual servers");

519 return -1;

520 }

521

522 log_info("%d virtual servers loaded", count);

523

524 return 0;

525 }

526

527 struct server *conf_get(const char *name) {

528 unsigned int hash = murmur3(name, strlen(name), hashtable_seed);

529 struct server *server = &servers[hash & (hashtable_bits)];

530 #ifndef SKIP_VERIFICATION

531 /* verification could be removed if we would fetch the domain name

532 * from the certificate instead of having it as a field in the

533 * configuration file */

534 const char *ptr = server->name;

535 const char *end = server->name + sizeof(server->name);

536 while (*ptr) {

537 if (*(ptr++) != *(name++))

538 return NULL;

539 if (ptr >= end)

540 return NULL;

541 }

542 #endif

543 return &servers[hash & (hashtable_bits)];

544 }

545

546 void conf_unload() {

547 free(servers);

548 }

549