@routr/connect 2.15.4 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/service.d.ts +15 -0
- package/dist/service.js +74 -41
- package/dist/utils.d.ts +13 -0
- package/dist/utils.js +15 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
<a href="https://
|
|
1
|
+
<a href="https://docs.routr.io/guides/develop-with-dev-containers"> <img src="https://img.shields.io/badge/Contribute%20with-Dev%20Containers-908a85?logo=docker" alt="Contribute with Dev Containers" />
|
|
2
2
|
|
|
3
3
|
This module is part of the [Routr](https://routr.io) project. By itself, it does not do much. It is intended to be used as a dependency for other modules. For more information about the project, please visit [https://github.com/fonoster/routr](https://github.com/fonoster/routr).
|
package/dist/service.d.ts
CHANGED
|
@@ -1,2 +1,17 @@
|
|
|
1
1
|
import { ConnectProcessorConfig } from "./types";
|
|
2
|
+
import { MessageRequest } from "@routr/common";
|
|
3
|
+
import { Response } from "@routr/processor";
|
|
4
|
+
import { CommonConnect as CC } from "@routr/common";
|
|
5
|
+
import { ILocationService } from "@routr/location";
|
|
6
|
+
/**
|
|
7
|
+
* Builds the connect processor's message handler. Exported so the dispatch can be
|
|
8
|
+
* unit tested without a gRPC server.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} deps - the API client and location service the handlers use
|
|
11
|
+
* @return {Function} the handler to pass to Processor#listen
|
|
12
|
+
*/
|
|
13
|
+
export declare const createDispatcher: (deps: {
|
|
14
|
+
apiClient: CC.APIClient;
|
|
15
|
+
location: ILocationService;
|
|
16
|
+
}) => (req: MessageRequest, res: Response) => Promise<void>;
|
|
2
17
|
export default function connectProcessor(config: ConnectProcessorConfig): void;
|
package/dist/service.js
CHANGED
|
@@ -42,6 +42,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
42
42
|
});
|
|
43
43
|
};
|
|
44
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.createDispatcher = void 0;
|
|
45
46
|
exports.default = connectProcessor;
|
|
46
47
|
const common_1 = require("@routr/common");
|
|
47
48
|
const location_1 = require("@routr/location");
|
|
@@ -52,50 +53,82 @@ const logger_1 = require("@fonoster/logger");
|
|
|
52
53
|
const request_1 = require("./handlers/request");
|
|
53
54
|
const registry_1 = require("./handlers/registry");
|
|
54
55
|
const register_1 = require("./handlers/register");
|
|
56
|
+
const utils_1 = require("./utils");
|
|
55
57
|
const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
|
|
58
|
+
/**
|
|
59
|
+
* Forwards a request that belongs to an existing dialog along its route set: strip
|
|
60
|
+
* our own Route, add our Via, and let the EdgePort send it to the next hop or the
|
|
61
|
+
* Request-URI. No routing and no access checks, the dialog was authorized when it
|
|
62
|
+
* was set up. BYE and ACK always took this path; since RFC 3261 section 12.2 makes
|
|
63
|
+
* any request with a To tag an in-dialog request, re-INVITE, UPDATE, INFO, REFER and
|
|
64
|
+
* the NOTIFY that follows a REFER take it too.
|
|
65
|
+
*
|
|
66
|
+
* @param {MessageRequest} req - the in-dialog request
|
|
67
|
+
* @param {Response} res - the response object used to send it on
|
|
68
|
+
* @return {void}
|
|
69
|
+
*/
|
|
70
|
+
const forwardInDialog = (req, res) => res.send((0, function_1.pipe)(req, processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.addSelfViaUsingExternalAddrs, processor_1.Alterations.removeSelfRoutes));
|
|
71
|
+
/**
|
|
72
|
+
* Builds the connect processor's message handler. Exported so the dispatch can be
|
|
73
|
+
* unit tested without a gRPC server.
|
|
74
|
+
*
|
|
75
|
+
* @param {object} deps - the API client and location service the handlers use
|
|
76
|
+
* @return {Function} the handler to pass to Processor#listen
|
|
77
|
+
*/
|
|
78
|
+
const createDispatcher = (deps) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
const { apiClient, location } = deps;
|
|
80
|
+
logger.verbose("connect processor received new request", {
|
|
81
|
+
ref: req.ref,
|
|
82
|
+
method: req.method,
|
|
83
|
+
type: req.message.messageType === common_2.CommonTypes.MessageType.RESPONSE
|
|
84
|
+
? "(response)"
|
|
85
|
+
: "(request)",
|
|
86
|
+
edgePort: req.edgePortRef
|
|
87
|
+
});
|
|
88
|
+
logger.silly(JSON.stringify(req, null, " "));
|
|
89
|
+
// If it is a response simply forwards to uac
|
|
90
|
+
if (processor_1.Helper.isTypeResponse(req)) {
|
|
91
|
+
// Remove the proxy and overwrite the contact with the sender info
|
|
92
|
+
return res.send(
|
|
93
|
+
// NOTE: We should consider making the overwriteContactWithSenderInfo an Agent/Peer level alteration
|
|
94
|
+
// pipe(req, A.overwriteContactWithSenderInfo, A.removeTopVia)
|
|
95
|
+
(0, function_1.pipe)(req, processor_1.Alterations.removeTopVia));
|
|
96
|
+
}
|
|
97
|
+
// REGISTER never carries a To tag and CANCEL keeps its own path (it follows
|
|
98
|
+
// the INVITE it cancels); everything else with a To tag is in a dialog.
|
|
99
|
+
if (req.method !== common_1.Method.REGISTER &&
|
|
100
|
+
req.method !== common_1.Method.CANCEL &&
|
|
101
|
+
(0, utils_1.isInDialog)(req)) {
|
|
102
|
+
return forwardInDialog(req, res);
|
|
103
|
+
}
|
|
104
|
+
switch (req.method) {
|
|
105
|
+
case common_1.Method.PUBLISH:
|
|
106
|
+
case common_1.Method.NOTIFY:
|
|
107
|
+
case common_1.Method.SUBSCRIBE:
|
|
108
|
+
case common_1.Method.MESSAGE:
|
|
109
|
+
res.sendMethodNotAllowed();
|
|
110
|
+
break;
|
|
111
|
+
case common_1.Method.REGISTER:
|
|
112
|
+
if (processor_1.Extensions.getHeaderValue(req, common_2.CommonTypes.ExtraHeader.GATEWAY_AUTH)) {
|
|
113
|
+
(0, registry_1.handleRegistry)(req, res);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
return (0, register_1.handleRegister)(apiClient, location)(req, res);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case common_1.Method.BYE:
|
|
120
|
+
case common_1.Method.ACK:
|
|
121
|
+
forwardInDialog(req, res);
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
return (0, request_1.handleRequest)(location, apiClient)(req, res);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
exports.createDispatcher = createDispatcher;
|
|
56
128
|
// eslint-disable-next-line require-jsdoc
|
|
57
129
|
function connectProcessor(config) {
|
|
58
130
|
const { bindAddr, locationAddr } = config;
|
|
59
131
|
const location = new location_1.LocationClient({ addr: locationAddr });
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
ref: req.ref,
|
|
63
|
-
method: req.method,
|
|
64
|
-
type: req.message.messageType === common_2.CommonTypes.MessageType.RESPONSE
|
|
65
|
-
? "(response)"
|
|
66
|
-
: "(request)",
|
|
67
|
-
edgePort: req.edgePortRef
|
|
68
|
-
});
|
|
69
|
-
logger.silly(JSON.stringify(req, null, " "));
|
|
70
|
-
// If it is a response simply forwards to uac
|
|
71
|
-
if (processor_1.Helper.isTypeResponse(req)) {
|
|
72
|
-
// Remove the proxy and overwrite the contact with the sender info
|
|
73
|
-
return res.send(
|
|
74
|
-
// NOTE: We should consider making the overwriteContactWithSenderInfo an Agent/Peer level alteration
|
|
75
|
-
// pipe(req, A.overwriteContactWithSenderInfo, A.removeTopVia)
|
|
76
|
-
(0, function_1.pipe)(req, processor_1.Alterations.removeTopVia));
|
|
77
|
-
}
|
|
78
|
-
switch (req.method) {
|
|
79
|
-
case common_1.Method.PUBLISH:
|
|
80
|
-
case common_1.Method.NOTIFY:
|
|
81
|
-
case common_1.Method.SUBSCRIBE:
|
|
82
|
-
case common_1.Method.MESSAGE:
|
|
83
|
-
res.sendMethodNotAllowed();
|
|
84
|
-
break;
|
|
85
|
-
case common_1.Method.REGISTER:
|
|
86
|
-
if (processor_1.Extensions.getHeaderValue(req, common_2.CommonTypes.ExtraHeader.GATEWAY_AUTH)) {
|
|
87
|
-
(0, registry_1.handleRegistry)(req, res);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
(0, register_1.handleRegister)(common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }), location)(req, res);
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
93
|
-
case common_1.Method.BYE:
|
|
94
|
-
case common_1.Method.ACK:
|
|
95
|
-
res.send((0, function_1.pipe)(req, processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.addSelfViaUsingExternalAddrs, processor_1.Alterations.removeSelfRoutes));
|
|
96
|
-
break;
|
|
97
|
-
default:
|
|
98
|
-
(0, request_1.handleRequest)(location, common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }))(req, res);
|
|
99
|
-
}
|
|
100
|
-
}));
|
|
132
|
+
const apiClient = common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr });
|
|
133
|
+
new processor_1.default({ bindAddr, name: "connect" }).listen((0, exports.createDispatcher)({ apiClient, location }));
|
|
101
134
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -18,6 +18,19 @@ export declare const getSipUri: (uri: {
|
|
|
18
18
|
user?: string;
|
|
19
19
|
host: string;
|
|
20
20
|
}) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a request belongs to an existing dialog: RFC 3261 section 12.2, a request
|
|
23
|
+
* inside a dialog carries the To tag the dialog was established with. Such a request is
|
|
24
|
+
* forwarded along the dialog's route set and is never routed as a new call, which
|
|
25
|
+
* would send a hold or transfer re-INVITE to the PSTN trunk and answer the provider's
|
|
26
|
+
* re-INVITE with 500 (Routr v1 fixed the same bug in issue #58). The EdgePort refuses
|
|
27
|
+
* an in-dialog INVITE or REFER for a dialog it did not proxy, so forwarding here does
|
|
28
|
+
* not bypass access checks for a forged To tag.
|
|
29
|
+
*
|
|
30
|
+
* @param {MessageRequest} req - The request
|
|
31
|
+
* @return {boolean} true when the To header carries a tag
|
|
32
|
+
*/
|
|
33
|
+
export declare const isInDialog: (req: MessageRequest) => boolean;
|
|
21
34
|
export declare const hasXConnectObjectHeader: (req: MessageRequest) => string;
|
|
22
35
|
export declare const getVerifierImpl: () => {
|
|
23
36
|
verify: (token: string) => Promise<import("@routr/common/dist/errors").ServiceUnavailableError | V.VerifyResponse>;
|
package/dist/utils.js
CHANGED
|
@@ -45,7 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
45
45
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
46
|
};
|
|
47
47
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
-
exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
|
|
48
|
+
exports.isFromTrustedEdgePort = exports.getSourceAddress = exports.getUntrustedHeadersToStrip = exports.getVerifierImpl = exports.hasXConnectObjectHeader = exports.isInDialog = exports.getSipUri = exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findNumberByTelUrl = exports.findDomain = exports.isKind = void 0;
|
|
49
49
|
/*
|
|
50
50
|
* Copyright (C) 2026 by Fonoster Inc (https://fonoster.com)
|
|
51
51
|
* http://github.com/fonoster/routr
|
|
@@ -206,6 +206,20 @@ const getTrunkURI = (trunk) => {
|
|
|
206
206
|
exports.getTrunkURI = getTrunkURI;
|
|
207
207
|
const getSipUri = (uri) => uri.user ? `sip:${uri.user}@${uri.host}` : `sip:${uri.host}`;
|
|
208
208
|
exports.getSipUri = getSipUri;
|
|
209
|
+
/**
|
|
210
|
+
* Whether a request belongs to an existing dialog: RFC 3261 section 12.2, a request
|
|
211
|
+
* inside a dialog carries the To tag the dialog was established with. Such a request is
|
|
212
|
+
* forwarded along the dialog's route set and is never routed as a new call, which
|
|
213
|
+
* would send a hold or transfer re-INVITE to the PSTN trunk and answer the provider's
|
|
214
|
+
* re-INVITE with 500 (Routr v1 fixed the same bug in issue #58). The EdgePort refuses
|
|
215
|
+
* an in-dialog INVITE or REFER for a dialog it did not proxy, so forwarding here does
|
|
216
|
+
* not bypass access checks for a forged To tag.
|
|
217
|
+
*
|
|
218
|
+
* @param {MessageRequest} req - The request
|
|
219
|
+
* @return {boolean} true when the To header carries a tag
|
|
220
|
+
*/
|
|
221
|
+
const isInDialog = (req) => { var _a; return !!((_a = req.message.to) === null || _a === void 0 ? void 0 : _a.tag); };
|
|
222
|
+
exports.isInDialog = isInDialog;
|
|
209
223
|
const hasXConnectObjectHeader = (req) => processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
|
|
210
224
|
exports.hasXConnectObjectHeader = hasXConnectObjectHeader;
|
|
211
225
|
const getVerifierImpl = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@routr/connect",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.17.0",
|
|
4
4
|
"description": "Default processor",
|
|
5
5
|
"author": "Pedro Sanders <psanders@fonoster.com>",
|
|
6
6
|
"homepage": "https://github.com/fonoster/routr#readme",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@fonoster/logger": "0.6.0",
|
|
23
|
-
"@routr/common": "^2.
|
|
24
|
-
"@routr/location": "^2.
|
|
25
|
-
"@routr/processor": "^2.
|
|
23
|
+
"@routr/common": "^2.17.0",
|
|
24
|
+
"@routr/location": "^2.17.0",
|
|
25
|
+
"@routr/processor": "^2.17.0",
|
|
26
26
|
"jsonwebtoken": "^9.0.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"bugs": {
|
|
42
42
|
"url": "https://github.com/fonoster/routr/issues"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "9bc2d0a407a82e7b3724bb139ba62504c7b3d49e"
|
|
45
45
|
}
|