├── .gitignore ├── src ├── ext │ ├── inih │ │ ├── custom.h │ │ ├── Makefile │ │ ├── LICENSE.txt │ │ ├── inih.h │ │ └── inih.c │ └── Makefile ├── teavpn2 │ ├── net │ │ ├── linux │ │ │ ├── Makefile │ │ │ ├── iface.h │ │ │ └── iface.c │ │ └── Makefile │ ├── client │ │ ├── linux │ │ │ ├── Makefile │ │ │ └── udp.h │ │ ├── Makefile │ │ ├── common.h │ │ └── entry.c │ ├── server │ │ ├── Makefile │ │ ├── linux │ │ │ ├── Makefile │ │ │ ├── udp_session.c │ │ │ ├── udp.h │ │ │ └── udp.c │ │ ├── common.h │ │ └── entry.c │ ├── gui │ │ ├── gui_utils.c │ │ ├── Makefile │ │ ├── gui_window.c │ │ ├── events.h │ │ ├── entry.c │ │ ├── gui_config.c │ │ ├── gui.h │ │ ├── gui_header.c │ │ ├── events.c │ │ └── gui_home.c │ ├── Makefile │ ├── allocator.h │ ├── arch │ │ ├── generic │ │ │ └── linux.h │ │ └── x86 │ │ │ └── linux.h │ ├── allocator.c │ ├── main.c │ ├── stack.h │ ├── print.h │ ├── mutex.h │ ├── packet.h │ ├── print.c │ ├── auth.c │ ├── common.h │ └── compiler_attributes.h └── Makefile ├── data └── server │ ├── users │ └── ammarfaizi2.ini │ ├── defaut_cert.pem │ └── default_key.pem ├── .github ├── .codecov.yml ├── work.bak └── workflows │ └── build.yml ├── config ├── server.ini └── client.ini ├── README.md ├── Documentation ├── developer-certificate-of-origin └── SubmittingPatches ├── Makefile └── configure /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.so 3 | *.swo 4 | *.swp 5 | *.log 6 | *.tmp 7 | *.lock 8 | *.dump 9 | *.gcno 10 | *.gcda 11 | *.gcov 12 | *.test 13 | *.tmp.c 14 | /*.patch 15 | /.deps/ 16 | /.vscode/ 17 | /teavpn2* 18 | /dump.txt 19 | /config.h 20 | /config.make -------------------------------------------------------------------------------- /src/ext/inih/custom.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Custom config for inih 4 | * 5 | * Copyright (C) 2021 Ammar Faizi 6 | */ 7 | #ifndef INIH__CUSTOM_H 8 | #define INIH__CUSTOM_H 9 | 10 | #define INI_USE_STACK 1 11 | #define INI_HANDLER_LINENO 1 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /data/server/users/ammarfaizi2.ini: -------------------------------------------------------------------------------- 1 | 2 | [auth] 3 | username = ammarfaizi2 4 | password = mypassword123 5 | 6 | [iface] 7 | ; 8 | ; Virtual network interface IP config for client 9 | ; (this is set from the server) 10 | ; 11 | mtu = 1470 12 | ipv4 = 10.5.5.2 13 | ipv4_netmask = 255.255.255.0 14 | ipv4_dgateway = 10.5.5.1 15 | -------------------------------------------------------------------------------- /src/ext/inih/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/ext/inih 11 | OBJ_CC += $(BASE_DIR)/src/ext/inih/inih.o 12 | -------------------------------------------------------------------------------- /src/teavpn2/net/linux/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/net/linux 11 | 12 | OBJ_CC += \ 13 | $(BASE_DIR)/src/teavpn2/net/linux/iface.o 14 | -------------------------------------------------------------------------------- /src/ext/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | INCLUDE_DIR += -I$(BASE_DIR)/src/ext 11 | 12 | include $(BASE_DIR)/src/ext/inih/Makefile 13 | 14 | clean_all: liburing_clean hpc_emerg_clean 15 | 16 | -------------------------------------------------------------------------------- /.github/.codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | 9 | parsers: 10 | gcov: 11 | branch_detection: 12 | conditional: yes 13 | loop: yes 14 | method: no 15 | macro: no 16 | 17 | comment: 18 | layout: "reach,diff,flags,tree" 19 | behavior: default 20 | require_changes: no 21 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | INCLUDE_DIR += -I$(BASE_DIR)/src 11 | DEP_DIRS += $(BASE_DEP_DIR)/src 12 | 13 | include $(BASE_DIR)/src/ext/Makefile 14 | include $(BASE_DIR)/src/teavpn2/Makefile 15 | -------------------------------------------------------------------------------- /src/teavpn2/net/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/net 11 | 12 | 13 | ifeq ($(CONFIG_LINUX),y) 14 | include $(BASE_DIR)/src/teavpn2/net/linux/Makefile 15 | endif 16 | -------------------------------------------------------------------------------- /src/teavpn2/client/linux/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/client/linux 11 | 12 | OBJ_CC += \ 13 | $(BASE_DIR)/src/teavpn2/client/linux/udp.o \ 14 | $(BASE_DIR)/src/teavpn2/client/linux/udp_epoll.o 15 | -------------------------------------------------------------------------------- /src/teavpn2/client/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/client 11 | 12 | ifeq ($(CONFIG_LINUX),y) 13 | include $(BASE_DIR)/src/teavpn2/client/linux/Makefile 14 | endif 15 | 16 | OBJ_CC += \ 17 | $(BASE_DIR)/src/teavpn2/client/entry.o 18 | -------------------------------------------------------------------------------- /src/teavpn2/server/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/server 11 | 12 | ifeq ($(CONFIG_LINUX),y) 13 | include $(BASE_DIR)/src/teavpn2/server/linux/Makefile 14 | endif 15 | 16 | OBJ_CC += \ 17 | $(BASE_DIR)/src/teavpn2/server/entry.o 18 | -------------------------------------------------------------------------------- /config/server.ini: -------------------------------------------------------------------------------- 1 | ; 2 | ; TeaVPN2 Server configuration 3 | ; 4 | 5 | [sys] 6 | thread = 4 7 | verbose_level = 4 8 | data_dir = data/server 9 | 10 | [socket] 11 | event_loop = epoll 12 | sock_type = udp 13 | bind_addr = 0.0.0.0 14 | bind_port = 44444 15 | backlog = 10 16 | max_conn = 32 17 | ssl_cert = data/server/default_cert.pem 18 | ssl_priv_key = data/server/default_key.pem 19 | 20 | [iface] 21 | dev = teavpn2-sr-01 22 | mtu = 1450 23 | ipv4 = 10.5.5.1 24 | ipv4_netmask = 255.255.255.0 25 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui_utils.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | 9 | void gui_utils_set_callback(const struct gui_callback *dest, size_t size) 10 | { 11 | while (size--) 12 | g_signal_connect(*dest[size].instance, dest[size].signal, 13 | dest[size].func, dest[size].data); 14 | } 15 | -------------------------------------------------------------------------------- /src/teavpn2/server/linux/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/server/linux 11 | 12 | OBJ_CC += \ 13 | $(BASE_DIR)/src/teavpn2/server/linux/udp.o \ 14 | $(BASE_DIR)/src/teavpn2/server/linux/udp_epoll.o \ 15 | $(BASE_DIR)/src/teavpn2/server/linux/udp_session.o 16 | -------------------------------------------------------------------------------- /config/client.ini: -------------------------------------------------------------------------------- 1 | ; 2 | ; TeaVPN2 Client configuration 3 | ; 4 | 5 | [sys] 6 | thread = 4 7 | verbose_level = 2 8 | data_dir = data/client 9 | 10 | [socket] 11 | use_encryption = 0 12 | event_loop = epoll 13 | sock_type = udp 14 | server_addr = 127.0.0.1 15 | server_port = 44444 16 | 17 | [iface] 18 | dev = teavpn2-cl-01 19 | 20 | ; 21 | ; Set override_default to 1 if you want to use VPN as 22 | ; your default internet connection. 23 | ; 24 | override_default = 0 25 | 26 | [auth] 27 | username = ammarfaizi2 28 | password = mypassword123 29 | -------------------------------------------------------------------------------- /src/teavpn2/net/linux/iface.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Network interface helper. 4 | * 5 | * Copyright (C) 2021 Ammar Faizi 6 | */ 7 | 8 | #ifndef TEAVPN2__NET__LINUX__IFACE_H 9 | #define TEAVPN2__NET__LINUX__IFACE_H 10 | 11 | #include 12 | #include 13 | 14 | extern int fd_set_nonblock(int fd); 15 | extern int tun_alloc(const char *dev, short flags); 16 | extern bool teavpn_iface_up(struct if_info *iface); 17 | extern bool teavpn_iface_down(struct if_info *iface); 18 | 19 | #endif /* #ifndef TEAVPN2__NET__LINUX__IFACE_H */ 20 | -------------------------------------------------------------------------------- /src/teavpn2/gui/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Alviro Iskandar Setiawan 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Alviro Iskandar Setiawan 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2/gui 11 | 12 | OBJ_CC += \ 13 | $(BASE_DIR)/src/teavpn2/gui/entry.o \ 14 | $(BASE_DIR)/src/teavpn2/gui/events.o \ 15 | $(BASE_DIR)/src/teavpn2/gui/gui_config.o \ 16 | $(BASE_DIR)/src/teavpn2/gui/gui_header.o \ 17 | $(BASE_DIR)/src/teavpn2/gui/gui_home.o \ 18 | $(BASE_DIR)/src/teavpn2/gui/gui_utils.o \ 19 | $(BASE_DIR)/src/teavpn2/gui/gui_window.o 20 | -------------------------------------------------------------------------------- /src/teavpn2/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: GPL-2.0-only 3 | # 4 | # @author Ammar Faizi https://www.facebook.com/ammarfaizi2 5 | # @license GPL-2.0-only 6 | # 7 | # Copyright (C) 2021 Ammar Faizi 8 | # 9 | 10 | DEP_DIRS += $(BASE_DEP_DIR)/src/teavpn2 11 | 12 | include $(BASE_DIR)/src/teavpn2/client/Makefile 13 | include $(BASE_DIR)/src/teavpn2/server/Makefile 14 | include $(BASE_DIR)/src/teavpn2/net/Makefile 15 | 16 | ifeq ($(CONFIG_GUI),y) 17 | include $(BASE_DIR)/src/teavpn2/gui/Makefile 18 | endif 19 | 20 | OBJ_CC += \ 21 | $(BASE_DIR)/src/teavpn2/allocator.o \ 22 | $(BASE_DIR)/src/teavpn2/auth.o \ 23 | $(BASE_DIR)/src/teavpn2/main.o \ 24 | $(BASE_DIR)/src/teavpn2/print.o 25 | -------------------------------------------------------------------------------- /src/teavpn2/allocator.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__ALLOCATOR_H 7 | #define TEAVPN2__ALLOCATOR_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | extern void *al64_calloc(size_t nmemb, size_t size); 15 | extern __malloc void *al64_malloc(size_t size); 16 | extern void al64_free(void *user); 17 | extern void *al64_realloc(void *user, size_t new_size); 18 | 19 | static inline void *al4096_malloc_mmap(size_t size) 20 | { 21 | void *ret; 22 | 23 | ret = mmap(NULL, size, PROT_READ | PROT_WRITE, 24 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 25 | if (ret == MAP_FAILED) 26 | return NULL; 27 | 28 | return ret; 29 | } 30 | 31 | 32 | static inline void al4096_free_munmap(void *user, size_t size) 33 | { 34 | if (user != NULL) 35 | munmap(user, size); 36 | } 37 | 38 | #endif /* #ifndef TEAVPN2__ALLOCATOR_H */ 39 | -------------------------------------------------------------------------------- /src/teavpn2/server/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__SERVER__COMMON_H 7 | #define TEAVPN2__SERVER__COMMON_H 8 | 9 | #include 10 | 11 | struct srv_cfg_sys { 12 | const char *cfg_file; 13 | char data_dir[128]; 14 | uint8_t thread_num; 15 | uint8_t verbose_level; 16 | }; 17 | 18 | 19 | struct srv_cfg_sock { 20 | bool use_encryption; 21 | int backlog; 22 | sock_type type; 23 | char bind_addr[64]; 24 | uint16_t bind_port; 25 | uint16_t max_conn; 26 | char event_loop[64]; 27 | char ssl_cert[256]; 28 | char ssl_priv_key[256]; 29 | }; 30 | 31 | 32 | struct srv_cfg_iface { 33 | char dev[IFACENAMESIZ]; 34 | uint16_t mtu; 35 | struct if_info iff; 36 | }; 37 | 38 | 39 | struct srv_cfg { 40 | struct srv_cfg_sys sys; 41 | struct srv_cfg_sock sock; 42 | struct srv_cfg_iface iface; 43 | }; 44 | 45 | extern int teavpn2_server_udp_run(struct srv_cfg *cfg); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # TeaVPN2 3 | TeaVPN2 is an open source VPN Software. Current supported platform is 4 | only Linux. We plan to expand to other platforms (contributors are 5 | welcomed). 6 | 7 | 8 | # Build Requirements 9 | - GNU Make 4.3 10 | - gcc 9.3+ or clang 11+ 11 | 12 | 13 | # Build 14 | ``` 15 | sudo apt-get install gcc clang make build-essential -y; 16 | git clone https://github.com/TeaInside/teavpn2; 17 | cd teavpn2; 18 | make -j$(nproc); 19 | ``` 20 | 21 | For build with GUI support: 22 | ``` 23 | ./configure --gui; 24 | make -j$(nproc); 25 | ``` 26 | 27 | # Issues 28 | We welcome bug reports, feature requests and questions through GitHub 29 | repository https://github.com/TeaInside/teavpn2 (kindly to open an issue). 30 | 31 | 32 | # Project Maintainer 33 | - Ammar Faizi ([@ammarfaizi2](https://github.com/ammarfaizi2)) 34 | 35 | 36 | # Community 37 | We are online on Telegram, see https://t.me/TeaInside 38 | 39 | 40 | # Contributing 41 | We accept pull request on the GitHub repository. 42 | 43 | 44 | # License 45 | This software is licensed under the GNU GPL-v2 license. 46 | -------------------------------------------------------------------------------- /data/server/defaut_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC1jCCAb6gAwIBAgIUML90D8ANcVgG5orNE7j9roGLXVQwDQYJKoZIhvcNAQEL 3 | BQAwEzERMA8GA1UEAwwIaW50ZWdyYWwwHhcNMjEwMjA3MDcwMjAxWhcNMzEwMjA1 4 | MDcwMjAxWjATMREwDwYDVQQDDAhpbnRlZ3JhbDCCASIwDQYJKoZIhvcNAQEBBQAD 5 | ggEPADCCAQoCggEBAL3NJfV+ozNmp4C+VRxP+rL3HyqW5WeBrIdzDQ7JyIRAJr2M 6 | jADisfU1GkR9gDmItUxXApv1jFFKq7ACEMByZ5s92RzJDy344LJHYVtOB2iHGptb 7 | P8zmPpgpHfRYH+sT2FyJmqWH4gENESjH4WipQfUr38m1HFtYuvEv6l2mGkd1xqZa 8 | zTP8ho+4+OH8epYkS53qKQgHDEAscPgiIamPikYN4kwjcWaeMS5rzlNgqrNiq5V+ 9 | KP+vJRYqM9EDJfzvnmc+KYyuZS1+6lUpG9VDOYlNkJe3EJcPwk9tXOoEo8ITQYW3 10 | IZZiov6/wsICOU8l3bPKswpGDdIFgviYEJSKlpsCAwEAAaMiMCAwCQYDVR0TBAIw 11 | ADATBgNVHREEDDAKgghpbnRlZ3JhbDANBgkqhkiG9w0BAQsFAAOCAQEAOkguqIyf 12 | LfU2y/y7qgMSWuwcRN2X5/gVEptb3mgLL1iAJK9jMscvTBZkgSlTDBcIhdVKr8Ka 13 | F++KVk2ShcG40KjnrQh6bw0/5E3d9/X0b5hCqQ4REa1e4O2c9j0TNz0vLYMT36kS 14 | Jz7z5FGL8GsC6wKmqLiwG8HjtT935ysUWAeY7rajbDaNdvP8kEEATyFOUNEBp5QF 15 | m7ybs+zXhRA+tjkb8Enhx3uN/Ekx7OfDUoaLk2Ue6UOFokkm31Y3hHE3vGkuHie3 16 | SawSUIpLU2QZIahsdWjUc6AjaQB1yOK7Htraqd8Zz44u5v1ccir47DQFG+NxP3Pi 17 | fSHdf2b7Fe1q9g== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /src/teavpn2/client/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__CLIENT__COMMON_H 7 | #define TEAVPN2__CLIENT__COMMON_H 8 | 9 | #include 10 | 11 | struct cli_cfg_sys { 12 | const char *cfg_file; 13 | char data_dir[128]; 14 | uint8_t thread_num; 15 | uint8_t verbose_level; 16 | }; 17 | 18 | 19 | struct cli_cfg_sock { 20 | bool use_encryption; 21 | sock_type type; 22 | char server_addr[64]; 23 | uint16_t server_port; 24 | char event_loop[64]; 25 | }; 26 | 27 | 28 | struct cli_cfg_iface { 29 | bool override_default; 30 | char dev[IFACENAMESIZ]; 31 | 32 | /* 33 | * Only used when net down and reconnect 34 | * (this is filled by the server). 35 | */ 36 | struct if_info iff; 37 | }; 38 | 39 | 40 | struct cli_cfg_auth { 41 | char username[TVPN_MAX_UNAME_LEN]; 42 | char password[TVPN_MAX_PASS_LEN]; 43 | }; 44 | 45 | 46 | struct cli_cfg { 47 | struct cli_cfg_sys sys; 48 | struct cli_cfg_sock sock; 49 | struct cli_cfg_iface iface; 50 | struct cli_cfg_auth auth; 51 | }; 52 | 53 | extern int client_parse_cfg_file(const char *cfg_file, struct cli_cfg *cfg); 54 | extern void teavpn2_client_udp_stop(void); 55 | extern int teavpn2_client_udp_run(struct cli_cfg *cfg); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui_window.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | 9 | 10 | void gui_window_create(struct gui *g) 11 | { 12 | g->window = g_object_new(GTK_TYPE_WINDOW, 13 | "application", g->app.self, 14 | "default-width", GUI_WINDOW_WIDTH, 15 | "default-height", GUI_WINDOW_HEIGHT, 16 | "resizable", FALSE, 17 | NULL); 18 | g->window_notebook = g_object_new(GTK_TYPE_NOTEBOOK, 19 | "parent", g->window, NULL); 20 | g->home = g_object_new(GTK_TYPE_BOX, 21 | "orientation", GTK_ORIENTATION_VERTICAL, 22 | "spacing", 5, NULL); 23 | g->config = g_object_new(GTK_TYPE_BOX, 24 | "orientation", GTK_ORIENTATION_VERTICAL, 25 | "spacing", 5, NULL); 26 | 27 | gtk_notebook_append_page(GTK_NOTEBOOK(g->window_notebook), 28 | GTK_WIDGET(g->home), gtk_label_new("Home")); 29 | gtk_notebook_append_page(GTK_NOTEBOOK(g->window_notebook), 30 | GTK_WIDGET(g->config), gtk_label_new("Configuration")); 31 | 32 | gui_header_create(g); 33 | gui_home_create(g); 34 | gui_config_create(g); 35 | 36 | gtk_window_set_focus(g->window, g->home_btn_connect); 37 | gtk_widget_show_all(GTK_WIDGET(g->window)); 38 | } 39 | -------------------------------------------------------------------------------- /src/teavpn2/gui/events.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Alviro Iskandar Setiawan 4 | */ 5 | 6 | #ifndef TEAVPN2__GUI__EVENTS_H 7 | #define TEAVPN2__GUI__EVENTS_H 8 | 9 | #include 10 | #include 11 | 12 | enum { 13 | CLIENT_EVENT_IDLE = 0, 14 | CLIENT_EVENT_CONNECTED = 1, 15 | CLIENT_EVENT_DISCONNECTED = 2, 16 | CLIENT_EVENT_ERROR = 3, 17 | }; 18 | 19 | extern int g_client_err_code; 20 | extern uint8_t g_client_vpn_state; 21 | extern struct tmutex g_client_vpn_state_lock; 22 | extern int teavpn2_gui_event_init(void); 23 | extern int teavpn2_gui_event_destroy(void); 24 | 25 | #ifdef CONFIG_GUI 26 | 27 | extern gboolean client_callback_event_loop(void *data); 28 | 29 | static inline void set_client_vpn_event(uint8_t state) 30 | { 31 | assert(state < CLIENT_EVENT_ERROR); 32 | mutex_lock(&g_client_vpn_state_lock); 33 | g_client_vpn_state = state; 34 | mutex_unlock(&g_client_vpn_state_lock); 35 | } 36 | 37 | static inline void set_client_vpn_err_event(int err_code) 38 | { 39 | mutex_lock(&g_client_vpn_state_lock); 40 | g_client_err_code = err_code; 41 | g_client_vpn_state = CLIENT_EVENT_ERROR; 42 | mutex_unlock(&g_client_vpn_state_lock); 43 | } 44 | 45 | #else /* #ifdef CONFIG_GUI */ 46 | 47 | static inline void set_client_vpn_event(uint8_t state) 48 | { 49 | (void) state; 50 | } 51 | 52 | static inline void set_client_vpn_err_event(int err_code) 53 | { 54 | (void) err_code; 55 | } 56 | 57 | #endif /* #ifdef CONFIG_GUI */ 58 | 59 | #endif /* #ifndef TEAVPN2__GUI__EVENTS_H */ 60 | -------------------------------------------------------------------------------- /Documentation/developer-certificate-of-origin: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | 13 | Developer's Certificate of Origin 1.1 14 | 15 | By making a contribution to this project, I certify that: 16 | 17 | (a) The contribution was created in whole or in part by me and I 18 | have the right to submit it under the open source license 19 | indicated in the file; or 20 | 21 | (b) The contribution is based upon previous work that, to the best 22 | of my knowledge, is covered under an appropriate open source 23 | license and I have the right under that license to submit that 24 | work with modifications, whether created in whole or in part 25 | by me, under the same open source license (unless I am 26 | permitted to submit under a different license), as indicated 27 | in the file; or 28 | 29 | (c) The contribution was provided directly to me by some other 30 | person who certified (a), (b) or (c) and I have not modified 31 | it. 32 | 33 | (d) I understand and agree that this project and the contribution 34 | are public and that a record of the contribution (including all 35 | personal information I submit with it, including my sign-off) is 36 | maintained indefinitely and may be redistributed consistent with 37 | this project or the open source license(s) involved. 38 | -------------------------------------------------------------------------------- /src/ext/inih/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | The "inih" library is distributed under the New BSD license: 3 | 4 | Copyright (c) 2009, Ben Hoyt 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of Ben Hoyt nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /src/teavpn2/gui/entry.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | 9 | 10 | static void app_startup(GtkApplication *self, void *user_data) 11 | { 12 | struct gui *gui = (struct gui *) user_data; 13 | 14 | gui->app.txt_buffer_log = gtk_text_buffer_new(NULL); 15 | gui->app.cfg_file = g_string_new(GUI_DEFAULT_CONFIG); 16 | gui->app.cli_state = CLIENT_STATE_DISCONNECTED; 17 | 18 | (void) self; 19 | } 20 | 21 | static void app_activate(GtkApplication *self, void *user_data) 22 | { 23 | gui_window_create((struct gui *) user_data); 24 | 25 | (void) self; 26 | } 27 | 28 | static void app_shutdown(GtkApplication *self, void *user_data) 29 | { 30 | g_string_free(((struct gui *) user_data)->app.cfg_file, TRUE); 31 | 32 | (void) self; 33 | } 34 | 35 | int gui_entry(int argc, char *argv[]) 36 | { 37 | int ret; 38 | struct gui gui; 39 | 40 | memset(&gui, 0, sizeof(struct gui)); 41 | 42 | gui.app.self = gtk_application_new(GUI_ID, G_APPLICATION_FLAGS_NONE); 43 | g_object_connect(gui.app.self, 44 | "signal::startup", app_startup, &gui, 45 | "signal::activate", app_activate, &gui, 46 | "signal::shutdown", app_shutdown, &gui, NULL); 47 | gdk_threads_add_timeout_full(G_PRIORITY_HIGH_IDLE, 100, 48 | client_callback_event_loop, &gui, NULL); 49 | if ((ret = teavpn2_gui_event_init())) { 50 | g_object_unref(gui.app.self); 51 | return ret; 52 | } 53 | 54 | gui_pr_buffer_init(4096); 55 | ret = g_application_run(G_APPLICATION(gui.app.self), argc, argv); 56 | g_object_unref(gui.app.self); 57 | teavpn2_gui_event_destroy(); 58 | 59 | return ret; 60 | } 61 | -------------------------------------------------------------------------------- /data/server/default_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9zSX1fqMzZqeA 3 | vlUcT/qy9x8qluVngayHcw0OyciEQCa9jIwA4rH1NRpEfYA5iLVMVwKb9YxRSquw 4 | AhDAcmebPdkcyQ8t+OCyR2FbTgdohxqbWz/M5j6YKR30WB/rE9hciZqlh+IBDREo 5 | x+FoqUH1K9/JtRxbWLrxL+pdphpHdcamWs0z/IaPuPjh/HqWJEud6ikIBwxALHD4 6 | IiGpj4pGDeJMI3FmnjEua85TYKqzYquVfij/ryUWKjPRAyX8755nPimMrmUtfupV 7 | KRvVQzmJTZCXtxCXD8JPbVzqBKPCE0GFtyGWYqL+v8LCAjlPJd2zyrMKRg3SBYL4 8 | mBCUipabAgMBAAECggEAb/000YZYK0L9wHQNafgy9xfi1tFUKF+V+hHDeZ9L5eSo 9 | Nx1PmlIoKIOdrR3T456rQpDwZC9dDLYfWWnOko6uXSYADgEAKOL5SZfedO40ZtOY 10 | umP74B1Wf5d7Kio5Iw6BkhBLJDJqAq7AGizd7lA4L74kfDKVubxACYF5KqqQROrQ 11 | F2w5S4lyeOppSCZKoEZ7XGuoni3BQ9dgtwTUn5MDnm6pvxUEj+BdamxebxzunZG8 12 | 9RrQqF0ocW9rpSi1KSZary+uq7v8ftnDJf2IwD/KiaZ+0pdMlipZXrZK8ZnjcsIF 13 | 8yOk52dhNyTbUvIGtn+lBA0XH5tj3rtfqe47h6Qq8QKBgQDfHfcrckzAJIplaCPn 14 | Of5tzW1Wtpp6PajiYZTE/LrUyiu22U2zP3PjR3y0OlHC2cRIdS8rQK/PglyO1XnE 15 | 33dLdJ+ivTufSqw0uPoVH8gLWeKj12v/+Qe7Y/L2uTOYgbhqoxZ4z8PFkDlkJIY7 16 | YSo6m72sviCkFh2T3qYaI3HWUwKBgQDZxjUWIuF8nQAZr3W+KAhDT4tHrwM+xoy3 17 | PZySOVXxPFcObvvFp4vg+te2oab5vBplHrU8jTUZVCuDVZwnAoCN261xbI6EYRwr 18 | DI8AlHgEb0vwT/IzF7uKDdwJmZqIO0fT7nlVn+xPx9Gh8C7Eu8QEQV4OSJS+R+q5 19 | okVXmGqlmQKBgQCczUhqBjgeSQ/iWJ/y3vUKYfbXnBlQk4jfIEkXb6414ad7J5jN 20 | wmhFcHYZi2ruj3C8o2507U8hfLJjx29+hrYmF14hVvBR8H65xs4qjl1ebNs03i6O 21 | hIuEjKex8VJrea8LcnZWjV0+uS88S9byYET3T4CrqCr0Zn5+71i4wfQ89QKBgGJv 22 | VBmQZKnF8YCqrabpQ2rhboxVUDs01fARuk7h+bXT12nfwpAB/pkP6SdVBDuHycqB 23 | Sdx321N6lzyDGtULLX5xmIFXV0gA+RGAWLcjZOhkQkf6avirNordXuM7+fywBvSF 24 | q3SHl/Ir2NbA0PL+CEkAHvqH1iv4J+IGth809qepAoGAA3SL5a0D65heoK83wTSQ 25 | r1DxRfIyDtG3zcLGOs2kU3sYMCTfCtGJJd2oUJxAsFTVWJu9DShWpie3CpqC9ZWk 26 | J3tPDaotLRpLaZ+ZheSNaek1MtwxIhV9hWjSnWW1vrGxjz3q+X1gsoSDl8xe5CQA 27 | auXMyrcwpDzvBDl7bkEo1wc= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /src/teavpn2/arch/generic/linux.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__ARCH__GENERIC__LINUX_H 7 | #define TEAVPN2__ARCH__GENERIC__LINUX_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static inline int __sys_epoll_wait(int epfd, struct epoll_event *events, 19 | int maxevents, int timeout) 20 | { 21 | int ret; 22 | ret = epoll_wait(epfd, events, maxevents, timeout); 23 | return unlikely(ret == -1) ? -errno : ret; 24 | } 25 | 26 | static inline ssize_t __sys_read(int fd, void *buf, size_t len) 27 | { 28 | ssize_t ret; 29 | ret = read(fd, buf, len); 30 | return unlikely(ret == -1) ? (ssize_t) -errno : ret; 31 | } 32 | 33 | static inline ssize_t __sys_write(int fd, const void *buf, size_t len) 34 | { 35 | ssize_t ret; 36 | ret = write(fd, buf, len); 37 | return unlikely(ret == -1) ? (ssize_t) -errno : ret; 38 | } 39 | 40 | static inline ssize_t __sys_recvfrom(int sockfd, void *buf, size_t len, 41 | int flags, struct sockaddr *src_addr, 42 | socklen_t *addrlen) 43 | { 44 | ssize_t ret; 45 | ret = recvfrom(sockfd, buf, len, flags, src_addr, addrlen); 46 | return unlikely(ret == -1) ? (ssize_t) -errno : ret; 47 | } 48 | 49 | static inline ssize_t __sys_sendto(int sockfd, const void *buf, size_t len, 50 | int flags, const struct sockaddr *dest_addr, 51 | socklen_t addrlen) 52 | { 53 | ssize_t ret; 54 | ret = sendto(sockfd, buf, len, flags, dest_addr, addrlen); 55 | return unlikely(ret == -1) ? (ssize_t) -errno : ret; 56 | } 57 | 58 | static inline int __sys_close(int fd) 59 | { 60 | int ret; 61 | ret = close(fd); 62 | return unlikely(ret == -1) ? -errno : ret; 63 | } 64 | 65 | #endif /* #ifndef TEAVPN2__ARCH__GENERIC__LINUX_H */ 66 | -------------------------------------------------------------------------------- /.github/work.bak: -------------------------------------------------------------------------------- 1 | 2 | name: C/C++ (Linux) 3 | 4 | on: 5 | pull_request: 6 | branches: 7 | - master 8 | push: 9 | branches: 10 | - master 11 | - dev_001 12 | 13 | jobs: 14 | main: 15 | name: Build and Test 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: [ ubuntu-18.04 ] 20 | 21 | target: [ clang-10.0.1, g++-10] 22 | 23 | include: 24 | - { target: clang-10.0.1, compiler: clang, cxx-version: 10.0.1 } 25 | - { target: g++-10, compiler: g++, cxx-version: 10.2.0 } 26 | 27 | runs-on: ubuntu-18.04 28 | steps: 29 | - uses: actions/checkout@v2 30 | 31 | # - name: sudo apt-get install valgrind -y 32 | # run: sudo apt-get install valgrind -y 33 | 34 | # #################### Unit tests and codecov #################### 35 | # - name: Run unit tests and gcov 36 | # run: env DEFAULT_OPTIMIZATION=-O1 make test -j$(nproc) 37 | # - name: Codecov report 38 | # run: bash .github/codecov.sh -X gcov 39 | # #################### End of unit tests and codecov #################### 40 | 41 | # - name: Cleaning unit tests and codecov 42 | # run: make clean; make clean_gcov 43 | 44 | - name: Build release binary 45 | run: env RELEASE_MODE=1 make -j$(nproc) 46 | 47 | ## Store generated artifacts 48 | - name: Store teavpn2 artifact 49 | uses: actions/upload-artifact@v2 50 | with: 51 | name: teavpn2 52 | path: teavpn2 53 | 54 | 55 | 56 | # - name: Build release binary 57 | # run: env RELEASE_MODE=1 DEFAULT_OPTIMIZATION=-O3 make -j$(nproc) 58 | 59 | # - name: Strip the release binaries 60 | # run: | 61 | # strip -s teavpn_server 62 | # strip -s teavpn_client 63 | 64 | # - name: Store teavpn_client artifact 65 | # uses: actions/upload-artifact@v2 66 | # with: 67 | # name: teavpn_client 68 | # path: teavpn_client 69 | -------------------------------------------------------------------------------- /src/teavpn2/allocator.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | static __always_inline void *mem_align(void *src, size_t size) 15 | { 16 | void *ret; 17 | uint8_t shift; 18 | 19 | ret = (void *)(((uintptr_t)src + size) & ~(size - 1ul)); 20 | shift = (uint8_t)((uintptr_t)ret - (uintptr_t)src); 21 | memcpy((void *)((uintptr_t)ret - 1ul), &shift, 1ul); 22 | 23 | assert(((uintptr_t)ret % size) == 0); 24 | 25 | return ret; 26 | } 27 | 28 | 29 | noinline void *al64_calloc(size_t nmemb, size_t size) 30 | { 31 | void *orig; 32 | size_t real_size = 0; 33 | 34 | if (unlikely(__builtin_mul_overflow(nmemb, size, &real_size))) { 35 | errno = ENOMEM; 36 | return NULL; 37 | } 38 | 39 | orig = calloc(1u, real_size + 64ul); 40 | if (unlikely(!orig)) 41 | return NULL; 42 | 43 | return mem_align(orig, 64ul); 44 | } 45 | 46 | 47 | noinline void *al64_malloc(size_t size) 48 | { 49 | void *orig; 50 | 51 | orig = malloc(size + 64ul); 52 | if (unlikely(!orig)) 53 | return NULL; 54 | 55 | return mem_align(orig, 64ul); 56 | } 57 | 58 | 59 | noinline void al64_free(void *user) 60 | { 61 | void *orig; 62 | uint8_t shift; 63 | 64 | if (unlikely(!user)) 65 | return; 66 | 67 | memcpy(&shift, (void *)((uintptr_t)user - 1ul), 1ul); 68 | orig = (void *)((uintptr_t)user - (uintptr_t)shift); 69 | free(orig); 70 | } 71 | 72 | 73 | noinline void *al64_realloc(void *user, size_t new_size) 74 | { 75 | void *tmp; 76 | void *orig; 77 | uint8_t shift; 78 | 79 | if (unlikely(!user)) 80 | return al64_malloc(new_size); 81 | 82 | memcpy(&shift, (void *)((uintptr_t)user - 1ul), 1ul); 83 | orig = (void *)((uintptr_t)user - (uintptr_t)shift); 84 | 85 | tmp = realloc(orig, new_size + 64ul); 86 | if (unlikely(!tmp)) 87 | return NULL; 88 | 89 | return mem_align(tmp, 64ul); 90 | } 91 | -------------------------------------------------------------------------------- /src/teavpn2/main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #ifdef CONFIG_GUI 10 | #undef fallthrough 11 | #include 12 | #endif 13 | 14 | void show_version(void) 15 | { 16 | puts("TeaVPN2 " TEAVPN2_VERSION); 17 | puts("Copyright (C) 2021 Ammar Faizi\n" 18 | "This is free software; see the source for copying conditions. There is NO\n" 19 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); 20 | } 21 | 22 | 23 | static void show_general_usage(const char *app) 24 | { 25 | printf(" Usage: %s [client|server] [options]\n\n", app); 26 | printf(" See:\n"); 27 | printf(" [Help]\n"); 28 | printf(" %s server --help\n", app); 29 | printf(" %s client --help\n", app); 30 | printf("\n"); 31 | printf(" [Version]\n"); 32 | printf(" %s --version, -V\n\n", app); 33 | } 34 | 35 | static int run_teavpn2(int argc, char *argv[]) 36 | { 37 | if (!strcmp("server", argv[1])) 38 | return run_server(argc, argv); 39 | 40 | if (!strcmp("client", argv[1])) 41 | return run_client(argc, argv); 42 | 43 | if (!strcmp("gui", argv[1])) { 44 | #ifdef CONFIG_GUI 45 | return gui_entry(1, argv); 46 | #else 47 | printf("This binary is not compiled with GUI support!\n"); 48 | return 1; 49 | #endif 50 | } 51 | 52 | if (!strcmp("--version", argv[1]) || !strcmp("-V", argv[1])) { 53 | show_version(); 54 | return 0; 55 | } 56 | 57 | if (!strcmp("--help", argv[1]) || !strcmp("-H", argv[1])) { 58 | show_general_usage(argv[0]); 59 | return 0; 60 | } 61 | 62 | printf("Invalid command: %s\n", argv[1]); 63 | show_general_usage(argv[0]); 64 | return 1; 65 | } 66 | 67 | 68 | int main(int argc, char *argv[]) 69 | { 70 | if (setvbuf(stdout, NULL, _IOLBF, 2048)) 71 | printf("Cannot set stdout buffer: %s\n", strerror(errno)); 72 | 73 | if (setvbuf(stderr, NULL, _IOLBF, 2048)) 74 | printf("Cannot set stderr buffer: %s\n", strerror(errno)); 75 | 76 | if (argc == 1) { 77 | show_general_usage(argv[0]); 78 | return 0; 79 | } 80 | 81 | #ifdef CONFIG_HPC_EMERGENCY 82 | if (emerg_init_handler(EMERG_INIT_BUG | EMERG_INIT_WARN)) { 83 | int ret = errno; 84 | printf("Cannot set emerg handler: %s\n", strerror(ret)); 85 | return -ret; 86 | } 87 | #endif 88 | 89 | return run_teavpn2(argc, argv); 90 | } 91 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui_config.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | 9 | 10 | static void btn_save_callback(GtkWidget *self, void *user_data) 11 | { 12 | pr_notice("Save"); 13 | (void) self; 14 | (void) user_data; 15 | } 16 | 17 | static void btn_save_as_callback(GtkWidget *self, void *user_data) 18 | { 19 | pr_notice("Save As"); 20 | (void) self; 21 | (void) user_data; 22 | } 23 | 24 | static void btn_cancel_callback(GtkWidget *self, void *user_data) 25 | { 26 | pr_notice("Cancel"); 27 | (void) self; 28 | (void) user_data; 29 | } 30 | 31 | void gui_config_create(struct gui *g) 32 | { 33 | GtkBox *config = g->config; 34 | GtkBox *box_btn; 35 | GtkWidget *frame_conf, *scroller; 36 | GtkWidget *btn_cancel; 37 | const struct gui_callback callbacks[] = { 38 | GUI_CALLBACK(&g->config_btn_save, "clicked", 39 | btn_save_callback, NULL), 40 | GUI_CALLBACK(&g->config_btn_save_as, "clicked", 41 | btn_save_as_callback, NULL), 42 | GUI_CALLBACK(&btn_cancel, "clicked", 43 | btn_cancel_callback, NULL), 44 | }; 45 | 46 | 47 | box_btn = g_object_new(GTK_TYPE_BOX, 48 | "orientation", GTK_ORIENTATION_HORIZONTAL, 49 | "margin-top", 5, 50 | "halign", GTK_ALIGN_CENTER, 51 | "spacing", 5, NULL); 52 | g->config_btn_save = g_object_new(GTK_TYPE_BUTTON, 53 | "label", "Save", NULL); 54 | g->config_btn_save_as = g_object_new(GTK_TYPE_BUTTON, 55 | "label", "Save As", NULL); 56 | btn_cancel = g_object_new(GTK_TYPE_BUTTON, 57 | "label", "Cancel", NULL); 58 | 59 | 60 | gtk_box_pack_start(box_btn, btn_cancel, FALSE, FALSE, 0); 61 | gtk_box_pack_start(box_btn, g->config_btn_save_as, FALSE, FALSE, 0); 62 | gtk_box_pack_start(box_btn, g->config_btn_save, FALSE, FALSE, 0); 63 | 64 | 65 | frame_conf = g_object_new(GTK_TYPE_FRAME, 66 | "margin-bottom", 5, 67 | "margin-start", 5, 68 | "margin-end", 5, NULL); 69 | scroller = g_object_new(GTK_TYPE_SCROLLED_WINDOW, 70 | "parent", frame_conf, NULL); 71 | g_object_new(GTK_TYPE_LABEL, 72 | "label", "Coming soon...", 73 | "parent", scroller, 74 | "vexpand", TRUE, NULL); 75 | 76 | gtk_box_pack_start(config, GTK_WIDGET(box_btn), FALSE, FALSE, 0); 77 | gtk_box_pack_start(config, frame_conf, TRUE, TRUE, 0); 78 | 79 | gui_utils_set_callback(callbacks, G_N_ELEMENTS(callbacks)); 80 | 81 | gtk_widget_set_sensitive(GTK_WIDGET(g->config), FALSE); 82 | } 83 | -------------------------------------------------------------------------------- /Documentation/SubmittingPatches: -------------------------------------------------------------------------------- 1 | To improve tracking of who did what, we use the "sign-off" procedure 2 | introduced by the Linux kernel. The sign-off is a simple line at the 3 | end of the explanation for the patch, which certifies that you wrote 4 | it or otherwise have the right to pass it on as an open-source patch. 5 | The rules are pretty simple: if you can certify the Developer's 6 | Certificate of Origin 1.1 (see the developer-certificate-of-origin 7 | file), then you just add a line saying 8 | 9 | Signed-off-by: Random J Developer 10 | 11 | using your real name (sorry, no pseudonyms or anonymous 12 | contributions). This line can be automatically added by git if you 13 | run the git-commit command with the -s option. 14 | 15 | If you like, you can put extra tags at the end: 16 | 17 | 1. "Reported-by:" is used to credit someone who found the bug that 18 | the patch attempts to fix. 19 | 2. "Acked-by:" says that the person who is more familiar with the area 20 | the patch attempts to modify liked the patch. 21 | 3. "Reviewed-by:", unlike the other tags, can only be offered by the 22 | reviewer and means that she is completely satisfied that the patch 23 | is ready for application. It is usually offered only after a 24 | detailed review. 25 | 4. "Tested-by:" is used to indicate that the person applied the patch 26 | and found it to have the desired effect. 27 | 28 | You can also create your own tag or use one that's in common usage 29 | such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:". 30 | 31 | Sometimes you need to slightly modify patches you receive in order to 32 | merge them, because the code is not exactly the same in your tree and 33 | the submitters'. If you stick strictly to rule (c), you should ask the 34 | submitter to rediff, but this is a totally counter-productive waste of 35 | time and energy. Rule (b) allows you to adjust the code, but then it 36 | is very impolite to change one submitter's code and make him endorse 37 | your bugs. To solve this problem, it is recommended that you add a 38 | line between the last Signed-off-by header and yours, indicating the 39 | nature of your changes. While there is nothing mandatory about this, 40 | it seems like prepending the description with your mail and/or name, 41 | all enclosed in square brackets, is noticeable enough to make it 42 | obvious that you are responsible for last-minute changes. Example : 43 | 44 | Signed-off-by: Random J Developer 45 | [lucky@maintainer.example.org: struct foo moved from foo.c to foo.h] 46 | Signed-off-by: Lucky K Maintainer 47 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #ifndef TEAVPN2__GUI__GUI_H 8 | #define TEAVPN2__GUI__GUI_H 9 | 10 | #ifdef CONFIG_GUI 11 | #include 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #define GUI_ID "com.teainside.teavpn2" 19 | #define GUI_PROGRAM_NAME "TeaVPN2" 20 | #define GUI_WINDOW_TITLE "TeaVPN2 Client" 21 | #define GUI_WINDOW_WIDTH 500 22 | #define GUI_WINDOW_HEIGHT 600 23 | #define GUI_DEFAULT_CONFIG "/etc/teavpn2/client.ini" 24 | 25 | #define GUI_CALLBACK(SF, SG, FC, DT)\ 26 | { .instance = (void **)SF, .signal = SG, .func = G_CALLBACK(FC), .data = DT } 27 | 28 | 29 | struct app; 30 | struct gui; 31 | struct gui_callback; 32 | 33 | #ifdef CONFIG_GUI 34 | enum cli_state { 35 | CLIENT_STATE_CONNECTED, 36 | CLIENT_STATE_DISCONNECTED, 37 | }; 38 | 39 | struct app { 40 | GtkApplication *self; 41 | 42 | GtkTextBuffer *txt_buffer_log; 43 | GString *cfg_file; 44 | enum cli_state cli_state; 45 | struct cli_cfg cli_cfg; 46 | }; 47 | 48 | struct gui { 49 | struct app app; 50 | 51 | /* window */ 52 | GtkWindow *window; 53 | GtkWidget *window_notebook; 54 | 55 | /* header */ 56 | GtkHeaderBar *header; 57 | GtkWidget *header_btn_open; 58 | GtkWidget *header_btn_about; 59 | 60 | /* home */ 61 | GtkBox *home; 62 | GtkWidget *home_lbl_path; 63 | GtkWidget *home_btn_connect; 64 | GtkWidget *home_txt_logger; 65 | GtkWidget *home_lbl_status; 66 | 67 | /* config */ 68 | GtkBox *config; 69 | GtkWidget *config_btn_save; 70 | GtkWidget *config_btn_save_as; 71 | 72 | /* */ 73 | }; 74 | 75 | struct gui_callback { 76 | void **instance; 77 | const char *signal; 78 | void (*func)(void); 79 | void *data; 80 | }; 81 | #endif /* #ifdef CONFIG_GUI */ 82 | 83 | 84 | /* entry.c */ 85 | extern int gui_entry(int argc, char *argv[]); 86 | 87 | 88 | /* gui_window.c */ 89 | extern void gui_window_create(struct gui *g); 90 | 91 | 92 | /* gui_header.c */ 93 | extern void gui_header_create(struct gui *g); 94 | 95 | 96 | /* gui_home.c */ 97 | extern void gui_home_create(struct gui *g); 98 | extern void gui_home_insert_txt_logger(struct gui *g, const char *msg); 99 | 100 | 101 | /* gui_config.c */ 102 | extern void gui_config_create(struct gui *g); 103 | 104 | 105 | /* gui_utils.c */ 106 | extern void gui_utils_set_callback(const struct gui_callback *dest, size_t size); 107 | 108 | #endif /* #ifndef #ifndef TEAVPN2__GUI__GUI_H */ 109 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui_header.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | 9 | 10 | static void btn_open_callback(GtkWidget *self, void *user_data) 11 | { 12 | struct gui *gui = (struct gui *) user_data; 13 | GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; 14 | char *file_name; 15 | GtkWidget *file_dialog; 16 | GtkFileChooser *file_chooser; 17 | GtkFileFilter *file_filter; 18 | 19 | file_filter = gtk_file_filter_new(); 20 | file_dialog = gtk_file_chooser_dialog_new("Open Configuration File", 21 | gui->window, action, "_Cancel", 22 | GTK_RESPONSE_CANCEL, "_Open", 23 | GTK_RESPONSE_ACCEPT, NULL); 24 | file_chooser = GTK_FILE_CHOOSER(file_dialog); 25 | 26 | gtk_file_filter_set_name(file_filter, "Configuration file"); 27 | gtk_file_filter_add_pattern(file_filter, "*.ini"); 28 | gtk_file_chooser_add_filter(file_chooser, file_filter); 29 | 30 | if (gtk_dialog_run(GTK_DIALOG(file_dialog)) == GTK_RESPONSE_ACCEPT) { 31 | file_name = gtk_file_chooser_get_filename(file_chooser); 32 | g_string_assign(gui->app.cfg_file, file_name); 33 | gtk_label_set_label(GTK_LABEL(gui->home_lbl_path), 34 | gui->app.cfg_file->str); 35 | g_free(file_name); 36 | } 37 | 38 | gtk_widget_destroy(file_dialog); 39 | (void) self; 40 | } 41 | 42 | static void btn_about_callback(GtkWidget *self, void *user_data) 43 | { 44 | gtk_show_about_dialog(GTK_WINDOW(user_data), 45 | "program-name", GUI_PROGRAM_NAME, 46 | "version", TEAVPN2_VERSION, 47 | "license-type", GTK_LICENSE_GPL_2_0, 48 | "website", "https://github.com/teainside/teavpn2", 49 | NULL); 50 | (void) self; 51 | } 52 | 53 | void gui_header_create(struct gui *g) 54 | { 55 | const struct gui_callback callbacks[] = { 56 | GUI_CALLBACK(&g->header_btn_open, "clicked", 57 | btn_open_callback, g), 58 | GUI_CALLBACK(&g->header_btn_about, "clicked", 59 | btn_about_callback, g->window), 60 | }; 61 | 62 | 63 | g->header = g_object_new(GTK_TYPE_HEADER_BAR, 64 | "title", GUI_WINDOW_TITLE, 65 | "show-close-button", TRUE, NULL); 66 | g->header_btn_open = gtk_button_new_from_icon_name("document-open", 67 | GTK_ICON_SIZE_MENU); 68 | g->header_btn_about = gtk_button_new_from_icon_name("help-about", 69 | GTK_ICON_SIZE_MENU); 70 | 71 | 72 | gtk_header_bar_pack_start(g->header, g->header_btn_open); 73 | gtk_header_bar_pack_end(g->header, g->header_btn_about); 74 | gtk_window_set_titlebar(g->window, GTK_WIDGET(g->header)); 75 | 76 | gui_utils_set_callback(callbacks, G_N_ELEMENTS(callbacks)); 77 | } 78 | -------------------------------------------------------------------------------- /src/teavpn2/stack.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | #ifndef TEAVPN2__STACK_H 6 | #define TEAVPN2__STACK_H 7 | 8 | #include 9 | #include 10 | 11 | 12 | struct bt_stack { 13 | uint16_t sp; 14 | uint16_t max_sp; 15 | uint16_t *arr; 16 | }; 17 | 18 | 19 | static inline int32_t bt_stack_pop(struct bt_stack *stk) 20 | { 21 | int32_t ret; 22 | uint16_t sp = stk->sp; 23 | 24 | if (sp == stk->max_sp) 25 | /* Stack is empty. */ 26 | return -1; 27 | 28 | ret = (int32_t)stk->arr[sp++]; 29 | stk->sp = sp; 30 | return ret; 31 | } 32 | 33 | 34 | static inline int32_t bt_stack_push(struct bt_stack *stk, uint16_t n) 35 | { 36 | uint16_t sp = stk->sp; 37 | 38 | if (sp == 0) 39 | /* Stack is full. */ 40 | return -1; 41 | 42 | stk->arr[--sp] = n; 43 | stk->sp = sp; 44 | return (int32_t)n; 45 | } 46 | 47 | 48 | static inline struct bt_stack *bt_stack_init(struct bt_stack *stk, 49 | uint16_t capacity) 50 | { 51 | if (unlikely(!stk)) { 52 | errno = -EINVAL; 53 | return NULL; 54 | } 55 | 56 | stk->arr = calloc_wrp(capacity, sizeof(*stk->arr)); 57 | if (unlikely(!stk->arr)) 58 | return NULL; 59 | 60 | stk->sp = capacity; 61 | stk->max_sp = capacity; 62 | return stk; 63 | } 64 | 65 | 66 | static inline void bt_stack_destroy(struct bt_stack *stk) 67 | { 68 | if (stk->arr) 69 | al64_free(stk->arr); 70 | } 71 | 72 | 73 | static inline void bt_stack_test(__maybe_unused struct bt_stack * stk) 74 | { 75 | #ifndef NDEBUG 76 | uint16_t i, j, capacity = stk->max_sp; 77 | 78 | assert(capacity > 0); 79 | 80 | for (i = 0; i < capacity; i++) { 81 | /* 82 | * Test stack is empty. 83 | */ 84 | assert(bt_stack_pop(stk) == -1); 85 | __asm__ volatile("":"+r"(stk)::"memory"); 86 | } 87 | 88 | for (i = 0; i < capacity; i++) { 89 | /* 90 | * Test fill the stack. 91 | */ 92 | assert(bt_stack_push(stk, i) == (int32_t)i); 93 | __asm__ volatile("":"+r"(stk)::"memory"); 94 | } 95 | 96 | for (i = 0; i < capacity; i++) { 97 | /* 98 | * Test stack is full. 99 | */ 100 | assert(bt_stack_push(stk, i) == -1); 101 | __asm__ volatile("":"+r"(stk)::"memory"); 102 | } 103 | 104 | for (j = capacity - 1, i = 0; i < capacity; i++, j--) { 105 | /* 106 | * Test stack is FIFO. 107 | */ 108 | assert(bt_stack_pop(stk) == j); 109 | __asm__ volatile("":"+r"(stk)::"memory"); 110 | } 111 | 112 | for (i = 0; i < capacity; i++) { 113 | /* 114 | * Test stack is empty. 115 | */ 116 | assert(bt_stack_pop(stk) == -1); 117 | __asm__ volatile("":"+r"(stk)::"memory"); 118 | } 119 | 120 | pr_debug("bt_stack_test success!"); 121 | #endif 122 | } 123 | 124 | 125 | #endif /* #ifndef TEAVPN2__STACK_H */ 126 | -------------------------------------------------------------------------------- /src/teavpn2/print.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Printing header 4 | * 5 | * Copyright (C) 2021 Ammar Faizi 6 | */ 7 | 8 | #ifndef TEAVPN2__PRINT_H 9 | #define TEAVPN2__PRINT_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | extern uint8_t __notice_level; 18 | 19 | extern void __printf(1, 2) 20 | __pr_notice(const char *fmt, ...); 21 | 22 | 23 | extern void __printf(1, 2) 24 | __pr_error(const char *fmt, ...); 25 | 26 | 27 | extern void __printf(1, 2) 28 | __pr_emerg(const char *fmt, ...); 29 | 30 | 31 | extern void __printf(1, 2) 32 | __pr_debug(const char *fmt, ...); 33 | 34 | 35 | extern void __printf(1, 2) 36 | __pr_warn(const char *fmt, ...); 37 | 38 | 39 | extern void __printf(3, 4) __noreturn 40 | __panic(const char *file, int lineno, const char *fmt, ...); 41 | 42 | #ifdef CONFIG_GUI 43 | extern int gui_pr_buffer_init(size_t len); 44 | extern size_t gui_pr_consume_buffer(char *buffer, size_t maxlen); 45 | extern size_t gui_pr_queue_buffer(const char *buffer, size_t len); 46 | #else /* #ifdef CONFIG_GUI */ 47 | static inline int gui_pr_buffer_init(size_t len) 48 | { 49 | (void) len; 50 | return 0; 51 | } 52 | 53 | static inline size_t gui_pr_consume_buffer(char *buffer, size_t maxlen) 54 | { 55 | (void) buffer; 56 | (void) maxlen; 57 | return 0; 58 | } 59 | 60 | static inline size_t gui_pr_queue_buffer(const char *buffer, size_t len) 61 | { 62 | (void) buffer; 63 | (void) len; 64 | return 0; 65 | } 66 | #endif /* #ifdef CONFIG_GUI */ 67 | 68 | static inline void set_notice_level(uint8_t level) 69 | { 70 | __notice_level = level; 71 | } 72 | 73 | 74 | #define PRERF "(errno=%d) %s" 75 | #define PREAR(NUM) ((int)(NUM)), strerror((int)(NUM)) 76 | 77 | #define pr_err __pr_error 78 | #define pr_error __pr_error 79 | #define pr_notice __pr_notice 80 | #define pr_emerg __pr_emerg 81 | #define pr_dbg __pr_debug 82 | #define pr_warn __pr_warn 83 | 84 | #ifdef CONFIG_HPC_EMERGENCY 85 | #define panic(...) \ 86 | do { \ 87 | __emerg_release_bug = true; \ 88 | BUG_ON(1); \ 89 | __panic(__FILE__, __LINE__, __VA_ARGS__); \ 90 | } while (0) 91 | #else 92 | #define panic(...) \ 93 | do { \ 94 | __panic(__FILE__, __LINE__, __VA_ARGS__); \ 95 | } while (0) 96 | #endif 97 | 98 | 99 | #ifdef NDEBUG 100 | /* No debug */ 101 | #define pr_debug(...) do { } while (0) 102 | #else 103 | /* Yes, do debug */ 104 | #define pr_debug __pr_debug 105 | #endif 106 | 107 | #ifndef MAX_NOTICE_LEVEL 108 | /* 109 | * prl_notice(n, "msg") where n is greater than `MAX_NOTICE_LEVEL` 110 | * will not be printed even if the `__notice_level` is greater than 111 | * n. 112 | * 113 | * The evaluation of `MAX_NOTICE_LEVEL` could be at compile time! 114 | */ 115 | #define MAX_NOTICE_LEVEL 5 116 | #endif 117 | 118 | #define DEFAULT_NOTICE_LEVEL 5 119 | 120 | #define prl_notice(LEVEL, ...) \ 121 | do { \ 122 | uint8_t __lc_notice_level = (uint8_t)(LEVEL); \ 123 | if (__lc_notice_level > (MAX_NOTICE_LEVEL)) \ 124 | break; \ 125 | if (__lc_notice_level > __notice_level) \ 126 | break; \ 127 | pr_notice(__VA_ARGS__); \ 128 | } while (0) 129 | 130 | #endif /* #ifndef TEAVPN2__PRINT_H */ 131 | -------------------------------------------------------------------------------- /src/teavpn2/mutex.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | #ifndef TEAVPN2__MUTEX_H 6 | #define TEAVPN2__MUTEX_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define MUTEX_PARANOIA 15 | #define MUTEX_LEAK_ASSERT 16 | 17 | struct tmutex { 18 | pthread_mutex_t mutex; 19 | 20 | #ifdef MUTEX_LEAK_ASSERT 21 | void *__leak_assert; 22 | #endif 23 | 24 | #ifdef MUTEX_PARANOIA 25 | _Atomic(bool) __locked; 26 | bool __need_destroy; 27 | #endif 28 | }; 29 | 30 | #define MUTEX_INITIALIZER \ 31 | { \ 32 | .mutex = PTHREAD_MUTEX_INITIALIZER \ 33 | } 34 | 35 | #define DEFINE_MUTEX(V) struct tmutex V = MUTEX_INITIALIZER 36 | 37 | 38 | #ifdef MUTEX_PARANOIA 39 | static __always_inline bool lockdep_is_held(struct tmutex *t) 40 | { 41 | return atomic_load(&t->__locked); 42 | } 43 | 44 | static __always_inline void lockdep_set_locked(struct tmutex *t, bool l) 45 | { 46 | atomic_store(&t->__locked, l); 47 | } 48 | 49 | #define lockdep_assert(cond) \ 50 | do { \ 51 | bool __cond = (cond); \ 52 | if (likely(__cond)) \ 53 | break; \ 54 | puts("Lockdep assertion failure: " #cond); \ 55 | BUG(); \ 56 | } while (0) 57 | 58 | #define lockdep_assert_held(m) \ 59 | lockdep_assert(lockdep_is_held(m)) 60 | 61 | #define lockdep_assert_not_held(m) \ 62 | lockdep_assert(!lockdep_is_held(m)) 63 | 64 | #else /* #ifdef MUTEX_PARANOIA */ 65 | static __always_inline bool lockdep_is_held(struct tmutex *t) 66 | { 67 | (void)t; 68 | return true; 69 | } 70 | 71 | static __always_inline void lockdep_set_locked(struct tmutex *t, bool l) 72 | { 73 | (void)t; 74 | (void)l; 75 | } 76 | 77 | #define lockdep_assert(cond) do { } while (0) 78 | #define lockdep_assert_held(m) do { } while (0) 79 | #define lockdep_assert_not_held(m) do { } while (0) 80 | #endif /* #ifdef MUTEX_PARANOIA */ 81 | 82 | static __always_inline int mutex_init(struct tmutex *m, 83 | const pthread_mutexattr_t *attr) 84 | { 85 | int ret = pthread_mutex_init(&m->mutex, attr); 86 | if (unlikely(ret)) { 87 | pr_err("pthread_mutex_init(): " PRERF, PREAR(ret)); 88 | return -ret; 89 | } 90 | 91 | #ifdef MUTEX_LEAK_ASSERT 92 | m->__leak_assert = malloc(1); 93 | BUG_ON(!m->__leak_assert); 94 | #endif 95 | 96 | #ifdef MUTEX_PARANOIA 97 | m->__need_destroy = true; 98 | #endif 99 | 100 | lockdep_set_locked(m, false); 101 | return 0; 102 | } 103 | 104 | static __always_inline int mutex_lock(struct tmutex *m) 105 | __acquires(m) 106 | { 107 | int ret = pthread_mutex_lock(&m->mutex); 108 | lockdep_set_locked(m, true); 109 | return ret; 110 | } 111 | 112 | static __always_inline int mutex_unlock(struct tmutex *m) 113 | __releases(m) 114 | { 115 | int ret; 116 | 117 | #ifdef MUTEX_PARANOIA 118 | if (unlikely(!lockdep_is_held(m))) { 119 | puts("Attempting to unlock non-locked mutex"); 120 | BUG(); 121 | } 122 | #endif 123 | ret = pthread_mutex_unlock(&m->mutex); 124 | lockdep_set_locked(m, false); 125 | return ret; 126 | } 127 | 128 | static __always_inline int mutex_trylock(struct tmutex *m) 129 | { 130 | int ret = pthread_mutex_trylock(&m->mutex); 131 | if (!ret) 132 | lockdep_set_locked(m, true); 133 | return ret; 134 | } 135 | 136 | static __always_inline int mutex_destroy(struct tmutex *m) 137 | { 138 | BUG_ON(!m->__need_destroy); 139 | 140 | #ifdef MUTEX_LEAK_ASSERT 141 | free(m->__leak_assert); 142 | #endif 143 | lockdep_assert_not_held(m); 144 | return pthread_mutex_destroy(&m->mutex); 145 | } 146 | 147 | #endif /* #ifndef TEAVPN2__MUTEX_H */ 148 | -------------------------------------------------------------------------------- /src/teavpn2/gui/events.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Alviro Iskandar Setiawan 4 | */ 5 | 6 | #include 7 | 8 | int g_client_err_code = 0; 9 | struct tmutex g_client_vpn_state_lock; 10 | uint8_t g_client_vpn_state = CLIENT_EVENT_IDLE; 11 | 12 | 13 | int teavpn2_gui_event_init(void) 14 | { 15 | return mutex_init(&g_client_vpn_state_lock, NULL); 16 | } 17 | 18 | int teavpn2_gui_event_destroy(void) 19 | { 20 | mutex_destroy(&g_client_vpn_state_lock); 21 | return 0; 22 | } 23 | 24 | static void client_event_connected_cb(struct gui *g) 25 | __must_hold(&g_client_vpn_state_lock) 26 | { 27 | char lbl_stat[0x250u]; 28 | struct cli_cfg *cfg = &g->app.cli_cfg; 29 | 30 | 31 | if (!g->home_btn_connect) { 32 | pr_err("Cannot get button connect widget"); 33 | return; 34 | } 35 | 36 | g_snprintf(lbl_stat, sizeof(lbl_stat), "Connected to \"%s@%s:%hu\"", 37 | cfg->auth.username, cfg->sock.server_addr, 38 | cfg->sock.server_port); 39 | 40 | g->app.cli_state = CLIENT_STATE_CONNECTED; 41 | gtk_button_set_label(GTK_BUTTON(g->home_btn_connect), "Disconnect"); 42 | gtk_label_set_label(GTK_LABEL(g->home_lbl_status), lbl_stat); 43 | gtk_widget_set_sensitive(g->home_btn_connect, TRUE); 44 | } 45 | 46 | static void client_event_disconnected_cb(struct gui *g) 47 | __must_hold(&g_client_vpn_state_lock) 48 | { 49 | if (!g->home_btn_connect) { 50 | pr_err("Cannot get button connect widget"); 51 | return; 52 | } 53 | 54 | g->app.cli_state = CLIENT_STATE_DISCONNECTED; 55 | gtk_button_set_label(GTK_BUTTON(g->home_btn_connect), "Connect"); 56 | gtk_label_set_label(GTK_LABEL(g->home_lbl_status), "Disconnected"); 57 | gtk_widget_set_sensitive(g->home_btn_connect, TRUE); 58 | gtk_widget_set_sensitive(g->header_btn_open, TRUE); 59 | } 60 | 61 | static void client_event_error_cb(struct gui *g, int err_code) 62 | __must_hold(&g_client_vpn_state_lock) 63 | { 64 | if (!g->home_btn_connect) { 65 | pr_err("Cannot get button connect widget"); 66 | return; 67 | } 68 | 69 | g->app.cli_state = CLIENT_STATE_DISCONNECTED; 70 | gtk_button_set_label(GTK_BUTTON(g->home_btn_connect), "Connect"); 71 | gtk_label_set_label(GTK_LABEL(g->home_lbl_status), "Disconnected"); 72 | gtk_widget_set_sensitive(g->home_btn_connect, TRUE); 73 | gtk_widget_set_sensitive(g->header_btn_open, TRUE); 74 | pr_err(PRERF, PREAR(-err_code)); 75 | } 76 | 77 | gboolean client_callback_event_loop(void *user_data) 78 | __acquires(&g_client_vpn_state_lock) 79 | __releases(&g_client_vpn_state_lock) 80 | { 81 | struct gui *gui = (struct gui *) user_data; 82 | unsigned try_num = 0; 83 | const unsigned max_try = 10; 84 | static char prbuf[4096]; 85 | size_t prbuf_len; 86 | 87 | while (unlikely(mutex_trylock(&g_client_vpn_state_lock))) { 88 | cpu_relax(); 89 | if (unlikely(try_num++ >= max_try)) 90 | return TRUE; 91 | } 92 | 93 | prbuf_len = gui_pr_consume_buffer(prbuf, sizeof(prbuf) - 1); 94 | if (prbuf_len) { 95 | prbuf[prbuf_len] = '\0'; 96 | gui_home_insert_txt_logger(gui, prbuf); 97 | } 98 | 99 | switch (g_client_vpn_state) { 100 | case CLIENT_EVENT_IDLE: 101 | cpu_relax(); 102 | goto skip_set; 103 | case CLIENT_EVENT_CONNECTED: 104 | client_event_connected_cb(gui); 105 | break; 106 | case CLIENT_EVENT_DISCONNECTED: 107 | client_event_disconnected_cb(gui); 108 | break; 109 | case CLIENT_EVENT_ERROR: 110 | client_event_error_cb(gui, g_client_err_code); 111 | break; 112 | default: 113 | BUG(); 114 | break; 115 | } 116 | 117 | g_client_vpn_state = CLIENT_EVENT_IDLE; 118 | 119 | skip_set: 120 | mutex_unlock(&g_client_vpn_state_lock); 121 | return TRUE; 122 | } 123 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-only 2 | VERSION = 0 3 | PATCHLEVEL = 1 4 | SUBLEVEL = 2 5 | EXTRAVERSION := -rc1 6 | NAME = Green Grass 7 | 8 | ifeq ($(O),) 9 | O := . 10 | endif 11 | 12 | ifeq ($(V),1) 13 | override Q := 14 | override S := @\# 15 | else 16 | override Q := @ 17 | override S := @ 18 | endif 19 | 20 | override USER_CFLAGS := $(CFLAGS) 21 | override USER_CXXFLAGS := $(CXXFLAGS) 22 | override USER_LDFLAGS := $(LDFLAGS) 23 | override USER_LIB_LDFLAGS := $(LIB_LDFLAGS) 24 | 25 | all: config.make default 26 | 27 | ifneq ($(MAKECMDGOALS),clean) 28 | ifneq ($(MAKECMDGOALS),distclean) 29 | config.make: configure 30 | @if [ ! -e "$@" ]; then \ 31 | echo "Running configure ..."; \ 32 | LDFLAGS="$(USER_LDFLAGS)" \ 33 | LIB_LDFLAGS="$(USER_LIB_LDFLAGS)" \ 34 | CFLAGS="$(USER_CFLAGS)" \ 35 | CXXFLAGS="$(USER_CXXFLAGS)" \ 36 | ./configure; \ 37 | else \ 38 | echo "$@ is out-of-date"; \ 39 | echo "Running configure ..."; \ 40 | LDFLAGS="$(USER_LDFLAGS)" \ 41 | LIB_LDFLAGS="$(USER_LIB_LDFLAGS)" \ 42 | CFLAGS="$(USER_CFLAGS)" \ 43 | CXXFLAGS="$(USER_CXXFLAGS)" \ 44 | sed -n "/.*Configured with/s/[^:]*: //p" "$@" | sh; \ 45 | fi; 46 | 47 | 48 | include config.make 49 | endif 50 | endif 51 | 52 | override TARGET_BIN = $(O)/teavpn2 53 | override CONFIG_H_PATH = $(O)/config.h 54 | override CONFIG_MAKE_PATH = $(O)/config.make 55 | override CONFIG_LOG_PATH = $(O)/config.log 56 | 57 | LD := $(CC) 58 | BASE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) 59 | BASE_DIR := $(strip $(patsubst %/, %, $(BASE_DIR))) 60 | BASE_DEP_DIR := $(O)/.deps 61 | MAKEFILE_FILE := $(lastword $(MAKEFILE_LIST)) 62 | INCLUDE_DIR = -I$(BASE_DIR) 63 | PACKAGE_NAME := $(TARGET_BIN)-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) 64 | OBJ_CC := 65 | OBJ_PRE_CC := 66 | DEP_DIRS := $(BASE_DEP_DIR) 67 | DEPFLAGS = -MT "$@" -MMD -MP -MF "$(@:$(BASE_DIR)/%.o=$(BASE_DEP_DIR)/%.d)" 68 | EXT_DEP_FILE := $(MAKEFILE_FILE) $(CONFIG_H_PATH) $(CONFIG_MAKE_PATH) 69 | 70 | CFLAGS += -DVERSION=$(VERSION) \ 71 | -DPATCHLEVEL=$(PATCHLEVEL) \ 72 | -DSUBLEVEL=$(SUBLEVEL) \ 73 | -DEXTRAVERSION="\"$(EXTRAVERSION)\"" 74 | 75 | CXXFLAGS += -DVERSION=$(VERSION) \ 76 | -DPATCHLEVEL=$(PATCHLEVEL) \ 77 | -DSUBLEVEL=$(SUBLEVEL) \ 78 | -DEXTRAVERSION="\"$(EXTRAVERSION)\"" 79 | 80 | include src/Makefile 81 | 82 | CFLAGS += $(INCLUDE_DIR) 83 | CXXFLAGS += $(INCLUDE_DIR) 84 | 85 | $(CONFIG_H_PATH): $(CONFIG_MAKE_PATH) 86 | 87 | $(TARGET_BIN): $(EXT_DEP_FILE) $(OBJ_PRE_CC) $(OBJ_CC) 88 | $(S)echo " LD " "$(@:$(BASE_DIR)/%=%)"; 89 | $(Q)$(LD) $(LDFLAGS) -o $(@) $(OBJ_PRE_CC) $(OBJ_CC) $(LIB_LDFLAGS); 90 | 91 | $(DEP_DIRS): 92 | $(S)echo " MKDIR " "$(@:$(BASE_DIR)/%=%)"; 93 | $(Q)mkdir -p $(@); 94 | 95 | $(OBJ_CC): $(EXT_DEP_FILE) | $(DEP_DIRS) 96 | $(OBJ_PRE_CC): $(EXT_DEP_FILE) | $(DEP_DIRS) 97 | 98 | %.o: %.c 99 | $(S)echo " CC " "$(@:$(BASE_DIR)/%=%)"; 100 | $(Q)$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $(@) $(<); 101 | 102 | %.o: %.cc 103 | $(S)echo " CXX " "$(@:$(BASE_DIR)/%=%)"; 104 | $(Q)$(CXX) $(DEPFLAGS) $(CXXFLAGS) -c -o $(@) $(<); 105 | 106 | %.o: %.cpp 107 | $(S)echo " CXX " "$(@:$(BASE_DIR)/%=%)"; 108 | $(Q)$(CXX) $(DEPFLAGS) $(CXXFLAGS) -c -o $(@) $(<); 109 | 110 | # 111 | # Include generated dependencies data. 112 | # 113 | -include $(OBJ_CC:$(BASE_DIR)/%.o=$(BASE_DEP_DIR)/%.d) 114 | -include $(OBJ_PRE_CC:$(BASE_DIR)/%.o=$(BASE_DEP_DIR)/%.d) 115 | 116 | default: $(TARGET_BIN) 117 | 118 | distclean: clean 119 | $(Q)rm -rfv $(CONFIG_H_PATH) $(CONFIG_MAKE_PATH) $(CONFIG_LOG_PATH); 120 | 121 | clean: 122 | $(Q)@rm -rfv $(DEP_DIRS) $(OBJ_CC) $(shell find . -name "*.o"); 123 | 124 | .PHONY: default all distclean clean 125 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build test 2 | 3 | on: 4 | # Trigger the workflow on push or pull requests. 5 | push: 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-22.04 11 | 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | # x86-64 gcc 17 | - arch: x86_64 18 | cc_pkg: gcc-x86-64-linux-gnu 19 | cxx_pkg: g++-x86-64-linux-gnu 20 | cc: x86_64-linux-gnu-gcc 21 | cxx: x86_64-linux-gnu-g++ 22 | 23 | # x86-64 clang 24 | - arch: x86_64 25 | cc_pkg: clang 26 | cxx_pkg: clang 27 | cc: clang 28 | cxx: clang++ 29 | 30 | # x86 (32-bit) gcc 31 | - arch: i686 32 | cc_pkg: gcc-i686-linux-gnu 33 | cxx_pkg: g++-i686-linux-gnu 34 | cc: i686-linux-gnu-gcc 35 | cxx: i686-linux-gnu-g++ 36 | 37 | # aarch64 gcc 38 | - arch: aarch64 39 | cc_pkg: gcc-aarch64-linux-gnu 40 | cxx_pkg: g++-aarch64-linux-gnu 41 | cc: aarch64-linux-gnu-gcc 42 | cxx: aarch64-linux-gnu-g++ 43 | 44 | # arm (32-bit) gcc 45 | - arch: arm 46 | cc_pkg: gcc-arm-linux-gnueabi 47 | cxx_pkg: g++-arm-linux-gnueabi 48 | cc: arm-linux-gnueabi-gcc 49 | cxx: arm-linux-gnueabi-g++ 50 | 51 | # powerpc64 52 | - arch: powerpc64 53 | cc_pkg: gcc-powerpc64-linux-gnu 54 | cxx_pkg: g++-powerpc64-linux-gnu 55 | cc: powerpc64-linux-gnu-gcc 56 | cxx: powerpc64-linux-gnu-g++ 57 | 58 | # powerpc 59 | - arch: powerpc 60 | cc_pkg: gcc-powerpc-linux-gnu 61 | cxx_pkg: g++-powerpc-linux-gnu 62 | cc: powerpc-linux-gnu-gcc 63 | cxx: powerpc-linux-gnu-g++ 64 | 65 | # alpha 66 | - arch: alpha 67 | cc_pkg: gcc-alpha-linux-gnu 68 | cxx_pkg: g++-alpha-linux-gnu 69 | cc: alpha-linux-gnu-gcc 70 | cxx: alpha-linux-gnu-g++ 71 | 72 | # mips64 73 | - arch: mips64 74 | cc_pkg: gcc-mips64-linux-gnuabi64 75 | cxx_pkg: g++-mips64-linux-gnuabi64 76 | cc: mips64-linux-gnuabi64-gcc 77 | cxx: mips64-linux-gnuabi64-g++ 78 | 79 | # mips 80 | - arch: mips 81 | cc_pkg: gcc-mips-linux-gnu 82 | cxx_pkg: g++-mips-linux-gnu 83 | cc: mips-linux-gnu-gcc 84 | cxx: mips-linux-gnu-g++ 85 | 86 | steps: 87 | - name: Checkout source 88 | uses: actions/checkout@v2 89 | 90 | - name: Install Compilers 91 | run: | 92 | if [[ "${{matrix.cc_pkg}}" == "clang" ]]; then \ 93 | wget https://apt.llvm.org/llvm.sh -O /tmp/llvm.sh; \ 94 | sudo bash /tmp/llvm.sh 17; \ 95 | sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-17 400; \ 96 | sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-17 400; \ 97 | else \ 98 | sudo apt-get update -y; \ 99 | sudo apt-get install -y ${{matrix.cc_pkg}} ${{matrix.cxx_pkg}}; \ 100 | fi; 101 | 102 | - name: Install GTK+3 103 | run: | 104 | if [[ "${{matrix.arch}}" == "x86_64" ]]; then \ 105 | sudo apt-get install -y libgtk-3-dev; \ 106 | fi; 107 | 108 | - name: Display compiler versions 109 | run: | 110 | ${{matrix.cc}} --version; 111 | ${{matrix.cxx}} --version; 112 | 113 | - name: Build 114 | run: | 115 | ./configure --cc=${{matrix.cc}} --cxx=${{matrix.cxx}}; 116 | make -j$(nproc); 117 | 118 | - name: Build with GUI 119 | run: | 120 | if [[ "${{matrix.arch}}" == "x86_64" ]]; then \ 121 | ./configure --cc=${{matrix.cc}} --cxx=${{matrix.cxx}} --gui; \ 122 | make -j$(nproc); \ 123 | fi; 124 | -------------------------------------------------------------------------------- /src/teavpn2/packet.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | #ifndef TEAVPN2__PACKET_H 6 | #define TEAVPN2__PACKET_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define TCLI_PKT_HANDSHAKE 0u 14 | #define TCLI_PKT_AUTH 1u 15 | #define TCLI_PKT_TUN_DATA 2u 16 | #define TCLI_PKT_REQSYNC 3u 17 | #define TCLI_PKT_SYNC 4u 18 | #define TCLI_PKT_CLOSE 5u 19 | 20 | #define TSRV_PKT_HANDSHAKE 0u 21 | #define TSRV_PKT_AUTH_OK 1u 22 | #define TSRV_PKT_TUN_DATA 2u 23 | #define TSRV_PKT_REQSYNC 3u 24 | #define TSRV_PKT_SYNC 4u 25 | #define TSRV_PKT_CLOSE 5u 26 | 27 | #define TSRV_PKT_HANDSHAKE_REJECT 6u 28 | #define TSRV_PKT_AUTH_REJECT 7u 29 | 30 | 31 | 32 | #define SIZE_ASSERT(TYPE, LEN) \ 33 | static_assert(sizeof(TYPE) == (LEN), \ 34 | "Bad " __stringify(sizeof(TYPE) == (LEN))) \ 35 | 36 | 37 | #define OFFSET_ASSERT(TYPE, MEM, EQU) \ 38 | static_assert(offsetof(TYPE, MEM) == (EQU), \ 39 | "Bad " __stringify(offsetof(TYPE, MEM) == (EQU))) 40 | 41 | 42 | struct pkt_handshake { 43 | struct teavpn2_version cur; 44 | struct teavpn2_version min; 45 | struct teavpn2_version max; 46 | }; 47 | OFFSET_ASSERT(struct pkt_handshake, cur, 0); 48 | OFFSET_ASSERT(struct pkt_handshake, min, 32); 49 | OFFSET_ASSERT(struct pkt_handshake, max, 64); 50 | SIZE_ASSERT(struct pkt_handshake, 96); 51 | 52 | 53 | #define TSRV_HREJECT_INVALID (1u << 0u) 54 | #define TSRV_HREJECT_VERSION_NOT_SUPPORTED (1u << 1u) 55 | struct pkt_handshake_reject { 56 | uint8_t reason; 57 | char msg[511]; 58 | }; 59 | OFFSET_ASSERT(struct pkt_handshake_reject, reason, 0); 60 | OFFSET_ASSERT(struct pkt_handshake_reject, msg, 1); 61 | SIZE_ASSERT(struct pkt_handshake_reject, 512); 62 | 63 | 64 | struct pkt_auth { 65 | char username[256]; 66 | char password[256]; 67 | }; 68 | OFFSET_ASSERT(struct pkt_auth, username, 0); 69 | OFFSET_ASSERT(struct pkt_auth, password, 256); 70 | SIZE_ASSERT(struct pkt_auth, 512); 71 | 72 | 73 | struct pkt_auth_res { 74 | uint8_t status; 75 | struct if_info iff; 76 | }; 77 | OFFSET_ASSERT(struct pkt_auth_res, status, 0); 78 | OFFSET_ASSERT(struct pkt_auth_res, iff, 2); 79 | SIZE_ASSERT(struct pkt_auth_res, 1 + 1 + sizeof(struct if_info)); 80 | 81 | 82 | struct pkt_tun_data { 83 | union { 84 | struct iphdr iphdr; 85 | uint8_t __raw[4096]; 86 | }; 87 | }; 88 | OFFSET_ASSERT(struct pkt_tun_data, __raw, 0); 89 | SIZE_ASSERT(struct pkt_tun_data, 4096); 90 | 91 | 92 | /* 93 | * Packet structure which is sent by the server. 94 | */ 95 | struct srv_pkt { 96 | uint8_t type; 97 | uint8_t pad_len; 98 | uint16_t len; 99 | union { 100 | struct pkt_handshake handshake; 101 | struct pkt_auth_res auth_res; 102 | struct pkt_tun_data tun_data; 103 | struct pkt_handshake_reject hs_reject; 104 | char __raw[4096]; 105 | }; 106 | }; 107 | OFFSET_ASSERT(struct srv_pkt, type, 0); 108 | OFFSET_ASSERT(struct srv_pkt, pad_len, 1); 109 | OFFSET_ASSERT(struct srv_pkt, len, 2); 110 | OFFSET_ASSERT(struct srv_pkt, handshake, 4); 111 | OFFSET_ASSERT(struct srv_pkt, auth_res, 4); 112 | OFFSET_ASSERT(struct srv_pkt, __raw, 4); 113 | SIZE_ASSERT(struct srv_pkt, 2 + 1 + 1 + 4096); 114 | 115 | 116 | /* 117 | * Packet structure which is sent by the client. 118 | */ 119 | struct cli_pkt { 120 | uint8_t type; 121 | uint8_t pad_len; 122 | uint16_t len; 123 | union { 124 | struct pkt_handshake handshake; 125 | struct pkt_auth auth; 126 | struct pkt_tun_data tun_data; 127 | char __raw[4096]; 128 | }; 129 | }; 130 | OFFSET_ASSERT(struct cli_pkt, type, 0); 131 | OFFSET_ASSERT(struct cli_pkt, pad_len, 1); 132 | OFFSET_ASSERT(struct cli_pkt, len, 2); 133 | OFFSET_ASSERT(struct cli_pkt, handshake, 4); 134 | OFFSET_ASSERT(struct cli_pkt, __raw, 4); 135 | SIZE_ASSERT(struct cli_pkt, 2 + 1 + 1 + 4096); 136 | 137 | 138 | struct sc_pkt { 139 | size_t len; 140 | union { 141 | struct cli_pkt cli; 142 | struct srv_pkt srv; 143 | char __raw[sizeof(struct cli_pkt)]; 144 | }; 145 | }; 146 | 147 | #define PKT_MIN_LEN (2 + 1 + 1) 148 | #define PKT_MAX_LEN (sizeof(struct cli_pkt)) 149 | 150 | static_assert(sizeof(struct cli_pkt) == sizeof(struct srv_pkt), 151 | "Fail to assert sizeof(struct cli_pkt) == sizeof(struct srv_pkt)"); 152 | 153 | #endif /* #ifndef TEAVPN2__PACKET_H */ 154 | -------------------------------------------------------------------------------- /src/teavpn2/print.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined(__linux__) 11 | #include 12 | static pthread_mutex_t get_time_lock = PTHREAD_MUTEX_INITIALIZER; 13 | static pthread_mutex_t print_lock = PTHREAD_MUTEX_INITIALIZER; 14 | static __maybe_unused pthread_mutex_t gui_buf_mutex = PTHREAD_MUTEX_INITIALIZER; 15 | #else 16 | #define pthread_mutex_lock(MUTEX) 17 | #define pthread_mutex_unlock(MUTEX) 18 | #define pthread_mutex_trylock(MUTEX) 19 | #endif 20 | 21 | uint8_t __notice_level = DEFAULT_NOTICE_LEVEL; 22 | 23 | 24 | #ifdef CONFIG_GUI 25 | 26 | static char *g_gui_buf = NULL; 27 | static size_t g_gui_buf_len = 0; 28 | static size_t g_gui_buf_maxlen = 0; 29 | 30 | int gui_pr_buffer_init(size_t len) 31 | { 32 | pthread_mutex_lock(&gui_buf_mutex); 33 | g_gui_buf = al64_malloc(len); 34 | if (!g_gui_buf) { 35 | pthread_mutex_unlock(&gui_buf_mutex); 36 | return -ENOMEM; 37 | } 38 | 39 | g_gui_buf_len = 0; 40 | g_gui_buf_maxlen = len; 41 | pthread_mutex_unlock(&gui_buf_mutex); 42 | return 0; 43 | } 44 | 45 | size_t gui_pr_consume_buffer(char *buffer, size_t maxlen) 46 | { 47 | size_t cpylen; 48 | 49 | if (unlikely(!maxlen)) 50 | return 0; 51 | 52 | pthread_mutex_lock(&gui_buf_mutex); 53 | if (maxlen < g_gui_buf_len) { 54 | size_t unconsumed_pos = maxlen; 55 | size_t memmove_len = g_gui_buf_len - unconsumed_pos; 56 | 57 | cpylen = maxlen; 58 | memcpy(buffer, g_gui_buf, cpylen); 59 | memmove(g_gui_buf, &g_gui_buf[unconsumed_pos], memmove_len); 60 | g_gui_buf_len -= cpylen; 61 | } else { 62 | cpylen = g_gui_buf_len; 63 | if (cpylen > 0) { 64 | memcpy(buffer, g_gui_buf, cpylen); 65 | g_gui_buf_len = 0; 66 | } 67 | } 68 | pthread_mutex_unlock(&gui_buf_mutex); 69 | return cpylen; 70 | } 71 | 72 | size_t gui_pr_queue_buffer(const char *buffer, size_t len) 73 | { 74 | size_t cpylen = 0; 75 | size_t remaining_size; 76 | 77 | pthread_mutex_lock(&gui_buf_mutex); 78 | remaining_size = g_gui_buf_maxlen - g_gui_buf_len; 79 | if (likely(remaining_size > 0)) { 80 | cpylen = len < remaining_size ? len : remaining_size; 81 | memcpy(&g_gui_buf[g_gui_buf_len], buffer, cpylen); 82 | g_gui_buf_len += cpylen; 83 | } 84 | pthread_mutex_unlock(&gui_buf_mutex); 85 | return cpylen; 86 | } 87 | #endif /* #ifdef CONFIG_GUI */ 88 | 89 | static __always_inline char *get_time(char *buf) 90 | __must_hold(&print_lock) 91 | { 92 | size_t len; 93 | char *time_chr; 94 | time_t rawtime; 95 | struct tm *timeinfo; 96 | 97 | pthread_mutex_lock(&get_time_lock); 98 | time(&rawtime); 99 | timeinfo = localtime(&rawtime); 100 | time_chr = asctime(timeinfo); 101 | len = strnlen(time_chr, 32) - 1; 102 | memcpy(buf, time_chr, len); 103 | buf[len] = '\0'; 104 | pthread_mutex_unlock(&get_time_lock); 105 | return buf; 106 | } 107 | 108 | 109 | #define PR_COPY_BUF(pr, buf, r, vbuf, ul, fmt, vl) \ 110 | do { \ 111 | r += snprintf(&vbuf[r], ul - r, "[%s] " pr, get_time(buf)); \ 112 | r += vsnprintf(&vbuf[r], ul - r, fmt, vl); \ 113 | vbuf[r++] = '\n'; \ 114 | vbuf[r] = '\0'; \ 115 | } while (0) 116 | 117 | 118 | #define DEFINE_PR_FUNC(NAME, PR) \ 119 | void __##NAME(const char *fmt, ...) \ 120 | { \ 121 | int r = 0; \ 122 | va_list vl; \ 123 | char buf[32]; \ 124 | char vbuf[2048]; \ 125 | const int ul = (int) sizeof(vbuf) - 4; \ 126 | \ 127 | va_start(vl, fmt); \ 128 | pthread_mutex_lock(&print_lock); \ 129 | PR_COPY_BUF(PR, buf, r, vbuf, ul, fmt, vl); \ 130 | r = (int) fwrite(vbuf, sizeof(char), (size_t) r, stdout); \ 131 | gui_pr_queue_buffer(vbuf, (size_t) r); \ 132 | pthread_mutex_unlock(&print_lock); \ 133 | va_end(vl); \ 134 | (void) r; \ 135 | } 136 | 137 | 138 | DEFINE_PR_FUNC(pr_notice, ""); 139 | DEFINE_PR_FUNC(pr_error, "Error: "); 140 | DEFINE_PR_FUNC(pr_emerg, "Emergency: "); 141 | DEFINE_PR_FUNC(pr_debug, "Debug: "); 142 | DEFINE_PR_FUNC(pr_warn, "Warning: "); 143 | 144 | 145 | void __panic(const char *file, int lineno, const char *fmt, ...) 146 | { 147 | va_list vl; 148 | #if defined(CONFIG_HPC_EMERGENCY) 149 | __emerg_release_bug = true; 150 | #endif 151 | pthread_mutex_trylock(&print_lock); 152 | pthread_mutex_trylock(&get_time_lock); 153 | puts("======================================================="); 154 | printf("Emergency: Panic - Not syncing: "); 155 | va_start(vl, fmt); 156 | vprintf(fmt, vl); 157 | va_end(vl); 158 | putchar('\n'); 159 | printf("Panic at %s:%d\n", file, lineno); 160 | #define dump_stack() 161 | /* TODO: Write real dump_stack() */ 162 | dump_stack(); 163 | #undef dump_stack 164 | puts("======================================================="); 165 | fflush(stdout); 166 | abort(); 167 | } 168 | -------------------------------------------------------------------------------- /src/teavpn2/gui/gui_home.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Khaerul Ilham 4 | * Copyright (C) 2021 Alviro Iskandar Setiawan 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | static void *run_vpn_thread(void *user_data) 13 | { 14 | struct gui *gui = (struct gui *) user_data; 15 | int ret = -EINVAL; 16 | struct cli_cfg *cfg = &gui->app.cli_cfg; 17 | 18 | memset(cfg, 0, sizeof(struct cli_cfg)); 19 | cfg->sys.cfg_file = gui->app.cfg_file->str; 20 | ret = client_parse_cfg_file(cfg->sys.cfg_file, cfg); 21 | if (unlikely(ret)) 22 | goto out; 23 | 24 | ret = -ESOCKTNOSUPPORT; 25 | switch (cfg->sock.type) { 26 | case SOCK_UDP: 27 | ret = teavpn2_client_udp_run(cfg); 28 | break; 29 | case SOCK_TCP: 30 | break; 31 | default: 32 | BUG(); 33 | break; 34 | } 35 | out: 36 | if (unlikely(ret)) 37 | set_client_vpn_err_event(ret); 38 | return NULL; 39 | } 40 | 41 | static void btn_connect_callback(GtkWidget *self, void *user_data) 42 | { 43 | int ret; 44 | struct gui *gui = (struct gui *) user_data; 45 | static pthread_t vpn_thread; 46 | 47 | 48 | /* 49 | * When the button is clicked, disable them. The callback 50 | * in the events.c is responsible to enable them. 51 | */ 52 | gtk_widget_set_sensitive(GTK_WIDGET(self), FALSE); 53 | gtk_widget_set_sensitive(gui->header_btn_open, FALSE); 54 | 55 | switch (gui->app.cli_state) { 56 | case CLIENT_STATE_DISCONNECTED: 57 | pr_notice("Connecting..."); 58 | gtk_button_set_label(GTK_BUTTON(self), "Connecting..."); 59 | ret = pthread_create(&vpn_thread, NULL, &run_vpn_thread, gui); 60 | if (unlikely(ret)) { 61 | pr_err("pthread_create(): " PRERF, PREAR(ret)); 62 | return; 63 | } 64 | 65 | ret = pthread_detach(vpn_thread); 66 | if (unlikely(ret)) 67 | pr_err("pthread_detach(): " PRERF, PREAR(ret)); 68 | break; 69 | 70 | case CLIENT_STATE_CONNECTED: 71 | stop_vpn: 72 | pr_notice("Disconnecting..."); 73 | gtk_button_set_label(GTK_BUTTON(self), "Disconnecting..."); 74 | teavpn2_client_udp_stop(); 75 | pthread_kill(vpn_thread, SIGTERM); 76 | break; 77 | 78 | default: 79 | BUG(); 80 | goto stop_vpn; 81 | } 82 | } 83 | 84 | void gui_home_insert_txt_logger(struct gui *g, const char *msg) 85 | { 86 | GtkTextMark *mark; 87 | GtkTextIter iter; 88 | GtkTextBuffer *txt_buffer_log = g->app.txt_buffer_log; 89 | 90 | 91 | gtk_text_buffer_get_end_iter(txt_buffer_log, &iter); 92 | gtk_text_buffer_insert(txt_buffer_log, &iter, msg, -1); 93 | gtk_text_iter_set_line_offset(&iter, 0); 94 | 95 | mark = gtk_text_buffer_get_mark(txt_buffer_log, "main_log"); 96 | gtk_text_buffer_move_mark(txt_buffer_log, mark, &iter); 97 | gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(g->home_txt_logger), 98 | mark); 99 | } 100 | 101 | void gui_home_create(struct gui *g) 102 | { 103 | GtkBox *home = g->home; 104 | GtkWidget *frame_conf, *frame_log, *scroller; 105 | GtkTextIter txt_iter; 106 | const struct gui_callback callbacks[] = { 107 | GUI_CALLBACK(&g->home_btn_connect, "clicked", 108 | btn_connect_callback, g), 109 | }; 110 | 111 | 112 | frame_conf = g_object_new(GTK_TYPE_FRAME, 113 | "label", "Configuration File", 114 | "margin-top", 5, 115 | "margin-start", 5, 116 | "margin-end", 5, NULL); 117 | g->home_lbl_path = g_object_new(GTK_TYPE_LABEL, 118 | "label", g->app.cfg_file->str, 119 | "parent", frame_conf, 120 | "margin-bottom", 10, 121 | "margin-start", 5, 122 | "margin-end", 5, NULL); 123 | g->home_btn_connect = g_object_new(GTK_TYPE_BUTTON, 124 | "label", "Connect", 125 | "halign", GTK_ALIGN_CENTER, NULL); 126 | frame_log = g_object_new(GTK_TYPE_FRAME, 127 | "label", "Log:", 128 | "margin-start", 5, 129 | "margin-end", 5, NULL); 130 | scroller = g_object_new(GTK_TYPE_SCROLLED_WINDOW, 131 | "parent", frame_log, NULL); 132 | g->home_txt_logger = g_object_new(GTK_TYPE_TEXT_VIEW, 133 | "parent", scroller, 134 | "vexpand", TRUE, 135 | "monospace", TRUE, 136 | "editable", FALSE, 137 | "buffer", g->app.txt_buffer_log, NULL); 138 | g->home_lbl_status = g_object_new(GTK_TYPE_LABEL, 139 | "label", "Disconnected", 140 | "wrap", TRUE, 141 | "selectable", TRUE, 142 | "margin-bottom", 5, NULL); 143 | 144 | gtk_text_buffer_get_end_iter(g->app.txt_buffer_log, &txt_iter); 145 | gtk_text_buffer_create_mark(g->app.txt_buffer_log, "main_log", 146 | &txt_iter, TRUE); 147 | 148 | 149 | gtk_box_pack_start(home, frame_conf, FALSE, FALSE, 0); 150 | gtk_box_pack_start(home, g->home_btn_connect, FALSE, FALSE, 0); 151 | gtk_box_pack_start(home, frame_log, TRUE, TRUE, 0); 152 | gtk_box_pack_end(home, g->home_lbl_status, FALSE, FALSE, 0); 153 | 154 | gui_utils_set_callback(callbacks, G_N_ELEMENTS(callbacks)); 155 | } 156 | -------------------------------------------------------------------------------- /src/teavpn2/auth.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const char *data_dir = NULL; 12 | 13 | struct user_parse_ctx { 14 | const char *user; 15 | const char *pass; 16 | const char *userfile; 17 | char fuser[0x100]; 18 | char fpass[0x100]; 19 | struct if_info iff; 20 | }; 21 | 22 | static inline bool validate_username_char(unsigned char c) 23 | { 24 | return ( 25 | ('0' <= c && c <= '9') || 26 | ('A' <= c && c <= 'Z') || 27 | ('a' <= c && c <= 'z') || 28 | (c == '_' || c == '-') 29 | ); 30 | } 31 | 32 | static bool validate_username(const char *u) 33 | { 34 | 35 | while (1) { 36 | unsigned char c = (unsigned char) *u++; 37 | if (!c) 38 | break; 39 | 40 | if (!validate_username_char(c)) 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | static int userfile_parse_auth(struct user_parse_ctx *ctx, const char *name, 48 | const char *val, int lineno) 49 | { 50 | char *mutable_val; 51 | memcpy(&mutable_val, &val, sizeof(mutable_val)); 52 | 53 | if (!strcmp(name, "username")) { 54 | strncpy(ctx->fuser, val, sizeof(ctx->fuser)); 55 | ctx->fuser[sizeof(ctx->fuser) - 1] = '\0'; 56 | } else if (!strcmp(name, "password")) { 57 | strncpy(ctx->fpass, val, sizeof(ctx->fpass)); 58 | ctx->fpass[sizeof(ctx->fpass) - 1] = '\0'; 59 | } else { 60 | pr_warn("Invalid name \"%s\" in section auth in %s:%d", name, 61 | ctx->userfile, lineno); 62 | } 63 | 64 | memset(mutable_val, 0, strlen(mutable_val)); 65 | return 1; 66 | } 67 | 68 | 69 | static int userfile_parse_iface(struct user_parse_ctx *ctx, const char *name, 70 | const char *val, int lineno) 71 | { 72 | if (!strcmp(name, "mtu")) { 73 | ctx->iff.ipv4_mtu = (uint16_t)strtoul(val, NULL, 10); 74 | } else if (!strcmp(name, "ipv4")) { 75 | strncpy(ctx->iff.ipv4, val, sizeof(ctx->iff.ipv4)); 76 | ctx->iff.ipv4[sizeof(ctx->iff.ipv4) - 1] = '\0'; 77 | } else if (!strcmp(name, "ipv4_netmask")) { 78 | strncpy(ctx->iff.ipv4_netmask, val, sizeof(ctx->iff.ipv4_netmask)); 79 | ctx->iff.ipv4_netmask[sizeof(ctx->iff.ipv4_netmask) - 1] = '\0'; 80 | } else if (!strcmp(name, "ipv4_dgateway")) { 81 | strncpy(ctx->iff.ipv4_dgateway, val, sizeof(ctx->iff.ipv4_dgateway)); 82 | ctx->iff.ipv4_dgateway[sizeof(ctx->iff.ipv4_dgateway) - 1] = '\0'; 83 | } else { 84 | pr_warn("Invalid name \"%s\" in section iface in %s:%d", name, 85 | ctx->userfile, lineno); 86 | } 87 | return 1; 88 | } 89 | 90 | 91 | /* 92 | * If success, returns 1. 93 | * If failure, returns 0. 94 | */ 95 | static int userfile_parser(void *user, const char *section, const char *name, 96 | const char *val, int lineno) 97 | { 98 | struct user_parse_ctx *ctx = user; 99 | 100 | if (!strcmp(section, "auth")) { 101 | userfile_parse_auth(ctx, name, val, lineno); 102 | } else if (!strcmp(section, "iface")) { 103 | userfile_parse_iface(ctx, name, val, lineno); 104 | } else { 105 | pr_warn("Invalid section \"%s\" in %s:%d", section, 106 | ctx->userfile, lineno); 107 | } 108 | return 1; 109 | } 110 | 111 | 112 | static int _teavpn2_auth(FILE *handle, const char *userfile, 113 | const char *username, const char *password, 114 | struct if_info *iff) 115 | { 116 | int ret = 0; 117 | struct user_parse_ctx ctx; 118 | 119 | memset(&ctx, 0, sizeof(ctx)); 120 | ctx.user = username; 121 | ctx.pass = password; 122 | ctx.userfile = userfile; 123 | ret = ini_parse_file(handle, userfile_parser, &ctx); 124 | if (ret) { 125 | prl_notice(2, "Failed to parse config file \"%s\"", userfile); 126 | ret = -EINVAL; 127 | goto out; 128 | } 129 | 130 | if (strcmp(ctx.user, ctx.fuser)) { 131 | pr_warn("username in file \"%s\" does not match with the file " 132 | "name", ctx.userfile); 133 | } 134 | 135 | if (strcmp(ctx.pass, ctx.fpass)) { 136 | ret = -EACCES; 137 | goto out; 138 | } 139 | 140 | *iff = ctx.iff; 141 | out: 142 | memset(&ctx, 0, sizeof(ctx)); 143 | __asm__ volatile("":"+m"(ctx)::"memory"); 144 | return ret; 145 | } 146 | 147 | 148 | bool teavpn2_auth(const char *username, const char *password, 149 | struct if_info *iff) 150 | { 151 | int err = 0; 152 | FILE *handle; 153 | bool ret = true; 154 | char userfile[512]; 155 | 156 | if (unlikely(!data_dir)) 157 | panic("data_dir is NULL"); 158 | 159 | if (!validate_username(username)) { 160 | prl_notice(2, "Invalid username %s", username); 161 | return false; 162 | } 163 | 164 | snprintf(userfile, sizeof(userfile), "%s/users/%s.ini", data_dir, 165 | username); 166 | 167 | handle = fopen(userfile, "rb"); 168 | if (!handle) { 169 | err = errno; 170 | prl_notice(2, "Cannot open user file: \"%s\": " PRERF, userfile, 171 | PREAR(err)); 172 | errno = err; 173 | return false; 174 | } 175 | 176 | err = _teavpn2_auth(handle, userfile, username, password, iff); 177 | if (err) { 178 | errno = -err; 179 | ret = false; 180 | } 181 | 182 | fclose(handle); 183 | return ret; 184 | } 185 | -------------------------------------------------------------------------------- /src/teavpn2/server/linux/udp_session.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | static __always_inline struct udp_map_bucket *addr_to_bkt( 12 | struct udp_map_bucket (*sess_map)[0x100u], uint32_t addr) 13 | { 14 | size_t idx1, idx2; 15 | idx1 = (addr >> 0u) & 0xffu; 16 | idx2 = (addr >> 8u) & 0xffu; 17 | return &(sess_map[idx1][idx2]); 18 | } 19 | 20 | 21 | static struct udp_sess *map_insert_udp_sess(struct srv_udp_state *state, 22 | uint32_t addr, 23 | struct udp_sess *sess) 24 | __acquires(&state->sess_map_lock) 25 | __releases(&state->sess_map_lock) 26 | { 27 | struct udp_sess *ret = sess; 28 | struct udp_map_bucket *bkt, *new_bkt; 29 | 30 | bkt = addr_to_bkt(state->sess_map, addr); 31 | mutex_lock(&state->sess_map_lock); 32 | if (!bkt->sess) { 33 | bkt->sess = sess; 34 | /* If first entry is empty, there should be no next! */ 35 | if (WARN_ON(bkt->next != NULL)) 36 | bkt->next = NULL; 37 | goto out; 38 | } 39 | 40 | new_bkt = malloc(sizeof(*new_bkt)); 41 | if (unlikely(!new_bkt)) { 42 | ret = NULL; 43 | goto out; 44 | } 45 | 46 | new_bkt->next = NULL; 47 | new_bkt->sess = sess; 48 | 49 | while (bkt->next) 50 | bkt = bkt->next; 51 | 52 | bkt->next = new_bkt; 53 | out: 54 | mutex_unlock(&state->sess_map_lock); 55 | return ret; 56 | } 57 | 58 | 59 | struct udp_sess *create_udp_sess(struct srv_udp_state *state, uint32_t addr, 60 | uint16_t port) 61 | __acquires(&state->sess_map_lock) 62 | __releases(&state->sess_map_lock) 63 | { 64 | int err = 0; 65 | uint16_t idx; 66 | int32_t stk_ret; 67 | struct udp_sess *sess, *ret = NULL; 68 | 69 | mutex_lock(&state->sess_stk_lock); 70 | stk_ret = bt_stack_pop(&state->sess_stk); 71 | if (unlikely(stk_ret == -1)) { 72 | pr_err("Client slot is full, cannot accept more client!"); 73 | err = EAGAIN; 74 | goto out; 75 | } 76 | 77 | idx = (uint16_t)stk_ret; 78 | sess = &state->sess_arr[idx]; 79 | sess->src_addr = addr; 80 | sess->src_port = port; 81 | ret = map_insert_udp_sess(state, addr, sess); 82 | if (unlikely(!ret)) { 83 | BUG_ON(bt_stack_push(&state->sess_stk, idx) == -1); 84 | pr_err("Cannot allocate memory on map_insert_udp_sess()!"); 85 | err = ENOMEM; 86 | goto out; 87 | } 88 | 89 | addr = htonl(addr); 90 | WARN_ON(!inet_ntop(AF_INET, &addr, sess->str_src_addr, 91 | sizeof(sess->str_src_addr))); 92 | 93 | udp_sess_update_last_act(sess); 94 | atomic_store(&sess->is_connected, true); 95 | atomic_fetch_add(&state->n_on_sess, 1); 96 | out: 97 | mutex_unlock(&state->sess_stk_lock); 98 | errno = err; 99 | return ret; 100 | } 101 | 102 | 103 | struct udp_sess * __hot lookup_udp_sess(struct srv_udp_state *state, 104 | uint32_t addr, uint16_t port) 105 | __acquires(&state->sess_map_lock) 106 | __releases(&state->sess_map_lock) 107 | { 108 | struct udp_sess *ret; 109 | struct udp_map_bucket *bkt; 110 | 111 | bkt = addr_to_bkt(state->sess_map, addr); 112 | mutex_lock(&state->sess_map_lock); 113 | do { 114 | ret = bkt->sess; 115 | if (ret) { 116 | if ((ret->src_addr == addr) && (ret->src_port == port)) 117 | goto out; 118 | else 119 | ret = NULL; 120 | } 121 | 122 | bkt = bkt->next; 123 | } while (bkt); 124 | out: 125 | mutex_unlock(&state->sess_map_lock); 126 | return ret; 127 | } 128 | 129 | 130 | static int remove_sess_from_bkt(struct srv_udp_state *state, 131 | struct udp_sess *cur_sess) 132 | __acquires(&state->sess_map_lock) 133 | __releases(&state->sess_map_lock) 134 | { 135 | int ret = 0; 136 | struct udp_sess *sess; 137 | struct udp_map_bucket *prev = NULL, *cur, *tmp; 138 | 139 | cur = addr_to_bkt(state->sess_map, cur_sess->src_addr); 140 | mutex_lock(&state->sess_map_lock); 141 | do { 142 | sess = cur->sess; 143 | if (sess == cur_sess) 144 | goto do_remove; 145 | 146 | prev = cur; 147 | cur = cur->next; 148 | } while (cur); 149 | 150 | ret = -ENOENT; 151 | goto out; 152 | 153 | do_remove: 154 | cur->sess = NULL; 155 | if (prev == NULL) { 156 | /* 157 | * WARNING!!! 158 | * It is illegal to `free(cur)` when `prev == NULL`. 159 | */ 160 | if (cur->next) { 161 | tmp = cur->next->next; 162 | cur->sess = cur->next->sess; 163 | free(cur->next); 164 | cur->next = tmp; 165 | pr_debug("put case 0"); 166 | } else { 167 | /* Pass! */ 168 | pr_debug("put case 1"); 169 | } 170 | } else { 171 | pr_debug("put case 2"); 172 | tmp = cur->next; 173 | free(cur); 174 | prev->next = tmp; 175 | } 176 | out: 177 | mutex_unlock(&state->sess_map_lock); 178 | if (ret) 179 | errno = -ret; 180 | return ret; 181 | } 182 | 183 | 184 | int delete_udp_session(struct srv_udp_state *state, struct udp_sess *sess) 185 | __acquires(&state->sess_stk_lock) 186 | __releases(&state->sess_stk_lock) 187 | { 188 | int ret = 0; 189 | mutex_lock(&state->sess_stk_lock); 190 | BUG_ON(bt_stack_push(&state->sess_stk, sess->idx) == -1); 191 | if (state->sess_map) 192 | ret = remove_sess_from_bkt(state, sess); 193 | reset_udp_session(sess, sess->idx); 194 | mutex_unlock(&state->sess_stk_lock); 195 | atomic_fetch_sub(&state->n_on_sess, 1); 196 | return ret; 197 | } 198 | -------------------------------------------------------------------------------- /src/teavpn2/client/linux/udp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__CLIENT__LINUX__UDP_H 7 | #define TEAVPN2__CLIENT__LINUX__UDP_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | #define EPOLL_EVT_ARR_NUM 3u 23 | #define UDP_LOOP_C_DEADLINE 64 24 | #define UDP_SESS_TIMEOUT 180 25 | 26 | struct cli_udp_state; 27 | 28 | 29 | struct epl_thread { 30 | /* 31 | * Pointer to the UDP state struct. 32 | */ 33 | struct cli_udp_state *state; 34 | 35 | /* 36 | * pthread reference. 37 | */ 38 | pthread_t thread; 39 | 40 | int epoll_fd; 41 | int epoll_timeout; 42 | struct epoll_event events[EPOLL_EVT_ARR_NUM]; 43 | 44 | /* 45 | * Is this thread online? 46 | */ 47 | _Atomic(bool) is_online; 48 | 49 | uint16_t idx; 50 | struct sc_pkt *pkt; 51 | }; 52 | 53 | 54 | struct timer_thread { 55 | _Atomic(bool) is_online; 56 | pthread_t thread; 57 | }; 58 | 59 | 60 | struct cli_udp_state { 61 | /* 62 | * @stop is false when event loop is supposed to run. 63 | * @stop is true when event loop needs to be stopped. 64 | */ 65 | volatile bool stop; 66 | 67 | /* 68 | * @in_emergency will be true in case we run out of 69 | * buffer, or when we are in the similar urgent 70 | * situation that needs more attention. 71 | */ 72 | volatile bool in_emergency; 73 | 74 | 75 | bool timeout_disconnect; 76 | 77 | 78 | /* 79 | * Loop counter. 80 | */ 81 | uint8_t loop_c; 82 | 83 | 84 | /* 85 | * When we're exiting, the main thread will wait for 86 | * the subthreads to exit for the given timeout. If 87 | * the subthreads won't exit, @threads_wont_exit is 88 | * set to true. This is an indicator that we are not 89 | * allowed to free() and close() the resources as it 90 | * may lead to UAF bug. 91 | */ 92 | bool threads_wont_exit; 93 | 94 | /* 95 | * @need_remove_iff is true when we need to remove 96 | * virtual network interface configuration before 97 | * exit, otherwise it's false. 98 | */ 99 | bool need_remove_iff; 100 | 101 | 102 | /* 103 | * For timeout timer. 104 | */ 105 | time_t last_t; 106 | 107 | 108 | struct timer_thread tt; 109 | 110 | 111 | /* 112 | * @sig should contain signal after signal interrupt 113 | * handler is called. If the signal interrupt handle 114 | * is never called, the value of @sig should be -1. 115 | */ 116 | int sig; 117 | 118 | event_loop_t evt_loop; 119 | int udp_fd; 120 | struct cli_cfg *cfg; 121 | 122 | 123 | _Atomic(uint16_t) n_on_threads; 124 | 125 | 126 | /* 127 | * @tun_fds is an array of TUN file descriptors. 128 | * Number of TUN file descriptor can be more than 129 | * one because on Linux it's possible to parallelize 130 | * the read/write to TUN fd. 131 | */ 132 | int *tun_fds; 133 | 134 | struct sc_pkt *pkt; 135 | 136 | union { 137 | /* 138 | * For epoll event loop. 139 | */ 140 | struct { 141 | struct epl_thread *epl_threads; 142 | }; 143 | 144 | 145 | /* 146 | * For io_uring event loop. 147 | */ 148 | struct { 149 | struct iou_thread *iou_threads; 150 | }; 151 | }; 152 | }; 153 | 154 | 155 | extern int teavpn2_udp_client_epoll(struct cli_udp_state *state); 156 | extern int teavpn2_udp_client_io_uring(struct cli_udp_state *state); 157 | extern int teavpn2_cli_udp_send_close_packet(struct cli_udp_state *state); 158 | 159 | 160 | static inline int send_close_packet(struct cli_udp_state *state) 161 | { 162 | return teavpn2_cli_udp_send_close_packet(state); 163 | } 164 | 165 | 166 | static __always_inline size_t cli_pprep(struct cli_pkt *cli_pkt, uint8_t type, 167 | uint16_t data_len, uint8_t pad_len) 168 | { 169 | cli_pkt->type = type; 170 | cli_pkt->len = htons(data_len); 171 | cli_pkt->pad_len = pad_len; 172 | return (size_t)(data_len + PKT_MIN_LEN); 173 | } 174 | 175 | 176 | static __always_inline size_t cli_pprep_handshake(struct cli_pkt *cli_pkt) 177 | { 178 | struct pkt_handshake *hand = &cli_pkt->handshake; 179 | struct teavpn2_version *cur = &hand->cur; 180 | const uint16_t data_len = (uint16_t)sizeof(*hand); 181 | 182 | memset(hand, 0, sizeof(*hand)); 183 | cur->ver = VERSION; 184 | cur->patch_lvl = PATCHLEVEL; 185 | cur->sub_lvl = SUBLEVEL; 186 | strncpy2(cur->extra, EXTRAVERSION, sizeof(cur->extra)); 187 | 188 | return cli_pprep(cli_pkt, TCLI_PKT_HANDSHAKE, data_len, 0); 189 | 190 | } 191 | 192 | 193 | static inline size_t cli_pprep_auth(struct cli_pkt *cli_pkt, const char *user, 194 | const char *pass) 195 | { 196 | struct pkt_auth *auth = &cli_pkt->auth; 197 | const uint16_t data_len = (uint16_t)sizeof(*auth); 198 | 199 | strncpy2(auth->username, user, sizeof(auth->username)); 200 | strncpy2(auth->password, pass, sizeof(auth->password)); 201 | return cli_pprep(cli_pkt, TCLI_PKT_AUTH, data_len, 0); 202 | } 203 | 204 | 205 | static __always_inline int get_unix_time(time_t *tm) 206 | { 207 | int ret; 208 | struct timeval tv; 209 | ret = gettimeofday(&tv, NULL); 210 | if (unlikely(ret)) { 211 | ret = errno; 212 | pr_err("gettimeofday(): " PRERF, PREAR(ret)); 213 | return -ret; 214 | } 215 | *tm = tv.tv_sec; 216 | return ret; 217 | } 218 | 219 | 220 | #endif /* #ifndef TEAVPN2__CLIENT__LINUX__UDP_H */ 221 | -------------------------------------------------------------------------------- /src/teavpn2/arch/x86/linux.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__ARCH__X86__LINUX_H 7 | #define TEAVPN2__ARCH__X86__LINUX_H 8 | 9 | /** 10 | * Note for syscall registers usage (x86-64): 11 | * - %rax is the syscall number. 12 | * - %rax is also the return value. 13 | * - %rdi is the 1st argument. 14 | * - %rsi is the 2nd argument. 15 | * - %rdx is the 3rd argument. 16 | * - %r10 is the 4th argument (**yes it's %r10, not %rcx!**). 17 | * - %r8 is the 5th argument. 18 | * - %r9 is the 6th argument. 19 | * 20 | * `syscall` instruction will clobber %r11 and %rcx. 21 | * 22 | * After the syscall returns to userspace: 23 | * - %r11 will contain %rflags. 24 | * - %rcx will contain the return address. 25 | * 26 | * IOW, after the syscall returns to userspace: 27 | * %r11 == %rflags and %rcx == %rip. 28 | */ 29 | 30 | #define __do_syscall0(NUM) ({ \ 31 | intptr_t rax; \ 32 | \ 33 | __asm__ volatile( \ 34 | "syscall" \ 35 | : "=a"(rax) /* %rax */ \ 36 | : "a"(NUM) /* %rax */ \ 37 | : "rcx", "r11", "memory" \ 38 | ); \ 39 | rax; \ 40 | }) 41 | 42 | #define __do_syscall1(NUM, ARG1) ({ \ 43 | intptr_t rax; \ 44 | \ 45 | __asm__ volatile( \ 46 | "syscall" \ 47 | : "=a"(rax) /* %rax */ \ 48 | : "a"((NUM)), /* %rax */ \ 49 | "D"((ARG1)) /* %rdi */ \ 50 | : "rcx", "r11", "memory" \ 51 | ); \ 52 | rax; \ 53 | }) 54 | 55 | #define __do_syscall2(NUM, ARG1, ARG2) ({ \ 56 | intptr_t rax; \ 57 | \ 58 | __asm__ volatile( \ 59 | "syscall" \ 60 | : "=a"(rax) /* %rax */ \ 61 | : "a"((NUM)), /* %rax */ \ 62 | "D"((ARG1)), /* %rdi */ \ 63 | "S"((ARG2)) /* %rsi */ \ 64 | : "rcx", "r11", "memory" \ 65 | ); \ 66 | rax; \ 67 | }) 68 | 69 | #define __do_syscall3(NUM, ARG1, ARG2, ARG3) ({ \ 70 | intptr_t rax; \ 71 | \ 72 | __asm__ volatile( \ 73 | "syscall" \ 74 | : "=a"(rax) /* %rax */ \ 75 | : "a"((NUM)), /* %rax */ \ 76 | "D"((ARG1)), /* %rdi */ \ 77 | "S"((ARG2)), /* %rsi */ \ 78 | "d"((ARG3)) /* %rdx */ \ 79 | : "rcx", "r11", "memory" \ 80 | ); \ 81 | rax; \ 82 | }) 83 | 84 | #define __do_syscall4(NUM, ARG1, ARG2, ARG3, ARG4) ({ \ 85 | intptr_t rax; \ 86 | register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 87 | \ 88 | __asm__ volatile( \ 89 | "syscall" \ 90 | : "=a"(rax) /* %rax */ \ 91 | : "a"((NUM)), /* %rax */ \ 92 | "D"((ARG1)), /* %rdi */ \ 93 | "S"((ARG2)), /* %rsi */ \ 94 | "d"((ARG3)), /* %rdx */ \ 95 | "r"(__r10) /* %r10 */ \ 96 | : "rcx", "r11", "memory" \ 97 | ); \ 98 | rax; \ 99 | }) 100 | 101 | #define __do_syscall5(NUM, ARG1, ARG2, ARG3, ARG4, ARG5) ({ \ 102 | intptr_t rax; \ 103 | register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 104 | register __typeof__(ARG5) __r8 __asm__("r8") = (ARG5); \ 105 | \ 106 | __asm__ volatile( \ 107 | "syscall" \ 108 | : "=a"(rax) /* %rax */ \ 109 | : "a"((NUM)), /* %rax */ \ 110 | "D"((ARG1)), /* %rdi */ \ 111 | "S"((ARG2)), /* %rsi */ \ 112 | "d"((ARG3)), /* %rdx */ \ 113 | "r"(__r10), /* %r10 */ \ 114 | "r"(__r8) /* %r8 */ \ 115 | : "rcx", "r11", "memory" \ 116 | ); \ 117 | rax; \ 118 | }) 119 | 120 | #define __do_syscall6(NUM, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) ({ \ 121 | intptr_t rax; \ 122 | register __typeof__(ARG4) __r10 __asm__("r10") = (ARG4); \ 123 | register __typeof__(ARG5) __r8 __asm__("r8") = (ARG5); \ 124 | register __typeof__(ARG6) __r9 __asm__("r9") = (ARG6); \ 125 | \ 126 | __asm__ volatile( \ 127 | "syscall" \ 128 | : "=a"(rax) /* %rax */ \ 129 | : "a"((NUM)), /* %rax */ \ 130 | "D"((ARG1)), /* %rdi */ \ 131 | "S"((ARG2)), /* %rsi */ \ 132 | "d"((ARG3)), /* %rdx */ \ 133 | "r"(__r10), /* %r10 */ \ 134 | "r"(__r8), /* %r8 */ \ 135 | "r"(__r9) /* %r9 */ \ 136 | : "rcx", "r11", "memory" \ 137 | ); \ 138 | rax; \ 139 | }) 140 | 141 | 142 | #include 143 | #include 144 | #include 145 | #include 146 | #include 147 | #include 148 | 149 | static inline int __sys_epoll_wait(int epfd, struct epoll_event *events, 150 | int maxevents, int timeout) 151 | { 152 | return (int) __do_syscall4(__NR_epoll_wait, epfd, events, maxevents, 153 | timeout); 154 | } 155 | 156 | static inline ssize_t __sys_read(int fd, void *buf, size_t len) 157 | { 158 | return (ssize_t) __do_syscall3(__NR_read, fd, buf, len); 159 | } 160 | 161 | static inline ssize_t __sys_write(int fd, const void *buf, size_t len) 162 | { 163 | return (ssize_t) __do_syscall3(__NR_write, fd, buf, len); 164 | } 165 | 166 | static inline ssize_t __sys_recvfrom(int sockfd, void *buf, size_t len, 167 | int flags, struct sockaddr *src_addr, 168 | socklen_t *addrlen) 169 | { 170 | return (ssize_t) __do_syscall6(__NR_recvfrom, sockfd, buf, len, flags, 171 | src_addr, addrlen); 172 | } 173 | 174 | static inline ssize_t __sys_sendto(int sockfd, const void *buf, size_t len, 175 | int flags, const struct sockaddr *dest_addr, 176 | socklen_t addrlen) 177 | { 178 | return (ssize_t) __do_syscall6(__NR_sendto, sockfd, buf, len, flags, 179 | dest_addr, addrlen); 180 | } 181 | 182 | static inline int __sys_close(int fd) 183 | { 184 | return (int) __do_syscall1(__NR_close, fd); 185 | } 186 | 187 | 188 | #endif /* #ifndef TEAVPN2__ARCH__X86__LINUX_H */ 189 | -------------------------------------------------------------------------------- /src/ext/inih/inih.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | SPDX-License-Identifier: BSD-3-Clause 4 | 5 | Copyright (C) 2009-2020, Ben Hoyt 6 | 7 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 8 | home page for more info: 9 | 10 | https://github.com/benhoyt/inih 11 | 12 | */ 13 | 14 | #ifndef INI_H 15 | #define INI_H 16 | 17 | /* Make this header file easier to include in C++ code */ 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #include 23 | #include 24 | 25 | /* Nonzero if ini_handler callback should accept lineno parameter. */ 26 | #ifndef INI_HANDLER_LINENO 27 | #define INI_HANDLER_LINENO 0 28 | #endif 29 | 30 | /* Typedef for prototype of handler function. */ 31 | #if INI_HANDLER_LINENO 32 | typedef int (*ini_handler)(void* user, const char* section, 33 | const char* name, const char* value, 34 | int lineno); 35 | #else 36 | typedef int (*ini_handler)(void* user, const char* section, 37 | const char* name, const char* value); 38 | #endif 39 | 40 | /* Typedef for prototype of fgets-style reader function. */ 41 | typedef char* (*ini_reader)(char* str, int num, void* stream); 42 | 43 | /* Parse given INI-style file. May have [section]s, name=value pairs 44 | (whitespace stripped), and comments starting with ';' (semicolon). Section 45 | is "" if name=value pair parsed before any section heading. name:value 46 | pairs are also supported as a concession to Python's configparser. 47 | 48 | For each name=value pair parsed, call handler function with given user 49 | pointer as well as section, name, and value (data only valid for duration 50 | of handler call). Handler should return nonzero on success, zero on error. 51 | 52 | Returns 0 on success, line number of first error on parse error (doesn't 53 | stop on first error), -1 on file open error, or -2 on memory allocation 54 | error (only when INI_USE_STACK is zero). 55 | */ 56 | int ini_parse(const char* filename, ini_handler handler, void* user); 57 | 58 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 59 | close the file when it's finished -- the caller must do that. */ 60 | int ini_parse_file(FILE* file, ini_handler handler, void* user); 61 | 62 | /* Same as ini_parse(), but takes an ini_reader function pointer instead of 63 | filename. Used for implementing custom or string-based I/O (see also 64 | ini_parse_string). */ 65 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 66 | void* user); 67 | 68 | /* Same as ini_parse(), but takes a zero-terminated string with the INI data 69 | instead of a file. Useful for parsing INI data from a network socket or 70 | already in memory. */ 71 | int ini_parse_string(const char* string, ini_handler handler, void* user); 72 | 73 | /* Nonzero to allow multi-line value parsing, in the style of Python's 74 | configparser. If allowed, ini_parse() will call the handler with the same 75 | name for each subsequent line parsed. */ 76 | #ifndef INI_ALLOW_MULTILINE 77 | #define INI_ALLOW_MULTILINE 1 78 | #endif 79 | 80 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 81 | the file. See https://github.com/benhoyt/inih/issues/21 */ 82 | #ifndef INI_ALLOW_BOM 83 | #define INI_ALLOW_BOM 1 84 | #endif 85 | 86 | /* Chars that begin a start-of-line comment. Per Python configparser, allow 87 | both ; and # comments at the start of a line by default. */ 88 | #ifndef INI_START_COMMENT_PREFIXES 89 | #define INI_START_COMMENT_PREFIXES ";#" 90 | #endif 91 | 92 | /* Nonzero to allow inline comments (with valid inline comment characters 93 | specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 94 | Python 3.2+ configparser behaviour. */ 95 | #ifndef INI_ALLOW_INLINE_COMMENTS 96 | #define INI_ALLOW_INLINE_COMMENTS 1 97 | #endif 98 | #ifndef INI_INLINE_COMMENT_PREFIXES 99 | #define INI_INLINE_COMMENT_PREFIXES ";" 100 | #endif 101 | 102 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ 103 | #ifndef INI_USE_STACK 104 | #define INI_USE_STACK 1 105 | #endif 106 | 107 | /* Maximum line length for any line in INI file (stack or heap). Note that 108 | this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ 109 | #ifndef INI_MAX_LINE 110 | #define INI_MAX_LINE 200 111 | #endif 112 | 113 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a 114 | fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is 115 | zero. */ 116 | #ifndef INI_ALLOW_REALLOC 117 | #define INI_ALLOW_REALLOC 0 118 | #endif 119 | 120 | /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK 121 | is zero. */ 122 | #ifndef INI_INITIAL_ALLOC 123 | #define INI_INITIAL_ALLOC 200 124 | #endif 125 | 126 | /* Stop parsing on first error (default is to keep parsing). */ 127 | #ifndef INI_STOP_ON_FIRST_ERROR 128 | #define INI_STOP_ON_FIRST_ERROR 0 129 | #endif 130 | 131 | /* Nonzero to call the handler at the start of each new section (with 132 | name and value NULL). Default is to only call the handler on 133 | each name=value pair. */ 134 | #ifndef INI_CALL_HANDLER_ON_NEW_SECTION 135 | #define INI_CALL_HANDLER_ON_NEW_SECTION 0 136 | #endif 137 | 138 | /* Nonzero to allow a name without a value (no '=' or ':' on the line) and 139 | call the handler with value NULL in this case. Default is to treat 140 | no-value lines as an error. */ 141 | #ifndef INI_ALLOW_NO_VALUE 142 | #define INI_ALLOW_NO_VALUE 0 143 | #endif 144 | 145 | /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory 146 | allocation functions (INI_USE_STACK must also be 0). These functions must 147 | have the same signatures as malloc/free/realloc and behave in a similar 148 | way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ 149 | #ifndef INI_CUSTOM_ALLOCATOR 150 | #define INI_CUSTOM_ALLOCATOR 0 151 | #endif 152 | 153 | 154 | #ifdef __cplusplus 155 | } 156 | #endif 157 | 158 | #endif /* INI_H */ 159 | -------------------------------------------------------------------------------- /src/teavpn2/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__COMMON_H 7 | #define TEAVPN2__COMMON_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #ifndef unlikely 24 | # define unlikely(X) __builtin_expect((bool)(X), 0) 25 | #endif 26 | 27 | #ifndef likely 28 | # define likely(X) __builtin_expect((bool)(X), 1) 29 | #endif 30 | 31 | #if defined(__clang__) 32 | # pragma clang diagnostic push 33 | # pragma clang diagnostic ignored "-Wreserved-id-macro" 34 | #endif 35 | 36 | #ifndef ____stringify 37 | # define ____stringify(EXPR) #EXPR 38 | #endif 39 | 40 | #ifndef __stringify 41 | # define __stringify(EXPR) ____stringify(EXPR) 42 | #endif 43 | 44 | #ifndef __acquires 45 | # define __acquires(LOCK) 46 | #endif 47 | 48 | #ifndef __releases 49 | # define __releases(LOCK) 50 | #endif 51 | 52 | #ifndef __must_hold 53 | # define __must_hold(LOCK) 54 | #endif 55 | 56 | #ifndef __hot 57 | # define __hot __attribute__((__hot__)) 58 | #endif 59 | 60 | #ifndef __cold 61 | # define __cold __attribute__((__cold__)) 62 | #endif 63 | 64 | 65 | #ifndef __READ_ONCE 66 | # define __READ_ONCE(x) (*(const volatile __typeof__(x) *)&(x)) 67 | #endif 68 | 69 | #ifndef READ_ONCE 70 | #define READ_ONCE(x) \ 71 | ({ \ 72 | __READ_ONCE(x); \ 73 | }) 74 | #endif 75 | 76 | #ifndef __WRITE_ONCE 77 | #define __WRITE_ONCE(x, val) \ 78 | do { \ 79 | *(volatile __typeof__(x) *)&(x) = (val); \ 80 | } while (0) 81 | #endif 82 | 83 | #ifndef WRITE_ONCE 84 | #define WRITE_ONCE(x, val) \ 85 | do { \ 86 | __WRITE_ONCE(x, val); \ 87 | } while (0) 88 | #endif 89 | 90 | #if defined(__clang__) 91 | # pragma clang diagnostic pop 92 | #endif 93 | 94 | #ifndef INET_ADDRSTRLEN 95 | # define IPV4_L (sizeof("xxx.xxx.xxx.xxx")) 96 | #else 97 | # define IPV4_L (INET_ADDRSTRLEN) 98 | #endif 99 | 100 | #ifndef INET6_ADDRSTRLEN 101 | # define IPV6_L (sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxx.xxx.xxx.xxx")) 102 | #else 103 | # define IPV6_L (INET6_ADDRSTRLEN) 104 | #endif 105 | 106 | #define STR(a) #a 107 | #define XSTR(a) STR(a) 108 | 109 | #define TEAVPN2_VERSION \ 110 | XSTR(VERSION) "." XSTR(PATCHLEVEL) "." XSTR(SUBLEVEL) EXTRAVERSION 111 | 112 | 113 | #define TVPN_MAX_UNAME_LEN (0x100u) 114 | #define TVPN_MAX_PASS_LEN (0x100u) 115 | 116 | extern int run_client(int argc, char *argv[]); 117 | extern int run_server(int argc, char *argv[]); 118 | 119 | #define IFACENAMESIZ 16u 120 | 121 | typedef enum _sock_type_t { 122 | SOCK_UDP, 123 | SOCK_TCP 124 | } sock_type; 125 | 126 | typedef sock_type sock_type_t; 127 | 128 | typedef enum _event_loop_t { 129 | EVTL_NOP, 130 | EVTL_EPOLL, 131 | EVTL_IO_URING 132 | } event_loop_t; 133 | 134 | 135 | /* Make it 32 bytes in size. */ 136 | struct teavpn2_version { 137 | uint8_t ver; 138 | uint8_t patch_lvl; 139 | uint8_t sub_lvl; 140 | char extra[29]; 141 | }; 142 | 143 | static_assert(offsetof(struct teavpn2_version, ver) == 0, 144 | "Bad offsetof(struct teavpn2_version, ver)"); 145 | 146 | static_assert(offsetof(struct teavpn2_version, patch_lvl) == 1, 147 | "Bad offsetof(struct teavpn2_version, patch_lvl)"); 148 | 149 | static_assert(offsetof(struct teavpn2_version, sub_lvl) == 2, 150 | "Bad offsetof(struct teavpn2_version, sub_lvl)"); 151 | 152 | static_assert(offsetof(struct teavpn2_version, extra) == 3, 153 | "Bad offsetof(struct teavpn2_version, extra)"); 154 | 155 | static_assert(sizeof(struct teavpn2_version) == 32, 156 | "Bad sizeof(struct teavpn2_version)"); 157 | 158 | 159 | struct if_info { 160 | char dev[IFACENAMESIZ]; 161 | char ipv4_pub[IPV4_L]; 162 | char ipv4[IPV4_L]; 163 | char ipv4_netmask[IPV4_L]; 164 | char ipv4_dgateway[IPV4_L]; 165 | #ifdef TEAVPN_IPV6_SUPPORT 166 | char ipv6_pub[IPV6_L]; 167 | char ipv6[IPV6_L]; 168 | char ipv6_netmask[IPV6_L]; 169 | char ipv6_dgateway[IPV6_L]; 170 | #endif 171 | uint16_t ipv4_mtu; 172 | #ifdef TEAVPN_IPV6_SUPPORT 173 | uint16_t ipv6_mtu; 174 | #endif 175 | }; 176 | 177 | static_assert(IFACENAMESIZ == 16u, "Bad IFACENAMESIZ value"); 178 | 179 | 180 | static_assert(offsetof(struct if_info, dev) == 0, 181 | "Bad offsetof(struct if_info, dev)"); 182 | 183 | static_assert(offsetof(struct if_info, ipv4_pub) == 16, 184 | "Bad offsetof(struct if_info, ipv4_pub)"); 185 | 186 | static_assert(offsetof(struct if_info, ipv4) == 16 + (IPV4_L * 1), 187 | "Bad offsetof(struct if_info, ipv4)"); 188 | 189 | static_assert(offsetof(struct if_info, ipv4_netmask) == 16 + (IPV4_L * 2), 190 | "Bad offsetof(struct if_info, ipv4_netmask)"); 191 | 192 | static_assert(offsetof(struct if_info, ipv4_dgateway) == 16 + (IPV4_L * 3), 193 | "Bad offsetof(struct if_info, ipv4_dgateway)"); 194 | 195 | #ifdef TEAVPN_IPV6_SUPPORT 196 | 197 | /* 198 | * TODO: Add IPv6 static assert. 199 | */ 200 | static_assert(0, "Fixme: Add IPv6 static assert"); 201 | 202 | #else /* #ifdef TEAVPN_IPV6_SUPPORT */ 203 | 204 | static_assert(offsetof(struct if_info, ipv4_mtu) == 16 + (IPV4_L * 4), 205 | "Bad offsetof(struct if_info, mtu)"); 206 | 207 | static_assert(sizeof(struct if_info) == 16 + (IPV4_L * 4) + sizeof(uint16_t), 208 | "Bad sizeof(struct if_info)"); 209 | 210 | #endif /* #ifdef TEAVPN_IPV6_SUPPORT */ 211 | 212 | extern const char *data_dir; 213 | extern void show_version(void); 214 | extern bool teavpn2_auth(const char *username, const char *password, 215 | struct if_info *iff); 216 | 217 | static inline void *calloc_wrp(size_t nmemb, size_t size) 218 | { 219 | int err; 220 | void *ret = al64_calloc(nmemb, size); 221 | if (unlikely(!ret)) { 222 | err = errno; 223 | /* The errno might change after pr_err, must backup! */ 224 | pr_err("calloc_wrp: " PRERF, PREAR(err)); 225 | errno = err; 226 | } 227 | return ret; 228 | } 229 | 230 | 231 | #if !defined(__clang__) 232 | /* 233 | * GCC false positive warnings are annoying! 234 | */ 235 | # pragma GCC diagnostic push 236 | # pragma GCC diagnostic ignored "-Warray-bounds" 237 | # pragma GCC diagnostic ignored "-Wstringop-overflow" 238 | # pragma GCC diagnostic ignored "-Wstringop-truncation" 239 | #endif 240 | static inline char *strncpy2(char *__restrict__ dst, 241 | const char *__restrict__ src, 242 | size_t n) 243 | { 244 | char *ret = strncpy(dst, src, n); 245 | ret[n - 1] = '\0'; 246 | return ret; 247 | } 248 | #if !defined(__clang__) 249 | # pragma GCC diagnostic pop 250 | #endif 251 | 252 | #if defined(__x86_64__) 253 | # include 254 | #else 255 | # include 256 | #endif 257 | 258 | #ifdef CONFIG_HPC_EMERGENCY 259 | # include 260 | #else 261 | # define WARN() 262 | # define WARN_ONCE() 263 | # define WARN_ON(COND) ({ COND; }) 264 | # define WARN_ON_ONCE(COND) ({ COND; }) 265 | # define BUG() 266 | # define BUG_ON(COND) ({ COND; }) 267 | #endif 268 | 269 | #ifndef cpu_relax 270 | #if defined(__x86_64__) 271 | static inline void __cpu_relax(void) 272 | { 273 | __asm__ __volatile__ ("pause" ::: "memory"); 274 | } 275 | #else 276 | static inline void __cpu_relax(void) 277 | { 278 | __asm__ __volatile__ ("" ::: "memory"); 279 | } 280 | #endif 281 | #define cpu_relax() __cpu_relax() 282 | #endif 283 | 284 | #endif 285 | -------------------------------------------------------------------------------- /src/ext/inih/inih.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | SPDX-License-Identifier: BSD-3-Clause 4 | 5 | Copyright (C) 2009-2020, Ben Hoyt 6 | 7 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 8 | home page for more info: 9 | 10 | https://github.com/benhoyt/inih 11 | 12 | */ 13 | 14 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 15 | #define _CRT_SECURE_NO_WARNINGS 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #if !INI_USE_STACK 25 | #if INI_CUSTOM_ALLOCATOR 26 | #include 27 | void* ini_malloc(size_t size); 28 | void ini_free(void* ptr); 29 | void* ini_realloc(void* ptr, size_t size); 30 | #else 31 | #include 32 | #define ini_malloc malloc 33 | #define ini_free free 34 | #define ini_realloc realloc 35 | #endif 36 | #endif 37 | 38 | #define MAX_SECTION 50 39 | #define MAX_NAME 50 40 | 41 | /* Used by ini_parse_string() to keep track of string parsing state. */ 42 | typedef struct { 43 | const char* ptr; 44 | size_t num_left; 45 | } ini_parse_string_ctx; 46 | 47 | /* Strip whitespace chars off end of given string, in place. Return s. */ 48 | static char* rstrip(char* s) 49 | { 50 | char* p = s + strlen(s); 51 | while (p > s && isspace((unsigned char)(*--p))) 52 | *p = '\0'; 53 | return s; 54 | } 55 | 56 | typedef union _ret_char { 57 | char *chr; 58 | const char* cchr; 59 | } ret_char; 60 | 61 | /* Return pointer to first non-whitespace char in given string. */ 62 | static char* lskip(const char* s) 63 | { 64 | ret_char ret; 65 | while (*s && isspace((unsigned char)(*s))) 66 | s++; 67 | 68 | ret.cchr = s; 69 | return ret.chr; 70 | } 71 | 72 | /* Return pointer to first char (of chars) or inline comment in given string, 73 | or pointer to NUL at end of string if neither found. Inline comment must 74 | be prefixed by a whitespace character to register as a comment. */ 75 | static char* find_chars_or_comment(const char* s, const char* chars) 76 | { 77 | ret_char ret; 78 | #if INI_ALLOW_INLINE_COMMENTS 79 | int was_space = 0; 80 | while (*s && (!chars || !strchr(chars, *s)) && 81 | !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { 82 | was_space = isspace((unsigned char)(*s)); 83 | s++; 84 | } 85 | #else 86 | while (*s && (!chars || !strchr(chars, *s))) { 87 | s++; 88 | } 89 | #endif 90 | ret.cchr = s; 91 | return ret.chr; 92 | } 93 | 94 | /* Similar to strncpy, but ensures dest (size bytes) is 95 | NUL-terminated, and doesn't pad with NULs. */ 96 | static char* strncpy0(char* dest, const char* src, size_t size) 97 | { 98 | /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ 99 | size_t i; 100 | for (i = 0; i < size - 1 && src[i]; i++) 101 | dest[i] = src[i]; 102 | dest[i] = '\0'; 103 | return dest; 104 | } 105 | 106 | /* See documentation in header file. */ 107 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 108 | void* user) 109 | { 110 | /* Uses a fair bit of stack (use heap instead if you need to) */ 111 | #if INI_USE_STACK 112 | char line[INI_MAX_LINE]; 113 | int max_line = INI_MAX_LINE; 114 | #else 115 | char* line; 116 | size_t max_line = INI_INITIAL_ALLOC; 117 | #endif 118 | #if INI_ALLOW_REALLOC && !INI_USE_STACK 119 | char* new_line; 120 | size_t offset; 121 | #endif 122 | char section[MAX_SECTION] = ""; 123 | char prev_name[MAX_NAME] = ""; 124 | 125 | char* start; 126 | char* end; 127 | char* name; 128 | char* value; 129 | int lineno = 0; 130 | int error = 0; 131 | 132 | #if !INI_USE_STACK 133 | line = (char*)ini_malloc(INI_INITIAL_ALLOC); 134 | if (!line) { 135 | return -2; 136 | } 137 | #endif 138 | 139 | #if INI_HANDLER_LINENO 140 | #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) 141 | #else 142 | #define HANDLER(u, s, n, v) handler(u, s, n, v) 143 | #endif 144 | 145 | /* Scan through stream line by line */ 146 | while (reader(line, (int)max_line, stream) != NULL) { 147 | #if INI_ALLOW_REALLOC && !INI_USE_STACK 148 | offset = strlen(line); 149 | while (offset == max_line - 1 && line[offset - 1] != '\n') { 150 | max_line *= 2; 151 | if (max_line > INI_MAX_LINE) 152 | max_line = INI_MAX_LINE; 153 | new_line = ini_realloc(line, max_line); 154 | if (!new_line) { 155 | ini_free(line); 156 | return -2; 157 | } 158 | line = new_line; 159 | if (reader(line + offset, (int)(max_line - offset), stream) == NULL) 160 | break; 161 | if (max_line >= INI_MAX_LINE) 162 | break; 163 | offset += strlen(line + offset); 164 | } 165 | #endif 166 | 167 | lineno++; 168 | 169 | start = line; 170 | #if INI_ALLOW_BOM 171 | if (lineno == 1 && (unsigned char)start[0] == 0xEF && 172 | (unsigned char)start[1] == 0xBB && 173 | (unsigned char)start[2] == 0xBF) { 174 | start += 3; 175 | } 176 | #endif 177 | start = lskip(rstrip(start)); 178 | 179 | if (strchr(INI_START_COMMENT_PREFIXES, *start)) { 180 | /* Start-of-line comment */ 181 | } 182 | #if INI_ALLOW_MULTILINE 183 | else if (*prev_name && *start && start > line) { 184 | /* Non-blank line with leading whitespace, treat as continuation 185 | of previous name's value (as per Python configparser). */ 186 | if (!HANDLER(user, section, prev_name, start) && !error) 187 | error = lineno; 188 | } 189 | #endif 190 | else if (*start == '[') { 191 | /* A "[section]" line */ 192 | end = find_chars_or_comment(start + 1, "]"); 193 | if (*end == ']') { 194 | *end = '\0'; 195 | strncpy0(section, start + 1, sizeof(section)); 196 | *prev_name = '\0'; 197 | #if INI_CALL_HANDLER_ON_NEW_SECTION 198 | if (!HANDLER(user, section, NULL, NULL) && !error) 199 | error = lineno; 200 | #endif 201 | } 202 | else if (!error) { 203 | /* No ']' found on section line */ 204 | error = lineno; 205 | } 206 | } 207 | else if (*start) { 208 | /* Not a comment, must be a name[=:]value pair */ 209 | end = find_chars_or_comment(start, "=:"); 210 | if (*end == '=' || *end == ':') { 211 | *end = '\0'; 212 | name = rstrip(start); 213 | value = end + 1; 214 | #if INI_ALLOW_INLINE_COMMENTS 215 | end = find_chars_or_comment(value, NULL); 216 | if (*end) 217 | *end = '\0'; 218 | #endif 219 | value = lskip(value); 220 | rstrip(value); 221 | 222 | /* Valid name[=:]value pair found, call handler */ 223 | strncpy0(prev_name, name, sizeof(prev_name)); 224 | if (!HANDLER(user, section, name, value) && !error) 225 | error = lineno; 226 | } 227 | else if (!error) { 228 | /* No '=' or ':' found on name[=:]value line */ 229 | #if INI_ALLOW_NO_VALUE 230 | *end = '\0'; 231 | name = rstrip(start); 232 | if (!HANDLER(user, section, name, NULL) && !error) 233 | error = lineno; 234 | #else 235 | error = lineno; 236 | #endif 237 | } 238 | } 239 | 240 | #if INI_STOP_ON_FIRST_ERROR 241 | if (error) 242 | break; 243 | #endif 244 | } 245 | 246 | #if !INI_USE_STACK 247 | ini_free(line); 248 | #endif 249 | 250 | return error; 251 | } 252 | 253 | /* See documentation in header file. */ 254 | int ini_parse_file(FILE* file, ini_handler handler, void* user) 255 | { 256 | return ini_parse_stream((ini_reader)fgets, file, handler, user); 257 | } 258 | 259 | /* See documentation in header file. */ 260 | int ini_parse(const char* filename, ini_handler handler, void* user) 261 | { 262 | FILE* file; 263 | int error; 264 | 265 | file = fopen(filename, "r"); 266 | if (!file) 267 | return -1; 268 | error = ini_parse_file(file, handler, user); 269 | fclose(file); 270 | return error; 271 | } 272 | 273 | /* An ini_reader function to read the next line from a string buffer. This 274 | is the fgets() equivalent used by ini_parse_string(). */ 275 | static char* ini_reader_string(char* str, int num, void* stream) { 276 | ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; 277 | const char* ctx_ptr = ctx->ptr; 278 | size_t ctx_num_left = ctx->num_left; 279 | char* strp = str; 280 | char c; 281 | 282 | if (ctx_num_left == 0 || num < 2) 283 | return NULL; 284 | 285 | while (num > 1 && ctx_num_left != 0) { 286 | c = *ctx_ptr++; 287 | ctx_num_left--; 288 | *strp++ = c; 289 | if (c == '\n') 290 | break; 291 | num--; 292 | } 293 | 294 | *strp = '\0'; 295 | ctx->ptr = ctx_ptr; 296 | ctx->num_left = ctx_num_left; 297 | return str; 298 | } 299 | 300 | /* See documentation in header file. */ 301 | int ini_parse_string(const char* string, ini_handler handler, void* user) { 302 | ini_parse_string_ctx ctx; 303 | 304 | ctx.ptr = string; 305 | ctx.num_left = strlen(string); 306 | return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, 307 | user); 308 | } 309 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | # 5 | # Copyright (C) 2023 Ammar Faizi 6 | # 7 | # Inspired by liburing's configure script. 8 | # 9 | 10 | set -e; 11 | 12 | cc=${CC:-gcc}; 13 | cxx=${CXX:-g++}; 14 | ld=${LD:-$cc}; 15 | build_dir=${O:-$(dirname $(realpath $0))}; 16 | 17 | USER_CFLAGS="${CFLAGS}"; 18 | USER_CXXFLAGS="${CXXFLAGS}"; 19 | USER_LDFLAGS="${LDFLAGS}"; 20 | USER_LIB_LDFLAGS="${LIB_LDFLAGS}"; 21 | 22 | for opt do 23 | optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)' || true); 24 | case "$opt" in 25 | --help|-h) 26 | show_help=yes; 27 | ;; 28 | --build-dir=*) 29 | build_dir="$optarg"; 30 | ;; 31 | --prefix=*) 32 | prefix="$optarg"; 33 | ;; 34 | --includedir=*) 35 | includedir="$optarg"; 36 | ;; 37 | --libdir=*) 38 | libdir="$optarg"; 39 | ;; 40 | --libdevdir=*) 41 | libdevdir="$optarg"; 42 | ;; 43 | --mandir=*) 44 | mandir="$optarg"; 45 | ;; 46 | --datadir=*) 47 | datadir="$optarg"; 48 | ;; 49 | --cc=??*) 50 | cc="$optarg"; 51 | ;; 52 | --cxx=*) 53 | cxx="$optarg"; 54 | ;; 55 | --gui) 56 | use_gui="yes"; 57 | ;; 58 | --debug) 59 | use_debug="yes"; 60 | ;; 61 | *) 62 | echo "ERROR: unknown option $opt"; 63 | echo "Try '$0 --help' for more information"; 64 | exit 1; 65 | ;; 66 | esac; 67 | done 68 | 69 | if test -z "$prefix"; then 70 | prefix=/usr; 71 | fi 72 | 73 | if test -z "$includedir"; then 74 | includedir="$prefix/include"; 75 | fi 76 | 77 | if test -z "$libdir"; then 78 | libdir="$prefix/lib"; 79 | fi 80 | 81 | if test -z "$libdevdir"; then 82 | libdevdir="$prefix/lib"; 83 | fi 84 | 85 | if test -z "$mandir"; then 86 | mandir="$prefix/man"; 87 | fi 88 | 89 | if test -z "$datadir"; then 90 | datadir="$prefix/share"; 91 | fi 92 | 93 | if test "$show_help" = "yes"; then 94 | cat < 133 | trap "rm -rf \"${tmp_dir}\"" EXIT INT HUP QUIT TERM; 134 | 135 | fatal() 136 | { 137 | echo $@; 138 | echo "Configure failed, check config.log and/or the above output"; 139 | rm -f "${config_make}"; 140 | rm -f "${config_h}"; 141 | exit 1; 142 | } 143 | 144 | # Print result for each configuration test 145 | print_config() 146 | { 147 | printf " %-55s%s\n" "$1" "$2"; 148 | } 149 | 150 | # Default flags. 151 | CFLAGS="-O2 -D_GNU_SOURCE -include ${build_dir}/config.h"; 152 | CXXFLAGS="-O2 -D_GNU_SOURCE -include ${build_dir}/config.h"; 153 | LDFLAGS="-O2"; 154 | LIB_LDFLAGS="-lpthread"; 155 | 156 | # Print configure header at the top of $config_h. 157 | printf "/*\n" > $config_h; 158 | printf " * Automatically generated by configure - do not modify!\n" >> $config_h; 159 | printf " *\n" >> $config_h; 160 | printf " * Configured with:" >> $config_h; 161 | printf " * '%s'" "$0" "$@" >> $config_h; 162 | printf "\n" >> $config_h; 163 | printf " */\n" >> $config_h; 164 | 165 | printf "# Automatically generated by configure - do not modify!\n" > $config_make; 166 | printf "# Configured with:" >> $config_make; 167 | printf " '%s'" "$0" "$@" >> $config_make; 168 | printf "\n" >> $config_make; 169 | 170 | add_config_make() 171 | { 172 | printf "%s\n" "$1=$2" >> $config_make; 173 | } 174 | 175 | add_config_h() 176 | { 177 | printf "%s\n" "#define $1" >> $config_h; 178 | } 179 | 180 | add_config() 181 | { 182 | add_config_make $1 "y"; 183 | add_config_h $1; 184 | print_config "Adding $1=y"; 185 | } 186 | 187 | add_make_var() 188 | { 189 | printf "%s\n" "$1=$2" >> $config_make; 190 | } 191 | 192 | do_cc() 193 | { 194 | # Run the compiler, capturing its output to the log. 195 | echo "${cc}" "$@" >> $config_log; 196 | "${cc}" "$@" >> $config_log 2>&1 || return $?; 197 | return 0; 198 | } 199 | 200 | do_cxx() 201 | { 202 | # Run the compiler, capturing its output to the log. 203 | echo "${cxx}" "$@" >> $config_log; 204 | "${cxx}" "$@" >> $config_log 2>&1 || return $?; 205 | return 0; 206 | } 207 | 208 | compile_cc() 209 | { 210 | local_cflags="$1"; 211 | local_ldflags="$2 $LIBS"; 212 | printf "\n\nCompiling test case %s:\n" "$3" >> $config_log; 213 | do_cc $CFLAGS $local_cflags -o "${tmp_exe}" "${tmp_c}" \ 214 | ${LDFLAGS} ${local_ldflags}; 215 | } 216 | 217 | compile_cxx() 218 | { 219 | local_cxxflags="$1"; 220 | local_ldflags="$2 $LIBS"; 221 | printf "\n\nCompiling test case %s:" "$3" >> $config_log; 222 | do_cxx $CXXFLAGS $local_cxxflags -o "${tmp_exe}" "${tmp_cpp}" \ 223 | ${LDFLAGS} ${local_ldflags}; 224 | } 225 | 226 | __has_cflags() 227 | { 228 | cat > $tmp_c << EOF 229 | int main(void) 230 | { 231 | return 0; 232 | } 233 | EOF 234 | compile_cc "-Werror $1" "" "$1" || return $?; 235 | return 0; 236 | } 237 | 238 | __has_cxxflags() 239 | { 240 | cat > $tmp_cpp << EOF 241 | int main(void) 242 | { 243 | return 0; 244 | } 245 | EOF 246 | compile_cxx "-Werror $1" "" "$1" || return $?; 247 | return 0; 248 | } 249 | 250 | __has_c_and_cxxflags() 251 | { 252 | __has_cflags $1 || return $?; 253 | __has_cxxflags $1 || return $?; 254 | return 0; 255 | } 256 | 257 | add_c_flag() 258 | { 259 | local_append_flag=$1; 260 | if test ! -z "$2"; then 261 | local_append_flag="$2"; 262 | fi; 263 | 264 | if __has_cflags $1; then 265 | ret=0; 266 | support="yes"; 267 | CFLAGS="$1 ${CFLAGS}"; 268 | else 269 | ret=1; 270 | support="no"; 271 | fi; 272 | 273 | print_config "Does CC support ${1}?" "${support}"; 274 | return $ret; 275 | } 276 | 277 | add_cxx_flag() 278 | { 279 | local_append_flag=$1; 280 | if test ! -z "$2"; then 281 | local_append_flag="$2"; 282 | fi; 283 | 284 | if __has_cxxflags $1; then 285 | support="yes"; 286 | CXXFLAGS="$1 ${CXXFLAGS}"; 287 | else 288 | support="no"; 289 | fi; 290 | ret=$?; 291 | 292 | print_config "Does CXX support ${1}?" "${support}"; 293 | return $ret; 294 | } 295 | 296 | add_c_and_cxx_flag() 297 | { 298 | add_c_flag "$@"; 299 | add_cxx_flag "$@"; 300 | } 301 | 302 | printf "\n%s:\n" "Compilers"; 303 | print_config "CC" "${cc}"; 304 | print_config "CXX" "${cxx}"; 305 | 306 | # Don't exit if add_* functions return non-zero. 307 | set +e; 308 | 309 | printf "\n%s:\n" "-W flags"; 310 | add_c_and_cxx_flag "-Wall"; 311 | add_c_and_cxx_flag "-Wextra"; 312 | add_c_and_cxx_flag "-Wsequence-point"; 313 | add_c_and_cxx_flag "-Wunreachable-code"; 314 | add_c_and_cxx_flag "-Wunreachable-code-loop-increment"; 315 | add_c_and_cxx_flag "-Wformat-signedness"; 316 | add_c_and_cxx_flag "-Wformat-security"; 317 | add_c_and_cxx_flag "-Wformat"; 318 | add_c_and_cxx_flag "-Wstack-usage=4096"; 319 | add_c_and_cxx_flag "-Wmissing-prototypes"; 320 | add_c_and_cxx_flag "-Wstrict-prototypes"; 321 | add_c_and_cxx_flag "-Wmissing-variable-declarations"; 322 | add_c_and_cxx_flag "-Wstrict-aliasing=3"; 323 | add_c_and_cxx_flag "-Wshorten-64-to-32"; 324 | add_c_and_cxx_flag "-Wunsafe-loop-optimizations"; 325 | 326 | printf "\n%s:\n" "-f flags"; 327 | add_c_and_cxx_flag "-fno-stack-protector"; 328 | add_c_and_cxx_flag "-fdata-sections"; 329 | add_c_and_cxx_flag "-ffunction-sections"; 330 | add_c_and_cxx_flag "-fno-strict-aliasing"; 331 | add_c_and_cxx_flag "-fvisibility=hidden"; 332 | add_c_and_cxx_flag "-flto" && LDFLAGS="-flto ${LDFLAGS}"; 333 | add_c_and_cxx_flag "-fpie -fPIE" && LDFLAGS="-fpie -fPIE ${LDFLAGS}"; 334 | 335 | printf "\n%s:\n" "Misc flags"; 336 | LDFLAGS="-rdynamic ${LDFLAGS}"; 337 | add_c_and_cxx_flag "-ggdb3" && LDFLAGS="-ggdb3 ${LDFLAGS}"; 338 | 339 | # Bring it back! 340 | set -e; 341 | 342 | printf "\n%s:\n" "CONFIG"; 343 | add_config "CONFIG_POLL"; 344 | add_config "CONFIG_EPOLL"; 345 | add_config "CONFIG_IO_URING"; 346 | add_config "CONFIG_TEAVPN_SERVER"; 347 | add_config "CONFIG_TEAVPN_CLIENT"; 348 | add_config "CONFIG_LINUX"; 349 | 350 | if test "${use_gui}" = "yes"; then 351 | add_config "CONFIG_GUI"; 352 | gtk3_cflags=`pkg-config --cflags gtk+-3.0`; 353 | gtk3_lib_ldflags=`pkg-config --libs gtk+-3.0`; 354 | CFLAGS="${CFLAGS} ${gtk3_cflags}"; 355 | CXXFLAGS="${CXXFLAGS} ${gtk3_cflags}"; 356 | LIB_LDFLAGS="${LD_FLAGS} ${gtk3_lib_ldflags}"; 357 | fi; 358 | 359 | if test "${use_debug}" = "yes"; then 360 | add_config "CONFIG_DEBUG"; 361 | else 362 | CFLAGS="${CFLAGS} -DNDEBUG"; 363 | CXXFLAGS="${CXXFLAGS} -DNDEBUG"; 364 | fi; 365 | 366 | add_make_var "CC" "${cc}"; 367 | add_make_var "CXX" "${cxx}"; 368 | add_make_var "CFLAGS" "$(echo "${CFLAGS} ${USER_CFLAGS}" | awk '{$1=$1};1')"; 369 | add_make_var "CXXFLAGS" "$(echo "${CXXFLAGS} ${USER_CXXFLAGS}" | awk '{$1=$1};1')"; 370 | add_make_var "LDFLAGS" "$(echo "${LDFLAGS} ${USER_LIB_LDFLAGS}" | awk '{$1=$1};1')"; 371 | add_make_var "LIB_LDFLAGS" "$(echo "${LIB_LDFLAGS} ${USER_LIB_LDFLAGS}" | awk '{$1=$1};1')"; 372 | printf "override O=%s\n" "${build_dir}" >> $config_make; 373 | ln -s "${config_make}" >> /dev/null 2>&1 || true; 374 | printf "\n"; 375 | -------------------------------------------------------------------------------- /src/teavpn2/server/linux/udp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #ifndef TEAVPN2__SERVER__LINUX__UDP_H 7 | #define TEAVPN2__SERVER__LINUX__UDP_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | /* 23 | * The number of events for epoll_wait() array argument. 24 | */ 25 | #define EPOLL_EVT_ARR_NUM 3u 26 | 27 | /* 28 | * Tolerance number of errors per session. 29 | */ 30 | #define UDP_SESS_MAX_ERR 5u 31 | 32 | 33 | #define EPOLL_TIMEOUT 10000 34 | 35 | 36 | #define UDP_SESS_TIMEOUT_NO_AUTH 30 37 | #define UDP_SESS_TIMEOUT_AUTH 180 38 | 39 | 40 | 41 | /* 42 | * UDP session struct. 43 | * 44 | * Each client has its own UDP session struct. 45 | */ 46 | struct udp_sess { 47 | /* 48 | * Private IP address (virtual network interface). 49 | */ 50 | uint32_t ipv4_iff; 51 | 52 | /* 53 | * @src_addr is the UDP session source address. 54 | * @src_port is the UDP session source port. 55 | */ 56 | uint32_t src_addr; 57 | uint16_t src_port; 58 | 59 | /* 60 | * UDP sessions are stored in the array, this 61 | * @idx contains the index position of each 62 | * instance. 63 | */ 64 | uint16_t idx; 65 | 66 | /* 67 | * UDP is stateless, we may not know whether the 68 | * client is still online or not, @last_act can 69 | * be used to handle timeout for session closing 70 | * in case we have abnormal session termination. 71 | */ 72 | time_t last_act; 73 | 74 | /* 75 | * Big endian @src_addr and @src_port for sendto() call. 76 | */ 77 | struct sockaddr_in addr; 78 | 79 | /* 80 | * Session username. 81 | */ 82 | char username[0x100]; 83 | 84 | /* 85 | * Human readable of @src_addr. 86 | */ 87 | char str_src_addr[IPV4_L]; 88 | 89 | 90 | /* 91 | * Loop counter. 92 | */ 93 | uint8_t loop_c; 94 | 95 | /* 96 | * Error counter. 97 | */ 98 | uint8_t err_c; 99 | 100 | bool is_authenticated; 101 | _Atomic(bool) is_connected; 102 | }; 103 | 104 | 105 | /* 106 | * Bucket for session map. 107 | * 108 | * This is a very simple hash table to handle multiple 109 | * sessions at once. 110 | */ 111 | struct udp_map_bucket; 112 | 113 | struct udp_map_bucket { 114 | struct udp_map_bucket *next; 115 | struct udp_sess *sess; 116 | }; 117 | 118 | 119 | struct srv_udp_state; 120 | 121 | 122 | /* 123 | * Epoll thread. 124 | * 125 | * When we use epoll as event loop, each thread will have 126 | * one instance of this struct. 127 | * 128 | */ 129 | struct epl_thread { 130 | /* 131 | * Pointer to the UDP state struct. 132 | */ 133 | struct srv_udp_state *state; 134 | 135 | /* 136 | * pthread reference. 137 | */ 138 | pthread_t thread; 139 | 140 | int epoll_fd; 141 | int epoll_timeout; 142 | struct epoll_event events[EPOLL_EVT_ARR_NUM]; 143 | 144 | /* 145 | * Is this thread online? 146 | */ 147 | _Atomic(bool) is_online; 148 | 149 | /* 150 | * We have array of struct epl_thread, the @idx here 151 | * saves the index of the current struct. 152 | */ 153 | uint16_t idx; 154 | 155 | struct sc_pkt *pkt; 156 | }; 157 | 158 | 159 | /* 160 | * UDP is stateless, we need a zombie reaper to close 161 | * stale sessions. 162 | */ 163 | struct zombie_reaper { 164 | _Atomic(bool) is_online; 165 | pthread_t thread; 166 | struct sc_pkt *pkt; 167 | }; 168 | 169 | 170 | /* 171 | * This is a struct for event loop which uses io_uring. 172 | * (Work in progress...) 173 | */ 174 | struct iou_thread { 175 | /* 176 | * Pointer to the UDP state struct. 177 | */ 178 | struct srv_udp_state *state; 179 | }; 180 | 181 | 182 | struct srv_udp_state { 183 | /* 184 | * @stop is false when event loop is supposed to run. 185 | * @stop is true when event loop needs to be stopped. 186 | */ 187 | volatile bool stop; 188 | 189 | /* 190 | * @in_emergency will be true in case we run out of 191 | * buffer, or when we are in the similar urgent 192 | * situation that needs more attention. 193 | */ 194 | volatile bool in_emergency; 195 | 196 | /* 197 | * When we're exiting, the main thread will wait for 198 | * the subthreads to exit for the given timeout. If 199 | * the subthreads won't exit, @threads_wont_exit is 200 | * set to true. This is an indicator that we are not 201 | * allowed to free() and close() the resources as it 202 | * may lead to UAF bug. 203 | */ 204 | bool threads_wont_exit; 205 | 206 | /* 207 | * @need_remove_iff is true when we need to remove 208 | * virtual network interface configuration before 209 | * exit, otherwise it's false. 210 | */ 211 | bool need_remove_iff; 212 | 213 | 214 | /* 215 | * @sig should contain signal after signal interrupt 216 | * handler is called. If the signal interrupt handle 217 | * is never called, the value of @sig should be -1. 218 | */ 219 | int sig; 220 | 221 | event_loop_t evt_loop; 222 | int udp_fd; 223 | struct srv_cfg *cfg; 224 | 225 | /* 226 | * Stack to retrieve free UDP session index in O(1) 227 | * time complexity. 228 | */ 229 | struct bt_stack sess_stk; 230 | struct tmutex sess_stk_lock; 231 | 232 | /* 233 | * Small hash table for session lookup after recvfrom(). 234 | */ 235 | struct udp_map_bucket (*sess_map)[0x100]; 236 | struct tmutex sess_map_lock; 237 | 238 | /* 239 | * @sess_arr is an array of UDP sessions. 240 | */ 241 | struct udp_sess *sess_arr; 242 | 243 | /* 244 | * Number of active sessions in @sess_arr. 245 | */ 246 | _Atomic(uint16_t) n_on_sess; 247 | 248 | 249 | _Atomic(uint16_t) n_on_threads; 250 | 251 | 252 | /* 253 | * @tun_fds is an array of TUN file descriptors. 254 | * Number of TUN file descriptor can be more than 255 | * one because on Linux it's possible to parallelize 256 | * the read/write to TUN fd. 257 | */ 258 | int *tun_fds; 259 | 260 | /* 261 | * Map @ipv4_ff to @sess_arr index. 262 | */ 263 | uint16_t (*ipv4_map)[0x100]; 264 | 265 | /* 266 | * Zombie reaper. 267 | */ 268 | struct zombie_reaper zr; 269 | 270 | 271 | union { 272 | /* 273 | * For epoll event loop. 274 | */ 275 | struct { 276 | struct epl_thread *epl_threads; 277 | }; 278 | 279 | 280 | /* 281 | * For io_uring event loop. 282 | */ 283 | struct { 284 | struct iou_thread *iou_threads; 285 | }; 286 | }; 287 | }; 288 | 289 | 290 | #define W_IP(CLIENT) ((CLIENT)->str_src_addr), ((CLIENT)->src_port) 291 | #define W_UN(CLIENT) ((CLIENT)->username) 292 | #define W_IU(CLIENT) W_IP(CLIENT), W_UN(CLIENT), ((CLIENT)->idx) 293 | #define PRWIU "%s:%d (%s) (cli_idx=%hu)" 294 | 295 | 296 | extern int teavpn2_udp_server_epoll(struct srv_udp_state *state); 297 | extern int teavpn2_udp_server_io_uring(struct srv_udp_state *state); 298 | extern struct udp_sess *create_udp_sess(struct srv_udp_state *state, 299 | uint32_t addr, uint16_t port); 300 | extern struct udp_sess *lookup_udp_sess(struct srv_udp_state *state, 301 | uint32_t addr, uint16_t port); 302 | extern int delete_udp_session(struct srv_udp_state *state, 303 | struct udp_sess *sess); 304 | 305 | 306 | static __always_inline void reset_udp_session(struct udp_sess *sess, uint16_t idx) 307 | { 308 | memset(sess, 0, sizeof(*sess)); 309 | sess->username[0] = '_'; 310 | sess->idx = idx; 311 | } 312 | 313 | 314 | static __always_inline size_t srv_pprep(struct srv_pkt *srv_pkt, uint8_t type, 315 | uint16_t data_len, uint8_t pad_len) 316 | { 317 | srv_pkt->type = type; 318 | srv_pkt->len = htons(data_len); 319 | srv_pkt->pad_len = pad_len; 320 | return (size_t)(data_len + PKT_MIN_LEN); 321 | } 322 | 323 | 324 | static __always_inline size_t srv_pprep_handshake_reject(struct srv_pkt *srv_pkt, 325 | uint8_t reason, 326 | const char *msg) 327 | { 328 | struct pkt_handshake_reject *rej = &srv_pkt->hs_reject; 329 | uint16_t data_len = (uint16_t)sizeof(*rej); 330 | 331 | rej->reason = reason; 332 | if (!msg) 333 | memset(rej->msg, 0, sizeof(rej->msg)); 334 | else 335 | strncpy2(rej->msg, msg, sizeof(rej->msg)); 336 | 337 | return srv_pprep(srv_pkt, TSRV_PKT_HANDSHAKE_REJECT, data_len, 0); 338 | } 339 | 340 | 341 | static __always_inline size_t srv_pprep_handshake(struct srv_pkt *srv_pkt) 342 | { 343 | struct pkt_handshake *hand = &srv_pkt->handshake; 344 | struct teavpn2_version *cur = &hand->cur; 345 | uint16_t data_len = (uint16_t)sizeof(*hand); 346 | 347 | memset(hand, 0, sizeof(*hand)); 348 | 349 | cur->ver = VERSION; 350 | cur->patch_lvl = PATCHLEVEL; 351 | cur->sub_lvl = SUBLEVEL; 352 | strncpy2(cur->extra, EXTRAVERSION, sizeof(cur->extra)); 353 | 354 | return srv_pprep(srv_pkt, TSRV_PKT_HANDSHAKE, data_len, 0); 355 | } 356 | 357 | 358 | static __always_inline size_t srv_pprep_sync(struct srv_pkt *srv_pkt) 359 | { 360 | return srv_pprep(srv_pkt, TSRV_PKT_SYNC, 0, 0); 361 | } 362 | 363 | 364 | static __always_inline size_t srv_pprep_reqsync(struct srv_pkt *srv_pkt) 365 | { 366 | return srv_pprep(srv_pkt, TSRV_PKT_REQSYNC, 0, 0); 367 | } 368 | 369 | 370 | static inline int get_unix_time(time_t *tm) 371 | { 372 | int ret; 373 | struct timeval tv; 374 | ret = gettimeofday(&tv, NULL); 375 | if (unlikely(ret)) { 376 | ret = errno; 377 | pr_err("gettimeofday(): " PRERF, PREAR(ret)); 378 | return -ret; 379 | } 380 | *tm = tv.tv_sec; 381 | return ret; 382 | } 383 | 384 | 385 | static inline int udp_sess_update_last_act(struct udp_sess *sess) 386 | { 387 | return get_unix_time(&sess->last_act); 388 | } 389 | 390 | 391 | /* 392 | * The @addr is the private IP address (virtual network interface). 393 | */ 394 | static inline void add_ipv4_route_map(uint16_t (*ipv4_map)[0x100], uint32_t addr, 395 | uint16_t idx) 396 | { 397 | /* 398 | * IPv4 looks like this: 399 | * AA.BB.CC.DD 400 | * 401 | * DD is the byte0. 402 | * CC is the byte1. 403 | */ 404 | 405 | uint16_t byte0, byte1; 406 | 407 | byte0 = (addr >> 0u) & 0xffu; 408 | byte1 = (addr >> 8u) & 0xffu; 409 | ipv4_map[byte0][byte1] = idx + 1u; 410 | } 411 | 412 | 413 | static inline void del_ipv4_route_map(uint16_t (*ipv4_map)[0x100], uint32_t addr) 414 | { 415 | /* 416 | * IPv4 looks like this: 417 | * AA.BB.CC.DD 418 | * 419 | * DD is the byte0 420 | * CC is the byte1 421 | */ 422 | 423 | uint16_t byte0, byte1; 424 | 425 | byte0 = (addr >> 0u) & 0xffu; 426 | byte1 = (addr >> 8u) & 0xffu; 427 | ipv4_map[byte0][byte1] = 0; 428 | } 429 | 430 | 431 | static inline int32_t get_ipv4_route_map(uint16_t (*ipv4_map)[0x100], uint32_t addr) 432 | { 433 | uint16_t ret, byte0, byte1; 434 | 435 | byte0 = (addr >> 0u) & 0xffu; 436 | byte1 = (addr >> 8u) & 0xffu; 437 | ret = ipv4_map[byte0][byte1]; 438 | 439 | if (ret == 0) 440 | /* Unmapped address. */ 441 | return -ENOENT; 442 | 443 | return (int32_t)(ret - 1); 444 | } 445 | 446 | #endif /* #ifndef TEAVPN2__SERVER__LINUX__UDP_H */ 447 | -------------------------------------------------------------------------------- /src/teavpn2/server/linux/udp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | static struct srv_udp_state *g_state = NULL; 14 | 15 | 16 | static void signal_intr_handler(int sig) 17 | { 18 | struct srv_udp_state *state; 19 | 20 | state = g_state; 21 | if (unlikely(!state)) { 22 | panic("signal_intr_handler is called when g_state is NULL"); 23 | __builtin_unreachable(); 24 | } 25 | 26 | if (state->sig == -1) { 27 | state->stop = true; 28 | state->sig = sig; 29 | putchar('\n'); 30 | } 31 | } 32 | 33 | 34 | static int alloc_tun_fds_array(struct srv_udp_state *state) 35 | { 36 | int *tun_fds; 37 | uint8_t i, nn; 38 | 39 | nn = state->cfg->sys.thread_num; 40 | tun_fds = calloc_wrp(nn, sizeof(*tun_fds)); 41 | if (unlikely(!tun_fds)) 42 | return -errno; 43 | 44 | for (i = 0; i < nn; i++) 45 | tun_fds[i] = -1; 46 | 47 | state->tun_fds = tun_fds; 48 | return 0; 49 | } 50 | 51 | 52 | static int select_event_loop(struct srv_udp_state *state) 53 | { 54 | struct srv_cfg_sock *sock = &state->cfg->sock; 55 | const char *evtl = sock->event_loop; 56 | 57 | if ((evtl[0] == '\0') || (!strcmp(evtl, "epoll"))) { 58 | state->evt_loop = EVTL_EPOLL; 59 | } else if (!strcmp(evtl, "io_uring") || 60 | !strcmp(evtl, "io uring") || 61 | !strcmp(evtl, "iouring") || 62 | !strcmp(evtl, "uring")) { 63 | state->evt_loop = EVTL_IO_URING; 64 | } else { 65 | pr_err("Invalid socket event loop: \"%s\"", evtl); 66 | return -EINVAL; 67 | } 68 | 69 | switch (state->evt_loop) { 70 | case EVTL_EPOLL: 71 | state->epl_threads = NULL; 72 | break; 73 | case EVTL_IO_URING: 74 | state->iou_threads = NULL; 75 | break; 76 | case EVTL_NOP: 77 | default: 78 | panic("Aiee... invalid event loop value (%u)", state->evt_loop); 79 | __builtin_unreachable(); 80 | } 81 | return 0; 82 | } 83 | 84 | 85 | static int init_state(struct srv_udp_state *state) 86 | { 87 | int ret; 88 | struct sigaction act = { .sa_handler = signal_intr_handler }; 89 | 90 | prl_notice(2, "Initializing server state..."); 91 | 92 | g_state = state; 93 | state->udp_fd = -1; 94 | state->sig = -1; 95 | 96 | ret = alloc_tun_fds_array(state); 97 | if (unlikely(ret)) 98 | return ret; 99 | 100 | ret = select_event_loop(state); 101 | if (unlikely(ret)) 102 | return ret; 103 | 104 | prl_notice(2, "Setting up signal interrupt handler..."); 105 | 106 | if (unlikely(sigaction(SIGINT, &act, NULL) < 0)) 107 | goto sig_err; 108 | if (unlikely(sigaction(SIGTERM, &act, NULL) < 0)) 109 | goto sig_err; 110 | if (unlikely(sigaction(SIGHUP, &act, NULL) < 0)) 111 | goto sig_err; 112 | 113 | act.sa_handler = SIG_IGN; 114 | if (unlikely(sigaction(SIGPIPE, &act, NULL) < 0)) 115 | goto sig_err; 116 | 117 | prl_notice(2, "Server state is initialized successfully!"); 118 | return ret; 119 | 120 | sig_err: 121 | ret = errno; 122 | pr_err("sigaction(): " PRERF, PREAR(ret)); 123 | return -ret; 124 | } 125 | 126 | 127 | static int socket_setup(int udp_fd, struct srv_udp_state *state) 128 | { 129 | int y; 130 | int err; 131 | int ret; 132 | const char *lv, *on; /* level and optname */ 133 | socklen_t len = sizeof(y); 134 | struct srv_cfg *cfg = state->cfg; 135 | const void *py = (const void *)&y; 136 | 137 | 138 | y = 6; 139 | ret = setsockopt(udp_fd, SOL_SOCKET, SO_PRIORITY, py, len); 140 | if (unlikely(ret)) { 141 | lv = "SOL_SOCKET"; 142 | on = "SO_PRIORITY"; 143 | goto out_err; 144 | } 145 | 146 | 147 | y = 1024 * 1024 * 50; 148 | ret = setsockopt(udp_fd, SOL_SOCKET, SO_RCVBUFFORCE, py, len); 149 | if (unlikely(ret)) { 150 | lv = "SOL_SOCKET"; 151 | on = "SO_RCVBUFFORCE"; 152 | goto out_err; 153 | } 154 | 155 | 156 | y = 1024 * 1024 * 100; 157 | ret = setsockopt(udp_fd, SOL_SOCKET, SO_SNDBUFFORCE, py, len); 158 | if (unlikely(ret)) { 159 | lv = "SOL_SOCKET"; 160 | on = "SO_SNDBUFFORCE"; 161 | goto out_err; 162 | } 163 | 164 | 165 | y = 50000; 166 | ret = setsockopt(udp_fd, SOL_SOCKET, SO_BUSY_POLL, py, len); 167 | if (unlikely(ret)) { 168 | lv = "SOL_SOCKET"; 169 | on = "SO_BUSY_POLL"; 170 | goto out_err; 171 | } 172 | 173 | 174 | /* 175 | * TODO: Use cfg to set some socket options. 176 | */ 177 | (void)cfg; 178 | return ret; 179 | 180 | 181 | out_err: 182 | err = errno; 183 | pr_err("setsockopt(udp_fd, %s, %s, %d): " PRERF, lv, on, y, PREAR(err)); 184 | return ret; 185 | } 186 | 187 | 188 | static int init_socket(struct srv_udp_state *state) 189 | { 190 | int ret; 191 | int type; 192 | int udp_fd; 193 | struct sockaddr_in addr; 194 | struct srv_cfg_sock *sock = &state->cfg->sock; 195 | 196 | 197 | type = SOCK_DGRAM; 198 | if (state->evt_loop != EVTL_IO_URING) 199 | type |= SOCK_NONBLOCK; 200 | 201 | 202 | prl_notice(2, "Initializing UDP socket..."); 203 | udp_fd = socket(AF_INET, type, 0); 204 | if (unlikely(udp_fd < 0)) { 205 | const char *q = (type & SOCK_NONBLOCK) ? " | SOCK_NONBLOCK" : ""; 206 | ret = errno; 207 | pr_err("socket(AF_INET, SOCK_DGRAM%s, 0): " PRERF, q, PREAR(ret)); 208 | return -ret; 209 | } 210 | prl_notice(2, "UDP socket initialized successfully (fd=%d)", udp_fd); 211 | 212 | 213 | prl_notice(2, "Setting up socket configuration..."); 214 | ret = socket_setup(udp_fd, state); 215 | if (unlikely(ret)) 216 | goto out_err; 217 | 218 | 219 | memset(&addr, 0, sizeof(addr)); 220 | addr.sin_family = AF_INET; 221 | addr.sin_port = htons(sock->bind_port); 222 | addr.sin_addr.s_addr = inet_addr(sock->bind_addr); 223 | prl_notice(2, "Binding UDP socket to %s:%hu...", sock->bind_addr, 224 | sock->bind_port); 225 | 226 | 227 | ret = bind(udp_fd, (struct sockaddr *)&addr, sizeof(addr)); 228 | if (unlikely(ret < 0)) { 229 | ret = errno; 230 | pr_err("bind(): " PRERF, PREAR(ret)); 231 | goto out_err; 232 | } 233 | 234 | 235 | state->udp_fd = udp_fd; 236 | return 0; 237 | 238 | 239 | out_err: 240 | __sys_close(udp_fd); 241 | return -ret; 242 | } 243 | 244 | 245 | static int init_iface(struct srv_udp_state *state) 246 | { 247 | uint8_t i, nn; 248 | int ret = 0, tun_fd, *tun_fds; 249 | const char *dev = state->cfg->iface.dev; 250 | short flags = IFF_TUN | IFF_NO_PI | IFF_MULTI_QUEUE; 251 | 252 | 253 | if (unlikely(!dev || !*dev)) { 254 | pr_err("iface dev cannot be empty!"); 255 | return -EINVAL; 256 | } 257 | 258 | 259 | prl_notice(2, "Initializing virtual network interface (%s)...", dev); 260 | 261 | 262 | tun_fds = state->tun_fds; 263 | nn = state->cfg->sys.thread_num; 264 | for (i = 0; i < nn; i++) { 265 | prl_notice(4, "Initializing tun_fds[%hhu]...", i); 266 | 267 | tun_fd = tun_alloc(dev, flags); 268 | if (unlikely(tun_fd < 0)) { 269 | pr_err("tun_alloc(\"%s\", %d): " PRERF, dev, flags, 270 | PREAR(-tun_fd)); 271 | ret = tun_fd; 272 | goto err; 273 | } 274 | 275 | if (state->evt_loop != EVTL_IO_URING) { 276 | ret = fd_set_nonblock(tun_fd); 277 | if (unlikely(ret < 0)) { 278 | pr_err("fd_set_nonblock(%d): " PRERF, tun_fd, 279 | PREAR(-ret)); 280 | __sys_close(tun_fd); 281 | goto err; 282 | } 283 | } 284 | 285 | tun_fds[i] = tun_fd; 286 | prl_notice(4, "Successfully initialized tun_fds[%hhu] (fd=%d)", 287 | i, tun_fd); 288 | } 289 | 290 | if (unlikely(!teavpn_iface_up(&state->cfg->iface.iff))) { 291 | pr_err("teavpn_iface_up(): cannot bring up network interface"); 292 | return -ENETDOWN; 293 | } 294 | 295 | state->need_remove_iff = true; 296 | prl_notice(2, "Virtual network interface initialized successfully!"); 297 | return ret; 298 | err: 299 | while (i--) { 300 | __sys_close(tun_fds[i]); 301 | tun_fds[i] = -1; 302 | } 303 | return ret; 304 | } 305 | 306 | 307 | static int init_udp_session_array(struct srv_udp_state *state) 308 | { 309 | int ret = 0; 310 | struct udp_sess *sess_arr; 311 | uint16_t i, max_conn = state->cfg->sock.max_conn; 312 | 313 | prl_notice(4, "Initializing UDP session array..."); 314 | sess_arr = calloc_wrp((size_t)max_conn, sizeof(*sess_arr)); 315 | if (unlikely(!sess_arr)) 316 | return -errno; 317 | 318 | state->sess_arr = sess_arr; 319 | for (i = 0; i < max_conn; i++) 320 | reset_udp_session(&sess_arr[i], i); 321 | 322 | return ret; 323 | } 324 | 325 | 326 | static int init_udp_session_map(struct srv_udp_state *state) 327 | { 328 | int ret; 329 | size_t len = 0x100u * 0x100u; 330 | struct udp_map_bucket (*sess_map)[0x100u]; 331 | 332 | prl_notice(4, "Initializing UDP session map..."); 333 | sess_map = calloc_wrp(len, sizeof(struct udp_map_bucket)); 334 | if (unlikely(!sess_map)) 335 | return -errno; 336 | 337 | ret = mutex_init(&state->sess_map_lock, NULL); 338 | if (unlikely(ret)) 339 | return -ret; 340 | 341 | state->sess_map = sess_map; 342 | return ret; 343 | } 344 | 345 | 346 | static int init_udp_session_stack(struct srv_udp_state *state) 347 | { 348 | int ret; 349 | uint16_t i, max_conn = state->cfg->sock.max_conn; 350 | 351 | prl_notice(4, "Initializing UDP session stack..."); 352 | if (unlikely(!bt_stack_init(&state->sess_stk, max_conn))) 353 | return -errno; 354 | 355 | ret = mutex_init(&state->sess_stk_lock, NULL); 356 | if (unlikely(ret)) 357 | return -ret; 358 | 359 | #ifndef NDEBUG 360 | for (i = 0; i < 100; i++) 361 | bt_stack_test(&state->sess_stk); 362 | #endif 363 | 364 | for (i = max_conn; i--;) { 365 | int32_t tmp = bt_stack_push(&state->sess_stk, (uint16_t)i); 366 | if (unlikely(tmp == -1)) { 367 | panic("Fatal bug in init_udp_session_stack!"); 368 | __builtin_unreachable(); 369 | } 370 | } 371 | 372 | return 0; 373 | } 374 | 375 | 376 | static int init_ipv4_map(struct srv_udp_state *state) 377 | { 378 | uint16_t (*ipv4_map)[0x100]; 379 | 380 | ipv4_map = calloc_wrp(0x100ul * 0x100ul, sizeof(uint16_t)); 381 | if (unlikely(!ipv4_map)) 382 | return -errno; 383 | 384 | state->ipv4_map = ipv4_map; 385 | return 0; 386 | } 387 | 388 | 389 | static int run_server_event_loop(struct srv_udp_state *state) 390 | { 391 | switch (state->evt_loop) { 392 | case EVTL_EPOLL: 393 | return teavpn2_udp_server_epoll(state); 394 | case EVTL_IO_URING: 395 | pr_err("run_client_event_loop() with io_uring: " PRERF, 396 | PREAR(EOPNOTSUPP)); 397 | return -EOPNOTSUPP; 398 | case EVTL_NOP: 399 | default: 400 | panic("Aiee... invalid event loop value (%u)", state->evt_loop); 401 | __builtin_unreachable(); 402 | } 403 | } 404 | 405 | 406 | static void close_udp_fd(struct srv_udp_state *state) 407 | { 408 | int udp_fd = state->udp_fd; 409 | 410 | if (udp_fd != -1) { 411 | prl_notice(2, "Closing udp_fd (fd=%d)...", udp_fd); 412 | __sys_close(udp_fd); 413 | } 414 | } 415 | 416 | 417 | static void close_tun_fds(struct srv_udp_state *state) 418 | { 419 | uint8_t i, nn; 420 | int *tun_fds = state->tun_fds; 421 | 422 | if (!tun_fds) 423 | return; 424 | 425 | nn = state->cfg->sys.thread_num; 426 | for (i = 0; i < nn; i++) { 427 | int tun_fd = tun_fds[i]; 428 | if (tun_fd == -1) 429 | continue; 430 | prl_notice(2, "Closing tun_fds[%hhu] (fd=%d)...", i, tun_fd); 431 | __sys_close(tun_fd); 432 | } 433 | } 434 | 435 | 436 | static void close_fds_state(struct srv_udp_state *state) 437 | { 438 | close_udp_fd(state); 439 | close_tun_fds(state); 440 | } 441 | 442 | 443 | static void destroy_state(struct srv_udp_state *state) 444 | { 445 | 446 | if (WARN_ON(state->threads_wont_exit)) 447 | /* 448 | * When we're exiting, the main thread will wait for 449 | * the subthreads to exit for the given timeout. If 450 | * the subthreads won't exit, @threads_wont_exit is 451 | * set to true. This is an indicator that we are not 452 | * allowed to free() and close() the resources as it 453 | * may lead to UAF bug. 454 | */ 455 | return; 456 | 457 | close_fds_state(state); 458 | bt_stack_destroy(&state->sess_stk); 459 | al64_free(state->sess_arr); 460 | al64_free(state->sess_map); 461 | al64_free(state->ipv4_map); 462 | al64_free(state->tun_fds); 463 | al64_free(state); 464 | } 465 | 466 | 467 | int teavpn2_server_udp_run(struct srv_cfg *cfg) 468 | { 469 | int ret; 470 | struct srv_udp_state *state; 471 | 472 | state = calloc_wrp(1ul, sizeof(*state)); 473 | if (unlikely(!state)) 474 | return -errno; 475 | 476 | state->cfg = cfg; 477 | ret = init_state(state); 478 | if (unlikely(ret)) 479 | goto out; 480 | ret = init_socket(state); 481 | if (unlikely(ret)) 482 | goto out; 483 | ret = init_iface(state); 484 | if (unlikely(ret)) 485 | goto out; 486 | ret = init_udp_session_array(state); 487 | if (unlikely(ret)) 488 | goto out; 489 | ret = init_udp_session_map(state); 490 | if (unlikely(ret)) 491 | goto out; 492 | ret = init_udp_session_stack(state); 493 | if (unlikely(ret)) 494 | goto out; 495 | ret = init_ipv4_map(state); 496 | if (unlikely(ret)) 497 | goto out; 498 | ret = run_server_event_loop(state); 499 | out: 500 | destroy_state(state); 501 | return ret; 502 | } 503 | -------------------------------------------------------------------------------- /src/teavpn2/client/entry.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | struct cfg_parse_ctx { 13 | struct cli_cfg *cfg; 14 | }; 15 | 16 | 17 | /* 18 | * Default config. 19 | */ 20 | static const uint16_t d_cli_server_port = 44444; 21 | static const char d_cli_dev[] = "tcli0"; 22 | static const char d_cli_cfg_file[] = "/etc/teavpn2/client.ini"; 23 | static const uint8_t d_num_of_threads = 2; 24 | 25 | 26 | static void set_default_config(struct cli_cfg *cfg) 27 | { 28 | struct cli_cfg_sys *sys = &cfg->sys; 29 | struct cli_cfg_sock *sock = &cfg->sock; 30 | struct cli_cfg_iface *iface = &cfg->iface; 31 | 32 | sys->cfg_file = d_cli_cfg_file; 33 | sys->thread_num = d_num_of_threads; 34 | 35 | strncpy2(iface->dev, d_cli_dev, sizeof(iface->dev)); 36 | strncpy2(iface->iff.dev, d_cli_dev, sizeof(iface->iff.dev)); 37 | 38 | sock->server_port = d_cli_server_port; 39 | } 40 | 41 | 42 | static void teavpn_client_show_help(const char *app) 43 | { 44 | printf("Usage: %s client [options]\n", app); 45 | 46 | printf("\n"); 47 | printf("TeaVPN Client Application\n"); 48 | printf("\n"); 49 | printf("Available options:\n"); 50 | printf(" -h, --help\t\t\tShow this help message.\n"); 51 | printf(" -V, --version\t\t\tShow application version.\n"); 52 | printf(" -c, --config=FILE\t\tSet config file (default: %s).\n", 53 | d_cli_cfg_file); 54 | printf(" -d, --data-dir=DIR\t\tSet data directory.\n"); 55 | printf(" -t, --thread=N\t\tSet number of threads (default: %hhu).\n", 56 | d_num_of_threads); 57 | 58 | printf("\n"); 59 | printf("[Config options]\n"); 60 | printf(" Virtual network interface:\n"); 61 | printf(" -D, --dev=DEV\t\t\tSet virtual network interface name" 62 | " (default: %s).\n", d_cli_dev); 63 | 64 | 65 | printf("\n"); 66 | printf(" Socket:\n"); 67 | printf(" -s, --sock-type=TYPE\t\tSet socket type (must be tcp or udp)" 68 | " (default: tcp).\n"); 69 | printf(" -H, --server-addr=IP\t\tSet server address.\n"); 70 | printf(" -P, --server-port=PORT\tSet server port (default: %d).\n", 71 | d_cli_server_port); 72 | 73 | printf("\n"); 74 | printf(" Auth:\n"); 75 | printf(" -u, --username\t\tSet username.\n"); 76 | printf(" -p, --password=PASSWORD\tSet Password.\n"); 77 | 78 | printf("\n"); 79 | printf("\n"); 80 | printf("For bug reporting, please open an issue on the GitHub repository." 81 | "\n"); 82 | printf("GitHub repository: https://github.com/TeaInside/teavpn2\n"); 83 | printf("\n"); 84 | printf("This software is licensed under GNU GPL-v2 license.\n"); 85 | } 86 | 87 | 88 | #define PR_CFG(C, FMT) printf(" " #C " = " FMT "\n", C) 89 | 90 | static void dump_client_cfg(struct cli_cfg *cfg) 91 | { 92 | puts("============================================="); 93 | puts(" Config dump "); 94 | puts("============================================="); 95 | PR_CFG(cfg->sys.cfg_file, "%s"); 96 | PR_CFG(cfg->sys.data_dir, "%s"); 97 | PR_CFG(cfg->sys.thread_num, "%hhu"); 98 | PR_CFG(cfg->sys.verbose_level, "%hhu"); 99 | putchar('\n'); 100 | printf(" cfg->sock.use_encryption = %hhu\n", 101 | (uint8_t)cfg->sock.use_encryption); 102 | printf(" cfg->sock.type = %s\n", 103 | (cfg->sock.type == SOCK_TCP) ? "SOCK_TCP" : 104 | ((cfg->sock.type == SOCK_UDP) ? "SOCK_UDP" : "unknown")); 105 | PR_CFG(cfg->sock.server_addr, "%s"); 106 | PR_CFG(cfg->sock.server_port, "%hu"); 107 | PR_CFG(cfg->sock.event_loop, "%s"); 108 | putchar('\n'); 109 | PR_CFG(cfg->iface.dev, "%s"); 110 | puts("============================================="); 111 | } 112 | 113 | 114 | static const struct option long_options[] = { 115 | {"help", no_argument, 0, 'h'}, 116 | {"version", no_argument, 0, 'V'}, 117 | {"verbose", optional_argument, 0, 'v'}, 118 | 119 | {"config", required_argument, 0, 'c'}, 120 | {"data-dir", required_argument, 0, 'd'}, 121 | {"thread", required_argument, 0, 't'}, 122 | 123 | {"dev", required_argument, 0, 'D'}, 124 | 125 | {"sock-type", required_argument, 0, 's'}, 126 | {"server-addr", required_argument, 0, 'H'}, 127 | {"server-port", required_argument, 0, 'P'}, 128 | {"encrypt", no_argument, 0, 'E'}, 129 | 130 | {"username", required_argument, 0, 'u'}, 131 | {"password", required_argument, 0, 'p'}, 132 | 133 | {0, 0, 0, 0} 134 | }; 135 | static const char short_opt[] = "hVv::c:d:t:D:s:H:P:Eu:p:"; 136 | 137 | 138 | static __cold int parse_argv(int argc, char *argv[], struct cli_cfg *cfg) 139 | { 140 | int c; 141 | struct cli_cfg_sys *sys = &cfg->sys; 142 | struct cli_cfg_sock *sock = &cfg->sock; 143 | struct cli_cfg_iface *iface = &cfg->iface; 144 | struct cli_cfg_auth *auth = &cfg->auth; 145 | 146 | while (1) { 147 | int opt_idx = 0; 148 | 149 | c = getopt_long(argc - 1, argv + 1, short_opt, long_options, 150 | &opt_idx); 151 | if (c == -1) 152 | break; 153 | 154 | switch (c) { 155 | case 'h': 156 | teavpn_client_show_help(argv[0]); 157 | exit(0); 158 | case 'V': 159 | show_version(); 160 | exit(0); 161 | case 'v': { 162 | uint8_t level = optarg ? (uint8_t)atoi(optarg) : 6u; 163 | set_notice_level(level); 164 | sys->verbose_level = level; 165 | break; 166 | } 167 | 168 | 169 | /* 170 | * Sys config 171 | */ 172 | case 'c': 173 | sys->cfg_file = optarg; 174 | break; 175 | case 'd': 176 | strncpy2(sys->data_dir, optarg, sizeof(sys->data_dir)); 177 | break; 178 | case 't': { 179 | int tmp = atoi(optarg); 180 | if (tmp <= 0) { 181 | pr_err("Thread num argument must be greater than 0"); 182 | return -EINVAL; 183 | } 184 | sys->thread_num = (uint8_t)tmp; 185 | break; 186 | } 187 | 188 | 189 | /* 190 | * Sock config 191 | */ 192 | case 's': { 193 | char tmp[5], *p = tmp; 194 | 195 | strncpy(tmp, optarg, sizeof(tmp)); 196 | tmp[sizeof(tmp) - 1] = '\0'; 197 | 198 | while (*p) { 199 | *p = (char)tolower((unsigned char)*p); 200 | p++; 201 | } 202 | 203 | if (!strcmp(tmp, "tcp")) { 204 | sock->type = SOCK_TCP; 205 | } else if (!strcmp(tmp, "udp")) { 206 | sock->type = SOCK_UDP; 207 | } else { 208 | pr_err("Invalid socket type: %s", optarg); 209 | return -EINVAL; 210 | } 211 | break; 212 | } 213 | case 'H': 214 | strncpy2(sock->server_addr, optarg, sizeof(sock->server_addr)); 215 | break; 216 | case 'P': 217 | sock->server_port = (uint16_t)atoi(optarg); 218 | break; 219 | case 'E': 220 | sock->use_encryption = atoi(optarg) ? true : false; 221 | break; 222 | 223 | /* 224 | * Iface config 225 | */ 226 | case 'D': 227 | strncpy2(iface->dev, optarg, sizeof(iface->dev)); 228 | break; 229 | 230 | /* Auth */ 231 | case 'u': 232 | strncpy2(auth->username, optarg, sizeof(auth->username)); 233 | break; 234 | case 'p': 235 | strncpy2(auth->password, optarg, sizeof(auth->password)); 236 | break; 237 | default: 238 | return -EINVAL; 239 | } 240 | } 241 | 242 | return 0; 243 | } 244 | 245 | 246 | static int cfg_parse_section_sys(struct cfg_parse_ctx *ctx, const char *name, 247 | const char *val, int lineno) 248 | { 249 | struct cli_cfg *cfg = ctx->cfg; 250 | if (!strcmp(name, "thread")) { 251 | cfg->sys.thread_num = (uint8_t)strtoul(val, NULL, 10); 252 | } else if (!strcmp(name, "verbose_level")) { 253 | uint8_t level = (uint8_t)strtoul(val, NULL, 10); 254 | set_notice_level(level); 255 | cfg->sys.verbose_level = level; 256 | } else if (!strcmp(name, "data_dir")) { 257 | strncpy(cfg->sys.data_dir, val, sizeof(cfg->sys.data_dir)); 258 | cfg->sys.data_dir[sizeof(cfg->sys.data_dir) - 1] = '\0'; 259 | } else { 260 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d\n", name, 261 | "sys", cfg->sys.cfg_file, lineno); 262 | return 0; 263 | } 264 | return 1; 265 | } 266 | 267 | 268 | static int cfg_parse_section_socket(struct cfg_parse_ctx *ctx, const char *name, 269 | const char *val, int lineno) 270 | { 271 | struct cli_cfg *cfg = ctx->cfg; 272 | if (!strcmp(name, "use_encryption")) { 273 | cfg->sock.use_encryption = atoi(val) ? true : false; 274 | } else if (!strcmp(name, "event_loop")) { 275 | strncpy(cfg->sock.event_loop, val, sizeof(cfg->sock.event_loop)); 276 | cfg->sock.event_loop[sizeof(cfg->sock.event_loop) - 1] = '\0'; 277 | } else if (!strcmp(name, "sock_type")) { 278 | char tmp[8], *p = tmp; 279 | strncpy(tmp, val, sizeof(tmp)); 280 | 281 | tmp[sizeof(tmp) - 1] = '\0'; 282 | while (*p) { 283 | *p = (char)tolower((unsigned char)*p); 284 | p++; 285 | } 286 | 287 | if (!strcmp(tmp, "tcp")) { 288 | cfg->sock.type = SOCK_TCP; 289 | } else if (!strcmp(tmp, "udp")) { 290 | cfg->sock.type = SOCK_UDP; 291 | } else { 292 | pr_err("Invalid socket type \"%s\" at %s:%d", val, 293 | cfg->sys.cfg_file, lineno); 294 | return 0; 295 | } 296 | } else if (!strcmp(name, "server_addr")) { 297 | strncpy(cfg->sock.server_addr, val, sizeof(cfg->sock.server_addr)); 298 | cfg->sock.server_addr[sizeof(cfg->sock.server_addr) - 1] = '\0'; 299 | } else if (!strcmp(name, "server_port")) { 300 | cfg->sock.server_port = (uint16_t)strtoul(val, NULL, 10); 301 | } else { 302 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d\n", name, 303 | "socket", cfg->sys.cfg_file, lineno); 304 | return 0; 305 | } 306 | return 1; 307 | } 308 | 309 | 310 | static int cfg_parse_section_iface(struct cfg_parse_ctx *ctx, const char *name, 311 | const char *val, int lineno) 312 | { 313 | struct cli_cfg *cfg = ctx->cfg; 314 | if (!strcmp(name, "dev")) { 315 | strncpy(cfg->iface.dev, val, sizeof(cfg->iface.dev)); 316 | cfg->iface.dev[sizeof(cfg->iface.dev) - 1] = '\0'; 317 | } else if (!strcmp(name, "override_default")) { 318 | cfg->iface.override_default = atoi(val) ? true : false; 319 | } else { 320 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d\n", name, 321 | "iface", cfg->sys.cfg_file, lineno); 322 | return 0; 323 | } 324 | return 1; 325 | } 326 | 327 | 328 | static int cfg_parse_section_auth(struct cfg_parse_ctx *ctx, const char *name, 329 | const char *val, int lineno) 330 | { 331 | struct cli_cfg *cfg = ctx->cfg; 332 | if (!strcmp(name, "username")) { 333 | strncpy(cfg->auth.username, val, sizeof(cfg->auth.username)); 334 | cfg->auth.username[sizeof(cfg->auth.username) - 1] = '\0'; 335 | } else if (!strcmp(name, "password")) { 336 | strncpy(cfg->auth.password, val, sizeof(cfg->auth.password)); 337 | cfg->auth.password[sizeof(cfg->auth.password) - 1] = '\0'; 338 | } else { 339 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d\n", name, 340 | "auth", cfg->sys.cfg_file, lineno); 341 | return 0; 342 | } 343 | return 1; 344 | } 345 | 346 | 347 | /* 348 | * If success, returns 1. 349 | * If failure, returns 0. 350 | */ 351 | static __cold int client_cfg_parser(void *user, const char *section, 352 | const char *name, const char *val, 353 | int lineno) 354 | { 355 | struct cfg_parse_ctx *ctx = (struct cfg_parse_ctx *)user; 356 | struct cli_cfg *cfg = ctx->cfg; 357 | 358 | if (!strcmp(section, "sys")) { 359 | return cfg_parse_section_sys(ctx, name, val, lineno); 360 | } else if (!strcmp(section, "socket")) { 361 | return cfg_parse_section_socket(ctx, name, val, lineno); 362 | } else if (!strcmp(section, "iface")) { 363 | return cfg_parse_section_iface(ctx, name, val, lineno); 364 | } else if (!strcmp(section, "auth")) { 365 | return cfg_parse_section_auth(ctx, name, val, lineno); 366 | } 367 | 368 | pr_err("Unknown section \"%s\" in at %s:%d", section, cfg->sys.cfg_file, 369 | lineno); 370 | return 0; 371 | } 372 | 373 | 374 | __cold int client_parse_cfg_file(const char *cfg_file, struct cli_cfg *cfg) 375 | { 376 | int ret; 377 | FILE *handle; 378 | struct cfg_parse_ctx ctx; 379 | 380 | ctx.cfg = cfg; 381 | 382 | if (!cfg_file) 383 | return 0; 384 | 385 | handle = fopen(cfg_file, "rb"); 386 | if (!handle) { 387 | ret = errno; 388 | pr_err("Cannot open config file \"%s\": " PRERF, cfg_file, 389 | PREAR(ret)); 390 | return -ret; 391 | } 392 | 393 | ret = ini_parse_file(handle, client_cfg_parser, &ctx); 394 | if (ret) { 395 | pr_err("Failed to parse config file \"%s\"", cfg_file); 396 | ret = -EINVAL; 397 | } 398 | 399 | fclose(handle); 400 | return ret; 401 | } 402 | 403 | 404 | /* 405 | * This function should return a positive integer or zero. 406 | * 407 | * The parser returns -errno value, hence we negate the return 408 | * value inside this function. 409 | */ 410 | __cold int run_client(int argc, char *argv[]) 411 | { 412 | int ret; 413 | struct cli_cfg cfg; 414 | memset(&cfg, 0, sizeof(cfg)); 415 | 416 | set_default_config(&cfg); 417 | 418 | pr_debug("Parsing argv..."); 419 | ret = parse_argv(argc, argv, &cfg); 420 | if (ret) 421 | return -ret; 422 | 423 | ret = client_parse_cfg_file(cfg.sys.cfg_file, &cfg); 424 | if (ret) { 425 | if (!(ret == -ENOENT && !strcmp(cfg.sys.cfg_file, d_cli_cfg_file))) 426 | return -ret; 427 | } 428 | 429 | dump_client_cfg(&cfg); 430 | 431 | if (!*cfg.auth.username) { 432 | pr_err("Username cannot be empty"); 433 | return EINVAL; 434 | } 435 | 436 | if (!*cfg.auth.password) { 437 | pr_err("Password cannot be empty"); 438 | return EINVAL; 439 | } 440 | 441 | switch (cfg.sock.type) { 442 | case SOCK_UDP: 443 | return -teavpn2_client_udp_run(&cfg); 444 | case SOCK_TCP: 445 | default: 446 | return ESOCKTNOSUPPORT; 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /src/teavpn2/net/linux/iface.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Network interface functions for TeaVPN2 (Linux) 4 | * 5 | * Copyright (C) 2021 Ammar Faizi 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | 25 | static __always_inline int is_ws(int the_chr) 26 | { 27 | return isspace((unsigned char)the_chr); 28 | } 29 | 30 | 31 | static noinline char *strtriml_move(char *str, size_t len) 32 | { 33 | size_t trimmed_len; 34 | char *orig = str, *end; 35 | 36 | if (unlikely(len == 0)) 37 | return orig; 38 | 39 | 40 | /* 41 | * We assume that `str + len` is the location of the NUL char 42 | */ 43 | end = str + len - 1; 44 | 45 | 46 | while (is_ws(*str)) { 47 | 48 | if (str == &end[1]) { 49 | *orig = '\0'; 50 | return orig; 51 | } 52 | 53 | str++; 54 | } 55 | 56 | 57 | if (*str == '\0' && str == &end[1]) { 58 | /* 59 | * All spaces C string, or empty C string will go here. 60 | */ 61 | *orig = '\0'; 62 | return orig; 63 | } 64 | 65 | 66 | while (is_ws(*end)) 67 | end--; 68 | 69 | 70 | trimmed_len = (size_t)(end - str) + 1u; 71 | if (orig != str) 72 | memmove(orig, str, trimmed_len); 73 | 74 | orig[trimmed_len] = '\0'; 75 | return orig; 76 | } 77 | 78 | 79 | 80 | static noinline char *strtrim_move(char *str) 81 | { 82 | /* TODO: Don't waste time just for strlen */ 83 | return strtriml_move(str, strlen(str)); 84 | } 85 | 86 | 87 | static char *sane_strncpy(char *__restrict__ dst, char *__restrict__ src, 88 | size_t len) 89 | { 90 | strncpy(dst, src, len); 91 | dst[len - 1] = '\0'; 92 | return dst; 93 | } 94 | 95 | 96 | /* 97 | * 98 | * Thanks to PHP 99 | * https://github.com/php/php-src/blob/e9d78339e7ff2edb8a1eda93d047ccaac25efa24/ext/standard/exec.c#L388-L468 100 | * 101 | */ 102 | __cold static noinline char *escapeshellarg(char *alloc, const char *str, 103 | size_t len, size_t *res_len) 104 | { 105 | size_t y = 0; 106 | size_t l = (len > 0) ? len : strlen(str); 107 | size_t x; 108 | char *cmd; 109 | 110 | if (alloc == NULL) { 111 | /* Worst case */ 112 | cmd = (char *)malloc((sizeof(char) * l * 4) + 1); 113 | if (!cmd) 114 | return NULL; 115 | } else { 116 | cmd = alloc; 117 | } 118 | 119 | #ifdef WIN32 120 | cmd[y++] = '"'; 121 | #else 122 | cmd[y++] = '\''; 123 | #endif 124 | 125 | for (x = 0; x < l; x++) { 126 | switch (str[x]) { 127 | #ifdef WIN32 128 | case '"': 129 | case '%': 130 | case '!': 131 | cmd[y++] = ' '; 132 | break; 133 | #else 134 | case '\'': 135 | cmd[y++] = '\''; 136 | cmd[y++] = '\\'; 137 | cmd[y++] = '\''; 138 | #endif 139 | fallthrough; 140 | default: 141 | cmd[y++] = str[x]; 142 | } 143 | } 144 | 145 | #ifdef WIN32 146 | if (y > 0 && '\\' == cmd[y - 1]) { 147 | int k = 0, n = y - 1; 148 | for (; n >= 0 && '\\' == cmd[n]; n--, k++); 149 | if (k % 2) { 150 | cmd[y++] = '\\'; 151 | } 152 | } 153 | cmd[y++] = '"'; 154 | #else 155 | cmd[y++] = '\''; 156 | #endif 157 | 158 | cmd[y] = '\0'; 159 | 160 | if (res_len != NULL) 161 | *res_len = y; 162 | 163 | return cmd; 164 | } 165 | 166 | 167 | 168 | /* https://www.kernel.org/doc/Documentation/networking/tuntap.txt 169 | * 170 | * Flags: IFF_TUN - TUN device (no Ethernet headers) 171 | * IFF_TAP - TAP device 172 | * 173 | * IFF_NO_PI - Do not provide packet information 174 | * IFF_MULTI_QUEUE - Create a queue of multiqueue device 175 | */ 176 | __cold int tun_alloc(const char *dev, short flags) 177 | { 178 | int fd; 179 | int err; 180 | struct ifreq ifr; 181 | bool retried = false; 182 | static const char *dtf = "/dev/net/tun"; 183 | 184 | if (unlikely((dev == NULL) || (*dev == '\0'))) { 185 | pr_error("tun_alloc(): dev cannot be empty"); 186 | return -EINVAL; 187 | } 188 | 189 | again: 190 | fd = open(dtf, O_RDWR); 191 | if (unlikely(fd < 0)) { 192 | err = errno; 193 | pr_err("open(\"%s\", O_RDWR): " PRERF, dtf, PREAR(err)); 194 | 195 | if ((!retried) && (err == ENOENT)) { 196 | /* 197 | * On android, it is located at /dev/tun 198 | */ 199 | dtf = "/dev/tun"; 200 | retried = !retried; 201 | prl_notice(0, "Set fallback to %s", dtf); 202 | goto again; 203 | } 204 | 205 | return -err; 206 | } 207 | 208 | memset(&ifr, 0, sizeof(ifr)); 209 | strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1); 210 | strtrim_move(ifr.ifr_name); 211 | ifr.ifr_flags = flags; 212 | 213 | if (unlikely(ioctl(fd, TUNSETIFF, &ifr) < 0)) { 214 | close(fd); 215 | err = errno; 216 | pr_err("ioctl(%d, TUNSETIFF, &ifr): " PRERF, fd, PREAR(err)); 217 | return -err; 218 | } 219 | 220 | return fd; 221 | } 222 | 223 | 224 | __cold int fd_set_nonblock(int fd) 225 | { 226 | int err; 227 | int flags; 228 | 229 | /* 230 | * Fixme: 231 | * O_NONBLOCK is defined but broken on SunOS 4.1.x and AIX 3.2.5. 232 | */ 233 | flags = fcntl(fd, F_GETFL, 0); 234 | if (unlikely(flags < 0)) { 235 | err = errno; 236 | pr_err("fcntl(%d, F_GETFL, 0): " PRERF, fd, PREAR(err)); 237 | return -err; 238 | } 239 | 240 | flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 241 | if (unlikely(flags < 0)) { 242 | err = errno; 243 | pr_err("fcntl(%d, F_SETFL, %d): " PRERF, fd, flags, PREAR(err)); 244 | return -err; 245 | } 246 | 247 | return flags; 248 | } 249 | 250 | 251 | 252 | 253 | static __cold char *shell_exec(const char *cmd, char *buf, size_t buflen, 254 | size_t *outlen) 255 | { 256 | int err; 257 | FILE *handle; 258 | bool use_malloc; 259 | size_t read_len; 260 | 261 | use_malloc = (buf == NULL); 262 | 263 | if (unlikely(use_malloc)) { 264 | buf = malloc(buflen); 265 | if (unlikely(buf == NULL)) { 266 | err = errno; 267 | pr_err("malloc(): " PRERF, PREAR(err)); 268 | goto out_err; 269 | } 270 | } 271 | 272 | handle = popen(cmd, "r"); 273 | if (unlikely(handle == NULL)) { 274 | err = errno; 275 | pr_err("popen(\"%s\", \"r\"): " PRERF, cmd, PREAR(err)); 276 | goto out_err; 277 | } 278 | 279 | memset(buf, 0, buflen); 280 | read_len = fread(buf, sizeof(char), buflen, handle); 281 | pclose(handle); 282 | 283 | if (likely(outlen)) 284 | *outlen = read_len; 285 | 286 | return buf; 287 | out_err: 288 | if (unlikely(use_malloc)) 289 | free(buf); 290 | return NULL; 291 | } 292 | 293 | 294 | 295 | #define IPV4_EL (IPV4_L * 2) 296 | #define IPV4_LI (IPV4_L + 7) 297 | #define IPV4_ELI (IPV4_EL + 1) 298 | 299 | #define EXEC_CMD(OUT, BUF, IP, CMD, ...) \ 300 | do { \ 301 | int p = 0; \ 302 | char __cbuf[sizeof((BUF)) * 3]; \ 303 | snprintf((BUF), sizeof((BUF)), (CMD), __VA_ARGS__); \ 304 | p = snprintf((__cbuf), sizeof(__cbuf), "%s %s", IP, ((BUF))); \ 305 | pr_notice("Executing: %s", (__cbuf)); \ 306 | if (suppress_err) \ 307 | p += snprintf((__cbuf) + p, \ 308 | sizeof(__cbuf) - (unsigned)p, \ 309 | " >> /dev/null 2>&1"); \ 310 | *(OUT) = system((__cbuf)); \ 311 | } while (0) 312 | 313 | 314 | static __cold __always_inline char *simple_esc_arg(char *buf, const char *str) 315 | { 316 | return escapeshellarg(buf, str, strlen(str), NULL); 317 | } 318 | 319 | 320 | static __cold __always_inline const char *find_ip_cmd(void) 321 | { 322 | int ret; 323 | static const char * const ip_bin[] = { 324 | "/bin/ip", 325 | "/sbin/ip", 326 | "/usr/bin/ip", 327 | "/usr/sbin/ip", 328 | "/usr/local/bin/ip", 329 | "/usr/local/sbin/ip", 330 | "/data/data/com.termux/files/usr/bin/ip" 331 | }; 332 | 333 | for (size_t i = 0; i < (sizeof(ip_bin) / sizeof(*ip_bin)); i++) { 334 | errno = 0; 335 | ret = access(ip_bin[i], R_OK | X_OK); 336 | prl_notice(0, "Locating %s: %s", ip_bin[i], strerror(errno)); 337 | if (ret == 0) 338 | return ip_bin[i]; 339 | } 340 | 341 | pr_err("Cannot find ip bin executable file"); 342 | return NULL; 343 | } 344 | 345 | static noinline bool teavpn_iface_toggle(struct if_info *iface, bool up, 346 | bool suppress_err); 347 | 348 | 349 | __cold bool teavpn_iface_up(struct if_info *iface) 350 | { 351 | return teavpn_iface_toggle(iface, true, false); 352 | } 353 | 354 | 355 | __cold bool teavpn_iface_down(struct if_info *iface) 356 | { 357 | return teavpn_iface_toggle(iface, false, true); 358 | } 359 | 360 | 361 | static __cold noinline bool teavpn_iface_toggle(struct if_info *iface, bool up, 362 | bool suppress_err) 363 | { 364 | #ifdef TEAVPN_IPV6_SUPPORT 365 | static_assert(0, "Fixme: Handle IPv6 assignment."); 366 | #endif 367 | int err; 368 | int ret; 369 | 370 | /* User data */ 371 | char uipv4[IPV4_LI]; 372 | char uipv4_nm[IPV4_LI]; /* Netmask */ 373 | char uipv4_nw[IPV4_LI]; /* Network */ 374 | char uipv4_bc[IPV4_LI]; /* Broadcast */ 375 | 376 | /* Escaped data */ 377 | char edev[sizeof(iface->dev) * 2]; 378 | char eipv4[IPV4_ELI]; 379 | char eipv4_nw[IPV4_ELI]; /* Network */ 380 | char eipv4_bc[IPV4_ELI]; /* Broadcast */ 381 | 382 | /* 32-bit big-endian data */ 383 | uint32_t tmp; 384 | uint32_t bipv4; 385 | uint32_t bipv4_nm; /* Netmask */ 386 | uint32_t bipv4_nw; /* Network */ 387 | uint32_t bipv4_bc; /* Broadcast */ 388 | 389 | uint8_t cidr; 390 | char cbuf[256]; 391 | const char *ip; 392 | size_t tmp_len; 393 | 394 | sane_strncpy(uipv4, iface->ipv4, sizeof(uipv4)); 395 | sane_strncpy(uipv4_nm, iface->ipv4_netmask, sizeof(uipv4_nm)); 396 | 397 | /* Convert netmask from chars to 32-bit big-endian integer */ 398 | if (unlikely(!inet_pton(AF_INET, uipv4_nm, &bipv4_nm))) { 399 | err = errno; 400 | err = err ? err : EINVAL; 401 | pr_err("inet_pton(%s): uipv4_nm: " PRERF, uipv4_nm, PREAR(err)); 402 | return false; 403 | } 404 | 405 | /* Convert netmask from 32-bit big-endian integer to CIDR number */ 406 | cidr = 0; 407 | tmp = bipv4_nm; 408 | while (tmp) { 409 | cidr++; 410 | tmp >>= 1; 411 | } 412 | 413 | if (unlikely(cidr > 32)) { 414 | pr_err("Invalid CIDR: %d from \"%s\"", cidr, uipv4_nm); 415 | return false; 416 | } 417 | 418 | /* Convert IPv4 from chars to big endian integer */ 419 | if (unlikely(!inet_pton(AF_INET, uipv4, &bipv4))) { 420 | err = errno; 421 | err = err ? err : EINVAL; 422 | pr_error("inet_pton(%s): uipv4: " PRERF, uipv4, PREAR(err)); 423 | return false; 424 | } 425 | 426 | /* Add CIDR to IPv4 */ 427 | tmp_len = strnlen(uipv4, sizeof(uipv4) - 1); 428 | snprintf(uipv4 + tmp_len, sizeof(uipv4) - tmp_len, "/%hhu", cidr); 429 | 430 | /* 431 | * Bitwise AND between IP address and netmask 432 | * will result in network address. 433 | */ 434 | bipv4_nw = bipv4 & bipv4_nm; 435 | 436 | /* 437 | * A bitwise OR between network address and inverted 438 | * netmask will give the broadcast address. 439 | */ 440 | bipv4_bc = bipv4_nw | (~bipv4_nm); 441 | 442 | /* Convert network address from 32-bit big-endian integer to chars */ 443 | if (unlikely(!inet_ntop(AF_INET, &bipv4_nw, uipv4_nw, IPV4_L))) { 444 | err = errno; 445 | err = err ? err : EINVAL; 446 | pr_error("inet_ntop(%" PRIx32 "): bipv4_nw: " PRERF, bipv4_nw, 447 | PREAR(err)); 448 | return false; 449 | } 450 | 451 | /* Add CIDR to network address */ 452 | tmp_len = strnlen(uipv4_nw, sizeof(uipv4_nw) - 1); 453 | snprintf(uipv4_nw + tmp_len, sizeof(uipv4_nw) - tmp_len, "/%hhu", cidr); 454 | 455 | /* Convert broadcast address from 32-bit big-endian integer to chars */ 456 | if (!inet_ntop(AF_INET, &bipv4_bc, uipv4_bc, IPV4_L)) { 457 | err = errno; 458 | err = err ? err : EINVAL; 459 | pr_error("inet_ntop(%" PRIx32 "): bipv4_bc: " PRERF, bipv4_bc, 460 | PREAR(err)); 461 | return false; 462 | } 463 | 464 | simple_esc_arg(eipv4, uipv4); 465 | simple_esc_arg(eipv4_nw, uipv4_nw); 466 | simple_esc_arg(eipv4_bc, uipv4_bc); 467 | simple_esc_arg(edev, iface->dev); 468 | 469 | ip = find_ip_cmd(); 470 | if (ip == NULL) 471 | return false; 472 | 473 | EXEC_CMD(&ret, cbuf, ip, "link set dev %s %s mtu %d", edev, 474 | (up ? "up" : "down"), iface->ipv4_mtu); 475 | 476 | if (unlikely(ret != 0)) 477 | return false; 478 | 479 | EXEC_CMD(&ret, cbuf, ip, "addr %s dev %s %s broadcast %s", 480 | (up ? "add" : "delete"), edev, eipv4, eipv4_bc); 481 | 482 | if (unlikely(ret != 0)) 483 | return false; 484 | 485 | if (likely(*iface->ipv4_pub != '\0')) { 486 | char *tmpc; 487 | char *rdgw; 488 | char *ipv4_pub = iface->ipv4_pub; 489 | char *erdgw = eipv4; /* Reuse buffer */ 490 | char *eipv4_pub = eipv4_nw; /* Reuse buffer */ 491 | char tmpbuf[128]; 492 | 493 | snprintf(tmpbuf, sizeof(tmpbuf), "%s route show", ip); 494 | 495 | /* Get real default gateway */ 496 | shell_exec(tmpbuf, cbuf, sizeof(cbuf) - 1, NULL); 497 | 498 | rdgw = cbuf; 499 | rdgw[sizeof(cbuf) - 1] = '\0'; 500 | rdgw = strstr(rdgw, "default via "); 501 | 502 | if (unlikely(rdgw == NULL)) { 503 | pr_err("Can't find default gateway from command: %s " 504 | "route show", ip); 505 | return false; 506 | } 507 | 508 | rdgw += sizeof("default via ") - 1; 509 | 510 | /* Just cut */ 511 | tmpc = rdgw; 512 | while ((*tmpc != ' ') && (*tmpc != '\0') && (*tmpc != '\n')) 513 | tmpc++; 514 | *tmpc = '\0'; 515 | 516 | simple_esc_arg(erdgw, rdgw); 517 | simple_esc_arg(eipv4_pub, ipv4_pub); 518 | 519 | if (up) { 520 | int tmp_ret; 521 | char __tmp[128]; 522 | snprintf(__tmp, sizeof(__tmp), 523 | "%s route delete %s/32 via %s >> /dev/null 2>&1", 524 | ip, eipv4_pub, erdgw); 525 | tmp_ret = system(__tmp); 526 | (void)tmp_ret; 527 | } 528 | 529 | 530 | /* We have the real default gateway in rdgw */ 531 | EXEC_CMD(&ret, cbuf, ip, "route %s %s/32 via %s", 532 | (up ? "add" : "delete"), eipv4_pub, erdgw); 533 | 534 | if (unlikely(ret != 0)) 535 | return false; 536 | 537 | 538 | if (likely(*iface->ipv4_dgateway != '\0')) { 539 | char *edgw = eipv4; /* Reuse buffer */ 540 | 541 | simple_esc_arg(edgw, iface->ipv4_dgateway); 542 | 543 | EXEC_CMD(&ret, cbuf, ip, "route %s 0.0.0.0/1 via %s", 544 | (up ? "add" : "delete"), edgw); 545 | 546 | if (unlikely(ret != 0)) 547 | return false; 548 | 549 | EXEC_CMD(&ret, cbuf, ip, "route %s 128.0.0.0/1 via %s", 550 | (up ? "add" : "delete"), edgw); 551 | 552 | if (unlikely(ret != 0)) 553 | return false; 554 | } 555 | } 556 | 557 | #undef IP 558 | 559 | return true; 560 | } 561 | -------------------------------------------------------------------------------- /src/teavpn2/server/entry.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Copyright (C) 2021 Ammar Faizi 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct cfg_parse_ctx { 12 | struct srv_cfg *cfg; 13 | }; 14 | 15 | 16 | /* 17 | * Default config. 18 | */ 19 | static const int d_srv_backlog = 10; 20 | static const uint16_t d_srv_bind_port = 44444; 21 | static const uint16_t d_srv_mtu = 1450; 22 | static const char d_srv_dev[] = "tsrv0"; 23 | static const char d_srv_ipv4[] = "10.5.5.1"; 24 | static const char d_srv_ipv4_netmask[] = "255.255.255.0"; 25 | static const char d_srv_cfg_file[] = "/etc/teavpn2/server.ini"; 26 | static const uint8_t d_num_of_threads = 2; 27 | static const uint16_t d_srv_max_conn = 32; 28 | 29 | 30 | static __cold void set_default_config(struct srv_cfg *cfg) 31 | { 32 | struct srv_cfg_sys *sys = &cfg->sys; 33 | struct srv_cfg_sock *sock = &cfg->sock; 34 | struct srv_cfg_iface *iface = &cfg->iface; 35 | 36 | sys->cfg_file = d_srv_cfg_file; 37 | sys->thread_num = d_num_of_threads; 38 | 39 | iface->iff.ipv4_mtu = d_srv_mtu; 40 | strncpy2(iface->dev, d_srv_dev, sizeof(iface->dev)); 41 | strncpy2(iface->iff.dev, d_srv_dev, sizeof(iface->iff.dev)); 42 | strncpy2(iface->iff.ipv4, d_srv_ipv4, sizeof(iface->iff.ipv4)); 43 | strncpy2(iface->iff.ipv4_netmask, d_srv_ipv4_netmask, 44 | sizeof(iface->iff.ipv4_netmask)); 45 | 46 | 47 | strncpy2(sock->bind_addr, "0.0.0.0", sizeof(cfg->sock.bind_addr)); 48 | sock->bind_port = d_srv_bind_port; 49 | sock->backlog = d_srv_backlog; 50 | sock->max_conn = d_srv_max_conn; 51 | } 52 | 53 | 54 | static __cold void teavpn_server_show_help(const char *app) 55 | { 56 | printf("Usage: %s server [options]\n", app); 57 | 58 | printf("\n"); 59 | printf("TeaVPN Server Application\n"); 60 | printf("\n"); 61 | printf("Available options:\n"); 62 | printf(" -h, --help\t\t\tShow this help message.\n"); 63 | printf(" -V, --version\t\t\tShow application version.\n"); 64 | printf(" -c, --config=FILE\t\tSet config file (default: %s).\n", 65 | d_srv_cfg_file); 66 | printf(" -d, --data-dir=DIR\t\tSet data directory.\n"); 67 | printf(" -t, --thread=N\t\tSet number of threads (default: %hhu).\n", 68 | d_num_of_threads); 69 | 70 | printf("\n"); 71 | printf("[Config options]\n"); 72 | printf(" Virtual network interface:\n"); 73 | printf(" -D, --dev=DEV\t\t\tSet virtual network interface name" 74 | " (default: %s).\n", d_srv_dev); 75 | printf(" -m, --mtu=MTU\t\t\tSet mtu value (default: %d).\n", 76 | d_srv_mtu); 77 | printf(" -4, --ipv4=IP\t\t\tSet IPv4 (default: %s).\n", d_srv_ipv4); 78 | printf(" -N, --ipv4-netmask=MASK\tSet IPv4 netmask (default: %s).\n", 79 | d_srv_ipv4_netmask); 80 | #ifdef TEAVPN_IPV6_SUPPORT 81 | printf(" -6, --ipv6=IP\t\t\tSet IPv6 (default: %s).\n", "???"); 82 | printf(" -M, --ipv6-netmask=MASK\tSet IPv6 netmask (default: %s).\n", 83 | "???"); 84 | #endif 85 | 86 | 87 | printf("\n"); 88 | printf(" Socket:\n"); 89 | printf(" -s, --sock-type=TYPE\t\tSet socket type (must be tcp or udp)" 90 | " (default: tcp).\n"); 91 | printf(" -H, --bind-addr=IP\t\tSet bind address (default 0.0.0.0).\n"); 92 | printf(" -P, --bind-port=PORT\t\tSet bind port (default: %d).\n", 93 | d_srv_bind_port); 94 | printf(" -k, --max-conn=N\t\tSet max connections (default: %d).\n", 95 | d_srv_max_conn); 96 | printf(" -B, --backlog=N\t\tSet socket listen backlog (default: %d)" 97 | ".\n", d_srv_backlog); 98 | printf(" -C, --ssl-cert=FILE\t\tSet SSL certificate.\n"); 99 | printf(" -K, --ssl-priv-key=FILE\tSet SSL private key.\n"); 100 | 101 | printf("\n"); 102 | printf("\n"); 103 | printf("For bug reporting, please open an issue on the GitHub repository." 104 | "\n"); 105 | printf("GitHub repository: https://github.com/TeaInside/teavpn2\n"); 106 | printf("\n"); 107 | printf("This software is licensed under GNU GPL-v2 license.\n"); 108 | } 109 | 110 | 111 | #define PR_CFG(C, FMT) printf(" " #C " = " FMT "\n", C) 112 | 113 | 114 | static __maybe_unused void dump_server_cfg(struct srv_cfg *cfg) 115 | { 116 | puts("============================================="); 117 | puts(" Config dump "); 118 | puts("============================================="); 119 | PR_CFG(cfg->sys.cfg_file, "%s"); 120 | PR_CFG(cfg->sys.data_dir, "%s"); 121 | PR_CFG(cfg->sys.thread_num, "%hhu"); 122 | PR_CFG(cfg->sys.verbose_level, "%hhu"); 123 | putchar('\n'); 124 | printf(" cfg->sock.use_encryption = %hhu\n", 125 | (uint8_t)cfg->sock.use_encryption); 126 | printf(" cfg->sock.type = %s\n", 127 | (cfg->sock.type == SOCK_TCP) ? "SOCK_TCP" : 128 | ((cfg->sock.type == SOCK_UDP) ? "SOCK_UDP" : "unknown")); 129 | PR_CFG(cfg->sock.bind_addr, "%s"); 130 | PR_CFG(cfg->sock.bind_port, "%hu"); 131 | PR_CFG(cfg->sock.event_loop, "%s"); 132 | PR_CFG(cfg->sock.max_conn, "%hu"); 133 | PR_CFG(cfg->sock.ssl_cert, "%s"); 134 | PR_CFG(cfg->sock.ssl_priv_key, "%s"); 135 | putchar('\n'); 136 | PR_CFG(cfg->iface.dev, "%s"); 137 | PR_CFG(cfg->iface.mtu, "%hu"); 138 | PR_CFG(cfg->iface.iff.ipv4, "%s"); 139 | PR_CFG(cfg->iface.iff.ipv4_netmask, "%s"); 140 | puts("============================================="); 141 | } 142 | 143 | /* TODO: Write my own getopt function. */ 144 | 145 | static const struct option long_options[] = { 146 | /* Help, version and verbose. */ 147 | {"help", no_argument, 0, 'h'}, 148 | {"version", no_argument, 0, 'V'}, 149 | {"verbose", optional_argument, 0, 'v'}, 150 | 151 | /* Sys. */ 152 | {"config", required_argument, 0, 'c'}, 153 | {"data-dir", required_argument, 0, 'd'}, 154 | {"thread", required_argument, 0, 't'}, 155 | 156 | /* Net. */ 157 | {"dev", required_argument, 0, 'D'}, 158 | {"mtu", required_argument, 0, 'm'}, 159 | {"ipv4", required_argument, 0, '4'}, 160 | {"ipv4-netmask", required_argument, 0, 'N'}, 161 | 162 | /* Socket. */ 163 | {"sock-type", required_argument, 0, 's'}, 164 | {"bind-addr", required_argument, 0, 'H'}, 165 | {"bind-port", required_argument, 0, 'P'}, 166 | {"backlog", required_argument, 0, 'B'}, 167 | {"encrypt", no_argument, 0, 'E'}, 168 | 169 | 170 | {0, 0, 0, 0} 171 | }; 172 | static const char short_opt[] = "hVv::c:d:t:D:m:4:N:s:H:P:B:E:"; 173 | 174 | static __cold int parse_argv(int argc, char *argv[], struct srv_cfg *cfg) 175 | { 176 | int c; 177 | struct srv_cfg_sys *sys = &cfg->sys; 178 | struct srv_cfg_sock *sock = &cfg->sock; 179 | struct srv_cfg_iface *iface = &cfg->iface; 180 | 181 | while (1) { 182 | int opt_idx = 0; 183 | 184 | c = getopt_long(argc, argv, short_opt, long_options, &opt_idx); 185 | if (c == -1) 186 | break; 187 | 188 | switch (c) { 189 | /* Help, version and verbose. */ 190 | case 'h': 191 | teavpn_server_show_help(argv[0]); 192 | goto exit_zero; 193 | case 'V': 194 | show_version(); 195 | goto exit_zero; 196 | case 'v': { 197 | uint8_t level = optarg ? (uint8_t)atoi(optarg) : 6u; 198 | set_notice_level(level); 199 | sys->verbose_level = level; 200 | break; 201 | } 202 | 203 | /* Sys */ 204 | case 'c': 205 | sys->cfg_file = optarg; 206 | break; 207 | case 'd': 208 | strncpy2(sys->data_dir, optarg, sizeof(sys->data_dir)); 209 | break; 210 | case 't': { 211 | int tmp = atoi(optarg); 212 | if (tmp <= 0) { 213 | pr_err("Thread num argument must be greater than 0"); 214 | return -EINVAL; 215 | } 216 | sys->thread_num = (uint8_t)tmp; 217 | break; 218 | } 219 | 220 | 221 | /* Net. */ 222 | case 'D': 223 | strncpy2(iface->dev, optarg, sizeof(iface->dev)); 224 | break; 225 | case 'm': { 226 | int tmp = atoi(optarg); 227 | if (tmp <= 0) { 228 | pr_err("MTU must be greater than 0"); 229 | return -EINVAL; 230 | } 231 | iface->mtu = (uint16_t)tmp; 232 | break; 233 | } 234 | case '4': 235 | strncpy2(iface->iff.ipv4, optarg, sizeof(iface->iff.ipv4)); 236 | break; 237 | case 'N': 238 | strncpy2(iface->iff.ipv4_netmask, optarg, 239 | sizeof(iface->iff.ipv4_netmask)); 240 | break; 241 | 242 | /* Socket. */ 243 | case 's': { 244 | char tmp[5], *p = tmp; 245 | 246 | strncpy(tmp, optarg, sizeof(tmp)); 247 | tmp[sizeof(tmp) - 1] = '\0'; 248 | 249 | while (*p) { 250 | *p = (char)tolower((unsigned char)*p); 251 | p++; 252 | } 253 | 254 | if (!strcmp(tmp, "tcp")) { 255 | sock->type = SOCK_TCP; 256 | } else if (!strcmp(tmp, "udp")) { 257 | sock->type = SOCK_UDP; 258 | } else { 259 | pr_err("Invalid socket type: %s", optarg); 260 | return -EINVAL; 261 | } 262 | break; 263 | } 264 | case 'H': 265 | strncpy(sock->bind_addr, optarg, sizeof(sock->bind_addr)); 266 | sock->bind_addr[sizeof(sock->bind_addr) - 1] = '\0'; 267 | break; 268 | case 'P': 269 | sock->bind_port = (uint16_t)atoi(optarg); 270 | break; 271 | case 'E': 272 | sock->use_encryption = atoi(optarg) ? true : false; 273 | break; 274 | } 275 | } 276 | 277 | return 0; 278 | 279 | exit_zero: 280 | exit(0); 281 | __builtin_unreachable(); 282 | } 283 | 284 | 285 | static int cfg_parse_section_sys(struct cfg_parse_ctx *ctx, const char *name, 286 | const char *val, int lineno) 287 | { 288 | struct srv_cfg *cfg = ctx->cfg; 289 | if (!strcmp(name, "thread")) { 290 | cfg->sys.thread_num = (uint8_t)strtoul(val, NULL, 10); 291 | } else if (!strcmp(name, "verbose_level")) { 292 | uint8_t level = (uint8_t)strtoul(val, NULL, 10); 293 | set_notice_level(level); 294 | cfg->sys.verbose_level = level; 295 | } else if (!strcmp(name, "data_dir")) { 296 | strncpy2(cfg->sys.data_dir, val, sizeof(cfg->sys.data_dir)); 297 | } else { 298 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d", name, 299 | "sys", cfg->sys.cfg_file, lineno); 300 | return 0; 301 | } 302 | return 1; 303 | } 304 | 305 | 306 | static int cfg_parse_section_socket(struct cfg_parse_ctx *ctx, const char *name, 307 | const char *val, int lineno) 308 | { 309 | struct srv_cfg *cfg = ctx->cfg; 310 | if (!strcmp(name, "use_encryption")) { 311 | cfg->sock.use_encryption = atoi(val) ? true : false; 312 | } else if (!strcmp(name, "event_loop")) { 313 | strncpy2(cfg->sock.event_loop, val, sizeof(cfg->sock.event_loop)); 314 | } else if (!strcmp(name, "sock_type")) { 315 | char tmp[8], *p = tmp; 316 | strncpy2(tmp, val, sizeof(tmp)); 317 | while (*p) { 318 | *p = (char)tolower((unsigned char)*p); 319 | p++; 320 | } 321 | 322 | if (!strcmp(tmp, "tcp")) { 323 | cfg->sock.type = SOCK_TCP; 324 | } else if (!strcmp(tmp, "udp")) { 325 | cfg->sock.type = SOCK_UDP; 326 | } else { 327 | pr_err("Invalid socket type \"%s\" at %s:%d", val, 328 | cfg->sys.cfg_file, lineno); 329 | return 0; 330 | } 331 | } else if (!strcmp(name, "bind_addr")) { 332 | strncpy2(cfg->sock.bind_addr, val, sizeof(cfg->sock.bind_addr)); 333 | } else if (!strcmp(name, "bind_port")) { 334 | cfg->sock.bind_port = (uint16_t)strtoul(val, NULL, 10); 335 | } else if (!strcmp(name, "backlog")) { 336 | cfg->sock.backlog = atoi(val); 337 | } else if (!strcmp(name, "max_conn")) { 338 | cfg->sock.max_conn = (uint16_t)strtoul(val, NULL, 10); 339 | } else if (!strcmp(name, "ssl_cert")) { 340 | strncpy2(cfg->sock.ssl_cert, val, sizeof(cfg->sock.ssl_cert)); 341 | } else if (!strcmp(name, "ssl_priv_key")) { 342 | strncpy2(cfg->sock.ssl_priv_key, val, sizeof(cfg->sock.ssl_priv_key)); 343 | } else { 344 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d", name, 345 | "socket", cfg->sys.cfg_file, lineno); 346 | return 0; 347 | } 348 | return 1; 349 | } 350 | 351 | 352 | static int cfg_parse_section_iface(struct cfg_parse_ctx *ctx, const char *name, 353 | const char *val, int lineno) 354 | { 355 | struct srv_cfg *cfg = ctx->cfg; 356 | if (!strcmp(name, "dev")) { 357 | strncpy2(cfg->iface.dev, val, sizeof(cfg->iface.dev)); 358 | cfg->iface.dev[sizeof(cfg->iface.dev) - 1] = '\0'; 359 | strncpy2(cfg->iface.iff.dev, val, sizeof(cfg->iface.iff.dev)); 360 | cfg->iface.iff.dev[sizeof(cfg->iface.iff.dev) - 1] = '\0'; 361 | } else if (!strcmp(name, "mtu")) { 362 | cfg->iface.mtu = (uint16_t)strtoul(val, NULL, 10); 363 | cfg->iface.iff.ipv4_mtu = cfg->iface.mtu; 364 | } else if (!strcmp(name, "ipv4")) { 365 | strncpy2(cfg->iface.iff.ipv4, val, sizeof(cfg->iface.iff.ipv4)); 366 | cfg->iface.iff.ipv4[sizeof(cfg->iface.iff.ipv4) - 1] = '\0'; 367 | } else if (!strcmp(name, "ipv4_netmask")) { 368 | strncpy2(cfg->iface.iff.ipv4_netmask, val, sizeof(cfg->iface.iff.ipv4_netmask)); 369 | cfg->iface.iff.ipv4_netmask[sizeof(cfg->iface.iff.ipv4_netmask) - 1] = '\0'; 370 | } else { 371 | pr_err("Unknown name \"%s\" in section \"%s\" at %s:%d", name, 372 | "iface", cfg->sys.cfg_file, lineno); 373 | return 0; 374 | } 375 | return 1; 376 | } 377 | 378 | 379 | /* 380 | * If success, returns 1. 381 | * If failure, returns 0. 382 | */ 383 | static int server_cfg_parser(void *user, const char *section, const char *name, 384 | const char *val, int lineno) 385 | { 386 | struct cfg_parse_ctx *ctx = (struct cfg_parse_ctx *)user; 387 | struct srv_cfg *cfg = ctx->cfg; 388 | 389 | if (!strcmp(section, "sys")) { 390 | return cfg_parse_section_sys(ctx, name, val, lineno); 391 | } else if (!strcmp(section, "socket")) { 392 | return cfg_parse_section_socket(ctx, name, val, lineno); 393 | } else if (!strcmp(section, "iface")) { 394 | return cfg_parse_section_iface(ctx, name, val, lineno); 395 | } 396 | 397 | pr_err("Unknown section \"%s\" in at %s:%d", section, cfg->sys.cfg_file, 398 | lineno); 399 | return 0; 400 | } 401 | 402 | 403 | static __cold int parse_cfg_file(const char *cfg_file, struct srv_cfg *cfg) 404 | { 405 | int ret; 406 | FILE *handle; 407 | struct cfg_parse_ctx ctx; 408 | 409 | ctx.cfg = cfg; 410 | 411 | if (!cfg_file) 412 | return 0; 413 | 414 | handle = fopen(cfg_file, "rb"); 415 | if (!handle) { 416 | ret = errno; 417 | pr_err("Cannot open config file \"%s\": " PRERF, cfg_file, 418 | PREAR(ret)); 419 | return -ret; 420 | } 421 | 422 | ret = ini_parse_file(handle, server_cfg_parser, &ctx); 423 | if (ret) { 424 | pr_err("Failed to parse config file \"%s\"", cfg_file); 425 | ret = -EINVAL; 426 | } 427 | 428 | fclose(handle); 429 | return ret; 430 | } 431 | 432 | 433 | __cold int run_server(int argc, char *argv[]) 434 | { 435 | int ret; 436 | struct srv_cfg cfg; 437 | memset(&cfg, 0, sizeof(cfg)); 438 | 439 | set_default_config(&cfg); 440 | ret = parse_argv(argc, argv, &cfg); 441 | if (ret) 442 | return -ret; 443 | 444 | ret = parse_cfg_file(cfg.sys.cfg_file, &cfg); 445 | if (ret) { 446 | if (!(ret == -ENOENT && !strcmp(cfg.sys.cfg_file, d_srv_cfg_file))) 447 | return -ret; 448 | } 449 | 450 | 451 | #ifndef NDEBUG 452 | dump_server_cfg(&cfg); 453 | #endif 454 | 455 | data_dir = cfg.sys.data_dir; 456 | switch (cfg.sock.type) { 457 | case SOCK_UDP: 458 | ret = -teavpn2_server_udp_run(&cfg); 459 | break; 460 | case SOCK_TCP: 461 | default: 462 | ret = ESOCKTNOSUPPORT; 463 | break; 464 | } 465 | data_dir = NULL; 466 | return ret; 467 | } 468 | -------------------------------------------------------------------------------- /src/teavpn2/compiler_attributes.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0-only 2 | /* 3 | * Thanks to the Linux Kernel 4 | * https://github.com/torvalds/linux/blob/a3b397b4fffb799d25658defafd962f0fb3e9fe0/include/linux/compiler_attributes.h 5 | * 6 | */ 7 | #if defined(__clang__) 8 | # pragma clang diagnostic push 9 | # pragma clang diagnostic ignored "-Wreserved-id-macro" 10 | #endif 11 | 12 | #ifndef __LINUX_COMPILER_ATTRIBUTES_H 13 | #ifndef __LINUX_COMPILER_ATTRIBUTES_ 14 | # define __LINUX_COMPILER_ATTRIBUTES_H 15 | #endif 16 | 17 | /* 18 | * The attributes in this file are unconditionally defined and they directly 19 | * map to compiler attribute(s), unless one of the compilers does not support 20 | * the attribute. In that case, __has_attribute is used to check for support 21 | * and the reason is stated in its comment ("Optional: ..."). 22 | * 23 | * Any other "attributes" (i.e. those that depend on a configuration option, 24 | * on a compiler, on an architecture, on plugins, on other attributes...) 25 | * should be defined elsewhere (e.g. compiler_types.h or compiler-*.h). 26 | * The intention is to keep this file as simple as possible, as well as 27 | * compiler- and version-agnostic (e.g. avoiding GCC_VERSION checks). 28 | * 29 | * This file is meant to be sorted (by actual attribute name, 30 | * not by #ifndef identifier 31 | # define identifier). Use the __attribute__((__name__)) syntax 32 | #endif 33 | * (i.e. with underscores) to avoid future collisions with other macros. 34 | * Provide links to the documentation of each supported compiler, if it exists. 35 | */ 36 | 37 | /* 38 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-alias-function-attribute 39 | */ 40 | #ifndef __alias 41 | # define __alias(symbol) __attribute__((__alias__(#symbol))) 42 | #endif 43 | 44 | /* 45 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-aligned-function-attribute 46 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-aligned-type-attribute 47 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-aligned-variable-attribute 48 | */ 49 | #ifndef __aligned 50 | # define __aligned(x) __attribute__((__aligned__(x))) 51 | #endif 52 | #ifndef __aligned_largest 53 | # define __aligned_largest __attribute__((__aligned__)) 54 | #endif 55 | 56 | /* 57 | * Note: users of __always_inline currently do not write "inline" themselves, 58 | * which seems to be required by gcc to apply the attribute according 59 | * to its docs (and also "warning: always_inline function might not be 60 | * inlinable [-Wattributes]" is emitted). 61 | * 62 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-always_005finline-function-attribute 63 | * clang: mentioned 64 | */ 65 | #ifndef __always_inline 66 | # define __always_inline inline __attribute__((__always_inline__)) 67 | #endif 68 | 69 | /* 70 | * The second argument is optional (default 0), so we use a variadic macro 71 | * to make the shorthand. 72 | * 73 | * Beware: Do not apply this to functions which may return 74 | * ERR_PTRs. Also, it is probably unwise to apply it to functions 75 | * returning extra information in the low bits (but in that case the 76 | * compiler should see some alignment anyway, when the return value is 77 | * massaged by 'flags = ptr & 3; ptr &= ~3;'). 78 | * 79 | * Optional: not supported by icc 80 | * 81 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-assume_005faligned-function-attribute 82 | * clang: https://clang.llvm.org/docs/AttributeReference.html#assume-aligned 83 | */ 84 | #if __has_attribute(__assume_aligned__) 85 | # define __assume_aligned(a, ...) __attribute__((__assume_aligned__(a, ## __VA_ARGS__))) 86 | #else 87 | # define __assume_aligned(a, ...) 88 | #endif 89 | 90 | /* 91 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-cold-function-attribute 92 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Label-Attributes.html#index-cold-label-attribute 93 | */ 94 | #ifndef __cold 95 | # define __cold __attribute__((__cold__)) 96 | #endif 97 | 98 | /* 99 | * Note the long name. 100 | * 101 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-const-function-attribute 102 | */ 103 | #ifndef __attribute_const__ 104 | # define __attribute_const__ __attribute__((__const__)) 105 | #endif 106 | 107 | /* 108 | * Optional: only supported since gcc >= 9 109 | * Optional: not supported by clang 110 | * Optional: not supported by icc 111 | * 112 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-copy-function-attribute 113 | */ 114 | #if __has_attribute(__copy__) 115 | # define __copy(symbol) __attribute__((__copy__(symbol))) 116 | #else 117 | # define __copy(symbol) 118 | #endif 119 | 120 | /* 121 | * Don't. Just don't. See commit 771c035372a0 ("deprecate the '__deprecated' 122 | * attribute warnings entirely and for good") for more information. 123 | * 124 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-deprecated-function-attribute 125 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-deprecated-type-attribute 126 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-deprecated-variable-attribute 127 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Enumerator-Attributes.html#index-deprecated-enumerator-attribute 128 | * clang: https://clang.llvm.org/docs/AttributeReference.html#deprecated 129 | */ 130 | #ifndef __deprecate 131 | # define __deprecated 132 | #endif 133 | 134 | /* 135 | * Optional: only supported since gcc >= 5.1 136 | * Optional: not supported by clang 137 | * Optional: not supported by icc 138 | * 139 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-designated_005finit-type-attribute 140 | */ 141 | #if __has_attribute(__designated_init__) 142 | # define __designated_init __attribute__((__designated_init__)) 143 | #else 144 | # define __designated_init 145 | #endif 146 | 147 | /* 148 | * Optional: only supported since clang >= 14.0 149 | * 150 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-error-function-attribute 151 | */ 152 | #if __has_attribute(__error__) 153 | # define __compiletime_error(msg) __attribute__((__error__(msg))) 154 | #else 155 | # define __compiletime_error(msg) 156 | #endif 157 | 158 | /* 159 | * Optional: not supported by clang 160 | * 161 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-externally_005fvisible-function-attribute 162 | */ 163 | #if __has_attribute(__externally_visible__) 164 | # define __visible __attribute__((__externally_visible__)) 165 | #else 166 | # define __visible 167 | #endif 168 | 169 | /* 170 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-format-function-attribute 171 | * clang: https://clang.llvm.org/docs/AttributeReference.html#format 172 | */ 173 | #ifndef __printf 174 | # define __printf(a, b) __attribute__((__format__(printf, a, b))) 175 | #endif 176 | #ifndef __scanf 177 | # define __scanf(a, b) __attribute__((__format__(scanf, a, b))) 178 | #endif 179 | 180 | /* 181 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-gnu_005finline-function-attribute 182 | * clang: https://clang.llvm.org/docs/AttributeReference.html#gnu-inline 183 | */ 184 | #ifndef __gnu_inline 185 | # define __gnu_inline __attribute__((__gnu_inline__)) 186 | #endif 187 | 188 | /* 189 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-malloc-function-attribute 190 | */ 191 | #ifndef __malloc 192 | # define __malloc __attribute__((__malloc__)) 193 | #endif 194 | 195 | /* 196 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#index-mode-type-attribute 197 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-mode-variable-attribute 198 | */ 199 | #ifndef __mode 200 | # define __mode(x) __attribute__((__mode__(x))) 201 | #endif 202 | 203 | /* 204 | * Optional: only supported since gcc >= 7 205 | * 206 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#index-no_005fcaller_005fsaved_005fregisters-function-attribute_002c-x86 207 | * clang: https://clang.llvm.org/docs/AttributeReference.html#no-caller-saved-registers 208 | */ 209 | #if __has_attribute(__no_caller_saved_registers__) 210 | # define __no_caller_saved_registers __attribute__((__no_caller_saved_registers__)) 211 | #else 212 | # define __no_caller_saved_registers 213 | #endif 214 | 215 | /* 216 | * Optional: not supported by clang 217 | * 218 | * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-noclone-function-attribute 219 | */ 220 | #if __has_attribute(__noclone__) 221 | # define __noclone __attribute__((__noclone__)) 222 | #else 223 | # define __noclone 224 | #endif 225 | 226 | /* 227 | * Add the pseudo keyword 'fallthrough' so case statement blocks 228 | * must end with any of these keywords: 229 | * break; 230 | * fallthrough; 231 | * continue; 232 | * goto