1 | /* |
2 | This file is part of Mtproto-proxy Library. |
3 | |
4 | Mtproto-proxy Library is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU Lesser General Public License as published by |
6 | the Free Software Foundation, either version 2 of the License, or |
7 | (at your option) any later version. |
8 | |
9 | Mtproto-proxy Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public License |
15 | along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | Copyright 2013 Vkontakte Ltd |
18 | 2013 Vitaliy Valtman |
19 | 2013 Anton Maydell |
20 | |
21 | Copyright 2014 Telegram Messenger Inc |
22 | 2014 Vitaly Valtman |
23 | 2014 Anton Maydell |
24 | |
25 | Copyright 2015-2016 Telegram Messenger Inc |
26 | 2015-2016 Vitaliy Valtman |
27 | */ |
28 | #include <arpa/inet.h> |
29 | #include <stdarg.h> |
30 | #include <unistd.h> |
31 | #include <fcntl.h> |
32 | |
33 | #include "common/kprintf.h" |
34 | #include "common/server-functions.h" |
35 | |
36 | #include "engine/engine.h" |
37 | #include "engine/engine-net.h" |
38 | |
39 | #include "net/net-tcp-rpc-client.h" |
40 | |
41 | void default_close_network_sockets (void) /* {{{ */ { |
42 | engine_t *E = engine_state; |
43 | |
44 | if (E->sfd > 0) { |
45 | close (E->sfd); |
46 | E->sfd = -1; |
47 | } |
48 | } |
49 | /* }}} */ |
50 | |
51 | int get_port_mod (void) /* {{{ */ { |
52 | return -1; |
53 | } |
54 | /* }}} */ |
55 | |
56 | int try_open_port (int port, int quit_on_fail) /* {{{ */ { |
57 | engine_t *E = engine_state; |
58 | int enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0; |
59 | if (engine_check_tcp_enabled ()) { |
60 | struct in_addr l; |
61 | l.s_addr = htonl(0x7f000001); |
62 | E->sfd = server_socket (port, l, engine_get_backlog (), enable_ipv6); |
63 | vkprintf (1, "opened tcp socket\n" ); |
64 | if (E->sfd < 0) { |
65 | if (quit_on_fail) { |
66 | kprintf ("cannot open server socket at port %d: %m\n" , port); |
67 | exit (1); |
68 | } else { |
69 | return -1; |
70 | } |
71 | } |
72 | } |
73 | return 0; |
74 | } |
75 | /* }}} */ |
76 | |
77 | int try_open_port_range (int start_port, int end_port, int mod_port, int rem_port, int quit_on_fail) /* {{{ */ { |
78 | int s = start_port; |
79 | for (;start_port <= end_port; start_port ++) { |
80 | if (mod_port && rem_port >= 0 && (start_port % mod_port) != (rem_port % mod_port)) { continue; } |
81 | if (try_open_port (start_port, 0) >= 0) { |
82 | return start_port; |
83 | } |
84 | } |
85 | if (quit_on_fail) { |
86 | kprintf ("cannot open server socket at port %d-%d\n" , s, end_port); |
87 | exit (2); |
88 | } |
89 | return -1; |
90 | } |
91 | /* }}} */ |
92 | |
93 | void engine_do_open_port (void) /* {{{ */ { |
94 | int port_mod = get_port_mod (); |
95 | |
96 | int port = engine_state->port; |
97 | int start_port = engine_state->start_port; |
98 | int end_port = engine_state->end_port; |
99 | |
100 | if (port > 0 && port < PRIVILEGED_TCP_PORTS) { |
101 | assert (try_open_port (port, 1) >= 0); |
102 | return; |
103 | } |
104 | |
105 | if (port <= 0 && start_port <= end_port && start_port < PRIVILEGED_TCP_PORTS) { |
106 | engine_state->port = try_open_port_range (start_port, end_port, 100, port_mod, 1); |
107 | assert (engine_state->port >= 0); |
108 | return; |
109 | } |
110 | } |
111 | /* }}} */ |
112 | |
113 | struct tcp_rpc_server_functions default_engine_tcp_rpc_methods = { |
114 | .execute = default_tl_tcp_rpcs_execute, |
115 | .check_ready = server_check_ready, |
116 | .flush_packet = tcp_rpc_flush_packet, |
117 | .rpc_check_perm = tcp_rpcs_default_check_perm, |
118 | .rpc_init_crypto = tcp_rpcs_init_crypto, |
119 | .rpc_close = default_tl_close_conn, |
120 | }; |
121 | |
122 | void engine_set_tcp_methods (struct tcp_rpc_server_functions *F) { |
123 | default_engine_tcp_rpc_methods = *F; |
124 | } |
125 | |
126 | |
127 | void engine_set_http_fallback (conn_type_t *http_type, struct http_server_functions *http_functions) { |
128 | default_engine_tcp_rpc_methods.http_fallback_type = http_type; |
129 | default_engine_tcp_rpc_methods.http_fallback_extra = http_functions; |
130 | } |
131 | |
132 | void engine_server_init (void) { |
133 | server_init (&ct_tcp_rpc_server, &default_engine_tcp_rpc_methods); |
134 | } |
135 | |
136 | void set_maxconn (int val) { |
137 | if (val <= 0) { |
138 | val = MAX_CONNECTIONS; |
139 | } |
140 | engine_state->maxconn = val; |
141 | tcp_set_max_connections (val); |
142 | } |
143 | |
144 | |
145 | static int f_parse_option_net (int val) { |
146 | switch (val) { |
147 | case 'b': |
148 | engine_set_backlog (atoi (optarg)); |
149 | break; |
150 | case 'c': |
151 | set_maxconn (atoi (optarg)); |
152 | break; |
153 | case 'p': |
154 | { |
155 | int start_port, end_port; |
156 | int x = sscanf (optarg, "%d:%d" , &start_port, &end_port); |
157 | if (!x) { |
158 | usage (); |
159 | } |
160 | if (x == 1) { |
161 | if (start_port <= 0) { |
162 | usage (); |
163 | } |
164 | engine_state->port = start_port; |
165 | } else { |
166 | if (start_port <= 0 || start_port > end_port) { |
167 | usage (); |
168 | } |
169 | engine_state->start_port = start_port; |
170 | engine_state->end_port = end_port; |
171 | } |
172 | } |
173 | break; |
174 | case '6': |
175 | engine_enable_ipv6 (); |
176 | break; |
177 | case 200: |
178 | engine_set_aes_pwd_file (optarg); |
179 | break; |
180 | case 214: |
181 | engine_disable_tcp (); |
182 | break; |
183 | case 224: |
184 | tcp_set_default_rpc_flags (0xffffffff, RPCF_USE_CRC32C); |
185 | break; |
186 | case 229: |
187 | tcp_set_default_rpc_flags (0xffffffff, RPCF_ALLOW_SKIP_DH); |
188 | break; |
189 | case 230: |
190 | tcp_force_enable_dh (); |
191 | break; |
192 | case 249: |
193 | tcp_set_max_accept_rate (atoi (optarg)); |
194 | break; |
195 | case 250: |
196 | tcp_set_max_dh_accept_rate (atoi (optarg)); |
197 | break; |
198 | case 372: |
199 | if (net_add_nat_info (optarg) < 0) { |
200 | usage (); |
201 | exit (2); |
202 | } |
203 | break; |
204 | case 373: |
205 | { |
206 | engine_t *E = engine_state; |
207 | assert (E); |
208 | if (inet_pton (AF_INET, optarg, &E->settings_addr) != 1) { |
209 | kprintf ("Can not convert '%s' to ip addr: %m\n" , optarg); |
210 | exit (4); |
211 | } |
212 | } |
213 | break; |
214 | default: |
215 | return -1; |
216 | } |
217 | return 0; |
218 | } |
219 | |
220 | static void parse_option_net_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) __attribute__ ((format (printf, 6, 7))); |
221 | static void parse_option_net_builtin (const char *name, int arg, int *var, int val, unsigned flags, const char *help, ...) { |
222 | char *h = NULL; |
223 | va_list ap; |
224 | va_start (ap, help); |
225 | assert (vasprintf (&h, help, ap) >= 0); |
226 | va_end (ap); |
227 | |
228 | parse_option_ex (name, arg, var, val, flags, f_parse_option_net, "%s" , h); |
229 | free (h); |
230 | } |
231 | |
232 | void engine_add_net_parse_options (void) { |
233 | parse_option_net_builtin ("backlog" , required_argument, 0, 'b', LONGOPT_TCP_SET, "sets backlog size" ); |
234 | parse_option_net_builtin ("connections" , required_argument, 0, 'c', LONGOPT_TCP_SET, "sets maximal connections number" ); |
235 | parse_option_net_builtin ("port" , required_argument, 0, 'p', LONGOPT_NET_SET, "<port> or <sport>:<eport> sets listening port number or port range" ); |
236 | parse_option_net_builtin ("aes-pwd" , required_argument, 0, 200, LONGOPT_NET_SET, "sets custom secret.conf file" ); |
237 | parse_option_net_builtin ("ipv6" , no_argument, 0, '6', LONGOPT_NET_SET, "enables ipv6 TCP/UDP support" ); |
238 | parse_option_net_builtin ("disable-tcp" , no_argument, 0, 214, LONGOPT_TCP_SET, "do not open listening tcp socket" ); |
239 | parse_option_net_builtin ("crc32c" , no_argument, 0, 224, LONGOPT_TCP_SET, "Try to use crc32c instead of crc32 in tcp rpc" ); |
240 | parse_option_net_builtin ("allow-skip-dh" , no_argument, 0, 229, LONGOPT_TCP_SET, "Allow skipping DH during RPC handshake" ); |
241 | parse_option_net_builtin ("force-dh" , no_argument, 0, 230, LONGOPT_TCP_SET, "Force using DH for all outbound RPC connections" ); |
242 | parse_option_net_builtin ("max-accept-rate" , required_argument, 0, 249, LONGOPT_TCP_SET, "max number of connections per second that is allowed to accept" ); |
243 | parse_option_net_builtin ("max-dh-accept-rate" , required_argument, 0, 250, LONGOPT_TCP_SET, "max number of DH connections per second that is allowed to accept" ); |
244 | parse_option_net_builtin ("nat-info" , required_argument, 0, 372, LONGOPT_NET_SET, "<local-addr>:<global-addr>\tsets network address translation for RPC protocol handshake" ); |
245 | parse_option_net_builtin ("address" , required_argument, 0, 373, LONGOPT_NET_SET, "tries to bind socket only to specified address" ); |
246 | } |
247 | |