@@ -34,6 +34,51 @@ type address =
34
34
35
35
(* console is listening on a Unix domain socket *)
36
36
37
+ (* This module limits VNC console sessions to at most one per VM/host.
38
+ Depending on configuration, either unlimited connections are allowed,
39
+ or only a single active connection per VM/host is allowed. *)
40
+ module Connection_limit = struct
41
+ module VMSet = Set. Make (String )
42
+
43
+ let active_connections : VMSet.t ref = ref VMSet. empty
44
+
45
+ let mutex = Mutex. create ()
46
+
47
+ let add vm_id =
48
+ Mutex. lock mutex ;
49
+ active_connections := VMSet. add vm_id ! active_connections ;
50
+ Mutex. unlock mutex
51
+
52
+ let remove vm_id =
53
+ Mutex. lock mutex ;
54
+ active_connections := VMSet. remove vm_id ! active_connections ;
55
+ Mutex. unlock mutex
56
+
57
+ let exists vm_id =
58
+ Mutex. lock mutex ;
59
+ let present = VMSet. mem vm_id ! active_connections in
60
+ Mutex. unlock mutex ; present
61
+
62
+ let can_add vm_id limit_console_sessions =
63
+ if not limit_console_sessions then
64
+ true
65
+ else
66
+ not (exists vm_id)
67
+ end
68
+
69
+ let connection_limit_reached __context vm_id =
70
+ let pool = Helpers. get_pool ~__context in
71
+ let limit_console_sessions =
72
+ Db.Pool. get_limit_console_sessions ~__context ~self: pool
73
+ in
74
+ if not (Connection_limit. can_add vm_id limit_console_sessions) then (
75
+ debug
76
+ " Console session limit reached: only one active session allowed for VM %s"
77
+ vm_id ;
78
+ true
79
+ ) else
80
+ false
81
+
37
82
let string_of_address = function
38
83
| Port x ->
39
84
" localhost:" ^ string_of_int x
@@ -84,72 +129,88 @@ let address_of_console __context console : address option =
84
129
) ;
85
130
address_option
86
131
87
- let real_proxy __context _ _ vnc_port s =
132
+ let real_proxy __context console _ _ vnc_port s =
88
133
try
89
- Http_svr. headers s (Http. http_200_ok () ) ;
90
- let vnc_sock =
91
- match vnc_port with
92
- | Port x ->
93
- Unixext. open_connection_fd " 127.0.0.1" x
94
- | Path x ->
95
- Unixext. open_connection_unix_fd x
96
- in
97
- (* Unixext.proxy closes fds itself so we must dup here *)
98
- let s' = Unix. dup s in
99
- debug " Connected; running proxy (between fds: %d and %d)"
100
- (Unixext. int_of_file_descr vnc_sock)
101
- (Unixext. int_of_file_descr s') ;
102
- Unixext. proxy vnc_sock s' ;
103
- debug " Proxy exited"
134
+ let vm = Db.Console. get_VM ~__context ~self: console in
135
+ let vm_id = Ref. string_of vm in
136
+ if connection_limit_reached __context vm_id then
137
+ Http_svr. headers s (Http. http_503_service_unavailable () )
138
+ else (
139
+ Http_svr. headers s (Http. http_200_ok () ) ;
140
+ let vnc_sock =
141
+ match vnc_port with
142
+ | Port x ->
143
+ Unixext. open_connection_fd " 127.0.0.1" x
144
+ | Path x ->
145
+ Unixext. open_connection_unix_fd x
146
+ in
147
+ (* Unixext.proxy closes fds itself so we must dup here *)
148
+ let s' = Unix. dup s in
149
+ debug " Connected; running proxy (between fds: %d and %d)"
150
+ (Unixext. int_of_file_descr vnc_sock)
151
+ (Unixext. int_of_file_descr s') ;
152
+ Connection_limit. add vm_id ;
153
+ Unixext. proxy vnc_sock s' ;
154
+ Connection_limit. remove vm_id ;
155
+ debug " Proxy exited"
156
+ )
104
157
with exn -> debug " error: %s" (ExnHelper. string_of_exn exn )
105
158
106
- let ws_proxy __context req protocol address s =
107
- let addr = match address with Port p -> string_of_int p | Path p -> p in
108
- let protocol =
109
- match protocol with `rfb -> " rfb" | `vt100 -> " vt100" | `rdp -> " rdp"
110
- in
111
- let real_path = Filename. concat " /var/lib/xcp" " websockproxy" in
112
- let sock =
113
- try Some (Fecomms. open_unix_domain_sock_client real_path)
114
- with e ->
115
- debug " Error connecting to wsproxy (%s)" (Printexc. to_string e) ;
116
- Http_svr. headers s (Http. http_501_method_not_implemented () ) ;
117
- None
159
+ let ws_proxy __context _ req protocol address s =
160
+ let pool = Helpers. get_pool ~__context in
161
+ let limit_console_sessions =
162
+ Db.Pool. get_limit_console_sessions ~__context ~self: pool
118
163
in
119
- (* Ensure we always close the socket *)
120
- finally
121
- (fun () ->
122
- let upgrade_successful =
123
- Option. map
124
- (fun sock ->
125
- try
126
- let result = (sock, Some (Ws_helpers. upgrade req s)) in
127
- result
128
- with _ -> (sock, None )
129
- )
130
- sock
131
- in
132
- Option. iter
133
- (function
134
- | sock , Some ty ->
135
- let wsprotocol =
136
- match ty with
137
- | Ws_helpers. Hixie76 ->
138
- " hixie76"
139
- | Ws_helpers. Hybi10 ->
140
- " hybi10"
141
- in
142
- let message =
143
- Printf. sprintf " %s:%s:%s" wsprotocol protocol addr
144
- in
145
- let len = String. length message in
146
- ignore (Unixext. send_fd_substring sock message 0 len [] s)
147
- | _ , None ->
148
- Http_svr. headers s (Http. http_501_method_not_implemented () )
149
- )
150
- upgrade_successful
151
- )
152
- (fun () -> Option. iter (fun sock -> Unix. close sock) sock)
164
+ (* Disable connection via websocket if the limit is set *)
165
+ if limit_console_sessions = true then
166
+ Http_svr. headers s (Http. http_503_service_unavailable () )
167
+ else
168
+ let addr = match address with Port p -> string_of_int p | Path p -> p in
169
+ let protocol =
170
+ match protocol with `rfb -> " rfb" | `vt100 -> " vt100" | `rdp -> " rdp"
171
+ in
172
+ let real_path = Filename. concat " /var/lib/xcp" " websockproxy" in
173
+ let sock =
174
+ try Some (Fecomms. open_unix_domain_sock_client real_path)
175
+ with e ->
176
+ debug " Error connecting to wsproxy (%s)" (Printexc. to_string e) ;
177
+ Http_svr. headers s (Http. http_501_method_not_implemented () ) ;
178
+ None
179
+ in
180
+ (* Ensure we always close the socket *)
181
+ finally
182
+ (fun () ->
183
+ let upgrade_successful =
184
+ Option. map
185
+ (fun sock ->
186
+ try
187
+ let result = (sock, Some (Ws_helpers. upgrade req s)) in
188
+ result
189
+ with _ -> (sock, None )
190
+ )
191
+ sock
192
+ in
193
+ Option. iter
194
+ (function
195
+ | sock , Some ty ->
196
+ let wsprotocol =
197
+ match ty with
198
+ | Ws_helpers. Hixie76 ->
199
+ " hixie76"
200
+ | Ws_helpers. Hybi10 ->
201
+ " hybi10"
202
+ in
203
+ let message =
204
+ Printf. sprintf " %s:%s:%s" wsprotocol protocol addr
205
+ in
206
+ let len = String. length message in
207
+ ignore (Unixext. send_fd_substring sock message 0 len [] s)
208
+ | _ , None ->
209
+ Http_svr. headers s (Http. http_501_method_not_implemented () )
210
+ )
211
+ upgrade_successful
212
+ )
213
+ (fun () -> Option. iter (fun sock -> Unix. close sock) sock)
153
214
154
215
let default_console_of_vm ~__context ~self =
155
216
try
@@ -247,7 +308,7 @@ let handler proxy_fn (req : Request.t) s _ =
247
308
check_vm_is_running_here __context console ;
248
309
match address_of_console __context console with
249
310
| Some vnc_port ->
250
- proxy_fn __context req protocol vnc_port s
311
+ proxy_fn __context console req protocol vnc_port s
251
312
| None ->
252
313
Http_svr. headers s (Http. http_404_missing () )
253
314
)
0 commit comments