@routr/connect 2.0.8-alpha.4 → 2.0.8-alpha.40

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.
@@ -0,0 +1,28 @@
1
+ import { MessageRequest, CommonTypes as CT, CommonConnect as CC } from "@routr/common";
2
+ import { RoutingDirection } from "./types";
3
+ export declare const checkAccess: (accessRequest: {
4
+ apiClient: CC.APIClient;
5
+ request: MessageRequest;
6
+ caller: CC.RoutableResourceUnion;
7
+ callee: CC.RoutableResourceUnion;
8
+ routingDirection: RoutingDirection;
9
+ }) => Promise<Record<string, unknown>>;
10
+ export declare const checkAgentOrPeerAccess: (request: MessageRequest, caller: CC.RoutableResourceUnion) => Promise<{
11
+ message: {
12
+ responseType: CT.ResponseType;
13
+ wwwAuthenticate: {
14
+ scheme: string;
15
+ realm: string;
16
+ qop: string;
17
+ opaque: string;
18
+ stale: boolean;
19
+ nonce: string;
20
+ algorithm: string;
21
+ };
22
+ };
23
+ }>;
24
+ export declare const checkAccessFromPSTN: (apiClient: CC.APIClient, request: MessageRequest, callee: CC.INumber) => Promise<{
25
+ message: {
26
+ responseType: CT.ResponseType;
27
+ };
28
+ }>;
package/dist/access.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.checkAccessFromPSTN = exports.checkAgentOrPeerAccess = exports.checkAccess = void 0;
13
+ /*
14
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
15
+ * http://github.com/fonoster
16
+ *
17
+ * This file is part of Routr.
18
+ *
19
+ * Licensed under the MIT License (the "License");
20
+ * you may not use this file except in compliance with
21
+ * the License. You may obtain a copy of the License at
22
+ *
23
+ * https://opensource.org/licenses/MIT
24
+ *
25
+ * Unless required by applicable law or agreed to in writing, software
26
+ * distributed under the License is distributed on an "AS IS" BASIS,
27
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28
+ * See the License for the specific language governing permissions and
29
+ * limitations under the License.
30
+ */
31
+ const logger_1 = require("@fonoster/logger");
32
+ const common_1 = require("@routr/common");
33
+ const types_1 = require("./types");
34
+ const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
35
+ const checkAccess = (accessRequest) => __awaiter(void 0, void 0, void 0, function* () {
36
+ const { apiClient, request, caller, callee, routingDirection } = accessRequest;
37
+ switch (routingDirection) {
38
+ case types_1.RoutingDirection.PEER_TO_PSTN:
39
+ case types_1.RoutingDirection.AGENT_TO_AGENT:
40
+ case types_1.RoutingDirection.AGENT_TO_PSTN:
41
+ return (0, exports.checkAgentOrPeerAccess)(request, caller);
42
+ case types_1.RoutingDirection.FROM_PSTN:
43
+ return (0, exports.checkAccessFromPSTN)(apiClient, request, callee);
44
+ case types_1.RoutingDirection.UNKNOWN:
45
+ return common_1.Auth.createForbideenResponse();
46
+ }
47
+ });
48
+ exports.checkAccess = checkAccess;
49
+ const checkAgentOrPeerAccess = (request, caller) => __awaiter(void 0, void 0, void 0, function* () {
50
+ // Calculate and return challenge
51
+ if (request.message.authorization) {
52
+ const auth = Object.assign({}, request.message.authorization);
53
+ auth.method = request.method;
54
+ const credentials = caller.credentials;
55
+ // Calculate response and compare with the one send by the endpoint
56
+ const calcRes = common_1.Auth.calculateAuthResponse(auth, {
57
+ username: credentials === null || credentials === void 0 ? void 0 : credentials.username,
58
+ secret: credentials === null || credentials === void 0 ? void 0 : credentials.password
59
+ });
60
+ if (calcRes !== auth.response) {
61
+ return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
62
+ }
63
+ }
64
+ else {
65
+ return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
66
+ }
67
+ });
68
+ exports.checkAgentOrPeerAccess = checkAgentOrPeerAccess;
69
+ const checkAccessFromPSTN = (apiClient, request, callee) => __awaiter(void 0, void 0, void 0, function* () {
70
+ // Get the Trunk associated with the SIP URI
71
+ const trunk = (yield apiClient.trunks.findBy({
72
+ fieldName: "inboundUri",
73
+ fieldValue: request.message.requestUri.host
74
+ })).items[0];
75
+ // If the Trunk or Number doesn't exist reject the call
76
+ if (!callee || !trunk) {
77
+ return common_1.Auth.createForbideenResponse();
78
+ }
79
+ if (callee.trunk.ref !== trunk.ref) {
80
+ return common_1.Auth.createForbideenResponse();
81
+ }
82
+ // Verify that the IP is whitelisted which means getting the access control list for the trunk
83
+ if (trunk.accessControlList) {
84
+ try {
85
+ const allow = trunk.accessControlList.allow.filter((net) => {
86
+ return common_1.IpUtils.hasIp(net, request.sender.host);
87
+ })[0];
88
+ if (!allow) {
89
+ return common_1.Auth.createUnauthorizedResponseWithoutChallenge();
90
+ }
91
+ }
92
+ catch (e) {
93
+ logger.error(e);
94
+ return common_1.Auth.createServerInternalErrorResponse();
95
+ }
96
+ }
97
+ // If the Trunk has a User/Password we must verify that the User/Password are valid
98
+ if (trunk.inboundCredentials) {
99
+ // Calculate and return challenge
100
+ if (request.message.authorization) {
101
+ const auth = Object.assign({}, request.message.authorization);
102
+ auth.method = request.method;
103
+ // Calculate response and compare with the one send by the endpoint
104
+ const calcRes = common_1.Auth.calculateAuthResponse(auth, {
105
+ username: trunk.inboundCredentials.username,
106
+ secret: trunk.inboundCredentials.password
107
+ });
108
+ if (calcRes !== auth.response) {
109
+ return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
110
+ }
111
+ }
112
+ else {
113
+ return common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host);
114
+ }
115
+ }
116
+ });
117
+ exports.checkAccessFromPSTN = checkAccessFromPSTN;
@@ -0,0 +1 @@
1
+ export declare const assertOnlyOneEnvIsSet: (envs: string[]) => void;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertOnlyOneEnvIsSet = void 0;
4
+ /*
5
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster/routr
7
+ *
8
+ * This file is part of Routr
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const logger_1 = require("@fonoster/logger");
23
+ const logger = (0, logger_1.getLogger)({ service: "common", filePath: __filename });
24
+ const assertOnlyOneEnvIsSet = (envs) => {
25
+ const envsSet = envs.filter((env) => process.env[env] != null);
26
+ if (envsSet.length > 1) {
27
+ logger.error(`only one of the following environment variables can be set: ${envs.join(", ")}.`);
28
+ process.exit(1);
29
+ }
30
+ };
31
+ exports.assertOnlyOneEnvIsSet = assertOnlyOneEnvIsSet;
package/dist/envs.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export declare const BIND_ADDR: string;
2
+ export declare const LOCATION_ADDR: string;
3
+ export declare const API_ADDR: string;
4
+ export declare const CONNECT_VERIFIER_ADDR: string;
5
+ export declare const CONNECT_VERIFIER_PUBLIC_KEY_PATH: string;
6
+ export declare const CONNECT_VERIFIER_OPTIONS: any;
package/dist/envs.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CONNECT_VERIFIER_OPTIONS = exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = exports.CONNECT_VERIFIER_ADDR = exports.API_ADDR = exports.LOCATION_ADDR = exports.BIND_ADDR = void 0;
5
+ /*
6
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
7
+ * http://github.com/fonoster
8
+ *
9
+ * This file is part of Routr.
10
+ *
11
+ * Licensed under the MIT License (the "License");
12
+ * you may not use this file except in compliance with
13
+ * the License. You may obtain a copy of the License at
14
+ *
15
+ * https://opensource.org/licenses/MIT
16
+ *
17
+ * Unless required by applicable law or agreed to in writing, software
18
+ * distributed under the License is distributed on an "AS IS" BASIS,
19
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
+ * See the License for the specific language governing permissions and
21
+ * limitations under the License.
22
+ */
23
+ const assertions_1 = require("./assertions");
24
+ (0, assertions_1.assertOnlyOneEnvIsSet)(["CONNECT_VERIFIER_ADDR", "CONNECT_VERIFIER_PUBLIC_KEY"]);
25
+ exports.BIND_ADDR = (_a = process.env.BIND_ADDR) !== null && _a !== void 0 ? _a : "0.0.0.0:51904";
26
+ exports.LOCATION_ADDR = process.env.LOCATION_ADDR;
27
+ exports.API_ADDR = process.env.API_ADDR;
28
+ exports.CONNECT_VERIFIER_ADDR = process.env.CONNECT_VERIFIER_ADDR;
29
+ exports.CONNECT_VERIFIER_PUBLIC_KEY_PATH = process.env.CONNECT_VERIFIER_PUBLIC_KEY_PATH;
30
+ exports.CONNECT_VERIFIER_OPTIONS = process.env.CONNECT_VERIFIER_OPTIONS
31
+ ? JSON.parse(process.env.CONNECT_VERIFIER_OPTIONS)
32
+ : { expiresIn: "1h", algorithm: ["RS256"] };
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ROUTING_DIRECTION } from "./types";
1
+ import { RoutingDirection } from "./types";
2
2
  /**
3
3
  * Throw when the API server is unavailable.
4
4
  */
