2 # Common functions for accessing inetd or xinetd
4 BEGIN { push(@INC, ".."); };
8 if ($config{'stunnel_path'} =~ /([^\/]+)$/) {
9 $stunnel_shortname = $1;
11 $webmin_pem = "$config_directory/miniserv.pem";
13 if (&foreign_check("inetd")) {
14 &foreign_require("inetd", "inetd-lib.pl");
17 if (&foreign_check("xinetd")) {
18 &foreign_require("xinetd", "xinetd-lib.pl");
23 # Search the inetd and xinetd configurations for stunnel lines
28 # List tunnels from inetd.conf
30 foreach $s (&foreign_call("inetd", "list_services")) {
31 $portmap{$s->[1]} = $s;
32 foreach $a (split(/\s+/, $s->[4])) {
36 foreach $i (&foreign_call("inetd", "list_inets")) {
37 if ($i->[8] eq $config{'stunnel_path'} ||
38 $i->[8] eq $stunnel_shortname) {
39 push(@rv, { 'type' => 'inetd',
41 'port' => $portmap{$i->[3]}->[2],
46 'index' => scalar(@rv),
53 # List tunnels from xinetd.conf
54 foreach $x (&foreign_call("xinetd", "get_xinetd_config")) {
55 next if ($x->{'name'} ne 'service');
56 local $q = $x->{'quick'};
57 if ($q->{'server'}->[0] eq $config{'stunnel_path'} ||
58 $q->{'server'}->[0] eq $stunnel_shortname) {
59 push(@rv, { 'type' => 'xinetd',
60 'name' => $x->{'value'},
61 'port' => &xinet_port($x),
62 'active' => $q->{'disable'}->[0] ne 'yes',
63 'command' => $q->{'server'}->[0],
64 'args' => join(" ", $q->{'server'}->[0],
65 @{$q->{'server_args'}}),
66 'index' => scalar(@rv),
67 'file' => $x->{'file'},
68 'xindex' => $x->{'index'} } );
75 # create_tunnel(&tunnel)
78 local ($pclash, $nclash);
80 # Check for xinet clash
81 foreach $x (&foreign_call("xinetd", "get_xinetd_config")) {
82 next if ($x->{'name'} ne 'service');
83 local $q = $x->{'quick'};
84 &error(&text('save_exinetd', $_[0]->{'name'}))
85 if ($x->{'value'} eq $_[0]->{'name'});
86 &error(&text('save_export', $_[0]->{'port'}, $x->{'value'}))
87 if (&xinet_port($x) == $_[0]->{'port'});
91 # Check if there is already a service for the port or with the name
92 ($pclash, $nclash) = &find_clashes($_[0]->{'name'}, $_[0]->{'port'});
93 if (!$pclash && $nclash) {
94 # The name is taken, but on a different port
95 &error(&text('save_enclash', $nclash->[2], $nclash->[1]));
97 local $iname = $pclash ? $pclash->[1] : $_[0]->{'name'};
99 # Check if there is an inetd entry on the name
100 foreach $i (&foreign_call("inetd", "list_inets")) {
101 &error(&text('save_einetd', $iname))
102 if ($i->[3] eq $iname);
106 local $addto = $_[0]->{'type'} ? $_[0]->{'type'} :
107 $has_xinetd ? "xinetd" : "inetd";
108 if ($addto eq 'xinetd') {
109 # Just add to xinetd.conf with a custom name and port
110 local $xinet = { 'name' => 'service',
111 'values' => [ $_[0]->{'name'} ] };
112 &foreign_call("xinetd", "set_member_value", $xinet, "port",
114 &foreign_call("xinetd", "set_member_value", $xinet, "socket_type",
116 &foreign_call("xinetd", "set_member_value", $xinet, "protocol",
118 &foreign_call("xinetd", "set_member_value", $xinet, "user",
120 &foreign_call("xinetd", "set_member_value", $xinet, "wait",
122 &foreign_call("xinetd", "set_member_value", $xinet, "disable",
123 $_[0]->{'active'} ? "no" : "yes");
124 &foreign_call("xinetd", "set_member_value", $xinet, "type",
126 &foreign_call("xinetd", "set_member_value", $xinet, "server",
128 local $args = $_[0]->{'args'};
129 $args =~ s/^\S+\s+//;
130 &foreign_call("xinetd", "set_member_value", $xinet, "server_args",
132 &foreign_call("xinetd", "create_xinet", $xinet);
134 elsif ($addto eq 'inetd') {
136 # Use existing /etc/services entry
137 $_[0]->{'name'} = $pclash->[1];
140 # Create /etc/services entry
141 &foreign_call("inetd", "create_service", $_[0]->{'name'},
142 $_[0]->{'port'}, "tcp", undef);
144 # Create inetd.conf entry
145 &foreign_call("inetd", "create_inet", $_[0]->{'active'},
146 $_[0]->{'name'}, "stream", "tcp", "nowait", "root",
147 $_[0]->{'command'}, $_[0]->{'args'});
151 # delete_stunnel(&tunnel)
154 if ($_[0]->{'type'} eq 'inetd') {
155 # Delete from inetd.conf
156 &foreign_call("inetd", "delete_inet", $_[0]->{'line'}, $_[0]->{'file'});
157 if ($_[0]->{'port'} >= 1024) {
158 # Delete the /etc/services entry as well
159 local ($oserv) = &find_clashes($_[0]->{'name'},
161 &foreign_call("inetd", "delete_service", $oserv->[0]);
164 elsif ($_[0]->{'type'} eq 'xinetd') {
166 local @xinets = &foreign_call("xinetd", "get_xinetd_config");
167 local $xinet = $xinets[$_[0]->{'xindex'}];
168 &foreign_call("xinetd", "delete_xinet", $xinet);
172 # modify_stunnel(&oldtunnel, &newtunnel)
175 if ($_[0]->{'type'} eq 'inetd') {
176 # Check if the name or port has changed
177 local ($pclash, $nclash) =
178 &find_clashes($_[1]->{'name'}, $_[1]->{'port'});
179 local ($oserv) = &find_clashes($_[0]->{'name'}, $_[0]->{'port'});
180 if ($_[0]->{'name'} ne $_[1]->{'name'}) {
181 # The name has changed
183 &error(&text('save_enclash', $nclash->[2],
187 if ($_[0]->{'port'} != $_[1]->{'port'}) {
188 # The port has changed ..
190 &error(&text('save_epclash', $_[1]->{'port'},
194 &foreign_call("inetd", "modify_service", $oserv->[0],
195 $_[1]->{'name'}, $_[1]->{'port'}, $oserv->[3],
199 local @inets = &foreign_call("inetd", "list_inets");
200 local ($oi) = grep { $_->[0] eq $_[0]->{'line'} &&
201 $_->[10] eq $_[0]->{'file'} } @inets;
202 &foreign_call("inetd", "modify_inet", $oi->[0],
203 $_[1]->{'active'}, $_[1]->{'name'}, $oi->[4],
204 $oi->[5], $oi->[6], $oi->[7], $oi->[8],
205 $_[1]->{'args'}, $oi->[10]);
207 elsif ($_[0]->{'type'} eq 'xinetd') {
208 # Get the old xinetd config
209 local @xinets = &foreign_call("xinetd", "get_xinetd_config");
210 local $xinet = $xinets[$_[0]->{'xindex'}];
212 # Check for name clash
213 if ($_[0]->{'name'} ne $_[1]->{'name'}) {
214 foreach $x (@xinets) {
215 next if ($x->{'name'} ne 'service');
216 &error(&text('save_exinetd', $_[1]->{'name'}))
217 if ($x->{'value'} eq $_[1]->{'name'});
221 # Check for port clash
222 if ($_[0]->{'port'} != $_[1]->{'port'}) {
223 foreach $x (@xinets) {
224 next if ($x->{'name'} ne 'service');
225 &error(&text('save_export', $_[1]->{'port'},
227 if (&xinet_port($x) == $_[1]->{'port'});
231 # If name or port has changed, convert to an UNLISTED service
232 if ($_[0]->{'name'} ne $_[1]->{'name'} ||
233 $_[0]->{'port'} != $_[1]->{'port'}) {
234 $xinet->{'values'} = [ $_[1]->{'name'} ];
235 &foreign_call("xinetd", "set_member_value", $xinet, "port",
237 &foreign_call("xinetd", "set_member_value", $xinet, "type",
241 &foreign_call("xinetd", "set_member_value", $xinet, "disable",
242 $_[1]->{'active'} ? "no" : "yes");
243 &foreign_call("xinetd", "set_member_value", $xinet, "server",
245 local $args = $_[1]->{'args'};
246 $args =~ s/^\S+\s+//;
247 &foreign_call("xinetd", "set_member_value", $xinet, "server_args",
249 &foreign_call("xinetd", "modify_xinet", $xinet);
253 # find_clashes(name, port)
256 local ($pclash, $nclash);
257 foreach $s (&foreign_call("inetd", "list_services")) {
258 local @aliases = split(/\s+/, $s->[4]);
259 $pclash = $s if ($s->[2] == $_[1]);
260 $nclash = $s if ($s->[1] eq $_[0] || &indexof($_[0], @aliases) >= 0);
262 return ($pclash, $nclash);
268 local $q = $_[0]->{'quick'};
269 local $p = $q->{'port'};
270 return $p->[0] if ($p);
271 local @s = getservbyname($_[0]->{'value'}, $q->{'protocol'}->[0]);
276 # Lock the file to which new tunnels will be added
280 local %iconfig = &foreign_config("xinetd");
281 &lock_file($iconfig{'xinetd_conf'});
284 local %iconfig = &foreign_config("inetd");
285 &lock_file($iconfig{'inetd_conf_file'});
289 # get_stunnel_version(&out)
290 sub get_stunnel_version
292 local $out = `$config{'stunnel_path'} -V 2>&1`;
294 $out = `$config{'stunnel_path'} -version 2>&1`;
297 return $out =~ /stunnel\s+(\S+)/ ? $1 : undef;
300 # get_stunnel_config(file)
301 # Returns an array of stunnel configuration sections, each of which is a hash
302 # reference containing the actual settings
303 sub get_stunnel_config
305 local (@rv, $service);
306 push(@rv, $service = { 'line' => 0, 'eline' => 0, 'values' => { } });
312 if (/^\s*\[(.*)\]/) {
313 push(@rv, $service = { 'name' => $1,
318 elsif (/^\s*(\S+)\s*=\s*(.*)/) {
319 $service->{'eline'} = $lnum;
320 $service->{'values'}->{lc($1)} = $2;
328 # create_stunnel_service(&service, file)
329 # Creates a service in an stunnel config file
330 sub create_stunnel_service
332 local $lref = &read_file_lines($_[1]);
333 push(@$lref, &stunnel_lines($_[0]));
337 # modify_stunnel_service(&service, file)
338 # Modifies an existing service in an stunnel config file
339 sub modify_stunnel_service
341 local $lref = &read_file_lines($_[1]);
342 splice(@$lref, $_[0]->{'line'}, $_[0]->{'eline'} - $_[0]->{'line'} + 1,
343 &stunnel_lines($_[0]));
347 # stunnel_lines(&service)
351 push(@rv, "[$_[0]->{'name'}]") if ($_[0]->{'name'});
352 foreach $k (keys %{$_[0]->{'values'}}) {
353 push(@rv, $k."=".$_[0]->{'values'}->{$k});
358 # apply_configuration()
359 # Apply the inetd and/or xinetd configuration
360 sub apply_configuration
363 local %iconfig = &foreign_config("inetd");
364 &system_logged("$iconfig{'restart_command'} >/dev/null 2>&1 </dev/null");
367 local %xconfig = &foreign_config("xinetd");
369 if (open(PID, $xconfig{'pid_file'})) {