|
17 | 17 |
|
18 | 18 | import { log } from './logging';
|
19 | 19 | import { LogVerbosity } from './constants';
|
20 |
| -import { Socket } from 'net'; |
| 20 | +import { isIPv4, Socket } from 'net'; |
21 | 21 | import * as http from 'http';
|
22 | 22 | import * as logging from './logging';
|
23 | 23 | import {
|
@@ -119,6 +119,58 @@ function getNoProxyHostList(): string[] {
|
119 | 119 | }
|
120 | 120 | }
|
121 | 121 |
|
| 122 | +interface CIDRNotation { |
| 123 | + ip: number; |
| 124 | + prefixLength: number; |
| 125 | +} |
| 126 | + |
| 127 | +/* |
| 128 | + * The groups correspond to CIDR parts as follows: |
| 129 | + * 1. ip |
| 130 | + * 2. prefixLength |
| 131 | + */ |
| 132 | + |
| 133 | +export function parseCIDR(cidrString: string): CIDRNotation | null { |
| 134 | + const splitRange = cidrString.split('/'); |
| 135 | + if (splitRange.length !== 2) { |
| 136 | + return null; |
| 137 | + } |
| 138 | + const prefixLength = parseInt(splitRange[1], 10); |
| 139 | + if (!isIPv4(splitRange[0]) || Number.isNaN(prefixLength) || prefixLength < 0 || prefixLength > 32) { |
| 140 | + return null; |
| 141 | + } |
| 142 | + return { |
| 143 | + ip: ipToInt(splitRange[0]), |
| 144 | + prefixLength: prefixLength |
| 145 | + }; |
| 146 | +} |
| 147 | + |
| 148 | +function ipToInt(ip: string) { |
| 149 | + return ip.split(".").reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0); |
| 150 | +} |
| 151 | + |
| 152 | +function isIpInCIDR(cidr: CIDRNotation, serverHost: string) { |
| 153 | + const ip = cidr.ip; |
| 154 | + const mask = -1 << (32 - cidr.prefixLength); |
| 155 | + const hostIP = ipToInt(serverHost); |
| 156 | + |
| 157 | + return (hostIP & mask) === (ip & mask); |
| 158 | +} |
| 159 | + |
| 160 | +function hostMatchesNoProxyList(serverHost: string): boolean { |
| 161 | + for (const host of getNoProxyHostList()) { |
| 162 | + const parsedCIDR = parseCIDR(host); |
| 163 | + // host is a CIDR and serverHost is an IP address |
| 164 | + if (isIPv4(serverHost) && parsedCIDR && isIpInCIDR(parsedCIDR, serverHost)) { |
| 165 | + return true; |
| 166 | + } else if (serverHost.endsWith(host)) { |
| 167 | + // host is a single IP or a domain name suffix |
| 168 | + return true; |
| 169 | + } |
| 170 | + } |
| 171 | + return false; |
| 172 | +} |
| 173 | + |
122 | 174 | export interface ProxyMapResult {
|
123 | 175 | target: GrpcUri;
|
124 | 176 | extraOptions: ChannelOptions;
|
@@ -147,13 +199,9 @@ export function mapProxyName(
|
147 | 199 | return noProxyResult;
|
148 | 200 | }
|
149 | 201 | const serverHost = hostPort.host;
|
150 |
| - for (const host of getNoProxyHostList()) { |
151 |
| - if (host === serverHost) { |
152 |
| - trace( |
153 |
| - 'Not using proxy for target in no_proxy list: ' + uriToString(target) |
154 |
| - ); |
155 |
| - return noProxyResult; |
156 |
| - } |
| 202 | + if (hostMatchesNoProxyList(serverHost)) { |
| 203 | + trace('Not using proxy for target in no_proxy list: ' + uriToString(target)); |
| 204 | + return noProxyResult; |
157 | 205 | }
|
158 | 206 | const extraOptions: ChannelOptions = {
|
159 | 207 | 'grpc.http_connect_target': uriToString(target),
|
|
0 commit comments