@@ -19,7 +19,7 @@ export declare class UnsuportedRoutingError extends Error {
19
19
  /**
20
20
  * Create a new ServiceUnavailableError.
21
21
  *
22
- * @param {string} routingDir - The routing direction
22
+ * @param {RoutingDirection} routingDirection - The routing direction
23
23
  */
24
- constructor(routingDir: ROUTING_DIRECTION);
24
+ constructor(routingDirection: RoutingDirection);
25
25
  }
package/dist/errors.js CHANGED
@@ -25,7 +25,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.UnsuportedRoutingError = exports.ServiceUnavailableError = void 0;
27
27
  /*
28
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
28
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
29
29
  * http://github.com/fonoster/routr
30
30
  *
31
31
  * This file is part of Routr
@@ -67,11 +67,11 @@ class UnsuportedRoutingError extends Error {
67
67
  /**
68
68
  * Create a new ServiceUnavailableError.
69
69
  *
70
- * @param {string} routingDir - The routing direction
70
+ * @param {RoutingDirection} routingDirection - The routing direction
71
71
  */
72
- constructor(routingDir) {
73
- super("unsupported routing direction: " + routingDir);
74
- this.code = grpc.status.UNKNOWN;
72
+ constructor(routingDirection) {
73
+ super("unsupported routing direction: " + routingDirection);
74
+ this.code = grpc.status.UNIMPLEMENTED;
75
75
  // Set the prototype explicitly.
76
76
  Object.setPrototypeOf(this, ServiceUnavailableError.prototype);
77
77
  }
@@ -1,6 +1,6 @@
1
1
  import { MessageRequest, Response } from "@routr/processor";
2
- import { ILocationService } from "@routr/location/src/types";
2
+ import { ILocationService } from "@routr/location";
3
3
  import { CommonConnect as CC } from "@routr/common";
4
- export declare const handleRegister: (location: ILocationService) => (request: MessageRequest, res: Response) => Promise<void>;
4
+ export declare const handleRegister: (apiClient: CC.APIClient, location: ILocationService) => (request: MessageRequest, res: Response) => Promise<void>;
5
5
  export declare const handleRegistry: (req: MessageRequest, res: Response) => void;
6
- export declare const handleRequest: (location: ILocationService, dataAPI?: CC.DataAPI) => (req: MessageRequest, res: Response) => Promise<void | MessageRequest>;
6
+ export declare const handleRequest: (location: ILocationService, apiClient?: CC.APIClient) => (request: MessageRequest, res: Response) => Promise<void>;
package/dist/handlers.js CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.handleRequest = exports.handleRegistry = exports.handleRegister = void 0;
13
13
  /*
14
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
14
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
15
15
  * http://github.com/fonoster/routr
16
16
  *
17
17
  * This file is part of Routr
@@ -34,40 +34,96 @@ const processor_1 = require("@routr/processor");
34
34
  const function_1 = require("fp-ts/function");
35
35
  const router_1 = require("./router");
36
36
  const common_1 = require("@routr/common");
37
- const handleRegister = (location) => {
37
+ const utils_1 = require("./utils");
38
+ const logger_1 = require("@fonoster/logger");
39
+ const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
40
+ const enforceE164 = processor_1.Alterations.enforceE164(common_1.Environment.ENFORCE_E164, common_1.Environment.ENFORCE_E164_WITH_MOBILE_PREFIX);
41
+ const jwtVerifier = (0, utils_1.getVerifierImpl)();
42
+ const handleRegister = (apiClient, location) => {
38
43
  return (request, res) => __awaiter(void 0, void 0, void 0, function* () {
39
- yield location.addRoute({
40
- aor: processor_1.Target.getTargetAOR(request),
41
- route: location_1.Helper.createRoute(request)
42
- });
43
- res.sendOk();
44
+ // Calculate and return challenge
45
+ if (request.message.authorization) {
46
+ const auth = Object.assign({}, request.message.authorization);
47
+ auth.method = request.method;
48
+ const fromURI = request.message.from.address.uri;
49
+ const peerOrAgent = yield (0, utils_1.findResource)(apiClient, fromURI.host, fromURI.user);
50
+ if (!peerOrAgent) {
51
+ return res.send(common_1.Auth.createForbideenResponse());
52
+ }
53
+ const credentials = peerOrAgent.credentials;
54
+ // Calculate response and compare with the one send by the endpoint
55
+ const calcRes = common_1.Auth.calculateAuthResponse(auth, {
56
+ username: credentials === null || credentials === void 0 ? void 0 : credentials.username,
57
+ secret: credentials === null || credentials === void 0 ? void 0 : credentials.password
58
+ });
59
+ if (calcRes !== auth.response) {
60
+ return res.send(common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host));
61
+ }
62
+ // TODO: Needs test
63
+ yield location.addRoute({
64
+ aor: "aor" in peerOrAgent ? peerOrAgent.aor : processor_1.Target.getTargetAOR(request),
65
+ route: location_1.Helper.createRoute(request)
66
+ });
67
+ res.sendOk();
68
+ }
69
+ else if ((0, utils_1.hasXConnectObjectHeader)(request)) {
70
+ const connectToken = processor_1.Extensions.getHeaderValue(request, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
71
+ try {
72
+ const payload = (yield jwtVerifier.verify(connectToken));
73
+ if (!payload.allowedMethods.includes(common_1.Method.REGISTER)) {
74
+ return res.send(common_1.Auth.createForbideenResponse());
75
+ }
76
+ yield location.addRoute({
77
+ aor: payload.aor,
78
+ route: location_1.Helper.createRoute(request)
79
+ });
80
+ }
81
+ catch (e) {
82
+ logger.verbose("unable to validate connect token", {
83
+ originalError: e.message
84
+ });
85
+ res.send(common_1.Auth.createForbideenResponse());
86
+ }
87
+ }
88
+ else {
89
+ res.send(common_1.Auth.createUnauthorizedResponse(request.message.requestUri.host));
90
+ }
44
91
  });
45
92
  };
46
93
  exports.handleRegister = handleRegister;
47
94
  // TODO: Needs test
48
95
  const handleRegistry = (req, res) => {
49
- const route = processor_1.Helper.createRouteFromLastMessage(req);
50
- res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeRoutes, processor_1.Alterations.removeXEdgePortRef));
96
+ const route = location_1.Helper.createRouteFromLastMessage(req);
97
+ res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeSelfRoutes, processor_1.Alterations.removeXEdgePortRef));
51
98
  };
52
99
  exports.handleRegistry = handleRegistry;
53
100
  // TODO: If request has X-Connect-Token then validate the JWT value and continue
54
- const handleRequest = (location, dataAPI) => (req, res) => __awaiter(void 0, void 0, void 0, function* () {
101
+ const handleRequest = (location, apiClient) => (request, res) => __awaiter(void 0, void 0, void 0, function* () {
55
102
  try {
103
+ const req = common_1.Environment.ENFORCE_E164 ? enforceE164(request) : request;
56
104
  const route = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.EDGEPORT_REF)
57
- ? processor_1.Helper.createRouteFromLastMessage(req)
58
- : yield (0, router_1.router)(location, dataAPI)(req);
105
+ ? location_1.Helper.createRouteFromLastMessage(req)
106
+ : yield (0, router_1.router)(location, apiClient)(req);
59
107
  if (!route)
60
108
  return res.sendNotFound();
61
- // Forward request to peer edgeport
62
- if (req.edgePortRef !== route.edgePortRef) {
63
- return (0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route),
64
- // The LP address belongs to another edgeport
65
- processor_1.Alterations.addRouteToListeningPoint(route), processor_1.Alterations.addXEdgePortRef, processor_1.Alterations.decreaseMaxForwards);
109
+ // If route is not type Route then return
110
+ if (!("listeningPoints" in route)) {
111
+ return res.send(route);
112
+ }
113
+ else {
114
+ // Forward request to peer edgeport
115
+ if (req.edgePortRef !== route.edgePortRef) {
116
+ return res.send((0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.addSelfRecordRoute(route),
117
+ // The order of the routes is important
118
+ processor_1.Alterations.addRouteToPeerEdgePort(route), processor_1.Alterations.addRouteToNextHop(route), processor_1.Alterations.addXEdgePortRef, processor_1.Alterations.decreaseMaxForwards));
119
+ }
120
+ res.send((0, tailor_1.tailor)(route, req));
66
121
  }
67
- res.send((0, tailor_1.tailor)(route, req));
68
122
  }
69
123
  catch (err) {
70
- res.sendError(err);
124
+ res.sendInternalServerError();
125
+ logger.error(err);
126
+ // res.sendError(err)
71
127
  }
72
128
  });
73
129
  exports.handleRequest = handleRequest;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ /*
5
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
6
+ * http://github.com/fonoster
7
+ *
8
+ * This file is part of Routr.
9
+ *
10
+ * Licensed under the MIT License (the "License");
11
+ * you may not use this file except in compliance with
12
+ * the License. You may obtain a copy of the License at
13
+ *
14
+ * https://opensource.org/licenses/MIT
15
+ *
16
+ * Unless required by applicable law or agreed to in writing, software
17
+ * distributed under the License is distributed on an "AS IS" BASIS,
18
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
+ * See the License for the specific language governing permissions and
20
+ * limitations under the License.
21
+ */
22
+ const common_1 = require("@routr/common");
23
+ const envs_1 = require("./envs");
24
+ common_1.HealthCheck.check(envs_1.BIND_ADDR);
package/dist/router.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { Route } from "@routr/common";
1
+ import { CommonConnect as CC, Route } from "@routr/common";
2
2
  import { MessageRequest } from "@routr/processor";
3
3
  import { ILocationService } from "@routr/location";
4
- import { CommonConnect as CC } from "@routr/common";
5
- export declare function router(location: ILocationService, dataAPI: CC.DataAPI): (req: MessageRequest) => Promise<Route>;
4
+ export declare function router(location: ILocationService, apiClient: CC.APIClient): (request: MessageRequest) => Promise<Route | Record<string, unknown>>;
package/dist/router.js CHANGED
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.router = void 0;
13
13
  /*
14
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
14
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
15
15
  * http://github.com/fonoster/routr
16
16
  *
17
17
  * This file is part of Routr
@@ -33,32 +33,87 @@ const common_1 = require("@routr/common");
33
33
  const utils_1 = require("./utils");
34
34
  const processor_1 = require("@routr/processor");
35
35
  const errors_1 = require("./errors");
36
- const location_1 = require("@routr/location");
37
36
  const logger_1 = require("@fonoster/logger");
38
- const common_2 = require("@routr/common");
37
+ const access_1 = require("./access");
39
38
  const logger = (0, logger_1.getLogger)({ service: "connect", filePath: __filename });
40
- const getSIPURI = (uri) => `sip:${uri.user}@${uri.host}`;
39
+ const jwtVerifier = (0, utils_1.getVerifierImpl)();
41
40
  // eslint-disable-next-line require-jsdoc
42
- function router(location, dataAPI) {
43
- return (req) => __awaiter(this, void 0, void 0, function* () {
44
- const fromURI = req.message.from.address.uri;
45
- const requestURI = req.message.requestUri;
41
+ function router(location, apiClient) {
42
+ return (request) => __awaiter(this, void 0, void 0, function* () {
43
+ var _a;
44
+ const fromURI = request.message.from.address.uri;
45
+ const requestURI = request.message.requestUri;
46
+ let caller;
47
+ let callee;
48
+ if ((0, utils_1.hasXConnectObjectHeader)(request)) {
49
+ const connectToken = processor_1.Extensions.getHeaderValue(request, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
50
+ try {
51
+ const payload = (yield jwtVerifier.verify(connectToken));
52
+ const domain = yield (0, utils_1.findDomain)(apiClient, payload.domain);
53
+ if (!payload.allowedMethods.includes(common_1.Method.INVITE)) {
54
+ return common_1.Auth.createForbideenResponse();
55
+ }
56
+ caller = {
57
+ apiVersion: common_1.CommonConnect.APIVersion.V2,
58
+ ref: payload.ref,
59
+ name: (_a = request.message.from.address.displayName) !== null && _a !== void 0 ? _a : common_1.CommonTypes.ANONYMOUS,
60
+ domain: domain,
61
+ domainRef: payload.domainRef,
62
+ username: common_1.CommonTypes.ANONYMOUS,
63
+ privacy: processor_1.Extensions.getHeaderValue(request, "Privacy"),
64
+ enabled: true
65
+ };
66
+ callee = (yield apiClient.peers.findBy({
67
+ fieldName: "aor",
68
+ fieldValue: payload.aorLink
69
+ })).items[0];
70
+ }
71
+ catch (e) {
72
+ logger.verbose("unable to validate connect token", {
73
+ originalError: e.message
74
+ });
75
+ return common_1.Auth.createForbideenResponse();
76
+ }
77
+ }
78
+ else {
79
+ caller = yield (0, utils_1.findResource)(apiClient, fromURI.host, fromURI.user);
80
+ callee = yield (0, utils_1.findResource)(apiClient, requestURI.host, requestURI.user);
81
+ }
82
+ const routingDirection = (0, utils_1.getRoutingDirection)(caller, callee);
46
83
  logger.verbose("routing request from: " +
47
- getSIPURI(fromURI) +
84
+ (0, utils_1.getSIPURI)(fromURI) +
48
85
  ", to: " +
49
- getSIPURI(requestURI), { fromURI: getSIPURI(fromURI), requestURI: getSIPURI(requestURI) });
50
- const caller = yield (0, utils_1.findResource)(dataAPI, fromURI.host, fromURI.user);
51
- const callee = yield (0, utils_1.findResource)(dataAPI, requestURI.host, requestURI.user);
52
- const routingDir = (0, utils_1.getRoutingDirection)(caller, callee);
53
- switch (routingDir) {
54
- case types_1.ROUTING_DIRECTION.AGENT_TO_PSTN:
55
- return yield toPSTN(dataAPI, req, caller, requestURI.user);
56
- case types_1.ROUTING_DIRECTION.AGENT_TO_AGENT:
57
- return agentToAgent(location, req);
58
- case types_1.ROUTING_DIRECTION.FROM_PSTN:
59
- return yield fromPSTN(location, dataAPI, callee);
86
+ (0, utils_1.getSIPURI)(requestURI), {
87
+ fromURI: (0, utils_1.getSIPURI)(fromURI),
88
+ requestURI: (0, utils_1.getSIPURI)(requestURI),
89
+ routingDirection
90
+ });
91
+ if (!(0, utils_1.hasXConnectObjectHeader)(request) &&
92
+ request.method === common_1.CommonTypes.Method.INVITE) {
93
+ const failedCheck = yield (0, access_1.checkAccess)({
94
+ apiClient,
95
+ request,
96
+ caller,
97
+ callee,
98
+ routingDirection
99
+ });
100
+ if (failedCheck) {
101
+ return failedCheck;
102
+ }
103
+ }
104
+ switch (routingDirection) {
105
+ case types_1.RoutingDirection.AGENT_TO_AGENT:
106
+ return agentToAgent(location, request);
107
+ case types_1.RoutingDirection.AGENT_TO_PEER:
108
+ return yield agentToPeer(location, callee, request);
109
+ case types_1.RoutingDirection.AGENT_TO_PSTN:
110
+ return yield agentToPSTN(request, caller, requestURI.user);
111
+ case types_1.RoutingDirection.FROM_PSTN:
112
+ return yield fromPSTN(apiClient, location, callee, request);
113
+ case types_1.RoutingDirection.PEER_TO_PSTN:
114
+ return yield peerToPSTN(apiClient, request);
60
115
  default:
61
- throw new errors_1.UnsuportedRoutingError(routingDir);
116
+ throw new errors_1.UnsuportedRoutingError(routingDirection);
62
117
  }
63
118
  });
64
119
  }
@@ -66,33 +121,50 @@ exports.router = router;
66
121
  // eslint-disable-next-line require-jsdoc
67
122
  function agentToAgent(location, req) {
68
123
  return __awaiter(this, void 0, void 0, function* () {
69
- return (yield location.findRoutes({ aor: processor_1.Target.getTargetAOR(req) }))[0];
124
+ return (yield location.findRoutes({ aor: processor_1.Target.getTargetAOR(req), callId: req.ref }))[0];
70
125
  });
71
126
  }
72
127
  /**
73
128
  * From PSTN routing.
74
129
  *
130
+ * @param {APIClient} apiClient - API client
75
131
  * @param {ILocationService} location - Location service
76
- * @param {uknown} _
77
132
  * @param {Resource} callee - The callee
133
+ * @param {MessageRequest} req - The request
78
134
  * @return {Promise<Route>}
79
135
  */
80
- function fromPSTN(location, _, callee) {
136
+ function fromPSTN(apiClient, location, callee, req) {
81
137
  var _a;
82
138
  return __awaiter(this, void 0, void 0, function* () {
139
+ const sessionAffinityRef = processor_1.Extensions.getHeaderValue(req, callee.sessionAffinityHeader);
140
+ let backend;
141
+ if (callee.aorLink.startsWith("backend:")) {
142
+ const peer = (yield apiClient.peers.findBy({
143
+ fieldName: "aor",
144
+ fieldValue: callee.aorLink
145
+ })).items[0];
146
+ backend = {
147
+ ref: peer.ref,
148
+ balancingAlgorithm: peer.balancingAlgorithm,
149
+ withSessionAffinity: peer.withSessionAffinity
150
+ };
151
+ }
83
152
  const route = (yield location.findRoutes({
84
- aor: callee.spec.location.aorLink
153
+ aor: callee.aorLink,
154
+ callId: req.ref,
155
+ sessionAffinityRef,
156
+ backend
85
157
  }))[0];
86
158
  if (!route) {
87
- throw new location_1.NotRoutesFoundForAOR(callee.spec.location.aorLink);
159
+ return null;
88
160
  }
89
161
  if (!route.headers)
90
162
  route.headers = [];
91
- (_a = callee.spec.location.props) === null || _a === void 0 ? void 0 : _a.forEach((prop) => {
163
+ (_a = callee.extraHeaders) === null || _a === void 0 ? void 0 : _a.forEach((prop) => {
92
164
  const p = {
93
165
  name: prop.name,
94
166
  value: prop.value,
95
- action: common_2.CommonTypes.HeaderModifierAction.ADD
167
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
96
168
  };
97
169
  route.headers.push(p);
98
170
  });
@@ -100,50 +172,107 @@ function fromPSTN(location, _, callee) {
100
172
  });
101
173
  }
102
174
  // eslint-disable-next-line require-jsdoc
103
- function toPSTN(dataAPI, req, caller, calleeNumber) {
104
- var _a, _b;
175
+ function agentToPSTN(req, agent, calleeNumber) {
176
+ var _a, _b, _c, _d, _e;
105
177
  return __awaiter(this, void 0, void 0, function* () {
106
- const domain = yield dataAPI.get(caller.spec.domainRef);
178
+ if (!((_a = agent.domain) === null || _a === void 0 ? void 0 : _a.egressPolicies)) {
179
+ // TODO: Create custom error
180
+ throw new Error(`no egress policy found for Domain ref: ${agent.domain.ref}`);
181
+ }
107
182
  // Look for Number in domain that matches regex callee
108
- const policy = (_a = domain.spec.context.egressPolicies) === null || _a === void 0 ? void 0 : _a.find((policy) => {
183
+ const policy = agent.domain.egressPolicies.find((policy) => {
109
184
  const regex = new RegExp(policy.rule);
110
185
  return regex.test(calleeNumber);
111
186
  });
112
- const number = yield dataAPI.get(policy === null || policy === void 0 ? void 0 : policy.numberRef);
113
- const trunk = yield dataAPI.get(number === null || number === void 0 ? void 0 : number.spec.trunkRef);
114
- if (!domain.spec.context.egressPolicies) {
115
- // TODO: Create custom error
116
- throw new Error(`no egress policy found for Domain ref: ${domain.ref}`);
117
- }
187
+ const trunk = (_b = policy.number) === null || _b === void 0 ? void 0 : _b.trunk;
118
188
  if (!trunk) {
119
- // TODO: Create custom error
120
- throw new Error(`no trunk associated with Number ref: ${number === null || number === void 0 ? void 0 : number.ref}`);
189
+ // This should never happen
190
+ throw new Error(`no trunk associated with Number ref: ${(_c = policy.number) === null || _c === void 0 ? void 0 : _c.ref}`);
121
191
  }
122
192
  const uri = (0, utils_1.getTrunkURI)(trunk);
123
- const egressListeningPoint = common_1.Helper.getListeningPoint(req, uri.transport);
193
+ return {
194
+ user: uri.user,
195
+ host: uri.host,
196
+ port: uri.port,
197
+ transport: (_d = uri.transport) === null || _d === void 0 ? void 0 : _d.toUpperCase(),
198
+ edgePortRef: req.edgePortRef,
199
+ listeningPoints: req.listeningPoints,
200
+ localnets: req.localnets,
201
+ externalAddrs: req.externalAddrs,
202
+ headers: [
203
+ // TODO: Find a more deterministic way to re-add the Privacy header
204
+ {
205
+ name: "Privacy",
206
+ action: common_1.CommonTypes.HeaderModifierAction.REMOVE
207
+ },
208
+ {
209
+ name: "Privacy",
210
+ value: ((_e = agent.privacy) === null || _e === void 0 ? void 0 : _e.toUpperCase()) === common_1.CommonTypes.Privacy.PRIVATE
211
+ ? common_1.CommonTypes.Privacy.PRIVATE.toLowerCase()
212
+ : common_1.CommonTypes.Privacy.NONE.toLowerCase(),
213
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
214
+ },
215
+ (0, utils_1.createRemotePartyId)(trunk, policy.number),
216
+ (0, utils_1.createPAssertedIdentity)(req, trunk, policy.number),
217
+ yield (0, utils_1.createTrunkAuthentication)(trunk)
218
+ ]
219
+ };
220
+ });
221
+ }
222
+ // eslint-disable-next-line require-jsdoc
223
+ function agentToPeer(location, callee, req) {
224
+ return __awaiter(this, void 0, void 0, function* () {
225
+ const backend = {
226
+ ref: callee.ref,
227
+ balancingAlgorithm: callee.balancingAlgorithm,
228
+ withSessionAffinity: callee.withSessionAffinity
229
+ };
230
+ return (yield location.findRoutes({
231
+ aor: callee.aor,
232
+ callId: req.ref,
233
+ backend
234
+ }))[0];
235
+ });
236
+ }
237
+ // eslint-disable-next-line require-jsdoc
238
+ function peerToPSTN(apiClient, req) {
239
+ return __awaiter(this, void 0, void 0, function* () {
240
+ const numberTel = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_NUMBER);
241
+ const privacy = processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.DOD_PRIVACY);
242
+ const number = yield (0, utils_1.findNumberByTelUrl)(apiClient, `tel:${numberTel}`);
243
+ if (!number) {
244
+ throw new Error(`no Number found for tel: ${numberTel}`);
245
+ }
246
+ if (!number.trunk) {
247
+ // TODO: Create custom error
248
+ throw new Error(`no trunk associated with Number ref: ${number.ref}`);
249
+ }
250
+ const uri = (0, utils_1.getTrunkURI)(number.trunk);
124
251
  return {
125
252
  user: uri.user,
126
253
  host: uri.host,
127
254
  port: uri.port,
128
255
  transport: uri.transport,
129
256
  edgePortRef: req.edgePortRef,
130
- egressListeningPoint,
257
+ listeningPoints: req.listeningPoints,
258
+ localnets: req.localnets,
259
+ externalAddrs: req.externalAddrs,
131
260
  headers: [
132
261
  // TODO: Find a more deterministic way to re-add the Privacy header
133
262
  {
134
263
  name: "Privacy",
135
- action: common_2.CommonTypes.HeaderModifierAction.REMOVE
264
+ action: common_1.CommonTypes.HeaderModifierAction.REMOVE
136
265
  },
137
266
  {
138
267
  name: "Privacy",
139
- value: ((_b = caller.spec.privacy) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === common_2.CommonTypes.Privacy.PRIVATE
140
- ? common_2.CommonTypes.Privacy.PRIVATE
141
- : common_2.CommonTypes.Privacy.NONE,
142
- action: common_2.CommonTypes.HeaderModifierAction.ADD
268
+ value: (privacy === null || privacy === void 0 ? void 0 : privacy.toLocaleLowerCase()) === common_1.CommonTypes.Privacy.PRIVATE
269
+ ? common_1.CommonTypes.Privacy.PRIVATE.toLowerCase()
270
+ : common_1.CommonTypes.Privacy.NONE.toLowerCase(),
271
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
143
272
  },
144
- (0, utils_1.createRemotePartyId)(trunk, number),
145
- (0, utils_1.createPAssertedIdentity)(req, trunk, number),
146
- yield (0, utils_1.createTrunkAuthentication)(dataAPI, trunk)
273
+ (0, utils_1.createRemotePartyId)(number.trunk, number),
274
+ (0, utils_1.createPAssertedIdentity)(req, number.trunk, number),
275
+ yield (0, utils_1.createTrunkAuthentication)(number.trunk)
147
276
  ]
148
277
  };
149
278
  });
package/dist/runner.js CHANGED
@@ -3,10 +3,9 @@
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
- var _a;
7
6
  Object.defineProperty(exports, "__esModule", { value: true });
8
7
  /*
9
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
8
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
10
9
  * http://github.com/fonoster/routr
11
10
  *
12
11
  * This file is part of Routr
@@ -27,9 +26,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
27
26
  require("./tracer").init("dispatcher");
28
27
  const service_1 = __importDefault(require("./service"));
29
28
  const common_1 = require("@routr/common");
29
+ const envs_1 = require("./envs");
30
30
  common_1.Assertions.assertEnvsAreSet(["LOCATION_ADDR", "API_ADDR"]);
31
31
  (0, service_1.default)({
32
- bindAddr: (_a = process.env.BIND_ADDR) !== null && _a !== void 0 ? _a : "0.0.0.0:51904",
33
- locationAddr: process.env.LOCATION_ADDR,
34
- apiAddr: process.env.API_ADDR
32
+ bindAddr: envs_1.BIND_ADDR,
33
+ locationAddr: envs_1.LOCATION_ADDR,
34
+ apiAddr: envs_1.API_ADDR
35
35
  });
package/dist/service.js CHANGED
@@ -59,10 +59,11 @@ function ConnectProcessor(config) {
59
59
  // Remove the proxy via before forwarding response
60
60
  return res.send(processor_1.Alterations.removeTopVia(req));
61
61
  }
62
- switch (req.method.toString()) {
62
+ switch (req.method) {
63
63
  case common_1.Method.PUBLISH:
64
64
  case common_1.Method.NOTIFY:
65
65
  case common_1.Method.SUBSCRIBE:
66
+ case common_1.Method.MESSAGE:
66
67
  res.sendMethodNotAllowed();
67
68
  break;
68
69
  case common_1.Method.REGISTER:
@@ -70,15 +71,15 @@ function ConnectProcessor(config) {
70
71
  (0, handlers_1.handleRegistry)(req, res);
71
72
  }
72
73
  else {
73
- (0, handlers_1.handleRegister)(location)(req, res);
74
+ (0, handlers_1.handleRegister)(common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }), location)(req, res);
74
75
  }
75
76
  break;
76
77
  case common_1.Method.BYE:
77
78
  case common_1.Method.ACK:
78
- res.send((0, tailor_1.tailor)(processor_1.Helper.createRouteFromLastMessage(req), req));
79
+ res.send((0, tailor_1.tailor)(location_1.Helper.createRouteFromLastMessage(req), req));
79
80
  break;
80
81
  default:
81
- yield (0, handlers_1.handleRequest)(location, common_2.CommonConnect.dataAPI(config.apiAddr))(req, res);
82
+ (0, handlers_1.handleRequest)(location, common_2.CommonConnect.apiClient({ apiAddr: config.apiAddr }))(req, res);
82
83
  }
83
84
  }));
84
85
  }
package/dist/tailor.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tailor = void 0;
4
4
  /*
5
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
5
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
6
6
  * http://github.com/fonoster/routr
7
7
  *
8
8
  * This file is part of Routr
@@ -22,7 +22,5 @@ exports.tailor = void 0;
22
22
  const processor_1 = require("@routr/processor");
23
23
  const function_1 = require("fp-ts/function");
24
24
  // Q: Should we add support for strict routing?
25
- const tailor = (route, req) => (0, function_1.pipe)(req, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.applyXHeaders(route), processor_1.Alterations.addRoute(route), processor_1.Alterations.addSelfRecordRoute(route), processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeRoutes, processor_1.Alterations.removeXEdgePortRef
26
- // Add updateContact for SIP.js support
27
- );
25
+ const tailor = (route, req) => (0, function_1.pipe)(req, processor_1.Alterations.decreaseMaxForwards, processor_1.Alterations.removeAuthorization, processor_1.Alterations.removeSelfRoutes, processor_1.Alterations.removeXEdgePortRef, processor_1.Alterations.fixInvalidContact, processor_1.Alterations.addSelfVia(route), processor_1.Alterations.applyXHeaders(route), processor_1.Alterations.addSelfRecordRoute(route), processor_1.Alterations.addRouteToNextHop(route));
28
26
  exports.tailor = tailor;
package/dist/tracer.js CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.init = void 0;
7
7
  /*
8
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
8
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
9
9
  * http://github.com/fonoster/routr
10
10
  *
11
11
  * This file is part of Routr
package/dist/types.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { HeaderModifier } from "@routr/common";
2
- export declare enum ROUTING_DIRECTION {
2
+ export declare enum RoutingDirection {
3
3
  FROM_PSTN = "from-pstn",
4
4
  AGENT_TO_AGENT = "agent-to-agent",
5
5
  AGENT_TO_PSTN = "agent-to-pstn",
6
+ AGENT_TO_PEER = "agent-to-peer",
6
7
  PEER_TO_AGENT = "peer-to-agent",
7
8
  PEER_TO_PSTN = "peer-to-pstn",
8
9
  UNKNOWN = "unknown"
package/dist/types.js CHANGED
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ROUTING_DIRECTION = void 0;
4
- var ROUTING_DIRECTION;
5
- (function (ROUTING_DIRECTION) {
6
- ROUTING_DIRECTION["FROM_PSTN"] = "from-pstn";
7
- ROUTING_DIRECTION["AGENT_TO_AGENT"] = "agent-to-agent";
8
- ROUTING_DIRECTION["AGENT_TO_PSTN"] = "agent-to-pstn";
9
- ROUTING_DIRECTION["PEER_TO_AGENT"] = "peer-to-agent";
10
- ROUTING_DIRECTION["PEER_TO_PSTN"] = "peer-to-pstn";
11
- ROUTING_DIRECTION["UNKNOWN"] = "unknown";
12
- })(ROUTING_DIRECTION = exports.ROUTING_DIRECTION || (exports.ROUTING_DIRECTION = {}));
3
+ exports.RoutingDirection = void 0;
4
+ var RoutingDirection;
5
+ (function (RoutingDirection) {
6
+ RoutingDirection["FROM_PSTN"] = "from-pstn";
7
+ RoutingDirection["AGENT_TO_AGENT"] = "agent-to-agent";
8
+ RoutingDirection["AGENT_TO_PSTN"] = "agent-to-pstn";
9
+ RoutingDirection["AGENT_TO_PEER"] = "agent-to-peer";
10
+ // WARNING: This is not supported yet
11
+ RoutingDirection["PEER_TO_AGENT"] = "peer-to-agent";
12
+ RoutingDirection["PEER_TO_PSTN"] = "peer-to-pstn";
13
+ RoutingDirection["UNKNOWN"] = "unknown";
14
+ })(RoutingDirection = exports.RoutingDirection || (exports.RoutingDirection = {}));
package/dist/utils.d.ts CHANGED
@@ -1,16 +1,24 @@
1
- import { HeaderModifier, MessageRequest, Transport } from "@routr/common";
2
- import { CommonConnect as CC } from "@routr/common";
3
- import { ROUTING_DIRECTION } from "./types";
4
- export declare const isKind: (res: CC.Resource, kind: CC.KIND) => boolean;
5
- export declare const findDomain: (dataAPI: CC.DataAPI, domainUri: string) => Promise<CC.Resource>;
6
- export declare const findResource: (dataAPI: CC.DataAPI, domainUri: string, userpart: string) => Promise<CC.Resource>;
7
- export declare const getRoutingDirection: (caller: CC.Resource, callee: CC.Resource) => ROUTING_DIRECTION;
8
- export declare const createPAssertedIdentity: (req: MessageRequest, trunk: CC.Resource, number: CC.Resource) => HeaderModifier;
9
- export declare const createRemotePartyId: (trunk: CC.Resource, number: CC.Resource) => HeaderModifier;
10
- export declare const createTrunkAuthentication: (dataAPI: CC.DataAPI, trunk: CC.Resource) => Promise<HeaderModifier>;
11
- export declare const getTrunkURI: (trunk: CC.Resource) => {
1
+ import { HeaderModifier, MessageRequest, Transport, CommonConnect as CC, Verifier as V } from "@routr/common";
2
+ import { RoutingDirection } from "./types";
3
+ export declare const isKind: (res: CC.RoutableResourceUnion, kind: CC.Kind) => boolean;
4
+ export declare const findDomain: (apiClient: CC.APIClient, domainUri: string) => Promise<CC.Domain>;
5
+ export declare const findNumberByTelUrl: (apiClient: CC.APIClient, telUrl: string) => Promise<CC.INumber>;
6
+ export declare const findResource: (apiClient: CC.APIClient, domainUri: string, userpart: string) => Promise<CC.RoutableResourceUnion>;
7
+ export declare const getRoutingDirection: (caller: CC.RoutableResourceUnion, callee: CC.RoutableResourceUnion) => RoutingDirection;
8
+ export declare const createPAssertedIdentity: (req: MessageRequest, trunk: CC.Trunk, number: CC.INumber) => HeaderModifier;
9
+ export declare const createRemotePartyId: (trunk: CC.Trunk, number: CC.INumber) => HeaderModifier;
10
+ export declare const createTrunkAuthentication: (trunk: CC.Trunk) => Promise<HeaderModifier>;
11
+ export declare const getTrunkURI: (trunk: CC.Trunk) => {
12
12
  host: string;
13
13
  port: number;
14
14
  user: string;
15
15
  transport: Transport;
16
16
  };
17
+ export declare const getSIPURI: (uri: {
18
+ user?: string;
19
+ host: string;
20
+ }) => string;
21
+ export declare const hasXConnectObjectHeader: (req: MessageRequest) => string;
22
+ export declare const getVerifierImpl: () => {
23
+ verify: (token: string) => Promise<import("@routr/common/dist/errors").ServiceUnavailableError | V.VerifyResponse>;
24
+ };
package/dist/utils.js CHANGED
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
26
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
27
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -8,10 +31,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
31
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
32
  });
10
33
  };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
11
37
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePartyId = exports.createPAssertedIdentity = exports.getRoutingDirection = exports.findResource = exports.findDomain = exports.isKind = void 0;
38
+ 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;
13
39
  /*
14
- * Copyright (C) 2022 by Fonoster Inc (https://fonoster.com)
40
+ * Copyright (C) 2023 by Fonoster Inc (https://fonoster.com)
15
41
  * http://github.com/fonoster/routr
16
42
  *
17
43
  * This file is part of Routr
@@ -28,117 +54,153 @@ exports.getTrunkURI = exports.createTrunkAuthentication = exports.createRemotePa
28
54
  * See the License for the specific language governing permissions and
29
55
  * limitations under the License.
30
56
  */
57
+ const jwt = __importStar(require("jsonwebtoken"));
58
+ const fs_1 = __importDefault(require("fs"));
31
59
  const common_1 = require("@routr/common");
32
- const common_2 = require("@routr/common");
60
+ const processor_1 = require("@routr/processor");
33
61
  const types_1 = require("./types");
62
+ const envs_1 = require("./envs");
63
+ // OMG, this is so ugly and hacky
34
64
  const isKind = (res, kind) => {
35
- if (res == null && kind === common_2.CommonConnect.KIND.UNKNOWN) {
65
+ if (res == null && kind === common_1.CommonConnect.Kind.UNKNOWN) {
66
+ return true;
67
+ }
68
+ else if (res == null) {
69
+ return false;
70
+ }
71
+ else if ("domainRef" in res && kind === common_1.CommonConnect.Kind.AGENT) {
72
+ return true;
73
+ }
74
+ else if ("telUrl" in res && kind === common_1.CommonConnect.Kind.NUMBER) {
75
+ return true;
76
+ }
77
+ else if ("aor" in res && kind === common_1.CommonConnect.Kind.PEER) {
36
78
  return true;
37
79
  }
38
- return (res === null || res === void 0 ? void 0 : res.kind.toLowerCase()) === kind;
39
80
  };
40
81
  exports.isKind = isKind;
41
- const findDomain = (dataAPI, domainUri) => __awaiter(void 0, void 0, void 0, function* () {
42
- return (yield dataAPI.findBy({
43
- kind: common_2.CommonConnect.KIND.DOMAIN,
44
- criteria: common_2.CommonConnect.FindCriteria.FIND_DOMAIN_BY_DOMAINURI,
45
- parameters: {
46
- domainUri
47
- }
48
- }))[0];
82
+ const findDomain = (apiClient, domainUri) => __awaiter(void 0, void 0, void 0, function* () {
83
+ return (yield apiClient.domains.findBy({
84
+ fieldName: "domainUri",
85
+ fieldValue: domainUri
86
+ })).items[0];
49
87
  });
50
88
  exports.findDomain = findDomain;
51
- const findResource = (dataAPI, domainUri, userpart) => __awaiter(void 0, void 0, void 0, function* () {
52
- const domain = yield (0, exports.findDomain)(dataAPI, domainUri);
53
- // TODO: Fix jsonpath not working for logical AND and OR
54
- let res = (yield dataAPI.findBy({
55
- kind: common_2.CommonConnect.KIND.NUMBER,
56
- criteria: common_2.CommonConnect.FindCriteria.FIND_NUMBER_BY_TELURL,
57
- parameters: {
58
- telUrl: `tel:${userpart}`
59
- }
60
- }))[0];
61
- res =
62
- res == null
63
- ? (yield dataAPI.findBy({
64
- kind: common_2.CommonConnect.KIND.AGENT,
65
- criteria: common_2.CommonConnect.FindCriteria.FIND_AGENT_BY_USERNAME,
66
- parameters: {
67
- username: userpart
68
- }
69
- }))[0]
70
- : res;
71
- if ((0, exports.isKind)(res, common_2.CommonConnect.KIND.AGENT) && res.spec.domainRef != (domain === null || domain === void 0 ? void 0 : domain.ref)) {
89
+ const findNumberByTelUrl = (apiClient, telUrl) => __awaiter(void 0, void 0, void 0, function* () {
90
+ return (yield apiClient.numbers.findBy({
91
+ fieldName: "telUrl",
92
+ fieldValue: telUrl
93
+ })).items[0];
94
+ });
95
+ exports.findNumberByTelUrl = findNumberByTelUrl;
96
+ const findResource = (apiClient, domainUri, userpart) => __awaiter(void 0, void 0, void 0, function* () {
97
+ const domain = yield (0, exports.findDomain)(apiClient, domainUri);
98
+ // First, try to find a number
99
+ const number = yield (0, exports.findNumberByTelUrl)(apiClient, `tel:${userpart}`);
100
+ if (number != null)
101
+ return number;
102
+ // Next, try to find an agent
103
+ const agent = (yield apiClient.agents.findBy({
104
+ fieldName: "username",
105
+ fieldValue: userpart
106
+ })).items[0];
107
+ if (agent && agent.domain.ref != (domain === null || domain === void 0 ? void 0 : domain.ref)) {
72
108
  // Not in the same domain
73
109
  return null;
74
110
  }
75
- return res;
111
+ if (agent != null)
112
+ return agent;
113
+ // Next, try to find a peer
114
+ return (yield apiClient.peers.findBy({
115
+ fieldName: "username",
116
+ fieldValue: userpart
117
+ })).items[0];
76
118
  });
77
119
  exports.findResource = findResource;
78
120
  const getRoutingDirection = (caller, callee) => {
79
- if ((0, exports.isKind)(caller, common_2.CommonConnect.KIND.AGENT) && (0, exports.isKind)(callee, common_2.CommonConnect.KIND.AGENT)) {
80
- return types_1.ROUTING_DIRECTION.AGENT_TO_AGENT;
121
+ if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.AGENT) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.AGENT)) {
122
+ return types_1.RoutingDirection.AGENT_TO_AGENT;
123
+ }
124
+ if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.AGENT) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.UNKNOWN)) {
125
+ return types_1.RoutingDirection.AGENT_TO_PSTN;
81
126
  }
82
- if ((0, exports.isKind)(caller, common_2.CommonConnect.KIND.AGENT) && (0, exports.isKind)(callee, common_2.CommonConnect.KIND.UNKNOWN)) {
83
- return types_1.ROUTING_DIRECTION.AGENT_TO_PSTN;
127
+ if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.PEER) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.AGENT)) {
128
+ return types_1.RoutingDirection.PEER_TO_AGENT;
84
129
  }
85
- if ((0, exports.isKind)(caller, common_2.CommonConnect.KIND.PEER) && (0, exports.isKind)(callee, common_2.CommonConnect.KIND.AGENT)) {
86
- return types_1.ROUTING_DIRECTION.PEER_TO_AGENT;
130
+ if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.AGENT) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.PEER)) {
131
+ return types_1.RoutingDirection.AGENT_TO_PEER;
87
132
  }
88
133
  // All we know is that the Number is managed by this instance of Routr
89
- if ((0, exports.isKind)(callee, common_2.CommonConnect.KIND.NUMBER)) {
90
- return types_1.ROUTING_DIRECTION.FROM_PSTN;
134
+ if ((0, exports.isKind)(callee, common_1.CommonConnect.Kind.NUMBER)) {
135
+ return types_1.RoutingDirection.FROM_PSTN;
91
136
  }
92
- if ((0, exports.isKind)(caller, common_2.CommonConnect.KIND.PEER) && (0, exports.isKind)(callee, common_2.CommonConnect.KIND.UNKNOWN)) {
93
- return types_1.ROUTING_DIRECTION.PEER_TO_PSTN;
137
+ if ((0, exports.isKind)(caller, common_1.CommonConnect.Kind.PEER) && (0, exports.isKind)(callee, common_1.CommonConnect.Kind.UNKNOWN)) {
138
+ return types_1.RoutingDirection.PEER_TO_PSTN;
94
139
  }
95
- return types_1.ROUTING_DIRECTION.UNKNOWN;
140
+ return types_1.RoutingDirection.UNKNOWN;
96
141
  };
97
142
  exports.getRoutingDirection = getRoutingDirection;
98
143
  const createPAssertedIdentity = (req, trunk, number) => {
99
144
  const displayName = req.message.from.address.displayName;
100
- const remoteNumber = number.spec.location.telUrl.split(":")[1];
145
+ const remoteNumber = number.telUrl.split(":")[1];
101
146
  const trunkHost = (0, exports.getTrunkURI)(trunk).host;
102
147
  return {
103
148
  name: "P-Asserted-Identity",
104
149
  value: displayName
105
150
  ? `"${displayName}" <sip:${remoteNumber}@${trunkHost};user=phone>`
106
151
  : `<sip:${remoteNumber}@${trunkHost};user=phone>`,
107
- action: common_2.CommonTypes.HeaderModifierAction.ADD
152
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
108
153
  };
109
154
  };
110
155
  exports.createPAssertedIdentity = createPAssertedIdentity;
111
156
  const createRemotePartyId = (trunk, number) => {
112
- const remoteNumber = number.spec.location.telUrl.split(":")[1];
157
+ const remoteNumber = number.telUrl.split(":")[1];
113
158
  const trunkHost = (0, exports.getTrunkURI)(trunk).host;
114
159
  return {
115
160
  name: "Remote-Party-ID",
116
161
  value: `<sip:${remoteNumber}@${trunkHost}>;screen=yes;party=calling`,
117
- action: common_2.CommonTypes.HeaderModifierAction.ADD
162
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
118
163
  };
119
164
  };
120
165
  exports.createRemotePartyId = createRemotePartyId;
121
- const createTrunkAuthentication = (dataAPI, trunk) => __awaiter(void 0, void 0, void 0, function* () {
166
+ const createTrunkAuthentication = (trunk) => __awaiter(void 0, void 0, void 0, function* () {
122
167
  var _a, _b;
123
- const credentials = yield dataAPI.get(trunk.spec.outbound.credentialsRef);
124
168
  return {
125
- name: common_2.CommonTypes.ExtraHeader.GATEWAY_AUTH,
126
- value: Buffer.from(`${(_a = credentials.spec.credentials) === null || _a === void 0 ? void 0 : _a.username}:${(_b = credentials.spec.credentials) === null || _b === void 0 ? void 0 : _b.password}`).toString("base64"),
127
- action: common_2.CommonTypes.HeaderModifierAction.ADD
169
+ name: common_1.CommonTypes.ExtraHeader.GATEWAY_AUTH,
170
+ value: Buffer.from(`${(_a = trunk.outboundCredentials) === null || _a === void 0 ? void 0 : _a.username}:${(_b = trunk.outboundCredentials) === null || _b === void 0 ? void 0 : _b.password}`).toString("base64"),
171
+ action: common_1.CommonTypes.HeaderModifierAction.ADD
128
172
  };
129
173
  });
130
174
  exports.createTrunkAuthentication = createTrunkAuthentication;
131
175
  const getTrunkURI = (trunk) => {
132
- var _a;
133
- const { user, host, port, transport } = (_a = trunk.spec.outbound) === null || _a === void 0 ? void 0 : _a.uris[0].uri;
134
- const t = !transport
135
- ? common_1.Transport.UDP
136
- : Object.values(common_1.Transport)[Object.values(common_1.Transport).indexOf(transport.toLowerCase())];
176
+ if (!trunk.uris) {
177
+ throw new Error(`trunk ${trunk.ref} has no outbound settings`);
178
+ }
179
+ const { user, host, port, transport } = trunk.uris[0];
137
180
  return {
138
181
  user,
139
182
  host,
140
183
  port: port !== null && port !== void 0 ? port : 5060,
141
- transport: t
184
+ transport: transport !== null && transport !== void 0 ? transport : common_1.Transport.UDP
142
185
  };
143
186
  };
144
187
  exports.getTrunkURI = getTrunkURI;
188
+ const getSIPURI = (uri) => uri.user ? `sip:${uri.user}@${uri.host}` : `sip:${uri.host}`;
189
+ exports.getSIPURI = getSIPURI;
190
+ const hasXConnectObjectHeader = (req) => processor_1.Extensions.getHeaderValue(req, common_1.CommonTypes.ExtraHeader.CONNECT_TOKEN);
191
+ exports.hasXConnectObjectHeader = hasXConnectObjectHeader;
192
+ const getVerifierImpl = () => {
193
+ if (envs_1.CONNECT_VERIFIER_PUBLIC_KEY_PATH) {
194
+ const publicKey = fs_1.default.readFileSync(envs_1.CONNECT_VERIFIER_PUBLIC_KEY_PATH, "utf8");
195
+ return {
196
+ verify: (token) => __awaiter(void 0, void 0, void 0, function* () {
197
+ return jwt.verify(token, publicKey, envs_1.CONNECT_VERIFIER_OPTIONS);
198
+ })
199
+ };
200
+ }
201
+ else if (envs_1.CONNECT_VERIFIER_ADDR) {
202
+ return { verify: common_1.Verifier.verifier(envs_1.CONNECT_VERIFIER_ADDR) };
203
+ }
204
+ return null;
205
+ };
206
+ exports.getVerifierImpl = getVerifierImpl;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routr/connect",
3
- "version": "2.0.8-alpha.4",
3
+ "version": "2.0.8-alpha.40",
4
4
  "description": "Default processor",
5
5
  "author": "Pedro Sanders <psanders@fonoster.com>",
6
6
  "homepage": "https://github.com/fonoster/routr#readme",
@@ -16,10 +16,11 @@
16
16
  "build": "tsc -b tsconfig.json"
17
17
  },
18
18
  "bin": {
19
- "run_connect": "dist/runner.js"
19
+ "run_connect": "dist/runner.js",
20
+ "healthcheck_connect": "dist/healthcheck.js"
20
21
  },
21
22
  "dependencies": {
22
- "@fonoster/logger": "0.3.18",
23
+ "@fonoster/logger": "0.3.20",
23
24
  "@opentelemetry/api": "^1.0.4",
24
25
  "@opentelemetry/exporter-jaeger": "^1.0.4",
25
26
  "@opentelemetry/instrumentation": "^0.27.0",
@@ -28,9 +29,13 @@
28
29
  "@opentelemetry/sdk-trace-base": "^1.0.4",
29
30
  "@opentelemetry/sdk-trace-node": "^1.0.4",
30
31
  "@opentelemetry/semantic-conventions": "^1.0.4",
31
- "@routr/common": "^2.0.8-alpha.4",
32
- "@routr/location": "^2.0.8-alpha.4",
33
- "@routr/processor": "^2.0.8-alpha.4"
32
+ "@routr/common": "^2.0.8-alpha.40",
33
+ "@routr/location": "^2.0.8-alpha.40",
34
+ "@routr/processor": "^2.0.8-alpha.40",
35
+ "jsonwebtoken": "^9.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/jsonwebtoken": "^9.0.1"
34
39
  },
35
40
  "files": [
36
41
  "dist"
@@ -45,5 +50,5 @@
45
50
  "bugs": {
46
51
  "url": "https://github.com/fonoster/routr/issues"
47
52
  },
48
- "gitHead": "320abeb05175976ddfca265096a2ea63004607f4"
53
+ "gitHead": "3ac346a36a43ba06578accf48d7a4adac24a0911"
49
54
  }