mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-20 06:28:15 +03:00
feat: add socks support
This commit is contained in:
@@ -30,7 +30,10 @@ export function coreService() {
|
||||
{
|
||||
intervalMs: 3000,
|
||||
onNewLog: (line) => {
|
||||
if (line.toLowerCase().includes('[error]') || line.toLowerCase().includes('[fatal]')) {
|
||||
if (
|
||||
line.toLowerCase().includes('[error]') ||
|
||||
line.toLowerCase().includes('[fatal]')
|
||||
) {
|
||||
ui.addNotification('Podkop Error', E('div', {}, line), 'error');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,3 +10,4 @@ export * from './validateVlessUrl';
|
||||
export * from './validateOutboundJson';
|
||||
export * from './validateTrojanUrl';
|
||||
export * from './validateProxyUrl';
|
||||
export * from './validateSocksUrl';
|
||||
|
||||
52
fe-app-podkop/src/validators/tests/validateSocksUrl.test.js
Normal file
52
fe-app-podkop/src/validators/tests/validateSocksUrl.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { validateSocksUrl } from '../validateSocksUrl';
|
||||
|
||||
const validUrls = [
|
||||
['socks4 basic', 'socks4://127.0.0.1:1080'],
|
||||
['socks4a basic', 'socks4a://127.0.0.1:1080'],
|
||||
['socks5 basic', 'socks5://127.0.0.1:1080'],
|
||||
['socks5 with username', 'socks5://user@127.0.0.1:1080'],
|
||||
['socks5 with username/password', 'socks5://user:pass@127.0.0.1:1080'],
|
||||
['socks5 with domain', 'socks5://user:pass@my.proxy.com:1080'],
|
||||
['socks5 with dash in domain', 'socks5://user:pass@fast-proxy.net:8080'],
|
||||
['socks5 with uppercase domain', 'socks5://USER:PASSWORD@Example.COM:1080'],
|
||||
];
|
||||
|
||||
const invalidUrls = [
|
||||
['no prefix', '127.0.0.1:1080'],
|
||||
['wrong prefix', 'http://127.0.0.1:1080'],
|
||||
['missing host', 'socks5://user:pass@:1080'],
|
||||
['missing port', 'socks5://127.0.0.1'],
|
||||
['invalid port (non-numeric)', 'socks5://127.0.0.1:abc'],
|
||||
['invalid port (too high)', 'socks5://127.0.0.1:99999'],
|
||||
['space in url', 'socks5://127.0. 0.1:1080'],
|
||||
['missing username when auth provided', 'socks5://:pass@127.0.0.1:1080'],
|
||||
['invalid domain chars', 'socks5://user:pass@exa_mple.com:1080'],
|
||||
['extra symbol', 'socks5:///127.0.0.1:1080'],
|
||||
];
|
||||
|
||||
describe('validateSocksUrl', () => {
|
||||
describe.each(validUrls)('Valid URL: %s', (_desc, url) => {
|
||||
it(`returns valid=true for "${url}"`, () => {
|
||||
const res = validateSocksUrl(url);
|
||||
expect(res.valid).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(invalidUrls)('Invalid URL: %s', (_desc, url) => {
|
||||
it(`returns valid=false for "${url}"`, () => {
|
||||
const res = validateSocksUrl(url);
|
||||
expect(res.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('detects invalid port range (0)', () => {
|
||||
const res = validateSocksUrl('socks5://127.0.0.1:0');
|
||||
expect(res.valid).toBe(false);
|
||||
});
|
||||
|
||||
it('detects invalid port range (65536)', () => {
|
||||
const res = validateSocksUrl('socks5://127.0.0.1:65536');
|
||||
expect(res.valid).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { ValidationResult } from './types';
|
||||
import { validateShadowsocksUrl } from './validateShadowsocksUrl';
|
||||
import { validateVlessUrl } from './validateVlessUrl';
|
||||
import { validateTrojanUrl } from './validateTrojanUrl';
|
||||
import { validateSocksUrl } from './validateSocksUrl';
|
||||
|
||||
// TODO refactor current validation and add tests
|
||||
export function validateProxyUrl(url: string): ValidationResult {
|
||||
@@ -17,8 +18,14 @@ export function validateProxyUrl(url: string): ValidationResult {
|
||||
return validateTrojanUrl(url);
|
||||
}
|
||||
|
||||
if (/^socks(4|4a|5):\/\//.test(url)) {
|
||||
return validateSocksUrl(url);
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
message: _('URL must start with vless:// or ss:// or trojan://'),
|
||||
message: _(
|
||||
'URL must start with vless://, ss://, trojan://, or socks4/5://',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
81
fe-app-podkop/src/validators/validateSocksUrl.ts
Normal file
81
fe-app-podkop/src/validators/validateSocksUrl.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { ValidationResult } from './types';
|
||||
import { validateDomain } from './validateDomain';
|
||||
import { validateIPV4 } from './validateIp';
|
||||
|
||||
export function validateSocksUrl(url: string): ValidationResult {
|
||||
try {
|
||||
if (!/^socks(4|4a|5):\/\//.test(url)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _(
|
||||
'Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
if (!url || /\s/.test(url)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: must not contain spaces'),
|
||||
};
|
||||
}
|
||||
|
||||
const body = url.replace(/^socks(4|4a|5):\/\//, '');
|
||||
const [authAndHost] = body.split('#'); // отбрасываем hash, если есть
|
||||
const [credentials, hostPortPart] = authAndHost.includes('@')
|
||||
? authAndHost.split('@')
|
||||
: [null, authAndHost];
|
||||
|
||||
if (credentials) {
|
||||
const [username, _password] = credentials.split(':');
|
||||
if (!username) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: missing username'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!hostPortPart) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: missing host and port'),
|
||||
};
|
||||
}
|
||||
|
||||
const [host, port] = hostPortPart.split(':');
|
||||
|
||||
if (!host) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: missing hostname or IP'),
|
||||
};
|
||||
}
|
||||
|
||||
if (!port) {
|
||||
return { valid: false, message: _('Invalid SOCKS URL: missing port') };
|
||||
}
|
||||
|
||||
const portNum = Number(port);
|
||||
if (!Number.isInteger(portNum) || portNum < 1 || portNum > 65535) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: invalid port number'),
|
||||
};
|
||||
}
|
||||
|
||||
const ipv4Result = validateIPV4(host);
|
||||
const domainResult = validateDomain(host);
|
||||
|
||||
if (!ipv4Result.valid && !domainResult.valid) {
|
||||
return {
|
||||
valid: false,
|
||||
message: _('Invalid SOCKS URL: invalid host format'),
|
||||
};
|
||||
}
|
||||
} catch (_e) {
|
||||
return { valid: false, message: _('Invalid SOCKS URL: parsing failed') };
|
||||
}
|
||||
|
||||
return { valid: true, message: _('Valid') };
|
||||
}
|
||||
Reference in New Issue
Block a user