SOCKS5 proxy and Tor per account #4

Closed
ann wants to merge 0 commits from feat/socks5-proxy into develop
Owner

An account's connections can go through a SOCKS5 proxy or Tor. quackchat (moving to libtacky from upstream tacky) depends on this per-account feature, which upstream tacky had.

What changes

  • tacky-core: stream::socks5 adds RFC 1928 CONNECT with RFC 1929 login. The target is named to the proxy (address type 3) and never resolved locally. The handshake runs inside the dial's step and stop. Reply codes map onto the dial's kinds, and a failure names the proxy.
    • Config.proxy and the new tacky_stream_config_t fields: every candidate goes through the proxy, WebSocket included. Under a proxy the SRV endpoint skips both the SRV lookup and host-meta and dials domain:5222. Nothing falls back to a direct connection.
    • net::socket::connect_via and tacky_net_socket_new_via give a host's own protocols the same path. The browser build refuses a proxy.
  • Bindings, session and HTTP: stream connect takes a proxy key and socket::connect a -proxy option. The session takes -proxy. tacky::http's spec takes proxy, which every dial of a transfer, redirects included, goes through. The one-call verbs take -proxy.
  • Account: account add/set take proxy (socks5://[user:pass@]host:port) and tor. The store holds two nullable columns, added in place to older files. proxy.tcl builds the route. Tor defaults to 127.0.0.1:9050, with a random login per account so each account gets its own circuits (IsolateSOCKSAuth).
    • The route reaches conn, bareconn and the registration session. A change reconfigures a live client and restarts an enabled account's connection.
    • Every HTTP transfer of the account (uploads, downloads, avatars by URL, the OAuth exchanges, XEP-0455) takes the route. The host transfer backend refuses such a transfer instead.
  • What a route rules out: calls and group calls are refused, and an incoming propose is rejected with unsupported-transports and logged as missed, because ICE would carry media around the proxy. Direct file transfers lose the data channel and fall back to IBB.
  • Docs: schema, make gen-api, the wire.tcl typing for tor, and an RFC 1928 row in features.md.

Gate run in the container on this branch

  • Tcl unit suite: 184 suites, 5,403 cases, 5,393 passed, 0 failed, 10 skipped (datachannel lane).
  • tacky-core cargo test --features tacky-keyring/fast-kdf: every suite passes, including cov_transport this time.
  • check-api, check-headers and check-native-neutral pass. make headers regenerated tacky_ffi.h, and the DOAP file is unchanged.

Not run

  • The webrtc media lane of the Tcl suite.
  • The Prosody and ejabberd integration rigs.
  • Any test against a real Tor daemon.
An account's connections can go through a SOCKS5 proxy or Tor. quackchat (moving to libtacky from upstream tacky) depends on this per-account feature, which upstream tacky had. **What changes** - **tacky-core**: `stream::socks5` adds RFC 1928 CONNECT with RFC 1929 login. The target is named to the proxy (address type 3) and never resolved locally. The handshake runs inside the dial's step and stop. Reply codes map onto the dial's kinds, and a failure names the proxy. - `Config.proxy` and the new `tacky_stream_config_t` fields: every candidate goes through the proxy, WebSocket included. Under a proxy the SRV endpoint skips both the SRV lookup and host-meta and dials `domain:5222`. Nothing falls back to a direct connection. - `net::socket::connect_via` and `tacky_net_socket_new_via` give a host's own protocols the same path. The browser build refuses a proxy. - **Bindings, session and HTTP**: `stream connect` takes a `proxy` key and `socket::connect` a `-proxy` option. The session takes `-proxy`. `tacky::http`'s spec takes `proxy`, which every dial of a transfer, redirects included, goes through. The one-call verbs take `-proxy`. - **Account**: `account add`/`set` take `proxy` (`socks5://[user:pass@]host:port`) and `tor`. The store holds two nullable columns, added in place to older files. `proxy.tcl` builds the route. Tor defaults to 127.0.0.1:9050, with a random login per account so each account gets its own circuits (IsolateSOCKSAuth). - The route reaches conn, bareconn and the registration session. A change reconfigures a live client and restarts an enabled account's connection. - Every HTTP transfer of the account (uploads, downloads, avatars by URL, the OAuth exchanges, XEP-0455) takes the route. The `host` transfer backend refuses such a transfer instead. - **What a route rules out**: calls and group calls are refused, and an incoming propose is rejected with `unsupported-transports` and logged as missed, because ICE would carry media around the proxy. Direct file transfers lose the data channel and fall back to IBB. - **Docs**: schema, `make gen-api`, the `wire.tcl` typing for `tor`, and an RFC 1928 row in `features.md`. **Gate run in the container on this branch** - Tcl unit suite: 184 suites, 5,403 cases, 5,393 passed, 0 failed, 10 skipped (datachannel lane). - tacky-core `cargo test --features tacky-keyring/fast-kdf`: every suite passes, including `cov_transport` this time. - `check-api`, `check-headers` and `check-native-neutral` pass. `make headers` regenerated `tacky_ffi.h`, and the DOAP file is unchanged. **Not run** - The webrtc media lane of the Tcl suite. - The Prosody and ejabberd integration rigs. - Any test against a real Tor daemon.
tacky-core dials through a SOCKS5 proxy (RFC 1928, with RFC 1929's login): stream::socks5 connects to the proxy by the transport's own resolve and RFC 8305 dial and asks it for the target by name (address type 3), so the name is resolved there and never here, an address literal going as one, all within the dial's step and given up on at the stop; the proxy's replies become the dial's kinds (4, what Tor answers for a name that does not resolve, is resolve; 2 and 5 refused; 6 timeout) and the failure names the proxy, so a dead proxy does not read as the server refusing. The stream's Config takes a proxy (tacky_stream_config_t's proxy_host, proxy_port, proxy_username, proxy_password, the browser build refusing one), every candidate goes through it, WebSocket included, and through it the SRV endpoint looks nothing up, neither SRV nor host-meta, both of which would ask around the proxy, and dials the domain on 5222; nothing falls back to a direct connection. net::socket gains connect_via and the C ABI tacky_net_socket_new_via for a host's own protocol. The login is never in Debug and is wiped on drop. Tests: the handshake against a proxy played on loopback (no login, a login, a refused login, a method we cannot do, every reply code, a bound name or IPv6 address read past, a silent proxy timing out, a stop, a dead proxy named), streams of each kind through a forwarding proxy, SRV through a proxy asking neither the lookup nor host-meta, the FFI constructor, and the config's new layout. 489832bc49
An account can go through a SOCKS5 proxy or Tor: account add and set take proxy (socks5://[user:pass@]host:port, parsed and refused at the door) and tor, the store keeps them as two nullable columns added in place to older files, and get answers them. proxy.tcl makes the route: the proxy as given or, with tor, Tor at the proxy or at 127.0.0.1:9050 with a random login per account for the life of the process, so each account rides circuits of its own (Tor's IsolateSOCKSAuth) and the exit sees no name. endpointOptions hands the route to conn, bareconn and the registration session alike, a change reaches a live client and restarts an enabled account's connection so nothing stays up on the old route, and the transfer object gives an account's every HTTP transfer the same route (uploads, downloads, avatars by URL, the OAuth exchanges, XEP-0455's status), refusing it on the host backend, which could not honour it. Calls and group calls are refused while a route is set, and a propose that rings is rejected with unsupported-transports and logged missed, because ICE sends UDP a SOCKS5 CONNECT cannot carry and the media would go around the proxy; a direct file transfer has no data channel then and rides IBB, inside the stream. features.md gains the RFC 1928 row. Tests: the parser, the endpoint, Tor's logins, what is stored and refused, the route on the conn and on a live client, the transfers' route, the session's dial dict, the call, group call and data-channel refusals; the mocks take -proxy and the endpointOptions expectations name it. 8e756669fc
Author
Owner

Merged into develop by hand as b526f78b (feat/socks5-rc5 onto release/rc5), in v1.0.0-rc5 (d1cca8e5), with eight follow-up commits from the review (4ec68d20 set dials after the commit and the route change ends calls, 03d8b9f5 transfer refuses on a route error, 7b16862b proxy redacted, d437e329 the proxy reject notifies a missed call, 9f83c291 register connect -proxy/-tor, e72c8827 the data-channel feature follows the route, 8e62aa6a the RFC 1929 version byte). The account.tcl conflict with rc5's PushConnectionColumns is resolved as one path: proxy and tor are connection columns, a route change restarts an enabled account once.

Merged into develop by hand as b526f78b (feat/socks5-rc5 onto release/rc5), in v1.0.0-rc5 (d1cca8e5), with eight follow-up commits from the review (4ec68d20 set dials after the commit and the route change ends calls, 03d8b9f5 transfer refuses on a route error, 7b16862b proxy redacted, d437e329 the proxy reject notifies a missed call, 9f83c291 register connect -proxy/-tor, e72c8827 the data-channel feature follows the route, 8e62aa6a the RFC 1929 version byte). The account.tcl conflict with rc5's PushConnectionColumns is resolved as one path: proxy and tor are connection columns, a route change restarts an enabled account once.
ann closed this pull request 2026-10-15 17:35:50 +00:00
ann referenced this pull request from a commit 2026-10-27 00:26:42 +00:00
schedule: the scheduler is our own server or a gateway we are registered with, never any domain that says it schedules, and resubmits, releases and cancels lose nothing, from the audit of 2026-10-26 (/data/another-im/audit-2026-10-26-libtacky.md #1 Critical, #3, #4, #5; S5 of the schedule sub-report): a chat's domain is its scheduler only when the account is registered with it (in the roster at to or both, roster transportUser's rule) and its disco#info names a gateway identity and the feature (Registered, Trusted); any other domain, a plain server running mod_schedule included, is never asked, and a <changed/> from anyone but our server or a registered gateway about a chat at itself is ignored before anything is asked or read, so a federated domain can no longer hold, drop or plant our scheduled messages or make us send disco#info to domains it names; a redirect is followed only to a registered gateway; a listed row is read with omemo decryptListed, side-effect free. Resubmit writes the msg column (it wrote row, which the table has not, so send_now of a refused submit and an edit of one never taken always threw). A submit left waiting by a lost answer goes again at the next sign-in or resumption only after its chat's list answered (SubmitWaiting, RecheckThenSubmit), which takes the item back by origin-id, and an edit made meanwhile is put to that item by <replace/> (PutChange) instead of a second submit. An item gone from a list, or a cancel answered item-not-found, keeps its row hidden as released for its copy (30 days) instead of deleting it when its time was ahead, which lost a send_now from another device or a release racing a cancel from this device's history; a <changed what='cancelled'/> naming the item drops it at once. docs/protocol/schedule-0.md §3, §4.4, §5, §6, §7 and §8 say so. Unit tests in test_schedule.tcl, each failing without the change but the cancelled-headline guard; test_schedule 37 of 37 in the container. Nothing changes for tackygram, which is a registered gateway with identity gateway/telegram; anotherim-server mod_schedule could also answer a submit repeating one of the user's origin-ids with that item.

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
another-im/libtacky!4
No description provided.