Commit a64f3584 authored by Ahmad Nemati's avatar Ahmad Nemati

init

parents
Pipeline #10 failed with stages
# Default ignored files
/workspace.xml
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" />
</settings>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mt.iml" filepath="$PROJECT_DIR$/.idea/mt.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
/*
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")
ARCH =
ifeq ($m, 32)
ARCH = -m32
endif
ifeq ($m, 64)
ARCH = -m64
endif
CFLAGS = $(ARCH) -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 = $(ARCH) -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
# MTProxy
Simple MT-Proto proxy
## Building
Install dependencies, you would need common set of tools for building from source, and development packages for `openssl` and `zlib`.
On Debian/Ubuntu:
```bash
apt install git curl build-essential libssl-dev zlib1g-dev
```
On CentOS/RHEL:
```bash
yum install openssl-devel zlib-devel
yum groupinstall "Development Tools"
```
Clone the repo:
```bash
git clone https://github.com/TelegramMessenger/MTProxy
cd MTProxy
```
To build, simply run `make`, the binary will be in `objs/bin/mtproto-proxy`:
```bash
make && cd objs/bin
```
If the build has failed, you should run `make clean` before building it again.
## Running
1. Obtain a secret, used to connect to telegram servers.
```bash
curl -s https://core.telegram.org/getProxySecret -o proxy-secret
```
2. Obtain current telegram configuration. It can change (occasionally), so we encourage you to update it once per day.
```bash
curl -s https://core.telegram.org/getProxyConfig -o proxy-multi.conf
```
3. Generate a secret to be used by users to connect to your proxy.
```bash
head -c 16 /dev/urandom | xxd -ps
```
4. Run `mtproto-proxy`:
```bash
./mtproto-proxy -u nobody -p 8888 -H 443 -S <secret> --aes-pwd proxy-secret proxy-multi.conf -M 1
```
... where:
- `nobody` is the username. `mtproto-proxy` calls `setuid()` to drop privilegies.
- `443` is the port, used by clients to connect to the proxy.
- `8888` is the local port. You can use it to get statistics from `mtproto-proxy`. Like `wget localhost:8888/stats`. You can only get this stat via loopback.
- `<secret>` is the secret generated at step 3. Also you can set multiple secrets: `-S <secret1> -S <secret2>`.
- `proxy-secret` and `proxy-multi.conf` are obtained at steps 1 and 2.
- `1` is the number of workers. You can increase the number of workers, if you have a powerful server.
Also feel free to check out other options using `mtproto-proxy --help`.
5. Generate the link with following schema: `tg://proxy?server=SERVER_NAME&port=PORT&secret=SECRET` (or let the official bot generate it for you).
6. Register your proxy with [@MTProxybot](https://t.me/MTProxybot) on Telegram.
7. Set received tag with arguments: `-P <proxy tag>`
8. Enjoy.
## Random padding
Due to some ISPs detecting MTProxy by packet sizes, random padding is
added to packets if such mode is enabled.
It's only enabled for clients which request it.
Add `dd` prefix to secret (`cafe...babe` => `ddcafe...babe`) to enable
this mode on client side.
## Systemd example configuration
1. Create systemd service file (it's standard path for the most Linux distros, but you should check it before):
```bash
nano /etc/systemd/system/MTProxy.service
```
2. Edit this basic service (especially paths and params):
```bash
[Unit]
Description=MTProxy
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/MTProxy
ExecStart=/opt/MTProxy/mtproto-proxy -u nobody -p 8888 -H 443 -S <secret> -P <proxy tag> <other params>
Restart=on-failure
[Install]
WantedBy=multi-user.target
```
3. Reload daemons:
```bash
systemctl daemon-reload
```
4. Test fresh MTProxy service:
```bash
systemctl restart MTProxy.service
# Check status, it should be active
systemctl status MTProxy.service
```
5. Enable it, to autostart service after reboot:
```bash
systemctl enable MTProxy.service
```
## Docker image
Telegram is also providing [official Docker image](https://hub.docker.com/r/telegrammessenger/proxy/).
Note: the image is outdated.
/*
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>
#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;
}
unsigned int a;
assert(
__get_cpuid(1,
&a,
(unsigned int*) &cached.ebx,
(unsigned int*) &cached.ecx,
(unsigned int*) &cached.edx
) != 0
);
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"
#define FASTMOV_RMI32_TO_SSE(dst, src) \
asm volatile ("movd %1, %0\n\t" : "=x" (dst) : "g" (src))
#ifdef __LP64__
#define FASTMOV_SSE_TO_LO_HI_DW(sse, lo, hi)\
do {\
uint64_t T; \
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (sse)); \
lo = (uint32_t) T; \
hi = (uint32_t) (T >> 32); \
} while(0)
#else
#define FASTMOV_SSE_TO_LO_HI_DW(sse, lo, hi)\
do {\
asm volatile ("movd %1, %0\n\t" : "=r" (lo) : "x" (sse)); \
sse = __builtin_ia32_psrldqi128(sse, 32); \
asm volatile ("movd %1, %0\n\t" : "=r" (hi) : "x" (sse)); \
} while(0)
#endif
#ifdef __LP64__
#define RETURN_SSE_UINT64(sse)\
do {\
uint64_t T; \
asm volatile ("movq %1, %0\n\t" : "=r" (T) : "x" (sse)); \
return T; \
} while(0)
// RMI == reg, mem, imm
#define FASTMOV_RMI64_TO_SSE(dst, src) \
asm volatile ("movq %1, %0\n\t" : "=x" (dst) : "g" (src))
#else
#define RETURN_SSE_UINT64(sse)\
do {\
uint32_t lo, hi;\
FASTMOV_SSE_TO_LO_HI_DW(sse, lo, hi);\
uint64_t T; \
T = (((uint64_t)hi)<<32) | lo; \
return T; \
} while(0)
#define FASTMOV_RMI64_TO_SSE(dst, src) \
do { \
uint64_t T = src; \
asm volatile ("movsd %1, %0\n\t" : "=x" (dst) : "m" (T)); \
} while(0)
#endif
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;
FASTMOV_RMI32_TO_SSE(C, 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);
unsigned lo, hi;
#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);
FASTMOV_SSE_TO_LO_HI_DW(D, lo, hi);
return hi;
#else
D = __builtin_ia32_punpckhqdq128 (D, D);
FASTMOV_SSE_TO_LO_HI_DW(D, lo, hi);
return crc32_table0[lo & 0xff] ^ crc32_table1[(lo & 0xff00) >> 8] ^ crc32_table2[(lo & 0xff0000) >> 16] ^ crc32_table[lo >> 24] ^ ((unsigned) hi);
#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;
RETURN_SSE_UINT64(D);
}
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;
FASTMOV_RMI64_TO_SSE(C, 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;
FASTMOV_RMI32_TO_SSE(D, 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);
RETURN_SSE_UINT64(D);
}
/* }}} */
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;
FASTMOV_RMI64_TO_SSE(D, 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,
};
#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
#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;
}
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>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
#endif
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>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
#endif
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;
if (engine_check_tcp_enabled ()) {
struct in_addr l;
l.s_addr = htonl(0x7f000001);
E->sfd = server_socket (port, l, engine_get_backlog (), 0);
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"
#include "engine/engine-net.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) {
if ((lrand48_j() & 1) || !(TCP_RPC_DATA(C)->flags & RPC_F_PAD)) {
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);
} else {
int x = -1;
struct raw_message m;
assert (rwm_create (&m, &x, 4) == 4);
assert (rwm_push_data (&m, &confirm, 4) == 4);
int z = lrand48_j() & 1;
while (z-- > 0) {
int t = lrand48_j();
assert (rwm_push_data (&m, &t, 4) == 4);
}
tcp_rpc_conn_send (JOB_REF_CREATE_PASS (C), &m, 0);
x = 0;
assert (rwm_create (&m, &x, 4) == 4);
z = lrand48_j() & 1;
while (z-- > 0) {
int t = lrand48_j();
assert (rwm_push_data (&m, &t, 4) == 4);
}
tcp_rpc_conn_send (JOB_REF_PASS (C), &m, 0);
}
}
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 (inner_len < 20) {
//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);
}
server_functions_t mtproto_front_functions;
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 2000:
engine_set_http_fallback (&ct_http_server, &http_methods_stats);
mtproto_front_functions.flags &= ~ENGINE_NO_PORT;
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 ("http-stats", no_argument, 0, 2000, "allow http server to answer on stats queries");
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,
.flags = ENGINE_NO_PORT
//.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;
if (!PID.ip) {
init_client_PID (c->our_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);
}
if (TCP_RPC_DATA(C)->flags & RPC_F_PAD) {
int x = lrand48_j();
int y = lrand48_j() & 3;
assert (rwm_push_data (raw, &x, y) == y);
}
int len = raw->total_bytes;
assert (!(len & 0xfc000000));
if (!(TCP_RPC_DATA(C)->flags & RPC_F_PAD)) {
assert (!(len & 3));
}
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_PAD 0x8000000
#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;
}
if (packet_len == 0xdddddddd) {
D->flags |= RPC_F_MEDIUM | RPC_F_PAD;
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 == 0xdddddddd || 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 0xdddddddd:
D->flags |= RPC_F_MEDIUM | RPC_F_EXTMODE2 | RPC_F_PAD;
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 & 0xc0000000) || (!(D->flags & RPC_F_PAD) && (packet_len & 3))) {
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);
if (D->flags & RPC_F_PAD) {
rwm_trunc (&msg, packet_len & -4);
}
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.ip);
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