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