Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 399 lines (337 sloc) 10.211 kB
d0508b2 @msantos Convert socket parameters to integers
authored
1 %% Copyright (c) 2010-2011, Michael Santos <michael.santos@gmail.com>
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
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,
a1310c1 @msantos Convenience fun to open character devices
authored
37 dev/1,
fe09a25 @msantos Remove close/2; add listen/1
authored
38 socket/3,
39 listen/1,listen/2,
40 connect/2,
560517c @msantos Communicate with Unix sockets
authored
41 accept/1,accept/2,
fe09a25 @msantos Remove close/2; add listen/1
authored
42 close/1,
3206498 @msantos recvfrom/4: return socket address
authored
43 recv/2,recvfrom/2,recvfrom/4,
2200494 @msantos Add sendto/2,3
authored
44 sendto/2, sendto/3,sendto/4,
eb432ee Add writev() support
Gregory Haskins authored
45 read/2, write/2, writev/2,
fe09a25 @msantos Remove close/2; add listen/1
authored
46 bind/2,
47 ioctl/3,
61b1302 @msantos Allocate pointers to buffers for ioctl requests
authored
48 setsockopt/4,
49
50 alloc/1,
51 buf/1,
7a64070 @msantos Return POSIX atom on error from forked setuid bin
authored
52 memcpy/2,
53
ca35538 Add support for asynch notifications on a file-descriptor
Gregory Haskins authored
54 watcher_create/3,
55 watcher_arm/1,
56 watcher_disarm/1,
57
7a64070 @msantos Return POSIX atom on error from forked setuid bin
authored
58 errno_id/1
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
59 ]).
dd34004 @msantos Initial support for BPF on BSDs
authored
60 -export([
61 unix_path_max/0,
62 sockaddr_common/2,
63 ntohl/1,
64 ntohs/1
65 ]).
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
66 -export([make_args/2,progname/0]).
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
67
68 -on_load(on_load/0).
69
5d8600f @msantos Dynamically create path to NIF relative to module.
authored
70
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
71 init() ->
72 on_load().
73
74 on_load() ->
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
75 erlang:load_nif(progname(), []).
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
76
77
d7487f7 @msantos Close any file descriptor
authored
78 close(_) ->
79 erlang:error(not_implemented).
80
560517c @msantos Communicate with Unix sockets
authored
81 fdrecv(_) ->
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
82 erlang:error(not_implemented).
83
560517c @msantos Communicate with Unix sockets
authored
84 accept(Socket) ->
f5dc15a @msantos Return socket address from accept/2
authored
85 case accept(Socket, 0) of
86 {ok, FD, <<>>} -> {ok, FD};
87 Error -> Error
88 end.
560517c @msantos Communicate with Unix sockets
authored
89 accept(_,_) ->
90 erlang:error(not_implemented).
91
74a854e @msantos Add sendto/4 and bind/2
authored
92 bind(_,_) ->
93 erlang:error(not_implemented).
94
560517c @msantos Communicate with Unix sockets
authored
95 connect(_,_) ->
96 erlang:error(not_implemented).
97
fe09a25 @msantos Remove close/2; add listen/1
authored
98 listen(Socket) when is_integer(Socket) ->
99 listen(Socket, ?BACKLOG).
560517c @msantos Communicate with Unix sockets
authored
100 listen(_,_) ->
101 erlang:error(not_implemented).
102
3206498 @msantos recvfrom/4: return socket address
authored
103 recv(Socket,Size) ->
104 recvfrom(Socket,Size).
105 recvfrom(Socket,Size) ->
106 case recvfrom(Socket, Size, 0, 0) of
107 {ok, Buf, <<>>} -> {ok, Buf};
108 Error -> Error
109 end.
110 recvfrom(_,_,_,_) ->
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
111 erlang:error(not_implemented).
112
dd34004 @msantos Initial support for BPF on BSDs
authored
113 read(_,_) ->
114 erlang:error(not_implemented).
115
d0508b2 @msantos Convert socket parameters to integers
authored
116 socket(Family, Type, Protocol) ->
117 socket_nif(maybe_atom(family, Family),
118 maybe_atom(type, Type),
119 maybe_atom(protocol, Protocol)).
d1b85c6 @msantos Fix for atom socket arguments
authored
120 socket_nif(_,_,_) ->
560517c @msantos Communicate with Unix sockets
authored
121 erlang:error(not_implemented).
122
65a61dc @msantos Add an NIF interface to ioctl()
authored
123 ioctl(_,_,_) ->
124 erlang:error(not_implemented).
61b1302 @msantos Allocate pointers to buffers for ioctl requests
authored
125 alloc(_) ->
126 erlang:error(not_implemented).
127 buf(_) ->
128 erlang:error(not_implemented).
129 memcpy(_,_) ->
130 erlang:error(not_implemented).
65a61dc @msantos Add an NIF interface to ioctl()
authored
131
ca35538 Add support for asynch notifications on a file-descriptor
Gregory Haskins authored
132 watcher_create(_,_,_) ->
133 erlang:error(not_implemented).
134 watcher_arm(_) ->
135 erlang:error(not_implemented).
136 watcher_disarm(_) ->
137 erlang:error(not_implemented).
138
2200494 @msantos Add sendto/2,3
authored
139 sendto(Socket, Buf) ->
140 sendto(Socket, Buf, 0, <<>>).
141 sendto(Socket, Buf, Flags) ->
142 sendto(Socket, Buf, Flags, <<>>).
74a854e @msantos Add sendto/4 and bind/2
authored
143 sendto(_,_,_,_) ->
194feb8 @msantos Add NIF versions of recvfrom/sendto
authored
144 erlang:error(not_implemented).
145
dd34004 @msantos Initial support for BPF on BSDs
authored
146 write(_,_) ->
147 erlang:error(not_implemented).
eb432ee Add writev() support
Gregory Haskins authored
148 writev(_,_) ->
149 erlang:error(not_implemented).
dd34004 @msantos Initial support for BPF on BSDs
authored
150
3649b5c @msantos Add an NIF setsockopt for promiscous mode
authored
151 setsockopt(_,_,_,_) ->
152 erlang:error(not_implemented).
153
7a64070 @msantos Return POSIX atom on error from forked setuid bin
authored
154 errno_id(_) ->
155 erlang:error(not_implemented).
156
560517c @msantos Communicate with Unix sockets
authored
157
a1310c1 @msantos Convenience fun to open character devices
authored
158 dev(Dev) when is_list(Dev) ->
159 open(0, [{dev, Dev}]).
160
560517c @msantos Communicate with Unix sockets
authored
161 open(Port) ->
162 open(Port, []).
163 open(Port, Options) when is_integer(Port), is_list(Options) ->
cfafb55 @msantos Clean up local socket temp directory
authored
164 Opt = case proplists:get_value(pipe, Options) of
165 undefined ->
edef854 @msantos Rename the mktmp utility module
authored
166 Tmp = procket_mktmp:dirname(),
167 ok = procket_mktmp:make_dir(Tmp),
cfafb55 @msantos Clean up local socket temp directory
authored
168 Path = Tmp ++ "/sock",
169 [{pipe, Path}, {tmpdir, Tmp}] ++ Options;
170 _ ->
171 [{tmpdir, false}] ++ Options
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
172 end,
5f67476 @msantos Underscore numbered function
authored
173 open_1(Port, Opt).
cfafb55 @msantos Clean up local socket temp directory
authored
174
5f67476 @msantos Underscore numbered function
authored
175 open_1(Port, Options) ->
cfafb55 @msantos Clean up local socket temp directory
authored
176 Pipe = proplists:get_value(pipe, Options),
560517c @msantos Communicate with Unix sockets
authored
177 {ok, Sockfd} = fdopen(Pipe),
cfafb55 @msantos Clean up local socket temp directory
authored
178 Cmd = make_args(Port, Options),
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
179 case os:cmd(Cmd) of
7a64070 @msantos Return POSIX atom on error from forked setuid bin
authored
180 "0" ->
560517c @msantos Communicate with Unix sockets
authored
181 FD = fdget(Sockfd),
cfafb55 @msantos Clean up local socket temp directory
authored
182 cleanup(Sockfd, Pipe, Options),
00214e3 @msantos Make the NIF stateless!
authored
183 FD;
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
184 Error ->
cfafb55 @msantos Clean up local socket temp directory
authored
185 cleanup(Sockfd, Pipe, Options),
7a64070 @msantos Return POSIX atom on error from forked setuid bin
authored
186 {error, errno_id(list_to_integer(Error))}
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
187 end.
188
cfafb55 @msantos Clean up local socket temp directory
authored
189 cleanup(Sockfd, Pipe, Options) ->
fe09a25 @msantos Remove close/2; add listen/1
authored
190 close(Sockfd),
191 ok = file:delete(Pipe),
cfafb55 @msantos Clean up local socket temp directory
authored
192 case proplists:get_value(tmpdir, Options) of
193 false ->
194 ok;
195 Path ->
edef854 @msantos Rename the mktmp utility module
authored
196 procket_mktmp:close(Path)
cfafb55 @msantos Clean up local socket temp directory
authored
197 end.
198
560517c @msantos Communicate with Unix sockets
authored
199 fdopen(Path) when is_list(Path) ->
200 fdopen(list_to_binary(Path));
201 fdopen(Path) when is_binary(Path), byte_size(Path) < ?UNIX_PATH_MAX ->
202 {ok, Socket} = socket(?PF_LOCAL, ?SOCK_STREAM, 0),
6dcac8f @msantos Fix sockaddr on BSD
authored
203 Len = byte_size(Path),
204 Sun = <<(sockaddr_common(?PF_LOCAL, Len))/binary,
205 Path/binary,
206 0:((unix_path_max()-Len)*8)
207 >>,
560517c @msantos Communicate with Unix sockets
authored
208 ok = bind(Socket, Sun),
209 ok = listen(Socket, ?BACKLOG),
210 {ok, Socket}.
211
212 fdget(Socket) ->
213 {ok, S} = accept(Socket),
214 fdrecv(S).
215
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
216 make_args(Port, Options) ->
5407f2b @msantos Re-add support for binding to a specific IP
authored
217 Args = reorder_args(Port, Options),
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
218 proplists:get_value(progname, Options, "sudo " ++ progname()) ++ " " ++
5407f2b @msantos Re-add support for binding to a specific IP
authored
219 string:join([ get_switch(Arg) || Arg <- Args ], " ") ++
6b0ebc8 @msantos sh printf is portable
authored
220 " > /dev/null 2>&1; printf $?".
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
221
5407f2b @msantos Re-add support for binding to a specific IP
authored
222 reorder_args(Port, Options) ->
223 NewOpts = case proplists:lookup(ip, Options) of
224 none ->
225 Options;
226 IP ->
227 proplists:delete(ip, Options) ++ [IP]
228 end,
229 [{port, Port}] ++ NewOpts.
230
6a0b92f @msantos Socket args can be either atoms or integers
authored
231 get_switch({pipe, Arg}) ->
721f6a5 @msantos Support IPv6 sockets
authored
232 "-u " ++ Arg;
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
233
6a0b92f @msantos Socket args can be either atoms or integers
authored
234 get_switch({protocol, Proto}) when is_atom(Proto) ->
235 get_switch({protocol, protocol(Proto)});
236 get_switch({protocol, Proto}) when is_integer(Proto) ->
237 "-P " ++ integer_to_list(Proto);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
238
6a0b92f @msantos Socket args can be either atoms or integers
authored
239 get_switch({type, Type}) when is_atom(Type) ->
240 get_switch({type, type(Type)});
241 get_switch({type, Type}) when is_integer(Type) ->
242 "-T " ++ integer_to_list(Type);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
243
6a0b92f @msantos Socket args can be either atoms or integers
authored
244 get_switch({family, Family}) when is_atom(Family) ->
245 get_switch({family, family(Family)});
246 get_switch({family, Family}) when is_integer(Family) ->
247 "-F " ++ integer_to_list(Family);
96f8a1e @msantos Allow arbitrary socket family, type and protocol
authored
248
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
249 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
250 get_switch({ip, Arg}) when is_list(Arg) -> Arg;
251
721f6a5 @msantos Support IPv6 sockets
authored
252 get_switch({port, Port}) when is_integer(Port) ->
253 "-p " ++ integer_to_list(Port);
254
315b8cf Add interface name sanitation
klaar authored
255 get_switch({interface, Name}) when is_list(Name) ->
58210fa @msantos Compile errors
authored
256 case is_interface(Name) of
bd91116 @msantos Allow setuid open of any character device
authored
257 true ->
258 "-I " ++ Name;
259 false ->
260 throw({bad_interface, Name})
261 end;
fa1f8e4 @msantos Simplify option handling
authored
262
bd91116 @msantos Allow setuid open of any character device
authored
263 get_switch({dev, Dev}) when is_list(Dev) ->
264 case is_device(Dev) of
265 true ->
266 "-d " ++ Dev;
267 false ->
268 throw({bad_device, Dev})
269 end;
dd34004 @msantos Initial support for BPF on BSDs
authored
270
fa1f8e4 @msantos Simplify option handling
authored
271 % Ignore any other arguments
272 get_switch(_Arg) ->
273 "".
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
274
bd91116 @msantos Allow setuid open of any character device
authored
275 is_interface(Name) when is_list(Name) ->
276 % An interface name is expected to consist of a reasonable
277 % subset of all characters, use a whitelist and extend it if needed
278 Name == [C || C <- Name, (((C bor 32) >= $a) and ((C bor 32) =< $z))
279 or ((C >= $0) and (C =< $9)) or (C == $.)].
280
281 is_device(Name) when is_list(Name) ->
58210fa @msantos Compile errors
authored
282 Name == [C || C <- Name, ((C >= $a) and (C =< $z))
31e7592 @msantos Allow '/' in character device name
authored
283 or ((C >= $0) and (C =< $9) or (C == $/))].
bd91116 @msantos Allow setuid open of any character device
authored
284
5bbc1bb @msantos Dynamically create path to procket executable too.
authored
285 progname() ->
286 filename:join([
287 filename:dirname(code:which(?MODULE)),
288 "..",
289 "priv",
290 ?MODULE
291 ]).
292
441fde8 @msantos Interface for requesting privileged sockets in Erlang.
authored
293
6a0b92f @msantos Socket args can be either atoms or integers
authored
294 %% Protocol family (aka domain)
295 family(unspec) -> 0;
296 family(inet) -> 2;
b6af970 @msantos portability: assign value for inet6 based on OS
authored
297 family(inet6) ->
298 case os:type() of
299 {unix, linux} -> 10;
300 {unix, darwin} -> 30;
301 {unix, freebsd} -> 28
302 end;
6a0b92f @msantos Socket args can be either atoms or integers
authored
303 family(packet) -> 17;
304 family(Proto) when Proto == local; Proto == unix; Proto == file -> 1;
305
306 family(0) -> unspec;
307 family(1) -> unix;
308 family(2) -> inet;
b6af970 @msantos portability: assign value for inet6 based on OS
authored
309 family(10) ->
310 case os:type() of
311 {unix, linux} -> inet6;
312 {unix, _} -> ccitt
313 end;
314 family(17) ->
315 case os:type() of
316 {unix, linux} -> packet;
317 {unix, _} -> route
318 end;
319 family(28) ->
320 case os:type() of
321 % linux: not defined
322 {unix, freebsd} -> inet6;
323 {unix, darwin} -> isdn
324 end;
325 family(30) ->
326 case os:type() of
327 {unix, linux} -> tipc;
328 {unix, freebsd} -> atm;
329 {unix, darwin} -> inet6
330 end.
6a0b92f @msantos Socket args can be either atoms or integers
authored
331
332
333 %% Socket type
334 type(stream) -> 1;
335 type(dgram) -> 2;
336 type(raw) -> 3;
337
338 type(1) -> stream;
339 type(2) -> dgram;
340 type(3) -> raw.
341
342
343 % Select a protocol within the family (0 means use the default
344 % protocol in the family)
1a47c1d @msantos Correct value for IPPROTO_RAW
authored
345 protocol(ip) -> 0;
6a0b92f @msantos Socket args can be either atoms or integers
authored
346 protocol(icmp) -> 1;
347 protocol(tcp) -> 6;
348 protocol(udp) -> 17;
1a47c1d @msantos Correct value for IPPROTO_RAW
authored
349 protocol(raw) -> 255;
6a0b92f @msantos Socket args can be either atoms or integers
authored
350
1a47c1d @msantos Correct value for IPPROTO_RAW
authored
351 protocol(0) -> ip;
6a0b92f @msantos Socket args can be either atoms or integers
authored
352 protocol(1) -> icmp;
353 protocol(6) -> tcp;
1a47c1d @msantos Correct value for IPPROTO_RAW
authored
354 protocol(255) -> raw.
6a0b92f @msantos Socket args can be either atoms or integers
authored
355
d0508b2 @msantos Convert socket parameters to integers
authored
356 maybe_atom(_Type, Value) when is_integer(Value) -> Value;
357 maybe_atom(family, Value) -> family(Value);
358 maybe_atom(type, Value) -> type(Value);
359 maybe_atom(protocol, Value) -> protocol(Value).
360
6a0b92f @msantos Socket args can be either atoms or integers
authored
361
6dcac8f @msantos Fix sockaddr on BSD
authored
362 %%
363 %% Portability
364 %%
365
366 % struct sockaddr
367 sockaddr_common(Family, Length) ->
368 case erlang:system_info(os_type) of
369 {unix,BSD} when BSD == darwin;
370 BSD == openbsd;
371 BSD == netbsd;
372 BSD == freebsd ->
373 <<Length:8, Family:8>>;
374 {unix,_} ->
375 <<Family:16/native>>
376 end.
377
378 % UNIX_PATH_MAX
379 unix_path_max() ->
380 case erlang:system_info(os_type) of
381 {unix,BSD} when BSD == darwin;
382 BSD == openbsd;
383 BSD == netbsd;
384 BSD == freebsd -> 104;
385 {unix,_} -> 108
386 end.
387
dd34004 @msantos Initial support for BPF on BSDs
authored
388
389 ntohl(<<I:32>>) ->
390 ntohl(I);
391 ntohl(I) when is_integer(I) ->
392 <<N:32>> = <<I:32/native>>,
393 N.
394 ntohs(<<I:32>>) ->
395 ntohs(I);
396 ntohs(I) when is_integer(I) ->
397 <<N:16>> = <<I:16/native>>,
398 N.
Something went wrong with that request. Please try again.