Commit cc7b7097 authored by vvaltman's avatar vvaltman

initial commit

parents
objs
dep
/*
This file is part of MTProto-proxy.
MTProto-proxy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-proxy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MTProto-proxy. If not, see <http://www.gnu.org/licenses/>.
This program is released under the GPL with the additional exemption
that compiling, linking, and/or using OpenSSL is allowed.
You are free to remove this exemption from derived works.
Copyright 2014-2018 Telegram Messenger Inc
*/
/*
This file is part of MTProto-proxy Library.
MTProto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with MTProto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2018 Telegram Messenger Inc
*/
OBJ = objs
DEP = dep
EXE = ${OBJ}/bin
COMMIT := $(shell git log -1 --pretty=format:"%H")
CFLAGS = -m64 -O3 -std=gnu11 -Wall -mpclmul -march=core2 -mfpmath=sse -mssse3 -fno-strict-aliasing -fno-strict-overflow -fwrapv -DAES=1 -DCOMMIT=\"${COMMIT}\" -D_GNU_SOURCE=1 -D_FILE_OFFSET_BITS=64
LDFLAGS = -m64 -ggdb -rdynamic -lm -lrt -lcrypto -lz -lpthread -lcrypto
LIB = ${OBJ}/lib
CINCLUDE = -iquote common -iquote .
LIBLIST = ${LIB}/libkdb.a
PROJECTS = common jobs mtproto net crypto engine
OBJDIRS := ${OBJ} $(addprefix ${OBJ}/,${PROJECTS}) ${EXE} ${LIB}
DEPDIRS := ${DEP} $(addprefix ${DEP}/,${PROJECTS})
ALLDIRS := ${DEPDIRS} ${OBJDIRS}
.PHONY: all clean
EXELIST := ${EXE}/mtproto-proxy
OBJECTS = \
${OBJ}/mtproto/mtproto-proxy.o ${OBJ}/mtproto/mtproto-config.o ${OBJ}/net/net-tcp-rpc-ext-server.o
DEPENDENCE_CXX := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS_CXX}))
DEPENDENCE_STRANGE := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS_STRANGE}))
DEPENDENCE_NORM := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${OBJECTS}))
LIB_OBJS_NORMAL := \
${OBJ}/common/crc32c.o \
${OBJ}/common/pid.o \
${OBJ}/common/sha1.o \
${OBJ}/common/sha256.o \
${OBJ}/common/md5.o \
${OBJ}/common/resolver.o \
${OBJ}/common/parse-config.o \
${OBJ}/crypto/aesni256.o \
${OBJ}/jobs/jobs.o ${OBJ}/common/mp-queue.o \
${OBJ}/net/net-events.o ${OBJ}/net/net-msg.o ${OBJ}/net/net-msg-buffers.o \
${OBJ}/net/net-config.o ${OBJ}/net/net-crypto-aes.o ${OBJ}/net/net-crypto-dh.o ${OBJ}/net/net-timers.o \
${OBJ}/net/net-connections.o \
${OBJ}/net/net-rpc-targets.o \
${OBJ}/net/net-tcp-connections.o ${OBJ}/net/net-tcp-rpc-common.o ${OBJ}/net/net-tcp-rpc-client.o ${OBJ}/net/net-tcp-rpc-server.o \
${OBJ}/net/net-http-server.o \
${OBJ}/common/tl-parse.o ${OBJ}/common/common-stats.o \
${OBJ}/engine/engine.o ${OBJ}/engine/engine-signals.o \
${OBJ}/engine/engine-net.o \
${OBJ}/engine/engine-rpc.o \
${OBJ}/engine/engine-rpc-common.o \
${OBJ}/net/net-thread.o ${OBJ}/net/net-stats.o ${OBJ}/common/proc-stat.o \
${OBJ}/common/kprintf.o \
${OBJ}/common/precise-time.o ${OBJ}/common/cpuid.o \
${OBJ}/common/server-functions.o ${OBJ}/common/crc32.o \
LIB_OBJS := ${LIB_OBJS_NORMAL}
DEPENDENCE_LIB := $(subst ${OBJ}/,${DEP}/,$(patsubst %.o,%.d,${LIB_OBJS}))
DEPENDENCE_ALL := ${DEPENDENCE_NORM} ${DEPENDENCE_STRANGE} ${DEPENDENCE_LIB}
OBJECTS_ALL := ${OBJECTS} ${LIB_OBJS}
all: ${ALLDIRS} ${EXELIST}
dirs: ${ALLDIRS}
create_dirs_and_headers: ${ALLDIRS}
${ALLDIRS}:
@test -d $@ || mkdir -p $@
-include ${DEPENDENCE_ALL}
${OBJECTS}: ${OBJ}/%.o: %.c | create_dirs_and_headers
${CC} ${CFLAGS} ${CINCLUDE} -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $<
${LIB_OBJS_NORMAL}: ${OBJ}/%.o: %.c | create_dirs_and_headers
${CC} ${CFLAGS} -fpic ${CINCLUDE} -c -MP -MD -MF ${DEP}/$*.d -MQ ${OBJ}/$*.o -o $@ $<
${EXELIST}: ${LIBLIST}
${EXE}/mtproto-proxy: ${OBJ}/mtproto/mtproto-proxy.o ${OBJ}/mtproto/mtproto-config.o ${OBJ}/net/net-tcp-rpc-ext-server.o
${CC} -o $@ $^ ${LIB}/libkdb.a ${LDFLAGS}
${LIB}/libkdb.a: ${LIB_OBJS}
rm -f $@ && ar rcs $@ $^
clean:
rm -rf ${OBJ} ${DEP} ${EXE} || true
force-clean: clean
to build simply run 'make'. Your binary will be objs/bin/mtproto-proxy
to run mtproto-proxy:
1. Obtain secret, which is used to connect to telegram servers.
curl -s https://digitalresistance.dog/getSecret -o proxy-secret
2. Obtail current telegram configuration. It can (rarely) change, so we encourage you to update it once per day.
curl -s https://digitalresistance.dog/getConfig -o proxy-multi.conf
3. Generate secret, which is used by users to connect to proxy
head -c 16 /dev/urandom | xxd -ps
4. Run mtproto-proxy
mtproto-proxy -u nobody -p 8888 -H 443 -S <secret> --aes-pwd proxy-secret proxy-multi.conf -M 1
where:
- nobody is user name. mtproto-proxy calls setuid() to drop privilegies
- 443 is port, used by clients to connect to proxy
- 8888 is local port. You can use it to get statistics from mtproto. Like wget localhost:8888/stats
You can only get this stat via loopback
- <secret> is secret generated on step 3.
- proxy-secret and proxy-multi.conf are obtains on steps 1 and 2
- 1 is number of workers. You can increase number of workers, if you have powerful server
- also feel free to look on other options in mtproto-prodxy help
5. generate link tg://proxy?server=SERVER_NAME&port=443&secret=SECRET
6. enjoy
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Anton Maydell
Copyright 2014-2017 Telegram Messenger Inc
2014-2017 Anton Maydell
*/
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "kprintf.h"
#include "precise-time.h"
#include "server-functions.h"
#include "common/common-stats.h"
#include "net/net-connections.h"
static int read_whole_file (char *filename, void *output, int olen) {
int fd = open (filename, O_RDONLY), n = -1;
if (fd < 0) {
vkprintf (1, "%s: open (\"%s\", O_RDONLY) failed. %m\n", __func__, filename);
return -1;
}
do {
n = read (fd, output, olen);
if (n < 0) {
if (errno == EINTR) {
continue;
}
vkprintf (1, "%s: read from %s failed. %m\n", __func__, filename);
}
break;
} while (1);
while (close (fd) < 0 && errno == EINTR) {}
if (n < 0) {
return -1;
}
if (n >= olen) {
vkprintf (1, "%s: output buffer is too small (%d bytes).\n", __func__, olen);
return -1;
}
unsigned char *p = output;
p[n] = 0;
return n;
}
static int parse_statm (const char *buf, long long *a, int m) {
static long long page_size = -1;
if (page_size < 0) {
page_size = sysconf (_SC_PAGESIZE);
assert (page_size > 0);
}
int i;
if (m > 7) {
m = 7;
}
const char *p = buf;
char *q;
errno = 0;
for (i = 0; i < m; i++) {
a[i] = strtoll (p, &q, 10);
if (p == q || errno) {
return -1;
}
a[i] *= page_size;
p = q;
}
return 0;
}
int am_get_memory_usage (pid_t pid, long long *a, int m) {
char proc_filename[32];
char buf[4096];
assert (snprintf (proc_filename, sizeof (proc_filename), "/proc/%d/statm", (int) pid) < sizeof (proc_filename));
if (read_whole_file (proc_filename, buf, sizeof (buf)) < 0) {
return -1;
}
return parse_statm (buf, a, m);
}
int am_get_memory_stats (am_memory_stat_t *S, int flags) {
if (!flags) {
return -1;
}
long long a[6];
if (flags & AM_GET_MEMORY_USAGE_SELF) {
if (am_get_memory_usage (getpid (), a, 6) < 0) {
return -1;
}
S->vm_size = a[0];
S->vm_rss = a[1];
S->vm_data = a[5];
}
if (flags & AM_GET_MEMORY_USAGE_OVERALL) {
char buf[16384], *p;
if (read_whole_file ("/proc/meminfo", buf, sizeof (buf)) < 0) {
return -1;
}
vkprintf (4, "/proc/meminfo: %s\n", buf);
char suffix[32];
long long value;
int r = 0;
for (p = strtok (buf, "\n"); r != 15 && p != NULL; p = strtok (NULL, "\n")) {
switch (*p++) {
case 'C':
if (!memcmp (p, "ached:", 6)) {
if (sscanf (p + 6, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
S->mem_cached = value << 10;
r |= 8;
}
}
break;
case 'M':
if (!memcmp (p, "emFree:", 7)) {
if (sscanf (p + 7, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
S->mem_free = value << 10;
r |= 1;
}
}
break;
case 'S':
if (!memcmp (p, "wapTotal:", 9)) {
if (sscanf (p + 9, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
S->swap_total = value << 10;
r |= 2;
}
} else if (!memcmp (p, "wapFree:", 8)) {
if (sscanf (p + 8, "%lld%31s", &value, suffix) == 2 && !strcmp (suffix, "kB")) {
S->swap_free = value << 10;
r |= 4;
}
}
break;
}
}
if (r != 15) {
return -1;
}
S->swap_used = S->swap_total - S->swap_free;
}
return 0;
}
struct stat_fun_en {
stat_fun_t func;
struct stat_fun_en *next;
};
struct stat_fun_en *stat_func_first = NULL;
int sb_register_stat_fun (stat_fun_t func) {
struct stat_fun_en *last = NULL, *p;
for (p = stat_func_first; p; p = p->next) {
last = p;
if (p->func == func) {
return 0;
}
}
p = malloc (sizeof (*p));
p->func = func;
p->next = NULL;
if (last) {
last->next = p;
} else {
stat_func_first = p;
}
return 1;
}
/************************ stats buffer functions **********************************/
void sb_init (stats_buffer_t *sb, char *buff, int size) {
sb->buff = buff;
sb->pos = 0;
sb->size = size;
sb->flags = 0;
}
void sb_alloc (stats_buffer_t *sb, int size) {
if (size < 16) {
size = 16;
}
sb->buff = malloc (size);
assert (sb->buff);
sb->pos = 0;
sb->size = size;
sb->flags = 1;
}
void sb_release (stats_buffer_t *sb) {
if (sb->flags & 1) {
free (sb->buff);
}
sb->buff = NULL;
}
static void sb_truncate (stats_buffer_t *sb) {
sb->buff[sb->size - 1] = 0;
sb->pos = sb->size - 2;
while (sb->pos >= 0 && sb->buff[sb->pos] != '\n') {
sb->buff[sb->pos--] = 0;
}
sb->pos++;
}
static int sb_full (stats_buffer_t *sb) {
return (sb->pos == sb->size - 1 && sb->buff[sb->pos]) || sb->pos >= sb->size;
}
void sb_prepare (stats_buffer_t *sb) {
sb->pos = prepare_stats (sb->buff, sb->size);
if (sb_full (sb)) {
sb_truncate (sb);
return;
}
struct stat_fun_en *p;
for (p = stat_func_first; p; p = p->next) {
p->func (sb);
if (sb_full (sb)) {
sb_truncate (sb);
return;
}
}
}
void sb_printf (stats_buffer_t *sb, const char *format, ...) {
if (sb->pos >= sb->size) { return; }
const int old_pos = sb->pos;
va_list ap;
va_start (ap, format);
sb->pos += vsnprintf (sb->buff + old_pos, sb->size - old_pos, format, ap);
va_end (ap);
if (sb->pos >= sb->size) {
if (sb->flags & 1) {
sb->size = 2 * sb->pos;
sb->buff = realloc (sb->buff, sb->size);
assert (sb->buff);
va_start (ap, format);
sb->pos = old_pos + vsnprintf (sb->buff + old_pos, sb->size - old_pos, format, ap);
va_end (ap);
assert (sb->pos < sb->size);
} else {
sb_truncate (sb);
}
}
}
/************************************************************************************/
void sb_memory (stats_buffer_t *sb, int flags) {
am_memory_stat_t S;
if (!am_get_memory_stats (&S, flags & AM_GET_MEMORY_USAGE_SELF)) {
sb_printf (sb,
"vmsize_bytes\t%lld\n"
"vmrss_bytes\t%lld\n"
"vmdata_bytes\t%lld\n",
S.vm_size, S.vm_rss, S.vm_data);
}
if (!am_get_memory_stats (&S, flags & AM_GET_MEMORY_USAGE_OVERALL)) {
sb_printf (sb,
"memfree_bytes\t%lld\n"
"memcached_bytes\t%lld\n"
"swap_used_bytes\t%lld\n"
"swap_total_bytes\t%lld\n",
S.mem_free, S.mem_cached, S.swap_used, S.swap_total);
}
}
void sb_print_queries (stats_buffer_t *sb, const char *const desc, long long q) {
sb_printf (sb, "%s\t%lld\nqps_%s\t%.3lf\n", desc, q, desc, safe_div (q, now - start_time));
}
int sb_sum_i (void **base, int len, int offset) {
int res = 0;
int i;
for (i = 0; i < len; i++) if (base[i]) {
res += *(int *)((base[i]) + offset);
}
return res;
}
long long sb_sum_ll (void **base, int len, int offset) {
long long res = 0;
int i;
for (i = 0; i < len; i++) if (base[i]) {
res += *(long long *)((base[i]) + offset);
}
return res;
}
double sb_sum_f (void **base, int len, int offset) {
double res = 0;
int i;
for (i = 0; i < len; i++) if (base[i]) {
res += *(double *)((base[i]) + offset);
}
return res;
}
void sbp_print_date (stats_buffer_t *sb, const char *key, time_t unix_time) {
struct tm b;
struct tm *t = gmtime_r (&unix_time, &b);
if (t) {
char s[256];
size_t l = strftime (s, sizeof (s), "%c", t);
if (l > 0) {
sb_printf (sb, "%s\t%s\n", key, s);
}
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Anton Maydell
Copyright 2014-2017 Telegram Messenger Inc
2014-2017 Anton Maydell
*/
#pragma once
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#define AM_GET_MEMORY_USAGE_SELF 1
#define AM_GET_MEMORY_USAGE_OVERALL 2
#define SB_PRINT_I64(x) sb_printf (&sb, "%s\t%lld\n", #x, x)
#define SB_PRINT_I32(x) sb_printf (&sb, "%s\t%d\n", #x, x)
#define SB_PRINT_QUERIES(x) sb_print_queries (&sb, #x, x)
#define SB_PRINT_DOUBLE(x) sb_printf (&sb, "%s\t%.6lf\n", #x, x)
#define SB_PRINT_TIME(x) sb_printf (&sb, "%s\t%.6lfs\n", #x, x)
#define SB_PRINT_PERCENT(x) sb_printf (&sb, "%s\t%.3lf%%\n", #x, x)
#define SBP_PRINT_I32(x) sb_printf (sb, "%s\t%d\n", #x, x)
#define SBP_PRINT_I64(x) sb_printf (sb, "%s\t%lld\n", #x, x)
#define SBP_PRINT_QUERIES(x) sb_print_queries (sb, #x, x)
#define SBP_PRINT_DOUBLE(x) sb_printf (sb, "%s\t%.6lf\n", #x, x)
#define SBP_PRINT_TIME(x) sb_printf (sb, "%s\t%.6lfs\n", #x, x)
#define SBP_PRINT_PERCENT(x) sb_printf (sb, "%s\t%.3lf%%\n", #x, x)
#define SBP_PRINT_DATE(x) sbp_print_date (sb, #x, x)
#define SBM_PRINT_I32(x) sb_printf (sb, "%s%s\t%d\n", MODULE_STAT_PREFIX_NAME ?: "", #x, x)
#define SBM_PRINT_I64(x) sb_printf (sb, "%s%s\t%lld\n", MODULE_STAT_PREFIX_NAME ?: "", #x, x)
#define SBM_PRINT_DOUBLE(x) sb_printf (sb, "%s%s\t%.6lf\n", MODULE_STAT_PREFIX_NAME ?: "", #x, x)
#define SBM_PRINT_TIME(x) sb_printf (sb, "%s%s\t%.6lfs\n", MODULE_STAT_PREFIX_NAME ?: "", #x, x)
#define SBM_PRINT_PERCENT(x) sb_printf (sb, "%s%s\t%.3lf%%\n", MODULE_STAT_PREFIX_NAME ?: "", #x, x)
static inline double safe_div (double x, double y) { return y > 0 ? x/y : 0; }
typedef struct {
long long vm_size;
long long vm_rss;
long long vm_data;
long long mem_free;
long long swap_total;
long long swap_free;
long long swap_used;
long long mem_cached;
} am_memory_stat_t;
int am_get_memory_usage (pid_t pid, long long *a, int m);
int am_get_memory_stats (am_memory_stat_t *S, int flags);
typedef struct stats_buffer {
char *buff;
int pos;
int size;
int flags;
} stats_buffer_t;
void sb_init (stats_buffer_t *sb, char *buff, int size);
void sb_alloc (stats_buffer_t *sb, int size);
void sb_release (stats_buffer_t *sb);
void sb_prepare (stats_buffer_t *sb);
void sb_printf (stats_buffer_t *sb, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
void sb_memory (stats_buffer_t *sb, int flags);
void sb_print_queries (stats_buffer_t *sb, const char *const desc, long long q);
void sbp_print_date (stats_buffer_t *sb, const char *key, time_t unix_time);
typedef void (*stat_fun_t) (stats_buffer_t *sb);
int sb_register_stat_fun (stat_fun_t fun);
int sb_sum_i (void **base, int len, int offset);
long long sb_sum_ll (void **base, int len, int offset);
double sb_sum_f (void **base, int len, int offset);
#define SB_SUM_I(name) \
sb_sum_i ((void **)MODULE_STAT_ARR, max_job_thread_id + 1, offsetof (MODULE_STAT_TYPE, name))
#define SB_SUM_LL(name) \
sb_sum_ll ((void **)MODULE_STAT_ARR, max_job_thread_id + 1, offsetof (MODULE_STAT_TYPE, name))
#define SB_SUM_F(name) \
sb_sum_f ((void **)MODULE_STAT_ARR, max_job_thread_id + 1, offsetof (MODULE_STAT_TYPE, name))
#define SB_SUM_ONE_I(name) sb_printf (sb, "%s%s\t%d\n", MODULE_STAT_PREFIX_NAME ?: "", #name, SB_SUM_I(name))
#define SB_SUM_ONE_LL(name) sb_printf (sb, "%s%s\t%lld\n", MODULE_STAT_PREFIX_NAME ?: "", #name, SB_SUM_LL(name))
#define SB_SUM_ONE_F(name) sb_printf (sb, "%s%s\t%lf\n", MODULE_STAT_PREFIX_NAME ?: "", #name, SB_SUM_F(name))
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#include <assert.h>
#include "cpuid.h"
#define CPUID_MAGIC 0x280147b8
kdb_cpuid_t *kdb_cpuid (void) {
static kdb_cpuid_t cached = { .magic = 0 };
if (cached.magic) {
assert (cached.magic == CPUID_MAGIC);
return &cached;
}
int a;
asm volatile ("cpuid\n\t"
: "=a" (a), "=b" (cached.ebx) , "=c" (cached.ecx), "=d" (cached.edx)
: "0" (1)
);
cached.magic = CPUID_MAGIC;
return &cached;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#pragma once
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
typedef long long v2di __attribute__ ((vector_size (16)));
typedef char v16qi __attribute__ ((vector_size (16)));
typedef short v8hi __attribute__ ((vector_size (16)));
typedef int v4si __attribute__ ((vector_size (16)));
typedef double v2df __attribute__ ((vector_size (16)));
typedef float v4sf __attribute__ ((vector_size (16)));
typedef struct {
int magic;
int ebx, ecx, edx;
} kdb_cpuid_t;
kdb_cpuid_t *kdb_cpuid (void);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include "crc32.h"
#include "common/cpuid.h"
#include "common/kprintf.h"
static const unsigned int crc32_table[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static const unsigned int crc32_table2[256] =
{
0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3,
0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7,
0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb,
0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf,
0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192,
0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496,
0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a,
0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e,
0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761,
0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265,
0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69,
0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d,
0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530,
0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034,
0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38,
0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c,
0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6,
0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2,
0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce,
0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca,
0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97,
0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93,
0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f,
0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b,
0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864,
0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60,
0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c,
0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768,
0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35,
0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31,
0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d,
0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539,
0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88,
0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c,
0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180,
0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484,
0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9,
0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd,
0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1,
0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5,
0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a,
0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e,
0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522,
0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026,
0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b,
0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f,
0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773,
0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277,
0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d,
0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189,
0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85,
0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81,
0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc,
0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8,
0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4,
0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0,
0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f,
0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b,
0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27,
0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23,
0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e,
0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a,
0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876,
0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72,
};
static const unsigned int crc32_table1[256] =
{
0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59,
0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685,
0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1,
0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d,
0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29,
0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5,
0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91,
0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d,
0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9,
0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065,
0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901,
0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd,
0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9,
0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315,
0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71,
0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad,
0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399,
0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45,
0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221,
0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd,
0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9,
0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835,
0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151,
0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d,
0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579,
0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5,
0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1,
0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d,
0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609,
0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5,
0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1,
0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d,
0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9,
0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05,
0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461,
0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd,
0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9,
0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75,
0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711,
0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd,
0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339,
0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5,
0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281,
0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d,
0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049,
0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895,
0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1,
0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d,
0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819,
0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5,
0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1,
0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d,
0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69,
0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5,
0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1,
0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d,
0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9,
0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625,
0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41,
0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d,
0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89,
0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555,
0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31,
0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed,
};
static const unsigned int crc32_table0[256] = {
0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee,
0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9,
0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701,
0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056,
0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871,
0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26,
0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e,
0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9,
0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0,
0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787,
0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f,
0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68,
0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f,
0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018,
0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0,
0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7,
0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3,
0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084,
0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c,
0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b,
0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c,
0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b,
0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3,
0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4,
0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed,
0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba,
0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002,
0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755,
0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72,
0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825,
0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d,
0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca,
0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5,
0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82,
0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a,
0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d,
0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a,
0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d,
0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5,
0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2,
0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb,
0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc,
0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04,
0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953,
0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174,
0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623,
0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b,
0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc,
0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8,
0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf,
0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907,
0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50,
0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677,
0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120,
0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98,
0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf,
0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6,
0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981,
0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639,
0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e,
0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949,
0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e,
0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6,
0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1,
};
crc32_partial_func_t crc32_partial;
crc32_combine_func_t compute_crc32_combine;
unsigned crc32_partial_generic (const void *data, long len, unsigned crc) {
const int *p = (const int *) data;
long x;
#define DO_ONE(v) crc ^= v; crc = crc32_table0[crc & 0xff] ^ crc32_table1[(crc & 0xff00) >> 8] ^ crc32_table2[(crc & 0xff0000) >> 16] ^ crc32_table[crc >> 24];
#define DO_FOUR(p) DO_ONE((p)[0]); DO_ONE((p)[1]); DO_ONE((p)[2]); DO_ONE((p)[3]);
for (x = (len >> 5); x > 0; x--) {
DO_FOUR (p);
DO_FOUR (p + 4);
p += 8;
}
if (len & 16) {
DO_FOUR (p);
p += 4;
}
if (len & 8) {
DO_ONE (p[0]);
DO_ONE (p[1]);
p += 2;
}
if (len & 4) {
DO_ONE (*p++);
}
#undef DO_ONE
#undef DO_FOUR
const char *q = (const char *) p;
if (len & 2) {
crc = crc32_table[(crc ^ q[0]) & 0xff] ^ (crc >> 8);
crc = crc32_table[(crc ^ q[1]) & 0xff] ^ (crc >> 8);
q += 2;
}
if (len & 1) {
crc = crc32_table[(crc ^ *q++) & 0xff] ^ (crc >> 8);
}
return crc;
}
/******************** CLMUL ********************/
#define CRC32_POLY 0x04c11db7u
#define CRC32_REFLECTED_POLY 0xedb88320u
#define CRC32_REFLECTED_X319 0x9570d49500000000ll
#define CRC32_REFLECTED_X255 0x01b5fd1d00000000ll
#define CRC32_REFLECTED_X191 0x65673b4600000000ll
#define CRC32_REFLECTED_X127 0x9ba54c6f00000000ll
#define CRC32_REFLECTED_X95 0xccaa009e00000000ll
#define CRC32_REFLECTED_X63 0xb8bc676500000000ll
#define CRC32_REFLECTED_POLY_33_BIT 0x1db710641ll
#define CRC32_REFLECTED_MU 0x1f7011641ll
#define CRC64_REFLECTED_X319 0x60095b008a9efa44ll
#define CRC64_REFLECTED_X191 0xe05dd497ca393ae4ll
#define CRC64_REFLECTED_X255 0x3be653a30fe1af51ll
#define CRC64_REFLECTED_X127 0xdabe95afc7875f40ll
#define CRC64_REFLECTED_X95 0x1dee8a5e222ca1dcll
#define CRC64_REFLECTED_POLY_65_BIT 0x92d8af2baf0e1e85ll
//mu(65-bit): 01001110000111110010001100110110000010111001010010110001111010101
#define CRC64_REFLECTED_MU 0x9c3e466c172963d5ll
static const char mask[64] __attribute__ ((aligned (64))) = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
static const v2di CRC32_K256 = { CRC32_REFLECTED_X319, CRC32_REFLECTED_X255 };
static const v2di CRC32_K128 = { CRC32_REFLECTED_X191, CRC32_REFLECTED_X127 };
static const v2di CRC32_K64 = { CRC32_REFLECTED_X95, CRC32_REFLECTED_X63 };
#ifndef CRC32_BARRETT_REDUCTION
static const v2di CRC32_MU __attribute__ ((unused));
#endif
static const v2di CRC32_MU = { CRC32_REFLECTED_MU, CRC32_REFLECTED_POLY_33_BIT };
static const v2di CRC64_K256 = { CRC64_REFLECTED_X319, CRC64_REFLECTED_X255 };
static const v2di CRC64_K128 = { CRC64_REFLECTED_X191, CRC64_REFLECTED_X127 };
static const v2di CRC64_MU = { CRC64_REFLECTED_MU, CRC64_REFLECTED_POLY_65_BIT };
static v2di crcXX_partial_clmul (const void *q, long len, v2di D, v2di E, v2di K256, v2di K128) __attribute__((aligned(32)));
v2di crcXX_partial_clmul (const void *q, long len, v2di D, v2di E, v2di K256, v2di K128) {
v2di G, H;
if (len >= 32) {
const void *e = ((const char *) q) + (len & -32l);
do {
G = *((v2di *) q);
H = *((v2di *) (q + 16));
G ^= __builtin_ia32_pclmulqdq128 (D, K256, 0x00);
H ^= __builtin_ia32_pclmulqdq128 (E, K256, 0x00);
D = __builtin_ia32_pclmulqdq128 (D, K256, 0x11);
E = __builtin_ia32_pclmulqdq128 (E, K256, 0x11);
D ^= G;
E ^= H;
q += 32;
} while (q != e);
}
if (len & 16) {
G = __builtin_ia32_pclmulqdq128 (D, K256, 0x00);
H = __builtin_ia32_pclmulqdq128 (E, K128, 0x00);
D = __builtin_ia32_pclmulqdq128 (D, K256, 0x11);
E = __builtin_ia32_pclmulqdq128 (E, K128, 0x11);
D ^= *((v2di *) q) ^ G ^ H ^ E;
q += 16;
} else {
G = __builtin_ia32_pclmulqdq128 (D, K128, 0x00);
D = __builtin_ia32_pclmulqdq128 (D, K128, 0x11);
D ^= G ^ E;
}
if ((len &= 15)) {
E = (v2di) __builtin_ia32_pshufb128 ( (v16qi) D, __builtin_ia32_loaddqu (mask + 32 + len));
H = (v2di) __builtin_ia32_loaddqu (mask + 16 + len);
D = (v2di) __builtin_ia32_pshufb128 ( (v16qi) D, (v16qi) H);
E ^= (v2di) __builtin_ia32_pshufb128 (*((v16qi *) q), (v16qi) H);
G = __builtin_ia32_pclmulqdq128 (D, K128, 0x00);
D = __builtin_ia32_pclmulqdq128 (D, K128, 0x11);
D ^= G ^ E;
}
return D;
}
unsigned crc32_partial_clmul (const void *data, long len, unsigned crc) {
if (len < 40) {
return crc32_partial_generic (data, len, crc);
}
/* works only for len >= 32 */
const char *q = (const char *) (((uintptr_t) data) & -16L);
int o = (int)(32 - ((uintptr_t) data & 15));
v2di D = (* (v2di *) q), E = (*(v2di *)(q + 16)), G, H;
{
v2di C;
asm volatile ("movd %1, %0\n\t" : "=x" (C) : "g" (crc));
asm volatile ("pcmpeqw %0, %0\n\t" : "=x" (G)); //G := 2 ^ 128 - 1
H = (v2di) __builtin_ia32_loaddqu (mask + o);
G = (v2di) __builtin_ia32_pshufb128 ((v16qi) G, (v16qi) H );
D ^= (v2di) __builtin_ia32_pshufb128 ((v16qi) C, (v16qi) H );
D &= G;
if (__builtin_expect (o <= 19, 0)) {
E ^= (v2di) __builtin_ia32_pshufb128 ((v16qi) C, (v16qi) __builtin_ia32_loaddqu (mask + 16 + o));
}
}
len -= o;
q += 32;
D = crcXX_partial_clmul (q, len, D, E, CRC32_K256, CRC32_K128);
D = __builtin_ia32_pslldqi128 (__builtin_ia32_psrldqi128 (D, 64), 32) ^ (v2di) __builtin_ia32_pclmulqdq128 (D, CRC32_K64, 0x00);
D ^= (v2di) __builtin_ia32_pclmulqdq128 (D, CRC32_K64, 0x10);
uint64_t T;
#ifdef CRC32_BARRETT_REDUCTION
H = (v2di) __builtin_ia32_punpckhdq128 ((v4si) (G ^ G), (v4si) D);
H = (v2di) __builtin_ia32_pclmulqdq128 (H, CRC32_MU, 0x00);
H = (v2di) __builtin_ia32_pclmulqdq128 (H, CRC32_MU, 0x10);
D ^= __builtin_ia32_pslldqi128 (H, 32);
D = __builtin_ia32_punpckhqdq128 (D, D);
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (D));
return (unsigned) (T >> 32);
#else
D = __builtin_ia32_punpckhqdq128 (D, D);
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (D));
crc = (unsigned) T;
return crc32_table0[crc & 0xff] ^ crc32_table1[(crc & 0xff00) >> 8] ^ crc32_table2[(crc & 0xff0000) >> 16] ^ crc32_table[crc >> 24] ^ ((unsigned) (T >> 32));
#endif
}
/******************** CRC-64 ********************/
static const uint64_t crc64_table[256] = {
0x0000000000000000LL, 0xb32e4cbe03a75f6fLL, 0xf4843657a840a05bLL, 0x47aa7ae9abe7ff34LL,
0x7bd0c384ff8f5e33LL, 0xc8fe8f3afc28015cLL, 0x8f54f5d357cffe68LL, 0x3c7ab96d5468a107LL,
0xf7a18709ff1ebc66LL, 0x448fcbb7fcb9e309LL, 0x0325b15e575e1c3dLL, 0xb00bfde054f94352LL,
0x8c71448d0091e255LL, 0x3f5f08330336bd3aLL, 0x78f572daa8d1420eLL, 0xcbdb3e64ab761d61LL,
0x7d9ba13851336649LL, 0xceb5ed8652943926LL, 0x891f976ff973c612LL, 0x3a31dbd1fad4997dLL,
0x064b62bcaebc387aLL, 0xb5652e02ad1b6715LL, 0xf2cf54eb06fc9821LL, 0x41e11855055bc74eLL,
0x8a3a2631ae2dda2fLL, 0x39146a8fad8a8540LL, 0x7ebe1066066d7a74LL, 0xcd905cd805ca251bLL,
0xf1eae5b551a2841cLL, 0x42c4a90b5205db73LL, 0x056ed3e2f9e22447LL, 0xb6409f5cfa457b28LL,
0xfb374270a266cc92LL, 0x48190ecea1c193fdLL, 0x0fb374270a266cc9LL, 0xbc9d3899098133a6LL,
0x80e781f45de992a1LL, 0x33c9cd4a5e4ecdceLL, 0x7463b7a3f5a932faLL, 0xc74dfb1df60e6d95LL,
0x0c96c5795d7870f4LL, 0xbfb889c75edf2f9bLL, 0xf812f32ef538d0afLL, 0x4b3cbf90f69f8fc0LL,
0x774606fda2f72ec7LL, 0xc4684a43a15071a8LL, 0x83c230aa0ab78e9cLL, 0x30ec7c140910d1f3LL,
0x86ace348f355aadbLL, 0x3582aff6f0f2f5b4LL, 0x7228d51f5b150a80LL, 0xc10699a158b255efLL,
0xfd7c20cc0cdaf4e8LL, 0x4e526c720f7dab87LL, 0x09f8169ba49a54b3LL, 0xbad65a25a73d0bdcLL,
0x710d64410c4b16bdLL, 0xc22328ff0fec49d2LL, 0x85895216a40bb6e6LL, 0x36a71ea8a7ace989LL,
0x0adda7c5f3c4488eLL, 0xb9f3eb7bf06317e1LL, 0xfe5991925b84e8d5LL, 0x4d77dd2c5823b7baLL,
0x64b62bcaebc387a1LL, 0xd7986774e864d8ceLL, 0x90321d9d438327faLL, 0x231c512340247895LL,
0x1f66e84e144cd992LL, 0xac48a4f017eb86fdLL, 0xebe2de19bc0c79c9LL, 0x58cc92a7bfab26a6LL,
0x9317acc314dd3bc7LL, 0x2039e07d177a64a8LL, 0x67939a94bc9d9b9cLL, 0xd4bdd62abf3ac4f3LL,
0xe8c76f47eb5265f4LL, 0x5be923f9e8f53a9bLL, 0x1c4359104312c5afLL, 0xaf6d15ae40b59ac0LL,
0x192d8af2baf0e1e8LL, 0xaa03c64cb957be87LL, 0xeda9bca512b041b3LL, 0x5e87f01b11171edcLL,
0x62fd4976457fbfdbLL, 0xd1d305c846d8e0b4LL, 0x96797f21ed3f1f80LL, 0x2557339fee9840efLL,
0xee8c0dfb45ee5d8eLL, 0x5da24145464902e1LL, 0x1a083bacedaefdd5LL, 0xa9267712ee09a2baLL,
0x955cce7fba6103bdLL, 0x267282c1b9c65cd2LL, 0x61d8f8281221a3e6LL, 0xd2f6b4961186fc89LL,
0x9f8169ba49a54b33LL, 0x2caf25044a02145cLL, 0x6b055fede1e5eb68LL, 0xd82b1353e242b407LL,
0xe451aa3eb62a1500LL, 0x577fe680b58d4a6fLL, 0x10d59c691e6ab55bLL, 0xa3fbd0d71dcdea34LL,
0x6820eeb3b6bbf755LL, 0xdb0ea20db51ca83aLL, 0x9ca4d8e41efb570eLL, 0x2f8a945a1d5c0861LL,
0x13f02d374934a966LL, 0xa0de61894a93f609LL, 0xe7741b60e174093dLL, 0x545a57dee2d35652LL,
0xe21ac88218962d7aLL, 0x5134843c1b317215LL, 0x169efed5b0d68d21LL, 0xa5b0b26bb371d24eLL,
0x99ca0b06e7197349LL, 0x2ae447b8e4be2c26LL, 0x6d4e3d514f59d312LL, 0xde6071ef4cfe8c7dLL,
0x15bb4f8be788911cLL, 0xa6950335e42fce73LL, 0xe13f79dc4fc83147LL, 0x521135624c6f6e28LL,
0x6e6b8c0f1807cf2fLL, 0xdd45c0b11ba09040LL, 0x9aefba58b0476f74LL, 0x29c1f6e6b3e0301bLL,
0xc96c5795d7870f42LL, 0x7a421b2bd420502dLL, 0x3de861c27fc7af19LL, 0x8ec62d7c7c60f076LL,
0xb2bc941128085171LL, 0x0192d8af2baf0e1eLL, 0x4638a2468048f12aLL, 0xf516eef883efae45LL,
0x3ecdd09c2899b324LL, 0x8de39c222b3eec4bLL, 0xca49e6cb80d9137fLL, 0x7967aa75837e4c10LL,
0x451d1318d716ed17LL, 0xf6335fa6d4b1b278LL, 0xb199254f7f564d4cLL, 0x02b769f17cf11223LL,
0xb4f7f6ad86b4690bLL, 0x07d9ba1385133664LL, 0x4073c0fa2ef4c950LL, 0xf35d8c442d53963fLL,
0xcf273529793b3738LL, 0x7c0979977a9c6857LL, 0x3ba3037ed17b9763LL, 0x888d4fc0d2dcc80cLL,
0x435671a479aad56dLL, 0xf0783d1a7a0d8a02LL, 0xb7d247f3d1ea7536LL, 0x04fc0b4dd24d2a59LL,
0x3886b22086258b5eLL, 0x8ba8fe9e8582d431LL, 0xcc0284772e652b05LL, 0x7f2cc8c92dc2746aLL,
0x325b15e575e1c3d0LL, 0x8175595b76469cbfLL, 0xc6df23b2dda1638bLL, 0x75f16f0cde063ce4LL,
0x498bd6618a6e9de3LL, 0xfaa59adf89c9c28cLL, 0xbd0fe036222e3db8LL, 0x0e21ac88218962d7LL,
0xc5fa92ec8aff7fb6LL, 0x76d4de52895820d9LL, 0x317ea4bb22bfdfedLL, 0x8250e80521188082LL,
0xbe2a516875702185LL, 0x0d041dd676d77eeaLL, 0x4aae673fdd3081deLL, 0xf9802b81de97deb1LL,
0x4fc0b4dd24d2a599LL, 0xfceef8632775faf6LL, 0xbb44828a8c9205c2LL, 0x086ace348f355aadLL,
0x34107759db5dfbaaLL, 0x873e3be7d8faa4c5LL, 0xc094410e731d5bf1LL, 0x73ba0db070ba049eLL,
0xb86133d4dbcc19ffLL, 0x0b4f7f6ad86b4690LL, 0x4ce50583738cb9a4LL, 0xffcb493d702be6cbLL,
0xc3b1f050244347ccLL, 0x709fbcee27e418a3LL, 0x3735c6078c03e797LL, 0x841b8ab98fa4b8f8LL,
0xadda7c5f3c4488e3LL, 0x1ef430e13fe3d78cLL, 0x595e4a08940428b8LL, 0xea7006b697a377d7LL,
0xd60abfdbc3cbd6d0LL, 0x6524f365c06c89bfLL, 0x228e898c6b8b768bLL, 0x91a0c532682c29e4LL,
0x5a7bfb56c35a3485LL, 0xe955b7e8c0fd6beaLL, 0xaeffcd016b1a94deLL, 0x1dd181bf68bdcbb1LL,
0x21ab38d23cd56ab6LL, 0x9285746c3f7235d9LL, 0xd52f0e859495caedLL, 0x6601423b97329582LL,
0xd041dd676d77eeaaLL, 0x636f91d96ed0b1c5LL, 0x24c5eb30c5374ef1LL, 0x97eba78ec690119eLL,
0xab911ee392f8b099LL, 0x18bf525d915feff6LL, 0x5f1528b43ab810c2LL, 0xec3b640a391f4fadLL,
0x27e05a6e926952ccLL, 0x94ce16d091ce0da3LL, 0xd3646c393a29f297LL, 0x604a2087398eadf8LL,
0x5c3099ea6de60cffLL, 0xef1ed5546e415390LL, 0xa8b4afbdc5a6aca4LL, 0x1b9ae303c601f3cbLL,
0x56ed3e2f9e224471LL, 0xe5c372919d851b1eLL, 0xa26908783662e42aLL, 0x114744c635c5bb45LL,
0x2d3dfdab61ad1a42LL, 0x9e13b115620a452dLL, 0xd9b9cbfcc9edba19LL, 0x6a978742ca4ae576LL,
0xa14cb926613cf817LL, 0x1262f598629ba778LL, 0x55c88f71c97c584cLL, 0xe6e6c3cfcadb0723LL,
0xda9c7aa29eb3a624LL, 0x69b2361c9d14f94bLL, 0x2e184cf536f3067fLL, 0x9d36004b35545910LL,
0x2b769f17cf112238LL, 0x9858d3a9ccb67d57LL, 0xdff2a94067518263LL, 0x6cdce5fe64f6dd0cLL,
0x50a65c93309e7c0bLL, 0xe388102d33392364LL, 0xa4226ac498dedc50LL, 0x170c267a9b79833fLL,
0xdcd7181e300f9e5eLL, 0x6ff954a033a8c131LL, 0x28532e49984f3e05LL, 0x9b7d62f79be8616aLL,
0xa707db9acf80c06dLL, 0x14299724cc279f02LL, 0x5383edcd67c06036LL, 0xe0ada17364673f59LL
};
crc64_partial_func_t crc64_partial;
crc64_combine_func_t compute_crc64_combine;
uint64_t crc64_feed_byte (uint64_t crc, unsigned char b) {
return crc64_table[(crc ^ b) & 0xff] ^ (crc >> 8);
}
uint64_t crc64_partial_one_table (const void *data, long len, uint64_t crc) {
const char *p = data;
for (; len > 0; len--) {
crc = crc64_table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
}
return crc;
}
static uint64_t crc64_barrett_reduction (v2di D) {
/* After reflection mu constant is 64 bit */
v2di E = __builtin_ia32_pclmulqdq128 (D, CRC64_MU, 0x00);
/* The carry-less multiplication has to be performed with a PCLMULQDQ and an XOR operation
since P(x) is 65 bit constant. */
D ^= __builtin_ia32_pclmulqdq128 (E, CRC64_MU, 0x10);
D = __builtin_ia32_punpckhqdq128 (D, D);
D ^= E;
uint64_t T;
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (D));
return T;
}
uint64_t crc64_partial_clmul (const void *data, long len, uint64_t crc) {
if (len <= 31) {
return crc64_partial_one_table (data, len, crc);
}
/* works only for len >= 32 */
const char *q = (const char *) (((uintptr_t) data) & -16L);
int o = (int)(32 - ((uintptr_t) data & 15));
v2di D = (* (v2di *) q), E = (*(v2di *)(q + 16)), C, G, H;
asm volatile ("movq %1, %0\n\t" : "=x" (C) : "g" (crc));
asm volatile ("pcmpeqw %0, %0\n\t" : "=x" (G)); //G := 2 ^ 128 - 1
H = (v2di) __builtin_ia32_loaddqu (mask + o);
G = (v2di) __builtin_ia32_pshufb128 ((v16qi) G, (v16qi) H );
D ^= (v2di) __builtin_ia32_pshufb128 ((v16qi) C, (v16qi) H );
D &= G;
if (o <= (32 - 9)) {
E ^= (v2di) __builtin_ia32_pshufb128 ((v16qi) C, (v16qi) __builtin_ia32_loaddqu (mask + 16 + o));
}
len -= o;
q += 32;
D = crcXX_partial_clmul (q, len, D, E, CRC64_K256, CRC64_K128);
D = (v2di) __builtin_ia32_pclmulqdq128 (CRC64_K128, D, 0x01) ^ __builtin_ia32_psrldqi128 (D, 64);
return crc64_barrett_reduction (D);
}
/* {{{ GF-32 */
unsigned gf32_mulx (unsigned a, unsigned poly) {
unsigned r = a >> 1;
if (a & 1) {
r ^= poly;
}
return r;
}
unsigned gf32_mul (unsigned a, unsigned b, unsigned poly) {
unsigned x = 0;
int i = 0;
do {
x = gf32_mulx (x, poly);
if (b & 1) {
x ^= a;
}
b >>= 1;
} while (++i < 32);
return x;
}
unsigned gf32_pow (unsigned a, int k, unsigned poly) {
if (!k) { return 0x80000000; }
unsigned x = gf32_pow (gf32_mul (a, a, poly), k >> 1, poly);
if (k & 1) {
x = gf32_mul (x, a, poly);
}
return x;
}
static unsigned gf32_matrix_times (unsigned *matrix, unsigned vector) {
unsigned sum = 0;
while (vector) {
if (vector & 1) {
sum ^= *matrix;
}
vector >>= 1;
matrix++;
}
return sum;
}
static void gf32_matrix_square (unsigned *square, unsigned *matrix) {
int n = 0;
do {
square[n] = gf32_matrix_times (matrix, matrix[n]);
} while (++n < 32);
}
void gf32_compute_powers_generic (unsigned *P, int size, unsigned poly) {
int n;
assert (size >= 0 && !(size & 31));
P[0] = poly;
for (n = 0; n < 31; n++) {
P[n+1] = 1U << n;
}
for (n = 1; n < (size / 32); n++) {
gf32_matrix_square (P + (n << 5), P + ((n - 1) << 5));
}
assert (P[size - 1]);
}
void gf32_compute_powers_clmul (unsigned *P, unsigned poly) {
int n;
assert (!((uintptr_t) P & 15l));
unsigned a = 1 << (31-7);
const unsigned b = gf32_mul (poly, poly, poly);
for (n = 0; n < 63; n++) {
P[0] = 0;
P[1] = gf32_mul (a, b, poly);
P[2] = 0;
P[3] = a;
a = gf32_mulx (gf32_mul (a, a, poly), poly);
P += 4;
}
}
unsigned gf32_combine_generic (unsigned *powers, unsigned crc1, int64_t len2) {
unsigned *p = powers + 64;
do {
p += 32;
if (len2 & 1) {
crc1 = gf32_matrix_times (p, crc1);
}
len2 >>= 1;
} while (len2);
return crc1;
}
uint64_t gf32_combine_clmul (unsigned *powers, unsigned crc1, int64_t len2) {
v2di D;
asm volatile ("movd %1, %0\n\t" : "=x" (D) : "g" (crc1));
D = __builtin_ia32_pslldqi128 (D, 96);
int n = __builtin_ffsll (len2);
unsigned int *p = powers + (4 * (n - 1));
len2 >>= n;
D = __builtin_ia32_pclmulqdq128 ( * ((v2di *) p), D, 0x11);
while (len2) {
p += 4;
if (len2 & 1) {
v2di E = *((v2di *) p);
D = __builtin_ia32_pclmulqdq128 (E, D, 0x11) ^ __builtin_ia32_pclmulqdq128 (E, D, 0x00);
}
len2 >>= 1;
}
D ^= (v2di) __builtin_ia32_pclmulqdq128 (* ((v2di *) (powers + 12)), D, 0x01);
D = __builtin_ia32_punpckhqdq128 (D, D);
uint64_t T;
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (D));
return T;
}
/* }}} */
static unsigned compute_crc32_combine_generic (unsigned crc1, unsigned crc2, int64_t len2) {
#define N (32*67)
static unsigned crc32_powers[N];
/* degenerate case (also disallow negative lengths) */
if (len2 <= 0) {
return crc1;
}
if (!crc32_powers[N-1]) {
gf32_compute_powers_generic (crc32_powers, N, CRC32_REFLECTED_POLY);
assert (crc32_powers[N-1]);
}
return gf32_combine_generic (crc32_powers, crc1, len2) ^ crc2;
#undef N
}
static unsigned compute_crc32_combine_clmul (unsigned crc1, unsigned crc2, int64_t len2) {
static unsigned int crc32_powers[252] __attribute__ ((aligned(16)));
if (len2 <= 0) {
return crc1;
}
unsigned int *p;
if (!crc32_powers[251]) {
gf32_compute_powers_clmul (crc32_powers, CRC32_REFLECTED_POLY);
p = crc32_powers + 8;
assert ( *((uint64_t *) (p + 0)) == CRC32_REFLECTED_X95);
assert ( *((uint64_t *) (p + 4)) == CRC32_REFLECTED_X127);
assert ( *((uint64_t *) (p + 6)) == CRC32_REFLECTED_X63);
assert ( *((uint64_t *) (p + 8)) == CRC32_REFLECTED_X191);
assert ( *((uint64_t *) (p + 10)) == CRC32_REFLECTED_X127);
assert ( *((uint64_t *) (p + 12)) == CRC32_REFLECTED_X319);
assert ( *((uint64_t *) (p + 14)) == CRC32_REFLECTED_X255);
assert (crc32_powers[251]);
}
uint64_t T = gf32_combine_clmul (crc32_powers, crc1, len2);
crc1 = (unsigned) T;
crc2 ^= (unsigned) (T >> 32);
return (crc32_table0[crc1 & 0xff] ^ crc32_table1[(crc1 & 0xff00) >> 8] ^ crc32_table2[(crc1 & 0xff0000) >> 16] ^ crc32_table[crc1 >> 24]) ^ crc2;
}
/******************** GF-64 (reversed) ********************/
static uint64_t gf64_mulx (uint64_t a) __attribute__ ((pure));
static uint64_t gf64_mulx (uint64_t a) {
uint64_t r = a >> 1;
if (a & 1) {
r ^= 0xc96c5795d7870f42ll;
}
return r;
}
static uint64_t gf64_mul (uint64_t a, uint64_t b) {
uint64_t x = 0;
int i = 0;
do {
x = gf64_mulx (x);
if (b & 1) {
x ^= a;
}
b >>= 1;
} while (++i < 64);
return x;
}
static uint64_t crc64_power_buf[126] __attribute__ ((aligned(16)));
void crc64_init_power_buf (void) {
int n;
uint64_t *p = crc64_power_buf;
assert (!((uintptr_t) p & 15l));
uint64_t a = 1ll << (63 - 7);
const uint64_t b = 0xc96c5795d7870f42ll;
for (n = 0; n < 63; n++) {
p[0] = gf64_mul (a, b);
p[1] = a;
a = gf64_mulx (gf64_mul (a, a));
p += 2;
}
p = crc64_power_buf;
assert (p[3] == 1ll << (63 - 15));
assert (p[4] == CRC64_REFLECTED_X95);
assert (p[5] == 1ll << (63 - 31));
assert (p[6] == CRC64_REFLECTED_X127);
assert (p[7] == 1ll);
assert (p[8] == CRC64_REFLECTED_X191);
assert (p[9] == CRC64_REFLECTED_X127);
assert (p[10] == CRC64_REFLECTED_X319);
assert (p[11] == CRC64_REFLECTED_X255);
assert (crc64_power_buf[125]);
}
static uint64_t compute_crc64_combine_clmul (uint64_t crc1, uint64_t crc2, int64_t len2) {
if (len2 <= 0) {
return crc1;
}
if (!crc64_power_buf[125]) {
crc64_init_power_buf ();
}
v2di D;
asm volatile ("movq %1, %0\n\t" : "=x" (D) : "g" (crc1));
D = __builtin_ia32_pslldqi128 (D, 64);
int n = __builtin_ffsll (len2);
uint64_t *p = crc64_power_buf + (2 * (n - 1));
len2 >>= n;
D = __builtin_ia32_pclmulqdq128 ( * ((v2di *) p), D, 0x11);
while (len2) {
p += 2;
if (len2 & 1) {
v2di E = *((v2di *) p);
D = __builtin_ia32_pclmulqdq128 (E, D, 0x11) ^ __builtin_ia32_pclmulqdq128 (E, D, 0x00);
}
len2 >>= 1;
}
return crc64_barrett_reduction (D) ^ crc2;
}
static uint64_t compute_crc64_combine_generic (uint64_t crc1, uint64_t crc2, int64_t len2) {
if (len2 <= 0) {
return crc1;
}
if (!crc64_power_buf[125]) {
crc64_init_power_buf ();
}
int n = __builtin_ffsll (len2);
uint64_t *p = crc64_power_buf + ((2 * (n - 1)) + 1);
len2 >>= n;
crc1 = gf64_mul (crc1, gf64_mulx (*p));
while (len2) {
p += 2;
if (len2 & 1) {
crc1 = gf64_mul (crc1, gf64_mulx (*p));
}
len2 >>= 1;
}
return crc1 ^ crc2;
}
/********************************* crc32 repair ************************/
struct fcb_table_entry {
unsigned p; //zeta ^ k
int i;
};
static int cmp_fcb_table_entry (const void *a, const void *b) {
const struct fcb_table_entry *x = a;
const struct fcb_table_entry *y = b;
if (x->p < y->p) { return -1; }
if (x->p > y->p) { return 1; }
if (x->i < y->i) { return -1; }
if (x->i > y->i) { return 1; }
return 0;
}
int crc32_find_corrupted_bit (int size, unsigned d) {
int i, j;
size += 4;
int n = size << 3;
int r = (int) (sqrt (n) + 0.5);
vkprintf (3, "n = %d, r = %d, d = 0x%08x\n", n, r, d);
struct fcb_table_entry *T = calloc (r, sizeof (struct fcb_table_entry));
assert (T != NULL);
T[0].i = 0;
T[0].p = 0x80000000u;
for (i = 1; i < r; i++) {
T[i].i = i;
T[i].p = gf32_mulx (T[i-1].p, CRC32_REFLECTED_POLY);
}
assert (gf32_mulx (0xdb710641, CRC32_REFLECTED_POLY) == 0x80000000);
qsort (T, r, sizeof (T[0]), cmp_fcb_table_entry);
const unsigned q = gf32_pow (0xdb710641, r, CRC32_REFLECTED_POLY);
unsigned A[32];
A[31] = q;
for (i = 30; i >= 0; i--) {
A[i] = gf32_mulx (A[i+1], CRC32_REFLECTED_POLY);
}
unsigned x = d;
int max_j = n / r, res = -1;
for (j = 0; j <= max_j; j++) {
int a = -1, b = r;
while (b - a > 1) {
int c = ((a + b) >> 1);
if (T[c].p <= x) { a = c; } else { b = c; }
}
if (a >= 0 && T[a].p == x) {
res = T[a].i + r * j;
break;
}
x = gf32_matrix_times (A, x);
}
free (T);
return res;
}
int crc32_repair_bit (unsigned char *input, int l, int k) {
if (k < 0) {
return -1;
}
int idx = k >> 5, bit = k & 31, i = (l - 1) - (idx - 1) * 4;
while (bit >= 8) {
i--;
bit -= 8;
}
if (i < 0) {
return -2;
}
if (i >= l) {
return -3;
}
int j = 7 - bit;
input[i] = (unsigned char)(input[i] ^ (1 << j));
return 0;
}
int crc32_check_and_repair (void *input, int l, unsigned *input_crc32, int force_exit) {
unsigned computed_crc32 = compute_crc32 (input, l);
const unsigned crc32_diff = computed_crc32 ^ (*input_crc32);
if (!crc32_diff) {
return 0;
}
int k = crc32_find_corrupted_bit (l, crc32_diff);
vkprintf (3, "find_corrupted_bit returns %d.\n", k);
int r = crc32_repair_bit (input, l, k);
vkprintf (3, "repair_bit returns %d.\n", r);
if (!r) {
assert (compute_crc32 (input, l) == *input_crc32);
if (force_exit) {
kprintf ("crc32_check_and_repair successfully repair one bit in %d bytes block.\n", l);
}
return 1;
}
if (!(crc32_diff & (crc32_diff - 1))) { /* crc32_diff is power of 2 */
*input_crc32 = computed_crc32;
if (force_exit) {
kprintf ("crc32_check_and_repair successfully repair one bit in crc32 (%d bytes block).\n", l);
}
return 2;
}
assert (!force_exit);
*input_crc32 = computed_crc32;
return -1;
}
static void crc32_init (void) __attribute__ ((constructor));
void crc32_init (void) {
kdb_cpuid_t *p = kdb_cpuid ();
if (p->ecx & 2) {
crc32_partial = crc32_partial_clmul;
crc64_partial = crc64_partial_clmul;
compute_crc32_combine = compute_crc32_combine_clmul;
compute_crc64_combine = compute_crc64_combine_clmul;
} else {
crc32_partial = crc32_partial_generic;
crc64_partial = crc64_partial_one_table;
compute_crc32_combine = compute_crc32_combine_generic;
compute_crc64_combine = compute_crc64_combine_generic;
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned (*crc32_partial_func_t) (const void *data, long len, unsigned crc);
typedef unsigned (*crc32_combine_func_t) (unsigned crc1, unsigned crc2, int64_t len2);
typedef uint64_t (*crc64_partial_func_t) (const void *data, long len, uint64_t crc);
typedef uint64_t (*crc64_combine_func_t) (uint64_t crc1, uint64_t crc2, int64_t len2);
extern crc32_partial_func_t crc32_partial;
extern crc64_partial_func_t crc64_partial;
extern crc32_combine_func_t compute_crc32_combine;
extern crc64_combine_func_t compute_crc64_combine;
static inline unsigned compute_crc32 (const void *data, long len) {
return crc32_partial (data, len, -1) ^ -1;
}
static inline uint64_t compute_crc64 (const void *data, long len) {
return crc64_partial (data, len, -1LL) ^ -1LL;
}
/* crc32_check_and_repair returns
0 : Cyclic redundancy check is ok
1 : Cyclic redundancy check fails, but we fix one bit in input
2 : Cyclic redundancy check fails, but we fix one bit in input_crc32
-1 : Cyclic redundancy check fails, no repair possible.
In this case *input_crc32 will be equal crc32 (input, l)
Case force_exit == 1 (case 1, 2: kprintf call, case -1: assert fail).
*/
int crc32_check_and_repair (void *input, int l, unsigned *input_crc32, int force_exit);
int crc32_find_corrupted_bit (int size, unsigned d);
int crc32_repair_bit (unsigned char *input, int l, int k);
/* these functions are exported only for testing purpose */
unsigned crc32_partial_generic (const void *data, long len, unsigned crc);
unsigned crc32_partial_clmul (const void *data, long len, unsigned crc);
uint64_t crc64_partial_one_table (const void *data, long len, uint64_t crc);
uint64_t crc64_partial_clmul (const void *data, long len, uint64_t crc);
uint64_t crc64_feed_byte (uint64_t crc, unsigned char b);
void gf32_compute_powers_generic (unsigned *P, int size, unsigned poly);
void gf32_compute_powers_clmul (unsigned *P, unsigned poly);
unsigned gf32_combine_generic (unsigned *powers, unsigned crc1, int64_t len2);
uint64_t gf32_combine_clmul (unsigned *powers, unsigned crc1, int64_t len2);
#ifdef __cplusplus
}
#endif
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <stdint.h>
#include "crc32c.h"
#include "kprintf.h"
#include "cpuid.h"
static unsigned crc32c_table[256] = {
0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,
0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,
0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,
0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,
0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,
0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,
0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,
0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,
0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,
0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,
0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,
0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,
0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,
0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,
0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,
0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,
0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,
0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,
0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,
0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,
0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,
0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,
0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,
0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,
0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,
0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,
0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,
0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,
0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,
0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,
0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,
0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,
0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,
0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,
0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,
0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,
0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,
0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,
0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,
0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,
0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,
0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,
0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,
0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,
0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,
0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,
0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,
0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,
0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,
0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,
0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,
0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,
0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,
0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,
0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,
0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,
0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,
0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,
0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,
0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,
0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,
0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,
0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,
0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351,
};
static unsigned crc32c_table2[256] = {
0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899,
0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945,
0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,
0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd,
0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918,
0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,
0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0,
0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c,
0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,
0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47,
0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823,
0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,
0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a,
0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6,
0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,
0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e,
0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d,
0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,
0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25,
0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9,
0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,
0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0,
0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4,
0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78,
0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f,
0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43,
0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27,
0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb,
0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e,
0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,
0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6,
0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a,
0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,
0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc,
0x66d73941, 0x7575a136, 0x419209af, 0x523091d8,
0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004,
0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1,
0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d,
0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,
0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185,
0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162,
0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,
0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da,
0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306,
0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,
0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f,
0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b,
0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,
0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464,
0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8,
0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,
0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600,
0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5,
0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,
0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d,
0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781,
0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,
0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba,
0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de,
0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,
0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7,
0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b,
0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,
0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483,
};
static unsigned crc32c_table1[256] = {
0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073,
0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469,
0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,
0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac,
0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9,
0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3,
0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c,
0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726,
0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67,
0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d,
0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2,
0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8,
0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed,
0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7,
0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828,
0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32,
0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa,
0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,
0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f,
0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75,
0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,
0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a,
0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5,
0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,
0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe,
0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4,
0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,
0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161,
0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634,
0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,
0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1,
0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb,
0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,
0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a,
0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5,
0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,
0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba,
0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0,
0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,
0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065,
0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24,
0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,
0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1,
0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb,
0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,
0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4,
0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b,
0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,
0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9,
0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3,
0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,
0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36,
0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63,
0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79,
0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6,
0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc,
0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd,
0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7,
0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238,
0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,
0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177,
0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d,
0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2,
0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8,
};
static unsigned crc32c_table0[256] = {
0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939,
0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca,
0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,
0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c,
0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804,
0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,
0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2,
0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11,
0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2,
0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41,
0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54,
0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,
0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f,
0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c,
0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,
0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a,
0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de,
0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d,
0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538,
0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb,
0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,
0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610,
0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405,
0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,
0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255,
0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6,
0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,
0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040,
0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368,
0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,
0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e,
0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d,
0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,
0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5,
0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0,
0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213,
0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b,
0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8,
0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,
0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e,
0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d,
0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,
0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b,
0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698,
0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0,
0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443,
0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656,
0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,
0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1,
0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12,
0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,
0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4,
0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc,
0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f,
0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a,
0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9,
0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,
0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99,
0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c,
0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,
0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57,
0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4,
0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,
0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842,
};
#ifdef __LP64__
static unsigned crc32c_partial_sse42 (const void *data, long len, unsigned crc) {
const char *p = data;
unsigned long long c = crc;
while ((((uintptr_t) p) & 7) && (len > 0)) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (c)
: "0" (c), "r" (p)
);
p++;
len--;
}
while (len >= 8) {
asm volatile (
"crc32q (%2), %1\n\t"
: "=r" (c)
: "0" (c), "r" (p)
);
p += 8;
len -= 8;
}
while (len > 0) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (c)
: "0" (c), "r" (p)
);
p++;
len--;
}
return c;
}
#define CRC32C_REFLECTED_X1023 0x7417153fll
#define CRC32C_REFLECTED_X2047 0x1426a815ll
#define CRC32C_REFLECTED_X4095 0xe986c148ll
#define CRC32C_REFLECTED_X8191 0xcdc220ddll
#define CRC32C_REFLECTED_X16383 0x1acaec54ll
static unsigned crc32c_partial_sse42_clmul (const void *data, long len, unsigned crc) {
const char *p = data;
if (unlikely ((((uintptr_t) p) & 1)) && (len > 0)) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p++;
len--;
}
if (unlikely ((((uintptr_t) p) & 2)) && (len >= 2)) {
asm volatile (
"crc32w (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p += 2;
len -= 2;
}
if ((((uintptr_t) p) & 4) && (len >= 4)) {
asm volatile (
"crc32l (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p += 4;
len -= 4;
}
if (unlikely (((uintptr_t) p) & 7)) {
while (len > 0) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p++;
len--;
}
return crc;
}
unsigned long long c = crc;
while (len >= 3 * 0x400) {
unsigned long long c1 = 0, c2 = 0;
const char *q = p + 0x400;
do {
asm volatile(
"crc32q (%3), %0\n\t"
"crc32q 0x400(%3), %1\n\t"
"crc32q 0x800(%3), %2\n\t"
"crc32q 8(%3), %0\n\t"
"crc32q 0x408(%3), %1\n\t"
"crc32q 0x808(%3), %2\n\t"
: "=r"(c), "=r"(c1), "=r"(c2)
: "r"(p), "0"(c), "1"(c1), "2"(c2));
p += 16;
} while (p < q);
static const v2di K = { CRC32C_REFLECTED_X16383, CRC32C_REFLECTED_X8191 };
v2di D, E;
asm volatile ("movq %1, %0\n\t" : "=x" (D) : "g" (c));
asm volatile ("movq %1, %0\n\t" : "=x" (E) : "g" (c1));
v2di F = __builtin_ia32_pclmulqdq128 (D, K, 0x00) ^ __builtin_ia32_pclmulqdq128 (E, K, 0x10);
asm volatile ("movq %1, %0\n\t" : "=g" (c1) : "x"(F));
unsigned int r;
asm volatile ("crc32l %1, %0\n\t" : "=r" (r) : "r"((int) c1), "0"(0));
p += 0x800;
len -= 3 * 0x400;
c = r ^ (c1 >> 32) ^ c2;
}
while (len >= 0x180) {
unsigned long long c1 = 0, c2 = 0;
asm volatile(
"crc32q (%3), %0\n\t"
"crc32q 0x80(%3), %1\n\t"
"crc32q 0x100(%3), %2\n\t"
"crc32q 0x8(%3), %0\n\t"
"crc32q 0x88(%3), %1\n\t"
"crc32q 0x108(%3), %2\n\t"
"crc32q 0x10(%3), %0\n\t"
"crc32q 0x90(%3), %1\n\t"
"crc32q 0x110(%3), %2\n\t"
"crc32q 0x18(%3), %0\n\t"
"crc32q 0x98(%3), %1\n\t"
"crc32q 0x118(%3), %2\n\t"
"crc32q 0x20(%3), %0\n\t"
"crc32q 0xa0(%3), %1\n\t"
"crc32q 0x120(%3), %2\n\t"
"crc32q 0x28(%3), %0\n\t"
"crc32q 0xa8(%3), %1\n\t"
"crc32q 0x128(%3), %2\n\t"
"crc32q 0x30(%3), %0\n\t"
"crc32q 0xb0(%3), %1\n\t"
"crc32q 0x130(%3), %2\n\t"
"crc32q 0x38(%3), %0\n\t"
"crc32q 0xb8(%3), %1\n\t"
"crc32q 0x138(%3), %2\n\t"
"crc32q 0x40(%3), %0\n\t"
"crc32q 0xc0(%3), %1\n\t"
"crc32q 0x140(%3), %2\n\t"
"crc32q 0x48(%3), %0\n\t"
"crc32q 0xc8(%3), %1\n\t"
"crc32q 0x148(%3), %2\n\t"
"crc32q 0x50(%3), %0\n\t"
"crc32q 0xd0(%3), %1\n\t"
"crc32q 0x150(%3), %2\n\t"
"crc32q 0x58(%3), %0\n\t"
"crc32q 0xd8(%3), %1\n\t"
"crc32q 0x158(%3), %2\n\t"
"crc32q 0x60(%3), %0\n\t"
"crc32q 0xe0(%3), %1\n\t"
"crc32q 0x160(%3), %2\n\t"
"crc32q 0x68(%3), %0\n\t"
"crc32q 0xe8(%3), %1\n\t"
"crc32q 0x168(%3), %2\n\t"
"crc32q 0x70(%3), %0\n\t"
"crc32q 0xf0(%3), %1\n\t"
"crc32q 0x170(%3), %2\n\t"
"crc32q 0x78(%3), %0\n\t"
"crc32q 0xf8(%3), %1\n\t"
"crc32q 0x178(%3), %2\n\t"
: "=r"(c), "=r"(c1), "=r"(c2)
: "r"(p), "0"(c), "1"(c1), "2"(c2));
static const v2di K = { CRC32C_REFLECTED_X2047, CRC32C_REFLECTED_X1023 };
v2di D, E;
asm volatile ("movq %1, %0\n\t" : "=x" (D) : "g" (c));
asm volatile ("movq %1, %0\n\t" : "=x" (E) : "g" (c1));
v2di F = __builtin_ia32_pclmulqdq128 (D, K, 0x00) ^ __builtin_ia32_pclmulqdq128 (E, K, 0x10);
asm volatile ("movq %1, %0\n\t" : "=g" (c1) : "x"(F));
unsigned int r;
asm volatile ("crc32l %1, %0\n\t" : "=r" (r) : "r"((int) c1), "0"(0));
p += 0x180;
len -= 0x180;
c = r ^ (c1 >> 32) ^ c2;
}
while (len >= 32) {
asm volatile (
"crc32q (%2), %1\n\t"
"crc32q 8(%2), %1\n\t"
"crc32q 0x10(%2), %1\n\t"
"crc32q 0x18(%2), %1\n\t"
: "=r" (c)
: "0" (c), "r" (p)
);
p += 32;
len -= 32;
}
while (len >= 8) {
asm volatile (
"crc32q (%2), %1\n\t"
: "=r" (c)
: "0" (c), "r" (p)
);
p += 8;
len -= 8;
}
crc = c;
if (len & 4) {
asm volatile (
"crc32l (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p += 4;
}
if (likely (!(len & 3))) {
return crc;
}
if (len & 2) {
asm volatile (
"crc32w (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p += 2;
}
if (len & 1) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
}
return crc;
}
#else
static unsigned crc32c_partial_sse42 (const void *data, long len, unsigned crc) {
const char *p = data;
while ((((uintptr_t) p) & 3) && (len > 0)) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p++;
len--;
}
while (len >= 4) {
asm volatile (
"crc32l (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p += 4;
len -= 4;
}
while (len > 0) {
asm volatile (
"crc32b (%2), %1\n\t"
: "=r" (crc)
: "0" (crc), "r" (p)
);
p++;
len--;
}
return crc;
}
#endif
unsigned crc32c_partial_four_tables (const void *data, long len, unsigned crc) {
const int *p = (const int *) data;
int x;
#define DO_ONE(v) crc ^= v; crc = crc32c_table0[crc & 0xff] ^ crc32c_table1[(crc & 0xff00) >> 8] ^ crc32c_table2[(crc & 0xff0000) >> 16] ^ crc32c_table[crc >> 24];
#define DO_FOUR(p) DO_ONE((p)[0]); DO_ONE((p)[1]); DO_ONE((p)[2]); DO_ONE((p)[3]);
for (x = (len >> 5); x > 0; x--) {
DO_FOUR (p);
DO_FOUR (p + 4);
p += 8;
}
if (len & 16) {
DO_FOUR (p);
p += 4;
}
if (len & 8) {
DO_ONE (p[0]);
DO_ONE (p[1]);
p += 2;
}
if (len & 4) {
DO_ONE (*p++);
}
#undef DO_ONE
#undef DO_FOUR
const char *q = (const char *) p;
if (len & 2) {
crc = crc32c_table[(crc ^ q[0]) & 0xff] ^ (crc >> 8);
crc = crc32c_table[(crc ^ q[1]) & 0xff] ^ (crc >> 8);
q += 2;
}
if (len & 1) {
crc = crc32c_table[(crc ^ *q++) & 0xff] ^ (crc >> 8);
}
return crc;
}
#define CRC32C_REFLECTED_POLY 0x82F63B78U
static unsigned crc32c_combine_generic (unsigned crc1, unsigned crc2, int64_t len2) {
#define N (32*67)
static unsigned int crc32c_powers[N];
/* degenerate case (also disallow negative lengths) */
if (len2 <= 0) {
return crc1;
}
if (!crc32c_powers[N-1]) {
gf32_compute_powers_generic (crc32c_powers, N, CRC32C_REFLECTED_POLY);
assert (crc32c_powers[N-1]);
}
return gf32_combine_generic (crc32c_powers, crc1, len2) ^ crc2;
}
static unsigned crc32c_combine_clmul (unsigned crc1, unsigned crc2, int64_t len2) {
static unsigned int crc32c_powers[252] __attribute__ ((aligned(16)));
if (len2 <= 0) {
return crc1;
}
if (!crc32c_powers[251]) {
gf32_compute_powers_clmul (crc32c_powers, CRC32C_REFLECTED_POLY);
assert (crc32c_powers[7 * 4 + 3] == (int) CRC32C_REFLECTED_X1023);
assert (crc32c_powers[8 * 4 + 3] == (int) CRC32C_REFLECTED_X2047);
assert (crc32c_powers[9 * 4 + 3] == (int) CRC32C_REFLECTED_X4095);
assert (crc32c_powers[10 * 4 + 3] == (int) CRC32C_REFLECTED_X8191);
assert (crc32c_powers[11 * 4 + 3] == (int) CRC32C_REFLECTED_X16383);
assert (crc32c_powers[251]);
}
uint64_t T = gf32_combine_clmul (crc32c_powers, crc1, len2);
unsigned crc = 0;
asm volatile (
"crc32l %2, %1\n\t"
: "=r" (crc)
: "0" (crc), "r" ( (unsigned) T)
);
return crc ^ ((unsigned) (T >> 32)) ^ crc2;
}
static void crc32c_init (void) __attribute__ ((constructor));
void crc32c_init (void) {
kdb_cpuid_t *p = kdb_cpuid ();
compute_crc32c_combine = &crc32c_combine_generic;
if (p->ecx & (1 << 20)) {
crc32c_partial = crc32c_partial_sse42;
#ifdef __LP64__
if (p->ecx & 2) {
crc32c_partial = crc32c_partial_sse42_clmul;
compute_crc32c_combine = &crc32c_combine_clmul;
}
#endif
} else {
crc32c_partial = &crc32c_partial_four_tables;
}
}
crc32_partial_func_t crc32c_partial;
crc32_combine_func_t compute_crc32c_combine;
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#pragma once
#include "common/crc32.h"
#ifdef __cplusplus
extern "C" {
#endif
//extern unsigned int crc32c_table[256];
extern crc32_partial_func_t crc32c_partial;
extern crc32_combine_func_t compute_crc32c_combine;
static inline unsigned compute_crc32c (const void *data, int len) {
return crc32c_partial (data, len, -1) ^ -1;
}
unsigned crc32c_partial_four_tables (const void *data, long len, unsigned crc);
#ifdef __cplusplus
}
#endif
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/file.h>
#include <unistd.h>
#include "kprintf.h"
#include "precise-time.h"
int verbosity;
const char *logname;
void reopen_logs_ext (int slave_mode) {
int fd;
fflush (stdout);
fflush (stderr);
if ((fd = open ("/dev/null", O_RDWR, 0)) != -1) {
dup2 (fd, 0);
dup2 (fd, 1);
dup2 (fd, 2);
if (fd > 2) {
close (fd);
}
}
if (logname && (fd = open (logname, O_WRONLY|O_APPEND|O_CREAT, 0640)) != -1) {
dup2 (fd, 1);
dup2 (fd, 2);
if (fd > 2) {
close (fd);
}
}
if (!slave_mode) {
vkprintf (1, "logs reopened.\n");
}
}
void reopen_logs (void) {
reopen_logs_ext (0);
}
int hexdump (const void *start, const void *end) {
char s[256];
const char *ptr = start;
while (ptr < (char *) end) {
int len = (const char *) end - ptr, i;
if (len > 16) {
len = 16;
}
int p = 0;
p += sprintf (s + p, "%08x", (int) (ptr - (char *) start));
for (i = 0; i < 16; i++) {
s[p ++] = ' ';
if (i == 8) {
s[p ++] = ' ';
}
if (i < len) {
p += sprintf (s + p, "%02x", (unsigned char) ptr[i]);
} else {
p += sprintf (s + p, " ");
}
}
s[p ++] = '\n';
nck_write (2, s, p);
ptr += 16;
}
return end - start;
}
double reindex_speed = (32 << 20);
void kdb_write (int fd, const void *buf, long long count, const char *filename) {
assert (count >= 0);
static double total_count;
static double last_time;
int write_fail_count = 0;
while (count) {
long long l = !reindex_speed ? count : count >= (1 << 20) ? (1 << 20) : count;
if (reindex_speed) {
double t = get_utime_monotonic ();
total_count = total_count * exp ((last_time - t) * 0.1);
last_time = t;
if (total_count > reindex_speed) {
double k = log (total_count / reindex_speed) * 10;
assert (k >= 0);
struct timespec ts;
ts.tv_nsec = ((int)((k - floor (k)) * 1e9)) % 1000000000;
ts.tv_sec = (int)k;
nanosleep (&ts, 0);
}
}
long long w = write (fd, buf, l);
if (w <= 0) {
assert (-1 <= w);
if (write_fail_count < 10000 && (w == 0 || errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) {
write_fail_count++;
continue;
}
fprintf (stderr, "kdb_write: write %lld bytes to the file '%s' returns %lld. %m\n", l, filename, w);
exit (1);
}
assert (w <= l);
write_fail_count = 0;
if (reindex_speed) {
static long long data_after_fsync;
data_after_fsync += w;
if (data_after_fsync >= (1 << 20)) {
if (fsync (fd) < 0) {
fprintf (stderr, "kdb_write: fsyncing file '%s' failed. %m\n", filename);
exit (1);
}
data_after_fsync = 0;
}
double t = get_utime_monotonic ();
total_count = total_count * exp ((last_time - t) * 0.1);
last_time = t;
total_count += w * 0.1;
}
count -= w;
buf += w;
}
}
static inline void kwrite_print_int (char **s, const char *name, int name_len, int i) {
if (i < 0) {
i = INT_MAX;
}
*--*s = ' ';
*--*s = ']';
do {
*--*s = i % 10 + '0';
i /= 10;
} while (i > 0);
*--*s = ' ';
while (--name_len >= 0) {
*--*s = name[name_len];
}
*--*s = '[';
}
int kwrite (int fd, const void *buf, int count) {
int old_errno = errno;
#define S_BUF_SIZE 100
#define S_DATA_SIZE 256
char s[S_BUF_SIZE + S_DATA_SIZE], *s_begin = s + S_BUF_SIZE;
kwrite_print_int (&s_begin, "time", 4, time (NULL));
kwrite_print_int (&s_begin, "pid" , 3, getpid ());
assert (s_begin >= s);
int s_count = s + S_BUF_SIZE - s_begin;
if (count <= S_DATA_SIZE) {
int i;
for (i = 0; i < count; i++) {
s[i + S_BUF_SIZE] = ((char *)buf)[i];
}
s_count += count;
count = 0;
}
int result = s_count + count;
while (s_count > 0) {
errno = 0;
int res = (int)write (fd, s_begin, (size_t)s_count);
if (errno && errno != EINTR) {
errno = old_errno;
return res;
}
if (!res) {
break;
}
if (res >= 0) {
s_begin += res;
s_count -= res;
}
}
while (count > 0) {
errno = 0;
int res = (int)write (fd, buf, (size_t)count);
if (errno && errno != EINTR) {
errno = old_errno;
return res;
}
if (!res) {
break;
}
if (res >= 0) {
buf += res;
count -= res;
}
}
errno = old_errno;
return result;
#undef S_BUF_SIZE
#undef S_DATA_SIZE
}
void kprintf (const char *format, ...) {
const int old_errno = errno;
struct tm t;
struct timeval tv;
char mp_kprintf_buf[PIPE_BUF];
if (gettimeofday (&tv, NULL) || !localtime_r (&tv.tv_sec, &t)) {
memset (&t, 0, sizeof (t));
}
int n = snprintf (mp_kprintf_buf, sizeof (mp_kprintf_buf), "[%d][%4d-%02d-%02d %02d:%02d:%02d.%06d local] ", getpid (), t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, (int) tv.tv_usec);
if (n < sizeof (mp_kprintf_buf) - 1) {
errno = old_errno;
va_list ap;
va_start (ap, format);
n += vsnprintf (mp_kprintf_buf + n, sizeof (mp_kprintf_buf) - n, format, ap);
va_end (ap);
}
if (n >= sizeof (mp_kprintf_buf)) {
n = sizeof (mp_kprintf_buf) - 1;
if (mp_kprintf_buf[n-1] != '\n') {
mp_kprintf_buf[n++] = '\n';
}
}
while (write (2, mp_kprintf_buf, n) < 0 && errno == EINTR);
//while (flock (2, LOCK_UN) < 0 && errno == EINTR);
errno = old_errno;
}
void nck_write (int fd, const void *data, size_t len) {
if (write (fd, data, len)) {}
}
void nck_pwrite (int fd, const void *data, size_t len, off_t offset) {
if (pwrite (fd, data, len, offset)) {}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2012 Vkontakte Ltd
2009-2012 Nikolai Durov
2009-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#pragma once
#include <stddef.h>
#include <sys/types.h>
extern int verbosity;
extern const char *logname;
void reopen_logs (void);
void reopen_logs_ext (int slave_mode);
int hexdump (const void *start, const void *end);
extern double reindex_speed;
// safely writes buf to fd, considering write speed limit
void kdb_write (int fd, const void *buf, long long count, const char *filename);
// write message with timestamp and pid, safe to call inside handler
int kwrite (int fd, const void *buf, int count);
// print message with timestamp
void kprintf (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
#define vkprintf(verbosity_level, format, ...) do { \
if ((verbosity_level) > verbosity) { \
break; \
} \
kprintf ((format), ##__VA_ARGS__); \
} while (0)
void nck_write (int fd, const void *data, size_t len);
void nck_pwrite (int fd, const void *data, size_t len, off_t offset);
/*
* RFC 1321 compliant MD5 implementation
*
* Copyright (C) 2006-2007 Christophe Devine
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License, version 2.1 as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
/*
* The MD5 algorithm was designed by Ron Rivest in 1991.
*
* http://www.ietf.org/rfc/rfc1321.txt
*/
// #include "xyssl/config.h"
#if !defined(XYSSL_MD5_C)
#include "md5.h"
#include <string.h>
#include <stdio.h>
/*
* 32-bit integer manipulation macros (little endian)
*/
#ifndef GET_ULONG_LE
#define GET_ULONG_LE(n,b,i) \
{ \
(n) = ( (unsigned long) (b)[(i) ] ) \
| ( (unsigned long) (b)[(i) + 1] << 8 ) \
| ( (unsigned long) (b)[(i) + 2] << 16 ) \
| ( (unsigned long) (b)[(i) + 3] << 24 ); \
}
#endif
#ifndef PUT_ULONG_LE
#define PUT_ULONG_LE(n,b,i) \
{ \
(b)[(i) ] = (unsigned char) ( (n) ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 3] = (unsigned char) ( (n) >> 24 ); \
}
#endif
/*
* MD5 context setup
*/
void md5_starts( md5_context *ctx )
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
}
static void md5_process( md5_context *ctx, unsigned char data[64] )
{
unsigned long X[16], A, B, C, D;
GET_ULONG_LE( X[ 0], data, 0 );
GET_ULONG_LE( X[ 1], data, 4 );
GET_ULONG_LE( X[ 2], data, 8 );
GET_ULONG_LE( X[ 3], data, 12 );
GET_ULONG_LE( X[ 4], data, 16 );
GET_ULONG_LE( X[ 5], data, 20 );
GET_ULONG_LE( X[ 6], data, 24 );
GET_ULONG_LE( X[ 7], data, 28 );
GET_ULONG_LE( X[ 8], data, 32 );
GET_ULONG_LE( X[ 9], data, 36 );
GET_ULONG_LE( X[10], data, 40 );
GET_ULONG_LE( X[11], data, 44 );
GET_ULONG_LE( X[12], data, 48 );
GET_ULONG_LE( X[13], data, 52 );
GET_ULONG_LE( X[14], data, 56 );
GET_ULONG_LE( X[15], data, 60 );
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a,b,c,d,k,s,t) \
{ \
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x,y,z) (z ^ (x & (y ^ z)))
P( A, B, C, D, 0, 7, 0xD76AA478 );
P( D, A, B, C, 1, 12, 0xE8C7B756 );
P( C, D, A, B, 2, 17, 0x242070DB );
P( B, C, D, A, 3, 22, 0xC1BDCEEE );
P( A, B, C, D, 4, 7, 0xF57C0FAF );
P( D, A, B, C, 5, 12, 0x4787C62A );
P( C, D, A, B, 6, 17, 0xA8304613 );
P( B, C, D, A, 7, 22, 0xFD469501 );
P( A, B, C, D, 8, 7, 0x698098D8 );
P( D, A, B, C, 9, 12, 0x8B44F7AF );
P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
P( B, C, D, A, 11, 22, 0x895CD7BE );
P( A, B, C, D, 12, 7, 0x6B901122 );
P( D, A, B, C, 13, 12, 0xFD987193 );
P( C, D, A, B, 14, 17, 0xA679438E );
P( B, C, D, A, 15, 22, 0x49B40821 );
#undef F
#define F(x,y,z) (y ^ (z & (x ^ y)))
P( A, B, C, D, 1, 5, 0xF61E2562 );
P( D, A, B, C, 6, 9, 0xC040B340 );
P( C, D, A, B, 11, 14, 0x265E5A51 );
P( B, C, D, A, 0, 20, 0xE9B6C7AA );
P( A, B, C, D, 5, 5, 0xD62F105D );
P( D, A, B, C, 10, 9, 0x02441453 );
P( C, D, A, B, 15, 14, 0xD8A1E681 );
P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
P( A, B, C, D, 9, 5, 0x21E1CDE6 );
P( D, A, B, C, 14, 9, 0xC33707D6 );
P( C, D, A, B, 3, 14, 0xF4D50D87 );
P( B, C, D, A, 8, 20, 0x455A14ED );
P( A, B, C, D, 13, 5, 0xA9E3E905 );
P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
P( C, D, A, B, 7, 14, 0x676F02D9 );
P( B, C, D, A, 12, 20, 0x8D2A4C8A );
#undef F
#define F(x,y,z) (x ^ y ^ z)
P( A, B, C, D, 5, 4, 0xFFFA3942 );
P( D, A, B, C, 8, 11, 0x8771F681 );
P( C, D, A, B, 11, 16, 0x6D9D6122 );
P( B, C, D, A, 14, 23, 0xFDE5380C );
P( A, B, C, D, 1, 4, 0xA4BEEA44 );
P( D, A, B, C, 4, 11, 0x4BDECFA9 );
P( C, D, A, B, 7, 16, 0xF6BB4B60 );
P( B, C, D, A, 10, 23, 0xBEBFBC70 );
P( A, B, C, D, 13, 4, 0x289B7EC6 );
P( D, A, B, C, 0, 11, 0xEAA127FA );
P( C, D, A, B, 3, 16, 0xD4EF3085 );
P( B, C, D, A, 6, 23, 0x04881D05 );
P( A, B, C, D, 9, 4, 0xD9D4D039 );
P( D, A, B, C, 12, 11, 0xE6DB99E5 );
P( C, D, A, B, 15, 16, 0x1FA27CF8 );
P( B, C, D, A, 2, 23, 0xC4AC5665 );
#undef F
#define F(x,y,z) (y ^ (x | ~z))
P( A, B, C, D, 0, 6, 0xF4292244 );
P( D, A, B, C, 7, 10, 0x432AFF97 );
P( C, D, A, B, 14, 15, 0xAB9423A7 );
P( B, C, D, A, 5, 21, 0xFC93A039 );
P( A, B, C, D, 12, 6, 0x655B59C3 );
P( D, A, B, C, 3, 10, 0x8F0CCC92 );
P( C, D, A, B, 10, 15, 0xFFEFF47D );
P( B, C, D, A, 1, 21, 0x85845DD1 );
P( A, B, C, D, 8, 6, 0x6FA87E4F );
P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
P( C, D, A, B, 6, 15, 0xA3014314 );
P( B, C, D, A, 13, 21, 0x4E0811A1 );
P( A, B, C, D, 4, 6, 0xF7537E82 );
P( D, A, B, C, 11, 10, 0xBD3AF235 );
P( C, D, A, B, 2, 15, 0x2AD7D2BB );
P( B, C, D, A, 9, 21, 0xEB86D391 );
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
}
/*
* MD5 process buffer
*/
void md5_update( md5_context *ctx, unsigned char *input, int ilen )
{
int fill;
unsigned long left;
if( ilen <= 0 )
return;
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += ilen;
ctx->total[0] &= 0xFFFFFFFF;
if( ctx->total[0] < (unsigned long) ilen )
ctx->total[1]++;
if( left && ilen >= fill )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, fill );
md5_process( ctx, ctx->buffer );
input += fill;
ilen -= fill;
left = 0;
}
while( ilen >= 64 )
{
md5_process( ctx, input );
input += 64;
ilen -= 64;
}
if( ilen > 0 )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, ilen );
}
}
static const unsigned char md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*
* MD5 final digest
*/
void md5_finish( md5_context *ctx, unsigned char output[16] )
{
unsigned long last, padn;
unsigned long high, low;
unsigned char msglen[8];
high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 );
PUT_ULONG_LE( low, msglen, 0 );
PUT_ULONG_LE( high, msglen, 4 );
last = ctx->total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
md5_update( ctx, (unsigned char *) md5_padding, padn );
md5_update( ctx, msglen, 8 );
PUT_ULONG_LE( ctx->state[0], output, 0 );
PUT_ULONG_LE( ctx->state[1], output, 4 );
PUT_ULONG_LE( ctx->state[2], output, 8 );
PUT_ULONG_LE( ctx->state[3], output, 12 );
}
/*
* output = MD5( input buffer )
*/
void md5( unsigned char *input, int ilen, unsigned char output[16] )
{
md5_context ctx;
md5_starts( &ctx );
md5_update( &ctx, input, ilen );
md5_finish( &ctx, output );
memset( &ctx, 0, sizeof( md5_context ) );
}
void md5_hex (char *input, int ilen, char output[32]) {
static char out[16], hcyf[16] = "0123456789abcdef";
int i;
md5 ((unsigned char *) input, ilen, (unsigned char *) out);
for (i = 0; i < 16; i++) {
output[2*i] = hcyf[(out[i] >> 4) & 15];
output[2*i+1] = hcyf[out[i] & 15];
}
}
/*
* output = MD5( file contents )
*/
int md5_file( char *path, unsigned char output[16] )
{
FILE *f;
size_t n;
md5_context ctx;
unsigned char buf[1024];
if( ( f = fopen( path, "rb" ) ) == NULL )
return( 1 );
md5_starts( &ctx );
while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
md5_update( &ctx, buf, (int) n );
md5_finish( &ctx, output );
memset( &ctx, 0, sizeof( md5_context ) );
if( ferror( f ) != 0 )
{
fclose( f );
return( 2 );
}
fclose( f );
return( 0 );
}
/*
* MD5 HMAC context setup
*/
void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen )
{
int i;
unsigned char sum[16];
if( keylen > 64 )
{
md5( key, keylen, sum );
keylen = 16;
key = sum;
}
memset( ctx->ipad, 0x36, 64 );
memset( ctx->opad, 0x5C, 64 );
for( i = 0; i < keylen; i++ )
{
ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] );
ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] );
}
md5_starts( ctx );
md5_update( ctx, ctx->ipad, 64 );
memset( sum, 0, sizeof( sum ) );
}
/*
* MD5 HMAC process buffer
*/
void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen )
{
md5_update( ctx, input, ilen );
}
/*
* MD5 HMAC final digest
*/
void md5_hmac_finish( md5_context *ctx, unsigned char output[16] )
{
unsigned char tmpbuf[16];
md5_finish( ctx, tmpbuf );
md5_starts( ctx );
md5_update( ctx, ctx->opad, 64 );
md5_update( ctx, tmpbuf, 16 );
md5_finish( ctx, output );
memset( tmpbuf, 0, sizeof( tmpbuf ) );
}
/*
* output = HMAC-MD5( hmac key, input buffer )
*/
void md5_hmac( unsigned char *key, int keylen, unsigned char *input, int ilen,
unsigned char output[16] )
{
md5_context ctx;
md5_hmac_starts( &ctx, key, keylen );
md5_hmac_update( &ctx, input, ilen );
md5_hmac_finish( &ctx, output );
memset( &ctx, 0, sizeof( md5_context ) );
}
#if defined(XYSSL_SELF_TEST)
/*
* RFC 1321 test vectors
*/
static const char md5_test_str[7][81] =
{
{ "" },
{ "a" },
{ "abc" },
{ "message digest" },
{ "abcdefghijklmnopqrstuvwxyz" },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" },
{ "12345678901234567890123456789012345678901234567890123456789012" \
"345678901234567890" }
};
static const unsigned char md5_test_sum[7][16] =
{
{ 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E },
{ 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8,
0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 },
{ 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0,
0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 },
{ 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D,
0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 },
{ 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00,
0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B },
{ 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5,
0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F },
{ 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55,
0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A }
};
/*
* Checkup routine
*/
int md5_self_test( int verbose )
{
int i;
unsigned char md5sum[16];
for( i = 0; i < 7; i++ )
{
if( verbose != 0 )
printf( " MD5 test #%d: ", i + 1 );
md5( (unsigned char *) md5_test_str[i],
strlen( md5_test_str[i] ), md5sum );
if( memcmp( md5sum, md5_test_sum[i], 16 ) != 0 )
{
if( verbose != 0 )
printf( "failed\n" );
return( 1 );
}
if( verbose != 0 )
printf( "passed\n" );
}
if( verbose != 0 )
printf( "\n" );
return( 0 );
}
#endif
#endif
/**
* \file md5.h
*/
#ifndef XYSSL_MD5_H
#define XYSSL_MD5_H
/**
* \brief MD5 context structure
*/
typedef struct
{
unsigned long total[2]; /*!< number of bytes processed */
unsigned long state[4]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
unsigned char ipad[64]; /*!< HMAC: inner padding */
unsigned char opad[64]; /*!< HMAC: outer padding */
}
md5_context;
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief MD5 context setup
*
* \param ctx context to be initialized
*/
void md5_starts( md5_context *ctx );
/**
* \brief MD5 process buffer
*
* \param ctx MD5 context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void md5_update( md5_context *ctx, unsigned char *input, int ilen );
/**
* \brief MD5 final digest
*
* \param ctx MD5 context
* \param output MD5 checksum result
*/
void md5_finish( md5_context *ctx, unsigned char output[16] );
/**
* \brief Output = MD5( input buffer )
*
* \param input buffer holding the data
* \param ilen length of the input data
* \param output MD5 checksum result
*/
void md5( unsigned char *input, int ilen, unsigned char output[16] );
void md5_hex (char *input, int ilen, char output[32]);
/**
* \brief Output = MD5( file contents )
*
* \param path input file name
* \param output MD5 checksum result
*
* \return 0 if successful, 1 if fopen failed,
* or 2 if fread failed
*/
int md5_file( char *path, unsigned char output[16] );
/**
* \brief MD5 HMAC context setup
*
* \param ctx HMAC context to be initialized
* \param key HMAC secret key
* \param keylen length of the HMAC key
*/
void md5_hmac_starts( md5_context *ctx, unsigned char *key, int keylen );
/**
* \brief MD5 HMAC process buffer
*
* \param ctx HMAC context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void md5_hmac_update( md5_context *ctx, unsigned char *input, int ilen );
/**
* \brief MD5 HMAC final digest
*
* \param ctx HMAC context
* \param output MD5 HMAC checksum result
*/
void md5_hmac_finish( md5_context *ctx, unsigned char output[16] );
/**
* \brief Output = HMAC-MD5( hmac key, input buffer )
*
* \param key HMAC secret key
* \param keylen length of the HMAC key
* \param input buffer holding the data
* \param ilen length of the input data
* \param output HMAC-MD5 result
*/
void md5_hmac( unsigned char *key, int keylen,
unsigned char *input, int ilen,
unsigned char output[16] );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int md5_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* md5.h */
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2018 Telegram Messenger Inc
2014-2015 Andrey Lopatin
2014-2018 Nikolai Durov
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <linux/futex.h>
#include <sys/syscall.h>
#include "server-functions.h"
#include "kprintf.h"
#include "precise-time.h"
#include "mp-queue.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
volatile int mpq_blocks_allocated, mpq_blocks_allocated_max, mpq_blocks_allocations, mpq_blocks_true_allocations, mpq_blocks_wasted, mpq_blocks_prepared;
volatile int mpq_small_blocks_allocated, mpq_small_blocks_allocated_max;
__thread int mpq_this_thread_id;
__thread void **thread_hazard_pointers;
volatile int mpq_threads;
struct mp_queue MqGarbageBlocks, MqPreparedBlocks;
struct mp_queue MqGarbageSmallBlocks, MqPreparedSmallBlocks;
#define MODULE mp_queue
MODULE_STAT_TYPE {
int mpq_active;
int mpq_allocated;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SBP_PRINT_I32 (mpq_blocks_allocated);
SBP_PRINT_I32 (mpq_blocks_allocated_max);
SBP_PRINT_I32 (mpq_blocks_allocations);
SBP_PRINT_I32 (mpq_blocks_true_allocations);
SBP_PRINT_I32 (mpq_blocks_wasted);
SBP_PRINT_I32 (mpq_blocks_prepared);
SBP_PRINT_I32 (mpq_small_blocks_allocated);
SBP_PRINT_I32 (mpq_small_blocks_allocated_max);
SB_SUM_ONE_I (mpq_active);
SB_SUM_ONE_I (mpq_allocated);
MODULE_STAT_FUNCTION_END
/* hazard pointers, one per thread */
void *mqb_hazard_ptr[MAX_MPQ_THREADS][THREAD_HPTRS] __attribute__ ((aligned(64)));
int is_hazard_ptr (void *ptr, int a, int b) {
barrier ();
int k = mpq_threads, q = mpq_this_thread_id;
barrier ();
int i, j, r = 0;
for (j = a; j <= b; j++) {
if (mqb_hazard_ptr[q][j] == ptr) {
r = 1;
break;
}
}
for (i = 1; i <= k; i++) {
if (i == q) {
continue;
}
for (j = a; j <= b; j++) {
if (mqb_hazard_ptr[i][j] == ptr) {
barrier ();
return r + 2;
}
}
}
barrier ();
return r;
}
/* initialize this thread id and return it */
int get_this_thread_id (void) {
int i = mpq_this_thread_id;
if (i) {
return i;
}
i = __sync_fetch_and_add (&mpq_threads, 1) + 1;
assert (i > 0 && i < MAX_MPQ_THREADS);
thread_hazard_pointers = mqb_hazard_ptr[i];
return mpq_this_thread_id = i;
}
/* custom semaphore implementation using futexes */
int mp_sem_post (mp_sem_t *sem) {
__sync_fetch_and_add (&sem->value, 1);
if (sem->waiting > 0) {
syscall (__NR_futex, &sem->value, FUTEX_WAKE, 1, NULL, 0, 0);
}
return 0;
}
int mp_sem_wait (mp_sem_t *sem) {
int v = sem->value, q = 0;
while (1) {
if (v > 0) {
v = __sync_fetch_and_add (&sem->value, -1);
if (v > 0) {
return 0;
}
v = __sync_add_and_fetch (&sem->value, 1);
} else {
if (v < 0 && q++ < 10) {
barrier ();
v = sem->value;
continue;
}
__sync_fetch_and_add (&sem->waiting, 1);
syscall (__NR_futex, &sem->value, FUTEX_WAIT, v, NULL, 0, 0);
__sync_fetch_and_add (&sem->waiting, -1);
v = sem->value;
q = 0;
}
}
}
int mp_sem_trywait (mp_sem_t *sem) {
int v = sem->value;
if (v > 0) {
v = __sync_fetch_and_add (&sem->value, -1);
if (v > 0) {
return 0;
}
__sync_fetch_and_add (&sem->value, 1);
}
return -1;
}
/* functions for one mp_queue_block */
// may invoke mpq_pop()/mpq_push() if allow_recursion=1
struct mp_queue_block *alloc_mpq_block (mqn_value_t first_val, int allow_recursion, int is_small) {
struct mp_queue_block *QB = 0;
int prepared = 0, align_bytes = 0;
long size = (is_small ? MPQ_SMALL_BLOCK_SIZE : MPQ_BLOCK_SIZE);
if (allow_recursion) {
QB = mpq_pop (is_small ? &MqGarbageSmallBlocks : &MqGarbageBlocks, MPQF_RECURSIVE);
if (QB) {
if (!is_hazard_ptr (QB, 0, 2)) {
// reclaiming garbage
assert (QB->mqb_magic == MQ_BLOCK_GARBAGE_MAGIC);
__sync_fetch_and_add (&mpq_blocks_wasted, -1);
align_bytes = QB->mqb_align_bytes;
} else {
mpq_push (is_small ? &MqGarbageSmallBlocks : &MqGarbageBlocks, QB, MPQF_RECURSIVE);
QB = 0;
}
}
if (!QB) {
QB = mpq_pop (is_small ? &MqPreparedSmallBlocks : &MqPreparedBlocks, MPQF_RECURSIVE);
if (QB) {
assert (QB->mqb_magic == MQ_BLOCK_PREPARED_MAGIC);
prepared = 1;
__sync_fetch_and_add (&mpq_blocks_prepared, -1);
align_bytes = QB->mqb_align_bytes;
}
}
}
if (!QB) {
char *new_block = malloc (offsetof (struct mp_queue_block, mqb_nodes) + size * (2 * sizeof (void *)) + MPQ_BLOCK_ALIGNMENT - sizeof (void *));
assert (new_block);
assert (!((long) new_block & (sizeof (void *) - 1)));
align_bytes = -(int)(long) new_block & (MPQ_BLOCK_ALIGNMENT - 1);
QB = (struct mp_queue_block *) (new_block + align_bytes);
__sync_fetch_and_add (&mpq_blocks_true_allocations, 1);
if (is_small) {
int t = __sync_fetch_and_add (&mpq_small_blocks_allocated, 1);
if (t >= mpq_small_blocks_allocated_max) {
__sync_bool_compare_and_swap (&mpq_small_blocks_allocated_max, mpq_small_blocks_allocated_max, t + 1);
}
} else {
int t = __sync_fetch_and_add (&mpq_blocks_allocated, 1);
if (t >= mpq_blocks_allocated_max) {
__sync_bool_compare_and_swap (&mpq_blocks_allocated_max, mpq_blocks_allocated_max, t + 1);
}
}
} else {
assert (QB->mqb_size == size);
}
__sync_fetch_and_add (&mpq_blocks_allocations, 1);
memset (QB, 0, offsetof (struct mp_queue_block, mqb_nodes));
QB->mqb_align_bytes = align_bytes;
QB->mqb_size = size;
QB->mqb_nodes[0].idx = MQN_SAFE;
QB->mqb_nodes[0].val = first_val;
if (!prepared) {
long i;
for (i = 1; i < size; i++) {
QB->mqb_nodes[i].idx = MQN_SAFE + i;
QB->mqb_nodes[i].val = 0;
}
}
if (first_val) {
QB->mqb_tail = 1;
}
QB->mqb_magic = MQ_BLOCK_USED_MAGIC;
return QB;
}
void free_mpq_block (struct mp_queue_block *QB) {
assert (QB->mqb_magic == MQ_BLOCK_USED_MAGIC);
assert ((unsigned) QB->mqb_align_bytes < MPQ_BLOCK_ALIGNMENT && !(QB->mqb_align_bytes & (sizeof (void *) - 1)));
QB->mqb_magic = MQ_BLOCK_FREE_MAGIC;
if (QB->mqb_size == MPQ_SMALL_BLOCK_SIZE) {
__sync_fetch_and_add (&mpq_small_blocks_allocated, -1);
} else {
assert (QB->mqb_size == MPQ_BLOCK_SIZE);
__sync_fetch_and_add (&mpq_blocks_allocated, -1);
}
free ((char *) QB - QB->mqb_align_bytes);
}
static inline void mpq_fix_state (struct mp_queue_block *QB) {
long h, t;
while (1) {
barrier();
t = QB->mqb_tail;
barrier();
h = QB->mqb_head;
barrier();
if ((unsigned long) h <= (unsigned long) t) {
break;
}
if (QB->mqb_tail != t) {
continue;
}
// here tail < head ; try to advance tail to head
// (or to some value h such that tail < h <= head)
if (__sync_bool_compare_and_swap (&QB->mqb_tail, t, h)) {
break;
}
}
}
mqn_value_t mpq_block_pop (struct mp_queue_block *QB) {
// fprintf (stderr, "%d:mpq_block_pop(%p)\n", mpq_this_thread_id, QB);
long size = QB->mqb_size;
while (1) {
long h = __sync_fetch_and_add (&QB->mqb_head, 1);
// fprintf (stderr, "%d: mpq_block_pop(%ld)\n", mpq_this_thread_id, h);
mpq_node_t *node = &QB->mqb_nodes[h & (size - 1)];
while (1) {
mpq_node_t d, e;
barrier();
mqn_value_t val = node->val;
barrier();
long safe_idx = node->idx;
barrier();
long idx = safe_idx & MQN_IDX_MASK;
if (idx > h) {
break;
}
d.val = val;
d.idx = safe_idx;
if (val) {
if (idx == h) {
e.idx = safe_idx + size;
e.val = 0;
if (__sync_bool_compare_and_swap (&node->pair, d.pair, e.pair)) {
// fprintf (stderr, "%d: mpq_block_pop(%ld) -> %lx\n", mpq_this_thread_id, h, (long) val);
return val;
}
} else {
e.val = val;
e.idx = idx; // clear 'safe' flag
if (__sync_bool_compare_and_swap (&node->pair, d.pair, e.pair)) {
break;
}
}
} else {
e.idx = (safe_idx & MQN_SAFE) + h + size;
e.val = 0;
if (__sync_bool_compare_and_swap (&node->pair, d.pair, e.pair)) {
break;
}
}
/* somebody changed this element while we were inspecting it, make another loop iteration */
}
barrier();
long t = QB->mqb_tail & MQN_IDX_MASK;
barrier();
if (t <= h + 1) {
mpq_fix_state (QB);
return 0;
}
/* now try again with a new value of h */
}
}
long mpq_block_push (struct mp_queue_block *QB, mqn_value_t val) {
int iterations = 0;
long size = QB->mqb_size;
// fprintf (stderr, "%d:mpq_block_push(%p)\n", mpq_this_thread_id, QB);
while (1) {
long t = __sync_fetch_and_add (&QB->mqb_tail, 1);
// fprintf (stderr, "%d: mpq_block_push(%ld)\n", mpq_this_thread_id, t);
if (t & MQN_SAFE) {
return -1L; // bad luck
}
mpq_node_t *node = &QB->mqb_nodes[t & (size - 1)];
barrier();
mqn_value_t old_val = node->val;
barrier();
long safe_idx = node->idx;
barrier();
long idx = safe_idx & MQN_IDX_MASK;
if (!old_val && idx <= t && ((safe_idx & MQN_SAFE) || QB->mqb_head <= t)) {
mpq_node_t d, e;
d.idx = safe_idx;
d.val = 0;
e.idx = MQN_SAFE + t;
e.val = val;
if (__sync_bool_compare_and_swap (&node->pair, d.pair, e.pair)) {
// fprintf (stderr, "%d: mpq_block_push(%ld) <- %lx\n", mpq_this_thread_id, t, (long) val);
return t; // pushed OK
}
}
barrier ();
long h = QB->mqb_head;
barrier ();
if (t - h >= size || ++iterations > 10) {
__sync_fetch_and_or (&QB->mqb_tail, MQN_SAFE); // closing queue
return -1L; // bad luck
}
}
}
/* functions for mp_queue = list of mp_queue_block's */
void init_mp_queue (struct mp_queue *MQ) {
assert (MQ->mq_magic != MQ_MAGIC && MQ->mq_magic != MQ_MAGIC_SEM);
memset (MQ, 0, sizeof (struct mp_queue));
MQ->mq_head = MQ->mq_tail = alloc_mpq_block (0, 0, 1);
MQ->mq_magic = MQ_MAGIC;
if (!MqGarbageBlocks.mq_magic) {
init_mp_queue (&MqGarbageBlocks);
init_mp_queue (&MqGarbageSmallBlocks);
} else if (!MqPreparedBlocks.mq_magic) {
init_mp_queue (&MqPreparedBlocks);
init_mp_queue (&MqPreparedSmallBlocks);
}
}
void init_mp_queue_w (struct mp_queue *MQ) {
init_mp_queue (MQ);
MODULE_STAT->mpq_active ++;
#if MPQ_USE_POSIX_SEMAPHORES
sem_init (&MQ->mq_sem, 0, 0);
#endif
MQ->mq_magic = MQ_MAGIC_SEM;
}
struct mp_queue *alloc_mp_queue (void) {
struct mp_queue *MQ = NULL;
assert (!posix_memalign ((void **)&MQ, 64, sizeof (*MQ)));
memset (MQ, 0, sizeof (*MQ));
init_mp_queue (MQ);
return MQ;
}
struct mp_queue *alloc_mp_queue_w (void) {
struct mp_queue *MQ = NULL;
assert (!posix_memalign ((void **)&MQ, 64, sizeof (*MQ)));
memset (MQ, 0, sizeof (*MQ));
MODULE_STAT->mpq_allocated ++;
init_mp_queue_w (MQ);
return MQ;
}
/* invoke only if sure that nobody else may be using this mp_queue in parallel */
void clear_mp_queue (struct mp_queue *MQ) {
MODULE_STAT->mpq_active --;
assert (MQ->mq_magic == MQ_MAGIC || MQ->mq_magic == MQ_MAGIC_SEM);
assert (MQ->mq_head && MQ->mq_tail);
struct mp_queue_block *QB = MQ->mq_head, *QBN;
for (QB = MQ->mq_head; QB; QB = QBN) {
QBN = QB->mqb_next;
assert (QB->mqb_next || QB == MQ->mq_tail);
QB->mqb_next = 0;
free_mpq_block (QB);
}
MQ->mq_head = MQ->mq_tail = 0;
MQ->mq_magic = 0;
}
void free_mp_queue (struct mp_queue *MQ) {
MODULE_STAT->mpq_allocated --;
clear_mp_queue (MQ);
free (MQ);
}
// may invoke mpq_push() to discard new empty block
mqn_value_t mpq_pop (struct mp_queue *MQ, int flags) {
void **hptr = &mqb_hazard_ptr[get_this_thread_id()][0];
long r = ((flags & MPQF_RECURSIVE) != 0);
struct mp_queue_block *QB;
mqn_value_t v;
while (1) {
QB = MQ->mq_head;
barrier ();
hptr[r] = QB;
barrier ();
__sync_synchronize ();
if (MQ->mq_head != QB) {
continue;
}
v = mpq_block_pop (QB);
if (v) {
break;
}
barrier ();
if (!QB->mqb_next) {
QB = 0;
break;
}
v = mpq_block_pop (QB);
if (v) {
break;
}
if (__sync_bool_compare_and_swap (&MQ->mq_head, QB, QB->mqb_next)) {
// want to free QB here, but this is complicated if somebody else holds a pointer
if (is_hazard_ptr (QB, 0, 2) <= 1) {
free_mpq_block (QB);
} else {
__sync_fetch_and_add (&mpq_blocks_wasted, 1);
// ... put QB into some GC queue? ...
QB->mqb_magic = MQ_BLOCK_GARBAGE_MAGIC;
mpq_push (QB->mqb_size == MPQ_SMALL_BLOCK_SIZE ? &MqGarbageSmallBlocks : &MqGarbageBlocks, QB, flags & MPQF_RECURSIVE);
}
}
}
if (flags & MPQF_STORE_PTR) {
hptr[2] = QB;
}
hptr[r] = 0;
return v;
}
/* 1 = definitely empty (for some serialization), 0 = possibly non-empty;
may invoke mpq_push() to discard empty block */
int mpq_is_empty (struct mp_queue *MQ) {
void **hptr = &mqb_hazard_ptr[get_this_thread_id()][0];
struct mp_queue_block *QB;
while (1) {
QB = MQ->mq_head;
barrier ();
*hptr = QB;
barrier ();
__sync_synchronize ();
if (MQ->mq_head != QB) {
continue;
}
barrier();
long h = QB->mqb_head;
barrier();
long t = QB->mqb_tail;
barrier();
if (!(t & MQN_SAFE)) {
*hptr = 0;
return t <= h;
}
t &= MQN_IDX_MASK;
if (t > h) {
*hptr = 0;
return 0;
}
barrier ();
if (!QB->mqb_next) {
*hptr = 0;
return 1;
}
if (__sync_bool_compare_and_swap (&MQ->mq_head, QB, QB->mqb_next)) {
// want to free QB here, but this is complicated if somebody else holds a pointer
if (is_hazard_ptr (QB, 0, 2) <= 1) {
free_mpq_block (QB);
} else {
__sync_fetch_and_add (&mpq_blocks_wasted, 1);
// ... put QB into some GC queue? ...
QB->mqb_magic = MQ_BLOCK_GARBAGE_MAGIC;
mpq_push (QB->mqb_size == MPQ_SMALL_BLOCK_SIZE ? &MqGarbageSmallBlocks : &MqGarbageBlocks, QB, 0);
}
}
}
*hptr = 0;
return 0;
}
/* may invoke mpq_alloc_block (which recursively invokes mpq_pop)
or mpq_push() (without needing to hold hazard pointer) to deal with blocks */
long mpq_push (struct mp_queue *MQ, mqn_value_t val, int flags) {
void **hptr = mqb_hazard_ptr[get_this_thread_id()];
long r = ((flags & MPQF_RECURSIVE) != 0);
while (1) {
struct mp_queue_block *QB = MQ->mq_tail;
barrier ();
hptr[r] = QB;
barrier ();
__sync_synchronize ();
if (MQ->mq_tail != QB) {
continue;
}
if (QB->mqb_next) {
__sync_bool_compare_and_swap (&MQ->mq_tail, QB, QB->mqb_next);
continue;
}
long pos = mpq_block_push (QB, val);
if (pos >= 0) {
if (flags & MPQF_STORE_PTR) {
hptr[2] = QB;
}
hptr[r] = 0;
return pos;
}
#define DBG(c) // fprintf (stderr, "[%d] pushing %lx to %p,%p: %c\n", mpq_this_thread_id, (long) val, MQ, QB, c);
DBG('A');
/*
if (__sync_fetch_and_add (&QB->mqb_next_allocators, 1)) {
// somebody else will allocate next block; busy wait instead of spuruous alloc/free
DBG('B')
while (!QB->mqb_next) {
barrier ();
}
DBG('C')
continue;
}
*/
int is_small = (QB == MQ->mq_head);
struct mp_queue_block *NQB;
if (!r) {
assert (!hptr[1]);
NQB = alloc_mpq_block (val, 1, is_small);
assert (!hptr[1]);
} else {
NQB = alloc_mpq_block (val, 0, is_small);
}
assert (hptr[r] == QB);
DBG('D')
if (__sync_bool_compare_and_swap (&QB->mqb_next, 0, NQB)) {
__sync_bool_compare_and_swap (&MQ->mq_tail, QB, NQB);
DBG('E')
if (flags & MPQF_STORE_PTR) {
hptr[2] = NQB;
}
hptr[r] = 0;
return 0;
} else {
DBG('F');
NQB->mqb_magic = MQ_BLOCK_PREPARED_MAGIC;
mpq_push (is_small ? &MqPreparedSmallBlocks : &MqPreparedBlocks, NQB, 0);
__sync_fetch_and_add (&mpq_blocks_prepared, 1);
}
}
#undef DBG
}
mqn_value_t mpq_pop_w (struct mp_queue *MQ, int flags) {
assert (MQ->mq_magic == MQ_MAGIC_SEM);
int s = -1, iterations = flags & MPQF_MAX_ITERATIONS;
while (iterations --> 0) {
#if MPQ_USE_POSIX_SEMAPHORES
s = sem_trywait (&MQ->mq_sem);
#else
s = mp_sem_trywait (&MQ->mq_sem);
#endif
if (!s) {
break;
}
#if MPQ_USE_POSIX_SEMAPHORES
assert (errno == EAGAIN || errno == EINTR);
#endif
}
while (s < 0) {
#if MPQ_USE_POSIX_SEMAPHORES
s = sem_wait (&MQ->mq_sem);
#else
s = mp_sem_wait (&MQ->mq_sem);
#endif
if (!s) {
break;
}
#if MPQ_USE_POSIX_SEMAPHORES
assert (errno == EAGAIN);
#endif
}
mqn_value_t *v = mpq_pop (MQ, flags);
assert (v);
return v;
}
mqn_value_t mpq_pop_nw (struct mp_queue *MQ, int flags) {
assert (MQ->mq_magic == MQ_MAGIC_SEM);
int s = -1, iterations = flags & MPQF_MAX_ITERATIONS;
while (iterations --> 0) {
#if MPQ_USE_POSIX_SEMAPHORES
s = sem_trywait (&MQ->mq_sem);
#else
s = mp_sem_trywait (&MQ->mq_sem);
#endif
if (s >= 0) {
break;
}
#if MPQ_USE_POSIX_SEMAPHORES
assert (errno == EAGAIN || errno == EINTR);
#endif
}
if (s < 0) {
return 0;
}
mqn_value_t *v = mpq_pop (MQ, flags);
assert (v);
return v;
}
long mpq_push_w (struct mp_queue *MQ, mqn_value_t v, int flags) {
assert (MQ->mq_magic == MQ_MAGIC_SEM);
long res = mpq_push (MQ, v, flags);
#if MPQ_USE_POSIX_SEMAPHORES
assert (sem_post (&MQ->mq_sem) >= 0);
#else
assert (mp_sem_post (&MQ->mq_sem) >= 0);
#endif
return res;
}
void *get_ptr_multithread_copy (void **ptr, void (*incref)(void *ptr)) {
void **hptr = &mqb_hazard_ptr[get_this_thread_id()][COMMON_HAZARD_PTR_NUM];
assert (*hptr == NULL);
void *R;
while (1) {
R = *ptr;
barrier ();
*hptr = R;
barrier ();
mfence ();
if (R != *ptr) {
continue;
}
incref (R);
barrier ();
*hptr = NULL;
break;
}
return R;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2018 Telegram Messenger Inc
2014-2015 Andrey Lopatin
2014-2018 Nikolai Durov
*/
#pragma once
#define MPQ_USE_POSIX_SEMAPHORES 0
#if MPQ_USE_POSIX_SEMAPHORES
#include <semaphore.h>
#endif
typedef struct mp_semaphore {
volatile int value;
volatile int waiting;
} mp_sem_t;
#define THREAD_HPTRS 16
#define MPQ_SMALL_BLOCK_SIZE 64
#define MPQ_BLOCK_SIZE 4096 // must be a power of 2
#define MPQ_BLOCK_ALIGNMENT 64
#ifdef _LP64
typedef int int128_t __attribute__((__mode__(TI)));
# define DLONG int128_t
// # define DLONG __int128
# define MQN_SAFE (-1LL << 63)
#else
# define DLONG long long
# define MQN_SAFE (-1L << 31)
#endif
#define MQN_IDX_MASK (~MQN_SAFE)
typedef void *mqn_value_t;
typedef struct mp_queue_node {
union {
struct {
long idx;
union {
long mqn_value;
void *mqn_ptr;
mqn_value_t val;
};
};
DLONG pair;
};
} mpq_node_t;
#define MQ_BLOCK_USED_MAGIC 0x1ebacaef
#define MQ_BLOCK_FREE_MAGIC 0x2e4afeda
#define MQ_BLOCK_GARBAGE_MAGIC 0x3a04dc7d
#define MQ_BLOCK_PREPARED_MAGIC 0x4b9b13cd
#define MQ_MAGIC 0x1aed9b43
#define MQ_MAGIC_SEM 0x1aedcd21
struct mp_queue_block {
long mqb_head __attribute__ ((aligned(64)));
int mqb_magic;
int mqb_align_bytes;
int mqb_size; // power of 2; one of MPQ_BLOCK_SIZE or MPQ_SMALL_BLOCK_SIZE
long mqb_tail __attribute__ ((aligned(64)));
struct mp_queue_block *mqb_next;
int mqb_next_allocators;
mpq_node_t mqb_nodes[MPQ_BLOCK_SIZE] __attribute__ ((aligned(64)));
};
struct mp_queue {
struct mp_queue_block *mq_head __attribute__ ((aligned(64)));
int mq_magic;
struct mp_queue_block *mq_tail __attribute__ ((aligned(64)));
#if MPQ_USE_POSIX_SEMAPHORES
sem_t mq_sem __attribute__ ((aligned(64)));
#else
mp_sem_t mq_sem __attribute__ ((aligned(64)));
#endif
};
extern volatile int mpq_blocks_allocated, mpq_blocks_allocated_max, mpq_blocks_allocations, mpq_blocks_true_allocations, mpq_blocks_wasted, mpq_blocks_prepared;
extern volatile int mpq_small_blocks_allocated, mpq_small_blocks_allocated_max;
#define MAX_MPQ_THREADS 256
extern __thread int mpq_this_thread_id;
extern __thread void **thread_hazard_pointers;
extern volatile int mpq_threads;
/* initialize this thread id and return it */
int get_this_thread_id (void);
/* functions for one mp_queue_block */
struct mp_queue_block *alloc_mpq_block (mqn_value_t first_val, int allow_recursion, int is_small);
void free_mpq_block (struct mp_queue_block *QB);
mqn_value_t mpq_block_pop (struct mp_queue_block *QB);
long mpq_block_push (struct mp_queue_block *QB, mqn_value_t val);
/* functions for mp_queue = list of mp_queue_block's */
void init_mp_queue (struct mp_queue *MQ);
struct mp_queue *alloc_mp_queue (void);
struct mp_queue *alloc_mp_queue_w (void);
void init_mp_queue_w (struct mp_queue *MQ);
void clear_mp_queue (struct mp_queue *MQ); // frees all mpq block chain; invoke only if nobody else is using mp-queue
void free_mp_queue (struct mp_queue *MQ); // same + invoke free()
// flags for mpq_push / mpq_pop functions
#define MPQF_RECURSIVE 8192
#define MPQF_STORE_PTR 4096
#define MPQF_MAX_ITERATIONS (MPQF_STORE_PTR - 1)
long mpq_push (struct mp_queue *MQ, mqn_value_t val, int flags);
mqn_value_t mpq_pop (struct mp_queue *MQ, int flags);
int mpq_is_empty (struct mp_queue *MQ);
long mpq_push_w (struct mp_queue *MQ, mqn_value_t val, int flags);
mqn_value_t mpq_pop_w (struct mp_queue *MQ, int flags);
mqn_value_t mpq_pop_nw (struct mp_queue *MQ, int flags);
int mp_sem_post (mp_sem_t *sem);
int mp_sem_wait (mp_sem_t *sem);
int mp_sem_trywait (mp_sem_t *sem);
#define COMMON_HAZARD_PTR_NUM 3
int is_hazard_ptr (void *ptr, int a, int b);
extern void *mqb_hazard_ptr[MAX_MPQ_THREADS][THREAD_HPTRS];
void *get_ptr_multithread_copy (void **ptr, void (*incref)(void *ptr));
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "md5.h"
#include "common/parse-config.h"
#include "resolver.h"
#include "kprintf.h"
#define MAX_CONFIG_SIZE (16 << 20)
static char *config_buff;
char *config_name, *cfg_start, *cfg_end, *cfg_cur;
int config_bytes, cfg_lno, cfg_lex = -1;
int cfg_skipspc (void) {
while (*cfg_cur == ' ' || *cfg_cur == 9 || *cfg_cur == 13 || *cfg_cur == 10 || *cfg_cur == '#') {
if (*cfg_cur == '#') {
do cfg_cur++; while (*cfg_cur && *cfg_cur != 10);
continue;
}
if (*cfg_cur == 10) {
cfg_lno++;
}
cfg_cur++;
}
return (unsigned char) *cfg_cur;
}
int cfg_skspc (void) {
while (*cfg_cur == ' ' || *cfg_cur == 9) {
cfg_cur++;
}
return (unsigned char) *cfg_cur;
}
int cfg_getlex (void) {
switch (cfg_skipspc()) {
case ';':
case ':':
case '{':
case '}':
return cfg_lex = *cfg_cur++;
case 0:
return cfg_lex = 0;
}
return cfg_lex = -1;
}
int cfg_getword (void) {
cfg_skspc();
char *s = cfg_cur;
if (*s != '[') {
while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '.' || *s == '-' || *s == '_') {
s++;
}
} else {
s++;
while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '.' || *s == '-' || *s == '_' || *s == ':') {
s++;
}
if (*s == ']') {
s++;
}
}
return s - cfg_cur;
}
int cfg_getstr (void) {
cfg_skspc();
char *s = cfg_cur;
if (*s == '"') { return 1; } // fix later
while (*s > ' ' && *s != ';') {
s++;
}
return s - cfg_cur;
}
long long cfg_getint (void) {
cfg_skspc ();
char *s = cfg_cur;
long long x = 0;
while (*s >= '0' && *s <= '9') {
x = x * 10 + *(s ++) - '0';
}
cfg_cur = s;
return x;
}
long long cfg_getint_zero (void) {
cfg_skspc ();
char *s = cfg_cur;
long long x = 0;
while (*s >= '0' && *s <= '9') {
x = x * 10 + *(s ++) - '0';
}
if (cfg_cur == s) {
return -1;
} else {
cfg_cur = s;
return x;
}
}
long long cfg_getint_signed_zero (void) {
cfg_skspc ();
char *s = cfg_cur;
long long x = 0;
int sgn = 1;
if (*s == '-') {
sgn = -1;
++s;
}
while (*s >= '0' && *s <= '9') {
x = x * 10 + sgn * (*(s++) - '0');
}
if (s == cfg_cur + (sgn < 0)) {
return (-1LL << 63);
} else {
cfg_cur = s;
return x;
}
}
void syntax (const char *msg, ...) {
if (!msg) {
msg = "syntax error";
}
if (cfg_lno) {
fprintf (stderr, "%s:%d: ", config_name, cfg_lno);
}
fprintf (stderr, "fatal: ");
va_list args;
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
int len = 0;
while (cfg_cur[len] && cfg_cur[len] != 13 && cfg_cur[len] != 10 && len < 20) {
len++;
}
fprintf (stderr, " near %.*s%s\n", len, cfg_cur, len >= 20 ? " ..." : "");
}
void syntax_warning (const char *msg, ...) {
va_list args;
if (cfg_lno) {
fprintf (stderr, "%s:%d: ", config_name, cfg_lno);
}
fputs ("warning: ", stderr);
va_start (args, msg);
vfprintf (stderr, msg, args);
va_end (args);
int len = 0;
while (cfg_cur[len] && cfg_cur[len] != 13 && cfg_cur[len] != 10 && len < 20) {
len++;
}
fprintf (stderr, " near %.*s%s\n", len, cfg_cur, len >= 20 ? " ..." : "");
}
int expect_lexem (int lexem) {
if (cfg_lex != lexem) {
syntax ("%c expected", lexem);
return -1;
} else {
return 0;
}
}
int expect_word (const char *name, int len) {
int l = cfg_getword ();
if (len != l || memcmp (name, cfg_cur, len)) {
syntax ("Expected %.*s", len, name);
return -1;
}
cfg_cur += l;
return 0;
}
struct hostent *cfg_gethost_ex (int verb) {
struct hostent *h;
int l = cfg_getword ();
if (!l || l > 63) {
syntax ("hostname expected");
return 0;
}
char c = cfg_cur[l];
//hostname = cfg_cur;
cfg_cur[l] = 0;
if (!(h = kdb_gethostbyname (cfg_cur)) || !h->h_addr_list || !h->h_addr) {
if (verbosity >= verb) {
syntax ("cannot resolve '%s'\n", cfg_cur);
}
*(cfg_cur += l) = c;
return 0;
}
*(cfg_cur += l) = c;
return h;
}
struct hostent *cfg_gethost (void) {
return cfg_gethost_ex (0);
}
void reset_config (void) {
assert (config_buff);
cfg_cur = cfg_start = config_buff;
cfg_end = cfg_start + config_bytes;
*cfg_end = 0;
cfg_lno = 0;
}
int load_config (const char *file, int fd) {
if (!config_buff) {
config_buff = malloc (MAX_CONFIG_SIZE+4);
assert (config_buff);
}
if (fd < 0) {
fd = open (file, O_RDONLY);
if (fd < 0) {
fprintf (stderr, "Can not open file %s: %m\n", file);
return -1;
}
}
int r;
config_bytes = r = read (fd, config_buff, MAX_CONFIG_SIZE + 1);
if (r < 0) {
fprintf (stderr, "error reading configuration file %s: %m\n", config_name);
return -2;
}
if (r > MAX_CONFIG_SIZE) {
fprintf (stderr, "configuration file %s too long (max %d bytes)\n", config_name, MAX_CONFIG_SIZE);
return -2;
}
if (config_name) {
free (config_name);
}
if (file) {
config_name = strdup (file);
}
reset_config ();
return fd;
}
void md5_hex_config (char *out) {
assert (config_buff);
md5_hex (config_buff, config_bytes, out);
}
void close_config (int *fd) {
if (config_buff) {
free (config_buff);
config_buff = NULL;
}
if (config_name) {
free (config_name);
config_name = NULL;
}
config_bytes = 0;
cfg_cur = cfg_start = cfg_end = NULL;
if (fd) {
if (*fd >= 0) {
assert (!close (*fd));
*fd = -1;
}
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#pragma once
extern char *config_file, *cfg_start, *cfg_end, *cfg_cur;
extern int config_bytes, cfg_lno, cfg_lex;
int cfg_skipspc (void);
int cfg_skspc (void);
int cfg_getlex (void);
int cfg_getword (void);
int cfg_getstr (void);
void syntax (const char *msg, ...);
void syntax_warning (const char *msg, ...);
int expect_lexem (int lexem);
int expect_word (const char *name, int len);
void reset_config (void);
int load_config (const char *file, int fd);
void close_config (int *fd);
void md5_hex_config (char *out);
struct hostent *cfg_gethost (void);
struct hostent *cfg_gethost_ex (int verb);
long long cfg_getint (void);
long long cfg_getint_zero (void);
long long cfg_getint_signed_zero (void);
#define Expect(l) { int t = expect_lexem (l); if (t < 0) { return t; } }
#define ExpectWord(s) { int t = expect_word (s, strlen (s)); if (t < 0) { return t; } }
#define Syntax(...) { syntax (__VA_ARGS__); return -1; }
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2011-2013 Vkontakte Ltd
2011-2013 Nikolai Durov
2011-2013 Andrey Lopatin
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include "common/pid.h"
npid_t PID;
void init_common_PID (void) {
if (!PID.pid) {
int p = getpid ();
assert (!(p & 0xffff0000));
PID.pid = p;
}
if (!PID.utime) {
PID.utime = time (0);
}
}
void init_client_PID (unsigned ip) {
if (ip && ip != 0x7f000001) {
PID.ip = ip;
}
// PID.port = 0;
init_common_PID ();
};
void init_server_PID (unsigned ip, int port) {
if (ip && ip != 0x7f000001) {
PID.ip = ip;
}
if (!PID.port) {
PID.port = port;
}
init_common_PID ();
};
/* returns 1 if X is a special case of Y, 2 if they match completely */
int matches_pid (npid_t *X, npid_t *Y) {
if (!memcmp (X, Y, sizeof (struct process_id))) {
return 2;
} else if ((!Y->ip || X->ip == Y->ip) && (!Y->port || X->port == Y->port) && (!Y->pid || X->pid == Y->pid) && (!Y->utime || X->utime == Y->utime)) {
return 1;
} else {
return 0;
}
}
int process_id_is_newer (struct process_id *a, struct process_id *b) {
assert (!memcmp (a, b, 6));
if (a->utime < b->utime) { return 0; }
if (a->utime > b->utime) { return 1; }
int x = (a->pid - b->pid) & 0x7fff;
if (x && x <= 0x3fff) { return 1; }
return 0;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2011-2013 Vkontakte Ltd
2011-2013 Nikolai Durov
2011-2013 Andrey Lopatin
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#pragma once
#pragma pack(push,4)
struct process_id {
unsigned ip;
short port;
unsigned short pid;
int utime;
};
struct process_id_ext {
unsigned ip;
short port;
unsigned short pid;
int utime;
int actor_id;
};
#pragma pack(pop)
typedef struct process_id npid_t;
typedef struct process_id_ext npidx_t;
extern npid_t PID;
void init_common_PID (void);
void init_client_PID (unsigned ip);
void init_server_PID (unsigned ip, int port);
/* returns 1 if X is a special case of Y, 2 if they match completely */
int matches_pid (struct process_id *X, struct process_id *Y);
int process_id_is_newer (struct process_id *a, struct process_id *b);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#include <assert.h>
#include <sys/time.h>
#include <time.h>
/* unistd.h defines _POSIX_TIMERS */
#include <unistd.h>
#include "precise-time.h"
__thread int now;
__thread double precise_now;
__thread long long precise_now_rdtsc;
long long precise_time;
long long precise_time_rdtsc;
double get_utime_monotonic (void) __attribute__ ((weak));
double get_utime_monotonic (void) {
struct timespec T;
#if _POSIX_TIMERS
assert (clock_gettime (CLOCK_MONOTONIC, &T) >= 0);
precise_now_rdtsc = rdtsc ();
return precise_now = T.tv_sec + (double) T.tv_nsec * 1e-9;
#else
#error "No high-precision clock"
return precise_now = time ();
#endif
}
double get_double_time (void) {
static double last_double_time = -1;
static long long next_rdtsc;
long long cur_rdtsc = rdtsc ();
if (cur_rdtsc > next_rdtsc) {
struct timeval tv;
gettimeofday (&tv, NULL);
next_rdtsc = cur_rdtsc + 1000000;
return (last_double_time = tv.tv_sec + 1e-6 * tv.tv_usec);
} else {
return last_double_time;
}
}
double get_utime (int clock_id) {
struct timespec T;
#if _POSIX_TIMERS
assert (clock_gettime (clock_id, &T) >= 0);
double res = T.tv_sec + (double) T.tv_nsec * 1e-9;
#else
#error "No high-precision clock"
double res = time ();
#endif
if (clock_id == CLOCK_REALTIME) {
precise_time = (long long) (res * (1LL << 32));
precise_time_rdtsc = rdtsc ();
}
return res;
}
long long get_precise_time (unsigned precision) {
unsigned long long diff = rdtsc() - precise_time_rdtsc;
if (diff > precision) {
get_utime (CLOCK_REALTIME);
}
return precise_time;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Anton Maydell
*/
#pragma once
#include <time.h>
/* RDTSC */
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void) {
unsigned long long int x;
__asm__ volatile ("rdtsc" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void) {
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif
/* net-event.h */
extern __thread int now;
extern __thread double precise_now;
extern __thread long long precise_now_rdtsc;
double get_utime_monotonic (void);
/* common/server-functions.h */
double get_utime (int clock_id);
extern long long precise_time; // (long long) (2^16 * precise unixtime)
extern long long precise_time_rdtsc; // when precise_time was obtained
long long get_precise_time (unsigned precision);
/* ??? */
double get_double_time (void);
static inline void precise_sleep (int seconds, int nanoseconds) {
struct timespec t;
t.tv_sec = seconds;
t.tv_nsec = nanoseconds;
nanosleep (&t, NULL);
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#include "common/proc-stat.h"
#include <stdio.h>
int read_proc_stats (int pid, int tid, struct proc_stats *s) {
const char *format = "%d %s %c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu";
char buf[256];
if (tid <= 0) {
sprintf (buf, "/proc/%d/stat", pid);
} else {
sprintf (buf, "/proc/%d/task/%d/stat", pid, tid);
}
FILE *proc = fopen (buf, "r");
if (proc) {
if (42 == fscanf (proc, format,
&s->pid,
s->comm,
&s->state,
&s->ppid,
&s->pgrp,
&s->session,
&s->tty_nr,
&s->tpgid,
&s->flags,
&s->minflt,
&s->cminflt,
&s->majflt,
&s->cmajflt,
&s->utime,
&s->stime,
&s->cutime,
&s->cstime,
&s->priority,
&s->nice,
&s->num_threads,
&s->itrealvalue,
&s->starttime,
&s->vsize,
&s->rss,
&s->rlim,
&s->startcode,
&s->endcode,
&s->startstack,
&s->kstkesp,
&s->kstkeip,
&s->signal,
&s->blocked,
&s->sigignore,
&s->sigcatch,
&s->wchan,
&s->nswap,
&s->cnswap,
&s->exit_signal,
&s->processor,
&s->rt_priority,
&s->policy,
&s->delayacct_blkio_ticks
)
) {
fclose(proc);
return 1;
} else {
fclose(proc);
return 0;
}
} else {
return 0;
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#pragma once
struct proc_stats {
int pid; // %d
char comm[256]; // %s
char state; // %c
int ppid; // %d
int pgrp; // %d
int session; // %d
int tty_nr; // %d
int tpgid; // %d
unsigned long flags; // %lu
unsigned long minflt; // %lu
unsigned long cminflt; // %lu
unsigned long majflt; // %lu
unsigned long cmajflt; // %lu
unsigned long utime; // %lu
unsigned long stime; // %lu
long cutime; // %ld
long cstime; // %ld
long priority; // %ld
long nice; // %ld
long num_threads; // %ld
long itrealvalue; // %ld
unsigned long starttime; // %lu
unsigned long vsize; // %lu
long rss; // %ld
unsigned long rlim; // %lu
unsigned long startcode; // %lu
unsigned long endcode; // %lu
unsigned long startstack; // %lu
unsigned long kstkesp; // %lu
unsigned long kstkeip; // %lu
unsigned long signal; // %lu
unsigned long blocked; // %lu
unsigned long sigignore; // %lu
unsigned long sigcatch; // %lu
unsigned long wchan; // %lu
unsigned long nswap; // %lu
unsigned long cnswap; // %lu
int exit_signal; // %d
int processor; // %d
unsigned long rt_priority; // %lu
unsigned long policy; // %lu
unsigned long long delayacct_blkio_ticks; // %llu
};
int read_proc_stats (int pid, int tid, struct proc_stats *s);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <netdb.h>
#include "kprintf.h"
#include "resolver.h"
#define HOSTS_FILE "/etc/hosts"
#define MAX_HOSTS_SIZE (1L << 24)
int kdb_hosts_loaded;
static int pr[] = {
29,
41,
59,
89,
131,
197,
293,
439,
659,
991,
1481,
2221,
3329,
4993,
7487,
11239,
16843,
25253,
37879,
56821,
85223,
127837,
191773,
287629,
431441,
647161,
970747,
1456121,
2184179,
3276253,
4914373,
7371571,
11057357,
16586039,
24879017,
37318507
};
#pragma pack(push,1)
struct host {
unsigned ip;
char len;
char name[];
};
#pragma pack(pop)
static unsigned ipaddr;
static char *h_array[] = {(char *)&ipaddr, 0};
static struct hostent hret = {
.h_aliases = 0,
.h_addrtype = AF_INET,
.h_length = 4,
.h_addr_list = h_array
};
/*
static unsigned char ipv6_addr[16];
static char *h_array6[] = {(char *)ipv6_addr, 0};
static struct hostent hret6 = {
.h_aliases = 0,
.h_addrtype = AF_INET6,
.h_length = 16,
.h_addr_list = h_array6
};
*/
static struct resolver_conf {
int hosts_loaded;
int hsize;
struct host **htable;
long long fsize;
int ftime;
} Hosts, Hosts_new;
static struct host *getHash (struct resolver_conf *R, const char *name, int len, unsigned ip) {
int h1 = 0, h2 = 0, i;
assert ((unsigned)len < 128);
for (i = 0; i < len; i++) {
h1 = (h1 * 17 + name[i]) % R->hsize;
h2 = (h2 * 239 + name[i]) % (R->hsize - 1);
}
++h2;
while (R->htable[h1]) {
if (len == R->htable[h1]->len && !memcmp (R->htable[h1]->name, name, len)) {
return R->htable[h1];
}
h1 += h2;
if (h1 >= R->hsize) {
h1 -= R->hsize;
}
}
if (!ip) {
return 0;
}
struct host *tmp = malloc (len + sizeof (struct host));
assert (tmp);
tmp->ip = ip;
tmp->len = len;
memcpy (tmp->name, name, len);
return R->htable[h1] = tmp;
}
static void free_resolver_data (struct resolver_conf *R) {
int s = R->hsize, i;
struct host **htable = R->htable;
if (htable) {
assert (s > 0);
for (i = 0; i < s; i++) {
struct host *tmp = htable[i];
if (tmp) {
free (tmp);
htable[i] = 0;
}
}
free (htable);
R->htable = 0;
R->hsize = 0;
}
R->hosts_loaded = 0;
}
static char *skipspc (char *ptr) {
while (*ptr == ' ' || *ptr == '\t') {
++ptr;
}
return ptr;
}
static char *skiptoeoln (char *ptr) {
while (*ptr && *ptr != '\n') {
++ptr;
}
if (*ptr) {
++ptr;
}
return ptr;
}
static char *getword (char **ptr, int *len) {
char *start = skipspc (*ptr), *tmp = start;
while (*tmp && *tmp != ' ' && *tmp != '\t' && *tmp != '\n') {
++tmp;
}
*ptr = tmp;
*len = tmp - start;
if (!*len) {
return 0;
}
return start;
}
static int readbyte (char **ptr) {
char *tmp;
unsigned val = strtoul (*ptr, &tmp, 10);
if (tmp == *ptr || val > 255) {
return -1;
}
*ptr = tmp;
return val;
}
static int parse_hosts (struct resolver_conf *R, char *data, int mode) {
char *ptr;
int ans = 0;
for (ptr = data; *ptr; ptr = skiptoeoln (ptr)) {
ptr = skipspc (ptr);
int i;
unsigned ip = 0;
for (i = 0; i < 4; i++) {
int res = readbyte (&ptr);
if (res < 0) {
break;
}
ip = (ip << 8) | res;
if (i < 3 && *ptr++ != '.') {
break;
}
}
//fprintf (stderr, "ip = %08x, i = %d\n", ip, i);
if (i < 4 || (*ptr != ' ' && *ptr != '\t') || !ip) {
continue;
}
char *word;
int wordlen;
do {
word = getword (&ptr, &wordlen);
if (word && wordlen < 128) {
//fprintf (stderr, "word = %.*s\n", wordlen, word);
if (mode) {
getHash (R, word, wordlen, ip);
}
++ans;
}
} while (word);
}
return ans;
}
static int kdb_load_hosts_internal (void) {
static struct stat s;
long long r;
int fd;
char *data;
if (stat (HOSTS_FILE, &s) < 0) {
return Hosts_new.hosts_loaded = -1;
}
if (!S_ISREG (s.st_mode)) {
return Hosts_new.hosts_loaded = -1;
}
if (Hosts.hosts_loaded > 0 && Hosts.fsize == s.st_size && Hosts.ftime == s.st_mtime) {
return 0;
}
if (s.st_size >= MAX_HOSTS_SIZE) {
return Hosts_new.hosts_loaded = -1;
}
fd = open (HOSTS_FILE, O_RDONLY);
if (fd < 0) {
return Hosts_new.hosts_loaded = -1;
}
Hosts_new.fsize = s.st_size;
Hosts_new.ftime = s.st_mtime;
data = malloc (s.st_size + 1);
if (!data) {
close (fd);
return Hosts_new.hosts_loaded = -1;
}
r = read (fd, data, s.st_size + 1);
if (verbosity > 1) {
fprintf (stderr, "read %lld of %lld bytes of "HOSTS_FILE"\n", r, Hosts_new.fsize);
}
close (fd);
if (r != s.st_size) {
free (data);
return Hosts_new.hosts_loaded = -1;
}
data[s.st_size] = 0;
int ans = parse_hosts (&Hosts_new, data, 0), i;
for (i = 0; i < sizeof (pr) / sizeof (int); i++) {
if (pr[i] > ans * 2) {
break;
}
}
if (i >= sizeof (pr) / sizeof (int)) {
free (data);
return Hosts_new.hosts_loaded = -1;
}
Hosts_new.hsize = pr[i];
if (verbosity > 1) {
fprintf (stderr, "IP table hash size: %d (for %d entries)\n", Hosts_new.hsize, ans);
}
Hosts_new.htable = malloc (sizeof (void *) * Hosts_new.hsize);
assert (Hosts_new.htable);
memset (Hosts_new.htable, 0, sizeof (void *) * Hosts_new.hsize);
int res = parse_hosts (&Hosts_new, data, 1);
assert (res == ans);
free (data);
return Hosts_new.hosts_loaded = 1;
}
int kdb_load_hosts (void) {
int res = kdb_load_hosts_internal ();
if (res < 0) {
if (kdb_hosts_loaded <= 0) {
kdb_hosts_loaded = res;
}
return kdb_hosts_loaded < 0 ? -1 : 0;
}
if (!res) {
assert (kdb_hosts_loaded > 0);
return 0;
}
assert (Hosts_new.hosts_loaded > 0);
if (kdb_hosts_loaded > 0) {
assert (Hosts.hosts_loaded > 0);
free_resolver_data (&Hosts);
}
memcpy (&Hosts, &Hosts_new, sizeof (Hosts));
memset (&Hosts_new, 0, sizeof (Hosts));
kdb_hosts_loaded = Hosts.hosts_loaded;
return 1;
}
int parse_ipv6 (unsigned short ipv6[8], char *str) {
return -1;
}
struct hostent *kdb_gethostbyname (const char *name) {
if (!kdb_hosts_loaded) {
kdb_load_hosts ();
}
int len = strlen (name);
if (name[0] == '[' && name[len-1] == ']' && len <= 64) {
/*
if (parse_ipv6 ((unsigned short *) ipv6_addr, name + 1) == len - 2) {
hret6.h_name = (char *)name;
return &hret6;
}
*/
char buf[64];
memcpy (buf, name + 1, len - 2);
buf[len - 2] = 0;
return gethostbyname2 (buf, AF_INET6);
}
if (kdb_hosts_loaded <= 0) {
return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6);
}
if (len >= 128) {
return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6);
}
struct host *res = getHash (&Hosts, name, len, 0);
if (!res) {
if (strchr (name, '.') || strchr (name, ':')) {
return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6);
} else {
return 0;
}
}
hret.h_name = (char *)name;
ipaddr = htonl (res->ip);
return &hret;
}
char *detect_hostname (void) {
static char *hostname = NULL;
static char hostname_buffer[256];
int r, i;
if (!hostname || !*hostname) {
hostname = getenv ("HOSTNAME");
if (!hostname || !*hostname) {
int fd = open ("/etc/hostname", O_RDONLY);
if (fd < 0) {
kprintf ("cannot read /etc/hostname: %m\n");
exit (2);
}
r = read (fd, hostname_buffer, 256);
if (r <= 0 || r >= 256) {
kprintf ("cannot read hostname from /etc/hostname: %d bytes read\n", r);
exit (2);
}
hostname_buffer[r] = 0;
close (fd);
hostname = hostname_buffer;
while (*hostname == 9 || *hostname == 32) {
hostname++;
}
i = 0;
while (hostname[i] > 32) {
i++;
}
hostname[i] = 0;
}
}
if (!hostname || !*hostname) {
kprintf ("fatal: cannot detect hostname\n");
exit (2);
}
i = 0;
while ((hostname[i] >= '0' && hostname[i] <= '9') || hostname[i] == '.' || hostname[i] == '-' || hostname[i] == '_' || (hostname[i] >= 'A' && hostname[i] <= 'Z') || (hostname[i] >= 'a' && hostname[i] <= 'z')) {
i++;
}
if (hostname[i] || i >= 64) {
kprintf ("fatal: bad hostname '%s'\n", hostname);
exit (2);
}
vkprintf (1, "hostname is %s\n", hostname);
return hostname;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
*/
#pragma once
#include <netdb.h>
#ifdef __cplusplus
extern "C" {
#endif
int kdb_hosts_loaded;
int kdb_load_hosts (void);
struct hostent *kdb_gethostbyname (const char *name);
char *detect_hostname (void);
#ifdef __cplusplus
}
#endif
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Nikolai Durov
2012-2013 Andrey Lopatin
2012-2013 Vitaliy Valtman
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#pragma once
#define TL_STAT 0x9d56e6b2
#define RPC_INVOKE_REQ 0x2374df3d
#define RPC_INVOKE_KPHP_REQ 0x99a37fda
#define RPC_REQ_RUNNING 0x346d5efa
#define RPC_REQ_ERROR 0x7ae432f5
#define RPC_REQ_RESULT 0x63aeda4e
#define RPC_READY 0x6a34cac7
#define RPC_STOP_READY 0x59d86654
#define RPC_SEND_SESSION_MSG 0x1ed5a3cc
#define RPC_RESPONSE_INDIRECT 0x2194f56e
#define RPC_PING 0x5730a2df
#define RPC_PONG 0x8430eaa7
#define RPC_DEST_ACTOR 0x7568aabd
#define RPC_DEST_ACTOR_FLAGS 0xf0a5acf7
#define RPC_DEST_FLAGS 0xe352035e
#define RPC_REQ_RESULT_FLAGS 0x8cc84ce1
#define MAX_TL_STRING_LENGTH 0xffffff
#define TL_ERROR_RETRY 503
#define TL_BOOL_TRUE 0x997275b5
#define TL_BOOL_FALSE 0xbc799737
#define TL_BOOL_STAT 0x92cbcbfa
#define TL_TRUE 0x3fedd339
#define TL_INT 0xa8509bda
#define TL_LONG 0x22076cba
#define TL_DOUBLE 0x2210c154
#define TL_STRING 0xb5286e24
#define TL_MAYBE_TRUE 0x3f9c8ef8
#define TL_MAYBE_FALSE 0x27930a7b
#define TL_VECTOR 0x1cb5c415
#define TL_VECTOR_TOTAL 0x10133f47
#define TL_TUPLE 0x9770768a
#define TL_DICTIONARY 0x1f4c618f
//
// Error codes
//
//
// Query syntax errors -1000...-1999
//
#define TL_ERROR_SYNTAX -1000
#define TL_ERROR_EXTRA_DATA -1001
#define TL_ERROR_HEADER -1002
#define TL_ERROR_WRONG_QUERY_ID -1003
#define TL_ERROR_NOT_ENOUGH_DATA -1004
//
// Syntax ok, bad can not start query. -2000...-2999
//
#define TL_ERROR_UNKNOWN_FUNCTION_ID -2000
#define TL_ERROR_PROXY_NO_TARGET -2001
#define TL_ERROR_WRONG_ACTOR_ID -2002
#define TL_ERROR_TOO_LONG_STRING -2003
#define TL_ERROR_VALUE_NOT_IN_RANGE -2004
#define TL_ERROR_QUERY_INCORRECT -2005
#define TL_ERROR_BAD_VALUE -2006
#define TL_ERROR_BINLOG_DISABLED -2007
#define TL_ERROR_FEATURE_DISABLED -2008
#define TL_ERROR_QUERY_IS_EMPTY -2009
#define TL_ERROR_INVALID_CONNECTION_ID -2010
#define TL_ERROR_WRONG_SPLIT -2011
#define TL_ERROR_TOO_BIG_OFFSET -2012
//
// Error processing query -3000...-3999
//
#define TL_ERROR_QUERY_TIMEOUT -3000
#define TL_ERROR_PROXY_INVALID_RESPONSE -3001
#define TL_ERROR_NO_CONNECTIONS -3002
#define TL_ERROR_INTERNAL -3003
#define TL_ERROR_AIO_FAIL -3004
#define TL_ERROR_AIO_TIMEOUT -3005
#define TL_ERROR_BINLOG_WAIT_TIMEOUT -3006
#define TL_ERROR_AIO_MAX_RETRY_EXCEEDED -3007
#define TL_ERROR_TTL -3008
#define TL_ERROR_BAD_METAFILE -3009
#define TL_ERROR_NOT_READY -3010
#define TL_ERROR_STORAGE_CACHE_MISS -3500
#define TL_ERROR_STORAGE_CACHE_NO_MTPROTO_CONN -3501
//
// Different errors -4000...-4999
//
#define TL_ERROR_UNKNOWN -4000
#define TL_IS_USER_ERROR(x) ((x) <= -1000 && (x) > -3000)
#define TL_NAMESPACE TL_
#define CONCAT(a,b) a ## b
#define TLN(nspc,name) CONCAT (nspc, name)
#define TLG(name) TL_ ## name
#define TL(x) TLN (TL_NAMESPACE, x)
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
2011-2013 Oleg Davydov
2012-2013 Arseny Smirnov
2012-2013 Aliaksei Levin
2012-2013 Anton Maydell
2013 Vitaliy Valtman
Copyright 2014-2018 Telegram Messenger Inc
2014-2018 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE 1
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <netinet/in.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include "common/kprintf.h"
#include "net/net-connections.h"
#include "net/net-events.h"
#include "net/net-msg-buffers.h"
#include "server-functions.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
long long max_allocated_buffer_bytes __attribute__ ((weak));
int engine_options_num;
char *engine_options[MAX_ENGINE_OPTIONS];
int start_time;
int daemonize = 0;
const char *username, *progname, *groupname;
int change_user_group (const char *username, const char *groupname) {
struct passwd *pw;
/* lose root privileges if we have them */
if (getuid() == 0 || geteuid() == 0) {
if (username == 0 || *username == '\0') {
username = DEFAULT_ENGINE_USER;
}
if ((pw = getpwnam (username)) == 0) {
kprintf ("change_user_group: can't find the user %s to switch to\n", username);
return -1;
}
gid_t gid = pw->pw_gid;
if (setgroups (1, &gid) < 0) {
kprintf ("change_user_group: failed to clear supplementary groups list: %m\n");
return -1;
}
if (groupname) {
struct group *g = getgrnam (groupname);
if (g == NULL) {
kprintf ("change_user_group: can't find the group %s to switch to\n", groupname);
return -1;
}
gid = g->gr_gid;
}
if (setgid (gid) < 0) {
kprintf ("change_user_group: setgid (%d) failed. %m\n", (int) gid);
return -1;
}
if (setuid (pw->pw_uid) < 0) {
kprintf ("change_user_group: failed to assume identity of user %s\n", username);
return -1;
}
}
return 0;
}
int change_user (const char *username) {
struct passwd *pw;
/* lose root privileges if we have them */
if (getuid() == 0 || geteuid() == 0) {
if (username == 0 || *username == '\0') {
username = DEFAULT_ENGINE_USER;
// fprintf (stderr, "can't run as root without the -u switch\n");
// return -1;
}
if ((pw = getpwnam (username)) == 0) {
kprintf ("can't find the user %s to switch to\n", username);
return -1;
}
gid_t gid = pw->pw_gid;
if (setgroups(1, &gid) < 0) {
kprintf ("failed to clear supplementary groups list: %m\n");
return -1;
}
if (initgroups(username, gid) != 0) {
kprintf ("failed to load groups of user %s: %m\n", username);
return -1;
}
if (setgid (pw->pw_gid) < 0 || setuid (pw->pw_uid) < 0) {
kprintf ("failed to assume identity of user %s\n", username);
return -1;
}
}
return 0;
}
int raise_file_rlimit (int maxfiles) {
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
kprintf ("failed to getrlimit number of files\n");
return -1;
} else {
if (rlim.rlim_cur < maxfiles)
rlim.rlim_cur = maxfiles + 3;
if (rlim.rlim_max < rlim.rlim_cur)
rlim.rlim_max = rlim.rlim_cur;
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
kprintf ("failed to set rlimit for open files. Try running as root or requesting smaller maxconns value.\n");
return -1;
}
}
return 0;
}
const char *get_version_string (void) __attribute__ ((weak));
const char *get_version_string (void) {
return "unknown compiled at " __DATE__ " " __TIME__ " by gcc " __VERSION__;
}
void print_backtrace (void) {
void *buffer[64];
int nptrs = backtrace (buffer, 64);
kwrite (2, "\n------- Stack Backtrace -------\n", 33);
backtrace_symbols_fd (buffer, nptrs, 2);
kwrite (2, "-------------------------------\n", 32);
const char *s = get_version_string ();
if (s) {
kwrite (2, s, strlen (s));
kwrite (2, "\n", 1);
}
}
pthread_t debug_main_pthread_id;
void kill_main (void) {
if (debug_main_pthread_id && debug_main_pthread_id != pthread_self ()) {
pthread_kill (debug_main_pthread_id, SIGABRT);
}
}
//can be called inside signal handler
void ksignal (int sig, void (*handler) (int)) {
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_ONSTACK | SA_RESTART;
act.sa_handler = handler;
if (sigaction (sig, &act, NULL) != 0) {
kwrite (2, "failed sigaction\n", 17);
//_exit (EXIT_FAILURE);
}
}
void ksignal_ex (int sig, void (*handler) (int, siginfo_t *, void *)) {
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
act.sa_sigaction = handler;
if (sigaction (sig, &act, NULL) != 0) {
kwrite (2, "failed sigaction\n", 17);
_exit (EXIT_FAILURE);
}
}
void queries_log_store (void *N, int limit, int max_size, int max_entry_size, int plain) __attribute__ ((weak));
void queries_log_store (void *N, int limit, int max_size, int max_entry_size, int plain) {}
void engine_set_terminal_attributes (void) __attribute__ ((weak));
void engine_set_terminal_attributes (void) {}
void extended_debug_handler (int sig, siginfo_t *info, void *cont) {
ksignal (sig, SIG_DFL);
print_backtrace ();
kill_main ();
_exit (EXIT_FAILURE);
}
void set_debug_handlers (void) {
ksignal_ex (SIGSEGV, extended_debug_handler);
ksignal_ex (SIGABRT, extended_debug_handler);
ksignal_ex (SIGFPE, extended_debug_handler);
ksignal_ex (SIGBUS, extended_debug_handler);
debug_main_pthread_id = pthread_self ();
}
void usage (void) __attribute ((weak));
void usage (void) {
printf ("usage: %s <args>\n",
progname ? progname : "SOMETHING");
exit (2);
}
long long parse_memory_limit (const char *s) {
long long x;
char c = 0;
if (sscanf (s, "%lld%c", &x, &c) < 1) {
kprintf ("Parsing limit for option fail: %s\n", s);
usage ();
exit (1);
}
switch (c | 0x20) {
case ' ': break;
case 'k': x <<= 10; break;
case 'm': x <<= 20; break;
case 'g': x <<= 30; break;
case 't': x <<= 40; break;
default:
kprintf ("Parsing limit fail. Unknown suffix '%c'.\n", c);
usage ();
exit (1);
}
return x;
}
struct engine_parse_option *engine_parse_options;
int engine_parse_options_size;
int engine_parse_options_num;
int find_parse_option (int val) {
int i;
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
int j;
for (j = 0; j < P->val_cnt; j++) {
if (P->vals[j] == val) {
return i;
}
}
}
return -1;
}
int find_parse_option_name (const char *name) {
int i;
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
int j;
for (j = 0; j < P->longopts_cnt; j++) {
if (!strcmp (P->longopts[j], name)) {
return i;
}
}
}
return -1;
}
int default_parse_option_func (int a) __attribute__ ((weak));
int default_parse_option_func (int a) { return -1; }
void parse_option_up (struct engine_parse_option *P) {
struct engine_parse_option *Q = P - 1;
while (Q >= engine_parse_options && Q->smallest_val > P->smallest_val) {
Q --;
}
Q ++;
if (Q != P) {
struct engine_parse_option T;
T = *P;
memmove (Q + 1, Q, (P - Q) * sizeof (struct engine_parse_option));
*Q = T;
}
}
void parse_option_down (struct engine_parse_option *P) {
struct engine_parse_option *Q = P + 1;
while (Q < engine_parse_options + engine_parse_options_num && Q->smallest_val < P->smallest_val) {
Q ++;
}
Q --;
if (Q != P) {
struct engine_parse_option T;
T = *Q;
memmove (P + 1, P, (P - Q) * sizeof (struct engine_parse_option));
*P = T;
}
}
void parse_option_internal (const char *name, int arg, int *var, int val, unsigned flags, int (*func)(int), char *help) {
int p = find_parse_option (val);
if (p >= 0) {
kprintf ("duplicate parse option %d\n", val);
usage ();
}
assert (engine_parse_options_num <= engine_parse_options_size);
if (engine_parse_options_num == engine_parse_options_size) {
engine_parse_options_size = 10 + 2 * engine_parse_options_size;
engine_parse_options = realloc (engine_parse_options, sizeof (struct engine_parse_option) * engine_parse_options_size);
}
assert (engine_parse_options_num < engine_parse_options_size);
struct engine_parse_option *P = &engine_parse_options[engine_parse_options_num ++];
P->arg = arg;
P->flags = flags;
P->func = func ? func : default_parse_option_func;
P->help = help;
P->longopts = malloc (sizeof (void *));
P->longopts[0] = name;
P->longopts_cnt = 1;
P->vals = malloc (sizeof (int));
P->vals[0] = val;
P->val_cnt = 1;
P->smallest_val = val;
P->base_val = val;
parse_option_up (P);
}
void parse_option_ex (const char *name, int arg, int *var, int val, unsigned flags, int (*func)(int), const char *help, ...) {
char *h;
va_list ap;
va_start (ap, help);
assert (vasprintf (&h, help, ap) >= 0);
va_end (ap);
parse_option_internal (name, arg, var, val, flags, func, h);
}
void parse_option (const char *name, int arg, int *var, int val, const char *help, ...) {
char *h;
va_list ap;
va_start (ap, help);
assert (vasprintf (&h, help, ap) >= 0);
va_end (ap);
parse_option_internal (name, arg, var, val, LONGOPT_CUSTOM_SET, NULL, h);
}
int builtin_parse_option (int val);
void parse_option_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) {
parse_option_internal (name, arg, var, val, flags, builtin_parse_option, help ? strdup (help) : NULL);
}
void remove_parse_option_completely (int val) {
int t = find_parse_option (val);
assert (t >= 0);
struct engine_parse_option *P = &engine_parse_options[t];
assert (P->vals[0] == val);
if (P->help) {
free (P->help);
}
free (P->vals);
free (P->longopts);
memmove (engine_parse_options + t, engine_parse_options + t + 1, (engine_parse_options_num - t - 1) * sizeof (struct engine_parse_option));
engine_parse_options_num --;
return;
}
void remove_parse_option (int val) {
int t = find_parse_option (val);
if (t < 0) {
kprintf ("Can not remove unknown option %d\n", val);
usage ();
}
struct engine_parse_option *P = &engine_parse_options[t];
if (P->val_cnt == 1) {
assert (P->vals[0] == val);
free (P->help);
free (P->vals);
free (P->longopts);
memmove (engine_parse_options + t, engine_parse_options + t + 1, (engine_parse_options_num - t - 1) * sizeof (struct engine_parse_option));
engine_parse_options_num --;
return;
}
int *new_vals = malloc (4 * (P->val_cnt - 1));
int i;
int p = 0;
for (i = 0; i < P->val_cnt; i++) {
if (P->vals[i] != val) {
new_vals[p ++] = P->vals[i];
}
}
free (P->vals);
P->vals = new_vals;
P->val_cnt --;
if (P->smallest_val == val) {
P->smallest_val = 0x7fffffff;
int i;
for (i = 0; i < P->val_cnt; i++) {
if (P->vals[i] < P->smallest_val) {
P->smallest_val = P->vals[i];
}
}
parse_option_down (P);
}
if (P->base_val == val) {
P->base_val = P->smallest_val;
}
}
void parse_option_alias (const char *name, int val) {
int l = find_parse_option (val);
if (l >= 0) {
if (val >= 33 && val <= 127) {
kprintf ("Duplicate option `%c`\n", (char)val);
} else {
kprintf ("Duplicate option %d\n", val);
}
usage ();
}
l = find_parse_option_name (name);
if (l < 0) {
kprintf ("can't find option '%s'\n", name);
usage ();
}
struct engine_parse_option *P = &engine_parse_options[l];
P->val_cnt ++;
P->vals = realloc (P->vals, 4 * P->val_cnt);
P->vals[P->val_cnt - 1] = val;
if (val < P->smallest_val) {
P->smallest_val = val;
parse_option_up (P);
}
}
void parse_option_long_alias (const char *name, const char *alias_name) {
int l = find_parse_option_name (alias_name);
if (l >= 0) {
kprintf ("Duplicate option %s\n", alias_name);
usage ();
}
l = find_parse_option_name (name);
if (l < 0) {
kprintf ("can't find option '%s'\n", name);
usage ();
}
struct engine_parse_option *P = &engine_parse_options[l];
P->longopts_cnt ++;
P->longopts = realloc (P->longopts, sizeof (void *) * P->longopts_cnt);
P->longopts[P->longopts_cnt - 1] = alias_name;
}
int parse_usage (void) {
int max = 0;
int i;
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
int cur = 0;
int j;
for (j = 0; j < P->val_cnt; j++) {
if (P->vals[j] <= 127) {
cur += 3;
}
}
for (j = 0; j < P->longopts_cnt; j++) {
cur += strlen (P->longopts[j]) + 3;
}
if (P->arg == required_argument) {
cur += 6;
} else if (P->arg == optional_argument) {
cur += 6;
}
if (cur > max) {
max = cur;
}
}
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
int cur = 0;
printf ("\t");
int j;
for (j = 0; j < P->longopts_cnt; j++) {
if (cur) {
printf ("/");
cur ++;
}
cur += strlen (P->longopts[j]) + 2;
printf ("--%s", P->longopts[j]);
}
for (j = 0; j < P->val_cnt; j++) {
if (P->vals[j] <= 127) {
if (cur) {
printf ("/");
cur ++;
}
printf ("-%c", (char)P->vals[j]);
cur += 2;
}
}
if (P->arg == required_argument) {
printf (" <arg>");
cur += 6;
} else if (P->arg == optional_argument) {
printf (" {arg}");
cur += 6;
}
while (cur < max) {
printf (" ");
cur ++;
}
printf ("\t");
if (P->help) {
char *e = P->help;
while (*e) {
printf ("%c", *e);
if (*e == '\n') {
printf ("\t");
int i;
for (i = 0; i < max; i++) {
printf (" ");
}
printf ("\t");
}
e ++;
}
printf ("\n");
// printf ("%s\n", global_longopts_help[s]);
} else {
printf ("no help provided\n");
}
}
return 0;
}
int builtin_parse_option (int val) {
switch (val) {
case 'v':
if (!optarg) {
verbosity++;
} else {
verbosity = atoi (optarg);
}
break;
case 'h':
usage ();
exit (2);
case 'u':
if (username) {
kprintf ("wrong option -u%s, username is already defined as '%s'.\n", optarg, username);
exit (1);
}
username = optarg;
break;
case 'l':
logname = optarg;
break;
case 'd':
if (!optarg) {
daemonize ^= 1;
} else {
daemonize = atoi (optarg) != 0;
}
break;
case 202:
errno = 0;
if (nice (atoi (optarg)) == -1 && errno) {
perror ("nice");
}
break;
case 208:
max_allocated_buffer_bytes = parse_memory_limit (optarg);
break;
default:
return -1;
}
return 0;
}
int parse_one_option (int val) {
int t = find_parse_option (val);
if (t < 0) {
return -1;
}
struct engine_parse_option *P = &engine_parse_options[t];
return P->func (P->base_val);
}
int parse_engine_options_long (int argc, char **argv) {
engine_options_num = argc;
memcpy ((void *)engine_options, argv, sizeof (void *) * argc);
int total_longopts = 0;
int total_shortopts_len = 0;
int i;
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
total_longopts += P->longopts_cnt;
int j;
for (j = 0; j < P->val_cnt; j++) {
if (P->vals[j] <= 127) {
total_shortopts_len += (P->arg == required_argument) ? 2 : 1;
}
}
}
char *shortopts = malloc (total_shortopts_len + 1);
assert (shortopts);
struct option *longopts = malloc ((total_longopts + 1) * sizeof (struct option));
int lpos = 0;
int spos = 0;
for (i = 0; i < engine_parse_options_num; i++) {
struct engine_parse_option *P = &engine_parse_options[i];
int j;
for (j = 0; j < P->longopts_cnt; j++) {
assert (lpos < total_longopts);
longopts[lpos].flag = NULL;
longopts[lpos].has_arg = P->arg;
longopts[lpos].name = P->longopts[j];
longopts[lpos].val = P->base_val;
lpos ++;
}
for (j = 0; j < P->val_cnt; j++) {
if (P->vals[j] <= 127) {
assert (spos < total_shortopts_len);
shortopts[spos ++] = P->vals[j];
if (P->arg == required_argument) {
assert (spos < total_shortopts_len);
shortopts[spos ++] = ':';
}
}
}
}
assert (lpos == total_longopts);
memset (&longopts[lpos], 0, sizeof (struct option));
assert (spos == total_shortopts_len);
shortopts[spos] = 0;
while (1) {
int option_index = -1;
int c = getopt_long (argc, argv, shortopts, longopts, &option_index);
if (c == -1) { break; }
if (!c) { continue; }
if (c == '?') {
kprintf ("Unrecognized option\n");
usage ();
}
if (parse_one_option (c) < 0) {
if (option_index >= 0) {
assert (option_index < total_longopts);
kprintf ("Can not parse option %s\n", longopts[option_index].name);
usage ();
} else if (c <= 127) {
kprintf ("Can not parse option '%c'\n", (char)c);
usage ();
} else {
kprintf ("Can not parse option %d\n", c);
usage ();
}
}
}
return 0;
}
int in_keep_options_list (const unsigned *list, unsigned num) {
if (!list) { return 0; }
const unsigned *a = list;
while (*a) {
if (*a == num) { return 1; }
a ++;
}
return 0;
}
void engine_add_net_parse_options (void) __attribute__ ((weak));
void engine_add_net_parse_options (void) {}
void engine_add_engine_parse_options (void) __attribute__ ((weak));
void engine_add_engine_parse_options (void) {}
void add_builtin_parse_options (void) {
parse_option_builtin ("verbosity", optional_argument, 0, 'v', LONGOPT_COMMON_SET, "sets or increases verbosity level");
parse_option_builtin ("help", no_argument, 0, 'h', LONGOPT_COMMON_SET, "prints help and exits");
parse_option_builtin ("user", required_argument, 0, 'u', LONGOPT_COMMON_SET, "sets user name to make setuid");
parse_option_builtin ("log", required_argument, 0, 'l', LONGOPT_COMMON_SET, "sets log file name");
parse_option_builtin ("daemonize", optional_argument, 0, 'd', LONGOPT_COMMON_SET, "changes between daemonize/not daemonize mode");
parse_option_builtin ("nice", required_argument, 0, 202, LONGOPT_COMMON_SET, "sets niceness");
parse_option_ex ("msg-buffers-size", required_argument, 0, 208, LONGOPT_COMMON_SET, builtin_parse_option, "sets maximal buffers size (default %lld)", (long long)MSG_DEFAULT_MAX_ALLOCATED_BYTES);
//parse_option_builtin ("tl-history", optional_argument, 0, 210, LONGOPT_NET_SET, "long },
//parse_option_builtin ("tl-op-stat", no_argument, 0, 211, LONGOPT_NET_SET, "enabled stat about op usage");
//{ "rwm-peak-recovery", no_argument, 0, 213},
engine_add_net_parse_options ();
engine_add_engine_parse_options ();
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
2011-2013 Oleg Davydov
2012-2013 Arseny Smirnov
2012-2013 Aliaksei Levin
2012-2013 Anton Maydell
2013 Vitaliy Valtman
Copyright 2014-2018 Telegram Messenger Inc
2014-2018 Vitaly Valtman
*/
#pragma once
#include <getopt.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __LP64__
# define PTR_BITS 64
# define BITS_STR "64"
#else
# define PTR_BITS 32
# define BITS_STR "32"
#endif
#define MAX_ENGINE_OPTIONS 1000
extern int engine_options_num;
extern char *engine_options[MAX_ENGINE_OPTIONS];
int change_user (const char *username);
int change_user_group (const char *username, const char *groupname);
int raise_file_rlimit (int maxfiles);
int fast_backtrace (void **buffer, int size);
void print_backtrace (void);
void ksignal (int sig, void (*handler) (int));
void set_debug_handlers (void);
int adjust_oom_score (int oom_score_adj);
extern int allow_core_dump;
extern int quit_steps, start_time;
extern int daemonize;
extern const char *username, *progname, *groupname;
/* keep mask defines */
#define LONGOPT_JOBS_SET 0x00000400
#define LONGOPT_COMMON_SET 0x00001000
#define LONGOPT_NET_SET (LONGOPT_TCP_SET)
#define LONGOPT_TCP_SET 0x00002000
#define LONGOPT_CUSTOM_SET 0x10000000
struct engine_parse_option {
int *vals;
int val_cnt;
int base_val;
int smallest_val;
const char **longopts;
int longopts_cnt;
int (*func)(int);
char *help;
unsigned flags;
int arg;
};
/* init_parse_option should be called before parse_option and parse_option_alias */
//void init_parse_options (int keep_mask, const unsigned char *keep_options_custom_list);
void init_parse_options (unsigned keep_mask, const unsigned *keep_options_custom_list);
int parse_engine_options_long (int argc, char **argv);
int parse_usage (void);
void parse_option (const char *name, int arg, int *var, int val, const char *help, ...) __attribute__ ((format (printf, 5, 6)));
void parse_option_ex (const char *name, int arg, int *var, int val, unsigned flags, int (*func)(int), const char *help, ...) __attribute__ ((format (printf, 7, 8)));
void parse_option_alias (const char *name, int val);
void parse_option_long_alias (const char *name, const char *alias_name);
void remove_parse_option (int val);
//void set_backlog (const char *arg);
//void set_maxconn (const char *arg);
long long parse_memory_limit (const char *s);
void add_builtin_parse_options (void);
typedef void (*extra_debug_handler_t)(void);
extern extra_debug_handler_t extra_debug_handler;
static inline void barrier (void) {
asm volatile("": : :"memory");
}
static inline void mfence (void) {
asm volatile ("mfence": : :"memory");
}
//extern struct multicast_host multicast_hosts[];
//extern int multicast_hosts_num;
#define DEFAULT_BACKLOG 8192
#define DEFAULT_ENGINE_USER "mtproxy"
#ifdef __cplusplus
}
#endif
/*
This file is part of KittenDB/Engine Library.
KittenDB/Engine Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
KittenDB/Engine Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with KittenDB/Engine Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 Telegram Messenger Inc
2016 Nikolai Durov
*/
#include <assert.h>
#include "sha1.h"
void sha1_starts (sha1_context *ctx) {
EVP_MD_CTX_init (ctx);
EVP_DigestInit_ex (ctx, EVP_sha1(), NULL);
}
void sha1_update (sha1_context *ctx, const unsigned char *input, int ilen) {
EVP_DigestUpdate (ctx, input, ilen);
}
void sha1_finish (sha1_context *ctx, unsigned char output[20]) {
unsigned olen = 0;
EVP_DigestFinal_ex (ctx, output, &olen);
assert (olen == 20);
}
void sha1 (const unsigned char *input, int ilen, unsigned char output[20]) {
sha1_context *ctx = EVP_MD_CTX_new();
sha1_starts (ctx);
sha1_update (ctx, input, ilen);
sha1_finish (ctx, output);
EVP_MD_CTX_free (ctx);
}
void sha1_two_chunks (const unsigned char *input1, int ilen1, const unsigned char *input2, int ilen2, unsigned char output[20]) {
sha1_context *ctx = EVP_MD_CTX_new();
sha1_starts (ctx);
sha1_update (ctx, input1, ilen1);
sha1_update (ctx, input2, ilen2);
sha1_finish (ctx, output);
EVP_MD_CTX_free (ctx);
}
/*
This file is part of KittenDB/Engine Library.
KittenDB/Engine Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
KittenDB/Engine Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with KittenDB/Engine Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 Telegram Messenger Inc
2016 Nikolai Durov
*/
#include <openssl/evp.h>
typedef EVP_MD_CTX sha1_context;
void sha1_starts (sha1_context *ctx);
void sha1_update (sha1_context *ctx, const unsigned char *input, int ilen);
void sha1_finish (sha1_context *ctx, unsigned char output[20]);
void sha1 (const unsigned char *input, int ilen, unsigned char output[20]);
void sha1_two_chunks (const unsigned char *input1, int ilen1, const unsigned char *input2, int ilen2, unsigned char output[20]);
/*
This file is part of KittenDB/Engine Library.
KittenDB/Engine Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
KittenDB/Engine Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with KittenDB/Engine Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 Telegram Messenger Inc
2016 Nikolai Durov
*/
#include <assert.h>
#include "sha256.h"
void sha256_starts (sha256_context *ctx) {
EVP_MD_CTX_init (ctx);
EVP_DigestInit_ex (ctx, EVP_sha256(), NULL);
}
void sha256_update (sha256_context *ctx, const unsigned char *input, int ilen) {
EVP_DigestUpdate (ctx, input, ilen);
}
void sha256_finish (sha256_context *ctx, unsigned char output[32]) {
unsigned olen = 0;
EVP_DigestFinal_ex (ctx, output, &olen);
assert (olen == 32);
}
void sha256 (const unsigned char *input, int ilen, unsigned char output[32]) {
sha256_context *ctx = EVP_MD_CTX_new();
sha256_starts (ctx);
sha256_update (ctx, input, ilen);
sha256_finish (ctx, output);
EVP_MD_CTX_free (ctx);
}
void sha256_two_chunks (const unsigned char *input1, int ilen1, const unsigned char *input2, int ilen2, unsigned char output[32]) {
sha256_context *ctx = EVP_MD_CTX_new();
sha256_starts (ctx);
sha256_update (ctx, input1, ilen1);
sha256_update (ctx, input2, ilen2);
sha256_finish (ctx, output);
EVP_MD_CTX_free (ctx);
}
/*
This file is part of KittenDB/Engine Library.
KittenDB/Engine Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
KittenDB/Engine Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with KittenDB/Engine Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 Telegram Messenger Inc
2016 Nikolai Durov
*/
#pragma once
#include <openssl/evp.h>
typedef EVP_MD_CTX sha256_context;
void sha256_starts (sha256_context *ctx);
void sha256_update (sha256_context *ctx, const unsigned char *input, int ilen);
void sha256_finish (sha256_context *ctx, unsigned char output[32]);
void sha256 (const unsigned char *input, int ilen, unsigned char output[32]);
void sha256_two_chunks (const unsigned char *input1, int ilen1, const unsigned char *input2, int ilen2, unsigned char output[32]);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Vitaliy Valtman
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#include "common/tl-parse.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#include "net/net-events.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
#include "net/net-rpc-targets.h"
#include "net/net-tcp-connections.h"
#include "net/net-tcp-rpc-common.h"
#include "net/net-tcp-rpc-server.h"
#include "common/cpuid.h"
#include "common/kprintf.h"
#include "common/server-functions.h"
#include "vv/vv-io.h"
#include "vv/vv-tree.h"
//#include "auto/TL/common.h"
//#include "auto/TL/tl-names.h"
#include "engine/engine.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
#define MODULE tl_parse
MODULE_STAT_TYPE {
long long rpc_queries_received, rpc_answers_error, rpc_answers_received;
long long rpc_sent_errors, rpc_sent_answers, rpc_sent_queries;
int tl_in_allocated, tl_out_allocated;
/* #ifdef TIME_DEBUG
long long tl_udp_flush_rdtsc;
long long tl_udp_flush_cnt;
#endif*/
};
MODULE_INIT
MODULE_STAT_FUNCTION
double uptime = time (0) - start_time;
SB_SUM_ONE_LL (rpc_queries_received);
SB_SUM_ONE_LL (rpc_answers_error);
SB_SUM_ONE_LL (rpc_answers_received);
SB_SUM_ONE_LL (rpc_sent_errors);
SB_SUM_ONE_LL (rpc_sent_answers);
SB_SUM_ONE_LL (rpc_sent_queries);
SB_SUM_ONE_I (tl_in_allocated);
SB_SUM_ONE_I (tl_out_allocated);
/*#ifdef TIME_DEBUG
SB_SUM_ONE_LL (tl_udp_flush_rdtsc);
SB_SUM_ONE_LL (tl_udp_flush_cnt);
#endif*/
sb_printf (sb,
"rpc_qps\t%lf\n"
"default_rpc_flags\t%u\n",
safe_div (SB_SUM_LL (rpc_queries_received), uptime), tcp_get_default_rpc_flags ()
);
MODULE_STAT_FUNCTION_END
void tl_query_header_delete (struct tl_query_header *h) {
if (__sync_fetch_and_add (&h->ref_cnt, -1) > 1) { return; }
assert (!h->ref_cnt);
free (h);
}
struct tl_query_header *tl_query_header_dup (struct tl_query_header *h) {
__sync_fetch_and_add (&h->ref_cnt, 1);
return h;
}
struct tl_query_header *tl_query_header_clone (struct tl_query_header *h_old) {
struct tl_query_header *h = malloc (sizeof (*h));
memcpy (h, h_old, sizeof (*h));
h->ref_cnt = 1;
return h;
}
int tlf_set_error_format (struct tl_in_state *tlio_in, int errnum, const char *format, ...) {
if (TL_ERROR) {
return 0;
}
assert (format);
char s[1000];
va_list l;
va_start (l, format);
vsnprintf (s, sizeof (s), format, l);
va_end (l);
vkprintf (2, "Error %s\n", s);
TL_ERRNUM = errnum;
TL_ERROR = strdup (s);
return 0;
}
int tls_set_error_format (struct tl_out_state *tlio_out, int errnum, const char *format, ...) {
if (tlio_out->error) {
return 0;
}
assert (format);
char s[1000];
va_list l;
va_start (l, format);
vsnprintf (s, sizeof (s), format, l);
va_end (l);
vkprintf (2, "Error %s\n", s);
tlio_out->errnum = errnum;
tlio_out->error = strdup (s);
return 0;
}
/* {{{ Raw msg methods */
static inline void __tl_raw_msg_fetch_raw_data (struct tl_in_state *tlio_in, void *buf, int len) {
assert (rwm_fetch_data (TL_IN_RAW_MSG, buf, len) == len);
}
static inline void __tl_raw_msg_fetch_move (struct tl_in_state *tlio_in, int len) {
assert (len >= 0);
assert (rwm_skip_data (TL_IN_RAW_MSG, len) == len);
}
static inline void __tl_raw_msg_fetch_lookup (struct tl_in_state *tlio_in, void *buf, int len) {
assert (rwm_fetch_lookup (TL_IN_RAW_MSG, buf, len) == len);
}
static inline void __tl_raw_msg_fetch_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int len) {
rwm_split_head (raw, TL_IN_RAW_MSG, len);
}
static inline void __tl_raw_msg_fetch_lookup_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int len) {
rwm_clone (raw, TL_IN_RAW_MSG);
rwm_trunc (raw, len);
}
static inline void __tl_raw_msg_fetch_mark (struct tl_in_state *tlio_in) {
assert (!TL_IN_MARK);
struct raw_message *T = malloc (sizeof (*T));
rwm_clone (T, TL_IN_RAW_MSG);
TL_IN_MARK = T;
TL_IN_MARK_POS = TL_IN_POS;
}
static inline void __tl_raw_msg_fetch_mark_restore (struct tl_in_state *tlio_in) {
assert (TL_IN_MARK);
rwm_free (TL_IN_RAW_MSG);
*TL_IN_RAW_MSG = *(struct raw_message *)TL_IN_MARK;
free (TL_IN_MARK);
TL_IN_MARK = 0;
int x = TL_IN_POS - TL_IN_MARK_POS;
TL_IN_POS -= x;
TL_IN_REMAINING += x;
}
static inline void __tl_raw_msg_fetch_mark_delete (struct tl_in_state *tlio_in) {
assert (TL_IN_MARK);
rwm_free (TL_IN_MARK);
free (TL_IN_MARK);
TL_IN_MARK = 0;
}
static inline void *__tl_raw_msg_store_get_ptr (struct tl_out_state *tlio_out, int len) {
return rwm_postpone_alloc (TL_OUT_RAW_MSG, len);
}
static inline void *__tl_raw_msg_store_get_prepend_ptr (struct tl_out_state *tlio_out, int len) {
return rwm_prepend_alloc (TL_OUT_RAW_MSG, len);
}
static inline void __tl_raw_msg_store_raw_data (struct tl_out_state *tlio_out, const void *buf, int len) {
assert (rwm_push_data (TL_OUT_RAW_MSG, buf, len) == len);
}
static inline void __tl_raw_msg_store_raw_msg (struct tl_out_state *tlio_out, struct raw_message *raw) {
rwm_union (TL_OUT_RAW_MSG, raw);
}
static inline void __tl_raw_msg_store_read_back (struct tl_out_state *tlio_out, int len) {
assert (rwm_fetch_data_back (TL_OUT_RAW_MSG, 0, len) == len);
}
static inline void __tl_raw_msg_store_read_back_nondestruct (struct tl_out_state *tlio_out, void *buf, int len) {
struct raw_message r;
rwm_clone (&r, TL_OUT_RAW_MSG);
assert (rwm_fetch_data_back (&r, buf, len) == len);
rwm_free (&r);
}
static inline void __tl_raw_msg_raw_msg_copy_through (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out, int len, int advance) {
if (!advance) {
struct raw_message r;
rwm_clone (&r, TL_IN_RAW_MSG);
rwm_trunc (&r, len);
rwm_union (TL_OUT_RAW_MSG, &r);
} else {
struct raw_message r;
rwm_split_head (&r, TL_IN_RAW_MSG, len);
rwm_union (TL_OUT_RAW_MSG, &r);
assert (TL_IN_RAW_MSG->magic == RM_INIT_MAGIC);
}
}
static inline void __tl_raw_msg_str_copy_through (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out, int len, int advance) {
if (advance) {
assert (rwm_fetch_data (TL_IN_RAW_MSG, TL_OUT_STR, len) == len);
TL_OUT += len;
} else {
assert (rwm_fetch_lookup (TL_IN_RAW_MSG, TL_OUT_STR, len) == len);
TL_OUT += len;
}
}
static inline void __tl_raw_msg_fetch_clear (struct tl_in_state *tlio_in) {
if (TL_IN_RAW_MSG) {
rwm_free (TL_IN_RAW_MSG);
free (TL_IN_RAW_MSG);
TL_IN = 0;
}
}
static inline void __tl_raw_msg_store_clear (struct tl_out_state *tlio_out) {
if (TL_OUT_RAW_MSG) {
rwm_free (TL_OUT_RAW_MSG);
free (TL_OUT_RAW_MSG);
TL_OUT = 0;
}
}
static inline void __tl_raw_msg_store_flush (struct tl_out_state *tlio_out) {
// struct udp_target *S = (struct udp_target *)TL_OUT_EXTRA;
assert (TL_OUT_RAW_MSG);
/*#ifdef TIME_DEBUG
long long r = rdtsc ();
#endif*/
assert (0);
/*#ifdef TIME_DEBUG
MODULE_STAT->tl_udp_flush_rdtsc += (rdtsc () - r);
MODULE_STAT->tl_udp_flush_cnt ++;
#endif*/
free (TL_OUT_RAW_MSG);
TL_OUT = 0;
//udp_target_flush ((struct udp_target *)TL_OUT_EXTRA);
}
/* }}} */
/* {{{ Tcp raw msg methods */
static inline void __tl_tcp_raw_msg_store_clear (struct tl_out_state *tlio_out) {
if (TL_OUT_RAW_MSG) {
rwm_free (TL_OUT_RAW_MSG);
free (TL_OUT_RAW_MSG);
job_decref (JOB_REF_PASS (TL_OUT_EXTRA));
TL_OUT = NULL;
TL_OUT_EXTRA = NULL;
}
}
static inline void __tl_tcp_raw_msg_store_flush (struct tl_out_state *tlio_out) {
assert (TL_OUT_RAW_MSG);
assert (TL_OUT_EXTRA);
tcp_rpc_conn_send (JOB_REF_PASS (TL_OUT_EXTRA), TL_OUT_RAW_MSG, 4);
TL_OUT = NULL;
}
static inline void __tl_tcp_raw_msg_store_flush_unaligned (struct tl_out_state *tlio_out) {
assert (TL_OUT_RAW_MSG);
assert (TL_OUT_EXTRA);
tcp_rpc_conn_send (JOB_REF_PASS (TL_OUT_EXTRA), TL_OUT_RAW_MSG, 12);
TL_OUT = NULL;
}
/* }}} */
/* {{{ Str methods */
static inline void __tl_str_fetch_raw_data (struct tl_in_state *tlio_in, void *buf, int len) {
memcpy (buf, TL_IN_STR, len);
TL_IN += len;
}
static inline void __tl_str_fetch_move (struct tl_in_state *tlio_in, int len) {
TL_IN += len;
}
static inline void __tl_str_fetch_lookup (struct tl_in_state *tlio_in, void *buf, int len) {
memcpy (buf, TL_IN_STR, len);
}
static inline void __tl_str_fetch_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int len) {
rwm_init (raw, 0);
rwm_push_data (raw, TL_IN, len);
TL_IN += len;
}
static inline void __tl_str_fetch_lookup_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int len) {
rwm_init (raw, 0);
rwm_push_data (raw, TL_IN, len);
}
static inline void *__tl_str_store_get_ptr (struct tl_out_state *tlio_out, int len) {
void *r = TL_OUT_STR;
TL_OUT += len;
return r;
}
static inline void *__tl_str_store_get_prepend_ptr (struct tl_out_state *tlio_out, int len) {
return TL_OUT_STR - TL_OUT_POS - len;
}
static inline void __tl_str_store_raw_data (struct tl_out_state *tlio_out, const void *buf, int len) {
memcpy (TL_OUT_STR, buf, len);
TL_OUT += len;
}
static inline void __tl_str_store_raw_msg (struct tl_out_state *tlio_out, struct raw_message *raw) {
int len = raw->total_bytes;
rwm_fetch_data (raw, TL_OUT_STR, raw->total_bytes);
TL_OUT += len;
}
static inline void __tl_str_store_read_back (struct tl_out_state *tlio_out, int len) {
TL_OUT -= len;
}
static inline void __tl_str_store_read_back_nondestruct (struct tl_out_state *tlio_out, void *buf, int len) {
memcpy (TL_OUT_STR - len, buf, len);
}
static inline void __tl_str_raw_msg_copy_through (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out, int len, int advance) {
assert (rwm_push_data (TL_OUT_RAW_MSG, TL_IN_STR, len) == len);
if (advance) {
TL_IN += advance;
}
}
static inline void __tl_str_str_copy_through (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out, int len, int advance) {
memcpy (TL_OUT_STR, TL_IN_STR, len);
TL_OUT += len;
if (advance) {
TL_IN += advance;
}
}
static inline void __tl_str_fetch_mark (struct tl_in_state *tlio_in) {
assert (!TL_IN_MARK);
TL_IN_MARK = TL_IN_STR;
TL_IN_MARK_POS = TL_IN_POS;
}
static inline void __tl_str_fetch_mark_restore (struct tl_in_state *tlio_in) {
TL_IN = TL_IN_MARK;
TL_IN_MARK = 0;
int x = TL_IN_POS - TL_IN_MARK_POS;
TL_IN_POS -= x;
TL_IN_REMAINING += x;
}
static inline void __tl_str_fetch_mark_delete (struct tl_in_state *tlio_in) {
TL_IN_MARK = 0;
}
static inline void __tl_str_store_clear (struct tl_out_state *tlio_out) {
TL_OUT = 0;
}
static inline void __tl_str_store_flush (struct tl_out_state *tlio_out) {
TL_OUT = 0;
}
/* }}} */
const struct tl_in_methods tl_in_raw_msg_methods = {
.fetch_raw_data = __tl_raw_msg_fetch_raw_data,
.fetch_move = __tl_raw_msg_fetch_move,
.fetch_lookup = __tl_raw_msg_fetch_lookup,
.fetch_raw_message = __tl_raw_msg_fetch_raw_message,
.fetch_lookup_raw_message = __tl_raw_msg_fetch_lookup_raw_message,
.fetch_clear = __tl_raw_msg_fetch_clear,
.fetch_mark = __tl_raw_msg_fetch_mark,
.fetch_mark_restore = __tl_raw_msg_fetch_mark_restore,
.fetch_mark_delete = __tl_raw_msg_fetch_mark_delete,
.flags = 0,
};
const struct tl_in_methods tl_in_str_methods = {
.fetch_raw_data = __tl_str_fetch_raw_data,
.fetch_move = __tl_str_fetch_move,
.fetch_lookup = __tl_str_fetch_lookup,
.fetch_raw_message = __tl_str_fetch_raw_message,
.fetch_lookup_raw_message = __tl_str_fetch_lookup_raw_message,
// .fetch_clear = __tl_str_fetch_clear,
.fetch_mark = __tl_str_fetch_mark,
.fetch_mark_restore = __tl_str_fetch_mark_restore,
.fetch_mark_delete = __tl_str_fetch_mark_delete,
.flags = 0,
.prepend_bytes = 0,
};
/*
const struct tl_out_methods tl_out_conn_simple_methods = {
.store_get_ptr = __tl_conn_store_get_ptr,
.store_raw_data = __tl_conn_store_raw_data,
.store_raw_msg = __tl_conn_store_raw_msg,
.store_read_back = __tl_conn_store_read_back,
.store_read_back_nondestruct = __tl_conn_store_read_back_nondestruct,
// .store_flush = __tl_conn_store_flush,
.store_clear = __tl_conn_store_clear,
.copy_through =
{
0, // none
__tl_str_conn_copy_through, // str
__tl_raw_msg_conn_copy_through, // raw_msg
__tl_raw_msg_conn_copy_through, // tcp raw msg
__tl_raw_msg_conn_copy_through, // gms msg
__tl_raw_msg_conn_copy_through // gms bcast
},
.flags = TLF_PERMANENT | TLF_DISABLE_PREPEND | TLF_NO_AUTOFLUSH | TLF_NOALIGN,
.prepend_bytes = 0
};*/
const struct tl_out_methods tl_out_raw_msg_methods = {
.store_get_ptr = __tl_raw_msg_store_get_ptr,
.store_get_prepend_ptr = __tl_raw_msg_store_get_prepend_ptr,
.store_raw_msg = __tl_raw_msg_store_raw_msg,
.store_raw_data = __tl_raw_msg_store_raw_data,
.store_read_back = __tl_raw_msg_store_read_back,
.store_read_back_nondestruct = __tl_raw_msg_store_read_back_nondestruct,
.store_clear = __tl_raw_msg_store_clear,
.store_flush = __tl_raw_msg_store_flush,
.copy_through =
{
0, // none
__tl_str_raw_msg_copy_through, // str
__tl_raw_msg_raw_msg_copy_through, // raw_msg
__tl_raw_msg_raw_msg_copy_through, // tcp conn
},
.flags = TLF_ALLOW_PREPEND
};
const struct tl_out_methods tl_out_raw_msg_methods_nosend = {
.store_get_ptr = __tl_raw_msg_store_get_ptr,
.store_get_prepend_ptr = __tl_raw_msg_store_get_prepend_ptr,
.store_raw_msg = __tl_raw_msg_store_raw_msg,
.store_raw_data = __tl_raw_msg_store_raw_data,
.store_read_back = __tl_raw_msg_store_read_back,
.store_read_back_nondestruct = __tl_raw_msg_store_read_back_nondestruct,
.store_clear = __tl_raw_msg_store_clear,
.copy_through =
{
0, // none
__tl_str_raw_msg_copy_through, // str
__tl_raw_msg_raw_msg_copy_through, // tcp conn
},
.flags = TLF_ALLOW_PREPEND
};
const struct tl_out_methods tl_out_tcp_raw_msg_methods = {
.store_get_ptr = __tl_raw_msg_store_get_ptr,
.store_get_prepend_ptr = __tl_raw_msg_store_get_prepend_ptr,
.store_raw_data = __tl_raw_msg_store_raw_data,
.store_raw_msg = __tl_raw_msg_store_raw_msg,
.store_read_back = __tl_raw_msg_store_read_back,
.store_read_back_nondestruct = __tl_raw_msg_store_read_back_nondestruct,
.store_clear = __tl_tcp_raw_msg_store_clear,
.store_flush = __tl_tcp_raw_msg_store_flush,
.copy_through =
{
0, // none
__tl_str_raw_msg_copy_through, // str
__tl_raw_msg_raw_msg_copy_through, // raw_msg
__tl_raw_msg_raw_msg_copy_through, // tcp conn
},
.flags = TLF_ALLOW_PREPEND
};
const struct tl_out_methods tl_out_tcp_raw_msg_unaligned_methods = {
.store_get_ptr = __tl_raw_msg_store_get_ptr,
.store_get_prepend_ptr = __tl_raw_msg_store_get_prepend_ptr,
.store_raw_data = __tl_raw_msg_store_raw_data,
.store_raw_msg = __tl_raw_msg_store_raw_msg,
.store_read_back = __tl_raw_msg_store_read_back,
.store_read_back_nondestruct = __tl_raw_msg_store_read_back_nondestruct,
.store_clear = __tl_tcp_raw_msg_store_clear,
.store_flush = __tl_tcp_raw_msg_store_flush_unaligned,
.copy_through =
{
0, // none
__tl_str_raw_msg_copy_through, // str
__tl_raw_msg_raw_msg_copy_through, // raw_msg
__tl_raw_msg_raw_msg_copy_through, // tcp conn
},
.flags = TLF_ALLOW_PREPEND | TLF_NOALIGN
};
const struct tl_out_methods tl_out_str_methods = {
.store_get_ptr = __tl_str_store_get_ptr,
.store_get_prepend_ptr = __tl_str_store_get_prepend_ptr,
.store_raw_data = __tl_str_store_raw_data,
.store_raw_msg = __tl_str_store_raw_msg,
.store_read_back = __tl_str_store_read_back,
.store_read_back_nondestruct = __tl_str_store_read_back_nondestruct,
.store_clear = __tl_str_store_clear,
.store_flush = __tl_str_store_flush,
.copy_through =
{
0, // none
__tl_str_str_copy_through, // str
__tl_raw_msg_str_copy_through, // raw_msg
__tl_raw_msg_str_copy_through, // tcp raw_msg
},
.flags = TLF_PERMANENT | TLF_ALLOW_PREPEND,
.prepend_bytes = 0
};
int tlf_set_error (struct tl_in_state *tlio_in, int errnum, const char *s) {
assert (s);
if (TL_ERROR) {
return 0;
}
vkprintf (2, "Error %s\n", s);
TL_ERROR = strdup (s);
TL_ERRNUM = errnum;
return 0;
}
int __tl_fetch_init (struct tl_in_state *tlio_in, void *in, void *in_extra, enum tl_type type, const struct tl_in_methods *methods, int size) {
assert (TL_IN_TYPE == tl_type_none);
assert (in);
TL_IN_TYPE = type;
TL_IN = in;
TL_IN_REMAINING = size;
TL_IN_POS = 0;
TL_IN_CUR_FLAGS = 0;
TL_IN_METHODS = methods;
if (TL_ERROR) {
free (TL_ERROR);
TL_ERROR = 0;
}
TL_ERRNUM = 0;
return 0;
}
int tlf_init_raw_message (struct tl_in_state *tlio_in, struct raw_message *msg, int size, int dup) {
struct raw_message *r = (struct raw_message *)malloc (sizeof (*r));
if (dup == 0) {
rwm_move (r, msg);
} else if (dup == 1) {
rwm_move (r, msg);
rwm_init (msg, 0);
} else {
rwm_clone (r, msg);
}
return __tl_fetch_init (tlio_in, r, 0, tl_type_raw_msg, &tl_in_raw_msg_methods, size);
}
int tlf_init_str (struct tl_in_state *tlio_in, const char *s, int size) {
return __tl_fetch_init (tlio_in, (void *)s, 0, tl_type_str, &tl_in_str_methods, size);
}
int tlf_query_flags (struct tl_in_state *tlio_in, struct tl_query_header *header) {
int flags = tl_fetch_int ();
if (tl_fetch_error ()) {
return -1;
}
if (header->flags & flags) {
tl_fetch_set_error_format (TL_ERROR_HEADER, "Duplicate flags in header 0x%08x", header->flags & flags);
return -1;
}
if (flags) {
tl_fetch_set_error_format (TL_ERROR_HEADER, "Unsupported flags in header 0x%08x", flags);
return -1;
}
header->flags |= flags;
return 0;
}
int tlf_query_header (struct tl_in_state *tlio_in, struct tl_query_header *header) {
assert (header);
memset (header, 0, sizeof (*header));
int t = tl_fetch_unread ();
if (TL_IN_METHODS->prepend_bytes) {
tl_fetch_skip (TL_IN_METHODS->prepend_bytes);
}
header->op = tl_fetch_int ();
header->real_op = header->op;
header->ref_cnt = 1;
if (header->op != (int)RPC_INVOKE_REQ && header->op != (int)RPC_INVOKE_KPHP_REQ) {
tl_fetch_set_error (TL_ERROR_HEADER, "Expected RPC_INVOKE_REQ or RPC_INVOKE_KPHP_REQ");
return -1;
}
header->qid = tl_fetch_long ();
if (header->op == (int)RPC_INVOKE_KPHP_REQ) {
//tl_fetch_raw_data (header->invoke_kphp_req_extra, 24);
if (tl_fetch_error ()) {
return -1;
}
MODULE_STAT->rpc_queries_received ++;
return t - tl_fetch_unread ();
}
while (1) {
int op = tl_fetch_lookup_int ();
int ok = 1;
switch (op) {
case RPC_DEST_ACTOR:
assert (tl_fetch_int () == (int)RPC_DEST_ACTOR);
header->actor_id = tl_fetch_long ();
break;
case RPC_DEST_ACTOR_FLAGS:
assert (tl_fetch_int () == (int)RPC_DEST_ACTOR_FLAGS);
header->actor_id = tl_fetch_long ();
tlf_query_flags (tlio_in, header);
break;
case RPC_DEST_FLAGS:
assert (tl_fetch_int () == (int)RPC_DEST_FLAGS);
tlf_query_flags (tlio_in, header);
break;
default:
ok = 0;
break;
}
if (tl_fetch_error ()) {
return -1;
}
if (!ok) {
MODULE_STAT->rpc_queries_received ++;
return t - tl_fetch_unread ();
}
}
}
int tlf_query_answer_flags (struct tl_in_state *tlio_in, struct tl_query_header *header) {
int flags = tl_fetch_int ();
if (tl_fetch_error ()) {
return -1;
}
if (header->flags & flags) {
tl_fetch_set_error_format (TL_ERROR_HEADER, "Duplicate flags in header 0x%08x", header->flags & flags);
return -1;
}
if (flags) {
tl_fetch_set_error_format (TL_ERROR_HEADER, "Unsupported flags in header 0x%08x", flags);
return -1;
}
header->flags |= flags;
return 0;
}
int tlf_query_answer_header (struct tl_in_state *tlio_in, struct tl_query_header *header) {
assert (header);
memset (header, 0, sizeof (*header));
int t = tl_fetch_unread ();
if (TL_IN_METHODS->prepend_bytes) {
tl_fetch_skip (TL_IN_METHODS->prepend_bytes);
}
header->op = tl_fetch_int ();
header->real_op = header->op;
header->ref_cnt = 1;
if (header->op != RPC_REQ_ERROR && header->op != RPC_REQ_RESULT ) {
tl_fetch_set_error (TL_ERROR_HEADER, "Expected RPC_REQ_ERROR or RPC_REQ_RESULT");
return -1;
}
header->qid = tl_fetch_long ();
while (1) {
int ok = 1;
if (header->op != RPC_REQ_ERROR) {
int op = tl_fetch_lookup_int ();
switch (op) {
case RPC_REQ_ERROR:
assert (tl_fetch_int () == RPC_REQ_ERROR);
header->op = RPC_REQ_ERROR_WRAPPED;
tl_fetch_long ();
break;
case RPC_REQ_ERROR_WRAPPED:
header->op = RPC_REQ_ERROR_WRAPPED;
break;
case RPC_REQ_RESULT_FLAGS:
assert (tl_fetch_int () == (int)RPC_REQ_RESULT_FLAGS);
tlf_query_answer_flags (tlio_in, header);
break;
default:
ok = 0;
break;
}
} else {
ok = 0;
}
if (tl_fetch_error ()) {
return -1;
}
if (!ok) {
if (header->op == RPC_REQ_ERROR || header->op == RPC_REQ_ERROR_WRAPPED) {
MODULE_STAT->rpc_answers_error ++;
} else {
MODULE_STAT->rpc_answers_received ++;
}
return t - tl_fetch_unread ();
}
}
}
static inline int __tl_store_init (struct tl_out_state *tlio_out, void *out, void *out_extra, enum tl_type type, const struct tl_out_methods *methods, int size, long long qid) {
assert (tlio_out);
assert (!TL_OUT_METHODS);
TL_OUT = out;
TL_OUT_EXTRA = out_extra;
if (out) {
TL_OUT_METHODS = methods;
TL_OUT_TYPE = type;
if (type != tl_type_none && !(methods->flags & (TLF_ALLOW_PREPEND | TLF_DISABLE_PREPEND))) {
TL_OUT_SIZE = (int *) methods->store_get_ptr (tlio_out, methods->prepend_bytes + (qid ? 12 : 0));
}
} else {
TL_OUT_TYPE = tl_type_none;
}
TL_OUT_POS = 0;
TL_OUT_QID = qid;
TL_OUT_REMAINING = size;
tlio_out->errnum = 0;
tlio_out->error = NULL;
return 0;
}
/*int tls_init_simple (struct tl_out_state *tlio_out, connection_job_t c) {
if (c) {
TL_OUT_PID = &(RPCS_DATA(c)->remote_pid);
} else {
TL_OUT_PID = 0;
}
return __tl_store_init (tlio_out, job_incref (c), 0, tl_type_conn, &tl_out_conn_simple_methods, (1 << 27), 0);
}*/
int tls_init_raw_msg (struct tl_out_state *tlio_out, struct process_id *pid, long long qid) {
if (pid) {
memcpy (&tlio_out->out_pid_buf, pid, 12);
TL_OUT_PID = &tlio_out->out_pid_buf;
} else {
TL_OUT_PID = 0;
}
struct raw_message *d = 0;
if (pid) {
d = (struct raw_message *)malloc (sizeof (*d));
rwm_init (d, 0);
}
return __tl_store_init (tlio_out, d, NULL, tl_type_raw_msg, &tl_out_raw_msg_methods, (1 << 27), qid);
}
int tls_init_tcp_raw_msg (struct tl_out_state *tlio_out, JOB_REF_ARG(c), long long qid) {
if (c) {
TL_OUT_PID = &(TCP_RPC_DATA(c)->remote_pid);
} else {
TL_OUT_PID = 0;
}
struct raw_message *d = 0;
if (c) {
d = (struct raw_message *)malloc (sizeof (*d));
rwm_init (d, 0);
}
return __tl_store_init (tlio_out, d, c, tl_type_tcp_raw_msg, &tl_out_tcp_raw_msg_methods, (1 << 27), qid);
}
int tls_init_tcp_raw_msg_unaligned (struct tl_out_state *tlio_out, JOB_REF_ARG(c), long long qid) {
if (c) {
TL_OUT_PID = &(TCP_RPC_DATA(c)->remote_pid);
} else {
TL_OUT_PID = 0;
}
struct raw_message *d = 0;
if (c) {
d = (struct raw_message *)malloc (sizeof (*d));
rwm_init (d, 0);
}
return __tl_store_init (tlio_out, d, c, tl_type_tcp_raw_msg, &tl_out_tcp_raw_msg_unaligned_methods, (1 << 27), qid);
}
int tls_init_str (struct tl_out_state *tlio_out, char *s, long long qid, int size) {
TL_OUT_PID = 0;
return __tl_store_init (tlio_out, s, s, tl_type_str, &tl_out_str_methods, size, qid);
}
int tls_init_raw_msg_nosend (struct tl_out_state *tlio_out) {
struct raw_message *d = (struct raw_message *)malloc (sizeof (*d));
rwm_init (d, 0);
return __tl_store_init (tlio_out, d, d, tl_type_raw_msg, &tl_out_raw_msg_methods_nosend, (1 << 27), 0);
}
/*
int tls_init_any (struct tl_out_state *tlio_out, enum tl_type type, void *out, long long qid) {
switch (type) {
case tl_type_conn:
return tls_init_connection (tlio_out, (connection_job_t )out, qid);
case tl_type_tcp_raw_msg:
return tls_init_tcp_raw_msg (tlio_out, out, qid);
default:
assert (0);
}
}*/
int tls_header (struct tl_out_state *tlio_out, struct tl_query_header *header) {
assert (tls_check (tlio_out, 0) >= 0);
assert (header->op == (int)RPC_REQ_ERROR || header->op == (int)RPC_REQ_RESULT || header->op == (int)RPC_INVOKE_REQ || header->op == (int)RPC_REQ_ERROR_WRAPPED);
if (header->op == (int)RPC_INVOKE_REQ) {
if (header->flags) {
tl_store_int (RPC_DEST_ACTOR_FLAGS);
tl_store_long (header->actor_id);
tl_store_int (header->flags);
} else if (header->actor_id) {
tl_store_int (RPC_DEST_ACTOR);
tl_store_long (header->actor_id);
}
} else if (header->op == RPC_REQ_ERROR_WRAPPED) {
tl_store_int (RPC_REQ_ERROR);
tl_store_long (TL_OUT_QID);
} else if (header->op == RPC_REQ_RESULT) {
if (header->flags) {
tl_store_int (RPC_REQ_RESULT_FLAGS);
tl_store_int (header->flags);
}
}
return 0;
}
int tls_end_ext (struct tl_out_state *tlio_out, int op) {
if (TL_OUT_TYPE == tl_type_none) {
return 0;
}
assert (TL_OUT);
assert (TL_OUT_TYPE);
if (tlio_out->error) {
// tl_store_clear ();
tl_store_clean ();
vkprintf (1, "tl_store_end: "PID_PRINT_STR" writing error %s, errnum %d, tl.out_pos = %d\n", PID_TO_PRINT(TL_OUT_PID), tlio_out->error, tlio_out->errnum, TL_OUT_POS);
//tl_store_clear ();
tl_store_int (RPC_REQ_ERROR);
tl_store_long (TL_OUT_QID);
tl_store_int (tlio_out->errnum);
tl_store_string0 (tlio_out->error);
MODULE_STAT->rpc_sent_errors ++;
} else {
if (op == RPC_REQ_RESULT) {
MODULE_STAT->rpc_sent_answers ++;
} else {
MODULE_STAT->rpc_sent_queries ++;
}
}
if (!(TL_OUT_FLAGS & TLF_NOALIGN)) {
assert (!(TL_OUT_POS & 3));
}
{
int *p;
if (TL_OUT_FLAGS & TLF_ALLOW_PREPEND) {
p = TL_OUT_SIZE = tl_store_get_prepend_ptr (TL_OUT_METHODS->prepend_bytes + (TL_OUT_QID ? 12 : 0));
} else {
p = TL_OUT_SIZE;
}
if (TL_OUT_QID) {
assert (op);
p += (TL_OUT_METHODS->prepend_bytes) / 4;
*p = op;
*(long long *)(p + 1) = TL_OUT_QID;
}
}
if (TL_OUT_METHODS->store_prefix) {
TL_OUT_METHODS->store_prefix (tlio_out);
}
if (!(TL_OUT_FLAGS & TLF_NO_AUTOFLUSH)) {
TL_OUT_METHODS->store_flush (tlio_out);
}
vkprintf (2, "tl_store_end: written %d bytes, qid = %lld, PID = " PID_PRINT_STR "\n", TL_OUT_POS, TL_OUT_QID, PID_TO_PRINT (TL_OUT_PID));
TL_OUT = 0;
TL_OUT_TYPE = tl_type_none;
TL_OUT_METHODS = 0;
TL_OUT_EXTRA = 0;
return 0;
}
int tls_init (struct tl_out_state *tlio_out, enum tl_type type, struct process_id *pid, long long qid) {
switch (type) {
case tl_type_raw_msg:
{
tls_init_raw_msg (tlio_out, pid, qid);
return 1;
}
case tl_type_tcp_raw_msg:
{
connection_job_t d = rpc_target_choose_connection (rpc_target_lookup (pid), pid);
if (d) {
vkprintf (2, "%s: Good connection " PID_PRINT_STR "\n", __func__, PID_TO_PRINT (pid));
tls_init_tcp_raw_msg (tlio_out, JOB_REF_PASS (d), qid);
return 1;
} else {
vkprintf (2, "%s: Bad connection " PID_PRINT_STR "\n", __func__, PID_TO_PRINT (pid));
return -1;
}
}
case tl_type_none:
vkprintf (2, "Trying to tl_init_store() with type tl_type_none, qid=%lld\n" , qid);
return -1;
default:
fprintf (stderr, "type = %d\n", type);
assert (0);
return 0;
}
}
struct tl_in_state *tl_in_state_alloc (void) {
MODULE_STAT->tl_in_allocated ++;
return calloc (sizeof (struct tl_in_state), 1);
}
void tl_in_state_free (struct tl_in_state *tlio_in) {
MODULE_STAT->tl_in_allocated --;
if (tlio_in->in_methods && tlio_in->in_methods->fetch_clear) {
tlio_in->in_methods->fetch_clear (tlio_in);
}
if (tlio_in->error) {
free (PTR_MOVE (tlio_in->error));
}
free (tlio_in);
}
struct tl_out_state *tl_out_state_alloc (void) {
MODULE_STAT->tl_out_allocated ++;
return calloc (sizeof (struct tl_out_state), 1);
}
void tl_out_state_free (struct tl_out_state *tlio_out) {
MODULE_STAT->tl_out_allocated --;
if (tlio_out->out_methods && tlio_out->out_methods->store_clear) {
tlio_out->out_methods->store_clear (tlio_out);
}
if (tlio_out->error) {
free (PTR_MOVE (tlio_out->error));
}
free (tlio_out);
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Vitaliy Valtman
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
*/
#pragma once
#include <assert.h>
#include <string.h>
#include "net/net-connections.h"
#include "rpc-const.h"
#include "jobs/jobs.h"
//#define RPC_INVOKE_REQ 0x2374df3d
//#define RPC_REQ_RESULT 0x63aeda4e
//#define RPC_REQ_ERROR 0x7ae432f5
#define TL_FETCH_FLAG_ALLOW_DATA_AFTER_QUERY 1
#define TL_ENGINE_NOP 0x166bb7c6
#define TLF_CRC32 1
#define TLF_PERMANENT 2
#define TLF_ALLOW_PREPEND 4
#define TLF_DISABLE_PREPEND 8
#define TLF_NOALIGN 16
#define TLF_NO_AUTOFLUSH 32
struct tl_query_header;
struct tl_query_header *tl_query_header_dup (struct tl_query_header *h);
struct tl_query_header *tl_query_header_clone (struct tl_query_header *h_old);
void tl_query_header_delete (struct tl_query_header *h);
#define RPC_REQ_ERROR_WRAPPED (RPC_REQ_ERROR + 1)
extern long long rpc_queries_received, rpc_queries_ok, rpc_queries_error;
struct tl_in_state;
struct tl_out_state;
struct tl_in_methods {
void (*fetch_raw_data)(struct tl_in_state *tlio, void *buf, int len);
void (*fetch_move)(struct tl_in_state *tlio, int len);
void (*fetch_lookup)(struct tl_in_state *tlio, void *buf, int len);
void (*fetch_clear)(struct tl_in_state *tlio);
void (*fetch_mark)(struct tl_in_state *tlio);
void (*fetch_mark_restore)(struct tl_in_state *tlio);
void (*fetch_mark_delete)(struct tl_in_state *tlio);
void (*fetch_raw_message)(struct tl_in_state *tlio, struct raw_message *raw, int len);
void (*fetch_lookup_raw_message)(struct tl_in_state *tlio, struct raw_message *raw, int len);
int flags;
int prepend_bytes;
};
struct tl_out_methods {
void *(*store_get_ptr)(struct tl_out_state *tlio, int len);
void *(*store_get_prepend_ptr)(struct tl_out_state *tlio, int len);
void (*store_raw_data)(struct tl_out_state *tlio, const void *buf, int len);
void (*store_raw_msg)(struct tl_out_state *tlio, struct raw_message *raw);
void (*store_read_back)(struct tl_out_state *tlio, int len);
void (*store_read_back_nondestruct)(struct tl_out_state *tlio, void *buf, int len);
unsigned (*store_crc32_partial)(struct tl_out_state *tlio, int len, unsigned start);
void (*store_flush)(struct tl_out_state *tlio);
void (*store_clear)(struct tl_out_state *tlio);
void (*copy_through[10])(struct tl_in_state *tlio_src, struct tl_out_state *tlio_dst, int len, int advance);
void (*store_prefix)(struct tl_out_state *tlio);
int flags;
int prepend_bytes;
};
enum tl_type {
tl_type_none,
tl_type_str,
//tl_type_conn,
//tl_type_nbit,
tl_type_raw_msg,
tl_type_tcp_raw_msg,
};
struct tl_in_state {
enum tl_type in_type;
const struct tl_in_methods *in_methods;
void *in;
void *in_mark;
int in_remaining;
int in_pos;
int in_mark_pos;
int in_flags;
char *error;
int errnum;
struct process_id in_pid_buf;
struct process_id *in_pid;
};
struct tl_out_state {
enum tl_type out_type;
const struct tl_out_methods *out_methods;
void *out;
void *out_extra;
int out_pos;
int out_remaining;
int *out_size;
char *error;
int errnum;
long long out_qid;
struct process_id out_pid_buf;
struct process_id *out_pid;
};
struct query_work_params;
struct tl_query_header {
long long qid;
long long actor_id;
int flags;
int op;
int real_op;
int ref_cnt;
struct query_work_params *qw_params;
};
extern const struct tl_in_methods tl_in_conn_methods;
extern const struct tl_in_methods tl_in_nbit_methods;
extern const struct tl_in_methods tl_in_raw_msg_methods;
extern const struct tl_out_methods tl_out_conn_methods;
extern const struct tl_out_methods tl_out_raw_msg_methods;
#define TL_IN (tlio_in->in)
#define TL_IN_CONN ((connection_job_t)(tlio_in->in))
#define TL_IN_NBIT ((nb_iterator_t *)(tlio_in->in))
#define TL_IN_RAW_MSG ((struct raw_message *)(tlio_in->in))
#define TL_IN_STR ((char *)(tlio_in->in))
#define TL_IN_TYPE (tlio_in->in_type)
#define TL_IN_REMAINING (tlio_in->in_remaining)
#define TL_IN_POS (tlio_in->in_pos)
#define TL_IN_METHODS (tlio_in->in_methods)
#define TL_IN_MARK (tlio_in->in_mark)
#define TL_IN_MARK_POS (tlio_in->in_mark_pos)
#define TL_IN_PID (tlio_in->in_pid)
#define TL_IN_FLAGS (tlio_in->in_methods->flags)
#define TL_IN_CUR_FLAGS (tlio_in->in_flags)
#define TL_OUT ((tlio_out->out))
#define TL_OUT_TYPE (tlio_out->out_type)
#define TL_OUT_SIZE (tlio_out->out_size)
#define TL_OUT_CONN ((connection_job_t)(tlio_out->out))
#define TL_OUT_RAW_MSG ((struct raw_message *)(tlio_out->out))
#define TL_OUT_STR ((char *)(tlio_out->out))
#define TL_OUT_POS (tlio_out->out_pos)
#define TL_OUT_REMAINING (tlio_out->out_remaining)
#define TL_OUT_METHODS (tlio_out->out_methods)
#define TL_OUT_QID (tlio_out->out_qid)
#define TL_OUT_EXTRA (tlio_out->out_extra)
#define TL_OUT_PID (tlio_out->out_pid)
#define TL_OUT_FLAGS (tlio_out->out_methods->flags)
#define TL_ERROR (tlio_in->error)
#define TL_ERRNUM (tlio_in->errnum)
//#define TL_COPY_THROUGH (tlio->copy_through)
//#define TL_ATTEMPT_NUM (tlio)->attempt_num
int tlf_set_error_format (struct tl_in_state *tlio_in, int errnum, const char *format, ...) __attribute__ (( format(printf,3,4) ));
#define tl_fetch_set_error_format(...) tlf_set_error_format (tlio_in, ## __VA_ARGS__)
int tlf_set_error (struct tl_in_state *tlio_in, int errnum, const char *s);
#define tl_fetch_set_error(...) tlf_set_error (tlio_in, ## __VA_ARGS__)
int tls_set_error_format (struct tl_out_state *tlio_out, int errnum, const char *format, ...) __attribute__ (( format(printf,3,4) ));
#define tl_store_set_error_format(...) tls_set_error_format (tlio_out, ## __VA_ARGS__)
//int tlf_init_connection (struct tl_in_state *tlio_in, connection_job_t c, int size);
//int tlf_init_iterator (struct tl_in_state *tlio_in, nb_iterator_t *it, int size);
//int tlf_init_iterator_noskip (struct tl_in_state *tlio_in, nb_iterator_t *it, int size);
// dup = 0 - delete reference
// dup = 1 - make msg valid raw message of size 0
// dup = 2 - clone message
int tlf_init_raw_message (struct tl_in_state *tlio_in, struct raw_message *msg, int size, int dup);
int tlf_init_str (struct tl_in_state *tlio_in, const char *s, int size);
//int tls_init_connection (struct tl_out_state *tlio_out, connection_job_t c, long long qid);
//int tls_init_connection_keep_error (struct tl_out_state *tlio_out, connection_job_t c, long long qid);
int tls_init_raw_msg (struct tl_out_state *tlio_out, struct process_id *pid, long long qid);
//int tls_init_raw_msg_keep_error (struct tl_out_state *tlio_out, struct process_id *pid, long long qid);
int tls_init_tcp_raw_msg (struct tl_out_state *tlio_out, JOB_REF_ARG (c), long long qid);
int tls_init_tcp_raw_msg_unaligned (struct tl_out_state *tlio_out, JOB_REF_ARG (c), long long qid);
//int tls_init_tcp_raw_msg_keep_error (struct tl_out_state *tlio_out, connection_job_t c, long long qid);
//int tls_init_simple (struct tl_out_state *tlio_out, connection_job_t c);
int tls_init_str (struct tl_out_state *tlio_out, char *s, long long qid, int size);
//int tls_init_str_keep_error (struct tl_out_state *tlio_out, char *s, long long qid, int size);
//int tls_init_any_keep_error (struct tl_out_state *tlio_out, enum tl_type type, void *out, long long qid);
int tls_init_raw_msg_nosend (struct tl_out_state *tlio_out);
//int tls_init_any (struct tl_out_state *tlio, enum tl_type type, void *out, long long qid);
int tls_init (struct tl_out_state *tlio_out, enum tl_type type, struct process_id *pid, long long qid);
//int tls_init_keep_error (struct tl_out_state *tlio_out, enum tl_type type, struct process_id *pid, long long qid);
int tlf_query_flags (struct tl_in_state *tlio_in, struct tl_query_header *header);
int tlf_query_header (struct tl_in_state *tlio_in, struct tl_query_header *header);
int tlf_query_answer_flags (struct tl_in_state *tlio_in, struct tl_query_header *header);
int tlf_query_answer_header (struct tl_in_state *tlio_in, struct tl_query_header *header);
int tls_header (struct tl_out_state *tlio_out, struct tl_query_header *header);
int tls_end_ext (struct tl_out_state *tlio_out, int op);
static inline int tlf_init_empty (struct tl_in_state *tlio_in) {
return tlf_init_str (tlio_in, "", 0);
}
static inline int tl_store_end_simple (struct tl_out_state *tlio_out) {
return tls_end_ext (tlio_out, 0);
}
#define tl_store_end_ext(type) tls_end_ext(tlio_out,type)
static inline int tlf_check (struct tl_in_state *tlio_in, int nbytes) /* {{{ */ {
if (!TL_IN_TYPE) {
tlf_set_error (tlio_in, TL_ERROR_INTERNAL, "Trying to read from unitialized in buffer");
return -1;
}
if (nbytes >= 0) {
if (TL_IN_REMAINING < nbytes) {
tlf_set_error_format (tlio_in, TL_ERROR_NOT_ENOUGH_DATA, "Trying to read %d bytes at position %d (size = %d)", nbytes, TL_IN_POS, TL_IN_POS + TL_IN_REMAINING);
return -1;
}
} else {
if (TL_IN_POS < -nbytes) {
tlf_set_error_format (tlio_in, TL_ERROR_NOT_ENOUGH_DATA, "Trying to read %d bytes at position %d (size = %d)", nbytes, TL_IN_POS, TL_IN_POS + TL_IN_REMAINING);
return -1;
}
}
if (TL_ERROR) {
return -1;
}
return 0;
}
/* }}} */
inline static void __tlf_raw_data (struct tl_in_state *tlio_in, void *buf, int size) /* {{{ */ {
TL_IN_METHODS->fetch_raw_data (tlio_in, buf, size);
TL_IN_POS += size;
TL_IN_REMAINING -= size;
}
/* }}} */
inline static void __tlf_skip_raw_data (struct tl_in_state *tlio_in, int size) /* {{{ */ {
TL_IN_METHODS->fetch_move (tlio_in, size);
TL_IN_POS += size;
TL_IN_REMAINING -= size;
}
/* }}} */
static inline int tlf_lookup_int (struct tl_in_state *tlio_in) /* {{{ */ {
if (tlf_check (tlio_in, 4) < 0) {
return -1;
}
int x;
TL_IN_METHODS->fetch_lookup (tlio_in, &x, 4);
return x;
}
/* }}} */
#define tl_fetch_lookup_int(...) tlf_lookup_int (tlio_in, ## __VA_ARGS__)
static inline int tlf_lookup_second_int (struct tl_in_state *tlio_in) /* {{{ */ {
if (tlf_check (tlio_in, 8) < 0) {
return -1;
}
int x[2];
TL_IN_METHODS->fetch_lookup (tlio_in, x, 8);
return x[1];
}
/* }}} */
#define tl_fetch_lookup_second_int(...) tlf_lookup_second_int (tlio_in, ## __VA_ARGS__)
static inline long long tlf_lookup_long (struct tl_in_state *tlio_in) /* {{{ */ {
if (tlf_check (tlio_in, 8) < 0) {
return -1;
}
long long x;
TL_IN_METHODS->fetch_lookup (tlio_in, &x, 8);
return x;
}
/* }}} */
#define tl_fetch_lookup_long(...) tlf_lookup_long (tlio_in, ## __VA_ARGS__)
static inline int tlf_lookup_data (struct tl_in_state *tlio_in, void *data, int len) /* {{{ */ {
if (tlf_check (tlio_in, len) < 0) {
return -1;
}
TL_IN_METHODS->fetch_lookup (tlio_in, data, len);
return len;
}
/* }}} */
#define tl_fetch_lookup_data(...) tlf_lookup_data (tlio_in, ## __VA_ARGS__)
static inline int tlf_int (struct tl_in_state *tlio_in) /* {{{ */ {
if (__builtin_expect (tlf_check (tlio_in, 4) < 0, 0)) {
return -1;
}
int x;
__tlf_raw_data (tlio_in, &x, 4);
return x;
}
/* }}} */
#define tl_fetch_int(...) tlf_int (tlio_in, ## __VA_ARGS__)
static inline double tlf_double (struct tl_in_state *tlio_in) /* {{{ */ {
if (__builtin_expect (tlf_check (tlio_in, sizeof (double)) < 0, 0)) {
return -1;
}
double x;
__tlf_raw_data (tlio_in, &x, sizeof (x));
return x;
}
/* }}} */
#define tl_fetch_double(...) tlf_double (tlio_in, ## __VA_ARGS__)
static inline long long tlf_long (struct tl_in_state *tlio_in) /* {{{ */ {
if (__builtin_expect (tlf_check (tlio_in, 8) < 0, 0)) {
return -1;
}
long long x;
__tlf_raw_data (tlio_in, &x, 8);
return x;
}
/* }}} */
#define tl_fetch_long(...) tlf_long (tlio_in, ## __VA_ARGS__)
static inline void tlf_mark (struct tl_in_state *tlio_in) /* {{{ */ {
TL_IN_METHODS->fetch_mark (tlio_in);
}
/* }}} */
#define tl_fetch_mark(...) tlf_mark (tlio_in, ## __VA_ARGS__)
static inline void tlf_mark_restore (struct tl_in_state *tlio_in) /* {{{ */ {
TL_IN_METHODS->fetch_mark_restore (tlio_in);
}
/* }}} */
#define tl_fetch_mark_restore(...) tlf_mark_restore (tlio_in, ## __VA_ARGS__)
static inline void tlf_mark_delete (struct tl_in_state *tlio_in) /* {{{ */ {
TL_IN_METHODS->fetch_mark_delete (tlio_in);
}
/* }}} */
#define tl_fetch_mark_delete(...) tlf_mark_delete (tlio_in, ## __VA_ARGS__)
static inline int tlf_string_len (struct tl_in_state *tlio_in, int max_len) /* {{{ */ {
if (tlf_check (tlio_in, 4) < 0) {
return -1;
}
int x = 0;
__tlf_raw_data (tlio_in, &x, 1);
if (x == 255) {
tlf_set_error (tlio_in, TL_ERROR_SYNTAX, "String len can not start with 0xff");
return -1;
}
if (x == 254) {
__tlf_raw_data (tlio_in, &x, 3);
}
if (x > max_len) {
tlf_set_error_format (tlio_in, TL_ERROR_TOO_LONG_STRING, "string is too long: max_len = %d, len = %d", max_len, x);
return -1;
}
if (x > TL_IN_REMAINING) {
tlf_set_error_format (tlio_in, TL_ERROR_NOT_ENOUGH_DATA, "string is too long: remaining_bytes = %d, len = %d", TL_IN_REMAINING, x);
return -1;
}
return x;
}
/* }}} */
#define tl_fetch_string_len(...) tlf_string_len (tlio_in, ## __VA_ARGS__)
static inline int tlf_pad (struct tl_in_state *tlio_in) /* {{{ */ {
int pad = (-TL_IN_POS) & 3;
if (tlf_check (tlio_in, pad) < 0) {
return -1;
}
int t = 0;
assert (TL_IN_REMAINING >= pad);
__tlf_raw_data (tlio_in, &t, pad);
if (t) {
tlf_set_error (tlio_in, TL_ERROR_SYNTAX, "Padding with non-zeroes");
return -1;
}
return pad;
}
/* }}} */
#define tl_fetch_pad(...) tlf_pad (tlio_in, ## __VA_ARGS__)
static inline int tlf_raw_data (struct tl_in_state *tlio_in, void *buf, int len) /* {{{ */ {
assert (!(len & 3));
if (tlf_check (tlio_in, len) < 0) {
return -1;
}
__tlf_raw_data (tlio_in, buf, len);
return len;
}
/* }}} */
#define tl_fetch_raw_data(...) tlf_raw_data (tlio_in, ## __VA_ARGS__)
static inline int tlf_string_data (struct tl_in_state *tlio_in, char *buf, int len) /* {{{ */ {
if (tlf_check (tlio_in, len) < 0) {
return -1;
}
__tlf_raw_data (tlio_in, buf, len);
if (tlf_pad (tlio_in) < 0) {
return -1;
}
return len;
}
/* }}} */
#define tl_fetch_string_data(...) tlf_string_data (tlio_in, ## __VA_ARGS__)
static inline int tlf_skip_string_data (struct tl_in_state *tlio_in, int len) /* {{{ */ {
if (tlf_check (tlio_in, len) < 0) {
return -1;
}
__tlf_skip_raw_data (tlio_in, len);
if (tlf_pad (tlio_in) < 0) {
return -1;
}
return len;
}
/* }}} */
#define tl_fetch_skip_string_data(...) tlf_skip_string_data (tlio_in, ## __VA_ARGS__)
static inline int tlf_string (struct tl_in_state *tlio_in, char *buf, int max_len) /* {{{ */ {
int l = tlf_string_len (tlio_in, max_len);
if (l < 0) {
return -1;
}
if (tlf_string_data (tlio_in, buf, l) < 0) {
return -1;
}
return l;
}
/* }}} */
#define tl_fetch_string(...) tlf_string (tlio_in, ## __VA_ARGS__)
static inline int tlf_skip_string (struct tl_in_state *tlio_in, int max_len) /* {{{ */ {
int l = tlf_string_len (tlio_in, max_len);
if (l < 0) {
return -1;
}
if (tlf_skip_string_data (tlio_in, l) < 0) {
return -1;
}
return l;
}
/* }}} */
#define tl_fetch_skip_string(...) tlf_skip_string (tlio_in, ## __VA_ARGS__)
static inline int tlf_string0 (struct tl_in_state *tlio_in, char *buf, int max_len) /* {{{ */ {
int l = tlf_string_len (tlio_in, max_len);
if (l < 0) {
return -1;
}
if (tlf_string_data (tlio_in, buf, l) < 0) {
return -1;
}
buf[l] = 0;
return l;
}
/* }}} */
#define tl_fetch_string0(...) tlf_string0 (tlio_in, ## __VA_ARGS__)
static inline int tlf_error (struct tl_in_state *tlio_in) /* {{{ */{
return TL_ERROR != 0;
}
/* }}} */
#define tl_fetch_error(...) tlf_error (tlio_in, ## __VA_ARGS__)
static inline int tlf_end (struct tl_in_state *tlio_in) /* {{{ */ {
if (TL_IN_REMAINING && !(TL_IN_CUR_FLAGS & (TL_FETCH_FLAG_ALLOW_DATA_AFTER_QUERY))) {
tlf_set_error_format (tlio_in, TL_ERROR_EXTRA_DATA, "extra %d bytes after query", TL_IN_REMAINING);
return -1;
}
return 1;
}
/* }}} */
#define tl_fetch_end(...) tlf_end (tlio_in, ## __VA_ARGS__)
static inline int tlf_check_str_end (struct tl_in_state *tlio_in, int size) /* {{{ */ {
if (TL_IN_REMAINING != size + ((-size - TL_IN_POS) & 3)) {
tlf_set_error_format (tlio_in, TL_ERROR_EXTRA_DATA, "extra %d bytes after query", TL_IN_REMAINING - size - ((-size - TL_IN_POS) & 3));
return -1;
}
return 1;
}
/* }}} */
#define tl_fetch_check_str_end(...) tlf_check_str_end (tlio_in, ## __VA_ARGS__)
static inline int tlf_unread (struct tl_in_state *tlio_in) /* {{{ */ {
return TL_IN_REMAINING;
}
/* }}} */
#define tl_fetch_unread(...) tlf_unread (tlio_in, ## __VA_ARGS__)
static inline int tlf_skip (struct tl_in_state *tlio_in, int len) /* {{{ */ {
if (tlf_check (tlio_in, len) < 0) {
return -1;
}
__tlf_skip_raw_data (tlio_in, len);
return len;
}
/* }}} */
#define tl_fetch_skip(...) tlf_skip (tlio_in, ## __VA_ARGS__)
/*
static inline int tl_fetch_move (int offset) {
if (tl_fetch_check (offset) < 0) {
return -1;
}
TL_IN_METHODS->fetch_move (offset);
TL_IN_POS += offset;
TL_IN_REMAINING -= offset;
return offset;
}*/
static inline int tls_check (struct tl_out_state *tlio_out, int size) /* {{{ */ {
if (TL_OUT_TYPE == tl_type_none) { return -1; }
if (TL_OUT_REMAINING < size) { return -1; }
return 0;
}
/* }}} */
static inline void __tls_raw_data (struct tl_out_state *tlio_out, const void *buf, int len) /* {{{ */ {
TL_OUT_METHODS->store_raw_data (tlio_out, buf, len);
TL_OUT_POS += len;
TL_OUT_REMAINING -= len;
}
/* }}} */
static inline void *tls_get_ptr (struct tl_out_state *tlio_out, int size) /* {{{ */ {
assert (tls_check (tlio_out, size) >= 0);
if (!size) { return 0; }
assert (size >= 0);
void *x = TL_OUT_METHODS->store_get_ptr (tlio_out, size);
TL_OUT_POS += size;
TL_OUT_REMAINING -= size;
return x;
}
/* }}} */
#define tl_store_get_ptr(...) tls_get_ptr (tlio_out, ## __VA_ARGS__)
static inline void *tls_get_prepend_ptr (struct tl_out_state *tlio_out, int size) /* {{{ */ {
assert (tls_check (tlio_out, size) >= 0);
if (!size) { return 0; }
assert (size >= 0);
void *x = TL_OUT_METHODS->store_get_prepend_ptr (tlio_out, size);
TL_OUT_POS += size;
TL_OUT_REMAINING -= size;
return x;
}
/* }}} */
#define tl_store_get_prepend_ptr(...) tls_get_prepend_ptr (tlio_out, ## __VA_ARGS__)
static inline int tls_int (struct tl_out_state *tlio_out, int x) /* {{{ */ {
assert (tls_check (tlio_out, 4) >= 0);
__tls_raw_data (tlio_out, &x, 4);
return 0;
}
/* }}} */
#define tl_store_int(...) tls_int (tlio_out, ## __VA_ARGS__)
static inline int tls_long (struct tl_out_state *tlio_out, long long x) /* {{{ */ {
assert (tls_check (tlio_out, 8) >= 0);
__tls_raw_data (tlio_out, &x, 8);
return 0;
}
/* }}} */
#define tl_store_long(...) tls_long (tlio_out, ## __VA_ARGS__)
static inline int tls_double (struct tl_out_state *tlio_out, double x) /* {{{ */ {
assert (tls_check (tlio_out, 8) >= 0);
__tls_raw_data (tlio_out, &x, 8);
return 0;
}
/* }}} */
#define tl_store_double(...) tls_double (tlio_out, ## __VA_ARGS__)
static inline int tls_string_len (struct tl_out_state *tlio_out, int len) /* {{{ */ {
assert (tls_check (tlio_out, 4) >= 0);
assert (len >= 0);
if (len < 254) {
__tls_raw_data (tlio_out, &len, 1);
} else {
assert (len < (1 << 24));
int x = (len << 8) + 0xfe;
__tls_raw_data (tlio_out, &x, 4);
}
return 0;
}
/* }}} */
#define tl_store_string_len(...) tls_string_len (tlio_out, ## __VA_ARGS__)
static inline int tls_raw_msg (struct tl_out_state *tlio_out, struct raw_message *raw, int dup) /* {{{ */ {
assert (tls_check (tlio_out, raw->total_bytes) >= 0);
int len = raw->total_bytes;
if (!dup) {
TL_OUT_METHODS->store_raw_msg (tlio_out, raw);
} else {
struct raw_message r;
rwm_clone (&r, raw);
TL_OUT_METHODS->store_raw_msg (tlio_out, &r);
}
TL_OUT_POS += len;
TL_OUT_REMAINING -= len;
return 0;
}
/* }}} */
#define tl_store_raw_msg(...) tls_raw_msg (tlio_out, ## __VA_ARGS__)
static inline int tls_pad (struct tl_out_state *tlio_out) /* {{{ */ {
assert (tls_check (tlio_out, 0) >= 0);
int x = 0;
int pad = (-TL_OUT_POS) & 3;
__tls_raw_data (tlio_out, &x, pad);
return 0;
}
/* }}} */
#define tl_store_pad(...) tls_pad (tlio_out, ## __VA_ARGS__)
static inline int tls_raw_data (struct tl_out_state *tlio_out, const void *s, int len) /* {{{ */ {
//assert (!(len & 3));
assert (tls_check (tlio_out, len) >= 0);
__tls_raw_data (tlio_out, s, len);
return len;
}
/* }}} */
#define tl_store_raw_data(...) tls_raw_data (tlio_out, ## __VA_ARGS__)
static inline int tls_string_data (struct tl_out_state *tlio_out, const char *s, int len) /* {{{ */ {
assert (tls_check (tlio_out, len) >= 0);
__tls_raw_data (tlio_out, s, len);
tls_pad (tlio_out);
return 0;
}
/* }}} */
#define tl_store_string_data(...) tls_string_data (tlio_out, ## __VA_ARGS__)
static inline int tls_string (struct tl_out_state *tlio_out, const char *s, int len) /* {{{ */ {
tls_string_len (tlio_out, len);
tls_string_data (tlio_out, s, len);
return 0;
}
/* }}} */
#define tls_string0(tlio_out,_s) tls_string (tlio_out, _s, strlen (_s))
#define tl_store_string(...) tls_string (tlio_out, ## __VA_ARGS__)
#define tl_store_string0(s) tl_store_string(s, strlen (s))
static inline int tls_clear (struct tl_out_state *tlio_out) /* {{{ */ {
assert (TL_OUT);
TL_OUT_METHODS->store_clear (tlio_out);
TL_OUT = 0;
TL_OUT_TYPE = tl_type_none;
TL_OUT_EXTRA = 0;
return 0;
}
/* }}} */
#define tl_store_clear(...) tls_clear (tlio_out, ## __VA_ARGS__)
static inline int tls_clean (struct tl_out_state *tlio_out) /* {{{ */ {
assert (TL_OUT);
TL_OUT_METHODS->store_read_back (tlio_out, TL_OUT_POS);
TL_OUT_REMAINING += TL_OUT_POS;
TL_OUT_POS = 0;
return 0;
}
/* }}} */
#define tl_store_clean(...) tls_clean (tlio_out, ## __VA_ARGS__)
/*static inline int tl_store_read_back_nondestruct (struct tchar *buf, int size) {
assert (size <= TL_OUT_POS);
TL_OUT_METHODS->store_read_back_nondestruct (buf, size);
return size;
}*/
#define tl_store_end() tl_store_end_ext(RPC_REQ_RESULT)
static inline int tl_copy_through (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out, int len, int advance) /* {{{ */ {
if (TL_IN_TYPE == tl_type_none || TL_OUT_TYPE == tl_type_none) {
return -1;
}
if (tlf_check (tlio_in, len) < 0 || tls_check (tlio_out, len) < 0) {
return -1;
}
tlio_out->out_methods->copy_through[tlio_in->in_type](tlio_in, tlio_out, len, advance);
if (advance) {
TL_IN_POS += len;
TL_IN_REMAINING -= len;
}
TL_OUT_POS += len;
TL_OUT_REMAINING -= len;
return len;
}
/* }}} */
static inline int tlf_int_range (struct tl_in_state *tlio_in, int min, int max) /* {{{ */ {
int x = tlf_int (tlio_in);
if (x < min || x > max) {
tlf_set_error_format (tlio_in, TL_ERROR_VALUE_NOT_IN_RANGE, "Expected int32 in range [%d,%d], %d presented", min, max, x);
}
return x;
}
/* }}} */
#define tl_fetch_int_range(...) tlf_int_range (tlio_in, ## __VA_ARGS__)
static inline int tlf_positive_int (struct tl_in_state *tlio_in) {
return tlf_int_range (tlio_in, 1, 0x7fffffff);
}
#define tl_fetch_positive_int(...) tlf_positive_int (tlio_in, ## __VA_ARGS__)
static inline int tlf_nonnegative_int (struct tl_in_state *tlio_in) {
return tlf_int_range (tlio_in, 0, 0x7fffffff);
}
#define tl_fetch_nonnegative_int(...) tlf_nonnegative_int (tlio_in, ## __VA_ARGS__)
static inline int tlf_int_subset (struct tl_in_state *tlio_in, int set) /* {{{ */ {
int x = tlf_int (tlio_in);
if (x & ~set) {
tlf_set_error_format (tlio_in, TL_ERROR_VALUE_NOT_IN_RANGE, "Expected int32 with only bits 0x%02x allowed, 0x%02x presented", set, x);
}
return x;
}
/* }}} */
#define tl_fetch_int_subset(...) tlf_int_subset (tlio_in, ## __VA_ARGS__)
static inline long long tlf_long_range (struct tl_in_state *tlio_in, long long min, long long max) /* {{{ */ {
long long x = tlf_long (tlio_in);
if (x < min || x > max) {
tlf_set_error_format (tlio_in, TL_ERROR_VALUE_NOT_IN_RANGE, "Expected int64 in range [%lld,%lld], %lld presented", min, max, x);
}
return x;
}
/* }}} */
static inline long long tlf_positive_long (struct tl_in_state *tlio_in) {
return tlf_long_range (tlio_in, 1, 0x7fffffffffffffffll);
}
#define tl_fetch_positive_long(...) tlf_positive_long (tlio_in, ## __VA_ARGS__)
static inline long long tlf_nonnegative_long (struct tl_in_state *tlio_in) {
return tlf_long_range (tlio_in, 0, 0x7fffffffffffffffll);
}
#define tl_fetch_nonnegative_long(...) tlf_nonnegative_long (tlio_in, ## __VA_ARGS__)
static int _tlf_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int len, int advance) {
if (__builtin_expect (tlf_check (tlio_in, len) < 0, 0)) {
return -1;
}
if (advance) {
TL_IN_METHODS->fetch_raw_message (tlio_in, raw, len);
TL_IN_POS += len;
TL_IN_REMAINING -= len;
} else {
TL_IN_METHODS->fetch_lookup_raw_message (tlio_in, raw, len);
}
return 0;
}
static inline int tlf_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int bytes) {
return _tlf_raw_message (tlio_in, raw, bytes, 1);
}
#define tl_fetch_raw_message(...) tlf_raw_message (tlio_in, ## __VA_ARGS__)
static inline int tlf_lookup_raw_message (struct tl_in_state *tlio_in, struct raw_message *raw, int bytes) {
return _tlf_raw_message (tlio_in, raw, bytes, 0);
}
#define tl_fetch_lookup_raw_message(...) tlf_lookup_raw_message (tlio_in, ## __VA_ARGS__)
static inline void tlf_copy_error (struct tl_in_state *tlio_in, struct tl_out_state *tlio_out) {
if (!tlio_out->error) {
if (tlio_in->error) {
tlio_out->error = strdup (tlio_in->error);
tlio_out->errnum = tlio_in->errnum;
}
}
}
#define tl_copy_error(...) tlf_copy_error (tlio_in, tlio_out, ## __VA_ARGS__)
struct tl_in_state *tl_in_state_alloc (void);
void tl_in_state_free (struct tl_in_state *tlio_in);
struct tl_out_state *tl_out_state_alloc (void);
void tl_out_state_free (struct tl_out_state *tlio_out);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Anton Maydell
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Anton Maydell
*/
#include "crypto/aesni256.h"
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "common/cpuid.h"
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
#include <openssl/modes.h>
void AES_ctr128_encrypt(
const unsigned char *in,
unsigned char *out,
size_t length,
const AES_KEY *key,
unsigned char ivec[AES_BLOCK_SIZE],
unsigned char ecount_buf[AES_BLOCK_SIZE],
unsigned int *num) {
CRYPTO_ctr128_encrypt(in, out, length, key, ivec, ecount_buf, num, (block128_f)AES_encrypt);
}
#endif
void tg_ssl_aes_ctr_crypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned long long offset) {
unsigned char iv_copy[16];
memcpy (iv_copy, iv, 16);
unsigned long long *p = (unsigned long long *) (iv_copy + 8);
(*p) += offset >> 4;
union {
unsigned char c[16];
unsigned long long d[2];
} u;
int i = offset & 15, l;
if (i) {
AES_encrypt (iv_copy, u.c, &ctx->u.key);
(*p)++;
l = i + size;
if (l > 16) {
l = 16;
}
size -= l - i;
do {
*out++ = (*in++) ^ u.c[i++];
} while (i < l);
}
const unsigned long long *I = (const unsigned long long *) in;
unsigned long long *O = (unsigned long long *) out;
int n = size >> 4;
while (--n >= 0) {
AES_encrypt (iv_copy, (unsigned char *) u.d, &ctx->u.key);
(*p)++;
*O++ = (*I++) ^ u.d[0];
*O++ = (*I++) ^ u.d[1];
}
l = size & 15;
if (l) {
AES_encrypt (iv_copy, u.c, &ctx->u.key);
in = (const unsigned char *) I;
out = (unsigned char *) O;
i = 0;
do {
*out++ = (*in++) ^ u.c[i++];
} while (i < l);
}
}
static void tg_ssl_aes_cbc_encrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]) {
AES_cbc_encrypt (in, out, size, &ctx->u.key, iv, AES_ENCRYPT);
}
static void tg_ssl_aes_cbc_decrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]) {
AES_cbc_encrypt (in, out, size, &ctx->u.key, iv, AES_DECRYPT);
}
static void tg_ssl_aes_ige_encrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]) {
AES_ige_encrypt (in, out, size, &ctx->u.key, iv, AES_ENCRYPT);
}
static void tg_ssl_aes_ige_decrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]) {
AES_ige_encrypt (in, out, size, &ctx->u.key, iv, AES_DECRYPT);
}
void tg_ssl_aes_ctr128_crypt (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned char ecount_buf[16], unsigned int *num) {
AES_ctr128_encrypt (in, out, size, &ctx->u.key, iv, ecount_buf, num);
}
static const struct tg_aes_methods ssl_aes_encrypt_methods = {
.cbc_crypt = tg_ssl_aes_cbc_encrypt,
.ige_crypt = tg_ssl_aes_ige_encrypt,
.ctr_crypt = tg_ssl_aes_ctr_crypt,
.ctr128_crypt = tg_ssl_aes_ctr128_crypt
};
void tg_aes_set_encrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits) {
AES_set_encrypt_key (key, bits, &ctx->u.key);
ctx->type = &ssl_aes_encrypt_methods;
}
static const struct tg_aes_methods ssl_aes_decrypt_methods = {
.cbc_crypt = tg_ssl_aes_cbc_decrypt,
.ige_crypt = tg_ssl_aes_ige_decrypt,
.ctr_crypt = NULL,
.ctr128_crypt = NULL
};
void tg_aes_set_decrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits) {
AES_set_decrypt_key (key, bits, &ctx->u.key);
ctx->type = &ssl_aes_decrypt_methods;
}
void tg_aes_ctx_cleanup (tg_aes_ctx_t *ctx) {
memset (ctx, 0, sizeof (tg_aes_ctx_t));
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Anton Maydell
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Anton Maydell
*/
#pragma once
#include <openssl/aes.h>
struct aesni256_ctx {
unsigned char a[256];
};
//TODO: move cbc_crypt, ige_crypt, ctr_crypt to the virtual method table
struct tg_aes_ctx;
struct tg_aes_methods {
void (*cbc_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]);
void (*ige_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]);
void (*ctr_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned long long offset);
void (*ctr128_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned char ecount_buf[16], unsigned int *num);
};
typedef struct tg_aes_ctx {
union {
AES_KEY key;
struct aesni256_ctx ctx;
} u;
const struct tg_aes_methods *type;
} tg_aes_ctx_t;
void tg_aes_set_encrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits);
void tg_aes_set_decrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits);
void tg_aes_ctx_cleanup (tg_aes_ctx_t *ctx);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#include <arpa/inet.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include "common/kprintf.h"
#include "common/server-functions.h"
#include "engine/engine.h"
#include "engine/engine-net.h"
#include "net/net-tcp-rpc-client.h"
void default_close_network_sockets (void) /* {{{ */ {
engine_t *E = engine_state;
if (E->sfd > 0) {
close (E->sfd);
E->sfd = -1;
}
}
/* }}} */
int get_port_mod (void) /* {{{ */ {
return -1;
}
/* }}} */
int try_open_port (int port, int quit_on_fail) /* {{{ */ {
engine_t *E = engine_state;
int enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0;
if (engine_check_tcp_enabled ()) {
struct in_addr l;
l.s_addr = htonl(0x7f000001);
E->sfd = server_socket (port, l, engine_get_backlog (), enable_ipv6);
vkprintf (1, "opened tcp socket\n");
if (E->sfd < 0) {
if (quit_on_fail) {
kprintf ("cannot open server socket at port %d: %m\n", port);
exit (1);
} else {
return -1;
}
}
}
return 0;
}
/* }}} */
int try_open_port_range (int start_port, int end_port, int mod_port, int rem_port, int quit_on_fail) /* {{{ */ {
int s = start_port;
for (;start_port <= end_port; start_port ++) {
if (mod_port && rem_port >= 0 && (start_port % mod_port) != (rem_port % mod_port)) { continue; }
if (try_open_port (start_port, 0) >= 0) {
return start_port;
}
}
if (quit_on_fail) {
kprintf ("cannot open server socket at port %d-%d\n", s, end_port);
exit (2);
}
return -1;
}
/* }}} */
void engine_do_open_port (void) /* {{{ */ {
int port_mod = get_port_mod ();
int port = engine_state->port;
int start_port = engine_state->start_port;
int end_port = engine_state->end_port;
if (port > 0 && port < PRIVILEGED_TCP_PORTS) {
assert (try_open_port (port, 1) >= 0);
return;
}
if (port <= 0 && start_port <= end_port && start_port < PRIVILEGED_TCP_PORTS) {
engine_state->port = try_open_port_range (start_port, end_port, 100, port_mod, 1);
assert (engine_state->port >= 0);
return;
}
}
/* }}} */
struct tcp_rpc_server_functions default_engine_tcp_rpc_methods = {
.execute = default_tl_tcp_rpcs_execute,
.check_ready = server_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_check_perm = tcp_rpcs_default_check_perm,
.rpc_init_crypto = tcp_rpcs_init_crypto,
.rpc_close = default_tl_close_conn,
};
void engine_set_tcp_methods (struct tcp_rpc_server_functions *F) {
default_engine_tcp_rpc_methods = *F;
}
void engine_set_http_fallback (conn_type_t *http_type, struct http_server_functions *http_functions) {
default_engine_tcp_rpc_methods.http_fallback_type = http_type;
default_engine_tcp_rpc_methods.http_fallback_extra = http_functions;
}
void engine_server_init (void) {
server_init (&ct_tcp_rpc_server, &default_engine_tcp_rpc_methods);
}
void set_maxconn (int val) {
if (val <= 0) {
val = MAX_CONNECTIONS;
}
engine_state->maxconn = val;
tcp_set_max_connections (val);
}
static int f_parse_option_net (int val) {
switch (val) {
case 'b':
engine_set_backlog (atoi (optarg));
break;
case 'c':
set_maxconn (atoi (optarg));
break;
case 'p':
{
int start_port, end_port;
int x = sscanf (optarg, "%d:%d", &start_port, &end_port);
if (!x) {
usage ();
}
if (x == 1) {
if (start_port <= 0) {
usage ();
}
engine_state->port = start_port;
} else {
if (start_port <= 0 || start_port > end_port) {
usage ();
}
engine_state->start_port = start_port;
engine_state->end_port = end_port;
}
}
break;
case '6':
engine_enable_ipv6 ();
break;
case 200:
engine_set_aes_pwd_file (optarg);
break;
case 214:
engine_disable_tcp ();
break;
case 224:
tcp_set_default_rpc_flags (0xffffffff, RPCF_USE_CRC32C);
break;
case 229:
tcp_set_default_rpc_flags (0xffffffff, RPCF_ALLOW_SKIP_DH);
break;
case 230:
tcp_force_enable_dh ();
break;
case 249:
tcp_set_max_accept_rate (atoi (optarg));
break;
case 250:
tcp_set_max_dh_accept_rate (atoi (optarg));
break;
case 372:
if (net_add_nat_info (optarg) < 0) {
usage ();
exit (2);
}
break;
case 373:
{
engine_t *E = engine_state;
assert (E);
if (inet_pton (AF_INET, optarg, &E->settings_addr) != 1) {
kprintf ("Can not convert '%s' to ip addr: %m\n", optarg);
exit (4);
}
}
break;
default:
return -1;
}
return 0;
}
static void parse_option_net_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) __attribute__ ((format (printf, 6, 7)));
static void parse_option_net_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) {
char *h = NULL;
va_list ap;
va_start (ap, help);
assert (vasprintf (&h, help, ap) >= 0);
va_end (ap);
parse_option_ex (name, arg, var, val, flags, f_parse_option_net, "%s", h);
free (h);
}
void engine_add_net_parse_options (void) {
parse_option_net_builtin ("backlog", required_argument, 0, 'b', LONGOPT_TCP_SET, "sets backlog size");
parse_option_net_builtin ("connections", required_argument, 0, 'c', LONGOPT_TCP_SET, "sets maximal connections number");
parse_option_net_builtin ("port", required_argument, 0, 'p', LONGOPT_NET_SET, "<port> or <sport>:<eport> sets listening port number or port range");
parse_option_net_builtin ("aes-pwd", required_argument, 0, 200, LONGOPT_NET_SET, "sets custom secret.conf file");
parse_option_net_builtin ("ipv6", no_argument, 0, '6', LONGOPT_NET_SET, "enables ipv6 TCP/UDP support");
parse_option_net_builtin ("disable-tcp", no_argument, 0, 214, LONGOPT_TCP_SET, "do not open listening tcp socket");
parse_option_net_builtin ("crc32c", no_argument, 0, 224, LONGOPT_TCP_SET, "Try to use crc32c instead of crc32 in tcp rpc");
parse_option_net_builtin ("allow-skip-dh", no_argument, 0, 229, LONGOPT_TCP_SET, "Allow skipping DH during RPC handshake");
parse_option_net_builtin ("force-dh", no_argument, 0, 230, LONGOPT_TCP_SET, "Force using DH for all outbound RPC connections");
parse_option_net_builtin ("max-accept-rate", required_argument, 0, 249, LONGOPT_TCP_SET, "max number of connections per second that is allowed to accept");
parse_option_net_builtin ("max-dh-accept-rate", required_argument, 0, 250, LONGOPT_TCP_SET, "max number of DH connections per second that is allowed to accept");
parse_option_net_builtin ("nat-info", required_argument, 0, 372, LONGOPT_NET_SET, "<local-addr>:<global-addr>\tsets network address translation for RPC protocol handshake");
parse_option_net_builtin ("address", required_argument, 0, 373, LONGOPT_NET_SET, "tries to bind socket only to specified address");
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#pragma once
void default_close_network_sockets (void);
void engine_do_open_port (void);
int try_open_port_range (int start_port, int end_port, int mod_port, int rem_port, int quit_on_fail);
int try_open_port (int port, int quit_on_fail);
int get_port_mod (void);
void engine_server_init (void);
void engine_set_tcp_methods (struct tcp_rpc_server_functions *F);
void engine_set_http_fallback (conn_type_t *http_type, struct http_server_functions *http_functions);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
//#include "net/net-buffers.h"
#include "net/net-events.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
//#include "net/net-rpc-server.h"
#include "net/net-rpc-targets.h"
#include "net/net-tcp-connections.h"
#include "net/net-tcp-rpc-common.h"
#include "net/net-tcp-rpc-server.h"
#include "common/cpuid.h"
#include "common/crc32.h"
#include "common/kprintf.h"
#include "common/server-functions.h"
#include "vv/vv-io.h"
//#include "TL/constants.h"
#include "engine/engine.h"
#include "engine/engine-rpc-common.h"
#include "common/tl-parse.h"
static int tl_act_nop (job_t job, struct tl_act_extra *extra) {
tls_int (extra->tlio_out, TL_TRUE);
return 0;
}
static int tl_act_stat (job_t job, struct tl_act_extra *extra) {
tl_engine_store_stats (extra->tlio_out);
return 0;
}
static inline struct tl_act_extra *tl_simple_parse_function (struct tl_in_state *tlio_in, int (*act)(job_t job, struct tl_act_extra *data)) {
tl_fetch_int ();
tl_fetch_end ();
if (tl_fetch_error ()) {
return 0;
}
struct tl_act_extra *extra = calloc (sizeof (*extra), 1);
assert (extra);
extra->flags = 3;
extra->start_rdtsc = rdtsc ();
extra->size = sizeof (*extra);
extra->act = act;
extra->type = QUERY_ALLOW_REPLICA_GET | QUERY_ALLOW_REPLICA_SET | QUERY_ALLOW_UNINIT;
return extra;
}
struct tl_act_extra *tl_default_parse_function (struct tl_in_state *tlio_in, long long actor_id) {
if (actor_id) {
return 0;
}
int f = tl_fetch_lookup_int ();
if (tl_fetch_error ()) {
return 0;
}
switch (f) {
case TL_ENGINE_STAT: return tl_simple_parse_function (tlio_in, tl_act_stat);
case TL_ENGINE_NOP: return tl_simple_parse_function (tlio_in, tl_act_nop);
}
return 0;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#pragma once
#define TL_ENGINE_STAT 0xefb3c36b
struct tl_act_extra *tl_default_parse_function (struct tl_in_state *tlio_in, long long actor_id);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#include "engine/engine-rpc.h"
#include "common/tl-parse.h"
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
//#include "net/net-buffers.h"
#include "net/net-events.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
//#include "net/net-rpc-server.h"
#include "net/net-rpc-targets.h"
#include "net/net-tcp-connections.h"
#include "net/net-tcp-rpc-common.h"
#include "net/net-tcp-rpc-server.h"
#include "common/cpuid.h"
#include "common/crc32.h"
#include "common/kprintf.h"
#include "common/server-functions.h"
#include "engine/engine-rpc-common.h"
#include "vv/vv-io.h"
#include "vv/vv-tree.h"
//#include "auto/TL/tl-names.h"
#include "engine/engine.h"
#include "common/common-stats.h"
double tl_aio_timeout;
struct tl_out_state *tl_aio_init_store (enum tl_type type, struct process_id *pid, long long qid) {
if (type == tl_type_raw_msg) {
struct tl_out_state *IO = tl_out_state_alloc ();
tls_init_raw_msg (IO, pid, qid);
return IO;
} else if (type == tl_type_tcp_raw_msg) {
connection_job_t d = rpc_target_choose_connection (rpc_target_lookup (pid), pid);
if (d) {
vkprintf (2, "%s: Good connection #%d for " PID_PRINT_STR "\n", __func__, CONN_INFO(d)->fd, PID_TO_PRINT (pid));
struct tl_out_state *IO = tl_out_state_alloc ();
tls_init_tcp_raw_msg (IO, JOB_REF_PASS (d), qid);
return IO;
} else {
vkprintf (2, "%s: Bad connection " PID_PRINT_STR "\n", __func__, PID_TO_PRINT (pid));
return NULL;
}
} else {
assert (0);
return NULL;
}
}
#define ENGINE_JOB_CLASS JF_CLASS_MAIN
static long long queries_allocated;
long long engine_get_allocated_queries (void) {
return queries_allocated;
}
#define rpc_custom_op_cmp(a,b) (a->op < b->op ? -1 : a->op > b->op ? 1 : 0)
#define X_TYPE struct rpc_custom_op *
#define X_CMP rpc_custom_op_cmp
#define TREE_NAME rpc_custom_op
#define TREE_MALLOC
#include "vv/vv-tree.c"
static struct tree_rpc_custom_op *rpc_custom_op_tree;
void register_custom_op_cb (unsigned op, void (*func)(struct tl_in_state *tlio_in, struct query_work_params *params)) {
struct rpc_custom_op *O = malloc (sizeof (*O));
O->op = op;
O->func = func;
rpc_custom_op_tree = tree_insert_rpc_custom_op (rpc_custom_op_tree, O, lrand48 ());
}
static struct tl_act_extra *(*tl_parse_function)(struct tl_in_state *tlio_in, long long actor_id);
static int (*tl_get_op_function)(struct tl_in_state *tlio_in);
static void (*tl_stat_function)(struct tl_out_state *tlio_out);
int tl_result_new_flags (int old_flags) {
return old_flags & 0xffff;
}
int tl_result_get_header_len (struct tl_query_header *h) {
if (!h->flags) { return 0; }
int s = 8;
return s;
}
int tl_result_make_header (int *ptr, struct tl_query_header *h) {
int *p = ptr;
if (!h->flags) { return 0; }
int new_flags = tl_result_new_flags (h->flags);
*p = RPC_REQ_RESULT_FLAGS;
p++;
*p = new_flags;
p ++;
return (p - ptr) * 4;
}
void tl_default_act_free (struct tl_act_extra *extra) {
if (extra->header) {
tl_query_header_delete (extra->header);
}
if (!(extra->flags & 1)) {
return;
}
free (extra);
}
struct tl_act_extra *tl_default_act_dup (struct tl_act_extra *extra) {
struct tl_act_extra *new = malloc (extra->size);
memcpy (new, extra, extra->size);
new->flags = new->flags | 3;
return new;
}
int need_dup (struct tl_act_extra *extra) {
return !(extra->flags & 1);
}
static tl_query_result_fun_t *tl_query_result_functions = NULL;
void tl_query_result_fun_set (tl_query_result_fun_t func, int query_type_id) {
if (!tl_query_result_functions) {
tl_query_result_functions = calloc (sizeof (void *), 16);
}
tl_query_result_functions[query_type_id] = func;
}
long long tl_generate_next_qid (int query_type_id) {
assert (((unsigned) query_type_id) < 16);
static unsigned int last_qid = 0;
if (!last_qid) {
last_qid = lrand48_j ();
}
return ((unsigned long long) ((query_type_id << 28) + (lrand48_j () & 0x0fffffff)) << 32) | (++last_qid);
}
long long tl_generate_next_qid (int query_type_id);
void engine_work_rpc_req_result (struct tl_in_state *tlio_in, struct query_work_params *params) {
if (!tl_query_result_functions) {
return;
}
struct tl_query_header *h = malloc (sizeof (*h));
if (tlf_query_answer_header (tlio_in, h) < 0) {
tl_query_header_delete (h);
return;
}
h->qw_params = params;
int query_type_id = (((unsigned long long) h->qid) >> 60);
tl_query_result_fun_t fun = tl_query_result_functions[query_type_id];
if (likely (fun != NULL)) {
fun (tlio_in, h);
} else {
vkprintf (1, "Unknown query type %d (qid = 0x%016llx). Skipping query result.\n", query_type_id, h->qid);
}
tl_query_header_delete (h);
}
int __tl_query_act_custom (struct tl_in_state *tlio_in, struct query_work_params *P) {
unsigned op = tl_fetch_lookup_int ();
struct rpc_custom_op *O = tree_lookup_ptr_rpc_custom_op (rpc_custom_op_tree, (void *)&op);
if (O) {
O->func (tlio_in, P);
}
return 0;
}
struct colon_extra {
struct raw_message *left;
char *left_error;
int left_error_code;
struct raw_message *right;
char *right_error;
int right_error_code;
struct raw_message **result;
char **error;
int *error_code;
job_t extra_ref;
};
struct ifeq_extra {
struct raw_message *left;
char *left_error;
int left_error_code;
struct raw_message *right;
char *right_error;
int right_error_code;
struct raw_message *check;
int check_result;
struct raw_message **result;
char **error;
int *error_code;
job_t extra_ref;
job_t right_job;
};
static int process_act_atom_subjob (job_t job, int op, struct job_thread *JT);
/* {{{ auto TL parse functions weak declaration */
struct paramed_type *skip_function_any (struct tl_in_state *tlio_in) __attribute__ ((weak));
struct paramed_type *skip_function_any (struct tl_in_state *tlio_in) { return NULL; }
struct paramed_type *fetch_function_any (struct tl_in_state *tlio_in) __attribute__ ((weak));
struct paramed_type *fetch_function_any (struct tl_in_state *tlio_in) { return NULL; }
int skip_type_any (struct tl_in_state *tlio_in, struct paramed_type *P) __attribute__ ((weak));
int skip_type_any (struct tl_in_state *tlio_in, struct paramed_type *P) { return -1; }
int fetch_type_any (struct tl_in_state *tlio_in, struct paramed_type *P) __attribute__ ((weak));
int fetch_type_any (struct tl_in_state *tlio_in, struct paramed_type *P) { return -1; }
void free_vars_to_be_freed (void) __attribute__ ((weak));
void free_vars_to_be_freed (void) {}
void tl_printf_clear (void) __attribute__ ((weak));
void tl_printf_clear (void) {}
static inline struct paramed_type *do_skip_function_any (struct tl_in_state *tlio_in) {
free_vars_to_be_freed ();
return skip_function_any (tlio_in);
}
static inline struct paramed_type *do_fetch_function_any (struct tl_in_state *tlio_in) {
free_vars_to_be_freed ();
tl_printf_clear ();
return fetch_function_any (tlio_in);
}
static inline int do_fetch_type_any (struct tl_in_state *tlio_in, struct paramed_type *P) {
tl_printf_clear ();
return fetch_type_any (tlio_in, P);
}
void paramed_type_free (struct paramed_type *P) __attribute__ ((weak));
void paramed_type_free (struct paramed_type *P) {}
struct paramed_type *paramed_type_dup (struct paramed_type *P) __attribute__ ((weak));
struct paramed_type *paramed_type_dup (struct paramed_type *P) { return 0; }
/* }}} */
static job_t fetch_query (job_t parent, struct tl_in_state *IO, struct raw_message **raw, char **error, int *error_code, long long actor_id, job_t extra_ref, job_t all_list, int status, struct tl_query_header *h) /* {{{ */ {
int fop = tl_get_op_function (IO);
struct tl_act_extra *extra = tl_default_parse_function (IO, actor_id);
if (!extra && tlf_error (IO)) {
*error = strdup (IO->error);
*error_code = IO->errnum;
return NULL;
}
if (!extra && tl_parse_function) {
extra = tl_parse_function (IO, actor_id);
}
if (!extra) {
tlf_set_error_format (IO, TL_ERROR_UNKNOWN_FUNCTION_ID, "Unknown op 0x%08x", tlf_lookup_int (IO));
*error = strdup (IO->error);
*error_code = IO->errnum;
return NULL;
}
if (!extra->free) {
extra->free = tl_default_act_free;
}
if (!extra->dup) {
extra->dup = tl_default_act_dup;
}
extra->op = fop;
assert (extra->act);
assert (extra->free);
assert (extra->dup);
extra->error = error;
extra->error_code = error_code;
extra->raw = raw;
extra->extra_ref = extra_ref ? job_incref (extra_ref) : 0;
extra = need_dup (extra) ? extra->dup (extra) : extra;
job_t job = create_async_job (process_act_atom_subjob, status | JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ABORT) | JSC_ALLOW (JC_ENGINE, JS_FINISH), extra->subclass, sizeof (void *), 0, JOB_REF_CREATE_PASS_N (parent));
*(void **)job->j_custom = extra;
if (all_list) {
insert_job_into_job_list (all_list, JOB_REF_CREATE_PASS (job), JSP_PARENT_ERROR);
}
queries_allocated ++;
return job;
}
/* }}} */
static int fetch_all_queries (job_t parent, struct tl_in_state *tlio_in) /* {{{ */ {
struct query_work_params *P = (struct query_work_params *) parent->j_custom;
struct tl_query_header *h = P->h;
job_t root = fetch_query (parent, tlio_in, &P->result, &P->error, &P->error_code, h->actor_id, 0, P->all_list, JSP_PARENT_RWE, h);
if (root == (void *)-1l) {
return -2;
} else if (root) {
schedule_job (JOB_REF_PASS (root));
return 0;
} else {
return -1;
}
}
/* }}} */
static int process_act_atom_subjob (job_t job, int op, struct job_thread *JT) /* {{{ */ {
if (op != JS_FINISH) {
if (parent_job_aborted (job)) {
return job_fatal (job, ECANCELED);
}
}
struct tl_act_extra *E = *(void **)job->j_custom;
switch (op) {
case JS_RUN: {
int ok = 1;
if (!ok && !(E->type & (QUERY_ALLOW_REPLICA_GET | QUERY_ALLOW_UNINIT))) {
if (E->raw) {
*E->error = strdup ("not coord anymore");
*E->error_code = TL_ERROR_BINLOG_DISABLED;
E->raw = 0;
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
}
return job_fatal (job, EIO);
} else {
if (!E->raw) {
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
return JOB_COMPLETED;
}
struct tl_out_state *IO = tl_out_state_alloc ();
tls_init_raw_msg_nosend (IO);
E->tlio_out = IO;
long long old_rdtsc = rdtsc ();
int res = E->act (job, E);
E->tlio_out = NULL;
long long rdtsc_delta = rdtsc () - old_rdtsc;
//vv_incr_stat_counter (STAT_QPS_TIME, rdtsc_delta);
//vv_op_stat_insert_rdtsc (E->op, rdtsc_delta);
//if (rdtsc_delta > (int)(0.05 * 2e9)) {
// long_queries_cpu_cnt ++;
//}
E->cpu_rdtsc += rdtsc_delta;
if (res >= 0 && !IO->error) {
//assert (TL_OUT_RAW_MSG);
struct raw_message *raw = malloc (sizeof (*raw));
rwm_clone (raw, (struct raw_message *)IO->out);
tl_out_state_free (IO);
if (E->raw) {
*E->raw = raw;
E->raw = 0;
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
}
return JOB_COMPLETED;
} else if (res == -2 && E->attempt < 5 && !IO->error && job->j_children > 0) {
tl_out_state_free (IO);
E->attempt ++;
return 0;
} else {
if (!IO->error) {
if (res == -2 && E->attempt >= 5) {
tls_set_error_format (IO, TL_ERROR_AIO_MAX_RETRY_EXCEEDED, "Maximum number of retries exceeded");
} else if (res == -2) {
tls_set_error_format (IO, TL_ERROR_BAD_METAFILE, "Error loading metafile");
} else {
tls_set_error_format (IO, TL_ERROR_UNKNOWN, "Unknown error");
}
}
assert (IO->error);
if (E->raw) {
*E->error = strdup (IO->error);
*E->error_code = IO->errnum;
E->raw = 0;
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
}
tl_out_state_free (IO);
return job_fatal (job, EIO);
}
}
assert (0);
}
case JS_ABORT:
if (!job->j_error) {
job->j_error = ECANCELED;
if (E->raw) {
*E->error = strdup ("Job cancelled");
*E->error_code = TL_ERROR_UNKNOWN;
E->raw = 0;
}
}
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
return JOB_COMPLETED;
case JS_FINISH:
queries_allocated --;
if (E->extra_ref) {
job_decref (JOB_REF_PASS (E->extra_ref));
}
E->free (E);
assert (job->j_refcnt == 1);
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
/* }}} */
static int process_query_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
struct query_work_params *P = (struct query_work_params *) job->j_custom;
struct tl_out_state *IO = NULL;
switch (op) {
case JS_RUN:
assert (!job->j_children);
assert (!P->wait_pos);
//assert (!P->wait_time);
if (!P->result && !P->error) {
P->error = strdup ("Unknown error");
P->error_code = TL_ERROR_UNKNOWN;
}
if (!P->answer_sent) {
if (P->fd && P->type == tl_type_raw_msg) {
connection_job_t C = connection_get_by_fd (P->fd);
if (C && CONN_INFO(C)->generation != P->generation) {
job_decref (JOB_REF_PASS (C));
}
if (C) {
IO = tl_out_state_alloc ();
tls_init_tcp_raw_msg (IO, JOB_REF_PASS (C), P->h->qid);
}
}
if (!IO) {
IO = tl_aio_init_store (P->type, &P->pid, P->h->qid);
}
}
if (IO) {
assert (!P->answer_sent);
//long long rdtsc_delta = rdtsc () - P->start_rdtsc;
//if (rdtsc_delta > engine_get_long_query_thres () * 2e9) {
// long_queries_cnt ++;
//}
if (P->error_code) {
tls_set_error_format (IO, P->error_code, "%s", P->error);
free (P->error);
P->error = 0;
} else {
int z = tl_result_get_header_len (P->h);
int *hptr = tls_get_ptr (IO, z);
assert (z == tl_result_make_header (hptr, P->h));
tls_raw_msg (IO, P->result, 0);
free (P->result);
P->result = NULL;
}
tls_end_ext (IO, RPC_REQ_RESULT);
tl_out_state_free (IO);
IO = NULL;
}
P->answer_sent ++;
job_timer_remove (job);
if (P->all_list) {
job_signal (JOB_REF_PASS (P->all_list), JS_ABORT);
}
return JOB_COMPLETED;
case JS_ALARM:
if (!job_timer_check (job)) {
return 0;
}
if (!P->answer_sent) {
IO = tl_aio_init_store (P->type, &P->pid, P->h->qid);
}
if (IO) {
if (P->error_code) {
tls_set_error_format (IO, P->error_code, "%s", P->error);
free (P->error);
P->error = NULL;
} else {
if (P->wait_pos/* || P->wait_time*/) {
tls_set_error_format (IO, TL_ERROR_AIO_TIMEOUT, "Binlog wait error");
} else {
tls_set_error_format (IO, TL_ERROR_AIO_TIMEOUT, "Aio wait error");
}
}
tls_end_ext (IO, RPC_REQ_RESULT);
tl_out_state_free (IO);
P->answer_sent ++;
}
//P->wait_time = job_delete_wait (P->wait_time);
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
if (P->all_list) {
job_signal (JOB_REF_PASS (P->all_list), JS_ABORT);
}
return JOB_COMPLETED;
case JS_ABORT:
//P->wait_time = job_delete_wait (P->wait_time);
if (!P->answer_sent) {
IO = tl_aio_init_store (P->type, &P->pid, P->h->qid);
}
if (IO) {
if (P->error_code) {
tls_set_error_format (IO, P->error_code, "%s", P->error);
free (P->error);
P->error = 0;
} else {
tls_set_error_format (IO, TL_ERROR_UNKNOWN, "Cancelled");
}
tls_end_ext (IO, RPC_REQ_RESULT);
P->answer_sent ++;
tl_out_state_free (IO);
IO = NULL;
}
job_timer_remove (job);
if (P->all_list) {
job_signal (JOB_REF_PASS (P->all_list), JS_ABORT);
}
return JOB_COMPLETED;
case JS_FINISH:
assert (!P->wait_pos);
//assert (!P->wait_time);
assert (!P->all_list);
assert (job->j_refcnt == 1);
if (P->P) {
paramed_type_free (P->P);
P->P = 0;
}
if (P->error) { free (P->error); }
if (P->result) {
rwm_free (P->result);
free (P->result);
}
if (P->src.magic) {
rwm_free (&P->src);
}
tl_query_header_delete (P->h);
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
/* }}} */
static int process_parse_subjob (job_t job, int op, struct job_thread *JT) /* {{{ */ {
struct query_work_params *P = (struct query_work_params *) job->j_custom;
switch (op) {
case JS_RUN: {
job->j_execute = process_query_job;
struct raw_message raw_copy;
rwm_clone (&raw_copy, &P->src);
struct tl_in_state *IO = tl_in_state_alloc ();
tlf_init_raw_message (IO, &P->src, P->src.total_bytes, 0);
int r = fetch_all_queries (job, IO);
tl_in_state_free (IO);
IO = NULL;
rwm_free (&raw_copy);
if (r < 0) {
return JOB_SENDSIG (JS_ABORT);
//return JOB_COMPLETED;
} else {
return 0;
}
}
case JS_ABORT:
case JS_ALARM:
case JS_FINISH:
return process_query_job (job, op, JT);
default:
return JOB_ERROR;
}
}
/* }}} */
static int process_query_custom_subjob (job_t job, int op, struct job_thread *JT) /* {{{ */ {
struct query_work_params *P = (struct query_work_params *) job->j_custom;
if (op == JS_RUN) {
struct tl_in_state *IO = tl_in_state_alloc ();
tlf_init_raw_message (IO, &P->src, P->src.total_bytes, 0);
__tl_query_act_custom (IO, P);
tl_in_state_free (IO);
job_timer_remove (job);
return JOB_COMPLETED;
}
switch (op) {
case JS_ABORT:
job_timer_remove (job);
if (!job->j_error) {
job->j_error = ECANCELED;
}
return JOB_COMPLETED;
case JS_ALARM:
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
return JOB_COMPLETED;
case JS_FINISH:
assert (job->j_refcnt == 1);
if (P->src.magic) {
rwm_free (&P->src);
}
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
/* }}} */
int create_query_job (job_t job, struct raw_message *raw, struct tl_query_header *h, double timeout, struct process_id *remote_pid, enum tl_type out_type, int fd, int generation) /* {{{ */ {
job->j_execute = process_parse_subjob;
struct process_id pd = *remote_pid;
remote_pid = &pd;
struct query_work_params *P = (struct query_work_params *) job->j_custom;
memset (P, 0, sizeof (*P));
P->h = tl_query_header_dup (h);
P->start_rdtsc = rdtsc ();
if (P->wait_coord) {
vkprintf (1, "wait coord query\n");
}
P->fd = fd;
P->generation = generation;
P->pid = *remote_pid;
P->type = out_type;
job_timer_insert (job, precise_now + timeout);
rwm_clone (&P->src, raw);
return JOB_SENDSIG (JS_RUN);
}
/* }}} */
int create_query_custom_job (job_t job, struct raw_message *raw, double timeout, int fd, int generation) /* {{{ */ {
job->j_execute = process_query_custom_subjob;
struct query_info *q = QUERY_INFO (job);
struct process_id p = q->src_pid;
enum tl_type type = q->src_type;
struct query_work_params *P = (struct query_work_params *) job->j_custom;
memset (P, 0, sizeof (*P));
P->pid = p;
P->type = type;
P->fd = fd;
P->generation = generation;
if (timeout > 0) {
job_timer_insert (job, precise_now + timeout);
}
rwm_clone (&P->src, raw);
return JOB_SENDSIG (JS_RUN);
}
/* }}} */
int query_job_run (job_t job, int fd, int generation) /* {{{ */ {
struct query_info *q = QUERY_INFO (job);
struct tl_in_state *IO = tl_in_state_alloc ();
tlf_init_raw_message (IO, &q->raw, q->raw.total_bytes, 0);
int op = tlf_lookup_int (IO);
struct tl_query_header *h = NULL;
int res;
if (op != RPC_INVOKE_REQ) {
if (rpc_custom_op_tree) {
struct raw_message r;
rwm_clone (&r, (struct raw_message *)IO->in);
res = create_query_custom_job (job, &r, 0, fd, generation);
rwm_free (&r);
} else {
res = JOB_COMPLETED;
}
} else {
//vv_incr_stat_counter (STAT_QPS_CNT, 1);
h = malloc (sizeof (*h));
tlf_query_header (IO, h);
if (tlf_error (IO)) {
struct tl_out_state *OUT = tl_aio_init_store (q->src_type, &q->src_pid, h ? h->qid : 0);
if (OUT) {
tls_set_error_format (OUT, IO->errnum, "%s", IO->error);
tls_end_ext (OUT, RPC_REQ_RESULT);
tl_out_state_free (OUT);
}
res = JOB_COMPLETED;
} else {
//tl_aio_init_store (q->src_type, &q->src_pid, h ? h->qid : 0);
struct raw_message r;
rwm_clone (&r, (struct raw_message *)IO->in);
res = create_query_job (job, &r, h, tl_aio_timeout, &q->src_pid, q->src_type, fd, generation);
rwm_free (&r);
}
}
if (h) {
tl_query_header_delete (h);
}
tl_in_state_free (IO);
return res;
}
/* }}} */
static int do_query_job_run (job_t job, int op, struct job_thread *JT) /* {{{ */ {
struct query_info *q = QUERY_INFO (job);
int fd = 0;
int generation = 0;
if (q->conn) {
rpc_target_insert_conn (q->conn);
fd = CONN_INFO((job_t)q->conn)->fd;
generation = CONN_INFO((job_t)q->conn)->generation;
job_decref (JOB_REF_PASS (q->conn));
}
if (op == JS_RUN) {
return query_job_run (job, fd, generation);
}
assert (!job_timer_active (job));
switch (op) {
case JS_ALARM:
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
return JOB_COMPLETED;
case JS_ABORT:
if (!job->j_error) {
job->j_error = ECANCELED;
}
return JOB_COMPLETED;
case JS_FINISH:
if (q->raw.magic) {
rwm_free (&q->raw);
}
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
/* }}} */
int do_create_query_job (struct raw_message *raw, int type, struct process_id *pid, void *conn) /* {{{ */ {
job_t job = create_async_job (do_query_job_run, JSP_PARENT_RWE | JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ABORT) | JSC_ALLOW (JC_ENGINE, JS_ALARM) | JSC_ALLOW (JC_ENGINE, JS_FINISH), -2, sizeof (struct query_work_params), JT_HAVE_TIMER, JOB_REF_NULL);
struct query_info *q = QUERY_INFO (job);
q->raw = *raw;
q->src_type = type;
q->src_pid = *pid;
q->conn = conn;
schedule_job (JOB_REF_PASS (job));
return 0;
}
/* }}} */
/* }}} */
int default_tl_close_conn (connection_job_t c, int who) {
rpc_target_delete_conn (c);
return 0;
}
int default_tl_tcp_rpcs_execute (connection_job_t c, int op, struct raw_message *raw) /* {{{ */ {
CONN_INFO(c)->last_response_time = precise_now;
//rpc_target_insert_conn (c);
if (op == RPC_PONG) {
do_create_query_job (raw, tl_type_tcp_raw_msg, &TCP_RPC_DATA(c)->remote_pid, NULL);
} else {
do_create_query_job (raw, tl_type_tcp_raw_msg, &TCP_RPC_DATA(c)->remote_pid, job_incref (c));
}
return 1;
}
/* }}} */
int tl_store_stats (struct tl_out_state *tlio_out, const char *s, int raw) /* {{{ */ {
int i, key_start = 0, value_start = -1;
if (!raw) {
tl_store_int (TL_STAT);
}
int *cnt_ptr = tl_store_get_ptr (4);
*cnt_ptr = 0;
for (i = 0; s[i]; i++) {
if (s[i] == '\n') {
if (value_start - key_start > 1 && value_start < i) {
tl_store_string (s + key_start, value_start - key_start - 1); /* - 1 (trim tabular) */
tl_store_string (s + value_start, i - value_start);
++*cnt_ptr;
}
key_start = i + 1;
value_start = -1;
} else if (s[i] == '\t') {
value_start = value_start == -1 ? i + 1 : -2;
}
}
return *cnt_ptr;
}
/* }}} */
static void default_stat_function (struct tl_out_state *tlio_out) {
static char buf[(1 << 12)];
prepare_stats (buf, (1 << 12) - 2);
tl_store_stats (tlio_out, buf, 0);
}
void tl_engine_store_stats (struct tl_out_state *tlio_out) {
if (tl_stat_function) {
tl_stat_function (tlio_out);
} else {
default_stat_function (tlio_out);
}
}
void engine_tl_init (struct tl_act_extra *(*parse)(struct tl_in_state *,long long), void (*stat)(), int (get_op)(struct tl_in_state *), double timeout, const char *name) {
tl_parse_function = parse;
tl_stat_function = stat;
tl_aio_timeout = timeout;
tl_get_op_function = get_op;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#pragma once
#include "common/tl-parse.h"
#include "common/precise-time.h"
struct stats_buffer;
struct tl_act_extra;
struct query_work_params {
struct event_timer ev;
enum tl_type type;
struct process_id pid;
struct raw_message src;
struct tl_query_header *h;
struct raw_message *result;
int error_code;
int answer_sent;
int wait_coord;
char *error;
void *wait_pos;
//void *wait_time;
struct paramed_type *P;
long long start_rdtsc;
long long total_work_rdtsc;
job_t all_list;
int fd;
int generation;
};
//extern struct tl_act_extra *(*tl_parse_function)(struct tl_in_state *tlio_in, long long actor_id);
typedef void (*tl_query_result_fun_t)(struct tl_in_state *tlio_in, struct tl_query_header *h);
//extern void (*tl_stat_function)(struct tl_out_state *tlio_out);
//extern int (*tl_get_op_function)(struct tl_in_state *tlio_in);
void tl_query_result_fun_set (tl_query_result_fun_t func, int query_type_id);
long long tl_generate_next_qid (int query_type_id);
int default_tl_rpcs_execute (connection_job_t c, int op, int len);
int default_tl_tcp_rpcs_execute (connection_job_t c, int op, struct raw_message *raw);
int default_tl_close_conn (connection_job_t c, int who);
int tl_store_stats (struct tl_out_state *tlio_out, const char *s, int raw);
extern char *tl_engine_name;
void register_custom_op_cb (unsigned op, void (*func)(struct tl_in_state *tlio_in, struct query_work_params *params));
void engine_work_rpc_req_result (struct tl_in_state *tlio_in, struct query_work_params *params);
void tl_engine_store_stats (struct tl_out_state *tlio_out);
const char *op_to_string (int op);
void tl_restart_all_ready (void);
void tl_default_act_free (struct tl_act_extra *extra);
int engine_check_allow_query (unsigned flags);
int tl_query_act (connection_job_t c, int op, int len);
int tl_query_act_tcp (connection_job_t c, int op, struct raw_message *raw);
struct tl_act_extra {
int size;
int flags;
int attempt;
int type;
int op;
int subclass;
unsigned long long hash;
long long start_rdtsc;
long long cpu_rdtsc;
struct tl_out_state *tlio_out;
int (*act)(job_t, struct tl_act_extra *data);
void (*free)(struct tl_act_extra *data);
struct tl_act_extra *(*dup)(struct tl_act_extra *data);
struct tl_query_header *header;
struct raw_message **raw;
char **error;
job_t extra_ref;
int *error_code;
int extra[0];
};
static inline struct tl_act_extra *tl_act_extra_init (void *buf, int size, int (*act)(job_t, struct tl_act_extra *)) {
struct tl_act_extra *extra = (struct tl_act_extra *)buf;
memset (extra, 0, sizeof (*extra));
extra->size = size + (int)sizeof (*extra);
extra->flags = 0;
extra->act = act;
extra->free = 0;
extra->dup = 0;
extra->start_rdtsc = rdtsc ();
extra->cpu_rdtsc = 0;
return extra;
}
#define QUERY_ALLOW_REPLICA_GET 1
#define QUERY_ALLOW_REPLICA_SET 2
#define QUERY_ALLOW_UNINIT 4
#define TL_PARSE_FUN_EX(tname,fname,dname,qtype,...) \
static struct tl_act_extra *fname (struct tl_in_state *tlio_in, ## __VA_ARGS__) { \
struct tl_act_extra *extra = tl_act_extra_init (stats_buff, sizeof (tname), dname); \
tname *e __attribute__ ((unused)); \
e = (void *)extra->extra; \
extra->type = qtype; \
extra->subclass = -1; \
#define TL_PARSE_FUN(name,...) TL_PARSE_FUN_EX(struct tl_ ## name,tl_ ## name,tl_do_ ## name,__VA_ARGS__)
#define TL_PARSE_FUN_GET(name,...) TL_PARSE_FUN_EX(struct tl_ ## name,tl_ ## name,tl_do_ ## name, QUERY_ALLOW_REPLICA_GET | QUERY_ALLOW_REPLICA_SET, ## __VA_ARGS__)
#define TL_PARSE_FUN_GET_ONLY(name,...) TL_PARSE_FUN_EX(struct tl_ ## name,tl_ ## name,tl_do_ ## name, QUERY_ALLOW_REPLICA_GET, ## __VA_ARGS__)
#define TL_PARSE_FUN_SET(name,...) TL_PARSE_FUN_EX(struct tl_ ## name,tl_ ## name,tl_do_ ## name, QUERY_ALLOW_REPLICA_SET, ## __VA_ARGS__)
#define TL_PARSE_FUN_END \
tl_fetch_end (); \
if (tl_fetch_error ()) { \
return 0; \
} \
return extra; \
}
/* ${engine}-interface-structures.h must contain #pragma pack(push,4) for use TL_DEFAULT_PARSE_FUN macro */
#define TL_DEFAULT_PARSE_FUN(name,qtype) \
TL_PARSE_FUN(name, qtype) \
if (tlf_check (tlio_in, sizeof (*e)) < 0) { tl_fetch_set_error_format (TL_ERROR_NOT_ENOUGH_DATA, "Not enougth data"); return 0; } \
tl_fetch_raw_data (e, sizeof (*e)); \
TL_PARSE_FUN_END
#define TL_DEFAULT_PARSE_FUN_GET_ONLY(name) TL_DEFAULT_PARSE_FUN(name,QUERY_ALLOW_REPLICA_GET)
#define TL_DEFAULT_PARSE_FUN_GET(name) TL_DEFAULT_PARSE_FUN(name,QUERY_ALLOW_REPLICA_GET | QUERY_ALLOW_REPLICA_SET)
#define TL_DEFAULT_PARSE_FUN_SET(name) TL_DEFAULT_PARSE_FUN(name,QUERY_ALLOW_REPLICA_SET)
#define TL_DO_FUN_EX(tname,dname) \
static int dname (job_t this_query_job, struct tl_act_extra *extra) { \
tname *e = (void *)extra->extra; \
struct tl_out_state *tlio_out __attribute__ ((unused)); \
tlio_out = extra->tlio_out; \
#define TL_DO_FUN_DECL_EX(tname,dname) \
static int dname (job_t this_query_job, struct tl_act_extra *extra);
#define TL_DO_FUN(name) TL_DO_FUN_EX(struct tl_ ## name __attribute__ ((unused)), tl_do_ ## name);
#define TL_DO_FUN_DECL(name) TL_DO_FUN_DECL_EX(struct tl_ ## name __attribute__ ((unused)), tl_do_ ## name);
#define TL_DO_PUBLIC_FUN_EX(tname,dname) \
int dname (job_t this_query_job, struct tl_act_extra *extra) { \
tname *e = (void *)extra->extra; \
struct tl_out_state *tlio_out = extra->tlio_out; \
#define TL_DO_PUBLIC_FUN_DECL_EX(tname,dname) \
int dname (job_t this_query_job, struct tl_act_extra *extra);
#define TL_DO_PUBLIC_FUN(name) TL_DO_PUBLIC_FUN_EX(struct tl_ ## name __attribute__ ((unused)), tl_do_ ## name);
#define TL_DO_PUBLIC_FUN_DECL(name) TL_DO_PUBLIC_FUN_DECL_EX(struct tl_ ## name __attribute__ ((unused)), tl_do_ ## name);
#define TL_DO_FUN_END \
return 0; \
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#include <signal.h>
#include <unistd.h>
#include "common/kprintf.h"
#include "common/server-functions.h"
#include "engine/engine.h"
#include "engine/engine-signals.h"
volatile static unsigned long long pending_signals;
void engine_set_terminal_attributes (void) __attribute__ ((weak));
void engine_set_terminal_attributes (void) {}
/* {{{ PENDING SIGNALS */
void signal_set_pending (int sig) {
__sync_fetch_and_or (&pending_signals, SIG2INT(sig));
}
int signal_check_pending (int sig) {
return (pending_signals & SIG2INT(sig)) != 0;
}
int signal_check_pending_and_clear (int sig) {
int res = (pending_signals & SIG2INT(sig)) != 0;
if (res) {
__sync_fetch_and_and (&pending_signals, ~SIG2INT(sig));
}
return res;
}
/* }}} */
void sigint_immediate_handler (const int sig) /* {{{ */ {
static const char message[] = "SIGINT handled immediately.\n";
kwrite (2, message, sizeof (message) - (size_t)1);
engine_set_terminal_attributes ();
_exit (1);
}
/* }}} */
void sigterm_immediate_handler (const int sig) /* {{{ */ {
static const char message[] = "SIGTERM handled immediately.\n";
kwrite (2, message, sizeof (message) - (size_t) 1);
engine_set_terminal_attributes ();
_exit (1);
}
/* }}} */
void sigint_handler (const int sig) /* {{{ */ {
static const char message[] = "SIGINT handled.\n";
kwrite (2, message, sizeof (message) - (size_t) 1);
signal_set_pending (SIGINT);
ksignal (sig, sigint_immediate_handler);
}
/* }}} */
void sigterm_handler (const int sig) /* {{{ */ {
static const char message[] = "SIGTERM handled.\n";
kwrite (2, message, sizeof (message) - (size_t) 1);
signal_set_pending (SIGTERM);
ksignal (sig, sigterm_immediate_handler);
}
/* }}} */
static const char sig_message[] = "received signal ??\n";
void default_signal_handler (const int sig) /* {{{ */ {
char msg[sizeof (sig_message)];
int i;
for (i = 0; i < sizeof (sig_message); i++) {
msg[i] = sig_message[i];
}
msg[sizeof (sig_message) - 4] = '0' + (sig / 10);
msg[sizeof (sig_message) - 3] = '0' + (sig % 10);
kwrite (2, msg, sizeof (sig_message) - (size_t) 1);
signal_set_pending (sig);
}
void quiet_signal_handler (const int sig) {
if (verbosity >= 1) {
char msg[sizeof (sig_message)];
int i;
for (i = 0; i < sizeof (sig_message); i++) {
msg[i] = sig_message[i];
}
msg[sizeof (sig_message) - 4] = '0' + (sig / 10);
msg[sizeof (sig_message) - 3] = '0' + (sig % 10);
kwrite (2, msg, sizeof (sig_message) - (size_t) 1);
}
signal_set_pending (sig);
}
/* }}} */
void empty_signal_handler (const int sig) {}
int interrupt_signal_raised (void) /* {{{ */ {
return (pending_signals & SIG_INTERRUPT_MASK) != 0;
}
/* }}} */
int engine_process_signals (void) /* {{{ */ {
engine_t *E = engine_state;
server_functions_t *F = E->F;
long long allowed = F->allowed_signals;
long long forbidden = 0;
while (1) {
long long t = allowed & pending_signals & ~forbidden;
if (!t) {
break;
}
int i = __builtin_ctzll (t);
if (!i) {
i += 64;
}
assert (F->signal_handlers[i]);
if (signal_check_pending_and_clear (i)) {
F->signal_handlers[i] ();
}
forbidden |= SIG2INT(i);
}
return 1;
}
/* }}} */
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#pragma once
void sigint_immediate_handler (const int sig);
void sigterm_immediate_handler (const int sig);
void sigterm_handler (const int sig);
void sigint_handler (const int sig);
void default_signal_handler (const int sig);
void quiet_signal_handler (const int sig);
void empty_signal_handler (const int sig);
int interrupt_signal_raised (void);
int engine_process_signals (void);
void empty_signal_handler (const int sig);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE 1
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <time.h>
#include <fcntl.h>
#include "common/common-stats.h"
#include "common/kprintf.h"
#include "common/precise-time.h"
#include "common/server-functions.h"
#include "common/tl-parse.h"
#include "engine/engine.h"
#include "engine/engine-net.h"
#include "engine/engine-rpc.h"
#include "engine/engine-signals.h"
#include "jobs/jobs.h"
#include "net/net-connections.h"
#include "net/net-crypto-aes.h"
#include "net/net-msg-buffers.h"
#include "net/net-thread.h"
#include "vv/vv-io.h"
#define DEFAULT_EPOLL_WAIT_TIMEOUT 37
char *local_progname;
double precise_now_diff;
engine_t *engine_state;
unsigned char server_ipv6[16];
void default_cron (void) {
double new_precise_now_diff = get_utime_monotonic () - get_double_time ();
precise_now_diff = precise_now_diff * 0.99 + 0.01 * new_precise_now_diff;
}
static void default_nop (void) {}
static int default_parse_option (int val) {
return -1;
}
/* {{{ SIGNAL ACTIONS */
static void default_sighup (void) {
}
static void default_sigusr1 (void) {
reopen_logs_ext (engine_check_slave_mode_enabled ());
}
static void default_sigrtmax_9 (void) {
}
static void default_sigrtmax_8 (void) {
}
static void default_sigrtmax_4 (void) {
}
static void default_sigrtmax_1 (void) {
}
static void default_sigrtmax (void) {
}
/* }}} */
void set_signals_handlers (void) /* {{{ */ {
ksignal (SIGINT, sigint_immediate_handler);
ksignal (SIGTERM, sigterm_immediate_handler);
set_debug_handlers ();
}
/* }}} */
/* {{{ PIPE TO WAKEUP MAIN THREAD */
static int pipe_read_end;
static int pipe_write_end;
void create_main_thread_pipe (void) {
int p[2];
if (pipe_read_end > 0) {
/* used in copyexec sending results child process */
vkprintf (2, "%s: closing #%d pipe read end file descriptor.\n", __func__, pipe_read_end);
close (pipe_read_end);
}
if (pipe_write_end > 0) {
vkprintf (2, "%s: closing #%d pipe write end file descriptor.\n", __func__, pipe_write_end);
close (pipe_write_end);
}
assert (pipe2 (p, O_NONBLOCK) >= 0);
pipe_read_end = p[0];
pipe_write_end = p[1];
}
void wakeup_main_thread (void) {
if (!pipe_write_end) { return; }
int x = 0;
int r = write (pipe_write_end, &x, 4);
if (r < 0) { assert (errno == EINTR || errno == EAGAIN); }
}
static int epoll_nop (int fd, void *data, event_t *ev) {
int x[100];
while (read (fd, x, 400) == 400) {}
return EVA_CONTINUE;
}
/* }}} */
const char *get_version_string_override (void) __attribute__ ((weak));
const char *get_version_string_override (void) {
return "unknown compiled at " __DATE__ " " __TIME__ " by gcc " __VERSION__;
}
const char *get_version_string (void) {
if (engine_state && engine_state->F && engine_state->F->FullVersionStr) {
return engine_state->F->FullVersionStr;
} else {
return get_version_string_override ();
}
}
void engine_set_epoll_wait_timeout (int epoll_wait_timeout) /* {{{ */ {
assert (1 <= epoll_wait_timeout && epoll_wait_timeout <= 1000);
engine_state->epoll_wait_timeout = epoll_wait_timeout;
}
/* }}} */
static void raise_file_limit (int maxconn) /* {{{ */ {
const int gap = 16;
if (getuid ()) {
struct rlimit rlim;
if (getrlimit (RLIMIT_NOFILE, &rlim) < 0) {
kprintf ("%s: getrlimit (RLIMIT_NOFILE) fail. %m\n", __func__);
exit (1);
}
if (maxconn > rlim.rlim_cur - gap) {
maxconn = rlim.rlim_cur - gap;
}
tcp_set_max_connections (maxconn);
} else {
if (raise_file_rlimit (maxconn + gap) < 0) {
kprintf ("fatal: cannot raise open file limit to %d\n", maxconn + gap);
exit (1);
}
}
}
/* }}} */
/* {{{ engine_init */
void engine_init (const char *const pwd_filename, int do_not_open_port) {
engine_t *E = engine_state;
if (!do_not_open_port) {
engine_do_open_port ();
}
raise_file_limit (E->maxconn);
int aes_load_res = aes_load_pwd_file (pwd_filename);
if (aes_load_res < 0 && (aes_load_res != -0x80000000 || pwd_filename)) {
kprintf ("fatal: cannot load secret definition file `%s'\n", pwd_filename);
exit (1);
}
if (change_user_group (username, groupname) < 0) {
kprintf ("fatal: cannot change user to %s\n", username ? username : "(none)");
exit (1);
}
if (!do_not_open_port && E->port <= 0 && E->start_port <= E->end_port) {
E->port = try_open_port_range (E->start_port, E->end_port, 100, get_port_mod (), 1);
assert (E->port >= 0);
}
unsigned int ipv4 = 0;
if (E->settings_addr.s_addr) {
ipv4 = ntohl (E->settings_addr.s_addr);
if ((ipv4 >> 24) != 10) {
kprintf ("Bad binded IP address " IP_PRINT_STR ", search in ifconfig\n", IP_TO_PRINT (ipv4));
ipv4 = 0;
}
}
init_server_PID (ipv4 ? ipv4 : get_my_ipv4 (), E->port);
get_my_ipv6 (server_ipv6);
init_msg_buffers (0);
init_async_jobs ();
int nc;
nc = engine_get_required_io_threads ();
if (nc <= 0) {
nc = DEFAULT_IO_JOB_THREADS;
}
create_new_job_class (JC_IO, nc, nc);
nc = engine_get_required_cpu_threads ();
if (nc <= 0) {
nc = DEFAULT_CPU_JOB_THREADS;
}
create_new_job_class (JC_CPU, nc, nc);
if (engine_check_multithread_enabled ()) {
int nc;
nc = engine_get_required_tcp_cpu_threads ();
if (nc <= 0) {
nc = 1;
}
create_new_job_class (JC_CONNECTION, nc, nc);
nc = engine_get_required_tcp_io_threads ();
if (nc <= 0) {
nc = 1;
}
create_new_job_class (JC_CONNECTION_IO, nc, nc);
create_new_job_class (JC_ENGINE, 1, 1);
}
create_main_thread_pipe ();
alloc_timer_manager (JC_EPOLL);
notification_event_job_create ();
kprintf ("Started as " PID_PRINT_STR "\n", PID_TO_PRINT (&PID));
}
/* }}} */
void server_init (conn_type_t *listen_connection_type, void *listen_connection_extra) /* {{{ */ {
engine_t *E = engine_state;
server_functions_t *F = E->F;
assert (F && "server functions aren't defined");
init_epoll ();
epoll_sethandler (pipe_read_end, 0, epoll_nop, NULL);
epoll_insert (pipe_read_end, EVT_READ | EVT_LEVEL);
if (daemonize) {
setsid ();
reopen_logs_ext (engine_check_slave_mode_enabled ());
}
if (!E->do_not_open_port) {
if (E->port <= 0) {
kprintf ("fatal: port isn't defined\n");
exit (1);
}
if (E->sfd <= 0) {
assert (try_open_port (E->port, 1) >= 0);
}
if (engine_check_tcp_enabled ()) {
if (!engine_check_ipv6_enabled ()) {
assert (init_listening_connection (E->sfd, listen_connection_type, listen_connection_extra) >= 0);
} else {
assert (init_listening_tcpv6_connection (E->sfd, listen_connection_type, listen_connection_extra, SM_IPV6) >= 0);
}
}
}
ksignal (SIGINT, sigint_handler);
ksignal (SIGTERM, sigterm_handler);
ksignal (SIGPIPE, empty_signal_handler);
ksignal (SIGPOLL, empty_signal_handler);
if (daemonize) {
ksignal (SIGHUP, default_signal_handler);
}
}
/* }}} */
void server_exit (void) /* {{{ */ {
engine_t *E = engine_state;
server_functions_t *F = E->F;
F->close_net_sockets ();
if (signal_check_pending (SIGTERM)) {
kprintf ("Terminated by SIGTERM.\n");
} else if (signal_check_pending (SIGINT)) {
kprintf ("Terminated by SIGINT.\n");
}
}
/* }}} */
/* {{{ precise cron */
struct event_precise_cron precise_cron_events = {
.next = &precise_cron_events,
.prev = &precise_cron_events
};
void precise_cron_function_insert (struct event_precise_cron *ev) {
ev->next = &precise_cron_events;
ev->prev = precise_cron_events.prev;
ev->next->prev = ev->prev->next = ev;
}
void precise_cron_function_remove (struct event_precise_cron *ev) {
ev->next->prev = ev->prev;
ev->prev->next = ev->next;
ev->prev = ev->next = NULL;
}
static void do_precise_cron (void) {
engine_t *E = engine_state;
server_functions_t *F = E->F;
engine_process_signals ();
static int last_cron_time;
if (last_cron_time != now) {
last_cron_time = now;
F->cron ();
}
if (F->precise_cron) {
F->precise_cron ();
}
if (precise_cron_events.next != &precise_cron_events) {
struct event_precise_cron ev = precise_cron_events;
ev.next->prev = &ev;
ev.prev->next = &ev;
precise_cron_events.next = precise_cron_events.prev = &precise_cron_events;
while (ev.next != &ev) {
struct event_precise_cron *e = ev.next;
ev.next->wakeup (ev.next);
if (e == ev.next) {
precise_cron_function_remove (e);
precise_cron_function_insert (e);
}
}
}
free_later_act ();
}
/* }}} */
double update_job_stats_gw (void *ex) {
update_all_thread_stats ();
return 10 + precise_now;
}
struct precise_cron_job_extra {
struct event_timer ev;
};
int precise_cron_job_run (job_t job, int op, struct job_thread *JT) /* {{{ */ {
if (op != JS_RUN && op != JS_ALARM) {
return JOB_ERROR;
}
if (op == JS_ALARM && !job_timer_check (job)) {
return 0;
}
do_precise_cron ();
job_timer_insert (job, precise_now + 0.001 * (1 + drand48_j ()));
return 0;
}
/* }}} */
int terminate_job_run (job_t job, int op, struct job_thread *JT) {
if (op == JS_RUN) {
engine_t *E = engine_state;
server_functions_t *F = E->F;
if (F->on_exit) {
F->on_exit ();
}
server_exit ();
exit (0);
return 0;
}
return JOB_ERROR;
}
void default_engine_server_start (void) /* {{{ */ {
engine_t *E = engine_state;
server_functions_t *F = E->F;
engine_server_init ();
vkprintf (1, "Server started\n");
register_custom_op_cb (RPC_REQ_RESULT, engine_work_rpc_req_result);
if (F->custom_ops) {
struct rpc_custom_op *O = F->custom_ops;
while (O->op) {
register_custom_op_cb (O->op, O->func);
O ++;
}
}
job_t precise_cron_job = create_async_job (precise_cron_job_run, JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ALARM) | JSC_ALLOW (JC_ENGINE, JS_FINISH), F->cron_subclass, sizeof (struct precise_cron_job_extra), JT_HAVE_TIMER, JOB_REF_NULL);
//struct precise_cron_job_extra *e = (void *)precise_cron_job->j_custom;
//memset (e, 0, sizeof (*e)); /* no need, create_async_job memsets itself */
precise_cron_job->j_refcnt ++;
schedule_job (JOB_REF_PASS (precise_cron_job));
job_t update_job_stats = job_timer_alloc (JC_MAIN, update_job_stats_gw, NULL);
job_timer_insert (update_job_stats, 1.0);
F->pre_loop ();
job_t terminate_job = create_async_job (terminate_job_run, JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_FINISH), -1, 0, 0, JOB_REF_NULL);
unlock_job (JOB_REF_CREATE_PASS (terminate_job));
int i;
vkprintf (0, "main loop\n");
for (i = 0; ; i++) {
epoll_work (engine_check_multithread_enabled () ? E->epoll_wait_timeout : 1);
if (interrupt_signal_raised ()) {
if (F->on_waiting_exit) {
while (1) {
useconds_t t = F->on_waiting_exit ();
if (t <= 0) {
break;
}
usleep (t);
run_pending_main_jobs ();
}
}
if (terminate_job) {
job_signal (JOB_REF_PASS (terminate_job), JS_RUN);
run_pending_main_jobs ();
}
break;
}
run_pending_main_jobs ();
}
sleep (120);
kprintf ("Did not exit after 120 seconds\n");
assert (0);
}
/* }}} */
#define DATA_BUF_SIZE (1 << 20)
static char data_buf[DATA_BUF_SIZE + 1];
int engine_prepare_stats (void) {
if (!engine_state) { return 0; }
stats_buffer_t sb;
sb_init (&sb, data_buf, DATA_BUF_SIZE);
if (engine_state->F->prepare_stats) {
engine_state->F->prepare_stats (&sb);
}
return sb.pos;
}
void engine_rpc_stats (struct tl_out_state *tlio_out) {
engine_prepare_stats ();
tl_store_stats (tlio_out, data_buf, 0);
}
void output_engine_stats (void) {
int len = engine_prepare_stats ();
if (len > 0) {
kprintf ("-------------- network/memcache statistics ------------\n");
kwrite (2, data_buf, len);
}
}
int default_get_op (struct tl_in_state *tlio_in) {
return tl_fetch_lookup_int ();
}
void usage ();
void check_signal_handler (server_functions_t *F, int sig, void (*default_f)(void)) {
if (F->allowed_signals & SIG2INT(sig)) {
if (!F->signal_handlers[sig]) {
F->signal_handlers[sig] = default_f;
}
}
}
unsigned long long default_signal_mask = SIG2INT(SIGHUP) | SIG2INT(SIGUSR1) | SIG2INT(OUR_SIGRTMAX) | SIG2INT(OUR_SIGRTMAX-1) | SIG2INT(OUR_SIGRTMAX-4) | SIG2INT(OUR_SIGRTMAX-8) | SIG2INT(OUR_SIGRTMAX-9);
static void check_server_functions (void) /* {{{ */ {
engine_t *E = engine_state;
server_functions_t *F = E->F;
F->allowed_signals = (F->allowed_signals | default_signal_mask) & ~F->forbidden_signals;
check_signal_handler (F, SIGHUP, default_sighup);
check_signal_handler (F, SIGUSR1, default_sigusr1);
check_signal_handler (F, SIGRTMAX-9, default_sigrtmax_9);
check_signal_handler (F, SIGRTMAX-8, default_sigrtmax_8);
check_signal_handler (F, SIGRTMAX-4, default_sigrtmax_4);
check_signal_handler (F, SIGRTMAX-1, default_sigrtmax_1);
check_signal_handler (F, SIGRTMAX, default_sigrtmax);
if (!F->close_net_sockets) { F->close_net_sockets = default_close_network_sockets; }
if (!F->cron) { F->cron = default_cron; }
if (!F->parse_option) { F->parse_option = default_parse_option; }
if (!F->prepare_parse_options) { F->prepare_parse_options = default_nop; }
if (!F->pre_init) { F->pre_init = default_nop; }
if (!F->pre_start) { F->pre_start = default_nop; }
if (!F->parse_extra_args) { F->parse_extra_args = default_parse_extra_args; }
if (!F->pre_loop) { F->pre_loop = default_nop; }
if (!F->epoll_timeout) { F->epoll_timeout = 1; }
if (!F->aio_timeout) { F->aio_timeout = 0.5; }
if (!F->get_op) { F->get_op = default_get_op; }
int i;
for (i = 1; i <= 64; i++) {
if (F->allowed_signals & SIG2INT (i)) {
//fix log spamming hack for image-engine:
ksignal (i, i == SIGCHLD ? quiet_signal_handler : default_signal_handler);
}
}
}
/* }}} */
void engine_startup (engine_t *E, server_functions_t *F) /* {{{ */ {
E->F = F;
E->modules = (ENGINE_DEFAULT_ENABLED_MODULES | F->default_modules) & ~F->default_modules_disabled;
engine_set_backlog (DEFAULT_BACKLOG);
tcp_set_default_rpc_flags (0xffffffff, RPCF_USE_CRC32C);
E->port = -1;
precise_now_diff = get_utime_monotonic () - get_double_time ();
assert (SIGRTMAX == OUR_SIGRTMAX);
assert (SIGRTMAX - SIGRTMIN >= 20);
E->sfd = 0;
E->epoll_wait_timeout = DEFAULT_EPOLL_WAIT_TIMEOUT;
E->maxconn = MAX_CONNECTIONS;
check_server_functions ();
}
/* }}} */
int default_main (server_functions_t *F, int argc, char *argv[]) {
set_signals_handlers ();
engine_t *E = calloc (sizeof (*E), 1);
engine_state = E;
engine_startup (E, F);
engine_set_epoll_wait_timeout (F->epoll_timeout);
if (F->tcp_methods) {
engine_set_tcp_methods (F->tcp_methods);
}
if (F->http_functions) {
conn_type_t *H = F->http_type;
if (!H) {
H = &ct_http_server;
}
assert (check_conn_functions (H, 1) >= 0);
engine_set_http_fallback (H, F->http_functions);
}
kprintf ("Invoking engine %s\n", F->FullVersionStr);
progname = argv[0];
local_progname = argv[0];
add_builtin_parse_options ();
F->prepare_parse_options ();
parse_engine_options_long (argc, argv);
F->parse_extra_args (argc - optind, argv + optind);
E->do_not_open_port = (F->flags & ENGINE_NO_PORT);
F->pre_init ();
engine_init (engine_get_aes_pwd_file (), E->do_not_open_port);
vkprintf (3, "Command line parsed\n");
F->pre_start ();
start_time = time (NULL);
if (F->run_script) {
int r = F->run_script ();
if (r >= 0) {
return 0;
} else {
return -r;
}
}
engine_tl_init (F->parse_function, engine_rpc_stats, F->get_op, F->aio_timeout, F->ShortVersionStr);
init_epoll ();
default_engine_server_start ();
return 0;
}
static int f_parse_option_engine (int val) {
switch (val) {
case 227:
engine_set_required_cpu_threads (atoi (optarg));
break;
case 228:
engine_set_required_io_threads (atoi (optarg));
break;
case 258:
if (optarg && atoi (optarg) == 0) {
engine_disable_multithread ();
} else {
engine_enable_multithread ();
epoll_sleep_ns = 10000;
}
break;
case 301:
engine_set_required_tcp_cpu_threads (atoi (optarg));
break;
case 302:
engine_set_required_tcp_io_threads (atoi (optarg));
break;
default:
return -1;
}
return 0;
}
static void parse_option_engine_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) __attribute__ ((format (printf, 6, 7)));
static void parse_option_engine_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) {
char *h;
va_list ap;
va_start (ap, help);
assert (vasprintf (&h, help, ap) >= 0);
va_end (ap);
parse_option_ex (name, arg, var, val, flags, f_parse_option_engine, h);
free (h);
}
void engine_add_engine_parse_options (void) {
parse_option_engine_builtin ("cpu-threads", required_argument, 0, 227, LONGOPT_JOBS_SET, "Number of CPU threads (1-64, default 8)");
parse_option_engine_builtin ("io-threads", required_argument, 0, 228, LONGOPT_JOBS_SET, "Number of I/O threads (1-64, default 16)");
parse_option_engine_builtin ("multithread", optional_argument, 0, 258, LONGOPT_JOBS_SET, "run in multithread mode");
parse_option_engine_builtin ("tcp-cpu-threads", required_argument, 0, 301, LONGOPT_JOBS_SET, "number of tcp-cpu threads");
parse_option_engine_builtin ("tcp-iothreads", required_argument, 0, 302, LONGOPT_JOBS_SET, "number of tcp-io threads");
}
void default_parse_extra_args (int argc, char *argv[]) /* {{{ */ {
if (argc != 0) {
vkprintf (0, "Extra args\n");
usage ();
}
}
/*}}}*/
int default_parse_option_func (int a) {
if (engine_state) {
server_functions_t *F = engine_state->F;
if (F->parse_option) {
return F->parse_option (a);
} else {
return -1;
}
} else {
return -1;
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
2013 Anton Maydell
Copyright 2014 Telegram Messenger Inc
2014 Vitaly Valtman
2014 Anton Maydell
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaliy Valtman
*/
#pragma once
// GLIBC DEFINES RTMAX as function
// engine_init () asserts, that OUT_SIGRTMAX == SIGRTMAX
#define OUR_SIGRTMAX 64
#include "common/common-stats.h"
#include "engine/engine-rpc.h"
#include "common/tl-parse.h"
#include "net/net-connections.h"
#include "net/net-tcp-rpc-server.h"
#include "net/net-http-server.h"
#define DEFAULT_LONG_QUERY_THRES 0.1
#define SIG2INT(sig) (((sig) == 64) ? 1ull : (1ull << (unsigned long long)(sig)))
#define SIG_INTERRUPT_MASK (SIG2INT(SIGTERM) | SIG2INT(SIGINT))
extern double precise_now_diff;
#pragma pack(push,4)
struct rpc_custom_op {
unsigned op;
void (*func)(struct tl_in_state *tlio_in, struct query_work_params *params);
};
#pragma pack(pop)
#define ENGINE_NO_AUTO_APPEND 2
#define ENGINE_NO_PORT 4
#define ENGINE_ENABLE_IPV6 0x4ull
#define ENGINE_ENABLE_TCP 0x10ull
#define ENGINE_ENABLE_MULTITHREAD 0x1000000ull
#define ENGINE_ENABLE_SLAVE_MODE 0x2000000ull
#define ENGINE_DEFAULT_ENABLED_MODULES (ENGINE_ENABLE_TCP)
typedef struct {
void (*cron) (void);
void (*precise_cron) (void);
void (*on_exit) (void);
int (*on_waiting_exit) (void); //returns 0 -> stop wait and exit, X > 0 wait X microsenconds */
void (*on_safe_quit) (void);
void (*close_net_sockets) (void);
unsigned long long flags;
unsigned long long allowed_signals;
unsigned long long forbidden_signals;
unsigned long long default_modules;
unsigned long long default_modules_disabled;
void (*prepare_stats)(stats_buffer_t *sb);
void (*prepare_parse_options)(void);
int (*parse_option)(int val);
void (*parse_extra_args)(int count, char *args[]);
void (*pre_init)(void);
void (*pre_start)(void);
void (*pre_loop)(void);
int (*run_script)(void);
const char *FullVersionStr;
const char *ShortVersionStr;
int epoll_timeout;
double aio_timeout;
struct tl_act_extra *(*parse_function) (struct tl_in_state *tlio_in, long long actor_id);
int (*get_op)(struct tl_in_state *tlio_in);
void (*signal_handlers[65])(void);
struct rpc_custom_op *custom_ops;
struct tcp_rpc_server_functions *tcp_methods;
conn_type_t *http_type;
struct http_server_functions *http_functions;
int cron_subclass;
int precise_cron_subclass;
} server_functions_t;
typedef struct {
struct in_addr settings_addr;
int do_not_open_port;
int epoll_wait_timeout;
int sfd;
unsigned long long modules;
int port;
int start_port, end_port;
int backlog;
int maxconn;
int required_io_threads;
int required_cpu_threads;
int required_tcp_cpu_threads;
int required_tcp_io_threads;
char *aes_pwd_file;
server_functions_t *F;
} engine_t;
typedef struct event_precise_cron {
struct event_precise_cron *next, *prev;
void (*wakeup)(struct event_precise_cron *arg);
} event_precise_cron_t;
void precise_cron_function_insert (struct event_precise_cron *ev);
void precise_cron_function_remove (struct event_precise_cron *ev);
void set_signals_handlers (void);
void engine_init (const char *const pwd_filename, int do_not_open_port);
void engine_set_epoll_wait_timeout (int epoll_wait_timeout);
int signal_check_pending_and_clear (int sig);
int signal_check_pending (int sig);
void signal_set_pending (int sig);
#define ENGINE_FLAG_PARAM(name,flag) \
static inline void engine_enable_ ## name (void) { engine_state->modules |= ENGINE_ENABLE_ ## flag; } \
static inline void engine_disable_ ## name (void) { engine_state->modules &= ~ENGINE_ENABLE_ ## flag; } \
static inline int engine_check_ ## name ## _enabled (void) { return (engine_state->modules & ENGINE_ENABLE_ ## flag) != 0; } \
static inline int engine_check_ ## name ## _disabled (void) { return (engine_state->modules & ENGINE_ENABLE_ ## flag) == 0; } \
#define ENGINE_STR_PARAM(name,field) \
static inline void engine_set_ ## name (const char *s) { \
if (engine_state->field) { \
free (engine_state->field); \
} \
engine_state->field = s ? strdup (s) : NULL; \
} \
static inline const char *engine_get_ ## name (void) { \
return engine_state->field; \
} \
#define ENGINE_T_PARAM(type,name,field) \
static inline void engine_set_ ## name (type s) { \
engine_state->field = s;\
} \
static inline type engine_get_ ## name (void) { \
return engine_state->field; \
} \
#define ENGINE_INT_PARAM(name,field) ENGINE_T_PARAM(int,name,field)
#define ENGINE_DOUBLE_PARAM(name,field) ENGINE_T_PARAM(double,name,field)
#define ENGINE_LONG_PARAM(name,field) ENGINE_T_PARAM(long long,name,field)
extern engine_t *engine_state;
ENGINE_FLAG_PARAM(ipv6,IPV6)
ENGINE_FLAG_PARAM(tcp,TCP)
ENGINE_FLAG_PARAM(multithread,MULTITHREAD)
ENGINE_FLAG_PARAM(slave_mode,SLAVE_MODE)
ENGINE_STR_PARAM(aes_pwd_file,aes_pwd_file)
ENGINE_INT_PARAM(backlog,backlog);
ENGINE_INT_PARAM(required_io_threads,required_io_threads);
ENGINE_INT_PARAM(required_cpu_threads,required_cpu_threads);
ENGINE_INT_PARAM(required_tcp_cpu_threads,required_tcp_cpu_threads);
ENGINE_INT_PARAM(required_tcp_io_threads,required_tcp_io_threads);
void reopen_logs (void);
int default_main (server_functions_t *F, int argc, char *argv[]);
void default_parse_extra_args (int argc, char *argv[]);
void engine_tl_init (struct tl_act_extra *(*parse)(struct tl_in_state *,long long), void (*stat)(), int (get_op)(struct tl_in_state *), double timeout, const char *name);
void server_init (conn_type_t *listen_connection_type, void *listen_connection_extra);
void usage (void);
extern engine_t *engine_state;
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2015 Telegram Messenger Inc
2014-2015 Nikolai Durov
2014 Andrey Lopatin
*/
#define _FILE_OFFSET_BITS 64
#define _XOPEN_SOURCE 500
#define _GNU_SOURCE 1
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/syscall.h>
#include <math.h>
#include <linux/futex.h>
#include "common/proc-stat.h"
#include "crc32.h"
#include "net/net-events.h"
//#include "net/net-buffers.h"
#include "server-functions.h"
#include "kprintf.h"
#include "precise-time.h"
#include "mp-queue.h"
#include "net/net-connections.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
//#include "auto/engine/engine.h"
#define JOB_SUBCLASS_OFFSET 3
struct job_thread JobThreads[MAX_JOB_THREADS] __attribute__((aligned(128)));
struct job_thread_stat {
unsigned long tot_sys;
unsigned long tot_user;
unsigned long recent_sys;
unsigned long recent_user;
};
struct job_thread_stat JobThreadsStats[MAX_JOB_THREADS] __attribute__((aligned(128)));
#define MODULE jobs
MODULE_STAT_TYPE {
double tot_idle_time, a_idle_time, a_idle_quotient;
long long jobs_allocated_memory;
int jobs_ran;
int job_timers_allocated;
double locked_since;
long long timer_ops;
long long timer_ops_scheduler;
};
MODULE_INIT
MODULE_STAT_FUNCTION
int uptime = time (0) - start_time;
double tm = get_utime_monotonic ();
double tot_recent_idle[16];
double tot_recent_q[16];
double tot_idle[16];
int tot_threads[16];
memset (tot_recent_idle, 0, sizeof (tot_recent_idle));
memset (tot_recent_q, 0, sizeof (tot_recent_q));
memset (tot_idle, 0, sizeof (tot_idle));
memset (tot_threads, 0, sizeof (tot_threads));
tot_recent_idle[JC_MAIN] = a_idle_time;
tot_recent_q[JC_MAIN] = a_idle_quotient;
tot_idle[JC_MAIN] = tot_idle_time;
int i,j;
for (i = 0; i < max_job_thread_id + 1; i++) {
if (MODULE_STAT_ARR[i]) {
assert (JobThreads[i].id == i);
int class = JobThreads[i].thread_class & JC_MASK;
tot_recent_idle[class] += MODULE_STAT_ARR[i]->a_idle_time;
tot_recent_q[class] += MODULE_STAT_ARR[i]->a_idle_quotient;
tot_idle[class] += MODULE_STAT_ARR[i]->tot_idle_time;
if (MODULE_STAT_ARR[i]->locked_since) {
double lt = MODULE_STAT_ARR[i]->locked_since;
tot_recent_idle[class] += (tm - lt);
tot_recent_q[class] += (tm - lt);
tot_idle[class] += (tm - lt);
}
tot_threads[class] ++;
}
}
sb_printf (sb, "thread_average_idle_percent\t");
for (i = 0; i < 16; i++) {
if (i != 0) {
sb_printf (sb, " ");
if (!(i & 3)) {
sb_printf (sb, " ");
}
}
sb_printf (sb, "%.3f", safe_div (tot_idle[i], uptime * tot_threads[i]) * 100);
}
sb_printf (sb, "\n");
sb_printf (sb, "thread_recent_idle_percent\t");
for (i = 0; i < 16; i++) {
if (i != 0) {
sb_printf (sb, " ");
if (!(i & 3)) {
sb_printf (sb, " ");
}
}
sb_printf (sb, "%.3f", safe_div (tot_recent_idle[i], tot_recent_q[i]) * 100);
}
sb_printf (sb, "\n");
sb_printf (sb, "tot_threads\t");
for (i = 0; i < 16; i++) {
if (i != 0) {
sb_printf (sb, " ");
if (!(i & 3)) {
sb_printf (sb, " ");
}
}
sb_printf (sb, "%d", tot_threads[i]);
}
sb_printf (sb, "\n");
double jb_cpu_load_u[16];
double jb_cpu_load_s[16];
double jb_cpu_load_t[16];
double jb_cpu_load_ru[16];
double jb_cpu_load_rs[16];
double jb_cpu_load_rt[16];
memset (jb_cpu_load_u, 0, sizeof (jb_cpu_load_u));
memset (jb_cpu_load_s, 0, sizeof (jb_cpu_load_u));
memset (jb_cpu_load_t, 0, sizeof (jb_cpu_load_u));
memset (jb_cpu_load_ru, 0, sizeof (jb_cpu_load_u));
memset (jb_cpu_load_rs, 0, sizeof (jb_cpu_load_u));
memset (jb_cpu_load_rt, 0, sizeof (jb_cpu_load_u));
double tot_cpu_load_u = 0;
double tot_cpu_load_s = 0;
double tot_cpu_load_t = 0;
double tot_cpu_load_ru = 0;
double tot_cpu_load_rs = 0;
double tot_cpu_load_rt = 0;
double max_cpu_load_u = 0;
double max_cpu_load_s = 0;
double max_cpu_load_t = 0;
double max_cpu_load_ru = 0;
double max_cpu_load_rs = 0;
double max_cpu_load_rt = 0;
for (i = 0; i < max_job_thread_id + 1; i++) {
if (MODULE_STAT_ARR[i]) {
assert (JobThreads[i].id == i);
int class = JobThreads[i].thread_class & JC_MASK;
jb_cpu_load_u[class] += JobThreadsStats[i].tot_user;
jb_cpu_load_s[class] += JobThreadsStats[i].tot_sys;
jb_cpu_load_t[class] += JobThreadsStats[i].tot_user + JobThreadsStats[i].tot_sys;
jb_cpu_load_ru[class] += JobThreadsStats[i].recent_user;
jb_cpu_load_rs[class] += JobThreadsStats[i].recent_sys;
jb_cpu_load_rt[class] += JobThreadsStats[i].recent_user + JobThreadsStats[i].recent_sys;
}
}
for (i = 0; i < 16; i++) {
tot_cpu_load_u += jb_cpu_load_u[i];
tot_cpu_load_s += jb_cpu_load_s[i];
tot_cpu_load_t += jb_cpu_load_t[i];
tot_cpu_load_ru += jb_cpu_load_ru[i];
tot_cpu_load_rs += jb_cpu_load_rs[i];
tot_cpu_load_rt += jb_cpu_load_rt[i];
#define max(a,b) (a) > (b) ? (a) : (b)
max_cpu_load_u = max (max_cpu_load_u, jb_cpu_load_u[i]);
max_cpu_load_s = max (max_cpu_load_s, jb_cpu_load_s[i]);
max_cpu_load_t = max (max_cpu_load_t, jb_cpu_load_t[i]);
max_cpu_load_ru = max (max_cpu_load_ru, jb_cpu_load_ru[i]);
max_cpu_load_rs = max (max_cpu_load_rs, jb_cpu_load_rs[i]);
max_cpu_load_rt = max (max_cpu_load_rt, jb_cpu_load_rt[i]);
#undef max
}
const double m_clk_to_hs = 100.0 / sysconf (_SC_CLK_TCK); /* hundredth of a second */
const double m_clk_to_ts = 0.1 * m_clk_to_hs; /* tenth of a second */
for (j = 0; j < 6; j++) {
double *b = NULL;
double d = 0;
switch (j) {
case 0:
sb_printf (sb, "thread_load_average_user\t");
b = jb_cpu_load_u;
d = uptime;
break;
case 1:
sb_printf (sb, "thread_load_average_sys\t");
b = jb_cpu_load_s;
d = uptime;
break;
case 2:
sb_printf (sb, "thread_load_average\t");
b = jb_cpu_load_t;
d = uptime;
break;
case 3:
sb_printf (sb, "thread_load_recent_user\t");
b = jb_cpu_load_ru;
d = 10;
break;
case 4:
sb_printf (sb, "thread_load_recent_sys\t");
b = jb_cpu_load_rs;
d = 10;
break;
case 5:
sb_printf (sb, "thread_load_recent\t");
b = jb_cpu_load_rt;
d = 10;
break;
default:
assert (0);
}
for (i = 0; i < 16; i++) {
if (i != 0) {
sb_printf (sb, " ");
if (!(i & 3)) {
sb_printf (sb, " ");
}
}
sb_printf (sb, "%.3f", safe_div (m_clk_to_hs * b[i], d * tot_threads[i]));
}
sb_printf (sb, "\n");
}
sb_printf (sb,
"load_average_user\t%.3f\n"
"load_average_sys\t%.3f\n"
"load_average_total\t%.3f\n"
"load_recent_user\t%.3f\n"
"load_recent_sys\t%.3f\n"
"load_recent_total\t%.3f\n"
"max_average_user\t%.3f\n"
"max_average_sys\t%.3f\n"
"max_average_total\t%.3f\n"
"max_recent_user\t%.3f\n"
"max_recent_sys\t%.3f\n"
"max_recent_total\t%.3f\n",
safe_div (m_clk_to_hs * tot_cpu_load_u, uptime),
safe_div (m_clk_to_hs * tot_cpu_load_s, uptime),
safe_div (m_clk_to_hs * tot_cpu_load_t, uptime),
m_clk_to_ts * tot_cpu_load_ru,
m_clk_to_ts * tot_cpu_load_rs,
m_clk_to_ts * tot_cpu_load_rt,
safe_div (m_clk_to_hs * max_cpu_load_u, uptime),
safe_div (m_clk_to_hs * max_cpu_load_s, uptime),
safe_div (m_clk_to_hs * max_cpu_load_t, uptime),
m_clk_to_ts * max_cpu_load_ru,
m_clk_to_ts * max_cpu_load_rs,
m_clk_to_ts * max_cpu_load_rt
);
SB_SUM_ONE_I (job_timers_allocated);
int jb_running[16], jb_active = 0;
long long jb_created = 0;
memset (jb_running, 0, sizeof (jb_running));
for (i = 1; i <= max_job_thread_id; i++) {
struct job_thread *JT = &JobThreads[i];
if (JT->status) {
jb_active += JT->jobs_active;
jb_created += JT->jobs_created;
for (j = 0; j <= JC_MAX; j++) {
jb_running[j] += JT->jobs_running[j];
}
}
}
sb_printf (sb,
"jobs_created\t%lld\n"
"jobs_active\t%d\n",
jb_created,
jb_active
);
sb_printf (sb, "jobs_running\t");
for (i = 0; i < 16; i++) {
if (i != 0) {
sb_printf (sb, " ");
if (!(i & 3)) {
sb_printf (sb, " ");
}
}
sb_printf (sb, "%d", jb_running[i]);
}
sb_printf (sb, "\n");
SB_SUM_ONE_LL (jobs_allocated_memory);
SB_SUM_ONE_LL (timer_ops);
SB_SUM_ONE_LL (timer_ops_scheduler);
MODULE_STAT_FUNCTION_END
long long jobs_get_allocated_memoty (void) {
return SB_SUM_LL (jobs_allocated_memory);
}
void update_thread_stat (int pid, int tid, int id) {
struct proc_stats s;
if (!tid) { tid = pid; }
read_proc_stats (pid, tid, &s);
struct job_thread_stat *S = &JobThreadsStats[id];
S->recent_sys = (s.stime - S->tot_sys);
S->recent_user = (s.utime - S->tot_user);
S->tot_sys = s.stime;
S->tot_user = s.utime;
}
void update_all_thread_stats (void) {
int i;
pid_t pid = getpid ();
for (i = 1; i <= max_job_thread_id; i++) {
update_thread_stat (pid, JobThreads[i].thread_system_id, i);
}
}
void wakeup_main_thread (void) __attribute__ ((weak));
void wakeup_main_thread (void) {}
#define JOB_THREAD_STACK_SIZE (4 << 20)
#define JTS_CREATED 1
#define JTS_RUNNING 2
#define JTS_PERFORMING 4
struct job_class JobClasses[JC_MAX + 1];
int max_job_thread_id;
int cur_job_threads;
int main_pthread_id_initialized;
pthread_t main_pthread_id;
struct job_thread *main_job_thread;
__thread struct job_thread *this_job_thread;
__thread job_t this_job;
long int lrand48_j (void) {
if (this_job_thread) {
long int t;
lrand48_r (&this_job_thread->rand_data, &t);
return t;
} else {
return lrand48 ();
}
}
long int mrand48_j (void) {
if (this_job_thread) {
long int t;
mrand48_r (&this_job_thread->rand_data, &t);
return t;
} else {
return mrand48 ();
}
}
double drand48_j (void) {
if (this_job_thread) {
double t;
drand48_r (&this_job_thread->rand_data, &t);
return t;
} else {
return drand48 ();
}
}
struct mp_queue MainJobQueue __attribute__((aligned(128)));
static struct thread_callback *jobs_cb_list;
void init_main_pthread_id (void) {
pthread_t self = pthread_self ();
if (main_pthread_id_initialized) {
assert (pthread_equal (main_pthread_id, self));
} else {
main_pthread_id = self;
main_pthread_id_initialized = 1;
}
}
void check_main_thread (void) {
pthread_t self = pthread_self ();
assert (main_pthread_id_initialized && pthread_equal (main_pthread_id, self));
}
static void set_job_interrupt_signal_handler (void);
void *job_thread (void *arg);
void *job_thread_sub (void *arg);
int create_job_thread_ex (int thread_class, void *(*thread_work)(void *)) {
assert (!(thread_class & ~JC_MASK));
assert (thread_class);
assert ((thread_class != JC_MAIN) ^ !cur_job_threads);
if (cur_job_threads >= MAX_JOB_THREADS) {
return -1;
}
check_main_thread ();
struct job_class *JC = &JobClasses[thread_class];
if (thread_class != JC_MAIN && JC->job_queue == &MainJobQueue) {
assert (main_job_thread);
JC->job_queue = alloc_mp_queue_w ();
main_job_thread->job_class_mask &= ~(1 << thread_class);
/*if (max_job_class_threads[thread_class] == 1) {
run_pending_main_jobs ();
}*/
}
assert (JC->job_queue);;
int i;
struct job_thread *JT = 0;
for (i = 1; i < MAX_JOB_THREADS; i++) {
if (!JobThreads[i].status && !JobThreads[i].pthread_id) {
JT = &JobThreads[i];
break;
}
}
if (!JT) {
return -1;
}
memset (JT, 0, sizeof (struct job_thread));
JT->status = JTS_CREATED;
JT->thread_class = thread_class;
JT->job_class_mask = 1 | (thread_class == JC_MAIN ? 0xffff : (1 << thread_class));
JT->job_queue = JC->job_queue;
JT->job_class = JC;
JT->id = i;
assert (JT->job_queue);
srand48_r (rdtsc () ^ lrand48 (), &JT->rand_data);
if (thread_class != JC_MAIN) {
pthread_attr_t attr;
pthread_attr_init (&attr);
pthread_attr_setstacksize (&attr, JOB_THREAD_STACK_SIZE);
int r = pthread_create (&JT->pthread_id, &attr, thread_work, (void *) JT);
pthread_attr_destroy (&attr);
if (r) {
vkprintf (0, "create_job_thread: pthread_create() failed: %s\n", strerror (r));
JT->status = 0;
return -1;
}
} else {
assert (!main_job_thread);
get_this_thread_id ();
JT->pthread_id = main_pthread_id;
this_job_thread = main_job_thread = JT;
set_job_interrupt_signal_handler ();
assert (JT->id == 1);
}
if (i > max_job_thread_id) {
max_job_thread_id = i;
}
cur_job_threads++;
JC->cur_threads ++;
return i;
}
int create_job_thread (int thread_class) {
struct job_class *JC = &JobClasses[thread_class];
return create_job_thread_ex (thread_class, JC->subclasses ? job_thread_sub : job_thread);
}
int create_job_class_threads (int job_class) {
assert (job_class != JC_MAIN);
int created = 0;
assert (job_class >= 1 && job_class <= JC_MAX);
struct job_class *JC = &JobClasses[job_class];
assert (JC->min_threads <= JC->max_threads);
check_main_thread ();
while (JC->cur_threads < JC->min_threads && cur_job_threads < MAX_JOB_THREADS) {
assert (create_job_thread (job_class) >= 0);
created++;
}
return created;
}
int init_async_jobs (void) {
init_main_pthread_id ();
if (!MainJobQueue.mq_magic) {
init_mp_queue_w (&MainJobQueue);
int i;
for (i = 0; i < JC_MAX + 1; i++) {
JobClasses[i].job_queue = &MainJobQueue;
}
}
if (!cur_job_threads) {
assert (create_job_thread (JC_MAIN) >= 0);
}
/*
int i;
for (i = 1; i < 16; i++) if (i != JC_MAIN) {
create_job_class_threads (i);
}*/
return cur_job_threads;
}
int create_new_job_class (int job_class, int min_threads, int max_threads) {
return create_job_class (job_class, min_threads, max_threads, 1);
}
int create_new_job_class_sub (int job_class, int min_threads, int max_threads, int subclass_cnt) {
return create_job_class_sub(job_class, min_threads, max_threads, 1, subclass_cnt);
}
int create_job_class (int job_class, int min_threads, int max_threads, int excl) {
assert (job_class >= 1 && job_class <= JC_MAX);
assert (min_threads >= 0 && max_threads >= min_threads);
struct job_class *JC = &JobClasses[job_class];
assert (!excl || !JC->min_threads);
if (min_threads < JC->min_threads || !JC->min_threads) {
JC->min_threads = min_threads;
}
if (max_threads > JC->max_threads) {
JC->max_threads = max_threads;
}
assert (JC->min_threads <= JC->max_threads);
if (MainJobQueue.mq_magic) {
return create_job_class_threads (job_class);
} else {
return 0;
}
}
int create_job_class_sub (int job_class, int min_threads, int max_threads, int excl, int subclass_cnt) {
assert (job_class >= 1 && job_class <= JC_MAX);
assert (min_threads >= 0 && max_threads >= min_threads);
struct job_subclass_list *L = calloc (sizeof (*L), 1);
L->subclass_cnt = subclass_cnt;
L->subclasses = calloc (sizeof (struct job_subclass), subclass_cnt + 2);
L->subclasses += 2;
int i;
for (i = -2; i < subclass_cnt; i++) {
L->subclasses[i].job_queue = alloc_mp_queue_w ();
L->subclasses[i].subclass_id = i;
}
for (i = 0; i < MAX_SUBCLASS_THREADS; i++) {
sem_post (&L->sem);
}
JobClasses[job_class].subclasses = L;
return create_job_class (job_class, min_threads, max_threads, excl);
}
/* ------ JOB THREAD CODE -------- */
int try_lock_job (job_t job, int set_flags, int clear_flags) {
while (1) {
barrier ();
int flags = job->j_flags;
if (flags & JF_LOCKED) {
return 0;
}
if (__sync_bool_compare_and_swap (&job->j_flags, flags, (flags & ~clear_flags) | set_flags | JF_LOCKED)) {
job->j_thread = this_job_thread;
return 1;
}
}
}
int unlock_job (JOB_REF_ARG (job)) {
assert (job->j_thread == this_job_thread);
struct job_thread *JT = job->j_thread;
int thread_class = JT->thread_class;
int save_subclass = job->j_subclass;
vkprintf (JOBS_DEBUG, "UNLOCK JOB %p, type %p, flags %08x, status %08x, sigclass %08x, refcnt %d\n", job, job->j_execute, job->j_flags, job->j_status, job->j_sigclass, job->j_refcnt);
while (1) {
barrier ();
assert (job->j_flags & JF_LOCKED);
int flags = job->j_flags;
int todo = flags & job->j_status & (-1 << 24);
if (!todo) /* {{{ */ {
int new_flags = flags & ~JF_LOCKED;
if (!__sync_bool_compare_and_swap (&job->j_flags, flags, new_flags)) {
continue;
}
if (job->j_refcnt >= 2) {
if (__sync_fetch_and_add (&job->j_refcnt, -1) != 1) {
return 0;
}
job->j_refcnt = 1;
}
assert (job->j_refcnt == 1);
vkprintf (JOBS_DEBUG, "DESTROYING JOB %p, type %p, flags %08x\n", job, job->j_execute, job->j_flags);
if (job->j_status & JSS_ALLOW (JS_FINISH)) {
// send signal 7 (JS_FINISH) if it is allowed
job->j_flags |= JFS_SET (JS_FINISH) | JF_LOCKED;
continue;
} else {
assert (0 && "unhandled JS_FINISH\n");
MODULE_STAT->jobs_allocated_memory -= sizeof (struct async_job) + job->j_custom_bytes;
// complete_job (job);
job_free (JOB_REF_PASS (job)); // ???
JT->jobs_active --;
return -1;
}
}
/* }}} */
int signo = 7 - __builtin_clz (todo);
int req_class = (job->j_sigclass >> (signo*4)) & 15;
int is_fast = job->j_status & JSS_FAST (signo);
int cur_subclass = job->j_subclass;
/* {{{ Try to run signal signo */
if (((JT->job_class_mask >> req_class) & 1) && (is_fast || !JT->current_job) && (cur_subclass == save_subclass)) {
job_t current_job = JT->current_job;
__sync_fetch_and_and (&job->j_flags, ~JFS_SET (signo));
JT->jobs_running[req_class] ++;
JT->current_job = job;
JT->status |= JTS_PERFORMING;
vkprintf (JOBS_DEBUG, "BEGIN JOB %p, type %p, flags %08x, status %08x, sigclass %08x (signal %d of class %d), refcnt %d\n", job, job->j_execute, job->j_flags, job->j_status, job->j_sigclass, signo, req_class, job->j_refcnt);
int custom = job->j_custom_bytes;
int res = job->j_execute (job, signo, JT);
JT->current_job = current_job;
if (!current_job) {
JT->status &= ~JTS_PERFORMING;
}
JT->jobs_running[req_class] --;
if (res == JOB_DESTROYED) {
MODULE_STAT->jobs_allocated_memory -= sizeof (struct async_job) + custom;
vkprintf (JOBS_DEBUG, "JOB %p DESTROYED: RES = %d\n", job, res);
JT->jobs_active --;
return res;
}
vkprintf (JOBS_DEBUG, "END JOB %p, type %p, flags %08x, status %08x, sigclass %08x (signal %d of class %d), refcnt %d, %d children: RES = %d\n", job, job->j_execute, job->j_flags, job->j_status, job->j_sigclass, signo, req_class, job->j_refcnt, job->j_children, res);
if (res == JOB_ERROR) {
kprintf ("fatal: thread %p of class %d: error while invoking method %d of job %p (type %p)\n", JT, thread_class, signo, job, job->j_execute);
assert (0 && "unknown job signal");
}
if (!(res & ~0x1ff)) {
if (res & 0xff) {
__sync_fetch_and_or (&job->j_flags, res << 24);
}
if (res & JOB_COMPLETED) {
complete_job (job);
}
}
continue;
}
/* }}} */
/* {{{ Try to Queue */
if (!req_class) {
// have a "fast" signal with *-class, put it into MAIN queue
req_class = JC_MAIN;
}
// have to insert job into queue of req_class
int queued_flag = JF_QUEUED_CLASS (req_class);
int new_flags = (flags | queued_flag) & ~JF_LOCKED;
if (!__sync_bool_compare_and_swap (&job->j_flags, flags, new_flags)) {
continue;
}
if (!(flags & queued_flag)) {
struct job_class *JC = &JobClasses[req_class];
if (!JC->subclasses) {
struct mp_queue *JQ = JC->job_queue;
assert (JQ);
vkprintf (JOBS_DEBUG, "RESCHEDULED JOB %p, type %p, flags %08x, refcnt %d -> Queue %d\n", job, job->j_execute, job->j_flags, job->j_refcnt, req_class);
vkprintf (JOBS_DEBUG, "sub=%p\n", JT->job_class->subclasses);
mpq_push_w (JQ, PTR_MOVE (job), 0);
if (JQ == &MainJobQueue && main_thread_interrupt_status == 1 && __sync_fetch_and_add (&main_thread_interrupt_status, 1) == 1) {
//pthread_kill (main_pthread_id, SIGRTMAX - 7);
vkprintf (JOBS_DEBUG, "WAKING UP MAIN THREAD\n");
wakeup_main_thread ();
}
} else {
assert (job->j_subclass == cur_subclass);
assert (cur_subclass >= -2);
assert (cur_subclass < JC->subclasses->subclass_cnt);
struct job_subclass *JSC = &JC->subclasses->subclasses[cur_subclass];
__sync_fetch_and_add (&JSC->total_jobs, 1);
vkprintf (JOBS_DEBUG, "RESCHEDULED JOB %p, type %p, flags %08x, refcnt %d -> Queue %d subclass %d\n", job, job->j_execute, job->j_flags, job->j_refcnt, req_class, cur_subclass);
mpq_push_w (JSC->job_queue, PTR_MOVE (job), 0);
struct mp_queue *JQ = JC->job_queue;
assert (JQ);
mpq_push_w (JQ, (void *)(long)(cur_subclass + JOB_SUBCLASS_OFFSET), 0);
}
return 1;
} else {
job_decref (JOB_REF_PASS (job));
return 0;
}
/* }}} */
}
}
// destroys one reference to job; sends signal signo to it
void job_send_signals (JOB_REF_ARG (job), int sigset) {
vkprintf (JOBS_DEBUG, "SENDING SIGNALS %08x to JOB %p, type %p, flags %08x, refcnt %d\n", sigset, job, job->j_execute, job->j_flags, job->j_refcnt);
assert (!(sigset & 0xffffff));
assert (job->j_refcnt > 0);
if ((job->j_flags & sigset) == sigset) {
assert (job->j_refcnt > 1 || !(job->j_flags & JFS_SET (JS_FINISH)));
job_decref (JOB_REF_PASS (job));
return;
}
if (try_lock_job (job, sigset, 0)) {
unlock_job (JOB_REF_PASS (job));
return;
}
__sync_fetch_and_or (&job->j_flags, sigset);
if (try_lock_job (job, 0, 0)) {
unlock_job (JOB_REF_PASS (job));
} else {
if (job->j_flags & JF_SIGINT) {
assert (job->j_thread);
pthread_kill (job->j_thread->pthread_id, SIGRTMAX - 7);
}
job_decref (JOB_REF_PASS (job));
}
}
// destroys one reference to job; sends signal signo to it
void job_signal (JOB_REF_ARG (job), int signo) {
assert ((unsigned) signo <= 7);
job_send_signals (JOB_REF_PASS (job), JFS_SET (signo));
}
// destroys one reference to job
void job_decref (JOB_REF_ARG (job)) {
if (job->j_refcnt >= 2) {
if (__sync_fetch_and_add (&job->j_refcnt, -1) != 1) {
return;
}
job->j_refcnt = 1;
}
assert (job->j_refcnt == 1);
job_signal (JOB_REF_PASS (job), JS_FINISH);
}
// creates one reference to job
job_t job_incref (job_t job) {
//if (job->j_refcnt == 1) {
// job->j_refcnt = 2;
//} else {
__sync_fetch_and_add (&job->j_refcnt, 1);
//}
return job;
}
void process_one_job (JOB_REF_ARG (job), int thread_class) {
struct job_thread *JT = this_job_thread;
assert (JT);
assert (job);
int queued_flag = job->j_flags & 0xffff & JT->job_class_mask;
if (try_lock_job (job, 0, queued_flag)) {
unlock_job (JOB_REF_PASS (job));
} else {
__sync_fetch_and_and (&job->j_flags, ~queued_flag);
if (try_lock_job (job, 0, 0)) {
unlock_job (JOB_REF_PASS (job));
} else {
job_decref (JOB_REF_PASS (job));
}
}
}
void complete_subjob (job_t job, JOB_REF_ARG (parent), int status) {
if (!parent) {
return;
}
if (parent->j_flags & JF_COMPLETED) {
job_decref (JOB_REF_PASS (parent));
return;
}
if (job->j_error && (status & JSP_PARENT_ERROR)) {
if (!parent->j_error) {
__sync_bool_compare_and_swap (&parent->j_error, 0, job->j_error);
}
if (status & JSP_PARENT_WAKEUP) {
__sync_fetch_and_add (&parent->j_children, -1);
}
vkprintf (JOBS_DEBUG, "waking up parent %p with JS_ABORT (%d children remaining)\n", parent, parent->j_children);
job_signal (JOB_REF_PASS (parent), JS_ABORT);
return;
}
if (status & JSP_PARENT_WAKEUP) {
if (__sync_fetch_and_add (&parent->j_children, -1) == 1 && (status & JSP_PARENT_RUN)) {
vkprintf (JOBS_DEBUG, "waking up parent %p with JS_RUN\n", parent);
job_signal (JOB_REF_PASS (parent), JS_RUN);
} else {
vkprintf (JOBS_DEBUG, "parent %p: %d children remaining\n", parent, parent->j_children);
job_decref (JOB_REF_PASS (parent));
}
return;
}
if (status & JSP_PARENT_RUN) {
job_signal (JOB_REF_PASS (parent), JS_RUN);
return;
}
job_decref (JOB_REF_PASS (parent));
}
void complete_job (job_t job) {
vkprintf (JOBS_DEBUG, "COMPLETE JOB %p, type %p, flags %08x, status %08x, error %d; refcnt=%d; PARENT %p\n", job, job->j_execute, job->j_flags, job->j_status, job->j_error, job->j_refcnt, job->j_parent);
assert (job->j_flags & JF_LOCKED);
if (job->j_flags & JF_COMPLETED) {
return;
}
__sync_fetch_and_or (&job->j_flags, JF_COMPLETED);
job_t parent = PTR_MOVE (job->j_parent);
if (!parent) {
return;
}
complete_subjob (job, JOB_REF_PASS (parent), job->j_status);
}
static void job_interrupt_signal_handler (const int sig) {
char buffer[256];
if (verbosity >= 2) {
kwrite (2, buffer, sprintf (buffer, "SIGRTMAX-7 (JOB INTERRUPT) caught in thread #%d running job %p.\n", this_job_thread ? this_job_thread->id : -1, this_job_thread ? this_job_thread->current_job : 0));
}
}
static void set_job_interrupt_signal_handler (void) {
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = job_interrupt_signal_handler;
if (sigaction (SIGRTMAX - 7, &act, NULL) != 0) {
kwrite (2, "failed sigaction\n", 17);
_exit (EXIT_FAILURE);
}
}
void *job_thread_ex (void *arg, void (*work_one)(void *, int)) {
struct job_thread *JT = arg;
this_job_thread = JT;
assert (JT->thread_class);
assert (!(JT->thread_class & ~JC_MASK));
get_this_thread_id ();
JT->thread_system_id = syscall (SYS_gettid);
set_job_interrupt_signal_handler ();
struct thread_callback *cb = jobs_cb_list;
while (cb) {
cb->new_thread ();
cb = cb->next;
}
JT->status |= JTS_RUNNING;
int thread_class = JT->thread_class;
struct mp_queue *Q = JT->job_queue;
// void **hptr = thread_hazard_pointers;
if (JT->job_class->max_threads == 1) {
JT->timer_manager = alloc_timer_manager (thread_class);
}
int prev_now = 0;
long long last_rdtsc = 0;
while (1) {
void *job = mpq_pop_nw (Q, 4);
if (!job) {
double wait_start = get_utime_monotonic ();
MODULE_STAT->locked_since = wait_start;
job = mpq_pop_w (Q, 4);
double wait_time = get_utime_monotonic () - wait_start;
MODULE_STAT->locked_since = 0;
MODULE_STAT->tot_idle_time += wait_time;
MODULE_STAT->a_idle_time += wait_time;
}
long long new_rdtsc = rdtsc ();
if (new_rdtsc - last_rdtsc > 1000000) {
get_utime_monotonic ();
now = time (0);
if (now > prev_now && now < prev_now + 60) {
while (prev_now < now) {
MODULE_STAT->a_idle_time *= 100.0 / 101;
MODULE_STAT->a_idle_quotient = a_idle_quotient * (100.0/101) + 1;
prev_now++;
}
} else {
if (now >= prev_now + 60) {
MODULE_STAT->a_idle_time = MODULE_STAT->a_idle_quotient;
}
prev_now = now;
}
last_rdtsc = new_rdtsc;
}
vkprintf (JOBS_DEBUG, "JOB THREAD #%d (CLASS %d): got job %p\n", JT->id, thread_class, job);
work_one (PTR_MOVE (job), thread_class);
}
pthread_exit (0);
}
static void process_one_sublist (unsigned long id, int class) {
struct job_thread *JT = this_job_thread;
assert (JT);
struct job_class *JC = JT->job_class;
assert (JC->subclasses);
struct job_subclass_list *J_SCL = JC->subclasses;
id -= JOB_SUBCLASS_OFFSET;
int subclass_id = id;
assert (subclass_id >= -2);
assert (subclass_id < JC->subclasses->subclass_cnt);
struct job_subclass *J_SC = &J_SCL->subclasses[subclass_id];
__sync_fetch_and_add (&J_SC->allowed_to_run_jobs, 1);
if (!__sync_bool_compare_and_swap (&J_SC->locked, 0, 1)) {
return;
}
if (subclass_id != -1) {
while (sem_wait (&J_SCL->sem) < 0);
} else {
int i;
for (i = 0; i < MAX_SUBCLASS_THREADS; i++) {
while (sem_wait (&J_SCL->sem));
}
}
while (1) {
while (J_SC->processed_jobs < J_SC->allowed_to_run_jobs) {
job_t job = mpq_pop_nw (J_SC->job_queue, 4);
assert (job);
process_one_job (JOB_REF_PASS (job), JT->thread_class);
J_SC->processed_jobs ++;
}
J_SC->locked = 0;
__sync_synchronize ();
if (J_SC->processed_jobs < J_SC->allowed_to_run_jobs &&
__sync_bool_compare_and_swap (&J_SC->locked, 0, 1)) {
continue;
}
break;
}
if (subclass_id != -1) {
while (sem_post (&J_SCL->sem) < 0);
} else {
int i;
for (i = 0; i < MAX_SUBCLASS_THREADS; i++) {
while (sem_post (&J_SCL->sem));
}
}
}
static void process_one_sublist_gw (void *x, int class) {
process_one_sublist ((long)x, class);
}
static void process_one_job_gw (void *x, int class) {
process_one_job (JOB_REF_PASS (x), class);
}
void *job_thread (void *arg) {
return job_thread_ex (arg, process_one_job_gw);
}
void *job_thread_sub (void *arg) {
return job_thread_ex (arg, process_one_sublist_gw);
}
int run_pending_main_jobs (void) {
if (!MainJobQueue.mq_magic) {
return -1;
}
struct job_thread *JT = this_job_thread;
assert (JT && JT->thread_class == JC_MAIN);
JT->status |= JTS_RUNNING;
int cnt = 0;
while (1) {
job_t job = mpq_pop_nw (&MainJobQueue, 4);
if (!job) {
break;
}
vkprintf (JOBS_DEBUG, "MAIN THREAD: got job %p\n", job);
process_one_job (JOB_REF_PASS (job), JC_MAIN);
cnt++;
}
JT->status &= ~JTS_RUNNING;
return cnt;
}
/* ------ JOB CREATION/QUEUEING ------ */
void job_change_signals (job_t job, unsigned long long job_signals) {
assert (job->j_flags & JF_LOCKED);
job->j_status = job_signals & 0xffff001f;
job->j_sigclass = (job_signals >> 32);
}
/* "destroys" one reference to parent_job */
job_t create_async_job (job_function_t run_job, unsigned long long job_signals, int job_subclass, int custom_bytes, unsigned long long job_type, JOB_REF_ARG (parent_job)) {
if (parent_job) {
if (job_signals & JSP_PARENT_WAKEUP) {
__sync_fetch_and_add (&parent_job->j_children, 1);
}
}
MODULE_STAT->jobs_allocated_memory += sizeof (struct async_job) + custom_bytes;
struct job_thread *JT = this_job_thread;
assert (JT);
void *p = malloc (sizeof (struct async_job) + custom_bytes + 64);
assert (p);
int align = -((uintptr_t) p) & 63;
job_t job = p + align;
assert (!(((uintptr_t) job) & 63));
job->j_flags = JF_LOCKED;
job->j_status = job_signals & 0xffff001f;
job->j_sigclass = (job_signals >> 32);
job->j_refcnt = 1;
job->j_error = 0;
job->j_children = 0;
job->j_custom_bytes = custom_bytes;
job->j_thread = JT;
job->j_align = align;
job->j_execute = run_job;
job->j_parent = PTR_MOVE (parent_job);
job->j_type = job_type;
job->j_subclass = job_subclass;
memset (job->j_custom, 0, custom_bytes);
JT->jobs_created ++;
JT->jobs_active ++;
if (job_type & JT_HAVE_TIMER) {
job_timer_init (job);
}
if (job_type & JT_HAVE_MSG_QUEUE) {
job_message_queue_init (job);
}
vkprintf (JOBS_DEBUG, "CREATING JOB %p, type %p, flags %08x, status %08x, sigclass %08x; PARENT %p\n", job, run_job, job->j_flags, job->j_status, job->j_sigclass, job->j_parent);
return job;
}
int schedule_job (JOB_REF_ARG (job)) {
assert (job->j_flags & JF_LOCKED);
job->j_flags |= JFS_SET (JS_RUN);
return unlock_job (JOB_REF_PASS (job));
}
int job_timer_wakeup_gateway (event_timer_t *et) {
job_t job = (job_t)((char *) et - offsetof (struct async_job, j_custom));
if (et->wakeup_time == et->real_wakeup_time) {
vkprintf (JOBS_DEBUG, "ALARM JOB %p, type %p, flags %08x, status %08x, refcnt %d; PARENT %p\n", job, job->j_execute, job->j_flags, job->j_status, job->j_refcnt, job->j_parent);
job_signal (JOB_REF_PASS (job), JS_ALARM);
} else {
vkprintf (JOBS_DEBUG, "ALARM JOB %p, type %p, flags %08x, status %08x, refcnt %d; PARENT %p. SKIPPED\n", job, job->j_execute, job->j_flags, job->j_status, job->j_refcnt, job->j_parent);
job_decref (JOB_REF_PASS (job));
}
return 0;
}
/* --------- JOB LIST JOBS --------
(enables several connections or jobs to wait for same job completion)
*/
struct job_list_job_node {
struct job_list_node *jl_next;
job_list_node_type_t jl_type;
job_t jl_job;
int jl_flags;
};
struct job_list_params {
event_timer_t timer;
struct job_list_node *first, *last;
};
int job_list_node_wakeup (job_t list_job, int op, struct job_list_node *w) {
struct job_list_job_node *wj = (struct job_list_job_node *) w;
complete_subjob (list_job, JOB_REF_PASS (wj->jl_job), wj->jl_flags);
free (wj);
return 0;
}
int process_job_list (job_t job, int op, struct job_thread *JT) {
assert (job->j_custom_bytes == sizeof (struct job_list_params));
struct job_list_params *P = (struct job_list_params *) job->j_custom;
struct job_list_node *w, *wn;
switch (op) {
case JS_FINISH:
assert (job->j_refcnt == 1);
assert (job->j_flags & JF_COMPLETED);
job_timer_remove (job);
return job_free (JOB_REF_PASS (job));
case JS_ABORT:
if (!job->j_error) {
job->j_error = ECANCELED;
}
case JS_ALARM:
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
default:
case JS_RUN:
assert (!(job->j_flags & JF_COMPLETED));
for (w = P->first; w; w = wn) {
wn = w->jl_next;
w->jl_next = 0;
w->jl_type (job, op, w);
}
P->first = P->last = 0;
job->j_status &= ~(JSS_ALLOW (JS_RUN) | JSS_ALLOW (JS_ABORT));
return JOB_COMPLETED;
}
}
job_t create_job_list (void) {
job_t job = create_async_job (process_job_list, JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ABORT) | JSC_ALLOW (JC_ENGINE, JS_FINISH), 0, sizeof (struct job_list_params), JT_HAVE_TIMER, JOB_REF_NULL);
struct job_list_params *P = (struct job_list_params *) job->j_custom;
P->first = 0;
P->last = 0;
P->timer.wakeup = 0;
unlock_job (JOB_REF_CREATE_PASS (job));
return job;
}
int insert_node_into_job_list (job_t list_job, struct job_list_node *w) {
assert (list_job->j_execute == process_job_list);
assert (!(list_job->j_flags & (JF_LOCKED | JF_COMPLETED)));
assert (try_lock_job (list_job, 0, 0));
w->jl_next = 0;
struct job_list_params *P = (struct job_list_params *) list_job->j_custom;
if (!P->first) {
P->first = P->last = w;
} else {
P->last->jl_next = w;
P->last = w;
}
unlock_job (JOB_REF_CREATE_PASS (list_job));
return 1;
}
int insert_job_into_job_list (job_t list_job, JOB_REF_ARG(job), int mode) {
check_thread_class (JC_ENGINE);
if (mode & JSP_PARENT_WAKEUP) {
__sync_fetch_and_add (&job->j_children, 1);
}
struct job_list_job_node *wj = malloc (sizeof (struct job_list_job_node));
assert (wj);
wj->jl_type = job_list_node_wakeup;
wj->jl_job = PTR_MOVE (job);
wj->jl_flags = mode;
return insert_node_into_job_list (list_job, (struct job_list_node *) wj);
}
int insert_connection_into_job_list (job_t list_job, connection_job_t c) {
assert (0);
return 0;
}
struct job_timer_manager_extra {
struct mp_queue *mpq;
};
job_t timer_manager_job;
int insert_event_timer (event_timer_t *et);
int remove_event_timer (event_timer_t *et);
void do_immediate_timer_insert (job_t W) {
MODULE_STAT->timer_ops ++;
struct event_timer *ev = (void *)W->j_custom;
int active = ev->h_idx > 0;
double r = ev->real_wakeup_time;
if (r > 0) {
ev->wakeup_time = r;
insert_event_timer (ev);
assert (ev->wakeup == job_timer_wakeup_gateway);
if (!active) {
job_incref (W);
}
} else {
ev->wakeup_time = 0;
remove_event_timer (ev);
if (active) {
job_decref (JOB_REF_PASS (W));
}
}
if (this_job_thread) {
this_job_thread->wakeup_time = timers_get_first ();
}
}
int do_timer_manager_job (job_t job, int op, struct job_thread *JT) {
if (op != JS_RUN && op != JS_AUX) {
return JOB_ERROR;
}
if (op == JS_AUX) {
thread_run_timers ();
JT->wakeup_time = timers_get_first ();
return 0;
}
struct job_timer_manager_extra *e = (void *)job->j_custom;
while (1) {
job_t W = mpq_pop_nw (e->mpq, 4);
if (!W) { break; }
do_immediate_timer_insert (W);
job_decref (JOB_REF_PASS (W));
}
return 0;
}
void jobs_check_all_timers (void) {
int i;
for (i = 1; i <= max_job_thread_id; i++) {
struct job_thread *JT = &JobThreads[i];
if (JT->timer_manager && JT->wakeup_time && JT->wakeup_time <= precise_now) {
job_signal (JOB_REF_CREATE_PASS (JT->timer_manager), JS_AUX);
}
}
}
job_t alloc_timer_manager (int thread_class) {
if (thread_class == JC_EPOLL && timer_manager_job) {
return job_incref (timer_manager_job);
}
job_t timer_manager = create_async_job (do_timer_manager_job, JSC_ALLOW (thread_class, JS_RUN) | JSC_ALLOW (thread_class, JS_AUX) | JSC_ALLOW (thread_class, JS_FINISH), 0, sizeof (struct job_timer_manager_extra), 0, JOB_REF_NULL);
timer_manager->j_refcnt = 1;
struct job_timer_manager_extra *e = (void *)timer_manager->j_custom;
e->mpq = alloc_mp_queue_w ();
unlock_job (JOB_REF_CREATE_PASS (timer_manager));
if (thread_class == JC_EPOLL) {
timer_manager_job = job_incref (timer_manager);
}
return timer_manager;
}
int do_timer_job (job_t job, int op, struct job_thread *JT) {
if (op == JS_ALARM) {
if (!job_timer_check (job)) {
return 0;
}
if (job->j_flags & JF_COMPLETED) {
return 0;
}
struct job_timer_info *e = (void *)job->j_custom;
double r = e->wakeup (e->extra);
if (r > 0) {
job_timer_insert (job, r);
} else if (r < 0) {
job_decref (JOB_REF_PASS (job));
}
return 0;
}
if (op == JS_ABORT) {
job_timer_remove (job);
return JOB_COMPLETED;
}
if (op == JS_FINISH) {
MODULE_STAT->job_timers_allocated --;
return job_free (JOB_REF_PASS (job));
}
return JOB_ERROR;
}
job_t job_timer_alloc (int thread_class, double (*alarm)(void *), void *extra) {
assert (thread_class > 0 && thread_class <= 0xf);
job_t t = create_async_job (do_timer_job, JSC_ALLOW (thread_class, JS_ABORT) | JSC_ALLOW (thread_class, JS_ALARM) | JSIG_FAST (JS_FINISH), 0, sizeof (struct job_timer_info), JT_HAVE_TIMER, JOB_REF_NULL);
t->j_refcnt = 1;
struct job_timer_info *e = (void *)t->j_custom;
e->wakeup = alarm;
e->extra = extra;
unlock_job (JOB_REF_CREATE_PASS (t));
MODULE_STAT->job_timers_allocated ++;
return t;
}
int job_timer_check (job_t job) {
assert (job->j_type & JT_HAVE_TIMER);
struct event_timer *ev = (void *)job->j_custom;
if (ev->real_wakeup_time == 0 || ev->real_wakeup_time != ev->wakeup_time) {
return 0;
}
job_timer_remove (job);
//ev->real_wakeup_time = 0;
return 1;
}
void job_timer_insert (job_t job, double timeout) {
assert (job->j_type & JT_HAVE_TIMER);
struct event_timer *ev = (void *)job->j_custom;
//timeout = (ceil (timeout * 1000)) * 0.001;
if (ev->real_wakeup_time == timeout) { return; }
ev->real_wakeup_time = timeout;
if (!ev->wakeup) {
ev->wakeup = job_timer_wakeup_gateway;
}
if (ev->flags & 255) {
if ((this_job_thread && (this_job_thread->id == (ev->flags & 255))) ||
(!this_job_thread && (ev->flags & 255) == 1)) {
do_immediate_timer_insert (job);
return;
}
} else {
if (!this_job_thread || this_job_thread->id == 1) {
ev->flags |= 1;
do_immediate_timer_insert (job);
return;
} else if (this_job_thread->timer_manager) {
ev->flags |= this_job_thread->id;
do_immediate_timer_insert (job);
return;
} else {
ev->flags |= 1;
}
}
assert (ev->flags & 255);
job_t m = NULL;
if ((ev->flags & 255) == 1) {
m = timer_manager_job;
} else {
m = JobThreads[ev->flags & 255].timer_manager;
}
MODULE_STAT->timer_ops_scheduler ++;
assert (m);
struct job_timer_manager_extra *e = (void *)m->j_custom;
mpq_push_w (e->mpq, job_incref (job), 0);
job_signal (JOB_REF_CREATE_PASS (m), JS_RUN);
}
void job_timer_remove (job_t job) {
assert (job->j_type & JT_HAVE_TIMER);
job_timer_insert (job, 0);
}
int job_timer_active (job_t job) {
assert (job->j_type & JT_HAVE_TIMER);
return ((struct event_timer *)job->j_custom)->real_wakeup_time > 0;
}
double job_timer_wakeup_time (job_t job) {
assert (job->j_type & JT_HAVE_TIMER);
return ((struct event_timer *)job->j_custom)->real_wakeup_time;
}
void job_timer_init (job_t job) {
assert (job->j_type & JT_HAVE_TIMER);
memset ((void *)job->j_custom, 0, sizeof (struct event_timer));
}
void register_thread_callback (struct thread_callback *cb) {
cb->next = jobs_cb_list;
jobs_cb_list = cb;
cb->new_thread ();
}
struct job_message_queue *job_message_queue_get (job_t job) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message_queue **q = (job->j_type & JT_HAVE_TIMER) ? sizeof (struct event_timer) + (void *)job->j_custom : (void *)job->j_custom;
return *q;
}
void job_message_queue_set (job_t job, struct job_message_queue *queue) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message_queue **q = (job->j_type & JT_HAVE_TIMER) ? sizeof (struct event_timer) + (void *)job->j_custom : (void *)job->j_custom;
assert (!*q);
*q = queue;
}
void job_message_queue_free (job_t job) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message_queue **q = (job->j_type & JT_HAVE_TIMER) ? sizeof (struct event_timer) + (void *)job->j_custom : (void *)job->j_custom;
struct job_message_queue *Q = *q;
if (Q) {
struct job_message *M;
while (Q->first) {
M = Q->first;
Q->first = M->next;
if (M->src) {
job_decref (JOB_REF_PASS (M->src));
}
if (M->message.magic) {
rwm_free (&M->message);
}
free (M);
}
assert (!Q->first);
Q->last = NULL;
while ((M = mpq_pop_nw (Q->unsorted, 4))) {
if (M->src) {
job_decref (JOB_REF_PASS (M->src));
}
if (M->message.magic) {
rwm_free (&M->message);
}
free (M);
}
free_mp_queue ((*q)->unsorted);
free (*q);
}
*q = NULL;
}
void job_message_queue_init (job_t job) {
struct job_message_queue *q = calloc (sizeof (*q), 1);
q->unsorted = alloc_mp_queue_w ();
job_message_queue_set (job, q);
}
void job_message_free_default (struct job_message *M) {
if (M->src) {
job_decref (JOB_REF_PASS (M->src));
}
if (M->message.magic) {
rwm_free (&M->message);
}
free (M);
}
void job_message_send (JOB_REF_ARG (job), JOB_REF_ARG (src), unsigned int type, struct raw_message *raw, int dup, int payload_ints, const unsigned int *payload, unsigned int flags, void (*destroy)(struct job_message *)) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message *M = malloc (sizeof (*M) + payload_ints * 4);
M->type = type;
M->flags = 0;
M->src = PTR_MOVE (src);
M->payload_ints = payload_ints;
M->next = NULL;
M->flags = flags;
M->destructor = destroy;
memcpy (M->payload, payload, payload_ints * 4);
(dup ? rwm_clone : rwm_move) (&M->message, raw);
struct job_message_queue *q = job_message_queue_get (job);
mpq_push_w (q->unsorted, M, 0);
job_signal (JOB_REF_PASS (job), JS_MSG);
}
/*
void job_message_send_data (JOB_REF_ARG (job), JOB_REF_ARG (src), unsigned int type, void *ptr1, void *ptr2, int int1, long long long1, int payload_ints, const unsigned int *payload, unsigned int flags) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message *M = malloc (sizeof (*M) + payload_ints * 4);
M->type = type;
M->flags = 0;
M->src = PTR_MOVE (src);
M->payload_ints = payload_ints;
M->next = NULL;
M->flags = flags;
memcpy (M->payload, payload, payload_ints * 4);
M->message_ptr1 = ptr1;
M->message_ptr2 = ptr2;
M->message_int1 = int1;
M->message_long1 = long1;
M->message_magic = 0;
struct job_message_queue *q = job_message_queue_get (job);
mpq_push_w (q->unsorted, M, 0);
job_signal (JOB_REF_PASS (job), JS_RUN);
}*/
void job_message_send_fake (JOB_REF_ARG (job), int (*receive_message)(job_t job, struct job_message *M, void *extra), void *extra, JOB_REF_ARG (src), unsigned int type, struct raw_message *raw, int dup, int payload_ints, const unsigned int *payload, unsigned int flags, void (*destroy)(struct job_message *)) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message *M = malloc (sizeof (*M) + payload_ints * 4);
M->type = type;
M->flags = 0;
M->src = PTR_MOVE (src);
M->payload_ints = payload_ints;
M->next = NULL;
M->flags = flags;
M->destructor = destroy;
memcpy (M->payload, payload, payload_ints * 4);
(dup ? rwm_clone : rwm_move) (&M->message, raw);
int r = receive_message (job, M, extra);
if (r == 1) {
job_message_free_default (M);
} else if (r == 2) {
if (M->destructor) {
M->destructor (M);
} else {
job_message_free_default (M);
}
}
job_decref (JOB_REF_PASS (job));
}
void job_message_queue_work (job_t job, int (*receive_message)(job_t job, struct job_message *M, void *extra), void *extra, unsigned int mask) {
assert (job->j_type & JT_HAVE_MSG_QUEUE);
struct job_message_queue *q = job_message_queue_get (job);
while (1) {
struct job_message *msg = mpq_pop_nw (q->unsorted, 4);
if (!msg) { break; }
msg->next = NULL;
if (q->last) {
q->last->next = msg;
q->last = msg;
} else {
q->last = q->first = msg;
}
}
struct job_message *last = NULL;
struct job_message **ptr = &q->first;
int stop = 0;
while (*ptr && !stop) {
struct job_message *M = *ptr;
unsigned int type = M->flags & JMC_TYPE_MASK;
assert (type);
if (mask & (1 << type)) {
struct job_message *next = M->next;
M->next = NULL;
int r;
if (type & JMC_CONTINUATION) {
assert (q->payload_magic);
r = job_message_continuation (job, M, q->payload_magic);
} else {
r = receive_message (job, M, extra);
}
if (r < 0) {
stop = 1;
} else if (r == 1) {
job_message_free_default (M);
} else if (r == 2) {
if (M->destructor) {
M->destructor (M);
} else {
job_message_free_default (M);
}
}
*ptr = next;
if (q->last == M) {
q->last = last;
}
} else {
last = M;
ptr = &last->next;
}
}
}
unsigned int *payload_continuation_create (unsigned int magic, int (*func)(job_t, struct job_message *, void *extra), void *extra) {
static __thread unsigned int payload_data[5];
payload_data[0] = magic;
*(void **)(payload_data + 1) = func;
*(void **)(payload_data + 3) = extra;
return payload_data;
}
int job_free (JOB_REF_ARG (job)) {
if (job->j_type & JT_HAVE_MSG_QUEUE) {
job_message_queue_free (job);
}
free (((void *)job) - job->j_align);
return JOB_DESTROYED;
}
struct notify_job_subscriber {
struct notify_job_subscriber *next;
job_t job;
};
struct notify_job_extra {
struct job_message_queue *message_queue;
int result;
struct notify_job_subscriber *first, *last;
};
#define TL_ENGINE_NOTIFICATION_SUBSCRIBE 0x8934a894
static int notify_job_receive_message (job_t NJ, struct job_message *M, void *extra) {
struct notify_job_extra *N = (void *)NJ->j_custom;
switch (M->type) {
case TL_ENGINE_NOTIFICATION_SUBSCRIBE:
if (N->result) {
complete_subjob (NJ, JOB_REF_PASS (M->src), JSP_PARENT_RWE);
} else {
struct notify_job_subscriber *S = malloc (sizeof (*S));
S->job = PTR_MOVE (M->src);
S->next = NULL;
if (N->last) {
N->last->next = S;
N->last = S;
} else {
N->last = N->first = S;
}
}
return 1;
default:
kprintf ("%s: unknown message type 0x%08x\n", __func__, M->type);
assert (0);
return 1;
}
}
static int notify_job_run (job_t NJ, int op, struct job_thread *JT) {
if (op == JS_MSG) {
job_message_queue_work (NJ, notify_job_receive_message, NULL, 0xffffff);
return 0;
}
if (op == JS_RUN || op == JS_ABORT) {
struct notify_job_extra *N = (void *)NJ->j_custom;
while (N->first) {
struct notify_job_subscriber *S = N->first;
N->first = S->next;
if (!N->first) {
N->last = NULL;
}
complete_subjob (NJ, JOB_REF_PASS (S->job), JSP_PARENT_RWE);
free (S);
}
return 0;
}
if (op == JS_FINISH) {
return job_free (JOB_REF_PASS (NJ));
}
return JOB_ERROR;
}
job_t notify_job_create (int sig_class) {
return create_async_job (notify_job_run, JSC_ALLOW (sig_class, JS_RUN) | JSC_ALLOW (sig_class, JS_ABORT) | JSC_ALLOW (sig_class, JS_MSG) | JSC_ALLOW (sig_class, JS_FINISH), 0, sizeof (struct notify_job_extra), JT_HAVE_MSG_QUEUE, JOB_REF_NULL);
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2015 Telegram Messenger Inc
2014-2015 Nikolai Durov
2014 Andrey Lopatin
*/
#pragma once
#include <stdlib.h>
#include <string.h>
#include <semaphore.h>
#include "net/net-events.h"
#include "net/net-msg.h"
#include "net/net-timers.h"
#define __joblocked
#define __jobref
#define MAX_SUBCLASS_THREADS 16
//#include "net/net-connections.h"
// verbosity level for jobs
#define JOBS_DEBUG 3
#define CONCAT(a,b) a ## b
#define PTR_MOVE(__ptr_v) \
({ typeof(__ptr_v) __ptr_v_save = __ptr_v; __ptr_v = NULL; __ptr_v_save; })
#define JOB_REF_ARG(__name) int __name ## _tag_int, job_t __name
#define JOB_REF_PASS(__ptr) 1, PTR_MOVE (__ptr)
#define JOB_REF_NULL 1, NULL
#define JOB_REF_CREATE_PASS(__ptr) 1, job_incref (__ptr)
#define JOB_REF_CREATE_PASS_N(__ptr) 1, __ptr ? job_incref (__ptr) : NULL
struct job_thread;
struct async_job;
typedef struct async_job *job_t;
typedef int (*job_function_t)(job_t job, int op, struct job_thread *JT);
extern __thread struct job_thread *this_job_thread;
extern __thread job_t this_job;
#define JOB_DESTROYED -0x80000000
#define JOB_COMPLETED 0x100
#define JOB_FINISH 0x80
#define JOB_ERROR -1
/* job signal numbers (0..7) */
#define JS_FREE -1 /* pseudo-signal, invoked to free job structure ("destructor") */
#define JS_RUN 0
#define JS_AUX 1
#define JS_MSG 2
#define JS_ALARM 4 /* usually sent by timer */
#define JS_ABORT 5 /* used for error propagation, especially from children */
#define JS_KILL 6
#define JS_FINISH 7
#define JS_SIG0 0
#define JS_SIG1 1
#define JS_SIG2 2
#define JS_SIG3 3
#define JS_SIG4 4
#define JS_SIG5 5
#define JS_SIG6 6
#define JS_SIG7 7
extern int engine_multithread_mode;
#define JC_EPOLL JC_MAIN
#define JC_METAFILE_READ JC_IO
#define JC_METAFILE_PREPARE JC_CPU
#define JC_CONNECTION 4
#define JC_CONNECTION_IO 5
#define JC_UDP 6
#define JC_UDP_IO 7
#define JC_ENGINE 8
#define JC_GMS JC_ENGINE
#define JC_GMS_CPU 10
#define JC_ENGINE_MULT 11
#define DEFAULT_IO_JOB_THREADS 16
#define DEFAULT_CPU_JOB_THREADS 8
#define DEFAULT_GMS_CPU_JOB_THREADS 8
// fake class
// no signals should be allowed
#define JC_MP_QUEUE 9
#define JC_NONE 0 // no signal (unless used with "fast" flag; then it means "any context")
#define JC_IO 1 // signal must be processed in I/O thread
#define JC_CPU 2 // signal must be processed in CPU thread
#define JC_MAIN 3 // signal must be processed in main thread (unless specified otherwise)
#define JC_MAX 0xf
#define JC_MASK JC_MAX
#define JF_LOCKED 0x10000 // job is "locked" (usually this means that a signal is being processed)
#define JF_SIGINT 0x20000 // signal interruption: if job is "locked" and we send a new signal to it, invoke pthread_signal() as well
#define JF_COMPLETED 0x40000 // used to signal job "completion" to outside observers
#define JF_QUEUED_CLASS(__c) (1 << (__c))
#define JF_QUEUED_MAIN JF_QUEUED_CLASS(JC_MAIN) // job is in MAIN execution queue
#define JF_QUEUED_IO JF_QUEUED_CLASS(JC_IO) // job is in IO execution queue
#define JF_QUEUED_CPU JF_QUEUED_CLASS(JC_CPU) // job is in CPU execution queue
#define JF_QUEUED 0xffff // job is in some execution queue
#define JT_HAVE_TIMER 1
#define JT_HAVE_MSG_QUEUE 2
#define JFS_SET(__s) (0x1000000U << (__s)) // j_flags: signal __s is awaiting delivery
#define JSS_ALLOW(__s) (0x1000000U << (__s)) // j_status: signal __s is allowed for delivery
#define JSS_FAST(__s) (0x10000U << (__s)) // j_status: signal __s is "fast" -- may be processed recursively in specified or in any context, not necessarily in order
#define JSS_ALLOW_FAST(__s) (0x1010000U << (__s))
#define JOB_SENDSIG(__s) (1 << (__s))
#define JSC_TYPE(__c,__s) (((unsigned long long)(__c) << ((__s) * 4 + 32)))
#define JSC_ALLOW(__c,__s) (JSC_TYPE(__c,__s) | JSS_ALLOW(__s))
#define JSC_FAST(__c,__s) (JSC_TYPE(__c,__s) | JSS_ALLOW_FAST(__s))
#define JSIG_MAIN(__s) JSC_ALLOW(JC_MAIN,__s)
#define JSIG_IO(__s) JSC_ALLOW(JC_IO,__s)
#define JSIG_CPU(__s) JSC_ALLOW(JC_CPU,__s)
#define JSIG_FAST(__s) JSC_FAST(JC_NONE,__s)
#define JSIG_ENGINE(__s) JSC_ALLOW(JC_ENGINE,__s)
#define JSP_PARENT_ERROR 1 // j_status: propagate error to j_error field in j_parent, and send ABORT to parent
#define JSP_PARENT_RUN 2 // j_status: send RUN to j_parent after job completion
#define JSP_PARENT_WAKEUP 4 // j_status: decrease j_parent's j_children; if it becomes 0, maybe send RUN
#define JSP_PARENT_RESPTR 8 // j_status: (result) pointer(s) kept in j_custom actually point inside j_parent; use them only if j_parent is still valid
#define JSP_PARENT_INCOMPLETE 0x10 // abort job if parent already completed
#define JSP_PARENT_RWE 7
#define JSP_PARENT_RWEP 0xf
#define JSP_PARENT_RWEI 0x17
#define JSP_PARENT_RWEPI 0x1f
#define JMC_UPDATE 1
#define JMC_FORCE_UPDATE 2
#define JMC_RPC_QUERY 3
#define JMC_TYPE_MASK 31
#define JMC_CONTINUATION 8
#define JMC_EXTRACT_ANSWER(__type) (((__type) >> 8) & 255)
#define JMC_ANSWER(__type) ((__type) << 8)
/* all fields here, with the exception of bits 24..31 and JF_LOCKED of j_flags, j_error, j_refcnt, j_children, may be changed only
by somebody who already owns a lock to this job, or has the only pointer to it. */
struct async_job { // must be partially compatible with `struct connection`
int j_flags; // bits 0..15: queue flags; bits 16..23: status; bits 24..31: received signals (only bits that can be changed without having lock)
int j_status; // bits 24..31: allowed signals; bits 16..23: corresponding signal is "fast"; bits 0..4: relation to parent
int j_sigclass; // bits (4*n)..(4*n-3): queue class of signal n, n=0..7
int j_refcnt; // reference counter, changed by job_incref() and job_decref(); when becomes zero, j_execute is invoked with op = JS_FREE
int j_error; // if non-zero, error code; may be overwritten by children (unless already non-zero: remembers first error only)
int j_children; // number of jobs to complete before scheduling this job
int j_align; // align of real allocated pointer
int j_custom_bytes;
unsigned int j_type; // Bit 0 - have event_timer (must be first bytes of j_custom)
// Bit 1 - have message queue (must be after event_timer or first, if there is no event_timer)
int j_subclass;
struct job_thread *j_thread; // thread currently processing this job
// maybe: reference to queue, position in queue -- if j_flags & JF_QUEUED -- to remove from queue if necessary
job_function_t j_execute; // invoked in correct context to process signals
job_t j_parent; // parent (dependent) job or 0
long long j_custom[0] __attribute__((aligned(64)));
} __attribute__((aligned(64)));
struct job_subclass {
int subclass_id;
int total_jobs;
int allowed_to_run_jobs;
int processed_jobs;
int locked;
struct mp_queue *job_queue;
};
struct job_subclass_list {
int subclass_cnt;
sem_t sem;
struct job_subclass *subclasses;
};
struct job_class {
int thread_class;
int min_threads;
int max_threads;
int cur_threads;
struct mp_queue *job_queue;
struct job_subclass_list *subclasses;
};
struct job_thread {
pthread_t pthread_id;
int id;
int thread_class;
int job_class_mask; // job classes allowed to run in this thread
int status; // 0 = absent; +1 = created, +2 = running/waiting, +4 = performing job
long long jobs_performed;
struct mp_queue *job_queue;
struct async_job *current_job; // job currently performed or 0 (for DEBUG only)
double current_job_start_time, last_job_time, tot_jobs_time;
int jobs_running[JC_MAX+1];
long long jobs_created;
long long jobs_active;
int thread_system_id;
struct drand48_data rand_data;
job_t timer_manager;
double wakeup_time;
struct job_class *job_class;
} __attribute__((aligned(128)));
struct job_message {
unsigned int type;
unsigned int flags;
unsigned int payload_ints;
job_t src;
void (*destructor)(struct job_message *M);
struct raw_message message;
struct job_message *next;
unsigned int payload[0];
};
struct job_message_queue {
struct mp_queue *unsorted;
struct job_message *first, *last;
unsigned int payload_magic;
};
struct job_timer_info {
struct event_timer ev;
void *extra;
double (*wakeup)(void *);
};
#define MAX_JOB_THREADS 256
long int lrand48_j (void);
long int mrand48_j (void);
double drand48_j (void);
int init_async_jobs (void);
int create_job_class (int job_class, int min_threads, int max_threads, int excl);
int create_job_class_sub (int job_class, int min_threads, int max_threads, int excl, int subclass_cnt);
job_t notify_job_create (int sig_class);
int create_job_thread_ex (int thread_class, void *(*thread_work)(void *));
int create_new_job_class (int job_class, int min_threads, int max_threads);
int create_new_job_class_sub (int job_class, int min_threads, int max_threads, int subclass_cnt);
void *job_thread_ex (void *arg, void (*work_one)(void *, int));
/* creates a new async job as described */
job_t create_async_job (job_function_t run_job, unsigned long long job_signals, int job_subclass, int custom_bytes, unsigned long long job_type, JOB_REF_ARG (parent_job));
void job_change_signals (job_t job, unsigned long long job_signals);
/* puts job into execution queue according to its priority class (actually, unlocks it and sends signal 0) */
int schedule_job (JOB_REF_ARG (job));
job_t job_incref (job_t job);
static inline job_t job_incref_f (job_t job) {
if (job) {
job_incref (job);
}
return job;
}
void job_decref (JOB_REF_ARG (job)); // if job->j_refcnt becomes 0, invokes j_execute with op = JS_FREE
static inline void job_decref_f (job_t job) {
job_decref (JOB_REF_PASS (job));
}
int unlock_job (JOB_REF_ARG (job));
int try_lock_job (job_t job, int set_flags, int clear_flags);
void complete_job (job_t job); // if JF_COMPLETED is not set, sets it and acts according to JFS_PARENT_*
int change_locked_job_subclass (job_t job, int new_subclass);
static inline int check_job_completion (job_t job) {
return job->j_flags & JF_COMPLETED;
}
static inline int check_job_validity (job_t job) {
return job && !check_job_completion (job);
}
static inline int check_parent_job_validity (job_t job) {
return check_job_validity (job->j_parent);
}
static inline int parent_job_aborted (job_t job) {
return (job->j_status & JSP_PARENT_INCOMPLETE) && job->j_parent && check_job_completion (job->j_parent);
}
static inline int job_parent_ptr_valid (job_t job) {
return (!(job->j_status & JSP_PARENT_RESPTR) || check_parent_job_validity (job));
}
static inline int job_fatal (job_t job, int error) {
if (!job->j_error) {
job->j_error = error;
}
return JOB_COMPLETED;
}
/* runs all pending jobs of class JF_CLASS_MAIN, then returns */
int run_pending_main_jobs (void);
/* ----------- JOB WAIT QUEUE ------ */
struct job_list_node;
typedef int (*job_list_node_type_t)(job_t list_job, int op, struct job_list_node *w);
struct job_list_node {
struct job_list_node *jl_next;
job_list_node_type_t jl_type;
int jl_custom[0];
};
job_t create_job_list (void);
int insert_job_into_job_list (job_t list_job, JOB_REF_ARG(job), int mode);
void update_all_thread_stats (void);
/* adds job to the list of jobs awaited by connection */
// int conn_wait_job (job_t job, connection_job_t c, double timeout, struct conn_query_functions *cq);
/* increases connection's generation (effectively clearing list of awaited jobs), then adds given job */
// int conn_wait_only_job (job_t job, connection_job_t c, double timeout, struct conn_query_functions *cq);
extern int max_job_thread_id;
void check_main_thread (void);
int job_timer_wakeup_gateway (event_timer_t *et);
int job_timer_check (job_t job);
void job_signal (JOB_REF_ARG (job), int signo);
void complete_subjob (job_t job, JOB_REF_ARG (parent), int status);
void job_timer_insert (job_t job, double timeout);
void job_timer_remove (job_t job);
int job_timer_active (job_t job);
void job_timer_init (job_t job);
double job_timer_wakeup_time (job_t job);
void jobs_check_all_timers (void);
static inline void check_thread_class (int class) {
assert (this_job_thread->job_class_mask & (1 << class));
}
void job_message_send (JOB_REF_ARG (job), JOB_REF_ARG (src), unsigned int type, struct raw_message *raw, int dup, int payload_ints, const unsigned int *payload, unsigned int flags, void (*destructor)(struct job_message *M));
void job_message_send_fake (JOB_REF_ARG (job), int (*receive_message)(job_t job, struct job_message *M, void *extra), void *extra, JOB_REF_ARG (src), unsigned int type, struct raw_message *raw, int dup, int payload_ints, const unsigned int *payload, unsigned int flags, void (*destructor)(struct job_message *M));
//void job_message_send_data (JOB_REF_ARG (job), JOB_REF_ARG (src), unsigned int type, void *ptr1, void *ptr2, int int1, long long long1, int payload_ints, const unsigned int *payload, unsigned int flags);
static inline void job_message_send_empty (JOB_REF_ARG (job), JOB_REF_ARG (src), unsigned int type, unsigned int flags) {
job_message_send (JOB_REF_PASS (job), JOB_REF_PASS (src), type, &empty_rwm, 1, 0, NULL, flags, NULL);
}
#define TL_TRUE 0x3fedd339
static inline int job_message_answer_true (struct job_message *M) {
if (M->src) {
job_message_send (JOB_REF_PASS (M->src), JOB_REF_NULL, TL_TRUE, &empty_rwm, 1, M->payload_ints, M->payload, JMC_EXTRACT_ANSWER (M->flags), NULL);
}
return 1;
}
static inline int job_message_continuation (job_t job, struct job_message *M, int payload_magic) {
if (M->payload_ints >= 1) {
assert (M->payload[0] == payload_magic);
assert (M->payload_ints == 5);
int (*func)(job_t, struct job_message *, void *) = *(void **)(M->payload + 1);
void *extra = *(void **)(M->payload + 3);
assert (func);
return func (job, M, extra);
}
return 1;
}
void job_message_queue_free (job_t job);
void job_message_queue_init (job_t job);
void job_message_queue_work (job_t job, int (*receive_message)(job_t job, struct job_message *M, void *extra), void *extra, unsigned int mask);
int job_free (JOB_REF_ARG (job));
job_t job_timer_alloc (int thread_class, double (*alarm)(void *), void *extra);
struct thread_callback {
struct thread_callback *next;
void (*new_thread)(void);
};
void register_thread_callback (struct thread_callback *cb);
job_t alloc_timer_manager (int thread_class);
struct job_message_payload {
job_t job;
int message_class;
int payload_ints;
unsigned int payload[0];
};
static inline struct job_message_payload *job_message_payload_alloc (JOB_REF_ARG (job), int message_class, int payload_ints, unsigned int *payload) {
struct job_message_payload *P = malloc (sizeof (*P) + 4 * payload_ints);
P->message_class = message_class;
P->payload_ints = payload_ints;
P->job = PTR_MOVE (job);
memcpy (P->payload, payload, 4 * payload_ints);
return P;
}
long long jobs_get_allocated_memoty (void);
unsigned int *payload_continuation_create (unsigned int magic, int (*func)(job_t, struct job_message *, void *extra), void *extra);
#define PAYLOAD_CONTINUATION(_magic,_func,_extra) 5, payload_continuation_create (_magic, _func, _extra)
extern struct job_thread JobThreads[];
#define CNCT2(a,b) a ## b
#define CNCT(a,b) CNCT2(a,b)
#define MODULE_STAT_TYPE struct CNCT(jobs_module_stat_,MODULE)
#define MODULE_STR(a) MODULE_STR2(a)
#define MODULE_STR2(a) #a
#define MODULE_STAT_PREFIX_NAME CNCT(jobs_module_state_prefix_,MODULE)
#define MODULE_STAT_PREFIX char *MODULE_STAT_PREFIX_NAME
#define MODULE_STAT CNCT(jobs_module_stat_,MODULE)
#define MODULE_STAT_ARR CNCT(jobs_module_list_stat_,MODULE)
#define MODULE_STAT_FUNCTION int CNCT(MODULE,_prepare_stat) (stats_buffer_t *sb) { \
sb_printf (sb, ">>>>>>%s>>>>>>\tstart\n", MODULE_STR(MODULE));
#define MODULE_STAT_FUNCTION_END \
sb_printf (sb, "<<<<<<%s<<<<<<\tend\n", MODULE_STR(MODULE)); \
return sb->pos; }
#define MODULE_INIT \
MODULE_STAT_TYPE *MODULE_STAT_ARR[MAX_JOB_THREADS]; \
__thread MODULE_STAT_TYPE *MODULE_STAT; \
MODULE_STAT_PREFIX; \
\
void CNCT(jobs_module_thread_init_,MODULE) (void) {\
int id = get_this_thread_id ();\
assert (id >= 0 && id < MAX_JOB_THREADS);\
MODULE_STAT = MODULE_STAT_ARR[id] = calloc (sizeof (MODULE_STAT_TYPE), 1);\
} \
\
struct thread_callback CNCT(MODULE,_thread_callback) = { \
.new_thread = CNCT(jobs_module_thread_init_, MODULE), \
.next = NULL \
}; \
void CNCT(jobs_module_register_,MODULE) (void) __attribute__ ((constructor));\
void CNCT(jobs_module_register_,MODULE) (void) { \
register_thread_callback (&CNCT(MODULE,_thread_callback)); \
}
/*
This file is part of MTProto-Server
MTProto-Server is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MTProto-Server. If not, see <http://www.gnu.org/licenses/>.
This program is released under the GPL with the additional exemption
that compiling, linking, and/or using OpenSSL is allowed.
You are free to remove this exemption from derived works.
Copyright 2012-2015 Nikolai Durov
2012-2013 Andrey Lopatin
2014-2018 Telegram Messenger Inc
*/
#pragma once
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/aes.h>
#include "rpc-const.h"
#define tls_push() { struct tl_out_state *tlio_out = tl_out_state_alloc ();
#define tls_pop() tl_out_state_free (tlio_out); }
#define TLS_START(C) tls_push(); tls_init_tcp_raw_msg (tlio_out, C, 0);
#define TLS_START_UNALIGN(C) tls_push(); tls_init_tcp_raw_msg_unaligned (tlio_out, C, 0);
#define TLS_END tl_store_end_ext (0); tls_pop();
/* DH key exchange protocol data structures */
#define CODE_req_pq 0x60469778
#define CODE_req_pq_multi 0xbe7e8ef1
#define CODE_req_DH_params 0xd712e4be
#define CODE_set_client_DH_params 0xf5045f1f
/* RPC for front/proxy */
#define RPC_PROXY_REQ 0x36cef1ee
#define RPC_PROXY_ANS 0x4403da0d
#define RPC_CLOSE_CONN 0x1fcf425d
#define RPC_CLOSE_EXT 0x5eb634a2
#define RPC_SIMPLE_ACK 0x3bac409b
/* not really a limit, for struct encrypted_message only */
// #define MAX_MESSAGE_INTS 16384
#define MAX_MESSAGE_INTS 1048576
#define MAX_PROTO_MESSAGE_INTS 524288
#pragma pack(push,4)
struct encrypted_message {
// unencrypted header
long long auth_key_id;
char msg_key[16];
// encrypted part, starts with encrypted header
long long server_salt;
long long session_id;
// first message follows
long long msg_id;
int seq_no;
int msg_len; // divisible by 4
int message[MAX_MESSAGE_INTS + 8];
};
#define MAX_PROXY_EXTRA_BYTES 16384
struct rpc_proxy_req {
int type; // RPC_PROXY_REQ
int flags;
long long ext_conn_id;
unsigned char remote_ipv6[16];
int remote_port;
unsigned char our_ipv6[16];
int our_port;
union {
int data[0];
struct {
int extra_bytes;
int extra[MAX_PROXY_EXTRA_BYTES / 4];
};
};
};
struct rpc_proxy_ans {
int type; // RPC_PROXY_ANS
int flags; // +16 = small error packet, +8 = flush immediately
long long ext_conn_id;
int data[];
};
struct rpc_close_conn {
int type; // RPC_CLOSE_CONN
long long ext_conn_id;
};
struct rpc_close_ext {
int type; // RPC_CLOSE_EXT
long long ext_conn_id;
};
struct rpc_simple_ack {
int type; // RPC_SIMPLE_ACK
long long ext_conn_id;
int confirm_key;
};
#pragma pack(pop)
/*
This file is part of MTProto-Server
MTProto-Server is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MTProto-Server. If not, see <http://www.gnu.org/licenses/>.
This program is released under the GPL with the additional exemption
that compiling, linking, and/or using OpenSSL is allowed.
You are free to remove this exemption from derived works.
Copyright 2012-2018 Nikolai Durov
2012-2014 Andrey Lopatin
2014-2018 Telegram Messenger Inc
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/mman.h>
#include "md5.h"
#include "resolver.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-connections.h"
#include "net/net-crypto-aes.h"
#include "mtproto-common.h"
#include "mtproto-config.h"
#include "engine/engine.h"
#include "common/parse-config.h"
#include "common/server-functions.h"
/*
*
* CONFIGURATION PARSER
*
*/
struct mf_config Config[2], *CurConf = Config, *NextConf = Config + 1;
//#define MAX_CONFIG_SIZE (1 << 20)
//char config_buff[MAX_CONFIG_SIZE+4], *config_filename, *cfg_start, *cfg_end, *cfg_cur;
//int config_bytes, cfg_lno, cfg_lex = -1;
char *config_filename;
static int cfg_getlex_ext (void) {
switch (cfg_skipspc()) {
case ';':
case ':':
case '{':
case '}':
return cfg_lex = *cfg_cur++;
case 'm':
if (!memcmp (cfg_cur, "min_connections", 15)) {
cfg_cur += 15;
return cfg_lex = 'x';
}
if (!memcmp (cfg_cur, "max_connections", 15)) {
cfg_cur += 15;
return cfg_lex = 'X';
}
break;
case 'p':
if (!memcmp (cfg_cur, "proxy_for", 9)) {
cfg_cur += 9;
return cfg_lex = 'Y';
} else if (!memcmp (cfg_cur, "proxy", 5)) {
cfg_cur += 5;
return cfg_lex = 'y';
}
break;
case 't':
if (!memcmp (cfg_cur, "timeout", 7)) {
cfg_cur += 7;
return cfg_lex = 't';
}
break;
case 'd':
if (!memcmp (cfg_cur, "default", 7)) {
cfg_cur += 7;
return cfg_lex = 'D';
}
break;
case 0:
return cfg_lex = 0;
}
return cfg_lex = -1;
}
void forget_cluster_targets (struct mf_group_stats *GS, struct mf_cluster *MFC, int do_destroy_targets) {
if (MFC->cluster_targets) {
MFC->cluster_targets = 0;
}
MFC->targets_num = MFC->write_targets_num = 0;
MFC->targets_allocated = 0;
}
void clear_mf_cluster (struct mf_group_stats *GS, struct mf_cluster *MFC, int do_destroy_targets) {
forget_cluster_targets (GS, MFC, do_destroy_targets);
MFC->flags = 0;
GS->tot_clusters --;
}
void clear_config (struct mf_config *MC, int do_destroy_targets) {
int j;
if (do_destroy_targets) {
for (j = 0; j < MC->tot_targets; j++) {
vkprintf (1, "destroying target %s:%d\n", inet_ntoa (CONN_TARGET_INFO(MC->targets[j])->target), CONN_TARGET_INFO(MC->targets[j])->port);
destroy_target (JOB_REF_PASS (MC->targets[j]));
}
memset (MC->targets, 0, MC->tot_targets * sizeof (conn_target_job_t));
}
for (j = 0; j < MC->auth_clusters; j++) {
clear_mf_cluster (&MC->auth_stats, &MC->auth_cluster[j], do_destroy_targets);
}
MC->tot_targets = 0;
MC->auth_clusters = 0;
memset (&MC->auth_stats, 0, sizeof (struct mf_group_stats));
}
conn_target_job_t *cfg_parse_server_port (struct mf_config *MC, int flags) {
if (MC->tot_targets >= MAX_CFG_TARGETS) {
syntax ("too many targets (%d)", MC->tot_targets);
return 0;
}
struct hostent *h = cfg_gethost ();
if (!h) {
return 0;
}
if (h->h_addrtype == AF_INET) {
default_cfg_ct.target = *((struct in_addr *) h->h_addr);
memset (default_cfg_ct.target_ipv6, 0, 16);
} else if (h->h_addrtype == AF_INET6) {
default_cfg_ct.target.s_addr = 0;
memcpy (default_cfg_ct.target_ipv6, h->h_addr, 16);
} else {
syntax ("cannot resolve hostname");
return 0;
}
//*(cfg_cur += l) = c;
cfg_getlex_ext ();
if (expect_lexem (':') < 0) {
return 0;
}
default_cfg_ct.port = cfg_getint();
if (!default_cfg_ct.port) {
syntax ("port number expected");
return 0;
}
if (default_cfg_ct.port <= 0 || default_cfg_ct.port >= 0x10000) {
syntax ("port number %d out of range", default_cfg_ct.port);
return 0;
}
default_cfg_ct.min_connections = MC->min_connections;
default_cfg_ct.max_connections = MC->max_connections;
default_cfg_ct.reconnect_timeout = 1.0 + 0.1 * drand48 ();
if ((flags & 1)) {
int was_created = -1;
conn_target_job_t D = create_target (&default_cfg_ct, &was_created);
MC->targets[MC->tot_targets] = D;
vkprintf (3, "new target %p created (%d): ip %s, port %d\n", D, was_created, inet_ntoa (default_cfg_ct.target), default_cfg_ct.port);
}
return &MC->targets[MC->tot_targets++];
}
static void init_old_mf_cluster (struct mf_group_stats *GS, struct mf_cluster *MFC, conn_target_job_t *targets, int targets_num, int flags, int cluster_id) {
MFC->flags = flags;
MFC->targets_num = targets_num;
MFC->write_targets_num = targets_num;
MFC->targets_allocated = 0;
MFC->cluster_targets = targets;
MFC->cluster_id = cluster_id;
GS->tot_clusters ++;
}
static int extend_old_mf_cluster (struct mf_cluster *MFC, conn_target_job_t *target, int cluster_id) {
if (MFC->cluster_targets + MFC->targets_num != target) {
return 0;
}
if (MFC->cluster_id != cluster_id) {
return 0;
}
MFC->write_targets_num = ++(MFC->targets_num);
return 1;
}
struct mf_cluster *mf_cluster_lookup (struct mf_config *MC, int cluster_id, int force) {
int i;
for (i = 0; i < MC->auth_clusters; i++) {
if (MC->auth_cluster[i].cluster_id == cluster_id) {
return &(MC->auth_cluster[i]);
}
}
return force ? MC->default_cluster : 0;
}
void dump_mf_cluster (struct mf_cluster *MFC) {
int i;
kprintf ("Current state of cluster `%s` (N=%d, M=%d, alloc=%d):\n", "(nil)", MFC->targets_num, MFC->write_targets_num, MFC->targets_allocated);
for (i = 0; i < MFC->targets_num; i++) {
kprintf ("Target #%d [%c]: %s:%d\n", i, i < MFC->write_targets_num ? 'W' : 'R', show_ip (ntohl (CONN_TARGET_INFO(MFC->cluster_targets[i])->target.s_addr)), CONN_TARGET_INFO(MFC->cluster_targets[i])->port);
}
}
static void preinit_config (struct mf_config *MC) {
MC->tot_targets = 0;
MC->auth_clusters = 0;
MC->min_connections = default_cfg_min_connections;
MC->max_connections = default_cfg_max_connections;
MC->timeout = 0.3;
MC->default_cluster_id = 0;
MC->default_cluster = 0;
}
// flags = 0 -- syntax check only (first pass), flags = 1 -- create targets and points as well (second pass)
// flags: +2 = allow proxies, +4 = allow proxies only, +16 = do not load file
int parse_config (struct mf_config *MC, int flags, int config_fd) {
conn_target_job_t *targ_ptr;
int have_proxy = 0;
assert (flags & 4);
if (!(flags & 17)) {
if (load_config (config_filename, config_fd) < 0) {
return -2;
}
}
reset_config ();
preinit_config (MC);
while (cfg_skipspc ()) {
int t, target_dc = 0;
switch (t = cfg_getlex_ext ()) {
case 't':
MC->timeout = cfg_getint ();
if (MC->timeout < 10 || MC->timeout > 30000) {
Syntax ("invalid timeout");
}
MC->timeout /= 1000;
break;
case 'D':
case 'Y': {
long long targ_dc = cfg_getint_signed_zero();
if (targ_dc < -0x8000 || targ_dc >= 0x8000) {
Syntax ("invalid target id (integer -32768..32767 expected)", targ_dc);
}
if (t == 'D') {
MC->default_cluster_id = targ_dc;
break;
}
if (*cfg_cur != ' ' && *cfg_cur != 9) {
Syntax ("space expected after target id");
}
cfg_skspc ();
target_dc = targ_dc;
}
case 'y': {
have_proxy |= 1;
if (MC->auth_clusters >= MAX_CFG_CLUSTERS) {
Syntax ("too many auth clusters", MC->auth_clusters);
}
targ_ptr = cfg_parse_server_port (MC, flags);
if (!targ_ptr) {
return -1;
}
struct mf_cluster *MFC = mf_cluster_lookup (MC, target_dc, 0);
if (!MFC) {
vkprintf (3, "-> added target to new auth_cluster #%d\n", MC->auth_clusters);
if (flags & 1) {
init_old_mf_cluster (&MC->auth_stats, &MC->auth_cluster[MC->auth_clusters], targ_ptr, 1, 1, target_dc);
} else {
MC->auth_cluster[MC->auth_clusters].cluster_id = target_dc;
}
MC->auth_clusters ++;
} else if (MFC == &MC->auth_cluster[MC->auth_clusters - 1]) {
vkprintf (3, "-> added target to old auth_cluster #%d\n", MC->auth_clusters - 1);
if (flags & 1) {
if (!extend_old_mf_cluster (MFC, targ_ptr, target_dc)) {
Syntax ("IMPOSSIBLE");
}
}
} else {
Syntax ("proxies for dc %d intermixed", target_dc);
}
break;
}
case 'X':
MC->max_connections = cfg_getint ();
if (MC->max_connections < MC->min_connections || MC->max_connections > 1000) {
Syntax ("invalid max connections");
}
break;
case 'x':
MC->min_connections = cfg_getint ();
if (MC->min_connections < 1 || MC->min_connections > MC->max_connections) {
Syntax ("invalid min connections");
}
break;
case 0:
break;
default:
Syntax ("'proxy <ip>:<port>;' expected");
}
if (!t) {
break;
}
cfg_getlex_ext ();
Expect (';');
}
if (have_proxy != 1) {
Syntax ("expected to find a mtproto-proxy configuration with `proxy' directives");
}
MC->have_proxy = have_proxy & 1;
if (!MC->auth_clusters) {
Syntax ("no MTProto next proxy servers defined to forward queries to");
}
MC->default_cluster = mf_cluster_lookup (MC, MC->default_cluster_id, 0);
return 0;
}
static int need_reload_config = 0;
// flags: +1 = create targets and connections, +2 = allow proxies, +4 = allow proxies only, +16 = do not re-load file itself, +32 = preload config + perform syntax check, do not apply
int do_reload_config (int flags) {
int res;
need_reload_config = 0;
int fd = -1;
assert (flags & 4);
if (!(flags & 16)) {
fd = open (config_filename, O_RDONLY);
if (fd < 0) {
kprintf ("cannot re-read config file %s: %m\n", config_filename);
return -1;
}
res = kdb_load_hosts ();
if (res > 0) {
vkprintf (1, "/etc/hosts changed, reloaded\n");
}
}
res = parse_config (NextConf, flags & -2, fd);
if (fd >= 0) {
close (fd);
}
// clear_config (NextConf);
if (res < 0) {
kprintf ("error while re-reading config file %s, new configuration NOT applied\n", config_filename);
return res;
}
if ((flags & 32)) {
return 0;
}
res = parse_config (NextConf, flags | 1, -1);
if (res < 0) {
clear_config (NextConf, 0);
kprintf ("fatal error while re-reading config file %s\n", config_filename);
exit (-res);
}
struct mf_config *tmp = CurConf;
CurConf = NextConf;
NextConf = tmp;
clear_config (NextConf, 1);
if (flags & 1) {
create_all_outbound_connections ();
}
CurConf->config_loaded_at = now ? now : time (0);
CurConf->config_bytes = config_bytes;
CurConf->config_md5_hex = malloc (33);
md5_hex_config (CurConf->config_md5_hex);
CurConf->config_md5_hex[32] = 0;
kprintf ("configuration file %s re-read successfully (%d bytes parsed), new configuration active\n", config_filename, config_bytes);
return 0;
}
/*
This file is part of MTProto-Server
MTProto-Server is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MTProto-Server. If not, see <http://www.gnu.org/licenses/>.
This program is released under the GPL with the additional exemption
that compiling, linking, and/or using OpenSSL is allowed.
You are free to remove this exemption from derived works.
Copyright 2012-2018 Nikolai Durov
2012-2014 Andrey Lopatin
2014-2018 Telegram Messenger Inc
*/
#pragma once
#define MAX_CFG_CLUSTERS 1024
#define MAX_CFG_TARGETS 4096
#define MAX_CLUSTER_TARGETS 1024
struct mf_cluster {
int targets_num; // 1 for old-fashioned
int write_targets_num;
int targets_allocated; // size of `cluster_targets` and `balance_hashes` arrays
int flags;
int cluster_id; // datacenter # or 0
conn_target_job_t *cluster_targets; // N entries
};
struct mf_group_stats {
int tot_clusters;
};
struct mf_config {
int tot_targets;
int auth_clusters, default_cluster_id;
int min_connections, max_connections;
double timeout;
int config_bytes, config_loaded_at;
char *config_md5_hex;
struct mf_group_stats auth_stats;
int have_proxy;
struct mf_cluster *default_cluster;
conn_target_job_t targets[MAX_CFG_TARGETS];
struct mf_cluster auth_cluster[MAX_CFG_CLUSTERS];
// struct mf_cluster *clusters_by_hash[MAX_CFG_CLUSTERS*2];
};
extern struct mf_config *CurConf;
extern char *config_filename;
extern struct conn_target_info default_cfg_ct;
extern int default_cfg_min_connections, default_cfg_max_connections;
// (re)load configuration file
int do_reload_config (int create_conn);
struct mf_cluster *mf_cluster_lookup (struct mf_config *MC, int cluster_id, int force);
/*
This file is part of MTProto-proxy
MTProto-proxy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
MTProto-Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with MTProto-Server. If not, see <http://www.gnu.org/licenses/>.
This program is released under the GPL with the additional exemption
that compiling, linking, and/or using OpenSSL is allowed.
You are free to remove this exemption from derived works.
Copyright 2012-2018 Nikolai Durov
2012-2014 Andrey Lopatin
2014-2018 Telegram Messenger Inc
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <netdb.h>
#include <ctype.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include "crc32.h"
#include "md5.h"
#include "resolver.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "server-functions.h"
#include "net/net-tcp-connections.h"
#include "net/net-rpc-targets.h"
#include "net/net-http-server.h"
#include "net/net-tcp-rpc-server.h"
#include "net/net-tcp-rpc-client.h"
#include "net/net-tcp-rpc-ext-server.h"
#include "net/net-crypto-aes.h"
#include "net/net-crypto-dh.h"
#include "mtproto-common.h"
#include "mtproto-config.h"
#include "common/tl-parse.h"
#include "engine/engine.h"
#ifndef COMMIT
#define COMMIT "unknown"
#endif
#define VERSION_STR "mtproxy-0.01"
const char FullVersionStr[] = VERSION_STR " compiled at " __DATE__ " " __TIME__ " by gcc " __VERSION__ " "
#ifdef __LP64__
"64-bit"
#else
"32-bit"
#endif
" after commit " COMMIT;
#define EXT_CONN_TABLE_SIZE (1 << 22)
#define EXT_CONN_HASH_SHIFT 20
#define EXT_CONN_HASH_SIZE (1 << EXT_CONN_HASH_SHIFT)
#define RPC_TIMEOUT_INTERVAL 5.0
#define MAX_HTTP_LISTEN_PORTS 128
#define HTTP_MAX_WAIT_TIMEOUT 960.0
#define PING_INTERVAL 5.0
#define STOP_INTERVAL (2 * ping_interval)
#define FAIL_INTERVAL (20 * ping_interval)
#define RESPONSE_FAIL_TIMEOUT 5
#define CONNECT_TIMEOUT 3
#define MAX_POST_SIZE (262144 * 4 - 4096)
#define DEFAULT_WINDOW_CLAMP 131072
// #define DEFAULT_OUTBOUND_CONNECTION_CREATION_RATE 1000000
#if 0
#define MAX_CONNECTION_BUFFER_SPACE (1 << 10) //(1 << 25)
#define MAX_MTFRONT_NB 1 //((NB_max * 3) >> 2)
#else
#define MAX_CONNECTION_BUFFER_SPACE (1 << 25)
#define MAX_MTFRONT_NB ((NB_max * 3) >> 2)
#endif
double ping_interval = PING_INTERVAL;
int window_clamp = DEFAULT_WINDOW_CLAMP;
#define PROXY_MODE_OUT 2
int proxy_mode;
#define IS_PROXY_IN 0
#define IS_PROXY_OUT 1
#define IS_PROXY_INOUT 1
#define TL_HTTP_QUERY_INFO 0xd45ab381
#define TL_PROXY_TAG 0xdb1e26ae
conn_type_t ct_http_server_mtfront, ct_tcp_rpc_ext_server_mtfront, ct_tcp_rpc_server_mtfront;
long long connections_failed_lru, connections_failed_flood;
long long api_invoke_requests;
volatile int sigpoll_cnt;
#define STATS_BUFF_SIZE (1 << 20)
int stats_buff_len;
char stats_buff[STATS_BUFF_SIZE];
// current HTTP query headers
char cur_http_origin[1024], cur_http_referer[1024], cur_http_user_agent[1024];
int cur_http_origin_len, cur_http_referer_len, cur_http_user_agent_len;
int check_conn_buffers (connection_job_t c);
void lru_insert_conn (connection_job_t c);
/*
*
* CONFIGURATION PARSER SETUP
*
*/
#define DEFAULT_CFG_MIN_CONNECTIONS 4
#define DEFAULT_CFG_MAX_CONNECTIONS 8
int default_cfg_min_connections = DEFAULT_CFG_MIN_CONNECTIONS;
int default_cfg_max_connections = DEFAULT_CFG_MAX_CONNECTIONS;
struct tcp_rpc_client_functions mtfront_rpc_client;
conn_type_t ct_tcp_rpc_client_mtfront;
struct conn_target_info default_cfg_ct = {
.min_connections = DEFAULT_CFG_MIN_CONNECTIONS,
.max_connections = DEFAULT_CFG_MAX_CONNECTIONS,
.type = &ct_tcp_rpc_client_mtfront,
.extra = (void *)&mtfront_rpc_client,
.reconnect_timeout = 17
};
/*
*
* EXTERNAL CONNECTIONS TABLE
*
*/
struct ext_connection {
struct ext_connection *o_prev, *o_next; // list of all with same out_fd
struct ext_connection *i_prev, *i_next; // list of all with same in_fd
struct ext_connection *h_next; // next in hash on (in_fd, in_conn_id)
int in_fd, in_gen;
int out_fd, out_gen;
long long in_conn_id;
long long out_conn_id;
long long auth_key_id;
struct ext_connection *lru_prev, *lru_next;
};
struct ext_connection_ref {
struct ext_connection *ref;
long long out_conn_id;
};
long long ext_connections, ext_connections_created;
struct ext_connection_ref OutExtConnections[EXT_CONN_TABLE_SIZE];
struct ext_connection *InExtConnectionHash[EXT_CONN_HASH_SIZE];
struct ext_connection ExtConnectionHead[MAX_CONNECTIONS];
void lru_delete_ext_conn (struct ext_connection *Ext);
static inline void check_engine_class (void) {
check_thread_class (JC_ENGINE);
}
static inline int ext_conn_hash (int in_fd, long long in_conn_id) {
unsigned long long h = (unsigned long long) in_fd * 11400714819323198485ULL + (unsigned long long) in_conn_id * 13043817825332782213ULL;
return (h >> (64 - EXT_CONN_HASH_SHIFT));
}
// makes sense only for !IS_PROXY_IN
// returns the only ext_connection with given in_fd
struct ext_connection *get_ext_connection_by_in_fd (int in_fd) {
check_engine_class ();
assert ((unsigned) in_fd < MAX_CONNECTIONS);
struct ext_connection *H = &ExtConnectionHead[in_fd];
struct ext_connection *Ex = H->i_next;
assert (H->i_next == H->i_prev);
if (!Ex || Ex == H) {
return 0;
}
assert (Ex->in_fd == in_fd);
return Ex;
}
// mode: 0 = find, 1 = delete, 2 = create if not found, 3 = find or create
struct ext_connection *get_ext_connection_by_in_conn_id (int in_fd, int in_gen, long long in_conn_id, int mode, int *created) {
check_engine_class ();
int h = ext_conn_hash (in_fd, in_conn_id);
struct ext_connection **prev = &InExtConnectionHash[h], *cur = *prev;
for (; cur; cur = *prev) {
if (cur->in_fd == in_fd && cur->in_conn_id == in_conn_id) {
assert (cur->out_conn_id);
if (mode == 0 || mode == 3) {
return cur;
}
if (mode != 1) {
return 0;
}
if (cur->i_next) {
cur->i_next->i_prev = cur->i_prev;
cur->i_prev->i_next = cur->i_next;
cur->i_next = cur->i_prev = 0;
}
if (cur->o_next) {
cur->o_next->o_prev = cur->o_prev;
cur->o_prev->o_next = cur->o_next;
cur->o_next = cur->o_prev = 0;
}
lru_delete_ext_conn (cur);
*prev = cur->h_next;
cur->h_next = 0;
int h = cur->out_conn_id & (EXT_CONN_TABLE_SIZE - 1);
assert (OutExtConnections[h].ref == cur);
assert (OutExtConnections[h].out_conn_id == cur->out_conn_id);
OutExtConnections[h].ref = 0;
cur->out_conn_id = 0;
memset (cur, 0, sizeof (struct ext_connection));
free (cur);
ext_connections--;
return (void *) -1L;
}
prev = &(cur->h_next);
}
if (mode != 2 && mode != 3) {
return 0;
}
assert (ext_connections < EXT_CONN_TABLE_SIZE / 2);
cur = calloc (sizeof (struct ext_connection), 1);
assert (cur);
cur->h_next = InExtConnectionHash[h];
InExtConnectionHash[h] = cur;
cur->in_fd = in_fd;
cur->in_gen = in_gen;
cur->in_conn_id = in_conn_id;
assert ((unsigned) in_fd < MAX_CONNECTIONS);
if (in_fd) {
struct ext_connection *H = &ExtConnectionHead[in_fd];
if (!H->i_next) {
H->i_next = H->i_prev = H;
}
assert (H->i_next == H);
cur->i_next = H;
cur->i_prev = H->i_prev;
H->i_prev->i_next = cur;
H->i_prev = cur;
}
h = in_conn_id ? lrand48() : in_fd;
while (OutExtConnections[h &= (EXT_CONN_TABLE_SIZE - 1)].ref) {
h = lrand48();
}
OutExtConnections[h].ref = cur;
cur->out_conn_id = OutExtConnections[h].out_conn_id = (OutExtConnections[h].out_conn_id | (EXT_CONN_TABLE_SIZE - 1)) + 1 + h;
assert (cur->out_conn_id);
if (created) {
++*created;
}
ext_connections++;
ext_connections_created++;
return cur;
}
struct ext_connection *find_ext_connection_by_out_conn_id (long long out_conn_id) {
check_engine_class ();
int h = out_conn_id & (EXT_CONN_TABLE_SIZE - 1);
struct ext_connection *cur = OutExtConnections[h].ref;
if (!cur || OutExtConnections[h].out_conn_id != out_conn_id) {
return 0;
}
assert (cur->out_conn_id == out_conn_id);
return cur;
}
// MUST be new
struct ext_connection *create_ext_connection (connection_job_t CI, long long in_conn_id, connection_job_t CO, long long auth_key_id) {
check_engine_class ();
struct ext_connection *Ex = get_ext_connection_by_in_conn_id (CONN_INFO(CI)->fd, CONN_INFO(CI)->generation, in_conn_id, 2, 0);
assert (Ex && "ext_connection already exists");
assert (!Ex->out_fd && !Ex->o_next && !Ex->auth_key_id);
assert (!CO || (unsigned) CONN_INFO(CO)->fd < MAX_CONNECTIONS);
assert (CO != CI);
if (CO) {
struct ext_connection *H = &ExtConnectionHead[CONN_INFO(CO)->fd];
assert (H->o_next);
Ex->o_next = H;
Ex->o_prev = H->o_prev;
H->o_prev->o_next = Ex;
H->o_prev = Ex;
Ex->out_fd = CONN_INFO(CO)->fd;
Ex->out_gen = CONN_INFO(CO)->generation;
}
Ex->auth_key_id = auth_key_id;
return Ex;
}
static int _notify_remote_closed (JOB_REF_ARG(C), long long out_conn_id);
void remove_ext_connection (struct ext_connection *Ex, int send_notifications) {
assert (Ex);
assert (Ex->out_conn_id);
assert (Ex == find_ext_connection_by_out_conn_id (Ex->out_conn_id));
if (Ex->out_fd) {
assert ((unsigned) Ex->out_fd < MAX_CONNECTIONS);
assert (Ex->o_next);
if (send_notifications & 1) {
connection_job_t CO = connection_get_by_fd_generation (Ex->out_fd, Ex->out_gen);
if (CO) {
_notify_remote_closed (JOB_REF_PASS (CO), Ex->out_conn_id);
}
}
}
if (Ex->in_fd) {
assert ((unsigned) Ex->in_fd < MAX_CONNECTIONS);
assert (Ex->i_next);
if (send_notifications & 2) {
connection_job_t CI = connection_get_by_fd_generation (Ex->in_fd, Ex->in_gen);
if (Ex->in_conn_id) {
assert (0);
} else {
if (CI) {
fail_connection (CI, -33);
job_decref (JOB_REF_PASS (CI));
}
}
}
}
assert (get_ext_connection_by_in_conn_id (Ex->in_fd, Ex->in_gen, Ex->in_conn_id, 1, 0) == (void *) -1L);
}
/*
*
* MULTIPROCESS STATISTICS
*
*/
#define MAX_WORKERS 256
struct worker_stats {
int cnt;
int updated_at;
struct buffers_stat bufs;
struct connections_stat conn;
int allocated_aes_crypto, allocated_aes_crypto_temp;
long long tot_dh_rounds[3];
int ev_heap_size;
int http_connections;
long long get_queries;
int pending_http_queries;
long long accept_calls_failed, accept_nonblock_set_failed, accept_connection_limit_failed,
accept_rate_limit_failed, accept_init_accepted_failed;
long long active_rpcs, active_rpcs_created;
long long rpc_dropped_running, rpc_dropped_answers;
long long tot_forwarded_queries, expired_forwarded_queries;
long long tot_forwarded_responses;
long long dropped_queries, dropped_responses;
long long tot_forwarded_simple_acks, dropped_simple_acks;
long long mtproto_proxy_errors;
long long connections_failed_lru, connections_failed_flood;
long long ext_connections, ext_connections_created;
long long http_queries, http_bad_headers;
};
struct worker_stats *WStats, SumStats;
int worker_id, workers, slave_mode, parent_pid;
int pids[MAX_WORKERS];
long long get_queries;
long long http_queries;
int pending_http_queries;
long long active_rpcs, active_rpcs_created;
long long rpc_dropped_running, rpc_dropped_answers;
long long tot_forwarded_queries, expired_forwarded_queries, dropped_queries;
long long tot_forwarded_responses, dropped_responses;
long long tot_forwarded_simple_acks, dropped_simple_acks;
long long mtproto_proxy_errors;
char proxy_tag[16];
int proxy_tag_set;
static void update_local_stats_copy (struct worker_stats *S) {
S->cnt++;
__sync_synchronize();
S->updated_at = now;
#define UPD(x) S->x = x;
fetch_tot_dh_rounds_stat (S->tot_dh_rounds);
fetch_connections_stat (&S->conn);
fetch_aes_crypto_stat (&S->allocated_aes_crypto, &S->allocated_aes_crypto_temp);
fetch_buffers_stat (&S->bufs);
UPD (ev_heap_size);
UPD (get_queries);
UPD (http_connections);
UPD (pending_http_queries);
UPD (active_rpcs);
UPD (active_rpcs_created);
UPD (rpc_dropped_running);
UPD (rpc_dropped_answers);
UPD (tot_forwarded_queries);
UPD (expired_forwarded_queries);
UPD (dropped_queries);
UPD (tot_forwarded_responses);
UPD (dropped_responses);
UPD (tot_forwarded_simple_acks);
UPD (dropped_simple_acks);
UPD (mtproto_proxy_errors);
UPD (connections_failed_lru);
UPD (connections_failed_flood);
UPD (ext_connections);
UPD (ext_connections_created);
UPD (http_queries);
UPD (http_bad_headers);
#undef UPD
__sync_synchronize();
S->cnt++;
__sync_synchronize();
}
static inline void add_stats (struct worker_stats *W) {
#define UPD(x) SumStats.x += W->x;
UPD (tot_dh_rounds[0]);
UPD (tot_dh_rounds[1]);
UPD (tot_dh_rounds[2]);
UPD (conn.active_connections);
UPD (conn.active_dh_connections);
UPD (conn.outbound_connections);
UPD (conn.active_outbound_connections);
UPD (conn.ready_outbound_connections);
UPD (conn.active_special_connections);
UPD (conn.max_special_connections);
UPD (conn.allocated_connections);
UPD (conn.allocated_outbound_connections);
UPD (conn.allocated_inbound_connections);
UPD (conn.allocated_socket_connections);
UPD (conn.allocated_targets);
UPD (conn.ready_targets);
UPD (conn.active_targets);
UPD (conn.inactive_targets);
UPD (conn.tcp_readv_calls);
UPD (conn.tcp_readv_intr);
UPD (conn.tcp_readv_bytes);
UPD (conn.tcp_writev_calls);
UPD (conn.tcp_writev_intr);
UPD (conn.tcp_writev_bytes);
UPD (conn.accept_calls_failed);
UPD (conn.accept_nonblock_set_failed);
UPD (conn.accept_rate_limit_failed);
UPD (conn.accept_init_accepted_failed);
UPD (allocated_aes_crypto);
UPD (allocated_aes_crypto_temp);
UPD (bufs.total_used_buffers_size);
UPD (bufs.allocated_buffer_bytes);
UPD (bufs.total_used_buffers);
UPD (bufs.allocated_buffer_chunks);
UPD (bufs.max_allocated_buffer_chunks);
UPD (bufs.max_allocated_buffer_bytes);
UPD (bufs.max_buffer_chunks);
UPD (bufs.buffer_chunk_alloc_ops);
UPD (ev_heap_size);
UPD (get_queries);
UPD (http_connections);
UPD (pending_http_queries);
UPD (active_rpcs);
UPD (active_rpcs_created);
UPD (rpc_dropped_running);
UPD (rpc_dropped_answers);
UPD (tot_forwarded_queries);
UPD (expired_forwarded_queries);
UPD (dropped_queries);
UPD (tot_forwarded_responses);
UPD (dropped_responses);
UPD (tot_forwarded_simple_acks);
UPD (dropped_simple_acks);
UPD (mtproto_proxy_errors);
UPD (connections_failed_lru);
UPD (connections_failed_flood);
UPD (ext_connections);
UPD (ext_connections_created);
UPD (http_queries);
UPD (http_bad_headers);
#undef UPD
}
void update_local_stats (void) {
if (!slave_mode) {
return;
}
update_local_stats_copy (WStats + worker_id * 2);
update_local_stats_copy (WStats + worker_id * 2 + 1);
}
void compute_stats_sum (void) {
if (!workers) {
return;
}
memset (&SumStats, 0, sizeof (SumStats));
int i;
for (i = 0; i < workers; i++) {
static struct worker_stats W;
struct worker_stats *F;
int s_cnt;
do {
F = WStats + i * 2;
do {
barrier ();
s_cnt = (++F)->cnt;
if (!(s_cnt & 1)) {
break;
}
s_cnt = (--F)->cnt;
} while (s_cnt & 1);
barrier ();
memcpy (&W, F, sizeof (W));
barrier ();
} while (s_cnt != F->cnt);
add_stats (&W);
}
}
/*
*
* SERVER
*
*/
void mtfront_prepare_stats (stats_buffer_t *sb) {
struct connections_stat conn;
struct buffers_stat bufs;
long long tot_dh_rounds[3];
int allocated_aes_crypto, allocated_aes_crypto_temp;
int uptime = now - start_time;
compute_stats_sum ();
fetch_connections_stat (&conn);
fetch_buffers_stat (&bufs);
fetch_tot_dh_rounds_stat (tot_dh_rounds);
fetch_aes_crypto_stat (&allocated_aes_crypto, &allocated_aes_crypto_temp);
sb_prepare (sb);
sb_memory (sb, AM_GET_MEMORY_USAGE_SELF);
#define S(x) ((x)+(SumStats.x))
#define S1(x) (SumStats.x)
#define SW(x) (workers ? S1(x) : S(x))
sb_printf (sb,
"config_filename\t%s\n"
"config_loaded_at\t%d\n"
"config_size\t%d\n"
"config_md5\t%s\n"
"config_auth_clusters\t%d\n"
"workers\t%d\n"
"queries_get\t%lld\n"
"qps_get\t%.3f\n"
"tot_forwarded_queries\t%lld\n"
"expired_forwarded_queries\t%lld\n"
"dropped_queries\t%lld\n"
"tot_forwarded_responses\t%lld\n"
"dropped_responses\t%lld\n"
"tot_forwarded_simple_acks\t%lld\n"
"dropped_simple_acks\t%lld\n"
"active_rpcs_created\t%lld\n"
"active_rpcs\t%lld\n"
"rpc_dropped_answers\t%lld\n"
"rpc_dropped_running\t%lld\n"
"window_clamp\t%d\n"
"total_ready_targets\t%d\n"
"total_allocated_targets\t%d\n"
"total_declared_targets\t%d\n"
"total_inactive_targets\t%d\n"
"total_connections\t%d\n"
"total_encrypted_connections\t%d\n"
"total_allocated_connections\t%d\n"
"total_allocated_outbound_connections\t%d\n"
"total_allocated_inbound_connections\t%d\n"
"total_allocated_socket_connections\t%d\n"
"total_dh_connections\t%d\n"
"total_dh_rounds\t%lld %lld %lld\n"
"total_special_connections\t%d\n"
"total_max_special_connections\t%d\n"
"total_accept_connections_failed\t%lld %lld %lld %lld %lld\n"
"ext_connections\t%lld\n"
"ext_connections_created\t%lld\n"
"total_active_network_events\t%d\n"
"total_network_buffers_used_size\t%lld\n"
"total_network_buffers_allocated_bytes\t%lld\n"
"total_network_buffers_used\t%d\n"
"total_network_buffer_chunks_allocated\t%d\n"
"total_network_buffer_chunks_allocated_max\t%d\n"
"mtproto_proxy_errors\t%lld\n"
"connections_failed_lru\t%lld\n"
"connections_failed_flood\t%lld\n"
"http_connections\t%d\n"
"pending_http_queries\t%d\n"
"http_queries\t%lld\n"
"http_bad_headers\t%lld\n"
"http_qps\t%.6f\n"
"proxy_mode\t%d\n"
"proxy_tag_set\t%d\n"
"version\t" VERSION_STR " compiled at " __DATE__ " " __TIME__ " by gcc " __VERSION__ " "
#ifdef __LP64__
"64-bit"
#else
"32-bit"
#endif
" after commit " COMMIT "\n",
config_filename,
CurConf->config_loaded_at,
CurConf->config_bytes,
CurConf->config_md5_hex,
CurConf->auth_stats.tot_clusters,
workers,
S(get_queries),
safe_div (S(get_queries), uptime),
S(tot_forwarded_queries),
S(expired_forwarded_queries),
S(dropped_queries),
S(tot_forwarded_responses),
S(dropped_responses),
S(tot_forwarded_simple_acks),
S(dropped_simple_acks),
S(active_rpcs_created),
S(active_rpcs),
S(rpc_dropped_answers),
S(rpc_dropped_running),
window_clamp,
SW(conn.ready_targets),
SW(conn.allocated_targets),
SW(conn.active_targets),
SW(conn.inactive_targets),
S(conn.active_connections),
S(allocated_aes_crypto),
S(conn.allocated_connections),
S(conn.allocated_outbound_connections),
S(conn.allocated_inbound_connections),
S(conn.allocated_socket_connections),
S(conn.active_dh_connections),
S(tot_dh_rounds[0]),
S(tot_dh_rounds[1]),
S(tot_dh_rounds[2]),
SW(conn.active_special_connections),
SW(conn.max_special_connections),
S(conn.accept_init_accepted_failed),
S(conn.accept_calls_failed),
S(conn.accept_connection_limit_failed),
S(conn.accept_rate_limit_failed),
S(conn.accept_nonblock_set_failed),
S(ext_connections),
S(ext_connections_created),
S(ev_heap_size),
SW(bufs.total_used_buffers_size),
SW(bufs.allocated_buffer_bytes),
SW(bufs.total_used_buffers),
SW(bufs.allocated_buffer_chunks),
SW(bufs.max_allocated_buffer_chunks),
S(mtproto_proxy_errors),
S(connections_failed_lru),
S(connections_failed_flood),
S(http_connections),
S(pending_http_queries),
S(http_queries),
S(http_bad_headers),
safe_div (S(http_queries), uptime),
proxy_mode,
proxy_tag_set
);
#undef S
#undef S1
#undef SW
}
/*
*
* JOB UTILS
*
*/
typedef int (*job_callback_func_t)(void *data, int len);
void schedule_job_callback (int context, job_callback_func_t func, void *data, int len);
struct job_callback_info {
job_callback_func_t func;
void *data[0];
};
int callback_job_run (job_t job, int op, struct job_thread *JT) {
struct job_callback_info *D = (struct job_callback_info *)(job->j_custom);
switch (op) {
case JS_RUN:
return D->func (D->data, job->j_custom_bytes - offsetof (struct job_callback_info, data));
// return JOB_COMPLETED;
case JS_FINISH:
return job_free (JOB_REF_PASS (job));
default:
assert (0);
}
}
void schedule_job_callback (int context, job_callback_func_t func, void *data, int len) {
job_t job = create_async_job (callback_job_run, JSP_PARENT_RWE | JSC_ALLOW (context, JS_RUN) | JSIG_FAST (JS_FINISH), -2, offsetof (struct job_callback_info, data) + len, 0, JOB_REF_NULL);
assert (job);
struct job_callback_info *D = (struct job_callback_info *)(job->j_custom);
D->func = func;
memcpy (D->data, data, len);
schedule_job (JOB_REF_PASS (job));
}
/*
*
* RPC CLIENT
*
*/
int client_send_message (JOB_REF_ARG (C), long long in_conn_id, struct tl_in_state *tlio_in, int flags);
int mtfront_client_ready (connection_job_t C);
int mtfront_client_close (connection_job_t C, int who);
int rpcc_execute (connection_job_t C, int op, struct raw_message *msg);
int tcp_rpcc_check_ready (connection_job_t C);
struct tcp_rpc_client_functions mtfront_rpc_client = {
.execute = rpcc_execute,
.check_ready = tcp_rpcc_default_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_check_perm = tcp_rpcc_default_check_perm,
.rpc_init_crypto = tcp_rpcc_init_crypto,
.rpc_start_crypto = tcp_rpcc_start_crypto,
.rpc_ready = mtfront_client_ready,
.rpc_close = mtfront_client_close
};
int rpcc_exists;
static int _notify_remote_closed (JOB_REF_ARG(C), long long out_conn_id) {
TLS_START (JOB_REF_PASS(C)) {
tl_store_int (RPC_CLOSE_CONN);
tl_store_long (out_conn_id);
} TLS_END;
return 1;
}
void push_rpc_confirmation (JOB_REF_ARG (C), int confirm) {
struct raw_message *msg = malloc (sizeof (struct raw_message));
rwm_create (msg, "\xdd", 1);
rwm_push_data (msg, &confirm, 4);
mpq_push_w (CONN_INFO(C)->out_queue, msg, 0);
job_signal (JOB_REF_PASS (C), JS_RUN);
}
struct client_packet_info {
struct event_timer ev;
struct raw_message msg;
connection_job_t conn;
int type;
};
int process_client_packet (struct tl_in_state *tlio_in, int op, connection_job_t C) {
int len = tl_fetch_unread ();
assert (op == tl_fetch_int ());
switch (op) {
case RPC_PONG:
return 1;
case RPC_PROXY_ANS:
if (len >= 16) {
int flags = tl_fetch_int ();
long long out_conn_id = tl_fetch_long ();
assert (tl_fetch_unread () == len - 16);
vkprintf (2, "got RPC_PROXY_ANS from connection %d:%llx, data size = %d, flags = %d\n", CONN_INFO(C)->fd, out_conn_id, tl_fetch_unread (), flags);
struct ext_connection *Ex = find_ext_connection_by_out_conn_id (out_conn_id);
connection_job_t D = 0;
if (Ex && Ex->out_fd == CONN_INFO(C)->fd && Ex->out_gen == CONN_INFO(C)->generation) {
D = connection_get_by_fd_generation (Ex->in_fd, Ex->in_gen);
}
if (D) {
vkprintf (2, "proxying answer into connection %d:%llx\n", Ex->in_fd, Ex->in_conn_id);
tot_forwarded_responses++;
client_send_message (JOB_REF_PASS(D), Ex->in_conn_id, tlio_in, flags);
} else {
vkprintf (2, "external connection not found, dropping proxied answer\n");
dropped_responses++;
_notify_remote_closed (JOB_REF_CREATE_PASS(C), out_conn_id);
}
return 1;
}
break;
case RPC_SIMPLE_ACK:
if (len == 16) {
long long out_conn_id = tl_fetch_long ();
int confirm = tl_fetch_int ();
vkprintf (2, "got RPC_SIMPLE_ACK for connection = %llx, value %08x\n", out_conn_id, confirm);
struct ext_connection *Ex = find_ext_connection_by_out_conn_id (out_conn_id);
connection_job_t D = 0;
if (Ex && Ex->out_fd == CONN_INFO(C)->fd && Ex->out_gen == CONN_INFO(C)->generation) {
D = connection_get_by_fd_generation (Ex->in_fd, Ex->in_gen);
}
if (D) {
vkprintf (2, "proxying simple ack %08x into connection %d:%llx\n", confirm, Ex->in_fd, Ex->in_conn_id);
if (Ex->in_conn_id) {
assert (0);
} else {
if (TCP_RPC_DATA(D)->flags & RPC_F_COMPACT) {
confirm = __builtin_bswap32 (confirm);
}
push_rpc_confirmation (JOB_REF_PASS (D), confirm);
}
tot_forwarded_simple_acks++;
} else {
vkprintf (2, "external connection not found, dropping simple ack\n");
dropped_simple_acks++;
_notify_remote_closed (JOB_REF_CREATE_PASS (C), out_conn_id);
}
return 1;
}
break;
case RPC_CLOSE_EXT:
if (len == 12) {
long long out_conn_id = tl_fetch_long ();
vkprintf (2, "got RPC_CLOSE_EXT for connection = %llx\n", out_conn_id);
struct ext_connection *Ex = find_ext_connection_by_out_conn_id (out_conn_id);
if (Ex) {
remove_ext_connection (Ex, 2);
}
return 1;
}
break;
default:
vkprintf (1, "unknown RPC operation %08x, ignoring\n", op);
}
return 0;
}
int client_packet_job_run (job_t job, int op, struct job_thread *JT) {
struct client_packet_info *D = (struct client_packet_info *)(job->j_custom);
switch (op) {
case JS_RUN: {
struct tl_in_state *tlio_in = tl_in_state_alloc ();
tlf_init_raw_message (tlio_in, &D->msg, D->msg.total_bytes, 0);
process_client_packet (tlio_in, D->type, D->conn);
tl_in_state_free (tlio_in);
return JOB_COMPLETED;
}
case JS_ALARM:
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
return JOB_COMPLETED;
case JS_ABORT:
if (!job->j_error) {
job->j_error = ECANCELED;
}
return JOB_COMPLETED;
case JS_FINISH:
if (D->conn) {
job_decref (JOB_REF_PASS (D->conn));
}
if (D->msg.magic) {
rwm_free (&D->msg);
}
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
int rpcc_execute (connection_job_t C, int op, struct raw_message *msg) {
vkprintf (2, "rpcc_execute: fd=%d, op=%08x, len=%d\n", CONN_INFO(C)->fd, op, msg->total_bytes);
CONN_INFO(C)->last_response_time = precise_now;
switch (op) {
case RPC_PONG:
break;
case RPC_PROXY_ANS:
case RPC_SIMPLE_ACK:
case RPC_CLOSE_EXT: {
job_t job = create_async_job (client_packet_job_run, JSP_PARENT_RWE | JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ABORT) | JSC_ALLOW (JC_ENGINE, JS_ALARM) | JSC_ALLOW (JC_ENGINE, JS_FINISH), -2, sizeof (struct client_packet_info), JT_HAVE_TIMER, JOB_REF_NULL);
struct client_packet_info *D = (struct client_packet_info *)(job->j_custom);
D->msg = *msg;
D->type = op;
D->conn = job_incref (C);
schedule_job (JOB_REF_PASS (job));
return 1;
}
default:
vkprintf (1, "unknown RPC operation %08x, ignoring\n", op);
}
return 0;
}
static inline int get_conn_tag (connection_job_t C) {
return 1 + (CONN_INFO(C)->generation & 0xffffff);
}
int mtfront_client_ready (connection_job_t C) {
check_engine_class ();
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int fd = CONN_INFO(C)->fd;
assert ((unsigned) fd < MAX_CONNECTIONS);
assert (!D->extra_int);
D->extra_int = get_conn_tag (C);
vkprintf (1, "Connected to RPC Middle-End (fd=%d)\n", fd);
rpcc_exists++;
struct ext_connection *H = &ExtConnectionHead[fd];
assert (!H->o_prev);
H->o_prev = H->o_next = H;
H->out_fd = fd;
CONN_INFO(C)->last_response_time = precise_now;
return 0;
}
int mtfront_client_close (connection_job_t C, int who) {
check_engine_class ();
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int fd = CONN_INFO(C)->fd;
assert ((unsigned) fd < MAX_CONNECTIONS);
vkprintf (1, "Disconnected from RPC Middle-End (fd=%d)\n", fd);
if (D->extra_int) {
assert (D->extra_int == get_conn_tag (C));
struct ext_connection *H = &ExtConnectionHead[fd], *Ex, *Ex_next;
assert (H->o_next);
for (Ex = H->o_next; Ex != H; Ex = Ex_next) {
Ex_next = Ex->o_next;
assert (Ex->out_fd == fd);
remove_ext_connection (Ex, 2);
}
assert (H->o_next == H && H->o_prev == H);
H->o_next = H->o_prev = 0;
H->out_fd = 0;
}
D->extra_int = 0;
return 0;
}
/*
*
* HTTP INTERFACE
*
*/
int hts_execute (connection_job_t C, struct raw_message *msg, int op);
int mtproto_http_alarm (connection_job_t C);
int mtproto_http_close (connection_job_t C, int who);
int hts_stats_execute (connection_job_t C, struct raw_message *msg, int op);
struct http_server_functions http_methods = {
.execute = hts_execute,
.ht_alarm = mtproto_http_alarm,
.ht_close = mtproto_http_close
};
struct http_server_functions http_methods_stats = {
.execute = hts_stats_execute
};
int ext_rpcs_execute (connection_job_t C, int op, struct raw_message *msg);
int mtproto_ext_rpc_ready (connection_job_t C);
int mtproto_ext_rpc_close (connection_job_t C, int who);
struct tcp_rpc_server_functions ext_rpc_methods = {
.execute = ext_rpcs_execute,
.check_ready = server_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_ready = mtproto_ext_rpc_ready,
.rpc_close = mtproto_ext_rpc_close,
//.http_fallback_type = &ct_http_server_mtfront,
//.http_fallback_extra = &http_methods,
.max_packet_len = MAX_POST_SIZE,
};
int mtproto_proxy_rpc_ready (connection_job_t C);
int mtproto_proxy_rpc_close (connection_job_t C, int who);
// ENGINE context
int do_close_in_ext_conn (void *_data, int s_len) {
assert (s_len == 4);
int fd = *(int *)_data;
struct ext_connection *Ex = get_ext_connection_by_in_fd (fd);
if (Ex) {
remove_ext_connection (Ex, 1);
}
return JOB_COMPLETED;
}
// NET_CPU context
int mtproto_http_close (connection_job_t C, int who) {
assert ((unsigned) CONN_INFO(C)->fd < MAX_CONNECTIONS);
vkprintf (3, "http connection closing (%d) by %d, %d queries pending\n", CONN_INFO(C)->fd, who, CONN_INFO(C)->pending_queries);
if (CONN_INFO(C)->pending_queries) {
assert (CONN_INFO(C)->pending_queries == 1);
pending_http_queries--;
CONN_INFO(C)->pending_queries = 0;
}
schedule_job_callback (JC_ENGINE, do_close_in_ext_conn, &CONN_INFO(C)->fd, 4);
return 0;
}
int mtproto_ext_rpc_ready (connection_job_t C) {
assert ((unsigned) CONN_INFO(C)->fd < MAX_CONNECTIONS);
vkprintf (3, "ext_rpc connection ready (%d)\n", CONN_INFO(C)->fd);
lru_insert_conn (C);
return 0;
}
int mtproto_ext_rpc_close (connection_job_t C, int who) {
assert ((unsigned) CONN_INFO(C)->fd < MAX_CONNECTIONS);
vkprintf (3, "ext_rpc connection closing (%d) by %d\n", CONN_INFO(C)->fd, who);
struct ext_connection *Ex = get_ext_connection_by_in_fd (CONN_INFO(C)->fd);
if (Ex) {
remove_ext_connection (Ex, 1);
}
return 0;
}
int mtproto_proxy_rpc_ready (connection_job_t C) {
check_engine_class ();
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int fd = CONN_INFO(C)->fd;
assert ((unsigned) fd < MAX_CONNECTIONS);
vkprintf (3, "proxy_rpc connection ready (%d)\n", fd);
struct ext_connection *H = &ExtConnectionHead[fd];
assert (!H->i_prev);
H->i_prev = H->i_next = H;
H->in_fd = fd;
assert (!D->extra_int);
D->extra_int = -get_conn_tag(C);
lru_insert_conn (C);
return 0;
}
int mtproto_proxy_rpc_close (connection_job_t C, int who) {
check_engine_class ();
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int fd = CONN_INFO(C)->fd;
assert ((unsigned) fd < MAX_CONNECTIONS);
vkprintf (3, "proxy_rpc connection closing (%d) by %d\n", fd, who);
if (D->extra_int) {
assert (D->extra_int == -get_conn_tag (C));
struct ext_connection *H = &ExtConnectionHead[fd], *Ex, *Ex_next;
assert (H->i_next);
for (Ex = H->i_next; Ex != H; Ex = Ex_next) {
Ex_next = Ex->i_next;
assert (Ex->in_fd == fd);
remove_ext_connection (Ex, 1);
}
assert (H->i_next == H && H->i_prev == H);
H->i_next = H->i_prev = 0;
H->in_fd = 0;
}
D->extra_int = 0;
return 0;
}
char mtproto_cors_http_headers[] =
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Methods: POST, OPTIONS\r\n"
"Access-Control-Allow-Headers: origin, content-type\r\n"
"Access-Control-Max-Age: 1728000\r\n";
int forward_mtproto_packet (struct tl_in_state *tlio_in, connection_job_t C, int len, int remote_ip_port[5], int rpc_flags);
int forward_tcp_query (struct tl_in_state *tlio_in, connection_job_t C, conn_target_job_t S, int flags, long long auth_key_id, int remote_ip_port[5], int our_ip_port[5]);
unsigned parse_text_ipv4 (char *str) {
int a, b, c, d;
if (sscanf (str, "%d.%d.%d.%d", &a, &b, &c, &d) != 4) {
return 0;
}
if ((a | b | c | d) & -0x100) {
return 0;
}
return (a << 24) | (b << 16) | (c << 8) | d;
}
int parse_text_ipv6 (unsigned char ip[16], const char *str) {
const char *ptr = str;
int i, k = -1;
if (*ptr == ':' && ptr[1] == ':') {
k = 0;
ptr += 2;
}
for (i = 0; i < 8; i++) {
int c = *ptr;
if (i > 0) {
if (c == ':') {
c = *++ptr;
} else if (k >= 0) {
break;
} else {
return -1; // ':' expected
}
if (c == ':') {
if (k >= 0) {
return -1; // second '::'
}
k = i;
c = *++ptr;
}
}
int j = 0, v = 0;
while ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
c |= 0x20;
v = (v << 4) + (c <= '9' ? c - '0' : c - 'a' + 10);
if (++j > 4) {
return -1; // more than 4 hex digits in component
}
c = *++ptr;
}
if (!j) {
if (k == i) {
break;
}
return -1; // hex digit or ':' expected
}
ip[2*i] = (v >> 8);
ip[2*i+1] = (v & 0xff);
}
if (*ptr) {
return -1;
}
/*
if (*ptr && *ptr != '/' && *ptr != ' ' && *ptr != '\n' && *ptr != '\r' && *ptr != '\t') {
return -1; // extra characters
}
*/
if (i < 8) {
assert (k >= 0 && k <= i);
int gap = 2 * (8 - i);
memmove (ip + 2*k + gap, ip + 2*k, 2 * (i - k));
memset (ip + 2*k, 0, gap);
}
return ptr - str;
}
struct http_query_info {
struct event_timer ev;
connection_job_t conn;
struct raw_message msg;
int conn_fd;
int conn_generation;
int flags;
int query_type;
int header_size;
int data_size;
int first_line_size;
int host_offset;
int host_size;
int uri_offset;
int uri_size;
char header[0];
};
int process_http_query (struct tl_in_state *tlio_in, job_t HQJ) {
struct http_query_info *D = (struct http_query_info *) HQJ->j_custom;
connection_job_t c = D->conn;
char *qHeaders = D->header + D->first_line_size;
int qHeadersLen = D->header_size - D->first_line_size;
assert (D->first_line_size > 0 && D->first_line_size <= D->header_size);
if (verbosity > 1) {
fprintf (stderr, "===============\n%.*s\n==============\n", D->header_size, D->header);
fprintf (stderr, "%d,%d,%d,%d\n", D->host_offset, D->host_size, D->uri_offset, D->uri_size);
fprintf (stderr, "hostname: '%.*s'\n", D->host_size, D->header + D->host_offset);
fprintf (stderr, "URI: '%.*s'\n", D->uri_size, D->header + D->uri_offset);
}
if (verbosity >= 2) {
char PostPreview[81];
int preview_len = (D->data_size < sizeof (PostPreview) ? D->data_size : sizeof(PostPreview) - 1);
tl_fetch_lookup_data (PostPreview, preview_len);
PostPreview[preview_len] = 0;
kprintf ("have %d POST bytes: `%.80s`\n", D->data_size, PostPreview);
}
char *qUri = D->header + D->uri_offset;
int qUriLen = D->uri_size;
char *get_qm_ptr = memchr (qUri, '?', D->uri_size);
if (get_qm_ptr) {
// qGet = get_qm_ptr + 1;
// qGetLen = qUri + qUriLen - qGet;
qUriLen = get_qm_ptr - qUri;
} else {
// qGet = 0;
// qGetLen = 0;
}
if (qUriLen >= 20) {
return -414;
}
if (qUriLen >= 4 && !memcmp (qUri, "/api", 4)) {
if (qUriLen >= 5 && qUri[4] == 'w') {
HTS_DATA(c)->query_flags |= QF_EXTRA_HEADERS;
extra_http_response_headers = mtproto_cors_http_headers;
} else {
HTS_DATA(c)->query_flags &= ~QF_EXTRA_HEADERS;
}
if (D->query_type == htqt_options) {
char response_buffer[512];
int len = snprintf (response_buffer, 511, "HTTP/1.1 200 OK\r\nConnection: %s\r\nContent-type: text/plain\r\nPragma: no-cache\r\nCache-control: no-store\r\n%sContent-length: 0\r\n\r\n", (HTS_DATA(c)->query_flags & QF_KEEPALIVE) ? "keep-alive" : "close", HTS_DATA(c)->query_flags & QF_EXTRA_HEADERS ? mtproto_cors_http_headers : "");
assert (len < 511);
struct raw_message *m = calloc (sizeof (struct raw_message), 1);
rwm_create (m, response_buffer, len);
http_flush (c, m);
return 0;
}
if (D->data_size & 3) {
return -404;
}
cur_http_origin_len = get_http_header (qHeaders, qHeadersLen, cur_http_origin, sizeof (cur_http_origin) - 1, "Origin", 6);
cur_http_referer_len = get_http_header (qHeaders, qHeadersLen, cur_http_referer, sizeof (cur_http_referer) - 1, "Referer", 7);
cur_http_user_agent_len = get_http_header (qHeaders, qHeadersLen, cur_http_user_agent, sizeof (cur_http_user_agent) - 1, "User-Agent", 10);
int tmp_ip_port[5], *remote_ip_port = 0;
if ((CONN_INFO(c)->remote_ip & 0xff000000) == 0x0a000000 || (CONN_INFO(c)->remote_ip & 0xff000000) == 0x7f000000) {
char x_real_ip[64], x_real_port[16];
int x_real_ip_len = get_http_header (qHeaders, qHeadersLen, x_real_ip, sizeof (x_real_ip) - 1, "X-Real-IP", 9);
int x_real_port_len = get_http_header (qHeaders, qHeadersLen, x_real_port, sizeof (x_real_port) - 1, "X-Real-Port", 11);
if (x_real_ip_len > 0) {
unsigned real_ip = parse_text_ipv4 (x_real_ip);
if (real_ip >= (1 << 24) || parse_text_ipv6 ((unsigned char *)tmp_ip_port, x_real_ip) > 0) {
if (real_ip >= (1 << 24)) {
tmp_ip_port[0] = 0;
tmp_ip_port[1] = 0;
tmp_ip_port[2] = 0xffff0000;
tmp_ip_port[3] = htonl (real_ip);
}
int port = (x_real_port_len > 0 ? atoi (x_real_port) : 0);
tmp_ip_port[4] = (port > 0 && port < 65536 ? port : 0);
remote_ip_port = tmp_ip_port;
vkprintf (3, "set remote IPv6:port to %08x:%08x:%08x:%08x:%08x according to X-Real-Ip '%s', X-Real-Port '%s'\n", tmp_ip_port[0], tmp_ip_port[1], tmp_ip_port[2], tmp_ip_port[3], tmp_ip_port[4], x_real_ip, x_real_port_len > 0 ? x_real_port : "");
}
}
}
int res = forward_mtproto_packet (tlio_in, c, D->data_size, remote_ip_port, 0);
return res ? 1 : -404;
}
return -404;
}
int http_query_job_run (job_t job, int op, struct job_thread *JT) {
struct http_query_info *HQ = (struct http_query_info *)(job->j_custom);
switch (op) {
case JS_RUN: { // ENGINE context
lru_insert_conn (HQ->conn);
struct tl_in_state *tlio_in = tl_in_state_alloc ();
tlf_init_raw_message (tlio_in, &HQ->msg, HQ->msg.total_bytes, 0);
int res = process_http_query (tlio_in, job);
tl_in_state_free (tlio_in);
assert (!HQ->msg.magic);
//rwm_free (&HQ->msg);
if (res < 0) {
write_http_error (HQ->conn, -res);
} else if (res > 0) {
assert (HQ->flags & 1);
HQ->flags &= ~1;
}
return JOB_COMPLETED;
}
case JS_ALARM:
if (!job->j_error) {
job->j_error = ETIMEDOUT;
}
return JOB_COMPLETED;
case JS_ABORT:
if (!job->j_error) {
job->j_error = ECANCELED;
}
return JOB_COMPLETED;
case JS_FINISH: // NET-CPU
if (HQ->flags & 1) {
connection_job_t c = HQ->conn ? job_incref (HQ->conn): connection_get_by_fd_generation (HQ->conn_fd, HQ->conn_generation);
if (c) {
assert (CONN_INFO(c)->pending_queries == 1);
CONN_INFO(c)->pending_queries--;
if (!(HTS_DATA(c)->query_flags & QF_KEEPALIVE) && CONN_INFO(c)->status == conn_working) {
connection_write_close (c);
}
job_decref (JOB_REF_PASS (c));
}
--pending_http_queries;
HQ->flags &= ~1;
}
if (HQ->conn) {
job_decref (JOB_REF_PASS (HQ->conn));
}
if (HQ->msg.magic) {
rwm_free (&HQ->msg);
}
return job_free (JOB_REF_PASS (job));
default:
return JOB_ERROR;
}
}
int hts_stats_execute (connection_job_t c, struct raw_message *msg, int op) {
struct hts_data *D = HTS_DATA(c);
// lru_insert_conn (c); // dangerous in net-cpu context
if (check_conn_buffers (c) < 0) {
return -429;
}
if (op != htqt_get || D->data_size != -1) {
D->query_flags &= ~QF_KEEPALIVE;
return -501;
}
if (CONN_INFO(c)->remote_ip != 0x7f000001) {
return -404;
}
if (D->uri_size != 6) {
return -404;
}
char ReqHdr[MAX_HTTP_HEADER_SIZE];
assert (rwm_fetch_data (msg, &ReqHdr, D->header_size) == D->header_size);
if (memcmp (ReqHdr + D->uri_offset, "/stats", 6)) {
return -404;
}
stats_buffer_t sb;
sb_alloc(&sb, 1 << 20);
mtfront_prepare_stats(&sb);
struct raw_message *raw = calloc (sizeof (*raw), 1);
rwm_init (raw, 0);
write_basic_http_header_raw (c, raw, 200, 0, sb.pos, 0, "text/plain");
assert (rwm_push_data (raw, sb.buff, sb.pos) == sb.pos);
mpq_push_w (CONN_INFO(c)->out_queue, raw, 0);
job_signal (JOB_REF_CREATE_PASS (c), JS_RUN);
sb_release (&sb);
return 0;
}
// NET-CPU context
int hts_execute (connection_job_t c, struct raw_message *msg, int op) {
struct hts_data *D = HTS_DATA(c);
vkprintf (2, "in hts_execute: connection #%d, op=%d, header_size=%d, data_size=%d, http_version=%d\n",
CONN_INFO(c)->fd, op, D->header_size, D->data_size, D->http_ver);
rwm_dump(msg);
fail_connection(c, -1);
return 0;
// lru_insert_conn (c); // dangerous in net-cpu context
if (check_conn_buffers (c) < 0) {
return -429;
}
if (D->data_size >= MAX_POST_SIZE) {
return -413;
}
if (!((D->query_type == htqt_post && D->data_size > 0) || (D->query_type == htqt_options && D->data_size < 0))) {
D->query_flags &= ~QF_KEEPALIVE;
return -501;
}
if (D->data_size < 0) {
D->data_size = 0;
}
if (D->uri_size > 14 || D->header_size > MAX_HTTP_HEADER_SIZE) {
return -414;
}
if (D->data_size > 0) {
int need_bytes = D->data_size + D->header_size - msg->total_bytes;
if (need_bytes > 0) {
vkprintf (2, "-- need %d more bytes, waiting\n", need_bytes);
return need_bytes;
}
}
assert (msg->total_bytes == D->header_size + D->data_size);
// create http query job here
job_t job = create_async_job (http_query_job_run, JSP_PARENT_RWE | JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_ABORT) | JSC_ALLOW (JC_ENGINE, JS_ALARM) | JSC_ALLOW (JC_CONNECTION, JS_FINISH), -2, sizeof (struct http_query_info) + D->header_size + 1, JT_HAVE_TIMER, JOB_REF_NULL);
assert (job);
struct http_query_info *HQ = (struct http_query_info *)(job->j_custom);
rwm_clone (&HQ->msg, msg);
HQ->conn = job_incref (c);
HQ->conn_fd = CONN_INFO(c)->fd;
HQ->conn_generation = CONN_INFO(c)->generation;
HQ->flags = 1; // pending_queries
assert (!CONN_INFO(c)->pending_queries);
CONN_INFO(c)->pending_queries++;
++pending_http_queries;
HQ->query_type = D->query_type;
HQ->header_size = D->header_size;
HQ->data_size = D->data_size;
HQ->first_line_size = D->first_line_size;
HQ->host_offset = D->host_offset;
HQ->host_size = D->host_size;
HQ->uri_offset = D->uri_offset;
HQ->uri_size = D->uri_size;
assert (rwm_fetch_data (&HQ->msg, HQ->header, HQ->header_size) == HQ->header_size);
HQ->header[HQ->header_size] = 0;
assert (HQ->msg.total_bytes == HQ->data_size);
schedule_job (JOB_REF_PASS (job));
return 0;
}
struct rpcs_exec_data {
struct raw_message msg;
connection_job_t conn;
int op;
int rpc_flags;
};
int do_rpcs_execute (void *_data, int s_len) {
struct rpcs_exec_data *data = _data;
assert (s_len == sizeof (struct rpcs_exec_data));
assert (data);
lru_insert_conn (data->conn);
int len = data->msg.total_bytes;
struct tl_in_state *tlio_in = tl_in_state_alloc ();
tlf_init_raw_message (tlio_in, &data->msg, len, 0);
int res = forward_mtproto_packet (tlio_in, data->conn, len, 0, data->rpc_flags);
tl_in_state_free (tlio_in);
job_decref (JOB_REF_PASS (data->conn));
if (!res) {
vkprintf (1, "ext_rpcs_execute: cannot forward mtproto packet\n");
}
return JOB_COMPLETED;
}
int ext_rpcs_execute (connection_job_t c, int op, struct raw_message *msg) {
int len = msg->total_bytes;
vkprintf (2, "ext_rpcs_execute: fd=%d, op=%08x, len=%d\n", CONN_INFO(c)->fd, op, len);
if (len > MAX_POST_SIZE) {
vkprintf (1, "ext_rpcs_execute: packet too long (%d bytes), skipping\n", len);
return SKIP_ALL_BYTES;
}
// lru_insert_conn (c); // dangerous in net-cpu context
if (check_conn_buffers (c) < 0) {
return SKIP_ALL_BYTES;
}
struct rpcs_exec_data data;
rwm_move (&data.msg, msg);
data.conn = job_incref (c);
data.rpc_flags = TCP_RPC_DATA(c)->flags & (RPC_F_QUICKACK | RPC_F_DROPPED | RPC_F_COMPACT_MEDIUM | RPC_F_EXTMODE3);
schedule_job_callback (JC_ENGINE, do_rpcs_execute, &data, sizeof (struct rpcs_exec_data));
return 1;
}
// NET-CPU context
int mtproto_http_alarm (connection_job_t C) {
vkprintf (2, "http_alarm() for connection %d\n", CONN_INFO(C)->fd);
assert (CONN_INFO(C)->status == conn_working);
HTS_DATA(C)->query_flags &= ~QF_KEEPALIVE;
write_http_error (C, 500);
if (CONN_INFO(C)->pending_queries) {
assert (CONN_INFO(C)->pending_queries == 1);
--pending_http_queries;
CONN_INFO(C)->pending_queries = 0;
}
HTS_DATA(C)->parse_state = -1;
connection_write_close (C);
return 0;
}
// NET-CPU context
int finish_postponed_http_response (void *_data, int len) {
assert (len == sizeof (connection_job_t));
connection_job_t C = *(connection_job_t *)_data;
if (!check_job_completion (C)) {
assert (CONN_INFO(C)->pending_queries >= 0);
assert (CONN_INFO(C)->pending_queries > 0);
assert (CONN_INFO(C)->pending_queries == 1);
CONN_INFO(C)->pending_queries = 0;
--pending_http_queries;
// check_conn_buffers (C);
http_flush (C, 0);
} else {
assert (!CONN_INFO(C)->pending_queries);
}
job_decref (JOB_REF_PASS (C));
return JOB_COMPLETED;
}
// ENGINE context
// problem: mtproto_http_alarm() may be invoked in parallel in NET-CPU context
int http_send_message (JOB_REF_ARG (C), struct tl_in_state *tlio_in, int flags) {
clear_connection_timeout (C);
struct hts_data *D = HTS_DATA(C);
if ((flags & 0x10) && TL_IN_REMAINING == 4) {
int error_code = tl_fetch_int ();
D->query_flags &= ~QF_KEEPALIVE;
write_http_error (C, -error_code);
} else {
char response_buffer[512];
TLS_START_UNALIGN (JOB_REF_CREATE_PASS (C)) {
int len = TL_IN_REMAINING;
tl_store_raw_data (response_buffer, snprintf (response_buffer, sizeof (response_buffer) - 1, "HTTP/1.1 200 OK\r\nConnection: %s\r\nContent-type: application/octet-stream\r\nPragma: no-cache\r\nCache-control: no-store\r\n%sContent-length: %d\r\n\r\n", (D->query_flags & QF_KEEPALIVE) ? "keep-alive" : "close", D->query_flags & QF_EXTRA_HEADERS ? mtproto_cors_http_headers : "", len));
assert (tl_copy_through (tlio_in, tlio_out, len, 1) == len);
} TLS_END;
}
assert (CONN_INFO(C)->status == conn_working && CONN_INFO(C)->pending_queries == 1);
assert ((unsigned) CONN_INFO(C)->fd < MAX_CONNECTIONS);
vkprintf (3, "detaching http connection (%d)\n", CONN_INFO(C)->fd);
struct ext_connection *Ex = get_ext_connection_by_in_fd (CONN_INFO(C)->fd);
if (Ex) {
remove_ext_connection (Ex, 1);
}
// reference to C is passed to the new job
schedule_job_callback (JC_CONNECTION, finish_postponed_http_response, &C, sizeof (connection_job_t));
return 1;
}
int client_send_message (JOB_REF_ARG(C), long long in_conn_id, struct tl_in_state *tlio_in, int flags) {
if (check_conn_buffers (C) < 0) {
job_decref (JOB_REF_PASS (C));
return -1;
}
if (in_conn_id) {
assert (0);
return 1;
}
if (CONN_INFO(C)->type == &ct_http_server_mtfront) {
return http_send_message (JOB_REF_PASS(C), tlio_in, flags);
}
TLS_START (JOB_REF_CREATE_PASS (C)) {
assert (tl_copy_through (tlio_in, tlio_out, TL_IN_REMAINING, 1) >= 0);
} TLS_END;
if (check_conn_buffers (C) < 0) {
job_decref (JOB_REF_PASS (C));
return -1;
} else {
job_decref (JOB_REF_PASS (C));
return 1;
}
}
/* ------------- process normal (encrypted) packet ----------------- */
// connection_job_t get_target_connection (conn_target_job_t S, int rotate);
conn_target_job_t choose_proxy_target (int target_dc) {
assert (CurConf->auth_clusters > 0);
struct mf_cluster *MFC = mf_cluster_lookup (CurConf, target_dc, 1);
if (!MFC) {
return 0;
}
int attempts = 5;
while (attempts --> 0) {
assert (MFC->targets_num > 0);
conn_target_job_t S = MFC->cluster_targets[lrand48() % MFC->targets_num];
connection_job_t C = 0;
rpc_target_choose_random_connections (S, 0, 1, &C);
if (C && TCP_RPC_DATA(C)->extra_int == get_conn_tag (C)) {
job_decref (JOB_REF_PASS (C));
return S;
}
}
return 0;
}
static int forward_mtproto_enc_packet (struct tl_in_state *tlio_in, connection_job_t C, long long auth_key_id, int len, int remote_ip_port[5], int rpc_flags) {
if (len < offsetof (struct encrypted_message, message) || (len & 15) != (offsetof (struct encrypted_message, server_salt) & 15)) {
return 0;
}
vkprintf (2, "received mtproto encrypted packet of %d bytes from connection %p (#%d~%d), key=%016llx\n", len, C, CONN_INFO(C)->fd, CONN_INFO(C)->generation, auth_key_id);
CONN_INFO(C)->query_start_time = get_utime_monotonic ();
conn_target_job_t S = choose_proxy_target (TCP_RPC_DATA(C)->extra_int4);
assert (TL_IN_REMAINING == len);
return forward_tcp_query (tlio_in, C, S, rpc_flags, auth_key_id, remote_ip_port, 0);
}
int forward_mtproto_packet (struct tl_in_state *tlio_in, connection_job_t C, int len, int remote_ip_port[5], int rpc_flags) {
int header[7];
if (len < sizeof (header) || (len & 3)) {
return 0;
}
assert (tl_fetch_lookup_data (header, sizeof (header)) == sizeof (header));
long long auth_key_id = *(long long *)header;
if (auth_key_id) {
return forward_mtproto_enc_packet (tlio_in, C, auth_key_id, len, remote_ip_port, rpc_flags);
}
vkprintf (2, "received mtproto packet of %d bytes\n", len);
int inner_len = header[4];
if (inner_len + 20 != len) {
vkprintf (1, "received packet with bad inner length: %d (%d expected)\n", inner_len, len - 20);
return 0;
}
if (len < 40) {
//must have at least function id and nonce
return 0;
}
int function = header[5];
if (function != CODE_req_pq && function != CODE_req_pq_multi && function != CODE_req_DH_params && function != CODE_set_client_DH_params) {
return 0;
}
conn_target_job_t S = choose_proxy_target (TCP_RPC_DATA(C)->extra_int4);
assert (len == TL_IN_REMAINING);
return forward_tcp_query (tlio_in, C, S, 2 | rpc_flags, 0, remote_ip_port, 0);
}
/*
*
* QUERY FORWARDING
*
*/
/* ----------- query rpc forwarding ------------ */
int forward_tcp_query (struct tl_in_state *tlio_in, connection_job_t c, conn_target_job_t S, int flags, long long auth_key_id, int remote_ip_port[5], int our_ip_port[5]) {
connection_job_t d = 0;
int c_fd = CONN_INFO(c)->fd;
struct ext_connection *Ex = get_ext_connection_by_in_fd (c_fd);
if (CONN_INFO(c)->type == &ct_tcp_rpc_ext_server_mtfront) {
flags |= TCP_RPC_DATA(c)->flags & RPC_F_DROPPED;
flags |= 0x1000;
} else if (CONN_INFO(c)->type == &ct_http_server_mtfront) {
flags |= 0x3005;
}
if (Ex && Ex->auth_key_id != auth_key_id) {
Ex->auth_key_id = auth_key_id;
}
if (Ex) {
assert (Ex->out_fd > 0 && Ex->out_fd < MAX_CONNECTIONS);
d = connection_get_by_fd_generation (Ex->out_fd, Ex->out_gen);
if (!d || !CONN_INFO(d)->target) {
if (d) {
job_decref (JOB_REF_PASS (d));
}
remove_ext_connection (Ex, 1);
Ex = 0;
}
}
if (!d) {
int attempts = 5;
while (S && attempts --> 0) {
rpc_target_choose_random_connections (S, 0, 1, &d);
if (d) {
if (TCP_RPC_DATA(d)->extra_int == get_conn_tag (d)) {
break;
} else {
job_decref (JOB_REF_PASS (d));
}
}
}
if (!d) {
vkprintf (2, "nowhere to forward user query from connection %d, dropping\n", CONN_INFO(c)->fd);
dropped_queries++;
if (CONN_INFO(c)->type == &ct_tcp_rpc_ext_server_mtfront) {
__sync_fetch_and_or (&TCP_RPC_DATA(c)->flags, RPC_F_DROPPED);
}
return 0;
}
if (flags & RPC_F_DROPPED) {
// there was at least one dropped inbound packet on this connection, have to close it now instead of forwarding next queries
fail_connection (c, -35);
return 0;
}
Ex = create_ext_connection (c, 0, d, auth_key_id);
}
tot_forwarded_queries++;
assert (Ex);
vkprintf (3, "forwarding user query from connection %d~%d (ext_conn_id %llx) into connection %d~%d (ext_conn_id %llx)\n", Ex->in_fd, Ex->in_gen, Ex->in_conn_id, Ex->out_fd, Ex->out_gen, Ex->out_conn_id);
if (proxy_tag_set) {
flags |= 8;
}
TLS_START (JOB_REF_PASS (d)); // open tlio_out context
tl_store_int (RPC_PROXY_REQ);
tl_store_int (flags);
tl_store_long (Ex->out_conn_id);
if (remote_ip_port) {
tl_store_raw_data (remote_ip_port, 20);
} else {
if (CONN_INFO(c)->remote_ip) {
tl_store_long (0);
tl_store_int (-0x10000);
tl_store_int (htonl (CONN_INFO(c)->remote_ip));
} else {
tl_store_raw_data (CONN_INFO(c)->remote_ipv6, 16);
}
tl_store_int (CONN_INFO(c)->remote_port);
}
if (our_ip_port) {
tl_store_raw_data (our_ip_port, 20);
} else {
if (CONN_INFO(c)->our_ip) {
tl_store_long (0);
tl_store_int (-0x10000);
tl_store_int (htonl (nat_translate_ip (CONN_INFO(c)->our_ip)));
} else {
tl_store_raw_data (CONN_INFO(c)->our_ipv6, 16);
}
tl_store_int (CONN_INFO(c)->our_port);
}
if (flags & 12) {
int *extra_size_ptr = tl_store_get_ptr (4);
int pos = TL_OUT_POS;
if (flags & 8) {
tl_store_int (TL_PROXY_TAG);
tl_store_string (proxy_tag, sizeof (proxy_tag));
}
if (flags & 4) {
tl_store_int (TL_HTTP_QUERY_INFO);
tl_store_string (cur_http_origin, cur_http_origin_len >= 0 ? cur_http_origin_len : 0);
tl_store_string (cur_http_referer, cur_http_referer_len >= 0 ? cur_http_referer_len : 0);
tl_store_string (cur_http_user_agent, cur_http_user_agent_len >= 0 ? cur_http_user_agent_len : 0);
}
*extra_size_ptr = TL_OUT_POS - pos;
}
int len = TL_IN_REMAINING;
assert (tl_copy_through (tlio_in, tlio_out, len, 1) == len);
TLS_END; // close tlio_out context
if (CONN_INFO(c)->type == &ct_http_server_mtfront) {
assert (CONN_INFO(c)->pending_queries >= 0);
assert (CONN_INFO(c)->pending_queries > 0);
assert (CONN_INFO(c)->pending_queries == 1);
set_connection_timeout (c, HTTP_MAX_WAIT_TIMEOUT);
}
return 1;
}
/* -------------------------- EXTERFACE ---------------------------- */
struct tl_act_extra *mtfront_parse_function (struct tl_in_state *tlio_in, long long actor_id) {
++api_invoke_requests;
if (actor_id != 0) {
tl_fetch_set_error (TL_ERROR_WRONG_ACTOR_ID, "MTProxy only supports actor_id = 0");
return 0;
}
int op = tl_fetch_int ();
if (tl_fetch_error ()) {
return 0;
}
switch (op) {
default:
tl_fetch_set_error_format (TL_ERROR_UNKNOWN_FUNCTION_ID, "Unknown op %08x", op);
return 0;
}
}
/* ------------------------ FLOOD CONTROL -------------------------- */
struct ext_connection ConnLRU = { .lru_prev = &ConnLRU, .lru_next = &ConnLRU };
void lru_delete_ext_conn (struct ext_connection *Ext) {
if (Ext->lru_next) {
Ext->lru_next->lru_prev = Ext->lru_prev;
Ext->lru_prev->lru_next = Ext->lru_next;
}
Ext->lru_next = Ext->lru_prev = 0;
}
void lru_insert_ext_conn (struct ext_connection *Ext) {
lru_delete_ext_conn (Ext);
Ext->lru_prev = ConnLRU.lru_prev;
Ext->lru_next = &ConnLRU;
Ext->lru_next->lru_prev = Ext;
Ext->lru_prev->lru_next = Ext;
}
void lru_delete_conn (connection_job_t c) {
struct ext_connection *Ext = get_ext_connection_by_in_fd (CONN_INFO(c)->fd);
if (Ext && Ext->in_fd == CONN_INFO(c)->fd) {
lru_delete_ext_conn (Ext);
}
}
void lru_insert_conn (connection_job_t c) {
struct ext_connection *Ext = get_ext_connection_by_in_fd (CONN_INFO(c)->fd);
if (Ext && Ext->in_fd == CONN_INFO(c)->fd && Ext->in_gen == CONN_INFO(c)->generation) {
lru_insert_ext_conn (Ext);
}
}
void check_all_conn_buffers (void) {
struct buffers_stat bufs;
fetch_buffers_stat (&bufs);
long long max_buffer_memory = bufs.max_buffer_chunks * (long long) MSG_BUFFERS_CHUNK_SIZE;
long long to_free = bufs.total_used_buffers_size - max_buffer_memory * 3/4;
while (to_free > 0 && ConnLRU.lru_next != &ConnLRU) {
struct ext_connection *Ext = ConnLRU.lru_next;
vkprintf (2, "check_all_conn_buffers(): closing connection %d because of %lld total used buffer vytes (%lld max, %lld bytes to free)\n", Ext->in_fd, bufs.total_used_buffers_size, max_buffer_memory, to_free);
connection_job_t d = connection_get_by_fd_generation (Ext->in_fd, Ext->in_gen);
if (d) {
int tot_used_bytes = CONN_INFO(d)->in.total_bytes + CONN_INFO(d)->in_u.total_bytes + CONN_INFO(d)->out.total_bytes + CONN_INFO(d)->out_p.total_bytes;
to_free -= tot_used_bytes * 2;
fail_connection (d, -500);
job_decref (JOB_REF_PASS (d));
}
lru_delete_ext_conn (Ext);
++connections_failed_lru;
}
}
int check_conn_buffers (connection_job_t c) {
int tot_used_bytes = CONN_INFO(c)->in.total_bytes + CONN_INFO(c)->in_u.total_bytes + CONN_INFO(c)->out.total_bytes + CONN_INFO(c)->out_p.total_bytes;
if (tot_used_bytes > MAX_CONNECTION_BUFFER_SPACE) {
vkprintf (2, "check_conn_buffers(): closing connection %d because of %d buffer bytes used (%d max)\n", CONN_INFO(c)->fd, tot_used_bytes, MAX_CONNECTION_BUFFER_SPACE);
fail_connection (c, -429);
++connections_failed_flood;
return -1;
}
return 0;
}
// invoked in NET-CPU context!
int mtfront_data_received (connection_job_t c, int bytes_received) {
// check_conn_buffers (c);
return 0;
}
// invoked in NET-CPU context!
int mtfront_data_sent (connection_job_t c, int bytes_sent) {
// lru_insert_conn (c);
return 0;
}
void init_ct_server_mtfront (void) {
assert (check_conn_functions (&ct_http_server, 1) >= 0);
memcpy (&ct_http_server_mtfront, &ct_http_server, sizeof (conn_type_t));
memcpy (&ct_tcp_rpc_ext_server_mtfront, &ct_tcp_rpc_ext_server, sizeof (conn_type_t));
memcpy (&ct_tcp_rpc_server_mtfront, &ct_tcp_rpc_server, sizeof (conn_type_t));
memcpy (&ct_tcp_rpc_client_mtfront, &ct_tcp_rpc_client, sizeof (conn_type_t));
ct_http_server_mtfront.data_received = &mtfront_data_received;
ct_tcp_rpc_ext_server_mtfront.data_received = &mtfront_data_received;
ct_tcp_rpc_server_mtfront.data_received = &mtfront_data_received;
ct_http_server_mtfront.data_sent = &mtfront_data_sent;
ct_tcp_rpc_ext_server_mtfront.data_sent = &mtfront_data_sent;
ct_tcp_rpc_server_mtfront.data_sent = &mtfront_data_sent;
}
/*
*
* PARSE ARGS & INITIALIZATION
*
*/
static void check_children_dead (void) {
int i, j;
for (j = 0; j < 11; j++) {
for (i = 0; i < workers; i++) {
if (pids[i]) {
int status = 0;
int res = waitpid (pids[i], &status, WNOHANG);
if (res == pids[i]) {
if (WIFEXITED (status) || WIFSIGNALED (status)) {
pids[i] = 0;
} else {
break;
}
} else if (res == 0) {
break;
} else if (res != -1 || errno != EINTR) {
pids[i] = 0;
} else {
break;
}
}
}
if (i == workers) {
break;
}
if (j < 10) {
usleep (100000);
}
}
if (j == 11) {
int cnt = 0;
for (i = 0; i < workers; i++) {
if (pids[i]) {
++cnt;
kill (pids[i], SIGKILL);
}
}
kprintf ("WARNING: %d children unfinished --> they are now killed\n", cnt);
}
}
static void kill_children (int signal) {
int i;
assert (workers);
for (i = 0; i < workers; i++) {
if (pids[i]) {
kill (pids[i], signal);
}
}
}
// SIGCHLD
void on_child_termination (void) {
}
void check_children_status (void) {
if (workers) {
int i;
for (i = 0; i < workers; i++) {
int status = 0;
int res = waitpid (pids[i], &status, WNOHANG);
if (res == pids[i]) {
if (WIFEXITED (status) || WIFSIGNALED (status)) {
kprintf ("Child %d terminated, aborting\n", pids[i]);
pids[i] = 0;
kill_children (SIGTERM);
check_children_dead ();
exit (EXIT_FAILURE);
}
} else if (res == 0) {
} else if (res != -1 || errno != EINTR) {
kprintf ("Child %d: unknown result during wait (%d, %m), aborting\n", pids[i], res);
pids[i] = 0;
kill_children (SIGTERM);
check_children_dead ();
exit (EXIT_FAILURE);
}
}
} else if (slave_mode) {
if (getppid () != parent_pid) {
kprintf ("Parent %d is changed to %d, aborting\n", parent_pid, getppid ());
exit (EXIT_FAILURE);
}
}
}
void check_special_connections_overflow (void) {
if (max_special_connections && !slave_mode) {
int max_user_conn = workers ? SumStats.conn.max_special_connections : max_special_connections;
int cur_user_conn = workers ? SumStats.conn.active_special_connections : active_special_connections;
if (cur_user_conn * 10 > max_user_conn * 9) {
vkprintf (0, "CRITICAL: used %d user connections out of %d\n", cur_user_conn, max_user_conn);
}
}
}
void cron (void) {
check_children_status ();
compute_stats_sum ();
check_special_connections_overflow ();
check_all_conn_buffers ();
}
int sfd;
int http_ports_num;
int http_sfd[MAX_HTTP_LISTEN_PORTS], http_port[MAX_HTTP_LISTEN_PORTS];
// static double next_create_outbound;
// int outbound_connections_per_second = DEFAULT_OUTBOUND_CONNECTION_CREATION_RATE;
void mtfront_pre_loop (void) {
int i, enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0;
tcp_maximize_buffers = 1;
if (!workers) {
for (i = 0; i < http_ports_num; i++) {
init_listening_tcpv6_connection (http_sfd[i], &ct_tcp_rpc_ext_server_mtfront, &ext_rpc_methods, enable_ipv6 | SM_LOWPRIO | SM_NOQACK | (max_special_connections ? SM_SPECIAL : 0));
// assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_MAXSEG, (int[]){1410}, sizeof (int)) >= 0);
// assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_NODELAY, (int[]){1}, sizeof (int)) >= 0);
listening_connection_job_t LC = Events[http_sfd[i]].data;
assert (LC);
CONN_INFO(LC)->window_clamp = window_clamp;
if (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_WINDOW_CLAMP, &window_clamp, 4) < 0) {
vkprintf (0, "error while setting window size for socket %d to %d: %m\n", http_sfd[i], window_clamp);
}
}
// create_all_outbound_connections ();
}
}
void precise_cron (void) {
update_local_stats ();
}
void mtfront_sigusr1_handler (void) {
reopen_logs_ext (slave_mode);
if (workers) {
kill_children (SIGUSR1);
}
}
/*
*
* MAIN
*
*/
void usage (void) {
printf ("usage: %s [-v] [-6] [-p<port>] [-H<http-port>{,<http-port>}] [-M<workers>] [-u<username>] [-b<backlog>] [-c<max-conn>] [-l<log-name>] [-W<window-size>] <config-file>\n", progname);
printf ("%s\n", FullVersionStr);
printf ("\tSimple MT-Proto proxy\n");
parse_usage ();
exit (2);
}
int f_parse_option (int val) {
char *colon, *ptr;
switch (val) {
case 'C':
max_special_connections = atoi (optarg);
if (max_special_connections < 0) {
max_special_connections = 0;
}
break;
case 'W':
window_clamp = atoi (optarg);
break;
case 'H':
ptr = optarg;
if (!*ptr) {
usage ();
return 2;
}
while (*ptr >= '1' && *ptr <= '9' && http_ports_num < MAX_HTTP_LISTEN_PORTS) {
int i = http_port[http_ports_num++] = strtol (ptr, &colon, 10);
assert (colon > ptr && i > 0 && i < 65536);
ptr = colon;
if (*ptr != ',') {
break;
} else {
ptr++;
}
}
if (*ptr) {
usage ();
return 2;
}
break;
/*
case 'o':
outbound_connections_per_second = atoi (optarg);
if (outbound_connections_per_second <= 0) {
outbound_connections_per_second = 1;
}
break;
*/
case 'M':
workers = atoi (optarg);
assert (workers >= 0 && workers <= MAX_WORKERS);
break;
case 'T':
ping_interval = atof (optarg);
if (ping_interval <= 0) {
ping_interval = PING_INTERVAL;
}
break;
case 'S':
case 'P':
{
if (strlen (optarg) != 32) {
kprintf ("'%c' option requires exactly 32 hex digits\n", val);
usage ();
}
unsigned char secret[16];
int i;
unsigned char b = 0;
for (i = 0; i < 32; i++) {
if (optarg[i] >= '0' && optarg[i] <= '9') {
b = b * 16 + optarg[i] - '0';
} else if (optarg[i] >= 'a' && optarg[i] <= 'f') {
b = b * 16 + optarg[i] - 'a' + 10;
} else if (optarg[i] >= 'A' && optarg[i] <= 'F') {
b = b * 16 + optarg[i] - 'A' + 10;
} else {
kprintf ("'S' option requires exactly 32 hex digits. '%c' is not hexdigit\n", optarg[i]);
usage ();
}
if (i & 1) {
secret[i / 2] = b;
b = 0;
}
}
if (val == 'S') {
tcp_rpcs_set_ext_secret (secret);
} else {
memcpy (proxy_tag, secret, sizeof (proxy_tag));
proxy_tag_set = 1;
}
}
break;
default:
return -1;
}
return 0;
}
void mtfront_prepare_parse_options (void) {
parse_option ("mtproto-secret", required_argument, 0, 'S', "16-byte secret in hex mode");
parse_option ("proxy-tag", required_argument, 0, 'P', "16-byte proxy tag in hex mode to be passed along with all forwarded queries");
parse_option ("max-special-connections", required_argument, 0, 'C', "sets maximal number of accepted client connections per worker");
parse_option ("window-clamp", required_argument, 0, 'W', "sets window clamp for client TCP connections");
parse_option ("http-ports", required_argument, 0, 'H', "comma-separated list of client (HTTP) ports to listen");
// parse_option ("outbound-connections-ps", required_argument, 0, 'o', "limits creation rate of outbound connections to mtproto-servers (default %d)", DEFAULT_OUTBOUND_CONNECTION_CREATION_RATE);
parse_option ("slaves", required_argument, 0, 'M', "spawn several slave workers");
parse_option ("ping-interval", required_argument, 0, 'T', "sets ping interval in second for local TCP connections (default %.3lf)", PING_INTERVAL);
}
void mtfront_parse_extra_args (int argc, char *argv[]) /* {{{ */ {
if (argc != 1) {
usage ();
exit (2);
}
config_filename = argv[0];
vkprintf (0, "config_filename = '%s'\n", config_filename);
}
// executed BEFORE dropping privileges
void mtfront_pre_init (void) {
init_ct_server_mtfront ();
int res = do_reload_config (0x26);
if (res < 0) {
fprintf (stderr, "config check failed! (code %d)\n", res);
exit (-res);
}
vkprintf (1, "config loaded!\n");
int i, enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0;
for (i = 0; i < http_ports_num; i++) {
http_sfd[i] = server_socket (http_port[i], engine_state->settings_addr, engine_get_backlog (), enable_ipv6);
if (http_sfd[i] < 0) {
fprintf (stderr, "cannot open http/tcp server socket at port %d: %m\n", http_port[i]);
exit (1);
}
}
if (workers) {
if (!kdb_hosts_loaded) {
kdb_load_hosts ();
}
WStats = mmap (0, 2 * workers * sizeof (struct worker_stats), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
assert (WStats);
// kprintf_multiprocessing_mode_enable ();
int real_parent_pid = getpid();
vkprintf (0, "creating %d workers\n", workers);
for (i = 0; i < workers; i++) {
int pid = fork ();
assert (pid >= 0);
if (!pid) {
worker_id = i;
workers = 0;
slave_mode = 1;
parent_pid = getppid ();
assert (parent_pid == real_parent_pid);
engine_enable_slave_mode ();
engine_state->do_not_open_port = 1;
break;
} else {
pids[i] = pid;
}
}
}
}
void mtfront_pre_start (void) {
int res = do_reload_config (0x17);
if (res < 0) {
fprintf (stderr, "config check failed! (code %d)\n", res);
exit (-res);
}
assert (CurConf->have_proxy);
proxy_mode |= PROXY_MODE_OUT;
mtfront_rpc_client.mode_flags |= TCP_RPC_IGNORE_PID;
ct_tcp_rpc_client_mtfront.flags |= C_EXTERNAL;
assert (proxy_mode == PROXY_MODE_OUT);
}
void mtfront_on_exit (void) {
if (workers) {
if (signal_check_pending (SIGTERM)) {
kill_children (SIGTERM);
}
check_children_dead ();
}
}
server_functions_t mtproto_front_functions = {
.default_modules_disabled = 0,
.cron = cron,
.precise_cron = precise_cron,
.pre_init = mtfront_pre_init,
.pre_start = mtfront_pre_start,
.pre_loop = mtfront_pre_loop,
.on_exit = mtfront_on_exit,
.prepare_stats = mtfront_prepare_stats,
.parse_option = f_parse_option,
.prepare_parse_options = mtfront_prepare_parse_options,
.parse_extra_args = mtfront_parse_extra_args,
.epoll_timeout = 1,
.FullVersionStr = FullVersionStr,
.ShortVersionStr = "mtproxy",
.parse_function = mtfront_parse_function,
.http_functions = &http_methods_stats
};
int main (int argc, char *argv[]) {
mtproto_front_functions.allowed_signals |= SIG2INT (SIGCHLD);
mtproto_front_functions.signal_handlers[SIGCHLD] = on_child_termination;
mtproto_front_functions.signal_handlers[SIGUSR1] = mtfront_sigusr1_handler;
return default_main (&mtproto_front_functions, argc, argv);
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Nikolai Durov
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
// #include <openssl/aes.h>
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-events.h" // for show_ipv6()
#include "net/net-config.h"
char pwd_config_buf[MAX_PWD_CONFIG_LEN + 128];
int pwd_config_len;
char pwd_config_md5[33] = {'n', 'o', 'n', 'e', 0};
int select_best_key_signature (int key_signature, int extra_num, const int *extra_key_signatures) {
assert (extra_num >= 0 && extra_num <= 16);
if (main_secret.secret_len < 4) {
return 0;
}
int main_key_id = main_secret.key_signature;
if (main_key_id == key_signature) {
return main_key_id;
}
int i;
for (i = 0; i < extra_num; i++) {
if (main_key_id == extra_key_signatures[i]) {
return main_key_id;
}
}
return 0;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Nikolai Durov
*/
#pragma once
#include "net/net-crypto-aes.h"
#define MAX_PWD_CONFIG_LEN 16384
#define RPCF_ALLOW_UNENC 1
#define RPCF_ALLOW_ENC 2
#define RPCF_REQ_DH 4
#define RPCF_ALLOW_SKIP_DH 8
#define RPCF_DISABLE_RPC 0x1000
#define RPCF_ALLOW_MC 0x2000
#define RPCF_ALLOW_SQL 0x4000
#define RPCF_ALLOW_HTTP 0x8000
#define RPCF_RESULT_VALID 0x80000000
extern char pwd_config_buf[MAX_PWD_CONFIG_LEN + 128];
extern int pwd_config_len;
extern char pwd_config_md5[33];
int select_best_key_signature (int key_signature, int extra_num, const int *extra_key_signatures);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
Copyright 2014 Telegram Messenger Inc
2014 Nikolai Durov
2014 Andrey Lopatin
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#include "crc32.h"
#include "jobs/jobs.h"
#include "net/net-events.h"
//#include "net/net-buffers.h"
#include "kprintf.h"
#include "precise-time.h"
#include "server-functions.h"
#include "net/net-connections.h"
#include "net/net-config.h"
#include "vv/vv-io.h"
#include "vv/vv-tree.h"
#include "pid.h"
#include "common/mp-queue.h"
#include "net/net-msg-buffers.h"
#include "net/net-tcp-connections.h"
#include "common/common-stats.h"
//struct process_id PID;
#define USE_EPOLLET 1
#define MAX_RECONNECT_INTERVAL 20
#define MODULE connections
static int max_accept_rate;
static double cur_accept_rate_remaining;
static double cur_accept_rate_time;
static int max_connection;
static int conn_generation;
static int max_connection_fd = MAX_CONNECTIONS;
int active_special_connections, max_special_connections = MAX_CONNECTIONS;
int special_listen_sockets;
static struct {
int fd, generation;
} special_socket[MAX_SPECIAL_LISTEN_SOCKETS];
static struct mp_queue *free_later_queue;
MODULE_STAT_TYPE {
int active_connections, active_dh_connections;
int outbound_connections, active_outbound_connections, ready_outbound_connections, listening_connections;
int allocated_outbound_connections, allocated_inbound_connections;
int inbound_connections, active_inbound_connections;
long long outbound_connections_created, inbound_connections_accepted;
int ready_targets;
long long netw_queries, netw_update_queries, total_failed_connections, total_connect_failures, unused_connections_closed;
int allocated_targets, active_targets, inactive_targets, free_targets;
int allocated_connections, allocated_socket_connections;
long long accept_calls_failed, accept_nonblock_set_failed, accept_connection_limit_failed,
accept_rate_limit_failed, accept_init_accepted_failed;
long long tcp_readv_calls, tcp_writev_calls, tcp_readv_intr, tcp_writev_intr;
long long tcp_readv_bytes, tcp_writev_bytes;
int free_later_size;
long long free_later_total;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_I (active_connections);
SB_SUM_ONE_I (active_dh_connections);
SB_SUM_ONE_I (outbound_connections);
SB_SUM_ONE_I (ready_outbound_connections);
SB_SUM_ONE_I (active_outbound_connections);
SB_SUM_ONE_LL (outbound_connections_created);
SB_SUM_ONE_LL (total_connect_failures);
SB_SUM_ONE_I (inbound_connections);
//SB_SUM_ONE_I (ready_inbound_connections);
SB_SUM_ONE_I (active_inbound_connections);
SB_SUM_ONE_LL (inbound_connections_accepted);
SB_SUM_ONE_I (listening_connections);
SB_SUM_ONE_LL (unused_connections_closed);
SB_SUM_ONE_I (ready_targets);
SB_SUM_ONE_I (allocated_targets);
SB_SUM_ONE_I (active_targets);
SB_SUM_ONE_I (inactive_targets);
SB_SUM_ONE_I (free_targets);
sb_printf (sb,
"max_connections\t%d\n"
"active_special_connections\t%d\n"
"max_special_connections\t%d\n"
,
max_connection_fd,
active_special_connections,
max_special_connections
);
SBP_PRINT_I32(max_accept_rate);
SBP_PRINT_DOUBLE(cur_accept_rate_remaining);
SBP_PRINT_I32(max_connection);
SBP_PRINT_I32(conn_generation);
SB_SUM_ONE_I (allocated_connections);
SB_SUM_ONE_I (allocated_outbound_connections);
SB_SUM_ONE_I (allocated_inbound_connections);
SB_SUM_ONE_I (allocated_socket_connections);
SB_SUM_ONE_LL (tcp_readv_calls);
SB_SUM_ONE_LL (tcp_readv_intr);
SB_SUM_ONE_LL (tcp_readv_bytes);
SB_SUM_ONE_LL (tcp_writev_calls);
SB_SUM_ONE_LL (tcp_writev_intr);
SB_SUM_ONE_LL (tcp_writev_bytes);
SB_SUM_ONE_I (free_later_size);
SB_SUM_ONE_LL (free_later_total);
SB_SUM_ONE_LL (accept_calls_failed);
SB_SUM_ONE_LL (accept_nonblock_set_failed);
SB_SUM_ONE_LL (accept_connection_limit_failed);
SB_SUM_ONE_LL (accept_rate_limit_failed);
SB_SUM_ONE_LL (accept_init_accepted_failed);
MODULE_STAT_FUNCTION_END
void fetch_connections_stat (struct connections_stat *st) {
#define COLLECT_I(__x) st->__x = SB_SUM_I (__x);
#define COLLECT_LL(__x) st->__x = SB_SUM_LL (__x);
COLLECT_I (active_connections);
COLLECT_I (active_dh_connections);
COLLECT_I (outbound_connections);
COLLECT_I (active_outbound_connections);
COLLECT_I (ready_outbound_connections);
st->max_special_connections = max_special_connections;
st->active_special_connections = active_special_connections;
COLLECT_I (allocated_connections);
COLLECT_I (allocated_outbound_connections);
COLLECT_I (allocated_inbound_connections);
COLLECT_I (allocated_socket_connections);
COLLECT_I (allocated_targets);
COLLECT_I (ready_targets);
COLLECT_I (active_targets);
COLLECT_I (inactive_targets);
COLLECT_LL (tcp_readv_calls);
COLLECT_LL (tcp_readv_intr);
COLLECT_LL (tcp_readv_bytes);
COLLECT_LL (tcp_writev_calls);
COLLECT_LL (tcp_writev_intr);
COLLECT_LL (tcp_writev_bytes);
COLLECT_LL (accept_calls_failed);
COLLECT_LL (accept_nonblock_set_failed);
COLLECT_LL (accept_rate_limit_failed);
COLLECT_LL (accept_init_accepted_failed);
COLLECT_LL (accept_connection_limit_failed);
#undef COLLECT_I
#undef COLLECT_LL
}
void connection_event_incref (int fd, long long val);
void tcp_set_max_accept_rate (int rate) {
max_accept_rate = rate;
}
int set_write_timer (connection_job_t C);
int prealloc_tcp_buffers (void);
int clear_connection_write_timeout (connection_job_t c);
static int tcp_recv_buffers_num;
static int tcp_recv_buffers_total_size;
static struct iovec tcp_recv_iovec[MAX_TCP_RECV_BUFFERS + 1];
static struct msg_buffer *tcp_recv_buffers[MAX_TCP_RECV_BUFFERS];
int prealloc_tcp_buffers (void) /* {{{ */ {
assert (!tcp_recv_buffers_num);
int i;
for (i = MAX_TCP_RECV_BUFFERS - 1; i >= 0; i--) {
struct msg_buffer *X = alloc_msg_buffer ((tcp_recv_buffers_num) ? tcp_recv_buffers[i + 1] : 0, TCP_RECV_BUFFER_SIZE);
if (!X) {
vkprintf (0, "**FATAL**: cannot allocate tcp receive buffer\n");
exit (2);
}
vkprintf (3, "allocated %d byte tcp receive buffer #%d at %p\n", X->chunk->buffer_size, i, X);
tcp_recv_buffers[i] = X;
tcp_recv_iovec[i + 1].iov_base = X->data;
tcp_recv_iovec[i + 1].iov_len = X->chunk->buffer_size;
++ tcp_recv_buffers_num;
tcp_recv_buffers_total_size += X->chunk->buffer_size;
}
return tcp_recv_buffers_num;
}
/* }}} */
int tcp_prepare_iovec (struct iovec *iov, int *iovcnt, int maxcnt, struct raw_message *raw) /* {{{ */ {
int t = rwm_prepare_iovec (raw, iov, maxcnt, raw->total_bytes);
if (t < 0) {
*iovcnt = maxcnt;
int i;
t = 0;
for (i = 0; i < maxcnt; i++) {
t += iov[i].iov_len;
}
assert (t < raw->total_bytes);
return t;
} else {
*iovcnt = t;
return raw->total_bytes;
}
}
/* }}} */
void assert_main_thread (void) {}
void assert_net_cpu_thread (void) {}
void assert_net_net_thread (void) {}
void assert_engine_thread (void) {
assert (this_job_thread && (this_job_thread->thread_class == JC_ENGINE || this_job_thread->thread_class == JC_MAIN));
}
socket_connection_job_t alloc_new_socket_connection (connection_job_t C);
#define X_TYPE connection_job_t
#define X_CMP(a,b) (((a) < (b)) ? -1 : ((a) > (b)) ? 1 : 0)
#define TREE_NAME connection
#define TREE_MALLOC
#define TREE_PTHREAD
#define TREE_INCREF job_incref
#define TREE_DECREF job_decref_f
#include "vv/vv-tree.c"
static inline int connection_is_active (int flags) {
return (flags & C_CONNECTED) && !(flags & C_READY_PENDING);
}
/* {{{ compute_conn_events */
#if USE_EPOLLET
static inline int compute_conn_events (socket_connection_job_t c) {
unsigned flags = SOCKET_CONN_INFO(c)->flags;
if (flags & C_ERROR) {
return 0;
} else {
return EVT_READ | EVT_WRITE | EVT_SPEC;
}
}
#else
static inline int compute_conn_events (connection_job_t c) {
unsigned flags = CONN_INFO(c)->flags;
if (flags & (C_ERROR | C_FAILED | C_NET_FAILED)) {
return 0;
}
return (((flags & (C_WANTRD | C_STOPREAD)) == C_WANTRD) ? EVT_READ : 0) | (flags & C_WANTWR ? EVT_WRITE : 0) | EVT_SPEC
| (((flags & (C_WANTRD | C_NORD)) == (C_WANTRD | C_NORD))
|| ((flags & (C_WANTWR | C_NOWR)) == (C_WANTWR | C_NOWR)) ? EVT_LEVEL : 0);
}
#endif
/* }}} */
void connection_write_close (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
if (c->status == conn_working) {
socket_connection_job_t S = c->io_conn;
if (S) {
__sync_fetch_and_or (&SOCKET_CONN_INFO(S)->flags, C_STOPREAD);
}
__sync_fetch_and_or (&c->flags, C_STOPREAD);
c->status = conn_write_close;
job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
}
}
/* }}} */
/* qack {{{ */
static inline void disable_qack (int fd) {
vkprintf (2, "disable TCP_QUICKACK for %d\n", fd);
assert (setsockopt (fd, IPPROTO_TCP, TCP_QUICKACK, (int[]){0}, sizeof (int)) >= 0);
}
static inline void cond_disable_qack (socket_connection_job_t C) {
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
if (c->flags & C_NOQACK) {
disable_qack (c->fd);
}
}
/* }}} */
/* {{{ cork
static inline void cond_reset_cork (connection_job_t c) {
if (c->flags & C_NOQACK) {
vkprintf (2, "disable TCP_CORK for %d\n", c->fd);
assert (setsockopt (c->fd, IPPROTO_TCP, TCP_CORK, (int[]){0}, sizeof (int)) >= 0);
vkprintf (2, "enable TCP_CORK for %d\n", c->fd);
assert (setsockopt (c->fd, IPPROTO_TCP, TCP_CORK, (int[]){1}, sizeof (int)) >= 0);
}
}
}}} */
/* {{{ CPU PART OF CONNECTION */
/* {{{ TIMEOUT */
int set_connection_timeout (connection_job_t C, double timeout) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
if (c->flags & C_ERROR) { return 0; }
__sync_fetch_and_and (&c->flags, ~C_ALARM);
if (timeout > 0) {
job_timer_insert (C, precise_now + timeout);
return 0;
} else {
job_timer_remove (C);
return 0;
}
}
/* }}} */
int clear_connection_timeout (connection_job_t C) /* {{{ */ {
set_connection_timeout (C, 0);
return 0;
}
/* }}} */
/* }}} */
/*
can be called from any thread and without lock
just sets error code and sends JS_ABORT to connection job
*/
void fail_connection (connection_job_t C, int err) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
if (!(__sync_fetch_and_or (&c->flags, C_ERROR) & C_ERROR)) {
c->status = conn_error;
if (c->error >= 0) {
c->error = err;
}
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
}
}
/* }}} */
/*
just runs ->reader and ->writer virtual methods
*/
int cpu_server_read_write (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
c->type->reader (C);
c->type->writer (C);
return 0;
}
/* }}} */
/*
frees connection structure, including mpq and buffers
*/
int cpu_server_free_connection (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
assert (C->j_refcnt == 1);
struct connection_info *c = CONN_INFO (C);
if (!(c->flags & C_ERROR)) {
vkprintf (0, "target = %p, basic=%d\n", c->target, c->basic_type);
}
assert (c->flags & C_ERROR);
assert (c->flags & C_FAILED);
assert (!c->target);
assert (!c->io_conn);
vkprintf (1, "Closing connection socket #%d\n", c->fd);
while (1) {
struct raw_message *raw = mpq_pop_nw (c->out_queue, 4);
if (!raw) { break; }
rwm_free (raw);
free (raw);
}
free_mp_queue (c->out_queue);
c->out_queue = NULL;
while (1) {
struct raw_message *raw = mpq_pop_nw (c->in_queue, 4);
if (!raw) { break; }
rwm_free (raw);
free (raw);
}
free_mp_queue (c->in_queue);
c->in_queue = NULL;
if (c->type->crypto_free) {
c->type->crypto_free (C);
}
close (c->fd);
c->fd = -1;
MODULE_STAT->allocated_connections --;
if (c->basic_type == ct_outbound) {
MODULE_STAT->allocated_outbound_connections --;
}
if (c->basic_type == ct_inbound) {
MODULE_STAT->allocated_inbound_connections --;
}
return c->type->free_buffers (C);
}
/* }}} */
/*
deletes link to io_conn
deletes link to target
aborts pending queries
updates stats
*/
int cpu_server_close_connection (connection_job_t C, int who) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO(C);
assert (c->flags & C_ERROR);
assert (c->status == conn_error);
assert (c->flags & C_FAILED);
if (c->error != -17) {
MODULE_STAT->total_failed_connections ++;
if (!connection_is_active (c->flags)) {
MODULE_STAT->total_connect_failures ++;
}
} else {
MODULE_STAT->unused_connections_closed ++;
}
if (c->flags & C_ISDH) {
MODULE_STAT->active_dh_connections --;
__sync_fetch_and_and (&c->flags, ~C_ISDH);
}
assert (c->io_conn);
job_signal (JOB_REF_PASS (c->io_conn), JS_ABORT);
if (c->target) {
MODULE_STAT->outbound_connections --;
if (connection_is_active (c->flags)) {
MODULE_STAT->active_outbound_connections --;
}
job_signal (JOB_REF_PASS (c->target), JS_RUN);
} else {
MODULE_STAT->inbound_connections --;
if (connection_is_active (c->flags)) {
MODULE_STAT->active_inbound_connections --;
}
}
if (connection_is_active (c->flags)) {
MODULE_STAT->active_connections --;
}
if (c->flags & C_SPECIAL) {
c->flags &= ~C_SPECIAL;
int orig_special_connections = __sync_fetch_and_add (&active_special_connections, -1);
if (orig_special_connections == max_special_connections) {
int i;
for (i = 0; i < special_listen_sockets; i++) {
connection_job_t LC = connection_get_by_fd_generation (special_socket[i].fd, special_socket[i].generation);
assert (LC);
job_signal (JOB_REF_PASS (LC), JS_AUX);
}
}
}
job_timer_remove (C);
return 0;
}
/* }}} */
int do_connection_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
connection_job_t C = job;
struct connection_info *c = CONN_INFO (C);
if (op == JS_RUN) { // RUN IN NET-CPU THREAD
assert_net_cpu_thread ();
if (!(c->flags & C_ERROR)) {
if (c->flags & C_READY_PENDING) {
assert (c->flags & C_CONNECTED);
__sync_fetch_and_and (&c->flags, ~C_READY_PENDING);
MODULE_STAT->active_outbound_connections ++;
MODULE_STAT->active_connections ++;
__sync_fetch_and_add (&CONN_TARGET_INFO(c->target)->active_outbound_connections, 1);
if (c->status == conn_connecting) {
if (!__sync_bool_compare_and_swap (&c->status, conn_connecting, conn_working)) {
assert (c->status == conn_error);
}
}
c->type->connected (C);
}
c->type->read_write (C);
}
return 0;
}
if (op == JS_ALARM) { // RUN IN NET-CPU THREAD
if (!job_timer_check (job)) {
return 0;
}
if (!(c->flags & C_ERROR)) {
c->type->alarm (C);
}
return 0;
}
if (op == JS_ABORT) { // RUN IN NET-CPU THREAD
assert (c->flags & C_ERROR);
if (!(__sync_fetch_and_or (&c->flags, C_FAILED) & C_FAILED)) {
c->type->close (C, 0);
}
return JOB_COMPLETED;
}
if (op == JS_FINISH) { // RUN IN NET-CPU THREAD
assert (C->j_refcnt == 1);
c->type->free (C);
return job_free (JOB_REF_PASS (C));
}
return JOB_ERROR;
}
/* }}} */
/*
allocates inbound or outbound connection
runs init_accepted or init_outbound
updates stats
creates socket_connection
*/
connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening_connection_job_t LCJ, unsigned peer, unsigned char peer_ipv6[16], int peer_port) /* {{{ */ {
if (cfd < 0) {
return NULL;
}
assert_main_thread ();
struct conn_target_info *CT = CTJ ? CONN_TARGET_INFO (CTJ) : NULL;
struct listening_connection_info *LC = LCJ ? LISTEN_CONN_INFO (LCJ) : NULL;
unsigned flags;
if ((flags = fcntl (cfd, F_GETFL, 0) < 0) || fcntl (cfd, F_SETFL, flags | O_NONBLOCK) < 0) {
kprintf ("cannot set O_NONBLOCK on accepted socket %d: %m\n", cfd);
MODULE_STAT->accept_nonblock_set_failed ++;
close (cfd);
return NULL;
}
flags = 1;
setsockopt (cfd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof (flags));
if (tcp_maximize_buffers) {
maximize_sndbuf (cfd, 0);
maximize_rcvbuf (cfd, 0);
}
if (cfd >= max_connection_fd) {
vkprintf (2, "cfd = %d, max_connection_fd = %d\n", cfd, max_connection_fd);
MODULE_STAT->accept_connection_limit_failed ++;
close (cfd);
return NULL;
}
if (cfd > max_connection) {
max_connection = cfd;
}
connection_job_t C = create_async_job (do_connection_job, JSC_ALLOW (JC_CONNECTION, JS_RUN) | JSC_ALLOW (JC_CONNECTION, JS_ALARM) | JSC_ALLOW (JC_CONNECTION, JS_ABORT) | JSC_ALLOW (JC_CONNECTION, JS_FINISH), -2, sizeof (struct connection_info), JT_HAVE_TIMER, JOB_REF_NULL);
struct connection_info *c = CONN_INFO (C);
//memset (c, 0, sizeof (*c)); /* no need, create_async_job memsets itself */
c->fd = cfd;
c->target = CTJ;
c->generation = new_conn_generation ();
c->flags = 0;//SS ? C_WANTWR : C_WANTRD;
if (LC) {
c->flags = C_CONNECTED;
}
int raw = C_RAWMSG;
if (raw) {
c->flags |= C_RAWMSG;
rwm_init (&c->in, 0);
rwm_init (&c->out, 0);
rwm_init (&c->in_u, 0);
rwm_init (&c->out_p, 0);
} else {
assert (0);
}
c->type = CT ? CT->type : LC->type;
c->extra = CT ? CT->extra : LC->extra;
assert (c->type);
c->basic_type = CT ? ct_outbound : ct_inbound;
c->status = CT ? conn_connecting : conn_working;
c->flags |= c->type->flags & C_EXTERNAL;
if (LC) {
c->flags |= LC->flags & C_EXTERNAL;
}
union sockaddr_in46 self;
unsigned self_addrlen = sizeof (self);
memset (&self, 0, sizeof (self));
getsockname (cfd, (struct sockaddr *) &self, &self_addrlen);
if (self.a4.sin_family == AF_INET) {
assert (self_addrlen == sizeof (struct sockaddr_in));
c->our_ip = ntohl (self.a4.sin_addr.s_addr);
c->our_port = ntohs (self.a4.sin_port);
assert (peer);
c->remote_ip = peer;
} else {
assert (self.a6.sin6_family == AF_INET6);
assert (!peer);
if (is_4in6 (peer_ipv6)) {
assert (is_4in6 (self.a6.sin6_addr.s6_addr));
c->our_ip = ntohl (extract_4in6 (self.a6.sin6_addr.s6_addr));
c->our_port = ntohs (self.a6.sin6_port);
c->remote_ip = ntohl (extract_4in6 (peer_ipv6));
} else {
memcpy (c->our_ipv6, self.a6.sin6_addr.s6_addr, 16);
c->our_port = ntohs (self.a6.sin6_port);
c->flags |= C_IPV6;
memcpy (c->remote_ipv6, peer_ipv6, 16);
}
}
c->remote_port = peer_port;
c->in_queue = alloc_mp_queue_w ();
c->out_queue = alloc_mp_queue_w ();
//c->out_packet_queue = alloc_mp_queue_w ();
if (CT) {
vkprintf (1, "New connection %s:%d -> %s:%d\n", show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
} else {
vkprintf (1, "New connection %s:%d -> %s:%d\n", show_remote_ip (C), c->remote_port, show_our_ip (C), c->our_port);
}
int (*func)(connection_job_t) = CT ? CT->type->init_outbound : LC->type->init_accepted;
vkprintf (3, "func = %p\n", func);
if (func (C) >= 0) {
if (CT) {
job_incref (CTJ);
MODULE_STAT->outbound_connections ++;
MODULE_STAT->allocated_outbound_connections ++;
MODULE_STAT->outbound_connections_created ++;
CT->outbound_connections ++;
} else {
MODULE_STAT->inbound_connections_accepted ++;
MODULE_STAT->allocated_inbound_connections ++;
MODULE_STAT->inbound_connections ++;
MODULE_STAT->active_inbound_connections ++;
MODULE_STAT->active_connections ++;
c->listening = LC->fd;
c->listening_generation = LC->generation;
if (LC->flags & C_NOQACK) {
c->flags |= C_NOQACK;
}
c->window_clamp = LC->window_clamp;
if (c->window_clamp) {
if (setsockopt (cfd, IPPROTO_TCP, TCP_WINDOW_CLAMP, &c->window_clamp, 4) < 0) {
vkprintf (0, "error while setting window size for socket %d to %d: %m\n", cfd, c->window_clamp);
} else {
int t1 = -1, t2 = -1;
socklen_t s1 = 4, s2 = 4;
getsockopt (cfd, IPPROTO_TCP, TCP_WINDOW_CLAMP, &t1, &s1);
getsockopt (cfd, SOL_SOCKET, SO_RCVBUF, &t2, &s2);
vkprintf (2, "window clamp for socket %d is %d, receive buffer is %d\n", cfd, t1, t2);
}
}
if (LC->flags & C_SPECIAL) {
c->flags |= C_SPECIAL;
__sync_fetch_and_add (&active_special_connections, 1);
if (active_special_connections > max_special_connections) {
vkprintf (active_special_connections >= max_special_connections + 16 ? 0 : 1, "ERROR: forced to accept connection when special connections limit was reached (%d of %d)\n", active_special_connections, max_special_connections);
}
if (active_special_connections >= max_special_connections) {
vkprintf (2, "**Invoking epoll_remove(%d)\n", LC->fd);
epoll_remove (LC->fd);
}
}
}
alloc_new_socket_connection (C);
MODULE_STAT->allocated_connections ++;
return C;
} else {
MODULE_STAT->accept_init_accepted_failed ++;
if (c->flags & C_RAWMSG) {
rwm_free (&c->in);
rwm_free (&c->out);
rwm_free (&c->in_u);
rwm_free (&c->out_p);
}
c->basic_type = ct_none;
close (cfd);
free_mp_queue (c->in_queue);
free_mp_queue (c->out_queue);
job_free (JOB_REF_PASS (C));
this_job_thread->jobs_active --;
return NULL;
}
}
/* }}} */
/* }}} */
/* {{{ IO PART OF CONNECTION */
/*
Have to have lock on socket_connection to run this method
removes event from evemt heap and epoll
*/
void fail_socket_connection (socket_connection_job_t C, int who) /* {{{ */ {
assert_main_thread ();
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
assert (C->j_flags & JF_LOCKED);
if (!(__sync_fetch_and_or (&c->flags, C_ERROR) & C_ERROR)) {
job_timer_remove (C);
remove_event_from_heap (c->ev, 0);
connection_event_incref (c->fd, -1);
epoll_insert (c->fd, 0);
c->ev = NULL;
c->type->socket_close (C);
fail_connection (c->conn, who);
}
}
/* }}} */
/*
Frees socket_connection structure
Removes link to cpu_connection
*/
int net_server_socket_free (socket_connection_job_t C) /* {{{ */ {
assert_net_net_thread ();
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
assert (!c->ev);
assert (c->flags & C_ERROR);
if (c->conn) {
fail_connection (c->conn, -201);
job_decref (JOB_REF_PASS (c->conn));
}
while (1) {
struct raw_message *raw = mpq_pop_nw (c->out_packet_queue, 4);
if (!raw) { break; }
rwm_free (raw);
free (raw);
}
free_mp_queue (c->out_packet_queue);
rwm_free (&c->out);
MODULE_STAT->allocated_socket_connections --;
return 0;
}
/* }}} */
/*
Reads data from socket until all data is read
Then puts it to conn->in_queue and send JS_RUN signal
*/
int net_server_socket_reader (socket_connection_job_t C) /* {{{ */ {
assert_net_net_thread ();
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
while ((c->flags & (C_WANTRD | C_NORD | C_STOPREAD | C_ERROR | C_NET_FAILED)) == C_WANTRD) {
if (!tcp_recv_buffers_num) {
prealloc_tcp_buffers ();
}
struct raw_message *in = malloc (sizeof (*in));
rwm_init (in, 0);
int s = tcp_recv_buffers_total_size;
assert (s > 0);
int p = 1;
__sync_fetch_and_or (&c->flags, C_NORD);
int r = readv (c->fd, tcp_recv_iovec + p, MAX_TCP_RECV_BUFFERS + 1 - p);
MODULE_STAT->tcp_readv_calls ++;
if (r <= 0) {
if (r < 0 && errno == EAGAIN) {
} else if (r < 0 && errno == EINTR) {
__sync_fetch_and_and (&c->flags, ~C_NORD);
MODULE_STAT->tcp_readv_intr ++;
continue;
} else {
vkprintf (1, "Connection %d: Fatal error %m\n", c->fd);
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
__sync_fetch_and_or (&c->flags, C_NET_FAILED);
return 0;
}
} else {
__sync_fetch_and_and (&c->flags, ~C_NORD);
}
if (verbosity > 0 && r < 0 && errno != EAGAIN) {
perror ("recv()");
}
vkprintf (2, "readv from %d: %d read out of %d\n", c->fd, r, s);
if (r <= 0) {
rwm_free (in);
free (in);
break;
}
MODULE_STAT->tcp_readv_bytes += r;
struct msg_part *mp = 0;
assert (p == 1);
mp = new_msg_part (0, tcp_recv_buffers[p - 1]);
assert (tcp_recv_buffers[p - 1]->data == tcp_recv_iovec[p].iov_base);
mp->offset = 0;
mp->data_end = r > tcp_recv_iovec[p].iov_len ? tcp_recv_iovec[p].iov_len : r;
r -= mp->data_end;
in->first = in->last = mp;
in->total_bytes = mp->data_end;
in->first_offset = 0;
in->last_offset = mp->data_end;
p ++;
int rs = r;
while (rs > 0) {
mp = new_msg_part (0, tcp_recv_buffers[p - 1]);
mp->offset = 0;
mp->data_end = rs > tcp_recv_iovec[p].iov_len ? tcp_recv_iovec[p].iov_len : rs;
rs -= mp->data_end;
in->last->next = mp;
in->last = mp;
in->last_offset = mp->data_end;
in->total_bytes += mp->data_end;
p ++;
}
assert (!rs);
int i;
for (i = 0; i < p - 1; i++) {
struct msg_buffer *X = alloc_msg_buffer (tcp_recv_buffers[i], TCP_RECV_BUFFER_SIZE);
if (!X) {
vkprintf (0, "**FATAL**: cannot allocate tcp receive buffer\n");
assert (0);
}
tcp_recv_buffers[i] = X;
tcp_recv_iovec[i + 1].iov_base = X->data;
tcp_recv_iovec[i + 1].iov_len = X->chunk->buffer_size;
}
assert (c->conn);
mpq_push_w (CONN_INFO(c->conn)->in_queue, in, 0);
job_signal (JOB_REF_CREATE_PASS (c->conn), JS_RUN);
}
return 0;
}
/* }}} */
/*
Get data from out raw message and writes it to socket
*/
int net_server_socket_writer (socket_connection_job_t C) /* {{{ */{
assert_net_net_thread ();
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
struct raw_message *out = &c->out;
int check_watermark = out->total_bytes >= c->write_low_watermark;
int t = 0;
int stop = c->flags & C_STOPWRITE;
while ((c->flags & (C_WANTWR | C_NOWR | C_ERROR | C_NET_FAILED)) == C_WANTWR) {
if (!out->total_bytes) {
__sync_fetch_and_and (&c->flags, ~C_WANTWR);
break;
}
struct iovec iov[384];
int iovcnt = -1;
int s = tcp_prepare_iovec (iov, &iovcnt, sizeof (iov) / sizeof (iov[0]), out);
assert (iovcnt > 0 && s > 0);
__sync_fetch_and_or (&c->flags, C_NOWR);
int r = writev (c->fd, iov, iovcnt);
MODULE_STAT->tcp_writev_calls ++;
if (r <= 0) {
if (r < 0 && errno == EAGAIN) {
if (++c->eagain_count > 100) {
kprintf ("Too much EAGAINs for connection %d (%s), dropping\n", c->fd, show_remote_socket_ip (C));
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
__sync_fetch_and_or (&c->flags, C_NET_FAILED);
return 0;
}
} else if (r < 0 && errno == EINTR) {
__sync_fetch_and_and (&c->flags, ~C_NOWR);
MODULE_STAT->tcp_writev_intr ++;
continue;
} else {
vkprintf (1, "Connection %d: Fatal error %m\n", c->fd);
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
__sync_fetch_and_or (&c->flags, C_NET_FAILED);
return 0;
}
} else {
__sync_fetch_and_and (&c->flags, ~C_NOWR);
MODULE_STAT->tcp_writev_bytes += r;
c->eagain_count = 0;
t += r;
}
if (verbosity && r < 0 && errno != EAGAIN) {
perror ("writev()");
}
vkprintf (2, "send/writev() to %d: %d written out of %d in %d chunks\n", c->fd, r, s, iovcnt);
if (r > 0) {
rwm_skip_data (out, r);
if (c->type->data_sent) {
c->type->data_sent (C, r);
}
}
}
if (check_watermark && out->total_bytes < c->write_low_watermark) {
if (c->type->ready_to_write) {
c->type->ready_to_write (C);
}
}
if (stop && !(c->flags & C_WANTWR)) {
vkprintf (1, "Closing write_close socket\n");
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
__sync_fetch_and_or (&c->flags, C_NET_FAILED);
}
vkprintf (2, "socket_server_writer: written %d bytes to %d, flags=0x%08x\n", t, c->fd, c->flags);
return out->total_bytes;
}
/* }}} */
/*
checks if outbound connections become connected
merges contents of out_packet_queue mpq to out raw message
runs socket_reader and socket_writer
*/
int net_server_socket_read_write (socket_connection_job_t C) /* {{{ */ {
assert_net_net_thread ();
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
if (c->flags & C_ERROR) {
return 0;
}
if (!(c->flags & C_CONNECTED)) {
if (!(c->flags & C_NOWR)) {
__sync_fetch_and_and (&c->flags, C_PERMANENT);
__sync_fetch_and_or (&c->flags, C_WANTRD | C_CONNECTED);
__sync_fetch_and_or (&CONN_INFO(c->conn)->flags, C_READY_PENDING | C_CONNECTED);
c->type->socket_connected (C);
job_signal (JOB_REF_CREATE_PASS (c->conn), JS_RUN);
} else {
return compute_conn_events (C);
}
}
vkprintf (2, "END processing connection %d, flags=%d\n", c->fd, c->flags);
while ((c->flags & (C_WANTRD | C_NORD | C_ERROR | C_STOPREAD | C_NET_FAILED)) == C_WANTRD) {
c->type->socket_reader (C);
}
struct raw_message *out = &c->out;
while (1) {
struct raw_message *raw = mpq_pop_nw (c->out_packet_queue, 4);
if (!raw) { break; }
rwm_union (out, raw);
free (raw);
}
if (out->total_bytes) {
__sync_fetch_and_or (&c->flags, C_WANTWR);
}
while ((c->flags & (C_NOWR | C_ERROR | C_WANTWR | C_NET_FAILED)) == C_WANTWR) {
c->type->socket_writer (C);
}
return compute_conn_events (C);
}
/* }}} */
/*
removes C_NOWR and C_NORD flags if necessary
reads errors from socket
sends JS_RUN signal to socket_connection
*/
int net_server_socket_read_write_gateway (int fd, void *data, event_t *ev) /* {{{ */ {
assert_main_thread ();
if (!data) { return EVA_REMOVE; }
assert ((int)ev->refcnt);
socket_connection_job_t C = (socket_connection_job_t) data;
assert (C);
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
assert (c->type);
if (ev->ready & EVT_FROM_EPOLL) {
// update C_NORD / C_NOWR only if we arrived from epoll()
vkprintf (2, "fd=%d state=%d ready=%d epoll_ready=%d\n", ev->fd, ev->state, ev->ready, ev->epoll_ready);
ev->ready &= ~EVT_FROM_EPOLL;
int clear_flags = 0;
if ((ev->state & EVT_READ) && (ev->ready & EVT_READ)) {
clear_flags |= C_NORD;
}
if ((ev->state & EVT_WRITE) && (ev->ready & EVT_WRITE)) {
clear_flags |= C_NOWR;
}
__sync_fetch_and_and (&c->flags, ~clear_flags);
if (ev->epoll_ready & EPOLLERR) {
int error = 0;
socklen_t errlen = sizeof (error);
if (getsockopt (c->fd, SOL_SOCKET, SO_ERROR, (void *) &error, &errlen) == 0) {
vkprintf (1, "got error for tcp socket #%d, [%s]:%d : %s\n", c->fd, show_remote_socket_ip (C), c->remote_port, strerror (error));
}
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
return EVA_REMOVE;
}
if (ev->epoll_ready & (EPOLLHUP | EPOLLERR | EPOLLRDHUP | EPOLLPRI)) {
vkprintf (!(ev->epoll_ready & EPOLLPRI), "socket %d: disconnected (epoll_ready=%02x), cleaning\n", c->fd, ev->epoll_ready);
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
return EVA_REMOVE;
}
}
job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
return EVA_CONTINUE;
}
/* }}} */
int do_socket_connection_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
socket_connection_job_t C = job;
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
if (op == JS_ABORT) { // MAIN THREAD
fail_socket_connection (C, -200);
return JOB_COMPLETED;
}
if (op == JS_RUN) { // IO THREAD
if (!(c->flags & C_ERROR)) {
int res = c->type->socket_read_write (job);
if (res != c->current_epoll_status) {
c->current_epoll_status = res;
return JOB_SENDSIG (JS_AUX);
}
}
return 0;
}
if (op == JS_AUX) { // MAIN THREAD
if (!(c->flags & C_ERROR)) {
epoll_insert (c->fd, compute_conn_events (job));
}
return 0;
}
if (op == JS_FINISH) { // ANY THREAD
assert (C->j_refcnt == 1);
c->type->socket_free (C);
return job_free (JOB_REF_PASS (C));
}
return JOB_ERROR;
}
/* }}} */
/*
creates socket_connection structure
insert event to epoll
*/
socket_connection_job_t alloc_new_socket_connection (connection_job_t C) /* {{{ */ {
assert_main_thread ();
struct connection_info *c = CONN_INFO (C);
socket_connection_job_t S = create_async_job (do_socket_connection_job, JSC_ALLOW (JC_CONNECTION_IO, JS_RUN) | JSC_ALLOW (JC_CONNECTION_IO, JS_ALARM) | JSC_ALLOW (JC_EPOLL, JS_ABORT) | JSC_ALLOW (JC_CONNECTION_IO, JS_FINISH) | JSC_ALLOW (JC_EPOLL, JS_AUX), -2, sizeof (struct socket_connection_info), JT_HAVE_TIMER, JOB_REF_NULL);
S->j_refcnt = 2;
struct socket_connection_info *s = SOCKET_CONN_INFO (S);
//memset (s, 0, sizeof (*s)); /* no need, create_async_job memsets itself */
s->fd = c->fd;
s->type = c->type;
s->conn = job_incref (C);
s->flags = C_WANTWR | C_WANTRD | (c->flags & C_CONNECTED);
s->our_ip = c->our_ip;
s->our_port = c->our_port;
memcpy (s->our_ipv6, c->our_ipv6, 16);
s->remote_ip = c->remote_ip;
s->remote_port = c->remote_port;
memcpy (s->remote_ipv6, c->remote_ipv6, 16);
s->out_packet_queue = alloc_mp_queue_w ();
struct event_descr *ev = Events + s->fd;
assert (!ev->data);
assert (!ev->refcnt);
s->ev = ev;
epoll_sethandler (s->fd, 0, net_server_socket_read_write_gateway, S);
s->current_epoll_status = compute_conn_events (S);
epoll_insert (s->fd, s->current_epoll_status);
c->io_conn = S;
rwm_init (&s->out, 0);
unlock_job (JOB_REF_CREATE_PASS (S));
MODULE_STAT->allocated_socket_connections ++;
return S;
}
/* }}} */
/* }}} */
/* {{{ LISTENING CONNECTION */
/*
accepts new connections
executes alloc_new_connection ()
*/
int net_accept_new_connections (listening_connection_job_t LCJ) /* {{{ */ {
struct listening_connection_info *LC = LISTEN_CONN_INFO (LCJ);
union sockaddr_in46 peer;
unsigned peer_addrlen;
int cfd, acc = 0;
while (Events[LC->fd].state & EVT_IN_EPOLL) {
peer_addrlen = sizeof (peer);
memset (&peer, 0, sizeof (peer));
cfd = accept (LC->fd, (struct sockaddr *) &peer, &peer_addrlen);
vkprintf (2, "%s: cfd = %d\n", __func__, cfd);
if (cfd < 0) {
if (errno != EAGAIN) {
MODULE_STAT->accept_calls_failed ++;
}
if (!acc) {
vkprintf ((errno == EAGAIN) * 2, "accept(%d) unexpectedly returns %d: %m\n", LC->fd, cfd);
}
break;
}
acc ++;
MODULE_STAT->inbound_connections_accepted ++;
if (max_accept_rate) {
cur_accept_rate_remaining += (precise_now - cur_accept_rate_time) * max_accept_rate;
cur_accept_rate_time = precise_now;
if (cur_accept_rate_remaining > max_accept_rate) {
cur_accept_rate_remaining = max_accept_rate;
}
if (cur_accept_rate_remaining < 1) {
MODULE_STAT->accept_rate_limit_failed ++;
close (cfd);
continue;
}
cur_accept_rate_remaining -= 1;
}
if (LC->flags & C_IPV6) {
assert (peer_addrlen == sizeof (struct sockaddr_in6));
assert (peer.a6.sin6_family == AF_INET6);
} else {
assert (peer_addrlen == sizeof (struct sockaddr_in));
assert (peer.a4.sin_family == AF_INET);
}
connection_job_t C;
if (peer.a4.sin_family == AF_INET) {
C = alloc_new_connection (cfd, NULL, LCJ,
ntohl (peer.a4.sin_addr.s_addr), NULL, ntohs (peer.a4.sin_port));
} else {
C = alloc_new_connection (cfd, NULL, LCJ,
0, peer.a6.sin6_addr.s6_addr, ntohs (peer.a6.sin6_port));
}
if (C) {
assert (CONN_INFO(C)->io_conn);
unlock_job (JOB_REF_PASS (C));
}
}
return 0;
}
/* }}} */
int do_listening_connection_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
listening_connection_job_t LCJ = job;
if (op == JS_RUN) {
net_accept_new_connections (LCJ);
return 0;
} else if (op == JS_AUX) {
vkprintf (2, "**Invoking epoll_insert(%d,%d)\n", LISTEN_CONN_INFO(LCJ)->fd, EVT_RWX);
epoll_insert (LISTEN_CONN_INFO(LCJ)->fd, EVT_RWX);
return 0;
}
return JOB_ERROR;
}
/* }}} */
int init_listening_connection_ext (int fd, conn_type_t *type, void *extra, int mode, int prio) /* {{{ */ {
if (check_conn_functions (type, 1) < 0) {
return -1;
}
if (fd >= max_connection_fd) {
vkprintf (0, "TOO big fd for listening connection %d (max %d)\n", fd, max_connection_fd);
return -1;
}
if (fd > max_connection) {
max_connection = fd;
}
listening_connection_job_t LCJ = create_async_job (do_listening_connection_job, JSC_ALLOW (JC_EPOLL, JS_RUN) | JSC_ALLOW (JC_EPOLL, JS_AUX) | JSC_ALLOW (JC_EPOLL, JS_FINISH), -2, sizeof (struct listening_connection_info), JT_HAVE_TIMER, JOB_REF_NULL);
LCJ->j_refcnt = 2;
struct listening_connection_info *LC = LISTEN_CONN_INFO (LCJ);
memset (LC, 0, sizeof (*LC));
LC->fd = fd;
LC->type = type;
LC->extra = extra;
struct event_descr *ev = Events + fd;
assert (!ev->data);
assert (!ev->refcnt);
LC->ev = ev;
LC->generation = new_conn_generation ();
if (mode & SM_LOWPRIO) {
prio = 10;
}
if (mode & SM_SPECIAL) {
LC->flags |= C_SPECIAL;
int idx = __sync_fetch_and_add (&special_listen_sockets, 1);
assert (idx < MAX_SPECIAL_LISTEN_SOCKETS);
special_socket[idx].fd = LC->fd;
special_socket[idx].generation = LC->generation;
}
if (mode & SM_NOQACK) {
LC->flags |= C_NOQACK;
disable_qack (LC->fd);
}
if (mode & SM_IPV6) {
LC->flags |= C_IPV6;
}
if (mode & SM_RAWMSG) {
LC->flags |= C_RAWMSG;
}
epoll_sethandler (fd, prio, net_server_socket_read_write_gateway, LCJ);
epoll_insert (fd, EVT_RWX);
MODULE_STAT->listening_connections ++;
unlock_job (JOB_REF_PASS (LCJ));
return 0;
}
int init_listening_connection (int fd, conn_type_t *type, void *extra) {
return init_listening_connection_ext (fd, type, extra, 0, -10);
}
int init_listening_tcpv6_connection (int fd, conn_type_t *type, void *extra, int mode) {
return init_listening_connection_ext (fd, type, extra, mode, -10);
}
/* }}} */
/* }}} */
/* {{{ connection refcnt */
void connection_event_incref (int fd, long long val) {
struct event_descr *ev = &Events[fd];
if (!__sync_add_and_fetch (&ev->refcnt, val) && ev->data) {
socket_connection_job_t C = ev->data;
ev->data = NULL;
job_decref (JOB_REF_PASS (C));
}
}
connection_job_t connection_get_by_fd (int fd) {
struct event_descr *ev = &Events[fd];
if (!(int)(ev->refcnt) || !ev->data) { return NULL; }
while (1) {
long long v = __sync_fetch_and_add (&ev->refcnt, (1ll << 32));
if (((int)v) != 0) { break; }
v = __sync_fetch_and_add (&ev->refcnt, -(1ll << 32));
if (((int)v) != 0) { continue; }
return NULL;
}
__sync_fetch_and_add (&ev->refcnt, 1 - (1ll << 32));
socket_connection_job_t C = job_incref (ev->data);
connection_event_incref (fd, -1);
if (C->j_execute == &do_listening_connection_job) {
return C;
}
assert (C->j_execute == &do_socket_connection_job);
struct socket_connection_info *c = SOCKET_CONN_INFO (C);
if (c->flags & C_ERROR) {
job_decref (JOB_REF_PASS (C));
return NULL;
} else {
assert (c->conn);
connection_job_t C2 = job_incref (c->conn);
job_decref (JOB_REF_PASS (C));
return C2;
}
}
connection_job_t connection_get_by_fd_generation (int fd, int generation) {
connection_job_t C = connection_get_by_fd (fd);
if (C && CONN_INFO(C)->generation != generation) {
job_decref (JOB_REF_PASS (C));
return NULL;
} else {
return C;
}
}
/* }}} */
/* {{{ Sample server functions */
int server_check_ready (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
if (c->status == conn_none || c->status == conn_connecting) {
return c->ready = cr_notyet;
}
if (c->status == conn_error || c->ready == cr_failed) {
return c->ready = cr_failed;
}
return c->ready = cr_ok;
}
/* }}} */
int server_noop (connection_job_t C) /* {{{ */ {
return 0;
}
/* }}} */
int server_failed (connection_job_t C) /* {{{ */ {
kprintf ("connection %d: call to pure virtual method\n", CONN_INFO(C)->fd);
assert (0);
return -1;
}
/* }}} */
int server_flush (connection_job_t C) /* {{{ */ {
//job_signal (job_incref (C), JS_RUN);
return 0;
}
/* }}} */
int check_conn_functions (conn_type_t *type, int listening) /* {{{ */ {
if (type->magic != CONN_FUNC_MAGIC) {
return -1;
}
if (!type->title) {
type->title = "(unknown)";
}
if (!type->socket_read_write) {
type->socket_read_write = net_server_socket_read_write;
}
if (!type->socket_reader) {
type->socket_reader = net_server_socket_reader;
}
if (!type->socket_writer) {
type->socket_writer = net_server_socket_writer;
}
if (!type->socket_close) {
type->socket_close = server_noop;
}
if (!type->accept) {
if (listening) {
type->accept = net_accept_new_connections;
} else {
type->accept = server_failed;
}
}
if (!type->init_accepted) {
if (listening) {
type->init_accepted = server_noop;
} else {
type->init_accepted = server_failed;
}
}
if (!type->close) {
type->close = cpu_server_close_connection;
}
if (!type->init_outbound) {
type->init_outbound = server_noop;
}
if (!type->wakeup) {
type->wakeup = server_noop;
}
if (!type->alarm) {
type->alarm = server_noop;
}
if (!type->connected) {
type->connected = server_noop;
}
if (!type->flush) {
type->flush = server_flush;
}
if (!type->check_ready) {
type->check_ready = server_check_ready;
}
if (!type->read_write) {
type->read_write = cpu_server_read_write;
}
if (!type->free) {
type->free = cpu_server_free_connection;
}
if (!type->socket_connected) {
type->socket_connected = server_noop;
}
if (!type->socket_free) {
type->socket_free = net_server_socket_free;
}
if (type->flags & C_RAWMSG) {
if (!type->free_buffers) {
type->free_buffers = cpu_tcp_free_connection_buffers;
}
if (!type->reader) {
type->reader = cpu_tcp_server_reader;
if (!type->parse_execute) {
return -1;
}
}
if (!type->writer) {
type->writer = cpu_tcp_server_writer;
}
} else {
if (!type->free_buffers) {
assert (0);
}
if (!type->reader) {
assert (0);
}
if (!type->writer) {
assert (0);
}
}
return 0;
}
/* }}} */
/* }}} */
/* CONN TARGETS {{{ */
void compute_next_reconnect (conn_target_job_t CT) /* {{{ */{
struct conn_target_info *S = CONN_TARGET_INFO (CT);
if (S->next_reconnect_timeout < S->reconnect_timeout || S->active_outbound_connections) {
S->next_reconnect_timeout = S->reconnect_timeout;
}
S->next_reconnect = precise_now + S->next_reconnect_timeout;
if (!S->active_outbound_connections && S->next_reconnect_timeout < MAX_RECONNECT_INTERVAL) {
S->next_reconnect_timeout = S->next_reconnect_timeout * 1.5 + drand48_j () * 0.2;
}
}
/* }}} */
static void count_connection_num (connection_job_t C, void *good_c, void *stopped_c, void *bad_c) /* {{{ */ {
int cr = CONN_INFO(C)->type->check_ready (C);
switch (cr) {
case cr_notyet:
case cr_busy:
break;
case cr_ok:
(*(int *)good_c)++;
break;
case cr_stopped:
(*(int *)stopped_c)++;
break;
case cr_failed:
(*(int *)bad_c)++;
break;
default:
assert (0);
}
}
/* }}} */
static void find_bad_connection (connection_job_t C, void *x) /* {{{ */ {
connection_job_t *T = x;
if (*T) { return; }
if (CONN_INFO(C)->flags & C_ERROR) {
*T = C;
}
}
/* }}} */
/*
Deletes failed connections (with flag C_ERROR) from target's tree
*/
void destroy_dead_target_connections (conn_target_job_t CTJ) /* {{{ */ {
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
struct tree_connection *T = CT->conn_tree;
if (T) {
__sync_fetch_and_add (&T->refcnt, 1);
}
while (1) {
connection_job_t CJ = NULL;
tree_act_ex_connection (T, find_bad_connection, &CJ);
if (!CJ) { break; }
if (connection_is_active (CONN_INFO (CJ)->flags)) {
__sync_fetch_and_add (&CT->active_outbound_connections, -1);
}
__sync_fetch_and_add (&CT->outbound_connections, -1);
T = tree_delete_connection (T, CJ);
}
int good_c = 0, bad_c = 0, stopped_c = 0;
tree_act_ex3_connection (T, count_connection_num, &good_c, &stopped_c, &bad_c);
int was_ready = CT->ready_outbound_connections;
CT->ready_outbound_connections = good_c;
if (was_ready != CT->ready_outbound_connections) {
MODULE_STAT->ready_outbound_connections += CT->ready_outbound_connections - was_ready;
}
if (was_ready && !CT->ready_outbound_connections) {
MODULE_STAT->ready_targets --;
}
if (!was_ready && CT->ready_outbound_connections) {
MODULE_STAT->ready_targets ++;
}
if (T == CT->conn_tree) {
tree_free_connection (T);
} else {
struct tree_connection *old = CT->conn_tree;
CT->conn_tree = T;
barrier ();
__sync_synchronize ();
free_tree_ptr_connection (old);
}
}
/* }}} */
/*
creates new connections for target
must be called in main thread, because we can allocate new connections only in main thread
*/
int create_new_connections (conn_target_job_t CTJ) /* {{{ */ {
assert_main_thread ();
destroy_dead_target_connections (CTJ);
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
int count = 0, good_c = 0, bad_c = 0, stopped_c = 0, need_c;
tree_act_ex3_connection (CT->conn_tree, count_connection_num, &good_c, &stopped_c, &bad_c);
int was_ready = CT->ready_outbound_connections;
CT->ready_outbound_connections = good_c;
if (was_ready != CT->ready_outbound_connections) {
MODULE_STAT->ready_outbound_connections += CT->ready_outbound_connections - was_ready;
}
if (was_ready && !CT->ready_outbound_connections) {
MODULE_STAT->ready_targets --;
}
if (!was_ready && CT->ready_outbound_connections) {
MODULE_STAT->ready_targets ++;
}
need_c = CT->min_connections + bad_c + ((stopped_c + 1) >> 1);
if (need_c > CT->max_connections) {
need_c = CT->max_connections;
}
if (precise_now >= CT->next_reconnect || CT->active_outbound_connections) {
struct tree_connection *T = CT->conn_tree;
if (T) {
__sync_fetch_and_add (&T->refcnt, 1);
}
while (CT->outbound_connections < need_c) {
if (CT->target.s_addr) {
vkprintf (1, "Creating NEW connection to %s:%d\n", inet_ntoa (CT->target), CT->port);
} else {
vkprintf (1, "Creating NEW ipv6 connection to [%s]:%d\n", show_ipv6 (CT->target_ipv6), CT->port);
}
int cfd = CT->target.s_addr ? client_socket (CT->target.s_addr, CT->port, 0) : client_socket_ipv6 (CT->target_ipv6, CT->port, SM_IPV6);
if (cfd < 0) {
if (CT->target.s_addr) {
vkprintf (1, "error connecting to %s:%d: %m\n", inet_ntoa (CT->target), CT->port);
} else {
vkprintf (1, "error connecting to [%s]:%d\n", show_ipv6 (CT->target_ipv6), CT->port);
}
break;
}
connection_job_t C = alloc_new_connection (cfd, CTJ, NULL,
ntohl (CT->target.s_addr), CT->target_ipv6, CT->port);
if (C) {
assert (CONN_INFO(C)->io_conn);
count ++;
unlock_job (JOB_REF_CREATE_PASS (C));
T = tree_insert_connection (T, C, lrand48_j ());
} else {
break;
}
}
if (T == CT->conn_tree) {
tree_free_connection (T);
} else {
struct tree_connection *old = CT->conn_tree;
CT->conn_tree = T;
__sync_synchronize ();
free_tree_ptr_connection (old);
}
compute_next_reconnect (CTJ);
}
return count;
}
/* }}} */
conn_target_job_t HTarget[PRIME_TARGETS];
pthread_mutex_t TargetsLock = PTHREAD_MUTEX_INITIALIZER;
/* must be called with mutex held */
/* mode = 0 -- lookup, mode = 1 -- insert, mode = -1 -- delete */
static conn_target_job_t find_target (struct in_addr ad, int port, conn_type_t *type, void *extra, int mode, conn_target_job_t new_target) /* {{{ */ {
assert (ad.s_addr);
unsigned h1 = ((unsigned long) type * 0xabacaba + ad.s_addr) % PRIME_TARGETS;
h1 = (h1 * 239 + port) % PRIME_TARGETS;
conn_target_job_t *prev = HTarget + h1, cur;
while ((cur = *prev) != 0) {
struct conn_target_info *S = CONN_TARGET_INFO (cur);
if (S->target.s_addr == ad.s_addr && S->port == port && S->type == type && S->extra == extra) {
if (mode < 0) {
*prev = S->hnext;
S->hnext = 0;
return cur;
}
assert (!mode);
return cur;
}
prev = &S->hnext;
}
assert (mode >= 0);
if (mode > 0) {
CONN_TARGET_INFO (new_target)->hnext = HTarget[h1];
HTarget[h1] = new_target;
return new_target;
}
return 0;
}
/* }}} */
/* must be called with mutex held */
/* mode = 0 -- lookup, mode = 1 -- insert, mode = -1 -- delete */
static conn_target_job_t find_target_ipv6 (unsigned char ad_ipv6[16], int port, conn_type_t *type, void *extra, int mode, conn_target_job_t new_target) /* {{{ */ {
assert (*(long long *)ad_ipv6 || ((long long *) ad_ipv6)[1]);
unsigned h1 = ((unsigned long) type * 0xabacaba) % PRIME_TARGETS;
int i;
for (i = 0; i < 4; i++) {
h1 = ((unsigned long long) h1 * 17239 + ((unsigned *) ad_ipv6)[i]) % PRIME_TARGETS;
}
h1 = (h1 * 239 + port) % PRIME_TARGETS;
conn_target_job_t *prev = HTarget + h1, cur;
while ((cur = *prev) != 0) {
struct conn_target_info *S = CONN_TARGET_INFO (cur);
if (
((long long *)S->target_ipv6)[1] == ((long long *)ad_ipv6)[1] &&
*(long long *)S->target_ipv6 == *(long long *)ad_ipv6 &&
S->port == port && S->type == type && !S->target.s_addr && S->extra == extra) {
if (mode < 0) {
*prev = S->hnext;
S->hnext = 0;
return cur;
}
assert (!mode);
return cur;
}
prev = &S->hnext;
}
assert (mode >= 0);
if (mode > 0) {
CONN_TARGET_INFO (new_target)->hnext = HTarget[h1];
HTarget[h1] = new_target;
return new_target;
}
return 0;
}
/* }}} */
static int free_target (conn_target_job_t CTJ) /* {{{ */ {
pthread_mutex_lock (&TargetsLock);
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
if (CT->global_refcnt > 0 || CT->conn_tree) {
pthread_mutex_unlock (&TargetsLock);
return -1;
}
assert (CT && CT->type && !CT->global_refcnt);
assert (!CT->conn_tree);
if (CT->target.s_addr) {
vkprintf (1, "Freeing unused target to %s:%d\n", inet_ntoa (CT->target), CT->port);
assert (CTJ == find_target (CT->target, CT->port, CT->type, CT->extra, -1, 0));
} else {
vkprintf (1, "Freeing unused ipv6 target to [%s]:%d\n", show_ipv6 (CT->target_ipv6), CT->port);
assert (CTJ == find_target_ipv6 (CT->target_ipv6, CT->port, CT->type, CT->extra, -1, 0));
}
pthread_mutex_unlock (&TargetsLock);
MODULE_STAT->inactive_targets --;
MODULE_STAT->free_targets ++;
job_decref (JOB_REF_PASS (CTJ));
return 1;
}
/* }}} */
static void fail_connection_gw (connection_job_t C) {
fail_connection (C, -17);
}
int clean_unused_target (conn_target_job_t CTJ) /* {{{ */ {
assert (CTJ);
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
assert (CT->type);
if (CT->global_refcnt) {
return 0;
}
if (CT->conn_tree) {
tree_act_connection (CT->conn_tree, fail_connection_gw);
return 0;
}
job_timer_remove (CTJ);
return 0;
}
/* }}} */
int destroy_target (JOB_REF_ARG (CTJ)) /* {{{ */ {
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
assert (CT);
assert (CT->type);
assert (CT->global_refcnt > 0);
int r;
if (!((r = __sync_add_and_fetch (&CT->global_refcnt, -1)))) {
MODULE_STAT->active_targets--;
MODULE_STAT->inactive_targets++;
job_signal (JOB_REF_PASS (CTJ), JS_RUN);
} else {
job_decref (JOB_REF_PASS (CTJ));
}
return r;
}
/*}}} */
int do_conn_target_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
if (epoll_fd <= 0) {
job_timer_insert (job, precise_now + 0.01);
return 0;
}
conn_target_job_t CTJ = job;
struct conn_target_info *CT = CONN_TARGET_INFO (CTJ);
if (op == JS_ALARM || op == JS_RUN) {
if (op == JS_ALARM && !job_timer_check (job)) {
return 0;
}
if (!CT->global_refcnt) {
destroy_dead_target_connections (CTJ);
clean_unused_target (CTJ);
compute_next_reconnect (CTJ);
} else {
create_new_connections (CTJ);
}
if (CTJ->j_flags & JF_COMPLETED) { return 0; }
if (CT->global_refcnt || CT->conn_tree) {
job_timer_insert (CTJ, precise_now + 0.1);
return 0;
} else {
if (free_target (CTJ) >= 0) {
return JOB_COMPLETED;
} else {
job_timer_insert (CTJ, precise_now + 0.1);
return 0;
}
}
}
if (op == JS_FINISH) {
assert (CTJ->j_flags & JF_COMPLETED);
MODULE_STAT->allocated_targets --;
return job_free (JOB_REF_PASS (job));
}
return JOB_ERROR;
}
/* }}} */
conn_target_job_t create_target (struct conn_target_info *source, int *was_created) /* {{{ */ {
if (check_conn_functions (source->type, 0) < 0) {
return NULL;
}
pthread_mutex_lock (&TargetsLock);
conn_target_job_t T =
source->target.s_addr ?
find_target (source->target, source->port, source->type, source->extra, 0, 0) :
find_target_ipv6 (source->target_ipv6, source->port, source->type, source->extra, 0, 0);
if (T) {
struct conn_target_info *t = CONN_TARGET_INFO (T);
t->min_connections = source->min_connections;
t->max_connections = source->max_connections;
t->reconnect_timeout = source->reconnect_timeout;
if (!__sync_fetch_and_add (&t->global_refcnt, 1)) {
MODULE_STAT->active_targets++;
MODULE_STAT->inactive_targets--;
if (was_created) {
*was_created = 2;
}
} else {
if (was_created) {
*was_created = 0;
}
}
job_incref (T);
} else {
//assert (MODULE_STAT->allocated_targets < MAX_TARGETS);
T = create_async_job (do_conn_target_job, JSC_ALLOW (JC_EPOLL, JS_RUN) | JSC_ALLOW (JC_EPOLL, JS_ABORT) | JSC_ALLOW (JC_EPOLL, JS_ALARM) | JSC_ALLOW (JC_EPOLL, JS_FINISH), -2, sizeof (struct conn_target_info), JT_HAVE_TIMER, JOB_REF_NULL);
T->j_refcnt = 2;
struct conn_target_info *t = CONN_TARGET_INFO (T);
memcpy (t, source, sizeof (*source));
job_timer_init (T);
//t->generation = 1;
MODULE_STAT->active_targets ++;
MODULE_STAT->allocated_targets ++;
if (source->target.s_addr) {
find_target (source->target, source->port, source->type, source->extra, 1, T);
} else {
find_target_ipv6 (source->target_ipv6, source->port, source->type, source->extra, 1, T);
}
if (was_created) {
*was_created = 1;
}
t->global_refcnt = 1;
schedule_job (JOB_REF_CREATE_PASS (T));
}
pthread_mutex_unlock (&TargetsLock);
return T;
}
/* }}} */
/* }}} */
void tcp_set_max_connections (int maxconn) /* {{{ */ {
max_connection_fd = maxconn;
if (!max_special_connections || max_special_connections > maxconn) {
max_special_connections = maxconn;
}
}
/* }}} */
int create_all_outbound_connections_limited (int limit) /* {{{ */ {
return 0;
/*int count = 0;
get_utime_monotonic ();
//close_some_unneeded_connections ();
//ready_outbound_connections = ready_targets = 0;
int new_ready_outbound_connections = 0;
int new_ready_targets = 0;
pthread_mutex_lock (&TargetsLock);
conn_target_job_t S;
for (S = CONN_TARGET_INFO(ActiveTargets)->next_target; S != ActiveTargets && count < limit; S = CONN_TARGET_INFO(S)->next_target) {
struct conn_target_info *s = CONN_TARGET_INFO (S);
assert (s->type && s->refcnt > 0);
count += create_new_connections (S);
if (s->ready_outbound_connections) {
new_ready_outbound_connections += s->ready_outbound_connections;
new_ready_targets++;
}
}
pthread_mutex_unlock (&TargetsLock);
MODULE_STAT->ready_targets = new_ready_targets;
MODULE_STAT->ready_outbound_connections = new_ready_outbound_connections;
return count; */
}
/* }}} */
int create_all_outbound_connections (void) /* {{{ */ {
return create_all_outbound_connections_limited (0x7fffffff);
}
/* }}} */
/* {{{ conn_target_get_connection */
static void check_connection (connection_job_t C, void *x) {
connection_job_t *P = x;
if (*P) { return; }
int r = CONN_INFO (C)->type->check_ready (C);
if (r == cr_ok) {
*P = C;
return;
}
}
static void check_connection_stopped (connection_job_t C, void *x) {
connection_job_t *P = x;
if (*P && CONN_INFO (*P)->ready == cr_ok) { return; }
int r = CONN_INFO (C)->type->check_ready (C);
if (r == cr_ok) {
*P = C;
return;
}
if (r == cr_stopped && (!*P || CONN_INFO (*P)->unreliability > CONN_INFO (C)->unreliability)) {
*P = C;
return;
}
}
connection_job_t conn_target_get_connection (conn_target_job_t CT, int allow_stopped) {
assert (CT);
struct conn_target_info *t = CONN_TARGET_INFO (CT);
struct tree_connection *T = get_tree_ptr_connection (&t->conn_tree);
connection_job_t S = NULL;
tree_act_ex_connection (T, allow_stopped ? check_connection_stopped : check_connection, &S);
if (S) { job_incref (S); }
tree_free_connection (T);
return S;
}
/* }}} */
void insert_free_later_struct (struct free_later *F) {
if (!free_later_queue) {
free_later_queue = alloc_mp_queue_w ();
}
mpq_push_w (free_later_queue, F, 0);
MODULE_STAT->free_later_size ++;
MODULE_STAT->free_later_total ++;
}
void free_later_act (void) {
if (!free_later_queue) { return; }
while (1) {
struct free_later *F = mpq_pop_nw (free_later_queue, 4);
if (!F) { return; }
MODULE_STAT->free_later_size --;
F->free (F->ptr);
free (F);
}
}
void free_connection_tree_ptr (struct tree_connection *T) /* {{{ */ {
free_tree_ptr_connection (T);
}
/* }}} */
void incr_active_dh_connections (void) {
MODULE_STAT->active_dh_connections ++;
}
int new_conn_generation (void) {
return __sync_fetch_and_add (&conn_generation, 1);
}
int get_cur_conn_generation (void) {
return conn_generation;
}
// -----
int nat_info_rules;
unsigned nat_info[MAX_NAT_INFO_RULES][2];
int net_add_nat_info (char *str) {
char *str2 = strrchr (str, ':');
if (!str2) {
fprintf (stderr, "expected <local-addr>:<global-addr> in --nat-info\n");
return -1;
}
*str2++ = 0;
struct in_addr l_addr, g_addr;
if (inet_pton (AF_INET, str, &l_addr) <= 0) {
fprintf (stderr, "cannot translate host '%s' in --nat-info\n", str);
return -1;
}
if (inet_pton (AF_INET, str2, &g_addr) <= 0) {
fprintf (stderr, "cannot translate host '%s' in --nat-info\n", str2);
return -1;
}
if (nat_info_rules >= MAX_NAT_INFO_RULES) {
fprintf (stderr, "too many rules in --nat-info\n");
return -1;
}
nat_info[nat_info_rules][0] = ntohl (l_addr.s_addr);
nat_info[nat_info_rules][1] = ntohl (g_addr.s_addr);
return nat_info_rules++;
}
unsigned nat_translate_ip (unsigned local_ip) {
int i;
vkprintf (6, "nat_info: %d rules\n", nat_info_rules);
for (i = 0; i < nat_info_rules; i++) {
vkprintf (6, "nat_info rule #%d: %s to %s\n", i, show_ip (nat_info[i][0]), show_ip (nat_info[i][1]));
if (nat_info[i][0] == local_ip) {
vkprintf (4, "translating ip by nat_info rules: %s to %s\n", show_ip (local_ip), show_ip (nat_info[i][1]));
return nat_info[i][1];
}
}
return local_ip;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
Copyright 2014 Telegram Messenger Inc
2014 Nikolai Durov
2014 Andrey Lopatin
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
//#include "net/net-buffers.h"
#include "net/net-events.h"
#include "net/net-msg.h"
#include "jobs/jobs.h"
#include "common/mp-queue.h"
#include "common/pid.h"
#define MAX_CONNECTIONS 65536
#define MAX_TARGETS 65536
#define PRIME_TARGETS 99961
#define MAX_SPECIAL_LISTEN_SOCKETS 64
#define MAX_TCP_RECV_BUFFERS 128
#define TCP_RECV_BUFFER_SIZE 1024
#define MAX_NET_RES (1L << 16)
//#define BUFF_SIZE 2048
#define CONN_CUSTOM_DATA_BYTES 256
#define NEED_MORE_BYTES (~(-1 << 31))
#define SKIP_ALL_BYTES (-1 << 31)
/* for connection flags */
#define C_WANTRD 1
#define C_WANTWR 2
#define C_WANTRW (C_WANTRD | C_WANTWR)
#define C_INCONN 4
#define C_ERROR 8
#define C_NORD 0x10
#define C_NOWR 0x20
#define C_NORW (C_NORD | C_NOWR)
#define C_INQUERY 0x40
#define C_FAILED 0x80
#define C_ALARM 0x100
#define C_AIO 0x200
#define C_INTIMEOUT 0x400
#define C_STOPREAD 0x800
#define C_REPARSE 0x1000
#define C_DFLUSH 0x2000
#define C_IPV6 0x4000
#define C_EXTERNAL 0x8000
#define C_SPECIAL 0x10000
#define C_NOQACK 0x20000
#define C_RAWMSG 0x40000
#define C_NET_FAILED 0x80000
#define C_CRYPTOIN 0x100000
#define C_CRYPTOOUT 0x200000
#define C_STOPPARSE 0x400000
#define C_ISDH 0x800000
#define C_READY_PENDING 0x1000000
#define C_CONNECTED 0x2000000
#define C_STOPWRITE 0x4000000
#define C_PERMANENT (C_IPV6 | C_RAWMSG)
/* for connection status */
enum {
conn_none, // closed/uninitialized
conn_connecting,
conn_working,
conn_error, // connection in bad state (it will be probably closed)
conn_listen, // listening for inbound connections
conn_write_close, // write all output buffer, then close; don't read input
conn_total_states // total number of connection states
};
/* for connection basic_type */
enum {
ct_none, // no connection (closed)
ct_listen, // listening socket
ct_inbound, // inbound connection
ct_outbound, // outbound connection
ct_pipe, // used for pipe reading
ct_job // used for async jobs ( net-jobs.h )
};
/* for connection->ready of outbound connections */
enum {
cr_notyet, // not ready yet (e.g. logging in)
cr_ok, // working
cr_stopped, // stopped (don't send more queries)
cr_busy, // busy (sending queries not allowed by protocol)
cr_failed // failed (possibly timed out)
};
typedef job_t connection_job_t;
typedef job_t socket_connection_job_t;
typedef job_t listening_connection_job_t;
typedef job_t conn_target_job_t;
typedef job_t query_job_t;
/* connection function table */
#define CONN_FUNC_MAGIC 0x11ef55aa
typedef struct conn_functions {
int magic;
int flags; /* may contain for example C_RAWMSG; (partially) inherited by inbound/outbound connections */
char *title;
int (*accept)(connection_job_t c); /* invoked for listen/accept connections of this type */
int (*init_accepted)(connection_job_t c); /* initialize a new accept()'ed connection */
int (*reader)(connection_job_t c); /* invoked from run() for reading network data */
int (*writer)(connection_job_t c); /* invoked from run() for writing data */
int (*close)(connection_job_t c, int who); /* invoked from run() whenever we need to close connection */
int (*parse_execute)(connection_job_t c); /* invoked from reader() for parsing and executing one query */
int (*init_outbound)(connection_job_t c); /* initializes newly created outbound connection */
int (*connected)(connection_job_t c); /* invoked from run() when outbound connection is established */
int (*check_ready)(connection_job_t c); /* updates conn->ready if necessary and returns it */
int (*wakeup_aio)(connection_job_t c, int r);/* invoked from net_aio.c::check_aio_completion when aio read operation is complete */
int (*write_packet)(connection_job_t c, struct raw_message *raw); /* adds necessary headers to packet */
int (*flush)(connection_job_t c); /* generates necessary padding and writes as much bytes as possible */
// CPU-NET METHODS
int (*free)(connection_job_t c);
int (*free_buffers)(connection_job_t c); /* invoked from close() to free all buffers */
int (*read_write)(connection_job_t c); /* invoked when an event related to connection of this type occurs */
int (*wakeup)(connection_job_t c); /* invoked from run() when pending_queries == 0 */
int (*alarm)(connection_job_t c); /* invoked when timer is out */
// NET-NET METHODS
int (*socket_read_write)(connection_job_t c); /* invoked when an event related to connection of this type occurs */
int (*socket_reader)(connection_job_t c); /* invoked from run() for reading network data */
int (*socket_writer)(connection_job_t c); /* invoked from run() for writing data */
int (*socket_connected)(connection_job_t c); /* invoked from run() when outbound connection is established */
int (*socket_free)(connection_job_t c);
int (*socket_close)(connection_job_t c);
// INLINE FUNCTIONS
int (*data_received)(connection_job_t c, int r); /* invoked after r>0 bytes are read from socket */
int (*data_sent)(connection_job_t c, int w); /* invoked after w>0 bytes are written into socket */
int (*ready_to_write)(connection_job_t c); /* invoked from server_writer when Out.total_bytes crosses write_low_watermark ("greater or equal" -> "less") */
// INLINE METHODS
int (*crypto_init)(connection_job_t c, void *key_data, int key_data_len); /* < 0 = error */
int (*crypto_free)(connection_job_t c);
int (*crypto_encrypt_output)(connection_job_t c); /* 0 = all ok, >0 = so much more bytes needed to encrypt last block */
int (*crypto_decrypt_input)(connection_job_t c); /* 0 = all ok, >0 = so much more bytes needed to decrypt last block */
int (*crypto_needed_output_bytes)(connection_job_t c); /* returns # of bytes needed to complete last output block */
} conn_type_t;
struct conn_target_info {
struct event_timer timer;
int min_connections;
int max_connections;
struct tree_connection *conn_tree;
//connection_job_t first_conn, last_conn;
conn_type_t *type;
void *extra;
struct in_addr target;
unsigned char target_ipv6[16];
int port;
int active_outbound_connections, outbound_connections;
int ready_outbound_connections;
double next_reconnect, reconnect_timeout, next_reconnect_timeout;
int custom_field;
conn_target_job_t next_target, prev_target;
conn_target_job_t hnext;
int global_refcnt;
};
struct connection_info {
struct event_timer timer;
int fd;
int generation;
int flags;
// connection_job_t next, prev;
conn_type_t *type;
void *extra;
conn_target_job_t target;
connection_job_t io_conn;
int basic_type;
int status;
int error;
int unread_res_bytes;
int skip_bytes;
int pending_queries;
int queries_ok;
char custom_data[CONN_CUSTOM_DATA_BYTES];
unsigned our_ip, remote_ip;
unsigned our_port, remote_port;
unsigned char our_ipv6[16], remote_ipv6[16];
double query_start_time;
double last_query_time;
double last_query_sent_time;
double last_response_time;
double last_query_timeout;
//event_timer_t timer;
//event_timer_t write_timer;
int limit_per_write, limit_per_sec;
int last_write_time, written_per_sec;
int unreliability;
int ready;
//int parse_state;
int write_low_watermark;
void *crypto;
void *crypto_temp;
int listening, listening_generation;
int window_clamp;
struct raw_message in_u, in, out, out_p;
struct mp_queue *in_queue;
struct mp_queue *out_queue;
//netbuffer_t *Tmp, In, Out;
//char in_buff[BUFF_SIZE];
//char out_buff[BUFF_SIZE];
};
struct socket_connection_info {
struct event_timer timer;
int fd;
int pad;
int flags;
int current_epoll_status;
conn_type_t *type;
event_t *ev;
connection_job_t conn;
struct mp_queue *out_packet_queue;
struct raw_message out;
unsigned our_ip, remote_ip;
unsigned our_port, remote_port;
unsigned char our_ipv6[16], remote_ipv6[16];
int write_low_watermark;
int eagain_count;
};
struct listening_connection_info {
struct event_timer timer;
int fd;
int generation;
int flags;
int current_epoll_status;
conn_type_t *type;
event_t *ev;
void *extra;
int window_clamp;
};
struct connections_stat {
int active_connections;
int active_dh_connections;
int outbound_connections;
int active_outbound_connections;
int ready_outbound_connections;
int active_special_connections;
int max_special_connections;
int allocated_connections;
int allocated_outbound_connections;
int allocated_inbound_connections;
int allocated_socket_connections;
int allocated_targets;
int ready_targets;
int active_targets;
int inactive_targets;
long long tcp_readv_calls;
long long tcp_readv_intr;
long long tcp_readv_bytes;
long long tcp_writev_calls;
long long tcp_writev_intr;
long long tcp_writev_bytes;
long long accept_calls_failed;
long long accept_nonblock_set_failed;
long long accept_rate_limit_failed;
long long accept_init_accepted_failed;
long long accept_connection_limit_failed;
};
#define QUERY_INFO(_c) ((struct query_info *)(_c)->j_custom)
#define CONN_INFO(_conn) ((struct connection_info *)((_conn)->j_custom))
#define LISTEN_CONN_INFO(_conn) ((struct listening_connection_info *)((_conn)->j_custom))
#define SOCKET_CONN_INFO(_conn) ((struct socket_connection_info *)((_conn)->j_custom))
#define CONN_TARGET_INFO(_conn_target) ((struct conn_target_info *)((_conn_target)->j_custom))
static inline const char *show_ip46 (unsigned ip, const unsigned char ipv6[16]) { return ip ? show_ip (ip) : show_ipv6 (ipv6); }
static inline const char *show_our_ip (connection_job_t c) { return show_ip46 (CONN_INFO(c)->our_ip, CONN_INFO(c)->our_ipv6); }
static inline const char *show_remote_ip (connection_job_t c) { return show_ip46 (CONN_INFO(c)->remote_ip, CONN_INFO(c)->remote_ipv6); }
static inline const char *show_our_socket_ip (socket_connection_job_t c) { return show_ip46 (SOCKET_CONN_INFO(c)->our_ip, SOCKET_CONN_INFO(c)->our_ipv6); }
static inline const char *show_remote_socket_ip (socket_connection_job_t c) { return show_ip46 (SOCKET_CONN_INFO(c)->remote_ip, SOCKET_CONN_INFO(c)->remote_ipv6); }
void fetch_connections_stat (struct connections_stat *st);
void compute_next_reconnect (conn_target_job_t CT);
int create_all_outbound_connections (void);
int clean_unused_target (conn_target_job_t S);
int create_new_connections (conn_target_job_t S);
int set_connection_timeout (connection_job_t C, double timeout);
int clear_connection_timeout (connection_job_t C);
int prepare_stats (char *buf, int size);
void fail_connection (connection_job_t C, int who);
void fail_socket_connection (socket_connection_job_t C, int who);
int destroy_target (JOB_REF_ARG (CTJ));
conn_target_job_t create_target (struct conn_target_info *source, int *was_created);
void compute_next_reconnect (conn_target_job_t CT);
static inline connection_job_t connection_incref (connection_job_t C) { return job_incref (C); }
static inline void connection_decref (connection_job_t C) { job_decref (JOB_REF_PASS (C)); }
connection_job_t connection_get_by_fd (int fd);
connection_job_t connection_get_by_fd_generation (int fd, int generation);
int cpu_server_reader (connection_job_t C);
int cpu_server_writer (connection_job_t C);
int cpu_server_read_write (connection_job_t C);
//int cpu_free_tmp_buffers (connection_job_t C);
int cpu_server_free_connection (connection_job_t C);
int cpu_free_connection_buffers (connection_job_t C);
int cpu_server_close_connection (connection_job_t C, int who);
int net_server_socket_reader (connection_job_t C);
int net_server_socket_writer (connection_job_t C);
int net_server_socket_read_write (connection_job_t C);
int net_accept_new_connections (connection_job_t C);
int set_connection_timeout (connection_job_t C, double timeout);
int clear_connection_timeout (connection_job_t C);
int server_check_ready (connection_job_t C);
int server_noop (connection_job_t C);
int server_failed (connection_job_t C);
void connection_write_close (connection_job_t C);
#define write_out_chk(c,data,len) assert(write_out (&CONN_INFO(c)->Out, data, len) == len);
#define write_out_old(c,data,len) write_out(&CONN_INFO(c)->Out, data, len)
#define read_in_old(c,data,len) read_in(&CONN_INFO(c)->In, data, len)
static inline int is_ipv6_localhost (unsigned char ipv6[16]) {
return !*(long long *)ipv6 && ((long long *)ipv6)[1] == 1LL << 56;
}
void assert_net_cpu_thread (void);
void assert_net_net_thread (void);
void assert_engine_thread (void);
connection_job_t conn_target_get_connection (conn_target_job_t CT, int allow_stopped);
void insert_connection_into_target (conn_target_job_t SS, connection_job_t C);
struct tree_connection *get_connection_tree (conn_target_job_t SS);
//void wakeup_main_thread (void);
void delete_connection_tree_ptr (struct tree_connection *T);
int init_listening_connection_ext (int fd, conn_type_t *type, void *extra, int mode, int prio);
int init_listening_connection (int fd, conn_type_t *type, void *extra);
int init_listening_tcpv6_connection (int fd, conn_type_t *type, void *extra, int mode);
//struct tree_connection *get_connection_tree_ptr (struct tree_connection **);
//void free_connection_tree_ptr (struct tree_connection *);
struct free_later {
void *ptr;
void (*free)(void *);
};
struct query_info {
struct event_timer ev;
struct raw_message raw;
int src_type;
struct process_id src_pid;
void *conn;
};
void free_later_act (void);
void incr_active_dh_connections (void);
int check_conn_functions (conn_type_t *type, int listening);
#define QUERY_INFO(_c) ((struct query_info *)(_c)->j_custom)
void insert_free_later_struct (struct free_later *F);
int new_conn_generation (void);
int get_cur_conn_generation (void);
void tcp_set_max_accept_rate (int rate);
void tcp_set_max_connections (int maxconn);
extern int max_special_connections, active_special_connections;
#define MAX_NAT_INFO_RULES 16
extern int nat_info_rules;
extern unsigned nat_info[MAX_NAT_INFO_RULES][2];
int net_add_nat_info (char *str);
unsigned nat_translate_ip (unsigned local_ip);
connection_job_t alloc_new_connection (int cfd, conn_target_job_t SS, connection_job_t LL, unsigned peer, unsigned char peer_ipv6[16], int peer_port);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Nikolai Durov
2014-2016 Vitaliy Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
// #include <openssl/aes.h>
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-crypto-aes.h"
#include "net/net-config.h"
#include "net/net-connections.h"
#include "md5.h"
#include "sha1.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
#define MODULE crypto_aes
MODULE_STAT_TYPE {
int allocated_aes_crypto, allocated_aes_crypto_temp;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_I (allocated_aes_crypto);
SB_SUM_ONE_I (allocated_aes_crypto_temp);
sb_printf (sb,
"aes_pwd_hash\t%s\n",
pwd_config_md5);
MODULE_STAT_FUNCTION_END
void fetch_aes_crypto_stat (int *allocated_aes_crypto_ptr, int *allocated_aes_crypto_temp_ptr) {
if (allocated_aes_crypto_ptr) {
*allocated_aes_crypto_ptr = SB_SUM_I (allocated_aes_crypto);
}
if (allocated_aes_crypto_temp_ptr) {
*allocated_aes_crypto_temp_ptr = SB_SUM_I (allocated_aes_crypto_temp);
}
}
aes_secret_t main_secret;
int aes_crypto_init (connection_job_t c, void *key_data, int key_data_len) {
assert (key_data_len == sizeof (struct aes_key_data));
struct aes_crypto *T = NULL;
assert (!posix_memalign ((void **)&T, 16, sizeof (struct aes_crypto)));
struct aes_key_data *D = key_data;
assert (T);
MODULE_STAT->allocated_aes_crypto ++;
tg_aes_set_decrypt_key (&T->read_aeskey, D->read_key, 256);
memcpy (T->read_iv, D->read_iv, 16);
tg_aes_set_encrypt_key (&T->write_aeskey, D->write_key, 256);
memcpy (T->write_iv, D->write_iv, 16);
// T->read_pos = T->write_pos = 0;
T->read_num = T->write_num = 0;
CONN_INFO(c)->crypto = T;
return 0;
}
int aes_crypto_ctr128_init (connection_job_t c, void *key_data, int key_data_len) {
assert (key_data_len == sizeof (struct aes_key_data));
struct aes_crypto *T = NULL;
assert (!posix_memalign ((void **)&T, 16, sizeof (struct aes_crypto)));
struct aes_key_data *D = key_data;
assert (T);
MODULE_STAT->allocated_aes_crypto ++;
tg_aes_set_encrypt_key (&T->read_aeskey, D->read_key, 256); // NB: *_encrypt_key here!
memcpy (T->read_iv, D->read_iv, 16);
tg_aes_set_encrypt_key (&T->write_aeskey, D->write_key, 256);
memcpy (T->write_iv, D->write_iv, 16);
// T->read_pos = T->write_pos = 0;
T->read_num = T->write_num = 0;
CONN_INFO(c)->crypto = T;
return 0;
}
int aes_crypto_free (connection_job_t c) {
if (CONN_INFO(c)->crypto) {
free (CONN_INFO(c)->crypto);
CONN_INFO(c)->crypto = 0;
MODULE_STAT->allocated_aes_crypto --;
}
if (CONN_INFO(c)->crypto_temp) {
free (CONN_INFO(c)->crypto_temp);
CONN_INFO(c)->crypto_temp = 0;
MODULE_STAT->allocated_aes_crypto_temp --;
}
return 0;
}
int aes_initialized;
static char rand_buf[64];
// filename = 0 -- use DEFAULT_PWD_FILE
// 1 = init ok, else < 0
int aes_load_pwd_file (const char *filename) {
int h = open ("/dev/random", O_RDONLY | O_NONBLOCK);
int r = 0;
if (h >= 0) {
r = read (h, rand_buf, 16);
if (r < 0) {
perror ("READ");
r = 0;
}
if (r > 0) {
vkprintf (2, "added %d bytes of real entropy to the AES security key\n", r);
}
if (r < 0) {
perror ("read from random");
r = 0;
}
close (h);
}
if (r < 16) {
h = open ("/dev/urandom", O_RDONLY);
if (h < 0) {
main_secret.secret_len = 0;
return -1;
}
int s = read (h, rand_buf + r, 16 - r);
if (r + s != 16) {
main_secret.secret_len = 0;
return -1;
}
close (h);
}
*(long *) rand_buf ^= lrand48_j();
srand48 (*(long *)rand_buf);
if (!filename) {
filename = DEFAULT_PWD_FILE;
}
h = open (filename, O_RDONLY);
if (h < 0) {
vkprintf (1, "cannot open password file %s: %m\n", filename);
return -0x80000000;
}
r = read (h, pwd_config_buf, MAX_PWD_CONFIG_LEN + 1);
close (h);
if (r < 0) {
vkprintf (1, "error reading password file %s: %m\n", filename);
return -1;
}
vkprintf (1, "loaded %d bytes from password file %s\n", r, filename);
if (r > MAX_PWD_CONFIG_LEN) {
pwd_config_len = 0;
return -1;
}
pwd_config_len = r;
memset (pwd_config_buf + r, 0, 4);
if (r < MIN_PWD_LEN || r > MAX_PWD_LEN) {
vkprintf (1, "secret file %s too long or too short: loaded %d bytes, expected %d..%d\n", filename, r, MIN_PWD_LEN, MAX_PWD_LEN);
return -1;
}
md5_hex (pwd_config_buf, pwd_config_len, pwd_config_md5);
memcpy (main_secret.secret, pwd_config_buf, r);
main_secret.secret_len = r;
aes_initialized = 1;
return 1;
}
int aes_generate_nonce (char res[16]) {
*(int *)(rand_buf + 16) = lrand48_j ();
*(int *)(rand_buf + 20) = lrand48_j ();
*(long long *)(rand_buf + 24) = rdtsc ();
struct timespec T;
assert (clock_gettime(CLOCK_REALTIME, &T) >= 0);
*(int *)(rand_buf + 32) = T.tv_sec;
*(int *)(rand_buf + 36) = T.tv_nsec;
(*(int *)(rand_buf + 40))++;
md5 ((unsigned char *)rand_buf, 44, (unsigned char *)res);
return 0;
}
// str := nonce_server.nonce_client.client_timestamp.server_ip.client_port.("SERVER"/"CLIENT").client_ip.server_port.master_key.nonce_server.[client_ipv6.server_ipv6].nonce_client
// key := SUBSTR(MD5(str+1),0,12).SHA1(str)
// iv := MD5(str+2)
int aes_create_keys (struct aes_key_data *R, int am_client, const char nonce_server[16], const char nonce_client[16], int client_timestamp,
unsigned server_ip, unsigned short server_port, const unsigned char server_ipv6[16],
unsigned client_ip, unsigned short client_port, const unsigned char client_ipv6[16],
const aes_secret_t *key, const unsigned char *temp_key, int temp_key_len) {
unsigned char str[16+16+4+4+2+6+4+2+MAX_PWD_LEN+16+16+4+16*2 + 256];
int i, str_len;
if (!key->secret_len) {
return -1;
}
assert (key->secret_len >= MIN_PWD_LEN && key->secret_len <= MAX_PWD_LEN);
memcpy (str, nonce_server, 16);
memcpy (str + 16, nonce_client, 16);
*((int *) (str + 32)) = client_timestamp;
*((unsigned *) (str + 36)) = server_ip;
*((unsigned short *) (str + 40)) = client_port;
memcpy (str + 42, am_client ? "CLIENT" : "SERVER", 6);
*((unsigned *) (str + 48)) = client_ip;
*((unsigned short *) (str + 52)) = server_port;
memcpy (str + 54, key->secret, key->secret_len);
memcpy (str + 54 + key->secret_len, nonce_server, 16);
str_len = 70 + key->secret_len;
if (!server_ip) {
assert (!client_ip);
memcpy (str + str_len, client_ipv6, 16);
memcpy (str + str_len + 16, server_ipv6, 16);
str_len += 32;
} else {
assert (client_ip);
}
memcpy (str + str_len, nonce_client, 16);
str_len += 16;
if (temp_key_len > sizeof (str)) {
temp_key_len = sizeof (str);
}
int first_len = str_len < temp_key_len ? str_len : temp_key_len;
for (i = 0; i < first_len; i++) {
str[i] ^= temp_key[i];
}
for (i = first_len; i < temp_key_len; i++) {
str[i] = temp_key[i];
}
if (str_len < temp_key_len) {
str_len = temp_key_len;
}
md5 (str + 1, str_len - 1, R->write_key);
sha1 (str, str_len, R->write_key + 12);
md5 (str + 2, str_len - 2, R->write_iv);
//memcpy (str + 42, !am_client ? "CLIENT" : "SERVER", 6);
str[42] ^= 'C' ^ 'S';
str[43] ^= 'L' ^ 'E';
str[44] ^= 'I' ^ 'R';
str[45] ^= 'E' ^ 'V';
str[46] ^= 'N' ^ 'E';
str[47] ^= 'T' ^ 'R';
md5 (str + 1, str_len - 1, R->read_key);
sha1 (str, str_len, R->read_key + 12);
md5 (str + 2, str_len - 2, R->read_iv);
memset (str, 0, str_len);
return 1;
}
int get_crypto_key_id (void) {
if (main_secret.secret_len >= 4) {
return main_secret.key_signature;
} else {
return 0;
}
}
int get_extra_crypto_key_ids (int *buf, int max) {
return 0;
}
int is_valid_crypto_key_id (int x) {
return x && x == main_secret.key_signature && main_secret.secret_len >= 4;
}
void free_crypto_temp (void *crypto, int len) {
memset (crypto, 0, len);
free (crypto);
MODULE_STAT->allocated_aes_crypto_temp --;
}
void *alloc_crypto_temp (int len) {
void *res = malloc (len);
assert (res);
MODULE_STAT->allocated_aes_crypto_temp ++;
return res;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Nikolai Durov
2014-2016 Vitaliy Valtman
*/
#pragma once
#include <openssl/aes.h>
#include "net/net-connections.h"
#include "crypto/aesni256.h"
#include "pid.h"
#define MIN_PWD_LEN 32
#define MAX_PWD_LEN 256
#define DEFAULT_PWD_FILE "secret"
int aes_crypto_init (connection_job_t c, void *key_data, int key_data_len); /* < 0 = error */
int aes_crypto_ctr128_init (connection_job_t c, void *key_data, int key_data_len);
int aes_crypto_free (connection_job_t c);
int aes_crypto_encrypt_output (connection_job_t c); /* 0 = all ok, >0 = so much more bytes needed to encrypt last block */
int aes_crypto_decrypt_input (connection_job_t c); /* 0 = all ok, >0 = so much more bytes needed to decrypt last block */
int aes_crypto_needed_output_bytes (connection_job_t c); /* returns # of bytes needed to complete last output block */
void fetch_aes_crypto_stat (int *allocated_aes_crypto_ptr, int *allocated_aes_crypto_temp_ptr);
typedef struct aes_secret {
int refcnt;
int secret_len;
union {
char secret[MAX_PWD_LEN+4];
int key_signature;
};
} aes_secret_t;
extern aes_secret_t main_secret;
/* for aes_crypto_init */
struct aes_key_data {
unsigned char read_key[32];
unsigned char read_iv[16];
unsigned char write_key[32];
unsigned char write_iv[16];
};
#define AES_KEY_DATA_LEN sizeof (struct aes_key_data)
/* for c->crypto */
struct aes_crypto {
unsigned char read_iv[16], write_iv[16];
unsigned char read_ebuf[16], write_ebuf[16]; /* for AES-CTR modes */
tg_aes_ctx_t read_aeskey __attribute__ ((aligned (16)));
tg_aes_ctx_t write_aeskey __attribute__ ((aligned (16)));
unsigned int read_num, write_num; /* for AES-CTR modes */
// long long read_pos, write_pos; /* for AES-CTR modes */
};
extern int aes_initialized;
int aes_load_pwd_data (void *data, int len);
int aes_load_pwd_file (const char *filename);
int aes_load_random (void);
int aes_get_pwd_data (void *data, int len);
int aes_generate_nonce (char res[16]);
int aes_create_keys (struct aes_key_data *R, int am_client, const char nonce_server[16], const char nonce_client[16], int client_timestamp,
unsigned server_ip, unsigned short server_port, const unsigned char server_ipv6[16],
unsigned client_ip, unsigned short client_port, const unsigned char client_ipv6[16],
const aes_secret_t *key, const unsigned char *temp_key, int temp_key_len);
int aes_create_udp_keys (struct aes_key_data *R, struct process_id *local_pid, struct process_id *remote_pid, int generation, const aes_secret_t *key);
void free_aes_secret (aes_secret_t *secret);
aes_secret_t *alloc_aes_secret (const char *key, int key_len);
static inline void aes_secret_decref (aes_secret_t *secret) { if (__sync_add_and_fetch (&secret->refcnt, -1) <= 0) { free_aes_secret (secret); } }
static inline void aes_secret_incref (aes_secret_t *secret) { __sync_fetch_and_add (&secret->refcnt, 1); }
void free_crypto_temp (void *crypto, int len);
void *alloc_crypto_temp (int len);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Nikolai Durov
2014 Andrey Lopatin
*/
#define _FILE_OFFSET_BITS 64
#define _XOPEN_SOURCE 500
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <openssl/bn.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include "crc32.h"
#include "net/net-events.h"
#include "server-functions.h"
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-connections.h"
#include "jobs/jobs.h"
#include "net/net-crypto-dh.h"
#include "common/common-stats.h"
#define MODULE crypto_dh
MODULE_STAT_TYPE {
long long tot_dh_rounds[3];
};
MODULE_INIT
MODULE_STAT_FUNCTION
sb_printf (sb,
"tot_dh_rounds\t%lld %lld %lld\n", SB_SUM_LL(tot_dh_rounds[0]), SB_SUM_LL(tot_dh_rounds[1]), SB_SUM_LL(tot_dh_rounds[2])
);
MODULE_STAT_FUNCTION_END
void fetch_tot_dh_rounds_stat (long long _tot_dh_rounds[3]) {
int i;
for (i = 0; i < 3; i++) {
_tot_dh_rounds[i] = SB_SUM_LL(tot_dh_rounds[i]);
}
}
const unsigned char rpc_dh_prime_bin[256] = {0x89, 0x52, 0x13, 0x1b, 0x1e, 0x3a, 0x69, 0xba, 0x5f, 0x85, 0xcf, 0x8b, 0xd2, 0x66, 0xc1, 0x2b, 0x13, 0x83, 0x16, 0x13, 0xbd, 0x2a, 0x4e, 0xf8, 0x35, 0xa4, 0xd5, 0x3f, 0x9d, 0xbb, 0x42, 0x48, 0x2d, 0xbd, 0x46, 0x2b, 0x31, 0xd8, 0x6c, 0x81, 0x6c, 0x59, 0x77, 0x52, 0x0f, 0x11, 0x70, 0x73, 0x9e, 0xd2, 0xdd, 0xd6, 0xd8, 0x1b, 0x9e, 0xb6, 0x5f, 0xaa, 0xac, 0x14, 0x87, 0x53, 0xc9, 0xe4, 0xf0, 0x72, 0xdc, 0x11, 0xa4, 0x92, 0x73, 0x06, 0x83, 0xfa, 0x00, 0x67, 0x82, 0x6b, 0x18, 0xc5, 0x1d, 0x7e, 0xcb, 0xa5, 0x2b, 0x82, 0x60, 0x75, 0xc0, 0xb9, 0x55, 0xe5, 0xac, 0xaf, 0xdd, 0x74, 0xc3, 0x79, 0x5f, 0xd9, 0x52, 0x0b, 0x48, 0x0f, 0x3b, 0xe3, 0xba, 0x06, 0x65, 0x33, 0x8a, 0x49, 0x8c, 0xa5, 0xda, 0xf1, 0x01, 0x76, 0x05, 0x09, 0xa3, 0x8c, 0x49, 0xe3, 0x00, 0x74, 0x64, 0x08, 0x77, 0x4b, 0xb3, 0xed, 0x26, 0x18, 0x1a, 0x64, 0x55, 0x76, 0x6a, 0xe9, 0x49, 0x7b, 0xb9, 0xc3, 0xa3, 0xad, 0x5c, 0xba, 0xf7, 0x6b, 0x73, 0x84, 0x5f, 0xbb, 0x96, 0xbb, 0x6d, 0x0f, 0x68, 0x4f, 0x95, 0xd2, 0xd3, 0x9c, 0xcb, 0xb4, 0xa9, 0x04, 0xfa, 0xb1, 0xde, 0x43, 0x49, 0xce, 0x1c, 0x20, 0x87, 0xb6, 0xc9, 0x51, 0xed, 0x99, 0xf9, 0x52, 0xe3, 0x4f, 0xd1, 0xa3, 0xfd, 0x14, 0x83, 0x35, 0x75, 0x41, 0x47, 0x29, 0xa3, 0x8b, 0xe8, 0x68, 0xa4, 0xf9, 0xec, 0x62, 0x3a, 0x5d, 0x24, 0x62, 0x1a, 0xba, 0x01, 0xb2, 0x55, 0xc7, 0xe8, 0x38, 0x5d, 0x16, 0xac, 0x93, 0xb0, 0x2d, 0x2a, 0x54, 0x0a, 0x76, 0x42, 0x98, 0x2d, 0x22, 0xad, 0xa3, 0xcc, 0xde, 0x5c, 0x8d, 0x26, 0x6f, 0xaa, 0x25, 0xdd, 0x2d, 0xe9, 0xf6, 0xd4, 0x91, 0x04, 0x16, 0x2f, 0x68, 0x5c, 0x45, 0xfe, 0x34, 0xdd, 0xab};
#define RPC_DH_GEN 3
#define RPC_PARAM_HASH 0x00620b93
int dh_params_select;
BIGNUM *rpc_dh_prime, *rpc_dh_generator;
__thread BN_CTX *rpc_BN_ctx;
static int is_good_rpc_dh_bin (const unsigned char *data) {
int i;
int ok = 0;
for (i = 0; i < 8; i++) {
if (data[i]) {
ok = 1;
break;
}
}
if (!ok) {
return 0;
}
for (i = 0; i < 8; i++) {
if (data[i] > rpc_dh_prime_bin[i]) {
return 0;
}
if (data[i] < rpc_dh_prime_bin[i]) {
return 1;
}
}
return 0;
}
pthread_mutex_t DhInitLock = PTHREAD_MUTEX_INITIALIZER;
// result: 1 = OK, 0 = already done, -1 = error
int init_dh_params (void) {
if (dh_params_select) {
return 0;
}
pthread_mutex_lock (&DhInitLock);
if (dh_params_select) {
pthread_mutex_unlock (&DhInitLock);
return 0;
}
rpc_dh_prime = BN_new();
assert (BN_bin2bn (rpc_dh_prime_bin, sizeof (rpc_dh_prime_bin), rpc_dh_prime));
rpc_dh_generator = BN_new();
BN_set_word (rpc_dh_generator, RPC_DH_GEN);
static unsigned char buf[264], shabuf[20];
*(int *)buf = RPC_DH_GEN;
*(int *)(buf + 4) = 0x000100fe;
assert (sizeof (rpc_dh_prime_bin) == sizeof (buf) - 8);
memcpy (buf + 8, rpc_dh_prime_bin, sizeof (rpc_dh_prime_bin));
SHA1 (buf, sizeof (buf), shabuf);
rpc_BN_ctx = BN_CTX_new ();
dh_params_select = *(int *)shabuf;
assert (dh_params_select == RPC_PARAM_HASH);
pthread_mutex_unlock (&DhInitLock);
return 1;
}
void create_g_a (unsigned char g_a[256], unsigned char a[256]) {
if (!rpc_BN_ctx) {
rpc_BN_ctx = BN_CTX_new ();
}
do {
assert (RAND_pseudo_bytes (a, 256) >= 0); /* if you write '>0', the assert will fail. It's very sad */
BIGNUM *dh_power = BN_new ();
assert (BN_bin2bn (a, 256, dh_power) == dh_power);
BIGNUM *value = BN_new ();
assert (BN_mod_exp (value, rpc_dh_generator, dh_power, rpc_dh_prime, rpc_BN_ctx) == 1);
BN_clear_free (dh_power);
int len = BN_num_bytes (value);
assert (len > 240 && len <= 256);
memset (g_a, 0, 256 - len);
assert (BN_bn2bin (value, g_a + (256 - len)) == len);
BN_free (value);
} while (!is_good_rpc_dh_bin (g_a));
}
int dh_first_round (unsigned char g_a[256], struct crypto_temp_dh_params *dh_params) {
dh_params->dh_params_select = dh_params_select;
create_g_a (g_a, dh_params->a);
dh_params->magic = CRYPTO_TEMP_DH_PARAMS_MAGIC;
MODULE_STAT->tot_dh_rounds[0] ++;
return 1;
}
static void dh_inner_round (unsigned char g_ab[256], const unsigned char g_b[256], const unsigned char a[256]) {
if (!rpc_BN_ctx) {
rpc_BN_ctx = BN_CTX_new ();
}
BIGNUM *dh_base = BN_new ();
assert (BN_bin2bn (g_b, 256, dh_base) == dh_base);
BIGNUM *dh_power = BN_new ();
assert (BN_bin2bn (a, 256, dh_power) == dh_power);
BIGNUM *key = BN_new ();
assert (BN_mod_exp (key, dh_base, dh_power, rpc_dh_prime, rpc_BN_ctx) == 1);
BN_free (dh_base);
BN_clear_free (dh_power);
int len = BN_num_bytes (key);
assert (len > 240 && len <= 256);
memset (g_ab, 0, 256 - len);
assert (BN_bn2bin (key, g_ab + (256 - len)) == len);
BN_clear_free (key);
}
int dh_second_round (unsigned char g_ab[256], unsigned char g_a[256], const unsigned char g_b[256]) {
unsigned char a[256];
if (!is_good_rpc_dh_bin (g_b)) {
return 0;
}
create_g_a (g_a, a);
dh_inner_round (g_ab, g_b, a);
memset (a, 0, sizeof (a));
vkprintf (2, "DH key is %02x%02x%02x...%02x%02x%02x\n", g_ab[0], g_ab[1], g_ab[2], g_ab[253], g_ab[254], g_ab[255]);
MODULE_STAT->tot_dh_rounds[1]++;
return 256;
}
int dh_third_round (unsigned char g_ab[256], const unsigned char g_b[256], struct crypto_temp_dh_params *dh_params) {
if (!is_good_rpc_dh_bin (g_b)) {
return 0;
}
dh_inner_round (g_ab, g_b, dh_params->a);
vkprintf (2, "DH key is %02x%02x%02x...%02x%02x%02x\n", g_ab[0], g_ab[1], g_ab[2], g_ab[253], g_ab[254], g_ab[255]);
MODULE_STAT->tot_dh_rounds[2]++;
return 256;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2014 Telegram Messenger Inc
2014 Nikolai Durov
2014 Andrey Lopatin
*/
#pragma once
#define CRYPTO_TEMP_DH_PARAMS_MAGIC 0xab45ccd3
struct crypto_temp_dh_params {
int magic;
int dh_params_select;
unsigned char a[256];
};
extern int dh_params_select;
int init_dh_params (void); // result: 1 = OK, 0 = already done, -1 = error
int dh_first_round (unsigned char g_a[256], struct crypto_temp_dh_params *dh_params);
int dh_second_round (unsigned char g_ab[256], unsigned char g_a[256], const unsigned char g_b[256]);
int dh_third_round (unsigned char g_ab[256], const unsigned char g_b[256], struct crypto_temp_dh_params *dh_params);
void fetch_tot_dh_rounds_stat (long long _tot_dh_rounds[3]);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
Copyright 2014-2016 Telegram Messenger Inc
2016 Vitaly Valtman
*/
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <limits.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/io.h>
#include <sys/socket.h>
#include <time.h>
#include <unistd.h>
#include "engine/engine.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "vv/vv-io.h"
/*
* generic events (epoll-based) machinery
*/
double tot_idle_time, a_idle_time, a_idle_quotient;
volatile int main_thread_interrupt_status;
event_t Events[MAX_EVENTS];
int epoll_fd;
static long long ev_timestamp;
static event_t *ev_heap[MAX_EVENTS+1];
int ev_heap_size;
long long epoll_calls;
long long epoll_intr;
long long event_timer_insert_ops;
long long event_timer_remove_ops;
int epoll_remove (int fd);
int init_epoll (void) {
int fd;
if (epoll_fd) {
return 0;
}
Events[0].fd = -1;
fd = epoll_create (MAX_EVENTS);
if (fd < 0) {
perror ("epoll_create()");
return -1;
}
epoll_fd = fd;
assert (fd > 0);
return fd;
}
/* returns positive value if ev1 is greater than ev2 */
/* since we use only "greater_ev(x,y) > 0" and "greater_ev(x,y) <= 0" compares, */
/* it is unimportant to distinguish "x<y" and "x==y" cases */
static int greater_ev (event_t *ev1, event_t *ev2) {
int x = ev1->priority - ev2->priority;
if (x) return x;
return (ev1->timestamp > ev2->timestamp) ? 1 : 0;
}
static event_t *pop_heap_head (void) {
int i, j, N = ev_heap_size;
event_t *ev, *x, *y;
if (!N) return 0;
ev = ev_heap[1];
assert (ev && ev->in_queue == 1);
ev->in_queue = 0;
if (!--ev_heap_size) return ev;
x = ev_heap[N--];
i = 1;
while (1) {
j = (i << 1);
if (j > N) break;
if (j < N && greater_ev (ev_heap[j], ev_heap[j+1]) > 0) j++;
y = ev_heap[j];
if (greater_ev (x, y) <= 0) break;
ev_heap[i] = y;
y->in_queue = i;
i = j;
}
ev_heap[i] = x;
x->in_queue = i;
return ev;
}
int remove_event_from_heap (event_t *ev, int allow_hole) {
int v = ev->fd, i, j, N = ev_heap_size;
event_t *x;
assert (v >= 0 && v < MAX_EVENTS && Events + v == ev);
i = ev->in_queue;
if (!i) return 0;
assert (i > 0 && i <= N);
ev->in_queue = 0;
do {
j = (i << 1);
if (j > N) break;
if (j < N && greater_ev (ev_heap[j], ev_heap[j+1]) > 0) j++;
ev_heap[i] = x = ev_heap[j];
x->in_queue = i;
i = j;
} while(1);
if (allow_hole) {
ev_heap[i] = 0;
return i;
}
if (i < N) {
ev = ev_heap[N];
ev_heap[N] = 0;
while (i > 1) {
j = (i >> 1);
x = ev_heap[j];
if (greater_ev (x, ev) <= 0) break;
ev_heap[i] = x;
x->in_queue = i;
i = j;
}
ev_heap[i] = ev;
ev->in_queue = i;
}
ev_heap_size--;
return N;
}
int put_event_into_heap (event_t *ev) {
int v = ev->fd, i, j;
event_t *x;
assert (v >= 0 && v < MAX_EVENTS && Events + v == ev);
i = ev->in_queue ? remove_event_from_heap (ev, 1) : ++ev_heap_size;
assert (i <= MAX_EVENTS);
while (i > 1) {
j = (i >> 1);
x = ev_heap[j];
if (greater_ev (x, ev) <= 0) break;
ev_heap[i] = x;
x->in_queue = i;
i = j;
}
ev_heap[i] = ev;
ev->in_queue = i;
return i;
}
int put_event_into_heap_tail (event_t *ev, int ts_delta) {
ev->timestamp = ev_timestamp + ts_delta;
return put_event_into_heap (ev);
}
int epoll_sethandler (int fd, int prio, event_handler_t handler, void *data) {
event_t *ev;
assert (fd >= 0 && fd < MAX_EVENTS);
ev = Events + fd;
if (ev->fd != fd) {
memset (ev, 0, sizeof (*ev));
ev->fd = fd;
}
assert (!ev->refcnt);
__sync_fetch_and_add (&ev->refcnt, 1);
ev->priority = prio;
ev->data = data;
ev->work = handler;
return 0;
}
int epoll_conv_flags (int flags) {
if (!flags) {
return 0;
}
int r = EPOLLERR;
// no need
// it is always set
//if (!(flags & EVT_NOHUP)) {
// r |= EPOLLHUP;
//}
if (flags & EVT_READ) {
r |= EPOLLIN;
}
if (flags & EVT_WRITE) {
r |= EPOLLOUT;
}
if (flags & EVT_SPEC) {
r |= EPOLLRDHUP | EPOLLPRI;
}
if (!(flags & EVT_LEVEL)) {
r |= EPOLLET;
}
return r;
}
int epoll_unconv_flags (int f) {
int r = EVT_FROM_EPOLL;
if (f & (EPOLLIN | EPOLLERR)) {
r |= EVT_READ;
}
if (f & EPOLLOUT) {
r |= EVT_WRITE;
}
if (f & (EPOLLRDHUP | EPOLLPRI)) {
r |= EVT_SPEC;
}
return r;
}
int epoll_insert (int fd, int flags) {
event_t *ev;
int ef;
struct epoll_event ee;
if (!flags) {
return epoll_remove (fd);
}
assert (fd >= 0 && fd < MAX_EVENTS);
ev = Events + fd;
if (ev->fd != fd) {
memset (ev, 0, sizeof(event_t));
ev->fd = fd;
}
flags &= EVT_NEW | EVT_NOHUP | EVT_LEVEL | EVT_RWX;
ev->ready = 0; // !!! this bugfix led to some AIO-related bugs, now fixed with the aid of C_REPARSE flag
if ((ev->state & (EVT_LEVEL | EVT_RWX | EVT_IN_EPOLL)) == flags + EVT_IN_EPOLL) {
return 0;
}
ev->state = (ev->state & ~(EVT_LEVEL | EVT_RWX)) | (flags & (EVT_LEVEL | EVT_RWX));
ef = epoll_conv_flags (flags);
if (ef != ev->epoll_state || (flags & EVT_NEW) || !(ev->state & EVT_IN_EPOLL)) {
ev->epoll_state = ef;
memset (&ee, 0, sizeof (ee));
ee.events = ef;
ee.data.fd = fd;
vkprintf (2, "epoll_mod(%d,0x%08x,%d,%d,%08x)\n", epoll_fd, ev->state, fd, ee.data.fd, ee.events);
if (epoll_ctl (epoll_fd, (ev->state & EVT_IN_EPOLL) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD, fd, &ee) < 0) {
vkprintf (0, "epoll_ctl(%d,0x%x,%d,%d,%08x): %m\n", epoll_fd, ev->state, fd, ee.data.fd, ee.events);
}
ev->state |= EVT_IN_EPOLL;
}
return 0;
}
int epoll_remove (int fd) {
event_t *ev;
assert (fd >= 0 && fd < MAX_EVENTS);
ev = Events + fd;
if (ev->fd != fd) { return -1; }
if (ev->state & EVT_IN_EPOLL) {
ev->state &= ~EVT_IN_EPOLL;
vkprintf (2, "epoll_del(%d,0x%08x,%d,%d,%08x)\n", epoll_fd, EPOLL_CTL_DEL, fd, 0, 0);
if (epoll_ctl (epoll_fd, EPOLL_CTL_DEL, fd, 0) < 0) {
perror ("epoll_ctl(DEL)");
}
}
return 0;
}
int epoll_close (int fd) {
event_t *ev;
assert (fd >= 0 && fd < MAX_EVENTS);
ev = Events + fd;
if (ev->fd != fd) {
return -1;
}
epoll_remove (fd);
if (ev->in_queue) {
remove_event_from_heap (ev, 0);
}
memset (ev, 0, sizeof (event_t));
ev->fd = -1;
return 0;
}
int thread_run_timers (void);
int epoll_run_timers (void) {
return thread_run_timers ();
}
int term_signal_received (void) {
return signal_check_pending (SIGINT) || signal_check_pending (SIGTERM);
}
int epoll_runqueue (void) {
event_t *ev;
int res, fd, cnt = 0;
if (!ev_heap_size) {
return 0;
}
vkprintf (3, "epoll_runqueue: %d events\n", ev_heap_size);
ev_timestamp += 2;
while (ev_heap_size && (ev = ev_heap[1])->timestamp < ev_timestamp && !term_signal_received ()) {
pop_heap_head();
fd = ev->fd;
assert (ev == Events + fd && fd >= 0 && fd < MAX_EVENTS);
if (ev->work) {
res = ev->work(fd, ev->data, ev);
} else {
res = EVA_REMOVE;
}
if (res == EVA_REMOVE || res == EVA_DESTROY || res <= EVA_ERROR) {
remove_event_from_heap (ev, 0);
epoll_remove (ev->fd);
if (res == EVA_DESTROY) {
if (!(ev->state & EVT_CLOSED)) {
close (ev->fd);
}
memset (ev, 0, sizeof(event_t));
}
if (res <= EVA_FATAL) {
perror ("fatal");
exit(1);
}
} else if (res == EVA_RERUN) {
ev->timestamp = ev_timestamp;
put_event_into_heap (ev);
} else if (res > 0) {
epoll_insert (fd, res & 0xf);
} else if (res == EVA_CONTINUE) {
ev->ready = 0;
}
cnt++;
}
return cnt;
}
double last_epoll_wait_at;
struct epoll_event new_ev_list[MAX_EVENTS];
int epoll_sleep_ns = 0;
int epoll_fetch_events (int timeout) {
epoll_calls ++;
int fd, i;
main_thread_interrupt_status = 1;
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = epoll_sleep_ns;
nanosleep (&ts, NULL);
int res = epoll_wait (epoll_fd, new_ev_list, MAX_EVENTS, timeout);
main_thread_interrupt_status = 0;
if (res < 0 && errno == EINTR) {
epoll_intr ++;
res = 0;
}
if (res < 0) {
perror ("epoll_wait()");
}
if (verbosity > 2 && res) {
kprintf ("epoll_wait(%d, ...) = %d\n", epoll_fd, res);
}
for (i = 0; i < res; i++) {
fd = new_ev_list[i].data.fd;
assert (fd >= 0 && fd < MAX_EVENTS);
event_t *ev = Events + fd;
assert (ev->fd == fd);
ev->ready |= epoll_unconv_flags (ev->epoll_ready = new_ev_list[i].events);
ev->timestamp = ev_timestamp;
put_event_into_heap (ev);
}
return res;
}
void jobs_check_all_timers (void);
int epoll_work (int timeout) {
int timeout2 = 10000;
if (1) {
now = time (0);
get_utime_monotonic ();
do {
epoll_runqueue ();
timeout2 = epoll_run_timers ();
} while ((timeout2 <= 0 || ev_heap_size) && !term_signal_received ());
}
if (term_signal_received ()) {
return 0;
}
double epoll_wait_start = get_utime_monotonic ();
epoll_fetch_events (1);
last_epoll_wait_at = get_utime_monotonic ();
double epoll_wait_time = last_epoll_wait_at - epoll_wait_start;
tot_idle_time += epoll_wait_time;
a_idle_time += epoll_wait_time;
now = time (0);
static int prev_now = 0;
if (now > prev_now && now < prev_now + 60) {
while (prev_now < now) {
a_idle_time *= 100.0 / 101;
a_idle_quotient = a_idle_quotient * (100.0/101) + 1;
prev_now++;
}
} else {
prev_now = now;
}
epoll_run_timers ();
jobs_check_all_timers ();
return epoll_runqueue();
}
// ------- end of definitions ----------
/*
* end (events)
*/
// From memcached.c: socket functions
int new_socket (int mode, int nonblock) {
int socket_fd;
int flags;
if ((socket_fd = socket (mode & SM_IPV6 ? AF_INET6 : AF_INET, mode & SM_UDP ? SOCK_DGRAM : SOCK_STREAM, 0)) == -1) {
perror ("socket()");
return -1;
}
if (mode & SM_IPV6) {
flags = (mode & SM_IPV6_ONLY) != 0;
if (setsockopt (socket_fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, 4) < 0) {
perror ("setting IPV6_V6ONLY");
close (socket_fd);
return -1;
}
}
if (!nonblock) {
return socket_fd;
}
if ((flags = fcntl (socket_fd, F_GETFL, 0)) < 0 || fcntl (socket_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror ("setting O_NONBLOCK");
close (socket_fd);
return -1;
}
return socket_fd;
}
/*
* Sets a socket's send buffer size to the maximum allowed by the system.
*/
void maximize_sndbuf (int socket_fd, int max) {
socklen_t intsize = sizeof(int);
int last_good = 0;
int min, avg;
int old_size;
if (max <= 0) {
max = MAX_UDP_SENDBUF_SIZE;
}
/* Start with the default size. */
if (getsockopt (socket_fd, SOL_SOCKET, SO_SNDBUF, &old_size, &intsize)) {
if (verbosity > 0) {
perror ("getsockopt (SO_SNDBUF)");
}
return;
}
/* Binary-search for the real maximum. */
min = last_good = old_size;
max = MAX_UDP_SENDBUF_SIZE;
while (min <= max) {
avg = ((unsigned int) min + max) / 2;
if (setsockopt (socket_fd, SOL_SOCKET, SO_SNDBUF, &avg, intsize) == 0) {
last_good = avg;
min = avg + 1;
} else {
max = avg - 1;
}
}
vkprintf (2, "<%d send buffer was %d, now %d\n", socket_fd, old_size, last_good);
}
/*
* Sets a socket's receive buffer size to the maximum allowed by the system.
*/
void maximize_rcvbuf (int socket_fd, int max) {
socklen_t intsize = sizeof(int);
int last_good = 0;
int min, avg;
int old_size;
if (max <= 0) {
max = MAX_UDP_RCVBUF_SIZE;
}
/* Start with the default size. */
if (getsockopt (socket_fd, SOL_SOCKET, SO_RCVBUF, &old_size, &intsize)) {
if (verbosity > 0) {
perror ("getsockopt (SO_RCVBUF)");
}
return;
}
/* Binary-search for the real maximum. */
min = last_good = old_size;
max = MAX_UDP_RCVBUF_SIZE;
while (min <= max) {
avg = ((unsigned int) min + max) / 2;
if (setsockopt (socket_fd, SOL_SOCKET, SO_RCVBUF, &avg, intsize) == 0) {
last_good = avg;
min = avg + 1;
} else {
max = avg - 1;
}
}
vkprintf (2, ">%d receive buffer was %d, now %d\n", socket_fd, old_size, last_good);
}
int tcp_maximize_buffers;
struct in_addr settings_addr;
int server_socket (int port, struct in_addr in_addr, int backlog, int mode) {
int socket_fd;
struct linger ling = {0, 0};
int flags = 1;
if ((socket_fd = new_socket (mode, 1)) == -1) {
return -1;
}
if (mode & SM_UDP) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
setsockopt (socket_fd, SOL_IP, IP_RECVERR, &flags, sizeof (flags));
} else {
setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags));
if (tcp_maximize_buffers) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
}
assert (setsockopt (socket_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof (flags)) >= 0);
assert (flags == 1);
setsockopt (socket_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof (ling));
setsockopt (socket_fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof (flags));
int x = 40;
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &x, sizeof (x)) >= 0);
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &x, sizeof (x)) >= 0);
x = 5;
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &x, sizeof (x)) >= 0);
}
if (mode & SM_REUSE) {
setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags));
}
if (!(mode & SM_IPV6)) {
struct sockaddr_in addr;
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr = in_addr;
if (bind (socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
perror ("bind()");
close (socket_fd);
return -1;
}
} else {
struct sockaddr_in6 addr;
memset (&addr, 0, sizeof (addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons (port);
addr.sin6_addr = in6addr_any;
if (bind (socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
perror ("bind()");
close (socket_fd);
return -1;
}
}
if (!(mode & SM_UDP) && listen (socket_fd, backlog) == -1) {
// perror("listen()");
close (socket_fd);
return -1;
}
return socket_fd;
}
int client_socket (in_addr_t in_addr, int port, int mode) {
int socket_fd;
struct sockaddr_in addr;
int flags = 1;
if (mode & SM_IPV6) {
return -1;
}
if ((socket_fd = new_socket (mode, 1)) == -1) {
return -1;
}
if (mode & SM_UDP) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
setsockopt (socket_fd, SOL_IP, IP_RECVERR, &flags, sizeof (flags));
} else {
setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags));
if (tcp_maximize_buffers) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
}
assert (setsockopt (socket_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof (flags)) >= 0);
assert (flags == 1);
setsockopt (socket_fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof (flags));
int x = 40;
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &x, sizeof (x)) >= 0);
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &x, sizeof (x)) >= 0);
x = 5;
assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &x, sizeof (x)) >= 0);
}
if (!(mode & SM_IPV6)) {
engine_t *E = engine_state;
if (E && E->settings_addr.s_addr) {
struct sockaddr_in localaddr;
memset (&localaddr, 0, sizeof (localaddr));
localaddr.sin_family = AF_INET;
localaddr.sin_port = 0;
localaddr.sin_addr = E->settings_addr;
if (bind (socket_fd, (struct sockaddr *) &localaddr, sizeof (localaddr)) == -1) {
perror ("bind()");
close (socket_fd);
return -1;
}
}
}
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr.s_addr = in_addr;
if (connect (socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1 && errno != EINPROGRESS) {
perror ("connect()");
close (socket_fd);
return -1;
}
return socket_fd;
}
int client_socket_ipv6 (const unsigned char in6_addr_ptr[16], int port, int mode) {
int socket_fd;
struct sockaddr_in6 addr;
int flags = 1;
if (!(mode & SM_IPV6)) {
return -1;
}
if ((socket_fd = new_socket (mode, 1)) == -1) {
return -1;
}
if (mode & SM_UDP) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
} else {
setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags));
if (tcp_maximize_buffers) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
}
setsockopt (socket_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof (flags));
setsockopt (socket_fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof (flags));
}
memset (&addr, 0, sizeof (addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons (port);
memcpy (&addr.sin6_addr, in6_addr_ptr, 16);
if (connect (socket_fd, (struct sockaddr *) &addr, sizeof (addr)) == -1 && errno != EINPROGRESS) {
perror ("connect()");
close (socket_fd);
return -1;
}
return socket_fd;
}
unsigned get_my_ipv4 (void) {
struct ifaddrs *ifa_first, *ifa;
unsigned my_ip = 0, my_netmask = -1;
char *my_iface = 0;
if (getifaddrs (&ifa_first) < 0) {
perror ("getifaddrs()");
return 0;
}
for (ifa = ifa_first; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET) {
continue;
}
if (!strncmp (ifa->ifa_name, "lo", 2)) {
continue;
}
unsigned ip = ntohl (((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr);
unsigned mask = ntohl (((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr);
// fprintf (stderr, "%08x %08x\t%s\n", ip, mask, ifa->ifa_name);
if ((ip & (-1 << 24)) == (10 << 24) && (mask < my_netmask || (my_ip >> 24) != 10)) {
my_ip = ip;
my_netmask = mask;
my_iface = ifa->ifa_name;
} else if ((ip & (-1 << 24)) != (127 << 24) && mask < my_netmask && (my_ip >> 24) != 10) {
my_ip = ip;
my_netmask = mask;
my_iface = ifa->ifa_name;
}
}
vkprintf (1, "using main IP %d.%d.%d.%d/%d at interface %s\n", (my_ip >> 24), (my_ip >> 16) & 255, (my_ip >> 8) & 255, my_ip & 255,
__builtin_clz (~my_netmask), my_iface ?: "(none)");
freeifaddrs (ifa_first);
return my_ip;
}
int get_my_ipv6 (unsigned char ipv6[16]) {
struct ifaddrs *ifa_first, *ifa;
char *my_iface = 0;
unsigned char ip[16];
unsigned char mask[16];
memset (mask, 0, sizeof (mask));
if (getifaddrs (&ifa_first) < 0) {
perror ("getifaddrs()");
return 0;
}
int found_auto = 0;
for (ifa = ifa_first; ifa; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6) {
continue;
}
memcpy (ip, &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, 16);
vkprintf (2, "test IP " IPV6_PRINT_STR " at interface %s\n", IPV6_TO_PRINT (ip), ifa->ifa_name);
if ((ip[0] & 0xf0) != 0x30 && (ip[0] & 0xf0) != 0x20) {
vkprintf (2, "not a global ipv6 address\n");
continue;
}
if (ip[11] == 0xff && ip[12] == 0xfe && (ip[8] & 2)) {
if (found_auto) { continue; }
my_iface = ifa->ifa_name;
memcpy (ipv6, ip, 16);
memcpy (mask, &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr, 16);
found_auto = 1;
} else {
my_iface = ifa->ifa_name;
memcpy (ipv6, ip, 16);
memcpy (mask, &((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr, 16);
break;
}
}
int m = 0;
while (m < 128 && mask[m / 8] == 0xff) { m += 8; }
if (m < 128) {
unsigned char c = mask[m / 8];
while (c & 1) {
c /= 2;
m ++;
}
}
vkprintf (1, "using main IP " IPV6_PRINT_STR "/%d at interface %s\n", IPV6_TO_PRINT (ipv6), m, my_iface);
freeifaddrs (ifa_first);
return 1;
}
/* IPv4/IPv6 address formatting functions */
const char *conv_addr (in_addr_t a, char *buf) {
static char abuf[64];
if (!buf) {
buf = abuf;
}
sprintf (buf, "%d.%d.%d.%d", a&255, (a>>8)&255, (a>>16)&255, a>>24);
return buf;
}
int conv_ipv6_internal (const unsigned short a[8], char *buf) {
int i, j = 0, k = 0, l = 0;
for (i = 0; i < 8; i++) {
if (a[i]) {
if (j > l) {
l = j;
k = i;
}
j = 0;
} else {
j++;
}
}
if (j == 8) {
memcpy (buf, "::", 3);
return 2;
}
if (l == 5 && a[5] == 0xffff) {
return sprintf (buf, "::ffff:%d.%d.%d.%d", a[6]&255, a[6]>>8, a[7]&255, a[7]>>8);
}
char *ptr = buf;
if (l) {
for (i = 0; i < k - l; i++) {
ptr += sprintf (ptr, "%x:", ntohs (a[i]));
}
if (!i || k == 8) {
*ptr++ = ':';
}
for (i = k; i < 8; i++) {
ptr += sprintf (ptr, ":%x", ntohs (a[i]));
}
} else {
for (i = 0; i < 7; i++) {
ptr += sprintf (ptr, "%x:", ntohs (a[i]));
}
ptr += sprintf (ptr, "%x", ntohs (a[i]));
}
return ptr - buf;
}
const char *conv_addr6 (const unsigned char a[16], char *buf) {
static char abuf[64];
if (!buf) {
buf = abuf;
}
conv_ipv6_internal ((const unsigned short *) a, buf);
return buf;
}
const char *show_ip (unsigned ip) {
static char abuf[256], *ptr = abuf;
char *res;
if (ptr > abuf + 200) {
ptr = abuf;
}
res = ptr;
ptr += sprintf (ptr, "%d.%d.%d.%d", ip >> 24, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff) + 1;
return res;
}
const char *show_ipv6 (const unsigned char ipv6[16]) {
static char abuf[256], *ptr = abuf;
char *res;
if (ptr > abuf + 200) {
ptr = abuf;
}
res = ptr;
ptr += conv_ipv6_internal ((const unsigned short *) ipv6, ptr) + 1;
return res;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
Copyright 2014-2016 Telegram Messenger Inc
2016 Vitaly Valtman
*/
#pragma once
#include <netinet/in.h>
#ifndef EPOLLRDHUP
#define EPOLLRDHUP 0x2000
#endif
#define MAX_EVENTS (1 << 19)
#define EVT_READ 4
#define EVT_WRITE 2
#define EVT_SPEC 1
#define EVT_RW (EVT_READ | EVT_WRITE)
#define EVT_RWX (EVT_READ | EVT_WRITE | EVT_SPEC)
#define EVT_LEVEL 8
#define EVT_OPEN 0x80
#define EVT_CLOSED 0x40
#define EVT_IN_EPOLL 0x20
#define EVT_NEW 0x100
#define EVT_NOHUP 0x200
#define EVT_FROM_EPOLL 0x400
#define EVA_CONTINUE 0
#define EVA_RERUN -2
#define EVA_REMOVE -3
#define EVA_DESTROY -5
#define EVA_ERROR -8
#define EVA_FATAL -666
#define MAX_UDP_SENDBUF_SIZE (1L << 24)
#define MAX_UDP_RCVBUF_SIZE (1L << 24)
typedef struct event_descr event_t;
typedef int (*event_handler_t)(int fd, void *data, event_t *ev);
struct event_descr {
int fd;
int state; // actions that we should wait for (read/write/special) + status
int ready; // actions we are ready to do
int epoll_state; // current state in epoll()
int epoll_ready; // result of epoll()
int timeout; // timeout in ms (UNUSED)
int priority; // priority (0-9)
int in_queue; // position in heap (0=not in queue)
long long timestamp;
long long refcnt;
event_handler_t work;
void *data;
// struct sockaddr_in peer;
};
extern double last_epoll_wait_at;
extern int ev_heap_size;
extern event_t Events[MAX_EVENTS];
extern double tot_idle_time, a_idle_time, a_idle_quotient;
int init_epoll (void);
int remove_event_from_heap (event_t *ev, int allow_hole);
int put_event_into_heap (event_t *ev);
int put_event_into_heap_tail (event_t *ev, int ts_delta);
int epoll_sethandler (int fd, int prio, event_handler_t handler, void *data);
int epoll_fetch_events (int timeout);
int epoll_work (int timeout);
int epoll_insert (int fd, int flags);
int epoll_remove (int fd);
int epoll_close (int fd);
extern int epoll_fd;
//extern volatile unsigned long long pending_signals;
extern volatile int main_thread_interrupt_status;
//int insert_event_timer (event_timer_t *et);
//int remove_event_timer (event_timer_t *et);
//static inline int event_timer_active (event_timer_t *et) { return et->h_idx; }
//static inline void event_timer_init (event_timer_t *et) { et->h_idx = 0;}
#define PRIVILEGED_TCP_PORTS 1024
extern int tcp_maximize_buffers;
extern struct in_addr settings_addr;
#define SM_UDP 1
#define SM_IPV6 2
#define SM_IPV6_ONLY 4
#define SM_LOWPRIO 8
#define SM_REUSE 16
#define SM_SPECIAL 0x10000
#define SM_NOQACK 0x20000
#define SM_RAWMSG 0x40000
int server_socket (int port, struct in_addr in_addr, int backlog, int mode);
int client_socket (in_addr_t in_addr, int port, int mode);
int client_socket_ipv6 (const unsigned char in6_addr_ptr[16], int port, int mode);
void maximize_sndbuf (int sfd, int max);
void maximize_rcvbuf (int sfd, int max);
unsigned get_my_ipv4 (void);
int get_my_ipv6 (unsigned char ipv6[16]);
union sockaddr_in46 {
struct sockaddr_in a4;
struct sockaddr_in6 a6;
};
static inline int is_4in6 (const unsigned char ipv6[16]) { return !*((long long *) ipv6) && ((int *) ipv6)[2] == -0x10000; }
static inline unsigned extract_4in6 (const unsigned char ipv6[16]) { return (((unsigned *) ipv6)[3]); }
static inline void set_4in6 (unsigned char ipv6[16], unsigned ip) { *(long long *) ipv6 = 0; ((int *) ipv6)[2] = -0x10000; ((unsigned *) ipv6)[3] = ip; }
const char *conv_addr (in_addr_t a, char *buf);
const char *show_ip (unsigned ip);
const char *conv_addr6 (const unsigned char a[16], char *buf);
const char *show_ipv6 (const unsigned char ipv6[16]);
extern int epoll_sleep_ns;
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2012 Vkontakte Ltd
2010-2012 Nikolai Durov
2010-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "crc32.h"
#include "kprintf.h"
#include "net/net-events.h"
#include "precise-time.h"
#include "net/net-connections.h"
#include "net/net-http-server.h"
/*
*
* HTTP SERVER INTERFACE
*
*/
#define SERVER_VERSION "MTProxy/1.0"
int http_connections;
long long http_queries, http_bad_headers, http_queries_size;
char *extra_http_response_headers = "";
int hts_std_wakeup (connection_job_t c);
int hts_parse_execute (connection_job_t c);
int hts_std_alarm (connection_job_t c);
int hts_do_wakeup (connection_job_t c);
int hts_init_accepted (connection_job_t c);
int hts_close_connection (connection_job_t c, int who);
int hts_write_packet (connection_job_t C, struct raw_message *raw);
conn_type_t ct_http_server = {
.magic = CONN_FUNC_MAGIC,
.title = "http_server",
.flags = C_RAWMSG,
.accept = net_accept_new_connections,
.init_accepted = hts_init_accepted,
.parse_execute = hts_parse_execute,
.close = hts_close_connection,
.init_outbound = server_failed,
.connected = server_failed,
.wakeup = hts_std_wakeup,
.alarm = hts_std_alarm,
.write_packet = hts_write_packet
};
enum http_query_parse_state {
htqp_start,
htqp_readtospace,
htqp_readtocolon,
htqp_readint,
htqp_skipspc,
htqp_skiptoeoln,
htqp_skipspctoeoln,
htqp_eoln,
htqp_wantlf,
htqp_wantlastlf,
htqp_linestart,
htqp_fatal,
htqp_done
};
int hts_default_execute (connection_job_t c, struct raw_message *raw, int op);
struct http_server_functions default_http_server = {
.execute = hts_default_execute,
.ht_wakeup = hts_do_wakeup,
.ht_alarm = hts_do_wakeup
};
int hts_default_execute (connection_job_t c, struct raw_message *raw, int op) {
struct hts_data *D = HTS_DATA(c);
vkprintf (1, "http_server: op=%d, header_size=%d\n", op, D->header_size);
switch (op) {
case htqt_empty:
break;
case htqt_get:
case htqt_post:
case htqt_head:
case htqt_options:
default:
D->query_flags |= QF_ERROR;
break;
}
return D->data_size >= 0 ? -413 : -501;
}
int hts_init_accepted (connection_job_t c) {
http_connections++;
return 0;
}
int hts_close_connection (connection_job_t c, int who) {
http_connections--;
if (HTS_FUNC(c)->ht_close != NULL) {
HTS_FUNC(c)->ht_close (c, who);
}
return cpu_server_close_connection (c, who);
}
static inline char *http_get_error_msg_text (int *code) {
/* the most frequent case */
if (*code == 200) {
return "OK";
}
switch (*code) {
/* python generated from old array */
case 201: return "Created";
case 202: return "Accepted";
case 204: return "No Content";
case 206: return "Partial Content";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 408: return "Request Timeout";
case 411: return "Length Required";
case 413: return "Request Entity Too Large";
case 414: return "Request URI Too Long";
case 418: return "I'm a teapot";
case 429: return "Too Many Requests";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
default: *code = 500;
}
return "Internal Server Error";
}
static char error_text_pattern[] =
"<html>\r\n"
"<head><title>%d %s</title></head>\r\n"
"<body bgcolor=\"white\">\r\n"
"<center><h1>%d %s</h1></center>\r\n"
"<hr><center>" SERVER_VERSION "</center>\r\n"
"</body>\r\n"
"</html>\r\n";
int write_http_error_raw (connection_job_t C, struct raw_message *raw, int code) {
if (code == 204) {
write_basic_http_header_raw (C, raw, code, 0, -1, 0, 0);
return 0;
} else {
static char buff[1024];
char *ptr = buff;
const char *error_message = http_get_error_msg_text (&code);
ptr += sprintf (ptr, error_text_pattern, code, error_message, code, error_message);
write_basic_http_header_raw (C, raw, code, 0, ptr - buff, 0, 0);
assert (rwm_push_data (raw, buff, ptr - buff) == ptr - buff);
return ptr - buff;
}
}
int write_http_error (connection_job_t C, int code) {
struct raw_message *raw = calloc (sizeof (*raw), 1);
rwm_init (raw, 0);
int r = write_http_error_raw (C, raw, code);
mpq_push_w (CONN_INFO(C)->out_queue, raw, 0);
job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
return r;
}
int hts_write_packet (connection_job_t C, struct raw_message *raw) {
rwm_union (&CONN_INFO(C)->out, raw);
return 0;
}
int hts_parse_execute (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
struct hts_data *D = HTS_DATA(C);
char *ptr, *ptr_s, *ptr_e;
int len;
long long tt;
D->parse_state = htqp_start;
struct raw_message raw;
rwm_clone (&raw, &c->in);
while (c->status == conn_working && !c->pending_queries && raw.total_bytes) {
if (c->flags & (C_ERROR | C_STOPPARSE)) {
break;
}
len = rwm_get_block_ptr_bytes (&raw);
assert (len > 0);
ptr = ptr_s = rwm_get_block_ptr (&raw);
ptr_e = ptr + len;
assert (ptr);
while (ptr < ptr_e && D->parse_state != htqp_done) {
switch (D->parse_state) {
case htqp_start:
//fprintf (stderr, "htqp_start: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
memset (D, 0, offsetof (struct hts_data, query_seqno));
D->query_seqno++;
D->query_type = htqt_none;
D->data_size = -1;
D->parse_state = htqp_readtospace;
case htqp_readtospace:
//fprintf (stderr, "htqp_readtospace: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
while (ptr < ptr_e && ((unsigned) *ptr > ' ')) {
if (D->wlen < 15) {
D->word[D->wlen] = *ptr;
}
D->wlen++;
ptr++;
}
if (D->wlen > 4096) {
D->parse_state = htqp_fatal;
break;
}
if (ptr == ptr_e) {
break;
}
D->parse_state = htqp_skipspc;
D->query_words++;
if (D->query_words == 1) {
D->query_type = htqt_error;
if (D->wlen == 3 && !memcmp (D->word, "GET", 3)) {
D->query_type = htqt_get;
} else if (D->wlen == 4) {
if (!memcmp (D->word, "HEAD", 4)) {
D->query_type = htqt_head;
} else if (!memcmp (D->word, "POST", 4)) {
D->query_type = htqt_post;
}
} else if (D->wlen == 7 && !memcmp (D->word, "OPTIONS", 7)) {
D->query_type = htqt_options;
}
if (D->query_type == htqt_error) {
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
}
} else if (D->query_words == 2) {
D->uri_offset = D->header_size;
D->uri_size = D->wlen;
if (!D->wlen) {
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
}
} else if (D->query_words == 3) {
D->parse_state = htqp_skipspctoeoln;
if (D->wlen != 0) {
/* HTTP/x.y */
if (D->wlen != 8) {
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
} else {
if (!memcmp (D->word, "HTTP/1.0", 8)) {
D->http_ver = HTTP_V10;
} else if (!memcmp (D->word, "HTTP/1.1", 8)) {
D->http_ver = HTTP_V11;
} else {
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
}
}
} else {
D->http_ver = HTTP_V09;
}
} else {
assert (D->query_flags & (QF_HOST | QF_CONNECTION));
if (D->wlen) {
if (D->query_flags & QF_HOST) {
D->host_offset = D->header_size;
D->host_size = D->wlen;
} else if (D->wlen == 10 && !strncasecmp (D->word, "keep-alive", 10)) {
D->query_flags |= QF_KEEPALIVE;
}
}
D->query_flags &= ~(QF_HOST | QF_CONNECTION);
D->parse_state = htqp_skipspctoeoln;
}
D->header_size += D->wlen;
break;
case htqp_skipspc:
case htqp_skipspctoeoln:
//fprintf (stderr, "htqp_skipspc[toeoln]: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
while (D->header_size < MAX_HTTP_HEADER_SIZE && ptr < ptr_e && (*ptr == ' ' || (*ptr == '\t' && D->query_words >= 8))) {
D->header_size++;
ptr++;
}
if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
D->parse_state = htqp_fatal;
break;
}
if (ptr == ptr_e) {
break;
}
if (D->parse_state == htqp_skipspctoeoln) {
D->parse_state = htqp_eoln;
break;
}
if (D->query_words < 3) {
D->wlen = 0;
D->parse_state = htqp_readtospace;
} else {
assert (D->query_words >= 4);
if (D->query_flags & QF_DATASIZE) {
if (D->data_size != -1) {
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
} else {
D->parse_state = htqp_readint;
D->data_size = 0;
}
} else if (D->query_flags & (QF_HOST | QF_CONNECTION)) {
D->wlen = 0;
D->parse_state = htqp_readtospace;
} else {
D->parse_state = htqp_skiptoeoln;
}
}
break;
case htqp_readtocolon:
//fprintf (stderr, "htqp_readtocolon: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
while (ptr < ptr_e && *ptr != ':' && *ptr > ' ') {
if (D->wlen < 15) {
D->word[D->wlen] = *ptr;
}
D->wlen++;
ptr++;
}
if (D->wlen > 4096) {
D->parse_state = htqp_fatal;
break;
}
if (ptr == ptr_e) {
break;
}
if (*ptr != ':') {
D->header_size += D->wlen;
D->parse_state = htqp_skiptoeoln;
D->query_flags |= QF_ERROR;
break;
}
ptr++;
if (D->wlen == 4 && !strncasecmp (D->word, "host", 4)) {
D->query_flags |= QF_HOST;
} else if (D->wlen == 10 && !strncasecmp (D->word, "connection", 10)) {
D->query_flags |= QF_CONNECTION;
} else if (D->wlen == 14 && !strncasecmp (D->word, "content-length", 14)) {
D->query_flags |= QF_DATASIZE;
} else {
D->query_flags &= ~(QF_HOST | QF_DATASIZE | QF_CONNECTION);
}
D->header_size += D->wlen + 1;
D->parse_state = htqp_skipspc;
break;
case htqp_readint:
//fprintf (stderr, "htqp_readint: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
tt = D->data_size;
while (ptr < ptr_e && *ptr >= '0' && *ptr <= '9') {
if (tt >= 0x7fffffffL / 10) {
D->query_flags |= QF_ERROR;
D->parse_state = htqp_skiptoeoln;
break;
}
tt = tt * 10 + (*ptr - '0');
ptr++;
D->header_size++;
D->query_flags &= ~QF_DATASIZE;
}
D->data_size = tt;
if (ptr == ptr_e) {
break;
}
if (D->query_flags & QF_DATASIZE) {
D->query_flags |= QF_ERROR;
D->parse_state = htqp_skiptoeoln;
} else {
D->parse_state = htqp_skipspctoeoln;
}
break;
case htqp_skiptoeoln:
//fprintf (stderr, "htqp_skiptoeoln: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
while (D->header_size < MAX_HTTP_HEADER_SIZE && ptr < ptr_e && (*ptr != '\r' && *ptr != '\n')) {
D->header_size++;
ptr++;
}
if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
D->parse_state = htqp_fatal;
break;
}
if (ptr == ptr_e) {
break;
}
D->parse_state = htqp_eoln;
case htqp_eoln:
if (ptr == ptr_e) {
break;
}
if (*ptr == '\r') {
ptr++;
D->header_size++;
}
D->parse_state = htqp_wantlf;
case htqp_wantlf:
//fprintf (stderr, "htqp_wantlf: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
if (ptr == ptr_e) {
break;
}
if (++D->query_words < 8) {
D->query_words = 8;
if (D->query_flags & QF_ERROR) {
D->parse_state = htqp_fatal;
break;
}
}
if (D->http_ver <= HTTP_V09) {
D->parse_state = htqp_wantlastlf;
break;
}
if (*ptr != '\n') {
D->query_flags |= QF_ERROR;
D->parse_state = htqp_skiptoeoln;
break;
}
ptr++;
D->header_size++;
D->parse_state = htqp_linestart;
case htqp_linestart:
//fprintf (stderr, "htqp_linestart: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
if (ptr == ptr_e) {
break;
}
if (!D->first_line_size) {
D->first_line_size = D->header_size;
}
if (*ptr == '\r') {
ptr++;
D->header_size++;
D->parse_state = htqp_wantlastlf;
break;
}
if (*ptr == '\n') {
D->parse_state = htqp_wantlastlf;
break;
}
if (D->query_flags & QF_ERROR) {
D->parse_state = htqp_skiptoeoln;
} else {
D->wlen = 0;
D->parse_state = htqp_readtocolon;
}
break;
case htqp_wantlastlf:
//fprintf (stderr, "htqp_wantlastlf: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
if (ptr == ptr_e) {
break;
}
if (*ptr != '\n') {
D->parse_state = htqp_fatal;
break;
}
ptr++;
D->header_size++;
if (!D->first_line_size) {
D->first_line_size = D->header_size;
}
D->parse_state = htqp_done;
case htqp_done:
//fprintf (stderr, "htqp_done: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
break;
case htqp_fatal:
//fprintf (stderr, "htqp_fatal: ptr=%p (%.8s), hsize=%d, qf=%d, words=%d\n", ptr, ptr, D->header_size, D->query_flags, D->query_words);
D->query_flags |= QF_ERROR;
D->parse_state = htqp_done;
break;
default:
assert (0);
}
}
len = ptr - ptr_s;
assert (rwm_skip_data (&raw, len) == len);
if (D->parse_state == htqp_done) {
if (D->header_size >= MAX_HTTP_HEADER_SIZE) {
D->query_flags |= QF_ERROR;
}
if (!(D->query_flags & QF_ERROR)) {
if (!HTS_FUNC(C)->execute) {
HTS_FUNC(C)->execute = hts_default_execute;
}
int res;
if (D->query_type == htqt_post && D->data_size < 0) {
// assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
res = -411;
} else if (D->query_type != htqt_post && D->data_size > 0) {
res = -413;
} else {
int bytes = D->header_size;
if (D->query_type == htqt_post) {
bytes += D->data_size;
}
struct raw_message r;
rwm_clone (&r, &c->in);
if (bytes < c->in.total_bytes) {
rwm_trunc (&r, bytes);
}
res = HTS_FUNC(C)->execute (C, &r, D->query_type);
rwm_free (&r);
}
http_queries++;
http_queries_size += D->header_size + D->data_size;
if (res > 0) {
//c->status = conn_reading_query;
rwm_free (&raw);
return res; // need more bytes
} else {
assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
if (res == SKIP_ALL_BYTES || !res) {
if (D->data_size > 0) {
int x = c->in.total_bytes;
int y = x > D->data_size ? D->data_size : x;
assert (rwm_skip_data (&c->in, y) == y);
if (y < x) {
D->parse_state = htqp_start;
return y - x;
}
}
} else {
if (res == -413) {
D->query_flags &= ~QF_KEEPALIVE;
}
write_http_error (C, -res);
D->query_flags &= ~QF_ERROR;
}
}
} else {
//fprintf (stderr, "[parse error]\n");
assert (rwm_skip_data (&c->in, D->header_size) == D->header_size);
http_bad_headers++;
}
if (D->query_flags & QF_ERROR) {
D->query_flags &= ~QF_KEEPALIVE;
write_http_error (C, 400);
}
if (!c->pending_queries && !(D->query_flags & QF_KEEPALIVE)) {
connection_write_close (C);
D->parse_state = -1;
return 0;
}
D->parse_state = htqp_start;
rwm_free (&raw);
rwm_clone (&raw, &c->in);
}
}
rwm_free (&raw);
return NEED_MORE_BYTES;
}
int hts_std_wakeup (connection_job_t c) {
if (HTS_FUNC(c)->ht_wakeup) {
HTS_FUNC(c)->ht_wakeup (c);
}
CONN_INFO(c)->generation = new_conn_generation ();
return 0;
}
int hts_std_alarm (connection_job_t c) {
if (HTS_FUNC(c)->ht_alarm) {
HTS_FUNC(c)->ht_alarm (c);
}
CONN_INFO(c)->generation = new_conn_generation ();
return 0;
}
int hts_do_wakeup (connection_job_t c) {
//struct hts_data *D = HTS_DATA(c);
assert (0);
return 0;
}
/*
*
* USEFUL HTTP FUNCTIONS
*
*/
#define HTTP_DATE_LEN 29
char now_date_string[] = "Thu, 01 Jan 1970 00:00:00 GMT";
int now_date_utime;
static char months [] = "JanFebMarAprMayJunJulAugSepOctNovDecGlk";
static char dows [] = "SunMonTueWedThuFriSatEar";
int dd [] =
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
void gen_http_date (char date_buffer[29], int time) {
int day, mon, year, hour, min, sec, xd, i, dow;
if (time < 0) time = 0;
sec = time % 60;
time /= 60;
min = time % 60;
time /= 60;
hour = time % 24;
time /= 24;
dow = (time + 4) % 7;
xd = time % (365 * 3 + 366);
time /= (365 * 3 + 366);
year = time * 4 + 1970;
if (xd >= 365) {
year++;
xd -= 365;
if (xd >= 365) {
year++;
xd -= 365;
if (xd >= 366) {
year++;
xd -= 366;
}
}
}
if (year & 3) {
dd[1] = 28;
} else {
dd[1] = 29;
}
for (i = 0; i < 12; i++) {
if (xd < dd[i]) {
break;
}
xd -= dd[i];
}
day = xd + 1;
mon = i;
assert (day >= 1 && day <= 31 && mon >=0 && mon <= 11 &&
year >= 1970 && year <= 2039);
sprintf (date_buffer, "%.3s, %.2d %.3s %d %.2d:%.2d:%.2d GM",
dows + dow * 3, day, months + mon * 3, year,
hour, min, sec);
date_buffer[28] = 'T';
}
int gen_http_time (char *date_buffer, int *time) {
char dow[4];
char month[4];
char tz[16];
int i, year, mon, day, hour, min, sec;
int argc = sscanf (date_buffer, "%3s, %d %3s %d %d:%d:%d %15s", dow, &day, month, &year, &hour, &min, &sec, tz);
if (argc != 8) {
return (argc > 0) ? -argc : -8;
}
for (mon = 0; mon < 12; mon++) {
if (!memcmp (months + mon * 3, month, 3)) {
break;
}
}
if (mon == 12) {
return -11;
}
if (year < 1970 || year > 2039) {
return -12;
}
if (hour < 0 || hour >= 24) {
return -13;
}
if (min < 0 || min >= 60) {
return -14;
}
if (sec < 0 || sec >= 60) {
return -15;
}
if (strcmp (tz, "GMT")) {
return -16;
}
int d = (year - 1970) * 365 + ((year - 1969) >> 2) + (day - 1);
if (!(year & 3) && mon >= 2) {
d++;
}
dd[1] = 28;
for (i = 0; i < mon; i++) {
d += dd[i];
}
*time = (((d * 24 + hour) * 60 + min) * 60) + sec;
return 0;
}
char *cur_http_date (void) {
if (now_date_utime != now) {
gen_http_date (now_date_string, now_date_utime = now);
}
return now_date_string;
}
int get_http_header (const char *qHeaders, const int qHeadersLen, char *buffer, int b_len, const char *arg_name, const int arg_len) {
const char *where = qHeaders;
const char *where_end = where + qHeadersLen;
while (where < where_end) {
const char *start = where;
while (where < where_end && (*where != ':' && *where != '\n')) {
++where;
}
if (where == where_end) {
buffer[0] = 0;
return -1;
}
if (*where == ':') {
if (arg_len == where - start && !strncasecmp (arg_name, start, arg_len)) {
where++;
while (where < where_end && (*where == 9 || *where == 32)) {
where++;
}
start = where;
while (where < where_end && *where != '\r' && *where != '\n') {
++where;
}
while (where > start && (where[-1] == ' ' || where[-1] == 9)) {
where--;
}
b_len--;
if (where - start < b_len) {
b_len = where - start;
}
memcpy (buffer, start, b_len);
buffer[b_len] = 0;
return b_len;
}
++where;
}
while (where < where_end && *where != '\n') {
++where;
}
if (where < where_end) {
++where;
}
}
buffer[0] = 0;
return -1;
}
static char header_pattern[] =
"HTTP/1.1 %d %s\r\n"
"Server: " SERVER_VERSION "\r\n"
"Date: %s\r\n"
"Content-Type: %.256s\r\n"
"Connection: %s\r\n%.1024s%.1024s";
int write_basic_http_header_raw (connection_job_t C, struct raw_message *raw, int code, int date, int len, const char *add_header, const char *content_type) {
struct hts_data *D = HTS_DATA(C);
if (D->http_ver >= HTTP_V10 || D->http_ver == 0) {
#define B_SZ 4096
static char buff[B_SZ], date_buff[32];
char *ptr = buff;
const char *error_message = http_get_error_msg_text (&code);
if (date) {
gen_http_date (date_buff, date);
}
ptr += snprintf (ptr, B_SZ - 64, header_pattern, code, error_message,
date ? date_buff : cur_http_date(),
content_type ? content_type : "text/html",
(D->query_flags & QF_KEEPALIVE) ? "keep-alive" : "close",
(D->query_flags & QF_EXTRA_HEADERS) && extra_http_response_headers ? extra_http_response_headers : "",
add_header ?: "");
D->query_flags &= ~QF_EXTRA_HEADERS;
assert (ptr < buff + B_SZ - 64);
if (len >= 0) {
ptr += sprintf (ptr, "Content-Length: %d\r\n", len);
}
ptr += sprintf (ptr, "\r\n");
assert (rwm_push_data (raw, buff, ptr - buff) == ptr - buff);
return ptr - buff;
}
return 0;
}
void http_flush (connection_job_t C, struct raw_message *raw) {
if (raw) {
mpq_push_w (CONN_INFO(C)->out_queue, raw, 0);
}
struct hts_data *D = HTS_DATA(C);
if (!CONN_INFO(C)->pending_queries && !(D->query_flags & QF_KEEPALIVE)) {
connection_write_close (C);
D->parse_state = -1;
}
job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
}
//int write_basic_http_header (connection_job_t C, struct raw_message *raw, int code, int date, int len, const char *add_header, const char *content_type) {
/*
*
* END (HTTP SERVER)
*
*/
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2012 Vkontakte Ltd
2010-2012 Nikolai Durov
2010-2012 Andrey Lopatin
2012 Anton Maydell
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "net/net-connections.h"
//#include "net/net-buffers.h"
#define MAX_HTTP_HEADER_SIZE 16384
struct http_server_functions {
void *info;
int (*execute)(connection_job_t c, struct raw_message *raw, int op); /* invoked from parse_execute() */
int (*ht_wakeup)(connection_job_t c);
int (*ht_alarm)(connection_job_t c);
int (*ht_close)(connection_job_t c, int who);
};
#define HTTP_V09 9
#define HTTP_V10 0x100
#define HTTP_V11 0x101
/* in conn->custom_data, 104 bytes */
struct hts_data {
int query_type;
int query_flags;
int query_words;
int header_size;
int first_line_size;
int data_size;
int host_offset;
int host_size;
int uri_offset;
int uri_size;
int http_ver;
int wlen;
char word[16];
void *extra;
int extra_int;
int extra_int2;
int extra_int3;
int extra_int4;
double extra_double, extra_double2;
int parse_state;
int query_seqno;
};
/* for hts_data.query_type */
enum hts_query_type {
htqt_none,
htqt_head,
htqt_get,
htqt_post,
htqt_options,
htqt_error,
htqt_empty
};
#define QF_ERROR 1
#define QF_HOST 2
#define QF_DATASIZE 4
#define QF_CONNECTION 8
#define QF_TRANSFER_ENCODING 16
#define QF_TRANSFER_ENCODING_CHUNKED 32
#define QF_KEEPALIVE 0x100
#define QF_EXTRA_HEADERS 0x200
#define HTS_DATA(c) ((struct hts_data *) (CONN_INFO(c)->custom_data))
#define HTS_FUNC(c) ((struct http_server_functions *) (CONN_INFO(c)->extra))
extern conn_type_t ct_http_server;
extern struct http_server_functions default_http_server;
int hts_do_wakeup (connection_job_t c);
int hts_parse_execute (connection_job_t c);
int hts_std_wakeup (connection_job_t c);
int hts_std_alarm (connection_job_t c);
int hts_init_accepted (connection_job_t c);
int hts_close_connection (connection_job_t c, int who);
void http_flush (connection_job_t C, struct raw_message *raw);
extern int http_connections;
extern long long http_queries, http_bad_headers, http_queries_size;
extern char *extra_http_response_headers;
/* useful functions */
int get_http_header (const char *qHeaders, const int qHeadersLen, char *buffer, int b_len, const char *arg_name, const int arg_len);
#define HTTP_DATE_LEN 29
void gen_http_date (char date_buffer[29], int time);
int gen_http_time (char *date_buffer, int *time);
char *cur_http_date (void);
//int write_basic_http_header (connection_job_t c, int code, int date, int len, const char *add_header, const char *content_type);
int write_basic_http_header_raw (connection_job_t c, struct raw_message *raw, int code, int date, int len, const char *add_header, const char *content_type);
int write_http_error (connection_job_t c, int code);
int write_http_error_raw (connection_job_t c, struct raw_message *raw, int code);
/* END */
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Nikolai Durov
2012-2013 Andrey Lopatin
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "kprintf.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
#include "common/server-functions.h"
#define MODULE raw_msg_buffer
int allocated_buffer_chunks, max_allocated_buffer_chunks, max_buffer_chunks;
long long max_allocated_buffer_bytes;
MODULE_STAT_TYPE {
long long total_used_buffers_size;
int total_used_buffers;
long long allocated_buffer_bytes;
long long buffer_chunk_alloc_ops;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_LL (total_used_buffers_size);
SB_SUM_ONE_I (total_used_buffers);
SB_SUM_ONE_LL (allocated_buffer_bytes);
SB_SUM_ONE_LL (buffer_chunk_alloc_ops);
sb_printf (sb,
"allocated_buffer_chunks\t%d\n"
"max_allocated_buffer_chunks\t%d\n"
"max_buffer_chunks\t%d\n"
"max_allocated_buffer_bytes\t%lld\n",
allocated_buffer_chunks,
max_allocated_buffer_chunks,
max_buffer_chunks,
max_allocated_buffer_bytes
);
MODULE_STAT_FUNCTION_END
void fetch_buffers_stat (struct buffers_stat *bs) {
bs->total_used_buffers_size = SB_SUM_LL (total_used_buffers_size);
bs->allocated_buffer_bytes = SB_SUM_LL (allocated_buffer_bytes);
bs->buffer_chunk_alloc_ops = SB_SUM_LL (buffer_chunk_alloc_ops);
bs->total_used_buffers = SB_SUM_I (total_used_buffers);
bs->allocated_buffer_chunks = allocated_buffer_chunks;
bs->max_allocated_buffer_chunks = max_allocated_buffer_chunks;
bs->max_allocated_buffer_bytes = max_allocated_buffer_bytes;
bs->max_buffer_chunks = max_buffer_chunks;
}
int buffer_size_values;
int rwm_peak_recovery;
struct msg_buffers_chunk ChunkHeaders[MAX_BUFFER_SIZE_VALUES];
__thread struct msg_buffers_chunk *ChunkSave[MAX_BUFFER_SIZE_VALUES];
int default_buffer_sizes[] = { 48, 512, 2048, 16384, 262144 };
int default_buffer_sizes_cnt = sizeof (default_buffer_sizes) / 4;
int free_std_msg_buffer (struct msg_buffers_chunk *C, struct msg_buffer *X);
void init_buffer_chunk_headers (void) {
int i;
struct msg_buffers_chunk *CH;
assert (!buffer_size_values);
for (i = 0, CH = ChunkHeaders; i < sizeof (default_buffer_sizes) / sizeof (int); i++, CH++) {
CH->magic = MSG_CHUNK_HEAD_MAGIC;
CH->buffer_size = default_buffer_sizes[i];
CH->ch_next = CH->ch_prev = CH;
CH->free_buffer = 0;
assert (!i || default_buffer_sizes[i] > default_buffer_sizes[i-1]);
}
assert (i);
buffer_size_values = i;
}
static inline void prepare_bs_inv (struct msg_buffers_chunk *C) {
int x = C->buffer_size + 16;
int i = __builtin_ctz (x);
x >>= i;
x = 1 - x;
int y = 1;
while (x) {
y *= 1 + x;
x *= x;
}
C->bs_inverse = y;
C->bs_shift = i;
}
static void lock_chunk_head (struct msg_buffers_chunk *CH) {
while (1) {
if (__sync_bool_compare_and_swap (&CH->magic, MSG_CHUNK_HEAD_MAGIC, MSG_CHUNK_HEAD_LOCKED_MAGIC)) {
break;
}
usleep (1000);
}
}
static void unlock_chunk_head (struct msg_buffers_chunk *CH) {
CH->magic = MSG_CHUNK_HEAD_MAGIC;
}
static int try_lock_chunk (struct msg_buffers_chunk *C) {
if (C->magic != MSG_CHUNK_USED_MAGIC || !__sync_bool_compare_and_swap (&C->magic, MSG_CHUNK_USED_MAGIC, MSG_CHUNK_USED_LOCKED_MAGIC)) {
return 0;
}
while (1) {
struct msg_buffer *X = mpq_pop_nw (C->free_block_queue, 4);
if (!X) { break; }
assert (X->chunk == C);
C->free_buffer (C, X);
}
return 1;
}
static void unlock_chunk (struct msg_buffers_chunk *C) {
while (1) {
while (1) {
struct msg_buffer *X = mpq_pop_nw (C->free_block_queue, 4);
if (!X) { break; }
assert (X->chunk == C);
C->free_buffer (C, X);
}
C->magic = MSG_CHUNK_USED_MAGIC;
if (mpq_is_empty (C->free_block_queue) || !try_lock_chunk (C)) {
break;
}
}
}
// returns locked chunk
struct msg_buffers_chunk *alloc_new_msg_buffers_chunk (struct msg_buffers_chunk *CH) {
unsigned magic = CH->magic;
assert (magic == MSG_CHUNK_HEAD_MAGIC || magic == MSG_CHUNK_HEAD_LOCKED_MAGIC);
if (allocated_buffer_chunks >= max_buffer_chunks) {
// ML
return 0;
}
struct msg_buffers_chunk *C = malloc (MSG_BUFFERS_CHUNK_SIZE);
if (!C) {
return 0;
}
int buffer_size = CH->buffer_size, two_power, chunk_buffers;
int buffer_hd_size = buffer_size + BUFF_HD_BYTES;
int align = buffer_hd_size & -buffer_hd_size;
if (align < 8) {
align = 8;
}
if (align > 64) {
align = 64;
}
int t = (MSG_BUFFERS_CHUNK_SIZE - offsetof (struct msg_buffers_chunk, free_cnt)) / (buffer_hd_size + 4);
two_power = 1;
while (two_power <= t) {
two_power <<= 1;
}
chunk_buffers = (MSG_BUFFERS_CHUNK_SIZE - offsetof (struct msg_buffers_chunk, free_cnt) - two_power * 4 - align) / buffer_hd_size;
assert (chunk_buffers > 0 && chunk_buffers < 65536 && chunk_buffers <= two_power);
C->magic = MSG_CHUNK_USED_LOCKED_MAGIC;
C->buffer_size = buffer_size;
C->free_buffer = free_std_msg_buffer;
C->ch_head = CH;
C->first_buffer = (struct msg_buffer *) (((long) C + offsetof (struct msg_buffers_chunk, free_cnt) + two_power * 4 + align - 1) & -align);
assert ((char *) (C->first_buffer) + chunk_buffers * buffer_hd_size <= (char *) C + MSG_BUFFERS_CHUNK_SIZE);
C->two_power = two_power;
C->tot_buffers = chunk_buffers;
C->refcnt = 1;
lock_chunk_head (CH);
CH->tot_buffers += chunk_buffers;
CH->free_buffers += chunk_buffers;
CH->tot_chunks++;
C->ch_next = CH->ch_next;
C->ch_prev = CH;
CH->ch_next = C;
C->ch_next->ch_prev = C;
unlock_chunk_head (CH);
MODULE_STAT->allocated_buffer_bytes += MSG_BUFFERS_CHUNK_SIZE;
__sync_fetch_and_add (&allocated_buffer_chunks, 1);
MODULE_STAT->buffer_chunk_alloc_ops ++;
while (1) {
barrier ();
int keep_max_allocated_buffer_chunks = max_allocated_buffer_chunks;
barrier ();
int keep_allocated_buffer_chunks = allocated_buffer_chunks;
barrier ();
if (keep_max_allocated_buffer_chunks >= keep_allocated_buffer_chunks) {
break;
}
__sync_bool_compare_and_swap (&max_allocated_buffer_chunks, keep_max_allocated_buffer_chunks, keep_allocated_buffer_chunks);
if (allocated_buffer_chunks >= max_buffer_chunks - 8 && max_buffer_chunks >= 32 && verbosity < 3) {
// verbosity = 3;
// vkprintf (1, "Setting verbosity to 3 (NOTICE) because of high buffer chunk usage (used %d, max %d)\n", allocated_buffer_chunks, max_buffer_chunks);
}
}
/*if (rwm_peak_recovery) {
if (allocated_buffer_chunks > (max_buffer_chunks >> 2)) {
do_udp_wait (1, 1.0);
}
if (allocated_buffer_chunks > (max_buffer_chunks >> 1)) {
do_udp_wait (2, 1.0);
}
}*/
prepare_bs_inv (C);
int i;
for (i = 0; i < chunk_buffers; i++) {
C->free_cnt[two_power+i] = 1;
}
memset (&C->free_cnt[two_power + chunk_buffers], 0, (two_power - chunk_buffers) * 2);
for (i = two_power - 1; i > 0; i--) {
C->free_cnt[i] = C->free_cnt[2*i] + C->free_cnt[2*i+1];
}
C->free_block_queue = alloc_mp_queue_w ();
//vkprintf (0, "allocated chunk %p\n", C);
return C;
};
void free_msg_buffers_chunk_internal (struct msg_buffers_chunk *C, struct msg_buffers_chunk *CH) {
assert (C->magic == MSG_CHUNK_USED_LOCKED_MAGIC);
unsigned magic = CH->magic;
assert (magic == MSG_CHUNK_HEAD_MAGIC || magic == MSG_CHUNK_HEAD_LOCKED_MAGIC);
assert (C->buffer_size == CH->buffer_size);
assert (C->tot_buffers == C->free_cnt[1]);
assert (CH == C->ch_head);
C->magic = 0;
C->ch_head = 0;
lock_chunk_head (CH);
C->ch_next->ch_prev = C->ch_prev;
C->ch_prev->ch_next = C->ch_next;
CH->tot_buffers -= C->tot_buffers;
CH->free_buffers -= C->tot_buffers;
CH->tot_chunks--;
unlock_chunk_head (CH);
assert (CH->tot_chunks >= 0);
__sync_fetch_and_add (&allocated_buffer_chunks, -1);
MODULE_STAT->allocated_buffer_bytes -= MSG_BUFFERS_CHUNK_SIZE;
memset (C, 0, sizeof (struct msg_buffers_chunk));
free (C);
int si = buffer_size_values - 1;
while (si > 0 && &ChunkHeaders[si-1] != CH) {
si--;
}
assert (si >= 0);
if (ChunkSave[si] == C) {
ChunkSave[si] = NULL;
}
free_mp_queue (C->free_block_queue);
C->free_block_queue = NULL;
}
void free_msg_buffers_chunk (struct msg_buffers_chunk *C) {
assert (C->magic == MSG_CHUNK_USED_LOCKED_MAGIC);
assert (C->free_cnt[1] == C->tot_buffers);
free_msg_buffers_chunk_internal (C, C->ch_head);
}
int init_msg_buffers (long max_buffer_bytes) {
if (!max_buffer_bytes) {
max_buffer_bytes = max_allocated_buffer_bytes ?: MSG_DEFAULT_MAX_ALLOCATED_BYTES;
}
assert (max_buffer_bytes >= 0 && max_buffer_bytes <= MSG_MAX_ALLOCATED_BYTES);
assert (max_buffer_bytes >= allocated_buffer_chunks * MSG_BUFFERS_CHUNK_SIZE);
max_allocated_buffer_bytes = max_buffer_bytes;
max_buffer_chunks = (unsigned long) max_buffer_bytes / MSG_BUFFERS_CHUNK_SIZE;
if (!buffer_size_values) {
init_buffer_chunk_headers ();
}
return 1;
}
static inline int get_buffer_no (struct msg_buffers_chunk *C, struct msg_buffer *X) {
unsigned x = ((char *) X - (char *) C->first_buffer);
x >>= C->bs_shift;
x *= C->bs_inverse;
assert (x <= (unsigned) C->tot_buffers && (char *) X == (char *) C->first_buffer + (C->buffer_size + 16) * x);
return x;
}
struct msg_buffer *alloc_msg_buffer_internal (struct msg_buffer *neighbor, struct msg_buffers_chunk *CH, struct msg_buffers_chunk *C_hint, int si) {
unsigned magic = CH->magic;
assert (magic == MSG_CHUNK_HEAD_MAGIC || magic == MSG_CHUNK_HEAD_LOCKED_MAGIC);
struct msg_buffers_chunk *C;
if (!C_hint) {
C = alloc_new_msg_buffers_chunk (CH);
if (!C) {
return 0;
}
} else {
int found = 0;
if (C_hint && C_hint->free_cnt[1] && try_lock_chunk (C_hint)) {
assert (C_hint->ch_head == CH);
C = C_hint;
if (C_hint->free_cnt[1]) {
found = 1;
} else {
unlock_chunk (C_hint);
}
}
if (!found) {
lock_chunk_head (CH);
struct msg_buffers_chunk *CF = C_hint ? C_hint : CH->ch_next;
C = CF;
do {
if (C == CH) {
C = C->ch_next;
continue;
}
if (!C->free_cnt[1]) {
C = C->ch_next;
continue;
}
if (!try_lock_chunk (C)) {
C = C->ch_next;
continue;
}
if (!C->free_cnt[1]) {
unlock_chunk (C);
C = C->ch_next;
continue;
}
found = 1;
break;
} while (C != CF);
unlock_chunk_head (CH);
if (!found) {
C = alloc_new_msg_buffers_chunk (CH);
if (!C) {
return 0;
}
}
if (C_hint) {
__sync_fetch_and_add (&C_hint->refcnt, -1);
}
}
}
assert (C != CH);
assert (C->free_cnt[1]);
assert (C->magic == MSG_CHUNK_USED_LOCKED_MAGIC);
ChunkSave[si] = C;
int two_power = C->two_power, i = 1;
if (neighbor && neighbor->chunk == C) {
int x = get_buffer_no (C, neighbor);
vkprintf (3, "alloc_msg_buffer: allocating neighbor buffer for %d\n", x);
int k = 0;
if (x < two_power - 1 && C->free_cnt[two_power + x + 1]) {
i = two_power + x + 1;
} else {
int j = 1, l = 0, r = two_power;
while (i < two_power) {
i <<= 1;
int m = (l + r) >> 1;
if (x < m) {
if (C->free_cnt[i] > 0) {
r = m;
if (C->free_cnt[i+1] > 0) {
j = i + 1;
}
} else {
l = m;
i++;
}
} else if (C->free_cnt[i+1] > 0) {
l = m;
i++;
} else {
k = i = j;
while (i < two_power) {
i <<= 1;
if (!C->free_cnt[i]) {
i++;
}
assert (-- C->free_cnt[i] >= 0);
}
break;
}
}
}
if (!k) {
k = i;
}
while (k > 0) {
assert (-- C->free_cnt[k] >= 0);
k >>= 1;
}
} else {
int j = C->free_cnt[1] < 16 ? C->free_cnt[1] : 16;
j = ((long long) lrand48_j() * j) >> 31;
assert (j >= 0 && j < C->free_cnt[1]);
while (i < two_power) {
assert (-- C->free_cnt[i] >= 0);
i <<= 1;
if (C->free_cnt[i] <= j) {
j -= C->free_cnt[i];
i++;
}
}
assert (-- C->free_cnt[i] == 0);
}
assert (C != CH);
unlock_chunk (C);
//-- CH->free_buffers;
i -= two_power;
vkprintf (3, "alloc_msg_buffer(%d) [chunk %p, size %d]: tot_buffers = %d, free_buffers = %d\n", i, C, C->buffer_size, CH->tot_buffers, CH->free_buffers);
assert (i >= 0 && i < C->tot_buffers);
struct msg_buffer *X = (struct msg_buffer *) ((char *) C->first_buffer + i * (C->buffer_size + 16));
X->chunk = C;
X->refcnt = 1;
X->magic = MSG_BUFFER_USED_MAGIC;
//__sync_fetch_and_add (&total_used_buffers, 1);
MODULE_STAT->total_used_buffers_size += C->buffer_size;
MODULE_STAT->total_used_buffers ++;
return X;
}
/* allocates buffer of at least given size, -1 = maximal */
struct msg_buffer *alloc_msg_buffer (struct msg_buffer *neighbor, int size_hint) {
if (!buffer_size_values) {
init_buffer_chunk_headers ();
}
int si = buffer_size_values - 1;
if (size_hint >= 0) {
while (si > 0 && ChunkHeaders[si-1].buffer_size >= size_hint) {
si--;
}
}
return alloc_msg_buffer_internal (neighbor, &ChunkHeaders[si], ChunkSave[si], si);
}
int free_std_msg_buffer (struct msg_buffers_chunk *C, struct msg_buffer *X) {
assert (!X->refcnt && X->magic == MSG_BUFFER_USED_MAGIC && C->magic == MSG_CHUNK_USED_LOCKED_MAGIC && X->chunk == C);
int x = get_buffer_no (C, X);
int two_power = C->two_power;
vkprintf (3, "free_msg_buffer(%d)\n", x);
x += two_power;
assert (!C->free_cnt[x]);
do {
assert (++C->free_cnt[x] > 0);
} while (x >>= 1);
X->magic = MSG_BUFFER_FREE_MAGIC;
X->refcnt = -0x40000000;
//++ C->ch_head->free_buffers;
MODULE_STAT->total_used_buffers --;
MODULE_STAT->total_used_buffers_size -= C->buffer_size;
//if (C->free_cnt[1] == C->tot_buffers && C->ch_head->free_buffers * 4 >= C->tot_buffers * 5) {
// free_msg_buffers_chunk (C);
//}
return 1;
}
static int free_msg_buffer_job (job_t job, int op, struct job_thread *JT) {
switch (op) {
case JS_RUN: {
struct msg_buffer *X = *(void **)job->j_custom;
struct msg_buffers_chunk *C = X->chunk;
unsigned magic = C->magic;
assert (magic == MSG_CHUNK_USED_MAGIC || magic == MSG_CHUNK_USED_LOCKED_MAGIC);
C->free_buffer (C, X);
return JOB_COMPLETED;
}
case JS_FINISH:
assert (job->j_refcnt == 1);
return job_free (JOB_REF_PASS (job));
default:
assert (0);
}
}
int free_msg_buffer (struct msg_buffer *X) {
if (X->magic != MSG_BUFFER_USED_MAGIC) {
vkprintf (0, "magic = 0x%08x\n", X->magic);
}
assert (X->magic == MSG_BUFFER_USED_MAGIC);
assert (!X->refcnt);
struct msg_buffers_chunk *C = X->chunk;
unsigned magic = C->magic;
assert (magic == MSG_CHUNK_USED_MAGIC || magic == MSG_CHUNK_USED_LOCKED_MAGIC);
if (C->free_buffer == free_std_msg_buffer) {
if (try_lock_chunk (C)) {
C->free_buffer (C, X);
unlock_chunk (C);
return 1;
} else {
mpq_push_w (C->free_block_queue, X, 0);
if (try_lock_chunk (C)) {
unlock_chunk (C);
}
return 1;
}
} else {
if (!this_job_thread || this_job_thread->thread_class == C->thread_class) {
return C->free_buffer (C, X);
} else {
job_t job = create_async_job (free_msg_buffer_job, JSC_ALLOW (C->thread_class, JS_RUN) | JSIG_FAST (JS_FINISH), C->thread_subclass, sizeof (void *), 0, JOB_REF_NULL);
*(void **)job->j_custom = X;
schedule_job (JOB_REF_PASS (job));
return 1;
}
}
}
int msg_buffer_reach_limit (double ratio) {
return SB_SUM_LL(total_used_buffers_size) >= ratio * max_allocated_buffer_bytes;
}
double msg_buffer_usage (void) {
return (double) SB_SUM_LL(total_used_buffers_size) / (double) max_allocated_buffer_bytes;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Nikolai Durov
2012-2013 Andrey Lopatin
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include <assert.h>
#include "common/mp-queue.h"
#define MSG_STD_BUFFER 2048
#define MSG_SMALL_BUFFER 512
#define MSG_TINY_BUFFER 48
#define MSG_BUFFERS_CHUNK_SIZE ((1L << 21) - 64)
#define MSG_DEFAULT_MAX_ALLOCATED_BYTES (1L << 28)
#ifdef _LP64
#define MSG_MAX_ALLOCATED_BYTES (1L << 40)
#else
#define MSG_MAX_ALLOCATED_BYTES (1L << 30)
#endif
#define MSG_BUFFER_FREE_MAGIC 0x4abdc351
#define MSG_BUFFER_USED_MAGIC 0x72e39317
#define MSG_BUFFER_SPECIAL_MAGIC 0x683caad3
#define MSG_CHUNK_USED_MAGIC 0x5c75e681
#define MSG_CHUNK_USED_LOCKED_MAGIC (~MSG_CHUNK_USED_MAGIC)
#define MSG_CHUNK_HEAD_MAGIC 0x2dfecca3
#define MSG_CHUNK_HEAD_LOCKED_MAGIC (~MSG_CHUNK_HEAD_MAGIC)
#define MAX_BUFFER_SIZE_VALUES 16
#define BUFF_HD_BYTES (offsetof (struct msg_buffer, data))
struct msg_buffer {
struct msg_buffers_chunk *chunk;
#ifndef _LP64
int resvd;
#endif
int refcnt;
int magic;
char data[0];
};
struct msg_buffers_chunk {
int magic;
int buffer_size;
int (*free_buffer)(struct msg_buffers_chunk *C, struct msg_buffer *B);
struct msg_buffers_chunk *ch_next, *ch_prev;
struct msg_buffers_chunk *ch_head;
struct msg_buffer *first_buffer;
int two_power; /* least two-power >= tot_buffers */
int tot_buffers;
int bs_inverse;
int bs_shift;
struct mp_queue *free_block_queue;
int thread_class;
int thread_subclass;
int refcnt;
union {
struct {
int tot_chunks;
int free_buffers;
};
unsigned short free_cnt[0];
};
};
struct buffers_stat {
long long total_used_buffers_size;
long long allocated_buffer_bytes;
long long buffer_chunk_alloc_ops;
int total_used_buffers;
int allocated_buffer_chunks, max_allocated_buffer_chunks, max_buffer_chunks;
long long max_allocated_buffer_bytes;
};
void fetch_buffers_stat (struct buffers_stat *bs);
int free_msg_buffer (struct msg_buffer *X);
static inline void msg_buffer_decref (struct msg_buffer *buffer) {
if (buffer->refcnt == 1 || __sync_fetch_and_add (&buffer->refcnt, -1) == 1) {
buffer->refcnt = 0;
free_msg_buffer (buffer);
}
}
int init_msg_buffers (long max_buffer_bytes);
struct msg_buffer *alloc_msg_buffer (struct msg_buffer *neighbor, int size_hint);
int free_msg_buffer (struct msg_buffer *buffer);
int msg_buffer_reach_limit (double ratio);
double msg_buffer_usage (void);
extern long long max_allocated_buffer_bytes;
extern int allocated_buffer_chunks, max_allocated_buffer_chunks;
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Nikolai Durov
2012-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#include "sha1.h"
#include "kprintf.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
#include "crc32c.h"
#include "crc32.h"
#include "crypto/aesni256.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
#include "common/server-functions.h"
struct raw_message empty_rwm = {
.first = NULL,
.last = NULL,
.total_bytes = 0,
.magic = RM_INIT_MAGIC,
.first_offset = 0,
.last_offset = 0
};
#define MODULE raw_msg
MODULE_STAT_TYPE {
int rwm_total_msgs;
int rwm_total_msg_parts;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_I (rwm_total_msgs);
SB_SUM_ONE_I (rwm_total_msg_parts);
MODULE_STAT_FUNCTION_END
static inline struct msg_part *alloc_msg_part (void) { MODULE_STAT->rwm_total_msg_parts ++; struct msg_part *mp = (struct msg_part *) malloc (sizeof (struct msg_part)); mp->magic = MSG_PART_MAGIC; return mp; }
static inline void free_msg_part (struct msg_part *mp) { MODULE_STAT->rwm_total_msg_parts --; assert (mp->magic == MSG_PART_MAGIC); free (mp); }
struct msg_part *new_msg_part (struct msg_part *neighbor, struct msg_buffer *X) /* {{{ */{
struct msg_part *mp = alloc_msg_part ();
assert (mp);
assert (mp->magic == MSG_PART_MAGIC);
mp->refcnt = 1;
mp->next = 0;
mp->part = X;
mp->offset = 0;
mp->data_end = 0;
return mp;
}
/* }}} */
#define check_msg_part_magic(x) \
{\
unsigned magic = (x)->magic;\
assert (magic == MSG_PART_MAGIC || magic == MSG_PART_LOCKED_MAGIC);\
}
static int msg_part_decref (struct msg_part *mp) /* {{{ */{
struct msg_part *mpn;
int cnt = 0;
while (mp) {
check_msg_part_magic (mp);
if (mp->refcnt == 1) {
mp->refcnt = 0;
} else {
if (__sync_fetch_and_add (&mp->refcnt, -1) > 1) {
break;
}
}
assert (mp->magic == MSG_PART_MAGIC);
assert (!mp->refcnt);
msg_buffer_decref (mp->part);
mpn = mp->next;
mp->part = 0;
mp->next = 0;
free_msg_part (mp);
mp = mpn;
cnt ++;
}
return cnt;
}
/* }}} */
// after this function non-empty raw message raw should have following properties:
// raw->last_offset = raw->last->data_end
// raw->last->next = NULL
// raw->last is locked, unless refcnt is 1 in full msg_part chain
struct msg_part *rwm_lock_last_part (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
if (!raw->first) { return NULL; }
struct msg_part *locked = NULL;
struct msg_part *mp = raw->last;
if (mp->next || raw->last_offset != mp->data_end) {
assert (raw->last_offset <= mp->data_end);
// trying to append bytes to a sub-message of a longer chain, have to fork the chain
fork_message_chain (raw);
} else {
if (mp->magic != MSG_PART_MAGIC || !__sync_bool_compare_and_swap (&mp->magic, MSG_PART_MAGIC, MSG_PART_LOCKED_MAGIC)) {
fork_message_chain (raw);
} else {
locked = mp;
barrier ();
// rare case - somebody changed value mp between first check and lock
if (mp->next || raw->last_offset != mp->data_end) {
locked->magic = MSG_PART_MAGIC;
locked = NULL;
fork_message_chain (raw);
}
}
}
return locked;
}
/* }}} */
// after this function non-empty raw message raw should have following properties:
// raw->first_offset == raw->first->offset
struct msg_part *rwm_lock_first_part (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
if (!raw->first) { return NULL; }
if (raw->first->refcnt == 1) {
raw->first->offset = raw->first_offset;
return NULL;
}
if (raw->first->offset == raw->first_offset) {
return NULL;
}
__sync_fetch_and_add (&raw->first->part->refcnt, 1);
struct msg_part *mp = new_msg_part (raw->first, raw->first->part);
mp->offset = raw->first_offset;
mp->data_end = raw->first->data_end;
if (raw->last == raw->first) {
raw->last = mp;
mp->data_end = raw->last_offset;
} else {
mp->next = raw->first->next;
assert (mp->next);
__sync_fetch_and_add (&mp->next->refcnt, 1);
}
msg_part_decref (raw->first);
raw->first = mp;
return NULL;
}
/* }}} */
// struct raw_message itself is not freed since it is usually part of a larger structure
int rwm_free (struct raw_message *raw) /* {{{ */ {
struct msg_part *mp = raw->first;
int t = raw->magic;
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
MODULE_STAT->rwm_total_msgs --;
memset (raw, 0, sizeof (*raw));
return t == RM_TMP_MAGIC ? 0 : msg_part_decref (mp);
}
/* }}} */
int rwm_compare (struct raw_message *l, struct raw_message *r) /* {{{ */ {
assert (l->magic == RM_INIT_MAGIC || l->magic == RM_TMP_MAGIC);
assert (r->magic == RM_INIT_MAGIC || r->magic == RM_TMP_MAGIC);
if (l && !l->total_bytes) { l = 0; }
if (r && !r->total_bytes) { r = 0; }
if (!l && !r) { return 0; }
if (!l) { return -1; }
if (!r) { return 1; }
struct msg_part *lp = l->first;
struct msg_part *rp = r->first;
int lo = l->first_offset;
int ro = r->first_offset;
int ls = (lp == l->last) ? l->last_offset - lo : lp->data_end - lo;
int rs = (rp == r->last) ? r->last_offset - ro : rp->data_end - ro;
while (1) {
if (ls && rs) {
int z = ls > rs ? rs : ls;
int x = memcmp (lp->part->data + lo, rp->part->data + ro, z);
if (x != 0) { return x; }
ls -= z;
rs -= z;
lo += z;
ro += z;
}
if (!ls) {
if (lp == l->last) {
return l->total_bytes == r->total_bytes ? 0 : -1;
}
lp = lp->next;
lo = lp->offset;
ls = (lp == l->last) ? l->last_offset - lo: lp->data_end - lo;
}
if (!rs) {
if (rp == r->last) {
return l->total_bytes == r->total_bytes ? 0 : 1;
}
rp = rp->next;
ro = rp->offset;
rs = (rp == r->last) ? r->last_offset - ro: rp->data_end - ro;
}
}
}
/* }}} */
// after this function non-empty raw message raw should have following properties:
// refcnt of all msg_parts in raw is 1
// raw->first_offset = raw->first->offset
// raw->last_offset = raw->last->offset
// raw->last->next = NULL
int fork_message_chain (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
struct msg_part *mp = raw->first, **mpp = &raw->first, *mpl = 0;
int copy_last = 0, res = 0, total_bytes = raw->total_bytes;
if (!mp) {
return 0;
}
int ok = 1;
if (raw->first_offset != mp->offset) {
if (mp->refcnt == 1) {
mp->offset = raw->first_offset;
} else {
ok = 0;
}
}
while (ok && mp != raw->last && mp->refcnt == 1) {
// can not be locked, since we have only possible link
assert (mp->magic == MSG_PART_MAGIC);
total_bytes -= (mp->data_end - mp->offset);
mpp = &mp->next;
mpl = mp;
mp = mp->next;
assert (mp);
}
if (!ok || mp->refcnt != 1 || mp != raw->last) {
struct msg_part *np = mp;
while (!copy_last) {
assert (mp);
check_msg_part_magic (mp);
struct msg_part *mpc = new_msg_part (mpl, mp->part);
__sync_fetch_and_add (&mpc->part->refcnt, 1);
mpc->offset = mp->offset;
mpc->data_end = mp->data_end;
if (mp == raw->first && raw->first_offset != mp->offset) {
mpc->offset = raw->first_offset;
}
if (mp == raw->last) {
mpc->data_end = raw->last_offset;
copy_last = 1;
raw->last = mpc;
}
*mpp = mpc;
total_bytes -= (mpc->data_end - mpc->offset);
++res;
mpp = &mpc->next;
mpl = mpc;
mp = mp->next;
}
msg_part_decref (np);
} else {
assert (mp == raw->last);
assert (mp->magic == MSG_PART_MAGIC);
if (raw->last_offset != mp->data_end) {
mp->data_end = raw->last_offset;
}
total_bytes -= (mp->data_end - mp->offset);
msg_part_decref (mp->next);
mp->next = NULL;
}
if (total_bytes) {
fprintf (stderr, "total_bytes = %d\n", total_bytes);
rwm_dump_sizes (raw);
}
assert (!total_bytes);
return res;
}
/* }}} */
void rwm_clean (struct raw_message *raw) /* {{{ */{
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
raw->first = raw->last = 0;
raw->first_offset = raw->last_offset = 0;
raw->total_bytes = 0;
}
/* }}} */
void rwm_clear (struct raw_message *raw) /* {{{ */{
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (raw->first && raw->magic == RM_INIT_MAGIC) {
msg_part_decref (raw->first);
}
rwm_clean (raw);
}
/* }}} */
void rwm_clone (struct raw_message *dest_raw, struct raw_message *src_raw) /* {{{ */ {
assert (src_raw->magic == RM_INIT_MAGIC || src_raw->magic == RM_TMP_MAGIC);
memcpy (dest_raw, src_raw, sizeof (struct raw_message));
if (src_raw->magic == RM_INIT_MAGIC && src_raw->first) {
if (src_raw->first->refcnt == 1) {
src_raw->first->refcnt ++;
} else {
__sync_fetch_and_add (&src_raw->first->refcnt, 1);
}
}
MODULE_STAT->rwm_total_msgs ++;
}
/* }}} */
void rwm_move (struct raw_message *dest_raw, struct raw_message *src_raw) /* {{{ */ {
assert (src_raw->magic == RM_INIT_MAGIC || src_raw->magic == RM_TMP_MAGIC);
*dest_raw = *src_raw;
memset (src_raw, 0, sizeof (*src_raw));
}
/* }}} */
int rwm_push_data_ext (struct raw_message *raw, const void *data, int alloc_bytes, int prepend, int small_buffer, int std_buffer) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
assert (alloc_bytes >= 0);
if (!alloc_bytes) {
return 0;
}
struct msg_part *mp, *mpl;
int res = 0;
struct msg_part *locked = NULL;
if (!raw->first) {
// create first part of empty message
// no need to lock in this case, because refcnt in chain is 1 in newly-created message
struct msg_buffer *X = alloc_msg_buffer (0, alloc_bytes >= small_buffer - prepend ? std_buffer : small_buffer);
if (!X) {
return 0;
}
mp = new_msg_part (0, X);
if (alloc_bytes <= std_buffer) {
if (prepend > std_buffer - alloc_bytes) {
prepend = std_buffer - alloc_bytes;
}
}
mp->offset = prepend;
int sz = X->chunk->buffer_size - prepend;
raw->first = raw->last = mp;
raw->first_offset = prepend;
if (sz >= alloc_bytes) {
mp->data_end = prepend + alloc_bytes;
raw->total_bytes = alloc_bytes;
raw->last_offset = alloc_bytes + prepend;
if (data) {
memcpy (X->data + prepend, data, alloc_bytes);
}
return alloc_bytes;
}
mp->data_end = sz + prepend;
alloc_bytes -= sz;
raw->total_bytes = sz;
raw->last_offset = sz + prepend;
res = sz;
if (data) {
memcpy (X->data + prepend, data, sz);
data += sz;
}
} else {
// lock last part and try to add data inside last it
locked = rwm_lock_last_part (raw);
mp = raw->last;
assert (mp);
assert (mp && !mp->next && raw->last_offset == mp->data_end);
struct msg_buffer *X = mp->part;
// try to expand msg part
// all other requirements are garanteed by rwm_lcok_last_part
if (X->refcnt == 1) {
int buffer_size = X->chunk->buffer_size;
int sz = buffer_size - raw->last_offset;
assert (sz >= 0 && sz <= buffer_size);
if (sz > 0) {
// can allocate sz bytes inside the last buffer in chain itself
if (sz >= alloc_bytes) {
if (data) {
memcpy (X->data + raw->last_offset, data, alloc_bytes);
}
raw->total_bytes += alloc_bytes;
raw->last_offset += alloc_bytes;
mp->data_end += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return alloc_bytes;
}
if (data) {
memcpy (X->data + raw->last_offset, data, sz);
data += sz;
}
raw->total_bytes += sz;
raw->last_offset += sz;
mp->data_end += sz;
alloc_bytes -= sz;
}
res = sz;
}
}
while (alloc_bytes > 0) {
mpl = mp;
struct msg_buffer *X = alloc_msg_buffer (mpl->part, raw->total_bytes + alloc_bytes >= std_buffer ? std_buffer : small_buffer);
if (!X) {
break;
}
mp = new_msg_part (mpl, X);
mpl->next = raw->last = mp;
int buffer_size = X->chunk->buffer_size;
if (buffer_size >= alloc_bytes) {
mp->data_end = alloc_bytes;
raw->total_bytes += alloc_bytes;
raw->last_offset = alloc_bytes;
if (data) {
memcpy (X->data, data, alloc_bytes);
}
res += alloc_bytes;
break;
}
mp->data_end = buffer_size;
alloc_bytes -= buffer_size;
raw->total_bytes += buffer_size;
raw->last_offset = buffer_size;
res += buffer_size;
if (data) {
memcpy (X->data, data, buffer_size);
data += buffer_size;
}
}
if (locked) { locked->magic = MSG_PART_MAGIC; }
return res;
}
/* }}} */
int rwm_push_data (struct raw_message *raw, const void *data, int alloc_bytes) /* {{{ */ {
return rwm_push_data_ext (raw, data, alloc_bytes, RM_PREPEND_RESERVE, MSG_SMALL_BUFFER, MSG_STD_BUFFER);
}
/* }}} */
int rwm_push_data_front (struct raw_message *raw, const void *data, int alloc_bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
assert (alloc_bytes >= 0);
if (!alloc_bytes) {
return 0;
}
struct msg_part *mp = 0;
int r = alloc_bytes;
struct msg_part *locked = NULL;
if (raw->first) {
locked = rwm_lock_first_part (raw);
mp = raw->first;
struct msg_buffer *X = raw->first->part;
if (X->refcnt == 1 && mp->refcnt == 1) {
int size = raw->first_offset;
if (alloc_bytes > size) {
memcpy (X->data, data + (alloc_bytes - size), size);
alloc_bytes -= size;
raw->first_offset = raw->first->offset = 0;
raw->total_bytes += size;
} else {
memcpy (X->data + size - alloc_bytes, data, alloc_bytes);
raw->first->offset -= alloc_bytes;
raw->first_offset = raw->first->offset;
raw->total_bytes += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return r;
}
}
}
while (alloc_bytes) {
struct msg_buffer *X = alloc_msg_buffer (raw->first ? raw->first->part : 0, alloc_bytes >= MSG_SMALL_BUFFER ? MSG_STD_BUFFER : MSG_SMALL_BUFFER);
assert (X);
int size = X->chunk->buffer_size;
mp = new_msg_part (raw->first, X);
mp->next = raw->first;
raw->first = mp;
if (alloc_bytes > size) {
memcpy (X->data, data + (alloc_bytes - size), size);
alloc_bytes -= size;
mp->data_end = size;
mp->offset = 0;
raw->total_bytes += size;
if (!raw->last) {
raw->last = mp;
raw->last_offset = mp->data_end;
}
} else {
memcpy (X->data + size - alloc_bytes, data, alloc_bytes);
mp->data_end = size;
mp->offset = (size - alloc_bytes);
raw->first_offset = mp->offset;
raw->total_bytes += alloc_bytes;
if (!raw->last) {
raw->last = mp;
raw->last_offset = mp->data_end;
}
if (locked) { locked->magic = MSG_PART_MAGIC; }
return r;
}
}
assert (0);
return r;
}
/* }}} */
int rwm_create (struct raw_message *raw, const void *data, int alloc_bytes) /* {{{ */ {
MODULE_STAT->rwm_total_msgs ++;
memset (raw, 0, sizeof (*raw));
raw->magic = RM_INIT_MAGIC;
return rwm_push_data (raw, data, alloc_bytes);
}
/* }}} */
int rwm_init (struct raw_message *raw, int alloc_bytes) /* {{{ */ {
return rwm_create (raw, 0, alloc_bytes);
}
/* }}} */
void *rwm_prepend_alloc (struct raw_message *raw, int alloc_bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
assert (alloc_bytes >= 0);
if (!alloc_bytes || alloc_bytes > MSG_STD_BUFFER) {
return 0;
}
// struct msg_part *mp, *mpl;
// int res = 0;
if (!raw->first) {
rwm_push_data (raw, 0, alloc_bytes);
assert (raw->first == raw->last);
assert (raw->total_bytes == alloc_bytes);
return raw->first->part->data + raw->first_offset;
}
struct msg_part *locked = rwm_lock_first_part (raw);
assert (raw->first_offset == raw->first->offset);
if (raw->first->refcnt == 1 && raw->first->offset >= alloc_bytes && raw->first->part->refcnt == 1) {
raw->first->offset -= alloc_bytes;
raw->first_offset -= alloc_bytes;
raw->total_bytes += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return raw->first->part->data + raw->first_offset;
}
assert (raw->first_offset == raw->first->offset);
struct msg_buffer *X = alloc_msg_buffer (raw->first ? raw->first->part : 0, alloc_bytes);
assert (X);
int size = X->chunk->buffer_size;
assert (size >= alloc_bytes);
struct msg_part *mp = new_msg_part (raw->first, X);
mp->next = raw->first;
raw->first = mp;
mp->data_end = size;
mp->offset = size - alloc_bytes;
raw->first_offset = mp->offset;
raw->total_bytes += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return raw->first->part->data + mp->offset;
}
/* }}} */
void *rwm_postpone_alloc (struct raw_message *raw, int alloc_bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC);
assert (alloc_bytes >= 0);
if (!alloc_bytes || alloc_bytes > MSG_STD_BUFFER) {
return 0;
}
// struct msg_part *mp, *mpl;
// int res = 0;
if (!raw->first) {
rwm_push_data (raw, 0, alloc_bytes);
assert (raw->first == raw->last);
assert (raw->total_bytes == alloc_bytes);
return raw->first->part->data + raw->first_offset;
}
struct msg_part *locked = rwm_lock_last_part (raw);
struct msg_part *mp = raw->last;
int size = mp->part->chunk->buffer_size;
if (size - mp->data_end >= alloc_bytes && mp->part->refcnt == 1) {
raw->total_bytes += alloc_bytes;
mp->data_end += alloc_bytes;
raw->last_offset += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return mp->part->data + mp->data_end - alloc_bytes;
}
struct msg_buffer *X = alloc_msg_buffer (mp->part, alloc_bytes);
assert (X);
size = X->chunk->buffer_size;
assert (size >= alloc_bytes);
mp = new_msg_part (raw->first, X);
raw->last->next = mp;
raw->last = mp;
mp->data_end = alloc_bytes;
mp->offset = 0;
raw->last_offset = alloc_bytes;
raw->total_bytes += alloc_bytes;
if (locked) { locked->magic = MSG_PART_MAGIC; }
return mp->part->data;
}
/* }}} */
int rwm_prepare_iovec (const struct raw_message *raw, struct iovec *iov, int iov_len, int bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (bytes > raw->total_bytes) {
bytes = raw->total_bytes;
}
assert (bytes >= 0);
int res = 0, total_bytes = raw->total_bytes, first_offset = raw->first_offset;
struct msg_part *mp = raw->first;
while (bytes > 0) {
assert (mp);
if (res == iov_len) {
return -1;
}
int sz = (mp == raw->last ? raw->last_offset : mp->data_end) - first_offset;
if (bytes < sz) {
iov[res].iov_base = mp->part->data + first_offset;
iov[res++].iov_len = bytes;
return res;
}
iov[res].iov_base = mp->part->data + first_offset;
iov[res++].iov_len = sz;
bytes -= sz;
total_bytes -= sz;
if (!mp->next) {
assert (mp == raw->last && !bytes && !total_bytes);
return res;
}
mp = mp->next;
first_offset = mp->offset;
}
return res;
}
/* }}} */
int rwm_process_memcpy (void *extra, const void *data, int len) /* {{{ */ {
if (extra) {
char **d = extra;
memcpy (*d, data, len);
*d += len;
}
return 0;
}
/* }}} */
int rwm_fetch_data_back (struct raw_message *raw, void *data, int bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (bytes > raw->total_bytes) {
bytes = raw->total_bytes;
}
assert (bytes >= 0);
if (!bytes) {
return 0;
}
return rwm_process_ex (raw, bytes, raw->total_bytes - bytes, RMPF_TRUNCATE, rwm_process_memcpy, data ? &data : NULL);
}
/* }}} */
int rwm_fetch_lookup_back (struct raw_message *raw, void *data, int bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (bytes > raw->total_bytes) {
bytes = raw->total_bytes;
}
assert (bytes >= 0);
if (!bytes) {
return 0;
}
return rwm_process_ex (raw, bytes, raw->total_bytes - bytes, 0, rwm_process_memcpy, data ? &data : NULL);
}
/* }}} */
int rwm_trunc (struct raw_message *raw, int len) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (len >= raw->total_bytes) {
return raw->total_bytes;
}
rwm_fetch_data_back (raw, 0, raw->total_bytes - len);
return len;
}
/* }}} */
int rwm_split (struct raw_message *raw, struct raw_message *tail, int bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
assert (bytes >= 0);
MODULE_STAT->rwm_total_msgs ++;
tail->magic = raw->magic;
if (bytes >= raw->total_bytes) {
tail->first = tail->last = 0;
tail->first_offset = tail->last_offset = 0;
tail->total_bytes = 0;
return bytes == raw->total_bytes ? 0 : -1;
}
if (raw->total_bytes - bytes <= raw->last_offset - raw->last->offset) {
int s = raw->total_bytes - bytes;
raw->last_offset -= s;
raw->total_bytes -= s;
tail->first = tail->last = raw->last;
if (raw->magic == RM_INIT_MAGIC) {
__sync_fetch_and_add (&tail->first->refcnt, 1);
}
tail->first_offset = raw->last_offset;
tail->last_offset = raw->last_offset + s;
tail->total_bytes = s;
return 0;
}
tail->total_bytes = raw->total_bytes - bytes;
raw->total_bytes = bytes;
struct msg_part *mp = raw->first;
int ok = 1;
while (bytes) {
assert (mp);
int sz = (mp == raw->last ? raw->last_offset : mp->data_end) - (mp == raw->first ? raw->first_offset : mp->offset);
if (mp->refcnt != 1) { ok = 0; }
if (sz < bytes) {
bytes -= sz;
mp = mp->next;
} else {
tail->last = raw->last;
tail->last_offset = raw->last_offset;
raw->last = mp;
raw->last_offset = (mp == raw->first ? raw->first_offset : mp->offset) + bytes;
tail->first = mp;
tail->first_offset = raw->last_offset;
if (raw->magic == RM_INIT_MAGIC) {
if (ok) {
mp->refcnt ++;
} else {
__sync_fetch_and_add (&mp->refcnt, 1);
}
}
bytes = 0;
}
}
return 0;
}
/* }}} */
int rwm_split_head (struct raw_message *head, struct raw_message *raw, int bytes) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
*head = *raw;
return rwm_split (head, raw, bytes);
}
/* }}} */
int rwm_union (struct raw_message *raw, struct raw_message *tail) /* {{{ */ {
//rwm_check (raw);
//rwm_check (tail);
assert (raw->magic == RM_INIT_MAGIC);
struct msg_part *locked = NULL;
// assert (raw != tail);
if (!raw->last) {
*raw = *tail;
MODULE_STAT->rwm_total_msgs --;
tail->magic = 0;
return 0;
} else if (tail->first) {
locked = rwm_lock_last_part (raw);
// this code ensures that this function will not create message with loop
// if there would be loop, that last msg_part in chains of raw and tail are same
// then they can not be simultaneously locked, so this call will make copy of chain
struct msg_part *l2 = rwm_lock_last_part (tail);
if (l2) { l2->magic = MSG_PART_MAGIC; }
l2 = rwm_lock_first_part (tail);
raw->last->next = tail->first;
__sync_fetch_and_add (&tail->first->refcnt, 1);
raw->last_offset = tail->last_offset;
raw->last = tail->last;
raw->total_bytes += tail->total_bytes;
if (l2) { l2->magic = MSG_PART_MAGIC; }
}
rwm_free (tail);
//rwm_check (raw);
if (locked) { locked->magic = MSG_PART_MAGIC; }
return 0;
}
/* }}} */
int rwm_dump_sizes (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (!raw->first) {
fprintf (stderr, "( ) # %d\n", raw->total_bytes);
assert (!raw->total_bytes);
} else {
int total_size = 0;
struct msg_part *mp = raw->first;
fprintf (stderr, "(");
while (mp != 0) {
int size = (mp == raw->last ? raw->last_offset : mp->data_end) - (mp == raw->first ? raw->first_offset : mp->offset);
fprintf (stderr, " %d", size);
total_size += size;
if (mp == raw->last) { break; }
mp = mp->next;
}
assert (mp == raw->last);
fprintf (stderr, " ) # %d\n", raw->total_bytes);
assert (total_size == raw->total_bytes);
}
return 0;
}
/* }}} */
int rwm_check (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
if (!raw->first) {
assert (!raw->total_bytes);
} else {
int total_size = 0;
struct msg_part *mp = raw->first;
assert (raw->first_offset >= raw->first->offset);
assert (raw->last_offset <= raw->last->data_end);
while (mp != 0) {
int size = (mp == raw->last ? raw->last_offset : mp->data_end) - (mp == raw->first ? raw->first_offset : mp->offset);
assert (mp->offset >= 0);
assert (mp->data_end <= mp->part->chunk->buffer_size);
total_size += size;
if (mp == raw->last) { break; }
mp = mp->next;
}
assert (mp == raw->last);
if (total_size != raw->total_bytes) {
fprintf (stderr, "total_size = %d, total_bytes = %d\n", total_size, raw->total_bytes);
rwm_dump_sizes (raw);
}
assert (total_size == raw->total_bytes);
}
return 0;
}
/* }}} */
int rwm_dump (struct raw_message *raw) /* {{{ */ {
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
struct raw_message t;
rwm_clone (&t, raw);
static char R[10004];
int r = rwm_fetch_data (&t, R, 10004);
int x = (r > 10000) ? 10000 : r;
hexdump (R, R + x);
if (r > x) {
fprintf (stderr, "%d bytes not printed\n", raw->total_bytes - x);
}
rwm_free (&t);
return 0;
}
/* }}} */
int rwm_process_ex (struct raw_message *raw, int bytes, int offset, int flags, int (*process_block)(void *extra, const void *data, int len), void *extra) /* {{{ */ {
//rwm_check (raw);
assert (raw->magic == RM_INIT_MAGIC || raw->magic == RM_TMP_MAGIC);
assert (bytes >= 0);
assert (offset >= 0);
if (bytes + offset > raw->total_bytes) {
bytes = raw->total_bytes - offset;
}
if (bytes <= 0) { return 0; }
// correct, because if raw->last == raw->first all bytes garanteed to be in this (only) msg part
if (raw->total_bytes - offset <= raw->last_offset - raw->last->offset) {
int x = raw->total_bytes - offset;
int r = process_block (extra, raw->last->part->data + raw->last_offset - x, bytes);
if (r >= 0) {
if (flags & RMPF_ADVANCE) {
if (raw->magic == RM_INIT_MAGIC) {
__sync_fetch_and_add (&raw->last->refcnt, 1);
msg_part_decref (raw->first);
}
raw->first = raw->last;
raw->first_offset = raw->last_offset - x + bytes;
raw->total_bytes -= offset + bytes;
}
if (flags & RMPF_TRUNCATE) {
raw->total_bytes -= x;
raw->last_offset -= x;
}
} else {
return r;
}
//rwm_check (raw);
return bytes;
}
int x = bytes, r;
struct msg_part *mp = raw->first;
int ok = 1;
int save_offset = offset;
while (mp) {
check_msg_part_magic (mp);
if (mp->refcnt != 1) { ok = 0; }
int start = (mp == raw->first) ? raw->first_offset : mp->offset;
int len = (mp == raw->last) ? raw->last_offset - start : mp->data_end - start;
if (len >= offset) {
start += offset;
len -= offset;
struct msg_part *np = mp;
int save_start = start;
int ok2 = ok;
while (bytes) {
if (len >= bytes) {
r = bytes > 0 ? process_block (extra, mp->part->data + start, bytes) : 0;
len = bytes; // to set last_offset
bytes = 0;
} else {
r = len > 0 ? process_block (extra, mp->part->data + start, len) : 0;
bytes -= len;
}
if (r < 0) {
//rwm_check (raw);
return r;
}
if (!bytes) { break; }
mp = mp->next;
assert (mp);
start = (mp == raw->first) ? raw->first_offset : mp->offset;
len = (mp == raw->last) ? raw->last_offset - start : mp->data_end - start;
assert (mp);
if (mp->refcnt != 1) { ok2 = 0; }
}
if (flags & RMPF_ADVANCE) {
if (save_offset + x == raw->total_bytes) {
rwm_clear (raw);
} else {
if (raw->magic == RM_INIT_MAGIC && mp != raw->first) {
if (ok2) {
mp->refcnt ++;
} else {
__sync_fetch_and_add (&mp->refcnt, 1);
}
msg_part_decref (raw->first);
}
raw->first = mp;
raw->first_offset = start + len;
if (ok2 && raw->magic == RM_INIT_MAGIC) {
mp->offset = start + len;
}
raw->total_bytes -= save_offset + x;
}
}
if (flags & RMPF_TRUNCATE) {
if (!save_offset) {
rwm_clear (raw);
} else {
raw->total_bytes = save_offset;
raw->last = np;
raw->last_offset = save_start;
if (ok) {
raw->last->data_end = raw->last_offset;
msg_part_decref (raw->last->next);
raw->last->next = NULL;
}
}
}
if (!raw->total_bytes) {
rwm_clear (raw);
}
//rwm_check (raw);
return x;
}
offset -= len;
mp = mp->next;
}
assert (0);
return 0;
}
/* }}} */
int rwm_process_and_advance (struct raw_message *raw, int bytes, int (*process_block)(void *extra, const void *data, int len), void *extra) /* {{{ */ {
return rwm_process_ex (raw, bytes, 0, RMPF_ADVANCE, process_block, extra);
}
/* }}} */
int rwm_process (struct raw_message *raw, int bytes, int (*process_block)(void *extra, const void *data, int len), void *extra) /* {{{ */ {
return rwm_process_ex (raw, bytes, 0, 0, process_block, extra);
}
/* }}} */
int rwm_process_from_offset (struct raw_message *raw, int bytes, int offset, int (*process_block)(void *extra, const void *data, int len), void *extra) /* {{{ */{
return rwm_process_ex (raw, bytes, offset, 0, process_block, extra);
}
/* }}} */
int rwm_transform_from_offset (struct raw_message *raw, int bytes, int offset, int (*transform_block)(void *extra, void *data, int len), void *extra) /* {{{ */ {
return rwm_process_ex (raw, bytes, offset, 0, (void *)transform_block, extra);
}
/* }}} */
/* rwm_sha1 {{{ */
int sha1_wrap (void *extra, const void *data, int len) {
sha1_update (extra, (void *)data, len);
return 0;
}
int rwm_sha1 (struct raw_message *raw, int bytes, unsigned char output[20]) {
sha1_context *ctx = EVP_MD_CTX_new();
sha1_starts (ctx);
int res = rwm_process (raw, bytes, sha1_wrap, ctx);
sha1_finish (ctx, output);
EVP_MD_CTX_free(ctx);
return res;
}
/* }}} */
/* {{{ crc32c */
static int crc32c_process (void *extra, const void *data, int len) {
unsigned crc32c = *(unsigned *)extra;
*(unsigned *)extra = crc32c_partial (data, len, crc32c);
return 0;
}
unsigned rwm_crc32c (struct raw_message *raw, int bytes) {
unsigned crc32c = ~0;
assert (rwm_process (raw, bytes, crc32c_process, &crc32c) == bytes);
return ~crc32c;
}
/* }}} */
/* {{{ crc32 */
static int crc32_process (void *extra, const void *data, int len) {
unsigned crc32 = *(unsigned *)extra;
*(unsigned *)extra = crc32_partial (data, len, crc32);
return 0;
}
unsigned rwm_crc32 (struct raw_message *raw, int bytes) {
unsigned crc32 = ~0;
assert (rwm_process (raw, bytes, crc32_process, &crc32) == bytes);
return ~crc32;
}
/* }}} */
/* custom crc32 {{{ */
struct custom_crc32_data {
crc32_partial_func_t partial;
unsigned crc32;
};
static int custom_crc32_process (void *extra, const void *data, int len) {
struct custom_crc32_data *DP = extra;
DP->crc32 = DP->partial (data, len, DP->crc32);
return 0;
}
unsigned rwm_custom_crc32 (struct raw_message *raw, int bytes, crc32_partial_func_t custom_crc32_partial) {
struct custom_crc32_data D;
D.partial = custom_crc32_partial;
D.crc32 = -1;
assert (raw->total_bytes >= bytes);
assert (rwm_process (raw, bytes, (void *)custom_crc32_process, &D) == bytes);
return ~D.crc32;
}
/* }}} */
int rwm_process_nop (void *extra, const void *data, int len) /* {{{ */ {
return 0;
}
/* }}} */
int rwm_fetch_data (struct raw_message *raw, void *buf, int bytes) /* {{{ */ {
if (buf) {
return rwm_process_and_advance (raw, bytes, rwm_process_memcpy, &buf);
} else {
return rwm_process_and_advance (raw, bytes, rwm_process_nop, 0);
}
}
/* }}} */
int rwm_skip_data (struct raw_message *raw, int bytes) /* {{{ */ {
return rwm_process_and_advance (raw, bytes, rwm_process_nop, 0);
}
/* }}} */
int rwm_fetch_lookup (struct raw_message *raw, void *buf, int bytes) /* {{{ */ {
if (buf) {
return rwm_process (raw, bytes, rwm_process_memcpy, &buf);
} else {
return rwm_process (raw, bytes, rwm_process_nop, 0);
}
}
/* }}} */
int rwm_get_block_ptr_bytes (struct raw_message *raw) {
if (!raw->total_bytes) {
return 0;
}
struct msg_part *mp = raw->first;
while (1) {
assert (mp);
int bytes = ((mp == raw->last) ? raw->last_offset : mp->data_end) - raw->first_offset;
if (bytes) {
return bytes;
}
assert (mp != raw->last);
if (mp->refcnt == 1) {
raw->first = mp->next;
mp->next = NULL;
} else {
raw->first = mp->next;
__sync_fetch_and_add (&mp->next->refcnt, 1);
}
msg_part_decref (mp);
raw->first_offset = raw->first->offset;
mp = mp->next;
}
}
void *rwm_get_block_ptr (struct raw_message *raw) {
if (!raw->first) { return NULL; }
return raw->first->part->data + raw->first_offset;
}
void rwm_to_tl_string (struct raw_message *raw) {
assert (raw->magic == RM_INIT_MAGIC);
if (raw->total_bytes < 0xfe) {
assert (rwm_push_data_front (raw, &raw->total_bytes, 1) == 1);
} else {
assert (rwm_push_data_front (raw, &raw->total_bytes, 3) == 3);
int b = 0xfe;
assert (rwm_push_data_front (raw, &b, 1) == 1);
}
int pad = (-raw->total_bytes) & 3;
if (pad) {
int zero = 0;
assert (rwm_push_data (raw, &zero, pad) == pad);
}
}
void rwm_from_tl_string (struct raw_message *raw) {
assert (raw->magic == RM_INIT_MAGIC);
int x = 0;
assert (raw->total_bytes > 0);
assert (rwm_fetch_data (raw, &x, 1) == 1);
assert (x != 0xff);
if (x == 0xfe) {
assert (raw->total_bytes >= 3);
assert (rwm_fetch_data (raw, &x, 3) == 3);
}
assert (raw->total_bytes >= x);
rwm_trunc (raw, x);
}
/*{{{ encrypt_decrypt */
struct rwm_encrypt_decrypt_tmp {
int bp;
int buf_left;
int left;
int block_size;
struct raw_message *raw;
struct tg_aes_ctx *ctx;
void (*crypt)(struct tg_aes_ctx *, const void *, void *, int, unsigned char *, void *, void *);
unsigned char *iv;
void *extra;
void *extra2;
char buf[16] __attribute__((aligned(16)));
};
int rwm_process_encrypt_decrypt (struct rwm_encrypt_decrypt_tmp *x, const void *data, int len) {
int bsize = x->block_size;
struct raw_message *res = x->raw;
if (!x->buf_left) {
struct msg_buffer *X = alloc_msg_buffer (res->last->part, x->left >= MSG_STD_BUFFER ? MSG_STD_BUFFER : x->left);
assert (X);
struct msg_part *mp = new_msg_part (res->last, X);
res->last->next = mp;
res->last = mp;
res->last_offset = 0;
x->buf_left = X->chunk->buffer_size;
}
x->left -= len;
assert (res->last_offset >= 0);
assert (x->buf_left >= 0);
assert (x->buf_left + res->last_offset <= res->last->part->chunk->buffer_size);
if (x->bp) {
int to_fill = bsize - x->bp;
if (len >= to_fill) {
memcpy (x->buf + x->bp, data, to_fill);
len -= to_fill;
data += to_fill;
x->bp = 0;
if (x->buf_left >= bsize) {
x->crypt (x->ctx, x->buf, res->last->part->data + res->last_offset, bsize, x->iv, x->extra, x->extra2);
res->last->data_end += bsize;
res->last_offset += bsize;
x->buf_left -= bsize;
} else {
x->crypt (x->ctx, x->buf, x->buf, bsize, x->iv, x->extra, x->extra2);
memcpy (res->last->part->data + res->last_offset, x->buf, x->buf_left);
int t = x->buf_left;
res->last->data_end += t;
struct msg_buffer *X = alloc_msg_buffer (res->last->part, x->left + len + bsize >= MSG_STD_BUFFER ? MSG_STD_BUFFER : x->left + len + bsize);
assert (X);
struct msg_part *mp = new_msg_part (res->last, X);
res->last->next = mp;
res->last = mp;
res->last_offset = 0;
x->buf_left = X->chunk->buffer_size;
assert (x->buf_left >= bsize - t);
memcpy (res->last->part->data, x->buf + t, bsize - t);
res->last_offset = bsize - t;
res->last->data_end = bsize - t;
x->buf_left -= (bsize - t);
}
res->total_bytes += bsize;
} else {
memcpy (x->buf + x->bp, data, len);
x->bp += len;
return 0;
}
}
if (len & (bsize - 1)) {
int l = len & -bsize;
memcpy (x->buf, data + l, len - l);
x->bp = len - l;
len = l;
}
assert (res->last_offset >= 0);
assert (x->buf_left >= 0);
assert (x->buf_left + res->last_offset <= res->last->part->chunk->buffer_size);
while (1) {
if (x->buf_left < bsize) {
struct msg_buffer *X = alloc_msg_buffer (res->last->part, x->left + len >= MSG_STD_BUFFER ? MSG_STD_BUFFER : x->left + len);
assert (X);
struct msg_part *mp = new_msg_part (res->last, X);
res->last->next = mp;
res->last = mp;
res->last_offset = 0;
x->buf_left = X->chunk->buffer_size;
}
assert (res->last_offset >= 0);
assert (x->buf_left >= 0);
assert (x->buf_left + res->last_offset <= res->last->part->chunk->buffer_size);
if (len <= x->buf_left) {
assert (!(len & (bsize - 1)));
x->crypt (x->ctx, data, (res->last->part->data + res->last_offset), len, x->iv, x->extra, x->extra2);
res->last->data_end += len;
res->last_offset += len;
res->total_bytes += len;
x->buf_left -= len;
return 0;
} else {
int t = x->buf_left & -bsize;
x->crypt (x->ctx, data, res->last->part->data + res->last_offset, t, x->iv, x->extra, x->extra2);
res->last->data_end += t;
res->last_offset += t;
res->total_bytes += t;
data += t;
len -= t;
x->buf_left -= t;
}
}
}
int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, struct tg_aes_ctx *ctx, void (*crypt)(struct tg_aes_ctx *ctx, const void *src, void *dst, int l, unsigned char *iv, void *extra, void *extra2), unsigned char *iv, int block_size, void *extra, void *extra2) {
assert (bytes >= 0);
assert (block_size && !(block_size & (block_size - 1)));
if (bytes > raw->total_bytes) {
bytes = raw->total_bytes;
}
bytes &= -block_size;
if (!bytes) {
return 0;
}
struct msg_part *locked = rwm_lock_last_part (res);
if (!res->last || res->last->part->refcnt != 1) {
int l = res->last ? bytes : bytes + RM_PREPEND_RESERVE;
struct msg_buffer *X = alloc_msg_buffer (res->last ? res->last->part : 0, l >= MSG_STD_BUFFER ? MSG_STD_BUFFER : l);
assert (X);
struct msg_part *mp = new_msg_part (res->last, X);
if (res->last) {
res->last->next = mp;
res->last = mp;
res->last_offset = 0;
} else {
res->last = res->first = mp;
res->last_offset = res->first_offset = mp->offset = mp->data_end = RM_PREPEND_RESERVE;
}
}
struct rwm_encrypt_decrypt_tmp t;
t.bp = 0;
t.crypt = crypt;
if (res->last->part->refcnt == 1) {
t.buf_left = res->last->part->chunk->buffer_size - res->last_offset;
} else {
t.buf_left = 0;
}
t.raw = res;
t.ctx = ctx;
t.iv = iv;
t.left = bytes;
t.extra = extra;
t.extra2 = extra2;
t.block_size = block_size;
int r = rwm_process_and_advance (raw, bytes, (void *)rwm_process_encrypt_decrypt, &t);
if (locked) {
locked->magic = MSG_PART_MAGIC;
}
return r;
}
/* }}} */
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Nikolai Durov
2012-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Vitaly Valtman
*/
#pragma once
#include <stdlib.h>
#include <sys/uio.h>
#include <assert.h>
#include "crypto/aesni256.h"
#include "net/net-msg-buffers.h"
#include "crc32.h"
/* INVARIANTS FOR MULTITHREAD USE:
- any raw message is valid until you have the link
- any tmp raw message is valid until it's parent is not modified
- pointer to raw message implies lock on it.
- msg part can be modified only if you have lock or you have only valid link
- msg buffer can be modified if it's reference counter is 1 and overlying msg part can be modified
- msg parts can not have loops
*/
/*
msg_part mp can be expanded to left, if mp->refcnt=1, mp->part->refcnt=1, mp=raw->first, where raw is raw_message we have in this thread
msg_part mp can be expanded to right, if mp->part->refcnt=1, mp->next=NULL and ((mp is locked) or (refcnt on chain from raw->first to mp is 1))
it is invalid to change any msg_parts after raw->last
*/
/*
* MESSAGE PARTS (struct msg_part)
*/
struct msg_part {
// fields inherited from msg_buffer
//struct msg_buffers_chunk *chunk;
#ifndef _LP64
int resvd;
#endif
int refcnt;
int magic;
// fields specific to msg_part
struct msg_part *next;
struct msg_buffer *part;
int offset; // data offset inside part->data
int data_end; // end of data offset inside part->data
};
extern int rwm_total_msg_parts;
extern int rwm_total_msgs;
#define MSG_PART_MAGIC 0x8341aa7
#define MSG_PART_LOCKED_MAGIC (~MSG_PART_MAGIC)
struct msg_part *new_msg_part (struct msg_part *neighbor, struct msg_buffer *X);
/*
* RAW MESSAGES (struct raw_message) = chains of MESSAGE PARTs
*/
// ordinary raw message (changing refcnt of pointed msg_parts)
#define RM_INIT_MAGIC 0x23513473
// temp raw message (doesn't change refcnts of pointed msg_parts), used for fast read iterators
#define RM_TMP_MAGIC 0x52a717f3
#define RM_PREPEND_RESERVE 128
struct raw_message {
struct msg_part *first, *last; // 'last' doesn't increase refcnt of pointed msg_part
int total_bytes; // bytes in the chain (extra bytes ignored even if present)
int magic; // one of RM_INIT_MAGIC, RM_TMP_MAGIC
int first_offset; // offset of first used byte inside first buffer data
int last_offset; // offset after last used byte inside last buffer data
};
/* NB: struct raw_message itself is never allocated or freed by the following functions since
it is usually part (field) of a larger structure
*/
int rwm_free (struct raw_message *raw);
int rwm_init (struct raw_message *raw, int alloc_bytes);
int rwm_create (struct raw_message *raw, const void *data, int alloc_bytes);
void rwm_clone (struct raw_message *dest_raw, struct raw_message *src_raw);
void rwm_move (struct raw_message *dest_raw, struct raw_message *src_raw);
int rwm_push_data (struct raw_message *raw, const void *data, int alloc_bytes);
int rwm_push_data_ext (struct raw_message *raw, const void *data, int alloc_bytes, int prepend, int small_buffer, int std_buffer);
int rwm_push_data_front (struct raw_message *raw, const void *data, int alloc_bytes);
int rwm_fetch_data (struct raw_message *raw, void *data, int bytes);
int rwm_skip_data (struct raw_message *raw, int bytes);
int rwm_fetch_lookup (struct raw_message *raw, void *buf, int bytes);
int rwm_fetch_data_back (struct raw_message *raw, void *data, int bytes);
int rwm_fetch_lookup_back (struct raw_message *raw, void *data, int bytes);
int rwm_trunc (struct raw_message *raw, int len);
int rwm_union (struct raw_message *raw, struct raw_message *tail);
int rwm_split (struct raw_message *raw, struct raw_message *tail, int bytes);
int rwm_split_head (struct raw_message *head, struct raw_message *raw, int bytes);
void *rwm_prepend_alloc (struct raw_message *raw, int alloc_bytes);
void *rwm_postpone_alloc (struct raw_message *raw, int alloc_bytes);
void rwm_clean (struct raw_message *raw);
void rwm_clear (struct raw_message *raw);
int rwm_check (struct raw_message *raw);
int fork_message_chain (struct raw_message *raw);
int rwm_compare (struct raw_message *l, struct raw_message *r);
int rwm_prepare_iovec (const struct raw_message *raw, struct iovec *iov, int iov_len, int bytes);
int rwm_dump_sizes (struct raw_message *raw);
int rwm_dump (struct raw_message *raw);
unsigned rwm_crc32c (struct raw_message *raw, int bytes);
unsigned rwm_crc32 (struct raw_message *raw, int bytes);
unsigned rwm_custom_crc32 (struct raw_message *raw, int bytes, crc32_partial_func_t custom_crc32_partial);
int rwm_process (struct raw_message *raw, int bytes, int (*process_block)(void *extra, const void *data, int len), void *extra);
#define RMPF_ADVANCE 1
#define RMPF_TRUNCATE 2
int rwm_process_ex (struct raw_message *raw, int bytes, int offset, int flags, int (*process_block)(void *extra, const void *data, int len), void *extra);
/* negative exit code of process stops processing */
int rwm_process_from_offset (struct raw_message *raw, int bytes, int offset, int (*process_block)(void *extra, const void *data, int len), void *extra);
/* warning: in current realization refcnt of message chain should be 1 */
int rwm_transform_from_offset (struct raw_message *raw, int bytes, int offset, int (*transform_block)(void *extra, void *data, int len), void *extra);
int rwm_process_and_advance (struct raw_message *raw, int bytes, int (*process_block)(void *extra, const void *data, int len), void *extra);
int rwm_sha1 (struct raw_message *raw, int bytes, unsigned char output[20]);
// int rwm_encrypt_decrypt (struct raw_message *raw, int bytes, tg_aes_ctx_t *ctx, unsigned char iv[32]);
// int rwm_encrypt_decrypt_cbc (struct raw_message *raw, int bytes, tg_aes_ctx_t *ctx, unsigned char iv[16]);
int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, tg_aes_ctx_t *ctx, void (*crypt)(tg_aes_ctx_t *ctx, const void *src, void *dst, int l, unsigned char *iv, void *extra, void *extra2), unsigned char *iv, int block_size, void *extra, void *extra2);
void *rwm_get_block_ptr (struct raw_message *raw);
int rwm_get_block_ptr_bytes (struct raw_message *raw);
void rwm_to_tl_string (struct raw_message *raw);
extern struct raw_message empty_rwm;
void rwm_from_tl_string (struct raw_message *raw);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "net/net-rpc-targets.h"
#include "vv/vv-tree.h"
//#include "net/net-rpc-common.h"
//#include "net/net-rpc-server.h"
#include "net/net-tcp-rpc-client.h"
#include "net/net-tcp-rpc-common.h"
#include "kprintf.h"
#include "net/net-connections.h"
#include "vv/vv-io.h"
#include "common/common-stats.h"
#include "common/mp-queue.h"
#include "common/server-functions.h"
#define rpc_target_cmp(a,b) (RPC_TARGET_INFO(a)->PID.port ? memcmp (&RPC_TARGET_INFO(a)->PID, &RPC_TARGET_INFO(b)->PID, 6) : memcmp (&RPC_TARGET_INFO(a)->PID, &RPC_TARGET_INFO(b)->PID, 8))
//DEFINE_TREE(rpc_target, rpc_target_job_t, rpc_target_cmp, MALLOC)
//DEFINE_TREE(rpc_target, struct rpc_target *, rpc_target_cmp)
#define X_TYPE rpc_target_job_t
#define X_CMP rpc_target_cmp
#define TREE_NAME rpc_target
#define TREE_PTHREAD
#define TREE_MALLOC
#include "vv/vv-tree.c"
#define X_TYPE connection_job_t
#define X_CMP(a,b) (((a) < (b)) ? -1 : ((a) > (b)) ? 1 : 0)
#define TREE_NAME connection
#define TREE_PTHREAD
#define TREE_MALLOC
#define TREE_INCREF job_incref
#define TREE_DECREF job_decref_f
#include "vv/vv-tree.c"
static struct tree_rpc_target *rpc_target_tree;
struct tree_rpc_target *get_rpc_target_tree_ptr (struct tree_rpc_target **T);
void free_rpc_target_tree_ptr (struct tree_rpc_target *T);
/* {{{ STAT */
#define MODULE rpc_targets
MODULE_STAT_TYPE {
long long total_rpc_targets;
long long total_connections_in_rpc_targets;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_LL (total_rpc_targets);
SB_SUM_ONE_LL (total_connections_in_rpc_targets);
MODULE_STAT_FUNCTION_END
/* }}} */
static rpc_target_job_t rpc_target_alloc (struct process_id PID) {
assert_engine_thread ();
rpc_target_job_t SS = calloc (sizeof (struct async_job) + sizeof (struct rpc_target_info), 1);
struct rpc_target_info *S = RPC_TARGET_INFO (SS);
S->PID = PID;
struct tree_rpc_target *old = rpc_target_tree;
if (old) {
__sync_fetch_and_add (&old->refcnt, 1);
}
rpc_target_tree = tree_insert_rpc_target (rpc_target_tree, SS, lrand48_j ());
MODULE_STAT->total_rpc_targets ++;
//hexdump ((void *)rpc_target_tree, (void *)(rpc_target_tree + 1));
free_tree_ptr_rpc_target (old);
return SS;
}
void rpc_target_insert_conn (connection_job_t C) {
assert_engine_thread ();
struct connection_info *c = CONN_INFO (C);
if (c->flags & (C_ERROR | C_NET_FAILED | C_FAILED)) {
return;
}
if (TCP_RPC_DATA(C)->in_rpc_target) { return; }
assert_net_cpu_thread ();
//st_update_host ();
struct rpc_target_info t;
t.PID = TCP_RPC_DATA(C)->remote_pid;
assert (t.PID.ip);
vkprintf (2, "rpc_target_insert_conn: ip = " IP_PRINT_STR ", port = %d, fd = %d\n", IP_TO_PRINT (t.PID.ip), (int) t.PID.port, c->fd);
rpc_target_job_t fake_target = ((void *)&t) - offsetof (struct async_job, j_custom);
rpc_target_job_t SS = tree_lookup_ptr_rpc_target (rpc_target_tree, fake_target);
if (!SS) {
SS = rpc_target_alloc (t.PID);
}
struct rpc_target_info *S = RPC_TARGET_INFO (SS);
connection_job_t D = tree_lookup_ptr_connection (S->conn_tree, C);
assert (!D);
struct tree_connection *old = S->conn_tree;
if (old) {
__sync_fetch_and_add (&old->refcnt, 1);
}
S->conn_tree = tree_insert_connection (S->conn_tree, job_incref (C), lrand48_j ());
MODULE_STAT->total_connections_in_rpc_targets ++;
__sync_synchronize ();
free_tree_ptr_connection (old);
TCP_RPC_DATA(C)->in_rpc_target = 1;
}
void rpc_target_delete_conn (connection_job_t C) {
assert_engine_thread ();
struct connection_info *c = CONN_INFO (C);
if (!TCP_RPC_DATA(C)->in_rpc_target) { return; }
assert_net_cpu_thread ();
//st_update_host ();
struct rpc_target_info t;
t.PID = TCP_RPC_DATA(C)->remote_pid;
if (!t.PID.ip) {
t.PID.ip = PID.ip;
}
vkprintf (2, "rpc_target_insert_conn: ip = " IP_PRINT_STR ", port = %d, fd = %d\n", IP_TO_PRINT (t.PID.ip), (int) t.PID.port, c->fd);
rpc_target_job_t fake_target = ((void *)&t) - offsetof (struct async_job, j_custom);
rpc_target_job_t SS = tree_lookup_ptr_rpc_target (rpc_target_tree, fake_target);
if (!SS) {
SS = rpc_target_alloc (t.PID);
}
struct rpc_target_info *S = RPC_TARGET_INFO (SS);
connection_job_t D = tree_lookup_ptr_connection (S->conn_tree, C);
assert (D);
struct tree_connection *old = S->conn_tree;
if (old) {
__sync_fetch_and_add (&old->refcnt, 1);
}
S->conn_tree = tree_delete_connection (S->conn_tree, C);
MODULE_STAT->total_connections_in_rpc_targets --;
__sync_synchronize ();
free_tree_ptr_connection (old);
TCP_RPC_DATA(C)->in_rpc_target = 0;
}
rpc_target_job_t rpc_target_lookup (struct process_id *pid) {
assert (pid);
struct rpc_target_info t;
t.PID = *pid;
if (!t.PID.ip) { t.PID.ip = PID.ip; }
rpc_target_job_t fake_target = ((void *)&t) - offsetof (struct async_job, j_custom);
assert (RPC_TARGET_INFO(fake_target) == &t);
int fast = this_job_thread && this_job_thread->thread_class == JC_ENGINE;
struct tree_rpc_target *T = fast ? rpc_target_tree : get_tree_ptr_rpc_target (&rpc_target_tree);
rpc_target_job_t S = tree_lookup_ptr_rpc_target (T, fake_target);
if (!fast) {
tree_free_rpc_target (T);
}
return S;
}
rpc_target_job_t rpc_target_lookup_hp (unsigned ip, int port) {
struct process_id p;
p.ip = ip;
p.port = port;
return rpc_target_lookup (&p);
}
rpc_target_job_t rpc_target_lookup_target (conn_target_job_t SS) {
struct conn_target_info *S = CONN_TARGET_INFO (SS);
if (S->custom_field == -1) {
return 0;
}
return rpc_target_lookup_hp (S->custom_field, S->port);
}
void check_connection (connection_job_t C, void *ex, void *ex2, void *ex3) {
int *best_unr = ex2;
if (*best_unr < 0) { return; }
connection_job_t *R = ex;
struct process_id *PID = ex3;
struct connection_info *c = CONN_INFO (C);
int r = c->type->check_ready (C);
if ((c->flags & (C_ERROR | C_FAILED | C_NET_FAILED)) || c->error) {
return;
}
if (r == cr_ok) {
if (!PID || matches_pid (&TCP_RPC_DATA(C)->remote_pid, PID) >= 1) {
*best_unr = -1;
*R = C;
}
} else if (r == cr_stopped && c->unreliability < *best_unr) {
if (!PID || matches_pid (&TCP_RPC_DATA(C)->remote_pid, PID) >= 1) {
*best_unr = c->unreliability;
*R = C;
}
}
}
struct connection_choose_extra {
connection_job_t *Arr;
int limit;
int pos;
int count;
};
void check_connection_arr (connection_job_t C, void *ex, void *ex2) {
struct connection_choose_extra *E = ex;
struct process_id *PID = ex2;
struct connection_info *c = CONN_INFO (C);
int r = c->type->check_ready (C);
if ((c->flags & (C_ERROR | C_FAILED | C_NET_FAILED)) || c->error || r != cr_ok) {
return;
}
if (PID && matches_pid (&TCP_RPC_DATA(C)->remote_pid, PID) < 1) {
return;
}
if (E->pos < E->limit) {
E->Arr[E->pos ++] = C;
} else {
int t = lrand48_j () % (E->count + 1);
if (t < E->limit) {
E->Arr[t] = C;
}
}
E->count ++;
}
connection_job_t rpc_target_choose_connection (rpc_target_job_t S, struct process_id *pid) {
if (!S) {
return 0;
}
int fast = this_job_thread && this_job_thread->thread_class == JC_ENGINE;
struct tree_connection *T = fast ? RPC_TARGET_INFO (S)->conn_tree : get_tree_ptr_connection (&RPC_TARGET_INFO (S)->conn_tree);
if (!T) {
if (!fast) {
tree_free_connection (T);
}
return NULL;
}
connection_job_t C = NULL;
int best_unr = 10000;
tree_act_ex3_connection (T, check_connection, &C, &best_unr, pid);
if (C) {
job_incref (C);
}
if (!fast) {
tree_free_connection (T);
}
return C;
}
int rpc_target_choose_random_connections (rpc_target_job_t S, struct process_id *pid, int limit, connection_job_t buf[]) {
if (!S) {
return 0;
}
struct connection_choose_extra E;
E.Arr = buf;
E.count = 0;
E.pos = 0;
E.limit = limit;
int fast = this_job_thread && this_job_thread->thread_class == JC_ENGINE;
struct tree_connection *T = fast ? RPC_TARGET_INFO (S)->conn_tree : get_tree_ptr_connection (&RPC_TARGET_INFO (S)->conn_tree);
if (!T) {
if (!fast) {
tree_free_connection (T);
}
return 0;
}
tree_act_ex2_connection (T, check_connection_arr, &E, pid);
int i;
for (i = 0; i < E.pos; i++) {
job_incref (buf[i]);
}
if (!fast) {
tree_free_connection (T);
}
return E.pos;
}
int rpc_target_get_state (rpc_target_job_t S, struct process_id *pid) {
connection_job_t C = rpc_target_choose_connection (S, pid);
if (!C) {
return -1;
}
int r = CONN_INFO(C)->type->check_ready (C);
job_decref (JOB_REF_PASS (C));
if (r == cr_ok) { return 1; }
else { return 0; }
}
void rpc_target_delete (rpc_target_job_t RT) {}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2013 Vkontakte Ltd
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "net/net-connections.h"
#include "net/net-tcp-rpc-common.h"
typedef job_t rpc_target_job_t;
struct tree_connection;
struct rpc_target_info {
struct event_timer timer;
int a, b;
//connection_job_t first, last;
//conn_target_job_t target;
struct tree_connection *conn_tree;
struct process_id PID;
};
#define RPC_TARGET_INFO(_c) ((struct rpc_target_info *)(_c)->j_custom)
rpc_target_job_t rpc_target_lookup (struct process_id *PID);
rpc_target_job_t rpc_target_lookup_hp (unsigned host, int port);
rpc_target_job_t rpc_target_lookup_target (conn_target_job_t targ);
connection_job_t rpc_target_choose_connection (rpc_target_job_t S, struct process_id *PID);
int rpc_target_choose_random_connections (rpc_target_job_t S, struct process_id *PID, int limit, connection_job_t buf[]);
void rpc_target_insert_conn (connection_job_t c);
void rpc_target_insert_target (conn_target_job_t t);
void rpc_target_insert_target_ext (conn_target_job_t t, unsigned host);
void rpc_target_delete_conn (connection_job_t c);
int rpc_target_get_state (rpc_target_job_t S, struct process_id *PID);
void rpc_target_delete (rpc_target_job_t S);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <time.h>
#include <unistd.h>
#include "crc32.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "server-functions.h"
#include "net/net-connections.h"
#include "net/net-config.h"
#include "vv/vv-io.h"
#include "pid.h"
#include "common/common-stats.h"
#include "net/net-msg-buffers.h"
#include "engine/engine.h"
struct process_id PID;
extern int zheap_debug;
long long queries_allocated;
long long max_queries_allocated;
long long max_queries_allocated_sec;
long long max_queries_allocated_prev_sec;
long long total_vv_tree_nodes;
int tl_rpc_op_stat __attribute__ ((weak));
int op_stat_write (stats_buffer_t *sb) __attribute__ ((weak));
int op_stat_write (stats_buffer_t *sb) { return 0; }
int my_pid;
int connections_prepare_stat (stats_buffer_t *sb);
int udp_prepare_stat (stats_buffer_t *sb);
int tl_parse_prepare_stat (stats_buffer_t *sb);
int raw_msg_prepare_stat (stats_buffer_t *sb);
int raw_msg_buffer_prepare_stat (stats_buffer_t *sb);
int crypto_aes_prepare_stat (stats_buffer_t *sb);
int crypto_dh_prepare_stat (stats_buffer_t *sb);
int jobs_prepare_stat (stats_buffer_t *sb);
int aio_prepare_stat (stats_buffer_t *sb);
int mp_queue_prepare_stat (stats_buffer_t *sb);
int timers_prepare_stat (stats_buffer_t *sb);
int rpc_targets_prepare_stat (stats_buffer_t *sb);
//static double safe_div (double x, double y) { return y > 0 ? x/y : 0; }
int recent_idle_percent (void) {
return a_idle_quotient > 0 ? a_idle_time / a_idle_quotient * 100 : a_idle_time;
}
extern long long epoll_calls;
extern long long epoll_intr;
extern long long event_timer_insert_ops;
extern long long event_timer_remove_ops;
extern long long long_queries_cnt;
extern long long long_cpu_queries_cnt;
int prepare_stats (char *buff, int buff_size) {
if (buff_size <= 0) {
/* (SIGSEGV guard) */
/* in snprintf function second arg type is size_t */
return 0;
}
double um = get_utime_monotonic ();
stats_buffer_t sb;
sb_init (&sb, buff, buff_size);
if (!my_pid) {
my_pid = getpid ();
}
int uptime = now - start_time;
sb_printf (&sb,
"pid\t%d\n"
"start_time\t%d\n"
"current_time\t%d\n"
"uptime\t%d\n"
"tot_idle_time\t%.3f\n"
"average_idle_percent\t%.3f\n"
"recent_idle_percent\t%.3f\n"
"active_network_events\t%d\n"
"time_after_epoll\t%.6f\n"
"epoll_calls\t%lld\n"
"epoll_intr\t%lld\n"
"PID\t" PID_PRINT_STR "\n"
,
my_pid,
start_time,
now,
uptime,
tot_idle_time,
uptime > 0 ? tot_idle_time / uptime * 100 : 0,
a_idle_quotient > 0 ? a_idle_time / a_idle_quotient * 100 : a_idle_time,
ev_heap_size,
get_utime (CLOCK_MONOTONIC) - last_epoll_wait_at,
epoll_calls,
epoll_intr,
PID_TO_PRINT (&PID)
);
connections_prepare_stat (&sb);
raw_msg_prepare_stat (&sb);
raw_msg_buffer_prepare_stat (&sb);
tl_parse_prepare_stat (&sb);
crypto_aes_prepare_stat (&sb);
crypto_dh_prepare_stat (&sb);
jobs_prepare_stat (&sb);
mp_queue_prepare_stat (&sb);
timers_prepare_stat (&sb);
rpc_targets_prepare_stat (&sb);
sb_printf (&sb,
"stats_generate_time\t%.6f\n",
get_utime_monotonic () - um);
return sb.pos;
}
void output_std_stats (void) {
static char debug_stats[1 << 20];
int len = prepare_stats (debug_stats, sizeof (debug_stats) - 1);
if (len > 0) {
kprintf ("-------------- network statistics ------------\n%s\n-------------------------------------\n", debug_stats);
}
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#include <errno.h>
#include <sys/uio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include "net/net-connections.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
#include "crypto/aesni256.h"
#include "net/net-crypto-aes.h"
#include "kprintf.h"
int cpu_tcp_free_connection_buffers (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
assert_net_cpu_thread ();
rwm_free (&c->in);
rwm_free (&c->in_u);
rwm_free (&c->out);
rwm_free (&c->out_p);
return 0;
}
/* }}} */
int cpu_tcp_server_writer (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO (C);
int stop = 0;
if (c->status == conn_write_close) {
stop = 1;
}
while (1) {
struct raw_message *raw = mpq_pop_nw (c->out_queue, 4);
if (!raw) { break; }
//rwm_union (out, raw);
c->type->write_packet (C, raw);
free (raw);
}
c->type->flush (C);
struct raw_message *raw = malloc (sizeof (*raw));
if (c->type->crypto_encrypt_output && c->crypto) {
c->type->crypto_encrypt_output (C);
*raw = c->out_p;
rwm_init (&c->out_p, 0);
} else {
*raw = c->out;
rwm_init (&c->out, 0);
}
if (raw->total_bytes && c->io_conn) {
mpq_push_w (SOCKET_CONN_INFO(c->io_conn)->out_packet_queue, raw, 0);
if (stop) {
__sync_fetch_and_or (&SOCKET_CONN_INFO(c->io_conn)->flags, C_STOPWRITE);
}
job_signal (JOB_REF_CREATE_PASS (c->io_conn), JS_RUN);
} else {
rwm_free (raw);
free (raw);
}
return 0;
}
/* }}} */
int cpu_tcp_server_reader (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO(C);
while (1) {
struct raw_message *raw = mpq_pop_nw (c->in_queue, 4);
if (!raw) { break; }
if (c->crypto) {
rwm_union (&c->in_u, raw);
} else {
rwm_union (&c->in, raw);
}
free (raw);
}
if (c->crypto) {
assert (c->type->crypto_decrypt_input (C) >= 0);
}
int r = c->in.total_bytes;
int s = c->skip_bytes;
if (c->type->data_received) {
c->type->data_received (C, r);
}
if (c->flags & (C_FAILED | C_ERROR | C_NET_FAILED)) {
return -1;
}
if (c->flags & C_STOPREAD) {
return 0;
}
int r1 = r;
if (s < 0) {
// have to skip s more bytes
if (r1 > -s) {
r1 = -s;
}
rwm_skip_data (&c->in, r1);
c->skip_bytes = s += r1;
vkprintf (2, "skipped %d bytes, %d more to skip\n", r1, -s);
if (s) {
return 0;
}
}
if (s > 0) {
// need to read s more bytes before invoking parse_execute()
if (r1 >= s) {
c->skip_bytes = s = 0;
}
vkprintf (1, "fetched %d bytes, %d available bytes, %d more to load\n", r, r1, s ? s - r1 : 0);
if (s) {
return 0;
}
}
while (!c->skip_bytes && !(c->flags & (C_ERROR | C_FAILED | C_NET_FAILED | C_STOPREAD)) && c->status != conn_error) {
int bytes = c->in.total_bytes;
if (!bytes) {
break;
}
int res = c->type->parse_execute (C);
// 0 - ok/done, >0 - need that much bytes, <0 - skip bytes, or NEED_MORE_BYTES
if (!res) {
} else if (res != NEED_MORE_BYTES) {
bytes = (c->crypto ? c->in.total_bytes : c->in_u.total_bytes);
// have to load or skip abs(res) bytes before invoking parse_execute
if (res < 0) {
res -= bytes;
} else {
res += bytes;
}
c->skip_bytes = res;
break;
} else {
break;
}
}
return 0;
}
/* }}} */
int cpu_tcp_aes_crypto_encrypt_output (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO (C);
struct aes_crypto *T = c->crypto;
assert (c->crypto);
struct raw_message *out = &c->out;
int l = out->total_bytes;
l &= ~15;
if (l) {
assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, l, &T->write_aeskey, (void *)T->write_aeskey.type->cbc_crypt, T->write_iv, 16, 0, 0) == l);
}
return (-out->total_bytes) & 15;
}
/* }}} */
int cpu_tcp_aes_crypto_decrypt_input (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO (C);
struct aes_crypto *T = c->crypto;
assert (c->crypto);
struct raw_message *in = &c->in_u;
int l = in->total_bytes;
l &= ~15;
if (l) {
assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, l, &T->read_aeskey, (void *)T->read_aeskey.type->cbc_crypt, T->read_iv, 16, 0, 0) == l);
}
return (-in->total_bytes) & 15;
}
/* }}} */
int cpu_tcp_aes_crypto_needed_output_bytes (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
assert (c->crypto);
return -c->out.total_bytes & 15;
}
/* }}} */
int cpu_tcp_aes_crypto_ctr128_encrypt_output (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO (C);
struct aes_crypto *T = c->crypto;
assert (c->crypto);
struct raw_message *out = &c->out;
int l = out->total_bytes;
if (l) {
assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, l, &T->write_aeskey, (void *)T->write_aeskey.type->ctr128_crypt, T->write_iv, 1, T->write_ebuf, &T->write_num) == l);
}
return 0;
}
/* }}} */
int cpu_tcp_aes_crypto_ctr128_decrypt_input (connection_job_t C) /* {{{ */ {
assert_net_cpu_thread ();
struct connection_info *c = CONN_INFO (C);
struct aes_crypto *T = c->crypto;
assert (c->crypto);
struct raw_message *in = &c->in_u;
int l = in->total_bytes;
if (l) {
assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, l, &T->read_aeskey, (void *)T->read_aeskey.type->ctr128_crypt, T->read_iv, 1, T->read_ebuf, &T->read_num) == l);
}
return 0;
}
/* }}} */
int cpu_tcp_aes_crypto_ctr128_needed_output_bytes (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
assert (c->crypto);
return 0;
}
/* }}} */
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "net/net-connections.h"
int cpu_tcp_server_writer (connection_job_t c);
int cpu_tcp_free_connection_buffers (connection_job_t c);
int cpu_tcp_server_reader (connection_job_t c);
int cpu_tcp_aes_crypto_decrypt_input (connection_job_t c);
int cpu_tcp_aes_crypto_encrypt_output (connection_job_t c);
int cpu_tcp_aes_crypto_needed_output_bytes (connection_job_t c);
int cpu_tcp_aes_crypto_ctr128_decrypt_input (connection_job_t c);
int cpu_tcp_aes_crypto_ctr128_encrypt_output (connection_job_t c);
int cpu_tcp_aes_crypto_ctr128_needed_output_bytes (connection_job_t c);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <stddef.h>
#include "crc32.h"
#include "crc32c.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-tcp-connections.h"
#include "net/net-tcp-rpc-common.h"
#include "net/net-tcp-rpc-client.h"
#include "vv/vv-io.h"
#include "rpc-const.h"
#include "net/net-config.h"
#include "net/net-crypto-aes.h"
#include "net/net-crypto-dh.h"
#include "net/net-thread.h"
/*
*
* BASIC RPC CLIENT INTERFACE
*
*/
int tcp_rpcc_parse_execute (connection_job_t c);
int tcp_rpcc_compact_parse_execute (connection_job_t c);
int tcp_rpcc_connected (connection_job_t c);
int tcp_rpcc_connected_nohs (connection_job_t c);
int tcp_rpcc_close_connection (connection_job_t c, int who);
int tcp_rpcc_init_outbound (connection_job_t c);
int tcp_rpc_client_check_ready (connection_job_t c);
int tcp_rpcc_default_check_perm (connection_job_t c);
int tcp_rpcc_init_crypto (connection_job_t c);
int tcp_rpcc_start_crypto (connection_job_t c, char *nonce, int key_select, unsigned char *temp_key, int temp_key_len);
conn_type_t ct_tcp_rpc_client = {
.magic = CONN_FUNC_MAGIC,
.title = "rpc_client",
.accept = server_failed,
.init_accepted = server_failed,
.parse_execute = tcp_rpcc_parse_execute,
.close = tcp_rpcc_close_connection,
.init_outbound = tcp_rpcc_init_outbound,
.connected = tcp_rpcc_connected,
.wakeup = server_noop,
.check_ready = tcp_rpc_client_check_ready,
.flush = tcp_rpc_flush,
.write_packet = tcp_rpc_write_packet,
.crypto_init = aes_crypto_init,
.crypto_free = aes_crypto_free,
.crypto_encrypt_output = cpu_tcp_aes_crypto_encrypt_output,
.crypto_decrypt_input = cpu_tcp_aes_crypto_decrypt_input,
.crypto_needed_output_bytes = cpu_tcp_aes_crypto_needed_output_bytes,
.flags = C_RAWMSG,
};
//int tcp_rpcc_default_execute (connection_job_t c, int op, struct raw_message *raw);
struct tcp_rpc_client_functions default_tcp_rpc_client = {
.execute = tcp_rpc_default_execute,
.check_ready = tcp_rpcc_default_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_check_perm = tcp_rpcc_default_check_perm,
.rpc_init_crypto = tcp_rpcc_init_crypto,
.rpc_start_crypto = tcp_rpcc_start_crypto,
.rpc_ready = server_noop,
};
static int tcp_rpcc_process_nonce_packet (connection_job_t C, struct raw_message *msg) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
union {
struct tcp_rpc_nonce_packet s;
struct tcp_rpc_nonce_ext_packet x;
struct tcp_rpc_nonce_dh_packet dh;
} P;
struct tcp_rpc_nonce_dh_packet *dh = 0;
int res;
unsigned char temp_dh[256];
int temp_dh_len = 0;
int packet_num = D->in_packet_num - 1;
int packet_type;
assert (rwm_fetch_lookup (msg, &packet_type, 4) == 4);
int packet_len = msg->total_bytes;
if (packet_num != -2 || packet_type != RPC_NONCE) {
return -2;
}
if (packet_len < sizeof (struct tcp_rpc_nonce_packet) || packet_len > sizeof (struct tcp_rpc_nonce_dh_packet)) {
return -3;
}
assert (rwm_fetch_data (msg, &P, packet_len) == packet_len);
vkprintf (2, "Processing nonce packet, crypto schema: %d, key select: %d\n", P.s.crypto_schema, P.s.key_select);
switch (P.s.crypto_schema) {
case RPC_CRYPTO_NONE:
if (packet_len != sizeof (struct tcp_rpc_nonce_packet)) {
return -3;
}
break;
case RPC_CRYPTO_AES:
if (packet_len != sizeof (struct tcp_rpc_nonce_packet)) {
return -3;
}
break;
case RPC_CRYPTO_AES_EXT:
if (packet_len < sizeof (struct tcp_rpc_nonce_ext_packet) - 4 * RPC_MAX_EXTRA_KEYS) {
return -3;
}
if (P.x.extra_keys_count < 0 || P.x.extra_keys_count > RPC_MAX_EXTRA_KEYS || packet_len != sizeof (struct tcp_rpc_nonce_ext_packet) + 4*(P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS)) {
return -3;
}
break;
case RPC_CRYPTO_AES_DH:
if (packet_len < sizeof (struct tcp_rpc_nonce_dh_packet) - 4 * RPC_MAX_EXTRA_KEYS) {
return -3;
}
if (P.x.extra_keys_count < 0 || P.x.extra_keys_count > RPC_MAX_EXTRA_KEYS || packet_len != sizeof (struct tcp_rpc_nonce_dh_packet) + 4 * (P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS)) {
return -3;
}
break;
default:
return -3;
}
switch (P.s.crypto_schema) {
case RPC_CRYPTO_NONE:
if (P.s.key_select) {
return -3;
}
if (D->crypto_flags & RPCF_ALLOW_UNENC) {
if (D->crypto_flags & RPCF_ALLOW_ENC) {
// release_all_unprocessed (&c->Out);
assert (!c->out_p.total_bytes);
}
D->crypto_flags = RPCF_ALLOW_UNENC;
} else {
return -5;
}
break;
case RPC_CRYPTO_AES_DH: {
dh = (struct tcp_rpc_nonce_dh_packet *)((char *) &P + 4 * (P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS));
if (!dh_params_select) {
init_dh_params ();
}
if (!dh->dh_params_select || dh->dh_params_select != dh_params_select) {
return -7;
}
if (!(D->crypto_flags & RPCF_REQ_DH) || !c->crypto_temp) {
return -7;
}
}
case RPC_CRYPTO_AES_EXT:
P.s.key_select = select_best_key_signature (P.s.key_select, P.x.extra_keys_count, P.x.extra_key_select);
case RPC_CRYPTO_AES:
if (!P.s.key_select || !select_best_key_signature (P.s.key_select, 0, 0)) {
return -3;
}
if (!(D->crypto_flags & RPCF_ALLOW_ENC)) {
return -5;
}
if (abs (P.s.crypto_ts - D->nonce_time) > 30) {
return -6;
}
if ((D->crypto_flags & (RPCF_REQ_DH | RPCF_ALLOW_SKIP_DH)) == RPCF_REQ_DH && !dh) {
return -7;
}
if (dh) {
temp_dh_len = dh_third_round (temp_dh, dh->g_a, c->crypto_temp);
if (temp_dh_len != 256) {
return -8;
}
//active_dh_connections++;
incr_active_dh_connections ();
__sync_fetch_and_or (&c->flags, C_ISDH);
}
if (c->crypto_temp) {
if (((struct crypto_temp_dh_params *) c->crypto_temp)->magic == CRYPTO_TEMP_DH_PARAMS_MAGIC) {
free_crypto_temp (c->crypto_temp, sizeof (struct crypto_temp_dh_params));
} else {
free_crypto_temp (c->crypto_temp, 0);
}
c->crypto_temp = 0;
}
res = TCP_RPCC_FUNC(C)->rpc_start_crypto (C, P.s.crypto_nonce, P.s.key_select, temp_dh, temp_dh_len);
if (res < 0) {
return -6;
}
break;
default:
return -4;
}
vkprintf (2, "Processed nonce packet, crypto flags = %d\n", D->crypto_flags);
return 0;
}
/* }}} */
static int tcp_rpcc_send_handshake_packet (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_data *D = TCP_RPC_DATA (C);
struct tcp_rpc_handshake_packet P;
assert (PID.ip);
memset (&P, 0, sizeof (P));
P.type = RPC_HANDSHAKE;
P.flags = tcp_get_default_rpc_flags () & RPCF_USE_CRC32C;
if (!D->remote_pid.port) {
D->remote_pid.ip = (c->remote_ip == 0x7f000001 ? 0 : c->remote_ip);
D->remote_pid.port = c->remote_port;
}
memcpy (&P.sender_pid, &PID, sizeof (struct process_id));
memcpy (&P.peer_pid, &D->remote_pid, sizeof (struct process_id));
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), sizeof (P), &P);
return 0;
}
/* }}} */
static int tcp_rpcc_send_handshake_error_packet (connection_job_t C, int error_code) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_handshake_error_packet P;
if (!PID.pid) {
init_client_PID (c->our_ip);
}
memset (&P, 0, sizeof (P));
P.type = RPC_HANDSHAKE_ERROR;
P.error_code = error_code;
memcpy (&P.sender_pid, &PID, sizeof (PID));
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), sizeof (P), &P);
return 0;
}
/* }}} */
static int tcp_rpcc_process_handshake_packet (connection_job_t C, struct raw_message *msg) /* {{{ */ {
//struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
struct tcp_rpc_handshake_packet P;
int packet_num = D->in_packet_num - 1;
int packet_len = msg->total_bytes;
int packet_type;
assert (rwm_fetch_lookup (msg, &packet_type, 4) == 4);
if (packet_num != -1 || packet_type != RPC_HANDSHAKE) {
return -2;
}
if (packet_len != sizeof (struct tcp_rpc_handshake_packet)) {
tcp_rpcc_send_handshake_error_packet (C, -3);
return -3;
}
assert (rwm_fetch_data (msg, &P, packet_len) == packet_len);
if (!matches_pid (&P.sender_pid, &D->remote_pid) && !(TCP_RPCC_FUNC(C)->mode_flags & TCP_RPC_IGNORE_PID)) {
vkprintf (1, "PID mismatch during client RPC handshake: local %08x:%d:%d:%d, remote %08x:%d:%d:%d\n",
D->remote_pid.ip, D->remote_pid.port, D->remote_pid.pid, D->remote_pid.utime, P.sender_pid.ip, P.sender_pid.port, P.sender_pid.pid, P.sender_pid.utime);
tcp_rpcc_send_handshake_error_packet (C, -6);
return -6;
}
if (!P.sender_pid.ip) {
P.sender_pid.ip = D->remote_pid.ip;
}
memcpy (&D->remote_pid, &P.sender_pid, sizeof (struct process_id));
if (!matches_pid (&PID, &P.peer_pid)) {
tcp_rpcc_send_handshake_error_packet (C, -4);
return -4;
}
if (P.flags & 0xff) {
tcp_rpcc_send_handshake_error_packet (C, -7);
return -7;
}
if (P.flags & RPCF_USE_CRC32C) {
if (!(tcp_get_default_rpc_flags () & RPCF_USE_CRC32C)) {
tcp_rpcc_send_handshake_error_packet (C, -8);
return -8;
}
D->crypto_flags |= RPCF_USE_CRC32C;
D->custom_crc_partial = crc32c_partial;
}
return 0;
}
/* }}} */
int tcp_rpcc_parse_execute (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
vkprintf (4, "%s. in_total_bytes = %d\n", __func__, c->in.total_bytes);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int len;
while (1) {
len = c->in.total_bytes;
if (len <= 0) {
break;
}
if (len < 4) {
return 4 - len;
}
int packet_len;
assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4);
if (packet_len <= 0 || (packet_len & 3) || (packet_len > TCP_RPCC_FUNC(C)->max_packet_len && TCP_RPCC_FUNC(C)->max_packet_len > 0)) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if (packet_len == 4) {
assert (rwm_skip_data (&c->in, 4) == 4);
continue;
}
if (packet_len < 16) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -2);
return 0;
}
if (len < packet_len) {
return packet_len - len;
}
struct raw_message msg;
if (c->in.total_bytes == packet_len) {
msg = c->in;
rwm_init (&c->in, 0);
} else {
rwm_split_head (&msg, &c->in, packet_len);
}
unsigned crc32;
assert (rwm_fetch_data_back (&msg, &crc32, 4) == 4);
unsigned packet_crc32 = rwm_custom_crc32 (&msg, packet_len - 4, D->custom_crc_partial);
if (crc32 != packet_crc32) {
vkprintf (1, "error while parsing packet: crc32 mismatch: %08x != %08x\n", packet_crc32, crc32);
fail_connection (C, -3);
rwm_free (&msg);
return 0;
}
assert (rwm_skip_data (&msg, 4) == 4); // len
int packet_num;
int packet_type;
assert (rwm_fetch_data (&msg, &packet_num, 4) == 4);
assert (rwm_fetch_lookup (&msg, &packet_type, 4) == 4);
packet_len -= 12;
if (verbosity > 2) {
fprintf (stderr, "received packet from connection %d\n", c->fd);
rwm_dump (&msg);
}
if (packet_num != D->in_packet_num) {
vkprintf (1, "error while parsing packet: got packet num %d, expected %d\n", packet_num, D->in_packet_num);
fail_connection (C, -4);
rwm_free (&msg);
return 0;
}
if (packet_num < 0) {
D->in_packet_num ++;
int res;
if (packet_num == -2) {
res = tcp_rpcc_process_nonce_packet (C, &msg);
if (res >= 0) {
res = tcp_rpcc_send_handshake_packet (C);
}
} else if (packet_num == -1) {
res = tcp_rpcc_process_handshake_packet (C, &msg);
if (res >= 0 && TCP_RPCC_FUNC(C)->rpc_ready) {
notification_event_insert_tcp_conn_ready (C);
}
} else {
vkprintf (1, "bad packet num %d\n", packet_num);
res = -5;
}
rwm_free (&msg);
if (res < 0) {
fail_connection (C, res);
return 0;
}
continue;
}
D->in_packet_num ++;
int res;
if (packet_type == RPC_PING) {
res = tcp_rpc_default_execute (C, packet_type, &msg);
} else {
res = TCP_RPCC_FUNC(C)->execute (C, packet_type, &msg);
}
if (res <= 0) {
rwm_free (&msg);
}
}
return 0;
}
/* }}} */
int tcp_rpcc_connected (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
TCP_RPC_DATA(C)->out_packet_num = -2;
c->last_query_sent_time = precise_now;
if (TCP_RPCC_FUNC(C)->rpc_check_perm) {
int res = TCP_RPCC_FUNC(C)->rpc_check_perm (C);
if (res < 0) {
return res;
}
res &= RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC | RPCF_REQ_DH | RPCF_ALLOW_SKIP_DH;
if (!(res & (RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC))) {
return -1;
}
TCP_RPC_DATA(C)->crypto_flags = res;
} else {
TCP_RPC_DATA(C)->crypto_flags = RPCF_ALLOW_ENC | RPCF_ALLOW_UNENC;
}
vkprintf (2, "RPC connection #%d: [%s]:%d -> [%s]:%d connected, crypto_flags = %d\n", c->fd, show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port, TCP_RPC_DATA(C)->crypto_flags);
assert (TCP_RPCC_FUNC(C)->rpc_init_crypto);
int res = TCP_RPCC_FUNC(C)->rpc_init_crypto (C);
if (res > 0) {
assert (TCP_RPC_DATA(C)->crypto_flags & RPCF_ENC_SENT);
} else {
return -1;
}
assert (TCP_RPCC_FUNC(C)->flush_packet);
TCP_RPCC_FUNC(C)->flush_packet (C);
return 0;
}
/* }}} */
int tcp_rpcc_close_connection (connection_job_t C, int who) {
if (TCP_RPCC_FUNC(C)->rpc_close) {
notification_event_insert_tcp_conn_close (C);
}
return cpu_server_close_connection (C, who);
}
int tcp_rpc_client_check_ready (connection_job_t c) {
return TCP_RPCC_FUNC(c)->check_ready (c);
}
int tcp_rpcc_default_check_ready (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
if (c->flags & C_ERROR) {
return c->ready = cr_failed;
}
const double CONNECT_TIMEOUT = 3.0;
if (c->status == conn_connecting || TCP_RPC_DATA(C)->in_packet_num < 0) {
//if (TCP_RPC_DATA(C)->in_packet_num == -1 && c->status == conn_running) {
// return c->ready = cr_ok;
//}
assert (c->last_query_sent_time != 0);
if (c->last_query_sent_time < precise_now - CONNECT_TIMEOUT) {
fail_connection (C, -6);
return c->ready = cr_failed;
}
return c->ready = cr_notyet;
}
if (c->status == conn_working) {
return c->ready = cr_ok;
}
fail_connection (C, -7);
return c->ready = cr_failed;
}
int tcp_rpcc_init_fake_crypto (connection_job_t c) {
if (!(TCP_RPC_DATA(c)->crypto_flags & RPCF_ALLOW_UNENC)) {
return -1;
}
struct tcp_rpc_nonce_packet buf;
memset (&buf, 0, sizeof (buf));
buf.type = RPC_NONCE;
buf.crypto_schema = RPC_CRYPTO_NONE;
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (c), sizeof (buf), &buf);
assert ((TCP_RPC_DATA(c)->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ENC_SENT)) == 0);
TCP_RPC_DATA(c)->crypto_flags |= RPCF_ENC_SENT;
return 1;
}
int tcp_rpcc_init_outbound (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "rpcc_init_outbound (%d)\n", c->fd);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
c->last_query_sent_time = precise_now;
D->custom_crc_partial = crc32_partial;
if (TCP_RPCC_FUNC(C)->rpc_check_perm) {
int res = TCP_RPCC_FUNC(C)->rpc_check_perm (C);
if (res < 0) {
return res;
}
res &= RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC | RPCF_REQ_DH | RPCF_ALLOW_SKIP_DH;
if (!(res & (RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC))) {
return -1;
}
if (res & RPCF_REQ_DH) {
if (tcp_add_dh_accept () < 0) {
return -1;
}
}
D->crypto_flags = res;
} else {
D->crypto_flags = RPCF_ALLOW_UNENC;
}
D->in_packet_num = -2;
return 0;
}
static int force_rpc_dh;
void tcp_force_enable_dh (void) {
force_rpc_dh |= 4;
}
int tcp_rpcc_default_check_perm (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "tcp_rpcc_default_check_perm(%d): [%s]:%d -> [%s]:%d\n", c->fd, show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
return RPCF_ALLOW_ENC | tcp_get_default_rpc_flags();
}
int tcp_rpcc_init_crypto (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
if (!(TCP_RPC_DATA(C)->crypto_flags & RPCF_ALLOW_ENC)) {
return tcp_rpcc_init_fake_crypto (C);
}
TCP_RPC_DATA(C)->nonce_time = time (0);
aes_generate_nonce (TCP_RPC_DATA(C)->nonce);
if (!dh_params_select) {
assert (init_dh_params () >= 0);
assert (dh_params_select);
}
union {
struct tcp_rpc_nonce_packet s;
struct tcp_rpc_nonce_ext_packet x;
struct tcp_rpc_nonce_dh_packet dh;
} buf;
int len = sizeof (struct tcp_rpc_nonce_packet);
memset (&buf, 0, sizeof (buf));
memcpy (buf.s.crypto_nonce, TCP_RPC_DATA(C)->nonce, 16);
buf.s.crypto_ts = TCP_RPC_DATA(C)->nonce_time;
buf.s.type = RPC_NONCE;
buf.s.key_select = main_secret.key_signature;
buf.s.crypto_schema = RPC_CRYPTO_AES;
int extra_keys = buf.x.extra_keys_count = 0;
assert (extra_keys >= 0 && extra_keys <= RPC_MAX_EXTRA_KEYS);
if (TCP_RPC_DATA(C)->crypto_flags & RPCF_REQ_DH) {
buf.s.crypto_schema = RPC_CRYPTO_AES_DH;
len = sizeof (struct tcp_rpc_nonce_dh_packet) + 4*(extra_keys - RPC_MAX_EXTRA_KEYS);
struct tcp_rpc_nonce_dh_packet *dh = (struct tcp_rpc_nonce_dh_packet *)((char *) &buf + 4*(extra_keys - RPC_MAX_EXTRA_KEYS));
dh->dh_params_select = dh_params_select;
assert (!c->crypto_temp);
c->crypto_temp = alloc_crypto_temp (sizeof (struct crypto_temp_dh_params));
assert (c->crypto_temp);
dh_first_round (dh->g_a, c->crypto_temp);
} else if (extra_keys) {
buf.s.crypto_schema = RPC_CRYPTO_AES_EXT;
len = offsetof (struct tcp_rpc_nonce_ext_packet, extra_key_select) + 4 * extra_keys;
}
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), len, &buf);
assert ((TCP_RPC_DATA(C)->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ENC_SENT)) == RPCF_ALLOW_ENC);
TCP_RPC_DATA(C)->crypto_flags |= RPCF_ENC_SENT;
assert (!c->crypto);
return 1;
}
int tcp_rpcc_start_crypto (connection_job_t C, char *nonce, int key_select, unsigned char *temp_key, int temp_key_len) {
struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
vkprintf (2, "rpcc_start_crypto: key_select = %d\n", key_select);
if (c->crypto) {
return -1;
}
if (c->in.total_bytes || c->out.total_bytes || !D->nonce_time) {
return -1;
}
if (!key_select) {
return -1;
}
aes_secret_t *secret = &main_secret;
struct aes_key_data aes_keys;
if (aes_create_keys (&aes_keys, 1, nonce, D->nonce, D->nonce_time, nat_translate_ip (c->remote_ip), c->remote_port, c->remote_ipv6, nat_translate_ip (c->our_ip), c->our_port, c->our_ipv6, secret, temp_key, temp_key_len) < 0) {
return -1;
}
if (aes_crypto_init (C, &aes_keys, sizeof (aes_keys)) < 0) {
return -1;
}
return 1;
}
/*
*
* END (BASIC RPC CLIENT)
*
*/
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
struct tcp_rpc_client_functions {
void *info;
void *rpc_extra;
int (*execute)(connection_job_t c, int op, struct raw_message *raw); /* invoked from parse_execute() */
int (*check_ready)(connection_job_t c); /* invoked from rpc_client_check_ready() */
int (*flush_packet)(connection_job_t c); /* execute this to push query to server */
int (*rpc_check_perm)(connection_job_t c); /* 1 = allow unencrypted, 2 = allow encrypted */
int (*rpc_init_crypto)(connection_job_t c); /* 1 = ok; -1 = no crypto */
int (*rpc_start_crypto)(connection_job_t c, char *nonce, int key_select, unsigned char *temp_key, int temp_key_len); /* 1 = ok; -1 = no crypto */
int (*rpc_wakeup)(connection_job_t c);
int (*rpc_alarm)(connection_job_t c);
int (*rpc_ready)(connection_job_t c);
int (*rpc_close)(connection_job_t c, int who);
int max_packet_len;
int mode_flags;
};
extern struct tcp_rpc_client_functions default_tcp_rpc_client;
#define TCP_RPC_IGNORE_PID RPC_MF_IGNORE_PID
extern conn_type_t ct_tcp_rpc_client;
int tcp_rpcc_parse_execute (connection_job_t c);
int tcp_rpcc_compact_parse_execute (connection_job_t c);
int tcp_rpcc_connected (connection_job_t c);
int tcp_rpcc_connected_nohs (connection_job_t c);
int tcp_rpcc_close_connection (connection_job_t c, int who);
int tcp_rpcc_init_outbound (connection_job_t c);
int tcp_rpc_client_check_ready (connection_job_t c);
void tcp_rpcc_flush_crypto (connection_job_t c);
int tcp_rpcc_flush (connection_job_t c);
// int tcp_rpcc_flush_packet (connection_job_t c); -- use tcp_rpc_flush_packet() instead
int tcp_rpcc_default_check_perm (connection_job_t c);
int tcp_rpcc_init_crypto (connection_job_t c);
int tcp_rpcc_start_crypto (connection_job_t c, char *nonce, int key_select, unsigned char *temp_key, int temp_key_len);
int tcp_rpcc_default_check_ready (connection_job_t c);
void tcp_force_enable_dh (void);
#define TCP_RPCC_FUNC(c) ((struct tcp_rpc_client_functions *) (CONN_INFO(c)->extra))
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#include <assert.h>
#include <stdio.h>
#include <sys/uio.h>
#include "common/precise-time.h"
#include "common/rpc-const.h"
#include "common/mp-queue.h"
#include "net/net-msg.h"
#include "net/net-tcp-connections.h"
#include "net/net-tcp-rpc-common.h"
#include "kprintf.h"
#include "vv/vv-io.h"
// Flags:
// Flag 1 - can not edit this message. Need to make copy.
void tcp_rpc_conn_send_init (connection_job_t C, struct raw_message *raw, int flags) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "%s: sending message of size %d to conn fd=%d\n", __func__, raw->total_bytes, c->fd);
assert (!(raw->total_bytes & 3));
int Q[2];
Q[0] = raw->total_bytes + 12;
Q[1] = TCP_RPC_DATA(C)->out_packet_num ++;
struct raw_message *r = malloc (sizeof (*r));
if (flags & 1) {
rwm_clone (r, raw);
} else {
*r = *raw;
}
rwm_push_data_front (r, Q, 8);
unsigned crc32 = rwm_custom_crc32 (r, r->total_bytes, TCP_RPC_DATA(C)->custom_crc_partial);
rwm_push_data (r, &crc32, 4);
socket_connection_job_t S = c->io_conn;
if (S) {
mpq_push_w (SOCKET_CONN_INFO (S)->out_packet_queue, r, 0);
job_signal (JOB_REF_CREATE_PASS (S), JS_RUN);
}
}
void tcp_rpc_conn_send_im (JOB_REF_ARG (C), struct raw_message *raw, int flags) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "%s: sending message of size %d to conn fd=%d\n", __func__, raw->total_bytes, c->fd);
assert (!(raw->total_bytes & 3));
int Q[2];
Q[0] = raw->total_bytes + 12;
Q[1] = TCP_RPC_DATA(C)->out_packet_num ++;
struct raw_message *r = malloc (sizeof (*r));
if (flags & 1) {
rwm_clone (r, raw);
} else {
*r = *raw;
}
rwm_push_data_front (r, Q, 8);
unsigned crc32 = rwm_custom_crc32 (r, r->total_bytes, TCP_RPC_DATA(C)->custom_crc_partial);
rwm_push_data (r, &crc32, 4);
rwm_union (&c->out, r);
free (r);
job_signal (JOB_REF_PASS (C), JS_RUN);
}
void tcp_rpc_conn_send (JOB_REF_ARG (C), struct raw_message *raw, int flags) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "%s: sending message of size %d to conn fd=%d\n", __func__, raw->total_bytes, c->fd);
if (!(flags & 8)) {
assert (!(raw->total_bytes & 3));
}
struct raw_message *r;
if (flags & 4) {
r = raw;
assert (!(flags & 1));
} else {
r = malloc (sizeof (*r));
if (flags & 1) {
rwm_clone (r, raw);
} else {
*r = *raw;
}
}
mpq_push_w (c->out_queue, r, 0);
job_signal (JOB_REF_PASS (C), JS_RUN);
}
void tcp_rpc_conn_send_data (JOB_REF_ARG (C), int len, void *Q) {
assert (!(len & 3));
struct raw_message r;
assert (rwm_create (&r, Q, len) == len);
tcp_rpc_conn_send (JOB_REF_PASS (C), &r, 0);
}
void tcp_rpc_conn_send_data_init (connection_job_t c, int len, void *Q) {
assert (!(len & 3));
struct raw_message r;
assert (rwm_create (&r, Q, len) == len);
tcp_rpc_conn_send_init (c, &r, 0);
}
void tcp_rpc_conn_send_data_im (JOB_REF_ARG (C), int len, void *Q) {
assert (!(len & 3));
struct raw_message r;
assert (rwm_create (&r, Q, len) == len);
tcp_rpc_conn_send_im (JOB_REF_PASS (C), &r, 0);
}
int tcp_rpc_default_execute (connection_job_t C, int op, struct raw_message *raw) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
vkprintf (1, "rpcc_execute: fd=%d, op=%d, len=%d\n", c->fd, op, raw->total_bytes);
if (op == RPC_PING && raw->total_bytes == 12) {
c->last_response_time = precise_now;
int P[3];
assert (rwm_fetch_data (raw, P, 12) == 12);
P[0] = RPC_PONG;
//P[1] = Q[1];
//P[2] = Q[2];
vkprintf (2, "received ping from " IP_PRINT_STR ":%d (val = %lld)\n", IP_TO_PRINT (c->remote_ip), (int)c->remote_port, *(long long *)(P + 1));
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), 12, P);
return 0;
}
c->last_response_time = precise_now;
return 0;
}
/* }}} */
int tcp_rpc_flush_packet (connection_job_t C) {
return CONN_INFO(C)->type->flush (C);
}
int tcp_rpc_write_packet (connection_job_t C, struct raw_message *raw) {
int Q[2];
if (!(TCP_RPC_DATA(C)->flags & (RPC_F_COMPACT | RPC_F_MEDIUM))) {
Q[0] = raw->total_bytes + 12;
Q[1] = TCP_RPC_DATA(C)->out_packet_num ++;
rwm_push_data_front (raw, Q, 8);
unsigned crc32 = rwm_custom_crc32 (raw, raw->total_bytes, TCP_RPC_DATA(C)->custom_crc_partial);
rwm_push_data (raw, &crc32, 4);
rwm_union (&CONN_INFO(C)->out, raw);
}
return 0;
}
int tcp_rpc_write_packet_compact (connection_job_t C, struct raw_message *raw) {
if (raw->total_bytes == 5) {
int flag = 0;
assert (rwm_fetch_data (raw, &flag, 1) == 1);
assert (flag == 0xdd);
rwm_union (&CONN_INFO(C)->out, raw);
return 0;
}
if (!(TCP_RPC_DATA(C)->flags & (RPC_F_COMPACT | RPC_F_MEDIUM))) {
return tcp_rpc_write_packet (C, raw);
}
int len = raw->total_bytes;
assert (!(len & 0xfc000003));
if (TCP_RPC_DATA(C)->flags & RPC_F_MEDIUM) {
rwm_push_data_front (raw, &len, 4);
} else if (len <= 0x7e * 4) {
len >>= 2;
rwm_push_data_front (raw, &len, 1);
} else {
len = (len << 6) | 0x7f;
rwm_push_data_front (raw, &len, 4);
}
rwm_union (&CONN_INFO(C)->out, raw);
return 0;
}
int tcp_rpc_flush (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
if (c->crypto) {
int pad_bytes = c->type->crypto_needed_output_bytes (C);
vkprintf (2, "tcp_rpcs_flush_packet: padding with %d bytes\n", pad_bytes);
if (pad_bytes > 0) {
assert (!(pad_bytes & 3));
static const int pad_str[3] = {4, 4, 4};
assert (pad_bytes <= 12);
assert (rwm_push_data (&c->out, pad_str, pad_bytes) == pad_bytes);
}
}
return 0;
}
void tcp_rpc_send_ping (connection_job_t C, long long ping_id) {
int P[3];
P[0] = RPC_PING;
*(long long *)(P + 1) = ping_id;
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), 12, P);
}
static unsigned default_rpc_flags = 0;
unsigned tcp_set_default_rpc_flags (unsigned and_flags, unsigned or_flags) {
return (default_rpc_flags = (default_rpc_flags & and_flags) | or_flags);
}
unsigned tcp_get_default_rpc_flags (void) {
return default_rpc_flags;
}
static __thread double cur_dh_accept_rate_remaining;
static __thread double cur_dh_accept_rate_time;
static double max_dh_accept_rate;
void tcp_set_max_dh_accept_rate (int rate) {
max_dh_accept_rate = rate;
}
int tcp_add_dh_accept (void) {
if (max_dh_accept_rate) {
cur_dh_accept_rate_remaining += (precise_now - cur_dh_accept_rate_time) * max_dh_accept_rate;
cur_dh_accept_rate_time = precise_now;
if (cur_dh_accept_rate_remaining > max_dh_accept_rate) {
cur_dh_accept_rate_remaining = max_dh_accept_rate;
}
if (cur_dh_accept_rate_remaining < 1) {
return -1;
}
cur_dh_accept_rate_remaining -= 1;
}
return 0;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "pid.h"
struct tcp_message {
connection_job_t c;
int op;
int packet_num;
struct raw_message raw;
};
#pragma pack(push,4)
struct tcp_rpc_nonce_packet {
int type;
int key_select; /* least significant 32 bits of key to use */
int crypto_schema; /* 0 = NONE, 1 = AES */
int crypto_ts;
char crypto_nonce[16];
};
#define RPC_MAX_EXTRA_KEYS 8
struct tcp_rpc_nonce_ext_packet {
int type; /* type = RPC_NONCE */
int key_select; /* least significant 32 bits of key to use */
int crypto_schema; /* 2 = AES+extra keys */
int crypto_ts;
char crypto_nonce[16];
int extra_keys_count;
int extra_key_select[RPC_MAX_EXTRA_KEYS];
};
struct tcp_rpc_nonce_dh_packet {
int type; /* type = RPC_NONCE */
int key_select; /* least significant 32 bits of key to use */
int crypto_schema; /* 3 = AES+extra keys+DH */
int crypto_ts;
char crypto_nonce[16];
int extra_keys_count;
int extra_key_select[RPC_MAX_EXTRA_KEYS];
int dh_params_select; /* least significant 32 bits of SHA1 of DH params : g:int p:string */
unsigned char g_a[256];
};
struct tcp_rpc_handshake_packet {
int type;
int flags;
struct process_id sender_pid;
struct process_id peer_pid;
/* more ints? */
};
struct tcp_rpc_handshake_error_packet {
int type;
int error_code;
struct process_id sender_pid;
};
#pragma pack(pop)
// Bit 1 - have to clone raw
// Bit 2 - delete reference to connection
// Bit 4 - raw is allocated pointer and it should be freed or reused
void tcp_rpc_conn_send (JOB_REF_ARG (C), struct raw_message *raw, int flags);
void tcp_rpc_conn_send_data (JOB_REF_ARG (C), int len, void *Q);
void tcp_rpc_conn_send_init (__joblocked connection_job_t C, struct raw_message *raw, int flags);
void tcp_rpc_conn_send_data_init (__joblocked connection_job_t c, int len, void *Q);
void tcp_rpc_conn_send_im (JOB_REF_ARG (C), struct raw_message *raw, int flags);
void tcp_rpc_conn_send_data_im (JOB_REF_ARG (C), int len, void *Q);
int tcp_rpc_default_execute (connection_job_t C, int op, struct raw_message *raw);
/* for crypto_flags in struct tcp_rpc_data */
#define RPCF_ALLOW_UNENC 1
#define RPCF_ALLOW_ENC 2
#define RPCF_REQ_DH 4
#define RPCF_ALLOW_SKIP_DH 8
#define RPCF_ENC_SENT 16
#define RPCF_SEQNO_HOLES 256
#define RPCF_QUICKACK 512
#define RPCF_COMPACT_OFF 1024
#define RPCF_USE_CRC32C 2048
/* for flags in struct tcp_rpc_data */
#define RPC_F_DROPPED 0x10000000
#define RPC_F_MEDIUM 0x20000000
#define RPC_F_COMPACT 0x40000000
#define RPC_F_COMPACT_MEDIUM (RPC_F_COMPACT | RPC_F_MEDIUM)
#define RPC_F_QUICKACK 0x80000000
#define RPC_F_EXTMODE1 0x10000
#define RPC_F_EXTMODE2 0x20000
#define RPC_F_EXTMODE3 0x30000
/* in conn->custom_data */
struct tcp_rpc_data {
//int packet_len;
//int packet_num;
//int packet_type;
//int packet_crc32;
int flags;
int in_packet_num;
int out_packet_num;
int crypto_flags; /* 1 = allow unencrypted, 2 = allow encrypted, 4 = require DH, 8 = crypto NONCE packet sent, 256 = packet numbers not sequential, 512 = allow quick ack packets, 1024 = compact mode off, 2048 = use CRC32-C instead of CRC32 */
struct process_id remote_pid;
char nonce[16];
int nonce_time;
int in_rpc_target;
union {
void *user_data;
void *extra;
};
int extra_int;
int extra_int2;
int extra_int3;
int extra_int4;
double extra_double, extra_double2;
crc32_partial_func_t custom_crc_partial;
};
//extern int default_rpc_flags; /* 0 = compatibility mode, RPC_USE_CRC32C = allow both CRC32C and CRC32 */
#define RPC_NONCE 0x7acb87aa
#define RPC_HANDSHAKE 0x7682eef5
#define RPC_HANDSHAKE_ERROR 0x6a27beda
#define RPC_CRYPTO_NONE 0
#define RPC_CRYPTO_AES 1
#define RPC_CRYPTO_AES_EXT 2
#define RPC_CRYPTO_AES_DH 3
#define RPC_MF_COMPACT_ALLOW 1
#define RPC_MF_COMPACT_FORCE 2
#define RPC_MF_IGNORE_PID 4
#define RPC_MF_OPPORT_CRYPTO 8
#define TCP_RPC_DATA(c) ((struct tcp_rpc_data *) (CONN_INFO(c)->custom_data))
int tcp_rpc_flush_packet (connection_job_t C);
int tcp_rpc_write_packet (connection_job_t C, struct raw_message *raw);
int tcp_rpc_write_packet_compact (connection_job_t C, struct raw_message *raw);
int tcp_rpc_flush (connection_job_t C);
void tcp_rpc_send_ping (connection_job_t C, long long ping_id);
unsigned tcp_set_default_rpc_flags (unsigned and_flags, unsigned or_flags);
unsigned tcp_get_default_rpc_flags (void);
void tcp_set_max_dh_accept_rate (int rate);
int tcp_add_dh_accept (void);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2018 Telegram Messenger Inc
2015-2016 Vitaly Valtman
2016-2018 Nikolai Durov
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "crc32.h"
#include "crc32c.h"
#include "common/sha256.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-connections.h"
#include "net/net-tcp-rpc-ext-server.h"
#include "net/net-tcp-connections.h"
#include "net/net-thread.h"
#include "rpc-const.h"
#include "net/net-crypto-aes.h"
//#include "net/net-config.h"
#include "vv/vv-io.h"
/*
*
* EXTERNAL RPC SERVER INTERFACE
*
*/
int tcp_rpcs_compact_parse_execute (connection_job_t c);
conn_type_t ct_tcp_rpc_ext_server = {
.magic = CONN_FUNC_MAGIC,
.flags = C_RAWMSG,
.title = "rpc_ext_server",
.init_accepted = tcp_rpcs_init_accepted_nohs,
.parse_execute = tcp_rpcs_compact_parse_execute,
.close = tcp_rpcs_close_connection,
.flush = tcp_rpc_flush,
.write_packet = tcp_rpc_write_packet_compact,
.connected = server_failed,
.wakeup = tcp_rpcs_wakeup,
.alarm = tcp_rpcs_alarm,
.crypto_init = aes_crypto_ctr128_init,
.crypto_free = aes_crypto_free,
.crypto_encrypt_output = cpu_tcp_aes_crypto_ctr128_encrypt_output,
.crypto_decrypt_input = cpu_tcp_aes_crypto_ctr128_decrypt_input,
.crypto_needed_output_bytes = cpu_tcp_aes_crypto_ctr128_needed_output_bytes,
};
int tcp_rpcs_default_execute (connection_job_t c, int op, struct raw_message *msg);
static unsigned char ext_secret[16][16];
static int ext_secret_cnt = 0;
void tcp_rpcs_set_ext_secret(unsigned char secret[16]) {
assert (ext_secret_cnt < 16);
memcpy (ext_secret[ext_secret_cnt ++], secret, 16);
}
/*
struct tcp_rpc_server_functions default_tcp_rpc_ext_server = {
.execute = tcp_rpcs_default_execute,
.check_ready = server_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_wakeup = tcp_rpcs_do_wakeup,
.rpc_alarm = tcp_rpcs_do_wakeup,
.rpc_check_perm = tcp_rpcs_default_check_perm,
.rpc_init_crypto = tcp_rpcs_init_crypto,
.rpc_ready = server_noop,
};
*/
int tcp_rpcs_compact_parse_execute (connection_job_t C) {
struct tcp_rpc_data *D = TCP_RPC_DATA (C);
if (D->crypto_flags & RPCF_COMPACT_OFF) {
return tcp_rpcs_parse_execute (C);
}
struct connection_info *c = CONN_INFO (C);
int len;
vkprintf (4, "%s. in_total_bytes = %d\n", __func__, c->in.total_bytes);
while (1) {
if (c->flags & C_ERROR) {
return NEED_MORE_BYTES;
}
if (c->flags & C_STOPPARSE) {
return NEED_MORE_BYTES;
}
len = c->in.total_bytes;
if (len <= 0) {
return NEED_MORE_BYTES;
}
int min_len = (D->flags & RPC_F_MEDIUM) ? 4 : 1;
if (len < min_len + 8) {
return min_len + 8 - len;
}
int packet_len = 0;
assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4);
if (D->in_packet_num == -3) {
vkprintf (1, "trying to determine connection type\n");
#if __ALLOW_UNOBFS__
if ((packet_len & 0xff) == 0xef) {
D->flags |= RPC_F_COMPACT;
assert (rwm_skip_data (&c->in, 1) == 1);
D->in_packet_num = 0;
vkprintf (1, "Short type\n");
continue;
}
if (packet_len == 0xeeeeeeee) {
D->flags |= RPC_F_MEDIUM;
assert (rwm_skip_data (&c->in, 4) == 4);
D->in_packet_num = 0;
vkprintf (1, "Medium type\n");
continue;
}
// http
if ((packet_len == *(int *)"HEAD" || packet_len == *(int *)"POST" || packet_len == *(int *)"GET " || packet_len == *(int *)"OPTI") && TCP_RPCS_FUNC(C)->http_fallback_type) {
D->crypto_flags |= RPCF_COMPACT_OFF;
vkprintf (1, "HTTP type\n");
return tcp_rpcs_parse_execute (C);
}
int tmp[2];
assert (rwm_fetch_lookup (&c->in, &tmp, 8) == 8);
if (!tmp[1]) {
D->crypto_flags |= RPCF_COMPACT_OFF;
vkprintf (1, "Long type\n");
return tcp_rpcs_parse_execute (C);
}
#endif
if (len < 64) {
#if __ALLOW_UNOBFS__
vkprintf (1, "random 64-byte header: first 0x%08x 0x%08x, need %d more bytes to distinguish\n", tmp[0], tmp[1], 64 - len);
#else
vkprintf (1, "\"random\" 64-byte header: have %d bytes, need %d more bytes to distinguish\n", len, 64 - len);
#endif
return 64 - len;
}
unsigned char random_header[64];
unsigned char k[48];
assert (rwm_fetch_lookup (&c->in, random_header, 64) == 64);
unsigned char random_header_sav[64];
memcpy (random_header_sav, random_header, 64);
struct aes_key_data key_data;
int ok = 0;
int secret_id;
for (secret_id = 0; secret_id < 1 || secret_id < ext_secret_cnt; secret_id++) {
if (ext_secret_cnt > 0) {
memcpy (k, random_header + 8, 32);
memcpy (k + 32, ext_secret[secret_id], 16);
sha256 (k, 48, key_data.read_key);
} else {
memcpy (key_data.read_key, random_header + 8, 32);
}
memcpy (key_data.read_iv, random_header + 40, 16);
int i;
for (i = 0; i < 32; i++) {
key_data.write_key[i] = random_header[55 - i];
}
for (i = 0; i < 16; i++) {
key_data.write_iv[i] = random_header[23 - i];
}
if (ext_secret_cnt > 0) {
memcpy (k, key_data.write_key, 32);
sha256 (k, 48, key_data.write_key);
}
aes_crypto_ctr128_init (C, &key_data, sizeof (key_data));
assert (c->crypto);
struct aes_crypto *T = c->crypto;
T->read_aeskey.type->ctr128_crypt (&T->read_aeskey, random_header, random_header, 64, T->read_iv, T->read_ebuf, &T->read_num);
unsigned tag = *(unsigned *)(random_header + 56);
if (tag == 0xeeeeeeee || tag == 0xefefefef) {
assert (rwm_skip_data (&c->in, 64) == 64);
rwm_union (&c->in_u, &c->in);
rwm_init (&c->in, 0);
// T->read_pos = 64;
D->in_packet_num = 0;
switch (tag) {
case 0xeeeeeeee:
D->flags |= RPC_F_MEDIUM | RPC_F_EXTMODE2;
break;
case 0xefefefef:
D->flags |= RPC_F_COMPACT | RPC_F_EXTMODE2;
break;
}
assert (c->type->crypto_decrypt_input (C) >= 0);
int target = *(short *)(random_header + 60);
D->extra_int4 = target;
vkprintf (1, "tcp opportunistic encryption mode detected, tag = %08x, target=%d\n", tag, target);
ok = 1;
break;
} else {
aes_crypto_free (C);
memcpy (random_header, random_header_sav, 64);
}
}
if (ok) {
continue;
}
if (ext_secret_cnt > 0) {
vkprintf (1, "invalid \"random\" 64-byte header, entering global skip mode\n");
return (-1 << 28);
}
#if __ALLOW_UNOBFS__
vkprintf (1, "short type with 64-byte header: first 0x%08x 0x%08x\n", tmp[0], tmp[1]);
D->flags |= RPC_F_COMPACT | RPC_F_EXTMODE1;
D->in_packet_num = 0;
assert (len >= 64);
assert (rwm_skip_data (&c->in, 64) == 64);
continue;
#else
vkprintf (1, "invalid \"random\" 64-byte header, entering global skip mode\n");
return (-1 << 28);
#endif
}
int packet_len_bytes = 4;
if (D->flags & RPC_F_MEDIUM) {
// packet len in `medium` mode
if (D->crypto_flags & RPCF_QUICKACK) {
D->flags = (D->flags & ~RPC_F_QUICKACK) | (packet_len & RPC_F_QUICKACK);
packet_len &= ~RPC_F_QUICKACK;
}
} else {
// packet len in `compact` mode
if (packet_len & 0x80) {
D->flags |= RPC_F_QUICKACK;
packet_len &= ~0x80;
} else {
D->flags &= ~RPC_F_QUICKACK;
}
if ((packet_len & 0xff) == 0x7f) {
packet_len = ((unsigned) packet_len >> 8);
if (packet_len < 0x7f) {
vkprintf (1, "error while parsing compact packet: got length %d in overlong encoding\n", packet_len);
fail_connection (C, -1);
return 0;
}
} else {
packet_len &= 0x7f;
packet_len_bytes = 1;
}
packet_len <<= 2;
}
if (packet_len <= 0 || (packet_len & 0xc0000003)) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if ((packet_len > TCP_RPCS_FUNC(C)->max_packet_len && TCP_RPCS_FUNC(C)->max_packet_len > 0)) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if (len < packet_len + packet_len_bytes) {
return packet_len + packet_len_bytes - len;
}
assert (rwm_skip_data (&c->in, packet_len_bytes) == packet_len_bytes);
struct raw_message msg;
int packet_type;
rwm_split_head (&msg, &c->in, packet_len);
assert (rwm_fetch_lookup (&msg, &packet_type, 4) == 4);
if (D->in_packet_num < 0) {
assert (D->in_packet_num == -3);
D->in_packet_num = 0;
}
if (verbosity > 2) {
fprintf (stderr, "received packet from connection %d (length %d, num %d, type %08x)\n", c->fd, packet_len, D->in_packet_num, packet_type);
rwm_dump (&msg);
}
int res = -1;
/* main case */
c->last_response_time = precise_now;
if (packet_type == RPC_PING) {
res = tcp_rpcs_default_execute (C, packet_type, &msg);
} else {
res = TCP_RPCS_FUNC(C)->execute (C, packet_type, &msg);
}
if (res <= 0) {
rwm_free (&msg);
}
D->in_packet_num++;
}
return NEED_MORE_BYTES;
}
/*
*
* END (EXTERNAL RPC SERVER)
*
*/
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016-2018 Telegram Messenger Inc
2016-2018 Nikolai Durov
*/
#pragma once
#define __ALLOW_UNOBFS__ 0
#include "net/net-tcp-rpc-server.h"
#include "net/net-connections.h"
extern conn_type_t ct_tcp_rpc_ext_server;
// extern struct tcp_rpc_server_functions default_tcp_rpc_server;
int tcp_rpcs_compact_parse_execute (connection_job_t c);
void tcp_rpcs_set_ext_secret(unsigned char secret[16]);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "crc32.h"
#include "crc32c.h"
#include "net/net-events.h"
#include "kprintf.h"
#include "precise-time.h"
#include "net/net-connections.h"
#include "net/net-tcp-rpc-server.h"
#include "net/net-tcp-connections.h"
#include "net/net-thread.h"
#include "rpc-const.h"
#include "net/net-crypto-aes.h"
#include "net/net-crypto-dh.h"
#include "net/net-config.h"
#include "vv/vv-io.h"
/*
*
* BASIC RPC SERVER INTERFACE
*
*/
int tcp_rpcs_wakeup (connection_job_t c);
int tcp_rpcs_parse_execute (connection_job_t c);
int tcp_rpcs_alarm (connection_job_t c);
int tcp_rpcs_do_wakeup (connection_job_t c);
int tcp_rpcs_init_accepted (connection_job_t c);
int tcp_rpcs_close_connection (connection_job_t c, int who);
int tcp_rpcs_init_accepted_nohs (connection_job_t c);
int tcp_rpcs_default_check_perm (connection_job_t c);
int tcp_rpcs_init_crypto (connection_job_t c, struct tcp_rpc_nonce_packet *P);
conn_type_t ct_tcp_rpc_server = {
.magic = CONN_FUNC_MAGIC,
.flags = C_RAWMSG,
.title = "rpc_tcp_server",
.init_accepted = tcp_rpcs_init_accepted,
.parse_execute = tcp_rpcs_parse_execute,
.close = tcp_rpcs_close_connection,
.flush = tcp_rpc_flush,
.write_packet = tcp_rpc_write_packet,
.connected = server_failed,
.wakeup = tcp_rpcs_wakeup,
.alarm = tcp_rpcs_alarm,
.crypto_init = aes_crypto_init,
.crypto_free = aes_crypto_free,
.crypto_encrypt_output = cpu_tcp_aes_crypto_encrypt_output,
.crypto_decrypt_input = cpu_tcp_aes_crypto_decrypt_input,
.crypto_needed_output_bytes = cpu_tcp_aes_crypto_needed_output_bytes,
};
int tcp_rpcs_default_execute (connection_job_t c, int op, struct raw_message *msg);
struct tcp_rpc_server_functions default_tcp_rpc_server = {
.execute = tcp_rpcs_default_execute,
.check_ready = server_check_ready,
.flush_packet = tcp_rpc_flush_packet,
.rpc_wakeup = tcp_rpcs_do_wakeup,
.rpc_alarm = tcp_rpcs_do_wakeup,
.rpc_check_perm = tcp_rpcs_default_check_perm,
.rpc_init_crypto = tcp_rpcs_init_crypto,
.rpc_ready = server_noop,
};
int tcp_rpcs_default_execute (connection_job_t C, int op, struct raw_message *raw) {
struct connection_info *c = CONN_INFO (C);
vkprintf (3, "%s: fd=%d, op=%d, len=%d\n", __func__, c->fd, op, raw->total_bytes);
if (op == RPC_PING && raw->total_bytes == 12) {
c->last_response_time = precise_now;
int P[3];
assert (rwm_fetch_data (raw, P, 12) == 12);
P[0] = RPC_PONG;
vkprintf (3, "received ping from " IP_PRINT_STR ":%d (val = %lld)\n", IP_TO_PRINT (c->remote_ip), (int)c->remote_port, *(long long *)(P + 1));
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (C), 12, P);
return 0;
}
return 0;
}
static int tcp_rpcs_process_nonce_packet (connection_job_t C, struct raw_message *msg) {
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
union {
struct tcp_rpc_nonce_packet s;
struct tcp_rpc_nonce_ext_packet x;
struct tcp_rpc_nonce_dh_packet dh;
} P;
struct tcp_rpc_nonce_dh_packet *dh = 0;
int res;
int packet_num = D->in_packet_num;
int packet_type;
assert (rwm_fetch_lookup (msg, &packet_type, 4) == 4);
int packet_len = msg->total_bytes;
if (packet_num != -2 || packet_type != RPC_NONCE) {
return -2;
}
if (packet_len < sizeof (struct tcp_rpc_nonce_packet) || packet_len > sizeof (struct tcp_rpc_nonce_dh_packet)) {
return -3;
}
assert (rwm_fetch_data (msg, &P, packet_len) == packet_len);
switch (P.s.crypto_schema) {
case RPC_CRYPTO_NONE:
if (packet_len != sizeof (struct tcp_rpc_nonce_packet)) {
return -3;
}
break;
case RPC_CRYPTO_AES:
if (packet_len != sizeof (struct tcp_rpc_nonce_packet)) {
return -3;
}
break;
case RPC_CRYPTO_AES_EXT:
if (packet_len < sizeof (struct tcp_rpc_nonce_ext_packet) - 4 * RPC_MAX_EXTRA_KEYS) {
return -3;
}
if (P.x.extra_keys_count < 0 || P.x.extra_keys_count > RPC_MAX_EXTRA_KEYS || packet_len != sizeof (struct tcp_rpc_nonce_ext_packet) + 4 * (P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS)) {
return -3;
}
break;
case RPC_CRYPTO_AES_DH:
if (packet_len < sizeof (struct tcp_rpc_nonce_dh_packet) - 4 * RPC_MAX_EXTRA_KEYS) {
return -3;
}
if (P.x.extra_keys_count < 0 || P.x.extra_keys_count > RPC_MAX_EXTRA_KEYS || packet_len != sizeof (struct tcp_rpc_nonce_dh_packet) + 4 * (P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS)) {
return -3;
}
break;
default:
return -3;
}
switch (P.s.crypto_schema) {
case RPC_CRYPTO_NONE:
if (P.s.key_select) {
return -3;
}
if (D->crypto_flags & RPCF_ALLOW_UNENC) {
D->crypto_flags = RPCF_ALLOW_UNENC;
} else {
return -5;
}
break;
case RPC_CRYPTO_AES_DH: {
dh = (struct tcp_rpc_nonce_dh_packet *)((char *) &P + 4*(P.x.extra_keys_count - RPC_MAX_EXTRA_KEYS));
if (!dh_params_select) {
init_dh_params ();
}
if (!dh->dh_params_select || dh->dh_params_select != dh_params_select) {
dh = 0;
}
}
case RPC_CRYPTO_AES_EXT:
P.s.key_select = select_best_key_signature (P.s.key_select, P.x.extra_keys_count, P.x.extra_key_select);
case RPC_CRYPTO_AES:
if (!P.s.key_select || !select_best_key_signature (P.s.key_select, 0, 0)) {
if (D->crypto_flags & RPCF_ALLOW_UNENC) {
D->crypto_flags = RPCF_ALLOW_UNENC;
break;
}
return -3;
}
if (!(D->crypto_flags & RPCF_ALLOW_ENC)) {
if (D->crypto_flags & RPCF_ALLOW_UNENC) {
D->crypto_flags = RPCF_ALLOW_UNENC;
break;
}
return -5;
}
D->nonce_time = (now ? now : time (0));
if (abs (P.s.crypto_ts - D->nonce_time) > 30) {
return -6; //less'om
}
D->crypto_flags &= ~RPCF_ALLOW_UNENC;
break;
default:
if (D->crypto_flags & RPCF_ALLOW_UNENC) {
D->crypto_flags = RPCF_ALLOW_UNENC;
break;
}
return -4;
}
if ((D->crypto_flags & (RPCF_REQ_DH | RPCF_ALLOW_ENC)) == (RPCF_REQ_DH | RPCF_ALLOW_ENC) && !dh) {
if (D->crypto_flags & RPCF_ALLOW_SKIP_DH) {
D->crypto_flags &= ~(RPCF_REQ_DH | RPCF_ALLOW_SKIP_DH);
} else {
return -7;
}
}
res = TCP_RPCS_FUNC(C)->rpc_init_crypto (C, &P.s);
if (res < 0) {
return -6;
}
return 0;
}
static int tcp_rpcs_send_handshake_packet (connection_job_t c) {
struct tcp_rpc_data *D = TCP_RPC_DATA(c);
struct tcp_rpc_handshake_packet P;
assert (PID.pid);
memset (&P, 0, sizeof (P));
P.type = RPC_HANDSHAKE;
P.flags = D->crypto_flags & RPCF_USE_CRC32C;
memcpy (&P.sender_pid, &PID, sizeof (struct process_id));
memcpy (&P.peer_pid, &D->remote_pid, sizeof (struct process_id));
tcp_rpc_conn_send_data_im (JOB_REF_CREATE_PASS (c), sizeof (P), &P);
return 0;
}
static int tcp_rpcs_send_handshake_error_packet (connection_job_t c, int error_code) {
struct tcp_rpc_handshake_error_packet P;
assert (PID.pid);
memset (&P, 0, sizeof (P));
P.type = RPC_HANDSHAKE_ERROR;
P.error_code = error_code;
memcpy (&P.sender_pid, &PID, sizeof (PID));
tcp_rpc_conn_send_data (JOB_REF_CREATE_PASS (c), sizeof (P), &P);
return 0;
}
static int tcp_rpcs_process_handshake_packet (connection_job_t C, struct raw_message *msg) {
struct connection_info *c = CONN_INFO (C);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
struct tcp_rpc_handshake_packet P;
if (!PID.ip) {
init_server_PID (c->our_ip, c->our_port);
if (!PID.ip) {
PID.ip = get_my_ipv4 ();
}
}
int packet_num = D->in_packet_num;
int packet_type;
assert (rwm_fetch_lookup (msg, &packet_type, 4) == 4);
int packet_len = msg->total_bytes;
if (packet_num != -1 || packet_type != RPC_HANDSHAKE) {
return -2;
}
if (packet_len != sizeof (struct tcp_rpc_handshake_packet)) {
tcp_rpcs_send_handshake_error_packet (C, -3);
return -3;
}
assert (rwm_fetch_data (msg, &P, packet_len) == packet_len);
memcpy (&D->remote_pid, &P.sender_pid, sizeof (struct process_id));
if (!matches_pid (&PID, &P.peer_pid) && !(TCP_RPCS_FUNC(C)->mode_flags & TCP_RPC_IGNORE_PID)) {
vkprintf (1, "PID mismatch during handshake: local %08x:%d:%d:%d, remote %08x:%d:%d:%d\n",
PID.ip, PID.port, PID.pid, PID.utime, P.peer_pid.ip, P.peer_pid.port, P.peer_pid.pid, P.peer_pid.utime);
tcp_rpcs_send_handshake_error_packet (C, -4);
return -4;
}
if (P.flags & 0xff) {
tcp_rpcs_send_handshake_error_packet (C, -7);
return -7;
}
if (P.flags & tcp_get_default_rpc_flags () & RPCF_USE_CRC32C) {
D->crypto_flags |= RPCF_USE_CRC32C;
}
return 0;
}
int tcp_rpcs_parse_execute (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
vkprintf (4, "%s. in_total_bytes = %d\n", __func__, c->in.total_bytes);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
int len;
while (1) {
if (c->flags & C_ERROR) {
return NEED_MORE_BYTES;
}
if (c->flags & C_STOPPARSE) {
return NEED_MORE_BYTES;
}
len = c->in.total_bytes;
if (len <= 0) {
return NEED_MORE_BYTES;
}
if (len < 4) {
return 4 - len;
}
int packet_len;
assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4);
if (D->crypto_flags & RPCF_QUICKACK) {
D->flags = (D->flags & ~RPC_F_QUICKACK) | (packet_len & RPC_F_QUICKACK);
packet_len &= ~RPC_F_QUICKACK;
}
if (packet_len <= 0 || (packet_len & 0xc0000003)) {
if (D->in_packet_num <= -2 && (packet_len == *(int *)"HEAD" || packet_len == *(int *)"POST" || packet_len == *(int *)"GET " || packet_len == *(int *)"OPTI") && TCP_RPCS_FUNC(C)->http_fallback_type) {
vkprintf (1, "switching to http fallback for connection %d\n", c->fd);
memset (c->custom_data, 0, sizeof (c->custom_data));
c->type = TCP_RPCS_FUNC(C)->http_fallback_type;
c->extra = TCP_RPCS_FUNC(C)->http_fallback_extra;
if (c->type->init_accepted (C) < 0) {
vkprintf (1, "http init_accepted() returns error for connection %d\n", c->fd);
fail_connection (C, -33);
return 0;
}
//nbit_set (&c->Q, &c->In);
return c->type->parse_execute (C);
}
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if ((packet_len > TCP_RPCS_FUNC(C)->max_packet_len && TCP_RPCS_FUNC(C)->max_packet_len > 0)) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if (packet_len == 4) {
assert (rwm_skip_data (&c->in, 4) == 4);
continue;
}
if (packet_len < 16) {
vkprintf (1, "error while parsing packet: bad packet length %d\n", packet_len);
fail_connection (C, -1);
return 0;
}
if (len < packet_len) {
return packet_len - len;
}
struct raw_message msg;
rwm_split_head (&msg, &c->in, packet_len);
unsigned crc32;
assert (rwm_fetch_data_back (&msg, &crc32, 4) == 4);
unsigned packet_crc32 = rwm_custom_crc32 (&msg, packet_len - 4, D->custom_crc_partial);
if (crc32 != packet_crc32) {
vkprintf (1, "error while parsing packet: crc32 = %08x != %08x\n", packet_crc32, crc32);
rwm_dump (&msg);
fail_connection (C, -1);
rwm_free (&msg);
return 0;
}
int packet_num;
int packet_type;
assert (rwm_skip_data (&msg, 4) == 4);
assert (rwm_fetch_data (&msg, &packet_num, 4) == 4);
assert (rwm_fetch_lookup (&msg, &packet_type, 4) == 4);
packet_len -= 12;
if (verbosity > 2) {
fprintf (stderr, "received packet from connection %d (num %d)\n", c->fd, packet_num);
rwm_dump (&msg);
}
int res = -1;
if (D->in_packet_num == -3) {
D->in_packet_num = 0;
}
if (!(D->crypto_flags & RPCF_SEQNO_HOLES) && packet_num != D->in_packet_num) {
vkprintf (1, "error while parsing packet: got packet num %d, expected %d\n", packet_num, D->in_packet_num);
fail_connection (C, -1);
rwm_free (&msg);
return 0;
} else if (packet_num < 0) {
/* this is for us */
if (packet_num == -2) {
res = tcp_rpcs_process_nonce_packet (C, &msg); // if res > 0, nonce packet sent in response
} else if (packet_num == -1) {
res = tcp_rpcs_process_handshake_packet (C, &msg);
if (res >= 0) {
res = tcp_rpcs_send_handshake_packet (C);
if (D->crypto_flags & RPCF_USE_CRC32C) {
D->custom_crc_partial = crc32c_partial;
}
notification_event_insert_tcp_conn_ready (C);
}
}
rwm_free (&msg);
if (res < 0) {
fail_connection (C, res);
return 0;
}
} else {
/* main case */
c->last_response_time = precise_now;
if (packet_type == RPC_PING) {
res = tcp_rpcs_default_execute (C, packet_type, &msg);
} else {
res = TCP_RPCS_FUNC(C)->execute (C, packet_type, &msg);
}
if (res <= 0) {
rwm_free (&msg);
}
}
D->in_packet_num++;
}
return NEED_MORE_BYTES;
}
int tcp_rpcs_wakeup (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
notification_event_insert_tcp_conn_wakeup (C);
if (c->out_p.total_bytes > 0) {
__sync_fetch_and_or (&c->flags, C_WANTWR);
}
//c->generation = ++conn_generation;
c->pending_queries = 0;
return 0;
}
int tcp_rpcs_alarm (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
notification_event_insert_tcp_conn_alarm (C);
if (c->out_p.total_bytes > 0) {
__sync_fetch_and_or (&c->flags, C_WANTWR);
}
//c->generation = ++conn_generation;
c->pending_queries = 0;
return 0;
}
int tcp_rpcs_close_connection (connection_job_t C, int who) {
if (TCP_RPCS_FUNC(C)->rpc_close) {
notification_event_insert_tcp_conn_close (C);
}
return cpu_server_close_connection (C, who);
}
int tcp_rpcs_do_wakeup (connection_job_t c) {
return 0;
}
int tcp_rpcs_init_accepted (connection_job_t C) {
struct connection_info *c = CONN_INFO (C);
c->last_query_sent_time = precise_now;
TCP_RPC_DATA(C)->custom_crc_partial = crc32_partial;
if (TCP_RPCS_FUNC(C)->rpc_check_perm) {
int res = TCP_RPCS_FUNC(C)->rpc_check_perm (C);
vkprintf (4, "tcp_rpcs_check_perm for connection %d: [%s]:%d -> [%s]:%d = %d\n", c->fd, show_remote_ip (C), c->remote_port, show_our_ip (C), c->our_port, res);
if (res < 0) {
return res;
}
res &= RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC | RPCF_REQ_DH | RPCF_ALLOW_SKIP_DH;
if (!(res & (RPCF_ALLOW_UNENC | RPCF_ALLOW_ENC))) {
return -1;
}
TCP_RPC_DATA(C)->crypto_flags = res;
} else {
TCP_RPC_DATA(C)->crypto_flags = RPCF_ALLOW_UNENC;
}
TCP_RPC_DATA(C)->in_packet_num = -2;
TCP_RPC_DATA(C)->out_packet_num = -2;
return 0;
}
int tcp_rpcs_init_accepted_nohs (connection_job_t c) {
TCP_RPC_DATA(c)->crypto_flags = RPCF_QUICKACK | RPCF_ALLOW_UNENC;
TCP_RPC_DATA(c)->in_packet_num = -3;
TCP_RPC_DATA(c)->custom_crc_partial = crc32_partial;
if (TCP_RPCS_FUNC(c)->rpc_ready) {
notification_event_insert_tcp_conn_ready (c);
}
return 0;
}
int tcp_rpcs_init_fake_crypto (connection_job_t c) {
if (!(TCP_RPC_DATA(c)->crypto_flags & RPCF_ALLOW_UNENC)) {
return -1;
}
struct tcp_rpc_nonce_packet buf;
memset (&buf, 0, sizeof (buf));
buf.type = RPC_NONCE;
buf.crypto_schema = RPC_CRYPTO_NONE;
assert ((TCP_RPC_DATA(c)->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ENC_SENT)) == 0);
TCP_RPC_DATA(c)->crypto_flags |= RPCF_ENC_SENT;
tcp_rpc_conn_send_data_init (c, sizeof (buf), &buf);
return 1;
}
#include "net/net-crypto-aes.h"
#include "net/net-config.h"
int tcp_rpcs_default_check_perm (connection_job_t C) {
return RPCF_ALLOW_ENC | RPCF_REQ_DH | tcp_get_default_rpc_flags();
}
int tcp_rpcs_init_crypto (connection_job_t C, struct tcp_rpc_nonce_packet *P) {
struct connection_info *c = CONN_INFO (C);
// fprintf (stderr, "rpcs_init_crypto (%p [fd=%d], '%.*s')\n", c, c->fd, key_len, key);
struct tcp_rpc_data *D = TCP_RPC_DATA(C);
if (c->crypto) {
return -1;
}
if ((D->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ALLOW_UNENC)) == RPCF_ALLOW_UNENC) {
return tcp_rpcs_init_fake_crypto (C);
}
if ((D->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ALLOW_UNENC)) != RPCF_ALLOW_ENC) {
return -1;
}
if (main_secret.key_signature != P->key_select) {
return -1;
}
aes_secret_t *secret = &main_secret;
union {
struct tcp_rpc_nonce_packet s;
struct tcp_rpc_nonce_ext_packet x;
struct tcp_rpc_nonce_dh_packet dh;
} buf;
struct tcp_rpc_nonce_dh_packet *old_dh = 0, *new_dh = 0;
unsigned char temp_dh[256];
int temp_dh_len = 0;
if (D->crypto_flags & RPCF_REQ_DH) {
new_dh = (struct tcp_rpc_nonce_dh_packet *)((char *)&buf - 4*RPC_MAX_EXTRA_KEYS);
if (P->crypto_schema != RPC_CRYPTO_AES_DH) {
return -1;
}
old_dh = (struct tcp_rpc_nonce_dh_packet *)((char *)P + 4*(((struct tcp_rpc_nonce_dh_packet *) P)->extra_keys_count - RPC_MAX_EXTRA_KEYS));
if (old_dh->dh_params_select != dh_params_select || !dh_params_select) {
return -1;
}
if (tcp_add_dh_accept () < 0) {
return -1;
}
temp_dh_len = dh_second_round (temp_dh, new_dh->g_a, old_dh->g_a);
assert (temp_dh_len == 256);
incr_active_dh_connections ();
__sync_fetch_and_or (&c->flags, C_ISDH);
}
aes_generate_nonce (D->nonce);
struct aes_key_data aes_keys;
if (aes_create_keys (&aes_keys, 0, D->nonce, P->crypto_nonce, P->crypto_ts, nat_translate_ip (c->our_ip), c->our_port, c->our_ipv6, nat_translate_ip (c->remote_ip), c->remote_port, c->remote_ipv6, secret, temp_dh, temp_dh_len) < 0) {
return -1;
}
if (aes_crypto_init (C, &aes_keys, sizeof (aes_keys)) < 0) {
return -1;
}
memcpy (buf.s.crypto_nonce, D->nonce, 16);
buf.s.crypto_ts = D->nonce_time;
buf.s.type = RPC_NONCE;
buf.s.key_select = secret->key_signature;
int buf_len;
if (!new_dh) {
buf.s.crypto_schema = RPC_CRYPTO_AES;
buf_len = sizeof (struct tcp_rpc_nonce_packet);
} else {
buf.dh.crypto_schema = RPC_CRYPTO_AES_DH;
buf_len = sizeof (struct tcp_rpc_nonce_dh_packet) - 4*RPC_MAX_EXTRA_KEYS;
buf.dh.extra_keys_count = 0;
new_dh->dh_params_select = dh_params_select;
}
assert ((D->crypto_flags & (RPCF_ALLOW_ENC | RPCF_ENC_SENT)) == RPCF_ALLOW_ENC);
D->crypto_flags |= RPCF_ENC_SENT;
tcp_rpc_conn_send_data_init (C, buf_len, &buf);
return 1;
}
/*
*
* END (BASIC RPC SERVER)
*
*/
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010-2013 Vkontakte Ltd
2010-2013 Nikolai Durov
2010-2013 Andrey Lopatin
2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "net/net-tcp-rpc-common.h"
#include "net/net-connections.h"
struct tcp_rpc_server_functions {
void *info;
void *rpc_extra;
int (*execute)(connection_job_t c, int op, struct raw_message *raw);/* invoked from parse_execute() */
int (*check_ready)(connection_job_t c); /* invoked from rpc_client_check_ready() */
int (*flush_packet)(connection_job_t c); /* execute this to push response to client */
int (*rpc_check_perm)(connection_job_t c); /* 1 = allow unencrypted, 2 = allow encrypted */
int (*rpc_init_crypto)(connection_job_t c, struct tcp_rpc_nonce_packet *P); /* 1 = ok; -1 = no crypto */
void *nop;
int (*rpc_wakeup)(connection_job_t c);
int (*rpc_alarm)(connection_job_t c);
int (*rpc_ready)(connection_job_t c);
int (*rpc_close)(connection_job_t c, int who);
int max_packet_len;
int mode_flags; /* 1 = ignore PID mismatch */
void *memcache_fallback_type, *memcache_fallback_extra;
void *http_fallback_type, *http_fallback_extra;
};
#define TCP_RPC_IGNORE_PID RPC_MF_IGNORE_PID
extern conn_type_t ct_tcp_rpc_server;
extern struct tcp_rpc_server_functions default_tcp_rpc_server;
int tcp_rpcs_wakeup (connection_job_t c);
int tcp_rpcs_parse_execute (connection_job_t c);
int tcp_rpcs_alarm (connection_job_t c);
int tcp_rpcs_do_wakeup (connection_job_t c);
int tcp_rpcs_init_accepted (connection_job_t c);
int tcp_rpcs_close_connection (connection_job_t c, int who);
int tcp_rpcs_flush (connection_job_t c);
int tcp_rpcs_init_accepted_nohs (connection_job_t c);
// int tcp_rpcs_flush_packet (connection_job_t c); -- use tcp_rpc_flush_packet () instead
int tcp_rpcs_default_check_perm (connection_job_t c);
int tcp_rpcs_init_crypto (connection_job_t c, struct tcp_rpc_nonce_packet *P);
#define TCP_RPCS_FUNC(c) ((struct tcp_rpc_server_functions *) (CONN_INFO(c)->extra))
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#define _FILE_OFFSET_BITS 64
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "net/net-thread.h"
#include "net/net-connections.h"
#include "net/net-msg.h"
#include "net/net-msg-buffers.h"
#include "net/net-tcp-rpc-client.h"
#include "net/net-tcp-rpc-common.h"
#include "net/net-tcp-rpc-server.h"
#include "common/mp-queue.h"
#include "common/kprintf.h"
#include "common/server-functions.h"
#define NEV_TCP_CONN_READY 1
#define NEV_TCP_CONN_CLOSE 2
#define NEV_TCP_CONN_ALARM 3
#define NEV_TCP_CONN_WAKEUP 4
struct notification_event {
int type;
void *who;
};
void run_notification_event (struct notification_event *ev) {
connection_job_t C = ev->who;
switch (ev->type) {
case NEV_TCP_CONN_READY:
if (TCP_RPCC_FUNC(C)->rpc_ready && TCP_RPCC_FUNC(C)->rpc_ready (C) < 0) {
fail_connection (C, -8);
}
job_decref (JOB_REF_PASS (C));
break;
case NEV_TCP_CONN_CLOSE:
TCP_RPCC_FUNC(C)->rpc_close (C, 0);
job_decref (JOB_REF_PASS (C));
break;
case NEV_TCP_CONN_ALARM:
TCP_RPCC_FUNC(C)->rpc_alarm (C);
job_decref (JOB_REF_PASS (C));
break;
case NEV_TCP_CONN_WAKEUP:
TCP_RPCC_FUNC(C)->rpc_wakeup (C);
job_decref (JOB_REF_PASS (C));
break;
default:
assert (0);
}
free (ev);
}
struct notification_event_job_extra {
struct mp_queue *queue;
};
static job_t notification_job;
int notification_event_run (job_t job, int op, struct job_thread *JT) {
if (op != JS_RUN) {
return JOB_ERROR;
}
struct notification_event_job_extra *E = (void *)job->j_custom;
while (1) {
struct notification_event *ev = mpq_pop_nw (E->queue, 4);
if (!ev) { break; }
run_notification_event (ev);
}
return 0;
}
void notification_event_job_create (void) {
notification_job = create_async_job (notification_event_run, JSC_ALLOW (JC_ENGINE, JS_RUN) | JSC_ALLOW (JC_ENGINE, JS_FINISH), 0, sizeof (struct notification_event_job_extra), 0, JOB_REF_NULL);
struct notification_event_job_extra *E = (void *)notification_job->j_custom;
E->queue = alloc_mp_queue_w ();
unlock_job (JOB_REF_CREATE_PASS (notification_job));
}
void notification_event_insert_conn (connection_job_t C, int type) {
struct notification_event *ev = malloc (sizeof (*ev));
ev->who = job_incref (C);
ev->type = type;
struct notification_event_job_extra *E = (void *)notification_job->j_custom;
mpq_push_w (E->queue, ev, 0);
job_signal (JOB_REF_CREATE_PASS (notification_job), JS_RUN);
}
void notification_event_insert_tcp_conn_close (connection_job_t C) {
notification_event_insert_conn (C, NEV_TCP_CONN_CLOSE);
}
void notification_event_insert_tcp_conn_ready (connection_job_t C) {
notification_event_insert_conn (C, NEV_TCP_CONN_READY);
}
void notification_event_insert_tcp_conn_alarm (connection_job_t C) {
notification_event_insert_conn (C, NEV_TCP_CONN_ALARM);
}
void notification_event_insert_tcp_conn_wakeup (connection_job_t C) {
notification_event_insert_conn (C, NEV_TCP_CONN_WAKEUP);
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#include "net/net-msg.h"
#include "net/net-connections.h"
void notification_event_insert_tcp_conn_alarm (connection_job_t C);
void notification_event_insert_tcp_conn_wakeup (connection_job_t C);
void notification_event_insert_tcp_conn_close (connection_job_t C);
void notification_event_insert_tcp_conn_ready (connection_job_t C);
void notification_event_job_create (void);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2009-2013 Vkontakte Ltd
2008-2013 Nikolai Durov
2008-2013 Andrey Lopatin
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#include "net/net-timers.h"
#include "jobs/jobs.h"
#include "common/common-stats.h"
#include "common/kprintf.h"
#include "common/precise-time.h"
/* {{{ STAT */
#define MODULE timers
MODULE_STAT_TYPE {
long long event_timer_insert_ops;
long long event_timer_remove_ops;
long long event_timer_alarms;
int total_timers;
};
MODULE_INIT
MODULE_STAT_FUNCTION
SB_SUM_ONE_LL (event_timer_insert_ops);
SB_SUM_ONE_LL (event_timer_remove_ops);
SB_SUM_ONE_LL (event_timer_alarms);
SB_SUM_ONE_I (total_timers);
MODULE_STAT_FUNCTION_END
/* }}} */
static __thread event_timer_t **et_heap;
__thread int et_heap_size;
static inline int basic_et_adjust (event_timer_t *et, int i) {
int j;
while (i > 1) {
j = (i >> 1);
if (et_heap[j]->wakeup_time <= et->wakeup_time) {
break;
}
et_heap[i] = et_heap[j];
et_heap[i]->h_idx = i;
i = j;
}
j = 2*i;
while (j <= et_heap_size) {
if (j < et_heap_size && et_heap[j]->wakeup_time > et_heap[j+1]->wakeup_time) {
j++;
}
if (et->wakeup_time <= et_heap[j]->wakeup_time) {
break;
}
et_heap[i] = et_heap[j];
et_heap[i]->h_idx = i;
i = j;
j <<= 1;
}
et_heap[i] = et;
et->h_idx = i;
return i;
}
int insert_event_timer (event_timer_t *et) {
if (!et_heap) {
et_heap = calloc (sizeof (void *), MAX_EVENT_TIMERS);
}
MODULE_STAT->event_timer_insert_ops ++;
int i;
if (et->h_idx) {
i = et->h_idx;
assert (i > 0 && i <= et_heap_size && et_heap[i] == et);
} else {
MODULE_STAT->total_timers ++;
assert (et_heap_size < MAX_EVENT_TIMERS);
i = ++et_heap_size;
}
return basic_et_adjust (et, i);
}
int remove_event_timer (event_timer_t *et) {
if (!et_heap) {
et_heap = calloc (sizeof (void *), MAX_EVENT_TIMERS);
}
int i = et->h_idx;
if (!i) {
return 0;
}
MODULE_STAT->total_timers --;
MODULE_STAT->event_timer_remove_ops ++;
assert (i > 0 && i <= et_heap_size && et_heap[i] == et);
et->h_idx = 0;
et = et_heap[et_heap_size--];
if (i > et_heap_size) {
return 1;
}
basic_et_adjust (et, i);
return 1;
}
int thread_run_timers (void) {
if (!et_heap) {
et_heap = calloc (sizeof (void *), MAX_EVENT_TIMERS);
}
double wait_time;
event_timer_t *et;
if (!et_heap_size) {
return 100000;
}
wait_time = et_heap[1]->wakeup_time - precise_now;
if (wait_time > 0) {
//do not remove this useful debug!
vkprintf (3, "%d event timers, next in %.3f seconds\n", et_heap_size, wait_time);
return (int) (wait_time*1000) + 1;
}
while (et_heap_size > 0 && et_heap[1]->wakeup_time <= precise_now) {
et = et_heap[1];
assert (et->h_idx == 1);
remove_event_timer (et);
et->wakeup (et);
MODULE_STAT->event_timer_alarms ++;
}
if (!et_heap_size) {
return 100000;
}
wait_time = et_heap[1]->wakeup_time - precise_now;
if (wait_time > 0) {
//do not remove this useful debug!
vkprintf (3, "%d event timers, next in %.3f seconds\n", et_heap_size, wait_time);
return (int) (wait_time*1000) + 1;
}
assert (0);
return 0;
}
double timers_get_first (void) {
if (!et_heap_size) { return 0; }
return et_heap[1]->wakeup_time;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#pragma once
#define MAX_EVENT_TIMERS (1 << 19)
typedef struct event_timer event_timer_t;
struct event_timer {
int h_idx;
int flags;
int (*wakeup)(event_timer_t *et);
double wakeup_time;
double real_wakeup_time;
};
int thread_run_timers (void);
double timers_get_first (void);
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Vitaliy Valtman
*/
#pragma once
#include <stdio.h>
#include <arpa/inet.h>
#define PID_PRINT_STR "[" IP_PRINT_STR ":%d:%d:%d]"
#define IP_PRINT_STR "%d.%d.%d.%d"
#define PID_TO_PRINT(a) IP_TO_PRINT((a)->ip), (int)(a)->port, (int)(a)->pid, (a)->utime
#define IP_TO_PRINT(a) ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff
#define IPV6_PRINT_STR "%s"
static inline char *IPV6_TO_PRINT(void *ip) {
unsigned short *ipv6 = ip;
static char s[100];
int p = 0;
p += sprintf (s + p, "%x:", htons (ipv6[0]));
if (!ipv6[1]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[1]));
}
if (!ipv6[2]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[2]));
}
if (!ipv6[3]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[3]));
}
if (!ipv6[4]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[4]));
}
if (!ipv6[5]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[5]));
}
if (!ipv6[6]) {
p += sprintf (s + p, ":");
} else {
p += sprintf (s + p, "%x:", htons (ipv6[6]));
}
if (ipv6[7]) {
p += sprintf (s + p, "%x", htons (ipv6[7]));
}
return s;
}
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2015-2016 Telegram Messenger Inc
2015-2016 Vitaly Valtman
*/
#include <assert.h>
long long total_vv_tree_nodes;
#define SUFFIX2(a,b) a ## b
#define SUFFIX(a,b) SUFFIX2(a,b)
#ifndef TREE_NAME
# define TREE_NAME any
#endif
#ifndef Y_TYPE
# define Y_TYPE int
#endif
#ifndef Y_CMP
# define Y_CMP(a,b) ((a) - (b))
#endif
#ifdef TREE_GLOBAL
# define TREE_PREFIX
#else
# define TREE_PREFIX static
#endif
#ifndef TREE_NODE_TYPE
# define TREE_NODE_TYPE struct SUFFIX(tree_, TREE_NAME)
#endif
#ifndef X_TYPE
# define X_TYPE int
#endif
#ifndef X_CMP
# define X_CMP(a,b) ((a) - (b))
#endif
#ifndef TREE_BODY_ONLY
TREE_NODE_TYPE {
TREE_NODE_TYPE *left, *right;
X_TYPE x;
Y_TYPE y;
#ifdef TREE_WEIGHT
int weight;
#endif
#if defined(TREE_COUNT) || defined (TREE_WEIGHT)
int count;
#endif
#ifdef TREE_PTHREAD
int refcnt;
#endif
};
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_alloc_,TREE_NAME) (X_TYPE x, Y_TYPE y) __attribute__ ((unused, warn_unused_result));
TREE_PREFIX void SUFFIX(tree_free_,TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
TREE_PREFIX void SUFFIX(tree_relax_,TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_next_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused));
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) __attribute__ ((unused));
TREE_PREFIX X_TYPE *SUFFIX(tree_lookup_value_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused));
TREE_PREFIX X_TYPE *SUFFIX(tree_lookup_value_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) __attribute__ ((unused));
#else
TREE_PREFIX X_TYPE SUFFIX(tree_lookup_ptr_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused));
#ifdef TREE_PTHREAD
TREE_PREFIX X_TYPE SUFFIX(tree_lookup_sub_ptr_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x) __attribute__ ((unused));
#endif
#endif
TREE_PREFIX void SUFFIX(tree_split_,TREE_NAME) (TREE_NODE_TYPE **L, TREE_NODE_TYPE **R,
TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_insert_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) __attribute__ ((unused, warn_unused_result));
TREE_PREFIX void SUFFIX(tree_insert_sub_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) __attribute__ ((unused));
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_insert_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) __attribute__ ((unused, warn_unused_result));
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_merge_,TREE_NAME) (TREE_NODE_TYPE *L, TREE_NODE_TYPE *R) __attribute__ ((unused, warn_unused_result));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_delete_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) __attribute__ ((unused, warn_unused_result));
TREE_PREFIX void SUFFIX(tree_delete_sub_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x) __attribute__ ((unused));
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_delete_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) __attribute__ ((unused, warn_unused_result));
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_get_min_, TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_get_max_, TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(tree_act_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE)) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(tree_act_ex_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *), void *ex) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(tree_act_ex2_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *, void *), void *ex, void *ex2) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(tree_act_ex3_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *, void *, void *), void *ex, void *ex2, void *ex3) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(tree_check_,TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_clear_, TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
TREE_PREFIX int SUFFIX(tree_count_,TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
#ifdef TREE_PTHREAD
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_clone_, TREE_NAME) (TREE_NODE_TYPE *T) __attribute__ ((unused));
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(get_tree_ptr_, TREE_NAME) (TREE_NODE_TYPE **T) __attribute__ ((unused));
TREE_PREFIX void SUFFIX(free_tree_ptr_, TREE_NAME)(TREE_NODE_TYPE *T) __attribute__ ((unused));
#endif
#endif
#ifndef TREE_HEADER_ONLY
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) {
long long c;
while (T && (c = X_CMP (x, T->x))) {
T = (c < 0) ? T->left : T->right;
}
return T;
}
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_next_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) {
long long c;
TREE_NODE_TYPE *B = 0;
while (T && (c = X_CMP (x, T->x))) {
if (c < 0) { B = T; T = T->left; }
else { T = T->right; }
}
return T ? T : B;
}
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_lookup_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) {
long long c;
while (T && (c = X_CMP ((*x), T->x))) {
T = (c < 0) ? T->left : T->right;
}
return T;
}
TREE_PREFIX X_TYPE *SUFFIX(tree_lookup_value_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) {
long long c;
while (T && (c = X_CMP (x, T->x))) {
T = (c < 0) ? T->left : T->right;
}
return T ? &T->x : NULL;
}
TREE_PREFIX X_TYPE *SUFFIX(tree_lookup_value_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) {
long long c;
while (T && (c = X_CMP ((*x), T->x))) {
T = (c < 0) ? T->left : T->right;
}
return T ? &T->x : NULL;
}
#else
TREE_PREFIX X_TYPE SUFFIX(tree_lookup_ptr_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) {
long long c;
while (T && (c = X_CMP (x, T->x))) {
T = (c < 0) ? T->left : T->right;
}
return T ? T->x : 0;
}
#ifdef TREE_PTHREAD
TREE_PREFIX X_TYPE SUFFIX(tree_lookup_sub_ptr_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x) {
TREE_NODE_TYPE *copy = SUFFIX(get_tree_ptr_,TREE_NAME)(T);
X_TYPE R = SUFFIX(tree_lookup_ptr_,TREE_NAME) (copy, x);
#ifdef TREE_INCREF
if (R) {
TREE_INCREF (R);
}
#endif
SUFFIX(tree_free_,TREE_NAME) (copy);
return R;
}
#endif
#endif
TREE_PREFIX void SUFFIX(tree_split_,TREE_NAME) (TREE_NODE_TYPE **L, TREE_NODE_TYPE **R,
TREE_NODE_TYPE *T, X_TYPE x) {
if (!T) { *L = *R = NULL; return; }
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = X_CMP (x, T->x);
if (c < 0) {
*R = T;
SUFFIX(tree_split_,TREE_NAME) (L, &T->left, T->left, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (*R);
#endif
} else {
*L = T;
SUFFIX(tree_split_,TREE_NAME) (&T->right, R, T->right, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (*L);
#endif
}
}
#ifdef TREE_NOPTR
TREE_PREFIX void SUFFIX(tree_split_p_,TREE_NAME) (TREE_NODE_TYPE **L, TREE_NODE_TYPE **R,
TREE_NODE_TYPE *T, X_TYPE *x) {
if (!T) { *L = *R = NULL; return; }
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = X_CMP ((*x), T->x);
if (c < 0) {
*R = T;
SUFFIX(tree_split_p_,TREE_NAME) (L, &T->left, T->left, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (*R);
#endif
} else {
*L = T;
SUFFIX(tree_split_p_,TREE_NAME) (&T->right, R, T->right, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (*L);
#endif
}
}
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_insert_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) {
TREE_NODE_TYPE *P;
if (!T) {
P = SUFFIX (tree_alloc_, TREE_NAME) (x, y);
#ifdef TREE_WEIGHT
P->weight = weight;
#endif
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (P);
#endif
return P;
}
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = Y_CMP (y, T->y);
if (c < 0) {
c = X_CMP (x, T->x);
assert (c);
if (c < 0) {
T->left = SUFFIX(tree_insert_,TREE_NAME) (T->left, x, y
#ifdef TREE_WEIGHT
,weight
#endif
);
} else {
T->right = SUFFIX(tree_insert_,TREE_NAME) (T->right, x, y
#ifdef TREE_WEIGHT
,weight
#endif
);
}
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (T);
#endif
return T;
}
P = SUFFIX (tree_alloc_, TREE_NAME) (x, y);
#ifdef TREE_WEIGHT
P->weight = weight;
#endif
SUFFIX(tree_split_,TREE_NAME) (&P->left, &P->right, T, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (P);
#endif
return P;
}
TREE_PREFIX void SUFFIX(tree_insert_sub_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) {
#ifdef TREE_PTHREAD
TREE_NODE_TYPE *TT = *T;
if (TT) {
__sync_fetch_and_add (&TT->refcnt, 1);
}
#endif
*T = SUFFIX(tree_insert_,TREE_NAME)(*T, x, y
#ifdef TREE_WEIGHT
, weight
#endif
);
#ifdef TREE_PTHREAD
if (TT) {
mfence ();
SUFFIX(free_tree_ptr_,TREE_NAME)(TT);
}
#endif
}
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_insert_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x, Y_TYPE y
#ifdef TREE_WEIGHT
, int weight
#endif
) {
TREE_NODE_TYPE *P;
if (!T) {
P = SUFFIX (tree_alloc_, TREE_NAME) (*x, y);
#ifdef TREE_WEIGHT
P->weight = weight;
#endif
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (P);
#endif
return P;
}
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = Y_CMP (y, T->y);
if (c < 0) {
c = X_CMP ((*x), T->x);
assert (c);
if (c < 0) {
T->left = SUFFIX(tree_insert_p_,TREE_NAME) (T->left, x, y
#ifdef TREE_WEIGHT
,weight
#endif
);
} else {
T->right = SUFFIX(tree_insert_p_,TREE_NAME) (T->right, x, y
#ifdef TREE_WEIGHT
,weight
#endif
);
}
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (T);
#endif
return T;
}
P = SUFFIX (tree_alloc_, TREE_NAME) (*x, y);
#ifdef TREE_WEIGHT
P->weight = weight;
#endif
SUFFIX(tree_split_p_,TREE_NAME) (&P->left, &P->right, T, x);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (P);
#endif
return P;
}
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_merge_,TREE_NAME) (TREE_NODE_TYPE *L, TREE_NODE_TYPE *R) {
if (!L) { return R; }
if (!R) { return L; }
if (Y_CMP (L->y, R->y) > 0) {
#ifdef TREE_PTHREAD
L = SUFFIX(tree_clone_,TREE_NAME) (L);
#endif
L->right = SUFFIX (tree_merge_,TREE_NAME) (L->right, R);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (L);
#endif
return L;
} else {
#ifdef TREE_PTHREAD
R = SUFFIX(tree_clone_,TREE_NAME) (R);
#endif
R->left = SUFFIX (tree_merge_,TREE_NAME) (L, R->left);
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (R);
#endif
return R;
}
}
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_delete_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE x) {
assert (T);
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = X_CMP (x, T->x);
if (!c) {
TREE_NODE_TYPE *N = SUFFIX(tree_merge_,TREE_NAME) (T->left, T->right);
T->left = T->right = NULL;
SUFFIX(tree_free_,TREE_NAME)(T);
return N;
} else if (c < 0) {
T->left = SUFFIX(tree_delete_,TREE_NAME) (T->left, x);
} else {
T->right = SUFFIX(tree_delete_,TREE_NAME) (T->right, x);
}
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (T);
#endif
return T;
}
TREE_PREFIX void SUFFIX(tree_delete_sub_,TREE_NAME) (TREE_NODE_TYPE **T, X_TYPE x) {
#ifdef TREE_PTHREAD
TREE_NODE_TYPE *TT = *T;
if (TT) {
__sync_fetch_and_add (&TT->refcnt, 1);
}
#endif
*T = SUFFIX(tree_delete_,TREE_NAME)(*T, x);
#ifdef TREE_PTHREAD
if (TT) {
mfence ();
SUFFIX(free_tree_ptr_,TREE_NAME)(TT);
}
#endif
}
#ifdef TREE_NOPTR
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_delete_p_,TREE_NAME) (TREE_NODE_TYPE *T, X_TYPE *x) {
assert (T);
#ifdef TREE_PTHREAD
T = SUFFIX(tree_clone_,TREE_NAME) (T);
#endif
long long c = X_CMP ((*x), T->x);
if (!c) {
TREE_NODE_TYPE *N = SUFFIX(tree_merge_,TREE_NAME) (T->left, T->right);
T->left = T->right = NULL;
SUFFIX(tree_free_,TREE_NAME)(T);
return N;
} else if (c < 0) {
T->left = SUFFIX(tree_delete_p_,TREE_NAME) (T->left, x);
} else {
T->right = SUFFIX(tree_delete_p_,TREE_NAME) (T->right, x);
}
#if defined(TREE_COUNT) || defined(TREE_WEIGHT)
SUFFIX(tree_relax_,TREE_NAME) (T);
#endif
return T;
}
#endif
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_get_min_, TREE_NAME) (TREE_NODE_TYPE *T) {
while (T && T->left) {
T = T->left;
}
return T;
}
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_get_max_, TREE_NAME) (TREE_NODE_TYPE *T) {
while (T && T->right) {
T = T->right;
}
return T;
}
TREE_PREFIX void SUFFIX(tree_act_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE x)) {
if (!T) { return; }
SUFFIX(tree_act_, TREE_NAME)(T->left, act);
act (T->x);
SUFFIX(tree_act_, TREE_NAME)(T->right, act);
}
TREE_PREFIX void SUFFIX(tree_act_ex_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *), void *ex) {
if (!T) { return; }
SUFFIX(tree_act_ex_, TREE_NAME)(T->left, act, ex);
act (T->x, ex);
SUFFIX(tree_act_ex_, TREE_NAME)(T->right, act, ex);
}
TREE_PREFIX void SUFFIX(tree_act_ex2_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *, void *), void *ex, void *ex2) {
if (!T) { return; }
SUFFIX(tree_act_ex2_, TREE_NAME)(T->left, act, ex, ex2);
act (T->x, ex, ex2);
SUFFIX(tree_act_ex2_, TREE_NAME)(T->right, act, ex, ex2);
}
TREE_PREFIX void SUFFIX(tree_act_ex3_, TREE_NAME) (TREE_NODE_TYPE *T, void (*act)(X_TYPE, void *, void *, void *), void *ex, void *ex2, void *ex3) {
if (!T) { return; }
SUFFIX(tree_act_ex3_, TREE_NAME)(T->left, act, ex, ex2, ex3);
act (T->x, ex, ex2, ex3);
SUFFIX(tree_act_ex3_, TREE_NAME)(T->right, act, ex, ex2, ex3);
}
#ifndef TREE_PTHREAD
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_clear_, TREE_NAME) (TREE_NODE_TYPE *T) {
if (!T) {
return 0;
}
SUFFIX(tree_clear_, TREE_NAME) (T->left);
SUFFIX(tree_clear_, TREE_NAME) (T->right);
SUFFIX(tree_free_, TREE_NAME) (T);
return 0;
}
#else
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_clear_, TREE_NAME) (TREE_NODE_TYPE *T) {
if (!T) {
return 0;
}
SUFFIX(tree_free_, TREE_NAME) (T);
return 0;
}
#endif
TREE_PREFIX void SUFFIX(tree_check_,TREE_NAME) (TREE_NODE_TYPE *T) {
if (!T) {
return;
}
if (T->left) { assert (Y_CMP (T->left->y, T->y) <= 0); assert (X_CMP (T->left->x, T->x) < 0); }
if (T->right) { assert (Y_CMP (T->right->y, T->y) <= 0); assert (X_CMP (T->right->x, T->x) > 0); }
SUFFIX (tree_check_, TREE_NAME) (T->left);
SUFFIX (tree_check_, TREE_NAME) (T->right);
}
TREE_PREFIX int SUFFIX(tree_count_,TREE_NAME) (TREE_NODE_TYPE *T) {
if (!T) {
return 0;
}
return 1 + SUFFIX (tree_count_, TREE_NAME) (T->left) + SUFFIX (tree_count_, TREE_NAME) (T->right);
}
TREE_PREFIX TREE_NODE_TYPE *SUFFIX (tree_alloc_, TREE_NAME) (X_TYPE x, Y_TYPE y) {
TREE_NODE_TYPE *T =
#ifndef TREE_MALLOC
zmalloc0 (sizeof (*T));
#else
calloc (sizeof (*T), 1);
#endif
T->x = x;
T->y = y;
#ifdef TREE_PTHREAD
T->refcnt = 1;
#endif
T->left = T->right = NULL;
__sync_fetch_and_add (&total_vv_tree_nodes, 1);
return T;
}
TREE_PREFIX void SUFFIX (tree_free_, TREE_NAME) (TREE_NODE_TYPE *T) {
#ifdef TREE_PTHREAD
if (!T) { return; }
if (__sync_fetch_and_add (&T->refcnt, -1) > 1) {
return;
}
assert (!T->refcnt);
if (T->left) { SUFFIX (tree_free_, TREE_NAME) ( T->left ); }
if (T->right) { SUFFIX (tree_free_, TREE_NAME) ( T->right ); }
#else
assert (T);
#endif
#ifdef TREE_DECREF
TREE_DECREF (T->x);
#endif
#ifndef TREE_MALLOC
zfree (T, sizeof (*T));
#else
free (T);
#endif
__sync_fetch_and_add (&total_vv_tree_nodes, -1);
}
#ifdef TREE_PTHREAD
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(tree_clone_, TREE_NAME) (TREE_NODE_TYPE *T) {
assert (T);
#ifdef TREE_INCREF
TREE_INCREF (T->x);
#endif
TREE_NODE_TYPE *R = SUFFIX (tree_alloc_, TREE_NAME) (T->x, T->y);
assert (R);
if (T->left) {
__sync_fetch_and_add (&T->left->refcnt, 1);
R->left = T->left;
}
if (T->right) {
__sync_fetch_and_add (&T->right->refcnt, 1);
R->right = T->right;
}
SUFFIX (tree_free_, TREE_NAME) (T);
return R;
}
#endif
#ifdef TREE_COUNT
TREE_PREFIX void SUFFIX(tree_relax_,TREE_NAME) (TREE_NODE_TYPE *T) {
T->count = 1 + (T->left ? T->left->count : 0) + (T->right ? T->right->count : 0);
}
#endif
#ifdef TREE_WEIGHT
TREE_PREFIX void SUFFIX(tree_relax_,TREE_NAME) (TREE_NODE_TYPE *T) {
T->count = T->weight + (T->left ? T->left->count : 0) + (T->right ? T->right->count : 0);
}
#endif
#ifdef TREE_PTHREAD
TREE_PREFIX void SUFFIX(incref_tree_ptr_,TREE_NAME) (TREE_NODE_TYPE *T) {
if (T) {
assert (__sync_fetch_and_add (&T->refcnt, 1) > 0);
}
}
TREE_PREFIX TREE_NODE_TYPE *SUFFIX(get_tree_ptr_, TREE_NAME) (TREE_NODE_TYPE **T) {
return get_ptr_multithread_copy ((void **)T, (void *)SUFFIX(incref_tree_ptr_,TREE_NAME));
}
TREE_PREFIX void SUFFIX(free_tree_ptr_, TREE_NAME)(TREE_NODE_TYPE *T) {
if (T && is_hazard_ptr (T, COMMON_HAZARD_PTR_NUM, COMMON_HAZARD_PTR_NUM)) {
struct free_later *F = malloc (sizeof (*F));
F->ptr = T;
F->free = (void *)SUFFIX(free_tree_ptr_, TREE_NAME);
insert_free_later_struct (F);
} else {
SUFFIX(tree_free_, TREE_NAME) (T);
}
}
#endif
#endif
#undef TREE_NAME
#undef Y_TYPE
#undef Y_CMP
#undef TREE_GLOBAL
#undef TREE_NODE_TYPE
#undef X_TYPE
#undef X_CMP
#undef SUFFIX2
#undef SUFFIX
#undef TREE_NOPTR
#undef TREE_GLOBAL
#undef TREE_MALLOC
#undef TREE_COUNT
#undef TREE_WEIGHT
#undef TREE_PREFIX
#undef TREE_INCREF
#undef TREE_DECREF
#undef TREE_HEADER_ONLY
#undef TREE_BODY_ONLY
#undef TREE_PTHREAD
/*
This file is part of Mtproto-proxy Library.
Mtproto-proxy Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Mtproto-proxy Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2012-2013 Vkontakte Ltd
2012-2013 Vitaliy Valtman
Copyright 2014-2016 Telegram Messenger Inc
2014-2016 Vitaly Valtman
*/
struct tree_any_ptr {
struct tree_any_ptr *left, *right;
void *x;
int y;
};
static inline void tree_act_any (struct tree_any_ptr *T, void (*f)(void *)) {
if (!T) { return; }
tree_act_any (T->left, f);
f (T->x);
tree_act_any (T->right, f);
}
static inline void tree_act_any_ex (struct tree_any_ptr *T, void (*f)(void *, void *), void *extra) {
if (!T) { return; }
tree_act_any_ex (T->left, f, extra);
f (T->x, extra);
tree_act_any_ex (T->right, f, extra);
}
static inline void tree_act_any_ex2 (struct tree_any_ptr *T, void (*f)(void *, void *, void *), void *extra, void *extra2) {
if (!T) { return; }
tree_act_any_ex2 (T->left, f, extra, extra2);
f (T->x, extra, extra2);
tree_act_any_ex2 (T->right, f, extra, extra2);
}
static inline void tree_act_any_ex3 (struct tree_any_ptr *T, void (*f)(void *, void *, void *, void *), void *extra, void *extra2, void *extra3) {
if (!T) { return; }
tree_act_any_ex3 (T->left, f, extra, extra2, extra3);
f (T->x, extra, extra2, extra3);
tree_act_any_ex3 (T->right, f, extra, extra2, extra3);
}
#define DEFINE_HASH(prefix,name,value_t,value_compare,value_hash) \
prefix hash_elem_ ## name ## _t *hash_lookup_ ## name (hash_table_ ## name ## _t *T, value_t x) __attribute__ ((unused)); \
prefix void hash_insert_ ## name (hash_table_ ## name ## _t *T, value_t x) __attribute__ ((unused)); \
prefix int hash_delete_ ## name (hash_table_ ## name ## _t *T, value_t x) __attribute__ ((unused)); \
prefix void hash_clear_ ## name (hash_table_ ## name ## _t *T) __attribute__ ((unused)); \
prefix void hash_clear_act_ ## name (hash_table_ ## name ## _t *T, void (*act)(value_t)) __attribute__ ((unused)); \
prefix hash_elem_ ## name ## _t *hash_lookup_ ## name (hash_table_ ## name ## _t *T, value_t x) { \
long long hash = value_hash (x); if (hash < 0) { hash = -hash; } if (hash < 0) { hash = 0;} \
if (T->mask) { hash = hash & T->mask;} \
else { hash %= (T->size);} \
if (!T->E[hash]) { return 0; } \
hash_elem_ ## name ## _t *E = T->E[hash]; \
do { \
if (!value_compare (E->x, x)) { return E; } \
E = E->next; \
} while (E != T->E[hash]); \
return 0; \
} \
\
prefix void hash_insert_ ## name (hash_table_ ## name ## _t *T, value_t x) { \
long long hash = value_hash (x); if (hash < 0) { hash = -hash; } if (hash < 0) { hash = 0;} \
if (T->mask) { hash = hash & T->mask;} \
else { hash %= (T->size);} \
hash_elem_ ## name ## _t *E = hash_alloc_ ## name (x); \
if (T->E[hash]) { \
E->next = T->E[hash]; \
E->prev = T->E[hash]->prev; \
E->next->prev = E; \
barrier (); \
E->prev->next = E; \
} else { \
E->next = E; \
E->prev = E; \
barrier (); \
T->E[hash] = E; \
} \
} \
\
prefix int hash_delete_ ## name (hash_table_ ## name ## _t *T, value_t x) { \
long long hash = value_hash (x); if (hash < 0) { hash = -hash; } if (hash < 0) { hash = 0;} \
if (T->mask) { hash = hash & T->mask;} \
else { hash %= (T->size);} \
if (!T->E[hash]) { return 0; } \
hash_elem_ ## name ## _t *E = T->E[hash]; \
int ok = 0; \
do { \
if (!value_compare (E->x, x)) { ok = 1; break; } \
E = E->next; \
} while (E != T->E[hash]); \
if (!ok) { return 0; } \
E->next->prev = E->prev; \
E->prev->next = E->next; \
if (T->E[hash] != E) { \
hash_free_ ## name (E); \
} else if (E->next == E) { \
T->E[hash] = 0; \
hash_free_ ## name (E); \
} else { \
T->E[hash] = E->next; \
hash_free_ ## name (E); \
} \
return 1; \
} \
\
prefix void hash_clear_ ## name (hash_table_ ## name ## _t *T) { \
int i; \
for (i = 0; i < T->size; i++) { \
if (T->E[i]) { \
hash_elem_ ## name ## _t *cur = T->E[i]; \
hash_elem_ ## name ## _t *first = cur; \
do { \
void *next = cur->next; \
hash_free_ ## name (cur); \
cur = next; \
} while (cur != first); \
T->E[i] = 0; \
} \
} \
} \
\
prefix void hash_clear_act_ ## name (hash_table_ ## name ## _t *T, void (*act)(value_t)) { \
int i; \
for (i = 0; i < T->size; i++) { \
if (T->E[i]) { \
hash_elem_ ## name ## _t *cur = T->E[i]; \
hash_elem_ ## name ## _t *first = cur; \
do { \
void *next = cur->next; \
act (cur->x); \
hash_free_ ## name (cur); \
cur = next; \
} while (cur != first); \
T->E[i] = 0; \
} \
} \
} \
#define DEFINE_HASH_STD_ALLOC_PREFIX(prefix,name,value_t,value_compare,value_hash)\
DECLARE_HASH_TYPE(name,value_t) \
prefix hash_elem_ ## name ## _t *hash_alloc_ ## name (value_t x); \
prefix void hash_free_ ## name (hash_elem_ ## name ## _t *T); \
DEFINE_HASH(prefix,name,value_t,value_compare,value_hash); \
hash_elem_ ## name ## _t *hash_alloc_ ## name (value_t x) { \
hash_elem_ ## name ## _t *E = zmalloc (sizeof (*E)); \
E->x = x; \
return E; \
} \
void hash_free_ ## name (hash_elem_ ## name ## _t *E) { \
zfree (E, sizeof (*E)); \
} \
#define DEFINE_HASH_STDNOZ_ALLOC_PREFIX(prefix,name,value_t,value_compare,value_hash)\
DECLARE_HASH_TYPE(name,value_t) \
prefix hash_elem_ ## name ## _t *hash_alloc_ ## name (value_t x); \
prefix void hash_free_ ## name (hash_elem_ ## name ## _t *T); \
DEFINE_HASH(prefix,name,value_t,value_compare,value_hash); \
hash_elem_ ## name ## _t *hash_alloc_ ## name (value_t x) { \
hash_elem_ ## name ## _t *E = malloc (sizeof (*E)); \
E->x = x; \
return E; \
} \
void hash_free_ ## name (hash_elem_ ## name ## _t *E) { \
free (E); \
} \
#define DEFINE_HASH_STD_ALLOC(name,value_t,value_compare,value_hash) \
DEFINE_HASH_STD_ALLOC_PREFIX(static,name,value_t,value_compare,value_hash)
#define DEFINE_HASH_STDNOZ_ALLOC(name,value_t,value_compare,value_hash) \
DEFINE_HASH_STDNOZ_ALLOC_PREFIX(static,name,value_t,value_compare,value_hash)
#define DECLARE_HASH_TYPE(name,value_t) \
struct hash_elem_ ## name { \
struct hash_elem_ ## name *next, *prev;\
value_t x;\
}; \
struct hash_table_ ## name {\
struct hash_elem_ ## name **E; \
int size; \
int mask; \
}; \
typedef struct hash_elem_ ## name hash_elem_ ## name ## _t; \
typedef struct hash_table_ ## name hash_table_ ## name ## _t; \
#define HASH_DEG2(name,deg) \
static struct hash_elem_ ## name *_hash_arr_ ## name[(1 << (deg))]; \
static struct hash_table_ ## name hash_table_ ## name ## _ptr = { \
.E = _hash_arr_ ## name, \
.size = (1 << (deg)), \
.mask = (1 << (deg)) - 1 \
}; \
static struct hash_table_ ## name *hash_table_ ## name = & hash_table_ ## name ## _ptr;
#define std_int_compare(a,b) ((a) - (b))
#define std_ll_ptr_compare(a,b) ((*(long long *)(a)) - (*(long long *)(b)))
#define std_int_hash(x) ((x) >= 0 ? (x) : -(x) >= 0 ? -(x) : 0)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment