1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2021. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(gen_tcp). 22 23 24-export([connect/3, connect/4, listen/2, accept/1, accept/2, 25 shutdown/2, close/1]). 26-export([send/2, recv/2, recv/3, unrecv/2]). 27-export([controlling_process/2]). 28-export([fdopen/2]). 29 30-include("inet_int.hrl"). 31-include("file.hrl"). 32 33-define(module_socket(Handler, Handle), 34 {'$inet', (Handler), (Handle)}). 35 36-type option() :: 37 {active, true | false | once | -32768..32767} | 38 {buffer, non_neg_integer()} | 39 {delay_send, boolean()} | 40 {deliver, port | term} | 41 {dontroute, boolean()} | 42 {exit_on_close, boolean()} | 43 {header, non_neg_integer()} | 44 {high_msgq_watermark, pos_integer()} | 45 {high_watermark, non_neg_integer()} | 46 {keepalive, boolean()} | 47 {linger, {boolean(), non_neg_integer()}} | 48 {low_msgq_watermark, pos_integer()} | 49 {low_watermark, non_neg_integer()} | 50 {mode, list | binary} | list | binary | 51 {nodelay, boolean()} | 52 {packet, 53 0 | 1 | 2 | 4 | raw | sunrm | asn1 | 54 cdr | fcgi | line | tpkt | http | httph | http_bin | httph_bin } | 55 {packet_size, non_neg_integer()} | 56 {priority, non_neg_integer()} | 57 {raw, 58 Protocol :: non_neg_integer(), 59 OptionNum :: non_neg_integer(), 60 ValueBin :: binary()} | 61 {recbuf, non_neg_integer()} | 62 {reuseaddr, boolean()} | 63 {send_timeout, non_neg_integer() | infinity} | 64 {send_timeout_close, boolean()} | 65 {show_econnreset, boolean()} | 66 {sndbuf, non_neg_integer()} | 67 {tos, non_neg_integer()} | 68 {tclass, non_neg_integer()} | 69 {ttl, non_neg_integer()} | 70 {recvtos, boolean()} | 71 {recvtclass, boolean()} | 72 {recvttl, boolean()} | 73 {ipv6_v6only, boolean()}. 74-type pktoptions_value() :: 75 {pktoptions, inet:ancillary_data()}. 76-type option_name() :: 77 active | 78 buffer | 79 delay_send | 80 deliver | 81 dontroute | 82 exit_on_close | 83 header | 84 high_msgq_watermark | 85 high_watermark | 86 keepalive | 87 linger | 88 low_msgq_watermark | 89 low_watermark | 90 mode | 91 nodelay | 92 packet | 93 packet_size | 94 priority | 95 {raw, 96 Protocol :: non_neg_integer(), 97 OptionNum :: non_neg_integer(), 98 ValueSpec :: (ValueSize :: non_neg_integer()) | 99 (ValueBin :: binary())} | 100 recbuf | 101 reuseaddr | 102 send_timeout | 103 send_timeout_close | 104 show_econnreset | 105 sndbuf | 106 tos | 107 tclass | 108 ttl | 109 recvtos | 110 recvtclass | 111 recvttl | 112 pktoptions | 113 ipv6_v6only. 114-type connect_option() :: 115 {ip, inet:socket_address()} | 116 {fd, Fd :: non_neg_integer()} | 117 {ifaddr, inet:socket_address()} | 118 inet:address_family() | 119 {port, inet:port_number()} | 120 {tcp_module, module()} | 121 {netns, file:filename_all()} | 122 {bind_to_device, binary()} | 123 option(). 124-type listen_option() :: 125 {ip, inet:socket_address()} | 126 {fd, Fd :: non_neg_integer()} | 127 {ifaddr, inet:socket_address()} | 128 inet:address_family() | 129 {port, inet:port_number()} | 130 {backlog, B :: non_neg_integer()} | 131 {tcp_module, module()} | 132 {netns, file:filename_all()} | 133 {bind_to_device, binary()} | 134 option(). 135-type socket() :: inet:socket(). 136 137-export_type([option/0, option_name/0, connect_option/0, listen_option/0, 138 socket/0, pktoptions_value/0]). 139 140%% 141%% Connect a socket 142%% 143 144-spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when 145 Address :: inet:socket_address() | inet:hostname(), 146 Port :: inet:port_number(), 147 Options :: [connect_option()], 148 Socket :: socket(), 149 Reason :: inet:posix(). 150 151connect(Address, Port, Opts) -> 152 connect(Address,Port,Opts,infinity). 153 154-spec connect(Address, Port, Options, Timeout) -> 155 {ok, Socket} | {error, Reason} when 156 Address :: inet:socket_address() | inet:hostname(), 157 Port :: inet:port_number(), 158 Options :: [inet:inet_backend() | connect_option()], 159 Timeout :: timeout(), 160 Socket :: socket(), 161 Reason :: timeout | inet:posix(). 162 163connect(Address, Port, Opts0, Time) -> 164 case inet:gen_tcp_module(Opts0) of 165 {?MODULE, Opts} -> 166 Timer = inet:start_timer(Time), 167 Res = (catch connect1(Address,Port,Opts,Timer)), 168 _ = inet:stop_timer(Timer), 169 case Res of 170 {ok,S} -> {ok,S}; 171 {error, einval} -> exit(badarg); 172 {'EXIT',Reason} -> exit(Reason); 173 Error -> Error 174 end; 175 {GenTcpMod, Opts} -> 176 GenTcpMod:connect(Address, Port, Opts, Time) 177 end. 178 179connect1(Address, Port, Opts0, Timer) -> 180 {Mod, Opts} = inet:tcp_module(Opts0, Address), 181 case Mod:getaddrs(Address,Timer) of 182 {ok,IPs} -> 183 case Mod:getserv(Port) of 184 {ok,TP} -> try_connect(IPs,TP,Opts,Timer,Mod,{error,einval}); 185 Error -> Error 186 end; 187 Error -> Error 188 end. 189 190try_connect([IP|IPs], Port, Opts, Timer, Mod, _) -> 191 Time = inet:timeout(Timer), 192 case Mod:connect(IP, Port, Opts, Time) of 193 {ok,S} -> {ok,S}; 194 {error,einval} -> {error, einval}; 195 {error,timeout} -> {error,timeout}; 196 Err1 -> try_connect(IPs, Port, Opts, Timer, Mod, Err1) 197 end; 198try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> 199 Err. 200 201 202 203%% 204%% Listen on a tcp port 205%% 206 207-spec listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} when 208 Port :: inet:port_number(), 209 Options :: [inet:inet_backend() | listen_option()], 210 ListenSocket :: socket(), 211 Reason :: system_limit | inet:posix(). 212 213listen(Port, Opts0) -> 214 case inet:gen_tcp_module(Opts0) of 215 {?MODULE, Opts1} -> 216 {Mod, Opts} = inet:tcp_module(Opts1), 217 case Mod:getserv(Port) of 218 {ok,TP} -> 219 Mod:listen(TP, Opts); 220 {error,einval} -> 221 exit(badarg); 222 Other -> Other 223 end; 224 {GenTcpMod, Opts} -> 225 GenTcpMod:listen(Port, Opts) 226 end. 227 228%% 229%% Generic tcp accept 230%% 231 232-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when 233 ListenSocket :: socket(), 234 Socket :: socket(), 235 Reason :: closed | system_limit | inet:posix(). 236 237accept(?module_socket(GenTcpMod, _) = S) when is_atom(GenTcpMod) -> 238 GenTcpMod:?FUNCTION_NAME(S, infinity); 239accept(S) when is_port(S) -> 240 case inet_db:lookup_socket(S) of 241 {ok, Mod} -> 242 Mod:accept(S); 243 Error -> 244 Error 245 end. 246 247-spec accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason} when 248 ListenSocket :: socket(), 249 Timeout :: timeout(), 250 Socket :: socket(), 251 Reason :: closed | timeout | system_limit | inet:posix(). 252 253accept(?module_socket(GenTcpMod, _) = S, Time) when is_atom(GenTcpMod) -> 254 GenTcpMod:?FUNCTION_NAME(S, Time); 255accept(S, Time) when is_port(S) -> 256 case inet_db:lookup_socket(S) of 257 {ok, Mod} -> 258 Mod:accept(S, Time); 259 Error -> 260 Error 261 end. 262 263%% 264%% Generic tcp shutdown 265%% 266 267-spec shutdown(Socket, How) -> ok | {error, Reason} when 268 Socket :: socket(), 269 How :: read | write | read_write, 270 Reason :: inet:posix(). 271 272shutdown(?module_socket(GenTcpMod, _) = S, How) when is_atom(GenTcpMod) -> 273 GenTcpMod:?FUNCTION_NAME(S, How); 274shutdown(S, How) when is_port(S) -> 275 case inet_db:lookup_socket(S) of 276 {ok, Mod} -> 277 Mod:shutdown(S, How); 278 Error -> 279 Error 280 end. 281 282%% 283%% Close 284%% 285 286-spec close(Socket) -> ok when 287 Socket :: socket(). 288 289close(?module_socket(GenTcpMod, _) = S) when is_atom(GenTcpMod) -> 290 GenTcpMod:?FUNCTION_NAME(S); 291close(S) -> 292 inet:tcp_close(S). 293 294%% 295%% Send 296%% 297 298-spec send(Socket, Packet) -> ok | {error, Reason} when 299 Socket :: socket(), 300 Packet :: iodata(), 301 Reason :: closed | {timeout, RestData} | inet:posix(), 302 RestData :: binary(). 303 304send(?module_socket(GenTcpMod, _) = S, Packet) when is_atom(GenTcpMod) -> 305 GenTcpMod:?FUNCTION_NAME(S, Packet); 306send(S, Packet) when is_port(S) -> 307 case inet_db:lookup_socket(S) of 308 {ok, Mod} -> 309 Mod:send(S, Packet); 310 Error -> 311 Error 312 end. 313 314%% 315%% Receive data from a socket (passive mode) 316%% 317 318-spec recv(Socket, Length) -> {ok, Packet} | {error, Reason} when 319 Socket :: socket(), 320 Length :: non_neg_integer(), 321 Packet :: string() | binary() | HttpPacket, 322 Reason :: closed | inet:posix(), 323 HttpPacket :: term(). 324 325recv(?module_socket(GenTcpMod, _) = S, Length) when is_atom(GenTcpMod) -> 326 GenTcpMod:?FUNCTION_NAME(S, Length, infinity); 327recv(S, Length) when is_port(S) -> 328 case inet_db:lookup_socket(S) of 329 {ok, Mod} -> 330 Mod:recv(S, Length); 331 Error -> 332 Error 333 end. 334 335-spec recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason} when 336 Socket :: socket(), 337 Length :: non_neg_integer(), 338 Timeout :: timeout(), 339 Packet :: string() | binary() | HttpPacket, 340 Reason :: closed | timeout | inet:posix(), 341 HttpPacket :: term(). 342 343recv(?module_socket(GenTcpMod, _) = S, Length, Time) when is_atom(GenTcpMod) -> 344 GenTcpMod:?FUNCTION_NAME(S, Length, Time); 345recv(S, Length, Time) when is_port(S) -> 346 case inet_db:lookup_socket(S) of 347 {ok, Mod} -> 348 Mod:recv(S, Length, Time); 349 Error -> 350 Error 351 end. 352 353unrecv(?module_socket(GenTcpMod, _) = S, Data) when is_atom(GenTcpMod) -> 354 GenTcpMod:?FUNCTION_NAME(S, Data); 355unrecv(S, Data) when is_port(S) -> 356 case inet_db:lookup_socket(S) of 357 {ok, Mod} -> 358 Mod:unrecv(S, Data); 359 Error -> 360 Error 361 end. 362 363%% 364%% Set controlling process 365%% 366 367-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when 368 Socket :: socket(), 369 Pid :: pid(), 370 Reason :: closed | not_owner | badarg | inet:posix(). 371 372controlling_process(?module_socket(GenTcpMod, _) = S, NewOwner) 373 when is_atom(GenTcpMod) -> 374 GenTcpMod:?FUNCTION_NAME(S, NewOwner); 375controlling_process(S, NewOwner) -> 376 case inet_db:lookup_socket(S) of 377 {ok, _Mod} -> % Just check that this is an open socket 378 inet:tcp_controlling_process(S, NewOwner); 379 Error -> 380 Error 381 end. 382 383 384 385%% 386%% Create a port/socket from a file descriptor 387%% 388fdopen(Fd, Opts0) -> 389 case inet:gen_tcp_module(Opts0) of 390 {?MODULE, Opts1} -> 391 {Mod, Opts} = inet:tcp_module(Opts1), 392 Mod:fdopen(Fd, Opts); 393 {GenTcpMod, Opts} -> 394 GenTcpMod:fdopen(Fd, Opts) 395 end. 396