Skip to content
Newer
Older
100644 302 lines (255 sloc) 8.11 KB
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
1 %% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
2 %% All rights reserved.
3 %%
4 %% Redistribution and use in source and binary forms, with or without
5 %% modification, are permitted provided that the following conditions
6 %% are met:
7 %%
8 %% Redistributions of source code must retain the above copyright
9 %% notice, this list of conditions and the following disclaimer.
10 %%
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 %%
15 %% Neither the name of the author nor the names of its contributors
16 %% may be used to endorse or promote products derived from this software
17 %% without specific prior written permission.
18 %%
19 %% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 %% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 %% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 %% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 %% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 %% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 %% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 %% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 %% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 %% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 %% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 %% POSSIBILITY OF SUCH DAMAGE.
31 -module(procket).
560517c @msantos Communicate with Unix sockets
authored
32 -include("procket.hrl").
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
33
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
34 -export([
fe09a25 @msantos Remove close/2; add listen/1
authored
35 init/0,
36 open/1,open/2,
37 socket/3,
38 listen/1,listen/2,
39 connect/2,
560517c @msantos Communicate with Unix sockets
authored
40 accept/1,accept/2,
fe09a25 @msantos Remove close/2; add listen/1
authored
41 close/1,
3206498 @msantos recvfrom/4: return socket address
authored
42 recv/2,recvfrom/2,recvfrom/4,
2200494 @msantos Add sendto/2,3
authored
43 sendto/2, sendto/3,sendto/4,
fe09a25 @msantos Remove close/2; add listen/1
authored
44 bind/2,
45 ioctl/3,
61b1302 @msantos Allocate pointers to buffers for ioctl requests
authored
46 setsockopt/4,
47
48 alloc/1,
49 buf/1,
50 memcpy/2
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
51 ]).
6dcac8f @msantos Fix sockaddr on BSD
authored
52 -export([unix_path_max/0, sockaddr_common/2]).
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
53 -export([make_args/2,progname/0]).
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
54
55 -on_load(on_load/0).
56
5d8600f @msantos Dynamically create path to NIF relative to module.
authored
57
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
58 init() ->
59 on_load().
60
61 on_load() ->
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
62 erlang:load_nif(progname(), []).
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
63
64
d7487f7 @msantos Close any file descriptor
authored
65 close(_) ->
66 erlang:error(not_implemented).
67
560517c @msantos Communicate with Unix sockets
authored
68 fdrecv(_) ->
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
69 erlang:error(not_implemented).
70
560517c @msantos Communicate with Unix sockets
authored
71 accept(Socket) ->
f5dc15a @msantos Return socket address from accept/2
authored
72 case accept(Socket, 0) of
73 {ok, FD, <<>>} -> {ok, FD};
74 Error -> Error
75 end.
560517c @msantos Communicate with Unix sockets
authored
76 accept(_,_) ->
77 erlang:error(not_implemented).
78
74a854e @msantos Add sendto/4 and bind/2
authored
79 bind(_,_) ->
80 erlang:error(not_implemented).
81
560517c @msantos Communicate with Unix sockets
authored
82 connect(_,_) ->
83 erlang:error(not_implemented).
84
fe09a25 @msantos Remove close/2; add listen/1
authored
85 listen(Socket) when is_integer(Socket) ->
86 listen(Socket, ?BACKLOG).
560517c @msantos Communicate with Unix sockets
authored
87 listen(_,_) ->
88 erlang:error(not_implemented).
89
3206498 @msantos recvfrom/4: return socket address
authored
90 recv(Socket,Size) ->
91 recvfrom(Socket,Size).
92 recvfrom(Socket,Size) ->
93 case recvfrom(Socket, Size, 0, 0) of
94 {ok, Buf, <<>>} -> {ok, Buf};
95 Error -> Error
96 end.
97 recvfrom(_,_,_,_) ->
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
98 erlang:error(not_implemented).
99
6a0b92f @msantos Socket args can be either atoms or integers
authored
100 socket(Family, Type, Protocol) when is_atom(Family) ->
101 socket(family(Family), Type, Protocol);
102 socket(Family, Type, Protocol) when is_atom(Type) ->
103 socket(Family, type(Type), Protocol);
104 socket(Family, Type, Protocol) when is_atom(Protocol) ->
105 socket(Family, Type, protocol(Protocol));
d1b85c6 @msantos Fix for atom socket arguments
authored
106 socket(Family, Type, Protocol)
107 when is_integer(Family), is_integer(Type), is_integer(Protocol) ->
108 socket_nif(Family, Type, Protocol).
109 socket_nif(_,_,_) ->
560517c @msantos Communicate with Unix sockets
authored
110 erlang:error(not_implemented).
111
65a61dc @msantos Add an NIF interface to ioctl()
authored
112 ioctl(_,_,_) ->
113 erlang:error(not_implemented).
61b1302 @msantos Allocate pointers to buffers for ioctl requests
authored
114 alloc(_) ->
115 erlang:error(not_implemented).
116 buf(_) ->
117 erlang:error(not_implemented).
118 memcpy(_,_) ->
119 erlang:error(not_implemented).
65a61dc @msantos Add an NIF interface to ioctl()
authored
120
2200494 @msantos Add sendto/2,3
authored
121 sendto(Socket, Buf) ->
122 sendto(Socket, Buf, 0, <<>>).
123 sendto(Socket, Buf, Flags) ->
124 sendto(Socket, Buf, Flags, <<>>).
74a854e @msantos Add sendto/4 and bind/2
authored
125 sendto(_,_,_,_) ->
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
126 erlang:error(not_implemented).
127
3649b5c @msantos Add an NIF setsockopt for promiscous mode
authored
128 setsockopt(_,_,_,_) ->
129 erlang:error(not_implemented).
130
560517c @msantos Communicate with Unix sockets
authored
131
132 open(Port) ->
133 open(Port, []).
134 open(Port, Options) when is_integer(Port), is_list(Options) ->
cfafb55 @msantos Clean up local socket temp directory
authored
135 Opt = case proplists:get_value(pipe, Options) of
136 undefined ->
137 Tmp = mktmp:dirname(),
138 ok = mktmp:make_dir(Tmp),
139 Path = Tmp ++ "/sock",
140 [{pipe, Path}, {tmpdir, Tmp}] ++ Options;
141 _ ->
142 [{tmpdir, false}] ++ Options
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
143 end,
560517c @msantos Communicate with Unix sockets
authored
144 open1(Port, Opt).
cfafb55 @msantos Clean up local socket temp directory
authored
145
560517c @msantos Communicate with Unix sockets
authored
146 open1(Port, Options) ->
cfafb55 @msantos Clean up local socket temp directory
authored
147 Pipe = proplists:get_value(pipe, Options),
560517c @msantos Communicate with Unix sockets
authored
148 {ok, Sockfd} = fdopen(Pipe),
cfafb55 @msantos Clean up local socket temp directory
authored
149 Cmd = make_args(Port, Options),
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
150 case os:cmd(Cmd) of
151 [] ->
560517c @msantos Communicate with Unix sockets
authored
152 FD = fdget(Sockfd),
cfafb55 @msantos Clean up local socket temp directory
authored
153 cleanup(Sockfd, Pipe, Options),
00214e3 @msantos Make the NIF stateless!
authored
154 FD;
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
155 Error ->
cfafb55 @msantos Clean up local socket temp directory
authored
156 cleanup(Sockfd, Pipe, Options),
00214e3 @msantos Make the NIF stateless!
authored
157 {error, {procket_cmd, Error}}
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
158 end.
159
cfafb55 @msantos Clean up local socket temp directory
authored
160 cleanup(Sockfd, Pipe, Options) ->
fe09a25 @msantos Remove close/2; add listen/1
authored
161 close(Sockfd),
162 ok = file:delete(Pipe),
cfafb55 @msantos Clean up local socket temp directory
authored
163 case proplists:get_value(tmpdir, Options) of
164 false ->
165 ok;
166 Path ->
167 mktmp:close(Path)
168 end.
169
560517c @msantos Communicate with Unix sockets
authored
170 fdopen(Path) when is_list(Path) ->
171 fdopen(list_to_binary(Path));
172 fdopen(Path) when is_binary(Path), byte_size(Path) < ?UNIX_PATH_MAX ->
173 {ok, Socket} = socket(?PF_LOCAL, ?SOCK_STREAM, 0),
6dcac8f @msantos Fix sockaddr on BSD
authored
174 Len = byte_size(Path),
175 Sun = <<(sockaddr_common(?PF_LOCAL, Len))/binary,
176 Path/binary,
177 0:((unix_path_max()-Len)*8)
178 >>,
560517c @msantos Communicate with Unix sockets
authored
179 ok = bind(Socket, Sun),
180 ok = listen(Socket, ?BACKLOG),
181 {ok, Socket}.
182
183 fdget(Socket) ->
184 {ok, S} = accept(Socket),
185 fdrecv(S).
186
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
187 make_args(Port, Options) ->
188 Bind = " " ++ case proplists:lookup(ip, Options) of
189 none ->
190 integer_to_list(Port);
191 IP ->
192 get_switch(IP) ++ ":" ++ integer_to_list(Port)
193 end,
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
194 proplists:get_value(progname, Options, "sudo " ++ progname()) ++ " " ++
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
195 string:join([ get_switch(proplists:lookup(Arg, Options)) || Arg <- [
196 pipe,
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
197 protocol,
198 family,
eba4ec3 Added option to bind a socket to a specific interface
klaar authored
199 type,
200 interface
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
201 ], proplists:lookup(Arg, Options) /= none ],
202 " ") ++ Bind.
203
6a0b92f @msantos Socket args can be either atoms or integers
authored
204 get_switch({pipe, Arg}) ->
205 "-p " ++ Arg;
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
206
6a0b92f @msantos Socket args can be either atoms or integers
authored
207 get_switch({protocol, Proto}) when is_atom(Proto) ->
208 get_switch({protocol, protocol(Proto)});
209 get_switch({protocol, Proto}) when is_integer(Proto) ->
210 "-P " ++ integer_to_list(Proto);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
211
6a0b92f @msantos Socket args can be either atoms or integers
authored
212 get_switch({type, Type}) when is_atom(Type) ->
213 get_switch({type, type(Type)});
214 get_switch({type, Type}) when is_integer(Type) ->
215 "-T " ++ integer_to_list(Type);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
216
6a0b92f @msantos Socket args can be either atoms or integers
authored
217 get_switch({family, Family}) when is_atom(Family) ->
218 get_switch({family, family(Family)});
219 get_switch({family, Family}) when is_integer(Family) ->
220 "-F " ++ integer_to_list(Family);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
221
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
222 get_switch({ip, Arg}) when is_tuple(Arg) -> inet_parse:ntoa(Arg);
eba4ec3 Added option to bind a socket to a specific interface
klaar authored
223 get_switch({ip, Arg}) when is_list(Arg) -> Arg;
224
315b8cf Add interface name sanitation
klaar authored
225 get_switch({interface, Name}) when is_list(Name) ->
226 % An interface name is expected to consist of a reasonable
227 % subset of all charactes, use a whitelist and extend it if needed
228 SName = [C || C <- Name, ((C >= $a) and (C =< $z)) or ((C >= $A) and (C =< $Z))
229 or ((C >= $0) and (C =< $9)) or (C == $.)],
230 "-I " ++ SName.
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
231
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
232 progname() ->
233 filename:join([
234 filename:dirname(code:which(?MODULE)),
235 "..",
236 "priv",
237 ?MODULE
238 ]).
239
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
240
6a0b92f @msantos Socket args can be either atoms or integers
authored
241 %% Protocol family (aka domain)
242 family(unspec) -> 0;
243 family(inet) -> 2;
244 family(packet) -> 17;
245 family(Proto) when Proto == local; Proto == unix; Proto == file -> 1;
246
247 family(0) -> unspec;
248 family(1) -> unix;
249 family(2) -> inet;
250 family(17) -> packet.
251
252
253 %% Socket type
254 type(stream) -> 1;
255 type(dgram) -> 2;
256 type(raw) -> 3;
257
258 type(1) -> stream;
259 type(2) -> dgram;
260 type(3) -> raw.
261
262
263 % Select a protocol within the family (0 means use the default
264 % protocol in the family)
265 protocol(raw) -> 0;
266 protocol(icmp) -> 1;
267 protocol(tcp) -> 6;
268 protocol(udp) -> 17;
269
270 protocol(0) -> raw;
271 protocol(1) -> icmp;
272 protocol(6) -> tcp;
273 protocol(17) -> udp.
274
275
6dcac8f @msantos Fix sockaddr on BSD
authored
276 %%
277 %% Portability
278 %%
279
280 % struct sockaddr
281 sockaddr_common(Family, Length) ->
282 case erlang:system_info(os_type) of
283 {unix,BSD} when BSD == darwin;
284 BSD == openbsd;
285 BSD == netbsd;
286 BSD == freebsd ->
287 <<Length:8, Family:8>>;
288 {unix,_} ->
289 <<Family:16/native>>
290 end.
291
292 % UNIX_PATH_MAX
293 unix_path_max() ->
294 case erlang:system_info(os_type) of
295 {unix,BSD} when BSD == darwin;
296 BSD == openbsd;
297 BSD == netbsd;
298 BSD == freebsd -> 104;
299 {unix,_} -> 108
300 end.
301
Something went wrong with that request. Please try again.