summary refs log tree commit diff
diff options
context:
space:
mode:
authorNakidai <nakidai@disroot.org>2025-07-09 18:17:21 +0300
committerNakidai <nakidai@disroot.org>2025-07-09 18:17:21 +0300
commit7c625e57d8bf86196fa434fa2343c50582cdbc9e (patch)
tree1afc167d41155eaae2a2c1544b8dc528bc275530
downloadfarsh-7c625e57d8bf86196fa434fa2343c50582cdbc9e.tar.gz
farsh-7c625e57d8bf86196fa434fa2343c50582cdbc9e.zip
Add code HEAD v1.0.0 master
-rw-r--r--Makefile3
-rw-r--r--README_RU96
-rw-r--r--common.h83
-rw-r--r--config-common.h10
-rw-r--r--config-farsh.h8
-rw-r--r--config-farshd.h14
-rw-r--r--crypto-sponge.h276
-rw-r--r--farsh.c353
-rw-r--r--farshd.c420
-rw-r--r--shorttypes.h36
10 files changed, 1299 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9347fa2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+all: farsh farshd
+clean:
+	rm -rf farsh farshd
diff --git a/README_RU b/README_RU
new file mode 100644
index 0000000..f82d001
--- /dev/null
+++ b/README_RU
@@ -0,0 +1,96 @@
+FAt Remoute SHell
+
+Перед сборкой убедиться, что в config-farshd.h выставлен нужный шелл.
+Выставить переменные окружения CC, CFLAGS и т.п. и собрать:
+
+	export CFLAGS="-O2"
+	make farsh
+	make farshd
+
+Ну или вручную:
+
+	cc -O2 -o farsh farsh.c
+	cc -O2 -o farshd farshd.c
+
+
+
+Приблизительный алгоритм работы программы.
+
+Максимальный размер пакета задаётся MAX_PKT_SZ, это нужно чтоб не возиться с
+Path MTU Discovery. Интервал отправки пакетов задаётся в PKT_INT. Эти
+значения ограничивают макс. пропускную способность. Значение PKT_INT на
+клиенте рекоммендуется выставить меньше чем на сервере.
+
+Бесконечный цикл на клиенте:
+
+1) Если есть данные с терминала, то пишем их в кольцевой буфер
+2) Если пришёл пакет, то читаем из него данные и пишем их в stdout
+3) Если прошло PKT_INT микросекунд или около того (PKT_INT - MIN_SLEEP), то
+   пора сформировать пакет:
+    1) если соединение не установлено, то выставляем в пакете бит CONNECT и
+       добавляем в него значение перемнной окружения TERM.
+    2) Получаем размеры терминала, если они отличаются от тех, что мы раньше
+       отправляли на сервер, то выставляем в пакете бит PING и добавляем
+       туда новые размеры терминала.
+    3) Если в кольцевом буфере есть данные, то добавляем их в пакет, а также
+       добавляем состояния потоков: количество принятых от сервера байт
+       данных (не просто байт а именно тех что пишем в терминал) и
+       количество байт данных которое сервер получил от клиента (клиент
+       получает эти данные с сервера). Выставляем бит SSTATE в пакете.
+    4) Если данных нет, но состояния потоков разные на сервере и клиенте, то
+       выставляем бит SSTATE в пакете и добавляем состояния потоков в пакет.
+    5) Если получился не пустой пакет, то отправляем его. Если с момента
+       последнего полученного от сервера пакета прошло TIMEOUT_P секунд, то
+       добавляем в пакет PING, размеры терминала и шлём.
+
+Клиент перестаёт слать пакеты с битом CONNECT после получения пакета от
+сервера с таким битом.
+
+Бесконечный цикл на сервере:
+
+1) Если что-то есть в клиентских псевдотерминалах, то читаем это в
+   клиентские колцевые буферы.
+2) Если есть пакет от клиента, то пишем данные из пакета в клиентский
+   псевдотерминал. Обновляем состояния потоков. Обновляем размеры терминала,
+   если они есть в паете.
+3) Прошло PKT_INT микросекунд или около того. Пробегаем по всем клиентам и
+   формируем пакет если:
+     а) был пакет от клиента с битом PING
+     б) был пакет от клиента с битом SSTATE
+     в) в кольцевом буфере клиента есть данные.
+
+После установки соединения сервер шлёт пакеты с битом CONNECT пока не
+получит от клиента подряд NOT_CONNECTED пакетов без бита CONNECT. После
+этого сервер перстаёт добавлять CONNECT бит в пакет и если получит пакет с
+этим битом от клиента, то отключит его. NOT_CONNECTED пакетов подряд нужно
+на случай перестановки пакетов (хоть это и маловероятно из-за большого
+интервала между пакетами).
+
+
+
+Формат пакета:
+
+Первые два байта это u16 переменная, которая может содержать флаги:
+CONNECT, PING, SSTATE.
+
+Вторые 2 байта, это u16 переменная, в которой будет размер данных в пакете
+(может быть 0).
+
+Флаг CONNECT озанчает что сразу за первыми 4 байтами пакета идёт буфер
+размером CONNECT байт с содержимым клиентской переменной TERM (если пакет от
+сервера, то ничего не добавляется).
+
+Флаг PING означает что сразу за первыми 4 байтами (или за первыми 4+CONNECT
+байтами) идут две u16 переменных, в которых содержатся размеры терминала.
+Сервер должен ответить на этот пакет, и он шлёт те значения которые получил
+последние.
+
+Флаг SSTATE означает что в пакет добавлены состояния потоков: две u32
+переменные.
+
+Значения флагов выбраны таким образом что образуемая ими u16 переменная
+содержит количество данных в пакете помимо данных для терминала (кроме
+случая, когда сервер шлёт пакет с битом CONNECT). Так добавив к этой
+переменной 4 (первые байты) можно получить смещение данных в пакете.
+
+
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..2059d81
--- /dev/null
+++ b/common.h
@@ -0,0 +1,83 @@
+/* common (not config, need code patch after change) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <time.h>
+
+#include "shorttypes.h"
+#include "crypto-sponge.h"
+
+#define ERRDIE(a, b) if ((a) == -1) perror((b)), exit(1)
+
+#define NONCE_SZ 16
+#define HMAC_SZ  16
+
+#define MIN_PKT_SZ NONCE_SZ + HMAC_SZ + 4 + 4 + 8 + 1
+#if MAX_PKT_SZ < MIN_PKT_SZ
+#error "MAX_PKT_SZ < MIN_PKT_SZ"
+#endif
+
+#define CONNECT (u16)32
+#define PING    (u16)4
+#define SSTATE  (u16)8
+
+u8  key[32];
+u8  password[PASSWORD_BUF_SZ];
+u32 prng_state[14];
+
+
+static void send_pkt
+(s32 skt, void *buf, u16 sz, struct sockaddr_storage *addr){
+	ssize_t res;
+	u8 *nonce, *tag;
+
+	/* nonce, tag */
+	nonce = (u8*)buf + sz;
+	tag = nonce + NONCE_SZ;
+	duplex257_prng_rand16(prng_state, nonce);
+
+	/* encrypt */
+	duplex257_ae_encrypt(key, nonce, tag, buf, sz);
+	sz += NONCE_SZ + HMAC_SZ;
+
+	/* send */
+	res = sendto(skt, buf, sz, 0,(struct sockaddr*)addr, sizeof(*addr));
+	ERRDIE(res, "sendto");
+	return;
+}
+
+static s32 recv_pkt
+(s32 skt, void *buf, u16 sz, struct sockaddr_storage *addr){
+	socklen_t skl;
+	s32 res;
+	u8 *nonce, *rtag;
+	u8 tag[HMAC_SZ];
+
+	/* recv */
+	skl = sizeof(*addr);
+	res = recvfrom(skt, buf, sz, 0, (struct sockaddr*)addr, &skl);
+	ERRDIE(res, "recvfrom");
+
+	/* drop packet if it too small */
+	if (res < 4 + NONCE_SZ + HMAC_SZ) return -1;
+
+	/* nonce, recieved tag */
+	rtag = buf + res - HMAC_SZ;
+	nonce  = rtag - NONCE_SZ;
+
+	/* decrypt */
+	res -= NONCE_SZ + HMAC_SZ;
+	duplex257_ae_decrypt(key, nonce, tag, buf, res);
+	if (0 != memcmp(tag, rtag, HMAC_SZ)) return -1;
+
+	return res;
+}
+
diff --git a/config-common.h b/config-common.h
new file mode 100644
index 0000000..64c1840
--- /dev/null
+++ b/config-common.h
@@ -0,0 +1,10 @@
+/* common config */
+
+#define PASSWORD_BUF_SZ 512 /* 256 russian utf8 chars */
+#define MAX_PKT_SZ 1024
+#define TIMEOUT_D  180 /* timeout (no packets) in seconds for disconnect */
+
+/* send packet every X microseconds, where
+ * PKT_INT  >=  X  >=  PKT_INT - MIN_SLEEP */
+#define MIN_SLEEP 3000
+
diff --git a/config-farsh.h b/config-farsh.h
new file mode 100644
index 0000000..4465b31
--- /dev/null
+++ b/config-farsh.h
@@ -0,0 +1,8 @@
+/* client config */
+
+#define DISCONNECT_KEY '~' /* press ESC and this key to disconnect */
+#define PKT_INT 24000u     /* packet sending interval in microseconds */
+#define RB_SZ 0x1000u /* ring buf size, 4kb, must be power of 2, 64kb max */
+#define TIMEOUT_P 10  /* timeout (no packets) in seconds for send ping */
+#define TIMEOUT_C 5   /* connection timeout in seconds */
+
diff --git a/config-farshd.h b/config-farshd.h
new file mode 100644
index 0000000..3a6e48e
--- /dev/null
+++ b/config-farshd.h
@@ -0,0 +1,14 @@
+/* server config */
+
+#define SHELL   "/bin/bash"
+#define LOGFILE "/tmp/farshd.log"
+#define MAX_PEERS 16
+#define PKT_INT 32000u /* packet sending interval in microseconds */
+
+/* ring buf size, 32kb, must be power of 2, 64kb max */
+#define RB_SZ 0x8000u
+
+/* the client is not fully connected until the server receives NOT_CONNECTED
+ * packets without the CONNECT flag */
+#define NOT_CONNECTED 8
+
diff --git a/crypto-sponge.h b/crypto-sponge.h
new file mode 100644
index 0000000..39cda11
--- /dev/null
+++ b/crypto-sponge.h
@@ -0,0 +1,276 @@
+#ifndef SPONGE_CRYPTO_H
+#define SPONGE_CRYPTO_H
+
+#include <arpa/inet.h>
+#include <string.h>
+#include "shorttypes.h"
+
+#define ROTL32(x, n) (((x)<<(n)) | ((x)>>(32-(n))))
+
+static void gimli(void *s){
+	u32 round, column;
+	u32 x, y, z;
+	u32 *state = s;
+
+	state[ 0] = ntohl(state[ 0]);
+	state[ 1] = ntohl(state[ 1]);
+	state[ 2] = ntohl(state[ 2]);
+	state[ 3] = ntohl(state[ 3]);
+	state[ 4] = ntohl(state[ 4]);
+	state[ 5] = ntohl(state[ 5]);
+	state[ 6] = ntohl(state[ 6]);
+	state[ 7] = ntohl(state[ 7]);
+	state[ 8] = ntohl(state[ 8]);
+	state[ 9] = ntohl(state[ 9]);
+	state[10] = ntohl(state[10]);
+	state[11] = ntohl(state[11]);
+
+	for (round = 24; round != 0; --round){
+		for (column = 0; column < 4; ++column){
+			x = ROTL32(state[    column], 24);
+			y = ROTL32(state[4 + column],  9);
+			z =        state[8 + column];
+
+			state[8 + column] = x ^ (z << 1) ^ ((y&z) << 2);
+			state[4 + column] = y ^ x        ^ ((x|z) << 1);
+			state[    column] = z ^ y        ^ ((x&y) << 3);
+		}
+
+		if ((round & 3) == 0) { // small swap: pattern s...s...s...
+			x = state[0];
+			state[0] = state[1];
+			state[1] = x;
+			x = state[2];
+			state[2] = state[3];
+			state[3] = x;
+		}
+
+		if ((round & 3) == 2) { // big swap: ..S...S...S. etc.
+			x = state[0];
+			state[0] = state[2];
+			state[2] = x;
+			x = state[1];
+			state[1] = state[3];
+			state[3] = x;
+		}
+
+		if ((round & 3) == 0) { // add constant: c...c...c... etc.
+			state[0] ^= (0x9e377900 | round);
+		}
+	}
+
+	state[ 0] = htonl(state[ 0]);
+	state[ 1] = htonl(state[ 1]);
+	state[ 2] = htonl(state[ 2]);
+	state[ 3] = htonl(state[ 3]);
+	state[ 4] = htonl(state[ 4]);
+	state[ 5] = htonl(state[ 5]);
+	state[ 6] = htonl(state[ 6]);
+	state[ 7] = htonl(state[ 7]);
+	state[ 8] = htonl(state[ 8]);
+	state[ 9] = htonl(state[ 9]);
+	state[10] = htonl(state[10]);
+	state[11] = htonl(state[11]);
+
+}
+
+#define FPERMUTE gimli
+
+#define MEMXOR16(dst, src) \
+	((u32*)dst)[0] ^= ((u32*)src)[0]; \
+	((u32*)dst)[1] ^= ((u32*)src)[1]; \
+	((u32*)dst)[2] ^= ((u32*)src)[2]; \
+	((u32*)dst)[3] ^= ((u32*)src)[3]
+
+#define MEMXOR32(dst, src) \
+	((u32*)dst)[0] ^= ((u32*)src)[0]; \
+	((u32*)dst)[1] ^= ((u32*)src)[1]; \
+	((u32*)dst)[2] ^= ((u32*)src)[2]; \
+	((u32*)dst)[3] ^= ((u32*)src)[3]; \
+	((u32*)dst)[4] ^= ((u32*)src)[4]; \
+	((u32*)dst)[5] ^= ((u32*)src)[5]; \
+	((u32*)dst)[6] ^= ((u32*)src)[6]; \
+	((u32*)dst)[7] ^= ((u32*)src)[7]
+
+
+/* reseedable prng
+ * state[12]: input buffer index
+ * state[13]: output buffer index
+ */
+
+static void duplex257_prng_feed(u32 state[14], u32 food){
+	state[13] = 0;
+	state[state[12]] ^= food;
+	state[12]++;
+	if (state[12] == 8){
+		((u8*)state)[32] ^= 0x80;
+		state[12] = 0;
+		FPERMUTE(state);
+	}
+	return;
+}
+
+static u32 duplex257_prng_rand(u32 state[14]){
+	u32 res;
+	if (state[12]){
+		((u8*)state)[state[12]] ^= 0x80;
+		FPERMUTE(state);
+		state[12] = 0;
+		state[13] = 1;
+		return state[0];
+	}
+
+	res = state[state[13]];
+	state[13]++;
+	if (state[13] == 8){
+		state[13] = 0;
+		((u8*)state)[0] ^= 0x80;
+		FPERMUTE(state);
+	}
+
+	return res;
+}
+
+static void duplex257_prng_rand16(u32 state[14], u8 dst[16]){
+	if (state[12]){
+		((u8*)state)[state[12]] ^= 0x80;
+		FPERMUTE(state);
+		state[12] = 0;
+		state[13] = 4;
+		memcpy(dst, state, 16);
+		return;
+	}
+
+	if (state[13] == 0){
+		state[13] = 4;
+		memcpy(dst, state, 16);
+	} else if (state[13] < 5){
+		state[13] = 0;
+		memcpy(dst, &state[4], 16);
+		((u8*)state)[0] ^= 0x80;
+		FPERMUTE(state);
+	} else {
+		((u8*)state)[0] ^= 0x80;
+		FPERMUTE(state);
+		state[13] = 4;
+		memcpy(dst, state, 16);
+	}
+	return;
+}
+
+
+/* duplex257 AE */
+
+static void duplex257_ae_encrypt
+    (const u8 key[32], const u8 nonce[16], u8 tag[16], u8 *src_dst, u32 sz){
+	static u8 state[48];
+	u32 i, n;
+
+	/* duplex_mute(key) */
+	memcpy(state, key, 32);
+	state[32] = 0x80;
+	FPERMUTE(state);
+
+	/* duplex_mute(nonce) */
+	MEMXOR16(state, nonce);
+	state[16] ^= 0x80;
+	FPERMUTE(state);
+
+	/* 32 bytes steps */
+	n = sz >> 5;
+	while (n){
+		/* duplex(src_dst), encrypt */
+		MEMXOR32(src_dst, state);
+		memcpy(state, src_dst, 32);
+		state[32] ^= 0x80;
+		FPERMUTE(state);
+		src_dst += 32;
+		n--;
+	}
+
+	/* 1 byte steps */
+	n = sz & 0x1Fu;
+	/* duplex(src_dst), encrypt */
+	for (i = 0; i < n; i++) src_dst[i] ^= state[i];
+	memcpy(state, src_dst, n);
+	state[n] ^= 0x80;
+	FPERMUTE(state);
+
+	memcpy(tag, state, 16);
+	memset(state, 0, sizeof(state));
+	return;
+}
+
+static void duplex257_ae_decrypt
+    (const u8 key[32], const u8 nonce[16], u8 tag[16], u8 *src_dst, u32 sz){
+	static u8 state[48];
+	u32 i, n;
+
+	/* duplex_mute(key) */
+	memcpy(state, key, 32);
+	state[32] = 0x80;
+	FPERMUTE(state);
+
+	/* duplex_mute(nonce) */
+	MEMXOR16(state, nonce);
+	state[16] ^= 0x80;
+	FPERMUTE(state);
+
+	/* 32 bytes steps */
+	n = sz >> 5;
+	while (n){
+		/* duplex(src_dst), encrypt */
+		MEMXOR32(src_dst, state);
+		MEMXOR32(state, src_dst);
+		state[32] ^= 0x80;
+		FPERMUTE(state);
+		src_dst += 32;
+		n--;
+	}
+
+	/* 1 byte steps */
+	n = sz & 0x1Fu;
+	/* duplex(src_dst), encrypt */
+	for (i = 0; i < n; i++){
+		src_dst[i] ^= state[i];
+		state[i] ^= src_dst[i];
+	}
+	state[n] ^= 0x80;
+	FPERMUTE(state);
+
+	memcpy(tag, state, 16);
+	memset(state, 0, sizeof(state));
+	return;
+}
+
+
+/* sponge */
+static void sponge256_absorb(u8 state[48], const u8 *src, u32 sz){
+	u32 n, i;
+
+	n = sz >> 5;
+	while (n){ /* 32 byte steps */
+		MEMXOR32(state, src);
+		FPERMUTE(state);
+		src += 32;
+		n--;
+	}
+
+	/* 1 byte steps */
+	n = sz & 0x1Fu;
+	for (i=0; i < n; i++) state[i] ^= src[i];
+	state[i] ^= 0x80;
+	FPERMUTE(state);
+	return;
+}
+
+/* sponge hash */
+static void sponge256_hash32(u8 dst[32], u8 *src, u32 sz){
+	static u8 state[48];
+	sponge256_absorb(state, src, sz);
+	memcpy(dst, state, 32);
+	memset(state, 0, sizeof(state));
+	return;
+}
+
+#endif /* SPONGE_CRYPTO_H */
diff --git a/farsh.c b/farsh.c
new file mode 100644
index 0000000..cd3d085
--- /dev/null
+++ b/farsh.c
@@ -0,0 +1,353 @@
+/* FAt Remote SHell client by eeeee
+ * Public Domain */
+
+#include "config-common.h"
+#include "config-farsh.h"
+#include "common.h"
+
+#include <sys/types.h>
+#include <netdb.h>
+
+struct termios old_attr;
+static void exyt(){ tcsetattr(STDOUT_FILENO, TCSANOW, &old_attr); return; }
+
+int main(int argc, char *argv[]){
+	s32 skt, res, fdmax;
+	s32 pkt_int = PKT_INT;
+	u32 temp = 0;
+	u32 connected = 0;
+	u32 lptime; /* last packet time */
+	u16 *pkt, pos;
+	u8  term[CONNECT];
+	u8  esc = 1;
+	struct sockaddr_storage sender;
+	struct sockaddr_in  *srv4, *snd4;
+	struct sockaddr_in6 *srv6, *snd6;
+	struct addrinfo *srv;
+	struct termios  attr;
+	struct timeval  slp;
+	struct timespec t1, t2;
+	struct winsize  wsc, wss; /* client, server */
+	fd_set rset;
+
+	/* ring buffer */
+	u8 *rb;
+	u32 rb_mask = RB_SZ - 1;
+	u32 rb_w  = 0;  /* written to ring buffer     */
+	u32 rb_wm = 0;  /* written masked (mod RB_SZ) */
+	u32 rb_rm = 0;  /* readed masked              */
+	u32 rb_free = RB_SZ;
+
+	/* stream state */
+	u32 rcvd   = 0; /* recieved data bytes by client */
+	u32 rcvd_s = 0; /* recieved data bytes by client (server version) */
+	u32 rb_r   = 0; /* readed from ring buffer by server */
+	u32 rb_r_s = 0; /* server version */
+
+	if (argc != 3){
+		fprintf(stderr, "Using:\n\t"
+		            "%s <host> <port>\n\n"
+		        "Example:\n\t"
+		            "%s 127.0.0.1 2222\n\t"
+		            "%s example.org 2222\n\n",
+		        argv[0], argv[0], argv[0]);
+		exit(1);
+	}
+
+	do { /* dns, ip/port str to num */
+		res = getaddrinfo(argv[1], argv[2], NULL, &srv);
+		temp++;
+		usleep(10000); /* 10 ms */
+	} while (res == EAI_AGAIN && temp < 5);
+	if (res){
+		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res));
+		exit(1);
+	}
+
+	/* tty */
+	if (isatty(STDOUT_FILENO) && isatty(STDIN_FILENO)){
+		/* save term attrs */
+		res = tcgetattr(STDOUT_FILENO, &old_attr);
+		ERRDIE(res, "tcgetattr");
+
+		/* restore term attrs at exit */
+		res = atexit(exyt); ERRDIE(res, "atexit");
+
+		/* echo off */
+		memcpy(&attr, &old_attr, sizeof(attr));
+		attr.c_lflag &= ~ECHO;
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+
+		/* get password */
+		fputs("Password: ", stdout);
+		rb = fgets(password, PASSWORD_BUF_SZ, stdin);
+		fputs("\nconnecting...\n", stdout);
+		if (rb == NULL)
+			fputs("can't read password\n", stderr), exit(1);
+
+		/* switch to raw mode */
+		cfmakeraw(&attr);
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+	} else {
+		fputs("this program can't work without tty\n", stderr);
+		exit(1);
+	}
+
+	/* password without '\r\n' */
+	res = 0;
+	while (password[res] != '\0'
+	    && password[res] != '\r'
+	    && password[res] != '\n') res++;
+
+	/* hash password */
+	sponge256_hash32(key, password, res);
+	memset(password, 0, PASSWORD_BUF_SZ);
+
+	/* feed prng with key and current time */
+	duplex257_prng_feed(prng_state, ((u32*)key)[0]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[1]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[2]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[3]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[4]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[5]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[6]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[7]);
+	clock_gettime(CLOCK_MONOTONIC, &t1);
+	duplex257_prng_feed(prng_state, (u32)t1.tv_sec);
+	duplex257_prng_feed(prng_state, (u32)t1.tv_nsec);
+
+	lptime = t1.tv_sec; /* init last packet time */
+
+	/* get term size */
+	res = ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsc);
+	ERRDIE(res, "TIOCGWINSZ");
+	memset(&wss, 0, sizeof(wss));
+
+	/* get TERM var */
+	memset(term, '\0', CONNECT);
+	rb = getenv("TERM");
+	if (rb != NULL){
+		res = strlen(rb);
+		if (res >= CONNECT)
+			fprintf(stderr, "strlen(TERM) > %d\n", CONNECT-1),
+			exit(1);
+
+		strcpy(term, rb);
+	}
+
+	/* packet buffer (recv/send) */
+	pkt = malloc(MAX_PKT_SZ);
+	if (pkt == NULL) fputs("pkt = malloc() failed\r\n",stderr), exit(1);
+
+	/* ring buffer */
+	rb = malloc(RB_SZ);
+	if (rb == NULL) fputs("ringbuf malloc failed\r\n", stderr), exit(1);
+
+	/* socket, setsockopt */
+	skt = socket(srv->ai_family, SOCK_DGRAM, 0); ERRDIE(skt, "socket");
+	res = 1;
+	res = setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
+	ERRDIE(res, "SO_REUSEADDR");
+
+	/* sockaddr */
+	srv4 = (struct sockaddr_in  *) srv->ai_addr;
+	srv6 = (struct sockaddr_in6 *) srv->ai_addr;
+	snd4 = (struct sockaddr_in  *) &sender;
+	snd6 = (struct sockaddr_in6 *) &sender;
+
+	/* fdmax for select */
+	fdmax = skt;
+	if (STDIN_FILENO > skt) fdmax = STDIN_FILENO;
+	fdmax++;
+
+
+inf_loop:
+	/* select */
+	FD_ZERO(&rset);
+	FD_SET(skt, &rset);
+	if (rb_free) FD_SET(STDIN_FILENO, &rset);
+
+	slp.tv_sec = 0;
+	slp.tv_usec = pkt_int;
+
+	/* the contents of the slp structure after calling select() is
+	 * undefined (not portable), so I manually count the sleep time */
+	clock_gettime(CLOCK_MONOTONIC, &t1);
+	select(fdmax, &rset, NULL, NULL, &slp);
+
+	/* recieve packet */
+	if (FD_ISSET(skt, &rset)){
+		res = recv_pkt(skt, pkt, MAX_PKT_SZ, &sender);
+		if (res < 4) goto ignore_p; /* ignore too small packets */
+
+		/* check ipv4 packet addr */
+		if (srv->ai_addr->sa_family == AF_INET){
+			if (srv4->sin_port != snd4->sin_port ||
+			    srv4->sin_addr.s_addr != snd4->sin_addr.s_addr)
+				goto ignore_p;
+		} else { /* check ipv6 packet addr */
+			res = memcmp(srv6->sin6_addr.s6_addr,
+			             snd6->sin6_addr.s6_addr, 16);
+			if (0 != res || srv6->sin6_port != snd6->sin6_port)
+				goto ignore_p;
+		}
+
+		pkt[0] = ntohs(pkt[0]);
+		pkt[1] = ntohs(pkt[1]);
+		if (pkt[0] == 0)
+			fputs("\r\nserver closed connection\r\n", stdout),
+			exit(0);
+
+		/* ignore packet with wrong size */
+		if (res != 4 + (pkt[0] & ~CONNECT) + pkt[1]) goto ignore_p;
+
+		/* packet recv time for entropy and last packet time */
+		clock_gettime(CLOCK_MONOTONIC, &t2);
+		duplex257_prng_feed(prng_state, t2.tv_nsec);
+		lptime = t2.tv_sec;
+
+		pos = 2;
+		connected |= pkt[0] & CONNECT;
+
+		if (pkt[0] & PING){ /* read server term size */
+			wss.ws_row = ntohs(pkt[pos++]);
+			wss.ws_col = ntohs(pkt[pos++]);
+		}
+
+		if (pkt[0] & SSTATE){
+			rcvd_s = ntohl(*(u32*)&pkt[pos]), pos += 2;
+			rb_r_s = ntohl(*(u32*)&pkt[pos]), pos += 2;
+
+			/* rb_w can overflow, so I can't ignore a packet if
+			 * rb_w < rb_r_s. That's why I do these checks */
+			if (RB_SZ < rb_w - rb_r_s ||
+			    RB_SZ < rb_r_s - rb_r) goto ignore_p;
+
+			/* rb_r_s is ok, update state of ring buffer */
+			rb_r = rb_r_s;
+			rb_free = RB_SZ - (rb_w - rb_r);
+			rb_rm = rb_r & rb_mask;
+
+			if (pkt[1]){ /* have data in packet */
+				/* temp: already recieved amount of data*/
+				temp = rcvd - rcvd_s;
+				if (temp < pkt[1]){
+					/* packet also have data that is not
+					 * recieved yet */
+					pos = (pos<<1) + temp;
+					pkt[1] -= temp;
+					write(STDOUT_FILENO, (u8*)pkt+pos,
+					                            pkt[1]);
+					rcvd += pkt[1];
+				}
+			}
+		}
+	}
+ignore_p:
+
+	/* read stdin to ringbuffer */
+	if (FD_ISSET(STDIN_FILENO, &rset)){
+		res = read(STDIN_FILENO, rb + rb_wm, 1);
+		
+		if (res != 1)
+			fputs("\r\nreading stdin return res<1\r\n", stderr),
+			exit(1);
+
+		if (esc == 0 && rb[rb_wm] == DISCONNECT_KEY){
+			memset(pkt, 0, 4 + CONNECT);
+			pkt[0] = htons(CONNECT);
+			send_pkt(skt, pkt, 4 + CONNECT,
+			           (struct sockaddr_storage*) srv->ai_addr);
+			fputs("\r\ndisconnected\r\n", stderr);
+			exit(0);
+		}
+
+		esc = rb[rb_wm] - 0x1B;
+
+		rb_w++;
+		rb_wm = rb_w & rb_mask;
+		rb_free--;
+	}
+
+	/* send packet every PKT_INT microseconds */
+	clock_gettime(CLOCK_MONOTONIC, &t2);
+	pkt_int -= abs((s32)t2.tv_nsec - (s32)t1.tv_nsec) / 1000;
+	if (pkt_int < MIN_SLEEP){
+		t2.tv_sec -= lptime;
+		if (!connected && t2.tv_sec >= TIMEOUT_C)
+			fputs("can't connect\r\n", stderr), exit(1);
+
+		if (t2.tv_sec >= TIMEOUT_P){
+			/* send PING if no packets from server for
+			 * TIMEOUT_P seconds */
+			wss.ws_row = 65535;
+			if (t2.tv_sec >= TIMEOUT_D)
+				fputs("\r\nserver timeout\r\n", stdout),
+				exit(0);
+		}
+
+		pkt_int = PKT_INT;
+		pos = 2; /* pos in array of u16 */
+		pkt[0] = 0;
+
+		/* add something to packet */
+		if (!connected){
+			/* add TERM var */
+			pkt[0] |= CONNECT;
+			memcpy(&pkt[pos], term, CONNECT);
+			pos += CONNECT >> 1;
+		}
+
+		ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsc);
+		if ((wsc.ws_row - wss.ws_row) | (wsc.ws_col - wss.ws_col)){
+			/* add term sizes */
+			pkt[0] |= PING;
+			pkt[pos++] = htons(wsc.ws_row);
+			pkt[pos++] = htons(wsc.ws_col);
+		}
+
+		pkt[1] = rb_w - rb_r;
+		if (pkt[1]){ /* have data */
+			/* add state of streams */
+			pkt[0] |= SSTATE;
+			*(u32*)&pkt[pos] = htonl(rcvd), pos += 2;
+			*(u32*)&pkt[pos] = htonl(rb_r), pos += 2;
+
+			/* add data */
+			pos <<= 1; /* now it pos in array of u8 */
+			res = MAX_PKT_SZ - HMAC_SZ - NONCE_SZ - pos;
+			if (res < pkt[1]) pkt[1] = res;
+			memcpy((u8*)pkt + pos, rb + rb_rm, pkt[1]);
+			pos += pkt[1];
+		} else {
+			if (rcvd != rcvd_s || rb_r != rb_r_s){
+				/* add state of streams */
+				pkt[0] |= SSTATE;
+				*(u32*)&pkt[pos] = htonl(rcvd), pos += 2;
+				*(u32*)&pkt[pos] = htonl(rb_r), pos += 2;
+
+				/* send state only once */
+				rcvd_s = rcvd;
+				rb_r_s = rb_r;
+			}
+			pos <<= 1; /* now it pos in array of u8 */
+		}
+
+		if (pkt[0]){
+			/* send packet */
+			pkt[0] = htons(pkt[0]);
+			pkt[1] = htons(pkt[1]);
+			send_pkt(skt, pkt, pos,
+			           (struct sockaddr_storage*) srv->ai_addr);
+		}
+	}
+
+	goto inf_loop;
+
+	return 0;
+}
+
+
+
diff --git a/farshd.c b/farshd.c
new file mode 100644
index 0000000..d20d17e
--- /dev/null
+++ b/farshd.c
@@ -0,0 +1,420 @@
+/* FAt Remote SHell daemon by eeeee
+ * Public Domain */
+
+#define _XOPEN_SOURCE 600
+
+#include "config-common.h"
+#include "config-farshd.h"
+#include "common.h"
+
+#include <signal.h>
+#include <fcntl.h>
+
+struct {
+	struct sockaddr_in6 a;
+	struct winsize ws;
+	u32 flags, notconnected;
+	s32 ptm, pid;
+	u32 lptime; /* time of last packet */
+	/* stream state */
+	u32 rcvd;   /* recieved data bytes from client */
+	u32 rcvd_c; /* client version */
+	u32 rb_r;   /* readed from ring buffer (recieved data by client) */
+	u32 rb_r_c; /* client version */
+	/* ring buffer */
+	u32 rb_w;  /* written to ring buffer */
+	u32 rb_wm; /* written masked (mod RB_SZ) */
+	u32 rb_rm; /* readed masked */
+	u32 rb_free, rb_free_for_read;
+	u8 rb[RB_SZ];
+} peer[MAX_PEERS];
+
+u32 ccnt;
+
+static void disconnect(s32 skt, s32 n){
+	u8 pkt[4 + NONCE_SZ + HMAC_SZ];
+
+	*(u32*)pkt = 0;
+	send_pkt(skt, &pkt, 4, (struct sockaddr_storage*) &peer[n].a);
+	kill(peer[n].pid, SIGKILL);
+	close(peer[n].ptm);
+	if (n != ccnt-1) memcpy(&peer[n], &peer[ccnt-1], sizeof(peer[0]));
+	ccnt--;
+	memset(&peer[ccnt], 0, sizeof(peer[0]));
+	return;
+}
+
+int main(int argc, char *argv[]){
+	u8  term[CONNECT];
+	u8  *home;
+	u16 *pkt;
+	u16 pos;
+	u16 port;
+	s32 skt, res, fdmax, pts, i;
+	s32 pkt_int = PKT_INT;
+	u32 arcv;
+	u32 rb_mask = RB_SZ - 1;
+	struct sockaddr_in6 c;
+	struct sigaction sa;
+	struct timeval   slp;
+	struct timespec  t1, t2;
+	struct termios   attr;
+	fd_set rset;
+
+	/* check shell */
+	res = access(SHELL, F_OK); ERRDIE(res, SHELL);
+	res = access(SHELL, X_OK); ERRDIE(res, SHELL);
+
+	/* arguments */
+	if (argc != 2 && argc != 3){
+		fprintf(stderr, "Using:\n\t%s [address] <port>\n\n"
+		        "Example:\n\t%s 2222\n\t%s 127.0.0.1 2222\n\n",
+		        argv[0], argv[0], argv[0]);
+		exit(1);
+	}
+
+	memset(&c, 0, sizeof(c));
+	port = (u16)atoi(argv[argc-1]);
+	if (argc == 3){
+		res = inet_pton(AF_INET6, argv[1], c.sin6_addr.s6_addr);
+		if (res != 1){
+			res = inet_pton(AF_INET, argv[1], &arcv);
+			if (res != 1){
+				fprintf(stderr, "wrong addr: %s\n",argv[1]);
+				exit(1);
+			}
+			c.sin6_addr.s6_addr[10] = 0xFF;
+			c.sin6_addr.s6_addr[11] = 0xFF;
+			memcpy(&c.sin6_addr.s6_addr[12], &arcv, 4);
+		}
+	}
+
+	/* get term attrs */
+	res = tcgetattr(STDOUT_FILENO, &attr); ERRDIE(res, "tcgetattr");
+
+	/* echo off */
+	attr.c_lflag &= ~ECHO;
+	res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+	ERRDIE(res, "tcsetattr");
+
+	/* get password */
+	fputs("Password: ", stdout);
+	home = fgets(password, PASSWORD_BUF_SZ, stdin);
+	fputs("\n", stdout);
+	if (home == NULL)
+		fputs("can't read password\n", stderr), exit(1);
+
+	/* echo on */
+	attr.c_lflag |= ECHO;
+	res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+	ERRDIE(res, "tcsetattr");
+
+	/* password without '\r\n' */
+	res = 0;
+	while (password[res] != '\0'
+	    && password[res] != '\r'
+	    && password[res] != '\n') res++;
+
+	/* hash password */
+	sponge256_hash32(key, password, res);
+	memset(password, 0, PASSWORD_BUF_SZ);
+
+	/* feed prng with key and current time */
+	duplex257_prng_feed(prng_state, ((u32*)key)[0]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[1]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[2]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[3]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[4]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[5]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[6]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[7]);
+	clock_gettime(CLOCK_MONOTONIC, &t1);
+	duplex257_prng_feed(prng_state, (u32)t1.tv_sec);
+	duplex257_prng_feed(prng_state, (u32)t1.tv_nsec);
+
+	/* daemonize: redirect std streams */
+	close(STDIN_FILENO);
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	res = open("/dev/null", O_RDONLY); ERRDIE(res, "open");
+	res = open(LOGFILE, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+	ERRDIE(res, "open");
+	res = open(LOGFILE, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+	ERRDIE(res, "open");
+
+	/* daemonize: fork 1 */
+	res = fork(); ERRDIE(res, "fork 1");
+	if (res != 0) exit(0);
+
+	/* daemonize: become session leader */
+	setsid();
+
+	/* daemonize: fork 2 */
+	res = fork(); ERRDIE(res, "fork 2");
+	if (res != 0) exit(0); /* kill session leader */
+
+	/* daemonized! */
+
+	/* chdir to HOME */
+	home = getenv("HOME");
+	if (home != NULL) chdir(home);
+
+	/* kill zombie */
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = SIG_IGN;
+	sa.sa_flags = SA_NOCLDWAIT;
+	sigaction(SIGCHLD, &sa, NULL);
+
+	/* init some vars */
+	memset(peer, 0, sizeof(peer));
+	ccnt = 0;
+
+	/* cur pkt buf */
+	pkt = malloc(MAX_PKT_SZ);
+	if (pkt == NULL) fputs("pkt = malloc() failed\n",stderr), exit(1);
+
+	/* socket, setsockopt */
+	skt = socket(PF_INET6, SOCK_DGRAM, 0); ERRDIE(skt, "socket");
+	res = 1;
+	res = setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
+	ERRDIE(res, "SO_REUSEADDR");
+
+	/* bind */
+	c.sin6_family = AF_INET6;
+	c.sin6_port = htons(port);
+	res = bind(skt, (struct sockaddr*) &c, sizeof(c));
+	ERRDIE(res, "bind");
+
+inf_loop:
+	/* select */
+	fdmax = skt + 1;
+	FD_ZERO(&rset);
+	FD_SET(skt, &rset);
+	for (i=0; i < ccnt; i++){
+		if (peer[i].rb_free == 0) continue;
+		FD_SET(peer[i].ptm, &rset);
+		if (fdmax <= peer[i].ptm) fdmax = peer[i].ptm + 1;
+	}
+
+	slp.tv_sec = 0;
+	slp.tv_usec = pkt_int;
+
+	/* the contents of the slp structure after calling select() is
+	 * undefined (not portable), so I manually count the sleep time */
+	clock_gettime(CLOCK_MONOTONIC, &t1);
+	select(fdmax, &rset, NULL, NULL, &slp);
+
+	/* recv packet from client */
+	if (FD_ISSET(skt, &rset)){
+		res = recv_pkt(skt, pkt, MAX_PKT_SZ,
+		                             (struct sockaddr_storage*) &c);
+
+		pkt[0] = ntohs(pkt[0]);
+		pkt[1] = ntohs(pkt[1]);
+		if (res < 4 || res != 4 + pkt[0] + pkt[1]) goto ignore_p;
+		for (i=0; i < ccnt; i++){
+			res = memcmp(peer[i].a.sin6_addr.s6_addr,
+			                           c.sin6_addr.s6_addr, 16);
+			if (res == 0 && c.sin6_port == peer[i].a.sin6_port)
+				break;
+		}
+
+		if (i == ccnt){
+			/* client not connected */
+			if (ccnt == MAX_PEERS || (pkt[0] & CONNECT) == 0)
+				goto ignore_p;
+
+			/* add peer to array */
+			memcpy(&peer[i].a, &c, sizeof(c));
+
+			/* peer is not fully connected yet */
+			peer[i].notconnected = NOT_CONNECTED;
+
+			/* init ring buffer */
+			peer[i].rb_free = peer[i].rb_free_for_read = RB_SZ;
+
+			/* pseudoterminal */
+			peer[i].ptm = posix_openpt(O_RDWR);
+			ERRDIE(peer[i].ptm, "posix_openpt");
+			res = grantpt (peer[i].ptm); ERRDIE(res,"grantpt");
+			res = unlockpt(peer[i].ptm); ERRDIE(res,"unlockpt");
+        		pts = open(ptsname(peer[i].ptm), O_RDWR);
+			ERRDIE(pts, "pts = open()");
+
+			/* read TERM from packet */
+			memset(term, '\0', CONNECT);
+			memcpy(term, &pkt[2], CONNECT-1);
+
+			/* fork */
+			peer[i].pid = fork(); ERRDIE(peer[i].pid, "fork");
+			if (peer[i].pid == 0){ /* child */
+				close(peer[i].ptm);
+				close(0), close(1), close(2);
+				dup(pts), dup(pts), dup(pts);
+				close(pts);
+				setsid();
+				res = ioctl(0, TIOCSCTTY, 1);
+				ERRDIE(res, "TIOCSCTTY");
+				setenv("TERM", term, 1);
+
+				res = execlp(SHELL, SHELL, NULL);
+				ERRDIE(res, "execlp");
+			}
+			close(pts);
+			ccnt++;
+		}
+
+		/* client connected */
+		clock_gettime(CLOCK_MONOTONIC, &t2);
+		duplex257_prng_feed(prng_state, t2.tv_nsec);
+		peer[i].lptime = t2.tv_sec;
+		pos = 2;
+		if (pkt[0] & CONNECT){
+			if (!peer[i].notconnected){
+				/* fully connected client sent packet with
+				 * CONNECT flag, disconnect him */
+				disconnect(skt, i);
+				goto ignore_p;
+			}
+			peer[i].notconnected = NOT_CONNECTED;
+			pos += CONNECT >> 1;
+		} else if (peer[i].notconnected) peer[i].notconnected--;
+
+		/* copy flags for responce */
+		peer[i].flags |= pkt[0];
+
+		/* ping, term sizes */
+		if (pkt[0] & PING){
+			peer[i].ws.ws_row = ntohs(pkt[pos++]);
+			peer[i].ws.ws_col = ntohs(pkt[pos++]);
+			ioctl(peer[i].ptm, TIOCSWINSZ, &peer[i].ws);
+		}
+
+		/* stream state */
+		if (pkt[0] & SSTATE){
+			peer[i].rb_r_c = ntohl(*(u32*)&pkt[pos]), pos += 2;
+			peer[i].rcvd_c = ntohl(*(u32*)&pkt[pos]), pos += 2;
+
+			/* rb_w can overflow, so I can't ignore a packet if
+			 * rb_w < rb_r_c. That's why I do these checks */
+			if (RB_SZ < peer[i].rb_w - peer[i].rb_r_c ||
+			    RB_SZ < peer[i].rb_r_c - peer[i].rb_r)
+				goto ignore_p;
+
+			/* rb_r_c is ok, update state of ring buffer */
+			peer[i].rb_r = peer[i].rb_r_c;
+			peer[i].rb_free = RB_SZ -
+			                      (peer[i].rb_w - peer[i].rb_r);
+			peer[i].rb_rm = peer[i].rb_r & rb_mask;
+			peer[i].rb_free_for_read = peer[i].rb_free;
+			if (peer[i].rb_free > peer[i].rb_rm)
+				peer[i].rb_free_for_read -= peer[i].rb_rm;
+
+			if (pkt[1]){ /* have data in packet */
+				/* arcv: already recieved amount of data*/
+				arcv = peer[i].rcvd - peer[i].rcvd_c;
+				if (arcv < pkt[1]){
+					/* packet also have data that is not
+					 * recieved yet */
+					pos = (pos<<1) + arcv;
+					pkt[1] -= arcv;
+					write(peer[i].ptm, (u8*)pkt+pos,
+					                            pkt[1]);
+					peer[i].rcvd += pkt[1];
+				}
+			}
+		}
+	}
+ignore_p:
+
+	/* read ptms to ring buffers */
+	for (i=0; i < ccnt; i++){
+		if (0 == FD_ISSET(peer[i].ptm, &rset)) continue;
+
+		res = read(peer[i].ptm, peer[i].rb + peer[i].rb_wm,
+			   peer[i].rb_free_for_read);
+
+		if (res < 1){/* disconnect */
+			disconnect(skt, i);
+			if (i != ccnt-1) i--;
+			continue;
+		}
+
+		peer[i].rb_w += res;
+		peer[i].rb_wm = peer[i].rb_w & rb_mask;
+		peer[i].rb_free -= res;
+		peer[i].rb_free_for_read = peer[i].rb_free;
+		if (peer[i].rb_free > peer[i].rb_rm)
+			peer[i].rb_free_for_read -= peer[i].rb_rm;
+	}
+
+	/* send packets to clients every PKT_INT (+- MIN_SLEEP) microsecs */
+	clock_gettime(CLOCK_MONOTONIC, &t2);
+	pkt_int -= abs((s32)t2.tv_nsec - (s32)t1.tv_nsec) / 1000;
+	if (pkt_int < MIN_SLEEP)
+	for (pkt_int=PKT_INT, i=0; i < ccnt; i++){
+		/* make packet for peer i */
+		pos = 2;
+		pkt[0] = 0;
+
+		/* disconnect on timeout */
+		if (t2.tv_sec - peer[i].lptime >= TIMEOUT_D){
+			disconnect(skt, i);
+			continue;
+		}
+
+		/* set PING bit after getting packet with PING from peer */
+		if (peer[i].flags & PING){
+			pkt[0] |= PING;
+			pkt[pos++] = htons(peer[i].ws.ws_row);
+			pkt[pos++] = htons(peer[i].ws.ws_col);
+		}
+
+		/* add streams state and data if data exist */
+		pkt[1] = peer[i].rb_w - peer[i].rb_r;
+		if (pkt[1]) {
+			/* add state of streams */
+			pkt[0] |= SSTATE;
+			*(u32*)&pkt[pos] = htonl(peer[i].rb_r), pos += 2;
+			*(u32*)&pkt[pos] = htonl(peer[i].rcvd), pos += 2;
+
+			/* add data */
+			pos <<= 1; /* now it pos in array of u8 */
+			res = MAX_PKT_SZ - HMAC_SZ - NONCE_SZ - pos;
+			if (res < pkt[1]) pkt[1] = res;
+			memcpy((u8*)pkt + pos, peer[i].rb + peer[i].rb_rm,
+			                                            pkt[1]);
+			pos += pkt[1];
+		} else { /* no data to send */
+			/* send streams state if it differ from peer state*/
+			if (peer[i].rb_r != peer[i].rb_r_c ||
+			    peer[i].rcvd != peer[i].rcvd_c){
+				pkt[0] |= SSTATE;
+				*(u32*)&pkt[pos] = htonl(peer[i].rb_r);
+				pos += 2;
+				*(u32*)&pkt[pos] = htonl(peer[i].rcvd);
+				pos += 2;
+
+				/* send state only once */
+				peer[i].rb_r_c = peer[i].rb_r;
+				peer[i].rcvd_c = peer[i].rcvd;
+			}
+			pos <<= 1;
+		}
+
+		peer[i].flags = 0;
+		if (pkt[0]){
+			/* add CONNECT bit if peer is not fully connected */
+			if (peer[i].notconnected) pkt[0] |= CONNECT;
+			pkt[0] = htons(pkt[0]);
+			pkt[1] = htons(pkt[1]);
+			send_pkt(skt, pkt, pos,
+			             (struct sockaddr_storage*) &peer[i].a);
+		}
+	}
+
+	goto inf_loop;
+
+	return 0;
+}
+
diff --git a/shorttypes.h b/shorttypes.h
new file mode 100644
index 0000000..7b2df58
--- /dev/null
+++ b/shorttypes.h
@@ -0,0 +1,36 @@
+#ifndef SHORTTYPES_H
+#define SHORTTYPES_H
+#include <limits.h>
+
+#if CHAR_BIT != 8
+	#error "CHAR_BIT != 8"
+#endif
+
+#if USHRT_MAX != 65535
+	#error "USHRT_MAX != 65535"
+#endif
+
+#if UINT_MAX != 4294967295U
+	#error "UINT_MAX != 4294967295U"
+#endif
+
+typedef   signed char  s8;
+typedef   signed short s16;
+typedef   signed int   s32;
+typedef unsigned char  u8;
+typedef unsigned short u16;
+typedef unsigned int   u32;
+
+#ifdef ULLONG_MAX
+	#if ULLONG_MAX == 18446744073709551615ULL
+		typedef   signed long long s64;
+		typedef unsigned long long u64;
+	#else
+		#error "ULLONG_MAX != 18446744073709551615ULL"
+	#endif
+#else
+	#error "no long long type"
+#endif
+
+
+#endif /* SHORTTYPES_H */