Skip to content

Commit

Permalink
Add RedsocksConf.stringify method
Browse files Browse the repository at this point in the history
`stringify` takes a RedsocksConfig, an internal object
representation of the redsocks.conf file, and transforms
it into a valid string that can be written to redsocks.conf.

Signed-off-by: Christina Ying Wang <[email protected]>
  • Loading branch information
cywang117 committed Jul 3, 2024
1 parent 1e224be commit 9c6681b
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/host-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as config from '../config';
import { pathOnRoot } from '../lib/host-utils';

export * from './proxy';
export * from './types';

const hostnamePath = pathOnRoot('/etc/hostname');

Expand Down
41 changes: 38 additions & 3 deletions src/host-config/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,31 @@ const isAuthField = (field: string): boolean =>
const blockRegexFor = (blockLabel: string) =>
new RegExp(`${blockLabel}\\s?{([\\s\\S]+?)}(?=\\s|$)`);

const baseBlock = {
log_debug: 'off',
log_info: 'on',
log: 'stderr',
daemon: 'off',
redirector: 'iptables',
};

export class RedsocksConf {
// public static stringify(_config: RedsocksConfig): string {
// return 'TODO';
// }
public static stringify(config: RedsocksConfig): string {
const blocks: string[] = [];

if (config.redsocks && Object.keys(config.redsocks).length > 0) {
blocks.push(RedsocksConf.stringifyBlock('base', baseBlock));
blocks.push(
RedsocksConf.stringifyBlock('redsocks', {
...config.redsocks,
local_ip: '127.0.0.1',
local_port: 12345,
}),
);
}

return blocks.length ? blocks.join('\n') : '';
}

public static parse(rawConf: string): RedsocksConfig {
const conf: RedsocksConfig = {};
Expand Down Expand Up @@ -63,6 +84,20 @@ export class RedsocksConf {
}
}

private static stringifyBlock(
label: string,
block: Record<string, any>,
): string {
const lines = Object.entries(block).map(([key, value]) => {
if (isAuthField(key)) {
// Add double quotes around login and password fields
value = `${value.startsWith('"') ? '' : '"'}${value}${value.endsWith('"') ? '' : '"'}`;
}
return `\t${key} = ${value};`;
});
return `${label} {\n${lines.join('\n')}\n}\n`;
}

/**
* Given the raw contents of a block redsocks.conf file,
* extract to a key-value object.
Expand Down
118 changes: 118 additions & 0 deletions test/unit/host-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,124 @@ import * as hostConfig from '~/src/host-config/index';
import log from '~/lib/supervisor-console';

describe('RedsocksConf', () => {
describe('stringify', () => {
it('stringifies RedsocksConfig into config string', () => {
const conf: hostConfig.RedsocksConfig = {
redsocks: {
type: 'socks5',
ip: 'example.org',
port: 1080,
login: '"foo"',
password: '"bar"',
},
};
const confStr = hostConfig.RedsocksConf.stringify(conf);
expect(confStr).to.equal(
stripIndent`
base {
log_debug = off;
log_info = on;
log = stderr;
daemon = off;
redirector = iptables;
}
redsocks {
type = socks5;
ip = example.org;
port = 1080;
login = "foo";
password = "bar";
local_ip = 127.0.0.1;
local_port = 12345;
}
` + '\n',
);
});

it('adds double quotes to auth fields if not exists', () => {
const conf: hostConfig.RedsocksConfig = {
redsocks: {
type: 'socks5',
ip: 'example.org',
port: 1080,
login: 'foo',
password: 'bar',
},
};
const confStr = hostConfig.RedsocksConf.stringify(conf);
expect(confStr).to.equal(
stripIndent`
base {
log_debug = off;
log_info = on;
log = stderr;
daemon = off;
redirector = iptables;
}
redsocks {
type = socks5;
ip = example.org;
port = 1080;
login = "foo";
password = "bar";
local_ip = 127.0.0.1;
local_port = 12345;
}
` + '\n',
);
});

it('accepts port field of type string', () => {
const conf = {
redsocks: {
type: 'socks5',
ip: 'example.org',
port: '1080',
login: 'foo',
password: 'bar',
},
} as unknown as hostConfig.RedsocksConfig;
const confStr = hostConfig.RedsocksConf.stringify(conf);
expect(confStr).to.equal(
stripIndent`
base {
log_debug = off;
log_info = on;
log = stderr;
daemon = off;
redirector = iptables;
}
redsocks {
type = socks5;
ip = example.org;
port = 1080;
login = "foo";
password = "bar";
local_ip = 127.0.0.1;
local_port = 12345;
}
` + '\n',
);
});

it('stringifies to empty string when provided empty RedsocksConfig', () => {
const conf: hostConfig.RedsocksConfig = {};
const confStr = hostConfig.RedsocksConf.stringify(conf);
expect(confStr).to.equal('');
});

it('stringifies to empty string when provided empty redsocks block', () => {
const conf: hostConfig.RedsocksConfig = {
redsocks: {} as hostConfig.ProxyConfig,
};
const confStr = hostConfig.RedsocksConf.stringify(conf);
expect(confStr).to.equal('');
});
});

describe('parse', () => {
it('parses config string into RedsocksConfig', () => {
const redsocksConfStr = stripIndent`
Expand Down

0 comments on commit 9c6681b

Please sign in to comment.