Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/node/ports.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as net from 'net';
7
8
/**
9
* Given a start point and a max number of retries, will find a port that
10
* is openable. Will return 0 in case no free port can be found.
11
*/
12
export function findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise<number> {
13
let done = false;
14
15
return new Promise(resolve => {
16
const timeoutHandle = setTimeout(() => {
17
if (!done) {
18
done = true;
19
return resolve(0);
20
}
21
}, timeout);
22
23
doFindFreePort(startPort, giveUpAfter, stride, (port) => {
24
if (!done) {
25
done = true;
26
clearTimeout(timeoutHandle);
27
return resolve(port);
28
}
29
});
30
});
31
}
32
33
function doFindFreePort(startPort: number, giveUpAfter: number, stride: number, clb: (port: number) => void): void {
34
if (giveUpAfter === 0) {
35
return clb(0);
36
}
37
38
const client = new net.Socket();
39
40
// If we can connect to the port it means the port is already taken so we continue searching
41
client.once('connect', () => {
42
dispose(client);
43
44
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
45
});
46
47
client.once('data', () => {
48
// this listener is required since node.js 8.x
49
});
50
51
client.once('error', (err: Error & { code?: string }) => {
52
dispose(client);
53
54
// If we receive any non ECONNREFUSED error, it means the port is used but we cannot connect
55
if (err.code !== 'ECONNREFUSED') {
56
return doFindFreePort(startPort + stride, giveUpAfter - 1, stride, clb);
57
}
58
59
// Otherwise it means the port is free to use!
60
return clb(startPort);
61
});
62
63
client.connect(startPort, '127.0.0.1');
64
}
65
66
// Reference: https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main/net/base/port_util.cc#56
67
export const BROWSER_RESTRICTED_PORTS: Record<number, boolean> = {
68
1: true, // tcpmux
69
7: true, // echo
70
9: true, // discard
71
11: true, // systat
72
13: true, // daytime
73
15: true, // netstat
74
17: true, // qotd
75
19: true, // chargen
76
20: true, // ftp data
77
21: true, // ftp access
78
22: true, // ssh
79
23: true, // telnet
80
25: true, // smtp
81
37: true, // time
82
42: true, // name
83
43: true, // nicname
84
53: true, // domain
85
69: true, // tftp
86
77: true, // priv-rjs
87
79: true, // finger
88
87: true, // ttylink
89
95: true, // supdup
90
101: true, // hostriame
91
102: true, // iso-tsap
92
103: true, // gppitnp
93
104: true, // acr-nema
94
109: true, // pop2
95
110: true, // pop3
96
111: true, // sunrpc
97
113: true, // auth
98
115: true, // sftp
99
117: true, // uucp-path
100
119: true, // nntp
101
123: true, // NTP
102
135: true, // loc-srv /epmap
103
137: true, // netbios
104
139: true, // netbios
105
143: true, // imap2
106
161: true, // snmp
107
179: true, // BGP
108
389: true, // ldap
109
427: true, // SLP (Also used by Apple Filing Protocol)
110
465: true, // smtp+ssl
111
512: true, // print / exec
112
513: true, // login
113
514: true, // shell
114
515: true, // printer
115
526: true, // tempo
116
530: true, // courier
117
531: true, // chat
118
532: true, // netnews
119
540: true, // uucp
120
548: true, // AFP (Apple Filing Protocol)
121
554: true, // rtsp
122
556: true, // remotefs
123
563: true, // nntp+ssl
124
587: true, // smtp (rfc6409)
125
601: true, // syslog-conn (rfc3195)
126
636: true, // ldap+ssl
127
989: true, // ftps-data
128
990: true, // ftps
129
993: true, // ldap+ssl
130
995: true, // pop3+ssl
131
1719: true, // h323gatestat
132
1720: true, // h323hostcall
133
1723: true, // pptp
134
2049: true, // nfs
135
3659: true, // apple-sasl / PasswordServer
136
4045: true, // lockd
137
5060: true, // sip
138
5061: true, // sips
139
6000: true, // X11
140
6566: true, // sane-port
141
6665: true, // Alternate IRC [Apple addition]
142
6666: true, // Alternate IRC [Apple addition]
143
6667: true, // Standard IRC [Apple addition]
144
6668: true, // Alternate IRC [Apple addition]
145
6669: true, // Alternate IRC [Apple addition]
146
6697: true, // IRC + TLS
147
10080: true // Amanda
148
};
149
150
export function isPortFree(port: number, timeout: number): Promise<boolean> {
151
return findFreePortFaster(port, 0, timeout).then(port => port !== 0);
152
}
153
154
/**
155
* Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener.
156
*/
157
export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number, hostname: string = '127.0.0.1'): Promise<number> {
158
let resolved: boolean = false;
159
let timeoutHandle: Timeout | undefined = undefined;
160
let countTried: number = 1;
161
const server = net.createServer({ pauseOnConnect: true });
162
function doResolve(port: number, resolve: (port: number) => void) {
163
if (!resolved) {
164
resolved = true;
165
server.removeAllListeners();
166
server.close();
167
if (timeoutHandle) {
168
clearTimeout(timeoutHandle);
169
}
170
resolve(port);
171
}
172
}
173
return new Promise<number>(resolve => {
174
timeoutHandle = setTimeout(() => {
175
doResolve(0, resolve);
176
}, timeout);
177
178
server.on('listening', () => {
179
doResolve(startPort, resolve);
180
});
181
server.on('error', err => {
182
if (err && ((<any>err).code === 'EADDRINUSE' || (<any>err).code === 'EACCES') && (countTried < giveUpAfter)) {
183
startPort++;
184
countTried++;
185
server.listen(startPort, hostname);
186
} else {
187
doResolve(0, resolve);
188
}
189
});
190
server.on('close', () => {
191
doResolve(0, resolve);
192
});
193
server.listen(startPort, hostname);
194
});
195
}
196
197
function dispose(socket: net.Socket): void {
198
try {
199
socket.removeAllListeners('connect');
200
socket.removeAllListeners('error');
201
socket.end();
202
socket.destroy();
203
socket.unref();
204
} catch (error) {
205
console.error(error); // otherwise this error would get lost in the callback chain
206
}
207
}
208
209