-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.js
182 lines (123 loc) · 3.73 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* Module dependencies
*/
var debug = require('debug')('radclient');
var radius = require('radius');
var dgram = require('dgram');
var fs = require('fs');
var path = require('path');
var util = require('util');
const RADIUS_PORT = 1812;
const DEFAULT_RETRIES = 3;
const DEFAULT_TIMEOUT = 2500;
/**
* Send packet to RADIUS server with options
*
* @param {object} packet
* @param {object} options
* @callback onSuccess
*/
module.exports = function(packet, options, callback) {
if (typeof options.host !== 'string') {
throw new TypeError('option `host` must be a string');
}
if (typeof callback !== 'function') {
throw new TypeError('option `callback` must be a function');
}
if (options.retries && typeof options.retries !== 'number') {
throw new TypeError('option `retries` must be a number');
}
if (options.timeout && typeof options.timeout !== 'number') {
throw new TypeError('option `timeout` must be a number');
}
if (options.dictionaries && typeof options.dictionaries !== 'string') {
throw new TypeError('option `dictionaries` must be a string');
}
// Handling retries
var retries = +options.retries || DEFAULT_RETRIES;
var check = function(err, response) {
if (err && err.retryable && --retries) {
debug('Retries left: [%s]', retries);
send(packet, options, check);
} else {
if (!retries) err = new Error('Maximum retries exceeded');
callback(err, response);
}
};
send(packet, options, check);
};
function send(packet, options, callback) {
var localPort = +options.localPort || getEphemeralPort();
var port = +options.port || RADIUS_PORT;
var host = options.host;
var ms = +options.timeout || DEFAULT_TIMEOUT;
// add dictionaries (if needed)
if (options.dictionaries) {
radius.add_dictionary(options.dictionaries);
}
// Init timeout
var t = setTimeout(function() {
if (socket) socket.close();
debug('ETIMEDOUT');
var err = new Error('ETIMEDOUT');
err.retryable = true;
return callback(err);
}, ms);
var socket = dgram.createSocket('udp4');
// Generic error handler
socket.on('error', function(err) {
return callback(err);
});
// Message handler
socket.on('message', function(msg, rinfo) {
debug('Received RADIUS response', rinfo);
// Closing socket since we don't need it anymore
socket.close();
// Decode and verify RADIUS response
var response = null;
try {
response = radius.decode({ packet: msg, secret: options.secret });
} catch(e) {
return callback(e);
}
debug('Decoded RADIUS response', response);
var isValid = radius.verify_response({
response: msg,
request: encPacket,
secret: packet.secret
});
debug('Response is %s', isValid ? 'valid' : 'invalid');
if (!isValid) return callback(new Error('RADIUS response is invalid'));
callback(null, response);
clearTimeout(t);
});
// Send packet after binding is done (listening)
socket.on('listening', function() {
debug('Socket binded on port %s', localPort);
this.send(encPacket, 0, encPacket.length, port, host, function(err, bytes) {
if (err) return callback(err);
debug('Sent %s bytes to %s:%s', bytes, host, port);
});
});
// Encoding packet
debug('Encoding packet %j', packet);
try {
var encPacket = radius.encode(packet);
} catch(e) {
debug('Failed to encode packet', e);
return callback(e);
}
// Bind on localPort
socket.bind(localPort);
}
/**
* Get an Ephemeral Port
* http://en.wikipedia.org/wiki/Ephemeral_port
*
* @return {number}
*/
function getEphemeralPort() {
var begin = 49152;
var end = 65535;
return Math.floor(Math.random() * (end - begin + 1 ) + begin);
}