Reformat
This commit is contained in:
230
mud.c
230
mud.c
@@ -4,19 +4,19 @@
|
||||
#define __APPLE_USE_RFC_3542
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <inttypes.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
@@ -110,7 +110,7 @@ struct path {
|
||||
struct path *next;
|
||||
};
|
||||
|
||||
struct public {
|
||||
struct crypto_pub {
|
||||
unsigned char send[MUD_PKEY_SIZE];
|
||||
unsigned char recv[MUD_PKEY_SIZE];
|
||||
};
|
||||
@@ -141,7 +141,7 @@ struct mud {
|
||||
uint64_t recv_time;
|
||||
uint64_t send_time;
|
||||
unsigned char secret[crypto_scalarmult_SCALARBYTES];
|
||||
struct public public;
|
||||
struct crypto_pub public;
|
||||
struct crypto_key private, last, next, current;
|
||||
int use_next;
|
||||
int aes;
|
||||
@@ -153,8 +153,8 @@ struct mud {
|
||||
} mtu;
|
||||
};
|
||||
|
||||
static
|
||||
int mud_encrypt_opt (const struct crypto_key *k, const struct crypto_opt *c)
|
||||
static int
|
||||
mud_encrypt_opt(const struct crypto_key *k, const struct crypto_opt *c)
|
||||
{
|
||||
if (k->aes) {
|
||||
return crypto_aead_aes256gcm_encrypt_afternm(
|
||||
@@ -168,8 +168,8 @@ int mud_encrypt_opt (const struct crypto_key *k, const struct crypto_opt *c)
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int mud_decrypt_opt (const struct crypto_key *k, const struct crypto_opt *c)
|
||||
static int
|
||||
mud_decrypt_opt(const struct crypto_key *k, const struct crypto_opt *c)
|
||||
{
|
||||
if (k->aes) {
|
||||
return crypto_aead_aes256gcm_decrypt_afternm(
|
||||
@@ -183,8 +183,8 @@ int mud_decrypt_opt (const struct crypto_key *k, const struct crypto_opt *c)
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void mud_write48 (unsigned char *dst, uint64_t src)
|
||||
static void
|
||||
mud_write48(unsigned char *dst, uint64_t src)
|
||||
{
|
||||
dst[0] = (unsigned char)(UINT64_C(255) & (src));
|
||||
dst[1] = (unsigned char)(UINT64_C(255) & (src >> 8));
|
||||
@@ -194,19 +194,20 @@ void mud_write48 (unsigned char *dst, uint64_t src)
|
||||
dst[5] = (unsigned char)(UINT64_C(255) & (src >> 40));
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t mud_read48 (const unsigned char *src)
|
||||
static uint64_t
|
||||
mud_read48(const unsigned char *src)
|
||||
{
|
||||
return ((uint64_t)src[0])
|
||||
| ((uint64_t)src[1]<<8)
|
||||
| ((uint64_t)src[2]<<16)
|
||||
| ((uint64_t)src[3]<<24)
|
||||
| ((uint64_t)src[4]<<32)
|
||||
| ((uint64_t)src[5]<<40);
|
||||
uint64_t ret = src[0];
|
||||
ret |= ((uint64_t)src[1]) << 8;
|
||||
ret |= ((uint64_t)src[2]) << 16;
|
||||
ret |= ((uint64_t)src[3]) << 24;
|
||||
ret |= ((uint64_t)src[4]) << 32;
|
||||
ret |= ((uint64_t)src[5]) << 40;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t mud_now (struct mud *mud)
|
||||
static uint64_t
|
||||
mud_now(struct mud *mud)
|
||||
{
|
||||
uint64_t now;
|
||||
#if defined CLOCK_REALTIME
|
||||
@@ -221,20 +222,20 @@ uint64_t mud_now (struct mud *mud)
|
||||
return now & ((UINT64_C(1) << 48) - 1);
|
||||
}
|
||||
|
||||
static
|
||||
uint64_t mud_abs_diff (uint64_t a, uint64_t b)
|
||||
static uint64_t
|
||||
mud_abs_diff(uint64_t a, uint64_t b)
|
||||
{
|
||||
return (a >= b) ? a - b : b - a;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_timeout (uint64_t now, uint64_t last, uint64_t timeout)
|
||||
static int
|
||||
mud_timeout(uint64_t now, uint64_t last, uint64_t timeout)
|
||||
{
|
||||
return ((!last) || ((now > last) && (now - last >= timeout)));
|
||||
}
|
||||
|
||||
static
|
||||
void mud_unmapv4 (struct sockaddr *addr)
|
||||
static void
|
||||
mud_unmapv4(struct sockaddr *addr)
|
||||
{
|
||||
if (addr->sa_family != AF_INET6)
|
||||
return;
|
||||
@@ -256,15 +257,15 @@ void mud_unmapv4 (struct sockaddr *addr)
|
||||
memcpy(addr, &sin, sizeof(sin));
|
||||
}
|
||||
|
||||
static
|
||||
size_t mud_addrlen (struct sockaddr_storage *addr)
|
||||
static size_t
|
||||
mud_addrlen(struct sockaddr_storage *addr)
|
||||
{
|
||||
return (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in)
|
||||
: sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
||||
static
|
||||
int mud_addrinfo (struct sockaddr_storage *addr, const char *host, int port)
|
||||
static int
|
||||
mud_addrinfo(struct sockaddr_storage *addr, const char *host, int port)
|
||||
{
|
||||
struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
@@ -291,8 +292,8 @@ int mud_addrinfo (struct sockaddr_storage *addr, const char *host, int port)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
ssize_t mud_send_path (struct mud *mud, struct path *path, uint64_t now,
|
||||
static ssize_t
|
||||
mud_send_path(struct mud *mud, struct path *path, uint64_t now,
|
||||
void *data, size_t size, int tc)
|
||||
{
|
||||
if (!size)
|
||||
@@ -321,14 +322,14 @@ ssize_t mud_send_path (struct mud *mud, struct path *path, uint64_t now,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_sso_int (int fd, int level, int optname, int opt)
|
||||
static int
|
||||
mud_sso_int(int fd, int level, int optname, int opt)
|
||||
{
|
||||
return setsockopt(fd, level, optname, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static
|
||||
int mud_cmp_ipaddr (struct ipaddr *a, struct ipaddr *b)
|
||||
static int
|
||||
mud_cmp_ipaddr(struct ipaddr *a, struct ipaddr *b)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
@@ -345,8 +346,8 @@ int mud_cmp_ipaddr (struct ipaddr *a, struct ipaddr *b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_cmp_addr (struct sockaddr *a, struct sockaddr *b)
|
||||
static int
|
||||
mud_cmp_addr(struct sockaddr *a, struct sockaddr *b)
|
||||
{
|
||||
if (a == b)
|
||||
return 0;
|
||||
@@ -375,8 +376,8 @@ int mud_cmp_addr (struct sockaddr *a, struct sockaddr *b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static
|
||||
void mud_set_path (struct path *path, struct ipaddr *local_addr,
|
||||
static void
|
||||
mud_set_path(struct path *path, struct ipaddr *local_addr,
|
||||
struct sockaddr *addr)
|
||||
{
|
||||
struct msghdr msg = {
|
||||
@@ -407,8 +408,8 @@ void mud_set_path (struct path *path, struct ipaddr *local_addr,
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
|
||||
path->tc = CMSG_DATA(cmsg);
|
||||
path->ctrl.size = CMSG_SPACE(MUD_PKTINFO_SIZE)
|
||||
+ CMSG_SPACE(sizeof(int));
|
||||
path->ctrl.size = CMSG_SPACE(MUD_PKTINFO_SIZE) +
|
||||
CMSG_SPACE(sizeof(int));
|
||||
}
|
||||
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
@@ -429,13 +430,13 @@ void mud_set_path (struct path *path, struct ipaddr *local_addr,
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
|
||||
path->tc = CMSG_DATA(cmsg);
|
||||
path->ctrl.size = CMSG_SPACE(sizeof(struct in6_pktinfo))
|
||||
+ CMSG_SPACE(sizeof(int));
|
||||
path->ctrl.size = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
|
||||
CMSG_SPACE(sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
struct path *mud_path (struct mud *mud, struct ipaddr *local_addr,
|
||||
static struct path *
|
||||
mud_path(struct mud *mud, struct ipaddr *local_addr,
|
||||
struct sockaddr *addr, int create)
|
||||
{
|
||||
if (local_addr->family != addr->sa_family)
|
||||
@@ -469,8 +470,8 @@ struct path *mud_path (struct mud *mud, struct ipaddr *local_addr,
|
||||
return path;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_ipaddrinfo (struct ipaddr *ipaddr, const char *name)
|
||||
static int
|
||||
mud_ipaddrinfo(struct ipaddr *ipaddr, const char *name)
|
||||
{
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
@@ -490,7 +491,9 @@ int mud_ipaddrinfo (struct ipaddr *ipaddr, const char *name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mud_peer (struct mud *mud, const char *name, const char *host, int port, int backup)
|
||||
int
|
||||
mud_peer(struct mud *mud, const char *name, const char *host, int port,
|
||||
int backup)
|
||||
{
|
||||
if (!name || !host || !port) {
|
||||
errno = EINVAL;
|
||||
@@ -509,8 +512,7 @@ int mud_peer (struct mud *mud, const char *name, const char *host, int port, int
|
||||
|
||||
mud_unmapv4((struct sockaddr *)&addr);
|
||||
|
||||
struct path *path = mud_path(mud, &local_addr,
|
||||
(struct sockaddr *)&addr, 1);
|
||||
struct path *path = mud_path(mud, &local_addr, (struct sockaddr *)&addr, 1);
|
||||
|
||||
if (!path) {
|
||||
errno = ENOMEM;
|
||||
@@ -523,7 +525,8 @@ int mud_peer (struct mud *mud, const char *name, const char *host, int port, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mud_get_key (struct mud *mud, unsigned char *key, size_t *size)
|
||||
int
|
||||
mud_get_key(struct mud *mud, unsigned char *key, size_t *size)
|
||||
{
|
||||
if (!key || !size || (*size < MUD_KEY_SIZE)) {
|
||||
errno = EINVAL;
|
||||
@@ -536,7 +539,8 @@ int mud_get_key (struct mud *mud, unsigned char *key, size_t *size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mud_set_key (struct mud *mud, unsigned char *key, size_t size)
|
||||
int
|
||||
mud_set_key(struct mud *mud, unsigned char *key, size_t size)
|
||||
{
|
||||
if (!key || (size < MUD_KEY_SIZE)) {
|
||||
errno = EINVAL;
|
||||
@@ -553,7 +557,8 @@ int mud_set_key (struct mud *mud, unsigned char *key, size_t size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mud_set_send_timeout_msec (struct mud *mud, unsigned msec)
|
||||
int
|
||||
mud_set_send_timeout_msec(struct mud *mud, unsigned msec)
|
||||
{
|
||||
if (!msec) {
|
||||
errno = EINVAL;
|
||||
@@ -565,7 +570,8 @@ int mud_set_send_timeout_msec (struct mud *mud, unsigned msec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mud_set_time_tolerance_sec (struct mud *mud, unsigned sec)
|
||||
int
|
||||
mud_set_time_tolerance_sec(struct mud *mud, unsigned sec)
|
||||
{
|
||||
if (!sec) {
|
||||
errno = EINVAL;
|
||||
@@ -577,7 +583,8 @@ int mud_set_time_tolerance_sec (struct mud *mud, unsigned sec)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mud_get_mtu (struct mud *mud)
|
||||
int
|
||||
mud_get_mtu(struct mud *mud)
|
||||
{
|
||||
if ((!mud->mtu.remote) ||
|
||||
(mud->mtu.local < mud->mtu.remote))
|
||||
@@ -586,7 +593,8 @@ int mud_get_mtu (struct mud *mud)
|
||||
return mud->mtu.remote;
|
||||
}
|
||||
|
||||
int mud_set_mtu (struct mud *mud, int mtu)
|
||||
int
|
||||
mud_set_mtu(struct mud *mud, int mtu)
|
||||
{
|
||||
if ((mtu < 500) ||
|
||||
(mtu > MUD_PACKET_MAX_SIZE - 50)) {
|
||||
@@ -602,8 +610,8 @@ int mud_set_mtu (struct mud *mud, int mtu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_setup_socket (int fd, int v4, int v6)
|
||||
static int
|
||||
mud_setup_socket(int fd, int v4, int v6)
|
||||
{
|
||||
if ((mud_sso_int(fd, SOL_SOCKET, SO_REUSEADDR, 1)) ||
|
||||
(v4 && mud_sso_int(fd, IPPROTO_IP, MUD_PKTINFO, 1)) ||
|
||||
@@ -616,14 +624,11 @@ int mud_setup_socket (int fd, int v4, int v6)
|
||||
mud_sso_int(fd, IPPROTO_IP, MUD_DFRAG, MUD_DFRAG_OPT);
|
||||
#endif
|
||||
|
||||
// mud_sso_int(fd, SOL_SOCKET, SO_RCVBUF, 1<<24);
|
||||
// mud_sso_int(fd, SOL_SOCKET, SO_SNDBUF, 1<<24);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_create_socket (int port, int v4, int v6)
|
||||
static int
|
||||
mud_create_socket(int port, int v4, int v6)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
@@ -646,8 +651,8 @@ int mud_create_socket (int port, int v4, int v6)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static
|
||||
void mud_keyx_init (struct mud *mud)
|
||||
static void
|
||||
mud_keyx_init(struct mud *mud)
|
||||
{
|
||||
randombytes_buf(mud->crypto.secret, sizeof(mud->crypto.secret));
|
||||
crypto_scalarmult_base(mud->crypto.public.send, mud->crypto.secret);
|
||||
@@ -655,7 +660,8 @@ void mud_keyx_init (struct mud *mud)
|
||||
mud->crypto.public.send[MUD_PKEY_SIZE - 1] = mud->crypto.aes;
|
||||
}
|
||||
|
||||
struct mud *mud_create (int port, int v4, int v6, int aes, int mtu)
|
||||
struct mud *
|
||||
mud_create(int port, int v4, int v6, int aes, int mtu)
|
||||
{
|
||||
if (sodium_init() == -1)
|
||||
return NULL;
|
||||
@@ -687,12 +693,14 @@ struct mud *mud_create (int port, int v4, int v6, int aes, int mtu)
|
||||
return mud;
|
||||
}
|
||||
|
||||
int mud_get_fd (struct mud *mud)
|
||||
int
|
||||
mud_get_fd(struct mud *mud)
|
||||
{
|
||||
return mud->fd;
|
||||
}
|
||||
|
||||
void mud_delete (struct mud *mud)
|
||||
void
|
||||
mud_delete(struct mud *mud)
|
||||
{
|
||||
if (!mud)
|
||||
return;
|
||||
@@ -712,8 +720,8 @@ void mud_delete (struct mud *mud)
|
||||
free(mud);
|
||||
}
|
||||
|
||||
static
|
||||
int mud_encrypt (struct mud *mud, uint64_t nonce,
|
||||
static int
|
||||
mud_encrypt(struct mud *mud, uint64_t nonce,
|
||||
unsigned char *dst, size_t dst_size,
|
||||
const unsigned char *src, size_t src_size)
|
||||
{
|
||||
@@ -727,10 +735,13 @@ int mud_encrypt (struct mud *mud, uint64_t nonce,
|
||||
|
||||
struct crypto_opt opt = {
|
||||
.dst = dst + MUD_U48_SIZE,
|
||||
.src = { .data = src,
|
||||
.size = src_size },
|
||||
.ad = { .data = dst,
|
||||
.size = MUD_U48_SIZE },
|
||||
.src = {
|
||||
.data = src,
|
||||
.size = src_size,
|
||||
},
|
||||
.ad = {
|
||||
.data = dst, .size = MUD_U48_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
mud_write48(opt.npub, nonce);
|
||||
@@ -745,8 +756,8 @@ int mud_encrypt (struct mud *mud, uint64_t nonce,
|
||||
return size;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_decrypt (struct mud *mud,
|
||||
static int
|
||||
mud_decrypt(struct mud *mud,
|
||||
unsigned char *dst, size_t dst_size,
|
||||
const unsigned char *src, size_t src_size)
|
||||
{
|
||||
@@ -757,10 +768,13 @@ int mud_decrypt (struct mud *mud,
|
||||
|
||||
struct crypto_opt opt = {
|
||||
.dst = dst,
|
||||
.src = { .data = src+MUD_U48_SIZE,
|
||||
.size = src_size-MUD_U48_SIZE },
|
||||
.ad = { .data = src,
|
||||
.size = MUD_U48_SIZE },
|
||||
.src = {
|
||||
.data = src + MUD_U48_SIZE,
|
||||
.size = src_size - MUD_U48_SIZE,
|
||||
},
|
||||
.ad = {
|
||||
.data = src, .size = MUD_U48_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
memcpy(opt.npub, src, MUD_U48_SIZE);
|
||||
@@ -781,8 +795,8 @@ int mud_decrypt (struct mud *mud,
|
||||
return size;
|
||||
}
|
||||
|
||||
static
|
||||
int mud_localaddr (struct ipaddr *local_addr, struct msghdr *msg, int family)
|
||||
static int
|
||||
mud_localaddr(struct ipaddr *local_addr, struct msghdr *msg, int family)
|
||||
{
|
||||
int cmsg_level = IPPROTO_IP;
|
||||
int cmsg_type = MUD_PKTINFO;
|
||||
@@ -818,8 +832,8 @@ int mud_localaddr (struct ipaddr *local_addr, struct msghdr *msg, int family)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void mud_ctrl_path (struct mud *mud, enum mud_msg msg, struct path *path,
|
||||
static void
|
||||
mud_ctrl_path(struct mud *mud, enum mud_msg msg, struct path *path,
|
||||
uint64_t now)
|
||||
{
|
||||
struct {
|
||||
@@ -856,8 +870,10 @@ void mud_ctrl_path (struct mud *mud, enum mud_msg msg, struct path *path,
|
||||
|
||||
struct crypto_opt opt = {
|
||||
.dst = ctrl.data + size,
|
||||
.ad = { .data = ctrl.zero,
|
||||
.size = size+2*MUD_U48_SIZE },
|
||||
.ad = {
|
||||
.data = ctrl.zero,
|
||||
.size = size + 2 * MUD_U48_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
size += 2 * MUD_U48_SIZE + MUD_MAC_SIZE;
|
||||
@@ -866,15 +882,15 @@ void mud_ctrl_path (struct mud *mud, enum mud_msg msg, struct path *path,
|
||||
mud_send_path(mud, path, now, &ctrl, size, 0);
|
||||
}
|
||||
|
||||
static
|
||||
void mud_recv_keyx (struct mud *mud, struct path *path, uint64_t now,
|
||||
static void
|
||||
mud_recv_keyx(struct mud *mud, struct path *path, uint64_t now,
|
||||
unsigned char *data)
|
||||
{
|
||||
struct crypto_key *key = &mud->crypto.next;
|
||||
|
||||
struct {
|
||||
unsigned char secret[crypto_scalarmult_BYTES];
|
||||
struct public public;
|
||||
struct crypto_pub public;
|
||||
} shared_send, shared_recv;
|
||||
|
||||
memcpy(&shared_recv.public, data, sizeof(shared_recv.public));
|
||||
@@ -928,7 +944,8 @@ void mud_recv_keyx (struct mud *mud, struct path *path, uint64_t now,
|
||||
mud->crypto.recv_time = now;
|
||||
}
|
||||
|
||||
int mud_recv (struct mud *mud, void *data, size_t size)
|
||||
int
|
||||
mud_recv(struct mud *mud, void *data, size_t size)
|
||||
{
|
||||
unsigned char packet[MUD_PACKET_MAX_SIZE];
|
||||
|
||||
@@ -974,10 +991,13 @@ int mud_recv (struct mud *mud, void *data, size_t size)
|
||||
|
||||
struct crypto_opt opt = {
|
||||
.dst = tmp,
|
||||
.src = { .data = packet+packet_size-MUD_MAC_SIZE,
|
||||
.size = MUD_MAC_SIZE },
|
||||
.ad = { .data = packet,
|
||||
.size = packet_size-MUD_MAC_SIZE },
|
||||
.src = {
|
||||
.data = packet + packet_size - MUD_MAC_SIZE,
|
||||
.size = MUD_MAC_SIZE,
|
||||
},
|
||||
.ad = {
|
||||
.data = packet, .size = packet_size - MUD_MAC_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
if (mud_decrypt_opt(&mud->crypto.private, &opt))
|
||||
@@ -1051,7 +1071,8 @@ int mud_recv (struct mud *mud, void *data, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mud_send_ctrl (struct mud *mud)
|
||||
void
|
||||
mud_send_ctrl(struct mud *mud)
|
||||
{
|
||||
struct path *path;
|
||||
|
||||
@@ -1087,7 +1108,8 @@ void mud_send_ctrl (struct mud *mud)
|
||||
}
|
||||
}
|
||||
|
||||
int mud_send (struct mud *mud, const void *data, size_t size, int tc)
|
||||
int
|
||||
mud_send(struct mud *mud, const void *data, size_t size, int tc)
|
||||
{
|
||||
mud_send_ctrl(mud);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user